init
This commit is contained in:
549
src/views/knowledge/aiDataset/form.vue
Normal file
549
src/views/knowledge/aiDataset/form.vue
Normal file
@@ -0,0 +1,549 @@
|
||||
<template>
|
||||
<el-drawer v-model="visible" :title="form.id ? '编辑' : '新增'" size="50%">
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="auto" v-loading="loading">
|
||||
<!-- Tab buttons -->
|
||||
<div role="tablist" class="mb-5 tabs tabs-boxed abs-bordered">
|
||||
<a role="tab" class="tab" :class="{ 'tab-active': activeTab === 'basic' }" @click="activeTab = 'basic'"
|
||||
><span class="inline-flex justify-center items-center mr-2 w-5 h-5 text-white rounded-full bg-primary">1</span>基础配置</a
|
||||
>
|
||||
<a role="tab" class="tab" :class="{ 'tab-active': activeTab === 'advanced' }" @click="activeTab = 'advanced'"
|
||||
><span class="inline-flex justify-center items-center mr-2 w-5 h-5 text-white rounded-full bg-primary">2</span>高级配置</a
|
||||
>
|
||||
<a role="tab" class="tab" :class="{ 'tab-active': activeTab === 'security' }" @click="activeTab = 'security'"
|
||||
><span class="inline-flex justify-center items-center mr-2 w-5 h-5 text-white rounded-full bg-primary">3</span>安全配置</a
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- Basic Configuration Tab -->
|
||||
<div v-show="activeTab === 'basic'" class="px-2">
|
||||
<el-row :gutter="30">
|
||||
<el-col :span="12" class="mb-6">
|
||||
<el-form-item label="头像" prop="avatarUrl">
|
||||
<upload-img v-model:image-url="form.avatarUrl" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb-6">
|
||||
<!-- Placeholder for alignment -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="30">
|
||||
<el-col :span="12" class="mb-6">
|
||||
<el-form-item label="名称" prop="name">
|
||||
<el-input v-model="form.name" maxlength="20" placeholder="请输入名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb-6">
|
||||
<el-form-item prop="sortOrder">
|
||||
<template #label>
|
||||
排序值
|
||||
<tip content="越大展示越靠前" />
|
||||
</template>
|
||||
<el-input-number v-model="form.sortOrder" :min="1" :max="9999" :step="1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="30">
|
||||
<el-col :span="24" class="mb-6">
|
||||
<el-form-item label="欢迎语" prop="welcomeMsg">
|
||||
<el-input
|
||||
v-model="form.welcomeMsg"
|
||||
type="textarea"
|
||||
placeholder="描述知识库的内容,详尽的描述将帮助AI能深入理解该知识库的内容,能更准确的检索到内容,提高该知识库的命中率。"
|
||||
maxlength="1024"
|
||||
show-word-limit
|
||||
rows="10"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- Advanced Configuration Tab -->
|
||||
<div v-show="activeTab === 'advanced'" class="px-2">
|
||||
<el-divider content-position="left">模型配置</el-divider>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="向量库" prop="storeId">
|
||||
<el-select v-model="form.storeId" placeholder="请选择向量库" clearable filterable :disabled="form.id !== ''">
|
||||
<el-option v-for="item in storeList" :key="item.storeId" :label="item.name" :value="item.storeId" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="向量模型" prop="embeddingModel">
|
||||
<el-select v-model="form.embeddingModel" placeholder="请选择向量模型" clearable filterable :disabled="form.id !== ''">
|
||||
<el-option v-for="item in embeddingModelList" :key="item.id" :label="item.name" :value="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="总结模型" prop="summaryModel">
|
||||
<el-select v-model="form.summaryModel" placeholder="请选择总结模型" clearable filterable>
|
||||
<el-option v-for="item in chatModelList" :key="item.id" :label="item.name" :value="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="重排模型" prop="rerankerModel">
|
||||
<template #label>
|
||||
重排模型
|
||||
<tip content="重排模型,如没有重排模型,则后台使用默认重排算法" />
|
||||
</template>
|
||||
<el-select v-model="form.rerankerModel" placeholder="请选择重排模型" clearable filterable>
|
||||
<el-option v-for="item in rerankerModelList" :key="item.id" :label="item.name" :value="item.name" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">检索配置</el-divider>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="会话轮数" prop="multiRound">
|
||||
<template #label>
|
||||
会话轮数
|
||||
<tip content="会话轮数,0代表不记忆上文会话" />
|
||||
</template>
|
||||
<el-input-number v-model="form.multiRound" :min="0" :max="5" :step="1" class="w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="topK">
|
||||
<template #label>
|
||||
匹配条数
|
||||
<tip content="向量数据库匹配最多几条结果" />
|
||||
</template>
|
||||
<el-input-number v-model="form.topK" :min="1" :max="5" :step="1" class="w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="fragmentSize">
|
||||
<template #label>
|
||||
分片值
|
||||
<tip content="分片值取决于模型自身能力,理论上分片值越大越准确" />
|
||||
</template>
|
||||
<el-input-number v-model="form.fragmentSize" :min="500" :max="9999" :step="1" class="w-full" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="score">
|
||||
<template #label>
|
||||
匹配率
|
||||
<tip content="向量数据库匹配率,建议不低于 50%" />
|
||||
</template>
|
||||
<el-slider v-model="form.score" :step="10" :min="10" :max="90" :format-tooltip="(value: number) => value + '%'" show-stops />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="emptyLlmFlag">
|
||||
<template #label>
|
||||
空查询
|
||||
<tip content="查询不到结果时,是否调用大模型查询" />
|
||||
</template>
|
||||
<el-switch v-model="form.emptyLlmFlag" :active-value="'1'" :inactive-value="'0'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" v-if="form.emptyLlmFlag !== '1'">
|
||||
<el-form-item prop="emptyDesc">
|
||||
<template #label>
|
||||
空提示
|
||||
<tip content="未匹配的时候,返回的文案" />
|
||||
</template>
|
||||
<el-input v-model="form.emptyDesc" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">处理选项</el-divider>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="文档总结" prop="preSummary">
|
||||
<el-switch v-model="form.preSummary" :active-value="'1'" :inactive-value="'0'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="aiOcrFlag">
|
||||
<template #label>
|
||||
AI OCR
|
||||
<tip content="PDF、图片等文件,自动进行 AI OCR 识别" />
|
||||
</template>
|
||||
<el-switch v-model="form.aiOcrFlag" :active-value="'1'" :inactive-value="'0'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="会话压缩" prop="preCompress">
|
||||
<el-switch v-model="form.preCompress" :active-value="'1'" :inactive-value="'0'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="standardFlag">
|
||||
<template #label>
|
||||
标注数据
|
||||
<tip content="使用已经标注修正后的答案,直接返回" />
|
||||
</template>
|
||||
<el-switch v-model="form.standardFlag" :active-value="'1'" :inactive-value="'0'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<!-- Security Configuration Tab -->
|
||||
<div v-show="activeTab === 'security'" class="px-2">
|
||||
<el-divider content-position="left">访问控制</el-divider>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否对外" prop="publicFlag">
|
||||
<el-switch v-model="form.publicFlag" :active-value="'1'" :inactive-value="'0'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="publicPassword">
|
||||
<template #label>
|
||||
安全密钥
|
||||
<tip content="对外服务,需要用户输入的密码" />
|
||||
</template>
|
||||
<el-input v-model="form.publicPassword" placeholder="请输入密码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="24">
|
||||
<el-form-item prop="visibleUsers">
|
||||
<template #label>
|
||||
可见范围
|
||||
<tip content="选择可以访问此知识库的用户,如果不选择则全部的用户可访问" />
|
||||
</template>
|
||||
<org-selector
|
||||
v-model="form.visibleUsers"
|
||||
:type="'user'"
|
||||
:multiple="true"
|
||||
:selectSelf="true"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">内容过滤</el-divider>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="敏感词过滤" prop="sensitiveFlag">
|
||||
<el-switch v-model="form.sensitiveFlag" :active-value="'1'" :inactive-value="'0'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="sensitiveMsg">
|
||||
<template #label>
|
||||
提示
|
||||
<tip content="命中敏感词,返回的文案" />
|
||||
</template>
|
||||
<el-input v-model="form.sensitiveMsg" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="left">附加信息</el-divider>
|
||||
|
||||
<el-row :gutter="30" class="mb-6">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="底部信息" prop="footer">
|
||||
<el-input type="textarea" maxlength="255" :rows="5" v-model="form.footer" placeholder="聊天框底部的信息,支持 HTML 语法" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="AiDatasetDialog">
|
||||
// import { useDict } from '/@/hooks/dict'; // Removed as it's unused
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { getDetails, addObj, putObj, validateName } from '/@/api/knowledge/aiDataset';
|
||||
import { list as aiModelList } from '/@/api/knowledge/aiModel';
|
||||
import { list } from '/@/api/knowledge/aiEmbedStore';
|
||||
import { rule } from '/@/utils/validate';
|
||||
import UploadImg from '/@/components/Upload/Image.vue';
|
||||
import OrgSelector from '/@/components/OrgSelector/index.vue';
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const activeTab = ref('basic'); // Added for tab management
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
name: '',
|
||||
avatarUrl: '',
|
||||
description: '',
|
||||
units: '0',
|
||||
fileSize: '0',
|
||||
multiRound: 3,
|
||||
topK: 2,
|
||||
score: 40,
|
||||
fragmentSize: 1000,
|
||||
sortOrder: 1,
|
||||
emptyLlmFlag: '0',
|
||||
emptyDesc: '知识库未匹配相关问题,请重新提问',
|
||||
sensitiveFlag: '1',
|
||||
preSummary: '0',
|
||||
preCompress: '0',
|
||||
sensitiveMsg: '您输入内容包含敏感词,请重新输入',
|
||||
publicFlag: '1',
|
||||
publicPassword: '' as string | undefined, // Allow undefined for publicPassword
|
||||
standardFlag: '0',
|
||||
aiOcrFlag: '1',
|
||||
welcomeMsg: '',
|
||||
footer: '',
|
||||
embeddingModel: '',
|
||||
summaryModel: '',
|
||||
storeId: '',
|
||||
rerankerModel: '',
|
||||
visibleUsers: [] as Array<any>, // 可见范围用户列表
|
||||
});
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
name: [
|
||||
{ required: true, message: '知识库名称不能为空', trigger: 'blur' },
|
||||
{
|
||||
validator: (rule: any, value: any, callback: any) => {
|
||||
validateName(rule, value, callback, form.id !== '');
|
||||
},
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
welcomeMsg: [{ required: true, message: '欢迎语不能为空', trigger: 'blur' }],
|
||||
multiRound: [{ required: true, message: '多轮会话不能为空', trigger: 'blur' }],
|
||||
topK: [{ required: true, message: '多轮会话不能为空', trigger: 'blur' }],
|
||||
avatarUrl: [{ required: true, message: '封面不能为空', trigger: 'blur' }],
|
||||
emptyDesc: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '提示不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
sensitiveMsg: [
|
||||
{ validator: rule.overLength, trigger: 'blur' },
|
||||
{
|
||||
required: true,
|
||||
message: '提示不能为空',
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
storeId: [{ required: true, message: '请选择向量库', trigger: 'change' }],
|
||||
embeddingModel: [{ required: true, message: '请选择向量模型', trigger: 'change' }],
|
||||
summaryModel: [{ required: true, message: '请选择总结模型', trigger: 'change' }],
|
||||
rerankerModel: [
|
||||
{
|
||||
validator: rule.overLength,
|
||||
trigger: 'blur',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const embeddingModelList = ref<Array<{ id: string; name: string }>>([]);
|
||||
const chatModelList = ref<Array<{ id: string; name: string }>>([]);
|
||||
const storeList = ref<Array<{ storeId: string; name: string }>>([]);
|
||||
const rerankerModelList = ref<Array<{ id: string; name: string }>>([]);
|
||||
|
||||
// Modify the function to fetch AI models based on modelType
|
||||
async function loadAiModelList() {
|
||||
try {
|
||||
const [embeddingResponse, chatResponse, rerankerResponse] = await Promise.all([
|
||||
aiModelList({ modelType: 'Embedding' }),
|
||||
aiModelList({ modelType: ['Chat', 'Reason'] }),
|
||||
aiModelList({ modelType: 'Reranker' }),
|
||||
]);
|
||||
|
||||
embeddingModelList.value = embeddingResponse.data.map((item: any) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
}));
|
||||
|
||||
chatModelList.value = chatResponse.data.map((item: any) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
}));
|
||||
|
||||
rerankerModelList.value = rerankerResponse.data.map((item: any) => ({
|
||||
id: item.id,
|
||||
name: item.name,
|
||||
}));
|
||||
|
||||
// Set default values if lists are not empty
|
||||
if (embeddingModelList.value.length > 0 && !form.embeddingModel) {
|
||||
form.embeddingModel = embeddingModelList.value[0].name;
|
||||
}
|
||||
if (chatModelList.value.length > 0 && !form.summaryModel) {
|
||||
form.summaryModel = chatModelList.value[0].name;
|
||||
}
|
||||
if (rerankerModelList.value.length > 0 && !form.rerankerModel) {
|
||||
form.rerankerModel = rerankerModelList.value[0].name;
|
||||
}
|
||||
} catch (error) {
|
||||
useMessage().error('加载AI模型列表失败');
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化表单数据
|
||||
const getAiDatasetData = (id: string) => {
|
||||
// 获取数据
|
||||
loading.value = true;
|
||||
getDetails({ id })
|
||||
.then((res: any) => {
|
||||
Object.assign(form, res.data);
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false;
|
||||
});
|
||||
};
|
||||
|
||||
// Modify the openDialog function
|
||||
const openDialog = async (id: string) => {
|
||||
visible.value = true;
|
||||
form.id = '';
|
||||
|
||||
// Reset form data
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
});
|
||||
|
||||
// Load the collection list and AI model list
|
||||
await loadAiModelList();
|
||||
|
||||
// 初始化向量库列表
|
||||
const { data } = await list();
|
||||
storeList.value = data;
|
||||
|
||||
// Set default value for storeId if list is not empty
|
||||
if (storeList.value.length > 0 && !form.storeId) {
|
||||
form.storeId = storeList.value[0].storeId;
|
||||
}
|
||||
|
||||
// Get aiDataset information
|
||||
if (id) {
|
||||
form.id = id;
|
||||
getAiDatasetData(id);
|
||||
} else {
|
||||
// Set default values for new records
|
||||
if (embeddingModelList.value.length > 0) {
|
||||
form.embeddingModel = embeddingModelList.value[0].name;
|
||||
}
|
||||
if (chatModelList.value.length > 0) {
|
||||
form.summaryModel = chatModelList.value[0].name;
|
||||
}
|
||||
if (rerankerModelList.value.length > 0) {
|
||||
form.rerankerModel = rerankerModelList.value[0].name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
try {
|
||||
// 步骤 1: 异步验证整个表单
|
||||
// dataFormRef.value.validate() 会返回一个 Promise
|
||||
// 如果验证失败,它会抛出错误,该错误将被下面的 catch 块捕获
|
||||
await dataFormRef.value.validate();
|
||||
|
||||
// 步骤 2: 如果验证通过,则继续执行提交逻辑
|
||||
// 特殊处理:如果存在 ID (即为编辑模式) 且密码字段包含 '**' (表示未修改),
|
||||
// 则将密码设置为空,避免将占位符提交到后端
|
||||
if (form.id && form.publicPassword?.includes('**')) {
|
||||
form.publicPassword = undefined;
|
||||
}
|
||||
|
||||
loading.value = true; // 开始加载状态,禁用提交按钮等
|
||||
// 根据是否存在 form.id 来判断是更新 (putObj) 还是新增 (addObj)
|
||||
form.id ? await putObj(form) : await addObj(form);
|
||||
|
||||
// 操作成功提示
|
||||
useMessage().success(form.id ? '修改成功' : '添加成功');
|
||||
visible.value = false; // 关闭抽屉(或弹窗)
|
||||
emit('refresh'); // 触发 'refresh' 事件,通知父组件刷新列表数据
|
||||
} catch (invalidFields: any) {
|
||||
// 步骤 3: 处理表单验证失败的情况
|
||||
// invalidFields 对象包含了所有未通过验证的字段及其错误信息
|
||||
// Element Plus 的 validate 方法在验证失败时会 reject 一个包含错误字段的对象
|
||||
if (invalidFields && Object.keys(invalidFields).length > 0) {
|
||||
const errorFields: string[] = Object.keys(invalidFields); // 获取所有验证失败的字段名
|
||||
|
||||
// 定义各选项卡包含的字段,用于定位错误字段所在的选项卡
|
||||
const tabFields: Record<string, string[]> = {
|
||||
basic: ['name', 'avatarUrl', 'welcomeMsg', 'sortOrder'], // 基础配置选项卡下的字段
|
||||
advanced: [
|
||||
'storeId',
|
||||
'embeddingModel',
|
||||
'summaryModel',
|
||||
'multiRound',
|
||||
'topK',
|
||||
'fragmentSize',
|
||||
'score',
|
||||
'emptyLlmFlag',
|
||||
'emptyDesc',
|
||||
'preSummary',
|
||||
'preCompress',
|
||||
'aiOcrFlag',
|
||||
'standardFlag',
|
||||
'rerankerModel',
|
||||
], // 高级配置选项卡下的字段
|
||||
security: ['publicFlag', 'publicPassword', 'visibleUsers', 'sensitiveFlag', 'sensitiveMsg', 'footer'], // 安全配置选项卡下的字段
|
||||
};
|
||||
|
||||
// 遍历 tabFields 来确定哪个选项卡包含第一个出错的字段
|
||||
for (const [tab, fieldsInTab] of Object.entries(tabFields)) {
|
||||
// .some() 方法检查 errorFields 数组中是否有任何一个字段存在于当前 fieldsInTab 数组中
|
||||
if (errorFields.some((errorField: string) => fieldsInTab.includes(errorField))) {
|
||||
activeTab.value = tab; // 如果找到,则切换到该选项卡
|
||||
break; // 找到第一个错误字段所在的选项卡后,停止遍历
|
||||
}
|
||||
}
|
||||
}
|
||||
// 如果验证失败,错误被捕获,提交过程在此处停止。
|
||||
// 函数将继续执行 finally 块。
|
||||
} finally {
|
||||
// 步骤 4: 无论提交成功还是失败,最后都会执行 finally 块
|
||||
loading.value = false; // 结束加载状态
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
243
src/views/knowledge/aiDataset/index.vue
Normal file
243
src/views/knowledge/aiDataset/index.vue
Normal file
@@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()" v-auth="'knowledge_aiDataset_add'">
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'knowledge_aiDataset_del'" @click="handleDelete(selectObjs)">
|
||||
删除
|
||||
</el-button>
|
||||
<right-toolbar
|
||||
v-model:showSearch="showSearch"
|
||||
:export="'knowledge_aiDataset_export'"
|
||||
@exportExcel="exportExcel"
|
||||
class="ml10 mr20"
|
||||
style="float: right"
|
||||
@queryTable="getDataList"
|
||||
></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
|
||||
<el-scrollbar class="h-[calc(100vh-280px)] mb-4">
|
||||
<div class="grid grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
|
||||
<div
|
||||
v-for="dataset in state.dataList"
|
||||
:key="dataset.id"
|
||||
class="group overflow-hidden bg-white rounded-lg shadow-sm border border-gray-100 transition-all duration-300 cursor-pointer dark:bg-gray-800 dark:border-gray-700 hover:shadow-lg hover:border-primary-100 hover:translate-y-[-2px]"
|
||||
@dblclick="go2document(dataset)"
|
||||
>
|
||||
<div class="p-5">
|
||||
<div class="flex items-start">
|
||||
<div class="flex overflow-hidden justify-center items-center rounded-lg border border-gray-200 size-12 dark:border-gray-700">
|
||||
<img :src="baseURL + dataset.avatarUrl" alt="Avatar" class="object-cover w-full h-full" />
|
||||
</div>
|
||||
<div class="overflow-hidden flex-1 ml-3">
|
||||
<div class="text-base font-medium text-gray-900 truncate dark:text-white">
|
||||
{{ dataset.name }}
|
||||
</div>
|
||||
<div class="flex items-center mt-1 text-xs text-gray-500 dark:text-gray-400">
|
||||
<el-icon class="mr-1"><Document /></el-icon>
|
||||
文档数:{{ dataset.units || 0 }}
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="dataset.publicFlag === '1'" class="ml-2">
|
||||
<el-tag size="small" type="success">已发布</el-tag>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overflow-y-auto mt-4 h-16 text-sm text-gray-600 dark:text-gray-300 line-clamp-3">
|
||||
{{ dataset.description || '暂无描述' }}
|
||||
</div>
|
||||
|
||||
<div class="flex justify-start items-center pt-3 mt-4 border-t border-gray-100 dark:border-gray-700">
|
||||
<el-button
|
||||
v-if="dataset.publicFlag === '1'"
|
||||
class="!p-2 text-gray-600 rounded-full transition-colors dark:text-gray-300 hover:text-primary hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'knowledge_aiDataset_edit'"
|
||||
@click="mountDialogRef.openDialog(dataset.id)"
|
||||
>
|
||||
<el-icon><Link /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
class="!p-2 text-gray-600 rounded-full transition-colors dark:text-gray-300 hover:text-primary hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'knowledge_aiDataset_edit'"
|
||||
@click="formDialogRef.openDialog(dataset.id)"
|
||||
>
|
||||
<el-icon><EditPen /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
class="!p-2 text-gray-600 rounded-full transition-colors dark:text-gray-300 hover:text-primary hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
text
|
||||
type="primary"
|
||||
v-auth="'knowledge_aiDataset_del'"
|
||||
@click="handleDelete([dataset.id])"
|
||||
>
|
||||
<el-icon><Delete /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<div class="mx-2 w-px h-4 bg-gray-200 dark:bg-gray-700"></div>
|
||||
|
||||
<el-button
|
||||
class="!p-2 text-gray-600 rounded-full transition-colors dark:text-gray-300 hover:text-primary hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
text
|
||||
type="primary"
|
||||
@click="recallDialogRef.openDialog(dataset)"
|
||||
>
|
||||
<el-icon><Connection /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<el-button
|
||||
class="!p-2 text-gray-600 rounded-full transition-colors dark:text-gray-300 hover:text-primary hover:bg-gray-100 dark:hover:bg-gray-700"
|
||||
text
|
||||
type="primary"
|
||||
@click="handleNavigateToChat(dataset)"
|
||||
>
|
||||
<el-icon><ChatDotRound /></el-icon>
|
||||
</el-button>
|
||||
|
||||
<div class="flex-grow"></div>
|
||||
|
||||
<el-checkbox
|
||||
class="ml-4"
|
||||
:value="selectObjs.includes(dataset.id)"
|
||||
@change="(val: boolean) => handleCardSelect(val, dataset.id)"
|
||||
></el-checkbox>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-scrollbar>
|
||||
|
||||
<!-- 无数据显示 -->
|
||||
<el-empty v-if="!state.dataList || state.dataList.length === 0" description="暂无数据"></el-empty>
|
||||
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
<mount-dialog ref="mountDialogRef" />
|
||||
<recall-dialog ref="recallDialogRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemAiDataset">
|
||||
import { BasicTableProps, useTable } from '/@/hooks/table';
|
||||
import { fetchList, delObjs } from '/@/api/knowledge/aiDataset';
|
||||
import { useMessage, useMessageBox } from '/@/hooks/message';
|
||||
import { EditPen, Delete, Document, Link, ChatDotRound, Connection } from '@element-plus/icons-vue';
|
||||
import { computed } from 'vue';
|
||||
|
||||
const router = useRouter();
|
||||
const baseURL = computed(() => import.meta.env.VITE_API_URL);
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const MountDialog = defineAsyncComponent(() => import('./mount.vue'));
|
||||
const RecallDialog = defineAsyncComponent(() => import('./recall.vue'));
|
||||
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref();
|
||||
const mountDialogRef = ref();
|
||||
const recallDialogRef = ref();
|
||||
// 搜索变量
|
||||
const queryRef = ref();
|
||||
const showSearch = ref(true);
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any;
|
||||
const multiple = ref(true);
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: fetchList,
|
||||
dataList: [],
|
||||
});
|
||||
|
||||
// table hook
|
||||
const { getDataList, currentChangeHandle, sizeChangeHandle, downBlobFile } = useTable(state);
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields();
|
||||
// 清空多选
|
||||
selectObjs.value = [];
|
||||
getDataList();
|
||||
};
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/knowledge/aiDataset/export', Object.assign(state.queryForm, { ids: selectObjs }), 'aiDataset.xlsx');
|
||||
};
|
||||
|
||||
// 多选事件 - 为卡片视图添加的选择函数
|
||||
const handleCardSelect = (selected: boolean, dataId: string) => {
|
||||
if (selected) {
|
||||
selectObjs.value.push(dataId);
|
||||
} else {
|
||||
selectObjs.value = selectObjs.value.filter((id: string) => id !== dataId);
|
||||
}
|
||||
multiple.value = selectObjs.value.length === 0;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
const go2document = (dataset: any) => {
|
||||
router.push({
|
||||
path: '/knowledge/aiDocument/index',
|
||||
query: {
|
||||
datasetId: dataset.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const handleNavigateToChat = (dataset: any) => {
|
||||
router.push({
|
||||
path: '/knowledge/aiChat/index',
|
||||
query: {
|
||||
datasetId: dataset.id,
|
||||
},
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
overflow-x: hidden !important;
|
||||
}
|
||||
|
||||
:deep(.el-checkbox) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.bg-primary-100 {
|
||||
background-color: var(--el-color-primary-light-9);
|
||||
}
|
||||
|
||||
.text-primary {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
</style>
|
||||
215
src/views/knowledge/aiDataset/mount.vue
Normal file
215
src/views/knowledge/aiDataset/mount.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
title="选择挂载方式"
|
||||
width="50%"
|
||||
:modal="false"
|
||||
>
|
||||
<div>
|
||||
<el-card size="mini">
|
||||
<el-row :gutter="10" justify="center">
|
||||
<el-col :span="8">
|
||||
<div :class="selectedMethod === 1 ? 'img-selected' : 'img-unselect'" @click="selectImg(1)">
|
||||
<div class="css-1awpln7">
|
||||
<div class="css-0">
|
||||
<img alt="" src="/@/assets/ai/link.svg" class="chakra-image css-0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div :class="selectedMethod === 2 ? 'img-selected' : 'img-unselect'" @click="selectImg(2)">
|
||||
<div class="css-1awpln7">
|
||||
<div class="css-0">
|
||||
<img alt="" src="/@/assets/ai/iframe.svg" class="chakra-image css-0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div :class="selectedMethod === 3 ? 'img-selected' : 'img-unselect'" @click="selectImg(3)">
|
||||
<div class="css-1awpln7">
|
||||
<div class="css-0">
|
||||
<img alt="" src="/@/assets/ai/script.svg" class="chakra-image css-0">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-card>
|
||||
</div>
|
||||
<div>
|
||||
<el-card size="mini" body-class="card-class">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<div v-if="selectedMethod === 1">
|
||||
<span>将下面链接复制到浏览器打开</span>
|
||||
<span>
|
||||
<el-button style="float: right" class="ml-2" type="primary" @click="openText(url())">打开</el-button>
|
||||
<el-button style="float: right" type="primary" @click="copyText(url())">复制</el-button>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="selectedMethod === 2">
|
||||
<span>复制下面 Iframe 加入到你的网站中</span>
|
||||
<span>
|
||||
<el-button style="float: right" type="primary" @click="copyText(iframe())">复制</el-button>
|
||||
</span>
|
||||
</div>
|
||||
<div v-if="selectedMethod === 3">
|
||||
<span>将下面代码加入到你的网站中</span>
|
||||
<span>
|
||||
<el-button style="float: right" type="primary" @click="copyText(script())">复制</el-button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<span v-if="selectedMethod === 1">
|
||||
<div class="mockup-code">
|
||||
<pre data-prefix="$"><code> {{ url() }}</code></pre>
|
||||
</div>
|
||||
</span>
|
||||
<span v-if="selectedMethod === 2">
|
||||
<div class="mockup-code">
|
||||
<pre data-prefix="$"><code> {{ iframe() }}</code></pre>
|
||||
</div>
|
||||
</span>
|
||||
<span v-if="selectedMethod === 3">
|
||||
<div class="mockup-code">
|
||||
<pre data-prefix="$"><code> {{ script() }}</code></pre>
|
||||
</div>
|
||||
</span>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="AiDatasetDialog">
|
||||
import commonFunction from "/@/utils/commonFunction";
|
||||
|
||||
const emit = defineEmits(['refresh']);
|
||||
const {copyText} = commonFunction();
|
||||
|
||||
const left_right = [
|
||||
{
|
||||
label: '左侧',
|
||||
value: 'left',
|
||||
},
|
||||
{
|
||||
label: '右侧',
|
||||
value: 'right',
|
||||
}
|
||||
]
|
||||
|
||||
const top_bottom = [
|
||||
{
|
||||
label: '顶部',
|
||||
value: 'top',
|
||||
},
|
||||
{
|
||||
label: '底部',
|
||||
value: 'bottom',
|
||||
}
|
||||
]
|
||||
const dialogVisible = ref(false)
|
||||
const selectedDatasetId = ref()
|
||||
const selectedMethod = ref(1)
|
||||
const data_btn_x = ref(16)
|
||||
const data_btn_y = ref(16)
|
||||
const data_stream = ref(true)
|
||||
const data_direction_x = ref('right')
|
||||
const data_direction_y = ref('bottom')
|
||||
const openDialog = (datasetid: any) => {
|
||||
dialogVisible.value = true
|
||||
selectedDatasetId.value = datasetid
|
||||
}
|
||||
|
||||
const currentHostname = window.location.origin;
|
||||
const openImageBase64 = ref("")
|
||||
const closeImageBase64 = ref("")
|
||||
|
||||
function handleOpenIconBeforeUpload(file: Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = e => {
|
||||
// 在这里处理 Base64 数据
|
||||
openImageBase64.value = e.target.result
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
function handleCloseIconBeforeUpload(file: Blob) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = e => {
|
||||
// 在这里处理 Base64 数据
|
||||
closeImageBase64.value = e.target.result
|
||||
};
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
const isMicro = import.meta.env.VITE_IS_MICRO === 'true' ? 1 : 0
|
||||
|
||||
const script = () => {
|
||||
return `<script id="chatbot-iframe" src="${currentHostname}/bot/embed.min.js?t=${Date.now()}" data-bot-src="${currentHostname}/bot/index.html#/${isMicro}/${selectedDatasetId.value}/chat" async defer><\/script>`
|
||||
}
|
||||
|
||||
const iframe = () => {
|
||||
return `<iframe src="${currentHostname}/bot/bot/index.html#/${isMicro}/${selectedDatasetId.value}/chat" style="width: 100%; height: 100%;" frameborder="0" allow="microphone"/>`
|
||||
}
|
||||
|
||||
const url = () => {
|
||||
return `${currentHostname}/bot/index.html#/${isMicro}/${selectedDatasetId.value}/chat`
|
||||
}
|
||||
const selectImg = (index: number) => {
|
||||
selectedMethod.value = index
|
||||
}
|
||||
|
||||
const openText = (url: string) => {
|
||||
window.open(url)
|
||||
}
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.img-unselect {
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border: 1.5px solid rgb(232, 235, 240);
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
background: #fbfbfc;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.img-unselect:hover {
|
||||
border-color: #1dbcd8;
|
||||
}
|
||||
|
||||
.img-selected {
|
||||
display: flex;
|
||||
-webkit-box-align: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
border-style: solid;
|
||||
border-image: initial;
|
||||
border-width: 1.5px;
|
||||
border-radius: 5px;
|
||||
position: relative;
|
||||
border-color: #1dbcd8;
|
||||
background: #f0f4ff;
|
||||
padding: 0px !important;
|
||||
}
|
||||
|
||||
.card-class {
|
||||
background-color: #F4F4F7;
|
||||
}
|
||||
</style>
|
||||
161
src/views/knowledge/aiDataset/recall.vue
Normal file
161
src/views/knowledge/aiDataset/recall.vue
Normal file
@@ -0,0 +1,161 @@
|
||||
<template>
|
||||
<el-drawer v-model="visible" title="召回测试" size="50%">
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" label-width="auto" v-loading="loading">
|
||||
<el-row>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="知识库名称">
|
||||
<el-input :placeholder="dataset?.name" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="召回数量" prop="topK">
|
||||
<el-input-number v-model="form.topK" :min="1" :max="10" :step="1" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="是否全文检索" prop="fullTextSearch">
|
||||
<el-radio-group v-model="form.fullTextSearch">
|
||||
<el-radio v-for="(item, index) in yes_no_type" :key="index" :label="item.value" border>{{ item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="是否重排" prop="reRanking">
|
||||
<el-radio-group v-model="form.reRanking">
|
||||
<el-radio v-for="(item, index) in yes_no_type" :key="index" :label="item.value" border>{{ item.label }}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="问题" prop="content">
|
||||
<el-input v-model="form.content" type="textarea" placeholder="请输入需要召回的问题内容" maxlength="1024" show-word-limit rows="6" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-divider content-position="center">召回结果</el-divider>
|
||||
|
||||
<el-table v-if="tableData.length > 0" :data="tableData" border style="width: 100%">
|
||||
<el-table-column type="index" label="序号" width="60" />
|
||||
<el-table-column prop="score" label="相似度" width="120" />
|
||||
<el-table-column prop="content" label="切片" :show-overflow-tooltip="true" />
|
||||
<el-table-column prop="metadata" label="元数据" width="200" :show-overflow-tooltip="true" />
|
||||
</el-table>
|
||||
|
||||
<el-empty v-else description="暂无数据" />
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleTest" :disabled="loading || !form.content">测试</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="AiDatasetRecall">
|
||||
import { useMessage } from '/@/hooks/message';
|
||||
import { fetchRecall } from '/@/api/knowledge/aiSlice';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// 定义接口
|
||||
interface RecallParams {
|
||||
datasetId: string;
|
||||
content: string;
|
||||
topK: number;
|
||||
fullTextSearch: boolean;
|
||||
reRanking: boolean;
|
||||
}
|
||||
|
||||
// 定义变量内容
|
||||
const visible = ref(false);
|
||||
const loading = ref(false);
|
||||
const tableData = ref([]);
|
||||
const dataset = ref<any>(null);
|
||||
|
||||
// 定义字典
|
||||
const { yes_no_type } = useDict('yes_no_type');
|
||||
|
||||
// 定义表单变量
|
||||
interface FormState {
|
||||
datasetId: string;
|
||||
content: string;
|
||||
topK: number;
|
||||
fullTextSearch: string;
|
||||
reRanking: string;
|
||||
}
|
||||
|
||||
const form = reactive<FormState>({
|
||||
datasetId: '',
|
||||
content: '',
|
||||
topK: 5,
|
||||
fullTextSearch: '1', // 是否全文检索
|
||||
reRanking: '1', // 是否重排
|
||||
});
|
||||
|
||||
// 表单校验规则
|
||||
const dataRules = ref({
|
||||
content: [{ required: true, message: '请输入问题内容', trigger: 'blur' }],
|
||||
});
|
||||
|
||||
// 打开抽屉
|
||||
const openDialog = (datasetVal: any) => {
|
||||
visible.value = true;
|
||||
form.datasetId = datasetVal.id;
|
||||
dataset.value = datasetVal;
|
||||
resetForm();
|
||||
};
|
||||
|
||||
// 重置表单
|
||||
const resetForm = () => {
|
||||
form.content = '';
|
||||
tableData.value = [];
|
||||
};
|
||||
|
||||
// 测试召回
|
||||
const handleTest = async () => {
|
||||
if (!form.content) {
|
||||
useMessage().warning('请输入问题内容');
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
try {
|
||||
const { data } = await fetchRecall({
|
||||
datasetId: form.datasetId,
|
||||
content: form.content,
|
||||
topK: form.topK,
|
||||
fullTextSearch: form.fullTextSearch === '1',
|
||||
reRanking: form.reRanking === '1',
|
||||
} as RecallParams);
|
||||
|
||||
if (data) {
|
||||
tableData.value = data;
|
||||
} else {
|
||||
tableData.value = [];
|
||||
useMessage().warning('未查询到数据');
|
||||
}
|
||||
} catch (error: any) {
|
||||
useMessage().error(error.msg || '操作失败');
|
||||
tableData.value = [];
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露方法
|
||||
defineExpose({
|
||||
openDialog,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(.el-drawer__body) {
|
||||
padding: 20px;
|
||||
overflow: auto;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user