优化混炼示方,新增种类配置

This commit is contained in:
geht
2026-05-25 19:44:14 +08:00
parent c85657d199
commit dc3f305303
34 changed files with 3892 additions and 104 deletions

View File

@@ -1,16 +1,78 @@
import { ref } from 'vue';
import { defHttp } from '/@/utils/http/axios';
import { getChildListBatch, loadTreeData } from './category.api';
/** MES 物料分类 - 原辅材料编码 */
/** MES 物料分类编码 */
export const MATERIAL_ROOT_CODE = 'XSLMES_MATERIAL';
/** MES 物料分类 - 原辅材料编码(兼容旧逻辑) */
export const MATERIAL_RAW_AUX_CODE = 'XSLMES_MATERIAL_RAW_AUX';
/** 原辅材料分类节点 ID运行时加载 */
/** 原辅材料分类节点 ID运行时加载,兼容旧逻辑 */
export const materialRawAuxCategoryId = ref('');
/** 是否为原辅材料的直接子类 */
/** MES 物料分类根节点 ID运行时加载 */
export const materialRootCategoryId = ref('');
/** MES 物料大类节点 ID 集合(运行时加载) */
export const materialMajorCategoryIds = ref<Set<string>>(new Set());
let materialCategoryContextLoading: Promise<void> | null = null;
/** 加载 MES 物料分类上下文(根节点 + 大类 ID */
export async function ensureMaterialCategoryContext(force = false) {
if (!force && materialRootCategoryId.value && materialMajorCategoryIds.value.size > 0) {
return;
}
if (materialCategoryContextLoading) {
await materialCategoryContextLoading;
return;
}
materialCategoryContextLoading = (async () => {
const rootRes = await defHttp.get(
{ url: '/sys/category/loadOne', params: { field: 'code', val: MATERIAL_ROOT_CODE } },
{ isTransformResponse: false },
);
if (rootRes?.success && rootRes?.result?.id) {
materialRootCategoryId.value = String(rootRes.result.id);
}
const auxRes = await defHttp.get(
{ url: '/sys/category/loadOne', params: { field: 'code', val: MATERIAL_RAW_AUX_CODE } },
{ isTransformResponse: false },
);
if (auxRes?.success && auxRes?.result?.id) {
materialRawAuxCategoryId.value = String(auxRes.result.id);
}
const majors = await loadTreeData({ async: false, pcode: MATERIAL_ROOT_CODE });
const majorIds = new Set<string>();
(Array.isArray(majors) ? majors : []).forEach((node) => {
const nodeKey = node?.key ?? node?.value ?? node?.id;
if (nodeKey != null) {
majorIds.add(String(nodeKey));
}
});
materialMajorCategoryIds.value = majorIds;
})();
try {
await materialCategoryContextLoading;
} finally {
materialCategoryContextLoading = null;
}
}
/** 是否为原辅材料的直接子类(兼容旧逻辑) */
export function isMaterialRawAuxSubCategory(pid?: string) {
return !!materialRawAuxCategoryId.value && pid === materialRawAuxCategoryId.value;
}
/** 是否为 MES 物料小类(父节点为物料大类) */
export function isMaterialMinorCategory(pid?: string) {
if (!pid) {
return false;
}
return materialMajorCategoryIds.value.has(String(pid));
}
/** 表单 Checkbox 布尔值 -> 数据库存储值 */
export function toIsRubberFlag(value: unknown) {
return value === true || value === '1' ? '1' : '0';
@@ -20,3 +82,206 @@ export function toIsRubberFlag(value: unknown) {
export function fromIsRubberFlag(value: unknown) {
return value === '1' || value === 1 || value === true;
}
//update-begin---author:cursor ---date:20260525 for【XSLMES-20260525-A50】统一加载MES物料分类树大类+小类)-----------
export interface MesMaterialCategoryMinorItem {
id: string;
name: string;
majorId: string;
majorName: string;
label: string;
}
export interface MesMaterialCategoryMajorItem {
id: string;
name: string;
minors: MesMaterialCategoryMinorItem[];
}
export interface MesMaterialCategoryTreeLoadResult {
majors: MesMaterialCategoryMajorItem[];
minors: MesMaterialCategoryMinorItem[];
treeNodes: Recordable[];
}
function normalizeCategoryNodeKey(node: Recordable): string {
const key = node?.key ?? node?.value ?? node?.id;
return key != null && String(key) !== '' ? String(key) : '';
}
function normalizeCategoryNodeTitle(node: Recordable): string {
return String(node?.title ?? node?.name ?? '');
}
function normalizeTreeSelectNodes(nodes: unknown): Recordable[] {
if (Array.isArray(nodes)) {
return nodes as Recordable[];
}
if (nodes && typeof nodes === 'object') {
const payload = nodes as Recordable;
if (Array.isArray(payload.result)) {
return payload.result;
}
if (Array.isArray(payload.records)) {
return payload.records;
}
}
return [];
}
/** 加载 MES 物料分类树:根下物料大类 + 各物料小类(供密炼物料/混炼示方选料复用) */
export async function loadMesMaterialCategoryTreeData(): Promise<MesMaterialCategoryTreeLoadResult> {
// 优先 loadTreeRoot与密炼物料列表页一致已验证可用
try {
const treeRes = await loadTreeData({ async: false, pcode: MATERIAL_ROOT_CODE });
const treeResult = buildMesMaterialCategoryTreeFromTreeSelect(normalizeTreeSelectNodes(treeRes));
if (treeResult.minors.length) {
if (!materialRootCategoryId.value) {
await ensureMaterialCategoryContext();
}
return treeResult;
}
} catch {
// 继续走 batch 兜底
}
let rootId = materialRootCategoryId.value;
if (!rootId) {
try {
const root = await defHttp.get<Recordable>({
url: '/sys/category/loadOne',
params: { field: 'code', val: MATERIAL_ROOT_CODE },
});
if (root?.id) {
rootId = String(root.id);
materialRootCategoryId.value = rootId;
}
} catch {
rootId = '';
}
}
if (!rootId) {
return { majors: [], minors: [], treeNodes: [] };
}
let majorRecords: Recordable[] = [];
let minorRecords: Recordable[] = [];
try {
const majorBatch = await getChildListBatch({ parentIds: rootId });
if (majorBatch?.success === false) {
return { majors: [], minors: [], treeNodes: [] };
}
majorRecords = (majorBatch?.result?.records || majorBatch?.records || []) as Recordable[];
if (majorRecords.length) {
const majorIds = majorRecords.map((item) => String(item.id)).filter(Boolean).join(',');
if (majorIds) {
const minorBatch = await getChildListBatch({ parentIds: majorIds });
if (minorBatch?.success !== false) {
minorRecords = (minorBatch?.result?.records || minorBatch?.records || []) as Recordable[];
}
}
}
} catch {
return { majors: [], minors: [], treeNodes: [] };
}
const result = buildMesMaterialCategoryTreeFromRecords(majorRecords, minorRecords);
materialMajorCategoryIds.value = new Set(result.majors.map((item) => item.id));
return result;
}
function buildMesMaterialCategoryTreeFromRecords(majorRecords: Recordable[], minorRecords: Recordable[]) {
const majorMap = new Map<string, { id: string; name: string; minors: MesMaterialCategoryMinorItem[] }>();
majorRecords.forEach((record) => {
const majorId = normalizeCategoryNodeKey(record);
if (!majorId) {
return;
}
majorMap.set(majorId, {
id: majorId,
name: normalizeCategoryNodeTitle(record),
minors: [],
});
});
minorRecords.forEach((record) => {
const majorId = record?.pid != null ? String(record.pid) : '';
const minorId = normalizeCategoryNodeKey(record);
const major = majorMap.get(majorId);
if (!major || !minorId) {
return;
}
major.minors.push({
id: minorId,
name: normalizeCategoryNodeTitle(record),
majorId: major.id,
majorName: major.name,
label: major.name && normalizeCategoryNodeTitle(record)
? `${major.name} / ${normalizeCategoryNodeTitle(record)}`
: normalizeCategoryNodeTitle(record) || major.name,
});
});
const majors: MesMaterialCategoryMajorItem[] = [];
const minors: MesMaterialCategoryMinorItem[] = [];
const treeNodes: Recordable[] = [];
majorMap.forEach((major) => {
if (!major.minors.length) {
return;
}
majors.push({ id: major.id, name: major.name, minors: major.minors });
minors.push(...major.minors);
treeNodes.push({
key: major.id,
title: major.name,
children: major.minors.map((minor) => ({ key: minor.id, title: minor.name })),
});
});
return { majors, minors, treeNodes };
}
function buildMesMaterialCategoryTreeFromTreeSelect(majorRaw: Recordable[]) {
const majors: MesMaterialCategoryMajorItem[] = [];
const minors: MesMaterialCategoryMinorItem[] = [];
const treeNodes: Recordable[] = [];
majorRaw.forEach((major) => {
const majorId = normalizeCategoryNodeKey(major);
const majorName = normalizeCategoryNodeTitle(major);
if (!majorId) {
return;
}
const majorMinors: MesMaterialCategoryMinorItem[] = [];
(Array.isArray(major.children) ? major.children : []).forEach((child) => {
const minorId = normalizeCategoryNodeKey(child);
const minorName = normalizeCategoryNodeTitle(child);
if (!minorId) {
return;
}
const item: MesMaterialCategoryMinorItem = {
id: minorId,
name: minorName,
majorId,
majorName,
label: majorName && minorName ? `${majorName} / ${minorName}` : minorName || majorName,
};
majorMinors.push(item);
minors.push(item);
});
if (!majorMinors.length) {
return;
}
majors.push({ id: majorId, name: majorName, minors: majorMinors });
treeNodes.push({
key: majorId,
title: majorName,
children: majorMinors.map((minor) => ({ key: minor.id, title: minor.name })),
});
});
materialMajorCategoryIds.value = new Set(majors.map((item) => item.id));
return { majors, minors, treeNodes };
}
//update-end---author:cursor ---date:20260525 for【XSLMES-20260525-A50】统一加载MES物料分类树大类+小类)-----------

View File

@@ -1,6 +1,6 @@
import { BasicColumn } from '/@/components/Table';
import { FormSchema } from '/@/components/Table';
import { isMaterialRawAuxSubCategory } from './category.constants';
import { isMaterialMinorCategory } from './category.constants';
export const columns: BasicColumn[] = [
{
@@ -81,6 +81,6 @@ export const formSchema: FormSchema[] = [
defaultValue: false,
renderComponentContent: '胶料',
colProps: { span: 24 },
show: ({ values }) => isMaterialRawAuxSubCategory(values.pid),
show: ({ values }) => isMaterialMinorCategory(values.pid),
},
];

View File

@@ -13,7 +13,8 @@
import {
MATERIAL_RAW_AUX_CODE,
materialRawAuxCategoryId,
isMaterialRawAuxSubCategory,
ensureMaterialCategoryContext,
isMaterialMinorCategory,
toIsRubberFlag,
fromIsRubberFlag,
} from '../category.constants';
@@ -40,7 +41,7 @@
function normalizeSubmitValues(values: Recordable) {
const payload = { ...values };
payload.isRubber = isMaterialRawAuxSubCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
payload.isRubber = isMaterialMinorCategory(payload.pid) ? toIsRubberFlag(payload.isRubber) : '0';
return payload;
}
//表单配置
@@ -64,6 +65,7 @@
setModalProps({ confirmLoading: false, minHeight: 80 });
isUpdate.value = !!data?.isUpdate;
await ensureMaterialRawAuxCategoryId();
await ensureMaterialCategoryContext();
// 代码逻辑说明: 分类字典data.record为空报错------------
isSubAdd.value = !data?.isUpdate && data.record && data.record.id;
if (data?.record) {