Revert "Merge pull request #424 from Wangnov/feat/i18n"

This reverts commit 1d915d8327, reversing
changes made to 009f7c84f6.
This commit is contained in:
shaw
2025-09-12 09:21:53 +08:00
parent 1d915d8327
commit 9c4dc714f8
80 changed files with 7026 additions and 19087 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -11,9 +11,7 @@
>
<i class="fas fa-layer-group text-sm text-white sm:text-base" />
</div>
<h3 class="text-lg font-bold text-gray-900 sm:text-xl">
{{ t('groupManagement.title') }}
</h3>
<h3 class="text-lg font-bold text-gray-900 sm:text-xl">账户分组管理</h3>
</div>
<button
class="p-1 text-gray-400 transition-colors hover:text-gray-600"
@@ -27,32 +25,26 @@
<div class="mb-6">
<button class="btn btn-primary px-4 py-2" @click="showCreateForm = true">
<i class="fas fa-plus mr-2" />
{{ t('groupManagement.createNewGroup') }}
创建新分组
</button>
</div>
<!-- 创建分组表单 -->
<div v-if="showCreateForm" class="mb-6 rounded-lg border border-blue-200 bg-blue-50 p-4">
<h4 class="mb-4 text-lg font-semibold text-gray-900">
{{ t('groupManagement.createGroup') }}
</h4>
<h4 class="mb-4 text-lg font-semibold text-gray-900">创建新分组</h4>
<div class="space-y-4">
<div>
<label class="mb-2 block text-sm font-semibold text-gray-700">{{
t('groupManagement.groupNameRequired')
}}</label>
<label class="mb-2 block text-sm font-semibold text-gray-700">分组名称 *</label>
<input
v-model="createForm.name"
class="form-input w-full"
:placeholder="t('groupManagement.groupNamePlaceholder')"
placeholder="输入分组名称"
type="text"
/>
</div>
<div>
<label class="mb-2 block text-sm font-semibold text-gray-700">{{
t('groupManagement.platformTypeRequired')
}}</label>
<label class="mb-2 block text-sm font-semibold text-gray-700">平台类型 *</label>
<div class="flex gap-4">
<label class="flex cursor-pointer items-center">
<input v-model="createForm.platform" class="mr-2" type="radio" value="claude" />
@@ -70,13 +62,11 @@
</div>
<div>
<label class="mb-2 block text-sm font-semibold text-gray-700">{{
t('groupManagement.descriptionOptional')
}}</label>
<label class="mb-2 block text-sm font-semibold text-gray-700">描述 (可选)</label>
<textarea
v-model="createForm.description"
class="form-input w-full resize-none"
:placeholder="t('groupManagement.descriptionPlaceholder')"
placeholder="分组描述..."
rows="2"
/>
</div>
@@ -88,11 +78,9 @@
@click="createGroup"
>
<div v-if="creating" class="loading-spinner mr-2" />
{{ creating ? t('groupManagement.creating') : t('groupManagement.create') }}
</button>
<button class="btn btn-secondary px-4 py-2" @click="cancelCreate">
{{ t('groupManagement.cancel') }}
{{ creating ? '创建中...' : '创建' }}
</button>
<button class="btn btn-secondary px-4 py-2" @click="cancelCreate">取消</button>
</div>
</div>
</div>
@@ -101,12 +89,12 @@
<div class="space-y-4">
<div v-if="loading" class="py-8 text-center">
<div class="loading-spinner-lg mx-auto mb-4" />
<p class="text-gray-500">{{ t('groupManagement.loading') }}</p>
<p class="text-gray-500">加载中...</p>
</div>
<div v-else-if="groups.length === 0" class="rounded-lg bg-gray-50 py-8 text-center">
<i class="fas fa-layer-group mb-4 text-4xl text-gray-300" />
<p class="text-gray-500">{{ t('groupManagement.noGroups') }}</p>
<p class="text-gray-500">暂无分组</p>
</div>
<div v-else class="grid grid-cols-1 gap-4 md:grid-cols-2">
@@ -121,7 +109,7 @@
{{ group.name }}
</h4>
<p class="mt-1 text-sm text-gray-500">
{{ group.description || t('groupManagement.noDescription') }}
{{ group.description || '暂无描述' }}
</p>
</div>
<div class="ml-4 flex items-center gap-2">
@@ -150,7 +138,7 @@
<div class="flex items-center gap-4">
<span>
<i class="fas fa-users mr-1" />
{{ group.memberCount || 0 }}{{ t('groupManagement.membersCount') }}
{{ group.memberCount || 0 }} 个成员
</span>
<span>
<i class="fas fa-clock mr-1" />
@@ -160,7 +148,7 @@
<div class="flex items-center gap-2">
<button
class="text-blue-600 transition-colors hover:text-blue-800"
:title="t('groupManagement.edit')"
title="编辑"
@click="editGroup(group)"
>
<i class="fas fa-edit" />
@@ -168,7 +156,7 @@
<button
class="text-red-600 transition-colors hover:text-red-800"
:disabled="group.memberCount > 0"
:title="t('groupManagement.delete')"
title="删除"
@click="deleteGroup(group)"
>
<i class="fas fa-trash" />
@@ -188,7 +176,7 @@
>
<div class="modal-content w-full max-w-lg p-4 sm:p-6">
<div class="mb-4 flex items-center justify-between">
<h3 class="text-lg font-bold text-gray-900">{{ t('groupManagement.editGroup') }}</h3>
<h3 class="text-lg font-bold text-gray-900">编辑分组</h3>
<button class="text-gray-400 transition-colors hover:text-gray-600" @click="cancelEdit">
<i class="fas fa-times" />
</button>
@@ -196,21 +184,17 @@
<div class="space-y-4">
<div>
<label class="mb-2 block text-sm font-semibold text-gray-700">{{
t('groupManagement.groupNameRequired')
}}</label>
<label class="mb-2 block text-sm font-semibold text-gray-700">分组名称 *</label>
<input
v-model="editForm.name"
class="form-input w-full"
:placeholder="t('groupManagement.groupNamePlaceholder')"
placeholder="输入分组名称"
type="text"
/>
</div>
<div>
<label class="mb-2 block text-sm font-semibold text-gray-700">{{
t('groupManagement.platformTypeLabel')
}}</label>
<label class="mb-2 block text-sm font-semibold text-gray-700">平台类型</label>
<div class="rounded-lg bg-gray-100 px-3 py-2 text-sm text-gray-600">
{{
editForm.platform === 'claude'
@@ -219,20 +203,16 @@
? 'Gemini'
: 'OpenAI'
}}
<span class="ml-2 text-xs text-gray-500">{{
t('groupManagement.cannotModify')
}}</span>
<span class="ml-2 text-xs text-gray-500">(不可修改)</span>
</div>
</div>
<div>
<label class="mb-2 block text-sm font-semibold text-gray-700">{{
t('groupManagement.descriptionOptional')
}}</label>
<label class="mb-2 block text-sm font-semibold text-gray-700">描述 (可选)</label>
<textarea
v-model="editForm.description"
class="form-input w-full resize-none"
:placeholder="t('groupManagement.descriptionPlaceholder')"
placeholder="分组描述..."
rows="2"
/>
</div>
@@ -244,11 +224,9 @@
@click="updateGroup"
>
<div v-if="updating" class="loading-spinner mr-2" />
{{ updating ? t('groupManagement.updating') : t('groupManagement.update') }}
</button>
<button class="btn btn-secondary flex-1 px-4 py-2" @click="cancelEdit">
{{ t('groupManagement.cancel') }}
{{ updating ? '更新中...' : '更新' }}
</button>
<button class="btn btn-secondary flex-1 px-4 py-2" @click="cancelEdit">取消</button>
</div>
</div>
</div>
@@ -258,12 +236,9 @@
<script setup>
import { ref, onMounted } from 'vue'
import { useI18n } from 'vue-i18n'
import { showToast } from '@/utils/toast'
import { apiClient } from '@/config/api'
const { t } = useI18n()
const emit = defineEmits(['close', 'refresh'])
const show = ref(true)
@@ -303,7 +278,7 @@ const loadGroups = async () => {
const response = await apiClient.get('/admin/account-groups')
groups.value = response.data || []
} catch (error) {
showToast(t('groupManagement.loadGroupsFailed'), 'error')
showToast('加载分组列表失败', 'error')
} finally {
loading.value = false
}
@@ -312,7 +287,7 @@ const loadGroups = async () => {
// 创建分组
const createGroup = async () => {
if (!createForm.value.name || !createForm.value.platform) {
showToast(t('groupManagement.fillRequiredFields'), 'error')
showToast('请填写必填项', 'error')
return
}
@@ -324,12 +299,12 @@ const createGroup = async () => {
description: createForm.value.description
})
showToast(t('groupManagement.groupCreated'), 'success')
showToast('分组创建成功', 'success')
cancelCreate()
await loadGroups()
emit('refresh')
} catch (error) {
showToast(error.response?.data?.error || t('groupManagement.createGroupFailed'), 'error')
showToast(error.response?.data?.error || '创建分组失败', 'error')
} finally {
creating.value = false
}
@@ -359,7 +334,7 @@ const editGroup = (group) => {
// 更新分组
const updateGroup = async () => {
if (!editForm.value.name) {
showToast(t('groupManagement.fillGroupName'), 'error')
showToast('请填写分组名称', 'error')
return
}
@@ -370,12 +345,12 @@ const updateGroup = async () => {
description: editForm.value.description
})
showToast(t('groupManagement.groupUpdated'), 'success')
showToast('分组更新成功', 'success')
cancelEdit()
await loadGroups()
emit('refresh')
} catch (error) {
showToast(error.response?.data?.error || t('groupManagement.updateGroupFailed'), 'error')
showToast(error.response?.data?.error || '更新分组失败', 'error')
} finally {
updating.value = false
}
@@ -395,21 +370,21 @@ const cancelEdit = () => {
// 删除分组
const deleteGroup = async (group) => {
if (group.memberCount > 0) {
showToast(t('groupManagement.groupHasMembers'), 'error')
showToast('分组内还有成员,无法删除', 'error')
return
}
if (!confirm(t('groupManagement.confirmDelete', { name: group.name }))) {
if (!confirm(`确定要删除分组 "${group.name}" 吗?`)) {
return
}
try {
await apiClient.delete(`/admin/account-groups/${group.id}`)
showToast(t('groupManagement.groupDeleted'), 'success')
showToast('分组删除成功', 'success')
await loadGroups()
emit('refresh')
} catch (error) {
showToast(error.response?.data?.error || t('groupManagement.deleteGroupFailed'), 'error')
showToast(error.response?.data?.error || '删除分组失败', 'error')
}
}

View File

@@ -12,11 +12,9 @@
<i class="fas fa-link text-white" />
</div>
<div class="flex-1">
<h4 class="mb-3 font-semibold text-blue-900 dark:text-blue-200">
{{ t('oauthFlow.claudeAccountAuth') }}
</h4>
<h4 class="mb-3 font-semibold text-blue-900 dark:text-blue-200">Claude 账户授权</h4>
<p class="mb-4 text-sm text-blue-800 dark:text-blue-300">
{{ t('oauthFlow.claudeAuthDescription') }}
请按照以下步骤完成 Claude 账户的授权
</p>
<div class="space-y-4">
@@ -32,7 +30,7 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-blue-900 dark:text-blue-200">
{{ t('oauthFlow.step1Title') }}
点击下方按钮生成授权链接
</p>
<button
v-if="!authUrl"
@@ -42,7 +40,7 @@
>
<i v-if="!loading" class="fas fa-link mr-2" />
<div v-else class="loading-spinner mr-2" />
{{ loading ? t('oauthFlow.generating') : t('oauthFlow.generateAuthLink') }}
{{ loading ? '生成中...' : '生成授权链接' }}
</button>
<div v-else class="space-y-3">
<div class="flex items-center gap-2">
@@ -54,7 +52,7 @@
/>
<button
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600"
:title="t('oauthFlow.copyLinkTooltip')"
title="复制链接"
@click="copyAuthUrl"
>
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
@@ -64,7 +62,7 @@
class="text-xs text-blue-600 hover:text-blue-700"
@click="regenerateAuthUrl"
>
<i class="fas fa-sync-alt mr-1" />{{ t('oauthFlow.regenerate') }}
<i class="fas fa-sync-alt mr-1" />重新生成
</button>
</div>
</div>
@@ -83,18 +81,18 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-blue-900 dark:text-blue-200">
{{ t('oauthFlow.step2Title') }}
在浏览器中打开链接并完成授权
</p>
<p class="mb-2 text-sm text-blue-700 dark:text-blue-300">
{{ t('oauthFlow.step2Description') }}
请在新标签页中打开授权链接登录您的 Claude 账户并授权
</p>
<div
class="rounded border border-yellow-300 bg-yellow-50 p-3 dark:border-yellow-700 dark:bg-yellow-900/30"
>
<p class="text-xs text-yellow-800 dark:text-yellow-300">
<i class="fas fa-exclamation-triangle mr-1" />
<strong>{{ t('oauthFlow.proxyNotice') }}</strong
>{{ t('oauthFlow.proxyNoticeText') }}
<strong>注意</strong
>如果您设置了代理请确保浏览器也使用相同的代理访问授权页面
</p>
</div>
</div>
@@ -113,32 +111,29 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-blue-900 dark:text-blue-200">
{{ t('oauthFlow.step3Title') }}
输入 Authorization Code
</p>
<p class="mb-3 text-sm text-blue-700 dark:text-blue-300">
{{ t('oauthFlow.step3Description') }}
<strong>{{ t('oauthFlow.authorizationCode') }}</strong
>{{ t('oauthFlow.step3DescriptionMiddle') }}
授权完成后页面会显示一个
<strong>Authorization Code</strong>请将其复制并粘贴到下方输入框
</p>
<div class="space-y-3">
<div>
<label
class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
<i class="fas fa-key mr-2 text-blue-500" />{{
t('oauthFlow.authorizationCode')
}}
<i class="fas fa-key mr-2 text-blue-500" />Authorization Code
</label>
<textarea
v-model="authCode"
class="form-input w-full resize-none font-mono text-sm"
:placeholder="t('oauthFlow.authCodePlaceholder')"
placeholder="粘贴从Claude页面获取的Authorization Code..."
rows="3"
/>
</div>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
<i class="fas fa-info-circle mr-1" />
{{ t('oauthFlow.authCodeHint') }}
请粘贴从Claude页面复制的Authorization Code
</p>
</div>
</div>
@@ -162,11 +157,9 @@
<i class="fas fa-robot text-white" />
</div>
<div class="flex-1">
<h4 class="mb-3 font-semibold text-green-900 dark:text-green-200">
{{ t('oauthFlow.geminiAccountAuth') }}
</h4>
<h4 class="mb-3 font-semibold text-green-900 dark:text-green-200">Gemini 账户授权</h4>
<p class="mb-4 text-sm text-green-800 dark:text-green-300">
{{ t('oauthFlow.geminiAuthDescription') }}
请按照以下步骤完成 Gemini 账户的授权
</p>
<div class="space-y-4">
@@ -182,7 +175,7 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-green-900 dark:text-green-200">
{{ t('oauthFlow.step1Title') }}
点击下方按钮生成授权链接
</p>
<button
v-if="!authUrl"
@@ -192,7 +185,7 @@
>
<i v-if="!loading" class="fas fa-link mr-2" />
<div v-else class="loading-spinner mr-2" />
{{ loading ? t('oauthFlow.generating') : t('oauthFlow.generateAuthLink') }}
{{ loading ? '生成中...' : '生成授权链接' }}
</button>
<div v-else class="space-y-3">
<div class="flex items-center gap-2">
@@ -204,7 +197,7 @@
/>
<button
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600"
:title="t('oauthFlow.copyLinkTooltip')"
title="复制链接"
@click="copyAuthUrl"
>
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
@@ -214,7 +207,7 @@
class="text-xs text-green-600 hover:text-green-700"
@click="regenerateAuthUrl"
>
<i class="fas fa-sync-alt mr-1" />{{ t('oauthFlow.regenerate') }}
<i class="fas fa-sync-alt mr-1" />重新生成
</button>
</div>
</div>
@@ -233,18 +226,18 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-green-900 dark:text-green-200">
{{ t('oauthFlow.step2Title') }}
在浏览器中打开链接并完成授权
</p>
<p class="mb-2 text-sm text-green-700 dark:text-green-300">
{{ t('oauthFlow.step2DescriptionGemini') }}
请在新标签页中打开授权链接登录您的 Gemini 账户并授权
</p>
<div
class="rounded border border-yellow-300 bg-yellow-50 p-3 dark:border-yellow-700 dark:bg-yellow-900/30"
>
<p class="text-xs text-yellow-800 dark:text-yellow-300">
<i class="fas fa-exclamation-triangle mr-1" />
<strong>{{ t('oauthFlow.proxyNotice') }}</strong
>{{ t('oauthFlow.proxyNoticeText') }}
<strong>注意</strong
>如果您设置了代理请确保浏览器也使用相同的代理访问授权页面
</p>
</div>
</div>
@@ -263,31 +256,29 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-green-900 dark:text-green-200">
{{ t('oauthFlow.step3Title') }}
输入 Authorization Code
</p>
<p class="mb-3 text-sm text-green-700 dark:text-green-300">
{{ t('oauthFlow.step3DescriptionGemini') }}
授权完成后页面会显示一个 Authorization Code请将其复制并粘贴到下方输入框
</p>
<div class="space-y-3">
<div>
<label
class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
<i class="fas fa-key mr-2 text-green-500" />{{
t('oauthFlow.authorizationCode')
}}
<i class="fas fa-key mr-2 text-green-500" />Authorization Code
</label>
<textarea
v-model="authCode"
class="form-input w-full resize-none font-mono text-sm"
:placeholder="t('oauthFlow.authCodePlaceholderGemini')"
placeholder="粘贴从Gemini页面获取的Authorization Code..."
rows="3"
/>
</div>
<div class="mt-2 space-y-1">
<p class="text-xs text-gray-600 dark:text-gray-400">
<i class="fas fa-check-circle mr-1 text-green-500" />
{{ t('oauthFlow.authCodeHintGemini') }}
请粘贴从Gemini页面复制的Authorization Code
</p>
</div>
</div>
@@ -312,11 +303,9 @@
<i class="fas fa-brain text-white" />
</div>
<div class="flex-1">
<h4 class="mb-3 font-semibold text-orange-900 dark:text-orange-200">
{{ t('oauthFlow.openaiAccountAuth') }}
</h4>
<h4 class="mb-3 font-semibold text-orange-900 dark:text-orange-200">OpenAI 账户授权</h4>
<p class="mb-4 text-sm text-orange-800 dark:text-orange-300">
{{ t('oauthFlow.openaiAuthDescription') }}
请按照以下步骤完成 OpenAI 账户的授权
</p>
<div class="space-y-4">
@@ -332,7 +321,7 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-orange-900 dark:text-orange-200">
{{ t('oauthFlow.step1Title') }}
点击下方按钮生成授权链接
</p>
<button
v-if="!authUrl"
@@ -342,7 +331,7 @@
>
<i v-if="!loading" class="fas fa-link mr-2" />
<div v-else class="loading-spinner mr-2" />
{{ loading ? t('oauthFlow.generating') : t('oauthFlow.generateAuthLink') }}
{{ loading ? '生成中...' : '生成授权链接' }}
</button>
<div v-else class="space-y-3">
<div class="flex items-center gap-2">
@@ -354,7 +343,7 @@
/>
<button
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600"
:title="t('oauthFlow.copyLinkTooltip')"
title="复制链接"
@click="copyAuthUrl"
>
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
@@ -364,7 +353,7 @@
class="text-xs text-orange-600 hover:text-orange-700"
@click="regenerateAuthUrl"
>
<i class="fas fa-sync-alt mr-1" />{{ t('oauthFlow.regenerate') }}
<i class="fas fa-sync-alt mr-1" />重新生成
</button>
</div>
</div>
@@ -383,23 +372,22 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-orange-900 dark:text-orange-200">
{{ t('oauthFlow.step2Title') }}
在浏览器中打开链接并完成授权
</p>
<p class="mb-2 text-sm text-orange-700 dark:text-orange-300">
{{ t('oauthFlow.step2DescriptionOpenAI') }}
请在新标签页中打开授权链接登录您的 OpenAI 账户并授权
</p>
<div
class="mb-3 rounded border border-amber-300 bg-amber-50 p-3 dark:border-amber-700 dark:bg-amber-900/30"
>
<p class="text-xs text-amber-800 dark:text-amber-300">
<i class="fas fa-clock mr-1" />
<strong>{{ t('oauthFlow.openaiImportantNote') }}</strong
>{{ t('oauthFlow.openaiLoadingNote') }}
<strong>重要提示</strong>授权后页面可能会加载较长时间请耐心等待
</p>
<p class="mt-2 text-xs text-amber-700 dark:text-amber-400">
{{ t('oauthFlow.openaiAddressNote') }}
当浏览器地址栏变为
<strong class="font-mono">http://localhost:1455/...</strong>
{{ t('oauthFlow.openaiAddressNoteMiddle') }}
开头时表示授权已完成
</p>
</div>
<div
@@ -407,8 +395,8 @@
>
<p class="text-xs text-yellow-800 dark:text-yellow-300">
<i class="fas fa-exclamation-triangle mr-1" />
<strong>{{ t('oauthFlow.proxyNotice') }}</strong
>{{ t('oauthFlow.proxyNoticeText') }}
<strong>注意</strong
>如果您设置了代理请确保浏览器也使用相同的代理访问授权页面
</p>
</div>
</div>
@@ -427,26 +415,23 @@
</div>
<div class="flex-1">
<p class="mb-2 font-medium text-orange-900 dark:text-orange-200">
{{ t('oauthFlow.step3TitleOpenAI') }}
输入授权链接或 Code
</p>
<p class="mb-3 text-sm text-orange-700 dark:text-orange-300">
{{ t('oauthFlow.step3DescriptionOpenAI') }}
<strong class="font-mono">http://localhost:1455/...</strong>
{{ t('oauthFlow.step3DescriptionOpenAIMiddle') }}
授权完成后当页面地址变为
<strong class="font-mono">http://localhost:1455/...</strong> 时:
</p>
<div class="space-y-3">
<div>
<label
class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>
<i class="fas fa-link mr-2 text-orange-500" />{{
t('oauthFlow.authLinkOrCode')
}}
<i class="fas fa-link mr-2 text-orange-500" />授权链接或 Code
</label>
<textarea
v-model="authCode"
class="form-input w-full resize-none font-mono text-sm"
:placeholder="t('oauthFlow.authCodePlaceholderOpenAI')"
placeholder="方式1复制完整的链接http://localhost:1455/auth/callback?code=...&#10;方式2仅复制 code 参数的值&#10;系统会自动识别并提取所需信息"
rows="3"
/>
</div>
@@ -455,18 +440,18 @@
>
<p class="text-xs text-blue-700 dark:text-blue-300">
<i class="fas fa-lightbulb mr-1" />
<strong>{{ t('oauthFlow.openaiTip') }}</strong
>{{ t('oauthFlow.openaiTipText') }}
<strong>提示</strong>您可以直接复制整个链接或仅复制 code
参数值系统会自动识别
</p>
<p class="mt-1 text-xs text-blue-600 dark:text-blue-400">
{{ t('oauthFlow.openaiLinkExample')
}}<span class="font-mono"
完整链接示例<span class="font-mono"
>http://localhost:1455/auth/callback?code=ac_4hm8...</span
>
</p>
<p class="text-xs text-blue-600">
{{ t('oauthFlow.openaiCodeExample')
}}<span class="font-mono">ac_4hm8iqmx9A2fzMy_cwye7U3W7...</span>
Code 示例<span class="font-mono"
>ac_4hm8iqmx9A2fzMy_cwye7U3W7...</span
>
</p>
</div>
</div>
@@ -485,7 +470,7 @@
type="button"
@click="$emit('back')"
>
{{ t('oauthFlow.previousStep') }}
上一步
</button>
<button
class="btn btn-primary flex-1 px-6 py-3 font-semibold"
@@ -494,7 +479,7 @@
@click="exchangeCode"
>
<div v-if="exchanging" class="loading-spinner mr-2" />
{{ exchanging ? t('oauthFlow.verifying') : t('oauthFlow.completeAuth') }}
{{ exchanging ? '验证中...' : '完成授权' }}
</button>
</div>
</div>
@@ -502,12 +487,9 @@
<script setup>
import { ref, computed, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { showToast } from '@/utils/toast'
import { useAccountsStore } from '@/stores/accounts'
const { t } = useI18n()
const props = defineProps({
platform: {
type: String,
@@ -562,16 +544,16 @@ watch(authCode, (newValue) => {
if (code) {
// 成功提取授权码
authCode.value = code
showToast(t('oauthFlow.successExtractCode'), 'success')
showToast('成功提取授权码!', 'success')
console.log('Successfully extracted authorization code from URL')
} else {
// URL 中没有 code 参数
showToast(t('oauthFlow.errorCodeNotFound'), 'error')
showToast('URL 中未找到授权码参数,请检查链接是否正确', 'error')
}
} catch (error) {
// URL 解析失败
console.error('Failed to parse URL:', error)
showToast(t('oauthFlow.errorLinkFormat'), 'error')
showToast('链接格式错误,请检查是否为完整的 URL', 'error')
}
} else if (props.platform === 'gemini' || props.platform === 'openai') {
// Gemini 和 OpenAI 平台可能使用不同的回调URL
@@ -582,14 +564,14 @@ watch(authCode, (newValue) => {
if (code) {
authCode.value = code
showToast(t('oauthFlow.successExtractCode'), 'success')
showToast('成功提取授权码!', 'success')
}
} catch (error) {
// 不是有效的URL保持原值
}
} else {
// 错误的 URL不是正确的 localhost 回调地址)
showToast(t('oauthFlow.errorWrongUrlFormat'), 'error')
showToast('请粘贴以 http://localhost:1455 或 http://localhost:45462 开头的链接', 'error')
}
}
// 如果不是 URL保持原值兼容直接输入授权码
@@ -625,7 +607,7 @@ const generateAuthUrl = async () => {
sessionId.value = result.sessionId
}
} catch (error) {
showToast(error.message || t('oauthFlow.generateAuthFailed'), 'error')
showToast(error.message || '生成授权链接失败', 'error')
} finally {
loading.value = false
}
@@ -643,7 +625,7 @@ const copyAuthUrl = async () => {
try {
await navigator.clipboard.writeText(authUrl.value)
copied.value = true
showToast(t('oauthFlow.linkCopied'), 'success')
showToast('链接已复制', 'success')
setTimeout(() => {
copied.value = false
}, 2000)
@@ -656,7 +638,7 @@ const copyAuthUrl = async () => {
document.execCommand('copy')
document.body.removeChild(input)
copied.value = true
showToast(t('oauthFlow.linkCopied'), 'success')
showToast('链接已复制', 'success')
setTimeout(() => {
copied.value = false
}, 2000)
@@ -713,7 +695,7 @@ const exchangeCode = async () => {
emit('success', tokenInfo)
} catch (error) {
showToast(error.message || t('oauthFlow.authFailed'), 'error')
showToast(error.message || '授权失败,请检查授权码是否正确', 'error')
} finally {
exchanging.value = false
}

View File

@@ -1,18 +1,14 @@
<template>
<div class="space-y-4">
<div class="flex items-center justify-between">
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300">
{{ t('proxyConfig.title') }}
</h4>
<h4 class="text-sm font-semibold text-gray-700 dark:text-gray-300">代理设置 (可选)</h4>
<label class="flex cursor-pointer items-center">
<input
v-model="proxy.enabled"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500"
type="checkbox"
/>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">{{
t('proxyConfig.enableProxy')
}}</span>
<span class="ml-2 text-sm text-gray-700 dark:text-gray-300">启用代理</span>
</label>
</div>
@@ -26,10 +22,10 @@
</div>
<div class="flex-1">
<p class="text-sm text-gray-700 dark:text-gray-300">
{{ t('proxyConfig.configDescription') }}
配置代理以访问受限的网络资源支持 SOCKS5 HTTP 代理
</p>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400">
{{ t('proxyConfig.stabilityNotice') }}
请确保代理服务器稳定可用否则会影响账户的正常使用
</p>
</div>
</div>
@@ -74,9 +70,9 @@
<div class="my-3 border-t border-gray-200 dark:border-gray-600"></div>
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{
t('proxyConfig.proxyType')
}}</label>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>代理类型</label
>
<select
v-model="proxy.type"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200"
@@ -89,24 +85,24 @@
<div class="grid grid-cols-2 gap-4">
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{
t('proxyConfig.hostAddress')
}}</label>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>主机地址</label
>
<input
v-model="proxy.host"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
:placeholder="t('proxyConfig.hostPlaceholder')"
placeholder="例如: 192.168.1.100"
type="text"
/>
</div>
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{
t('proxyConfig.port')
}}</label>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>端口</label
>
<input
v-model="proxy.port"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
:placeholder="t('proxyConfig.portPlaceholder')"
placeholder="例如: 1080"
type="number"
/>
</div>
@@ -124,31 +120,31 @@
class="ml-2 cursor-pointer text-sm text-gray-700 dark:text-gray-300"
for="proxyAuth"
>
{{ t('proxyConfig.needsAuth') }}
需要身份验证
</label>
</div>
<div v-if="showAuth" class="grid grid-cols-2 gap-4">
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{
t('proxyConfig.username')
}}</label>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>用户名</label
>
<input
v-model="proxy.username"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
:placeholder="t('proxyConfig.usernamePlaceholder')"
placeholder="代理用户名"
type="text"
/>
</div>
<div>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300">{{
t('proxyConfig.password')
}}</label>
<label class="mb-2 block text-sm font-medium text-gray-700 dark:text-gray-300"
>密码</label
>
<div class="relative">
<input
v-model="proxy.password"
class="form-input w-full border-gray-300 pr-10 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
:placeholder="t('proxyConfig.passwordPlaceholder')"
placeholder="代理密码"
:type="showPassword ? 'text' : 'password'"
/>
<button
@@ -168,8 +164,8 @@
>
<p class="text-xs text-blue-700 dark:text-blue-300">
<i class="fas fa-info-circle mr-1" />
<strong>{{ t('proxyConfig.tip') }}</strong
>{{ t('proxyConfig.apiRequestNotice') }}
<strong>提示</strong
>代理设置将用于所有与此账户相关的API请求请确保代理服务器支持HTTPS流量转发
</p>
</div>
</div>
@@ -178,9 +174,6 @@
<script setup>
import { ref, watch, onUnmounted } from 'vue'
import { useI18n } from 'vue-i18n'
const { t } = useI18n()
const props = defineProps({
modelValue: {