AI人工智能前端页面
AI配置:AI平台信息、AI模型配置、AI知识库类型
AI技能:AI问答
master
xs 4 months ago
parent 47fecb911f
commit c6dae4d699

@ -0,0 +1,77 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { AiKnowledgeBaseTypeVO, AiKnowledgeBaseTypeForm, AiKnowledgeBaseTypeQuery } from '@/api/ai/base/aiKnowledgeBaseType/types';
/**
* AI
* @param query
* @returns {*}
*/
export const listAiKnowledgeBaseType = (query?: AiKnowledgeBaseTypeQuery): AxiosPromise<AiKnowledgeBaseTypeVO[]> => {
return request({
url: '/ai/aiKnowledgeBaseType/list',
method: 'get',
params: query
});
};
/**
* AI
* @param knowledgeBaseTypeId
*/
export const getAiKnowledgeBaseType = (knowledgeBaseTypeId: string | number): AxiosPromise<AiKnowledgeBaseTypeVO> => {
return request({
url: '/ai/aiKnowledgeBaseType/' + knowledgeBaseTypeId,
method: 'get'
});
};
/**
* AI
* @param data
*/
export const addAiKnowledgeBaseType = (data: AiKnowledgeBaseTypeForm) => {
return request({
url: '/ai/aiKnowledgeBaseType',
method: 'post',
data: data
});
};
/**
* AI
* @param data
*/
export const updateAiKnowledgeBaseType = (data: AiKnowledgeBaseTypeForm) => {
return request({
url: '/ai/aiKnowledgeBaseType',
method: 'put',
data: data
});
};
/**
* AI
* @param knowledgeBaseTypeId
*/
export const delAiKnowledgeBaseType = (knowledgeBaseTypeId: string | number | Array<string | number>) => {
return request({
url: '/ai/aiKnowledgeBaseType/' + knowledgeBaseTypeId,
method: 'delete'
});
};
/**
* AI
* @param query
* @returns {*}
*/
export function getAiKnowledgeBaseTypeList (query) {
return request({
url: '/ai/aiKnowledgeBaseType/getAiKnowledgeBaseTypeList',
method: 'get',
params: query
});
};

@ -0,0 +1,56 @@
export interface AiKnowledgeBaseTypeVO {
/**
*
*/
knowledgeBaseTypeId: string | number;
/**
*
*/
knowledgeBaseTypeName: string;
/**
*
*/
remark: string;
}
export interface AiKnowledgeBaseTypeForm extends BaseEntity {
/**
*
*/
knowledgeBaseTypeId?: string | number;
/**
*
*/
knowledgeBaseTypeName?: string;
/**
*
*/
remark?: string;
}
export interface AiKnowledgeBaseTypeQuery extends PageQuery {
/**
*
*/
knowledgeBaseTypeId?: string | number;
/**
*
*/
knowledgeBaseTypeName?: string;
/**
*
*/
params?: any;
}

@ -0,0 +1,92 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { AiModelVO, AiModelForm, AiModelQuery } from '@/api/ai/base/aiModel/types';
/**
* AI
* @param query
* @returns {*}
*/
export const listAiModel = (query?: AiModelQuery): AxiosPromise<AiModelVO[]> => {
return request({
url: '/ai/aiModel/list',
method: 'get',
params: query
});
};
/**
* AI
* @param modelId
*/
export const getAiModel = (modelId: string | number): AxiosPromise<AiModelVO> => {
return request({
url: '/ai/aiModel/' + modelId,
method: 'get'
});
};
/**
* AI
* @param data
*/
export const addAiModel = (data: AiModelForm) => {
return request({
url: '/ai/aiModel',
method: 'post',
data: data
});
};
/**
* AI
* @param data
*/
export const updateAiModel = (data: AiModelForm) => {
return request({
url: '/ai/aiModel',
method: 'put',
data: data
});
};
/**
* AI
* @param modelId
*/
export const delAiModel = (modelId: string | number | Array<string | number>) => {
return request({
url: '/ai/aiModel/' + modelId,
method: 'delete'
});
};
/**
* AI
* @param query
* @returns {*}
*/
export function getAiModelList (query) {
return request({
url: '/ai/aiModel/getAiModelList',
method: 'get',
params: query
});
};
/**
* AIAPI
* @returns {*}
* @param provider
* @param data
*/
export function testAIModel (provider,data) {
return request({
url: '/ai/aiModel/testAIModel?provider='+provider,
method: 'post',
data: data
});
};

@ -0,0 +1,286 @@
export interface AiModelVO {
/**
*
*/
modelId: string | number;
/**
*
*/
modelName: string;
/**
* AIIDai_platform
*/
platformId: string | number;
/**
* IDai_model_type
*/
modelTypeId: string | number;
/**
* IDai_base_model
*/
baseModelId: string | number;
/**
* (1token2)
*/
chargeType: string;
/**
*
*/
modelPrice: number;
/**
*
*/
apiEndpoint: string;
/**
*
*/
apiKey: string;
/**
*
*/
apiSecret: string;
/**
* (1milvus2weaviate,)
*/
vectorLibrary: string;
/**
*
*/
description: string;
/**
* (0-1);00.5-0.8
*/
modelTemperature: number;
/**
* (0-1);AIAI
*/
vocabularyAttribute: number;
/**
* (-2.0-2.0);AI
*/
topicAttribute: number;
/**
* (-2.0-2.0);AI
*/
duplicateAttribute: number;
/**
* (1-16000);AI500-800800-20002000-36004000
*/
maxReply: number;
/**
* (10)
*/
activeFlag: string;
}
export interface AiModelForm extends BaseEntity {
/**
*
*/
modelId?: string | number;
/**
*
*/
modelName?: string;
/**
* AIIDai_platform
*/
platformId?: string | number;
/**
* IDai_model_type
*/
modelTypeId?: string | number;
/**
* IDai_base_model
*/
baseModelId?: string | number;
/**
* (1token2)
*/
chargeType?: string;
/**
*
*/
modelPrice?: number;
/**
*
*/
apiEndpoint?: string;
/**
*
*/
apiKey?: string;
/**
*
*/
apiSecret?: string;
/**
* (1milvus2weaviate,)
*/
vectorLibrary?: string;
/**
*
*/
description?: string;
/**
* (0-1);00.5-0.8
*/
modelTemperature?: number;
/**
* (0-1);AIAI
*/
vocabularyAttribute?: number;
/**
* (-2.0-2.0);AI
*/
topicAttribute?: number;
/**
* (-2.0-2.0);AI
*/
duplicateAttribute?: number;
/**
* (1-16000);AI500-800800-20002000-36004000
*/
maxReply?: number;
/**
* (10)
*/
activeFlag?: string;
}
export interface AiModelQuery extends PageQuery {
/**
*
*/
modelId?: string | number;
/**
*
*/
modelName?: string;
/**
* AIIDai_platform
*/
platformId?: string | number;
/**
* IDai_model_type
*/
modelTypeId?: string | number;
/**
* IDai_base_model
*/
baseModelId?: string | number;
/**
* (1token2)
*/
chargeType?: string;
/**
*
*/
modelPrice?: number;
/**
*
*/
apiEndpoint?: string;
/**
*
*/
apiKey?: string;
/**
*
*/
apiSecret?: string;
/**
* (1milvus2weaviate,)
*/
vectorLibrary?: string;
/**
*
*/
description?: string;
/**
* (0-1);00.5-0.8
*/
modelTemperature?: number;
/**
* (0-1);AIAI
*/
vocabularyAttribute?: number;
/**
* (-2.0-2.0);AI
*/
topicAttribute?: number;
/**
* (-2.0-2.0);AI
*/
duplicateAttribute?: number;
/**
* (1-16000);AI500-800800-20002000-36004000
*/
maxReply?: number;
/**
* (10)
*/
activeFlag?: string;
/**
*
*/
params?: any;
}

