mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-24 09:41:17 +00:00
refactor: standardize code formatting and linting configuration
- Replace .eslintrc.js with .eslintrc.cjs for better ES module compatibility - Add .prettierrc configuration for consistent code formatting - Update package.json with new lint and format scripts - Add nodemon.json for development hot reloading configuration - Standardize code formatting across all JavaScript and Vue files - Update web admin SPA with improved linting rules and formatting - Add prettier configuration to web admin SPA 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,158 +1,118 @@
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<div
|
||||
v-if="show"
|
||||
class="fixed inset-0 modal z-50 flex items-center justify-center p-3 sm:p-4"
|
||||
>
|
||||
<div class="modal-content w-full max-w-4xl p-4 sm:p-6 md:p-8 mx-auto max-h-[90vh] overflow-y-auto custom-scrollbar">
|
||||
<div class="flex items-center justify-between mb-4 sm:mb-6">
|
||||
<div v-if="show" class="modal fixed inset-0 z-50 flex items-center justify-center p-3 sm:p-4">
|
||||
<div
|
||||
class="modal-content custom-scrollbar mx-auto max-h-[90vh] w-full max-w-4xl overflow-y-auto p-4 sm:p-6 md:p-8"
|
||||
>
|
||||
<div class="mb-4 flex items-center justify-between sm:mb-6">
|
||||
<div class="flex items-center gap-2 sm:gap-3">
|
||||
<div class="w-8 h-8 sm:w-10 sm:h-10 bg-gradient-to-br from-purple-500 to-purple-600 rounded-lg sm:rounded-xl flex items-center justify-center">
|
||||
<i class="fas fa-layer-group text-white text-sm sm:text-base" />
|
||||
<div
|
||||
class="flex h-8 w-8 items-center justify-center rounded-lg bg-gradient-to-br from-purple-500 to-purple-600 sm:h-10 sm:w-10 sm:rounded-xl"
|
||||
>
|
||||
<i class="fas fa-layer-group text-sm text-white sm:text-base" />
|
||||
</div>
|
||||
<h3 class="text-lg sm:text-xl font-bold text-gray-900">
|
||||
账户分组管理
|
||||
</h3>
|
||||
<h3 class="text-lg font-bold text-gray-900 sm:text-xl">账户分组管理</h3>
|
||||
</div>
|
||||
<button
|
||||
class="text-gray-400 hover:text-gray-600 transition-colors p-1"
|
||||
<button
|
||||
class="p-1 text-gray-400 transition-colors hover:text-gray-600"
|
||||
@click="$emit('close')"
|
||||
>
|
||||
<i class="fas fa-times text-lg sm:text-xl" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 添加分组按钮 -->
|
||||
<div class="mb-6">
|
||||
<button
|
||||
class="btn btn-primary px-4 py-2"
|
||||
@click="showCreateForm = true"
|
||||
>
|
||||
<button class="btn btn-primary px-4 py-2" @click="showCreateForm = true">
|
||||
<i class="fas fa-plus mr-2" />
|
||||
创建新分组
|
||||
</button>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 创建分组表单 -->
|
||||
<div
|
||||
v-if="showCreateForm"
|
||||
class="mb-6 p-4 bg-blue-50 rounded-lg border border-blue-200"
|
||||
>
|
||||
<h4 class="text-lg font-semibold text-gray-900 mb-4">
|
||||
创建新分组
|
||||
</h4>
|
||||
<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">创建新分组</h4>
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">分组名称 *</label>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700">分组名称 *</label>
|
||||
<input
|
||||
v-model="createForm.name"
|
||||
type="text"
|
||||
class="form-input w-full"
|
||||
placeholder="输入分组名称"
|
||||
>
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">平台类型 *</label>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700">平台类型 *</label>
|
||||
<div class="flex gap-4">
|
||||
<label class="flex items-center cursor-pointer">
|
||||
<input
|
||||
v-model="createForm.platform"
|
||||
type="radio"
|
||||
value="claude"
|
||||
class="mr-2"
|
||||
>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input v-model="createForm.platform" class="mr-2" type="radio" value="claude" />
|
||||
<span class="text-sm text-gray-700">Claude</span>
|
||||
</label>
|
||||
<label class="flex items-center cursor-pointer">
|
||||
<input
|
||||
v-model="createForm.platform"
|
||||
type="radio"
|
||||
value="gemini"
|
||||
class="mr-2"
|
||||
>
|
||||
<label class="flex cursor-pointer items-center">
|
||||
<input v-model="createForm.platform" class="mr-2" type="radio" value="gemini" />
|
||||
<span class="text-sm text-gray-700">Gemini</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">描述 (可选)</label>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700">描述 (可选)</label>
|
||||
<textarea
|
||||
v-model="createForm.description"
|
||||
rows="2"
|
||||
class="form-input w-full resize-none"
|
||||
placeholder="分组描述..."
|
||||
rows="2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex gap-3">
|
||||
<button
|
||||
class="btn btn-primary px-4 py-2"
|
||||
:disabled="!createForm.name || !createForm.platform || creating"
|
||||
@click="createGroup"
|
||||
>
|
||||
<div
|
||||
v-if="creating"
|
||||
class="loading-spinner mr-2"
|
||||
/>
|
||||
<div v-if="creating" class="loading-spinner mr-2" />
|
||||
{{ creating ? '创建中...' : '创建' }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary px-4 py-2"
|
||||
@click="cancelCreate"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button class="btn btn-secondary px-4 py-2" @click="cancelCreate">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 分组列表 -->
|
||||
<div class="space-y-4">
|
||||
<div
|
||||
v-if="loading"
|
||||
class="text-center py-8"
|
||||
>
|
||||
<div v-if="loading" class="py-8 text-center">
|
||||
<div class="loading-spinner-lg mx-auto mb-4" />
|
||||
<p class="text-gray-500">
|
||||
加载中...
|
||||
</p>
|
||||
<p class="text-gray-500">加载中...</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="groups.length === 0"
|
||||
class="text-center py-8 bg-gray-50 rounded-lg"
|
||||
>
|
||||
<i class="fas fa-layer-group text-4xl text-gray-300 mb-4" />
|
||||
<p class="text-gray-500">
|
||||
暂无分组
|
||||
</p>
|
||||
|
||||
<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">暂无分组</p>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else
|
||||
class="grid gap-4 grid-cols-1 md:grid-cols-2"
|
||||
>
|
||||
|
||||
<div v-else class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||
<div
|
||||
v-for="group in groups"
|
||||
:key="group.id"
|
||||
class="bg-white rounded-lg border p-4 hover:shadow-md transition-shadow"
|
||||
class="rounded-lg border bg-white p-4 transition-shadow hover:shadow-md"
|
||||
>
|
||||
<div class="flex items-start justify-between mb-3">
|
||||
<div class="mb-3 flex items-start justify-between">
|
||||
<div class="flex-1">
|
||||
<h4 class="font-semibold text-gray-900">
|
||||
{{ group.name }}
|
||||
</h4>
|
||||
<p class="text-sm text-gray-500 mt-1">
|
||||
<p class="mt-1 text-sm text-gray-500">
|
||||
{{ group.description || '暂无描述' }}
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 ml-4">
|
||||
<div class="ml-4 flex items-center gap-2">
|
||||
<span
|
||||
:class="[
|
||||
'px-2 py-1 text-xs font-medium rounded-full',
|
||||
group.platform === 'claude'
|
||||
'rounded-full px-2 py-1 text-xs font-medium',
|
||||
group.platform === 'claude'
|
||||
? 'bg-purple-100 text-purple-700'
|
||||
: 'bg-blue-100 text-blue-700'
|
||||
]"
|
||||
@@ -161,7 +121,7 @@
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex items-center justify-between text-sm text-gray-600">
|
||||
<div class="flex items-center gap-4">
|
||||
<span>
|
||||
@@ -175,16 +135,16 @@
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button
|
||||
class="text-blue-600 hover:text-blue-800 transition-colors"
|
||||
class="text-blue-600 transition-colors hover:text-blue-800"
|
||||
title="编辑"
|
||||
@click="editGroup(group)"
|
||||
>
|
||||
<i class="fas fa-edit" />
|
||||
</button>
|
||||
<button
|
||||
class="text-red-600 hover:text-red-800 transition-colors"
|
||||
title="删除"
|
||||
class="text-red-600 transition-colors hover:text-red-800"
|
||||
:disabled="group.memberCount > 0"
|
||||
title="删除"
|
||||
@click="deleteGroup(group)"
|
||||
>
|
||||
<i class="fas fa-trash" />
|
||||
@@ -196,72 +156,59 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 编辑分组模态框 -->
|
||||
<div
|
||||
v-if="showEditForm"
|
||||
class="fixed inset-0 modal z-60 flex items-center justify-center p-3 sm:p-4"
|
||||
class="modal z-60 fixed inset-0 flex items-center justify-center p-3 sm:p-4"
|
||||
>
|
||||
<div class="modal-content w-full max-w-lg p-4 sm:p-6">
|
||||
<div class="flex items-center justify-between mb-4">
|
||||
<h3 class="text-lg font-bold text-gray-900">
|
||||
编辑分组
|
||||
</h3>
|
||||
<button
|
||||
class="text-gray-400 hover:text-gray-600 transition-colors"
|
||||
@click="cancelEdit"
|
||||
>
|
||||
<div class="mb-4 flex items-center justify-between">
|
||||
<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>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="space-y-4">
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">分组名称 *</label>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700">分组名称 *</label>
|
||||
<input
|
||||
v-model="editForm.name"
|
||||
type="text"
|
||||
class="form-input w-full"
|
||||
placeholder="输入分组名称"
|
||||
>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">平台类型</label>
|
||||
<div class="px-3 py-2 bg-gray-100 rounded-lg text-sm text-gray-600">
|
||||
{{ editForm.platform === 'claude' ? 'Claude' : 'Gemini' }}
|
||||
<span class="text-xs text-gray-500 ml-2">(不可修改)</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">描述 (可选)</label>
|
||||
<textarea
|
||||
v-model="editForm.description"
|
||||
rows="2"
|
||||
class="form-input w-full resize-none"
|
||||
placeholder="分组描述..."
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<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' ? 'Claude' : 'Gemini' }}
|
||||
<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">描述 (可选)</label>
|
||||
<textarea
|
||||
v-model="editForm.description"
|
||||
class="form-input w-full resize-none"
|
||||
placeholder="分组描述..."
|
||||
rows="2"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button
|
||||
class="btn btn-primary px-4 py-2 flex-1"
|
||||
class="btn btn-primary flex-1 px-4 py-2"
|
||||
:disabled="!editForm.name || updating"
|
||||
@click="updateGroup"
|
||||
>
|
||||
<div
|
||||
v-if="updating"
|
||||
class="loading-spinner mr-2"
|
||||
/>
|
||||
<div v-if="updating" class="loading-spinner mr-2" />
|
||||
{{ updating ? '更新中...' : '更新' }}
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-secondary px-4 py-2 flex-1"
|
||||
@click="cancelEdit"
|
||||
>
|
||||
取消
|
||||
</button>
|
||||
<button class="btn btn-secondary flex-1 px-4 py-2" @click="cancelEdit">取消</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -325,7 +272,7 @@ const createGroup = async () => {
|
||||
showToast('请填写必填项', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
creating.value = true
|
||||
try {
|
||||
await apiClient.post('/admin/account-groups', {
|
||||
@@ -333,7 +280,7 @@ const createGroup = async () => {
|
||||
platform: createForm.value.platform,
|
||||
description: createForm.value.description
|
||||
})
|
||||
|
||||
|
||||
showToast('分组创建成功', 'success')
|
||||
cancelCreate()
|
||||
await loadGroups()
|
||||
@@ -372,14 +319,14 @@ const updateGroup = async () => {
|
||||
showToast('请填写分组名称', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
updating.value = true
|
||||
try {
|
||||
await apiClient.put(`/admin/account-groups/${editingGroup.value.id}`, {
|
||||
name: editForm.value.name,
|
||||
description: editForm.value.description
|
||||
})
|
||||
|
||||
|
||||
showToast('分组更新成功', 'success')
|
||||
cancelEdit()
|
||||
await loadGroups()
|
||||
@@ -408,11 +355,11 @@ const deleteGroup = async (group) => {
|
||||
showToast('分组内还有成员,无法删除', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
if (!confirm(`确定要删除分组 "${group.name}" 吗?`)) {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
await apiClient.delete(`/admin/account-groups/${group.id}`)
|
||||
showToast('分组删除成功', 'success')
|
||||
@@ -427,4 +374,4 @@ const deleteGroup = async (group) => {
|
||||
onMounted(() => {
|
||||
loadGroups()
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -2,66 +2,55 @@
|
||||
<div class="space-y-6">
|
||||
<!-- Claude OAuth流程 -->
|
||||
<div v-if="platform === 'claude'">
|
||||
<div class="bg-blue-50 p-6 rounded-lg border border-blue-200">
|
||||
<div class="rounded-lg border border-blue-200 bg-blue-50 p-6">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-10 h-10 bg-blue-500 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<div
|
||||
class="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-blue-500"
|
||||
>
|
||||
<i class="fas fa-link text-white" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h4 class="font-semibold text-blue-900 mb-3">
|
||||
Claude 账户授权
|
||||
</h4>
|
||||
<p class="text-sm text-blue-800 mb-4">
|
||||
请按照以下步骤完成 Claude 账户的授权:
|
||||
</p>
|
||||
|
||||
<h4 class="mb-3 font-semibold text-blue-900">Claude 账户授权</h4>
|
||||
<p class="mb-4 text-sm text-blue-800">请按照以下步骤完成 Claude 账户的授权:</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- 步骤1: 生成授权链接 -->
|
||||
<div class="bg-white/80 rounded-lg p-4 border border-blue-300">
|
||||
<div class="rounded-lg border border-blue-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-600 text-xs font-bold text-white"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-blue-900 mb-2">
|
||||
点击下方按钮生成授权链接
|
||||
</p>
|
||||
<button
|
||||
<p class="mb-2 font-medium text-blue-900">点击下方按钮生成授权链接</p>
|
||||
<button
|
||||
v-if="!authUrl"
|
||||
:disabled="loading"
|
||||
class="btn btn-primary px-4 py-2 text-sm"
|
||||
:disabled="loading"
|
||||
@click="generateAuthUrl"
|
||||
>
|
||||
<i
|
||||
v-if="!loading"
|
||||
class="fas fa-link mr-2"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="loading-spinner mr-2"
|
||||
/>
|
||||
<i v-if="!loading" class="fas fa-link mr-2" />
|
||||
<div v-else class="loading-spinner mr-2" />
|
||||
{{ loading ? '生成中...' : '生成授权链接' }}
|
||||
</button>
|
||||
<div
|
||||
v-else
|
||||
class="space-y-3"
|
||||
>
|
||||
<div v-else class="space-y-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
:value="authUrl"
|
||||
<input
|
||||
class="form-input flex-1 bg-gray-50 font-mono text-xs"
|
||||
readonly
|
||||
class="form-input flex-1 text-xs font-mono bg-gray-50"
|
||||
>
|
||||
<button
|
||||
class="px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
||||
type="text"
|
||||
:value="authUrl"
|
||||
/>
|
||||
<button
|
||||
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200"
|
||||
title="复制链接"
|
||||
@click="copyAuthUrl"
|
||||
>
|
||||
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
<button
|
||||
class="text-xs text-blue-600 hover:text-blue-700"
|
||||
@click="regenerateAuthUrl"
|
||||
>
|
||||
@@ -71,56 +60,58 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 步骤2: 访问链接并授权 -->
|
||||
<div class="bg-white/80 rounded-lg p-4 border border-blue-300">
|
||||
<div class="rounded-lg border border-blue-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-600 text-xs font-bold text-white"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-blue-900 mb-2">
|
||||
在浏览器中打开链接并完成授权
|
||||
</p>
|
||||
<p class="text-sm text-blue-700 mb-2">
|
||||
<p class="mb-2 font-medium text-blue-900">在浏览器中打开链接并完成授权</p>
|
||||
<p class="mb-2 text-sm text-blue-700">
|
||||
请在新标签页中打开授权链接,登录您的 Claude 账户并授权。
|
||||
</p>
|
||||
<div class="bg-yellow-50 p-3 rounded border border-yellow-300">
|
||||
<div class="rounded border border-yellow-300 bg-yellow-50 p-3">
|
||||
<p class="text-xs text-yellow-800">
|
||||
<i class="fas fa-exclamation-triangle mr-1" />
|
||||
<strong>注意:</strong>如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。
|
||||
<strong>注意:</strong
|
||||
>如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 步骤3: 输入授权码 -->
|
||||
<div class="bg-white/80 rounded-lg p-4 border border-blue-300">
|
||||
<div class="rounded-lg border border-blue-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-blue-600 text-xs font-bold text-white"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-blue-900 mb-2">
|
||||
输入 Authorization Code
|
||||
</p>
|
||||
<p class="text-sm text-blue-700 mb-3">
|
||||
授权完成后,页面会显示一个 <strong>Authorization Code</strong>,请将其复制并粘贴到下方输入框:
|
||||
<p class="mb-2 font-medium text-blue-900">输入 Authorization Code</p>
|
||||
<p class="mb-3 text-sm text-blue-700">
|
||||
授权完成后,页面会显示一个
|
||||
<strong>Authorization Code</strong>,请将其复制并粘贴到下方输入框:
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
||||
<i class="fas fa-key text-blue-500 mr-2" />Authorization Code
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700">
|
||||
<i class="fas fa-key mr-2 text-blue-500" />Authorization Code
|
||||
</label>
|
||||
<textarea
|
||||
v-model="authCode"
|
||||
rows="3"
|
||||
<textarea
|
||||
v-model="authCode"
|
||||
class="form-input w-full resize-none font-mono text-sm"
|
||||
placeholder="粘贴从Claude页面获取的Authorization Code..."
|
||||
rows="3"
|
||||
/>
|
||||
</div>
|
||||
<p class="text-xs text-gray-500 mt-2">
|
||||
<p class="mt-2 text-xs text-gray-500">
|
||||
<i class="fas fa-info-circle mr-1" />
|
||||
请粘贴从Claude页面复制的Authorization Code
|
||||
</p>
|
||||
@@ -133,69 +124,58 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Gemini OAuth流程 -->
|
||||
<div v-else-if="platform === 'gemini'">
|
||||
<div class="bg-green-50 p-6 rounded-lg border border-green-200">
|
||||
<div class="rounded-lg border border-green-200 bg-green-50 p-6">
|
||||
<div class="flex items-start gap-4">
|
||||
<div class="w-10 h-10 bg-green-500 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<div
|
||||
class="flex h-10 w-10 flex-shrink-0 items-center justify-center rounded-lg bg-green-500"
|
||||
>
|
||||
<i class="fas fa-robot text-white" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<h4 class="font-semibold text-green-900 mb-3">
|
||||
Gemini 账户授权
|
||||
</h4>
|
||||
<p class="text-sm text-green-800 mb-4">
|
||||
请按照以下步骤完成 Gemini 账户的授权:
|
||||
</p>
|
||||
|
||||
<h4 class="mb-3 font-semibold text-green-900">Gemini 账户授权</h4>
|
||||
<p class="mb-4 text-sm text-green-800">请按照以下步骤完成 Gemini 账户的授权:</p>
|
||||
|
||||
<div class="space-y-4">
|
||||
<!-- 步骤1: 生成授权链接 -->
|
||||
<div class="bg-white/80 rounded-lg p-4 border border-green-300">
|
||||
<div class="rounded-lg border border-green-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-green-600 text-xs font-bold text-white"
|
||||
>
|
||||
1
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-green-900 mb-2">
|
||||
点击下方按钮生成授权链接
|
||||
</p>
|
||||
<button
|
||||
<p class="mb-2 font-medium text-green-900">点击下方按钮生成授权链接</p>
|
||||
<button
|
||||
v-if="!authUrl"
|
||||
:disabled="loading"
|
||||
class="btn btn-primary px-4 py-2 text-sm"
|
||||
:disabled="loading"
|
||||
@click="generateAuthUrl"
|
||||
>
|
||||
<i
|
||||
v-if="!loading"
|
||||
class="fas fa-link mr-2"
|
||||
/>
|
||||
<div
|
||||
v-else
|
||||
class="loading-spinner mr-2"
|
||||
/>
|
||||
<i v-if="!loading" class="fas fa-link mr-2" />
|
||||
<div v-else class="loading-spinner mr-2" />
|
||||
{{ loading ? '生成中...' : '生成授权链接' }}
|
||||
</button>
|
||||
<div
|
||||
v-else
|
||||
class="space-y-3"
|
||||
>
|
||||
<div v-else class="space-y-3">
|
||||
<div class="flex items-center gap-2">
|
||||
<input
|
||||
type="text"
|
||||
:value="authUrl"
|
||||
<input
|
||||
class="form-input flex-1 bg-gray-50 font-mono text-xs"
|
||||
readonly
|
||||
class="form-input flex-1 text-xs font-mono bg-gray-50"
|
||||
>
|
||||
<button
|
||||
class="px-3 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg transition-colors"
|
||||
type="text"
|
||||
:value="authUrl"
|
||||
/>
|
||||
<button
|
||||
class="rounded-lg bg-gray-100 px-3 py-2 transition-colors hover:bg-gray-200"
|
||||
title="复制链接"
|
||||
@click="copyAuthUrl"
|
||||
>
|
||||
<i :class="copied ? 'fas fa-check text-green-500' : 'fas fa-copy'" />
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
<button
|
||||
class="text-xs text-green-600 hover:text-green-700"
|
||||
@click="regenerateAuthUrl"
|
||||
>
|
||||
@@ -205,59 +185,60 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 步骤2: 操作说明 -->
|
||||
<div class="bg-white/80 rounded-lg p-4 border border-green-300">
|
||||
<div class="rounded-lg border border-green-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-green-600 text-xs font-bold text-white"
|
||||
>
|
||||
2
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-blue-900 mb-2">
|
||||
在浏览器中打开链接并完成授权
|
||||
</p>
|
||||
<p class="text-sm text-blue-700 mb-2">
|
||||
<p class="mb-2 font-medium text-blue-900">在浏览器中打开链接并完成授权</p>
|
||||
<p class="mb-2 text-sm text-blue-700">
|
||||
请在新标签页中打开授权链接,登录您的 Gemini 账户并授权。
|
||||
</p>
|
||||
<div class="bg-yellow-50 p-3 rounded border border-yellow-300">
|
||||
<div class="rounded border border-yellow-300 bg-yellow-50 p-3">
|
||||
<p class="text-xs text-yellow-800">
|
||||
<i class="fas fa-exclamation-triangle mr-1" />
|
||||
<strong>注意:</strong>如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。
|
||||
<strong>注意:</strong
|
||||
>如果您设置了代理,请确保浏览器也使用相同的代理访问授权页面。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- 步骤3: 输入授权码 -->
|
||||
<div class="bg-white/80 rounded-lg p-4 border border-green-300">
|
||||
<div class="rounded-lg border border-green-300 bg-white/80 p-4">
|
||||
<div class="flex items-start gap-3">
|
||||
<div class="w-6 h-6 bg-green-600 text-white rounded-full flex items-center justify-center text-xs font-bold flex-shrink-0">
|
||||
<div
|
||||
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded-full bg-green-600 text-xs font-bold text-white"
|
||||
>
|
||||
3
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="font-medium text-green-900 mb-2">
|
||||
输入 Authorization Code
|
||||
</p>
|
||||
<p class="text-sm text-green-700 mb-3">
|
||||
<p class="mb-2 font-medium text-green-900">输入 Authorization Code</p>
|
||||
<p class="mb-3 text-sm text-green-700">
|
||||
授权完成后,页面会显示一个 Authorization Code,请将其复制并粘贴到下方输入框:
|
||||
</p>
|
||||
<div class="space-y-3">
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-2">
|
||||
<i class="fas fa-key text-green-500 mr-2" />Authorization Code
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700">
|
||||
<i class="fas fa-key mr-2 text-green-500" />Authorization Code
|
||||
</label>
|
||||
<textarea
|
||||
v-model="authCode"
|
||||
rows="3"
|
||||
<textarea
|
||||
v-model="authCode"
|
||||
class="form-input w-full resize-none font-mono text-sm"
|
||||
placeholder="粘贴从Gemini页面获取的Authorization Code..."
|
||||
rows="3"
|
||||
/>
|
||||
</div>
|
||||
<div class="mt-2 space-y-1">
|
||||
<p class="text-xs text-gray-600">
|
||||
<i class="fas fa-check-circle text-green-500 mr-1" />
|
||||
请粘贴从Gemini页面复制的Authorization Code
|
||||
<i class="fas fa-check-circle mr-1 text-green-500" />
|
||||
请粘贴从Gemini页面复制的Authorization Code
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -269,25 +250,22 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="flex gap-3 pt-4">
|
||||
<button
|
||||
type="button"
|
||||
class="flex-1 px-6 py-3 bg-gray-100 text-gray-700 rounded-xl font-semibold hover:bg-gray-200 transition-colors"
|
||||
<button
|
||||
class="flex-1 rounded-xl bg-gray-100 px-6 py-3 font-semibold text-gray-700 transition-colors hover:bg-gray-200"
|
||||
type="button"
|
||||
@click="$emit('back')"
|
||||
>
|
||||
上一步
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
<button
|
||||
class="btn btn-primary flex-1 px-6 py-3 font-semibold"
|
||||
:disabled="!canExchange || exchanging"
|
||||
class="btn btn-primary flex-1 py-3 px-6 font-semibold"
|
||||
type="button"
|
||||
@click="exchangeCode"
|
||||
>
|
||||
<div
|
||||
v-if="exchanging"
|
||||
class="loading-spinner mr-2"
|
||||
/>
|
||||
<div v-if="exchanging" class="loading-spinner mr-2" />
|
||||
{{ exchanging ? '验证中...' : '完成授权' }}
|
||||
</button>
|
||||
</div>
|
||||
@@ -330,15 +308,15 @@ const canExchange = computed(() => {
|
||||
// 监听授权码输入,自动提取URL中的code参数
|
||||
watch(authCode, (newValue) => {
|
||||
if (!newValue || typeof newValue !== 'string') return
|
||||
|
||||
|
||||
const trimmedValue = newValue.trim()
|
||||
|
||||
|
||||
// 如果内容为空,不处理
|
||||
if (!trimmedValue) return
|
||||
|
||||
|
||||
// 检查是否是 URL 格式(包含 http:// 或 https://)
|
||||
const isUrl = trimmedValue.startsWith('http://') || trimmedValue.startsWith('https://')
|
||||
|
||||
|
||||
// 如果是 URL 格式
|
||||
if (isUrl) {
|
||||
// 检查是否是正确的 localhost:45462 开头的 URL
|
||||
@@ -346,7 +324,7 @@ watch(authCode, (newValue) => {
|
||||
try {
|
||||
const url = new URL(trimmedValue)
|
||||
const code = url.searchParams.get('code')
|
||||
|
||||
|
||||
if (code) {
|
||||
// 成功提取授权码
|
||||
authCode.value = code
|
||||
@@ -367,7 +345,7 @@ watch(authCode, (newValue) => {
|
||||
try {
|
||||
const url = new URL(trimmedValue)
|
||||
const code = url.searchParams.get('code')
|
||||
|
||||
|
||||
if (code) {
|
||||
authCode.value = code
|
||||
showToast('成功提取授权码!', 'success')
|
||||
@@ -387,16 +365,18 @@ watch(authCode, (newValue) => {
|
||||
const generateAuthUrl = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const proxyConfig = props.proxy?.enabled ? {
|
||||
proxy: {
|
||||
type: props.proxy.type,
|
||||
host: props.proxy.host,
|
||||
port: parseInt(props.proxy.port),
|
||||
username: props.proxy.username || null,
|
||||
password: props.proxy.password || null
|
||||
}
|
||||
} : {}
|
||||
|
||||
const proxyConfig = props.proxy?.enabled
|
||||
? {
|
||||
proxy: {
|
||||
type: props.proxy.type,
|
||||
host: props.proxy.host,
|
||||
port: parseInt(props.proxy.port),
|
||||
username: props.proxy.username || null,
|
||||
password: props.proxy.password || null
|
||||
}
|
||||
}
|
||||
: {}
|
||||
|
||||
if (props.platform === 'claude') {
|
||||
const result = await accountsStore.generateClaudeAuthUrl(proxyConfig)
|
||||
authUrl.value = result.authUrl
|
||||
@@ -448,11 +428,11 @@ const copyAuthUrl = async () => {
|
||||
// 交换授权码
|
||||
const exchangeCode = async () => {
|
||||
if (!canExchange.value) return
|
||||
|
||||
|
||||
exchanging.value = true
|
||||
try {
|
||||
let data = {}
|
||||
|
||||
|
||||
if (props.platform === 'claude') {
|
||||
// Claude使用sessionId和callbackUrl(即授权码)
|
||||
data = {
|
||||
@@ -466,7 +446,7 @@ const exchangeCode = async () => {
|
||||
sessionId: sessionId.value
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 添加代理配置(如果启用)
|
||||
if (props.proxy?.enabled) {
|
||||
data.proxy = {
|
||||
@@ -477,14 +457,14 @@ const exchangeCode = async () => {
|
||||
password: props.proxy.password || null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let tokenInfo
|
||||
if (props.platform === 'claude') {
|
||||
tokenInfo = await accountsStore.exchangeClaudeCode(data)
|
||||
} else if (props.platform === 'gemini') {
|
||||
tokenInfo = await accountsStore.exchangeGeminiCode(data)
|
||||
}
|
||||
|
||||
|
||||
emit('success', tokenInfo)
|
||||
} catch (error) {
|
||||
showToast(error.message || '授权失败,请检查授权码是否正确', 'error')
|
||||
@@ -492,4 +472,4 @@ const exchangeCode = async () => {
|
||||
exchanging.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@@ -1,117 +1,97 @@
|
||||
<template>
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<h4 class="text-sm font-semibold text-gray-700">
|
||||
代理设置 (可选)
|
||||
</h4>
|
||||
<label class="flex items-center cursor-pointer">
|
||||
<input
|
||||
v-model="proxy.enabled"
|
||||
<h4 class="text-sm font-semibold text-gray-700">代理设置 (可选)</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"
|
||||
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
|
||||
>
|
||||
/>
|
||||
<span class="ml-2 text-sm text-gray-700">启用代理</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="proxy.enabled"
|
||||
class="bg-gray-50 p-4 rounded-lg border border-gray-200 space-y-4"
|
||||
>
|
||||
<div class="flex items-start gap-3 mb-3">
|
||||
<div class="w-8 h-8 bg-gray-500 rounded-lg flex items-center justify-center flex-shrink-0">
|
||||
<i class="fas fa-server text-white text-sm" />
|
||||
|
||||
<div v-if="proxy.enabled" class="space-y-4 rounded-lg border border-gray-200 bg-gray-50 p-4">
|
||||
<div class="mb-3 flex items-start gap-3">
|
||||
<div class="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-gray-500">
|
||||
<i class="fas fa-server text-sm text-white" />
|
||||
</div>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm text-gray-700">
|
||||
配置代理以访问受限的网络资源。支持 SOCKS5 和 HTTP 代理。
|
||||
</p>
|
||||
<p class="text-xs text-gray-500 mt-1">
|
||||
<p class="mt-1 text-xs text-gray-500">
|
||||
请确保代理服务器稳定可用,否则会影响账户的正常使用。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">代理类型</label>
|
||||
<select
|
||||
v-model="proxy.type"
|
||||
class="form-input w-full"
|
||||
>
|
||||
<option value="socks5">
|
||||
SOCKS5
|
||||
</option>
|
||||
<option value="http">
|
||||
HTTP
|
||||
</option>
|
||||
<option value="https">
|
||||
HTTPS
|
||||
</option>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700">代理类型</label>
|
||||
<select v-model="proxy.type" class="form-input w-full">
|
||||
<option value="socks5">SOCKS5</option>
|
||||
<option value="http">HTTP</option>
|
||||
<option value="https">HTTPS</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">主机地址</label>
|
||||
<input
|
||||
v-model="proxy.host"
|
||||
type="text"
|
||||
placeholder="例如: 192.168.1.100"
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700">主机地址</label>
|
||||
<input
|
||||
v-model="proxy.host"
|
||||
class="form-input w-full"
|
||||
>
|
||||
placeholder="例如: 192.168.1.100"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">端口</label>
|
||||
<input
|
||||
v-model="proxy.port"
|
||||
type="number"
|
||||
placeholder="例如: 1080"
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700">端口</label>
|
||||
<input
|
||||
v-model="proxy.port"
|
||||
class="form-input w-full"
|
||||
>
|
||||
placeholder="例如: 1080"
|
||||
type="number"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="space-y-4">
|
||||
<div class="flex items-center">
|
||||
<input
|
||||
id="proxyAuth"
|
||||
<input
|
||||
id="proxyAuth"
|
||||
v-model="showAuth"
|
||||
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500"
|
||||
type="checkbox"
|
||||
class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 rounded focus:ring-blue-500"
|
||||
>
|
||||
<label
|
||||
for="proxyAuth"
|
||||
class="ml-2 text-sm text-gray-700 cursor-pointer"
|
||||
>
|
||||
/>
|
||||
<label class="ml-2 cursor-pointer text-sm text-gray-700" for="proxyAuth">
|
||||
需要身份验证
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="showAuth"
|
||||
class="grid grid-cols-2 gap-4"
|
||||
>
|
||||
|
||||
<div v-if="showAuth" class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">用户名</label>
|
||||
<input
|
||||
v-model="proxy.username"
|
||||
type="text"
|
||||
placeholder="代理用户名"
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700">用户名</label>
|
||||
<input
|
||||
v-model="proxy.username"
|
||||
class="form-input w-full"
|
||||
>
|
||||
placeholder="代理用户名"
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-medium text-gray-700 mb-2">密码</label>
|
||||
<label class="mb-2 block text-sm font-medium text-gray-700">密码</label>
|
||||
<div class="relative">
|
||||
<input
|
||||
v-model="proxy.password"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
placeholder="代理密码"
|
||||
<input
|
||||
v-model="proxy.password"
|
||||
class="form-input w-full pr-10"
|
||||
>
|
||||
<button
|
||||
placeholder="代理密码"
|
||||
:type="showPassword ? 'text' : 'password'"
|
||||
/>
|
||||
<button
|
||||
class="absolute inset-y-0 right-0 flex items-center pr-3 text-gray-400 hover:text-gray-600"
|
||||
type="button"
|
||||
class="absolute inset-y-0 right-0 pr-3 flex items-center text-gray-400 hover:text-gray-600"
|
||||
@click="showPassword = !showPassword"
|
||||
>
|
||||
<i :class="showPassword ? 'fas fa-eye-slash' : 'fas fa-eye'" />
|
||||
@@ -120,11 +100,12 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-blue-50 p-3 rounded-lg border border-blue-200">
|
||||
|
||||
<div class="rounded-lg border border-blue-200 bg-blue-50 p-3">
|
||||
<p class="text-xs text-blue-700">
|
||||
<i class="fas fa-info-circle mr-1" />
|
||||
<strong>提示:</strong>代理设置将用于所有与此账户相关的API请求。请确保代理服务器支持HTTPS流量转发。
|
||||
<strong>提示:</strong
|
||||
>代理设置将用于所有与此账户相关的API请求。请确保代理服务器支持HTTPS流量转发。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@@ -132,7 +113,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, watch, onUnmounted } from 'vue'
|
||||
import { ref, watch, onUnmounted } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
@@ -158,38 +139,60 @@ const showAuth = ref(!!(proxy.value.username || proxy.value.password))
|
||||
const showPassword = ref(false)
|
||||
|
||||
// 监听modelValue变化,只在真正需要更新时才更新
|
||||
watch(() => props.modelValue, (newVal) => {
|
||||
// 只有当值真正不同时才更新,避免循环
|
||||
if (JSON.stringify(newVal) !== JSON.stringify(proxy.value)) {
|
||||
proxy.value = { ...newVal }
|
||||
showAuth.value = !!(newVal.username || newVal.password)
|
||||
}
|
||||
}, { deep: true })
|
||||
watch(
|
||||
() => props.modelValue,
|
||||
(newVal) => {
|
||||
// 只有当值真正不同时才更新,避免循环
|
||||
if (JSON.stringify(newVal) !== JSON.stringify(proxy.value)) {
|
||||
proxy.value = { ...newVal }
|
||||
showAuth.value = !!(newVal.username || newVal.password)
|
||||
}
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
|
||||
// 监听各个字段单独变化,而不是整个对象
|
||||
watch(() => proxy.value.enabled, (newVal) => {
|
||||
emitUpdate()
|
||||
})
|
||||
watch(
|
||||
() => proxy.value.enabled,
|
||||
() => {
|
||||
emitUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
watch(() => proxy.value.type, (newVal) => {
|
||||
emitUpdate()
|
||||
})
|
||||
watch(
|
||||
() => proxy.value.type,
|
||||
() => {
|
||||
emitUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
watch(() => proxy.value.host, (newVal) => {
|
||||
emitUpdate()
|
||||
})
|
||||
watch(
|
||||
() => proxy.value.host,
|
||||
() => {
|
||||
emitUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
watch(() => proxy.value.port, (newVal) => {
|
||||
emitUpdate()
|
||||
})
|
||||
watch(
|
||||
() => proxy.value.port,
|
||||
() => {
|
||||
emitUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
watch(() => proxy.value.username, (newVal) => {
|
||||
emitUpdate()
|
||||
})
|
||||
watch(
|
||||
() => proxy.value.username,
|
||||
() => {
|
||||
emitUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
watch(() => proxy.value.password, (newVal) => {
|
||||
emitUpdate()
|
||||
})
|
||||
watch(
|
||||
() => proxy.value.password,
|
||||
() => {
|
||||
emitUpdate()
|
||||
}
|
||||
)
|
||||
|
||||
// 监听认证开关
|
||||
watch(showAuth, (newVal) => {
|
||||
@@ -207,17 +210,17 @@ function emitUpdate() {
|
||||
if (updateTimer) {
|
||||
clearTimeout(updateTimer)
|
||||
}
|
||||
|
||||
|
||||
// 设置新的定时器,延迟发送更新
|
||||
updateTimer = setTimeout(() => {
|
||||
const data = { ...proxy.value }
|
||||
|
||||
|
||||
// 如果不需要认证,清空用户名密码
|
||||
if (!showAuth.value) {
|
||||
data.username = ''
|
||||
data.password = ''
|
||||
}
|
||||
|
||||
|
||||
emit('update:modelValue', data)
|
||||
}, 100) // 100ms 延迟
|
||||
}
|
||||
@@ -228,4 +231,4 @@ onUnmounted(() => {
|
||||
clearTimeout(updateTimer)
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user