1、文档选择添加公共库
This commit is contained in:
@@ -11,8 +11,25 @@
|
||||
<div class="doc-layout">
|
||||
<!-- 左侧目录树 -->
|
||||
<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">
|
||||
<span>文档目录</span>
|
||||
<span>{{ activeSource === 'company' ? '公司文档目录' : '公共库目录' }}</span>
|
||||
</div>
|
||||
<div class="tree-container">
|
||||
<a-tree
|
||||
@@ -43,11 +60,12 @@
|
||||
<div class="doc-header">
|
||||
<div class="doc-actions">
|
||||
<span class="doc-tips"
|
||||
>已选择 {{ checkedDirKeys.length }} 个目录,
|
||||
{{ selectedFileList.length }} 个文件</span
|
||||
>当前已选择 {{ checkedDirKeys.length }} 个目录,
|
||||
{{ selectedFileList.length }} 个文件;合计
|
||||
{{ mergedDirKeys.length }} 个目录,{{ mergedFileKeys.length }} 个文件</span
|
||||
>
|
||||
<a-space>
|
||||
<a-button @click="clearSelection">清空选择</a-button>
|
||||
<a-button @click="clearSelection">清空当前</a-button>
|
||||
<a-button type="primary" @click="confirmSelection"
|
||||
>确认选择</a-button
|
||||
>
|
||||
@@ -57,10 +75,14 @@
|
||||
<div class="doc-content">
|
||||
<a-table
|
||||
:dataSource="docList"
|
||||
:columns="docColumns"
|
||||
:columns="currentDocColumns"
|
||||
:loading="docLoading"
|
||||
rowKey="id"
|
||||
:scroll="{ y: 400 }"
|
||||
:scroll="
|
||||
activeSource === 'public'
|
||||
? { x: 2300, y: 400 }
|
||||
: { y: 400 }
|
||||
"
|
||||
:pagination="pagination"
|
||||
@change="handleTableChange"
|
||||
size="middle"
|
||||
@@ -93,7 +115,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, ref } from 'vue';
|
||||
import { computed, h, ref } from 'vue';
|
||||
import { FileOutlined } from '@ant-design/icons-vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { Empty } from 'ant-design-vue';
|
||||
@@ -107,23 +129,86 @@
|
||||
const props = defineProps<{
|
||||
currentCompanyId: number;
|
||||
selectionKey?: string;
|
||||
selectionState?: Partial<FileSelectionStatePayload>;
|
||||
}>();
|
||||
|
||||
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 selectedKeys = ref<(string | number)[]>([]);
|
||||
const expandedKeys = ref<SelectionKey[]>([]);
|
||||
const selectedKeys = ref<SelectionKey[]>([]);
|
||||
const simpleImage = Empty.PRESENTED_IMAGE_SIMPLE;
|
||||
const activeSource = ref<DocSourceType>('company');
|
||||
|
||||
// const currentKbId = ref('');
|
||||
// const currentKbName = ref('');
|
||||
|
||||
const docColumns = ref([
|
||||
const companyDocColumns = [
|
||||
{
|
||||
title: '文件名',
|
||||
dataIndex: 'fileName',
|
||||
@@ -149,7 +234,155 @@
|
||||
key: 'uploadTime',
|
||||
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[]>([]);
|
||||
@@ -175,21 +408,29 @@
|
||||
const selectionStateMap = ref<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
dirKeys: (string | number)[];
|
||||
dirFileSelections: DirFileSelectionMap;
|
||||
}
|
||||
FileSelectionStatePayload
|
||||
>
|
||||
>({});
|
||||
// const currentSectionIndex = ref(2); // 默认是审计内容3
|
||||
const selectedDocList = ref<string[]>([]);
|
||||
const selectedFileList = ref<string[]>([]);
|
||||
const selectedFileKeys = ref<(string | number)[]>([]);
|
||||
const checkedDirKeys = ref<(string | number)[]>([]); // 新增:勾选的目录keys
|
||||
const dirFileKeyCache = ref<Record<string, (string | number)[]>>({});
|
||||
const dirFileSelections = ref<DirFileSelectionMap>({});
|
||||
const selectedFileKeys = ref<SelectionKey[]>([]);
|
||||
const checkedDirKeys = ref<SelectionKey[]>([]);
|
||||
const dirFileKeyCache = ref<Record<string, SelectionKey[]>>({});
|
||||
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) => {
|
||||
if (dirKey === undefined || dirKey === null) return '';
|
||||
return String(dirKey);
|
||||
@@ -197,48 +438,76 @@
|
||||
|
||||
const getCurrentDirKey = () => getDirSelectionKey(selectedKeys.value[0]);
|
||||
|
||||
const getAllSelectedFileKeys = () => {
|
||||
return Array.from(
|
||||
new Set(
|
||||
Object.values(dirFileSelections.value)
|
||||
.flat()
|
||||
.filter((key) => key !== undefined && key !== null)
|
||||
)
|
||||
);
|
||||
const getAllSelectedFileKeys = (sourceState: SourceSelectionState) => {
|
||||
return dedupeKeys([
|
||||
...sourceState.fileKeys,
|
||||
...Object.values(sourceState.dirFileSelections).flat()
|
||||
]);
|
||||
};
|
||||
|
||||
const syncSelectedFileState = () => {
|
||||
const allSelectedFileKeys = getAllSelectedFileKeys();
|
||||
const sourceState = getCurrentSourceState();
|
||||
const allSelectedFileKeys = getAllSelectedFileKeys(sourceState);
|
||||
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());
|
||||
};
|
||||
|
||||
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 = () => {
|
||||
selectionStateMap.value[getSelectionStorageKey()] = {
|
||||
dirKeys: [...checkedDirKeys.value],
|
||||
dirFileSelections: Object.fromEntries(
|
||||
Object.entries(dirFileSelections.value).map(([key, value]) => [
|
||||
key,
|
||||
[...value]
|
||||
])
|
||||
)
|
||||
};
|
||||
const sourceState = getCurrentSourceState();
|
||||
sourceState.dirKeys = [...checkedDirKeys.value];
|
||||
sourceState.fileKeys = [...getAllSelectedFileKeys(sourceState)];
|
||||
sourceState.currentDirKey =
|
||||
selectedKeys.value[0] !== undefined ? selectedKeys.value[0] : undefined;
|
||||
selectionStateMap.value[getSelectionStorageKey()] = getMergedSelectionPayload();
|
||||
};
|
||||
|
||||
const restoreSelectionState = () => {
|
||||
const state = selectionStateMap.value[getSelectionStorageKey()];
|
||||
checkedDirKeys.value = [...(state?.dirKeys || [])];
|
||||
selectedDocList.value = checkedDirKeys.value.map((key) => key.toString());
|
||||
dirFileSelections.value = Object.fromEntries(
|
||||
Object.entries(state?.dirFileSelections || {}).map(([key, value]) => [
|
||||
key,
|
||||
[...value]
|
||||
])
|
||||
const normalizedState = normalizeSelectionState(
|
||||
selectionStateMap.value[getSelectionStorageKey()]
|
||||
);
|
||||
selectionStateMap.value[getSelectionStorageKey()] = normalizedState;
|
||||
activeSource.value = normalizedState.activeSource;
|
||||
syncSelectedFileState();
|
||||
};
|
||||
|
||||
const syncSelectionStateFromProps = () => {
|
||||
selectionStateMap.value[getSelectionStorageKey()] = normalizeSelectionState(
|
||||
props.selectionState
|
||||
);
|
||||
};
|
||||
|
||||
const handleDocSelectCancel = () => {
|
||||
saveSelectionState();
|
||||
showDocSelect.value = false;
|
||||
@@ -246,15 +515,12 @@
|
||||
|
||||
const confirmSelection = () => {
|
||||
saveSelectionState();
|
||||
const payload = getMergedSelectionPayload();
|
||||
|
||||
// 传递选择的数据给父组件
|
||||
emit('confirm', {
|
||||
dirKeys: [...checkedDirKeys.value],
|
||||
fileKeys: getAllSelectedFileKeys()
|
||||
});
|
||||
emit('confirm', payload);
|
||||
|
||||
message.success(
|
||||
`已选择 ${checkedDirKeys.value.length} 个目录和 ${getAllSelectedFileKeys().length} 个文件`
|
||||
`已选择 ${payload.dirKeys.length} 个目录和 ${payload.fileKeys.length} 个文件`
|
||||
);
|
||||
showDocSelect.value = false;
|
||||
};
|
||||
@@ -262,25 +528,43 @@
|
||||
const loadAllCloudDocs = async () => {
|
||||
try {
|
||||
const params = {
|
||||
companyId: props.currentCompanyId
|
||||
companyId: activeSource.value === 'public' ? 0 : props.currentCompanyId
|
||||
};
|
||||
const result = await listAiCloudDoc(params);
|
||||
allDirs.value = result || [];
|
||||
|
||||
// 默认展开根节点并选中第一个目录
|
||||
if (allDirs.value.length > 0) {
|
||||
const rootDirs = allDirs.value.filter((item) => item.parentId === 0);
|
||||
if (rootDirs.length > 0) {
|
||||
expandedKeys.value = [0];
|
||||
const sourceState = getCurrentSourceState();
|
||||
const rootDirs = allDirs.value.filter((item) => item.parentId === 0);
|
||||
const availableIds = new Set(allDirs.value.map((item) => item.id));
|
||||
const firstSelectedDirFromFiles = Object.keys(
|
||||
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;
|
||||
|
||||
// 如果已经有选中的目录,保持选中状态,否则选中第一个目录
|
||||
if (selectedKeys.value.length === 0) {
|
||||
selectedKeys.value = [rootDirs[0].id!];
|
||||
}
|
||||
expandedKeys.value = rootDirs.map((item) => item.id!).filter(Boolean);
|
||||
selectedKeys.value = preferredSelectedKey ? [preferredSelectedKey] : [];
|
||||
|
||||
syncSelectedFileState();
|
||||
loadCloudFiles();
|
||||
}
|
||||
syncSelectedFileState();
|
||||
if (selectedKeys.value.length > 0) {
|
||||
await loadCloudFiles();
|
||||
} else {
|
||||
docList.value = [];
|
||||
pagination.value.total = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
message.error('加载目录列表失败');
|
||||
@@ -296,6 +580,8 @@
|
||||
// 树节点选择
|
||||
const onSelect = (keys: (string | number)[]) => {
|
||||
selectedKeys.value = keys;
|
||||
const sourceState = getCurrentSourceState();
|
||||
sourceState.currentDirKey = keys[0];
|
||||
pagination.value.current = 1;
|
||||
syncSelectedFileState();
|
||||
loadCloudFiles();
|
||||
@@ -309,8 +595,8 @@
|
||||
: [];
|
||||
};
|
||||
|
||||
const getDirFileKeys = async (dirKey: string | number) => {
|
||||
const cacheKey = String(dirKey);
|
||||
const getDirFileKeys = async (dirKey: SelectionKey) => {
|
||||
const cacheKey = `${activeSource.value}_${String(dirKey)}`;
|
||||
if (dirFileKeyCache.value[cacheKey]) {
|
||||
return dirFileKeyCache.value[cacheKey];
|
||||
}
|
||||
@@ -331,6 +617,7 @@
|
||||
const onDirCheck = async (checkedKeys: any) => {
|
||||
const nextCheckedKeys = getCheckedKeyList(checkedKeys);
|
||||
const prevCheckedKeys = [...checkedDirKeys.value];
|
||||
const sourceState = getCurrentSourceState();
|
||||
const addedKeys = nextCheckedKeys.filter(
|
||||
(key) => !prevCheckedKeys.includes(key)
|
||||
);
|
||||
@@ -340,6 +627,7 @@
|
||||
|
||||
checkedDirKeys.value = nextCheckedKeys;
|
||||
selectedDocList.value = nextCheckedKeys.map((key) => key.toString());
|
||||
sourceState.dirKeys = [...nextCheckedKeys];
|
||||
|
||||
if (!addedKeys.length && !removedKeys.length) {
|
||||
saveSelectionState();
|
||||
@@ -351,10 +639,10 @@
|
||||
addedKeys.map((key) => getDirFileKeys(key))
|
||||
);
|
||||
addedKeys.forEach((key, index) => {
|
||||
dirFileSelections.value[String(key)] = [...addedFileGroups[index]];
|
||||
sourceState.dirFileSelections[String(key)] = [...addedFileGroups[index]];
|
||||
});
|
||||
removedKeys.forEach((key) => {
|
||||
delete dirFileSelections.value[String(key)];
|
||||
delete sourceState.dirFileSelections[String(key)];
|
||||
});
|
||||
syncSelectedFileState();
|
||||
} catch (error) {
|
||||
@@ -391,27 +679,31 @@
|
||||
|
||||
// 文件选择变化
|
||||
const onFileSelectionChange = (
|
||||
selectedRowKeys: (string | number)[],
|
||||
selectedRows: AiCloudFile[]
|
||||
selectedRowKeys: SelectionKey[]
|
||||
) => {
|
||||
const currentDirKey = getCurrentDirKey();
|
||||
if (!currentDirKey) return;
|
||||
|
||||
dirFileSelections.value[currentDirKey] = [...selectedRowKeys];
|
||||
const sourceState = getCurrentSourceState();
|
||||
sourceState.dirFileSelections[currentDirKey] = [...selectedRowKeys];
|
||||
sourceState.fileKeys = getAllSelectedFileKeys(sourceState);
|
||||
selectedFileKeys.value = selectedRowKeys;
|
||||
selectedFileList.value = getAllSelectedFileKeys().map((key) =>
|
||||
selectedFileList.value = sourceState.fileKeys.map((key) =>
|
||||
key.toString()
|
||||
);
|
||||
saveSelectionState();
|
||||
};
|
||||
|
||||
// 清空选择
|
||||
const clearSelection = () => {
|
||||
const sourceState = getCurrentSourceState();
|
||||
selectedDocList.value = [];
|
||||
selectedFileList.value = [];
|
||||
selectedFileKeys.value = [];
|
||||
checkedDirKeys.value = [];
|
||||
dirFileSelections.value = {};
|
||||
sourceState.dirKeys = [];
|
||||
sourceState.fileKeys = [];
|
||||
sourceState.currentDirKey = undefined;
|
||||
sourceState.dirFileSelections = {};
|
||||
|
||||
saveSelectionState();
|
||||
};
|
||||
@@ -457,11 +749,21 @@
|
||||
|
||||
const open = () => {
|
||||
showDocSelect.value = true;
|
||||
syncSelectionStateFromProps();
|
||||
restoreSelectionState();
|
||||
dirFileKeyCache.value = {};
|
||||
loadAllCloudDocs();
|
||||
};
|
||||
|
||||
const handleSourceChange = async (source: string) => {
|
||||
saveSelectionState();
|
||||
activeSource.value = source === 'public' ? 'public' : 'company';
|
||||
selectedKeys.value = [];
|
||||
dirFileKeyCache.value = {};
|
||||
syncSelectedFileState();
|
||||
await loadAllCloudDocs();
|
||||
};
|
||||
|
||||
defineExpose({ open });
|
||||
|
||||
</script>
|
||||
|
||||
@@ -492,6 +492,7 @@
|
||||
ref="fileModal"
|
||||
:current-company-id="currentCompanyId"
|
||||
:selection-key="currentFileSelectionKey"
|
||||
:selection-state="getTableFileSelection(currentFileSelectionKey)"
|
||||
@confirm="handleFileSelectConfirm"
|
||||
/>
|
||||
<HistoryModal
|
||||
@@ -556,6 +557,75 @@
|
||||
hasContent
|
||||
} 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 props = defineProps<{
|
||||
@@ -602,16 +672,10 @@
|
||||
const currentFileSelectionKey = ref('');
|
||||
const selectedDocList = ref<string[]>([]);
|
||||
const selectedFileList = ref<string[]>([]);
|
||||
const selectedFileKeys = ref<(string | number)[]>([]);
|
||||
const checkedDirKeys = ref<(string | number)[]>([]);
|
||||
const selectedFileKeys = ref<SelectionKey[]>([]);
|
||||
const checkedDirKeys = ref<SelectionKey[]>([]);
|
||||
const tableFileSelectionMap = reactive<
|
||||
Record<
|
||||
string,
|
||||
{
|
||||
dirKeys: (string | number)[];
|
||||
fileKeys: (string | number)[];
|
||||
}
|
||||
>
|
||||
Record<string, TableFileSelectionState>
|
||||
>({});
|
||||
|
||||
// ========== 取证单相关 ==========
|
||||
@@ -704,12 +768,7 @@
|
||||
};
|
||||
|
||||
const getTableFileSelection = (tableKey: string) => {
|
||||
return (
|
||||
tableFileSelectionMap[tableKey] || {
|
||||
dirKeys: [],
|
||||
fileKeys: []
|
||||
}
|
||||
);
|
||||
return normalizeTableFileSelection(tableFileSelectionMap[tableKey]);
|
||||
};
|
||||
|
||||
const applyTableFileSelection = (tableKey: string) => {
|
||||
@@ -722,13 +781,9 @@
|
||||
|
||||
const saveTableFileSelection = (
|
||||
tableKey: string,
|
||||
dirKeys: (string | number)[] = [],
|
||||
fileKeys: (string | number)[] = []
|
||||
selection?: Partial<TableFileSelectionState>
|
||||
) => {
|
||||
tableFileSelectionMap[tableKey] = {
|
||||
dirKeys: [...dirKeys],
|
||||
fileKeys: [...fileKeys]
|
||||
};
|
||||
tableFileSelectionMap[tableKey] = normalizeTableFileSelection(selection);
|
||||
};
|
||||
|
||||
// ========== 通用方法 ==========
|
||||
@@ -777,13 +832,9 @@
|
||||
};
|
||||
|
||||
/* 处理文件选择确认 */
|
||||
const handleFileSelectConfirm = (data: { dirKeys: (string | number)[], fileKeys: (string | number)[] }) => {
|
||||
const handleFileSelectConfirm = (data: TableFileSelectionState) => {
|
||||
if (currentFileSelectionKey.value) {
|
||||
saveTableFileSelection(
|
||||
currentFileSelectionKey.value,
|
||||
data.dirKeys,
|
||||
data.fileKeys
|
||||
);
|
||||
saveTableFileSelection(currentFileSelectionKey.value, data);
|
||||
}
|
||||
|
||||
checkedDirKeys.value = data.dirKeys;
|
||||
@@ -1438,8 +1489,12 @@
|
||||
saveTableGenerationData(tableKey, requestData, responseData, interfaceName);
|
||||
saveTableFileSelection(
|
||||
tableKey,
|
||||
Array.isArray(requestData?.docList) ? requestData.docList : [],
|
||||
Array.isArray(requestData?.fileList) ? requestData.fileList : []
|
||||
requestData?.fileSelectionState || {
|
||||
dirKeys: Array.isArray(requestData?.docList) ? requestData.docList : [],
|
||||
fileKeys: Array.isArray(requestData?.fileList)
|
||||
? requestData.fileList
|
||||
: []
|
||||
}
|
||||
);
|
||||
|
||||
// 使用数据映射函数
|
||||
@@ -1877,7 +1932,8 @@
|
||||
const normalizedRequestData = {
|
||||
...(generationData.requestData || {}),
|
||||
docList: [...currentSelection.dirKeys],
|
||||
fileList: [...currentSelection.fileKeys]
|
||||
fileList: [...currentSelection.fileKeys],
|
||||
fileSelectionState: normalizeTableFileSelection(currentSelection)
|
||||
};
|
||||
|
||||
const aiHistory = {
|
||||
|
||||
Reference in New Issue
Block a user