Skip to content

Commit 17cdceb

Browse files
authored
Merge pull request Wei-Shaw#1109 from GuangYiDing/feat/subscription-guide
feat(subscriptions): 订阅管理页面添加教程指南弹窗
2 parents a14babd + aadc6a7 commit 17cdceb

3 files changed

Lines changed: 157 additions & 2 deletions

File tree

frontend/src/i18n/locales/en.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1759,7 +1759,37 @@ export default {
17591759
pleaseSelectGroup: 'Please select a group',
17601760
validityDaysRequired: 'Please enter a valid number of days (at least 1)',
17611761
revokeConfirm:
1762-
"Are you sure you want to revoke the subscription for '{user}'? This action cannot be undone."
1762+
"Are you sure you want to revoke the subscription for '{user}'? This action cannot be undone.",
1763+
guide: {
1764+
title: 'Subscription Management Guide',
1765+
subtitle: 'Subscription mode lets you assign time-based usage quotas to users, with daily/weekly/monthly limits. Follow these steps to get started.',
1766+
showGuide: 'Usage Guide',
1767+
step1: {
1768+
title: 'Create a Subscription Group',
1769+
line1: 'Go to "Group Management" page, click "Create Group"',
1770+
line2: 'Set billing type to "Subscription", configure daily/weekly/monthly quota limits',
1771+
line3: 'Save the group and ensure its status is "Active"',
1772+
link: 'Go to Group Management'
1773+
},
1774+
step2: {
1775+
title: 'Assign Subscription to User',
1776+
line1: 'Click the "Assign Subscription" button in the top right',
1777+
line2: 'Search for a user by email and select them',
1778+
line3: 'Choose a subscription group, set validity days, then click "Assign"'
1779+
},
1780+
step3: {
1781+
title: 'Manage Existing Subscriptions'
1782+
},
1783+
actions: {
1784+
adjust: 'Adjust',
1785+
adjustDesc: 'Extend or shorten the subscription validity period',
1786+
resetQuota: 'Reset Quota',
1787+
resetQuotaDesc: 'Reset daily/weekly/monthly usage to zero',
1788+
revoke: 'Revoke',
1789+
revokeDesc: 'Immediately terminate the subscription (irreversible)'
1790+
},
1791+
tip: 'Tip: Only groups with billing type "Subscription" and status "Active" appear in the group dropdown. If no options are available, create one in Group Management first.'
1792+
}
17631793
},
17641794

17651795
// Accounts

frontend/src/i18n/locales/zh.ts

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1838,7 +1838,37 @@ export default {
18381838
pleaseSelectUser: '请选择用户',
18391839
pleaseSelectGroup: '请选择分组',
18401840
validityDaysRequired: '请输入有效的天数(至少1天)',
1841-
revokeConfirm: "确定要撤销 '{user}' 的订阅吗?此操作无法撤销。"
1841+
revokeConfirm: "确定要撤销 '{user}' 的订阅吗?此操作无法撤销。",
1842+
guide: {
1843+
title: '订阅管理教程',
1844+
subtitle: '订阅模式允许你按时间周期为用户分配使用额度,支持日/周/月配额限制。按照以下步骤即可完成配置。',
1845+
showGuide: '使用指南',
1846+
step1: {
1847+
title: '创建订阅分组',
1848+
line1: '前往「分组管理」页面,点击「创建分组」',
1849+
line2: '将计费类型设为「订阅」,配置日/周/月额度限制',
1850+
line3: '保存分组,确保状态为「正常」',
1851+
link: '前往分组管理'
1852+
},
1853+
step2: {
1854+
title: '分配订阅给用户',
1855+
line1: '点击本页右上角「分配订阅」按钮',
1856+
line2: '在弹窗中搜索用户邮箱并选择目标用户',
1857+
line3: '选择订阅分组、设置有效期天数,点击「分配」'
1858+
},
1859+
step3: {
1860+
title: '管理已有订阅'
1861+
},
1862+
actions: {
1863+
adjust: '调整',
1864+
adjustDesc: '延长或缩短订阅有效期',
1865+
resetQuota: '重置配额',
1866+
resetQuotaDesc: '将日/周/月用量归零,重新开始计算',
1867+
revoke: '撤销',
1868+
revokeDesc: '立即终止该用户的订阅,不可恢复'
1869+
},
1870+
tip: '提示:订阅分组下拉列表中只会显示计费类型为「订阅」且状态为「正常」的分组。如果没有可选项,请先到分组管理中创建。'
1871+
}
18421872
},
18431873

18441874
// Accounts Management

