完善模板编辑与模型管理体验
This commit is contained in:
@@ -20,12 +20,21 @@
|
||||
<div class="mc-provider">密钥:{{ item.api_key_preview || '未设置' }}</div>
|
||||
<div class="mc-provider">流式传输:{{ item.supports_streaming ? '支持' : '关闭' }}</div>
|
||||
<div class="mc-provider">思考模式:{{ item.enable_reasoning ? '开启' : '关闭' }}</div>
|
||||
<div v-if="isDeepSeek(item) && balanceMap[item.id]" class="mc-provider">
|
||||
余额:{{ formatBalanceText(balanceMap[item.id]) }}
|
||||
</div>
|
||||
</div>
|
||||
<div :class="['mc-status', item.status === 'enabled' ? 'on' : 'off']">
|
||||
{{ item.status === 'enabled' ? '已启用' : '已禁用' }}
|
||||
</div>
|
||||
<div class="mc-actions">
|
||||
<a-button size="small" @click="runTest(item)">测试</a-button>
|
||||
<a-button size="small" :loading="testingMap[item.id]" @click="runTest(item)">
|
||||
<template #icon><reload-outlined /></template>
|
||||
刷新测试
|
||||
</a-button>
|
||||
<a-button v-if="isDeepSeek(item)" size="small" :loading="balanceLoadingMap[item.id]" @click="fetchBalance(item)">
|
||||
查看余额
|
||||
</a-button>
|
||||
<a-button size="small" @click="openEdit(item)">编辑</a-button>
|
||||
<a-button size="small" @click="toggleStatus(item)">{{ item.status === 'enabled' ? '禁用' : '启用' }}</a-button>
|
||||
</div>
|
||||
@@ -39,7 +48,13 @@
|
||||
<a-input v-model:value="form.name" />
|
||||
</a-form-item>
|
||||
<a-form-item label="供应厂商">
|
||||
<a-input v-model:value="form.provider" />
|
||||
<a-select v-model:value="providerPreset" @change="applyProviderPreset">
|
||||
<a-select-option value="deepseek">DeepSeek</a-select-option>
|
||||
<a-select-option value="custom">自定义</a-select-option>
|
||||
</a-select>
|
||||
</a-form-item>
|
||||
<a-form-item v-if="providerPreset === 'custom'" label="自定义厂商名称">
|
||||
<a-input v-model:value="form.provider" placeholder="例如 OpenAI / Anthropic / 其他" />
|
||||
</a-form-item>
|
||||
<a-form-item label="API 格式">
|
||||
<a-select v-model:value="form.api_format">
|
||||
@@ -66,7 +81,8 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { Modal, message } from 'ant-design-vue'
|
||||
import { message } from 'ant-design-vue'
|
||||
import { ReloadOutlined } from '@ant-design/icons-vue'
|
||||
import { useModelStore } from '@/stores/model'
|
||||
|
||||
const store = useModelStore()
|
||||
@@ -75,6 +91,41 @@ const modalOpen = ref(false)
|
||||
const isEdit = ref(false)
|
||||
const editId = ref(0)
|
||||
const form = ref({ name: '', provider: '', api_format: 'openai', api_endpoint: '', api_key: '', supports_streaming: false, enable_reasoning: false })
|
||||
const providerPreset = ref<'deepseek' | 'custom'>('custom')
|
||||
const testingMap = ref<Record<number, boolean>>({})
|
||||
const balanceLoadingMap = ref<Record<number, boolean>>({})
|
||||
const balanceMap = ref<Record<number, { is_available: boolean; balance_infos: Array<{ currency: string; total_balance: string; granted_balance: string; topped_up_balance: string }> }>>({})
|
||||
|
||||
function isDeepSeek(item: any) {
|
||||
const provider = (item?.provider || '').trim().toLowerCase()
|
||||
return provider === 'deepseek'
|
||||
}
|
||||
|
||||
function applyProviderPreset(value: 'deepseek' | 'custom') {
|
||||
if (value === 'deepseek') {
|
||||
form.value.provider = 'DeepSeek'
|
||||
form.value.api_format = 'openai'
|
||||
if (!form.value.api_endpoint || form.value.api_endpoint.includes('deepseek')) {
|
||||
form.value.api_endpoint = 'https://api.deepseek.com'
|
||||
}
|
||||
return
|
||||
}
|
||||
if (form.value.provider === 'DeepSeek') {
|
||||
form.value.provider = ''
|
||||
}
|
||||
}
|
||||
|
||||
function inferProviderPreset(item?: any) {
|
||||
providerPreset.value = isDeepSeek(item || form.value) ? 'deepseek' : 'custom'
|
||||
}
|
||||
|
||||
function formatBalanceText(data: { is_available: boolean; balance_infos: Array<{ currency: string; total_balance: string; granted_balance: string; topped_up_balance: string }> }) {
|
||||
const infos = data?.balance_infos || []
|
||||
if (!infos.length) return data?.is_available ? '可用' : '不可用'
|
||||
return infos
|
||||
.map((item) => `${item.currency} ${item.total_balance}(充值 ${item.topped_up_balance} / 赠送 ${item.granted_balance})`)
|
||||
.join(';')
|
||||
}
|
||||
|
||||
async function refreshList() {
|
||||
await store.fetchList()
|
||||
@@ -88,6 +139,8 @@ onMounted(async () => {
|
||||
function openAdd() {
|
||||
isEdit.value = false
|
||||
form.value = { name: '', provider: '', api_format: 'openai', api_endpoint: '', api_key: '', supports_streaming: false, enable_reasoning: false }
|
||||
providerPreset.value = 'deepseek'
|
||||
applyProviderPreset('deepseek')
|
||||
modalOpen.value = true
|
||||
}
|
||||
|
||||
@@ -103,6 +156,7 @@ function openEdit(item: any) {
|
||||
supports_streaming: !!item.supports_streaming,
|
||||
enable_reasoning: !!item.enable_reasoning,
|
||||
}
|
||||
inferProviderPreset(item)
|
||||
modalOpen.value = true
|
||||
}
|
||||
|
||||
@@ -124,15 +178,27 @@ async function toggleStatus(item: any) {
|
||||
}
|
||||
|
||||
async function runTest(item: any) {
|
||||
testingMap.value[item.id] = true
|
||||
try {
|
||||
const result: any = await store.test(item.id)
|
||||
Modal.info({
|
||||
title: '连接测试结果',
|
||||
width: 680,
|
||||
content: JSON.stringify(result.data, null, 2),
|
||||
})
|
||||
message.success(result.data?.message || `模型 ${item.name} 测试成功`, 2)
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '连接测试失败')
|
||||
message.error(error.message || '连接测试失败', 2)
|
||||
} finally {
|
||||
testingMap.value[item.id] = false
|
||||
}
|
||||
}
|
||||
|
||||
async function fetchBalance(item: any) {
|
||||
balanceLoadingMap.value[item.id] = true
|
||||
try {
|
||||
const result: any = await store.balance(item.id)
|
||||
balanceMap.value[item.id] = result.data
|
||||
message.success(`已刷新 ${item.name} 余额`, 2)
|
||||
} catch (error: any) {
|
||||
message.error(error.message || '余额查询失败', 2)
|
||||
} finally {
|
||||
balanceLoadingMap.value[item.id] = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user