feat: 仿真地图库改为从标准场景库选择

This commit is contained in:
JiangSheng
2026-04-01 10:32:23 +08:00
parent 79d6c3c85b
commit 8077727797
9 changed files with 298 additions and 13 deletions

View File

@@ -89,3 +89,11 @@ export const statisticWorkspaceConfidenceApi = (params: { poolName: string }) =>
export const statisticMachineConfidenceApi = (params: { poolName: string }) => {
return get(`${PREFIX}taskpool/statisticMachineConfidence`, params);
};
export const getSimulationPoolTaskApi = (params: {
poolName: string;
version: string;
taskCode: string;
}) => {
return get(`${PREFIX}taskpool/getSimulationPoolTask`, params);
};

View File

@@ -11,6 +11,7 @@
:collapse-tags="multiple"
:collapse-tags-tooltip="multiple"
:fit-input-width="false"
:disabled="disabled"
@change="onChange"
/>
<span v-else class="plain-label">
@@ -66,6 +67,7 @@ interface Props {
modelValue?: any;
clearable?: boolean;
editable?: boolean;
disabled?: boolean;
size?: '' | 'large' | 'default' | 'small';
multiple?: boolean;
}
@@ -74,6 +76,7 @@ const props = withDefaults(defineProps<Props>(), {
modelValue: '',
clearable: false,
editable: true,
disabled: false,
size: 'default',
multiple: true,
});

View File

@@ -11,6 +11,7 @@
:size="size"
:show-all-levels="false"
:placeholder="$t('通用.请选择')"
:disabled="disabled"
collapse-tags
:collapse-tags-tooltip="false"
:max-collapse-tags="1"
@@ -81,6 +82,7 @@ interface Props {
modelValue?: any;
modelName?: string;
editable?: boolean;
disabled?: boolean;
size?: '' | 'large' | 'default' | 'small';
dirType?: number;
}
@@ -89,6 +91,7 @@ const props = withDefaults(defineProps<Props>(), {
modelValue: null,
modelName: '',
editable: true,
disabled: false,
size: 'default',
dirType: DIR_TYPE.KNOWLEDGE,
});

View File