@ -0,0 +1,97 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { AiPlatformVO, AiPlatformForm, AiPlatformQuery } from '@/api/ai/base/aiPlatform/types';
/**
* AI
* @param query
* @returns {*}
*/
export const listAiPlatform = (query?: AiPlatformQuery): AxiosPromise<AiPlatformVO[]> => {
return request({
url: '/ai/base/aiPlatform/list',
method: 'get',
params: query
});
};
/**
* AI
* @param query
* @returns {*}
*/
export const listAiBaseModels = (query?: AiPlatformQuery): AxiosPromise<AiPlatformVO[]> => {
return request({
url: '/ai/base/aiPlatform/getAiBaseModelList',
method: 'get',
params: query
});
};
/**
* AI
* @param platformId
*/
export const getAiPlatform = (platformId: string | number): AxiosPromise<AiPlatformVO> => {
return request({
url: '/ai/base/aiPlatform/' + platformId,
method: 'get'
});
};
/**
* AI
* @param data
*/
export const addAiPlatform = (data: AiPlatformForm) => {
return request({
url: '/ai/base/aiPlatform',
method: 'post',
data: data
});
};
/**
* AI
* @param data
*/
export const updateAiPlatform = (data: AiPlatformForm) => {
return request({
url: '/ai/base/aiPlatform',
method: 'put',
data: data
});
};
/**
* AI
* @param platformId
*/
export const delAiPlatform = (platformId: string | number | Array<string | number>) => {
return request({
url: '/ai/base/aiPlatform/' + platformId,
method: 'delete'
});
};
/**
* AI
* @param query
* @returns {*}
*/
export function getAiPlatformList (query) {
return request({
url: '/ai/base/aiPlatform/getAiPlatformList',
method: 'get',
params: query
});
};

@ -0,0 +1,106 @@
export interface AiPlatformVO {
/**
* ID
*/
platformId: string | number;
/**
* DeepSeek, , AI
*/
platformName: string;
/**
*
*/
platformDescription: string;
/**
* base api url
*/
baseApiUrl: string;
/**
*
*/
platformIcon: string;
/**
* 1,API_KEY,2,OAUTH2,API_KEY
*/
authMethod: string;
}
export interface AiPlatformForm extends BaseEntity {
/**
* ID
*/
platformId?: string | number;
/**
* DeepSeek, , AI
*/
platformName?: string;
/**
*
*/
platformDescription?: string;
/**
* base api url
*/
baseApiUrl?: string;
/**
*
*/
platformIcon?: string;
/**
* 1,API_KEY,2,OAUTH2,API_KEY
*/
authMethod?: string;
}
export interface AiPlatformQuery extends PageQuery {
/**
* ID
*/
platformId?: string | number;
/**
* DeepSeek, , AI
*/
platformName?: string;
/**
*
*/
platformDescription?: string;
/**
* base api url
*/
baseApiUrl?: string;
/**
*
*/
platformIcon?: string;
/**
* 1,API_KEY,2,OAUTH2,API_KEY
*/
authMethod?: string;
/**
*
*/
params?: any;
}

@ -0,0 +1,136 @@
import request from '@/utils/request';
import { AxiosPromise } from 'axios';
import { AskVO } from '@/api/ai/skill/aiChat/types';
import EventSourcePolyfill from 'event-source-polyfill';
import { AiChatMessageForm } from '@/api/ai/skill/aiChat/types';
/**
* AI
* @param query
* @returns {*}
*/
export function getAiModelJoinList (query) {
return request({
url: '/ai/assistant/getAiModelJoinList',
method: 'get',
params: query
});
};
/**
* AI
* @param query
* @returns {*}
*/
export function getAiChatMessageList (query) {
return request({
url: '/ai/assistant/getAiChatMessageList',
method: 'get',
params: query
});
};
/**
* ID
* @param query
* @returns {*}
*/
export function getAiChatMessages (sessionId: string) {
return request({
url: '/ai/assistant/getAiChatMessages/'+sessionId,
method: 'get',
});
};
/**
* AI
* @param data
*/
export const updateAiMessageTopic = (data: AiChatMessageForm) => {
return request({
url: '/ai/assistant/updateAiChatMessageTopic',
method: 'post',
data: data
});
};
/**
* AI
* @param sessionId
*/
export const delAiMessage = (sessionId: string) => {
return request({
url: '/ai/assistant/' + sessionId,
method: 'delete'
});
};
/**
* AI
* @param query
* @returns {*}
*/
export const aiAsk = (query?: AskVO): AxiosPromise<AskVO[]> => {
return request({
url: '/ai/api/stream/ask',
method: 'get',
params: query
});
};
export const aiStreamAsk = (query?: AskVO): AxiosPromise<AskVO[]> => {
return request({
url: '/ai/api/stream/stream',
method: 'post',
params: query
});
};
export const aiSqlAsk = (query?: AskVO): AxiosPromise<AskVO[]> => {
return request({
url: '/ai/api/chat/sql',
method: 'post',
params: query
});
};
export const streamDeepSeek = (prompt, handlers) => {
const url = `/ai/api/stream/stream?prompt=${encodeURIComponent(prompt)}`;
const eventSource = new EventSourcePolyfill(url, {
headers: {
'Content-Type': 'text/event-stream'
},
withCredentials: false, // 根据你的CORS配置调整
heartbeatTimeout: 300000 // 5分钟超时
});
eventSource.onmessage = (event) => {
if (event.data === '[DONE]') {
handlers.onComplete();
eventSource.close();
} else {
try {
handlers.onData(event.data);
} catch (e) {
console.error('Error processing chunk:', e);
}
}
};
eventSource.onerror = (error) => {
handlers.onError(new Error(`EventSource failed: ${error.message || 'Unknown error'}`));
eventSource.close();
};
return () => {
eventSource.close();
};
};

