|
|
@ -4,21 +4,21 @@ |
|
|
|
|
|
|
|
|
<!-- F4: 健康状态汇总卡片 --> |
|
|
<!-- F4: 健康状态汇总卡片 --> |
|
|
<div v-if="healthSummary" class="health-summary" style="display:flex;gap:12px;margin:12px 0;flex-wrap:wrap;"> |
|
|
<div v-if="healthSummary" class="health-summary" style="display:flex;gap:12px;margin:12px 0;flex-wrap:wrap;"> |
|
|
<div style="padding:10px 16px;background:#f0fdf4;border:1px solid #bbf7d0;border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:#16a34a;">{{ healthSummary.online }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:#166534;">🟢 在线</div> |
|
|
|
|
|
|
|
|
<div style="padding:10px 16px;background:var(--color-success-bg);border:1px solid var(--color-success-border);border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:var(--color-success-text);">{{ healthSummary.online }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:var(--color-success-text-strong);">🟢 在线</div> |
|
|
</div> |
|
|
</div> |
|
|
<div style="padding:10px 16px;background:#fef2f2;border:1px solid #fecaca;border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:#dc2626;">{{ healthSummary.offline }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:#991b1b;">🔴 离线</div> |
|
|
|
|
|
|
|
|
<div style="padding:10px 16px;background:var(--color-error-bg);border:1px solid var(--color-error-border);border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:var(--color-error-text);">{{ healthSummary.offline }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:var(--color-error-text-strong);">🔴 离线</div> |
|
|
</div> |
|
|
</div> |
|
|
<div v-if="healthSummary.unknown" style="padding:10px 16px;background:#fffbeb;border:1px solid #fde68a;border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:#d97706;">{{ healthSummary.unknown }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:#92400e;">🟡 未检测</div> |
|
|
|
|
|
|
|
|
<div v-if="healthSummary.unknown" style="padding:10px 16px;background:var(--color-warning-bg);border:1px solid var(--color-warning-border);border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:var(--color-warning-text);">{{ healthSummary.unknown }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:var(--color-warning-text-strong);">🟡 未检测</div> |
|
|
</div> |
|
|
</div> |
|
|
<div style="padding:10px 16px;background:#f9fafb;border:1px solid #e5e7eb;border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:#374151;">{{ healthSummary.total }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:#6b7280;">📊 总计</div> |
|
|
|
|
|
|
|
|
<div style="padding:10px 16px;background:var(--color-bg-subtle);border:1px solid var(--color-border);border-radius:8px;min-width:100px;text-align:center;"> |
|
|
|
|
|
<div style="font-size:22px;font-weight:700;color:var(--color-text-primary);">{{ healthSummary.total }}</div> |
|
|
|
|
|
<div style="font-size:11px;color:var(--color-text-secondary);">📊 总计</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
|
|
|
|
|
|
@ -34,34 +34,34 @@ |
|
|
<!-- F6: Fallback 链可视化 --> |
|
|
<!-- F6: Fallback 链可视化 --> |
|
|
<div v-if="fallbackChains.length > 0" class="fallback-section"> |
|
|
<div v-if="fallbackChains.length > 0" class="fallback-section"> |
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;"> |
|
|
<div style="display:flex;align-items:center;gap:8px;margin-bottom:8px;"> |
|
|
<span style="font-size:13px;font-weight:600;color:#1e40af;">🔗 Fallback 链(按优先级排列)</span> |
|
|
|
|
|
|
|
|
<span style="font-size:13px;font-weight:600;color:var(--color-info-text-strong);">🔗 Fallback 链(按优先级排列)</span> |
|
|
<t-tag size="small" variant="light" theme="primary" style="cursor:pointer;" @click="showFallbackHelp = !showFallbackHelp">❓ 帮助</t-tag> |
|
|
<t-tag size="small" variant="light" theme="primary" style="cursor:pointer;" @click="showFallbackHelp = !showFallbackHelp">❓ 帮助</t-tag> |
|
|
</div> |
|
|
</div> |
|
|
<div v-if="showFallbackHelp" class="fallback-help"> |
|
|
<div v-if="showFallbackHelp" class="fallback-help"> |
|
|
<div style="font-weight:600;color:#1e40af;margin-bottom:8px;">📖 Fallback 链使用说明</div> |
|
|
|
|
|
<p>Fallback 链是<span style="color:#059669;font-weight:600;">高可用性机制</span>,主模型故障时自动切换到备用模型。</p> |
|
|
|
|
|
|
|
|
<div style="font-weight:600;color:var(--color-info-text-strong);margin-bottom:8px;">📖 Fallback 链使用说明</div> |
|
|
|
|
|
<p>Fallback 链是<span style="color:var(--color-success-text-strong);font-weight:600;">高可用性机制</span>,主模型故障时自动切换到备用模型。</p> |
|
|
<p>✔ 勾选「设为活跃配置」启用 | 📊 调整「优先级」数值(越大越优先)| 🔀 拖拽列表项快速调整</p> |
|
|
<p>✔ 勾选「设为活跃配置」启用 | 📊 调整「优先级」数值(越大越优先)| 🔀 拖拽列表项快速调整</p> |
|
|
</div> |
|
|
</div> |
|
|
<div v-for="chain in fallbackChains" :key="chain.appType" style="margin-bottom:8px;"> |
|
|
<div v-for="chain in fallbackChains" :key="chain.appType" style="margin-bottom:8px;"> |
|
|
<div style="font-size:12px;color:#1e40af;font-weight:600;">{{ getAppTypeLabel(chain.appType) }}</div> |
|
|
|
|
|
|
|
|
<div style="font-size:12px;color:var(--color-info-text-strong);font-weight:600;">{{ getAppTypeLabel(chain.appType) }}</div> |
|
|
<div v-for="(item, idx) in chain.configs" :key="item.id" |
|
|
<div v-for="(item, idx) in chain.configs" :key="item.id" |
|
|
style="display:flex;align-items:center;gap:6px;padding:4px 8px;margin:2px 0;background:white;border-radius:4px;font-size:12px;cursor:grab;border:1px solid #e5e7eb;" |
|
|
|
|
|
|
|
|
style="display:flex;align-items:center;gap:6px;padding:4px 8px;margin:2px 0;background:white;border-radius:4px;font-size:12px;cursor:grab;border:1px solid var(--color-border);" |
|
|
draggable="true" |
|
|
draggable="true" |
|
|
@dragstart="onDragStart($event, item, chain)" |
|
|
@dragstart="onDragStart($event, item, chain)" |
|
|
@dragover.prevent |
|
|
@dragover.prevent |
|
|
@drop="onDrop($event, item, chain)"> |
|
|
@drop="onDrop($event, item, chain)"> |
|
|
<span style="color:#6b7280;font-weight:600;">#{{ idx + 1 }}</span> |
|
|
|
|
|
<span v-if="idx < chain.configs.length - 1" style="color:#93c5fd;">↓</span> |
|
|
|
|
|
<span v-else style="color:#d1d5db;">·</span> |
|
|
|
|
|
|
|
|
<span style="color:var(--color-text-secondary);font-weight:600;">#{{ idx + 1 }}</span> |
|
|
|
|
|
<span v-if="idx < chain.configs.length - 1" style="color:var(--color-info-border);">↓</span> |
|
|
|
|
|
<span v-else style="color:var(--color-border);">·</span> |
|
|
<strong>{{ item.name }}</strong> |
|
|
<strong>{{ item.name }}</strong> |
|
|
<code style="font-size:11px;background:#f3f4f6;padding:1px 4px;border-radius:3px;">{{ item.model_name }}</code> |
|
|
|
|
|
<span style="color:#6b7280;">P={{ item.priority }}</span> |
|
|
|
|
|
|
|
|
<code style="font-size:11px;background:var(--color-bg-code);padding:1px 4px;border-radius:3px;">{{ item.model_name }}</code> |
|
|
|
|
|
<span style="color:var(--color-text-secondary);">P={{ item.priority }}</span> |
|
|
<span v-if="healthMap[item.id]" :title="getHealthTooltip(item.id)" |
|
|
<span v-if="healthMap[item.id]" :title="getHealthTooltip(item.id)" |
|
|
:style="{ color: healthMap[item.id].status === 'ONLINE' ? '#16a34a' : '#dc2626' }"> |
|
|
|
|
|
|
|
|
:style="{ color: healthMap[item.id].status === 'ONLINE' ? 'var(--color-success-text)' : 'var(--color-error-text)' }"> |
|
|
{{ healthMap[item.id].status === 'ONLINE' ? '🟢' : healthMap[item.id].status === 'UNKNOWN' ? '🟡' : '🔴' }} |
|
|
{{ healthMap[item.id].status === 'ONLINE' ? '🟢' : healthMap[item.id].status === 'UNKNOWN' ? '🟡' : '🔴' }} |
|
|
<span v-if="healthMap[item.id].latencyMs" style="font-size:10px;">{{ healthMap[item.id].latencyMs }}ms</span> |
|
|
<span v-if="healthMap[item.id].latencyMs" style="font-size:10px;">{{ healthMap[item.id].latencyMs }}ms</span> |
|
|
</span> |
|
|
</span> |
|
|
<span v-else style="color:#d1d5db;" title="未检测">🟡</span> |
|
|
|
|
|
|
|
|
<span v-else style="color:var(--color-border);" title="未检测">🟡</span> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
@ -75,22 +75,22 @@ |
|
|
<t-tag size="small" variant="light" theme="primary">{{ getAppTypeLabel(row.app_type) }}</t-tag> |
|
|
<t-tag size="small" variant="light" theme="primary">{{ getAppTypeLabel(row.app_type) }}</t-tag> |
|
|
</template> |
|
|
</template> |
|
|
<template #model_name="{ row }"> |
|
|
<template #model_name="{ row }"> |
|
|
<code style="font-size:12px;background:#f3f4f6;padding:2px 6px;border-radius:4px;">{{ row.model_name }}</code> |
|
|
|
|
|
|
|
|
<code style="font-size:12px;background:var(--color-bg-code);padding:2px 6px;border-radius:4px;">{{ row.model_name }}</code> |
|
|
</template> |
|
|
</template> |
|
|
<template #base_url="{ row }"> |
|
|
<template #base_url="{ row }"> |
|
|
<span style="font-size:12px;color:#6b7280;max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;" :title="row.base_url || getProviderBaseUrl(row.provider)"> |
|
|
|
|
|
|
|
|
<span style="font-size:12px;color:var(--color-text-secondary);max-width:180px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block;" :title="row.base_url || getProviderBaseUrl(row.provider)"> |
|
|
{{ row.base_url || getProviderBaseUrl(row.provider) || '-' }} |
|
|
{{ row.base_url || getProviderBaseUrl(row.provider) || '-' }} |
|
|
</span> |
|
|
</span> |
|
|
</template> |
|
|
</template> |
|
|
<template #api_key="{ row }"> |
|
|
<template #api_key="{ row }"> |
|
|
<code style="font-size:12px;background:#f3f4f6;padding:2px 6px;border-radius:4px;">{{ row.api_key || '-' }}</code> |
|
|
|
|
|
|
|
|
<code style="font-size:12px;background:var(--color-bg-code);padding:2px 6px;border-radius:4px;">{{ row.api_key || '-' }}</code> |
|
|
</template> |
|
|
</template> |
|
|
<template #status="{ row }"> |
|
|
<template #status="{ row }"> |
|
|
<t-tag size="small" :theme="row.is_active ? 'success' : 'default'" variant="light"> |
|
|
<t-tag size="small" :theme="row.is_active ? 'success' : 'default'" variant="light"> |
|
|
{{ row.is_active ? '🟢 活跃' : '⚫ 未激活' }} |
|
|
{{ row.is_active ? '🟢 活跃' : '⚫ 未激活' }} |
|
|
</t-tag> |
|
|
</t-tag> |
|
|
<span v-if="row.is_active && healthMap[row.id]" :title="getHealthTooltip(row.id)" |
|
|
<span v-if="row.is_active && healthMap[row.id]" :title="getHealthTooltip(row.id)" |
|
|
:style="{ cursor: 'help', color: healthMap[row.id].status === 'ONLINE' ? '#16a34a' : healthMap[row.id].status === 'UNKNOWN' ? '#d97706' : '#dc2626', marginLeft: '4px' }"> |
|
|
|
|
|
|
|
|
:style="{ cursor: 'help', color: healthMap[row.id].status === 'ONLINE' ? 'var(--color-success-text)' : healthMap[row.id].status === 'UNKNOWN' ? 'var(--color-warning-text)' : 'var(--color-error-text)', marginLeft: '4px' }"> |
|
|
{{ healthMap[row.id].status === 'ONLINE' ? '●' : '●' }} |
|
|
{{ healthMap[row.id].status === 'ONLINE' ? '●' : '●' }} |
|
|
</span> |
|
|
</span> |
|
|
</template> |
|
|
</template> |
|
|
@ -110,7 +110,7 @@ |
|
|
<!-- F1: 测试结果提示 --> |
|
|
<!-- F1: 测试结果提示 --> |
|
|
<div v-if="testResult.visible" class="test-result-toast" |
|
|
<div v-if="testResult.visible" class="test-result-toast" |
|
|
:style="{ position: 'fixed', bottom: '20px', right: '20px', padding: '12px 20px', borderRadius: '8px', zIndex: 9999, color: 'white', maxWidth: '400px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', |
|
|
:style="{ position: 'fixed', bottom: '20px', right: '20px', padding: '12px 20px', borderRadius: '8px', zIndex: 9999, color: 'white', maxWidth: '400px', boxShadow: '0 4px 12px rgba(0,0,0,0.15)', |
|
|
background: testResult.success ? '#16a34a' : '#dc2626' }"> |
|
|
|
|
|
|
|
|
background: testResult.success ? 'var(--color-success-text)' : 'var(--color-error-text)' }"> |
|
|
<div style="font-weight:600;">{{ testResult.success ? '✅ 连接成功' : '❌ 连接失败' }}</div> |
|
|
<div style="font-weight:600;">{{ testResult.success ? '✅ 连接成功' : '❌ 连接失败' }}</div> |
|
|
<div style="font-size:12px;margin-top:4px;">延迟:{{ testResult.latencyMs }}ms<span v-if="testResult.dimensions"> · 维度:{{ testResult.dimensions }}</span></div> |
|
|
<div style="font-size:12px;margin-top:4px;">延迟:{{ testResult.latencyMs }}ms<span v-if="testResult.dimensions"> · 维度:{{ testResult.dimensions }}</span></div> |
|
|
<div v-if="testResult.errorMessage" style="font-size:11px;margin-top:4px;opacity:0.9;">{{ testResult.errorMessage }}</div> |
|
|
<div v-if="testResult.errorMessage" style="font-size:11px;margin-top:4px;opacity:0.9;">{{ testResult.errorMessage }}</div> |
|
|
@ -124,7 +124,7 @@ |
|
|
:footer="false" |
|
|
:footer="false" |
|
|
:close-on-overlay-click="false" |
|
|
:close-on-overlay-click="false" |
|
|
> |
|
|
> |
|
|
<div style="font-size:12px;color:#6b7280;margin-bottom:16px;">💡 核心三要素:API Key + API 地址 + 模型名称,填入即可使用</div> |
|
|
|
|
|
|
|
|
<div style="font-size:12px;color:var(--color-text-secondary);margin-bottom:16px;">💡 核心三要素:API Key + API 地址 + 模型名称,填入即可使用</div> |
|
|
<t-form label-align="top"> |
|
|
<t-form label-align="top"> |
|
|
<!-- 应用类型 --> |
|
|
<!-- 应用类型 --> |
|
|
<t-form-item label="应用类型"> |
|
|
<t-form-item label="应用类型"> |
|
|
@ -142,7 +142,7 @@ |
|
|
<t-button variant="outline" size="small" @click="showApiKey = !showApiKey">{{ showApiKey ? '🙈 隐藏' : '👁️ 显示' }}</t-button> |
|
|
<t-button variant="outline" size="small" @click="showApiKey = !showApiKey">{{ showApiKey ? '🙈 隐藏' : '👁️ 显示' }}</t-button> |
|
|
</t-space> |
|
|
</t-space> |
|
|
<template #help> |
|
|
<template #help> |
|
|
<span v-if="editModal.mode === 'edit'" style="font-size:11px;color:#6b7280;">💡 API Key 已脱敏,留空保留原值</span> |
|
|
|
|
|
|
|
|
<span v-if="editModal.mode === 'edit'" style="font-size:11px;color:var(--color-text-secondary);">💡 API Key 已脱敏,留空保留原值</span> |
|
|
</template> |
|
|
</template> |
|
|
</t-form-item> |
|
|
</t-form-item> |
|
|
|
|
|
|
|
|
@ -151,9 +151,9 @@ |
|
|
<t-input v-model="editModal.form.base_url" :placeholder="providerBaseUrlPlaceholder || '输入 API 地址'" @blur="onBaseUrlBlur" /> |
|
|
<t-input v-model="editModal.form.base_url" :placeholder="providerBaseUrlPlaceholder || '输入 API 地址'" @blur="onBaseUrlBlur" /> |
|
|
<template #help> |
|
|
<template #help> |
|
|
<t-space size="small" style="font-size:12px;margin-top:4px;align-items:center;"> |
|
|
<t-space size="small" style="font-size:12px;margin-top:4px;align-items:center;"> |
|
|
<span style="color:#6b7280;">快捷选择:</span> |
|
|
|
|
|
|
|
|
<span style="color:var(--color-text-secondary);">快捷选择:</span> |
|
|
<t-select v-model="editModal.form.provider" :options="providerOptions" style="max-width:200px;" size="small" @change="onProviderChange" /> |
|
|
<t-select v-model="editModal.form.provider" :options="providerOptions" style="max-width:200px;" size="small" @change="onProviderChange" /> |
|
|
<span v-show="providerTip" style="color:#3b82f6;">💡 {{ providerTip }}</span> |
|
|
|
|
|
|
|
|
<span v-show="providerTip" style="color:var(--color-info-text);">💡 {{ providerTip }}</span> |
|
|
</t-space> |
|
|
</t-space> |
|
|
</template> |
|
|
</template> |
|
|
</t-form-item> |
|
|
</t-form-item> |
|
|
@ -167,7 +167,7 @@ |
|
|
@keydown.enter.prevent="onDropdownKeyDown('enter')" @keydown.escape="modelDropdownOpen = false"> |
|
|
@keydown.enter.prevent="onDropdownKeyDown('enter')" @keydown.escape="modelDropdownOpen = false"> |
|
|
<template #suffix> |
|
|
<template #suffix> |
|
|
<t-button v-show="editModal.form.model_name" variant="text" size="small" |
|
|
<t-button v-show="editModal.form.model_name" variant="text" size="small" |
|
|
@click="editModal.form.model_name = ''; modelDropdownOpen = false; updateNameSuggestion()" style="color:#9ca3af;">✕</t-button> |
|
|
|
|
|
|
|
|
@click="editModal.form.model_name = ''; modelDropdownOpen = false; updateNameSuggestion()" style="color:var(--color-text-tertiary);">✕</t-button> |
|
|
</template> |
|
|
</template> |
|
|
</t-input> |
|
|
</t-input> |
|
|
<t-button variant="outline" size="small" @click="fetchModelsFromApi" :disabled="fetchLoading" |
|
|
<t-button variant="outline" size="small" @click="fetchModelsFromApi" :disabled="fetchLoading" |
|
|
@ -177,19 +177,19 @@ |
|
|
</t-space> |
|
|
</t-space> |
|
|
<!-- 自定义下拉列表 --> |
|
|
<!-- 自定义下拉列表 --> |
|
|
<div v-if="modelDropdownOpen && filteredModels.length > 0" class="model-dropdown" |
|
|
<div v-if="modelDropdownOpen && filteredModels.length > 0" class="model-dropdown" |
|
|
style="position:relative;top:100%;left:0;right:0;z-index:100;background:white;border:1px solid #d1d5db;border-radius:8px;max-height:240px;overflow-y:auto;margin-top:4px;box-shadow:0 4px 12px rgba(0,0,0,0.12);"> |
|
|
|
|
|
|
|
|
style="position:relative;top:100%;left:0;right:0;z-index:100;background:white;border:1px solid var(--color-border);border-radius:8px;max-height:240px;overflow-y:auto;margin-top:4px;box-shadow:0 4px 12px rgba(0,0,0,0.12);"> |
|
|
<div v-for="(m, idx) in filteredModels" :key="m.id" |
|
|
<div v-for="(m, idx) in filteredModels" :key="m.id" |
|
|
@click="selectModel(m)" @mouseenter="modelDropdownHighlight = idx" |
|
|
@click="selectModel(m)" @mouseenter="modelDropdownHighlight = idx" |
|
|
:style="{ padding: '8px 12px', cursor: 'pointer', fontSize: '13px', |
|
|
:style="{ padding: '8px 12px', cursor: 'pointer', fontSize: '13px', |
|
|
borderBottom: idx < filteredModels.length - 1 ? '1px solid #f3f4f6' : 'none', |
|
|
|
|
|
background: idx === modelDropdownHighlight ? '#eff6ff' : 'white' }"> |
|
|
|
|
|
|
|
|
borderBottom: idx < filteredModels.length - 1 ? '1px solid var(--color-bg-code)' : 'none', |
|
|
|
|
|
background: idx === modelDropdownHighlight ? 'var(--color-info-bg)' : 'white' }"> |
|
|
<div style="font-weight:500;">{{ m.id }}</div> |
|
|
<div style="font-weight:500;">{{ m.id }}</div> |
|
|
<div v-if="m.owned_by" style="font-size:11px;color:#6b7280;margin-top:1px;">{{ m.owned_by }}</div> |
|
|
|
|
|
|
|
|
<div v-if="m.owned_by" style="font-size:11px;color:var(--color-text-secondary);margin-top:1px;">{{ m.owned_by }}</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
<template #help> |
|
|
<template #help> |
|
|
<div style="font-size:11px;color:#6b7280;margin-top:4px;">💡 填入 API Key 和地址后点击「获取模型」自动填充</div> |
|
|
|
|
|
<div v-if="fetchedModels.length > 0" style="font-size:11px;color:#16a34a;margin-top:2px;">✅ 已获取 {{ fetchedModels.length }} 个模型</div> |
|
|
|
|
|
|
|
|
<div style="font-size:11px;color:var(--color-text-secondary);margin-top:4px;">💡 填入 API Key 和地址后点击「获取模型」自动填充</div> |
|
|
|
|
|
<div v-if="fetchedModels.length > 0" style="font-size:11px;color:var(--color-success-text);margin-top:2px;">✅ 已获取 {{ fetchedModels.length }} 个模型</div> |
|
|
</template> |
|
|
</template> |
|
|
</t-form-item> |
|
|
</t-form-item> |
|
|
|
|
|
|
|
|
@ -228,7 +228,7 @@ |
|
|
<t-form-item v-if="editModal.form.app_type === 'EMBEDDING'" label="📐 向量维度(必填)"> |
|
|
<t-form-item v-if="editModal.form.app_type === 'EMBEDDING'" label="📐 向量维度(必填)"> |
|
|
<t-input-number v-model="editModal.form.embeddingDimensions" :min="1" :max="8192" placeholder="1024" style="max-width:240px;" /> |
|
|
<t-input-number v-model="editModal.form.embeddingDimensions" :min="1" :max="8192" placeholder="1024" style="max-width:240px;" /> |
|
|
<template #help> |
|
|
<template #help> |
|
|
<span style="font-size:11px;color:#d97706;">⚠️ 修改维度后需重建向量表</span> |
|
|
|
|
|
|
|
|
<span style="font-size:11px;color:var(--color-warning-text);">⚠️ 修改维度后需重建向量表</span> |
|
|
</template> |
|
|
</template> |
|
|
</t-form-item> |
|
|
</t-form-item> |
|
|
|
|
|
|
|
|
@ -236,7 +236,7 @@ |
|
|
<t-form-item label="📝 配置名称(必填)"> |
|
|
<t-form-item label="📝 配置名称(必填)"> |
|
|
<t-input v-model="editModal.form.name" :placeholder="nameSuggestion || '如:生产环境-DeepSeek对话'" /> |
|
|
<t-input v-model="editModal.form.name" :placeholder="nameSuggestion || '如:生产环境-DeepSeek对话'" /> |
|
|
<template #help> |
|
|
<template #help> |
|
|
<span v-if="nameSuggestion && !editModal.form.name" style="font-size:11px;color:#3b82f6;"> |
|
|
|
|
|
|
|
|
<span v-if="nameSuggestion && !editModal.form.name" style="font-size:11px;color:var(--color-info-text);"> |
|
|
💡 建议:<a @click.prevent="editModal.form.name = nameSuggestion" style="color:var(--td-brand-color);cursor:pointer;text-decoration:underline;">{{ nameSuggestion }}</a> |
|
|
💡 建议:<a @click.prevent="editModal.form.name = nameSuggestion" style="color:var(--td-brand-color);cursor:pointer;text-decoration:underline;">{{ nameSuggestion }}</a> |
|
|
</span> |
|
|
</span> |
|
|
</template> |
|
|
</template> |
|
|
@ -276,10 +276,10 @@ |
|
|
<!-- F7: 导入弹窗 --> |
|
|
<!-- F7: 导入弹窗 --> |
|
|
<t-dialog v-model:visible="showImportDialog" header="📥 导入模型配置" width="600px" :footer="false"> |
|
|
<t-dialog v-model:visible="showImportDialog" header="📥 导入模型配置" width="600px" :footer="false"> |
|
|
<t-upload :auto-upload="false" theme="file" accept=".json" @change="onImportFileSelect" /> |
|
|
<t-upload :auto-upload="false" theme="file" accept=".json" @change="onImportFileSelect" /> |
|
|
<div v-if="importPreview" style="margin:12px 0;padding:12px;background:#f9fafb;border-radius:8px;"> |
|
|
|
|
|
|
|
|
<div v-if="importPreview" style="margin:12px 0;padding:12px;background:var(--color-bg-subtle);border-radius:8px;"> |
|
|
<div style="font-size:13px;font-weight:600;margin-bottom:8px;">导入预览</div> |
|
|
<div style="font-size:13px;font-weight:600;margin-bottom:8px;">导入预览</div> |
|
|
<div style="font-size:12px;">共 {{ importPreview.configs.length }} 条配置</div> |
|
|
<div style="font-size:12px;">共 {{ importPreview.configs.length }} 条配置</div> |
|
|
<div v-for="c in importPreview.configs.slice(0, 5)" :key="c.name" style="font-size:11px;color:#6b7280;margin:2px 0;"> |
|
|
|
|
|
|
|
|
<div v-for="c in importPreview.configs.slice(0, 5)" :key="c.name" style="font-size:11px;color:var(--color-text-secondary);margin:2px 0;"> |
|
|
· {{ c.name }} ({{ c.appType || c.app_type }}) - {{ c.modelName || c.model_name }} |
|
|
· {{ c.name }} ({{ c.appType || c.app_type }}) - {{ c.modelName || c.model_name }} |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
</div> |
|
|
@ -287,7 +287,7 @@ |
|
|
<t-select v-model="importConflict" :options="importConflictOptions" style="max-width:300px;" /> |
|
|
<t-select v-model="importConflict" :options="importConflictOptions" style="max-width:300px;" /> |
|
|
</t-form-item> |
|
|
</t-form-item> |
|
|
<div v-if="importResult" style="margin:12px 0;padding:12px;border-radius:8px;" |
|
|
<div v-if="importResult" style="margin:12px 0;padding:12px;border-radius:8px;" |
|
|
:style="{ background: importResult.errors?.length ? '#fef2f2' : '#f0fdf4' }"> |
|
|
|
|
|
|
|
|
:style="{ background: importResult.errors?.length ? 'var(--color-error-bg)' : 'var(--color-success-bg)' }"> |
|
|
<div style="font-size:13px;font-weight:600;">导入完成</div> |
|
|
<div style="font-size:13px;font-weight:600;">导入完成</div> |
|
|
<div style="font-size:12px;margin-top:4px;">新增 {{ importResult.added }} · 覆盖 {{ importResult.overwritten }} · 跳过 {{ importResult.skipped }}</div> |
|
|
<div style="font-size:12px;margin-top:4px;">新增 {{ importResult.added }} · 覆盖 {{ importResult.overwritten }} · 跳过 {{ importResult.skipped }}</div> |
|
|
</div> |
|
|
</div> |
|
|
@ -713,7 +713,7 @@ onUnmounted(() => { clearInterval(healthTimer); clearTimeout(testResultTimer); d |
|
|
</script> |
|
|
</script> |
|
|
|
|
|
|
|
|
<style scoped> |
|
|
<style scoped> |
|
|
.fallback-section { margin:12px 0; padding:12px; background:#eff6ff; border:1px solid #bfdbfe; border-radius:8px; } |
|
|
|
|
|
.fallback-help { margin-bottom:12px; padding:12px; background:white; border:1px solid #93c5fd; border-radius:6px; font-size:12px; line-height:1.6; color:#374151; } |
|
|
|
|
|
|
|
|
.fallback-section { margin:12px 0; padding:12px; background:var(--color-info-bg); border:1px solid var(--color-info-border); border-radius:8px; } |
|
|
|
|
|
.fallback-help { margin-bottom:12px; padding:12px; background:white; border:1px solid var(--color-info-border); border-radius:6px; font-size:12px; line-height:1.6; color:var(--color-text-primary); } |
|
|
.fallback-help p { margin:4px 0; } |
|
|
.fallback-help p { margin:4px 0; } |
|
|
</style> |
|
|
</style> |