mirror of
https://github.com/Wei-Shaw/claude-relay-service.git
synced 2026-01-23 19:57:20 +00:00
Revert "合并所有新功能到Wei-Shaw仓库(排除ApiStatsView.vue)"
This commit is contained in:
@@ -110,70 +110,26 @@
|
||||
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-2 sm:text-sm"
|
||||
>名称 <span class="text-red-500">*</span></label
|
||||
>
|
||||
<input
|
||||
v-model="form.name"
|
||||
class="form-input w-full border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.name }"
|
||||
:placeholder="
|
||||
form.createType === 'batch'
|
||||
? '输入基础名称(将自动添加序号)'
|
||||
: '为您的 API Key 取一个名称'
|
||||
"
|
||||
required
|
||||
type="text"
|
||||
@input="errors.name = ''"
|
||||
/>
|
||||
<div>
|
||||
<input
|
||||
v-model="form.name"
|
||||
class="form-input flex-1 border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
:class="{ 'border-red-500': errors.name }"
|
||||
:placeholder="
|
||||
form.createType === 'batch'
|
||||
? '输入基础名称(将自动添加序号)'
|
||||
: '为您的 API Key 取一个名称'
|
||||
"
|
||||
required
|
||||
type="text"
|
||||
@input="errors.name = ''"
|
||||
/>
|
||||
</div>
|
||||
<p v-if="errors.name" class="mt-1 text-xs text-red-500 dark:text-red-400">
|
||||
{{ errors.name }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 图标上传 -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300">
|
||||
图标 (可选)
|
||||
</label>
|
||||
<div class="space-y-3">
|
||||
<!-- 当前图标预览 -->
|
||||
<div v-if="form.icon" class="flex items-center gap-3">
|
||||
<div
|
||||
class="h-12 w-12 overflow-hidden rounded-lg border border-gray-200 dark:border-gray-600"
|
||||
>
|
||||
<img alt="API Key图标" class="h-full w-full object-cover" :src="form.icon" />
|
||||
</div>
|
||||
<button
|
||||
class="text-sm text-red-500 hover:text-red-700 dark:text-red-400 dark:hover:text-red-300"
|
||||
type="button"
|
||||
@click="form.icon = ''"
|
||||
>
|
||||
移除图标
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 图标上传按钮 -->
|
||||
<div class="flex items-center gap-3">
|
||||
<input
|
||||
ref="iconInput"
|
||||
accept="image/*"
|
||||
class="hidden"
|
||||
type="file"
|
||||
@change="handleIconUpload"
|
||||
/>
|
||||
<button
|
||||
class="flex items-center gap-2 rounded-md border border-gray-300 bg-white px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="$refs.iconInput.click()"
|
||||
>
|
||||
<i class="fas fa-upload" />
|
||||
选择图标
|
||||
</button>
|
||||
<p class="text-xs text-gray-500 dark:text-gray-400">
|
||||
支持 PNG、JPG 格式,建议尺寸 64x64px
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 标签 -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
@@ -428,56 +384,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GPT-5 High推理级别周费用限制 -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>GPT-5 High推理级别周费用限制 (美元)</label
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = '5'"
|
||||
>
|
||||
$5
|
||||
</button>
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = '20'"
|
||||
>
|
||||
$20
|
||||
</button>
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = '50'"
|
||||
>
|
||||
$50
|
||||
</button>
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = ''"
|
||||
>
|
||||
自定义
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
v-model="form.weeklyGPT5HighCostLimit"
|
||||
class="form-input w-full border-gray-300 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 dark:text-gray-400">
|
||||
设置 GPT-5 High推理级别的周费用限制(周一到周日),0 或留空表示无限制
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>并发限制 (可选)</label
|
||||
@@ -898,7 +804,6 @@
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, computed, onMounted } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { showToast } from '@/utils/toast'
|
||||
import { useClientsStore } from '@/stores/clients'
|
||||
import { useApiKeysStore } from '@/stores/apiKeys'
|
||||
@@ -957,7 +862,6 @@ const form = reactive({
|
||||
concurrencyLimit: '',
|
||||
dailyCostLimit: '',
|
||||
weeklyOpusCostLimit: '',
|
||||
weeklyGPT5HighCostLimit: '', // 新增:GPT-5 High推理级别周费用限制
|
||||
expireDuration: '',
|
||||
customExpireDate: '',
|
||||
expiresAt: null,
|
||||
@@ -973,8 +877,7 @@ const form = reactive({
|
||||
modelInput: '',
|
||||
enableClientRestriction: false,
|
||||
allowedClients: [],
|
||||
tags: [],
|
||||
icon: '' // 新增:图标(base64编码)
|
||||
tags: []
|
||||
})
|
||||
|
||||
// 加载支持的客户端和已存在的标签
|
||||
@@ -995,35 +898,6 @@ onMounted(async () => {
|
||||
}
|
||||
})
|
||||
|
||||
// 处理图标上传
|
||||
const handleIconUpload = (event) => {
|
||||
const file = event.target.files?.[0]
|
||||
if (!file) return
|
||||
|
||||
// 检查文件类型
|
||||
if (!file.type.startsWith('image/')) {
|
||||
ElMessage.error('请选择图片文件')
|
||||
return
|
||||
}
|
||||
|
||||
// 检查文件大小 (限制为 2MB)
|
||||
if (file.size > 2 * 1024 * 1024) {
|
||||
ElMessage.error('图片大小不能超过 2MB')
|
||||
return
|
||||
}
|
||||
|
||||
// 读取文件并转换为 base64
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
form.icon = e.target.result
|
||||
ElMessage.success('图标上传成功')
|
||||
}
|
||||
reader.onerror = () => {
|
||||
ElMessage.error('图标上传失败')
|
||||
}
|
||||
reader.readAsDataURL(file)
|
||||
}
|
||||
|
||||
// 刷新账号列表
|
||||
const refreshAccounts = async () => {
|
||||
accountsLoading.value = true
|
||||
@@ -1178,22 +1052,7 @@ const removeRestrictedModel = (index) => {
|
||||
}
|
||||
|
||||
// 常用模型列表
|
||||
const commonModels = ref([
|
||||
// Claude 模型
|
||||
'claude-opus-4-20250514',
|
||||
'claude-opus-4-1-20250805',
|
||||
// OpenAI 模型
|
||||
'gpt-5',
|
||||
'gpt-5 minimal',
|
||||
'gpt-5 low',
|
||||
'gpt-5 medium',
|
||||
'gpt-5 high',
|
||||
'gpt-4o',
|
||||
'gpt-4o-mini',
|
||||
'o1',
|
||||
'o1-mini',
|
||||
'o1-preview'
|
||||
])
|
||||
const commonModels = ref(['claude-opus-4-20250514', 'claude-opus-4-1-20250805'])
|
||||
|
||||
// 可用的快捷模型(过滤掉已在限制列表中的)
|
||||
const availableQuickModels = computed(() => {
|
||||
@@ -1296,11 +1155,6 @@ const createApiKey = async () => {
|
||||
form.weeklyOpusCostLimit !== '' && form.weeklyOpusCostLimit !== null
|
||||
? parseFloat(form.weeklyOpusCostLimit)
|
||||
: 0,
|
||||
// 新增:GPT-5 High推理级别周费用限制
|
||||
weeklyGPT5HighCostLimit:
|
||||
form.weeklyGPT5HighCostLimit !== '' && form.weeklyGPT5HighCostLimit !== null
|
||||
? parseFloat(form.weeklyGPT5HighCostLimit)
|
||||
: 0,
|
||||
expiresAt: form.expirationMode === 'fixed' ? form.expiresAt || undefined : undefined,
|
||||
expirationMode: form.expirationMode,
|
||||
activationDays: form.expirationMode === 'activation' ? form.activationDays : undefined,
|
||||
@@ -1309,8 +1163,7 @@ const createApiKey = async () => {
|
||||
enableModelRestriction: form.enableModelRestriction,
|
||||
restrictedModels: form.restrictedModels,
|
||||
enableClientRestriction: form.enableClientRestriction,
|
||||
allowedClients: form.allowedClients,
|
||||
icon: form.icon || undefined // 新增:图标
|
||||
allowedClients: form.allowedClients
|
||||
}
|
||||
|
||||
// 处理Claude账户绑定(区分OAuth和Console)
|
||||
|
||||
@@ -32,14 +32,16 @@
|
||||
class="mb-1.5 block text-xs font-semibold text-gray-700 dark:text-gray-300 sm:mb-3 sm:text-sm"
|
||||
>名称</label
|
||||
>
|
||||
<input
|
||||
v-model="form.name"
|
||||
class="form-input w-full border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
maxlength="100"
|
||||
placeholder="请输入API Key名称"
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
<div>
|
||||
<input
|
||||
v-model="form.name"
|
||||
class="form-input flex-1 border-gray-300 text-sm dark:border-gray-600 dark:bg-gray-700 dark:text-gray-200 dark:placeholder-gray-400"
|
||||
maxlength="100"
|
||||
placeholder="请输入API Key名称"
|
||||
required
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
<p class="mt-1 text-xs text-gray-500 dark:text-gray-400 sm:mt-2">
|
||||
用于识别此 API Key 的用途
|
||||
</p>
|
||||
@@ -320,56 +322,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- GPT-5 High推理级别周费用限制 -->
|
||||
<div>
|
||||
<label class="mb-2 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>GPT-5 High推理级别周费用限制 (美元)</label
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<div class="flex gap-2">
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = '5'"
|
||||
>
|
||||
$5
|
||||
</button>
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = '20'"
|
||||
>
|
||||
$20
|
||||
</button>
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = '50'"
|
||||
>
|
||||
$50
|
||||
</button>
|
||||
<button
|
||||
class="rounded bg-gray-100 px-2 py-1 text-xs font-medium hover:bg-gray-200 dark:bg-gray-700 dark:text-gray-300 dark:hover:bg-gray-600"
|
||||
type="button"
|
||||
@click="form.weeklyGPT5HighCostLimit = ''"
|
||||
>
|
||||
自定义
|
||||
</button>
|
||||
</div>
|
||||
<input
|
||||
v-model="form.weeklyGPT5HighCostLimit"
|
||||
class="form-input w-full border-gray-300 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 dark:text-gray-400">
|
||||
设置 GPT-5 High推理级别的周费用限制(周一到周日),0 或留空表示无限制
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label class="mb-3 block text-sm font-semibold text-gray-700 dark:text-gray-300"
|
||||
>并发限制</label
|
||||
@@ -762,7 +714,6 @@ const form = reactive({
|
||||
concurrencyLimit: '',
|
||||
dailyCostLimit: '',
|
||||
weeklyOpusCostLimit: '',
|
||||
weeklyGPT5HighCostLimit: '', // 新增:GPT-5 High推理级别周费用限制
|
||||
permissions: 'all',
|
||||
claudeAccountId: '',
|
||||
geminiAccountId: '',
|
||||
@@ -792,22 +743,7 @@ const removeRestrictedModel = (index) => {
|
||||
}
|
||||
|
||||
// 常用模型列表
|
||||
const commonModels = ref([
|
||||
// Claude 模型
|
||||
'claude-opus-4-20250514',
|
||||
'claude-opus-4-1-20250805',
|
||||
// OpenAI 模型
|
||||
'gpt-5',
|
||||
'gpt-5 minimal',
|
||||
'gpt-5 low',
|
||||
'gpt-5 medium',
|
||||
'gpt-5 high',
|
||||
'gpt-4o',
|
||||
'gpt-4o-mini',
|
||||
'o1',
|
||||
'o1-mini',
|
||||
'o1-preview'
|
||||
])
|
||||
const commonModels = ref(['claude-opus-4-20250514', 'claude-opus-4-1-20250805'])
|
||||
|
||||
// 可用的快捷模型(过滤掉已在限制列表中的)
|
||||
const availableQuickModels = computed(() => {
|
||||
@@ -894,11 +830,6 @@ const updateApiKey = async () => {
|
||||
form.weeklyOpusCostLimit !== '' && form.weeklyOpusCostLimit !== null
|
||||
? parseFloat(form.weeklyOpusCostLimit)
|
||||
: 0,
|
||||
// 新增:GPT-5 High推理级别周费用限制
|
||||
weeklyGPT5HighCostLimit:
|
||||
form.weeklyGPT5HighCostLimit !== '' && form.weeklyGPT5HighCostLimit !== null
|
||||
? parseFloat(form.weeklyGPT5HighCostLimit)
|
||||
: 0,
|
||||
permissions: form.permissions,
|
||||
tags: form.tags
|
||||
}
|
||||
@@ -1122,7 +1053,6 @@ onMounted(async () => {
|
||||
form.concurrencyLimit = props.apiKey.concurrencyLimit || ''
|
||||
form.dailyCostLimit = props.apiKey.dailyCostLimit || ''
|
||||
form.weeklyOpusCostLimit = props.apiKey.weeklyOpusCostLimit || ''
|
||||
form.weeklyGPT5HighCostLimit = props.apiKey.weeklyGPT5HighCostLimit || '' // 新增
|
||||
form.permissions = props.apiKey.permissions || 'all'
|
||||
// 处理 Claude 账号(区分 OAuth 和 Console)
|
||||
if (props.apiKey.claudeConsoleAccountId) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="w-full">
|
||||
<div class="relative h-8 w-full overflow-hidden rounded-md shadow-sm" :class="containerClass">
|
||||
<div class="relative h-8 w-full overflow-hidden rounded-lg shadow-sm" :class="containerClass">
|
||||
<!-- 背景层 -->
|
||||
<div class="absolute inset-0" :class="backgroundClass"></div>
|
||||
|
||||
@@ -12,17 +12,17 @@
|
||||
></div>
|
||||
|
||||
<!-- 文字层 - 使用双层文字技术确保可读性 -->
|
||||
<div class="relative z-10 flex h-full items-center justify-between px-2.5">
|
||||
<div class="relative z-10 flex h-full items-center justify-between px-3">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<i :class="['text-[10px]', iconClass]" />
|
||||
<span class="text-[10px] font-semibold" :class="labelTextClass">{{ label }}</span>
|
||||
<i :class="['text-xs', iconClass]" />
|
||||
<span class="text-xs font-semibold" :class="labelTextClass">{{ label }}</span>
|
||||
</div>
|
||||
<div class="mr-1 flex items-center gap-0.5">
|
||||
<span class="text-[10px] font-bold tabular-nums" :class="currentValueClass">
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span class="text-xs font-bold tabular-nums" :class="currentValueClass">
|
||||
${{ current.toFixed(2) }}
|
||||
</span>
|
||||
<span class="text-[9px] font-medium" :class="limitTextClass">
|
||||
/${{ limit.toFixed(2) }}
|
||||
<span class="text-xs font-medium" :class="limitTextClass">
|
||||
/ ${{ limit.toFixed(2) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -48,7 +48,7 @@ const props = defineProps({
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: (value) => ['daily', 'opus', 'window', 'gpt5-high'].includes(value)
|
||||
validator: (value) => ['daily', 'opus', 'window'].includes(value)
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
@@ -88,8 +88,6 @@ const backgroundClass = computed(() => {
|
||||
return 'bg-violet-50/50 dark:bg-violet-950/20'
|
||||
case 'window':
|
||||
return 'bg-sky-50/50 dark:bg-sky-950/20'
|
||||
case 'gpt5-high':
|
||||
return 'bg-orange-50/50 dark:bg-orange-950/20'
|
||||
default:
|
||||
return 'bg-gray-100/50 dark:bg-gray-800/30'
|
||||
}
|
||||
@@ -129,16 +127,6 @@ const progressBarClass = computed(() => {
|
||||
}
|
||||
}
|
||||
|
||||
if (props.type === 'gpt5-high') {
|
||||
if (p >= 90) {
|
||||
return 'bg-red-400 dark:bg-red-500'
|
||||
} else if (p >= 70) {
|
||||
return 'bg-amber-400 dark:bg-amber-500'
|
||||
} else {
|
||||
return 'bg-orange-400 dark:bg-orange-500'
|
||||
}
|
||||
}
|
||||
|
||||
return 'bg-gray-300 dark:bg-gray-400'
|
||||
})
|
||||
|
||||
@@ -163,9 +151,6 @@ const iconClass = computed(() => {
|
||||
case 'window':
|
||||
colorClass = 'text-blue-700 dark:text-blue-400'
|
||||
break
|
||||
case 'gpt5-high':
|
||||
colorClass = 'text-orange-700 dark:text-orange-400'
|
||||
break
|
||||
default:
|
||||
colorClass = 'text-gray-600 dark:text-gray-400'
|
||||
}
|
||||
@@ -182,9 +167,6 @@ const iconClass = computed(() => {
|
||||
case 'window':
|
||||
iconName = 'fas fa-clock'
|
||||
break
|
||||
case 'gpt5-high':
|
||||
iconName = 'fas fa-brain'
|
||||
break
|
||||
default:
|
||||
iconName = 'fas fa-infinity'
|
||||
}
|
||||
@@ -209,8 +191,6 @@ const labelTextClass = computed(() => {
|
||||
return 'text-purple-900 dark:text-purple-100'
|
||||
case 'window':
|
||||
return 'text-blue-900 dark:text-blue-100'
|
||||
case 'gpt5-high':
|
||||
return 'text-orange-900 dark:text-orange-100'
|
||||
default:
|
||||
return 'text-gray-900 dark:text-gray-100'
|
||||
}
|
||||
@@ -239,8 +219,6 @@ const currentValueClass = computed(() => {
|
||||
return 'text-purple-800 dark:text-purple-200'
|
||||
case 'window':
|
||||
return 'text-blue-800 dark:text-blue-200'
|
||||
case 'gpt5-high':
|
||||
return 'text-orange-800 dark:text-orange-200'
|
||||
default:
|
||||
return 'text-gray-900 dark:text-gray-100'
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user