2026-04-03 09:56:14 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<BasicModal destroyOnClose @register="registerModal" :canFullscreen="false" width="600px" wrapClassName="ai-model-modal">
|
|
|
|
|
|
<div class="modal">
|
|
|
|
|
|
<div class="header">
|
|
|
|
|
|
<span class="header-title">
|
|
|
|
|
|
<span v-if="dataIndex ==='list' || dataIndex ==='add'" :class="dataIndex === 'list' ? '' : 'add-header-title pointer'" @click="goToList">
|
|
|
|
|
|
选择供应商
|
|
|
|
|
|
<a-tooltip title="供应商文档" v-if="dataIndex ==='list'">
|
|
|
|
|
|
<a style="color: #333333" href="https://help.jeecg.com/aigc/guide/model/#2-%E4%BE%9B%E5%BA%94%E5%95%86%E9%80%89%E6%8B%A9" target="_blank">
|
|
|
|
|
|
<Icon style="position:relative;left: -2px;top:1px" icon="ant-design:question-circle-outlined"></Icon>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
<span v-if="dataIndex === 'add'" class="add-header-title"> > </span>
|
|
|
|
|
|
<span v-if="dataIndex === 'add'" style="color: #1f2329">添加 {{ providerName }}</span>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
|
|
|
|
|
|
<a-select v-if="dataIndex === 'list'" :bordered="false" class="header-select" size="small" v-model:value="modelType" @change="handleChange">
|
|
|
|
|
|
<a-select-option v-for="item in modelTypeOption" :value="item.value">{{ item.text }}</a-select-option>
|
|
|
|
|
|
</a-select>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="model-content" v-if="dataIndex === 'list'">
|
|
|
|
|
|
<a-row :span="24">
|
|
|
|
|
|
<a-col :xxl="12" :xl="12" :lg="12" :md="12" :sm="12" :xs="24" v-for="item in modelTypeList">
|
|
|
|
|
|
<a-card class="model-card" @click="handleClick(item)">
|
|
|
|
|
|
<div class="model-header">
|
|
|
|
|
|
<div class="flex">
|
2026-05-18 20:05:03 +08:00
|
|
|
|
<img :src="getImage(item.value)" :class="['header-img', item.value === 'VLLM' ? 'header-img-lg' : '']" />
|
2026-04-03 09:56:14 +08:00
|
|
|
|
<div class="header-text">{{ item.title }}</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-card>
|
|
|
|
|
|
</a-col>
|
|
|
|
|
|
</a-row>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<a-tabs v-model:activeKey="activeKey" v-if="dataIndex === 'add' || dataIndex === 'edit'">
|
|
|
|
|
|
<a-tab-pane :key="1">
|
|
|
|
|
|
<template #tab>
|
|
|
|
|
|
<span style="display: flex">
|
|
|
|
|
|
基础信息
|
|
|
|
|
|
<a-tooltip title="基础信息文档">
|
|
|
|
|
|
<a @click.stop style="color: unset" href="https://help.jeecg.com/aigc/guide/model/#31-%E5%A1%AB%E5%86%99%E5%9F%BA%E7%A1%80%E4%BF%A1%E6%81%AF" target="_blank">
|
|
|
|
|
|
<Icon style="position:relative;left:2px;top:1px" icon="ant-design:question-circle-outlined"></Icon>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<div class="model-content">
|
|
|
|
|
|
<BasicForm @register="registerForm">
|
|
|
|
|
|
<template #modelType="{ model, field }">
|
|
|
|
|
|
<a-select v-model:value="model[field]" @change="handleModelTypeChange" :disabled="modelTypeDisabled">
|
|
|
|
|
|
<a-select-option v-for="item in modelTypeAddOption" :value="item">
|
|
|
|
|
|
<span v-if="item === 'LLM'">语言模型</span>
|
|
|
|
|
|
<span v-else-if="item === 'EMBED'">向量模型</span>
|
|
|
|
|
|
<span v-else-if="item === 'IMAGE'">图像模型</span>
|
|
|
|
|
|
</a-select-option>
|
|
|
|
|
|
</a-select>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2026-05-18 20:05:03 +08:00
|
|
|
|
<template #extraParams="{ model, field }">
|
|
|
|
|
|
<a-input v-model:value="model[field]" readonly placeholder="点击右侧按钮编辑JSON参数">
|
|
|
|
|
|
<template #suffix>
|
|
|
|
|
|
<FullscreenOutlined style="cursor: pointer;" @click="openExtraParamsModal" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</a-input>
|
|
|
|
|
|
<div style="margin-top: 4px; color: #999; font-size: 12px; line-height: 1.5;">
|
|
|
|
|
|
目前只支持图片传递,固定格式为 "image_url":"图片地址"(适用于qwen-vl-ocr等视觉模型)<br/>
|
|
|
|
|
|
在qwen3.5-plus最新视觉模型中需要额外传递 "incremental_output": true
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<a-modal v-model:open="extraParamsVisible" title="编辑额外参数" width="600px" @ok="saveExtraParams" destroyOnClose>
|
|
|
|
|
|
<JCodeEditor v-model:value="extraParamsTemp" language="javascript" fullScreen height="500px" />
|
|
|
|
|
|
</a-modal>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
2026-04-03 09:56:14 +08:00
|
|
|
|
<template #modelName="{ model, field }">
|
|
|
|
|
|
<AutoComplete v-model:value="model[field]" :options="modelNameAddOption" :filter-option="filterOption">
|
|
|
|
|
|
<template #option="{ value, label, descr, type }">
|
|
|
|
|
|
<a-tooltip placement="right" color="#ffffff" :overlayInnerStyle="{ color:'#646a73' }">
|
|
|
|
|
|
<template #title>
|
|
|
|
|
|
<div v-html="getTitle(descr)"></div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<div style="display: flex;justify-content: space-between;">
|
|
|
|
|
|
<span>{{label}}</span>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<a-tag v-if="type && type.split(',').includes('text')" color="#E8D7C3">文本</a-tag>
|
|
|
|
|
|
<a-tag v-if="type && type.split(',').includes('imageGen')" color="#FFEBD3">图像生成</a-tag>
|
|
|
|
|
|
<a-tag v-if="type && type.split(',').includes('image')" color="#C3D9DC">图像分析</a-tag>
|
|
|
|
|
|
<a-tag v-if="type && type.split(',').includes('vector')" color="#D4E0D8">向量</a-tag>
|
|
|
|
|
|
<a-tag v-if="type && type.split(',').includes('embeddings')" color="#FFEBD3">文本嵌入</a-tag>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</AutoComplete>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</BasicForm>
|
|
|
|
|
|
<a-alert v-if="!modelActivate" message="模型未激活,请通过下方「保存并激活」按钮激活当前模型" type="warning" show-icon />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
<a-tab-pane :key="2" v-if="modelParamsShow">
|
|
|
|
|
|
<template #tab>
|
|
|
|
|
|
<span style="display: flex">
|
|
|
|
|
|
高级配置
|
|
|
|
|
|
<a-tooltip title="高级配置文档">
|
|
|
|
|
|
<a @click.stop style="color: unset" href="https://help.jeecg.com/aigc/guide/model/#32-%E9%85%8D%E7%BD%AE%E9%AB%98%E7%BA%A7%E5%8F%82%E6%95%B0" target="_blank">
|
|
|
|
|
|
<Icon style="position:relative;left:2px;top:1px" icon="ant-design:question-circle-outlined"></Icon>
|
|
|
|
|
|
</a>
|
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<AiModelSeniorForm ref="modelParamsRef" :modelParams="modelParams"></AiModelSeniorForm>
|
|
|
|
|
|
</a-tab-pane>
|
|
|
|
|
|
</a-tabs>
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<template v-if="dataIndex === 'add' || dataIndex === 'edit'" #footer>
|
|
|
|
|
|
<a-button @click="cancel">关闭</a-button>
|
|
|
|
|
|
<a-button @click="test" v-if="modelActivate" :loading="testLoading" type="default">测试连接</a-button>
|
|
|
|
|
|
<a-button @click="save" type="primary" ghost="true">保存</a-button>
|
|
|
|
|
|
<a-button @click="test(false)" v-if="!modelActivate" :loading="testLoading" type="primary" >保存并激活</a-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else #footer> </template>
|
|
|
|
|
|
</BasicModal>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts">
|
|
|
|
|
|
import { ref, reactive } from 'vue';
|
|
|
|
|
|
import BasicModal from '@/components/Modal/src/BasicModal.vue';
|
|
|
|
|
|
import { useModal, useModalInner } from '@/components/Modal';
|
|
|
|
|
|
import { initDictOptions } from '@/utils/dict';
|
|
|
|
|
|
import model from './model.json';
|
|
|
|
|
|
import { AutoComplete } from 'ant-design-vue';
|
|
|
|
|
|
|
|
|
|
|
|
import BasicForm from '@/components/Form/src/BasicForm.vue';
|
|
|
|
|
|
import { useForm } from '@/components/Form';
|
|
|
|
|
|
import { formSchema, imageList } from '../model.data';
|
|
|
|
|
|
import { editModel, queryById, saveModel, testConn } from '../model.api';
|
|
|
|
|
|
import { useMessage } from '/@/hooks/web/useMessage';
|
|
|
|
|
|
const {createMessage: $message, createConfirm} = useMessage();
|
2026-05-18 20:05:03 +08:00
|
|
|
|
import { FullscreenOutlined } from '@ant-design/icons-vue';
|
2026-04-03 09:56:14 +08:00
|
|
|
|
import AiModelSeniorForm from './AiModelSeniorForm.vue';
|
2026-05-18 20:05:03 +08:00
|
|
|
|
import JCodeEditor from '/@/components/Form/src/jeecg/components/JCodeEditor.vue';
|
2026-04-03 09:56:14 +08:00
|
|
|
|
import { cloneDeep } from "lodash-es";
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'AddModelModal',
|
|
|
|
|
|
components: {
|
|
|
|
|
|
BasicForm,
|
|
|
|
|
|
BasicModal,
|
|
|
|
|
|
AiModelSeniorForm,
|
|
|
|
|
|
AutoComplete,
|
2026-05-18 20:05:03 +08:00
|
|
|
|
JCodeEditor,
|
|
|
|
|
|
FullscreenOutlined,
|
2026-04-03 09:56:14 +08:00
|
|
|
|
},
|
|
|
|
|
|
emits: ['success', 'register'],
|
|
|
|
|
|
setup(props, { emit }) {
|
|
|
|
|
|
//ai类型数据
|
|
|
|
|
|
const modelTypeData = ref<any>([]);
|
|
|
|
|
|
//模型类型下拉框
|
|
|
|
|
|
const modelTypeOption = ref<any>([]);
|
|
|
|
|
|
//模型类型禁用状态
|
|
|
|
|
|
const modelTypeDisabled = ref<boolean>(false);
|
|
|
|
|
|
//模型类型
|
|
|
|
|
|
const modelType = ref<string>('all');
|
|
|
|
|
|
//模型供应商
|
|
|
|
|
|
const modelTypeList = ref<any>([]);
|
|
|
|
|
|
//list:供应商选择页面,add 添加编辑
|
|
|
|
|
|
const dataIndex = ref<string>('list');
|
|
|
|
|
|
//供应商名称
|
|
|
|
|
|
const providerName = ref<string>('');
|
|
|
|
|
|
//添加模型类型的option
|
|
|
|
|
|
const modelTypeAddOption = ref<any>([]);
|
|
|
|
|
|
//添加模型名称的option
|
|
|
|
|
|
const modelNameAddOption = ref<any>([]);
|
|
|
|
|
|
//模型数据
|
|
|
|
|
|
const modelData = ref<any>({});
|
|
|
|
|
|
//tab切换对应的key
|
|
|
|
|
|
const activeKey = ref<number>(1);
|
|
|
|
|
|
//模型参数
|
|
|
|
|
|
const modelParams = ref<any>({});
|
|
|
|
|
|
//是否显示模型参数
|
|
|
|
|
|
const modelParamsShow = ref<boolean>(false);
|
|
|
|
|
|
//模型参数ref
|
|
|
|
|
|
const modelParamsRef = ref();
|
|
|
|
|
|
//测试按钮loading状态
|
|
|
|
|
|
const testLoading = ref<boolean>(false);
|
|
|
|
|
|
//模型是否已激活
|
|
|
|
|
|
const modelActivate = ref<boolean>(false);
|
2026-05-18 20:05:03 +08:00
|
|
|
|
//特殊参数
|
|
|
|
|
|
const extraParamsVisible = ref<boolean>(false);
|
|
|
|
|
|
const extraParamsTemp = ref<string>('');
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 打开特殊参数编辑弹窗
|
|
|
|
|
|
*/
|
|
|
|
|
|
function openExtraParamsModal() {
|
|
|
|
|
|
const formVal = getFieldsValue();
|
|
|
|
|
|
let val = formVal.extraParams || '';
|
|
|
|
|
|
if (val) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
val = JSON.stringify(JSON.parse(val), null, 2);
|
|
|
|
|
|
} catch (e) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
extraParamsTemp.value = val;
|
|
|
|
|
|
extraParamsVisible.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 保存特殊参数
|
|
|
|
|
|
*/
|
|
|
|
|
|
function saveExtraParams() {
|
|
|
|
|
|
const val = extraParamsTemp.value;
|
|
|
|
|
|
if (val) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
JSON.parse(val);
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
$message.error('JSON格式不正确,请检查');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
setFieldsValue({ extraParams: val });
|
|
|
|
|
|
extraParamsVisible.value = false;
|
|
|
|
|
|
}
|
2026-04-03 09:56:14 +08:00
|
|
|
|
|
|
|
|
|
|
const getImage = (name) => {
|
|
|
|
|
|
return imageList.value[name];
|
|
|
|
|
|
};
|
|
|
|
|
|
//自动填充文本搜索事件
|
|
|
|
|
|
const filterOption = (input: string, option: any)=>{
|
|
|
|
|
|
return option.value.toUpperCase().indexOf(input.toUpperCase()) >= 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//表单配置
|
2026-05-18 20:05:03 +08:00
|
|
|
|
const [registerForm, { resetFields, setFieldsValue, getFieldsValue, validate, clearValidate }] = useForm({
|
2026-04-03 09:56:14 +08:00
|
|
|
|
schemas: formSchema,
|
|
|
|
|
|
showActionButtonGroup: false,
|
|
|
|
|
|
layout: 'vertical',
|
|
|
|
|
|
wrapperCol: { span: 24 },
|
2026-05-18 20:05:03 +08:00
|
|
|
|
labelCol: { span: 24 },
|
2026-04-03 09:56:14 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//注册modal
|
|
|
|
|
|
const [registerModal, { closeModal, setModalProps }] = useModalInner(async (data) => {
|
|
|
|
|
|
activeKey.value = 1;
|
|
|
|
|
|
modelParamsShow.value = false;
|
|
|
|
|
|
if(dataIndex.value !== 'list') {
|
|
|
|
|
|
//重置表单
|
|
|
|
|
|
await resetFields();
|
|
|
|
|
|
}
|
|
|
|
|
|
setModalProps({ minHeight: 500 });
|
|
|
|
|
|
if (data.id) {
|
|
|
|
|
|
dataIndex.value = 'edit';
|
|
|
|
|
|
let values = await queryById({ id: data.id });
|
|
|
|
|
|
if (values) {
|
|
|
|
|
|
if(values.result.credential){
|
|
|
|
|
|
let credential = JSON.parse(values.result.credential);
|
|
|
|
|
|
if(credential.secretKey){
|
|
|
|
|
|
values.result.secretKey = credential.secretKey;
|
|
|
|
|
|
}
|
|
|
|
|
|
if(credential.apiKey){
|
|
|
|
|
|
values.result.apiKey = credential.apiKey;
|
|
|
|
|
|
}
|
2026-05-18 20:05:03 +08:00
|
|
|
|
if (credential.httpVersionOne) {
|
|
|
|
|
|
values.result.httpVersionOne = credential.httpVersionOne;
|
|
|
|
|
|
}
|
2026-04-03 09:56:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
let provider = values.result.provider;
|
|
|
|
|
|
let data = model.data.filter((item) => {
|
|
|
|
|
|
return item.value.includes(provider);
|
|
|
|
|
|
});
|
|
|
|
|
|
if (data && data.length > 0) {
|
|
|
|
|
|
modelTypeAddOption.value = data[0].type;
|
|
|
|
|
|
modelNameAddOption.value = data[0][values.result.modelType];
|
|
|
|
|
|
}
|
|
|
|
|
|
if(values.result.modelType && values.result.modelType === 'LLM'){
|
|
|
|
|
|
modelParamsShow.value = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (values.result.activateFlag) {
|
|
|
|
|
|
modelActivate.value = true;
|
|
|
|
|
|
}else{
|
|
|
|
|
|
modelActivate.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if(values.result.modelParams){
|
2026-05-18 20:05:03 +08:00
|
|
|
|
let allParams = JSON.parse(values.result.modelParams);
|
|
|
|
|
|
if (allParams.extraParams) {
|
|
|
|
|
|
values.result.extraParams = JSON.stringify(allParams.extraParams, null, 2);
|
|
|
|
|
|
delete allParams.extraParams;
|
|
|
|
|
|
}
|
|
|
|
|
|
modelParams.value = allParams;
|
2026-04-03 09:56:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
modelTypeDisabled.value = true;
|
|
|
|
|
|
//表单赋值
|
|
|
|
|
|
await setFieldsValue({
|
|
|
|
|
|
...values.result,
|
|
|
|
|
|
});
|
|
|
|
|
|
//初始化模型提供者
|
|
|
|
|
|
initModelProvider();
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
modelTypeDisabled.value = false;
|
|
|
|
|
|
//初始化模型提供者
|
|
|
|
|
|
initModelProvider();
|
|
|
|
|
|
dataIndex.value = 'list';
|
|
|
|
|
|
modelNameAddOption.value = [];
|
|
|
|
|
|
modelActivate.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
//初始化模型类型
|
|
|
|
|
|
initModelTypeOption();
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化 模型类型字典
|
|
|
|
|
|
*/
|
|
|
|
|
|
function initModelTypeOption() {
|
|
|
|
|
|
initDictOptions('model_type').then((data) => {
|
|
|
|
|
|
modelTypeOption.value = cloneDeep(data);
|
|
|
|
|
|
//update-begin---author:wangshuai---date:2025-03-04---for: 解决页面tab刷新一次就多一个全部类型的选项---
|
|
|
|
|
|
if(data[0].value != 'all'){
|
|
|
|
|
|
modelTypeOption.value.unshift({
|
|
|
|
|
|
text: '全部类型',
|
|
|
|
|
|
value: 'all',
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
//update-end---author:wangshuai---date:2025-03-04---for: 解决页面tab刷新一次就多一个全部类型的选项---
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 下拉框值选中事件
|
|
|
|
|
|
* @param value
|
|
|
|
|
|
*/
|
|
|
|
|
|
function handleChange(value) {
|
|
|
|
|
|
if ('all' == value) {
|
|
|
|
|
|
modelTypeList.value = model.data;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
let data = model.data.filter((item) => {
|
|
|
|
|
|
return item.type.includes(value);
|
|
|
|
|
|
});
|
|
|
|
|
|
modelTypeList.value = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 初始化模型提供者
|
|
|
|
|
|
*/
|
|
|
|
|
|
function initModelProvider() {
|
|
|
|
|
|
modelTypeList.value = model.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 供应商点击事件
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param item
|
|
|
|
|
|
*/
|
|
|
|
|
|
function handleClick(item) {
|
|
|
|
|
|
dataIndex.value = 'add';
|
|
|
|
|
|
modelData.value = item;
|
|
|
|
|
|
providerName.value = item.title;
|
|
|
|
|
|
modelTypeAddOption.value = item.type;
|
|
|
|
|
|
setTimeout(()=>{
|
|
|
|
|
|
setFieldsValue({ 'provider': item.value, 'baseUrl': item.baseUrl })
|
|
|
|
|
|
},100)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 保存
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function save() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
setModalProps({ confirmLoading: true });
|
|
|
|
|
|
let values = await validate();
|
|
|
|
|
|
let credential = {
|
|
|
|
|
|
apiKey: values.apiKey,
|
2026-05-18 20:05:03 +08:00
|
|
|
|
secretKey: values.secretKey,
|
|
|
|
|
|
httpVersionOne: values.httpVersionOne,
|
2026-04-03 09:56:14 +08:00
|
|
|
|
}
|
2026-05-18 20:05:03 +08:00
|
|
|
|
let params = {};
|
2026-04-03 09:56:14 +08:00
|
|
|
|
if(modelParamsRef.value){
|
2026-05-18 20:05:03 +08:00
|
|
|
|
let seniorParams = modelParamsRef.value.emitChange();
|
|
|
|
|
|
if(seniorParams){
|
|
|
|
|
|
params = { ...seniorParams };
|
2026-04-03 09:56:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-18 20:05:03 +08:00
|
|
|
|
if (values.extraParams) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
params.extraParams = JSON.parse(values.extraParams);
|
|
|
|
|
|
} catch(e) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Object.keys(params).length > 0) {
|
|
|
|
|
|
values.modelParams = JSON.stringify(params);
|
|
|
|
|
|
}
|
|
|
|
|
|
delete values.extraParams;
|
2026-04-03 09:56:14 +08:00
|
|
|
|
if(modelActivate.value){
|
|
|
|
|
|
values.activateFlag = 1
|
|
|
|
|
|
}else{
|
|
|
|
|
|
values.activateFlag = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
values.credential = JSON.stringify(credential);
|
|
|
|
|
|
//新增
|
|
|
|
|
|
if (!values.id) {
|
|
|
|
|
|
values.provider = modelData.value.value;
|
|
|
|
|
|
await saveModel(values);
|
|
|
|
|
|
closeModal();
|
|
|
|
|
|
emit('success');
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await editModel(values);
|
|
|
|
|
|
closeModal();
|
|
|
|
|
|
emit('success');
|
|
|
|
|
|
}
|
|
|
|
|
|
}catch(e){
|
|
|
|
|
|
if(e.hasOwnProperty('errorFields')){
|
|
|
|
|
|
activeKey.value = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
setModalProps({ confirmLoading: false });
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 取消
|
|
|
|
|
|
*/
|
|
|
|
|
|
function cancel() {
|
|
|
|
|
|
dataIndex.value = 'list';
|
|
|
|
|
|
closeModal();
|
|
|
|
|
|
emit('success');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 测试连接
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function test(onlyTest = false) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
testLoading.value = true;
|
|
|
|
|
|
let values = await validate();
|
|
|
|
|
|
let credential = {
|
|
|
|
|
|
apiKey: values.apiKey,
|
|
|
|
|
|
secretKey: values.secretKey,
|
2026-05-18 20:05:03 +08:00
|
|
|
|
httpVersionOne: values.httpVersionOne,
|
2026-04-03 09:56:14 +08:00
|
|
|
|
};
|
2026-05-18 20:05:03 +08:00
|
|
|
|
let params = {};
|
2026-04-03 09:56:14 +08:00
|
|
|
|
if (modelParamsRef.value) {
|
2026-05-18 20:05:03 +08:00
|
|
|
|
//update-begin---author:wangshuai---date:2026-03-20---for:【issues/8】保存激活qwen-vl-ocr模型报错---
|
|
|
|
|
|
let seniorParams = modelParamsRef.value.emitChange();
|
|
|
|
|
|
if (seniorParams) {
|
|
|
|
|
|
params = { ...seniorParams };
|
|
|
|
|
|
//update-end---author:wangshuai---date:2026-03-20---for:【issues/8】保存激活qwen-vl-ocr模型报错---
|
2026-04-03 09:56:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-18 20:05:03 +08:00
|
|
|
|
if (values.extraParams) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
params.extraParams = JSON.parse(values.extraParams);
|
|
|
|
|
|
} catch(e) {}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (Object.keys(params).length > 0) {
|
|
|
|
|
|
values.modelParams = JSON.stringify(params);
|
|
|
|
|
|
}
|
|
|
|
|
|
delete values.extraParams;
|
2026-04-03 09:56:14 +08:00
|
|
|
|
values.credential = JSON.stringify(credential);
|
|
|
|
|
|
if (!values.provider) {
|
|
|
|
|
|
values.provider = modelData.value.value;
|
|
|
|
|
|
}
|
|
|
|
|
|
//测试
|
|
|
|
|
|
await testConn(values).then(async (result) => {
|
|
|
|
|
|
if(onlyTest){
|
|
|
|
|
|
$message.success('测试连接成功');
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
modelActivate.value = true;
|
|
|
|
|
|
await save();
|
|
|
|
|
|
});
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
if (e.hasOwnProperty('errorFields')) {
|
|
|
|
|
|
activeKey.value = 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
testLoading.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 模型类型选择事件
|
|
|
|
|
|
* @param value
|
|
|
|
|
|
*/
|
|
|
|
|
|
async function handleModelTypeChange(value) {
|
|
|
|
|
|
await setFieldsValue({ modelName: '' });
|
|
|
|
|
|
await clearValidate('modelName');
|
|
|
|
|
|
await setFieldsValue({
|
|
|
|
|
|
modelName: modelData.value[value+'DefaultValue']
|
|
|
|
|
|
})
|
|
|
|
|
|
modelNameAddOption.value = modelData.value[value];
|
|
|
|
|
|
if(value === 'LLM'){
|
|
|
|
|
|
modelParamsShow.value = true;
|
|
|
|
|
|
}else{
|
|
|
|
|
|
modelParamsShow.value = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if(value === "IMAGE" && modelData.value.baseImageUrl){
|
|
|
|
|
|
setFieldsValue({ 'baseUrl': modelData.value.baseImageUrl })
|
|
|
|
|
|
} else if(modelData.value.baseUrl) {
|
|
|
|
|
|
setFieldsValue({ 'baseUrl': modelData.value.baseUrl })
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 选择供应商
|
|
|
|
|
|
*/
|
|
|
|
|
|
function goToList() {
|
|
|
|
|
|
if (dataIndex.value === 'add') {
|
|
|
|
|
|
dataIndex.value = 'list';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取标题
|
|
|
|
|
|
* @param title
|
|
|
|
|
|
*/
|
|
|
|
|
|
function getTitle(title) {
|
|
|
|
|
|
if(!title){
|
|
|
|
|
|
return "暂无描述内容";
|
|
|
|
|
|
}
|
|
|
|
|
|
return title.replaceAll("\n","<br>")
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
registerModal,
|
|
|
|
|
|
modelTypeData,
|
|
|
|
|
|
modelTypeOption,
|
|
|
|
|
|
modelType,
|
|
|
|
|
|
handleChange,
|
|
|
|
|
|
modelTypeList,
|
|
|
|
|
|
getImage,
|
|
|
|
|
|
handleClick,
|
|
|
|
|
|
dataIndex,
|
|
|
|
|
|
providerName,
|
|
|
|
|
|
save,
|
|
|
|
|
|
cancel,
|
|
|
|
|
|
registerForm,
|
|
|
|
|
|
handleModelTypeChange,
|
|
|
|
|
|
modelTypeAddOption,
|
|
|
|
|
|
modelNameAddOption,
|
|
|
|
|
|
goToList,
|
|
|
|
|
|
modelTypeDisabled,
|
|
|
|
|
|
activeKey,
|
|
|
|
|
|
modelParams,
|
|
|
|
|
|
modelParamsShow,
|
|
|
|
|
|
modelActivate,
|
|
|
|
|
|
modelParamsRef,
|
|
|
|
|
|
filterOption,
|
|
|
|
|
|
getTitle,
|
|
|
|
|
|
test,
|
|
|
|
|
|
testLoading,
|
2026-05-18 20:05:03 +08:00
|
|
|
|
extraParamsVisible,
|
|
|
|
|
|
extraParamsTemp,
|
|
|
|
|
|
openExtraParamsModal,
|
|
|
|
|
|
saveExtraParams,
|
2026-04-03 09:56:14 +08:00
|
|
|
|
};
|
|
|
|
|
|
},
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="less">
|
|
|
|
|
|
.modal {
|
|
|
|
|
|
padding: 12px 20px 20px 20px;
|
|
|
|
|
|
.header {
|
|
|
|
|
|
padding: 0 24px 24px 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
.header-title {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: bold;
|
|
|
|
|
|
}
|
|
|
|
|
|
.header-select {
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.add-header-title {
|
|
|
|
|
|
color: #646a73;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.model-content {
|
|
|
|
|
|
.model-header {
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
.header-img {
|
|
|
|
|
|
width: 32px;
|
|
|
|
|
|
height: 32px;
|
|
|
|
|
|
margin-right: 12px;
|
2026-05-18 20:05:03 +08:00
|
|
|
|
object-fit: contain;
|
|
|
|
|
|
}
|
|
|
|
|
|
.header-img-lg {
|
|
|
|
|
|
width: 48px;
|
|
|
|
|
|
height: 48px;
|
2026-04-03 09:56:14 +08:00
|
|
|
|
}
|
|
|
|
|
|
.header-text {
|
|
|
|
|
|
width: calc(100% - 80px);
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
align-content: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.model-card {
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
:deep(.ant-card .ant-card-body) {
|
|
|
|
|
|
padding: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.pointer {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
:deep(.jeecg-basic-modal-close){
|
|
|
|
|
|
span{
|
|
|
|
|
|
margin-left: 0 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|
|
|
|
|
|
<style lang="less">
|
|
|
|
|
|
.ai-model-modal{
|
|
|
|
|
|
.jeecg-basic-modal-close > span{
|
|
|
|
|
|
margin-left: 0 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|