1、文档选择添加公共库

This commit is contained in:
2026-05-21 15:15:43 +08:00
parent 310fb9d3c1
commit d7d0874869
2 changed files with 469 additions and 111 deletions

View File

@@ -11,8 +11,25 @@
<div class="doc-layout"> <div class="doc-layout">
<!-- 左侧目录树 --> <!-- 左侧目录树 -->
<div class="dir-tree-panel"> <div class="dir-tree-panel">
<div
style="
padding: 0 16px;
border-bottom: 1px solid #e8e8e8;
background: #fff;
"
>
<a-tabs
:activeKey="activeSource"
size="small"
:tabBarStyle="{ margin: 0 }"
@change="handleSourceChange"
>
<a-tab-pane key="company" tab="公司文档" />
<a-tab-pane key="public" tab="公共库" />
</a-tabs>
</div>
<div class="dir-header"> <div class="dir-header">
<span>文档目录</span> <span>{{ activeSource === 'company' ? '公司文档目录' : '公共库目录' }}</span>
</div> </div>
<div class="tree-container"> <div class="tree-container">
<a-tree <a-tree
@@ -43,11 +60,12 @@
<div class="doc-header"> <div class="doc-header">
<div class="doc-actions"> <div class="doc-actions">
<span class="doc-tips" <span class="doc-tips"
>已选择 {{ checkedDirKeys.length }} 个目录, >当前已选择 {{ checkedDirKeys.length }} 个目录,
{{ selectedFileList.length }} 个文件</span {{ selectedFileList.length }} 个文件合计
{{ mergedDirKeys.length }} 个目录{{ mergedFileKeys.length }} 个文件</span
> >
<a-space> <a-space>
<a-button @click="clearSelection">清空选择</a-button> <a-button @click="clearSelection">清空当前</a-button>
<a-button type="primary" @click="confirmSelection" <a-button type="primary" @click="confirmSelection"
>确认选择</a-button >确认选择</a-button
> >
@@ -57,10 +75,14 @@
<div class="doc-content"> <div class="doc-content">
<a-table <a-table
:dataSource="docList" :dataSource="docList"
:columns="docColumns" :columns="currentDocColumns"
:loading="docLoading" :loading="docLoading"
rowKey="id" rowKey="id"
:scroll="{ y: 400 }" :scroll="
activeSource === 'public'
? { x: 2300, y: 400 }
: { y: 400 }
"
:pagination="pagination" :pagination="pagination"
@change="handleTableChange" @change="handleTableChange"
size="middle" size="middle"
@@ -93,7 +115,7 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue'; import { computed, h, ref } from 'vue';
import { FileOutlined } from '@ant-design/icons-vue'; import { FileOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue'; import { message } from 'ant-design-vue';
import { Empty } from 'ant-design-vue'; import { Empty } from 'ant-design-vue';
@@ -107,23 +129,86 @@
const props = defineProps<{ const props = defineProps<{
currentCompanyId: number; currentCompanyId: number;
selectionKey?: string; selectionKey?: string;
selectionState?: Partial<FileSelectionStatePayload>;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
(e: 'confirm', data: { dirKeys: (string | number)[], fileKeys: (string | number)[] }): void; (e: 'confirm', data: FileSelectionStatePayload): void;
}>(); }>();
type DirFileSelectionMap = Record<string, (string | number)[]>; type SelectionKey = string | number;
type DirFileSelectionMap = Record<string, SelectionKey[]>;
type DocSourceType = 'company' | 'public';
type SourceSelectionState = {
dirKeys: SelectionKey[];
fileKeys: SelectionKey[];
currentDirKey?: SelectionKey;
dirFileSelections: DirFileSelectionMap;
};
type FileSelectionStatePayload = {
activeSource: DocSourceType;
company: SourceSelectionState;
public: SourceSelectionState;
dirKeys: SelectionKey[];
fileKeys: SelectionKey[];
};
const dedupeKeys = (keys: SelectionKey[] = []) => {
return Array.from(
new Set(keys.filter((key) => key !== undefined && key !== null))
);
};
const cloneDirFileSelections = (
dirFileSelections?: DirFileSelectionMap
): DirFileSelectionMap => {
return Object.fromEntries(
Object.entries(dirFileSelections || {}).map(([key, value]) => [
key,
[...value]
])
);
};
const cloneSourceSelectionState = (
state?: Partial<SourceSelectionState>
): SourceSelectionState => {
return {
dirKeys: dedupeKeys([...(state?.dirKeys || [])]),
fileKeys: dedupeKeys([...(state?.fileKeys || [])]),
currentDirKey: state?.currentDirKey,
dirFileSelections: cloneDirFileSelections(state?.dirFileSelections)
};
};
const normalizeSelectionState = (
state?: Partial<FileSelectionStatePayload>
): FileSelectionStatePayload => {
const hasSourceState = !!(state?.company || state?.public);
const company = cloneSourceSelectionState(state?.company);
const publicState = cloneSourceSelectionState(state?.public);
if (!hasSourceState) {
company.dirKeys = dedupeKeys([...(state?.dirKeys || [])]);
company.fileKeys = dedupeKeys([...(state?.fileKeys || [])]);
}
return {
activeSource: state?.activeSource === 'public' ? 'public' : 'company',
company,
public: publicState,
dirKeys: dedupeKeys([...company.dirKeys, ...publicState.dirKeys]),
fileKeys: dedupeKeys([...company.fileKeys, ...publicState.fileKeys])
};
};
// 树形结构相关 // 树形结构相关
const expandedKeys = ref<(string | number)[]>([]); const expandedKeys = ref<SelectionKey[]>([]);
const selectedKeys = ref<(string | number)[]>([]); const selectedKeys = ref<SelectionKey[]>([]);
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE; const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
const activeSource = ref<DocSourceType>('company');
// const currentKbId = ref(''); const companyDocColumns = [
// const currentKbName = ref('');
const docColumns = ref([
{ {
title: '文件名', title: '文件名',
dataIndex: 'fileName', dataIndex: 'fileName',
@@ -149,7 +234,155 @@
key: 'uploadTime', key: 'uploadTime',
width: 180 width: 180
} }
]); ];
const getDocFieldValue = (
record: AiCloudFile,
keys: string[],
fallback = '-'
) => {
const row = record as Record<string, any>;
for (const key of keys) {
const value = row[key];
if (value !== undefined && value !== null && value !== '') {
return value;
}
}
return fallback;
};
const renderDocText = (record: AiCloudFile, keys: string[]) => {
return getDocFieldValue(record, keys);
};
const renderDocLink = (record: AiCloudFile, keys: string[]) => {
const value = getDocFieldValue(record, keys);
if (value === '-') {
return '-';
}
return h(
'a',
{
href: String(value),
target: '_blank',
rel: 'noreferrer'
},
String(value)
);
};
const publicDocColumns = [
{
title: '文件名称',
dataIndex: 'fileName',
key: 'fileName',
width: 180,
ellipsis: true,
customRender: ({ record }: { record: AiCloudFile }) =>
renderDocText(record, ['fileName', 'name'])
},
{
title: '制度标题',
dataIndex: 'title',
key: 'title',
width: 220,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['title', 'documentTitle'])
},
{
title: '发文字号',
dataIndex: 'issueNumber',
key: 'issueNumber',
width: 160,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['issueNumber', 'documentNo', 'fileNo'])
},
{
title: '版本号',
dataIndex: 'version',
key: 'version',
width: 160,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['version', 'versionNumber', 'versionName'])
},
{
title: '成文日期',
dataIndex: 'documentDate',
key: 'documentDate',
width: 160,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['documentDate', 'signDate', 'writeDate'])
},
{
title: '生效日期',
dataIndex: 'effectiveDate',
key: 'effectiveDate',
width: 160,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['effectiveDate'])
},
{
title: '废止日期',
dataIndex: 'abolishDate',
key: 'abolishDate',
width: 160,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['abolishDate', 'invalidDate'])
},
{
title: '适用业务范围',
dataIndex: 'businessScope',
key: 'businessScope',
width: 200,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['businessScope'])
},
{
title: '关联制度',
dataIndex: 'relatedDocuments',
key: 'relatedDocuments',
width: 220,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['relatedDocuments', 'relatedDoc', 'relatedFiles'])
},
{
title: '适用区域',
dataIndex: 'region',
key: 'region',
width: 160,
ellipsis: true,
customRender: ({ record, text }: { record: AiCloudFile; text: any }) =>
text || renderDocText(record, ['region', 'applyRegion'])
},
{
title: '下载链接',
dataIndex: 'fileUrl',
key: 'fileUrl',
width: 220,
ellipsis: true,
customRender: ({ record }: { record: AiCloudFile }) =>
renderDocLink(record, ['fileUrl', 'downloadUrl', 'url'])
},
{
title: '备注',
dataIndex: 'comments',
key: 'comments',
width: 160,
ellipsis: true
}
];
const currentDocColumns = computed(() => {
return activeSource.value === 'public' ? publicDocColumns : companyDocColumns;
});
// 计算树形数据 // 计算树形数据
const docList = ref<AiCloudFile[]>([]); const docList = ref<AiCloudFile[]>([]);
@@ -175,21 +408,29 @@
const selectionStateMap = ref< const selectionStateMap = ref<
Record< Record<
string, string,
{ FileSelectionStatePayload
dirKeys: (string | number)[];
dirFileSelections: DirFileSelectionMap;
}
> >
>({}); >({});
// const currentSectionIndex = ref(2); // 默认是审计内容3
const selectedDocList = ref<string[]>([]); const selectedDocList = ref<string[]>([]);
const selectedFileList = ref<string[]>([]); const selectedFileList = ref<string[]>([]);
const selectedFileKeys = ref<(string | number)[]>([]); const selectedFileKeys = ref<SelectionKey[]>([]);
const checkedDirKeys = ref<(string | number)[]>([]); // 新增勾选的目录keys const checkedDirKeys = ref<SelectionKey[]>([]);
const dirFileKeyCache = ref<Record<string, (string | number)[]>>({}); const dirFileKeyCache = ref<Record<string, SelectionKey[]>>({});
const dirFileSelections = ref<DirFileSelectionMap>({});
const getSelectionStorageKey = () => props.selectionKey || '__default__'; const getSelectionStorageKey = () => props.selectionKey || '__default__';
const getCurrentSelectionState = () => {
const key = getSelectionStorageKey();
if (!selectionStateMap.value[key]) {
selectionStateMap.value[key] = normalizeSelectionState();
}
return selectionStateMap.value[key];
};
const getCurrentSourceState = () => {
const state = getCurrentSelectionState();
return activeSource.value === 'public' ? state.public : state.company;
};
const getDirSelectionKey = (dirKey?: string | number) => { const getDirSelectionKey = (dirKey?: string | number) => {
if (dirKey === undefined || dirKey === null) return ''; if (dirKey === undefined || dirKey === null) return '';
return String(dirKey); return String(dirKey);
@@ -197,48 +438,76 @@
const getCurrentDirKey = () => getDirSelectionKey(selectedKeys.value[0]); const getCurrentDirKey = () => getDirSelectionKey(selectedKeys.value[0]);
const getAllSelectedFileKeys = () => { const getAllSelectedFileKeys = (sourceState: SourceSelectionState) => {
return Array.from( return dedupeKeys([
new Set( ...sourceState.fileKeys,
Object.values(dirFileSelections.value) ...Object.values(sourceState.dirFileSelections).flat()
.flat() ]);
.filter((key) => key !== undefined && key !== null)
)
);
}; };
const syncSelectedFileState = () => { const syncSelectedFileState = () => {
const allSelectedFileKeys = getAllSelectedFileKeys(); const sourceState = getCurrentSourceState();
const allSelectedFileKeys = getAllSelectedFileKeys(sourceState);
const currentDirKey = getCurrentDirKey(); const currentDirKey = getCurrentDirKey();
selectedFileKeys.value = [...(dirFileSelections.value[currentDirKey] || [])]; sourceState.fileKeys = [...allSelectedFileKeys];
sourceState.currentDirKey =
selectedKeys.value[0] !== undefined ? selectedKeys.value[0] : undefined;
checkedDirKeys.value = [...sourceState.dirKeys];
selectedDocList.value = sourceState.dirKeys.map((key) => key.toString());
selectedFileKeys.value = [
...(sourceState.dirFileSelections[currentDirKey] || [])
];
selectedFileList.value = allSelectedFileKeys.map((key) => key.toString()); selectedFileList.value = allSelectedFileKeys.map((key) => key.toString());
}; };
const mergedDirKeys = computed(() => {
const state = getCurrentSelectionState();
return dedupeKeys([...state.company.dirKeys, ...state.public.dirKeys]);
});
const mergedFileKeys = computed(() => {
const state = getCurrentSelectionState();
return dedupeKeys([
...getAllSelectedFileKeys(state.company),
...getAllSelectedFileKeys(state.public)
]);
});
const getMergedSelectionPayload = (): FileSelectionStatePayload => {
const state = getCurrentSelectionState();
return normalizeSelectionState({
activeSource: activeSource.value,
company: cloneSourceSelectionState(state.company),
public: cloneSourceSelectionState(state.public),
dirKeys: mergedDirKeys.value,
fileKeys: mergedFileKeys.value
});
};
const saveSelectionState = () => { const saveSelectionState = () => {
selectionStateMap.value[getSelectionStorageKey()] = { const sourceState = getCurrentSourceState();
dirKeys: [...checkedDirKeys.value], sourceState.dirKeys = [...checkedDirKeys.value];
dirFileSelections: Object.fromEntries( sourceState.fileKeys = [...getAllSelectedFileKeys(sourceState)];
Object.entries(dirFileSelections.value).map(([key, value]) => [ sourceState.currentDirKey =
key, selectedKeys.value[0] !== undefined ? selectedKeys.value[0] : undefined;
[...value] selectionStateMap.value[getSelectionStorageKey()] = getMergedSelectionPayload();
])
)
};
}; };
const restoreSelectionState = () => { const restoreSelectionState = () => {
const state = selectionStateMap.value[getSelectionStorageKey()]; const normalizedState = normalizeSelectionState(
checkedDirKeys.value = [...(state?.dirKeys || [])]; selectionStateMap.value[getSelectionStorageKey()]
selectedDocList.value = checkedDirKeys.value.map((key) => key.toString());
dirFileSelections.value = Object.fromEntries(
Object.entries(state?.dirFileSelections || {}).map(([key, value]) => [
key,
[...value]
])
); );
selectionStateMap.value[getSelectionStorageKey()] = normalizedState;
activeSource.value = normalizedState.activeSource;
syncSelectedFileState(); syncSelectedFileState();
}; };
const syncSelectionStateFromProps = () => {
selectionStateMap.value[getSelectionStorageKey()] = normalizeSelectionState(
props.selectionState
);
};
const handleDocSelectCancel = () => { const handleDocSelectCancel = () => {
saveSelectionState(); saveSelectionState();
showDocSelect.value = false; showDocSelect.value = false;
@@ -246,15 +515,12 @@
const confirmSelection = () => { const confirmSelection = () => {
saveSelectionState(); saveSelectionState();
const payload = getMergedSelectionPayload();
// 传递选择的数据给父组件 emit('confirm', payload);
emit('confirm', {
dirKeys: [...checkedDirKeys.value],
fileKeys: getAllSelectedFileKeys()
});
message.success( message.success(
`已选择 ${checkedDirKeys.value.length} 个目录和 ${getAllSelectedFileKeys().length} 个文件` `已选择 ${payload.dirKeys.length} 个目录和 ${payload.fileKeys.length} 个文件`
); );
showDocSelect.value = false; showDocSelect.value = false;
}; };
@@ -262,25 +528,43 @@
const loadAllCloudDocs = async () => { const loadAllCloudDocs = async () => {
try { try {
const params = { const params = {
companyId: props.currentCompanyId companyId: activeSource.value === 'public' ? 0 : props.currentCompanyId
}; };
const result = await listAiCloudDoc(params); const result = await listAiCloudDoc(params);
allDirs.value = result || []; allDirs.value = result || [];
// 默认展开根节点并选中第一个目录 const sourceState = getCurrentSourceState();
if (allDirs.value.length > 0) { const rootDirs = allDirs.value.filter((item) => item.parentId === 0);
const rootDirs = allDirs.value.filter((item) => item.parentId === 0); const availableIds = new Set(allDirs.value.map((item) => item.id));
if (rootDirs.length > 0) { const firstSelectedDirFromFiles = Object.keys(
expandedKeys.value = [0]; sourceState.dirFileSelections || {}
).find((key) => {
const dirId = Number(key);
return sourceState.dirFileSelections[key]?.length > 0 && availableIds.has(dirId);
});
const preferredSelectedKey =
(selectedKeys.value[0] && availableIds.has(selectedKeys.value[0] as number)
? selectedKeys.value[0]
: undefined) ||
(sourceState.currentDirKey !== undefined &&
availableIds.has(sourceState.currentDirKey as number)
? sourceState.currentDirKey
: undefined) ||
sourceState.dirKeys.find((key) => availableIds.has(key as number)) ||
(firstSelectedDirFromFiles !== undefined
? Number(firstSelectedDirFromFiles)
: undefined) ||
rootDirs[0]?.id;
// 如果已经有选中的目录,保持选中状态,否则选中第一个目录 expandedKeys.value = rootDirs.map((item) => item.id!).filter(Boolean);
if (selectedKeys.value.length === 0) { selectedKeys.value = preferredSelectedKey ? [preferredSelectedKey] : [];
selectedKeys.value = [rootDirs[0].id!];
}
syncSelectedFileState(); syncSelectedFileState();
loadCloudFiles(); if (selectedKeys.value.length > 0) {
} await loadCloudFiles();
} else {
docList.value = [];
pagination.value.total = 0;
} }
} catch (error) { } catch (error) {
message.error('加载目录列表失败'); message.error('加载目录列表失败');
@@ -296,6 +580,8 @@
// 树节点选择 // 树节点选择
const onSelect = (keys: (string | number)[]) => { const onSelect = (keys: (string | number)[]) => {
selectedKeys.value = keys; selectedKeys.value = keys;
const sourceState = getCurrentSourceState();
sourceState.currentDirKey = keys[0];
pagination.value.current = 1; pagination.value.current = 1;
syncSelectedFileState(); syncSelectedFileState();
loadCloudFiles(); loadCloudFiles();
@@ -309,8 +595,8 @@
: []; : [];
}; };
const getDirFileKeys = async (dirKey: string | number) => { const getDirFileKeys = async (dirKey: SelectionKey) => {
const cacheKey = String(dirKey); const cacheKey = `${activeSource.value}_${String(dirKey)}`;
if (dirFileKeyCache.value[cacheKey]) { if (dirFileKeyCache.value[cacheKey]) {
return dirFileKeyCache.value[cacheKey]; return dirFileKeyCache.value[cacheKey];
} }
@@ -331,6 +617,7 @@
const onDirCheck = async (checkedKeys: any) => { const onDirCheck = async (checkedKeys: any) => {
const nextCheckedKeys = getCheckedKeyList(checkedKeys); const nextCheckedKeys = getCheckedKeyList(checkedKeys);
const prevCheckedKeys = [...checkedDirKeys.value]; const prevCheckedKeys = [...checkedDirKeys.value];
const sourceState = getCurrentSourceState();
const addedKeys = nextCheckedKeys.filter( const addedKeys = nextCheckedKeys.filter(
(key) => !prevCheckedKeys.includes(key) (key) => !prevCheckedKeys.includes(key)
); );
@@ -340,6 +627,7 @@
checkedDirKeys.value = nextCheckedKeys; checkedDirKeys.value = nextCheckedKeys;
selectedDocList.value = nextCheckedKeys.map((key) => key.toString()); selectedDocList.value = nextCheckedKeys.map((key) => key.toString());
sourceState.dirKeys = [...nextCheckedKeys];
if (!addedKeys.length && !removedKeys.length) { if (!addedKeys.length && !removedKeys.length) {
saveSelectionState(); saveSelectionState();
@@ -351,10 +639,10 @@
addedKeys.map((key) => getDirFileKeys(key)) addedKeys.map((key) => getDirFileKeys(key))
); );
addedKeys.forEach((key, index) => { addedKeys.forEach((key, index) => {
dirFileSelections.value[String(key)] = [...addedFileGroups[index]]; sourceState.dirFileSelections[String(key)] = [...addedFileGroups[index]];
}); });
removedKeys.forEach((key) => { removedKeys.forEach((key) => {
delete dirFileSelections.value[String(key)]; delete sourceState.dirFileSelections[String(key)];
}); });
syncSelectedFileState(); syncSelectedFileState();
} catch (error) { } catch (error) {
@@ -391,27 +679,31 @@
// 文件选择变化 // 文件选择变化
const onFileSelectionChange = ( const onFileSelectionChange = (
selectedRowKeys: (string | number)[], selectedRowKeys: SelectionKey[]
selectedRows: AiCloudFile[]
) => { ) => {
const currentDirKey = getCurrentDirKey(); const currentDirKey = getCurrentDirKey();
if (!currentDirKey) return; if (!currentDirKey) return;
dirFileSelections.value[currentDirKey] = [...selectedRowKeys]; const sourceState = getCurrentSourceState();
sourceState.dirFileSelections[currentDirKey] = [...selectedRowKeys];
sourceState.fileKeys = getAllSelectedFileKeys(sourceState);
selectedFileKeys.value = selectedRowKeys; selectedFileKeys.value = selectedRowKeys;
selectedFileList.value = getAllSelectedFileKeys().map((key) => selectedFileList.value = sourceState.fileKeys.map((key) =>
key.toString() key.toString()
); );
saveSelectionState(); saveSelectionState();
}; };
// 清空选择
const clearSelection = () => { const clearSelection = () => {
const sourceState = getCurrentSourceState();
selectedDocList.value = []; selectedDocList.value = [];
selectedFileList.value = []; selectedFileList.value = [];
selectedFileKeys.value = []; selectedFileKeys.value = [];
checkedDirKeys.value = []; checkedDirKeys.value = [];
dirFileSelections.value = {}; sourceState.dirKeys = [];
sourceState.fileKeys = [];
sourceState.currentDirKey = undefined;
sourceState.dirFileSelections = {};
saveSelectionState(); saveSelectionState();
}; };
@@ -457,11 +749,21 @@
const open = () => { const open = () => {
showDocSelect.value = true; showDocSelect.value = true;
syncSelectionStateFromProps();
restoreSelectionState(); restoreSelectionState();
dirFileKeyCache.value = {}; dirFileKeyCache.value = {};
loadAllCloudDocs(); loadAllCloudDocs();
}; };
const handleSourceChange = async (source: string) => {
saveSelectionState();
activeSource.value = source === 'public' ? 'public' : 'company';
selectedKeys.value = [];
dirFileKeyCache.value = {};
syncSelectedFileState();
await loadAllCloudDocs();
};
defineExpose({ open }); defineExpose({ open });
</script> </script>

View File

@@ -492,6 +492,7 @@
ref="fileModal" ref="fileModal"
:current-company-id="currentCompanyId" :current-company-id="currentCompanyId"
:selection-key="currentFileSelectionKey" :selection-key="currentFileSelectionKey"
:selection-state="getTableFileSelection(currentFileSelectionKey)"
@confirm="handleFileSelectConfirm" @confirm="handleFileSelectConfirm"
/> />
<HistoryModal <HistoryModal
@@ -556,6 +557,75 @@
hasContent hasContent
} from './data/funcs'; } from './data/funcs';
type SelectionKey = string | number;
type DocSourceType = 'company' | 'public';
type DirFileSelectionMap = Record<string, SelectionKey[]>;
type SourceSelectionState = {
dirKeys: SelectionKey[];
fileKeys: SelectionKey[];
currentDirKey?: SelectionKey;
dirFileSelections: DirFileSelectionMap;
};
type TableFileSelectionState = {
activeSource: DocSourceType;
company: SourceSelectionState;
public: SourceSelectionState;
dirKeys: SelectionKey[];
fileKeys: SelectionKey[];
};
const dedupeSelectionKeys = (keys: SelectionKey[] = []) => {
return Array.from(
new Set(keys.filter((key) => key !== undefined && key !== null))
);
};
const cloneDirFileSelections = (
dirFileSelections?: DirFileSelectionMap
): DirFileSelectionMap => {
return Object.fromEntries(
Object.entries(dirFileSelections || {}).map(([key, value]) => [
key,
[...value]
])
);
};
const cloneSourceSelectionState = (
state?: Partial<SourceSelectionState>
): SourceSelectionState => {
return {
dirKeys: dedupeSelectionKeys([...(state?.dirKeys || [])]),
fileKeys: dedupeSelectionKeys([...(state?.fileKeys || [])]),
currentDirKey: state?.currentDirKey,
dirFileSelections: cloneDirFileSelections(state?.dirFileSelections)
};
};
const normalizeTableFileSelection = (
selection?: Partial<TableFileSelectionState>
): TableFileSelectionState => {
const hasSourceState = !!(selection?.company || selection?.public);
const company = cloneSourceSelectionState(selection?.company);
const publicState = cloneSourceSelectionState(selection?.public);
if (!hasSourceState) {
company.dirKeys = dedupeSelectionKeys([...(selection?.dirKeys || [])]);
company.fileKeys = dedupeSelectionKeys([...(selection?.fileKeys || [])]);
}
return {
activeSource: selection?.activeSource === 'public' ? 'public' : 'company',
company,
public: publicState,
dirKeys: dedupeSelectionKeys([...company.dirKeys, ...publicState.dirKeys]),
fileKeys: dedupeSelectionKeys([
...company.fileKeys,
...publicState.fileKeys
])
};
};
const useForm = Form.useForm; const useForm = Form.useForm;
const props = defineProps<{ const props = defineProps<{
@@ -602,16 +672,10 @@
const currentFileSelectionKey = ref(''); const currentFileSelectionKey = ref('');
const selectedDocList = ref<string[]>([]); const selectedDocList = ref<string[]>([]);
const selectedFileList = ref<string[]>([]); const selectedFileList = ref<string[]>([]);
const selectedFileKeys = ref<(string | number)[]>([]); const selectedFileKeys = ref<SelectionKey[]>([]);
const checkedDirKeys = ref<(string | number)[]>([]); const checkedDirKeys = ref<SelectionKey[]>([]);
const tableFileSelectionMap = reactive< const tableFileSelectionMap = reactive<
Record< Record<string, TableFileSelectionState>
string,
{
dirKeys: (string | number)[];
fileKeys: (string | number)[];
}
>
>({}); >({});
// ========== 取证单相关 ========== // ========== 取证单相关 ==========
@@ -704,12 +768,7 @@
}; };
const getTableFileSelection = (tableKey: string) => { const getTableFileSelection = (tableKey: string) => {
return ( return normalizeTableFileSelection(tableFileSelectionMap[tableKey]);
tableFileSelectionMap[tableKey] || {
dirKeys: [],
fileKeys: []
}
);
}; };
const applyTableFileSelection = (tableKey: string) => { const applyTableFileSelection = (tableKey: string) => {
@@ -722,13 +781,9 @@
const saveTableFileSelection = ( const saveTableFileSelection = (
tableKey: string, tableKey: string,
dirKeys: (string | number)[] = [], selection?: Partial<TableFileSelectionState>
fileKeys: (string | number)[] = []
) => { ) => {
tableFileSelectionMap[tableKey] = { tableFileSelectionMap[tableKey] = normalizeTableFileSelection(selection);
dirKeys: [...dirKeys],
fileKeys: [...fileKeys]
};
}; };
// ========== 通用方法 ========== // ========== 通用方法 ==========
@@ -777,13 +832,9 @@
}; };
/* 处理文件选择确认 */ /* 处理文件选择确认 */
const handleFileSelectConfirm = (data: { dirKeys: (string | number)[], fileKeys: (string | number)[] }) => { const handleFileSelectConfirm = (data: TableFileSelectionState) => {
if (currentFileSelectionKey.value) { if (currentFileSelectionKey.value) {
saveTableFileSelection( saveTableFileSelection(currentFileSelectionKey.value, data);
currentFileSelectionKey.value,
data.dirKeys,
data.fileKeys
);
} }
checkedDirKeys.value = data.dirKeys; checkedDirKeys.value = data.dirKeys;
@@ -1438,8 +1489,12 @@
saveTableGenerationData(tableKey, requestData, responseData, interfaceName); saveTableGenerationData(tableKey, requestData, responseData, interfaceName);
saveTableFileSelection( saveTableFileSelection(
tableKey, tableKey,
Array.isArray(requestData?.docList) ? requestData.docList : [], requestData?.fileSelectionState || {
Array.isArray(requestData?.fileList) ? requestData.fileList : [] dirKeys: Array.isArray(requestData?.docList) ? requestData.docList : [],
fileKeys: Array.isArray(requestData?.fileList)
? requestData.fileList
: []
}
); );
// 使用数据映射函数 // 使用数据映射函数
@@ -1877,7 +1932,8 @@
const normalizedRequestData = { const normalizedRequestData = {
...(generationData.requestData || {}), ...(generationData.requestData || {}),
docList: [...currentSelection.dirKeys], docList: [...currentSelection.dirKeys],
fileList: [...currentSelection.fileKeys] fileList: [...currentSelection.fileKeys],
fileSelectionState: normalizeTableFileSelection(currentSelection)
}; };
const aiHistory = { const aiHistory = {