mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-22 16:43:35 +00:00
Merge branch 'Wei-Shaw:main' into main
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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, '配置信息已复制到剪贴板')
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"'
|
||||
|
||||
Reference in New Issue
Block a user