@ -0,0 +1,100 @@
export interface AskVO {
/**
*
*/
question: string;
/**
* api
*/
provider: string;
}
export interface AIModelVO {
/**
* ID
*/
modelId: number;
/**
*
*/
modelType: string;
/**
*
*/
modelName: string;
/**
*
*/
platformCode: string;
/**
*
*/
platformIcon: string;
/**
*
*/
defaultFlag: string;
}
export interface AiChatMessageForm extends BaseEntity {
/**
*
*/
chatMessageId?: string | number;
/**
* ID
*/
sessionId?: string;
/**
*
*/
messageTopic?: string;
/**
*
*/
deductCost?: number;
/**
* Tokens
*/
totalToken?: number;
/**
* IDai_model
*/
modelId?: string | number;
/**
* IDai_knowledge_base
*/
knowledgeBaseId?: string | number;
/**
* (1AI23AISQL,4AI)
*/
messageType?: string;
/**
*
*/
remark?: string;
}

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1752731920240" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="14461" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M826.368 325.632c0-7.168 2.048-10.24 10.24-10.24h123.904c7.168 0 10.24 2.048 10.24 10.24v621.568c0 7.168-2.048 10.24-10.24 10.24h-122.88c-8.192 0-10.24-4.096-10.24-10.24l-1.024-621.568z m-8.192-178.176c0-50.176 35.84-79.872 79.872-79.872 48.128 0 79.872 32.768 79.872 79.872 0 52.224-33.792 79.872-81.92 79.872-46.08 1.024-77.824-27.648-77.824-79.872zM462.848 584.704C441.344 497.664 389.12 307.2 368.64 215.04h-2.048c-16.384 92.16-58.368 247.808-92.16 369.664h188.416zM243.712 712.704l-62.464 236.544c-2.048 7.168-4.096 8.192-12.288 8.192H54.272c-8.192 0-10.24-2.048-8.192-12.288l224.256-783.36c4.096-13.312 7.168-26.624 8.192-65.536 0-6.144 2.048-8.192 7.168-8.192H450.56c6.144 0 8.192 2.048 10.24 8.192l250.88 849.92c2.048 7.168 0 10.24-7.168 10.24H573.44c-7.168 0-10.24-2.048-12.288-7.168l-65.536-236.544c1.024 1.024-251.904 0-251.904 0z" fill="#FF7F18" p-id="14462"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1755235660914" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="27180" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M490.08 116.288L868.736 928.32a32 32 0 1 0 58.016-27.072L548.064 89.28a32 32 0 1 0-57.984 27.04z" fill="#FAAC08" p-id="27181"></path><path d="M550.176 116.288L171.488 928.32a32 32 0 1 1-57.984-27.072L492.16 89.28a32 32 0 0 1 58.016 27.04z" fill="#1C1C1E" p-id="27182"></path></svg>

After

Width:  |  Height:  |  Size: 616 B

@ -335,12 +335,26 @@ export const dynamicRoutes: RouteRecordRaw[] = [
hidden: true,
permissions: ['mes:orderInfo:edit'],
children: [
// {
// path: 'index/:productOrderId(\\d+)',
// component: () => import('@/views/mes/orderInfo/editProductOrder.vue'),
// name: 'productOrderEdit',
// meta: { title: '生产任务', activeMenu: '/mes/productplan', noCache: true }
// }
{
path: 'index/:productOrderId(\\d+)',
component: () => import('@/views/mes/orderInfo/editProductOrder.vue'),
name: 'productOrderEdit',
meta: { title: '生产任务', activeMenu: '/mes/orderInfo', noCache: true }
}
]
},
{
path: '/mes/product-order',
component: Layout,
hidden: true,
permissions: ['mes:orderInfo:edit'],
children: [
{
path: 'index',
component: () => import('@/views/mes/orderInfo/batchAddOrder.vue'),
name: 'batchAddOrder',
meta: { title: '生产任务', activeMenu: '/mes/orderInfo', noCache: true }
}
]
},
{
@ -502,7 +516,63 @@ export const dynamicRoutes: RouteRecordRaw[] = [
meta: { title: '保养工单申请', activeMenu: '/dms/maint/maintEdit', noCache: true }
}
]
}
},
{
path: '/ai/skill/knowledgeBaseDocs',
component: Layout,
hidden: true,
permissions: ['ai:aiKnowledgeBaseDocs:list'],
children: [
{
path: 'index',
component: () => import('@/views/ai/skill/aiKnowledge/knowledgeBaseDocs.vue'),
name: 'KnowledgeBaseDocs',
meta: { title: '知识库详情', activeMenu: '/ai/skill/aiKnowledge', noCache: true }
}
]
},
{
path: '/ai/skill/knowledgeBaseQA',
component: Layout,
hidden: true,
permissions: ['ai:aiKnowledgeBaseDocs:qa'],
children: [
{
path: 'index',
component: () => import('@/views/ai/skill/aiChat/index.vue'),
name: 'KnowledgeBaseQA',
meta: { title: '知识库问答', activeMenu: '/ai/skill/aiKnowledge', noCache: true }
}
]
},
// {
// path: '/knowledge-base-preview',
// name: 'KnowledgeBasePreview',
// component: () => import('@/views/ai/skill/aiKnowledge/KnowledgeBasePreview.vue'),
// hidden: true
// },
// {
// path: '/knowledge-base-upload',
// name: 'KnowledgeBaseUpload',
// component: () => import('@/views/ai/skill/aiKnowledge/KnowledgeBaseUpload.vue'),
// hidden: true
// },
// {
// path: '/knowledge-base-qa',
// name: 'KnowledgeBaseQA',
// component: () => import('@/views/ai/skill/aiKnowledge/KnowledgeBaseQA.vue'),
// hidden: true
// },
// {
// path: '/ai-chat-settings',
// name: 'AiChatSettings',
// component: () => import('@/views/ai/base/aiApp/aiAppConfig.vue'),
// hidden: true
// }
];

