1、文档选择添加公共库
This commit is contained in:
@@ -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 saveSelectionState = () => {
|
const mergedDirKeys = computed(() => {
|
||||||
selectionStateMap.value[getSelectionStorageKey()] = {
|
const state = getCurrentSelectionState();
|
||||||
dirKeys: [...checkedDirKeys.value],
|
return dedupeKeys([...state.company.dirKeys, ...state.public.dirKeys]);
|
||||||
dirFileSelections: Object.fromEntries(
|
});
|
||||||
Object.entries(dirFileSelections.value).map(([key, value]) => [
|
|
||||||
key,
|
const mergedFileKeys = computed(() => {
|
||||||
[...value]
|
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 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 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);
|
||||||
if (rootDirs.length > 0) {
|
const availableIds = new Set(allDirs.value.map((item) => item.id));
|
||||||
expandedKeys.value = [0];
|
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;
|
||||||
|
|
||||||
// 如果已经有选中的目录,保持选中状态,否则选中第一个目录
|
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>
|
||||||
|
|||||||
@@ -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 = {
|
||||||
|
|||||||
Reference in New Issue
Block a user