Merge branch 'Wei-Shaw:main' into main

This commit is contained in:
jft0m
2025-10-13 11:32:02 +08:00
committed by GitHub
21 changed files with 729 additions and 344 deletions

View File

@@ -261,6 +261,10 @@
background: rgba(0, 0, 0, 0.6);
}
.dark .modal {
background: rgba(0, 0, 0, 0.75);
}
.modal-content {
background: rgba(255, 255, 255, 0.98);
border-radius: 24px;
@@ -271,6 +275,14 @@
/* 移除模糊效果 */
}
.dark .modal-content {
background: rgba(17, 24, 39, 0.95);
border: 1px solid rgba(75, 85, 99, 0.3);
box-shadow:
0 10px 25px -5px rgba(0, 0, 0, 0.3),
0 0 0 1px rgba(255, 255, 255, 0.05);
}
/* 弹窗滚动内容样式 */
.modal-scroll-content {
max-height: calc(90vh - 160px);

View File

@@ -2,7 +2,7 @@
<Teleport to="body">
<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-2xl overflow-y-auto p-4 sm:p-6 md:p-8"
class="modal-content custom-scrollbar mx-auto max-h-[90vh] w-full max-w-2xl overflow-y-auto rounded-2xl bg-white/90 p-4 shadow-xl backdrop-blur-xl dark:bg-gray-800/95 dark:shadow-2xl 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">
@@ -16,7 +16,7 @@
</h3>
</div>
<button
class="p-1 text-gray-400 transition-colors hover:text-gray-600"
class="p-1 text-gray-400 transition-colors hover:text-gray-600 dark:text-gray-500 dark:hover:text-gray-300"
@click="$emit('close')"
>
<i class="fas fa-times text-lg sm:text-xl" />
@@ -419,18 +419,6 @@ watch(
</script>
<style scoped>
.modal-content {
background: rgba(255, 255, 255, 0.9);
border-radius: 16px;
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
}
:global(.dark) .modal-content {
background: rgba(17, 24, 39, 0.85);
}
.loading-spinner {
width: 20px;
height: 20px;

View File

@@ -346,151 +346,76 @@
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Claude 专属账号</label
>
<select
v-model="form.claudeAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions && !['all', 'claude'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
<optgroup v-if="localAccounts.claudeGroups.length > 0" label="账号分组">
<option
v-for="group in localAccounts.claudeGroups"
:key="group.id"
:value="`group:${group.id}`"
>
分组 - {{ group.name }}
</option>
</optgroup>
<optgroup v-if="localAccounts.claude.length > 0" label="专属账号">
<option
v-for="account in localAccounts.claude"
:key="account.id"
:value="
account.platform === 'claude-console' ? `console:${account.id}` : account.id
"
>
{{ account.name }} ({{
account.platform === 'claude-console' ? 'Console' : 'OAuth'
}})
</option>
</optgroup>
</select>
<AccountSelector
v-model="claudeAccountSelectorValue"
:accounts="localAccounts.claude"
default-option-text="请选择Claude账号"
:disabled="!isServiceSelectable('claude')"
:groups="localAccounts.claudeGroups"
placeholder="请选择Claude账号"
platform="claude"
:special-options="accountSpecialOptions"
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Gemini 专属账号</label
>
<select
v-model="form.geminiAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions && !['all', 'gemini'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
<optgroup v-if="localAccounts.geminiGroups.length > 0" label="账号分组">
<option
v-for="group in localAccounts.geminiGroups"
:key="group.id"
:value="`group:${group.id}`"
>
分组 - {{ group.name }}
</option>
</optgroup>
<optgroup v-if="localAccounts.gemini.length > 0" label="专属账号">
<option
v-for="account in localAccounts.gemini"
:key="account.id"
:value="account.id"
>
{{ account.name }}
</option>
</optgroup>
</select>
<AccountSelector
v-model="geminiAccountSelectorValue"
:accounts="localAccounts.gemini"
default-option-text="请选择Gemini账号"
:disabled="!isServiceSelectable('gemini')"
:groups="localAccounts.geminiGroups"
placeholder="请选择Gemini账号"
platform="gemini"
:special-options="accountSpecialOptions"
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>OpenAI 专属账号</label
>
<select
v-model="form.openaiAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions && !['all', 'openai'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
<optgroup v-if="localAccounts.openaiGroups.length > 0" label="账号分组">
<option
v-for="group in localAccounts.openaiGroups"
:key="group.id"
:value="`group:${group.id}`"
>
分组 - {{ group.name }}
</option>
</optgroup>
<optgroup v-if="localAccounts.openai.length > 0" label="专属账号">
<option
v-for="account in localAccounts.openai"
:key="account.id"
:value="account.id"
>
{{ account.name }}
</option>
</optgroup>
</select>
<AccountSelector
v-model="openaiAccountSelectorValue"
:accounts="localAccounts.openai"
default-option-text="请选择OpenAI账号"
:disabled="!isServiceSelectable('openai')"
:groups="localAccounts.openaiGroups"
placeholder="请选择OpenAI账号"
platform="openai"
:special-options="accountSpecialOptions"
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Bedrock 专属账号</label
>
<select
v-model="form.bedrockAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions && !['all', 'openai'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
<optgroup v-if="localAccounts.bedrock.length > 0" label="专属账号">
<option
v-for="account in localAccounts.bedrock"
:key="account.id"
:value="account.id"
>
{{ account.name }}
</option>
</optgroup>
</select>
<AccountSelector
v-model="bedrockAccountSelectorValue"
:accounts="localAccounts.bedrock"
default-option-text="请选择Bedrock账号"
:disabled="!isServiceSelectable('openai')"
:groups="[]"
placeholder="请选择Bedrock账号"
platform="bedrock"
:special-options="accountSpecialOptions"
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Droid 专属账号</label
>
<select
v-model="form.droidAccountId"
class="form-input w-full border-gray-300 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-200"
:disabled="form.permissions && !['all', 'droid'].includes(form.permissions)"
>
<option value="">不修改</option>
<option value="SHARED_POOL">使用共享账号池</option>
<optgroup v-if="localAccounts.droidGroups.length > 0" label="账号分组">
<option
v-for="group in localAccounts.droidGroups"
:key="group.id"
:value="`group:${group.id}`"
>
分组 - {{ group.name }}
</option>
</optgroup>
<optgroup v-if="localAccounts.droid.length > 0" label="专属账号">
<option
v-for="account in localAccounts.droid"
:key="account.id"
:value="account.id"
>
{{ account.name }}
</option>
</optgroup>
</select>
<AccountSelector
v-model="droidAccountSelectorValue"
:accounts="localAccounts.droid"
default-option-text="请选择Droid账号"
:disabled="!isServiceSelectable('droid')"
:groups="localAccounts.droidGroups"
placeholder="请选择Droid账号"
platform="droid"
:special-options="accountSpecialOptions"
/>
</div>
</div>
</div>
@@ -524,6 +449,7 @@ import { ref, reactive, computed, onMounted } from 'vue'
import { showToast } from '@/utils/toast'
import { useApiKeysStore } from '@/stores/apiKeys'
import { apiClient } from '@/config/api'
import AccountSelector from '@/components/common/AccountSelector.vue'
const props = defineProps({
selectedKeys: {
@@ -594,6 +520,37 @@ const form = reactive({
isActive: null // null表示不修改
})
const UNCHANGED_OPTION_VALUE = '__KEEP_ORIGINAL__'
const accountSpecialOptions = [
{ value: UNCHANGED_OPTION_VALUE, label: '不修改' },
{ value: 'SHARED_POOL', label: '使用共享账号池' }
]
const createAccountSelectorModel = (field) =>
computed({
get: () => (form[field] === '' ? UNCHANGED_OPTION_VALUE : form[field]),
set: (value) => {
if (!value || value === UNCHANGED_OPTION_VALUE) {
form[field] = ''
} else {
form[field] = value
}
}
})
const claudeAccountSelectorValue = createAccountSelectorModel('claudeAccountId')
const geminiAccountSelectorValue = createAccountSelectorModel('geminiAccountId')
const openaiAccountSelectorValue = createAccountSelectorModel('openaiAccountId')
const bedrockAccountSelectorValue = createAccountSelectorModel('bedrockAccountId')
const droidAccountSelectorValue = createAccountSelectorModel('droidAccountId')
const isServiceSelectable = (service) => {
if (!form.permissions) return true
if (form.permissions === 'all') return true
return form.permissions === service
}
// 标签管理方法
const addTag = () => {
if (newTag.value && newTag.value.trim()) {

View File

@@ -200,10 +200,6 @@ const getDisplayedApiKey = () => {
}
}
const droidEndpoint = computed(() => {
return getBaseUrlPrefix() + '/droid/claude'
})
// 通用复制工具,包含降级处理
const copyTextWithFallback = async (text, successMessage) => {
try {
@@ -235,9 +231,7 @@ const copyFullConfig = async () => {
// 构建环境变量配置格式
const configText = `ANTHROPIC_BASE_URL="${currentBaseUrl.value}"
ANTHROPIC_AUTH_TOKEN="${key}"
# 提示:如需调用 /droid/claude 端点(已在后台添加 Droid 账号),请将 ANTHROPIC_BASE_URL 改为 "${droidEndpoint.value}" 或根据实际环境调整。`
ANTHROPIC_AUTH_TOKEN="${key}"`
await copyTextWithFallback(configText, '配置信息已复制到剪贴板')
}

View File

@@ -62,6 +62,28 @@
<!-- 选项列表 -->
<div class="custom-scrollbar flex-1 overflow-y-auto">
<!-- 特殊选项 -->
<div
v-if="specialOptionsList.length > 0"
class="border-b border-gray-200 dark:border-gray-600"
>
<div
v-for="option in specialOptionsList"
:key="`special-${option.value}`"
class="cursor-pointer px-4 py-2 transition-colors hover:bg-gray-50 dark:hover:bg-gray-700"
:class="{ 'bg-blue-50 dark:bg-blue-900/20': modelValue === option.value }"
@click="selectAccount(option.value)"
>
<span class="text-gray-700 dark:text-gray-300">{{ option.label }}</span>
<span
v-if="option.description"
class="ml-2 text-xs text-gray-400 dark:text-gray-500"
>
{{ option.description }}
</span>
</div>
</div>
<!-- 默认选项 -->
<div
class="cursor-pointer px-4 py-2 transition-colors hover:bg-gray-50 dark:hover:bg-gray-700"
@@ -264,6 +286,10 @@ const props = defineProps({
defaultOptionText: {
type: String,
default: '使用共享账号池'
},
specialOptions: {
type: Array,
default: () => []
}
})
@@ -276,9 +302,17 @@ const dropdownRef = ref(null)
const dropdownStyle = ref({})
const triggerRef = ref(null)
const lastDirection = ref('') // 记住上次的显示方向
const specialOptionsList = computed(() => props.specialOptions || [])
// 获取选中的标签
const selectedLabel = computed(() => {
const matchedSpecial = specialOptionsList.value.find(
(option) => option.value === props.modelValue
)
if (matchedSpecial) {
return matchedSpecial.label
}
// 如果没有选中值,显示默认选项文本
if (!props.modelValue) return props.defaultOptionText

View File

@@ -296,15 +296,6 @@
</p>
</div>
</div>
<p class="mt-3 text-xs text-purple-700 dark:text-purple-300 sm:text-sm">
🚀 如果你在后台添加了 <strong>Droid</strong> 类型账号请将上述命令中的
<code class="rounded bg-purple-100 px-1 dark:bg-purple-900">{{ currentBaseUrl }}</code>
替换为
<code class="rounded bg-purple-100 px-1 dark:bg-purple-900">{{
droidClaudeBaseUrl
}}</code
>其余配置保持不变
</p>
</div>
<!-- VSCode 插件配置 -->
@@ -514,17 +505,6 @@
{{ line }}
</div>
</div>
<p class="mt-2 text-xs text-yellow-700 dark:text-yellow-300 sm:text-sm">
🚀 如果你要使用 <strong>Droid</strong> 类型账号池请把配置中的
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">{{
openaiBaseUrl
}}</code>
替换为
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">{{
droidOpenaiBaseUrl
}}</code
>
</p>
<p class="mt-3 text-sm text-yellow-700 dark:text-yellow-300">
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900"
@@ -1009,15 +989,6 @@
</div>
</div>
</div>
<p class="mt-3 text-xs text-orange-700 dark:text-orange-300 sm:text-sm">
🚀 如果你创建了 <strong>Droid</strong> 类型账号请把上述命令中的
<code class="rounded bg-orange-100 px-1 dark:bg-orange-900">{{ currentBaseUrl }}</code>
替换为
<code class="rounded bg-orange-100 px-1 dark:bg-orange-900">{{
droidClaudeBaseUrl
}}</code
>其余配置保持不变
</p>
</div>
<!-- VSCode 插件配置 (macOS) -->
@@ -1185,17 +1156,6 @@
{{ line }}
</div>
</div>
<p class="mt-2 text-xs text-yellow-700 dark:text-yellow-300 sm:text-sm">
🚀 如果你要使用 <strong>Droid</strong> 类型账号池请把配置中的
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">{{
openaiBaseUrl
}}</code>
替换为
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">{{
droidOpenaiBaseUrl
}}</code
>
</p>
<p class="mt-3 text-sm text-yellow-700 dark:text-yellow-300">
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900"
@@ -1674,15 +1634,6 @@
</div>
</div>
</div>
<p class="mt-3 text-xs text-orange-700 dark:text-orange-300 sm:text-sm">
🚀 如果你创建了 <strong>Droid</strong> 类型账号请把上述命令中的
<code class="rounded bg-orange-100 px-1 dark:bg-orange-900">{{ currentBaseUrl }}</code>
替换为
<code class="rounded bg-orange-100 px-1 dark:bg-orange-900">{{
droidClaudeBaseUrl
}}</code
>其余配置保持不变
</p>
</div>
<!-- Gemini CLI 环境变量设置 -->
@@ -1818,17 +1769,6 @@
{{ line }}
</div>
</div>
<p class="mt-2 text-xs text-yellow-700 dark:text-yellow-300 sm:text-sm">
🚀 如果你要使用 <strong>Droid</strong> 类型账号池请把配置中的
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">{{
openaiBaseUrl
}}</code>
替换为
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900">{{
droidOpenaiBaseUrl
}}</code
>
</p>
<p class="mt-3 text-sm text-yellow-700 dark:text-yellow-300">
<code class="rounded bg-yellow-100 px-1 dark:bg-yellow-900"
@@ -2293,8 +2233,6 @@ const codexConfigContent = computed(() => {
'[model_providers.crs]',
'name = "crs"',
`base_url = "${openaiBaseUrl.value}"`,
'# 若使用 Droid 类型账号,请改为以下地址',
`# base_url = "${droidOpenaiBaseUrl.value}"`,
'wire_api = "responses"',
'requires_openai_auth = true',
'env_key = "CRS_OAI_KEY"'