作者 dong

资料管理界面新增查询编辑功能添加了确定所属知识库的功能,按钮管理界面的增删改查及界面按钮开关实时操作开启和关闭功能

@@ -12,16 +12,16 @@ @@ -12,16 +12,16 @@
12 /> 12 />
13 </Teleport> 13 </Teleport>
14 <!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-35】富文本,图片上传遮挡其他按钮 --> 14 <!-- update-end--author:liaozhiyang---date:20240517---for:【TV360X-35】富文本,图片上传遮挡其他按钮 -->
15 - <Editor :id="tinymceId" ref="elRef" :disabled="disabled" :init="initOptions" :style="{ visibility: 'hidden' }" v-if="!initOptions.inline"></Editor> 15 + <Editor :id="tinymceId" ref="elRef" :disabled="disabled" :init="initOptions" :style="{ visibility: 'hidden' }" v-if="!initOptions.inline" />
16 <slot v-else></slot> 16 <slot v-else></slot>
17 - <ProcessMask ref="processMaskRef" :show="showUploadMask"/> 17 + <ProcessMask ref="processMaskRef" :show="showUploadMask" />
18 </div> 18 </div>
19 </template> 19 </template>
20 20
21 <script lang="ts"> 21 <script lang="ts">
22 import type { RawEditorOptions } from 'tinymce'; 22 import type { RawEditorOptions } from 'tinymce';
23 import tinymce from 'tinymce/tinymce'; 23 import tinymce from 'tinymce/tinymce';
24 - import Editor from '@tinymce/tinymce-vue' 24 + import Editor from '@tinymce/tinymce-vue';
25 import 'tinymce/themes/silver'; 25 import 'tinymce/themes/silver';
26 import 'tinymce/icons/default/icons'; 26 import 'tinymce/icons/default/icons';
27 import 'tinymce/models/dom'; 27 import 'tinymce/models/dom';
@@ -83,27 +83,27 @@ @@ -83,27 +83,27 @@
83 }, 83 },
84 showImageUpload: { 84 showImageUpload: {
85 type: Boolean, 85 type: Boolean,
86 - default: true, 86 + default: false,
87 }, 87 },
88 showUploadMask: { 88 showUploadMask: {
89 type: Boolean, 89 type: Boolean,
90 - default: false, 90 + default: true,
91 }, 91 },
92 //是否聚焦 92 //是否聚焦
93 - autoFocus:{ 93 + autoFocus: {
94 type: Boolean, 94 type: Boolean,
95 default: true, 95 default: true,
96 - } 96 + },
97 }; 97 };
98 98
99 export default defineComponent({ 99 export default defineComponent({
100 name: 'Tinymce', 100 name: 'Tinymce',
101 - components: { ImgUpload,Editor,ProcessMask }, 101 + components: { ImgUpload, Editor, ProcessMask },
102 inheritAttrs: false, 102 inheritAttrs: false,
103 props: tinymceProps as any, 103 props: tinymceProps as any,
104 emits: ['change', 'update:modelValue', 'inited', 'init-error'], 104 emits: ['change', 'update:modelValue', 'inited', 'init-error'],
105 setup(props, { emit, attrs }) { 105 setup(props, { emit, attrs }) {
106 - console.log("---Tinymce---初始化---") 106 + console.log('---Tinymce---初始化---');
107 107
108 const editorRef = ref<Nullable<any>>(null); 108 const editorRef = ref<Nullable<any>>(null);
109 const fullscreen = ref(false); 109 const fullscreen = ref(false);
@@ -155,6 +155,8 @@ @@ -155,6 +155,8 @@
155 language: langName.value, 155 language: langName.value,
156 branding: false, 156 branding: false,
157 default_link_target: '_blank', 157 default_link_target: '_blank',
  158 + forced_root_block: false,
  159 + force_br_newlines: true, // 换行使用<br>
158 link_title: false, 160 link_title: false,
159 object_resizing: true, 161 object_resizing: true,
160 toolbar_mode: 'sliding', 162 toolbar_mode: 'sliding',
@@ -166,26 +168,26 @@ @@ -166,26 +168,26 @@
166 skin_url: publicPath + 'resource/tinymce/skins/ui/' + skinName.value, 168 skin_url: publicPath + 'resource/tinymce/skins/ui/' + skinName.value,
167 images_upload_handler: (blobInfo, process) => 169 images_upload_handler: (blobInfo, process) =>
168 new Promise((resolve, reject) => { 170 new Promise((resolve, reject) => {
169 - let params = {  
170 - file: blobInfo.blob(),  
171 - filename: blobInfo.filename(),  
172 - data: { biz: 'jeditor', jeditor: '1' },  
173 - };  
174 - const uploadSuccess = (res) => {  
175 - if (res.success) {  
176 - if (res.message == 'local') {  
177 - const img = 'data:image/jpeg;base64,' + blobInfo.base64();  
178 - resolve(img); 171 + let params = {
  172 + file: blobInfo.blob(),
  173 + filename: blobInfo.filename(),
  174 + data: { biz: 'jeditor', jeditor: '1' },
  175 + };
  176 + const uploadSuccess = (res) => {
  177 + if (res.success) {
  178 + if (res.message == 'local') {
  179 + const img = 'data:image/jpeg;base64,' + blobInfo.base64();
  180 + resolve(img);
  181 + } else {
  182 + let img = getFileAccessHttpUrl(res.message);
  183 + resolve(img);
  184 + }
179 } else { 185 } else {
180 - let img = getFileAccessHttpUrl(res.message);  
181 - resolve(img);  
182 - }  
183 - } else {  
184 reject('上传失败!'); 186 reject('上传失败!');
185 - }  
186 - };  
187 - uploadFile(params, uploadSuccess);  
188 - }), 187 + }
  188 + };
  189 + uploadFile(params, uploadSuccess);
  190 + }),
189 content_css: publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css', 191 content_css: publicPath + 'resource/tinymce/skins/ui/' + skinName.value + '/content.min.css',
190 ...options, 192 ...options,
191 setup: (editor: any) => { 193 setup: (editor: any) => {
@@ -217,7 +219,7 @@ @@ -217,7 +219,7 @@
217 if (!editor) { 219 if (!editor) {
218 return; 220 return;
219 } 221 }
220 - editor?.setMode && editor.setMode(attrs.disabled ? 'readonly' : 'design'); 222 + editor?.setMode && editor.setMode(attrs.disabled ? 'readonly' : 'design');
221 } 223 }
222 ); 224 );
223 225
@@ -339,12 +341,12 @@ @@ -339,12 +341,12 @@
339 * @param fileList 341 * @param fileList
340 */ 342 */
341 function handleLoading(fileLength,showMask){ 343 function handleLoading(fileLength,showMask){
342 - if(fileLength && fileLength > 0){ 344 + if (fileLength && fileLength > 0) {
343 setTimeout(() => { 345 setTimeout(() => {
344 - props?.showUploadMask && processMaskRef.value.calcProcess(fileLength)  
345 - },100)  
346 - }else{  
347 - props?.showUploadMask && (processMaskRef.value.showMask = showMask); 346 + props?.showUploadMask && processMaskRef.value.calcProcess(fileLength);
  347 + }, 100);
  348 + } else {
  349 + props?.showUploadMask && (processMaskRef.value.showMask = showMask);
348 } 350 }
349 } 351 }
350 function getUploadingImgName(name: string) { 352 function getUploadingImgName(name: string) {
@@ -421,7 +423,7 @@ @@ -421,7 +423,7 @@
421 targetElem, 423 targetElem,
422 424
423 handleLoading, 425 handleLoading,
424 - processMaskRef 426 + processMaskRef,
425 }; 427 };
426 }, 428 },
427 }); 429 });
@@ -441,7 +443,7 @@ @@ -441,7 +443,7 @@
441 visibility: hidden; 443 visibility: hidden;
442 } 444 }
443 .tox:not(.tox-tinymce-inline) .tox-editor-header { 445 .tox:not(.tox-tinymce-inline) .tox-editor-header {
444 - padding:0; 446 + padding: 0;
445 } 447 }
446 // update-begin--author:liaozhiyang---date:20240527---for:【TV360X-329】富文本禁用状态下工具栏划过边框丢失 448 // update-begin--author:liaozhiyang---date:20240527---for:【TV360X-329】富文本禁用状态下工具栏划过边框丢失
447 .tox .tox-tbtn--disabled, 449 .tox .tox-tbtn--disabled,
@@ -456,7 +458,9 @@ @@ -456,7 +458,9 @@
456 458
457 html[data-theme='dark'] { 459 html[data-theme='dark'] {
458 .@{prefix-cls} { 460 .@{prefix-cls} {
459 - .tox .tox-edit-area__iframe {background-color: #141414;} 461 + .tox .tox-edit-area__iframe {
  462 + background-color: #141414;
  463 + }
460 } 464 }
461 } 465 }
462 </style> 466 </style>
@@ -8,7 +8,7 @@ @@ -8,7 +8,7 @@
8 </template> 8 </template>
9 <script lang="ts" setup> 9 <script lang="ts" setup>
10 import { computed, ref } from 'vue'; 10 import { computed, ref } from 'vue';
11 - import {useDesign} from "@/hooks/web/useDesign"; 11 + import { useDesign } from '@/hooks/web/useDesign';
12 12
13 const props = defineProps({ 13 const props = defineProps({
14 backColor: { 14 backColor: {
@@ -67,44 +67,42 @@ @@ -67,44 +67,42 @@
67 </script> 67 </script>
68 68
69 <style lang="less"> 69 <style lang="less">
70 -//noinspection LessUnresolvedVariable  
71 -@prefix-cls: ~'@{namespace}-tinymce-process-mask'; 70 + //noinspection LessUnresolvedVariable
  71 + @prefix-cls: ~'@{namespace}-tinymce-process-mask';
72 72
73 -.@{prefix-cls} {  
74 -  
75 - & {  
76 - position: absolute; /* 或者使用固定定位等其他方式 */  
77 - top: 0;  
78 - left: 0;  
79 - right: 0;  
80 - bottom: 0;  
81 - background-color: rgba(0, 0, 0, 0.5); /* 半透明遮罩 */  
82 - display: flex;  
83 - justify-content: center;  
84 - align-items: center;  
85 - overflow: hidden;  
86 - z-index: 99;  
87 - } 73 + .@{prefix-cls} {
  74 + & {
  75 + position: absolute; /* 或者使用固定定位等其他方式 */
  76 + top: 0;
  77 + left: 0;
  78 + right: 0;
  79 + bottom: 0;
  80 + background-color: rgba(0, 0, 0, 0.5); /* 半透明遮罩 */
  81 + display: flex;
  82 + justify-content: center;
  83 + align-items: center;
  84 + overflow: hidden;
  85 + z-index: 99;
  86 + }
88 87
89 - .progress-bar-rear {  
90 - width: 100px; /* 进度条宽度 */  
91 - height: 10px; /* 进度条高度 */  
92 - background-color: v-bind(rearColor); /* 进度条颜色 */  
93 - border-radius: 4px;  
94 - } 88 + .progress-bar-rear {
  89 + width: 100px; /* 进度条宽度 */
  90 + height: 10px; /* 进度条高度 */
  91 + background-color: v-bind(rearColor); /* 进度条颜色 */
  92 + border-radius: 4px;
  93 + }
95 94
96 - .progress-bar-front {  
97 - height: 10px; /* 进度条高度 */  
98 - background-color: v-bind(frontColor); /* 进度条颜色 */  
99 - border-radius: 4px;  
100 - } 95 + .progress-bar-front {
  96 + height: 10px; /* 进度条高度 */
  97 + background-color: v-bind(frontColor); /* 进度条颜色 */
  98 + border-radius: 4px;
  99 + }
101 100
102 - .value {  
103 - color: #fff;  
104 - margin-left: 5px;  
105 - font-size: 16px;  
106 - font-weight: 600; 101 + .value {
  102 + color: #fff;
  103 + margin-left: 5px;
  104 + font-size: 16px;
  105 + font-weight: 600;
  106 + }
107 } 107 }
108 -}  
109 -  
110 </style> 108 </style>
@@ -12,8 +12,6 @@ export const toolbar = @@ -12,8 +12,6 @@ export const toolbar =
12 12
13 export const simplePlugins = 'lists image link fullscreen'; 13 export const simplePlugins = 'lists image link fullscreen';
14 14
15 -export const simpleToolbar = [  
16 - 'undo redo styles bold italic alignleft aligncenter alignright alignjustify bullist numlist outdent indent lists image link fullscreen',  
17 -]; 15 +export const simpleToolbar = ['styles bold italic alignleft aligncenter alignright alignjustify bullist numlist outdent indent lists image link '];
18 16
19 export const menubar = 'file edit insert view format table'; 17 export const menubar = 'file edit insert view format table';
  1 +import { defHttp } from '/@/utils/http/axios';
  2 +import { useMessage } from '/@/hooks/web/useMessage';
  3 +
  4 +const { createConfirm } = useMessage();
  5 +
  6 +enum Api {
  7 + list = '/airagbutton/airagButton/list',
  8 + save = '/airagbutton/airagButton/add',
  9 + edit = '/airagbutton/airagButton/edit',
  10 + deleteOne = '/airagbutton/airagButton/delete',
  11 + deleteBatch = '/airagbutton/airagButton/deleteBatch',
  12 + importExcel = '/airagbutton/airagButton/importExcel',
  13 + exportXls = '/airagbutton/airagButton/exportXls',
  14 +}
  15 +/**
  16 + * 导出api
  17 + * @param params
  18 + */
  19 +export const getExportUrl = Api.exportXls;
  20 +/**
  21 + * 导入api
  22 + */
  23 +export const getImportUrl = Api.importExcel;
  24 +/**
  25 + * 列表接口
  26 + * @param params
  27 + */
  28 +export const list = (params) => defHttp.get({ url: Api.list, params });
  29 +
  30 +/**
  31 + * 删除单个
  32 + */
  33 +export const deleteOne = (params, handleSuccess) => {
  34 + return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
  35 + handleSuccess();
  36 + });
  37 +};
  38 +/**
  39 + * 批量删除
  40 + * @param params
  41 + */
  42 +export const batchDelete = (params, handleSuccess) => {
  43 + createConfirm({
  44 + iconType: 'warning',
  45 + title: '确认删除',
  46 + content: '是否删除选中数据',
  47 + okText: '确认',
  48 + cancelText: '取消',
  49 + onOk: () => {
  50 + return defHttp.delete({ url: Api.deleteBatch, data: params }, { joinParamsToUrl: true }).then(() => {
  51 + handleSuccess();
  52 + });
  53 + },
  54 + });
  55 +};
  56 +/**
  57 + * 保存或者更新
  58 + * @param params
  59 + * @param isUpdate - 是否为更新操作
  60 + */
  61 +export const saveOrUpdate = (params, isUpdate) => {
  62 + const url = isUpdate ? Api.edit : Api.save;
  63 + return defHttp.post({ url: url, params });
  64 +};
  1 +import { BasicColumn } from '/@/components/Table';
  2 +import { FormSchema } from '/@/components/Table';
  3 +import { Switch } from 'ant-design-vue';
  4 +import { h, reactive } from 'vue';
  5 +import { saveOrUpdate } from '@/views/super/airag/airagbutton/AiragButton.api';
  6 +//列表数据
  7 +export const columns: BasicColumn[] = [
  8 + {
  9 + title: '按钮名称',
  10 + align: 'center',
  11 + dataIndex: 'buttonName',
  12 + },
  13 + {
  14 + title: '按钮开关',
  15 + align: 'center',
  16 + dataIndex: 'buttonSwitch',
  17 + customRender: ({ text, record }) => {
  18 + return h(Switch, {
  19 + checked: text === 'Y',
  20 + loading: switchLoading[record.id],
  21 + onChange: (checked: boolean) => handleSwitchChange(checked, record),
  22 + });
  23 + },
  24 + },
  25 + {
  26 + title: '按钮值',
  27 + align: 'center',
  28 + dataIndex: 'buttonValues',
  29 + },
  30 +];
  31 +// 定义开关状态映射
  32 +const switchLoading = reactive<Record<string, boolean>>({});
  33 +
  34 +// 处理开关状态变化
  35 +async function handleSwitchChange(checked: boolean, record: Recordable) {
  36 + const newValue = checked ? 'Y' : 'N';
  37 + const oldValue = record.buttonSwitch;
  38 +
  39 + // 立即更新本地状态
  40 + record.buttonSwitch = newValue;
  41 + switchLoading[record.id] = true;
  42 +
  43 + try {
  44 + // 调用API更新后端
  45 + await saveOrUpdate(
  46 + {
  47 + id: record.id,
  48 + buttonSwitch: newValue,
  49 + },
  50 + true
  51 + );
  52 + } catch {
  53 + // 出错时回滚状态
  54 + record.buttonSwitch = oldValue;
  55 + } finally {
  56 + switchLoading[record.id] = false;
  57 + }
  58 +}
  59 +//查询数据
  60 +export const searchFormSchema: FormSchema[] = [
  61 + {
  62 + label: '按钮名称',
  63 + field: 'buttonName',
  64 + component: 'JInput',
  65 + },
  66 + {
  67 + label: '按钮值',
  68 + field: 'buttonValues',
  69 + component: 'JInput',
  70 + },
  71 +];
  72 +//表单数据
  73 +export const formSchema: FormSchema[] = [
  74 + {
  75 + label: '按钮名称',
  76 + field: 'buttonName',
  77 + component: 'Input',
  78 + required: true,
  79 + },
  80 + {
  81 + label: '按钮开关',
  82 + field: 'buttonSwitch',
  83 + component: 'JSwitch',
  84 + required: true,
  85 + componentProps: {},
  86 + },
  87 + {
  88 + label: '按钮值',
  89 + field: 'buttonValues',
  90 + component: 'Input',
  91 + required: true,
  92 + },
  93 + // TODO 主键隐藏字段,目前写死为ID
  94 + {
  95 + label: '',
  96 + field: 'id',
  97 + component: 'Input',
  98 + show: false,
  99 + },
  100 +];
  101 +
  102 +// 高级查询数据
  103 +export const superQuerySchema = {
  104 + buttonName: { title: '按钮名称', order: 0, view: 'text', type: 'string' },
  105 + buttonSwitch: { title: '按钮开关', order: 1, view: 'switch', type: 'string' },
  106 + buttonValues: { title: '按钮值', order: 2, view: 'text', type: 'string' },
  107 +};
  108 +
  109 +/**
  110 + * 流程表单调用这个方法获取formSchema
  111 + * @param param
  112 + */
  113 +export function getBpmFormSchema(_formData): FormSchema[] {
  114 + // 默认和原始表单保持一致 如果流程中配置了权限数据,这里需要单独处理formSchema
  115 + return formSchema;
  116 +}
  1 +<template>
  2 + <div>
  3 + <!--引用表格-->
  4 + <BasicTable @register="registerTable" :rowSelection="rowSelection">
  5 + <!--插槽:table标题-->
  6 + <template #tableTitle>
  7 + <a-button type="primary" v-auth="'airagbutton:airag_button:add'" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
  8 + <a-button type="primary" v-auth="'airagbutton:airag_button:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
  9 + 导出</a-button
  10 + >
  11 + <j-upload-button type="primary" v-auth="'airagbutton:airag_button:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"
  12 + >导入</j-upload-button
  13 + >
  14 + <a-dropdown v-if="selectedRowKeys.length > 0">
  15 + <template #overlay>
  16 + <a-menu>
  17 + <a-menu-item key="1" @click="batchHandleDelete">
  18 + <Icon icon="ant-design:delete-outlined" />
  19 + 删除
  20 + </a-menu-item>
  21 + </a-menu>
  22 + </template>
  23 + <a-button v-auth="'airagbutton:airag_button:deleteBatch'"
  24 + >批量操作
  25 + <Icon icon="mdi:chevron-down" />
  26 + </a-button>
  27 + </a-dropdown>
  28 + <!-- 高级查询 -->
  29 + <super-query :config="superQueryConfig" @search="handleSuperQuery" />
  30 + </template>
  31 + <!--操作栏-->
  32 + <template #action="{ record }">
  33 + <TableAction :actions="getTableAction(record)" :dropDownActions="getDropDownAction(record)" />
  34 + </template>
  35 + <!--字段回显插槽-->
  36 + <template #bodyCell="{ column, record, index, text }"> </template>
  37 + </BasicTable>
  38 + <!-- 表单区域 -->
  39 + <AiragButtonModal @register="registerModal" @success="handleSuccess" />
  40 + </div>
  41 +</template>
  42 +
  43 +<script lang="ts" name="airagbutton-airagButton" setup>
  44 + import { ref, reactive } from 'vue';
  45 + import { BasicTable, TableAction } from '/@/components/Table';
  46 + import { useModal } from '/@/components/Modal';
  47 + import { useListPage } from '/@/hooks/system/useListPage';
  48 + import AiragButtonModal from './components/AiragButtonModal.vue';
  49 + import { columns, searchFormSchema, superQuerySchema } from './AiragButton.data';
  50 + import { list, deleteOne, batchDelete, getImportUrl, getExportUrl, saveOrUpdate } from './AiragButton.api';
  51 + import { useUserStore } from '/@/store/modules/user';
  52 + const queryParam = reactive<any>({});
  53 + const checkedKeys = ref<Array<string | number>>([]);
  54 + const userStore = useUserStore();
  55 + //注册model
  56 + const [registerModal, { openModal }] = useModal();
  57 + //注册table数据
  58 + const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
  59 + tableProps: {
  60 + title: '按钮表单',
  61 + api: list,
  62 + columns,
  63 + canResize: false,
  64 + formConfig: {
  65 + //labelWidth: 120,
  66 + schemas: searchFormSchema,
  67 + autoSubmitOnEnter: true,
  68 + showAdvancedButton: true,
  69 + fieldMapToNumber: [],
  70 + fieldMapToTime: [],
  71 + },
  72 + actionColumn: {
  73 + width: 120,
  74 + fixed: 'right',
  75 + },
  76 + beforeFetch: (params) => {
  77 + return Object.assign(params, queryParam);
  78 + },
  79 + },
  80 + exportConfig: {
  81 + name: '按钮表单',
  82 + url: getExportUrl,
  83 + params: queryParam,
  84 + },
  85 + importConfig: {
  86 + url: getImportUrl,
  87 + success: handleSuccess,
  88 + },
  89 + });
  90 +
  91 + const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
  92 +
  93 + // 高级查询配置
  94 + const superQueryConfig = reactive(superQuerySchema);
  95 +
  96 + /**
  97 + * 高级查询事件
  98 + */
  99 + function handleSuperQuery(params) {
  100 + Object.keys(params).map((k) => {
  101 + queryParam[k] = params[k];
  102 + });
  103 + reload();
  104 + }
  105 + /**
  106 + * 新增事件
  107 + */
  108 + function handleAdd() {
  109 + openModal(true, {
  110 + isUpdate: false,
  111 + showFooter: true,
  112 + });
  113 + }
  114 + /**
  115 + * 编辑事件
  116 + */
  117 + function handleEdit(record: Recordable) {
  118 + openModal(true, {
  119 + record,
  120 + isUpdate: true,
  121 + showFooter: true,
  122 + });
  123 + }
  124 + /**
  125 + * 详情
  126 + */
  127 + function handleDetail(record: Recordable) {
  128 + openModal(true, {
  129 + record,
  130 + isUpdate: true,
  131 + showFooter: false,
  132 + });
  133 + }
  134 + /**
  135 + * 删除事件
  136 + */
  137 + async function handleDelete(record) {
  138 + await deleteOne({ id: record.id }, handleSuccess);
  139 + }
  140 + /**
  141 + * 批量删除事件
  142 + */
  143 + async function batchHandleDelete() {
  144 + await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
  145 + }
  146 + /**
  147 + * 成功回调
  148 + */
  149 + function handleSuccess() {
  150 + (selectedRowKeys.value = []) && reload();
  151 + }
  152 + /**
  153 + * 操作栏
  154 + */
  155 + function getTableAction(record) {
  156 + return [
  157 + {
  158 + label: '编辑',
  159 + onClick: handleEdit.bind(null, record),
  160 + auth: 'airagbutton:airag_button:edit',
  161 + },
  162 + ];
  163 + }
  164 + /**
  165 + * 下拉操作栏
  166 + */
  167 + function getDropDownAction(record) {
  168 + return [
  169 + {
  170 + label: '详情',
  171 + onClick: handleDetail.bind(null, record),
  172 + },
  173 + {
  174 + label: '删除',
  175 + popConfirm: {
  176 + title: '是否确认删除',
  177 + confirm: handleDelete.bind(null, record),
  178 + placement: 'topLeft',
  179 + },
  180 + auth: 'airagbutton:airag_button:delete',
  181 + },
  182 + ];
  183 + }
  184 +</script>
  185 +
  186 +<style lang="less" scoped>
  187 + :deep(.ant-picker),
  188 + :deep(.ant-input-number) {
  189 + width: 100%;
  190 + }
  191 +</style>
  1 +<template>
  2 + <div style="min-height: 400px">
  3 + <BasicForm @register="registerForm" />
  4 + <div style="width: 100%; text-align: center" v-if="!formDisabled">
  5 + <a-button @click="submitForm" pre-icon="ant-design:check" type="primary">提 交</a-button>
  6 + </div>
  7 + </div>
  8 +</template>
  9 +
  10 +<script lang="ts">
  11 + import { BasicForm, useForm } from '/@/components/Form/index';
  12 + import { computed, defineComponent } from 'vue';
  13 + import { defHttp } from '/@/utils/http/axios';
  14 + import { propTypes } from '/@/utils/propTypes';
  15 + import { getBpmFormSchema } from '../AiragButton.data';
  16 + import { saveOrUpdate } from '../AiragButton.api';
  17 +
  18 + export default defineComponent({
  19 + name: 'AiragButtonForm',
  20 + components: {
  21 + BasicForm,
  22 + },
  23 + props: {
  24 + formData: propTypes.object.def({}),
  25 + formBpm: propTypes.bool.def(true),
  26 + },
  27 + setup(props) {
  28 + const [registerForm, { setFieldsValue, setProps, getFieldsValue }] = useForm({
  29 + labelWidth: 150,
  30 + schemas: getBpmFormSchema(props.formData),
  31 + showActionButtonGroup: false,
  32 + baseColProps: { span: 24 },
  33 + });
  34 +
  35 + const formDisabled = computed(() => {
  36 + if (props.formData.disabled === false) {
  37 + return false;
  38 + }
  39 + return true;
  40 + });
  41 +
  42 + let formData = {};
  43 + const queryByIdUrl = '/airagbutton/airagButton/queryById';
  44 + async function initFormData() {
  45 + let params = { id: props.formData.dataId };
  46 + const data = await defHttp.get({ url: queryByIdUrl, params });
  47 + formData = { ...data };
  48 + //设置表单的值
  49 + await setFieldsValue(formData);
  50 + //默认是禁用
  51 + await setProps({ disabled: formDisabled.value });
  52 + }
  53 +
  54 + async function submitForm() {
  55 + let data = getFieldsValue();
  56 + let params = Object.assign({}, formData, data);
  57 + console.log('表单数据', params);
  58 + await saveOrUpdate(params, true);
  59 + }
  60 +
  61 + initFormData();
  62 +
  63 + return {
  64 + registerForm,
  65 + formDisabled,
  66 + submitForm,
  67 + };
  68 + },
  69 + });
  70 +</script>
  1 +<template>
  2 + <BasicModal v-bind="$attrs" @register="registerModal" destroyOnClose :title="title" :width="800" @ok="handleSubmit">
  3 + <BasicForm @register="registerForm" name="AiragButtonForm" />
  4 + </BasicModal>
  5 +</template>
  6 +
  7 +<script lang="ts" setup>
  8 + import { ref, computed, unref } from 'vue';
  9 + import { BasicModal, useModalInner } from '/@/components/Modal';
  10 + import { BasicForm, useForm } from '/@/components/Form/index';
  11 + import { formSchema } from '../AiragButton.data';
  12 + import { saveOrUpdate } from '../AiragButton.api';
  13 + // Emits声明
  14 + const emit = defineEmits(['register', 'success']);
  15 + const isUpdate = ref(true);
  16 + const isDetail = ref(false);
  17 + //表单配置
  18 + const [registerForm, { setProps, resetFields, setFieldsValue, validate, scrollToField }] = useForm({
  19 + labelWidth: 150,
  20 + schemas: formSchema,
  21 + showActionButtonGroup: false,
  22 + baseColProps: { span: 24 },
  23 + });
  24 + //表单赋值
  25 + const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
  26 + //重置表单
  27 + await resetFields();
  28 + setModalProps({ confirmLoading: false, showCancelBtn: !!data?.showFooter, showOkBtn: !!data?.showFooter });
  29 + isUpdate.value = !!data?.isUpdate;
  30 + isDetail.value = !!data?.showFooter;
  31 + if (unref(isUpdate)) {
  32 + //表单赋值
  33 + await setFieldsValue({
  34 + ...data.record,
  35 + });
  36 + }
  37 + // 隐藏底部时禁用整个表单
  38 + setProps({ disabled: !data?.showFooter });
  39 + });
  40 + //设置标题
  41 + const title = computed(() => (!unref(isUpdate) ? '新增' : !unref(isDetail) ? '详情' : '编辑'));
  42 + //表单提交事件
  43 + async function handleSubmit(v) {
  44 + try {
  45 + let values = await validate();
  46 + setModalProps({ confirmLoading: true });
  47 + //提交表单
  48 + await saveOrUpdate(values, isUpdate.value);
  49 + //关闭弹窗
  50 + closeModal();
  51 + //刷新列表
  52 + emit('success');
  53 + } catch ({ errorFields }) {
  54 + if (errorFields) {
  55 + const firstField = errorFields[0];
  56 + if (firstField) {
  57 + scrollToField(firstField.name, { behavior: 'smooth', block: 'center' });
  58 + }
  59 + }
  60 + return Promise.reject(errorFields);
  61 + } finally {
  62 + setModalProps({ confirmLoading: false });
  63 + }
  64 + }
  65 +</script>
  66 +
  67 +<style lang="less" scoped>
  68 + /** 时间和数字输入框样式 */
  69 + :deep(.ant-input-number) {
  70 + width: 100%;
  71 + }
  72 +
  73 + :deep(.ant-calendar-picker) {
  74 + width: 100%;
  75 + }
  76 +</style>
