feat(frontend): 实现新手引导功能

- 添加 Guide 组件和引导步骤配置
- 实现 useOnboardingTour 和 useTourStepDescription composables
- 添加 onboarding store 管理引导状态
- 更新多个视图和组件以支持引导功能
- 添加国际化支持(中英文)
- 删除旧的实现指南文档
This commit is contained in:
IanShaw027
2025-12-29 15:21:05 +08:00
parent c01db6b180
commit dd247e55e9
30 changed files with 3968 additions and 36 deletions

View File

@@ -38,7 +38,7 @@
/>
</svg>
</button>
<button @click="showCreateModal = true" class="btn btn-primary">
<button @click="showCreateModal = true" class="btn btn-primary" data-tour="accounts-create-btn">
<svg
class="mr-2 h-5 w-5"
fill="none"
@@ -373,7 +373,7 @@
:proxies="proxies"
:groups="groups"
@close="showCreateModal = false"
@created="loadAccounts"
@created="() => { loadAccounts(); if (onboardingStore.isCurrentStep(`[data-tour='account-form-submit']`)) onboardingStore.nextStep(500) }"
/>
<!-- Edit Account Modal -->
@@ -495,6 +495,7 @@ import { ref, reactive, computed, onMounted, onUnmounted, type ComponentPublicIn
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { useAuthStore } from '@/stores/auth'
import { useOnboardingStore } from '@/stores/onboarding'
import { adminAPI } from '@/api/admin'
import type { Account, Proxy, Group } from '@/types'
import type { Column } from '@/components/common/types'
@@ -524,6 +525,7 @@ import { formatRelativeTime } from '@/utils/format'
const { t } = useI18n()
const appStore = useAppStore()
const authStore = useAuthStore()
const onboardingStore = useOnboardingStore()
// Table columns
const columns = computed<Column[]>(() => {

View File

@@ -23,7 +23,11 @@
/>
</svg>
</button>
<button @click="showCreateModal = true" class="btn btn-primary">
<button
@click="showCreateModal = true"
class="btn btn-primary"
data-tour="groups-create-btn"
>
<svg
class="mr-2 h-5 w-5"
fill="none"
@@ -244,6 +248,7 @@
required
class="input"
:placeholder="t('admin.groups.enterGroupName')"
data-tour="group-form-name"
/>
</div>
<div>
@@ -257,7 +262,11 @@
</div>
<div>
<label class="input-label">{{ t('admin.groups.form.platform') }}</label>
<Select v-model="createForm.platform" :options="platformOptions" />
<Select
v-model="createForm.platform"
:options="platformOptions"
data-tour="group-form-platform"
/>
<p class="input-hint">{{ t('admin.groups.platformHint') }}</p>
</div>
<div v-if="createForm.subscription_type !== 'subscription'">
@@ -269,10 +278,11 @@
min="0.001"
required
class="input"
data-tour="group-form-multiplier"
/>
<p class="input-hint">{{ t('admin.groups.rateMultiplierHint') }}</p>
</div>
<div v-if="createForm.subscription_type !== 'subscription'">
<div v-if="createForm.subscription_type !== 'subscription'" data-tour="group-form-exclusive">
<div class="mb-1.5 flex items-center gap-1">
<label class="text-sm font-medium text-gray-700 dark:text-gray-300">
{{ t('admin.groups.form.exclusive') }}
@@ -390,6 +400,7 @@
form="create-group-form"
:disabled="submitting"
class="btn btn-primary"
data-tour="group-form-submit"
>
<svg
v-if="submitting"
@@ -432,7 +443,13 @@
>
<div>
<label class="input-label">{{ t('admin.groups.form.name') }}</label>
<input v-model="editForm.name" type="text" required class="input" />
<input
v-model="editForm.name"
type="text"
required
class="input"
data-tour="edit-group-form-name"
/>
</div>
<div>
<label class="input-label">{{ t('admin.groups.form.description') }}</label>
@@ -440,7 +457,12 @@
</div>
<div>
<label class="input-label">{{ t('admin.groups.form.platform') }}</label>
<Select v-model="editForm.platform" :options="platformOptions" :disabled="true" />
<Select
v-model="editForm.platform"
:options="platformOptions"
:disabled="true"
data-tour="group-form-platform"
/>
<p class="input-hint">{{ t('admin.groups.platformNotEditable') }}</p>
</div>
<div v-if="editForm.subscription_type !== 'subscription'">
@@ -452,6 +474,7 @@
min="0.001"
required
class="input"
data-tour="group-form-multiplier"
/>
</div>
<div v-if="editForm.subscription_type !== 'subscription'">
@@ -580,6 +603,7 @@
form="edit-group-form"
:disabled="submitting"
class="btn btn-primary"
data-tour="group-form-submit"
>
<svg
v-if="submitting"
@@ -625,6 +649,7 @@
import { ref, reactive, computed, onMounted, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { useAppStore } from '@/stores/app'
import { useOnboardingStore } from '@/stores/onboarding'
import { adminAPI } from '@/api/admin'
import type { Group, GroupPlatform, SubscriptionType } from '@/types'
import type { Column } from '@/components/common/types'
@@ -640,6 +665,7 @@ import PlatformIcon from '@/components/common/PlatformIcon.vue'
const { t } = useI18n()
const appStore = useAppStore()
const onboardingStore = useOnboardingStore()
const columns = computed<Column[]>(() => [
{ key: 'name', label: t('admin.groups.columns.name'), sortable: true },
@@ -809,9 +835,14 @@ const handleCreateGroup = async () => {
appStore.showSuccess(t('admin.groups.groupCreated'))
closeCreateModal()
loadGroups()
// Only advance tour if active, on submit step, and creation succeeded
if (onboardingStore.isCurrentStep('[data-tour="group-form-submit"]')) {
onboardingStore.nextStep(500)
}
} catch (error: any) {
appStore.showError(error.response?.data?.detail || t('admin.groups.failedToCreate'))
console.error('Error creating group:', error)
// Don't advance tour on error
} finally {
submitting.value = false
}