@ -0,0 +1,235 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="知识库类型名称" prop="knowledgeBaseTypeName" label-width="120px">
<el-input v-model="queryParams.knowledgeBaseTypeName" placeholder="请输入知识库类型名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="queryParams.remark" placeholder="请输入备注" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"></el-button>
<el-button icon="Refresh" @click="resetQuery"></el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<template #header>
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['ai:aiKnowledgeBaseType:add']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['ai:aiKnowledgeBaseType:edit']"></el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['ai:aiKnowledgeBaseType:remove']"></el-button>
</el-col>
<!-- <el-col :span="1.5">-->
<!-- <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['ai:aiKnowledgeBaseType:export']"></el-button>-->
<!-- </el-col>-->
<!-- <right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @queryTable="getList"></right-toolbar>-->
</el-row>
</template>
<el-table v-loading="loading" :data="aiKnowledgeBaseTypeList" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="编号" align="center" prop="knowledgeBaseTypeId" v-if="columns[0].visible"/>
<el-table-column label="知识库类型名称" align="center" prop="knowledgeBaseTypeName" v-if="columns[2].visible"/>
<el-table-column label="备注" align="center" prop="remark" v-if="columns[3].visible"/>
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
<template #default="scope">
<el-tooltip content="修改" placement="top">
<el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['ai:aiKnowledgeBaseType:edit']"></el-button>
</el-tooltip>
<el-tooltip content="删除" placement="top">
<el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['ai:aiKnowledgeBaseType:remove']"></el-button>
</el-tooltip>
</template>
</el-table-column>
</el-table>
<pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />
</el-card>
<!-- 添加或修改AI知识库类型对话框 -->
<el-dialog :title="dialog.title" v-model="dialog.visible" width="500px" append-to-body>
<el-form ref="aiKnowledgeBaseTypeFormRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="类型名称" prop="knowledgeBaseTypeName">
<el-input v-model="form.knowledgeBaseTypeName" placeholder="请输入知识库类型名称" />
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button :loading="buttonLoading" type="primary" @click="submitForm"> </el-button>
<el-button @click="cancel"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup name="AiKnowledgeBaseType" lang="ts">
import { listAiKnowledgeBaseType, getAiKnowledgeBaseType, delAiKnowledgeBaseType, addAiKnowledgeBaseType, updateAiKnowledgeBaseType } from '@/api/ai/base/aiKnowledgeBaseType';
import { AiKnowledgeBaseTypeVO, AiKnowledgeBaseTypeQuery, AiKnowledgeBaseTypeForm } from '@/api/ai/base/aiKnowledgeBaseType/types';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const aiKnowledgeBaseTypeList = ref<AiKnowledgeBaseTypeVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const aiKnowledgeBaseTypeFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
//
const columns = ref<FieldOption[]>([
{ key: 0, label: `主键`, visible: true },
{ key: 1, label: `租户号`, visible: true },
{ key: 2, label: `知识库类型名称`, visible: true },
{ key: 3, label: `备注`, visible: true },
{ key: 4, label: `创建时间`, visible: true },
{ key: 5, label: `创建人`, visible: true },
{ key: 6, label: `更新时间`, visible: true },
{ key: 7, label: `更新人`, visible: true },
{ key: 8, label: `创建部门`, visible: true },
{ key: 9, label: `删除标志`, visible: true },
]);
const initFormData: AiKnowledgeBaseTypeForm = {
knowledgeBaseTypeId: undefined,
knowledgeBaseTypeName: undefined,
remark: undefined,
}
const data = reactive<PageData<AiKnowledgeBaseTypeForm, AiKnowledgeBaseTypeQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
knowledgeBaseTypeId: undefined,
knowledgeBaseTypeName: undefined,
remark: undefined,
params: {
}
},
rules: {
knowledgeBaseTypeName: [
{ required: true, message: "知识库类型名称不能为空", trigger: "blur" }
]
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询AI知识库类型列表 */
const getList = async () => {
loading.value = true;
const res = await listAiKnowledgeBaseType(queryParams.value);
aiKnowledgeBaseTypeList.value = res.rows;
total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
aiKnowledgeBaseTypeFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: AiKnowledgeBaseTypeVO[]) => {
ids.value = selection.map(item => item.knowledgeBaseTypeId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
dialog.visible = true;
dialog.title = "添加AI知识库类型";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: AiKnowledgeBaseTypeVO) => {
reset();
const _knowledgeBaseTypeId = row?.knowledgeBaseTypeId || ids.value[0]
const res = await getAiKnowledgeBaseType(_knowledgeBaseTypeId);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改AI知识库类型";
}
/** 提交按钮 */
const submitForm = () => {
aiKnowledgeBaseTypeFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.knowledgeBaseTypeId) {
await updateAiKnowledgeBaseType(form.value).finally(() => buttonLoading.value = false);
} else {
await addAiKnowledgeBaseType(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: AiKnowledgeBaseTypeVO) => {
const _knowledgeBaseTypeIds = row?.knowledgeBaseTypeId || ids.value;
await proxy?.$modal.confirm('是否确认删除AI知识库类型编号为"' + _knowledgeBaseTypeIds + '"的数据项?').finally(() => loading.value = false);
await delAiKnowledgeBaseType(_knowledgeBaseTypeIds);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('ai/aiKnowledgeBaseType/export', {
...queryParams.value
}, `aiKnowledgeBaseType_${new Date().getTime()}.xlsx`)
}
onMounted(() => {
getList();
});
</script>

@ -0,0 +1,461 @@
<template>
<el-dialog v-model="props.visible" title="AI模型配置" width="600px" :close-on-click-modal="false"
@update:model-value="val => emit('update:visible', val)">
<el-form :model="form" label-width="120px" :rules="rules" ref="formRef">
<!-- 基本信息模型名称 -->
<el-form-item label="模型名称" prop="modelName">
<el-input v-model="form.modelName" placeholder="请输入AI模型名称"/>
</el-form-item>
<!-- AI平台选择 -->
<el-form-item label="AI平台" prop="platformId">
<el-select v-model="form.platformId" placeholder="请选择AI平台" @change="onPlatformChange"
:disabled="form.modelId">
<el-option v-for="item in aiPlatformList" :key="item.platformId" :label="item.platformName"
:value="item.platformId"/>
</el-select>
</el-form-item>
<!-- 基础模型选择 -->
<el-form-item label="基础模型" prop="baseModelId">
<el-select v-model="form.baseModelId" placeholder="请选择基础模型" @change="onBaseModelChange">
<el-option v-for="item in baseModelList || []" :key="item.baseModelId" :label="item.baseModelName"
:value="item.baseModelId"/>
</el-select>
</el-form-item>
<!-- 模型类型选择 -->
<el-form-item label="模型类型" prop="modelTypeName">
<el-input v-model="form.modelTypeName" disabled/>
</el-form-item>
<!-- API端点自动赋值 -->
<el-form-item label="API端点" prop="apiEndpoint">
<el-select v-model="form.apiEndpoint" placeholder="请选择API端点">
<el-option v-for="item in apiEndpointList || []" :key="item.apiEndpointId" :label="item.apiEndpointName"
:value="item.apiEndpointId"/>
</el-select>
</el-form-item>
<!-- API Key -->
<el-form-item label="API Key" prop="apiKey">
<el-input v-model="form.apiKey" placeholder="请输入API Key"/>
</el-form-item>
<!-- API Secret -->
<el-form-item label="API Secret" prop="apiSecret">
<el-input v-model="form.apiSecret" placeholder="请输入API Secret"/>
</el-form-item>
<!-- 高级信息 -->
<el-card v-if="false">
<el-divider>高级信息</el-divider>
<!-- 预设加载放在高级信息最上面 -->
<el-form-item label="加载预设">
<el-radio-group v-model="preset" @change="applyPreset">
<el-radio-button label="创意"/>
<el-radio-button label="平衡"/>
<el-radio-button label="精确"/>
</el-radio-group>
</el-form-item>
<el-form-item label="模型温度" prop="temperature">
<div class="slider-input-group">
<el-slider :model-value="form.temperature" @update:model-value="val => form.temperature = val" :min="0"
:max="1" :step="0.01" style="width: 120px;"/>
<el-input-number v-model="form.temperature" :min="0" :max="1" :step="0.01"
style="width: 120px; margin-left: 8px;"/>
</div>
</el-form-item>
<el-form-item label="词汇属性" prop="top_p">
<div class="slider-input-group">
<el-slider :model-value="form.top_p" @update:model-value="val => form.top_p = val" :min="0" :max="1"
:step="0.01" style="width: 120px;"/>
<el-input-number v-model="form.top_p" :min="0" :max="1" :step="0.01"
style="width: 120px; margin-left: 8px;"/>
</div>
</el-form-item>
<el-form-item label="话题属性" prop="presence_penalty">
<div class="slider-input-group">
<el-slider :model-value="form.presence_penalty" @update:model-value="val => form.presence_penalty = val"
:min="-2" :max="2" :step="0.01" style="width: 120px;"/>
<el-input-number v-model="form.presence_penalty" :min="-2" :max="2" :step="0.01"
style="width: 120px; margin-left: 8px;"/>
</div>
</el-form-item>
<el-form-item label="重复属性" prop="frequency_penalty">
<div class="slider-input-group">
<el-slider :model-value="form.frequency_penalty" @update:model-value="val => form.frequency_penalty = val"
:min="-2" :max="2" :step="0.01" style="width: 120px;"/>
<el-input-number v-model="form.frequency_penalty" :min="-2" :max="2" :step="0.01"
style="width: 120px; margin-left: 8px;"/>
</div>
</el-form-item>
<el-form-item label="最大回复" prop="max_tokens">
<el-input-number v-model="form.max_tokens" :min="1" :max="16000"/>
</el-form-item>
'
</el-card>
</el-form>
<template #footer>
<el-button @click="onTest" :loading="buttonLoading">测试</el-button>
<el-button type="primary" :loading="buttonLoading" @click="onSave"></el-button>
<el-button @click="onClose"></el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import {ref, reactive, watch, computed} from 'vue'
import {ElMessage, ElMessageBox} from 'element-plus'
import {getAiPlatformList, listAiBaseModels} from '@/api/ai/base/aiPlatform';
import {AiPlatformVO, AiPlatformQuery, AiPlatformForm} from '@/api/ai/base/aiPlatform/types';
import {listAiModel, getAiModel, delAiModel, addAiModel, updateAiModel, testAIModel} from '@/api/ai/base/aiModel';
import {AiModelVO, AiModelQuery, AiModelForm} from '@/api/ai/base/aiModel/types';
const {proxy} = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps<{
visible: boolean
model: any
}>()
const emit = defineEmits(['update:visible', 'update:model'])
const formRef = ref()
const testing = ref(false)
const preset = ref('')
const aiPlatformList = ref<AiPlatformVO[]>([]);
const baseModelList = ref([]);
const apiEndpointList = ref([]);
const baseModelId = ref();
const displayApiKey = ref('********');
const displayApiSecret = ref('********');
const currentApiKey = ref('');
const currentApiSecret = ref('');
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
// API
const platforms = [
{label: 'OpenAI', value: 'openai'},
{label: 'DeepSeek', value: 'deepseek'},
{label: '文心一言', value: 'wenxin'},
{label: '通义千问', value: 'qianwen'},
]
const modelTypes: Record<string, any[]> = {
openai: [
{label: 'GPT-3.5', value: 'gpt-3.5'},
{label: 'GPT-4', value: 'gpt-4'},
],
deepseek: [
{label: 'R1', value: 'r1'},
],
wenxin: [
{label: 'ERNIE-Bot', value: 'ernie-bot'},
],
qianwen: [
{label: 'Qwen-7B', value: 'qwen-7b'},
],
}
const baseModels: Record<string, any[]> = {
'gpt-3.5': [
{label: 'gpt-3.5-turbo', value: 'gpt-3.5-turbo'},
{label: 'gpt-3.5-turbo-16k', value: 'gpt-3.5-turbo-16k'},
],
'gpt-4': [
{label: 'gpt-4', value: 'gpt-4'},
{label: 'gpt-4-32k', value: 'gpt-4-32k'},
],
'r1': [
{label: 'deepseek-r1', value: 'deepseek-r1'},
],
'ernie-bot': [
{label: 'ERNIE-Bot', value: 'ernie-bot'},
],
'qwen-7b': [
{label: 'Qwen-7B', value: 'qwen-7b'},
],
}
const apiEndpoints: Record<string, string> = {
'gpt-3.5-turbo': 'https://api.openai.com/v1/chat/completions',
'gpt-3.5-turbo-16k': 'https://api.openai.com/v1/chat/completions',
'gpt-4': 'https://api.openai.com/v1/chat/completions',
'gpt-4-32k': 'https://api.openai.com/v1/chat/completions',
'deepseek-r1': 'https://api.deepseek.com/v1/chat/completions',
'ernie-bot': 'https://wenxin.baidu.com/moduleApi/portal/api/rest/1.0/ernie_bot',
'Qwen-7B': 'https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation',
}
const form = reactive({
modelId: undefined,
platformId: undefined,
modelTypeId: undefined,
modelTypeCode: '',
modelTypeName: '',
baseModelId: undefined,
apiEndpoint: '',
modelName: '',
apiKey: '',
apiSecret: '',
temperature: 0.7,
top_p: 1,
presence_penalty: 0,
frequency_penalty: 0,
max_tokens: 2048,
})
const rules = {
platformId: [{required: true, message: '请选择AI平台', trigger: 'change'}],
baseModelId: [{required: true, message: '请选择基础模型', trigger: 'change'}],
modelName: [{required: true, message: '请输入模型名称', trigger: 'blur'}],
}
//
watch(() => props.visible, async (newVal) => {
console.log('visible changed:', newVal, 'model:', props.model)
resetForm()
currentApiKey.value = '';
currentApiSecret.value = '';
if (newVal && props.model) {
//
Object.assign(form, props.model)
currentApiKey.value = props.model.apiKey;
currentApiSecret.value = props.model.apiSecret;
form.apiKey = displayApiKey.value;
form.apiSecret = displayApiSecret.value;
await getBaseModelList();
await getEndPointAndModelType();
//
// setPresetByValues()
} else if (newVal && !props.model) {
//
// resetForm()
}
}, {immediate: true})
//
function resetForm() {
Object.assign(form, {
modelId: undefined,
platformId: undefined,
modelTypeId: undefined,
modelTypeName: '',
baseModelId: undefined,
apiEndpoint: '',
modelName: '',
apiKey: '',
apiSecret: '',
temperature: 0.7,
top_p: 1,
presence_penalty: 0,
frequency_penalty: 0,
max_tokens: 2048,
})
baseModelList.value = [];
apiEndpointList.value = [];
preset.value = ''
}
function onPlatformChange(newPlatformId) {
form.modelTypeId = ''
form.modelTypeCode = '';
form.modelTypeName = '';
form.baseModelId = ''
form.apiEndpoint = ''
getBaseModelList()
}
function onModelTypeChange() {
form.baseModelId = ''
form.apiEndpoint = ''
}
function onBaseModelChange(newBaseModelId) {
// form.apiEndpoint = apiEndpoints[form.baseModel] || ''
form.modelTypeId = '';
form.modelTypeCode = '';
form.modelTypeName = '';
form.apiEndpoint = ''
getEndPointAndModelType();
}
function applyPreset(val: string) {
if (val === '创意') {
form.temperature = 0.95
form.top_p = 1
form.presence_penalty = 1.2
form.frequency_penalty = 0.8
form.max_tokens = 4096
} else if (val === '平衡') {
form.temperature = 0.7
form.top_p = 0.9
form.presence_penalty = 0.5
form.frequency_penalty = 0.5
form.max_tokens = 2048
} else if (val === '精确') {
form.temperature = 0.2
form.top_p = 0.7
form.presence_penalty = 0
form.frequency_penalty = 0
form.max_tokens = 1024
}
}
function onTest() {
let platformCode = getAiPlatformCode();
formRef.value?.validate(async (valid: boolean) => {
if (!valid) return
buttonLoading.value = true;
// API
let apiKey = '';
let apiKeyEncryptFlag = '';
let apiSecret = '';
let apiSecretEncryptFlag = '';
if (form.modelId) {
if (form.apiKey === displayApiKey.value) {
apiKey = currentApiKey.value;
apiKeyEncryptFlag = '1';
} else {
apiKey = form.apiKey;
apiKeyEncryptFlag = '0';
}
if (form.apiSecret === displayApiSecret.value) {
apiSecret = currentApiSecret.value;
apiSecretEncryptFlag = '1';
} else {
apiSecret = form.apiSecret;
apiSecretEncryptFlag = '0';
}
} else {
apiKey = form.apiKey;
apiKeyEncryptFlag = '0';
apiSecret = form.apiSecret;
apiSecretEncryptFlag = '0';
}
const paramsData = {
apiKey: apiKey,
apiKeyEncryptFlag: apiKeyEncryptFlag,
apiSecret: apiSecret,
apiSecretEncryptFlag: apiSecretEncryptFlag,
modelType: form.modelTypeCode
}
await testAIModel(platformCode, paramsData).finally(() => buttonLoading.value = false);
// await new Promise(r => setTimeout(r, 1000))
proxy?.$modal.msgSuccess("测试成功");
})
}
function onSave() {
// alert(JSON.stringify(form))
formRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.modelId) {
if (form.apiKey === displayApiKey.value) {
form.apiKey = undefined;
}
if (form.apiSecret === displayApiSecret.value) {
form.apiSecret = undefined;
}
await updateAiModel(form).finally(() => buttonLoading.value = false);
} else {
await addAiModel(form).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
//
emit('update:model', {...form})
emit('update:visible', false)
// await getList();
}
})
}
function onClose() {
emit('update:visible', false)
}
/** 查询AI平台列表 */
const getPlatformList = async () => {
const res = await getAiPlatformList({});
console.log(res);
aiPlatformList.value = res.data;
}
/** 查询基础模型列表 */
const getBaseModelList = async () => {
const res = await listAiBaseModels({platformId: form.platformId});
console.log(res);
baseModelList.value = res.data;
}
const getEndPointAndModelType = async () => {
baseModelList.value.forEach((item: any) => {
if (item.baseModelId === form.baseModelId) {
// alert(JSON.stringify(item))
apiEndpointList.value = item.aiApiEndpointList;
if (apiEndpointList.value && apiEndpointList.value.length > 0) {
apiEndpointList.value.forEach((endpoint: any) => {
if (endpoint.defaultFlag === '1') {
form.apiEndpoint = endpoint.apiEndpointPath;
}
})
}
form.modelTypeId = item.modelTypeId;
form.modelTypeCode = item.modelTypeCode;
form.modelTypeName = item.modelTypeName;
}
})
}
const getAiPlatformCode = () => {
const selectedOption = aiPlatformList.value.find(opt => opt.platformId === form.platformId);
return selectedOption ? selectedOption.platformCode : '';
// this.selectedCode = selectedOption ? selectedOption.code : '';
}
onMounted(() => {
getPlatformList();
});
</script>
<style scoped>
.slider-value {
margin-left: 8px;
color: #888;
font-size: 14px;
min-width: 40px;
display: inline-block;
}
.slider-input-group {
display: flex;
align-items: center;
}
</style>