frontend/src/views/admin/SubscriptionsView.vue

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,13 @@
144144
</div>
145145
</div>
146146
</div>
147+
<button
148+
@click="showGuideModal = true"
149+
class="btn btn-secondary"
150+
:title="t('admin.subscriptions.guide.showGuide')"
151+
>
152+
<Icon name="questionCircle" size="md" />
153+
</button>
147154
<button @click="showAssignModal = true" class="btn btn-primary">
148155
<Icon name="plus" size="md" class="mr-2" />
149156
{{ t('admin.subscriptions.assignSubscription') }}
@@ -638,6 +645,85 @@
638645
@confirm="confirmResetQuota"
639646
@cancel="showResetQuotaConfirm = false"
640647
/>
648+
<!-- Subscription Guide Modal -->
649+
<teleport to="body">
650+
<transition name="modal">
651+
<div v-if="showGuideModal" class="fixed inset-0 z-50 flex items-center justify-center p-4" @mousedown.self="showGuideModal = false">
652+
<div class="fixed inset-0 bg-black/50" @click="showGuideModal = false"></div>
653+
<div class="relative max-h-[85vh] w-full max-w-2xl overflow-y-auto rounded-xl bg-white p-6 shadow-2xl dark:bg-dark-800">
654+
<button type="button" class="absolute right-4 top-4 text-gray-400 hover:text-gray-600 dark:hover:text-gray-200" @click="showGuideModal = false">
655+
<svg class="h-5 w-5" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2"><path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" /></svg>
656+
</button>
657+
658+
<h2 class="mb-4 text-lg font-bold text-gray-900 dark:text-white">{{ t('admin.subscriptions.guide.title') }}</h2>
659+
<p class="mb-5 text-sm text-gray-500 dark:text-gray-400">{{ t('admin.subscriptions.guide.subtitle') }}</p>
660+
661+
<!-- Step 1 -->
662+
<div class="mb-5">
663+
<h3 class="mb-2 flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-white">
664+
<span class="flex h-6 w-6 items-center justify-center rounded-full bg-primary-100 text-xs font-bold text-primary-700 dark:bg-primary-900/40 dark:text-primary-300">1</span>
665+
{{ t('admin.subscriptions.guide.step1.title') }}
666+
</h3>
667+
<ol class="ml-8 list-decimal space-y-1 text-sm text-gray-600 dark:text-gray-300">
668+
<li>{{ t('admin.subscriptions.guide.step1.line1') }}</li>
669+
<li>{{ t('admin.subscriptions.guide.step1.line2') }}</li>
670+
<li>{{ t('admin.subscriptions.guide.step1.line3') }}</li>
671+
</ol>
672+
<div class="ml-8 mt-2">
673+
<router-link
674+
to="/admin/groups"
675+
@click="showGuideModal = false"
676+
class="inline-flex items-center gap-1 text-sm font-medium text-primary-600 hover:text-primary-700 dark:text-primary-400 dark:hover:text-primary-300"
677+
>
678+
{{ t('admin.subscriptions.guide.step1.link') }}
679+
<Icon name="arrowRight" size="xs" />
680+
</router-link>
681+
</div>
682+
</div>
683+
684+
<!-- Step 2 -->
685+
<div class="mb-5">
686+
<h3 class="mb-2 flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-white">
687+
<span class="flex h-6 w-6 items-center justify-center rounded-full bg-primary-100 text-xs font-bold text-primary-700 dark:bg-primary-900/40 dark:text-primary-300">2</span>
688+
{{ t('admin.subscriptions.guide.step2.title') }}
689+
</h3>
690+
<ol class="ml-8 list-decimal space-y-1 text-sm text-gray-600 dark:text-gray-300">
691+
<li>{{ t('admin.subscriptions.guide.step2.line1') }}</li>
692+
<li>{{ t('admin.subscriptions.guide.step2.line2') }}</li>
693+
<li>{{ t('admin.subscriptions.guide.step2.line3') }}</li>
694+
</ol>
695+
</div>
696+
697+
<!-- Step 3 -->
698+
<div class="mb-5">
699+
<h3 class="mb-2 flex items-center gap-2 text-sm font-semibold text-gray-900 dark:text-white">
700+
<span class="flex h-6 w-6 items-center justify-center rounded-full bg-primary-100 text-xs font-bold text-primary-700 dark:bg-primary-900/40 dark:text-primary-300">3</span>
701+
{{ t('admin.subscriptions.guide.step3.title') }}
702+
</h3>
703+
<div class="ml-8 overflow-hidden rounded-lg border border-gray-200 dark:border-dark-600">
704+
<table class="w-full text-sm">
705+
<tbody>
706+
<tr v-for="(row, i) in guideActionRows" :key="i" class="border-b border-gray-100 dark:border-dark-700 last:border-0">
707+
<td class="whitespace-nowrap bg-gray-50 px-3 py-2 font-medium text-gray-700 dark:bg-dark-700 dark:text-gray-300">{{ row.action }}</td>
708+
<td class="px-3 py-2 text-gray-600 dark:text-gray-400">{{ row.desc }}</td>
709+
</tr>
710+
</tbody>
711+
</table>
712+
</div>
713+
</div>
714+
715+
<!-- Tip -->
716+
<div class="rounded-lg bg-blue-50 p-3 text-xs text-blue-700 dark:bg-blue-900/20 dark:text-blue-300">
717+
{{ t('admin.subscriptions.guide.tip') }}
718+
</div>
719+
720+
<div class="mt-4 text-right">
721+
<button type="button" class="btn btn-primary btn-sm" @click="showGuideModal = false">{{ t('common.close') }}</button>
722+
</div>
723+
</div>
724+
</div>
725+
</transition>
726+
</teleport>
641727
</AppLayout>
642728
</template>
643729

@@ -674,6 +760,15 @@ interface GroupOption {
674760
rate: number
675761
}
676762
763+
// Guide modal state
764+
const showGuideModal = ref(false)
765+
766+
const guideActionRows = computed(() => [
767+
{ action: t('admin.subscriptions.guide.actions.adjust'), desc: t('admin.subscriptions.guide.actions.adjustDesc') },
768+
{ action: t('admin.subscriptions.guide.actions.resetQuota'), desc: t('admin.subscriptions.guide.actions.resetQuotaDesc') },
769+
{ action: t('admin.subscriptions.guide.actions.revoke'), desc: t('admin.subscriptions.guide.actions.revokeDesc') }
770+
])
771+
677772
// User column display mode: 'email' or 'username'
678773
const userColumnMode = ref<'email' | 'username'>('email')
679774
const USER_COLUMN_MODE_KEY = 'subscription-user-column-mode'

0 commit comments

Comments
 (0)