作者 dong

资料管理界面增删改查及导入功能

... ... @@ -6,7 +6,7 @@
<template #tableTitle>
<a-button type="primary" v-auth="'embeddings:embeddings:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
<a-button type="primary" v-auth="'embeddings:embeddings:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
<j-upload-button type="primary" v-auth="'embeddings:embeddings:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"
<j-upload-button type="primary" v-auth="'embeddings:embeddings:importWord'" preIcon="ant-design:import-outlined" @click="onImportXls"
>导入</j-upload-button
>
<a-dropdown v-if="selectedRowKeys.length > 0">
... ... @@ -39,14 +39,15 @@
</template>
<script lang="ts" name="test-test" setup>
import { ref, reactive, computed, unref } from 'vue';
import { BasicTable, useTable, TableAction } from '/@/components/Table';
import { ref, reactive } from 'vue';
import { BasicTable, TableAction } from '/@/components/Table';
import { useModal } from '/@/components/Modal';
import { useListPage } from '/@/hooks/system/useListPage';
import TestModal from './components/TestModal.vue';
import { columns, searchFormSchema, superQuerySchema } from './Test.data';
import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Test.api';
import { useUserStore } from '/@/store/modules/user';
import JUploadButton from "@/components/Button/src/JUploadButton.vue";
const queryParam = reactive<any>({});
const checkedKeys = ref<Array<string | number>>([]);
const userStore = useUserStore();
... ... @@ -85,12 +86,10 @@
success: handleSuccess,
},
});
const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
// 高级查询配置
const superQueryConfig = reactive(superQuerySchema);
/**
* 高级查询事件
*/
... ... @@ -107,6 +106,11 @@
openModal(true, {
isUpdate: false,
showFooter: true,
record: {
// 关键:添加初始值
docName: '',
text: '',
},
});
}
/**
... ... @@ -167,6 +171,7 @@
{
label: '详情',
onClick: handleDetail.bind(null, record),
auth: 'embeddings:embeddings:edit',
},
{
label: '删除',
... ...
... ... @@ -11,54 +11,24 @@ enum Api {
deleteBatch = '/embeddings/embeddings/deleteBatch',
importExcel = '/embeddings/embeddings/importExcel',
exportXls = '/embeddings/embeddings/exportXls',
importWord = '/embeddings/embeddings/importWord',
queryid = '/queryById',
}
/**
* 导出api
* @param params
*/
export const getExportUrl = Api.exportXls;
/**
* 导入api
*/
export const getImportUrl = Api.importExcel;
export const getImportUrl = Api.importWord;
/**
* 列表接口
* @param params
*/
export const list = async (params) => {
try {
// 设置足够大的size获取所有数据
const res = await defHttp.get({
url: Api.list,
params: { ...params, size: 1000 }
});
if (res?.records && Array.isArray(res.records)) {
// 将metadata中的字段提取到顶层
res.records = res.records.map(item => ({
...item,
...item.metadata, // 将metadata中的字段展开
docName: item.metadata?.docName || '',
knowledgeId: item.metadata?.knowledgeId || '',
docId: item.metadata?.docId || '',
index: item.metadata?.index || '',
}));
}
return res;
} catch (error) {
console.error("Error fetching data:", error);
return {
records: [],
total: 0,
size: 10,
current: 1,
pages: 0
};
}
}
export const list = (params) => defHttp.get({ url: Api.list, params });
/**
* 删除单个
... ... @@ -68,7 +38,6 @@ export const deleteOne = (params, handleSuccess) => {
handleSuccess();
});
};
/**
* 批量删除
* @param params
... ... @@ -87,7 +56,6 @@ export const batchDelete = (params, handleSuccess) => {
},
});
};
/**
* 保存或者更新
* @param params
... ...
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { h } from 'vue';
//import { Tooltip } from 'ant-design-vue';
// 列表数据
//列表数据
export const columns: BasicColumn[] = [
{
/*{
title: 'ID',
align: 'center',
dataIndex: 'id',
width: 200,
},
{
title: '文本内容',
customRender: ({ text, record }) => {
// 调试:检查ID值和原始记录
console.log(`ID渲染 - 值: ${text}, 记录:`, record);
// 如果ID为空,显示详细的调试信息
if (!text || text === 'ID-未找到') {
return h('div', { style: 'color: #f50; cursor: help;', title: '点击查看详情' }, [
h('span', { style: 'font-weight: bold;' }, 'ID缺失'),
h('div', { style: 'font-size: 10px; margin-top: 4px;' }, `字段: ${Object.keys(record._raw || {}).join(', ')}`),
]);
}
return h('span', { style: 'font-family: monospace' }, text);
},
},*/
/*{
title: '文件名称',
align: 'center',
dataIndex: 'text',
width: 300
},
},*/
{
title: '文名称',
title: '文名称',
align: 'center',
dataIndex: 'docName',
width: 150
width: 180,
customRender: ({ record }) => {
return renderMetadataField(record, 'docName');
},
},
{
title: '知识ID',
title: '文件内容',
align: 'center',
dataIndex: 'text',
},
/* {
title: '知识库ID',
align: 'center',
dataIndex: 'knowledgeId',
width: 150
width: 150,
customRender: ({ record }) => {
return renderMetadataField(record, 'knowledgeId');
},
},
{
title: '文档名称',
align: 'center',
dataIndex: 'docName',
width: 180,
customRender: ({ record }) => {
return renderMetadataField(record, 'docName');
},
},
{
title: '文档ID',
align: 'center',
dataIndex: 'docId',
width: 150
width: 150,
customRender: ({ record }) => {
return renderMetadataField(record, 'docId');
},
},
{
title: '索引',
title: '索引位置',
align: 'center',
dataIndex: 'index',
width: 80
width: 100,
customRender: ({ record }) => {
return renderMetadataField(record, 'index');
},
},
{
title: '相似度',
title: 'metadata',
align: 'center',
dataIndex: 'similarity',
width: 100
},
dataIndex: 'metadata',
customRender: ({ text }) => {
if (!text) return '-';
try {
// 尝试解析JSON
const metadata = typeof text === 'string' ? JSON.parse(text) : text;
// 创建元数据标签
const tags = Object.entries(metadata).map(([key, value]) => h(tags, { color: 'blue' }, `${key}: ${value}`));
return h(
Tooltip,
{
title: h('pre', JSON.stringify(metadata, null, 2)),
overlayStyle: { maxWidth: '600px' },
},
() => h('div', { style: 'display: flex; flex-wrap: wrap; gap: 4px;' }, tags)
);
} catch (e) {
// 无法解析时显示原始文本
const displayText = typeof text === 'string' ? text : JSON.stringify(text);
const shortText = displayText.length > 20 ? displayText.substring(0, 20) + '...' : displayText;
return h(Tooltip, { title: displayText }, () => h('span', shortText));
}
},
},*/
];
// 查询数据
export const searchFormSchema: FormSchema[] = [];
function renderMetadataField(record: any, fieldName: string) {
if (!record.metadata) return '-';
// 表单数据
export const formSchema: FormSchema[] = [
try {
// 尝试解析metadata
const metadata = typeof record.metadata === 'string' ? JSON.parse(record.metadata) : record.metadata;
// 获取字段值
const value = metadata[fieldName];
if (value === undefined || value === null) {
return h('span', { style: 'color: #999' }, '无');
}
// 根据字段类型渲染
switch (fieldName) {
case 'knowledgeId':
case 'docId':
return h('span', { style: 'font-family: monospace' }, value);
case 'index':
return h('span', { style: 'font-weight: bold' }, value);
default:
return h('span', value);
}
} catch (e) {
return h('span', { style: 'color: #f50' }, '解析错误');
}
}
//查询数据
export const searchFormSchema: FormSchema[] = [
{
label: 'text',
label: '文本内容',
field: 'text',
component: 'Input',
},
];
//表单数据
export const formSchema: FormSchema[] = [
{
label: '文件名称',
field: 'docName', // 使用新的字段名
component: 'Input',
required: true,
componentProps: {
placeholder: '请输入文档名称',
},
},
{
label: '文本内容',
field: 'text',
required: true,
component: 'InputTextArea',
},
// TODO 主键隐藏字段,目前写死为ID
{
label: '',
field: 'id',
... ... @@ -67,13 +179,14 @@ export const formSchema: FormSchema[] = [
// 高级查询数据
export const superQuerySchema = {
name: { title: 'text', order: 0, view: 'text', type: 'string' },
name: { title: 'name', order: 0, view: 'text', type: 'string' },
};
/**
* 流程表单调用这个方法获取formSchema
* 流程表单调用这个方法获取formSchemaqrtz_fired_triggers
* @param param
*/
export function getBpmFormSchema(_formData): FormSchema[] {
// 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
return formSchema;
}
... ...
<template>
<div style="min-height: 400px">
<BasicForm @register="registerForm"></BasicForm>
<div style="width: 100%;text-align: center" v-if="!formDisabled">
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button>
</div>
<div style="min-height: 400px">
<BasicForm @register="registerForm" />
<div style="width: 100%; text-align: center" v-if="!formDisabled">
<a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button>
</div>
</div>
</template>
<script lang="ts">
import {BasicForm, useForm} from '/@/components/Form/index';
import {computed, defineComponent} from 'vue';
import {defHttp} from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
import {getBpmFormSchema} from '../Test.data';
import {saveOrUpdate} from '../Test.api';
export default defineComponent({
name: "TestForm",
components:{
BasicForm
},
props:{
formData: propTypes.object.def({}),
formBpm: propTypes.bool.def(true),
},
setup(props){
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
schemas: getBpmFormSchema(props.formData),
showActionButtonGroup: false,
baseColProps: {span: 24}
});
import { BasicForm, useForm } from '/@/components/Form/index';
import { computed, defineComponent } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { propTypes } from '/@/utils/propTypes';
import { getBpmFormSchema } from '../Test.data';
import { saveOrUpdate } from '../Test.api';
const formDisabled = computed(()=>{
if(props.formData.disabled === false){
return false;
}
return true;
});
export default defineComponent({
name: 'TestForm',
components: {
BasicForm,
},
props: {
formData: propTypes.object.def({}),
formBpm: propTypes.bool.def(true),
},
setup(props) {
const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
labelWidth: 150,
schemas: getBpmFormSchema(props.formData),
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
let formData = {};
const queryByIdUrl = '/test/test/queryById';
async function initFormData(){
let params = {id: props.formData.dataId};
const data = await defHttp.get({url: queryByIdUrl, params});
formData = {...data}
//设置表单的值
await setFieldsValue(formData);
//默认是禁用
await setProps({disabled: formDisabled.value})
}
const formDisabled = computed(() => {
if (props.formData.disabled === false) {
return false;
}
return true;
});
async function submitForm() {
let data = getFieldsValue();
let params = Object.assign({}, formData, data);
console.log('表单数据', params)
await saveOrUpdate(params, true)
}
let formData = {};
const queryByIdUrl = '/test/test/queryById';
async function initFormData() {
let params = { id: props.formData.dataId };
const data = await defHttp.get({ url: queryByIdUrl, params });
formData = { ...data };
//设置表单的值
await setFieldsValue(formData);
//默认是禁用
await setProps({ disabled: formDisabled.value });
}
initFormData();
return {
registerForm,
formDisabled,
submitForm,
}
}
});
</script>
\ No newline at end of file
async function submitForm() {
let data = getFieldsValue();
let params = Object.assign({}, formData, data);
console.log('表单数据', params);
await saveOrUpdate(params, true);
}
initFormData();
return {
registerForm,
formDisabled,
submitForm,
};
},
});
</script>
... ...
<template>
<BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
<BasicForm @register="registerForm" name="TestForm" />
<BasicForm @register="registerForm" name="TestForm" />
</BasicModal>
</template>
<script lang="ts" setup>
import {ref, computed, unref} from 'vue';
import {BasicModal, useModalInner} from '/@/components/Modal';
import {BasicForm, useForm} from '/@/components/Form/index';
import {formSchema} from '../Test.data';
import {saveOrUpdate} from '../Test.api';
// Emits声明
const emit = defineEmits(['register','success']);
const isUpdate = ref(true);
const isDetail = ref(false);
//表单配置
const [registerForm, { setProps,resetFields, setFieldsValue, validate, scrollToField }] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: {span: 24}
});
//表单赋值
const [registerModal, {setModalProps, closeModal}] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({confirmLoading: false,showCancelBtn:!!data?.showFooter,showOkBtn:!!data?.showFooter});
isUpdate.value = !!data?.isUpdate;
isDetail.value = !!data?.showFooter;
if (unref(isUpdate)) {
//表单赋值
await setFieldsValue({
...data.record,
});
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter })
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));
//表单提交事件
async function handleSubmit(v) {
import { ref, computed, unref } from 'vue';
import { BasicModal, useModalInner } from '/@/components/Modal';
import { BasicForm, useForm } from '/@/components/Form/index';
import { formSchema } from '../Test.data';
import { saveOrUpdate } from '../Test.api';
// Emits声明
const emit = defineEmits(['register', 'success']);
const isUpdate = ref(true);
const isDetail = ref(false);
//表单配置
const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField }] = useForm({
labelWidth: 150,
schemas: formSchema,
showActionButtonGroup: false,
baseColProps: { span: 24 },
});
//表单赋值
const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
//重置表单
await resetFields();
setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
isUpdate.value = !!data?.isUpdate;
isDetail.value = !!data?.showFooter;
if (unref(isUpdate)) {
const formData = { ...data.record };
// 如果是详情模式,提取 metadata 中的 docName
if (formData.metadata) {
try {
let values = await validate();
setModalProps({confirmLoading: true});
//提交表单
await saveOrUpdate(values, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} catch ({ errorFields }) {
if (errorFields) {
const firstField = errorFields[0];
if (firstField) {
scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
}
}
return Promise.reject(errorFields);
} finally {
setModalProps({confirmLoading: false});
const metadata = typeof formData.metadata === 'string' ? JSON.parse(formData.metadata) : formData.metadata;
formData.docName = metadata.docName || '未命名';
} catch (e) {
console.error('元数据解析失败', e);
formData.docName = '元数据格式错误';
}
}
await setFieldsValue(formData);
}
// 隐藏底部时禁用整个表单
setProps({ disabled: !data?.showFooter });
});
//设置标题
const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));
//表单提交事件
async function handleSubmit() {
try {
let values = await validate();
// 仅保留需要的字段,其他由后端自动生成
const payload = {
id: values.id,
docName: values.docName, // 文件名称
text: values.text, // 文件内容
};
setModalProps({ confirmLoading: true });
//提交表单
await saveOrUpdate(payload, isUpdate.value);
//关闭弹窗
closeModal();
//刷新列表
emit('success');
} catch ({ errorFields }) {
if (errorFields) {
const firstField = errorFields[0];
if (firstField) {
scrollToField(firstField.name, { behavior: 'smooth', block: any });
}
}
return Promise.reject(errorFields);
} finally {
setModalProps({ confirmLoading: false });
}
}
</script>
<style lang="less" scoped>
/** 时间和数字输入框样式 */
/** 时间和数字输入框样式 */
:deep(.ant-input-number) {
width: 100%;
}
... ... @@ -73,4 +90,4 @@
:deep(.ant-calendar-picker) {
width: 100%;
}
</style>
\ No newline at end of file
</style>
... ...