2025-12-10 20:52:44 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<Dialog
|
|
|
|
|
|
:model-value="internalOpen"
|
2026-06-30 14:14:12 +08:00
|
|
|
|
:title="legacyT(isEditMode ? '编辑提供商' : '添加提供商')"
|
|
|
|
|
|
:description="legacyT(isEditMode ? '更新提供商配置。API 端点和密钥需在详情页面单独管理。' : '创建新的提供商配置。创建后可以为其添加 API 端点和密钥。')"
|
2025-12-10 20:52:44 +08:00
|
|
|
|
:icon="isEditMode ? SquarePen : Server"
|
2026-01-10 18:43:53 +08:00
|
|
|
|
size="xl"
|
2025-12-10 20:52:44 +08:00
|
|
|
|
@update:model-value="handleDialogUpdate"
|
|
|
|
|
|
>
|
2025-12-12 16:15:36 +08:00
|
|
|
|
<form
|
2026-01-10 18:43:53 +08:00
|
|
|
|
class="space-y-5"
|
2025-12-12 16:15:36 +08:00
|
|
|
|
@submit.prevent="handleSubmit"
|
|
|
|
|
|
>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<!-- 基本信息 -->
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<div class="space-y-3">
|
2025-12-12 16:15:36 +08:00
|
|
|
|
<h3 class="text-sm font-medium border-b pb-2">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('基本信息') }}
|
2025-12-12 16:15:36 +08:00
|
|
|
|
</h3>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
2026-02-05 02:23:41 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label for="name">{{ legacyT('名称 *') }}</Label>
|
2026-02-05 02:23:41 +08:00
|
|
|
|
<Input
|
|
|
|
|
|
id="name"
|
|
|
|
|
|
v-model="form.name"
|
2026-06-30 14:14:12 +08:00
|
|
|
|
:placeholder="legacyT('例如: OpenAI 主账号')"
|
2026-02-05 02:23:41 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label>{{ legacyT('提供商类型') }}</Label>
|
2026-02-04 10:24:25 +08:00
|
|
|
|
<Select
|
|
|
|
|
|
v-model="form.provider_type"
|
|
|
|
|
|
:disabled="isEditMode"
|
|
|
|
|
|
>
|
|
|
|
|
|
<SelectTrigger>
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<SelectValue :placeholder="legacyT('请选择')" />
|
2026-02-04 10:24:25 +08:00
|
|
|
|
</SelectTrigger>
|
|
|
|
|
|
<SelectContent>
|
2026-02-27 13:54:46 +08:00
|
|
|
|
<!-- 新建模式:允许自定义及各反代类型 -->
|
2026-02-05 16:34:28 +08:00
|
|
|
|
<template v-if="!isEditMode">
|
|
|
|
|
|
<SelectItem value="custom">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('自定义') }}
|
2026-02-05 16:34:28 +08:00
|
|
|
|
</SelectItem>
|
2026-03-01 23:32:48 +08:00
|
|
|
|
<SelectItem value="vertex_ai">
|
|
|
|
|
|
Vertex AI
|
|
|
|
|
|
</SelectItem>
|
2026-07-27 15:53:28 +08:00
|
|
|
|
<SelectItem value="claude_code">
|
|
|
|
|
|
{{ legacyT('Claude Code(实验性功能)') }}
|
2026-02-27 13:54:46 +08:00
|
|
|
|
</SelectItem>
|
2026-02-05 16:34:28 +08:00
|
|
|
|
<SelectItem value="codex">
|
|
|
|
|
|
Codex
|
|
|
|
|
|
</SelectItem>
|
2026-05-06 02:29:17 +08:00
|
|
|
|
<SelectItem value="chatgpt_web">
|
|
|
|
|
|
ChatGPT Web
|
|
|
|
|
|
</SelectItem>
|
2026-03-10 23:07:50 +08:00
|
|
|
|
<SelectItem value="gemini_cli">
|
|
|
|
|
|
Gemini CLI
|
|
|
|
|
|
</SelectItem>
|
2026-05-16 19:23:09 +08:00
|
|
|
|
<SelectItem value="grok">
|
|
|
|
|
|
Grok
|
|
|
|
|
|
</SelectItem>
|
2026-02-09 01:05:48 +08:00
|
|
|
|
<SelectItem value="kiro">
|
|
|
|
|
|
Kiro
|
|
|
|
|
|
</SelectItem>
|
2026-05-18 15:23:08 +08:00
|
|
|
|
<SelectItem value="windsurf">
|
|
|
|
|
|
Windsurf
|
|
|
|
|
|
</SelectItem>
|
2026-02-06 16:37:06 +08:00
|
|
|
|
<SelectItem value="antigravity">
|
|
|
|
|
|
Antigravity
|
|
|
|
|
|
</SelectItem>
|
2026-02-05 16:34:28 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
<!-- 编辑模式:显示所有类型(兼容已有数据) -->
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
<SelectItem value="custom">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('自定义') }}
|
2026-02-05 16:34:28 +08:00
|
|
|
|
</SelectItem>
|
2026-03-01 23:32:48 +08:00
|
|
|
|
<SelectItem value="vertex_ai">
|
|
|
|
|
|
Vertex AI
|
|
|
|
|
|
</SelectItem>
|
2026-02-05 16:34:28 +08:00
|
|
|
|
<SelectItem value="claude_code">
|
2026-07-27 15:53:28 +08:00
|
|
|
|
{{ legacyT('Claude Code(实验性功能)') }}
|
2026-02-05 16:34:28 +08:00
|
|
|
|
</SelectItem>
|
|
|
|
|
|
<SelectItem value="codex">
|
|
|
|
|
|
Codex
|
|
|
|
|
|
</SelectItem>
|
2026-05-06 02:29:17 +08:00
|
|
|
|
<SelectItem value="chatgpt_web">
|
|
|
|
|
|
ChatGPT Web
|
|
|
|
|
|
</SelectItem>
|
2026-02-05 16:34:28 +08:00
|
|
|
|
<SelectItem value="gemini_cli">
|
2026-03-10 23:07:50 +08:00
|
|
|
|
Gemini CLI
|
2026-02-05 16:34:28 +08:00
|
|
|
|
</SelectItem>
|
2026-05-16 19:23:09 +08:00
|
|
|
|
<SelectItem value="grok">
|
|
|
|
|
|
Grok
|
|
|
|
|
|
</SelectItem>
|
2026-02-09 01:05:48 +08:00
|
|
|
|
<SelectItem value="kiro">
|
|
|
|
|
|
Kiro
|
|
|
|
|
|
</SelectItem>
|
2026-05-18 15:23:08 +08:00
|
|
|
|
<SelectItem value="windsurf">
|
|
|
|
|
|
Windsurf
|
|
|
|
|
|
</SelectItem>
|
2026-02-05 16:34:28 +08:00
|
|
|
|
<SelectItem value="antigravity">
|
|
|
|
|
|
Antigravity
|
|
|
|
|
|
</SelectItem>
|
|
|
|
|
|
</template>
|
2026-02-04 10:24:25 +08:00
|
|
|
|
</SelectContent>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
<p
|
|
|
|
|
|
v-if="!isEditMode && form.provider_type !== 'custom'"
|
|
|
|
|
|
class="text-xs text-muted-foreground"
|
|
|
|
|
|
>
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('反代使用固定端点且不可修改') }}
|
2026-02-04 10:24:25 +08:00
|
|
|
|
</p>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</div>
|
2026-02-04 16:44:26 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label for="website">{{ legacyT('主站链接') }}</Label>
|
2026-02-04 16:44:26 +08:00
|
|
|
|
<Input
|
|
|
|
|
|
id="website"
|
|
|
|
|
|
v-model="form.website"
|
2026-06-30 14:14:12 +08:00
|
|
|
|
:placeholder="legacyT('https://example.com(可选)')"
|
2026-02-04 16:44:26 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-02-04 10:24:25 +08:00
|
|
|
|
</div>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<!-- 计费与限流 / 请求配置 -->
|
|
|
|
|
|
<div class="space-y-3">
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<h3 class="text-sm font-medium border-b pb-2">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('计费与限流') }}
|
2026-01-10 18:43:53 +08:00
|
|
|
|
</h3>
|
|
|
|
|
|
<h3 class="text-sm font-medium border-b pb-2">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('请求配置') }}
|
2026-01-10 18:43:53 +08:00
|
|
|
|
</h3>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label>{{ legacyT('计费类型') }}</Label>
|
2025-12-12 16:15:36 +08:00
|
|
|
|
<Select
|
|
|
|
|
|
v-model="form.billing_type"
|
|
|
|
|
|
>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<SelectTrigger>
|
|
|
|
|
|
<SelectValue />
|
|
|
|
|
|
</SelectTrigger>
|
|
|
|
|
|
<SelectContent>
|
2026-01-12 23:28:37 +08:00
|
|
|
|
<SelectItem value="monthly_quota">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('月卡额度') }}
|
2026-01-12 23:28:37 +08:00
|
|
|
|
</SelectItem>
|
|
|
|
|
|
<SelectItem value="pay_as_you_go">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('按量付费') }}
|
2026-01-12 23:28:37 +08:00
|
|
|
|
</SelectItem>
|
|
|
|
|
|
<SelectItem value="free_tier">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('免费套餐') }}
|
2026-01-12 23:28:37 +08:00
|
|
|
|
</SelectItem>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</SelectContent>
|
|
|
|
|
|
</Select>
|
|
|
|
|
|
</div>
|
2026-01-16 13:08:39 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label>{{ legacyT('最大重试次数') }}</Label>
|
2026-01-16 13:08:39 +08:00
|
|
|
|
<Input
|
|
|
|
|
|
:model-value="form.max_retries ?? ''"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
min="0"
|
2026-02-27 19:10:47 +08:00
|
|
|
|
max="999"
|
2026-06-30 14:14:12 +08:00
|
|
|
|
:placeholder="legacyT('默认 2')"
|
2026-01-16 13:08:39 +08:00
|
|
|
|
@update:model-value="(v) => form.max_retries = parseNumberInput(v)"
|
|
|
|
|
|
/>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-01-28 00:04:47 +08:00
|
|
|
|
<!-- 超时配置 -->
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-4">
|
|
|
|
|
|
<div class="space-y-1.5">
|
|
|
|
|
|
<Label>
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('流式首字节超时') }}
|
|
|
|
|
|
<span class="text-xs text-muted-foreground">{{ legacyT('(秒)') }}</span>
|
2026-01-28 00:04:47 +08:00
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
:model-value="form.stream_first_byte_timeout ?? ''"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
min="1"
|
|
|
|
|
|
max="300"
|
|
|
|
|
|
step="1"
|
|
|
|
|
|
placeholder="30"
|
|
|
|
|
|
@update:model-value="(v) => form.stream_first_byte_timeout = parseNumberInput(v)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="space-y-1.5">
|
|
|
|
|
|
<Label>
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('非流式请求超时') }}
|
|
|
|
|
|
<span class="text-xs text-muted-foreground">{{ legacyT('(秒)') }}</span>
|
2026-01-28 00:04:47 +08:00
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
:model-value="form.request_timeout ?? ''"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
min="1"
|
2026-07-12 03:04:15 +08:00
|
|
|
|
max="1200"
|
2026-01-28 00:04:47 +08:00
|
|
|
|
step="1"
|
|
|
|
|
|
placeholder="300"
|
|
|
|
|
|
@update:model-value="(v) => form.request_timeout = parseNumberInput(v)"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-31 13:35:08 +08:00
|
|
|
|
<!-- 提供商内转移限制 -->
|
|
|
|
|
|
<div class="grid grid-cols-2 gap-2 sm:gap-4">
|
2026-07-26 15:06:56 +08:00
|
|
|
|
<div class="min-w-0 space-y-1.5">
|
|
|
|
|
|
<Label
|
|
|
|
|
|
for="max-transfer-count"
|
|
|
|
|
|
class="whitespace-nowrap text-xs sm:text-sm"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ legacyT('最大转移次数') }}
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="max-transfer-count"
|
2026-07-30 01:03:05 +08:00
|
|
|
|
:model-value="form.max_transfer_count === 0 ? '' : form.max_transfer_count"
|
2026-07-26 15:06:56 +08:00
|
|
|
|
type="number"
|
|
|
|
|
|
min="0"
|
|
|
|
|
|
step="1"
|
2026-07-30 01:03:05 +08:00
|
|
|
|
:placeholder="legacyT('0 (不限制)')"
|
2026-07-26 15:06:56 +08:00
|
|
|
|
@update:model-value="(v) => form.max_transfer_count = parseNumberInput(v, { min: 0 }) ?? 0"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="min-w-0 space-y-1.5">
|
|
|
|
|
|
<Label
|
|
|
|
|
|
for="max-transfer-timeout-seconds"
|
|
|
|
|
|
class="whitespace-nowrap text-xs sm:text-sm"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ legacyT('最大转移超时') }}
|
|
|
|
|
|
<span class="text-xs text-muted-foreground">{{ legacyT('(秒)') }}</span>
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<Input
|
|
|
|
|
|
id="max-transfer-timeout-seconds"
|
2026-07-30 01:03:05 +08:00
|
|
|
|
:model-value="form.max_transfer_timeout_seconds === 0 ? '' : form.max_transfer_timeout_seconds"
|
2026-07-26 15:06:56 +08:00
|
|
|
|
type="number"
|
|
|
|
|
|
min="0"
|
|
|
|
|
|
step="1"
|
2026-07-30 01:03:05 +08:00
|
|
|
|
:placeholder="legacyT('0 (不限制)')"
|
2026-07-26 15:06:56 +08:00
|
|
|
|
@update:model-value="(v) => form.max_transfer_timeout_seconds = parseNumberInput(v, { min: 0 }) ?? 0"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<!-- 月卡配置 -->
|
2025-12-12 16:15:36 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-if="form.billing_type === 'monthly_quota'"
|
|
|
|
|
|
class="grid grid-cols-2 gap-4 p-3 border rounded-lg bg-muted/50"
|
|
|
|
|
|
>
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label class="text-xs">{{ legacyT('周期额度 (USD)') }}</Label>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<Input
|
|
|
|
|
|
:model-value="form.monthly_quota_usd ?? ''"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
step="0.01"
|
|
|
|
|
|
min="0"
|
|
|
|
|
|
@update:model-value="(v) => form.monthly_quota_usd = parseNumberInput(v, { allowFloat: true })"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label class="text-xs">{{ legacyT('重置周期 (天)') }}</Label>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<Input
|
|
|
|
|
|
:model-value="form.quota_reset_day ?? ''"
|
|
|
|
|
|
type="number"
|
|
|
|
|
|
min="1"
|
|
|
|
|
|
max="365"
|
|
|
|
|
|
@update:model-value="(v) => form.quota_reset_day = parseNumberInput(v) ?? 30"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2025-12-10 20:52:44 +08:00
|
|
|
|
<Label class="text-xs">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('周期开始时间') }} <span class="text-red-500">*</span>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</Label>
|
2025-12-12 16:15:36 +08:00
|
|
|
|
<Input
|
|
|
|
|
|
v-model="form.quota_last_reset_at"
|
|
|
|
|
|
type="datetime-local"
|
|
|
|
|
|
/>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</div>
|
2026-01-10 18:43:53 +08:00
|
|
|
|
<div class="space-y-1.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<Label class="text-xs">{{ legacyT('过期时间') }}</Label>
|
2025-12-12 16:15:36 +08:00
|
|
|
|
<Input
|
|
|
|
|
|
v-model="form.quota_expires_at"
|
|
|
|
|
|
type="datetime-local"
|
|
|
|
|
|
/>
|
2026-01-10 18:43:53 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-27 13:54:46 +08:00
|
|
|
|
<!-- 功能开关 -->
|
2026-01-28 15:53:33 +08:00
|
|
|
|
<div class="space-y-3">
|
|
|
|
|
|
<h3 class="text-sm font-medium border-b pb-2">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('功能开关') }}
|
2026-01-28 15:53:33 +08:00
|
|
|
|
</h3>
|
2026-02-27 13:54:46 +08:00
|
|
|
|
|
2026-01-28 15:53:33 +08:00
|
|
|
|
<div class="flex items-center justify-between p-3 border rounded-lg bg-muted/50">
|
|
|
|
|
|
<div class="space-y-0.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<span class="text-sm font-medium">{{ legacyT('格式转换保持优先级') }}</span>
|
2026-01-28 15:53:33 +08:00
|
|
|
|
<p class="text-xs text-muted-foreground">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('跨格式请求时保持原优先级排名,不降级到格式匹配的提供商之后') }}
|
2026-01-28 15:53:33 +08:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Switch
|
|
|
|
|
|
:model-value="form.keep_priority_on_conversion"
|
|
|
|
|
|
@update:model-value="(v: boolean) => form.keep_priority_on_conversion = v"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-02-27 13:54:46 +08:00
|
|
|
|
<div class="flex items-center justify-between p-3 border rounded-lg bg-muted/50">
|
|
|
|
|
|
<div class="space-y-0.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<span class="text-sm font-medium">{{ legacyT('号池调度模式') }}</span>
|
2026-02-27 13:54:46 +08:00
|
|
|
|
<p class="text-xs text-muted-foreground">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('启用后该提供商的密钥将由号池统一调度') }}
|
2026-02-27 13:54:46 +08:00
|
|
|
|
</p>
|
2026-01-10 18:43:53 +08:00
|
|
|
|
</div>
|
2026-02-27 13:54:46 +08:00
|
|
|
|
<Switch
|
|
|
|
|
|
:model-value="form.pool_mode_enabled"
|
|
|
|
|
|
@update:model-value="(v: boolean) => form.pool_mode_enabled = v"
|
2026-02-07 14:30:46 +08:00
|
|
|
|
/>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</div>
|
2026-05-13 18:25:13 +08:00
|
|
|
|
|
2026-05-19 10:17:37 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-if="form.provider_type === 'kiro'"
|
|
|
|
|
|
class="flex items-center justify-between p-3 border rounded-lg bg-muted/50"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="space-y-0.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<span class="text-sm font-medium">{{ legacyT('模拟缓存模式') }}</span>
|
2026-05-19 10:17:37 +08:00
|
|
|
|
<p class="text-xs text-muted-foreground leading-relaxed">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('启用后仅对 Kiro 请求模拟 prompt cache 读写计量。') }}
|
2026-05-19 10:17:37 +08:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Switch
|
|
|
|
|
|
:model-value="form.kiro_simulated_cache_enabled"
|
|
|
|
|
|
@update:model-value="(v: boolean) => form.kiro_simulated_cache_enabled = v"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-08-13 09:57:17 +08:00
|
|
|
|
<div
|
|
|
|
|
|
v-if="form.provider_type === 'codex'"
|
|
|
|
|
|
class="flex items-center justify-between gap-4 p-3 border rounded-lg bg-muted/50"
|
|
|
|
|
|
data-testid="codex-fingerprint-convergence-setting"
|
|
|
|
|
|
>
|
|
|
|
|
|
<div class="space-y-0.5">
|
|
|
|
|
|
<Label
|
|
|
|
|
|
for="codex-fingerprint-convergence"
|
|
|
|
|
|
class="text-sm font-medium"
|
|
|
|
|
|
>
|
|
|
|
|
|
{{ legacyT('Codex OAuth 指纹收敛') }}
|
|
|
|
|
|
</Label>
|
|
|
|
|
|
<p class="text-xs text-muted-foreground leading-relaxed">
|
|
|
|
|
|
{{ legacyT('统一同一 OAuth 账号的设备与会话标识;关闭时保持现有透传行为。') }}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Switch
|
|
|
|
|
|
id="codex-fingerprint-convergence"
|
|
|
|
|
|
:model-value="form.codex_fingerprint_convergence_enabled"
|
|
|
|
|
|
:aria-label="legacyT('Codex OAuth 指纹收敛')"
|
|
|
|
|
|
@update:model-value="(v: boolean) => form.codex_fingerprint_convergence_enabled = v"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-07-31 10:59:24 +08:00
|
|
|
|
<div class="flex items-center justify-between p-3 border rounded-lg bg-muted/50">
|
|
|
|
|
|
<div class="space-y-0.5">
|
|
|
|
|
|
<span class="text-sm font-medium">{{ legacyT('Responses WebSocket 模式') }}</span>
|
|
|
|
|
|
<p class="text-xs text-muted-foreground leading-relaxed">
|
|
|
|
|
|
{{ legacyT('允许此提供商处理标准 Responses API WebSocket 请求。仅在已验证兼容性后启用。') }}
|
|
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<Switch
|
|
|
|
|
|
:model-value="form.responses_websocket_enabled"
|
|
|
|
|
|
@update:model-value="(v: boolean) => form.responses_websocket_enabled = v"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2026-05-14 11:14:20 +08:00
|
|
|
|
<div class="flex items-center justify-between gap-4 p-3 border rounded-lg bg-muted/50">
|
2026-05-13 18:25:13 +08:00
|
|
|
|
<div class="space-y-0.5">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
<span class="text-sm font-medium">{{ legacyT('敏感信息保护') }}</span>
|
2026-05-13 18:25:13 +08:00
|
|
|
|
<p class="text-xs text-muted-foreground leading-relaxed">
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('请前往模块管理-敏感信息保护中配置详细规则。') }}
|
2026-05-13 18:25:13 +08:00
|
|
|
|
</p>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</form>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<Button
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
variant="outline"
|
|
|
|
|
|
:disabled="loading"
|
2025-12-12 16:15:36 +08:00
|
|
|
|
@click="handleCancel"
|
2025-12-10 20:52:44 +08:00
|
|
|
|
>
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ legacyT('取消') }}
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</Button>
|
|
|
|
|
|
<Button
|
2026-01-10 18:43:53 +08:00
|
|
|
|
:disabled="loading || !form.name"
|
2025-12-12 16:15:36 +08:00
|
|
|
|
@click="handleSubmit"
|
2025-12-10 20:52:44 +08:00
|
|
|
|
>
|
2026-06-30 14:14:12 +08:00
|
|
|
|
{{ submitLabel }}
|
2025-12-10 20:52:44 +08:00
|
|
|
|
</Button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</Dialog>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
2026-02-27 13:54:46 +08:00
|
|
|
|
import { ref, computed, watch } from 'vue'
|
2025-12-12 16:15:36 +08:00
|
|
|
|
import {
|
|
|
|
|
|
Dialog,
|
|
|
|
|
|
Button,
|
|
|
|
|
|
Input,
|
|
|
|
|
|
Label,
|
|
|
|
|
|
Select,
|
|
|
|
|
|
SelectTrigger,
|
|
|
|
|
|
SelectValue,
|
|
|
|
|
|
SelectContent,
|
|
|
|
|
|
SelectItem,
|
2026-01-10 18:43:53 +08:00
|
|
|
|
Switch,
|
2025-12-12 16:15:36 +08:00
|
|
|
|
} from '@/components/ui'
|
2025-12-10 20:52:44 +08:00
|
|
|
|
import { Server, SquarePen } from 'lucide-vue-next'
|
|
|
|
|
|
import { useToast } from '@/composables/useToast'
|
|
|
|
|
|
import { useFormDialog } from '@/composables/useFormDialog'
|
2026-06-30 14:14:12 +08:00
|
|
|
|
import { useI18n } from '@/i18n'
|
2026-04-13 14:01:22 +08:00
|
|
|
|
import {
|
|
|
|
|
|
createProvider,
|
|
|
|
|
|
normalizePoolAdvancedConfig,
|
|
|
|
|
|
updateProvider,
|
2026-05-18 15:23:08 +08:00
|
|
|
|
type ProviderType,
|
2026-04-13 14:01:22 +08:00
|
|
|
|
type ProviderWithEndpointsSummary,
|
|
|
|
|
|
} from '@/api/endpoints'
|
2025-12-10 20:52:44 +08:00
|
|
|
|
import { parseApiError } from '@/utils/errorParser'
|
|
|
|
|
|
import { parseNumberInput } from '@/utils/form'
|
2026-04-24 13:29:05 +08:00
|
|
|
|
import { dateTimeLocalToRfc3339, formatDateTimeLocalInput } from '@/utils/date'
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{
|
|
|
|
|
|
modelValue: boolean
|
|
|
|
|
|
provider?: ProviderWithEndpointsSummary | null // 编辑模式时传入
|
2026-02-06 16:37:06 +08:00
|
|
|
|
maxPriority?: number // 当前已有的最大优先级值
|
2025-12-10 20:52:44 +08:00
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
|
'update:modelValue': [value: boolean]
|
2025-12-12 20:22:03 +08:00
|
|
|
|
'providerCreated': []
|
2026-03-01 11:57:09 +08:00
|
|
|
|
'providerUpdated': [provider: ProviderWithEndpointsSummary]
|
2025-12-10 20:52:44 +08:00
|
|
|
|
}>()
|
|
|
|
|
|
|
|
|
|
|
|
const { success, error: showError } = useToast()
|
2026-06-30 14:14:12 +08:00
|
|
|
|
const { legacyT } = useI18n()
|
2025-12-10 20:52:44 +08:00
|
|
|
|
const loading = ref(false)
|
|
|
|
|
|
|
|
|
|
|
|
// 内部状态
|
|
|
|
|
|
const internalOpen = computed(() => props.modelValue)
|
|
|
|
|
|
|
2026-02-06 16:37:06 +08:00
|
|
|
|
// 计算新建时的默认优先级
|
|
|
|
|
|
const defaultPriority = computed(() => {
|
|
|
|
|
|
if (props.maxPriority != null) {
|
|
|
|
|
|
return Math.min(props.maxPriority + 10, 10000)
|
|
|
|
|
|
}
|
|
|
|
|
|
return 100
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-06-30 14:14:12 +08:00
|
|
|
|
const submitLabel = computed(() => {
|
|
|
|
|
|
if (loading.value) {
|
|
|
|
|
|
return legacyT(isEditMode.value ? '保存中...' : '创建中...')
|
|
|
|
|
|
}
|
|
|
|
|
|
return legacyT(isEditMode.value ? '保存' : '创建')
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-10 20:52:44 +08:00
|
|
|
|
// 表单数据
|
|
|
|
|
|
const form = ref({
|
|
|
|
|
|
name: '',
|
2026-05-18 15:23:08 +08:00
|
|
|
|
provider_type: 'custom' as ProviderType,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
description: '',
|
|
|
|
|
|
website: '',
|
|
|
|
|
|
// 计费配置
|
|
|
|
|
|
billing_type: 'pay_as_you_go' as 'monthly_quota' | 'pay_as_you_go' | 'free_tier',
|
|
|
|
|
|
monthly_quota_usd: undefined as number | undefined,
|
|
|
|
|
|
quota_reset_day: 30,
|
|
|
|
|
|
quota_last_reset_at: '', // 周期开始时间
|
|
|
|
|
|
quota_expires_at: '',
|
2026-02-06 16:37:06 +08:00
|
|
|
|
provider_priority: 100,
|
2026-01-28 15:53:33 +08:00
|
|
|
|
keep_priority_on_conversion: false, // 格式转换时是否保持优先级
|
2025-12-10 20:52:44 +08:00
|
|
|
|
// 状态配置
|
|
|
|
|
|
is_active: true,
|
|
|
|
|
|
rate_limit: undefined as number | undefined,
|
|
|
|
|
|
concurrent_limit: undefined as number | undefined,
|
2026-01-10 18:43:53 +08:00
|
|
|
|
// 请求配置
|
|
|
|
|
|
max_retries: undefined as number | undefined,
|
2026-07-26 15:06:56 +08:00
|
|
|
|
max_transfer_count: 0,
|
|
|
|
|
|
max_transfer_timeout_seconds: 0,
|
2026-01-28 00:04:47 +08:00
|
|
|
|
// 超时配置(秒)
|
|
|
|
|
|
stream_first_byte_timeout: undefined as number | undefined,
|
|
|
|
|
|
request_timeout: undefined as number | undefined,
|
2026-02-27 13:54:46 +08:00
|
|
|
|
// 号池模式
|
|
|
|
|
|
pool_mode_enabled: false,
|
2026-08-13 09:57:17 +08:00
|
|
|
|
// Codex 专属配置
|
|
|
|
|
|
codex_fingerprint_convergence_enabled: false,
|
2026-05-19 10:17:37 +08:00
|
|
|
|
// Kiro 专属配置
|
|
|
|
|
|
kiro_simulated_cache_enabled: false,
|
2026-07-31 10:59:24 +08:00
|
|
|
|
// Responses WebSocket 配置
|
|
|
|
|
|
responses_websocket_enabled: false,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
// 重置表单
|
|
|
|
|
|
function resetForm() {
|
|
|
|
|
|
form.value = {
|
|
|
|
|
|
name: '',
|
2026-02-04 10:24:25 +08:00
|
|
|
|
provider_type: 'custom',
|
2025-12-10 20:52:44 +08:00
|
|
|
|
description: '',
|
|
|
|
|
|
website: '',
|
|
|
|
|
|
billing_type: 'pay_as_you_go',
|
|
|
|
|
|
monthly_quota_usd: undefined,
|
|
|
|
|
|
quota_reset_day: 30,
|
|
|
|
|
|
quota_last_reset_at: '',
|
|
|
|
|
|
quota_expires_at: '',
|
2026-02-06 16:37:06 +08:00
|
|
|
|
provider_priority: defaultPriority.value,
|
2026-01-28 15:53:33 +08:00
|
|
|
|
keep_priority_on_conversion: false,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
is_active: true,
|
|
|
|
|
|
rate_limit: undefined,
|
|
|
|
|
|
concurrent_limit: undefined,
|
2026-01-10 18:43:53 +08:00
|
|
|
|
// 请求配置
|
|
|
|
|
|
max_retries: undefined,
|
2026-07-26 15:06:56 +08:00
|
|
|
|
max_transfer_count: 0,
|
|
|
|
|
|
max_transfer_timeout_seconds: 0,
|
2026-01-28 00:04:47 +08:00
|
|
|
|
// 超时配置
|
|
|
|
|
|
stream_first_byte_timeout: undefined,
|
|
|
|
|
|
request_timeout: undefined,
|
2026-02-27 13:54:46 +08:00
|
|
|
|
// 号池模式
|
|
|
|
|
|
pool_mode_enabled: false,
|
2026-08-13 09:57:17 +08:00
|
|
|
|
// Codex 专属配置
|
|
|
|
|
|
codex_fingerprint_convergence_enabled: false,
|
2026-05-19 10:17:37 +08:00
|
|
|
|
// Kiro 专属配置
|
|
|
|
|
|
kiro_simulated_cache_enabled: false,
|
2026-07-31 10:59:24 +08:00
|
|
|
|
// Responses WebSocket 配置
|
|
|
|
|
|
responses_websocket_enabled: false,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 加载提供商数据(编辑模式)
|
|
|
|
|
|
function loadProviderData() {
|
|
|
|
|
|
if (!props.provider) return
|
2026-04-13 14:01:22 +08:00
|
|
|
|
const poolAdvanced = normalizePoolAdvancedConfig(props.provider.pool_advanced)
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
|
|
|
|
|
form.value = {
|
|
|
|
|
|
name: props.provider.name,
|
2026-02-04 10:24:25 +08:00
|
|
|
|
provider_type: props.provider.provider_type || 'custom',
|
2025-12-10 20:52:44 +08:00
|
|
|
|
description: props.provider.description || '',
|
|
|
|
|
|
website: props.provider.website || '',
|
|
|
|
|
|
billing_type: (props.provider.billing_type as 'monthly_quota' | 'pay_as_you_go' | 'free_tier') || 'pay_as_you_go',
|
|
|
|
|
|
monthly_quota_usd: props.provider.monthly_quota_usd || undefined,
|
|
|
|
|
|
quota_reset_day: props.provider.quota_reset_day || 30,
|
2026-04-24 13:29:05 +08:00
|
|
|
|
quota_last_reset_at: formatDateTimeLocalInput(props.provider.quota_last_reset_at),
|
|
|
|
|
|
quota_expires_at: formatDateTimeLocalInput(props.provider.quota_expires_at),
|
2025-12-10 20:52:44 +08:00
|
|
|
|
provider_priority: props.provider.provider_priority || 999,
|
2026-01-28 15:53:33 +08:00
|
|
|
|
keep_priority_on_conversion: props.provider.keep_priority_on_conversion ?? false,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
is_active: props.provider.is_active,
|
|
|
|
|
|
rate_limit: undefined,
|
|
|
|
|
|
concurrent_limit: undefined,
|
2026-01-10 18:43:53 +08:00
|
|
|
|
// 请求配置
|
|
|
|
|
|
max_retries: props.provider.max_retries ?? undefined,
|
2026-07-26 15:06:56 +08:00
|
|
|
|
max_transfer_count: props.provider.max_transfer_count ?? 0,
|
|
|
|
|
|
max_transfer_timeout_seconds: props.provider.max_transfer_timeout_seconds ?? 0,
|
2026-01-28 00:04:47 +08:00
|
|
|
|
// 超时配置
|
|
|
|
|
|
stream_first_byte_timeout: props.provider.stream_first_byte_timeout ?? undefined,
|
|
|
|
|
|
request_timeout: props.provider.request_timeout ?? undefined,
|
2026-02-27 13:54:46 +08:00
|
|
|
|
// 号池模式
|
2026-04-13 14:01:22 +08:00
|
|
|
|
pool_mode_enabled: poolAdvanced !== null,
|
2026-08-13 09:57:17 +08:00
|
|
|
|
// Codex 专属配置
|
|
|
|
|
|
codex_fingerprint_convergence_enabled: props.provider.codex_fingerprint_convergence_enabled ?? false,
|
2026-05-19 10:17:37 +08:00
|
|
|
|
// Kiro 专属配置
|
|
|
|
|
|
kiro_simulated_cache_enabled: props.provider.kiro_simulated_cache_enabled ?? false,
|
2026-07-31 10:59:24 +08:00
|
|
|
|
// Responses WebSocket 配置
|
|
|
|
|
|
responses_websocket_enabled: props.provider.responses_websocket_enabled ?? false,
|
2025-12-10 20:52:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 useFormDialog 统一处理对话框逻辑
|
|
|
|
|
|
const { isEditMode, handleDialogUpdate, handleCancel } = useFormDialog({
|
|
|
|
|
|
isOpen: () => props.modelValue,
|
|
|
|
|
|
entity: () => props.provider,
|
|
|
|
|
|
isLoading: loading,
|
|
|
|
|
|
onClose: () => emit('update:modelValue', false),
|
|
|
|
|
|
loadData: loadProviderData,
|
|
|
|
|
|
resetForm,
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-03-01 23:32:48 +08:00
|
|
|
|
// 新建模式下切换 provider_type 时不自动开启号池模式
|
|
|
|
|
|
watch(() => form.value.provider_type, () => {
|
2026-02-27 13:54:46 +08:00
|
|
|
|
if (!isEditMode.value) {
|
2026-03-01 23:32:48 +08:00
|
|
|
|
form.value.pool_mode_enabled = false
|
2026-02-27 13:54:46 +08:00
|
|
|
|
}
|
2026-05-19 10:17:37 +08:00
|
|
|
|
if (form.value.provider_type !== 'kiro') {
|
|
|
|
|
|
form.value.kiro_simulated_cache_enabled = false
|
|
|
|
|
|
}
|
2026-08-13 09:57:17 +08:00
|
|
|
|
if (form.value.provider_type !== 'codex') {
|
|
|
|
|
|
form.value.codex_fingerprint_convergence_enabled = false
|
|
|
|
|
|
}
|
2026-02-27 13:54:46 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
2025-12-10 20:52:44 +08:00
|
|
|
|
// 提交表单
|
|
|
|
|
|
const handleSubmit = async () => {
|
|
|
|
|
|
// 月卡类型必须设置周期开始时间
|
|
|
|
|
|
if (form.value.billing_type === 'monthly_quota' && !form.value.quota_last_reset_at) {
|
2026-06-30 14:14:12 +08:00
|
|
|
|
showError(legacyT('月卡类型必须设置周期开始时间'), legacyT('验证失败'))
|
2025-12-10 20:52:44 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-24 13:29:05 +08:00
|
|
|
|
const quotaLastResetAt = dateTimeLocalToRfc3339(form.value.quota_last_reset_at)
|
|
|
|
|
|
if (form.value.billing_type === 'monthly_quota' && !quotaLastResetAt) {
|
2026-06-30 14:14:12 +08:00
|
|
|
|
showError(legacyT('周期开始时间必须是合法时间'), legacyT('验证失败'))
|
2026-04-24 13:29:05 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const quotaExpiresAt = dateTimeLocalToRfc3339(form.value.quota_expires_at)
|
|
|
|
|
|
if (form.value.quota_expires_at && !quotaExpiresAt) {
|
2026-06-30 14:14:12 +08:00
|
|
|
|
showError(legacyT('过期时间必须是合法时间'), legacyT('验证失败'))
|
2026-04-24 13:29:05 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-10 20:52:44 +08:00
|
|
|
|
loading.value = true
|
|
|
|
|
|
try {
|
2026-04-13 14:01:22 +08:00
|
|
|
|
const currentPoolAdvanced = normalizePoolAdvancedConfig(props.provider?.pool_advanced)
|
2026-05-14 11:14:20 +08:00
|
|
|
|
const basePayload = {
|
2026-01-10 18:43:53 +08:00
|
|
|
|
name: form.value.name,
|
2026-02-04 10:24:25 +08:00
|
|
|
|
provider_type: form.value.provider_type,
|
2026-01-10 18:43:53 +08:00
|
|
|
|
description: form.value.description || undefined,
|
|
|
|
|
|
website: form.value.website || undefined,
|
|
|
|
|
|
billing_type: form.value.billing_type,
|
|
|
|
|
|
monthly_quota_usd: form.value.monthly_quota_usd,
|
|
|
|
|
|
quota_reset_day: form.value.quota_reset_day,
|
2026-04-24 13:29:05 +08:00
|
|
|
|
quota_last_reset_at: quotaLastResetAt,
|
|
|
|
|
|
quota_expires_at: quotaExpiresAt,
|
2026-01-28 15:53:33 +08:00
|
|
|
|
keep_priority_on_conversion: form.value.keep_priority_on_conversion,
|
2026-07-31 10:59:24 +08:00
|
|
|
|
responses_websocket_enabled: form.value.responses_websocket_enabled,
|
2026-01-10 18:43:53 +08:00
|
|
|
|
is_active: form.value.is_active,
|
|
|
|
|
|
// 请求配置
|
|
|
|
|
|
max_retries: form.value.max_retries ?? undefined,
|
2026-07-26 15:06:56 +08:00
|
|
|
|
max_transfer_count: form.value.max_transfer_count,
|
|
|
|
|
|
max_transfer_timeout_seconds: form.value.max_transfer_timeout_seconds,
|
2026-01-28 00:04:47 +08:00
|
|
|
|
// 超时配置(null 表示清除,使用全局配置)
|
|
|
|
|
|
stream_first_byte_timeout: form.value.stream_first_byte_timeout ?? null,
|
|
|
|
|
|
request_timeout: form.value.request_timeout ?? null,
|
2026-02-27 13:54:46 +08:00
|
|
|
|
pool_advanced: form.value.pool_mode_enabled
|
2026-04-13 14:01:22 +08:00
|
|
|
|
? (currentPoolAdvanced ?? {})
|
2026-02-27 13:54:46 +08:00
|
|
|
|
: null,
|
2026-08-13 09:57:17 +08:00
|
|
|
|
...(form.value.provider_type === 'codex'
|
|
|
|
|
|
? {
|
|
|
|
|
|
codex_fingerprint_convergence_enabled: form.value.codex_fingerprint_convergence_enabled,
|
|
|
|
|
|
}
|
|
|
|
|
|
: {}),
|
2026-05-19 10:17:37 +08:00
|
|
|
|
...(form.value.provider_type === 'kiro'
|
|
|
|
|
|
? {
|
|
|
|
|
|
config: {
|
|
|
|
|
|
kiro: {
|
|
|
|
|
|
simulated_cache_enabled: form.value.kiro_simulated_cache_enabled,
|
|
|
|
|
|
},
|
|
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
: {}),
|
2026-05-14 11:14:20 +08:00
|
|
|
|
}
|
2025-12-10 20:52:44 +08:00
|
|
|
|
|
|
|
|
|
|
if (isEditMode.value && props.provider) {
|
|
|
|
|
|
// 更新提供商
|
2026-03-06 13:13:01 +08:00
|
|
|
|
const updated = await updateProvider(props.provider.id, {
|
|
|
|
|
|
...basePayload,
|
|
|
|
|
|
provider_priority: form.value.provider_priority,
|
|
|
|
|
|
})
|
2026-06-30 14:14:12 +08:00
|
|
|
|
success(legacyT('提供商更新成功'))
|
2026-03-01 11:57:09 +08:00
|
|
|
|
emit('providerUpdated', updated)
|
2025-12-10 20:52:44 +08:00
|
|
|
|
} else {
|
2026-03-06 13:13:01 +08:00
|
|
|
|
// 创建提供商(优先级由后端自动置顶)
|
|
|
|
|
|
await createProvider(basePayload)
|
2026-06-30 14:14:12 +08:00
|
|
|
|
success(legacyT('提供商已创建,请继续添加端点和密钥,或在优先级管理中调整顺序'), legacyT('创建成功'))
|
2025-12-12 20:22:03 +08:00
|
|
|
|
emit('providerCreated')
|
2025-12-10 20:52:44 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emit('update:modelValue', false)
|
2026-02-22 00:43:41 +08:00
|
|
|
|
} catch (error: unknown) {
|
2025-12-10 20:52:44 +08:00
|
|
|
|
const action = isEditMode.value ? '更新' : '创建'
|
2026-06-30 14:14:12 +08:00
|
|
|
|
showError(parseApiError(error, legacyT(`${action}提供商失败`)), legacyT(`${action}失败`))
|
2025-12-10 20:52:44 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
loading.value = false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|