Files
school-developer/src/views/knowledge/aiEmbedStore/form.vue
吴红兵 1f645dad3e init
2025-12-02 10:37:49 +08:00

318 lines
9.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog width="40%" :title="form.storeId ? '编辑' : '新增'" v-model="visible" :close-on-click-modal="false" draggable>
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
<el-row :gutter="24">
<el-col :span="24" class="mb20">
<el-form-item label="类型" prop="storeType">
<el-select v-model="form.storeType" placeholder="请选择类型">
<el-option :label="item.label" :value="item.value" v-for="(item, index) in embed_store_type" :key="index"></el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="名称" prop="name">
<el-input v-model="form.name" placeholder="请输入名称" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20" v-if="form.storeType !== 'qdrant' && form.storeType !== 'redis' && form.storeType !== 'pgvector'">
<el-form-item label="URI" prop="uri">
<el-input v-model="form.uri" placeholder="请输入链接地址" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20" v-if="form.storeType === 'qdrant' || form.storeType === 'redis' || form.storeType === 'pgvector'">
<el-form-item label="Host" prop="host">
<el-input v-model="form.host" placeholder="请输入Host" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20" v-if="form.storeType === 'qdrant' || form.storeType === 'redis' || form.storeType === 'pgvector'">
<el-form-item label="端口" prop="port">
<el-input-number v-model="form.port" placeholder="请输入端口" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20" v-if="form.storeType !== 'pgvector'">
<el-form-item label="密钥" prop="apiKey">
<el-input v-model="form.apiKey" placeholder="请输入密钥" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20" v-if="form.storeType === 'milvus'">
<el-form-item label="数据库" prop="extData">
<el-input v-model="form.extData" placeholder="请输入数据库" />
</el-form-item>
</el-col>
<!-- PgVector 特有配置 -->
<template v-if="form.storeType === 'pgvector'">
<el-col :span="24" class="mb20">
<el-form-item label="用户名" prop="pgUsername">
<el-input v-model="form.pgUsername" placeholder="请输入用户名" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="密码" prop="pgPassword">
<el-input v-model="form.pgPassword" type="password" placeholder="请输入密码" show-password />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="数据库" prop="pgDatabase">
<el-input v-model="form.pgDatabase" placeholder="请输入数据库名" />
</el-form-item>
</el-col>
<el-col :span="24" class="mb20">
<el-form-item label="维度" prop="pgDimension">
<el-input-number v-model="form.pgDimension" :min="1" :max="4096" placeholder="请输入向量维度" />
</el-form-item>
</el-col>
</template>
<el-col :span="24" class="mb20">
<el-form-item prop="useTls" v-if="form.storeType === 'qdrant'">
<template #label> TLS<tip content="HTTPS安全认证" /> </template>
<el-radio-group v-model="form.useTls">
<el-radio :key="index" :label="item.value" border v-for="(item, index) in yes_no_type">{{ item.label }} </el-radio>
</el-radio-group>
</el-form-item>
</el-col>
</el-row>
</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-dialog>
</template>
<script setup lang="ts" name="AiEmbedStoreDialog">
import { useDict } from '/@/hooks/dict';
import { useMessage } from '/@/hooks/message';
import { getObj, addObj, putObj, validateExist } from '/@/api/knowledge/aiEmbedStore';
const emit = defineEmits(['refresh']);
// 定义变量内容
const dataFormRef = ref();
const visible = ref(false);
const loading = ref(false);
// 定义字典
const { embed_store_type, yes_no_type } = useDict('embed_store_type', 'yes_no_type');
// 提交表单数据
const form = reactive({
storeId: '',
name: '',
storeType: 'milvus',
host: '127.0.0.1',
port: 6334,
uri: 'http://127.0.0.1:19530',
apiKey: '',
extData: 'default',
useTls: '0',
// PgVector 特有字段
pgUsername: 'postgres',
pgPassword: 'postgres',
pgDatabase: 'database',
pgDimension: 4096,
});
// 监听 storeType 变化,设置默认端口
watch(
() => form.storeType,
(newType) => {
if (newType === 'pgvector') {
form.port = 5432;
} else if (newType === 'qdrant') {
form.port = 6334;
} else if (newType === 'redis') {
form.port = 6379;
}
}
);
// 定义校验规则
const dataRules = ref({
name: [
{ required: true, message: '名称不能为空', trigger: 'blur' },
{ max: 64, message: '长度不能超过64个字符', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
validateExist(rule, value, callback, form.storeId !== '');
},
trigger: 'blur',
},
],
storeType: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
host: [
{ required: true, message: 'Host不能为空', trigger: 'blur' },
{ max: 255, message: '长度不能超过255个字符', trigger: 'blur' },
],
port: [
{ required: true, message: '端口不能为空', trigger: 'blur' },
{ type: 'number', max: 65535, message: '端口不能超过65535', trigger: 'blur' },
],
uri: [
{ required: true, message: '地址不能为空', trigger: 'blur' },
{ max: 255, message: '长度不能超过255个字符', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
// URI变化时重新验证数据库字段
nextTick(() => {
if (dataFormRef.value) {
dataFormRef.value.validateField('extData');
}
});
callback();
},
trigger: 'blur',
},
],
extData: [
{ max: 255, message: '长度不能超过255个字符', trigger: 'blur' },
{
validator: (rule: any, value: any, callback: any) => {
// 检查URI是否包含.zilliz.com.cn在线服务
if (form.uri && form.uri.includes('.zilliz.com.cn')) {
if (!value) {
callback(new Error('使用Zilliz在线服务时数据库字段不能为空'));
return;
}
// 检查数据库字段格式是否为 db_xxxx
const dbPattern = /^db_\w+$/;
if (!dbPattern.test(value)) {
callback(new Error('使用Zilliz在线服务时数据库字段必须是 db_id 的形式'));
return;
}
}
callback();
},
trigger: 'blur',
},
],
// PgVector 校验规则
pgUsername: [
{ required: true, message: '用户名不能为空', trigger: 'blur' },
{ max: 64, message: '长度不能超过64个字符', trigger: 'blur' },
],
pgPassword: [
{ required: true, message: '密码不能为空', trigger: 'blur' },
{ max: 255, message: '长度不能超过255个字符', trigger: 'blur' },
],
pgDatabase: [
{ required: true, message: '数据库名不能为空', trigger: 'blur' },
{ max: 64, message: '长度不能超过64个字符', trigger: 'blur' },
],
pgDimension: [
{ required: true, message: '维度不能为空', trigger: 'blur' },
{ type: 'number', min: 1, max: 4096, message: '维度必须在1-4096之间', trigger: 'blur' },
],
});
// 打开弹窗
const openDialog = (id: string) => {
visible.value = true;
form.storeId = '';
form.extData = '';
// 重置表单数据
nextTick(() => {
dataFormRef.value?.resetFields();
});
// 获取aiEmbedStore信息
if (id) {
form.storeId = id;
getaiEmbedStoreData(id);
}
};
// 提交
const onSubmit = async () => {
const valid = await dataFormRef.value.validate().catch(() => {});
if (!valid) return false;
try {
loading.value = true;
// 修复TypeScript错误正确处理apiKey的类型
const submitForm = { ...form };
// 处理 pgvector 类型的特殊逻辑
if (form.storeType === 'pgvector') {
// 将 pgvector 的配置信息存储到 extData 中
const pgConfig = {
username: form.pgUsername,
password: form.pgPassword,
database: form.pgDatabase,
dimension: form.pgDimension,
};
submitForm.extData = JSON.stringify(pgConfig);
// 清空不需要的字段
submitForm.apiKey = '';
} else {
// 处理其他类型的 apiKey
if (submitForm.apiKey?.includes('***')) {
submitForm.apiKey = '';
}
}
// 移除 pgvector 特有的临时字段
delete (submitForm as any).pgUsername;
delete (submitForm as any).pgPassword;
delete (submitForm as any).pgDatabase;
delete (submitForm as any).pgDimension;
form.storeId ? await putObj(submitForm) : await addObj(submitForm);
useMessage().success(form.storeId ? '修改成功' : '添加成功');
visible.value = false;
emit('refresh');
} catch (err: any) {
useMessage().error(err.msg);
} finally {
loading.value = false;
}
};
// 初始化表单数据
const getaiEmbedStoreData = (id: string) => {
// 获取数据
loading.value = true;
getObj({ storeId: id })
.then((res: any) => {
Object.assign(form, res.data);
// 如果是 pgvector 类型,解析 extData 中的配置
if (form.storeType === 'pgvector' && form.extData) {
try {
const pgConfig = JSON.parse(form.extData);
form.pgUsername = pgConfig.username || 'postgres';
form.pgPassword = pgConfig.password || 'postgres';
form.pgDatabase = pgConfig.database || 'database';
form.pgDimension = pgConfig.dimension || 4096;
} catch (e) {
// 解析失败时使用默认值
form.pgUsername = 'postgres';
form.pgPassword = 'postgres';
form.pgDatabase = 'database';
form.pgDimension = 4096;
}
}
})
.finally(() => {
loading.value = false;
});
};
// 暴露变量
defineExpose({
openDialog,
});
</script>