@ -0,0 +1,527 @@
<template>
<div class="p-2">
<transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
<div v-show="showSearch" class="mb-[10px]">
<el-card shadow="hover">
<el-form ref="queryFormRef" :model="queryParams" :inline="true">
<el-form-item label="模型名称" prop="modelName">
<el-input v-model="queryParams.modelName" placeholder="请输入模型名称" clearable @keyup.enter="handleQuery" />
</el-form-item>
<el-form-item label="AI平台" prop="platformId">
<el-select v-model="queryParams.platformId" placeholder="请选择AI平台" clearable @keyup.enter="handleQuery">
<el-option v-for="item in aiPlatformList" :key="item.platformId" :label="item.platformName"
:value="item.platformId"/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="Search" @click="handleQuery"></el-button>
<el-button icon="Refresh" @click="resetQuery"></el-button>
<el-button type="success" plain icon="Plus" @click="handleAdd" v-hasPermi="['ai:aiModel:add']"></el-button>
</el-form-item>
</el-form>
</el-card>
</div>
</transition>
<el-card shadow="never">
<!-- <template #header>-->
<!-- <el-row :gutter="10" class="mb8">-->
<!-- <el-col :span="1.5">-->
<!-- <el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['ai:aiModel:add']"></el-button>-->
<!-- </el-col>-->
<!-- <el-col :span="1.5">-->
<!-- <el-button type="success" plain icon="Edit" :disabled="single" @click="handleUpdate()" v-hasPermi="['ai:aiModel:edit']"></el-button>-->
<!-- </el-col>-->
<!-- <el-col :span="1.5">-->
<!-- <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()" v-hasPermi="['ai:aiModel:remove']"></el-button>-->
<!-- </el-col>-->
<!-- <el-col :span="1.5">-->
<!-- <el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['ai:aiModel:export']"></el-button>-->
<!-- </el-col>-->
<!-- <right-toolbar v-model:showSearch="showSearch" :columns="columns" :search="true" @queryTable="getList"></right-toolbar>-->
<!-- </el-row>-->
<!-- </template>-->
<div class="model-card-scroll" v-loading="loading">
<el-empty v-if="aiModelList.length === 0" description="暂无模型" />
<div v-else class="card-row-scroll">
<el-card v-for="item in aiModelList" :key="item.modelId" class="model-card" shadow="hover">
<div class="card-header">
<div class="platform-icon">
<img v-if="item.platformIcon" :src="item.platformIcon" :alt="item.platformIcon" class="icon-img" />
<span v-else class="icon-emoji">🤖</span>
</div>
<span class="model-name">{{ item.modelName }}</span>
</div>
<div class="card-body">
<div class="card-row"><span class="label">AI平台</span>{{ item.platformName }}</div>
<div class="card-row"><span class="label">模型类型</span>{{ item.modelTypeName }}</div>
<div class="card-row"><span class="label">基础模型</span>{{ item.baseModelName }}</div>
</div>
<div class="card-actions-bottom">
<el-button size="small" @click="onEdit(item)"></el-button>
<el-button size="small" type="danger" @click="handleDelete(item)"></el-button>
</div>
</el-card>
</div>
</div>
<!-- <pagination v-show="total > 0" :total="total" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" @pagination="getList" />-->
</el-card>
<!-- 添加或修改AI模型对话框 -->
<ModelSettings :visible="showDialog" :model="editModel" @update:visible="showDialog = $event" @update:model="onDialogSave" />
</div>
</template>
<script setup name="AiModel" lang="ts">
import { listAiModel, getAiModel, delAiModel, addAiModel, updateAiModel } from '@/api/ai/base/aiModel';
import { AiModelVO, AiModelQuery, AiModelForm } from '@/api/ai/base/aiModel/types';
import ModelSettings from './ModelSettings.vue'
import {ref} from "vue";
import {AiPlatformVO} from "@/api/ai/base/aiPlatform/types";
import {getAiPlatformList} from '@/api/ai/base/aiPlatform';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const aiModelList = ref<AiModelVO[]>([]);
const aiPlatformList = ref<AiPlatformVO[]>([]);
const buttonLoading = ref(false);
const loading = ref(true);
const showSearch = ref(true);
const ids = ref<Array<string | number>>([]);
const single = ref(true);
const multiple = ref(true);
const total = ref(0);
const queryFormRef = ref<ElFormInstance>();
const aiModelFormRef = ref<ElFormInstance>();
const dialog = reactive<DialogOption>({
visible: false,
title: ''
});
//
const columns = ref<FieldOption[]>([
{ key: 0, label: `主键`, visible: true },
{ key: 1, label: `租户ID`, visible: true },
{ key: 2, label: `模型名称`, visible: true },
{ key: 3, label: `AI平台ID关联ai_platform`, visible: true },
{ key: 4, label: `模型类型ID关联ai_model_type`, visible: true },
{ key: 5, label: `基础模型ID关联ai_base_model`, visible: true },
{ key: 6, label: `计费类型(1token计费2次数计费)`, visible: true },
{ key: 7, label: `模型价格`, visible: true },
{ key: 8, label: `请求地址`, visible: true },
{ key: 9, label: `秘钥`, visible: true },
{ key: 10, label: `密钥`, visible: true },
{ key: 11, label: `向量库(1milvus2weaviate,)`, visible: true },
{ key: 12, label: `描述`, visible: true },
{ key: 13, label: `高级配置—模型温度(0-1);值越大回复内容越赋有多样性创造性、随机性设为0根据事实回答希望得到精准答案应该降低该参数日常聊天建议0.5-0.8`, visible: true },
{ key: 14, label: `高级配置—词汇属性(0-1);值越小AI生成的内容越单调也越容易理解值越大AI回复的词汇范围越大越多样化`, visible: true },
{ key: 15, label: `高级配置—话题属性(-2.0-2.0);值越大越能够让AI更好的控制新话题的引入建议微调或不变`, visible: true },
{ key: 16, label: `高级配置—重复属性(-2.0-2.0);值越大越能够让AI更好的避免重复之前说过的话建议微调或不变`, visible: true },
{ key: 17, label: `高级配置—最大回复(1-16000);设置AI最大回复内容大小会影响返回结果内容的长度普通聊天建议500-800短文生成建议800-2000代码生成建议2000-3600长文生成建议4000左右`, visible: true },
{ key: 18, label: `激活标识(1是0否)`, visible: true },
{ key: 19, label: `创建时间`, visible: true },
{ key: 20, label: `更新时间`, visible: true },
{ key: 21, label: `创建人`, visible: true },
{ key: 22, label: `更新人`, visible: true },
{ key: 23, label: `创建部门`, visible: true },
{ key: 24, label: `删除标志`, visible: true },
]);
const initFormData: AiModelForm = {
modelId: undefined,
modelName: undefined,
platformId: undefined,
modelTypeId: undefined,
baseModelId: undefined,
chargeType: undefined,
modelPrice: undefined,
apiEndpoint: undefined,
apiKey: undefined,
apiSecret: undefined,
vectorLibrary: undefined,
description: undefined,
modelTemperature: undefined,
vocabularyAttribute: undefined,
topicAttribute: undefined,
duplicateAttribute: undefined,
maxReply: undefined,
activeFlag: undefined,
}
const data = reactive<PageData<AiModelForm, AiModelQuery>>({
form: {...initFormData},
queryParams: {
pageNum: 1,
pageSize: 10,
modelId: undefined,
modelName: undefined,
platformId: undefined,
modelTypeId: undefined,
baseModelId: undefined,
chargeType: undefined,
modelPrice: undefined,
apiEndpoint: undefined,
apiKey: undefined,
apiSecret: undefined,
vectorLibrary: undefined,
description: undefined,
modelTemperature: undefined,
vocabularyAttribute: undefined,
topicAttribute: undefined,
duplicateAttribute: undefined,
maxReply: undefined,
activeFlag: undefined,
params: {
}
},
rules: {
modelId: [
{ required: true, message: "主键不能为空", trigger: "blur" }
],
modelName: [
{ required: true, message: "模型名称不能为空", trigger: "blur" }
],
platformId: [
{ required: true, message: "AI平台ID关联ai_platform不能为空", trigger: "blur" }
],
modelTypeId: [
{ required: true, message: "模型类型ID关联ai_model_type不能为空", trigger: "blur" }
],
baseModelId: [
{ required: true, message: "基础模型ID关联ai_base_model不能为空", trigger: "blur" }
],
chargeType: [
{ required: true, message: "计费类型(1token计费2次数计费)不能为空", trigger: "change" }
],
modelPrice: [
{ required: true, message: "模型价格不能为空", trigger: "blur" }
],
apiEndpoint: [
{ required: true, message: "请求地址不能为空", trigger: "blur" }
],
apiKey: [
{ required: true, message: "秘钥不能为空", trigger: "blur" }
],
apiSecret: [
{ required: true, message: "密钥不能为空", trigger: "blur" }
],
vectorLibrary: [
{ required: true, message: "向量库(1milvus2weaviate,)不能为空", trigger: "blur" }
],
description: [
{ required: true, message: "描述不能为空", trigger: "blur" }
],
modelTemperature: [
{ required: true, message: "高级配置—模型温度(0-1);值越大回复内容越赋有多样性创造性、随机性设为0根据事实回答希望得到精准答案应该降低该参数日常聊天建议0.5-0.8不能为空", trigger: "blur" }
],
vocabularyAttribute: [
{ required: true, message: "高级配置—词汇属性(0-1);值越小AI生成的内容越单调也越容易理解值越大AI回复的词汇范围越大越多样化不能为空", trigger: "blur" }
],
topicAttribute: [
{ required: true, message: "高级配置—话题属性(-2.0-2.0);值越大越能够让AI更好的控制新话题的引入建议微调或不变不能为空", trigger: "blur" }
],
duplicateAttribute: [
{ required: true, message: "高级配置—重复属性(-2.0-2.0);值越大越能够让AI更好的避免重复之前说过的话建议微调或不变不能为空", trigger: "blur" }
],
maxReply: [
{ required: true, message: "高级配置—最大回复(1-16000);设置AI最大回复内容大小会影响返回结果内容的长度普通聊天建议500-800短文生成建议800-2000代码生成建议2000-3600长文生成建议4000左右不能为空", trigger: "blur" }
],
activeFlag: [
{ required: true, message: "激活标识(1是0否)不能为空", trigger: "blur" }
],
}
});
const { queryParams, form, rules } = toRefs(data);
/** 查询AI模型列表 */
const getList = async () => {
loading.value = true;
const res = await listAiModel(queryParams.value);
aiModelList.value = res.data;
// total.value = res.total;
loading.value = false;
}
/** 取消按钮 */
const cancel = () => {
reset();
dialog.visible = false;
}
/** 表单重置 */
const reset = () => {
form.value = {...initFormData};
aiModelFormRef.value?.resetFields();
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.value.pageNum = 1;
getList();
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value?.resetFields();
handleQuery();
}
/** 多选框选中数据 */
const handleSelectionChange = (selection: AiModelVO[]) => {
ids.value = selection.map(item => item.modelId);
single.value = selection.length != 1;
multiple.value = !selection.length;
}
/** 新增按钮操作 */
const handleAdd = () => {
reset();
editModel.value = null
showDialog.value = true
dialog.title = "添加AI模型";
}
/** 修改按钮操作 */
const handleUpdate = async (row?: AiModelVO) => {
reset();
const _modelId = row?.modelId || ids.value[0]
const res = await getAiModel(_modelId);
Object.assign(form.value, res.data);
dialog.visible = true;
dialog.title = "修改AI模型";
}
/** 提交按钮 */
const submitForm = () => {
aiModelFormRef.value?.validate(async (valid: boolean) => {
if (valid) {
buttonLoading.value = true;
if (form.value.modelId) {
await updateAiModel(form.value).finally(() => buttonLoading.value = false);
} else {
await addAiModel(form.value).finally(() => buttonLoading.value = false);
}
proxy?.$modal.msgSuccess("操作成功");
dialog.visible = false;
await getList();
}
});
}
/** 删除按钮操作 */
const handleDelete = async (row?: AiModelVO) => {
const _modelIds = row?.modelId || ids.value;
const _modelNames = row?.modelName || ids.value;
await proxy?.$modal.confirm('是否确认删除AI模型名称为"' + _modelNames + '"的数据项?').finally(() => loading.value = false);
await delAiModel(_modelIds);
proxy?.$modal.msgSuccess("删除成功");
await getList();
}
/** 导出按钮操作 */
const handleExport = () => {
proxy?.download('ai/aiModel/export', {
...queryParams.value
}, `aiModel_${new Date().getTime()}.xlsx`)
}
/** 查询AI平台列表 */
const getPlatformList = async () => {
const res = await getAiPlatformList({});
console.log(res);
aiPlatformList.value = res.data;
}
onMounted(() => {
getList();
getPlatformList();
});
const showDialog = ref(false)
const editModel = ref<any | null>(null)
function onAdd() {
editModel.value = null
showDialog.value = true
}
function onEdit(row: any) {
editModel.value = { ...row }
showDialog.value = true
}
function onDialogSave(model: any) {
getList();
// if (editModel.value && editModel.value.id) {
// //
// const idx = allModels.value.findIndex(m => m.id === editModel.value.id)
// if (idx !== -1) allModels.value[idx] = { ...model, id: editModel.value.id }
// ElMessage.success('')
// } else {
// //
// const newId = Math.max(...allModels.value.map(m => m.id), 0) + 1
// allModels.value.push({ ...model, id: newId })
// ElMessage.success('')
// }
// showDialog.value = false
// fetchList()
}
function onDelete(row: any) {
ElMessageBox.confirm('确定要删除该模型吗?', '提示', { type: 'warning' })
.then(() => {
// allModels.value = allModels.value.filter(m => m.id !== row.id)
// fetchList()
ElMessage.success('删除成功')
})
}
</script>
<style scoped>
.model-manage-fullscreen {
/* position: fixed; */
/* inset: 0; */
/* width: 100vw; */
/* height: 100vh; */
background: #f7f8fa;
overflow-y: auto;
padding: 0 0 32px 0;
display: flex;
flex-direction: column;
min-height: 100%;
}
.toolbar {
display: flex;
align-items: center;
flex-direction: row;
gap: 16px;
margin: 32px 0 16px 0;
max-width: 1200px;
width: 100%;
padding: 0 16px;
justify-content: flex-start;
}
.toolbar-input {
width: 200px;
}
.toolbar-select {
width: 160px;
}
.toolbar-btn {
min-width: 80px;
}
.model-card-scroll {
/* width: 100vw; */
padding: 0 0 0 16px;
overflow-x: unset;
}
.card-row-scroll {
display: flex;
flex-wrap: wrap;
gap: 24px;
min-height: 180px;
padding-bottom: 16px;
}
.model-card {
position: relative;
border-radius: 16px;
width: 360px;
height: 220px;
min-width: 360px;
max-width: 360px;
min-height: 220px;
max-height: 220px;
display: flex;
flex-direction: column;
justify-content: center;
transition: box-shadow 0.2s;
word-break: break-all;
}
.card-header {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 12px;
margin-bottom: 12px;
}
.platform-icon {
width: 50px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background: #f4f4f4;
border-radius: 8px;
overflow: hidden;
}
.icon-img {
width: 50px;
height: 40px;
object-fit: contain;
}
.icon-emoji {
font-size: 22px;
}
.model-name {
font-size: 20px;
font-weight: 700;
color: #222;
flex: 1;
word-break: break-all;
white-space: normal;
}
.card-actions {
display: flex;
gap: 8px;
}
.card-body {
font-size: 15px;
color: #555;
display: flex;
flex-direction: column;
gap: 6px;
}
.card-row {
color: #555;
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 4px;
flex-wrap: wrap;
word-break: break-all;
white-space: normal;
line-height: 1.7;
}
.card-row .label {
color: #888;
min-width: 70px;
display: inline-block;
word-break: keep-all;
}
/* 新增右下角按钮样式 */
.card-actions-bottom {
position: absolute;
right: 16px;
bottom: 10px;
display: flex;
gap: 8px;
}
</style>

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save