@@ -20,29 +20,29 @@ export const list = async (params) => { @@ -20,29 +20,29 @@ export const list = async (params) => {
20 try { 20 try {
21 const res = await defHttp.get({ 21 const res = await defHttp.get({
22 url: Api.list, 22 url: Api.list,
23 - params: { ...params, size: 1000 } 23 + params: { ...params, size: 1000 },
24 }); 24 });
25 25
26 if (res?.records && Array.isArray(res.records)) { 26 if (res?.records && Array.isArray(res.records)) {
27 - res.records = res.records.map(item => ({ 27 + res.records = res.records.map((item) => ({
28 ...item, 28 ...item,
29 ...item.metadata, 29 ...item.metadata,
30 question: item.question || '', 30 question: item.question || '',
31 - answer: item.answer || '' 31 + answer: item.answer || '',
32 })); 32 }));
33 } 33 }
34 return res; 34 return res;
35 } catch (error) { 35 } catch (error) {
36 - console.error("Error fetching question embeddings:", error); 36 + console.error('Error fetching question embeddings:', error);
37 return { 37 return {
38 records: [], 38 records: [],
39 total: 0, 39 total: 0,
40 size: 10, 40 size: 10,
41 current: 1, 41 current: 1,
42 - pages: 0 42 + pages: 0,
43 }; 43 };
44 } 44 }
45 -} 45 +};
46 46
47 export const deleteOne = (params, handleSuccess) => { 47 export const deleteOne = (params, handleSuccess) => {
48 return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => { 48 return defHttp.delete({ url: Api.deleteOne, params }, { joinParamsToUrl: true }).then(() => {
@@ -12,27 +12,27 @@ export const columns: BasicColumn[] = [ @@ -12,27 +12,27 @@ export const columns: BasicColumn[] = [
12 title: '问题', 12 title: '问题',
13 align: 'center', 13 align: 'center',
14 dataIndex: 'question', 14 dataIndex: 'question',
15 - width: 250 15 + width: 250,
16 }, 16 },
17 { 17 {
18 title: '回答', 18 title: '回答',
19 align: 'center', 19 align: 'center',
20 dataIndex: 'answer', 20 dataIndex: 'answer',
21 - width: 300 21 + width: 300,
22 }, 22 },
23 { 23 {
24 title: '原文', 24 title: '原文',
25 align: 'center', 25 align: 'center',
26 dataIndex: 'text', 26 dataIndex: 'text',
27 - width: 300 27 + width: 300,
28 }, 28 },
29 { 29 {
30 title: '元数据', 30 title: '元数据',
31 align: 'center', 31 align: 'center',
32 dataIndex: 'metadata', 32 dataIndex: 'metadata',
33 width: 200, 33 width: 200,
34 - customRender: ({ text }) => JSON.stringify(text || {})  
35 - } 34 + customRender: ({ text }) => JSON.stringify(text || {}),
  35 + },
36 ]; 36 ];
37 37
38 export const searchFormSchema: FormSchema[] = [ 38 export const searchFormSchema: FormSchema[] = [
@@ -40,14 +40,14 @@ export const searchFormSchema: FormSchema[] = [ @@ -40,14 +40,14 @@ export const searchFormSchema: FormSchema[] = [
40 field: 'question', 40 field: 'question',
41 label: '问题', 41 label: '问题',
42 component: 'Input', 42 component: 'Input',
43 - colProps: { span: 8 } 43 + colProps: { span: 8 },
44 }, 44 },
45 { 45 {
46 field: 'answer', 46 field: 'answer',
47 label: '回答', 47 label: '回答',
48 component: 'Input', 48 component: 'Input',
49 - colProps: { span: 8 }  
50 - } 49 + colProps: { span: 8 },
  50 + },
51 ]; 51 ];
52 52
53 export const formSchema: FormSchema[] = [ 53 export const formSchema: FormSchema[] = [
@@ -55,39 +55,39 @@ export const formSchema: FormSchema[] = [ @@ -55,39 +55,39 @@ export const formSchema: FormSchema[] = [
55 field: 'id', 55 field: 'id',
56 label: 'ID', 56 label: 'ID',
57 component: 'Input', 57 component: 'Input',
58 - show: false 58 + show: false,
59 }, 59 },
60 { 60 {
61 field: 'question', 61 field: 'question',
62 label: '问题', 62 label: '问题',
63 component: 'InputTextArea', 63 component: 'InputTextArea',
64 required: true, 64 required: true,
65 - colProps: { span: 24 } 65 + colProps: { span: 24 },
66 }, 66 },
67 { 67 {
68 field: 'answer', 68 field: 'answer',
69 label: '回答', 69 label: '回答',
70 component: 'InputTextArea', 70 component: 'InputTextArea',
71 required: true, 71 required: true,
72 - colProps: { span: 24 } 72 + colProps: { span: 24 },
73 }, 73 },
74 { 74 {
75 field: 'text', 75 field: 'text',
76 label: '原文', 76 label: '原文',
77 component: 'InputTextArea', 77 component: 'InputTextArea',
78 - colProps: { span: 24 } 78 + colProps: { span: 24 },
79 }, 79 },
80 { 80 {
81 field: 'metadata', 81 field: 'metadata',
82 label: '元数据', 82 label: '元数据',
83 component: 'InputTextArea', 83 component: 'InputTextArea',
84 - colProps: { span: 24 }  
85 - } 84 + colProps: { span: 24 },
  85 + },
86 ]; 86 ];
87 87
88 export const superQuerySchema = { 88 export const superQuerySchema = {
89 question: { title: '问题', order: 0, view: 'text', type: 'string' }, 89 question: { title: '问题', order: 0, view: 'text', type: 'string' },
90 - answer: { title: '回答', order: 1, view: 'text', type: 'string' } 90 + answer: { title: '回答', order: 1, view: 'text', type: 'string' },
91 }; 91 };
92 92
93 export function getBpmFormSchema(_formData): FormSchema[] { 93 export function getBpmFormSchema(_formData): FormSchema[] {
@@ -8,9 +8,7 @@ @@ -8,9 +8,7 @@
8 <a-dropdown v-if="selectedRowKeys.length > 0"> 8 <a-dropdown v-if="selectedRowKeys.length > 0">
9 <template #overlay> 9 <template #overlay>
10 <a-menu> 10 <a-menu>
11 - <a-menu-item key="1" @click="batchHandleDelete">  
12 - <Icon icon="ant-design:delete-outlined" />删除  
13 - </a-menu-item> 11 + <a-menu-item key="1" @click="batchHandleDelete"> <Icon icon="ant-design:delete-outlined" />删除 </a-menu-item>
14 </a-menu> 12 </a-menu>
15 </template> 13 </template>
16 <a-button>批量操作<Icon icon="mdi:chevron-down" /></a-button> 14 <a-button>批量操作<Icon icon="mdi:chevron-down" /></a-button>
@@ -26,92 +24,90 @@ @@ -26,92 +24,90 @@
26 </template> 24 </template>
27 25
28 <script lang="ts" setup> 26 <script lang="ts" setup>
29 -import { ref, reactive } from 'vue';  
30 -import { BasicTable, useTable, TableAction } from '/@/components/Table';  
31 -import { useModal } from '/@/components/Modal';  
32 -import { useListPage } from '/@/hooks/system/useListPage';  
33 -import QuestionEmbeddingModal from './components/QuestionEmbeddingModal.vue';  
34 -import { columns, searchFormSchema, superQuerySchema } from './QuestionEmbedding.data';  
35 -import { list, deleteOne, batchDelete, getImportZipUrl, getExportUrl } from './QuestionEmbedding.api'; 27 + import { reactive } from 'vue';
  28 + import { BasicTable, TableAction } from '/@/components/Table';
  29 + import { useModal } from '/@/components/Modal';
  30 + import { useListPage } from '/@/hooks/system/useListPage';
  31 + import QuestionEmbeddingModal from './components/QuestionEmbeddingModal.vue';
  32 + import { columns, searchFormSchema, superQuerySchema } from './QuestionEmbedding.data';
  33 + import { list, deleteOne, batchDelete, getImportZipUrl, getExportUrl } from './QuestionEmbedding.api';
36 34
37 -const queryParam = reactive<any>({});  
38 -const [registerModal, { openModal }] = useModal(); 35 + const queryParam = reactive<any>({});
  36 + const [registerModal, { openModal }] = useModal();
39 37
40 -const { tableContext, onExportXls, onImportXls } = useListPage({  
41 - tableProps: {  
42 - title: '问答向量库',  
43 - api: list,  
44 - columns,  
45 - formConfig: {  
46 - schemas: searchFormSchema,  
47 - autoSubmitOnEnter: true,  
48 - showAdvancedButton: true 38 + const { tableContext, onExportXls, onImportXls } = useListPage({
  39 + tableProps: {
  40 + title: '问答向量库',
  41 + api: list,
  42 + columns,
  43 + formConfig: {
  44 + schemas: searchFormSchema,
  45 + autoSubmitOnEnter: true,
  46 + showAdvancedButton: true,
  47 + },
  48 + actionColumn: {
  49 + width: 120,
  50 + fixed: 'right',
  51 + },
  52 + beforeFetch: (params) => Object.assign(params, queryParam),
49 }, 53 },
50 - actionColumn: {  
51 - width: 120,  
52 - fixed: 'right' 54 + exportConfig: {
  55 + name: '问答向量库',
  56 + url: getExportUrl,
53 }, 57 },
54 - beforeFetch: (params) => Object.assign(params, queryParam)  
55 - },  
56 - exportConfig: {  
57 - name: '问答向量库',  
58 - url: getExportUrl  
59 - },  
60 - importConfig: {  
61 - url: getImportZipUrl,  
62 - success: handleSuccess  
63 - }  
64 -}); 58 + importConfig: {
  59 + url: getImportZipUrl,
  60 + success: handleSuccess,
  61 + },
  62 + });
65 63
66 -const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;  
67 -const superQueryConfig = reactive(superQuerySchema); 64 + const [registerTable, { reload }, { rowSelection, selectedRowKeys }] = tableContext;
  65 + const superQueryConfig = reactive(superQuerySchema);
68 66
69 -function handleSuperQuery(params) {  
70 - Object.assign(queryParam, params);  
71 - reload();  
72 -} 67 + function handleSuperQuery(params) {
  68 + Object.assign(queryParam, params);
  69 + reload();
  70 + }
73 71
74 -function handleAdd() {  
75 - openModal(true, { isUpdate: false, showFooter: true });  
76 -} 72 + function handleAdd() {
  73 + openModal(true, { isUpdate: false, showFooter: true });
  74 + }
77 75
78 -function handleEdit(record) {  
79 - openModal(true, { record, isUpdate: true, showFooter: true });  
80 -} 76 + function handleEdit(record) {
  77 + openModal(true, { record, isUpdate: true, showFooter: true });
  78 + }
81 79
82 -function handleDetail(record) {  
83 - openModal(true, { record, isUpdate: true, showFooter: false });  
84 -} 80 + function handleDetail(record) {
  81 + openModal(true, { record, isUpdate: true, showFooter: false });
  82 + }
85 83
86 -async function handleDelete(record) {  
87 - await deleteOne({ id: record.id }, handleSuccess);  
88 -} 84 + async function handleDelete(record) {
  85 + await deleteOne({ id: record.id }, handleSuccess);
  86 + }
89 87
90 -async function batchHandleDelete() {  
91 - await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);  
92 -} 88 + async function batchHandleDelete() {
  89 + await batchDelete({ ids: selectedRowKeys.value }, handleSuccess);
  90 + }
93 91
94 -function handleSuccess() {  
95 - selectedRowKeys.value = [];  
96 - reload();  
97 -} 92 + function handleSuccess() {
  93 + selectedRowKeys.value = [];
  94 + reload();
  95 + }
98 96
99 -function getTableAction(record) {  
100 - return [  
101 - { label: '编辑', onClick: handleEdit.bind(null, record) }  
102 - ];  
103 -} 97 + function getTableAction(record) {
  98 + return [{ label: '编辑', onClick: handleEdit.bind(null, record) }];
  99 + }
104 100
105 -function getDropDownAction(record) {  
106 - return [  
107 - { label: '详情', onClick: handleDetail.bind(null, record) },  
108 - {  
109 - label: '删除',  
110 - popConfirm: {  
111 - title: '确认删除此问答?',  
112 - confirm: handleDelete.bind(null, record)  
113 - }  
114 - }  
115 - ];  
116 -} 101 + function getDropDownAction(record) {
  102 + return [
  103 + { label: '详情', onClick: handleDetail.bind(null, record) },
  104 + {
  105 + label: '删除',
  106 + popConfirm: {
  107 + title: '确认删除此问答?',
  108 + confirm: handleDelete.bind(null, record),
  109 + },
  110 + },
  111 + ];
  112 + }
117 </script> 113 </script>
@@ -5,54 +5,46 @@ @@ -5,54 +5,46 @@
5 </template> 5 </template>
6 6
7 <script lang="ts" setup> 7 <script lang="ts" setup>
8 -import { ref, computed, unref } from 'vue';  
9 -import { BasicModal, useModalInner } from '/@/components/Modal';  
10 -import { BasicForm, useForm } from '/@/components/Form/index';  
11 -import { formSchema } from '../QuestionEmbedding.data';  
12 -import { saveOrUpdate } from '../QuestionEmbedding.api';  
13 -  
14 -const emit = defineEmits(['register', 'success']);  
15 -const isUpdate = ref(true);  
16 -const isDetail = ref(false);  
17 -  
18 -const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({  
19 - labelWidth: 150,  
20 - schemas: formSchema,  
21 - showActionButtonGroup: false,  
22 - baseColProps: { span: 24 }  
23 -});  
24 -  
25 -const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {  
26 - await resetFields();  
27 - setModalProps({  
28 - confirmLoading: false,  
29 - showCancelBtn: !!data?.showFooter,  
30 - showOkBtn: !!data?.showFooter 8 + import { ref, computed, unref } from 'vue';
  9 + import { BasicModal, useModalInner } from '/@/components/Modal';
  10 + import { BasicForm, useForm } from '/@/components/Form/index';
  11 + import { formSchema } from '../QuestionEmbedding.data';
  12 + import { saveOrUpdate } from '../QuestionEmbedding.api';
  13 + const emit = defineEmits(['register', 'success']);
  14 + const isUpdate = ref(true);
  15 + const isDetail = ref(false);
  16 + const [registerForm, { setProps, resetFields, setFieldsValue, validate }] = useForm({
  17 + labelWidth: 150,
  18 + schemas: formSchema,
  19 + showActionButtonGroup: false,
  20 + baseColProps: { span: 24 },
31 }); 21 });
32 -  
33 - isUpdate.value = !!data?.isUpdate;  
34 - isDetail.value = !!data?.showFooter;  
35 -  
36 - if (unref(isUpdate)) {  
37 - await setFieldsValue({  
38 - ...data.record, 22 + const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
  23 + await resetFields();
  24 + setModalProps({
  25 + confirmLoading: false,
  26 + showCancelBtn: !!data?.showFooter,
  27 + showOkBtn: !!data?.showFooter,
39 }); 28 });
  29 + isUpdate.value = !!data?.isUpdate;
  30 + isDetail.value = !!data?.showFooter;
  31 + if (unref(isUpdate)) {
  32 + await setFieldsValue({
  33 + ...data.record,
  34 + });
  35 + }
  36 + setProps({ disabled: !data?.showFooter });
  37 + });
  38 + const title = computed(() => (!unref(isUpdate) ? '新增问答' : !unref(isDetail) ? '问答详情' : '编辑问答'));
  39 + async function handleSubmit() {
  40 + try {
  41 + const values = await validate();
  42 + setModalProps({ confirmLoading: true });
  43 + await saveOrUpdate(values, isUpdate.value);
  44 + closeModal();
  45 + emit('success');
  46 + } finally {
  47 + setModalProps({ confirmLoading: false });
  48 + }
40 } 49 }
41 -  
42 - setProps({ disabled: !data?.showFooter });  
43 -});  
44 -  
45 -const title = computed(() => (!unref(isUpdate) ? '新增问答' : !unref(isDetail) ? '问答详情' : '编辑问答'));  
46 -  
47 -async function handleSubmit() {  
48 - try {  
49 - const values = await validate();  
50 - setModalProps({ confirmLoading: true });  
51 - await saveOrUpdate(values, isUpdate.value);  
52 - closeModal();  
53 - emit('success');  
54 - } finally {  
55 - setModalProps({ confirmLoading: false });  
56 - }  
57 -}  
58 </script> 50 </script>
@@ -39,18 +39,76 @@ @@ -39,18 +39,76 @@
39 </template> 39 </template>
40 40
41 <script lang="ts" name="test-test" setup> 41 <script lang="ts" name="test-test" setup>
42 - import { ref, reactive } from 'vue';  
43 - import { BasicTable, TableAction } from '/@/components/Table'; 42 + import { ref, reactive, computed, onMounted } from 'vue';
  43 + import { BasicColumn, BasicTable, TableAction } from '/@/components/Table';
44 import { useModal } from '/@/components/Modal'; 44 import { useModal } from '/@/components/Modal';
45 import { useListPage } from '/@/hooks/system/useListPage'; 45 import { useListPage } from '/@/hooks/system/useListPage';
46 import TestModal from './components/TestModal.vue'; 46 import TestModal from './components/TestModal.vue';
47 - import { columns, searchFormSchema, superQuerySchema } from './Test.data';  
48 - import { list, deleteOne, batchDelete, getImportUrl, getExportUrl } from './Test.api'; 47 + import { searchFormSchema, superQuerySchema } from './Test.data';
  48 + import { list, batchDelete, getImportUrl, getExportUrl, deleteOne, listknowledge } from './Test.api';
49 import { useUserStore } from '/@/store/modules/user'; 49 import { useUserStore } from '/@/store/modules/user';
50 - import JUploadButton from "@/components/Button/src/JUploadButton.vue"; 50 + import JUploadButton from '@/components/Button/src/JUploadButton.vue';
  51 + import { columns as defaultColumns } from './Test.data'; // 导入默认列配置
51 const queryParam = reactive<any>({}); 52 const queryParam = reactive<any>({});
52 const checkedKeys = ref<Array<string | number>>([]); 53 const checkedKeys = ref<Array<string | number>>([]);
53 const userStore = useUserStore(); 54 const userStore = useUserStore();
  55 + // 添加知识库名称映射
  56 + const knowledgeMap = ref<Record<string, string>>({});
  57 +
  58 + // 加载知识库列表
  59 + async function loadKnowledgeMap() {
  60 + try {
  61 + const res = await listknowledge({});
  62 + const map: Record<string, string> = {};
  63 + res.forEach((item) => {
  64 + if (item.id && item.name) {
  65 + map[item.id] = item.name;
  66 + }
  67 + });
  68 + knowledgeMap.value = map;
  69 + } catch (e) {
  70 + console.error('加载知识库列表失败', e);
  71 + }
  72 + }
  73 +
  74 + onMounted(() => {
  75 + loadKnowledgeMap();
  76 + });
  77 +
  78 + // 创建符合 BasicColumn 类型的知识库名称列
  79 + const knowledgeColumn: BasicColumn = {
  80 + title: '知识库名称',
  81 + align: 'center',
  82 + dataIndex: 'knowledgeName',
  83 + width: 150,
  84 + customRender: ({ record }) => {
  85 + try {
  86 + const metadata = typeof record.metadata === 'string' ? JSON.parse(record.metadata) : record.metadata;
  87 +
  88 + const knowledgeId = metadata?.knowledgeId;
  89 +
  90 + if (knowledgeId && knowledgeMap.value[knowledgeId]) {
  91 + return knowledgeMap.value[knowledgeId];
  92 + } else if (knowledgeId) {
  93 + return `未知知识库(${knowledgeId})`;
  94 + } else {
  95 + return '无知识库';
  96 + }
  97 + } catch (e) {
  98 + return '元数据解析失败';
  99 + }
  100 + },
  101 + };
  102 +
  103 + // 创建完整的列配置
  104 + const tableColumns = computed<BasicColumn[]>(() => {
  105 + // 从默认列配置中过滤掉可能存在的旧知识库列
  106 + const filteredColumns = defaultColumns.filter((col) => col.dataIndex !== 'name' && col.title !== '知识库名称');
  107 +
  108 + // 添加新的知识库列
  109 + return [...filteredColumns, knowledgeColumn];
  110 + });
  111 +
54 //注册model 112 //注册model
55 const [registerModal, { openModal }] = useModal(); 113 const [registerModal, { openModal }] = useModal();
56 //注册table数据 114 //注册table数据
@@ -58,7 +116,7 @@ @@ -58,7 +116,7 @@
58 tableProps: { 116 tableProps: {
59 title: 'test', 117 title: 'test',
60 api: list, 118 api: list,
61 - columns, 119 + columns: tableColumns,
62 canResize: false, 120 canResize: false,
63 formConfig: { 121 formConfig: {
64 //labelWidth: 120, 122 //labelWidth: 120,
@@ -73,6 +131,13 @@ @@ -73,6 +131,13 @@
73 fixed: 'right', 131 fixed: 'right',
74 }, 132 },
75 beforeFetch: (params) => { 133 beforeFetch: (params) => {
  134 + // 处理知识库查询参数
  135 + if (params.knowledgeId) {
  136 + // 直接使用对象而不是JSON字符串
  137 + queryParam.metadata = { knowledgeId: params.knowledgeId };
  138 + } else {
  139 + delete queryParam.metadata;
  140 + }
76 return Object.assign(params, queryParam); 141 return Object.assign(params, queryParam);
77 }, 142 },
78 }, 143 },
@@ -5,6 +5,7 @@ const { createConfirm } = useMessage(); @@ -5,6 +5,7 @@ const { createConfirm } = useMessage();
5 5
6 enum Api { 6 enum Api {
7 list = '/embeddings/embeddings/list', 7 list = '/embeddings/embeddings/list',
  8 + listknowledge = '/embeddings/embeddings/listknowledge',
8 save = '/embeddings/embeddings/add', 9 save = '/embeddings/embeddings/add',
9 edit = '/embeddings/embeddings/edit', 10 edit = '/embeddings/embeddings/edit',
10 deleteOne = '/embeddings/embeddings/delete', 11 deleteOne = '/embeddings/embeddings/delete',
@@ -29,6 +30,7 @@ export const getImportUrl = Api.importWord; @@ -29,6 +30,7 @@ export const getImportUrl = Api.importWord;
29 * @param params 30 * @param params
30 */ 31 */
31 export const list = (params) => defHttp.get({ url: Api.list, params }); 32 export const list = (params) => defHttp.get({ url: Api.list, params });
  33 +export const listknowledge = (params) => defHttp.get({ url: Api.listknowledge, params });
32 34
33 /** 35 /**
34 * 删除单个 36 * 删除单个
1 import { BasicColumn } from '/@/components/Table'; 1 import { BasicColumn } from '/@/components/Table';
2 import { FormSchema } from '/@/components/Table'; 2 import { FormSchema } from '/@/components/Table';
3 import { h } from 'vue'; 3 import { h } from 'vue';
  4 +import { listknowledge } from '@/views/super/airag/test/Test.api';
4 //import { Tooltip } from 'ant-design-vue'; 5 //import { Tooltip } from 'ant-design-vue';
5 6
6 //列表数据 7 //列表数据
@@ -43,15 +44,15 @@ export const columns: BasicColumn[] = [ @@ -43,15 +44,15 @@ export const columns: BasicColumn[] = [
43 align: 'center', 44 align: 'center',
44 dataIndex: 'text', 45 dataIndex: 'text',
45 }, 46 },
46 - /* {  
47 - title: '知识库ID', 47 + {
  48 + title: '知识库名称',
48 align: 'center', 49 align: 'center',
49 - dataIndex: 'knowledgeId', 50 + dataIndex: 'knowledgeName',
50 width: 150, 51 width: 150,
51 - customRender: ({ record }) => { 52 + /*customRender: ({ record }) => {
52 return renderMetadataField(record, 'knowledgeId'); 53 return renderMetadataField(record, 'knowledgeId');
53 - },  
54 - }, 54 + },*/
  55 + } /*
55 { 56 {
56 title: '文档名称', 57 title: '文档名称',
57 align: 'center', 58 align: 'center',
@@ -109,7 +110,7 @@ export const columns: BasicColumn[] = [ @@ -109,7 +110,7 @@ export const columns: BasicColumn[] = [
109 return h(Tooltip, { title: displayText }, () => h('span', shortText)); 110 return h(Tooltip, { title: displayText }, () => h('span', shortText));
110 } 111 }
111 }, 112 },
112 - },*/ 113 + },*/,
113 ]; 114 ];
114 115
115 function renderMetadataField(record: any, fieldName: string) { 116 function renderMetadataField(record: any, fieldName: string) {
@@ -125,7 +126,6 @@ function renderMetadataField(record: any, fieldName: string) { @@ -125,7 +126,6 @@ function renderMetadataField(record: any, fieldName: string) {
125 if (value === undefined || value === null) { 126 if (value === undefined || value === null) {
126 return h('span', { style: 'color: #999' }, '无'); 127 return h('span', { style: 'color: #999' }, '无');
127 } 128 }
128 -  
129 // 根据字段类型渲染 129 // 根据字段类型渲染
130 switch (fieldName) { 130 switch (fieldName) {
131 case 'knowledgeId': 131 case 'knowledgeId':
@@ -150,6 +150,21 @@ export const searchFormSchema: FormSchema[] = [ @@ -150,6 +150,21 @@ export const searchFormSchema: FormSchema[] = [
150 field: 'text', 150 field: 'text',
151 component: 'Input', 151 component: 'Input',
152 }, 152 },
  153 + // 新增知识库选择字段
  154 + {
  155 + label: '知识库',
  156 + field: 'knowledgeId', // 注意:这里使用knowledgeId作为字段名
  157 + component: 'ApiSelect',
  158 + componentProps: {
  159 + api: listknowledge, // 使用知识库接口
  160 + labelField: 'name', // 显示知识库名称
  161 + valueField: 'id', // 提交知识库ID
  162 + showSearch: true,
  163 + filterOption: (input: string, option: any) => {
  164 + return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
  165 + },
  166 + },
  167 + },
153 ]; 168 ];
154 //表单数据 169 //表单数据
155 export const formSchema: FormSchema[] = [ 170 export const formSchema: FormSchema[] = [
@@ -166,7 +181,23 @@ export const formSchema: FormSchema[] = [ @@ -166,7 +181,23 @@ export const formSchema: FormSchema[] = [
166 label: '文本内容', 181 label: '文本内容',
167 field: 'text', 182 field: 'text',
168 required: true, 183 required: true,
169 - component: 'InputTextArea', 184 + component: 'JEditor',
  185 + },
  186 + // 新增知识库选择字段
  187 + {
  188 + label: '知识库',
  189 + field: 'knowledgeId', // 注意:这里使用knowledgeId作为字段名
  190 + component: 'ApiSelect',
  191 + required: true,
  192 + componentProps: {
  193 + api: listknowledge, // 使用知识库接口
  194 + labelField: 'name', // 显示知识库名称
  195 + valueField: 'id', // 提交知识库ID
  196 + showSearch: true,
  197 + filterOption: (input: string, option: any) => {
  198 + return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
  199 + },
  200 + },
170 }, 201 },
171 // TODO 主键隐藏字段,目前写死为ID 202 // TODO 主键隐藏字段,目前写死为ID
172 { 203 {
@@ -36,6 +36,9 @@ @@ -36,6 +36,9 @@
36 try { 36 try {
37 const metadata = typeof formData.metadata === 'string' ? JSON.parse(formData.metadata) : formData.metadata; 37 const metadata = typeof formData.metadata === 'string' ? JSON.parse(formData.metadata) : formData.metadata;
38 formData.docName = metadata.docName || '未命名'; 38 formData.docName = metadata.docName || '未命名';
  39 +
  40 + // 关键修复:设置知识库ID
  41 + formData.knowledgeId = metadata.knowledgeId;
39 } catch (e) { 42 } catch (e) {
40 console.error('元数据解析失败', e); 43 console.error('元数据解析失败', e);
41 formData.docName = '元数据格式错误'; 44 formData.docName = '元数据格式错误';
@@ -54,11 +57,15 @@ @@ -54,11 +57,15 @@
54 try { 57 try {
55 let values = await validate(); 58 let values = await validate();
56 59
  60 + const metadata = {
  61 + knowledgeId: values.knowledgeId,
  62 + };
57 // 仅保留需要的字段,其他由后端自动生成 63 // 仅保留需要的字段,其他由后端自动生成
58 const payload = { 64 const payload = {
59 id: values.id, 65 id: values.id,
60 docName: values.docName, // 文件名称 66 docName: values.docName, // 文件名称
61 text: values.text, // 文件内容 67 text: values.text, // 文件内容
  68 + metadata: metadata,
62 }; 69 };
63 setModalProps({ confirmLoading: true }); 70 setModalProps({ confirmLoading: true });
64 //提交表单 71 //提交表单