|
|
@@ -3,53 +3,40 @@
|
|
|
<!-- 搜索区域 -->
|
|
|
<a-form :model="queryParams" ref="queryRef" layout="inline" v-show="showSearch" class="search-form">
|
|
|
<a-form-item label="服务名称" name="serviceName">
|
|
|
- <a-input v-model:value="queryParams.serviceName" placeholder="请输入服务名称" allowClear @pressEnter="handleQuery" />
|
|
|
+ <a-input v-model:value="queryParams.serviceName" placeholder="请输入服务名称" allowClear style="width: 240px" @pressEnter="handleQuery" />
|
|
|
</a-form-item>
|
|
|
<a-form-item label="厂商" name="provider">
|
|
|
- <a-select v-model:value="queryParams.provider" placeholder="请选择厂商" allowClear style="width: 200px">
|
|
|
+ <a-select v-model:value="queryParams.provider" placeholder="请选择厂商" allowClear style="width: 240px">
|
|
|
<a-select-option v-for="item in providerOptions" :key="item.value" :value="item.value">{{ item.label }}</a-select-option>
|
|
|
</a-select>
|
|
|
</a-form-item>
|
|
|
<a-form-item label="状态" name="enabled">
|
|
|
- <a-select v-model:value="queryParams.enabled" placeholder="请选择状态" allowClear style="width: 200px">
|
|
|
+ <a-select v-model:value="queryParams.enabled" placeholder="请选择状态" allowClear style="width: 240px">
|
|
|
<a-select-option value="1">启用</a-select-option>
|
|
|
<a-select-option value="0">停用</a-select-option>
|
|
|
</a-select>
|
|
|
</a-form-item>
|
|
|
<a-form-item>
|
|
|
- <a-button type="primary" @click="handleQuery">
|
|
|
- <template #icon><SearchOutlined /></template>
|
|
|
- 搜索
|
|
|
- </a-button>
|
|
|
- <a-button style="margin-left: 8px" @click="resetQuery">
|
|
|
- <template #icon><ReloadOutlined /></template>
|
|
|
- 重置
|
|
|
- </a-button>
|
|
|
+ <a-button type="primary" @click="handleQuery"><SearchOutlined />搜索</a-button>
|
|
|
+ <a-button style="margin-left: 8px" @click="resetQuery"><ReloadOutlined />重置</a-button>
|
|
|
</a-form-item>
|
|
|
</a-form>
|
|
|
|
|
|
<!-- 操作按钮 -->
|
|
|
- <a-row :gutter="10" class="mb8">
|
|
|
- <a-col :span="1.5">
|
|
|
- <a-button type="primary" @click="handleAdd" v-hasPermi="['ai:service:add']">
|
|
|
- <template #icon><PlusOutlined /></template>
|
|
|
- 新增
|
|
|
- </a-button>
|
|
|
- </a-col>
|
|
|
- <a-col :span="1.5">
|
|
|
- <a-button type="primary" :disabled="single" @click="handleUpdate" v-hasPermi="['ai:service:edit']">
|
|
|
- <template #icon><EditOutlined /></template>
|
|
|
- 修改
|
|
|
- </a-button>
|
|
|
- </a-col>
|
|
|
- <a-col :span="1.5">
|
|
|
- <a-button danger :disabled="multiple" @click="handleDelete" v-hasPermi="['ai:service:remove']">
|
|
|
- <template #icon><DeleteOutlined /></template>
|
|
|
- 删除
|
|
|
- </a-button>
|
|
|
- </a-col>
|
|
|
- <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
|
|
- </a-row>
|
|
|
+ <div class="button-toolbar">
|
|
|
+ <a-row :gutter="10">
|
|
|
+ <a-col :span="1.5">
|
|
|
+ <a-button type="primary" @click="handleAdd" v-hasPermi="['ai:service:add']"><PlusOutlined />新增</a-button>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="1.5">
|
|
|
+ <a-button type="primary" :disabled="single" @click="handleUpdate" v-hasPermi="['ai:service:edit']" style="background-color: #52c41a; border-color: #52c41a"><EditOutlined />修改</a-button>
|
|
|
+ </a-col>
|
|
|
+ <a-col :span="1.5">
|
|
|
+ <a-button danger :disabled="multiple" @click="handleDelete" v-hasPermi="['ai:service:remove']"><DeleteOutlined />删除</a-button>
|
|
|
+ </a-col>
|
|
|
+ <right-toolbar v-model:showSearch="showSearch" @queryTable="getList" />
|
|
|
+ </a-row>
|
|
|
+ </div>
|
|
|
|
|
|
<!-- 数据表格 -->
|
|
|
<a-table
|
|
|
@@ -118,195 +105,133 @@
|
|
|
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
|
|
|
|
|
|
<!-- 新增/修改对话框 -->
|
|
|
- <a-modal :title="title" v-model:open="open" width="750px" :maskClosable="false" :destroyOnClose="true">
|
|
|
- <a-form ref="serviceRef" :model="form" :rules="rules" :label-col="{ span: 6 }">
|
|
|
+ <a-modal :title="title" v-model:open="open" width="720px" :maskClosable="false" :destroyOnClose="true">
|
|
|
+ <a-form ref="serviceRef" :model="form" :rules="rules" :label-col="{ span: 6 }" :wrapper-col="{ span: 16 }">
|
|
|
<a-divider orientation="left">基本信息</a-divider>
|
|
|
|
|
|
- <a-row :gutter="16">
|
|
|
+ <a-row :gutter="24">
|
|
|
<a-col :span="12">
|
|
|
<a-form-item label="服务名称" name="serviceName">
|
|
|
- <a-input v-model:value="form.serviceName" placeholder="显示名称,如:GPT-4o" />
|
|
|
+ <a-input v-model:value="form.serviceName" placeholder="如:GPT-4o" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
<a-col :span="12">
|
|
|
<a-form-item label="服务代码" name="serviceCode">
|
|
|
- <a-input v-model:value="form.serviceCode" placeholder="唯一标识,如:openai-gpt4o" :disabled="form.serviceId != null" />
|
|
|
+ <a-input v-model:value="form.serviceCode" placeholder="如:openai-gpt4o" :disabled="form.serviceId != null" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
</a-row>
|
|
|
-
|
|
|
- <a-row :gutter="16">
|
|
|
+
|
|
|
+ <a-row :gutter="24">
|
|
|
<a-col :span="12">
|
|
|
<a-form-item label="厂商" name="provider">
|
|
|
- <a-select v-model:value="form.provider" placeholder="请选择厂商" @change="handleProviderChange" style="width: 100%">
|
|
|
+ <a-select v-model:value="form.provider" placeholder="请选择厂商" @change="handleProviderChange">
|
|
|
<a-select-option v-for="item in providerOptions" :key="item.value" :value="item.value">
|
|
|
- <span>{{ item.label }}</span>
|
|
|
- <span class="provider-url">{{ item.url }}</span>
|
|
|
+ {{ item.label }}
|
|
|
</a-select-option>
|
|
|
</a-select>
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
<a-col :span="12">
|
|
|
<a-form-item label="模型代码" name="modelCode">
|
|
|
- <a-input v-model:value="form.modelCode" placeholder="厂商模型标识,如:gpt-4o、glm-4" />
|
|
|
+ <a-input v-model:value="form.modelCode" placeholder="如:gpt-4o、glm-4" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
</a-row>
|
|
|
|
|
|
- <a-divider orientation="left">
|
|
|
- <span>API配置</span>
|
|
|
- <a-tooltip placement="top">
|
|
|
- <template #title>
|
|
|
- <div style="max-width: 280px;">
|
|
|
- <p style="margin-bottom: 8px;"><b>如何获取 API Key?</b></p>
|
|
|
- <p>• OpenAI: platform.openai.com</p>
|
|
|
- <p>• 智谱AI: open.bigmodel.cn</p>
|
|
|
- <p>• 阿里云: dashscope.console.aliyun.com</p>
|
|
|
- <p>• 月之暗面: platform.moonshot.cn</p>
|
|
|
- <p>• DeepSeek: platform.deepseek.com</p>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <QuestionCircleOutlined class="help-icon" />
|
|
|
- </a-tooltip>
|
|
|
- </a-divider>
|
|
|
-
|
|
|
- <a-form-item label="API地址" name="apiUrl">
|
|
|
- <a-input v-model:value="form.apiUrl" placeholder="选择厂商后自动填充,也可手动修改" />
|
|
|
- </a-form-item>
|
|
|
+ <a-divider orientation="left">API配置</a-divider>
|
|
|
|
|
|
- <a-row :gutter="16">
|
|
|
+ <a-row :gutter="24">
|
|
|
+ <a-col :span="24">
|
|
|
+ <a-form-item label="API地址" name="apiUrl" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
|
|
|
+ <a-input v-model:value="form.apiUrl" placeholder="选择厂商后自动填充,也可手动修改" />
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+
|
|
|
+ <a-row :gutter="24">
|
|
|
<a-col :span="12">
|
|
|
<a-form-item label="API密钥" name="apiKey">
|
|
|
- <div class="key-input-wrapper">
|
|
|
- <a-input v-model:value="form.apiKey" placeholder="从厂商平台获取的 API Key" />
|
|
|
- <a-button
|
|
|
- v-if="form.serviceId && isKeyMasked"
|
|
|
- class="view-key-btn"
|
|
|
- type="link"
|
|
|
- @click="handleViewKey"
|
|
|
- v-hasPermi="['ai:service:viewkey']"
|
|
|
- >
|
|
|
- <template #icon><EyeOutlined /></template>
|
|
|
- 查看原文
|
|
|
- </a-button>
|
|
|
- </div>
|
|
|
+ <a-input-password v-model:value="form.apiKey" placeholder="API Key" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
<a-col :span="12">
|
|
|
<a-form-item label="API Secret" name="apiSecret">
|
|
|
- <a-input v-model:value="form.apiSecret" placeholder="部分厂商需要(如百度)" />
|
|
|
+ <a-input-password v-model:value="form.apiSecret" placeholder="部分厂商需要" />
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
+
|
|
|
+ <a-row v-if="form.serviceId && isKeyMasked">
|
|
|
+ <a-col :span="24">
|
|
|
+ <a-form-item :wrapper-col="{ offset: 3 }">
|
|
|
+ <a-button type="link" size="small" @click="handleViewKey" v-hasPermi="['ai:service:viewkey']" style="padding: 0;">
|
|
|
+ <EyeOutlined /> 查看完整密钥
|
|
|
+ </a-button>
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
</a-row>
|
|
|
|
|
|
- <a-divider orientation="left">
|
|
|
- <span>模型参数</span>
|
|
|
- <a-tooltip placement="top">
|
|
|
- <template #title>
|
|
|
- <div style="max-width: 280px;">
|
|
|
- <p><b>温度</b>: 控制随机性,0=确定,0.7=推荐,1.5+=创意</p>
|
|
|
- <p style="margin-top: 6px;"><b>Top P</b>: 核采样,0.95推荐</p>
|
|
|
- <p style="margin-top: 6px;"><b>最大Token</b>: 回复长度上限,1Token≈0.75中文字</p>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <QuestionCircleOutlined class="help-icon" />
|
|
|
- </a-tooltip>
|
|
|
- </a-divider>
|
|
|
+ <a-divider orientation="left">模型参数</a-divider>
|
|
|
|
|
|
- <a-row :gutter="16">
|
|
|
+ <a-row :gutter="24">
|
|
|
<a-col :span="8">
|
|
|
- <a-form-item label="温度" name="temperature">
|
|
|
+ <a-form-item label="温度" name="temperature" :label-col="{ span: 8 }" :wrapper-col="{ span: 14 }">
|
|
|
<a-input-number v-model:value="form.temperature" :min="0" :max="2" :step="0.1" :precision="2" style="width: 100%" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
<a-col :span="8">
|
|
|
- <a-form-item label="Top P" name="topP">
|
|
|
+ <a-form-item label="Top P" name="topP" :label-col="{ span: 8 }" :wrapper-col="{ span: 14 }">
|
|
|
<a-input-number v-model:value="form.topP" :min="0" :max="1" :step="0.05" :precision="2" style="width: 100%" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
<a-col :span="8">
|
|
|
- <a-form-item label="最大Token" name="maxTokens">
|
|
|
+ <a-form-item label="最大Token" name="maxTokens" :label-col="{ span: 10 }" :wrapper-col="{ span: 14 }">
|
|
|
<a-input-number v-model:value="form.maxTokens" :min="1" :max="128000" :step="1024" style="width: 100%" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
</a-row>
|
|
|
-
|
|
|
- <a-form-item label="系统提示词" name="systemPrompt">
|
|
|
- <a-textarea v-model:value="form.systemPrompt" :rows="3" placeholder="设定AI角色,如:你是一个专业、友好的AI助手" />
|
|
|
- </a-form-item>
|
|
|
-
|
|
|
- <a-divider orientation="left">
|
|
|
- <span>支持的能力</span>
|
|
|
- <a-tooltip placement="top">
|
|
|
- <template #title>
|
|
|
- <div style="max-width: 320px;">
|
|
|
- <p style="margin-bottom: 8px;">勾选此服务支持的能力,对话时会显示对应按钮</p>
|
|
|
- <p><b>流式输出</b>: 逐字显示回复,体验更好</p>
|
|
|
- <p><b>视觉理解</b>: 可上传图片分析(GPT-4o/GLM-4V)</p>
|
|
|
- <p><b>工具调用</b>: Function Calling能力</p>
|
|
|
- <p><b>深度思考</b>: AI先推理再回答(智谱/DeepSeek)</p>
|
|
|
- <p><b>联网搜索</b>: 获取最新信息(智谱/Kimi)</p>
|
|
|
- <p><b>文件解析</b>: 上传文件分析(Kimi/通义)</p>
|
|
|
- </div>
|
|
|
- </template>
|
|
|
- <QuestionCircleOutlined class="help-icon" />
|
|
|
- </a-tooltip>
|
|
|
- </a-divider>
|
|
|
-
|
|
|
- <a-row :gutter="16">
|
|
|
- <a-col :span="8">
|
|
|
- <a-form-item label="流式输出">
|
|
|
- <a-switch v-model:checked="form.supportsStream" checkedValue="1" unCheckedValue="0" />
|
|
|
- </a-form-item>
|
|
|
- </a-col>
|
|
|
- <a-col :span="8">
|
|
|
- <a-form-item label="视觉理解">
|
|
|
- <a-switch v-model:checked="form.supportsVision" checkedValue="1" unCheckedValue="0" />
|
|
|
- </a-form-item>
|
|
|
- </a-col>
|
|
|
- <a-col :span="8">
|
|
|
- <a-form-item label="工具调用">
|
|
|
- <a-switch v-model:checked="form.supportsTools" checkedValue="1" unCheckedValue="0" />
|
|
|
+
|
|
|
+ <a-row :gutter="24">
|
|
|
+ <a-col :span="24">
|
|
|
+ <a-form-item label="系统提示词" name="systemPrompt" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
|
|
|
+ <a-textarea v-model:value="form.systemPrompt" :rows="2" placeholder="设定AI角色,如:你是一个专业的AI助手" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
</a-row>
|
|
|
|
|
|
- <a-row :gutter="16">
|
|
|
- <a-col :span="8">
|
|
|
- <a-form-item label="深度思考">
|
|
|
- <a-switch v-model:checked="form.supportsThinking" checkedValue="1" unCheckedValue="0" />
|
|
|
- </a-form-item>
|
|
|
- </a-col>
|
|
|
- <a-col :span="8">
|
|
|
- <a-form-item label="联网搜索">
|
|
|
- <a-switch v-model:checked="form.supportsSearch" checkedValue="1" unCheckedValue="0" />
|
|
|
- </a-form-item>
|
|
|
- </a-col>
|
|
|
- <a-col :span="8">
|
|
|
- <a-form-item label="文件解析">
|
|
|
- <a-switch v-model:checked="form.supportsFiles" checkedValue="1" unCheckedValue="0" />
|
|
|
+ <a-divider orientation="left">支持能力</a-divider>
|
|
|
+
|
|
|
+ <a-row :gutter="24">
|
|
|
+ <a-col :span="24">
|
|
|
+ <a-form-item label="能力配置" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
|
|
|
+ <a-checkbox-group v-model:value="capabilitiesChecked" :options="capabilityOptions" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
</a-row>
|
|
|
|
|
|
<a-divider orientation="left">状态设置</a-divider>
|
|
|
|
|
|
- <a-row :gutter="16">
|
|
|
+ <a-row :gutter="24">
|
|
|
<a-col :span="8">
|
|
|
- <a-form-item label="启用状态">
|
|
|
+ <a-form-item label="启用状态" :label-col="{ span: 10 }" :wrapper-col="{ span: 14 }">
|
|
|
<a-switch v-model:checked="form.enabled" checkedValue="1" unCheckedValue="0" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
<a-col :span="8">
|
|
|
- <a-form-item label="排序" name="sortOrder">
|
|
|
+ <a-form-item label="排序" name="sortOrder" :label-col="{ span: 8 }" :wrapper-col="{ span: 14 }">
|
|
|
<a-input-number v-model:value="form.sortOrder" :min="0" style="width: 100%" />
|
|
|
</a-form-item>
|
|
|
</a-col>
|
|
|
</a-row>
|
|
|
-
|
|
|
- <a-form-item label="备注" name="remark">
|
|
|
- <a-input v-model:value="form.remark" placeholder="备注信息" />
|
|
|
- </a-form-item>
|
|
|
+
|
|
|
+ <a-row :gutter="24">
|
|
|
+ <a-col :span="24">
|
|
|
+ <a-form-item label="备注" name="remark" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
|
|
|
+ <a-input v-model:value="form.remark" placeholder="备注信息" />
|
|
|
+ </a-form-item>
|
|
|
+ </a-col>
|
|
|
+ </a-row>
|
|
|
</a-form>
|
|
|
|
|
|
<template #footer>
|
|
|
@@ -320,6 +245,7 @@
|
|
|
<script setup name="AiService">
|
|
|
import { SearchOutlined, ReloadOutlined, PlusOutlined, EditOutlined, DeleteOutlined, StarOutlined } from '@ant-design/icons-vue'
|
|
|
import { VideoCameraOutlined, PictureOutlined, SettingOutlined, QuestionCircleOutlined, ThunderboltOutlined, FileOutlined, EyeOutlined } from '@ant-design/icons-vue'
|
|
|
+import { Modal, message } from 'ant-design-vue'
|
|
|
import { listService, getService, addService, updateService, delService, setDefaultService, viewApiKey } from '@/api/system/ai/service'
|
|
|
|
|
|
const { proxy } = getCurrentInstance()
|
|
|
@@ -336,13 +262,13 @@ const title = ref('')
|
|
|
const open = ref(false)
|
|
|
|
|
|
const columns = [
|
|
|
- { title: '服务名称', key: 'serviceName', dataIndex: 'serviceName', minWidth: 140 },
|
|
|
- { title: '服务代码', dataIndex: 'serviceCode', key: 'serviceCode', width: 150 },
|
|
|
- { title: '厂商', key: 'providerName', width: 100, align: 'center' },
|
|
|
- { title: '模型', dataIndex: 'modelCode', key: 'modelCode', width: 150 },
|
|
|
- { title: '支持能力', key: 'capabilities', width: 180, align: 'center' },
|
|
|
+ { title: '服务名称', key: 'serviceName', dataIndex: 'serviceName', width: 120, ellipsis: true },
|
|
|
+ { title: '服务代码', dataIndex: 'serviceCode', key: 'serviceCode', width: 140, ellipsis: true },
|
|
|
+ { title: '厂商', key: 'providerName', width: 90, align: 'center', ellipsis: true },
|
|
|
+ { title: '模型', dataIndex: 'modelCode', key: 'modelCode', width: 140, ellipsis: true },
|
|
|
+ { title: '支持能力', key: 'capabilities', width: 220, align: 'center' },
|
|
|
{ title: '状态', key: 'enabled', width: 80, align: 'center' },
|
|
|
- { title: '操作', key: 'action', width: 150, align: 'center', fixed: 'right' }
|
|
|
+ { title: '操作', key: 'action', width: 120, align: 'center', fixed: 'right' }
|
|
|
]
|
|
|
|
|
|
const providerOptions = ref([
|
|
|
@@ -381,6 +307,38 @@ const isKeyMasked = computed(() => {
|
|
|
return form.value.apiKey && form.value.apiKey.includes('****')
|
|
|
})
|
|
|
|
|
|
+// 能力配置选项
|
|
|
+const capabilityOptions = [
|
|
|
+ { label: '流式输出', value: 'supportsStream' },
|
|
|
+ { label: '视觉理解', value: 'supportsVision' },
|
|
|
+ { label: '工具调用', value: 'supportsTools' },
|
|
|
+ { label: '深度思考', value: 'supportsThinking' },
|
|
|
+ { label: '联网搜索', value: 'supportsSearch' },
|
|
|
+ { label: '文件解析', value: 'supportsFiles' }
|
|
|
+]
|
|
|
+
|
|
|
+// 能力配置双向绑定
|
|
|
+const capabilitiesChecked = computed({
|
|
|
+ get() {
|
|
|
+ const checked = []
|
|
|
+ if (form.value.supportsStream === '1') checked.push('supportsStream')
|
|
|
+ if (form.value.supportsVision === '1') checked.push('supportsVision')
|
|
|
+ if (form.value.supportsTools === '1') checked.push('supportsTools')
|
|
|
+ if (form.value.supportsThinking === '1') checked.push('supportsThinking')
|
|
|
+ if (form.value.supportsSearch === '1') checked.push('supportsSearch')
|
|
|
+ if (form.value.supportsFiles === '1') checked.push('supportsFiles')
|
|
|
+ return checked
|
|
|
+ },
|
|
|
+ set(val) {
|
|
|
+ form.value.supportsStream = val.includes('supportsStream') ? '1' : '0'
|
|
|
+ form.value.supportsVision = val.includes('supportsVision') ? '1' : '0'
|
|
|
+ form.value.supportsTools = val.includes('supportsTools') ? '1' : '0'
|
|
|
+ form.value.supportsThinking = val.includes('supportsThinking') ? '1' : '0'
|
|
|
+ form.value.supportsSearch = val.includes('supportsSearch') ? '1' : '0'
|
|
|
+ form.value.supportsFiles = val.includes('supportsFiles') ? '1' : '0'
|
|
|
+ }
|
|
|
+})
|
|
|
+
|
|
|
const rules = {
|
|
|
serviceName: [{ required: true, message: '请输入服务名称', trigger: 'blur' }],
|
|
|
serviceCode: [{ required: true, message: '请输入服务代码', trigger: 'blur' }],
|
|
|
@@ -461,15 +419,19 @@ function handleUpdate(row) {
|
|
|
|
|
|
function handleViewKey() {
|
|
|
if (!form.value.serviceId) return
|
|
|
- proxy.$modal.confirm('查看完整API Key是敏感操作,确认继续?').then(() => {
|
|
|
- viewApiKey(form.value.serviceId).then(response => {
|
|
|
- if (response.data) {
|
|
|
- form.value.apiKey = response.data.apiKey || ''
|
|
|
- form.value.apiSecret = response.data.apiSecret || ''
|
|
|
- proxy.$modal.msgSuccess('已显示完整密钥')
|
|
|
- }
|
|
|
- })
|
|
|
- }).catch(() => {})
|
|
|
+ Modal.confirm({
|
|
|
+ title: '提示',
|
|
|
+ content: '查看完整API Key是敏感操作,确认继续?',
|
|
|
+ onOk() {
|
|
|
+ return viewApiKey(form.value.serviceId).then(response => {
|
|
|
+ if (response.data) {
|
|
|
+ form.value.apiKey = response.data.apiKey || ''
|
|
|
+ form.value.apiSecret = response.data.apiSecret || ''
|
|
|
+ message.success('已显示完整密钥')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function handleProviderChange(val) {
|
|
|
@@ -486,13 +448,13 @@ function submitForm() {
|
|
|
proxy.$refs['serviceRef'].validate().then(() => {
|
|
|
if (form.value.serviceId) {
|
|
|
updateService(form.value).then(() => {
|
|
|
- proxy.$modal.msgSuccess('修改成功')
|
|
|
+ message.success('修改成功')
|
|
|
open.value = false
|
|
|
getList()
|
|
|
})
|
|
|
} else {
|
|
|
addService(form.value).then(() => {
|
|
|
- proxy.$modal.msgSuccess('新增成功')
|
|
|
+ message.success('新增成功')
|
|
|
open.value = false
|
|
|
getList()
|
|
|
})
|
|
|
@@ -507,32 +469,45 @@ function cancel() {
|
|
|
|
|
|
function handleDelete(row) {
|
|
|
const serviceIds = row.serviceId ? [row.serviceId] : ids.value
|
|
|
- proxy.$modal.confirm('确认删除选中的AI服务吗?').then(() => {
|
|
|
- return delService(serviceIds)
|
|
|
- }).then(() => {
|
|
|
- getList()
|
|
|
- proxy.$modal.msgSuccess('删除成功')
|
|
|
- }).catch(() => {})
|
|
|
+ Modal.confirm({
|
|
|
+ title: '提示',
|
|
|
+ content: '确认删除选中的AI服务吗?',
|
|
|
+ onOk() {
|
|
|
+ return delService(serviceIds).then(() => {
|
|
|
+ getList()
|
|
|
+ message.success('删除成功')
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function handleStatusChange(row) {
|
|
|
const text = row.enabled === '1' ? '启用' : '停用'
|
|
|
- proxy.$modal.confirm(`确认${text}「${row.serviceName}」吗?`).then(() => {
|
|
|
- return updateService({ serviceId: row.serviceId, enabled: row.enabled })
|
|
|
- }).then(() => {
|
|
|
- proxy.$modal.msgSuccess(`${text}成功`)
|
|
|
- }).catch(() => {
|
|
|
- row.enabled = row.enabled === '1' ? '0' : '1'
|
|
|
+ Modal.confirm({
|
|
|
+ title: '提示',
|
|
|
+ content: `确认${text}「${row.serviceName}」吗?`,
|
|
|
+ onOk() {
|
|
|
+ return updateService({ serviceId: row.serviceId, enabled: row.enabled }).then(() => {
|
|
|
+ message.success(`${text}成功`)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ onCancel() {
|
|
|
+ row.enabled = row.enabled === '1' ? '0' : '1'
|
|
|
+ }
|
|
|
})
|
|
|
}
|
|
|
|
|
|
function handleSetDefault(row) {
|
|
|
- proxy.$modal.confirm(`确认将「${row.serviceName}」设为默认服务吗?`).then(() => {
|
|
|
- return setDefaultService(row.serviceId)
|
|
|
- }).then(() => {
|
|
|
- getList()
|
|
|
- proxy.$modal.msgSuccess('设置成功')
|
|
|
- }).catch(() => {})
|
|
|
+ Modal.confirm({
|
|
|
+ title: '提示',
|
|
|
+ content: `确认将「${row.serviceName}」设为默认服务吗?`,
|
|
|
+ onOk() {
|
|
|
+ return setDefaultService(row.serviceId).then(() => {
|
|
|
+ getList()
|
|
|
+ message.success('设置成功')
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
}
|
|
|
|
|
|
function getProviderTagColor(provider) {
|
|
|
@@ -552,18 +527,106 @@ function getProviderTagColor(provider) {
|
|
|
getList()
|
|
|
</script>
|
|
|
|
|
|
-<style scoped>
|
|
|
-.search-form {
|
|
|
+<style lang="scss" scoped>
|
|
|
+// 搜索表单布局
|
|
|
+.search-form.ant-form-inline {
|
|
|
+ background: #fff;
|
|
|
+ padding: 0;
|
|
|
+ border-radius: 4px;
|
|
|
+
|
|
|
+ .ant-form-item {
|
|
|
+ margin-right: 16px;
|
|
|
+ margin-bottom: 16px;
|
|
|
+ display: inline-flex;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .ant-form-item-label {
|
|
|
+ padding-right: 8px;
|
|
|
+ flex-shrink: 0;
|
|
|
+
|
|
|
+ > label {
|
|
|
+ white-space: nowrap;
|
|
|
+ height: 32px;
|
|
|
+ line-height: 32px;
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-form-item-control {
|
|
|
+ flex: 0 0 auto;
|
|
|
+ display: inline-block;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-form-item-control-input {
|
|
|
+ min-height: 32px;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ }
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-right: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 按钮工具栏
|
|
|
+.button-toolbar {
|
|
|
background: #fff;
|
|
|
- padding: 20px 20px 0;
|
|
|
+ padding: 0;
|
|
|
border-radius: 4px;
|
|
|
margin-bottom: 16px;
|
|
|
+
|
|
|
+ .ant-row {
|
|
|
+ margin: 0;
|
|
|
+
|
|
|
+ .ant-col {
|
|
|
+ .ant-btn {
|
|
|
+ margin-right: 8px;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-right: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 表格容器
|
|
|
+.ant-table-wrapper {
|
|
|
+ background: #fff;
|
|
|
+ padding: 0;
|
|
|
+ border-radius: 4px;
|
|
|
+}
|
|
|
+
|
|
|
+// 表格样式
|
|
|
+:deep(.ant-table) {
|
|
|
+ .ant-table-thead > tr > th {
|
|
|
+ background-color: #fafafa;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #262626;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-table-tbody > tr > td {
|
|
|
+ padding: 12px 16px;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 按钮图标间距
|
|
|
+.ant-btn {
|
|
|
+ .anticon + span,
|
|
|
+ span + .anticon {
|
|
|
+ margin-left: 4px;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
.service-name {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
gap: 8px;
|
|
|
+ white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
.default-tag {
|
|
|
@@ -574,35 +637,31 @@ getList()
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
- gap: 6px;
|
|
|
- flex-wrap: wrap;
|
|
|
-}
|
|
|
-
|
|
|
-.help-icon {
|
|
|
- color: #999;
|
|
|
- font-size: 14px;
|
|
|
- margin-left: 4px;
|
|
|
- cursor: help;
|
|
|
- vertical-align: middle;
|
|
|
- transition: color 0.2s;
|
|
|
+ gap: 4px;
|
|
|
+ flex-wrap: nowrap;
|
|
|
}
|
|
|
|
|
|
-.help-icon:hover {
|
|
|
- color: #1890ff;
|
|
|
-}
|
|
|
-
|
|
|
-.provider-url {
|
|
|
- float: right;
|
|
|
- color: #999;
|
|
|
- font-size: 12px;
|
|
|
-}
|
|
|
-
|
|
|
-.key-input-wrapper {
|
|
|
- width: 100%;
|
|
|
-}
|
|
|
-
|
|
|
-.view-key-btn {
|
|
|
- margin-top: 4px;
|
|
|
- font-size: 12px;
|
|
|
+// 弹窗表单样式
|
|
|
+:deep(.ant-modal) {
|
|
|
+ .ant-divider {
|
|
|
+ margin: 16px 0 12px;
|
|
|
+
|
|
|
+ .ant-divider-inner-text {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #333;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-form-item {
|
|
|
+ margin-bottom: 16px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .ant-checkbox-group {
|
|
|
+ .ant-checkbox-wrapper {
|
|
|
+ margin-right: 24px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
</style>
|