|
@@ -152,6 +152,24 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
|
|
|
+ <el-table-column :label="t('mainPlan.mainStatus')" align="center" width="140">
|
|
|
+ <template #default="scope">
|
|
|
+ <div class="status-container">
|
|
|
+ <el-switch
|
|
|
+ v-model="scope.row.status"
|
|
|
+ :active-value="1"
|
|
|
+ :inactive-value="0"
|
|
|
+ :disabled="scope.row.initialStatus === 1"
|
|
|
+ @change="handleStatusChange(scope.row)"
|
|
|
+ class="status-switch"
|
|
|
+ />
|
|
|
+ <span class="status-text">
|
|
|
+ {{ getStatusText(scope.row) }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+
|
|
|
<el-table-column :label="t('main.mileage')" key="mileageRule" align="center"
|
|
|
:width="columnWidths.mileageRule" v-if="false">
|
|
|
<template #default="scope">
|
|
@@ -294,11 +312,6 @@
|
|
|
</el-table-column>
|
|
|
</el-table-column>
|
|
|
|
|
|
- <el-table-column :label="t('common.status')" align="center" width="100">
|
|
|
- <template #default="scope">
|
|
|
- {{ getStatusText(scope.row) }}
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
<!--
|
|
|
<el-table-column :label="t('iotMaintain.numberOfMaterials')" align="center" width="90">
|
|
|
<template #default="scope">
|
|
@@ -403,7 +416,7 @@
|
|
|
type="primary"
|
|
|
:icon="Box"
|
|
|
@click="handleSelectMaterial"
|
|
|
- :disabled="!currentBomItem"
|
|
|
+ :disabled="!currentBomItem || isButtonsDisabled"
|
|
|
>
|
|
|
{{ t('stock.selectMaterial') }}
|
|
|
</el-button>
|
|
@@ -420,7 +433,7 @@
|
|
|
type="primary"
|
|
|
:icon="Plus"
|
|
|
@click="handleAddMaterial"
|
|
|
- :disabled="!currentBomItem"
|
|
|
+ :disabled="!currentBomItem || isButtonsDisabled"
|
|
|
>
|
|
|
新增物料
|
|
|
</el-button>
|
|
@@ -468,7 +481,7 @@
|
|
|
placeholder="请输入物料名称(必填)"
|
|
|
style="width: 100%"
|
|
|
:class="{ 'is-required-input': row.isNew && !row.materialName?.trim() }"
|
|
|
- :disabled="isMaterialDisabled(row)"
|
|
|
+ :disabled="isMaterialDisabled(row)"
|
|
|
/>
|
|
|
</el-tooltip>
|
|
|
<!-- 非新增行:正常显示 -->
|
|
@@ -904,8 +917,13 @@ const displayedMaterialList = computed(() => {
|
|
|
const isMaterialDisabled = (material) => {
|
|
|
// 根据物料的bomNodeId找到对应的保养项
|
|
|
const bomItem = list.value.find(item => item.bomNodeId === material.bomNodeId);
|
|
|
- // 如果保养项存在且rule=1(不消耗物料),则禁用
|
|
|
- return bomItem && bomItem.rule === 1;
|
|
|
+ // 如果保养项不存在,默认不禁用
|
|
|
+ if (!bomItem) return false;
|
|
|
+ // 如果保养项状态为已完成(status=1),则禁用该物料
|
|
|
+ if (bomItem.status === 1) return true;
|
|
|
+ // 如果保养项rule=1(不消耗物料),则禁用
|
|
|
+ if (bomItem.rule === 1) return true;
|
|
|
+ return false;
|
|
|
};
|
|
|
|
|
|
// 物料表格行类名 - 用于设置灰色背景
|
|
@@ -919,15 +937,27 @@ const getMaterialRowClassName = ({ row }) => {
|
|
|
return '';
|
|
|
};
|
|
|
|
|
|
+// 计算属性:判断按钮是否应该禁用
|
|
|
+const isButtonsDisabled = computed(() => {
|
|
|
+ if (!currentBomItem.value) return true;
|
|
|
+
|
|
|
+ // 条件1:保养状态为已完成(status=1)
|
|
|
+ if (currentBomItem.value.status === 1) return true;
|
|
|
+
|
|
|
+ // 条件2:消耗物料字段为不消耗物料(rule=1)
|
|
|
+ if (currentBomItem.value.rule === 1) return true;
|
|
|
+
|
|
|
+ return false;
|
|
|
+});
|
|
|
+
|
|
|
// 切换显示所有物料/当前保养项物料
|
|
|
const toggleShowAllMaterials = () => {
|
|
|
showAllMaterials.value = !showAllMaterials.value;
|
|
|
|
|
|
// 切换显示模式时,需要同步更新 materialList
|
|
|
if (!showAllMaterials.value && currentBomItem.value) {
|
|
|
- // 切换到显示当前保养项物料
|
|
|
- const uniqueKey = `${currentBomItem.value.deviceId}${currentBomItem.value.bomNodeId}`;
|
|
|
- materialList.value = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+ // 切换到显示当前保养项物料时,使用新的刷新方法
|
|
|
+ refreshCurrentBomItemMaterials();
|
|
|
} else if (showAllMaterials.value) {
|
|
|
// 切换到显示所有物料
|
|
|
const allMaterials = [];
|
|
@@ -936,6 +966,10 @@ const toggleShowAllMaterials = () => {
|
|
|
}
|
|
|
materialList.value = allMaterials;
|
|
|
}
|
|
|
+ console.log('切换显示模式:', {
|
|
|
+ showAllMaterials: showAllMaterials.value,
|
|
|
+ materialCount: materialList.value.length
|
|
|
+ });
|
|
|
};
|
|
|
|
|
|
// 为表格行添加类名,实现高亮效果
|
|
@@ -1228,6 +1262,43 @@ const handleSizeChange = (newSize: number) => {
|
|
|
currentPage.value = 1 // 重置到第一页
|
|
|
}
|
|
|
|
|
|
+// 处理状态变化
|
|
|
+const handleStatusChange = (row: IotMainWorkOrderBomVO) => {
|
|
|
+ // 如果是从未完成(0)切换到完成(1)
|
|
|
+ if (row.status === 1 && row.initialStatus === 0) {
|
|
|
+ // 检查消耗物料规则:如果设置为不消耗物料(rule=1),则跳过物料校验
|
|
|
+ if (row.rule === 1) {
|
|
|
+ console.log(`保养项 ${row.name} 设置为不消耗物料,跳过物料校验`);
|
|
|
+ message.success(`${row.deviceCode}-${formatMaintItemName(row.name)} 已标记为完成`);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 校验物料数据
|
|
|
+ const isValid = validateBomItemMaterials(row.bomNodeId);
|
|
|
+
|
|
|
+ if (!isValid) {
|
|
|
+ // 获取无效物料信息用于提示
|
|
|
+ const invalidMaterials = getInvalidMaterialsInfo(row.bomNodeId);
|
|
|
+ const errorMessage = `请填写物料必填项\n无效物料:\n${invalidMaterials.join('\n')}`;
|
|
|
+
|
|
|
+ message.error(errorMessage);
|
|
|
+
|
|
|
+ // 重置状态为未完成
|
|
|
+ row.status = 0;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`保养项 ${row.name} 状态改为: ${row.status === 1 ? '完成' : '未完成'}`);
|
|
|
+
|
|
|
+ // 这里可以添加状态变化后的业务逻辑
|
|
|
+ // 例如:如果状态变为完成,可以自动填充完成时间等
|
|
|
+ if (row.status === 1) {
|
|
|
+ // 保养完成时的逻辑
|
|
|
+ message.success(`${row.deviceCode}-${formatMaintItemName(row.name)} 已标记为完成`);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
// 运行时间周期 全局校验方法(在submitForm中调用)
|
|
|
const validateAllRunningTimes = (): boolean => {
|
|
|
let isValid = true;
|
|
@@ -1277,11 +1348,11 @@ const handleAddMaterial = () => {
|
|
|
|
|
|
// 临时标识(用于区分新记录)
|
|
|
isNew: true,
|
|
|
- tempId: Date.now() // 临时ID用于唯一标识
|
|
|
+ tempId: Date.now() + Math.random() // 临时ID用于唯一标识
|
|
|
};
|
|
|
|
|
|
// 生成唯一键
|
|
|
- const uniqueKey = `${currentBomItem.value.deviceId}${currentBomItem.value.bomNodeId}`;
|
|
|
+ const uniqueKey = `${currentBomItem.value.bomNodeId}`;
|
|
|
|
|
|
// 更新 bomMaterialsMap
|
|
|
if (!bomMaterialsMap.value[uniqueKey]) {
|
|
@@ -1322,20 +1393,56 @@ const openDeviceBomMaterials = (row: any) => {
|
|
|
deviceBomMaterialsRef.value.open(formData.value.deptId, bomNodeId.value, row, type)
|
|
|
}
|
|
|
|
|
|
+// 刷新当前保养项的物料列表显示
|
|
|
+const refreshCurrentBomItemMaterials = () => {
|
|
|
+ if (!currentBomItem.value) {
|
|
|
+ materialList.value = [];
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const uniqueKey = `${currentBomItem.value.bomNodeId}`;
|
|
|
+
|
|
|
+ // 确保从映射中获取最新数据
|
|
|
+ const currentMaterials = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+
|
|
|
+ // 强制更新 materialList 的引用,确保响应式更新
|
|
|
+ materialList.value = [...currentMaterials];
|
|
|
+
|
|
|
+ console.log('刷新物料列表:', {
|
|
|
+ currentBomItem: currentBomItem.value.name,
|
|
|
+ materialCount: materialList.value.length,
|
|
|
+ uniqueKey: uniqueKey,
|
|
|
+ showAllMaterials: showAllMaterials.value
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
const selectChoose = (selectedMaterial) => {
|
|
|
+ // 检查当前是否有选中的保养项
|
|
|
+ if (!currentBomItem.value) {
|
|
|
+ message.warning('请先选择一个保养项');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const targetBomNodeId = currentBomItem.value.bomNodeId;
|
|
|
+ const targetDeviceId = currentBomItem.value.deviceId;
|
|
|
+
|
|
|
selectedMaterial.bomNodeId = bomNodeId.value
|
|
|
// 关联 bomNodeId
|
|
|
const processedMaterials = selectedMaterial.map(material => ({
|
|
|
...material,
|
|
|
- bomNodeId: bomNodeId.value // 统一关联当前行的 bomNodeId
|
|
|
+ bomNodeId: targetBomNodeId, // 统一关联当前行的 bomNodeId
|
|
|
+ deviceId: targetDeviceId // 确保 deviceId 也正确关联
|
|
|
}));
|
|
|
|
|
|
+ // 生成唯一键
|
|
|
+ const uniqueKey = `${targetBomNodeId}`;
|
|
|
+ // 初始化物料映射(如果不存在)
|
|
|
+ if (!bomMaterialsMap.value[uniqueKey]) {
|
|
|
+ bomMaterialsMap.value[uniqueKey] = [];
|
|
|
+ }
|
|
|
+
|
|
|
// 避免重复添加
|
|
|
processedMaterials.forEach(newMaterial => {
|
|
|
- const uniqueKey = `${newMaterial.deviceId}${bomNodeId.value}`;
|
|
|
- if (!bomMaterialsMap.value[uniqueKey]) {
|
|
|
- bomMaterialsMap.value[uniqueKey] = [];
|
|
|
- }
|
|
|
// 检查是否已存在相同 工厂+成本中心+库存地点+bomNodeId + materialCode 的条目
|
|
|
const isExist = bomMaterialsMap.value[uniqueKey].some(item =>
|
|
|
item.bomNodeId === bomNodeId.value &&
|
|
@@ -1350,10 +1457,8 @@ const selectChoose = (selectedMaterial) => {
|
|
|
}
|
|
|
});
|
|
|
|
|
|
- // 同步到当前显示的 materialList(如果当前选中了该保养项)
|
|
|
- if (currentBomItem.value && currentBomItem.value.bomNodeId === bomNodeId.value) {
|
|
|
- materialList.value = bomMaterialsMap.value[`${currentBomItem.value.deviceId}${bomNodeId.value}`] || [];
|
|
|
- }
|
|
|
+ // 关键修复:立即更新当前显示的物料列表
|
|
|
+ refreshCurrentBomItemMaterials();
|
|
|
|
|
|
// 选择物料后立即计算费用
|
|
|
nextTick(() => {
|
|
@@ -1514,51 +1619,115 @@ const queryParams = reactive({
|
|
|
|
|
|
// 获取指定bomNodeId的物料数量
|
|
|
const getMaterialCount = (bomNodeId: number) => {
|
|
|
- console.log('当前BOM节点:' + bomNodeId)
|
|
|
- return materialList.value.filter(item => item.bomNodeId === bomNodeId).length
|
|
|
-}
|
|
|
+ const uniqueKey = `${bomNodeId}`;
|
|
|
+ const materials = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+
|
|
|
+ // 只计算有效的物料数量
|
|
|
+ return materials.filter(material =>
|
|
|
+ !isMaterialDisabled(material) &&
|
|
|
+ material.materialName &&
|
|
|
+ material.unit &&
|
|
|
+ (material.unitPrice || 0) > 0 &&
|
|
|
+ (material.quantity || 0) > 0
|
|
|
+ ).length;
|
|
|
+}
|
|
|
+
|
|
|
+// 校验保养项下物料数据的方法
|
|
|
+const validateBomItemMaterials = (bomNodeId: number): boolean => {
|
|
|
+ const uniqueKey = `${bomNodeId}`;
|
|
|
+ const materials = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+
|
|
|
+ // 如果没有物料数据,直接返回true(允许完成)
|
|
|
+ if (materials.length === 0) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查所有物料的单价和消耗数量是否都大于0
|
|
|
+ const hasInvalidMaterial = materials.some(material => {
|
|
|
+ const unitPrice = Number(material.unitPrice) || 0;
|
|
|
+ const quantity = Number(material.quantity) || 0;
|
|
|
+
|
|
|
+ return unitPrice <= 0 || quantity <= 0;
|
|
|
+ });
|
|
|
+
|
|
|
+ return !hasInvalidMaterial;
|
|
|
+};
|
|
|
+
|
|
|
+// 获取校验失败的物料信息(用于错误提示)
|
|
|
+const getInvalidMaterialsInfo = (bomNodeId: number): string[] => {
|
|
|
+ const uniqueKey = `${bomNodeId}`;
|
|
|
+ const materials = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+ const invalidMaterials = [];
|
|
|
+
|
|
|
+ materials.forEach(material => {
|
|
|
+ const unitPrice = Number(material.unitPrice) || 0;
|
|
|
+ const quantity = Number(material.quantity) || 0;
|
|
|
+
|
|
|
+ if (unitPrice <= 0 || quantity <= 0) {
|
|
|
+ invalidMaterials.push(`${material.materialName || material.materialCode} (单价: ${unitPrice}, 数量: ${quantity})`);
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return invalidMaterials;
|
|
|
+};
|
|
|
|
|
|
-// 删除物料(在表格中)
|
|
|
const handleDeleteMaterialInTable = (material) => {
|
|
|
// 确认删除对话框
|
|
|
message.confirm('确认删除该物料吗?', { title: '提示' }).then(() => {
|
|
|
const targetBomNodeId = material.bomNodeId;
|
|
|
- const uniqueKey = `${material.deviceId}${targetBomNodeId}`;
|
|
|
+
|
|
|
+ // 修复:统一使用 bomNodeId 作为键(与新增物料保持一致)
|
|
|
+ const uniqueKey = `${targetBomNodeId}`;
|
|
|
+
|
|
|
+ console.log('删除物料调试信息:', {
|
|
|
+ material,
|
|
|
+ uniqueKey,
|
|
|
+ bomMaterialsMapKeys: Object.keys(bomMaterialsMap.value),
|
|
|
+ currentMaterials: bomMaterialsMap.value[uniqueKey] || []
|
|
|
+ });
|
|
|
|
|
|
// 从 bomMaterialsMap 中删除
|
|
|
if (bomMaterialsMap.value[uniqueKey]) {
|
|
|
- const mapIndex = bomMaterialsMap.value[uniqueKey].findIndex(item =>
|
|
|
- (item.isNew && item.tempId === material.tempId) ||
|
|
|
- (!item.isNew &&
|
|
|
+ let mapIndex = -1;
|
|
|
+
|
|
|
+ // 简化查找逻辑
|
|
|
+ if (material.isNew && material.tempId) {
|
|
|
+ // 新增物料:通过 tempId 查找
|
|
|
+ mapIndex = bomMaterialsMap.value[uniqueKey].findIndex(item =>
|
|
|
+ item.isNew && item.tempId === material.tempId
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ // 已有物料:通过关键字段查找
|
|
|
+ mapIndex = bomMaterialsMap.value[uniqueKey].findIndex(item =>
|
|
|
+ !item.isNew &&
|
|
|
item.factoryId === material.factoryId &&
|
|
|
item.costCenterId === material.costCenterId &&
|
|
|
item.storageLocationId === material.storageLocationId &&
|
|
|
- item.materialCode === material.materialCode)
|
|
|
- );
|
|
|
+ item.materialCode === material.materialCode
|
|
|
+ );
|
|
|
+ }
|
|
|
|
|
|
if (mapIndex !== -1) {
|
|
|
+ // 执行删除
|
|
|
bomMaterialsMap.value[uniqueKey].splice(mapIndex, 1);
|
|
|
- }
|
|
|
- }
|
|
|
+ message.success('物料删除成功');
|
|
|
|
|
|
- // 从 materialList 中删除
|
|
|
- const index = materialList.value.findIndex(item =>
|
|
|
- (item.isNew && item.tempId === material.tempId) ||
|
|
|
- (!item.isNew &&
|
|
|
- item.bomNodeId === targetBomNodeId &&
|
|
|
- item.factoryId === material.factoryId &&
|
|
|
- item.costCenterId === material.costCenterId &&
|
|
|
- item.storageLocationId === material.storageLocationId &&
|
|
|
- item.materialCode === material.materialCode)
|
|
|
- );
|
|
|
+ console.log('删除成功后的物料列表:', bomMaterialsMap.value[uniqueKey]);
|
|
|
|
|
|
- if (index !== -1) {
|
|
|
- materialList.value.splice(index, 1);
|
|
|
+ // 关键修复:立即更新当前显示的物料列表
|
|
|
+ refreshCurrentBomItemMaterials();
|
|
|
+
|
|
|
+ // 删除物料后重新计算费用
|
|
|
+ nextTick(() => {
|
|
|
+ calculateTotalCost();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ message.warning('未找到要删除的物料记录');
|
|
|
+ console.error('未找到匹配的物料记录:', material);
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ message.warning('未找到对应的物料列表');
|
|
|
}
|
|
|
- // 删除物料后重新计算费用
|
|
|
- nextTick(() => {
|
|
|
- calculateTotalCost();
|
|
|
- });
|
|
|
}).catch(() => {
|
|
|
// 用户取消删除
|
|
|
});
|
|
@@ -1604,6 +1773,38 @@ const submitForm = async () => {
|
|
|
return; // 校验失败则终止提交
|
|
|
}
|
|
|
|
|
|
+ // 校验所有设置为完成状态的保养项的物料数据
|
|
|
+ const invalidBomItems = [];
|
|
|
+
|
|
|
+ list.value.forEach(row => {
|
|
|
+ // 只校验状态为完成(1)且消耗物料规则为消耗(0)的保养项
|
|
|
+ if (row.status === 1 && row.rule === '0') {
|
|
|
+ const isValid = validateBomItemMaterials(row.bomNodeId);
|
|
|
+ if (!isValid) {
|
|
|
+ invalidBomItems.push({
|
|
|
+ name: `${row.deviceCode}-${formatMaintItemName(row.name)}`,
|
|
|
+ invalidMaterials: getInvalidMaterialsInfo(row.bomNodeId)
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 如果有校验失败的保养项,显示错误信息并终止提交
|
|
|
+ if (invalidBomItems.length > 0) {
|
|
|
+ let errorMessage = '请填写物料必填项\n\n';
|
|
|
+
|
|
|
+ invalidBomItems.forEach((item, index) => {
|
|
|
+ errorMessage += `${index + 1}. ${item.name}:\n`;
|
|
|
+ item.invalidMaterials.forEach(material => {
|
|
|
+ errorMessage += ` - ${material}\n`;
|
|
|
+ });
|
|
|
+ errorMessage += '\n';
|
|
|
+ });
|
|
|
+
|
|
|
+ message.error(errorMessage);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
// 校验表格数据
|
|
|
const isValid = validateTableData()
|
|
|
if (!isValid) return
|
|
@@ -1616,10 +1817,17 @@ const submitForm = async () => {
|
|
|
? item.lastNaturalDate
|
|
|
: (item.lastNaturalDate ? dayjs(item.lastNaturalDate).valueOf() : null)
|
|
|
}));
|
|
|
+
|
|
|
+ // 从 bomMaterialsMap 获取所有物料数据,而不是 materialList
|
|
|
+ const allMaterials = [];
|
|
|
+ for (const key in bomMaterialsMap.value) {
|
|
|
+ allMaterials.push(...bomMaterialsMap.value[key]);
|
|
|
+ }
|
|
|
+
|
|
|
const data = {
|
|
|
mainWorkOrder: formData.value,
|
|
|
mainWorkOrderBom: convertedList,
|
|
|
- mainWorkOrderMaterials: materialList.value
|
|
|
+ mainWorkOrderMaterials: allMaterials
|
|
|
}
|
|
|
await IotMainWorkOrderApi.fillWorkOrder(data)
|
|
|
message.success(t('common.createSuccess'))
|
|
@@ -2066,7 +2274,7 @@ const validateTableData = (): boolean => {
|
|
|
if (formData.value.outsourcingFlag !== 1) {
|
|
|
list.value.forEach((row, index) => {
|
|
|
const rowNumber = index + 1;
|
|
|
- const deviceIdentifier = `${row.deviceCode}-${row.name}`;
|
|
|
+ const deviceIdentifier = `${row.deviceCode}-${formatMaintItemName(row.name)}`;
|
|
|
|
|
|
// 检查消耗物料规则,如果为1(不消耗物料)则跳过校验
|
|
|
if (row.rule === 1) {
|
|
@@ -2079,10 +2287,24 @@ const validateTableData = (): boolean => {
|
|
|
(row.delayNaturalDate || 0) > 0 ||
|
|
|
(row.delayDuration || 0) > 0;
|
|
|
|
|
|
- // 未设置推迟保养且未选择物料
|
|
|
- if (!hasDelay && getMaterialCount(row.bomNodeId) === 0) {
|
|
|
- materialRequiredErrors.push(`第${rowNumber}行【${deviceIdentifier}】未添加物料`);
|
|
|
- isValid = false;
|
|
|
+ // 未设置推迟保养时,需要校验物料
|
|
|
+ if (!hasDelay) {
|
|
|
+ // 使用 bomMaterialsMap 获取物料,更可靠
|
|
|
+ const uniqueKey = `${row.bomNodeId}`;
|
|
|
+ const materials = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+
|
|
|
+ // 过滤有效的物料(非禁用、非无效状态)
|
|
|
+ const validMaterials = materials.filter(material =>
|
|
|
+ material.materialName && // 物料名称不为空
|
|
|
+ material.unit && // 单位不为空
|
|
|
+ (material.unitPrice || 0) > 0 && // 单价大于0
|
|
|
+ (material.quantity || 0) > 0 // 数量大于0
|
|
|
+ );
|
|
|
+
|
|
|
+ if (validMaterials.length === 0) {
|
|
|
+ materialRequiredErrors.push(`第${rowNumber}行【${deviceIdentifier}】未添加有效物料`);
|
|
|
+ isValid = false;
|
|
|
+ }
|
|
|
}
|
|
|
});
|
|
|
}
|
|
@@ -2174,15 +2396,18 @@ onMounted(async () => {
|
|
|
list.value = []
|
|
|
if (Array.isArray(data)) {
|
|
|
list.value = data.map(item => {
|
|
|
+ // 记录初始状态,用于判断是否可编辑
|
|
|
+ const initialStatus = item.status;
|
|
|
+
|
|
|
// 提取分组名称
|
|
|
const group = item.name && item.name.includes('->')
|
|
|
? item.name.split('->')[0].trim()
|
|
|
: '';
|
|
|
|
|
|
// 处理物料数据映射
|
|
|
- if (item.deviceBomMaterials && item.deviceBomMaterials.length > 0) {
|
|
|
+ /* if (item.deviceBomMaterials && item.deviceBomMaterials.length > 0) {
|
|
|
// 生成唯一键
|
|
|
- const uniqueKey = `${item.deviceId}${item.bomNodeId}`;
|
|
|
+ const uniqueKey = `${item.bomNodeId}`;
|
|
|
|
|
|
// 转换物料字段映射
|
|
|
const mappedMaterials = item.deviceBomMaterials.map(material => ({
|
|
@@ -2190,11 +2415,12 @@ onMounted(async () => {
|
|
|
materialName: material.name, // name -> materialName
|
|
|
materialCode: material.code, // code -> materialCode
|
|
|
projectDepartment: material.storageLocation, // storageLocation -> projectDepartment
|
|
|
- totalInventoryQuantity: material.stockQuantity // stockQuantity -> totalInventoryQuantity
|
|
|
+ totalInventoryQuantity: material.stockQuantity, // stockQuantity -> totalInventoryQuantity
|
|
|
+ deviceId: item.deviceId // 添加 deviceId,从保养项中获取
|
|
|
}));
|
|
|
// 存储到映射表中
|
|
|
bomMaterialsMap.value[uniqueKey] = mappedMaterials;
|
|
|
- }
|
|
|
+ } */
|
|
|
|
|
|
if (item.mileageRule === 0) {
|
|
|
item.nextMaintenanceKm = calculateNextMaintenanceKm(item);
|
|
@@ -2212,6 +2438,7 @@ onMounted(async () => {
|
|
|
return {
|
|
|
...item,
|
|
|
group,
|
|
|
+ initialStatus, // 保存初始状态
|
|
|
lastNaturalDate: item.lastNaturalDate,
|
|
|
lastMaintenanceDate: item.lastMaintenanceDate
|
|
|
? dayjs(item.lastMaintenanceDate).format("YYYY-MM-DD") // 时间戳 → 日期字符串
|
|
@@ -2229,34 +2456,74 @@ onMounted(async () => {
|
|
|
}
|
|
|
// 查询当前保养工单已经关联的所有物料
|
|
|
const materials = await IotMainWorkOrderBomMaterialApi.getWorkOrderBomMaterials(queryParams);
|
|
|
- materialList.value = []
|
|
|
- // 修改:如果有已关联的物料,使用接口返回的数据覆盖默认数据
|
|
|
+ // 重新初始化物料映射,确保接口数据完全覆盖
|
|
|
+ const tempBomMaterialsMap = {};
|
|
|
+ // 首先,用 getWorkOrderBomMaterials 返回的数据初始化映射(主数据源)
|
|
|
if (Array.isArray(materials) && materials.length > 0) {
|
|
|
- // materialList.value = materials;
|
|
|
-
|
|
|
- // 同时更新 bomMaterialsMap
|
|
|
materials.forEach(material => {
|
|
|
- const uniqueKey = `${material.deviceId}${material.bomNodeId}`;
|
|
|
- if (!bomMaterialsMap.value[uniqueKey]) {
|
|
|
- bomMaterialsMap.value[uniqueKey] = [];
|
|
|
+ const uniqueKey = `${material.bomNodeId}`;
|
|
|
+ if (!tempBomMaterialsMap[uniqueKey]) {
|
|
|
+ tempBomMaterialsMap[uniqueKey] = [];
|
|
|
}
|
|
|
+
|
|
|
+ // 通过 bomNodeId 在 list 中查找对应的保养项,获取 deviceId
|
|
|
+ const correspondingItem = list.value.find(item => item.bomNodeId === material.bomNodeId);
|
|
|
+ const deviceId = correspondingItem ? correspondingItem.deviceId : null;
|
|
|
+
|
|
|
+ const materialWithDeviceId = {
|
|
|
+ ...material,
|
|
|
+ deviceId: deviceId
|
|
|
+ };
|
|
|
+
|
|
|
// 避免重复添加
|
|
|
- const existingIndex = bomMaterialsMap.value[uniqueKey].findIndex(item =>
|
|
|
+ const existingIndex = tempBomMaterialsMap[uniqueKey].findIndex(item =>
|
|
|
item.materialCode === material.materialCode &&
|
|
|
item.factoryId === material.factoryId &&
|
|
|
item.costCenterId === material.costCenterId &&
|
|
|
item.storageLocationId === material.storageLocationId
|
|
|
);
|
|
|
+
|
|
|
if (existingIndex === -1) {
|
|
|
- bomMaterialsMap.value[uniqueKey].push(material);
|
|
|
+ tempBomMaterialsMap[uniqueKey].push(materialWithDeviceId);
|
|
|
} else {
|
|
|
- // 如果已存在,更新数据(使用接口返回的最新数据)
|
|
|
- bomMaterialsMap.value[uniqueKey][existingIndex] = material;
|
|
|
+ tempBomMaterialsMap[uniqueKey][existingIndex] = materialWithDeviceId;
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
+ // 其次,对于 getWorkOrderBOMs 接口返回的 deviceBomMaterials,
|
|
|
+ // 只有当 getWorkOrderBomMaterials 中没有对应保养项的物料时,才使用 deviceBomMaterials 作为备用
|
|
|
+ list.value.forEach(item => {
|
|
|
+ const uniqueKey = `${item.bomNodeId}`;
|
|
|
+
|
|
|
+ // 如果 getWorkOrderBomMaterials 中没有这个保养项的物料,且 deviceBomMaterials 有值,则使用备用数据
|
|
|
+ if ((!tempBomMaterialsMap[uniqueKey] || tempBomMaterialsMap[uniqueKey].length === 0) &&
|
|
|
+ item.deviceBomMaterials && item.deviceBomMaterials.length > 0) {
|
|
|
+
|
|
|
+ tempBomMaterialsMap[uniqueKey] = item.deviceBomMaterials.map(material => ({
|
|
|
+ ...material,
|
|
|
+ materialName: material.name,
|
|
|
+ materialCode: material.code,
|
|
|
+ projectDepartment: material.storageLocation,
|
|
|
+ totalInventoryQuantity: material.stockQuantity,
|
|
|
+ deviceId: item.deviceId
|
|
|
+ }));
|
|
|
+ } else if (!tempBomMaterialsMap[uniqueKey]) {
|
|
|
+ // 确保每个保养项在映射中都有对应的数组(即使是空的)
|
|
|
+ tempBomMaterialsMap[uniqueKey] = [];
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // 更新响应式映射
|
|
|
+ bomMaterialsMap.value = tempBomMaterialsMap;
|
|
|
+
|
|
|
+ // 如果有数据,选中第一个保养项并显示其物料
|
|
|
if (list.value.length > 0) {
|
|
|
- handleBomItemClick(list.value[0]);
|
|
|
+ // 确保第一个保养项的物料正确显示
|
|
|
+ const firstItem = list.value[0];
|
|
|
+ const uniqueKey = `${firstItem.bomNodeId}`;
|
|
|
+ materialList.value = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+
|
|
|
+ handleBomItemClick(firstItem);
|
|
|
}
|
|
|
// 页面初始化完成后立即计算费用
|
|
|
nextTick(() => {
|
|
@@ -2291,11 +2558,9 @@ const handleBomItemClick = (item) => {
|
|
|
currentBomItem.value = item;
|
|
|
// 切换到当前保养项物料视图
|
|
|
showAllMaterials.value = false;
|
|
|
- // 生成唯一键
|
|
|
- const uniqueKey = `${item.deviceId}${item.bomNodeId}`;
|
|
|
|
|
|
- // 更新物料列表
|
|
|
- materialList.value = bomMaterialsMap.value[uniqueKey] || [];
|
|
|
+ // 使用新的刷新方法
|
|
|
+ refreshCurrentBomItemMaterials();
|
|
|
|
|
|
// 切换保养项时重新计算费用
|
|
|
nextTick(() => {
|
|
@@ -2503,19 +2768,27 @@ const handleRowClick = (row) => {
|
|
|
:deep(.disabled-material-row) {
|
|
|
background-color: #f5f7fa !important;
|
|
|
color: #c0c4cc !important;
|
|
|
+ cursor: not-allowed !important;
|
|
|
}
|
|
|
|
|
|
:deep(.disabled-material-row:hover>td) {
|
|
|
background-color: #f5f7fa !important;
|
|
|
}
|
|
|
|
|
|
+:deep(.disabled-material-row .el-input.is-disabled .el-input__inner) {
|
|
|
+ background-color: #f5f7fa !important;
|
|
|
+ color: #c0c4cc !important;
|
|
|
+ cursor: not-allowed !important;
|
|
|
+}
|
|
|
+
|
|
|
:deep(.disabled-material-row .el-input-number.is-disabled) {
|
|
|
opacity: 0.6;
|
|
|
+ cursor: not-allowed !important;
|
|
|
}
|
|
|
|
|
|
:deep(.disabled-material-row .el-button.is-disabled) {
|
|
|
opacity: 0.6;
|
|
|
- cursor: not-allowed;
|
|
|
+ cursor: not-allowed !important;
|
|
|
}
|
|
|
|
|
|
/* 新增物料行的样式 */
|
|
@@ -2567,4 +2840,50 @@ const handleRowClick = (row) => {
|
|
|
border-bottom-color: #ffeeba !important;
|
|
|
}
|
|
|
|
|
|
+/* 状态列容器样式 - 水平排列 */
|
|
|
+.status-container {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ gap: 8px; /* 设置开关和文本之间的间距 */
|
|
|
+ white-space: nowrap; /* 防止换行 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 状态开关样式 */
|
|
|
+.status-switch {
|
|
|
+ flex-shrink: 0; /* 防止开关被压缩 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 状态列样式优化 */
|
|
|
+.status-text {
|
|
|
+ font-size: 12px;
|
|
|
+ color: #666;
|
|
|
+ min-width: 40px; /* 为文本设置最小宽度,确保对齐 */
|
|
|
+ text-align: left;
|
|
|
+ flex-shrink: 0; /* 防止文本被压缩 */
|
|
|
+}
|
|
|
+
|
|
|
+/* 禁用状态的switch样式 */
|
|
|
+:deep(.el-switch.is-disabled) {
|
|
|
+ opacity: 0.6;
|
|
|
+}
|
|
|
+
|
|
|
+/* 已完成状态的特殊样式 */
|
|
|
+:deep(.status-completed .el-switch__core) {
|
|
|
+ background-color: #67c23a;
|
|
|
+ border-color: #67c23a;
|
|
|
+}
|
|
|
+
|
|
|
+/* 保养中状态的样式 */
|
|
|
+:deep(.status-maintaining .el-switch__core) {
|
|
|
+ background-color: #409eff;
|
|
|
+ border-color: #409eff;
|
|
|
+}
|
|
|
+
|
|
|
+/* 延迟状态的样式 */
|
|
|
+:deep(.status-delayed .el-switch__core) {
|
|
|
+ background-color: #e6a23c;
|
|
|
+ border-color: #e6a23c;
|
|
|
+}
|
|
|
+
|
|
|
</style>
|