@@ -75,12 +75,12 @@ import {
findNodePath,
extractCategoryNodeTypes,
collectNodeCodes,
generateFakeId,
} from '@/utils/node';
import nodeDetailDialog from './nodeDetailDialog.vue';
import { TASK_CALCULATE_STATUS, TASK_PROCESS_STATUS } from '@/utils/enum/task';
import { disposeTagKey } from '@/views/task/projectDetail/components/project';
import { ElMessage } from 'element-plus';
import { generateFakeId } from '@/utils/node';
import { useI18n } from 'vue-i18n';
import { TABLE_NAME } from '@/utils/enum/tableName';
import { disposeTaskMembers } from '@/utils/task';
@@ -308,12 +308,35 @@ const onNodeDetailConfirmFun = (formData: any) => {
} else {
addRow.pid = checkRowData[0].uuid;
}
const standardSceneChildren = formData._standardSceneChildren || [];
delete addRow._standardSceneChildren;
nextTick(async () => {
await getVxeRef()?.insertAt(addRow, insertPosition);
await nextTick();
if (standardSceneChildren.length > 0) {
for (const child of standardSceneChildren) {
const childRow = {
...child,
parentId: addRow.fakeId,
fakeId: generateFakeId(),
depth: (addRow.depth || 0) + 1,
};
await getVxeRef()?.insertAt(childRow, -1);
}
}
if (checkRowData[0]) {
getVxeRef()?.setTreeExpand(checkRowData[0], true);
}
if (standardSceneChildren.length > 0) {
const { fullData } = getVxeRef()?.getTableData();
const insertedRow = fullData?.find((r: any) => r.fakeId === addRow.fakeId);
if (insertedRow) {
getVxeRef()?.setTreeExpand(insertedRow, true);
}
}
// emits('updateCallback', 'add');
});
} else {

View File

@@ -16,13 +16,7 @@
ref="tableFormRef"
:tableName="localTableName"
v-model:data="formData"
:formAttrs="{
nodeCode: { disabled: formData.nodeType === NODE_TYPE.DISCIPLINE },
englishName: { disabled: formData.nodeType === NODE_TYPE.DISCIPLINE },
nodeType: { disabled: localOperationType === 'edit' },
confidence: { min: 0, max: 100, step: 0.01 },
imageFileId: { multiple: true },
}"
:formAttrs="computedFormAttrs"
:rule-data="ruleData"
:itemNum="localItemNum"
@change="onFormChangeFun"
@@ -31,23 +25,44 @@
<NodeNameMixed
v-model="formData.nodeName"
:nodeType="formData.nodeType"
:editable="true"
editable
:disabled="isAutoFilled"
/>
</template>
<template #form-nodeCode>
<StandardSceneNodeCodeSelect
v-if="enableSelectCode && formData.nodeType === NODE_TYPE.TASK"
ref="sceneSelectRef"
v-model="formData.nodeCode"
:disabled="isAutoFilled && localOperationType === OPERATION_TYPE.EDIT"
@select="onSceneSelectFun"
/>
<el-input
v-else
v-model="formData.nodeCode"
:placeholder="$t('通用.请输入')"
:disabled="computedFormAttrs.nodeCode?.disabled"
/>
</template>
<template #form-flowTemplate>
<FlowTemplateSelect v-model="selectedFlowTemplate" />
<FlowTemplateSelect v-model="selectedFlowTemplate" :disabled="isAutoFilled" />
</template>
<template #form-reportTemplate>
<ReportTemplateSelect v-model="formData.reportTemplate" />
<ReportTemplateSelect v-model="formData.reportTemplate" :disabled="isAutoFilled" />
</template>
<template #form-standard>
<KnowledgeSelect v-model="standard" v-model:modelName="formData.standardName" />
<KnowledgeSelect
v-model="standard"
v-model:modelName="formData.standardName"
:disabled="isAutoFilled"
/>
</template>
<template #form-exceptionFile>
<KnowledgeSelect
v-model="exceptionFile"
v-model:modelName="formData.exceptionFileName"
:dirType="DIR_TYPE.EXCEPTION_CASE"
:disabled="isAutoFilled"
/>
</template>
</TableForm>
@@ -74,6 +89,7 @@ import FlowTemplateSelect from './flowTemplateSelect.vue';
import ReportTemplateSelect from './reportTemplateSelect.vue';
import KnowledgeSelect from './knowledgeSelect.vue';
import NodeNameMixed from './nodeNameMixed.vue';
import StandardSceneNodeCodeSelect from './standardSceneNodeCodeSelect.vue';
import { TABLE_NAME } from '@/utils/enum/tableName';
import { useDict } from '@/utils/useDict';
import { DIR_TYPE } from '@/utils/enum/data.ts';
@@ -98,6 +114,7 @@ interface Props {
belongProject?: boolean;
parentCategoryPath?: string[];
existingNodeCodes?: string[];
enableSelectCode?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
@@ -115,6 +132,7 @@ const props = withDefaults(defineProps<Props>(), {
belongProject: false,
parentCategoryPath: () => [],
existingNodeCodes: () => [],
enableSelectCode: false,
});
const emits = defineEmits(['update:modelValue', 'confirm']);
@@ -137,12 +155,117 @@ const tagSortOrderList = ref<string[]>([]);
const tagNameMap = ref<Map<string, string>>(new Map());
const formData = ref<any>({});
const isAutoFilled = ref(false);
const autoFilledFields = ref<string[]>([]);
const standardSceneChildren = ref<any[]>([]);
const sceneSelectRef = ref<any>(null);
const onSceneSelectFun = (scene: any) => {
if (!scene) {
clearAutoFillFun();
return;
}
const fillFields: string[] = [];
Object.keys(scene).forEach((key) => {
if (['nodeCode', 'children', 'fakeId', 'uuid', 'id', 'parentId'].includes(key)) {
return;
}
if (scene[key] !== null && scene[key] !== undefined && scene[key] !== '') {
formData.value[key] = scene[key];
fillFields.push(key);
}
});
standardSceneChildren.value = Array.isArray(scene.children) ? scene.children : [];
if (fillFields.length > 0) {
autoFilledFields.value = fillFields;
isAutoFilled.value = true;
nextTick(() => {
if (formData.value.standard) {
standard.value = formData.value.standard;
}
if (formData.value.exceptionFile) {
exceptionFile.value = formData.value.exceptionFile;
}
if (formData.value.flowTemplate) {
selectedFlowTemplate.value = formData.value.flowTemplate;
}
});
}
};
const clearAutoFillFun = () => {
autoFilledFields.value.forEach((key) => {
formData.value[key] = null;
});
autoFilledFields.value = [];
isAutoFilled.value = false;
formData.value.nodeCode = '';
standardSceneChildren.value = [];
standard.value = null;
exceptionFile.value = null;
selectedFlowTemplate.value = null;
};
const computedFormAttrs = computed(() => {
const base: any = {
nodeCode: { disabled: formData.value.nodeType === NODE_TYPE.DISCIPLINE || isAutoFilled.value },
englishName: {
disabled: formData.value.nodeType === NODE_TYPE.DISCIPLINE || isAutoFilled.value,
},
nodeType: { disabled: localOperationType.value === 'edit' || isAutoFilled.value },
confidence: { min: 0, max: 100, step: 0.01, disabled: isAutoFilled.value },
imageFileId: { multiple: true, disabled: isAutoFilled.value },
};
if (isAutoFilled.value) {
return new Proxy(base, {
get(target, prop) {
if (prop in target) {
return target[prop];
}
return { disabled: true };
},
});
}
return base;
});
watch(
() => props.modelValue,
(val) => {
if (val) {
if (localOperationType.value === OPERATION_TYPE.ADD) {
autoFilledFields.value = [];
standardSceneChildren.value = [];
const isEditDisabled =
props.enableSelectCode &&
props.operationType === OPERATION_TYPE.EDIT &&
(props.nodeType === NODE_TYPE.TASK || props.nodeType === NODE_TYPE.PERFORMANCE);
if (isEditDisabled) {
isAutoFilled.value = true;
} else if (
props.operationType === OPERATION_TYPE.ADD &&
props.enableSelectCode &&
props.nodeType === NODE_TYPE.TASK
) {
isAutoFilled.value = true;
resetFun();
nextTick(() => {
sceneSelectRef.value?.fetchListFun();
});
} else if (props.operationType === OPERATION_TYPE.ADD) {
isAutoFilled.value = false;
resetFun();
} else {
isAutoFilled.value = false;
}
nextTick(() => {
if (formData.value) {
@@ -225,6 +348,11 @@ const onConfirmFun = async () => {
// submitData.payAttentionMemberIds = submitData.payAttentionMemberList;
// }
disposeTaskMembers('setListToIdsNoTranslate', submitData);
if (isAutoFilled.value && props.enableSelectCode) {
submitData._standardSceneChildren = standardSceneChildren.value;
}
emits('confirm', submitData);
}
};

View File

@@ -6,6 +6,7 @@
:options="disciplineOptions"
:size="size"
:placeholder="$t('通用.请选择')"
:disabled="disabled"
filterable
v-bind="$attrs"
/>
@@ -14,6 +15,7 @@
v-model="localValue"
:size="size"
:placeholder="$t('通用.请输入')"
:disabled="disabled"
v-bind="$attrs"
/>
</template>
@@ -29,6 +31,7 @@ interface Props {
modelValue?: string;
nodeType?: string;
editable?: boolean;
disabled?: boolean;
size?: '' | 'large' | 'default' | 'small';
}
@@ -36,6 +39,7 @@ const props = withDefaults(defineProps<Props>(), {
modelValue: '',
nodeType: '',
editable: false,
disabled: false,
size: 'default',
});

View File

@@ -11,6 +11,7 @@
:collapse-tags="multiple"
:collapse-tags-tooltip="multiple"
:fit-input-width="false"
:disabled="disabled"
@change="onChange"
/>
<span v-else class="plain-label">
@@ -67,6 +68,7 @@ interface Props {
modelValue?: any;
clearable?: boolean;
editable?: boolean;
disabled?: boolean;
size?: '' | 'large' | 'default' | 'small';
multiple?: boolean;
}
@@ -75,6 +77,7 @@ const props = withDefaults(defineProps<Props>(), {
modelValue: '',
clearable: false,
editable: true,
disabled: false,
size: 'default',
multiple: true,
});

View File

@@ -0,0 +1,106 @@
<template>
<el-select
v-model="selected"
:placeholder="$t('通用.请选择')"
:disabled="disabled"
:loading="loading"
filterable
clearable
class="standard-scene-node-code-select"
@change="onChangeFun"
>
<el-option
v-for="item in sceneList"
:key="item.nodeCode"
:label="item.nodeCode"
:value="item.nodeCode"
/>
</el-select>
</template>
<script setup lang="ts">
import { ref, computed } from 'vue';
import { getTaskPoolVersionsApi, getTaskPoolApi } from '@/api/task/taskpool';
import { STANDARD_SCENE_POOL_NAME, transformPoolNodesToTree } from '@/utils/node';
import { NODE_TYPE } from '@/utils/enum/node';
interface Props {
modelValue?: string;
disabled?: boolean;
}
const props = withDefaults(defineProps<Props>(), {
modelValue: '',
disabled: false,
});
const emits = defineEmits(['update:modelValue', 'select']);
const selected = computed({
get: () => props.modelValue,
set: (val) => emits('update:modelValue', val),
});
const loading = ref(false);
const sceneList = ref<any[]>([]);
const fetchListFun = async () => {
if (sceneList.value.length > 0) {
return;
}
loading.value = true;
try {
const versionRes: any = await getTaskPoolVersionsApi({ poolName: STANDARD_SCENE_POOL_NAME });
if (
versionRes?.code !== 200 ||
!Array.isArray(versionRes.data) ||
versionRes.data.length === 0
) {
return;
}
const latestVersion = versionRes.data[0].poolVersion;
const res: any = await getTaskPoolApi({
poolName: STANDARD_SCENE_POOL_NAME,
version: latestVersion,
});
if (res?.code === 200 && res.data?.nodes) {
const tree = transformPoolNodesToTree(res.data.nodes);
const tasks: any[] = [];
const walkTree = (nodes: any[]) => {
nodes.forEach((node: any) => {
if (node.nodeType === NODE_TYPE.TASK) {
tasks.push(node);
}
if (Array.isArray(node.children) && node.children.length > 0) {
walkTree(node.children);
}
});
};
walkTree(tree);
sceneList.value = tasks;
}
} catch (error) {
console.error('获取标准场景库列表失败', error);
} finally {
loading.value = false;
}
};
const onChangeFun = (nodeCode: string) => {
if (!nodeCode) {
emits('select', null);
return;
}
const scene = sceneList.value.find((item: any) => item.nodeCode === nodeCode);
emits('select', scene || null);
};
defineExpose({ fetchListFun });
</script>
<style lang="scss" scoped>
.standard-scene-node-code-select {
width: 100%;
}
</style>

View File

@@ -69,6 +69,7 @@
:data="tableData"
:loading="loading"
:editMode="true"
:enableSelectCode="enableSelectCode"
>
<template #otherOptions>
<el-tooltip :content="$t('工况库.导入Excel')" placement="top">
@@ -255,6 +256,7 @@ import { TABLE_NAME } from '@/utils/enum/tableName';
import { CommonStore } from '@/stores/common';
import { useTaskStore, getEarlyPoolData } from '@/stores/taskPool';
import { jumpPage } from '@/utils/common';
import { enableConfigByTenant, TENANT_ENUM } from '@/tenants/tenant';
const commonStore = CommonStore();
const taskStore = useTaskStore();
@@ -274,6 +276,11 @@ const emit = defineEmits<{
const { t } = useI18n();
const tenantId = getUserTenantId();
const loading = ref(false);
const enableSelectCode = computed(() => {
return props.pageType === 'loadcase' && enableConfigByTenant([TENANT_ENUM.LYRIC]);
});
let originalSnapshot: any = null;
const tableData = ref<any>([]);