feat: 支持Dark Mode

This commit is contained in:
shaw
2025-08-22 22:09:38 +08:00
parent 8328b6ddac
commit d2f0ac37a9
37 changed files with 3226 additions and 1155 deletions

View File

@@ -11,10 +11,12 @@
>
<i class="fas fa-edit text-sm text-white sm:text-base" />
</div>
<h3 class="text-lg font-bold text-gray-900 sm:text-xl">编辑 API Key</h3>
<h3 class="text-lg font-bold text-gray-900 dark:text-gray-100 sm:text-xl">
编辑 API Key
</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" />
@@ -26,36 +28,40 @@
@submit.prevent="updateApiKey"
>
<div>
<label class="mb-1.5 block text-xs font-semibold text-gray-700 sm:mb-3 sm:text-sm"
<label
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-sm"
>名称</label
>
<input
class="form-input w-full cursor-not-allowed bg-gray-100 text-sm"
class="form-input w-full cursor-not-allowed bg-gray-100 text-sm dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400"
disabled
type="text"
:value="form.name"
/>
<p class="mt-1 text-xs text-gray-500 sm:mt-2">名称不可修改</p>
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400 sm:mt-2">名称不可修改</p>
</div>
<!-- 标签 -->
<div>
<label class="mb-1.5 block text-xs font-semibold text-gray-700 sm:mb-3 sm:text-sm"
<label
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-sm"
>标签</label
>
<div class="space-y-4">
<!-- 已选择的标签 -->
<div v-if="form.tags.length > 0">
<div class="mb-2 text-xs font-medium text-gray-600">已选择的标签:</div>
<div class="mb-2 text-xs font-medium text-gray-600 dark:text-gray-400">
已选择的标签:
</div>
<div class="flex flex-wrap gap-2">
<span
v-for="(tag, index) in form.tags"
:key="'selected-' + index"
class="inline-flex items-center gap-1 rounded-full bg-blue-100 px-3 py-1 text-sm text-blue-800"
class="inline-flex items-center gap-1 rounded-full bg-blue-100 px-3 py-1 text-sm text-blue-800 dark:bg-blue-900/30 dark:text-blue-400"
>
{{ tag }}
<button
class="ml-1 hover:text-blue-900"
class="ml-1 hover:text-blue-900 dark:hover:text-blue-300"
type="button"
@click="removeTag(index)"
>
@@ -67,16 +73,18 @@
<!-- 可选择的已有标签 -->
<div v-if="unselectedTags.length > 0">
<div class="mb-2 text-xs font-medium text-gray-600">点击选择已有标签:</div>
<div class="mb-2 text-xs font-medium text-gray-600 dark:text-gray-400">
点击选择已有标签:
</div>
<div class="flex flex-wrap gap-2">
<button
v-for="tag in unselectedTags"
:key="'available-' + tag"
class="inline-flex items-center gap-1 rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700 transition-colors hover:bg-blue-100 hover:text-blue-700"
class="inline-flex items-center gap-1 rounded-full bg-gray-100 px-3 py-1 text-sm text-gray-700 transition-colors hover:bg-blue-100 hover:text-blue-700 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-blue-900/30 dark:hover:text-blue-400"
type="button"
@click="selectTag(tag)"
>
<i class="fas fa-tag text-xs text-gray-500" />
<i class="fas fa-tag text-xs text-gray-500 dark:text-gray-400" />
{{ tag }}
</button>
</div>
@@ -84,11 +92,13 @@
<!-- 创建新标签 -->
<div>
<div class="mb-2 text-xs font-medium text-gray-600">创建新标签:</div>
<div class="mb-2 text-xs font-medium text-gray-600 dark:text-gray-400">
创建新标签:
</div>
<div class="flex gap-2">
<input
v-model="newTag"
class="form-input flex-1"
class="form-input flex-1 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
placeholder="输入新标签名称"
type="text"
@keypress.enter.prevent="addTag"
@@ -103,65 +113,79 @@
</div>
</div>
<p class="text-xs text-gray-500">用于标记不同团队或用途方便筛选管理</p>
<p class="text-xs text-gray-500 dark:text-gray-400">
用于标记不同团队或用途方便筛选管理
</p>
</div>
</div>
<!-- 速率限制设置 -->
<div class="rounded-lg border border-blue-200 bg-blue-50 p-3">
<div
class="rounded-lg border border-blue-200 bg-blue-50 p-3 dark:border-blue-700 dark:bg-blue-900/20"
>
<div class="mb-2 flex items-center gap-2">
<div
class="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded bg-blue-500"
>
<i class="fas fa-tachometer-alt text-xs text-white" />
</div>
<h4 class="text-sm font-semibold text-gray-800">速率限制设置 (可选)</h4>
<h4 class="text-sm font-semibold text-gray-800 dark:text-gray-200">
速率限制设置 (可选)
</h4>
</div>
<div class="space-y-2">
<div class="grid grid-cols-1 gap-2 lg:grid-cols-3">
<div>
<label class="mb-1 block text-xs font-medium text-gray-700"
<label class="mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300"
>时间窗口 (分钟)</label
>
<input
v-model="form.rateLimitWindow"
class="form-input w-full text-sm"
class="form-input w-full text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
min="1"
placeholder="无限制"
type="number"
/>
<p class="ml-2 mt-0.5 text-xs text-gray-500">时间段单位</p>
<p class="ml-2 mt-0.5 text-xs text-gray-500 dark:text-gray-400">时间段单位</p>
</div>
<div>
<label class="mb-1 block text-xs font-medium text-gray-700">请求次数限制</label>
<label class="mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300"
>请求次数限制</label
>
<input
v-model="form.rateLimitRequests"
class="form-input w-full text-sm"
class="form-input w-full text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
min="1"
placeholder="无限制"
type="number"
/>
<p class="ml-2 mt-0.5 text-xs text-gray-500">窗口内最大请求</p>
<p class="ml-2 mt-0.5 text-xs text-gray-500 dark:text-gray-400">窗口内最大请求</p>
</div>
<div>
<label class="mb-1 block text-xs font-medium text-gray-700">Token 限制</label>
<label class="mb-1 block text-xs font-medium text-gray-700 dark:text-gray-300"
>Token 限制</label
>
<input
v-model="form.tokenLimit"
class="form-input w-full text-sm"
class="form-input w-full text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
placeholder="无限制"
type="number"
/>
<p class="ml-2 mt-0.5 text-xs text-gray-500">窗口内最大Token</p>
<p class="ml-2 mt-0.5 text-xs text-gray-500 dark:text-gray-400">
窗口内最大Token
</p>
</div>
</div>
<!-- 示例说明 -->
<div class="rounded-lg bg-blue-100 p-2">
<h5 class="mb-1 text-xs font-semibold text-blue-800">💡 使用示例</h5>
<div class="space-y-0.5 text-xs text-blue-700">
<div class="rounded-lg bg-blue-100 p-2 dark:bg-blue-900/30">
<h5 class="mb-1 text-xs font-semibold text-blue-800 dark:text-blue-400">
💡 使用示例
</h5>
<div class="space-y-0.5 text-xs text-blue-700 dark:text-blue-300">
<div>
<strong>示例1:</strong> 时间窗口=60请求次数=1000 每60分钟最多1000次请求
</div>
@@ -178,34 +202,34 @@
</div>
<div>
<label class="mb-3 block text-sm font-semibold text-gray-700"
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>每日费用限制 (美元)</label
>
<div class="space-y-3">
<div class="flex gap-2">
<button
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200"
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
type="button"
@click="form.dailyCostLimit = '50'"
>
$50
</button>
<button
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200"
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
type="button"
@click="form.dailyCostLimit = '100'"
>
$100
</button>
<button
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200"
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
type="button"
@click="form.dailyCostLimit = '200'"
>
$200
</button>
<button
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200"
class="rounded-lg bg-gray-100 px-3 py-1 text-sm font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
type="button"
@click="form.dailyCostLimit = ''"
>
@@ -214,28 +238,32 @@
</div>
<input
v-model="form.dailyCostLimit"
class="form-input w-full"
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
min="0"
placeholder="0 表示无限制"
step="0.01"
type="number"
/>
<p class="text-xs text-gray-500">
<p class="text-xs text-gray-500 dark:text-gray-400">
设置此 API Key 每日的费用限制超过限制将拒绝请求0 或留空表示无限制
</p>
</div>
</div>
<div>
<label class="mb-3 block text-sm font-semibold text-gray-700">并发限制</label>
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>并发限制</label
>
<input
v-model="form.concurrencyLimit"
class="form-input w-full"
class="form-input w-full dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
min="0"
placeholder="0 表示无限制"
type="number"
/>
<p class="mt-2 text-xs text-gray-500">设置此 API Key 可同时处理的最大请求数</p>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
设置此 API Key 可同时处理的最大请求数
</p>
</div>
<!-- 激活账号 -->
@@ -244,49 +272,75 @@
<input
id="editIsActive"
v-model="form.isActive"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="checkbox"
/>
<label
class="ml-2 cursor-pointer text-sm font-semibold text-gray-700"
class="ml-2 cursor-pointer text-sm font-semibold text-gray-700 dark:text-gray-300"
for="editIsActive"
>
激活账号
</label>
</div>
<p class="mb-4 text-xs text-gray-500">
<p class="mb-4 text-xs text-gray-500 dark:text-gray-400">
取消勾选将禁用此 API Key暂停所有请求客户端返回 401 错误
</p>
</div>
<div>
<label class="mb-3 block text-sm font-semibold text-gray-700">服务权限</label>
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
>服务权限</label
>
<div class="flex gap-4">
<label class="flex cursor-pointer items-center">
<input v-model="form.permissions" class="mr-2" type="radio" value="all" />
<span class="text-sm text-gray-700">全部服务</span>
<input
v-model="form.permissions"
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="radio"
value="all"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">全部服务</span>
</label>
<label class="flex cursor-pointer items-center">
<input v-model="form.permissions" class="mr-2" type="radio" value="claude" />
<span class="text-sm text-gray-700"> Claude</span>
<input
v-model="form.permissions"
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="radio"
value="claude"
/>
<span class="text-sm text-gray-700 dark:text-gray-300"> Claude</span>
</label>
<label class="flex cursor-pointer items-center">
<input v-model="form.permissions" class="mr-2" type="radio" value="gemini" />
<span class="text-sm text-gray-700"> Gemini</span>
<input
v-model="form.permissions"
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="radio"
value="gemini"
/>
<span class="text-sm text-gray-700 dark:text-gray-300"> Gemini</span>
</label>
<label class="flex cursor-pointer items-center">
<input v-model="form.permissions" class="mr-2" type="radio" value="openai" />
<span class="text-sm text-gray-700"> OpenAI</span>
<input
v-model="form.permissions"
class="mr-2 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="radio"
value="openai"
/>
<span class="text-sm text-gray-700 dark:text-gray-300"> OpenAI</span>
</label>
</div>
<p class="mt-2 text-xs text-gray-500">控制此 API Key 可以访问哪些服务</p>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
控制此 API Key 可以访问哪些服务
</p>
</div>
<div>
<div class="mb-3 flex items-center justify-between">
<label class="text-sm font-semibold text-gray-700">专属账号绑定</label>
<label class="text-sm font-semibold text-gray-700 dark:text-gray-300"
>专属账号绑定</label
>
<button
class="flex items-center gap-1 text-sm text-blue-600 transition-colors hover:text-blue-800 disabled:cursor-not-allowed disabled:opacity-50"
class="flex items-center gap-1 text-sm text-blue-600 transition-colors hover:text-blue-800 disabled:cursor-not-allowed disabled:opacity-50 dark:text-blue-400 dark:hover:text-blue-300"
:disabled="accountsLoading"
title="刷新账号列表"
type="button"
@@ -304,7 +358,9 @@
</div>
<div class="grid grid-cols-1 gap-3">
<div>
<label class="mb-1 block text-sm font-medium text-gray-600">Claude 专属账号</label>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Claude 专属账号</label
>
<AccountSelector
v-model="form.claudeAccountId"
:accounts="localAccounts.claude"
@@ -316,7 +372,9 @@
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600">Gemini 专属账号</label>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Gemini 专属账号</label
>
<AccountSelector
v-model="form.geminiAccountId"
:accounts="localAccounts.gemini"
@@ -328,7 +386,9 @@
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600">OpenAI 专属账号</label>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>OpenAI 专属账号</label
>
<AccountSelector
v-model="form.openaiAccountId"
:accounts="localAccounts.openai"
@@ -340,7 +400,9 @@
/>
</div>
<div>
<label class="mb-1 block text-sm font-medium text-gray-600">Bedrock 专属账号</label>
<label class="mb-1 block text-sm font-medium text-gray-600 dark:text-gray-400"
>Bedrock 专属账号</label
>
<AccountSelector
v-model="form.bedrockAccountId"
:accounts="localAccounts.bedrock"
@@ -352,7 +414,9 @@
/>
</div>
</div>
<p class="mt-2 text-xs text-gray-500">修改绑定账号将影响此API Key的请求路由</p>
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
修改绑定账号将影响此API Key的请求路由
</p>
</div>
<div>
@@ -360,11 +424,11 @@
<input
id="editEnableModelRestriction"
v-model="form.enableModelRestriction"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="checkbox"
/>
<label
class="ml-2 cursor-pointer text-sm font-semibold text-gray-700"
class="ml-2 cursor-pointer text-sm font-semibold text-gray-700 dark:text-gray-300"
for="editEnableModelRestriction"
>
启用模型限制
@@ -373,25 +437,30 @@
<div v-if="form.enableModelRestriction" class="space-y-3">
<div>
<label class="mb-2 block text-sm font-medium text-gray-600">限制的模型列表</label>
<label class="mb-2 block text-sm font-medium text-gray-600 dark:text-gray-400"
>限制的模型列表</label
>
<div
class="mb-3 flex min-h-[32px] flex-wrap gap-2 rounded-lg border border-gray-200 bg-gray-50 p-2"
class="mb-3 flex min-h-[32px] flex-wrap gap-2 rounded-lg border border-gray-200 bg-gray-50 p-2 dark:border-gray-600 dark:bg-gray-700"
>
<span
v-for="(model, index) in form.restrictedModels"
:key="index"
class="inline-flex items-center rounded-full bg-red-100 px-3 py-1 text-sm text-red-800"
class="inline-flex items-center rounded-full bg-red-100 px-3 py-1 text-sm text-red-800 dark:bg-red-900/30 dark:text-red-400"
>
{{ model }}
<button
class="ml-2 text-red-600 hover:text-red-800"
class="ml-2 text-red-600 hover:text-red-800 dark:text-red-400 dark:hover:text-red-300"
type="button"
@click="removeRestrictedModel(index)"
>
<i class="fas fa-times text-xs" />
</button>
</span>
<span v-if="form.restrictedModels.length === 0" class="text-sm text-gray-400">
<span
v-if="form.restrictedModels.length === 0"
class="text-sm text-gray-400 dark:text-gray-500"
>
暂无限制的模型
</span>
</div>
@@ -401,7 +470,7 @@
<button
v-for="model in availableQuickModels"
:key="model"
class="flex-shrink-0 rounded-lg bg-gray-100 px-3 py-1 text-xs text-gray-700 transition-colors hover:bg-gray-200 sm:text-sm"
class="flex-shrink-0 rounded-lg bg-gray-100 px-3 py-1 text-xs text-gray-700 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600 sm:text-sm"
type="button"
@click="quickAddRestrictedModel(model)"
>
@@ -409,7 +478,7 @@
</button>
<span
v-if="availableQuickModels.length === 0"
class="text-sm italic text-gray-400"
class="text-sm italic text-gray-400 dark:text-gray-500"
>
所有常用模型已在限制列表中
</span>
@@ -419,7 +488,7 @@
<div class="flex gap-2">
<input
v-model="form.modelInput"
class="form-input flex-1"
class="form-input flex-1 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
placeholder="输入模型名称,按回车添加"
type="text"
@keydown.enter.prevent="addRestrictedModel"
@@ -433,7 +502,7 @@
</button>
</div>
</div>
<p class="mt-2 text-xs text-gray-500">
<p class="mt-2 text-xs text-gray-500 dark:text-gray-400">
设置此API Key无法访问的模型例如claude-opus-4-20250514
</p>
</div>
@@ -446,11 +515,11 @@
<input
id="editEnableClientRestriction"
v-model="form.enableClientRestriction"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500"
class="h-4 w-4 rounded border-gray-300 bg-gray-100 text-blue-600 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700"
type="checkbox"
/>
<label
class="ml-2 cursor-pointer text-sm font-semibold text-gray-700"
class="ml-2 cursor-pointer text-sm font-semibold text-gray-700 dark:text-gray-300"
for="editEnableClientRestriction"
>
启用客户端限制
@@ -459,8 +528,12 @@
<div v-if="form.enableClientRestriction" class="space-y-3">
<div>
<label class="mb-2 block text-sm font-medium text-gray-600">允许的客户端</label>
<p class="mb-3 text-xs text-gray-500">勾选允许使用此API Key的客户端</p>
<label class="mb-2 block text-sm font-medium text-gray-600 dark:text-gray-400"
>允许的客户端</label
>
<p class="mb-3 text-xs text-gray-500 dark:text-gray-400">
勾选允许使用此API Key的客户端
</p>
<div class="space-y-2">
<div v-for="client in supportedClients" :key="client.id" class="flex items-start">
<input
@@ -471,8 +544,12 @@
:value="client.id"
/>
<label class="ml-2 flex-1 cursor-pointer" :for="`edit_client_${client.id}`">
<span class="text-sm font-medium text-gray-700">{{ client.name }}</span>
<span class="block text-xs text-gray-500">{{ client.description }}</span>
<span class="text-sm font-medium text-gray-700 dark:text-gray-300">{{
client.name
}}</span>
<span class="block text-xs text-gray-500 dark:text-gray-400">{{
client.description
}}</span>
</label>
</div>
</div>
@@ -482,7 +559,7 @@
<div class="flex gap-3 pt-4">
<button
class="flex-1 rounded-xl bg-gray-100 px-6 py-3 font-semibold text-gray-700 transition-colors hover:bg-gray-200"
class="flex-1 rounded-xl bg-gray-100 px-6 py-3 font-semibold text-gray-700 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
type="button"
@click="$emit('close')"
>