| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326 |
- <template>
- <div :class="prefixCls" class="case-data-cleaning-page">
- <!-- 清洗流程公共顶栏:品牌 / 案件 / 模板库 / 下载管理 / 窗口控制 -->
- <CaseFlowTopBar />
- <div class="page-body">
- <div class="page-header">
- <div class="page-header-main">
- <div class="page-title-area">
- <Button type="text" class="back-btn" @click="handleBack">←</Button>
- <div class="page-title-block">
- <div class="page-title">案件数据清洗</div>
- </div>
- </div>
- <div class="page-header-actions">
- <Button @click="toggleLeft">{{ leftCollapsed ? '展开左侧' : '收起左侧' }}</Button>
- <Button type="primary" :loading="importingData" @click="handleImportData">导入数据</Button>
- </div>
- </div>
- </div>
- <div class="main-layout" :style="{ gridTemplateColumns: layoutTemplateColumns }">
- <FileTreePanel />
- <div v-show="!leftCollapsed" class="panel-resizer" @mousedown="startResize('left', $event)"></div>
- <section class="middle-panel">
- <div class="template-match-bar" :class="{ 'is-empty': !hasTemplateSelected }">
- <div class="template-match-main">
- <span class="template-match-icon"></span>
- <div class="template-match-copy">
- <div class="template-match-label">匹配模板</div>
- <div class="template-match-name">
- {{ currentTemplateDisplayName }}
- <span v-if="!hasTemplateSelected" class="template-match-badge is-pending">待配置</span>
- <span v-else-if="templateDetailFromMock" class="template-match-badge">示例</span>
- </div>
- </div>
- </div>
- <Button type="link" class="template-select-trigger" @click="openTemplateModal">选择模板</Button>
- </div>
- <template v-if="hasTemplateSelected">
- <div class="middle-panel-body">
- <FieldMappingCard />
- <div
- class="template-foot-panel"
- :class="{
- 'is-collapsed': showRecognitionPanel && !recognitionPanelExpanded,
- 'is-compact': !showRecognitionPanel,
- }"
- >
- <RecognitionRulePanel />
- <ChildTemplatePanel />
- </div>
- </div>
- </template>
- <MiddleEmptyState v-else />
- </section>
- <div class="panel-resizer" @mousedown="startResize('middle', $event)"></div>
- <section class="right-panel">
- <div class="panel-header">
- <div class="panel-title-wrap">
- <div class="panel-title">数据清洗区</div>
- </div>
- </div>
- <div class="toolbar toolbar--column-actions">
- <div class="toolbar-head">
- <span class="toolbar-label">列操作</span>
- </div>
- <div class="toolbar-actions">
- <Button size="small" type="primary" @click="openAddColumn">新增列</Button>
- <Button size="small" @click="openSplitColumn">拆分列</Button>
- <Button size="small" @click="openEditColumn">编辑列</Button>
- <Button size="small" @click="openDeleteColumn">删除列</Button>
- </div>
- <div class="toolbar-meta toolbar-meta--header-row">
- <span class="meta-label">表头行</span>
- <Input
- v-model:value="headerRowInput"
- size="small"
- class="meta-input"
- placeholder="输入行号"
- @pressEnter="applyHeaderRowInput"
- @blur="applyHeaderRowInput"
- />
- </div>
- </div>
- <div class="right-panel-body">
- <DataPreviewTable />
- <CleaningRulePanel />
- </div>
- </section>
- </div>
- </div>
- <AddColumnModal />
- <SplitColumnModal />
- <Modal v-model:open="deleteColumnDialogOpen" title="删除列" :width="520" :centered="true" @ok="confirmDeleteColumn">
- <div>确认删除列「{{ selectedAddedColumn || '-' }}」?</div>
- </Modal>
- <TemplateSelectModal />
- </div>
- </template>
- <script lang="ts" setup name="ViewsDataCleaning">
- import { computed, onBeforeUnmount, onMounted, provide, ref, watch } from 'vue';
- import { Button, Input, Modal } from 'ant-design-vue';
- import { useDesign } from '@/core/hooks/web/useDesign';
- import { useGo, useQuery } from '@/core/hooks/web/usePage';
- import { PageEnum } from '@/core/enums/pageEnum';
- import { useMessage } from '@/core/hooks/web/useMessage';
- import { tplDetail } from '@/plat/api/tpl/tplApi';
- import type { TableInfo } from '@/plat/api/tpl/types/dto';
- import { resetImportProgressSession } from './importProgressSession';
- import { FILE_INFO_STORAGE_KEY, IMPORT_FILE_INFO_STORAGE_KEY } from './cleaning/constants';
- import type { PreviewFileDataset, TemplateListItem, TemplateTab, TreeFileInfo } from './cleaning/types';
- import { collectMatchedSheetsWithoutHeaderRow } from './cleaning/utils/headerRow';
- import { mockTemplateMap } from './cleaning/utils/mockData';
- import {
- hasRecognitionBasicInfoFilledContent,
- normalizeRecognitionBasicInfoText,
- parseRecognitionBasicInfoRows,
- resolveRecognitionBasicInfoTargetField,
- } from './cleaning/utils/recognition';
- import { sanitizeFileInfoTree, sanitizeSelectedRuleList } from './cleaning/utils/ruleSerialize';
- import {
- getTableRuleTargetKey,
- getTableRuleTargetLabel,
- normalizeTableRuleList,
- } from './cleaning/utils/tableRuleBuild';
- import {
- buildStoredLineNo,
- cloneDeepSafe,
- getRequestErrorMessage,
- normalizeFieldMatchText,
- normalizeRecognitionConfigText,
- normalizeRuleText,
- normalizeTemplateHeaders,
- normalizeTemplateId,
- } from './cleaning/utils/text';
- import {
- findFirstSelectableKey,
- findRootByNodeId,
- findTreeNodeByKey,
- isFileRootItem,
- isTemplateMatchedFailStatus,
- isTreeSheetNode,
- } from './cleaning/utils/treeUtils';
- import AddColumnModal from './cleaning/components/AddColumnModal.vue';
- import SplitColumnModal from './cleaning/components/SplitColumnModal.vue';
- import TemplateSelectModal from './cleaning/components/TemplateSelectModal.vue';
- import { CleaningContextKey } from './cleaning/context';
- import CleaningRulePanel from './cleaning/components/CleaningRulePanel.vue';
- import { useCleaningRules } from './cleaning/composables/useCleaningRules';
- import FieldMappingCard from './cleaning/components/FieldMappingCard.vue';
- import RecognitionRulePanel from './cleaning/components/RecognitionRulePanel.vue';
- import ChildTemplatePanel from './cleaning/components/ChildTemplatePanel.vue';
- import MiddleEmptyState from './cleaning/components/MiddleEmptyState.vue';
- import { useFieldMapping } from './cleaning/composables/useFieldMapping';
- import { useRecognitionRules } from './cleaning/composables/useRecognitionRules';
- import { useTemplateLibrary } from './cleaning/composables/useTemplateLibrary';
- import { useCaseDataCleaningShared } from './cleaning/composables/useCaseDataCleaningShared';
- import { importFileList } from '@/case/api/govern/governApi';
- import FileTreePanel from './cleaning/components/FileTreePanel.vue';
- import DataPreviewTable from './cleaning/components/DataPreviewTable.vue';
- import { useCaseDataCleaningActions } from './cleaning/composables/useCaseDataCleaningActions';
- import CaseFlowTopBar from './components/CaseFlowTopBar.vue';
- const { prefixCls } = useDesign('case-data-cleaning');
- const go = useGo();
- const pageQuery = useQuery();
- const { createMessage } = useMessage();
- const importingData = ref(false);
- const leftCollapsed = ref(false);
- const leftPanelWidth = ref(300);
- const middlePanelWidth = ref(520);
- const minLeftWidth = 220;
- const maxLeftWidth = 520;
- const minMiddleWidth = 360;
- const maxMiddleWidth = 900;
- const minRightWidth = 560;
- const resizeState = ref<{ type: 'left' | 'middle' | ''; startX: number; startLeft: number; startMiddle: number }>({
- type: '',
- startX: 0,
- startLeft: 300,
- startMiddle: 520,
- });
- const pendingClientX = ref<number | null>(null);
- const rafId = ref<number | null>(null);
- const layoutTemplateColumns = computed(() => {
- if (leftCollapsed.value) {
- return `${middlePanelWidth.value}px 6px minmax(${minRightWidth}px, 1fr)`;
- }
- return `${leftPanelWidth.value}px 6px ${middlePanelWidth.value}px 6px minmax(${minRightWidth}px, 1fr)`;
- });
- const resolveSelectableTreeKey = (key: string) => {
- const selectedNode = findTreeNodeByKey(treeData.value, key);
- if (!selectedNode) return '';
- if (selectedNode.selectable !== false) return selectedNode.key;
- return findFirstSelectableKey(selectedNode.children || []);
- };
- const switchNodeState = async (nextKey: string) => {
- if (!nextKey) return;
- if (activeNodeKey.value === nextKey) return;
- saveCurrentNodeState();
- setNodeAnalyzing(nextKey, true);
- activeNodeKey.value = nextKey;
- const currentNode = findTreeNodeByKey(treeData.value, nextKey)?.raw || null;
- structureSyncSuspended.value = true;
- try {
- const cached = nodeStateCache.get(nextKey);
- if (cached) {
- applyNodeCacheState(cached);
- await loadTemplateDetail(cached.selectedTemplateId, { silent: true, requestDetail: false });
- touchNodeCache(nextKey, cached);
- return;
- }
- resetNodeWorkState();
- const shouldLoadNodeTemplate = !isTemplateMatchedFailStatus(currentNode?.fileStatus);
- await loadTemplateDetail(shouldLoadNodeTemplate ? currentNode?.templateId : undefined, {
- silent: true,
- requestDetail: false,
- });
- await applyNodePreset(currentNode);
- const initialState = buildNodeCacheState();
- touchNodeCache(nextKey, initialState);
- if (!initialNodeStatusSignatureMap.has(nextKey)) {
- initialNodeStatusSignatureMap.set(nextKey, buildNodeStatusSignature(initialState));
- }
- } finally {
- structureSyncSuspended.value = false;
- syncActiveNodeStructure();
- setNodeAnalyzing(nextKey, false);
- }
- };
- const applyPreviewDataset = (dataset: PreviewFileDataset) => {
- rightTableHeaders.value = [...dataset.headers];
- addedColumns.value = [];
- generatedColumnHeaderRowMap.value = {};
- selectedAddedColumn.value = '';
- sourceTableRows.value = dataset.rows.length ? dataset.rows : [];
- tablePreviewRows.value = cloneDeepSafe(sourceTableRows.value);
- return true;
- };
- const syncNodeStructure = (node?: TreeFileInfo | null) => {
- if (!isTreeSheetNode(node)) return;
- const templateId = normalizeTemplateId(
- templateDetail.value?.id ?? activeTemplateItem.value?.id ?? selectedTemplateId.value ?? node.templateId,
- );
- const mainId = getSelectedTemplateMainId(node.mainId);
- const tableName =
- normalizeRuleText(templateDetail.value?.tableNameCn) ||
- normalizeRuleText(activeTemplateItem.value?.tableNameCn) ||
- normalizeRuleText(node.tableName);
- const baseHeaders = rightTableHeaders.value.filter((header) => !addedColumns.value.includes(header));
- const existingHeaders = normalizeTemplateHeaders(node.header);
- const hasRealHeaderConfig =
- headerRowIndex.value !== null ||
- !!existingHeaders.length ||
- addedColumns.value.length > 0 ||
- Object.values(effectiveFieldMapping.value).some(Boolean) ||
- baseHeaders.length > 0;
- const shouldPersistRecognitionBasicInfo = hasRecognitionBasicInfoFilledContent(recognitionBasicInfoRows.value);
- const ignoreLines = Array.from(ignoredRowsSet.value).sort((a, b) => a - b);
- const groupColumns = buildEnabledGroupingColumnsPayload();
- node.templateId = templateId || node.templateId || null;
- node.mainId = mainId || null;
- node.tableName = tableName || node.tableName || null;
- if (headerRowIndex.value !== null) {
- node.lineNo = buildStoredLineNo(headerRowIndex.value);
- }
- if (hasRealHeaderConfig) {
- node.header = baseHeaders.length ? [...baseHeaders] : [];
- }
- node.ignoreLines = ignoreLines;
- node.groupColumns = groupColumns;
- node.tableRuleList = buildTableRuleListFromState(node.tableRuleList);
- node.childTemplateName = normalizeRuleText(childTemplateName.value) || null;
- node.hasNew = !!node.childTemplateName;
- if (shouldPersistRecognitionBasicInfo) {
- node.funcRegx = buildRecognitionBasicInfoFuncRegx();
- } else if (Object.prototype.hasOwnProperty.call(node, 'funcRegx')) {
- node.funcRegx = null;
- }
- persistFileListToSessionStorage();
- };
- const hasMatchedTargetField = (header: string) => !!matchedTargetFieldsByHeader.value[header]?.length;
- const getColumnRuleBadgeState = (colIndex: number) => {
- if (colIndex < 0) return false;
- const rules = sanitizeSelectedRuleList(selectRules.value[colIndex] || []);
- const hasEnabledRules = rules.some((rule) => rule && rule.key && rule.enabled);
- if (hasEnabledRules) return 'active';
- return rules.some((rule) => rule && rule.key) ? 'draft' : '';
- };
- const isTargetHandledByRecognitionRule = (targetKey: string) =>
- recognitionRuleHandledTargetKeySet.value.has(targetKey);
- const requiredTotal = computed(() => fixedTargetFields.value.filter((item) => isMappingTargetRequired(item)).length);
- const DEFAULT_TEMPLATE_CATEGORY = '全部分类';
- const templateModalOpen = ref(false);
- const activeTemplateTab = ref<TemplateTab>('template');
- const activeCategory = ref(DEFAULT_TEMPLATE_CATEGORY);
- const templateKeyword = ref('');
- const templateSource = ref<number>(1);
- const modalTemplateDetail = ref<TableInfo | null>(null);
- const templatePreviewLoading = ref(false);
- let templatePreviewRequestToken = 0;
- const categories = computed(() => {
- const set = new Set<string>(
- templateCategoryOptions.value.length ? templateCategoryOptions.value : [DEFAULT_TEMPLATE_CATEGORY],
- );
- templateList.value.forEach((t) => set.add(t.classify || FALLBACK_TEMPLATE_CATEGORY));
- return Array.from(set);
- });
- const showTemplateTabSwitch = computed(
- () => activeCategory.value === DEFAULT_TEMPLATE_CATEGORY || pluginCategorySet.value.has(activeCategory.value),
- );
- const syncModalTemplateItem = (preferredId?: string | number | null) => {
- const currentBaseList = filteredTemplateList.value;
- if (activeTemplateTab.value === 'plugin') {
- const currentPluginId = modalTemplateItem.value?.id;
- const matchedPlugin = currentBaseList.find((item) => String(item.id) === String(currentPluginId)) || null;
- modalTemplateItem.value = matchedPlugin;
- return modalTemplateItem.value;
- }
- const preferredTemplateId = normalizeTemplateId(preferredId);
- const currentTemplateId = normalizeTemplateId(modalTemplateItem.value?.id);
- if (preferredTemplateId) {
- const matched = currentBaseList.find((item) => normalizeTemplateId(item.id) === preferredTemplateId) || null;
- if (matched) {
- modalTemplateItem.value = matched;
- return matched;
- }
- }
- if (currentTemplateId) {
- const currentMatched = currentBaseList.find((item) => normalizeTemplateId(item.id) === currentTemplateId) || null;
- if (currentMatched) {
- modalTemplateItem.value = currentMatched;
- return currentMatched;
- }
- }
- modalTemplateItem.value = currentBaseList[0] || null;
- return modalTemplateItem.value;
- };
- const resolveTemplateInfoById = (templateId?: string | number | null) => {
- const id = normalizeTemplateId(templateId);
- if (!id) return null;
- const activeDetail = normalizeTemplateId(templateDetail.value?.id) === id ? templateDetail.value : null;
- const activeItem = normalizeTemplateId(activeTemplateItem.value?.id) === id ? activeTemplateItem.value : null;
- const cachedDetail = templateDetailCache.get(id) || null;
- const listItem = getTemplateListItemById(id) || mockTemplateMap[id] || null;
- return (
- mergeTemplateInfo(activeItem || listItem || activeDetail, activeDetail || cachedDetail) ||
- activeDetail ||
- cachedDetail ||
- activeItem ||
- listItem ||
- null
- );
- };
- const syncModalTemplatePreviewState = (
- template?: TemplateListItem | null,
- options: { preferAppliedDetail?: boolean } = {},
- ) => {
- modalTemplateItem.value = template || null;
- modalTemplateDetail.value = null;
- templatePreviewLoading.value = false;
- templatePreviewRequestToken += 1;
- const id = normalizeTemplateId(template?.id);
- if (!id || Number(template?.id) < 0) return null;
- if (options.preferAppliedDetail && normalizeTemplateId(templateDetail.value?.id) === id && templateDetail.value) {
- modalTemplateDetail.value = templateDetail.value;
- return modalTemplateDetail.value;
- }
- const cachedDetail = templateDetailCache.get(id);
- if (cachedDetail) {
- modalTemplateDetail.value = cachedDetail;
- return modalTemplateDetail.value;
- }
- return null;
- };
- const loadTemplatePreviewDetail = async (
- template?: TemplateListItem | null,
- options: { preferAppliedDetail?: boolean } = {},
- ) => {
- const existingDetail = syncModalTemplatePreviewState(template, options);
- if (existingDetail) {
- return existingDetail;
- }
- const id = normalizeTemplateId(template?.id);
- if (!id || Number(template?.id) < 0) return null;
- const requestToken = ++templatePreviewRequestToken;
- templatePreviewLoading.value = true;
- try {
- const detail = await tplDetail({ id });
- if (requestToken !== templatePreviewRequestToken) return null;
- if (detail) {
- templateDetailCache.set(id, detail);
- modalTemplateDetail.value = detail;
- mergeTemplateListItem(mergeTemplateInfo(template, detail));
- modalTemplateItem.value = getTemplateListItemById(id) || template || null;
- }
- return modalTemplateDetail.value;
- } catch (error) {
- if (requestToken === templatePreviewRequestToken) {
- modalTemplateDetail.value = null;
- }
- return null;
- } finally {
- if (requestToken === templatePreviewRequestToken) {
- templatePreviewLoading.value = false;
- }
- }
- };
- const closeTemplateModal = () => {
- templateModalOpen.value = false;
- templatePreviewRequestToken += 1;
- templatePreviewLoading.value = false;
- modalTemplateDetail.value = null;
- modalTemplateItem.value = null;
- };
- watch([activeCategory, templateSource, templateKeyword, activeTemplateTab], async () => {
- if (!templateModalOpen.value) return;
- if (!showTemplateTabSwitch.value) {
- activeTemplateTab.value = 'template';
- }
- const previewTarget = syncModalTemplateItem(
- activeTemplateTab.value === 'plugin' ? undefined : selectedTemplateId.value,
- );
- await loadTemplatePreviewDetail(previewTarget, {
- preferAppliedDetail:
- activeTemplateTab.value !== 'plugin' && normalizeTemplateId(previewTarget?.id) === selectedTemplateId.value,
- });
- });
- watch(activeTemplateTab, (tab) => {
- if (!templateModalOpen.value) return;
- if (tab === 'plugin') {
- modalTemplateDetail.value = null;
- templatePreviewLoading.value = false;
- templatePreviewRequestToken += 1;
- }
- });
- const activeNodeInfo = computed(() => {
- return activeNodeKey.value ? findTreeNodeByKey(treeData.value, activeNodeKey.value)?.raw || null : null;
- });
- // Phase 5:底层共享状态收进唯一 store。页面同名解构接住,原有代码与模板零改动。
- const caseDataCleaningShared = useCaseDataCleaningShared({
- activeNodeInfo,
- applyPreviewDataset,
- createMessage,
- getColumnRuleBadgeState,
- hasMatchedTargetField,
- pageQuery,
- requiredTotal,
- resolveSelectableTreeKey,
- resolveTemplateInfoById,
- switchNodeState,
- syncNodeStructure,
- });
- const {
- addColumnDialogOpen,
- addColumnForm,
- addColumnMode,
- addedColumns,
- analyzingNodeKeys,
- applyPresetHeadersFromNode,
- availableSourceHeaders,
- buildRecognitionDirectionConf,
- childTemplateName,
- collectChildTemplateNames,
- currentColumn,
- deleteColumnDialogOpen,
- dragOverContentIndex,
- draggingContentIndex,
- findDuplicateChildTemplateNames,
- getColumnRuleBadgeTitle,
- getFocusedNodeKeyFromQuery,
- getMatchedHeaderStatusClass,
- getMatchedHeaderStatusText,
- getMatchedHeaderStatusTitle,
- handleSelect,
- hasDuplicateHeaderName,
- headerRowIndex,
- initialNodeStatusSignatureMap,
- isMatchSuccessful,
- isSheetMatchComplete,
- loadPreviewDatasetFromFile,
- mappingPopoverOpenKey,
- mockGridTemplate,
- openAddColumn,
- openDeleteColumn,
- openSplitColumn,
- originFilterKeyword,
- recognitionBasicInfoBaseFuncRegx,
- recognitionBasicInfoManualVisible,
- remapGroupingColumnRules,
- remapSelectRuleHeaderRefs,
- resolveParentTemplateInfoById,
- rightTableHeaders,
- selectedAddedColumn,
- selectedContentIndex,
- selectedKeys,
- selectedRowIndex,
- selectedSourceField,
- setNodeAnalyzing,
- showDuplicateChildTemplateWarning,
- sourceTableRows,
- splitColumnDialogOpen,
- splitColumnForm,
- structureSyncSuspended,
- syncActiveNodeStructure,
- tablePreviewRows,
- } = caseDataCleaningShared;
- // 既有域:cleaningRules
- const cleaningRules = useCleaningRules({
- addedColumns,
- createMessage,
- currentColumn,
- headerRowIndex,
- rightTableHeaders,
- sourceTableRows,
- tablePreviewRows,
- });
- const {
- absoluteValueChecked,
- activeNodeKey,
- activeNodeTableRuleMap,
- activeRules,
- activeTemplateItem,
- addedColumnConfig,
- applyTableRuleRequiredFlags,
- buildAmountZfHandledTargetKeySet,
- buildEffectiveFieldMapping,
- buildGeneratedColumnHeaderRowMap,
- buildRecognitionBasicInfoMatchedTargetKeySet,
- currentRuleTitle,
- dateConfigType,
- decimalEnabled,
- decimalSourceBase,
- decimalTargetBase,
- effectiveFieldMapping,
- extractDataAfter,
- extractDataBefore,
- extractDataMode,
- extractSegmentRow,
- extractSegmentValue,
- fieldMapping,
- fileList,
- fixedTargetFields,
- generatedColumnHeaderRowMap,
- getEnabledGroupingColumnsForHeader,
- getGeneratedColumnHeaderRowIndex,
- groupingColumnsDraft,
- groupingEnabled,
- ignoredRowsSet,
- magnificationValue,
- matchedTargetFieldsByHeader,
- omitLineInput,
- rebuildGeneratedColumnsInSourceRows,
- recognitionAmountZfEnabled,
- recognitionBasicInfoMatchedTargetKeySet,
- recognitionBasicInfoRows,
- recognitionDirectionFields,
- recognitionRuleHandledTargetKeySet,
- removeCustomChecked,
- removeCustomText,
- removeSpaceChecked,
- removeTabChecked,
- replaceEnabled,
- replaceMode,
- replaceSource,
- replaceTarget,
- resolveTemplateFieldMeta,
- runRulePipeline,
- selectRules,
- selectedRules,
- selectedTemplateId,
- setGeneratedColumnCell,
- sortMode,
- splitColumnConfig,
- syncExtractDataMode,
- templateDetail,
- treeData,
- } = cleaningRules;
- // 既有域:recognitionApi
- const recognitionApi = useRecognitionRules({
- absoluteValueChecked,
- activeNodeKey,
- activeRules,
- addedColumnConfig,
- addedColumns,
- buildGeneratedColumnHeaderRowMap,
- childTemplateName,
- createMessage,
- currentColumn,
- currentRuleTitle,
- dateConfigType,
- decimalEnabled,
- decimalSourceBase,
- decimalTargetBase,
- extractDataAfter,
- extractDataBefore,
- extractSegmentRow,
- extractSegmentValue,
- fieldMapping,
- fixedTargetFields,
- generatedColumnHeaderRowMap,
- groupingColumnsDraft,
- groupingEnabled,
- headerRowIndex,
- magnificationValue,
- mappingPopoverOpenKey,
- omitLineInput,
- originFilterKeyword,
- recognitionAmountZfEnabled,
- recognitionBasicInfoBaseFuncRegx,
- recognitionBasicInfoManualVisible,
- recognitionBasicInfoRows,
- recognitionDirectionFields,
- removeCustomChecked,
- removeCustomText,
- removeSpaceChecked,
- removeTabChecked,
- replaceEnabled,
- replaceMode,
- replaceSource,
- replaceTarget,
- rightTableHeaders,
- runRulePipeline,
- selectRules,
- selectedAddedColumn,
- selectedRowIndex,
- selectedRules,
- selectedTemplateId,
- sourceTableRows,
- splitColumnConfig,
- syncActiveNodeStructure,
- });
- const {
- activeRecognitionRule,
- buildNodeCacheState,
- headerRowInput,
- nodeStateCache,
- recognitionBasicInfoExpanded,
- recognitionPanelExpanded,
- recognitionRuleTabs,
- saveCurrentNodeState,
- showRecognitionPanel,
- touchNodeCache,
- } = recognitionApi;
- // 既有域:templateApi
- const templateApi = useTemplateLibrary({
- DEFAULT_TEMPLATE_CATEGORY,
- activeCategory,
- activeNodeInfo,
- activeTemplateItem,
- activeTemplateTab,
- addedColumnConfig,
- addedColumns,
- childTemplateName,
- createMessage,
- fieldMapping,
- headerRowIndex,
- recognitionAmountZfEnabled,
- recognitionBasicInfoBaseFuncRegx,
- recognitionBasicInfoManualVisible,
- recognitionBasicInfoRows,
- recognitionDirectionFields,
- rightTableHeaders,
- selectRules,
- selectedRules,
- selectedTemplateId,
- splitColumnConfig,
- syncActiveNodeStructure,
- templateDetail,
- templateKeyword,
- templateSource,
- });
- const {
- FALLBACK_TEMPLATE_CATEGORY,
- checkTemplateNameAvailable,
- currentTemplateDisplayName,
- currentTemplateName,
- filteredTemplateList,
- getTemplateListItemById,
- hasTemplateSelected,
- isSystemTemplateRecord,
- loadTemplateCategories,
- loadTemplateList,
- mergeTemplateInfo,
- mergeTemplateListItem,
- modalTemplateItem,
- pluginCategorySet,
- showChildTemplateExistsWarning,
- syncSelectedTemplateState,
- templateCategoryOptions,
- templateDetailCache,
- templateDetailFromMock,
- templateList,
- templateListLoaded,
- templateListLoading,
- templateSearchBaseList,
- } = templateApi;
- // categories 依赖 templateApi 解构出的 ref,必须在解构之后注册,否则 setup 期首次求值触发 TDZ 报错
- watch(categories, (nextCategories) => {
- if (!nextCategories.includes(activeCategory.value)) {
- activeCategory.value = DEFAULT_TEMPLATE_CATEGORY;
- }
- });
- // 既有域:mappingApi
- const mappingApi = useFieldMapping({
- activeNodeInfo,
- activeNodeKey,
- activeNodeTableRuleMap,
- activeTemplateItem,
- addedColumns,
- buildRecognitionDirectionConf,
- createMessage,
- currentColumn,
- effectiveFieldMapping,
- fieldMapping,
- fixedTargetFields,
- headerRowIndex,
- ignoredRowsSet,
- isTargetHandledByRecognitionRule,
- mappingPopoverOpenKey,
- matchedTargetFieldsByHeader,
- originFilterKeyword,
- recognitionDirectionFields,
- resolveTemplateFieldMeta,
- rightTableHeaders,
- selectedAddedColumn,
- selectedRowIndex,
- sourceTableRows,
- tablePreviewRows,
- templateDetail,
- treeData,
- });
- const {
- activeMappingTargetKey,
- autoMatchFields,
- clearOriginHoverPreviewState,
- focusPreviewColumn,
- headerFormatValidationMap,
- isMappingTargetRequired,
- isTargetMatched,
- resolveRecognitionDirectionFields,
- tableMockRef,
- } = mappingApi;
- // 待人工确认工作清单(智能清洗入口):清单数据、单文件定位、仅清洗当前表模式
- const loadFallbackTree = async (): Promise<TreeFileInfo[]> => {
- const focusedKey = getFocusedNodeKeyFromQuery();
- const batchId = String(pageQuery.value?.batchId || '');
- if (!focusedKey && !batchId) return [];
- try {
- const roots = (await importFileList(batchId ? ({ batchId } as any) : {})) as unknown as TreeFileInfo[];
- const list = Array.isArray(roots) ? roots : [];
- if (!list.length) return [];
- if (focusedKey) {
- const root = findRootByNodeId(list, focusedKey);
- // 只装 focusKey 所属文件:清洗页保持「单文件树」语义(与重新清洗入口一致),
- // 也避免「导入数据」把全案件的表一起洗掉
- return root ? sanitizeFileInfoTree(cloneDeepSafe([root])) : [];
- }
- return sanitizeFileInfoTree(cloneDeepSafe(list));
- } catch (error) {
- return [];
- }
- };
- // Phase 5:页面动作域。页面同名解构接住,原有代码与模板零改动。
- const cleaningActions = useCaseDataCleaningActions({
- loadFallbackTree,
- DEFAULT_TEMPLATE_CATEGORY,
- absoluteValueChecked,
- activeCategory,
- activeMappingTargetKey,
- activeNodeInfo,
- activeNodeKey,
- activeRecognitionRule,
- activeRules,
- activeTemplateItem,
- activeTemplateTab,
- addColumnDialogOpen,
- addColumnForm,
- addColumnMode,
- addedColumnConfig,
- addedColumns,
- analyzingNodeKeys,
- applyPresetHeadersFromNode,
- applyPreviewDataset,
- applyTableRuleRequiredFlags,
- autoMatchFields,
- availableSourceHeaders,
- buildAmountZfHandledTargetKeySet,
- buildEffectiveFieldMapping,
- buildGeneratedColumnHeaderRowMap,
- buildNodeCacheState,
- buildRecognitionBasicInfoMatchedTargetKeySet,
- buildRecognitionDirectionConf,
- checkTemplateNameAvailable,
- childTemplateName,
- clearOriginHoverPreviewState,
- collectChildTemplateNames,
- createMessage,
- currentColumn,
- currentRuleTitle,
- currentTemplateName,
- dateConfigType,
- decimalEnabled,
- decimalSourceBase,
- decimalTargetBase,
- deleteColumnDialogOpen,
- dragOverContentIndex,
- draggingContentIndex,
- effectiveFieldMapping,
- extractDataAfter,
- extractDataBefore,
- extractDataMode,
- extractSegmentRow,
- extractSegmentValue,
- fieldMapping,
- fileList,
- findDuplicateChildTemplateNames,
- fixedTargetFields,
- focusPreviewColumn,
- generatedColumnHeaderRowMap,
- getEnabledGroupingColumnsForHeader,
- getFocusedNodeKeyFromQuery,
- getGeneratedColumnHeaderRowIndex,
- getTemplateListItemById,
- groupingColumnsDraft,
- groupingEnabled,
- headerFormatValidationMap,
- headerRowIndex,
- headerRowInput,
- initialNodeStatusSignatureMap,
- isMappingTargetRequired,
- isMatchSuccessful,
- isSheetMatchComplete,
- isTargetHandledByRecognitionRule,
- isTargetMatched,
- loadPreviewDatasetFromFile,
- loadTemplateCategories,
- loadTemplateList,
- loadTemplatePreviewDetail,
- magnificationValue,
- mappingPopoverOpenKey,
- matchedTargetFieldsByHeader,
- mergeTemplateInfo,
- mergeTemplateListItem,
- nodeStateCache,
- omitLineInput,
- originFilterKeyword,
- rebuildGeneratedColumnsInSourceRows,
- recognitionAmountZfEnabled,
- recognitionBasicInfoBaseFuncRegx,
- recognitionBasicInfoExpanded,
- recognitionBasicInfoManualVisible,
- recognitionBasicInfoMatchedTargetKeySet,
- recognitionBasicInfoRows,
- recognitionDirectionFields,
- recognitionRuleTabs,
- remapGroupingColumnRules,
- remapSelectRuleHeaderRefs,
- removeCustomChecked,
- removeCustomText,
- removeSpaceChecked,
- removeTabChecked,
- replaceEnabled,
- replaceMode,
- replaceSource,
- replaceTarget,
- requiredTotal,
- resolveParentTemplateInfoById,
- resolveRecognitionDirectionFields,
- resolveSelectableTreeKey,
- resolveTemplateFieldMeta,
- resolveTemplateInfoById,
- rightTableHeaders,
- runRulePipeline,
- selectRules,
- selectedAddedColumn,
- selectedContentIndex,
- selectedKeys,
- selectedRowIndex,
- selectedRules,
- selectedSourceField,
- selectedTemplateId,
- showChildTemplateExistsWarning,
- showDuplicateChildTemplateWarning,
- sourceTableRows,
- splitColumnConfig,
- splitColumnForm,
- switchNodeState,
- syncExtractDataMode,
- syncModalTemplateItem,
- syncSelectedTemplateState,
- templateDetail,
- templateDetailCache,
- templateDetailFromMock,
- templateKeyword,
- templateListLoaded,
- templateListLoading,
- templateModalOpen,
- templateSource,
- treeData,
- });
- const {
- applyHeaderRow,
- applyHeaderRowInput,
- applyNodeCacheState,
- applyNodePreset,
- applyRecognitionAmountZfState,
- applyRecognitionBasicInfoState,
- applyRecognitionDirectionState,
- buildEnabledGroupingColumnsPayload,
- buildNodeStatusSignature,
- buildRecognitionBasicInfoFuncRegx,
- buildTableRuleListFromState,
- confirmDeleteColumn,
- ensureChildTemplateNamesAvailable,
- getHeaderFormatErrorTitle,
- getMatchedTargetFieldText,
- getSelectedTemplateMainId,
- getSheetNodeVisualState,
- hasHeaderFormatError,
- initializePage,
- loadTemplateDetail,
- onClickCell,
- onClickHeader,
- openEditColumn,
- openTemplateModal,
- persistFileListToSessionStorage,
- resetNodeWorkState,
- syncHeaderRowInput,
- } = cleaningActions;
- watch(
- recognitionRuleTabs,
- (tabs) => {
- if (!tabs.length) {
- activeRecognitionRule.value = '';
- return;
- }
- if (!tabs.includes(activeRecognitionRule.value)) {
- activeRecognitionRule.value = tabs[0];
- }
- },
- { immediate: true },
- );
- watch(
- fixedTargetFields,
- (fields) => {
- if (activeMappingTargetKey.value && !fields.some((item) => item.key === activeMappingTargetKey.value)) {
- activeMappingTargetKey.value = '';
- }
- },
- { deep: true },
- );
- const startResize = (type: 'left' | 'middle', event: MouseEvent) => {
- event.preventDefault();
- resizeState.value = {
- type,
- startX: event.clientX,
- startLeft: leftPanelWidth.value,
- startMiddle: middlePanelWidth.value,
- };
- document.body.classList.add('is-resizing-layout');
- document.body.style.cursor = 'col-resize';
- };
- const applyResize = () => {
- if (!resizeState.value.type || pendingClientX.value === null) return;
- const deltaX = pendingClientX.value - resizeState.value.startX;
- if (resizeState.value.type === 'left' && !leftCollapsed.value) {
- const next = resizeState.value.startLeft + deltaX;
- leftPanelWidth.value = Math.min(maxLeftWidth, Math.max(minLeftWidth, next));
- }
- if (resizeState.value.type === 'middle') {
- const next = resizeState.value.startMiddle + deltaX;
- middlePanelWidth.value = Math.min(maxMiddleWidth, Math.max(minMiddleWidth, next));
- }
- rafId.value = null;
- };
- const onResizeMouseMove = (event: MouseEvent) => {
- if (!resizeState.value.type) return;
- event.preventDefault();
- pendingClientX.value = event.clientX;
- if (rafId.value === null) {
- rafId.value = window.requestAnimationFrame(applyResize);
- }
- };
- const stopResize = () => {
- resizeState.value.type = '';
- resizeState.value.startX = 0;
- pendingClientX.value = null;
- if (rafId.value !== null) {
- window.cancelAnimationFrame(rafId.value);
- rafId.value = null;
- }
- document.body.classList.remove('is-resizing-layout');
- document.body.style.cursor = '';
- };
- const toggleLeft = () => {
- leftCollapsed.value = !leftCollapsed.value;
- };
- const handleImportData = async () => {
- saveCurrentNodeState();
- syncActiveNodeStructure();
- const sanitizedFileInfos = sanitizeFileInfoTree(fileList.value, { keepRuleKey: false });
- if (!sanitizedFileInfos.length) {
- createMessage.warning('未找到可导入的数据,请先上传并完成模板匹配');
- return;
- }
- // 只有文件根、没有任何工作表时,后端不会产生清洗任务;不要跳到进度页
- if (!sanitizedFileInfos.some((item) => (item.children || []).length > 0)) {
- createMessage.warning('未找到可清洗文件,请先上传并匹配文件');
- return;
- }
- const matchedSheetsWithoutHeaderRow = collectMatchedSheetsWithoutHeaderRow(sanitizedFileInfos);
- if (matchedSheetsWithoutHeaderRow.length) {
- const previewNames = matchedSheetsWithoutHeaderRow.slice(0, 3).join('、');
- const suffix =
- matchedSheetsWithoutHeaderRow.length > 3 ? ` 等 ${matchedSheetsWithoutHeaderRow.length} 个工作表` : '';
- createMessage.warning(`已匹配模板的工作表必须先填写表头所在行:${previewNames}${suffix}`);
- return;
- }
- try {
- const childTemplateNameValid = await ensureChildTemplateNamesAvailable(sanitizedFileInfos);
- if (!childTemplateNameValid) {
- return;
- }
- } catch (error) {
- createMessage.error(getRequestErrorMessage(error, '模板名称校验失败'));
- return;
- }
- sessionStorage.setItem(FILE_INFO_STORAGE_KEY, JSON.stringify(sanitizedFileInfos));
- importingData.value = true;
- try {
- const resolveMappedHeaderByTargetField = (item?: TreeFileInfo | null, targetField?: string) => {
- const normalizedTarget = normalizeFieldMatchText(targetField);
- if (!normalizedTarget) return '';
- const matchedRule = normalizeTableRuleList(item?.tableRuleList).find((rule, index) => {
- const targetKey = normalizeFieldMatchText(getTableRuleTargetKey(rule, index));
- const targetLabel = normalizeFieldMatchText(getTableRuleTargetLabel(rule, index));
- return normalizedTarget === targetKey || normalizedTarget === targetLabel;
- });
- return normalizeRecognitionConfigText(matchedRule?.fileColName);
- };
- const buildImportRecognitionBasicInfoFuncRegx = (item?: TreeFileInfo | null) => {
- const source = normalizeRecognitionConfigText(item?.funcRegx);
- if (!source) return '';
- const rows = parseRecognitionBasicInfoRows(source);
- if (!rows.length) return source;
- if (!hasRecognitionBasicInfoFilledContent(rows)) return '';
- const payload = rows.reduce(
- (acc, row) => {
- const targetField = resolveRecognitionBasicInfoTargetField(row.targetField);
- const headerMatch = normalizeRecognitionBasicInfoText(row.headerMatch);
- const endFlag = normalizeRecognitionBasicInfoText(row.endFlag);
- const mappedHeader = resolveMappedHeaderByTargetField(item, targetField);
- const nextKey = headerMatch || mappedHeader || targetField;
- if (!nextKey) return acc;
- acc[nextKey] = [headerMatch, endFlag];
- return acc;
- },
- {} as Record<string, string[]>,
- );
- return Object.keys(payload).length ? JSON.stringify(payload) : '';
- };
- const buildImportFileInfoPayload = (item?: TreeFileInfo | null): TreeFileInfo | null => {
- if (!item) return null;
- const nextItem = cloneDeepSafe(item) as TreeFileInfo;
- const children = Array.isArray(nextItem.children)
- ? nextItem.children
- .map((child) => buildImportFileInfoPayload(child))
- .filter((child): child is TreeFileInfo => Boolean(child))
- : nextItem.children;
- if (isFileRootItem(nextItem)) {
- const rootPayload = {
- ...nextItem,
- children,
- } as TreeFileInfo & { childTemplateName?: string | null };
- delete rootPayload.childTemplateName;
- return rootPayload;
- }
- const childName = normalizeRuleText(nextItem.childTemplateName);
- const hasNew = !!childName || !!nextItem.hasNew;
- const importTemplateId = normalizeTemplateId(
- nextItem.templateId ?? templateDetail.value?.id ?? activeTemplateItem.value?.id ?? selectedTemplateId.value,
- );
- const currentTemplate = resolveTemplateInfoById(importTemplateId);
- const parentTemplateId = normalizeTemplateId(nextItem.mainId) || normalizeTemplateId(currentTemplate?.mainId);
- const parentTemplate = (parentTemplateId && resolveTemplateInfoById(parentTemplateId)) || currentTemplate;
- const currentFuncRegx = normalizeRecognitionConfigText(nextItem.funcRegx);
- const importFuncRegx = currentFuncRegx ? buildImportRecognitionBasicInfoFuncRegx(nextItem) : '';
- const sheetPayload = {
- ...nextItem,
- tableName: hasNew ? childName : nextItem.tableName,
- tableNameEn: hasNew
- ? normalizeRuleText(parentTemplate?.tableNameEn) ||
- normalizeRuleText(currentTemplate?.tableNameEn) ||
- normalizeRuleText(nextItem.tableNameEn) ||
- null
- : nextItem.tableNameEn,
- hasNew,
- classify: hasNew
- ? normalizeRuleText(parentTemplate?.classify) ||
- normalizeRuleText(currentTemplate?.classify) ||
- normalizeRuleText(nextItem.classify) ||
- null
- : nextItem.classify,
- funcRegx: importFuncRegx || null,
- children,
- } as TreeFileInfo & { childTemplateName?: string | null };
- return sheetPayload;
- };
- const importFileInfos = sanitizedFileInfos
- .map((item) => buildImportFileInfoPayload(item))
- .filter((item): item is TreeFileInfo => Boolean(item));
- sessionStorage.setItem(IMPORT_FILE_INFO_STORAGE_KEY, JSON.stringify(importFileInfos));
- resetImportProgressSession();
- await go(PageEnum.BASE_CLEAN_PROGRESS);
- } catch {
- createMessage.error('导入数据失败');
- } finally {
- importingData.value = false;
- }
- };
- const handleBack = () => {
- // 返回上一级:文件上传页;带上来时的来源地址,保证再返回仍能回到原界面(如工作台)
- const redirect = String(pageQuery.value.redirect || '');
- go(redirect ? { path: PageEnum.BASE_UPLOAD, query: { redirect } } : PageEnum.BASE_UPLOAD);
- };
- onMounted(() => {
- initializePage();
- window.addEventListener('mousemove', onResizeMouseMove);
- window.addEventListener('mouseup', stopResize);
- });
- onBeforeUnmount(() => {
- saveCurrentNodeState();
- window.removeEventListener('mousemove', onResizeMouseMove);
- window.removeEventListener('mouseup', stopResize);
- document.body.classList.remove('is-resizing-layout');
- document.body.style.cursor = '';
- });
- // Phase 2:把 Modal 子组件需要的页面级共享状态/方法 provide 下去(子组件内部按名取用)
- provide(CleaningContextKey, {
- DEFAULT_TEMPLATE_CATEGORY,
- FALLBACK_TEMPLATE_CATEGORY,
- activeCategory,
- activeNodeInfo,
- activeNodeKey,
- activeTemplateItem,
- activeTemplateTab,
- addColumnDialogOpen,
- addColumnForm,
- addColumnMode,
- addedColumnConfig,
- addedColumns,
- applyHeaderRow,
- applyPresetHeadersFromNode,
- applyPreviewDataset,
- applyRecognitionAmountZfState,
- applyRecognitionBasicInfoState,
- applyRecognitionDirectionState,
- autoMatchFields,
- availableSourceHeaders,
- caseDataCleaningShared,
- categories,
- childTemplateName,
- cleaningActions,
- cleaningRules,
- closeTemplateModal,
- createMessage,
- currentColumn,
- dragOverContentIndex,
- draggingContentIndex,
- fieldMapping,
- filteredTemplateList,
- fixedTargetFields,
- generatedColumnHeaderRowMap,
- getColumnRuleBadgeState,
- getColumnRuleBadgeTitle,
- getGeneratedColumnHeaderRowIndex,
- getMatchedHeaderStatusClass,
- getMatchedHeaderStatusText,
- getMatchedHeaderStatusTitle,
- groupingColumnsDraft,
- handleSelect,
- hasDuplicateHeaderName,
- headerRowIndex,
- headerRowInput,
- ignoredRowsSet,
- isSystemTemplateRecord,
- isTargetHandledByRecognitionRule,
- leftCollapsed,
- loadPreviewDatasetFromFile,
- loadTemplateDetail,
- loadTemplatePreviewDetail,
- mappingApi,
- mappingPopoverOpenKey,
- mergeTemplateInfo,
- mockGridTemplate,
- modalTemplateDetail,
- modalTemplateItem,
- nodeStateCache,
- openTemplateModal,
- originFilterKeyword,
- recognitionAmountZfEnabled,
- recognitionApi,
- recognitionBasicInfoRows,
- remapGroupingColumnRules,
- remapSelectRuleHeaderRefs,
- resetNodeWorkState,
- rightTableHeaders,
- runRulePipeline,
- saveCurrentNodeState,
- selectRules,
- selectedAddedColumn,
- selectedContentIndex,
- selectedKeys,
- selectedRowIndex,
- selectedSourceField,
- selectedTemplateId,
- setGeneratedColumnCell,
- showTemplateTabSwitch,
- sortMode,
- sourceTableRows,
- splitColumnConfig,
- splitColumnDialogOpen,
- splitColumnForm,
- structureSyncSuspended,
- syncHeaderRowInput,
- syncModalTemplatePreviewState,
- tableMockRef,
- tablePreviewRows,
- templateApi,
- templateDetailCache,
- templateKeyword,
- templateModalOpen,
- templatePreviewLoading,
- templateSearchBaseList,
- templateSource,
- treeData,
- });
- </script>
- <style lang="less">
- // 样式已按域拆分到 ./cleaning/styles/ 下,由此处引入(保持全局,非 scoped)。
- @import './cleaning/styles/index.less';
- </style>
|