瀏覽代碼

Merge remote-tracking branch 'origin/master'

lipenghui 4 周之前
父節點
當前提交
08ffb5104c

+ 8 - 9
src/views/pms/bom/MaterialListDrawer.vue

@@ -24,10 +24,10 @@
           </div>
         </div>
 
-        <el-table :data="materials" style="width: 100%" fit :row-style="{ height: '48px' }">
-          <el-table-column prop="name" label="物料名称" min-width="150" show-overflow-tooltip/>
-          <el-table-column prop="code" label="物料编码" min-width="80" />
-          <el-table-column prop="quantity" label="数量" min-width="70" align="center">
+        <el-table :data="materials" style="width: 100%" fit :row-style="{ height: '38px' }">
+          <el-table-column prop="name" :label="t('workOrderMaterial.materialName')" min-width="150" show-overflow-tooltip/>
+          <el-table-column prop="code" :label="t('workOrderMaterial.materialCode')" min-width="80" />
+          <el-table-column prop="quantity" :label="t('route.quantity')" min-width="70" align="center">
             <template #default="scope">
               <el-form
                 :model="scope.row"
@@ -49,9 +49,9 @@
               </el-form>
             </template>
           </el-table-column>
-          <el-table-column prop="unit" label="单位" min-width="50" align="center"/>
+          <el-table-column prop="unit" :label="t('workOrderMaterial.unit')" min-width="50" align="center"/>
           <el-table-column
-            label="创建时间"
+            :label="t('deviceList.createTime')"
             align="center"
             prop="createTime"
             min-width="110"
@@ -64,13 +64,13 @@
                 type="success"
                 :loading="scope.row.updating"
                 @click="handleUpdate(scope.row)"
-              >更新</el-button>
+              >{{ t('iotDevice.update') }}</el-button>
 
               <el-button
                 size="small"
                 type="danger"
                 @click="handleDelete(scope.row)"
-              >删除</el-button>
+              >{{ t('info.delete') }}</el-button>
             </template>
           </el-table-column>
         </el-table>
@@ -165,7 +165,6 @@ const loadMaterials = async (nodeId) => {
   try {
     loading.value = true
     // API调用
-    // const data = await PmsMaterialApi.listByBomId(queryParams)
     const data = await CommonBomMaterialApi.getCommonBomMaterialPage(queryParams)
     materials.value = data.list.map(item => ({
       ...item,

+ 157 - 8
src/views/pms/device/MaterialListDrawerDevice.vue

@@ -24,20 +24,48 @@
           </div>
         </div>
 
-        <el-table :data="materials" style="width: 100%">
-          <el-table-column prop="name" :label="t('workOrderMaterial.materialName')" width="180" />
-          <el-table-column prop="code" :label="t('workOrderMaterial.materialCode')" width="180" />
-          <!-- <el-table-column prop="model" :label="t('deviceForm.model')" width="180" /> -->
-          <el-table-column prop="unit" :label="t('workOrderMaterial.unit')" width="180" />
+        <el-table :data="materials" style="width: 100%" fit :row-style="{ height: '38px' }">
+          <el-table-column prop="name" :label="t('workOrderMaterial.materialName')" min-width="150" show-overflow-tooltip/>
+          <el-table-column prop="code" :label="t('workOrderMaterial.materialCode')" min-width="80" />
+          <el-table-column prop="quantity" :label="t('route.quantity')" min-width="70" align="center">
+            <template #default="scope">
+              <el-form
+                :model="scope.row"
+                :rules="quantityRules"
+                ref="quantityFormRef"
+                @submit.prevent
+              >
+                <el-form-item prop="quantity" :error="scope.row.quantityError">
+                  <el-input
+                    v-model.number="scope.row.quantity"
+                    type="number"
+                    size="small"
+                    placeholder="请输入数量"
+                    @blur="clearQuantityError(scope.row)"
+                    @keyup.enter="handleUpdate(scope.row)"
+                    :class="{ 'error-input': scope.row.quantityError }"
+                  />
+                </el-form-item>
+              </el-form>
+            </template>
+          </el-table-column>
+          <el-table-column prop="unit" :label="t('workOrderMaterial.unit')" min-width="50" />
           <el-table-column
             :label="t('deviceList.createTime')"
             align="center"
             prop="createTime"
-            width="180"
+            min-width="110"
             :formatter="dateFormatter"
           />
-          <el-table-column :label="t('maintain.operation')" align="right">
+          <el-table-column :label="t('maintain.operation')" width="180" fixed="right">
             <template #default="scope">
+              <el-button
+                size="small"
+                type="success"
+                :loading="scope.row.updating"
+                @click="handleUpdate(scope.row)"
+              >{{ t('iotDevice.update') }}</el-button>
+
               <el-button size="small" type="danger" @click="handleDelete(scope.row)"
                 >{{ t('info.delete') }}</el-button
               >
@@ -60,6 +88,7 @@ import { defineEmits, defineOptions, ref, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import * as DeviceMaterialApi from '@/api/pms/iotdevicematerial'
 import { dateFormatter } from '@/utils/formatTime'
+import {IotDeviceMaterialApi, IotDeviceMaterialVO} from "@/api/pms/iotdevicematerial";
 const { t } = useI18n() // 国际化
 const drawerVisible = ref<boolean>(false)
 const emit = defineEmits(['update:modelValue', 'add', 'delete', 'refresh'])
@@ -80,6 +109,24 @@ const queryParams = reactive({
   code: ''
 })
 
+const quantityRules: FormRules = {
+  quantity: [
+    { required: true, message: '数量不能为空', trigger: 'blur' },
+    {
+      validator: (rule, value, callback) => {
+        if (isNaN(value)) {
+          callback(new Error('请输入有效数字'));
+        } else if (value <= 0) {
+          callback(new Error('数量必须大于0'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur'
+    }
+  ]
+};
+
 const windowWidth = ref(window.innerWidth)
 // 动态计算百分比
 const computedSize = computed(() => {
@@ -122,7 +169,11 @@ const loadMaterials = async (nodeId) => {
     loading.value = true
     // API调用
     const data = await DeviceMaterialApi.IotDeviceMaterialApi.getIotDeviceMaterialPage(queryParams)
-    materials.value = data.list
+    materials.value = data.list.map(item => ({
+      ...item,
+      updating: false, // 更新状态标记
+      quantityError: '' // 错误状态标记
+    }))
     total.value = data.total
   } catch (error) {
     ElMessage.error('数据加载失败')
@@ -131,6 +182,52 @@ const loadMaterials = async (nodeId) => {
   }
 }
 
+/** 清除数量错误状态 */
+const clearQuantityError = (row: IotDeviceMaterialVO) => {
+  // 延迟清除错误状态,避免在验证过程中清除
+  setTimeout(() => {
+    row.quantityError = '';
+  }, 100);
+}
+
+/** 更新物料数据 */
+const handleUpdate = async (row: IotDeviceMaterialVO) => {
+  try {
+    if (isNaN(row.quantity)) {
+      row.quantityError = '请输入有效数字';
+      ElMessage.warning('请输入有效的数字');
+      return;
+    }
+
+    if (row.quantity <= 0) {
+      row.quantityError = '数量必须大于0';
+      ElMessage.warning('数量必须大于0');
+      return;
+    }
+
+    // 清除错误状态
+    row.quantityError = '';
+
+    // 设置更新状态
+    row.updating = true
+    // 构造更新参数
+    const updateParams = {
+      ...row,
+      bomNodeId: props.nodeId
+    }
+    // 调用更新接口
+    await DeviceMaterialApi.IotDeviceMaterialApi.updateIotDeviceMaterial(updateParams)
+    // 更新成功反馈
+    message.success('数量更新成功')
+    // 可选:刷新父组件数据
+    // emit('refresh')
+  } catch (error) {
+    ElMessage.error('更新失败')
+  } finally {
+    row.updating = false
+  }
+}
+
 // 打开抽屉
 const openDrawer = () => {
   drawerVisible.value = true
@@ -165,6 +262,58 @@ defineExpose({ openDrawer, closeDrawer, loadMaterials }) // 暴露方法给父
 </script>
 
 <style lang="scss" scoped>
+
+/* 添加表格样式优化 */
+.el-table {
+  :deep(.cell) {
+    white-space: nowrap;  /* 防止单元格内容换行 */
+  }
+
+  /* 表头样式 */
+  :deep(th.el-table__cell) {
+    background-color: #f5f7fa;
+    font-weight: bold;
+    color: #606266;
+  }
+}
+
+/* 优化输入框样式 */
+.el-input {
+  width: 80px;
+
+  :deep(.el-input__inner) {
+    text-align: center;
+    padding: 0 5px;
+  }
+  &.error-input :deep(.el-input__inner) {
+    border-color: #f56c6c;
+    background-color: #fff6f7;
+  }
+}
+
+/* 错误提示样式 */
+:deep(.el-form-item__error) {
+  position: absolute;
+  top: 100%;
+  left: 0;
+  padding-top: 2px;
+  font-size: 12px;
+  line-height: 1;
+  color: #f56c6c;
+  white-space: nowrap;
+}
+
+/* 更新按钮自定义样式 */
+.el-button--success {
+  background-color: #e8f5e9;
+  border-color: #c8e6c9;
+  color: #2e7d32;
+
+  &:hover {
+    background-color: #c8e6c9;
+  }
+}
+
 .info-header {
   display: flex;
   align-items: center;

+ 1 - 0
src/views/pms/device/bom/BomList.vue

@@ -269,6 +269,7 @@ const chooseMaterial = async (selectedMaterials) => {
       materialId: material.id,
       name: material.name,
       code: material.code,
+      quantity: material.quantity
     }))
 
     // 调用批量添加接口

+ 5 - 71
src/views/pms/iotmainworkorder/IotMainWorkOrder.vue

@@ -181,7 +181,7 @@
           <el-table-column :label="t('mainPlan.remainKm')"
                            align="center" prop="remainKm" :width="columnWidths.remainKm">
             <template #default="{ row }">
-              {{ row.remainKm !== null ? row.remainKm.toFixed(2) : '-' }}
+              {{ row.remainKm != null ? row.remainKm.toFixed(2) : '-' }}
             </template>
           </el-table-column>
         </el-table-column>
@@ -203,7 +203,7 @@
           <el-table-column :label="t('mainPlan.remainH')"
                            align="center" prop="remainH" :width="columnWidths.remainH">
             <template #default="{ row }">
-              {{ row.remainH !== null ? row.remainH.toFixed(2) : '-' }}
+              {{ row.remainH != null ? row.remainH.toFixed(2) : '-' }}
             </template>
           </el-table-column>
           <el-table-column
@@ -641,7 +641,6 @@ import dayjs from 'dayjs'
 import MaterialListDrawer from "@/views/pms/iotmainworkorder/SelectedMaterialDrawer.vue";
 import WorkOrderMaterial from "@/views/pms/iotmainworkorder/WorkOrderMaterial.vue";
 import DeviceBomMaterials from "@/views/pms/iotmainworkorder/DeviceBomMaterials.vue";
-import { IotDevicePersonApi, IotDevicePersonVO } from '@/api/pms/iotdeviceperson'
 import {DICT_TYPE, getIntDictOptions} from "@/utils/dict";
 // 引入图标
 import { View, Clock, Box, Document } from '@element-plus/icons-vue';
@@ -868,13 +867,6 @@ const handleSizeChange = (newSize: number) => {
   currentPage.value = 1 // 重置到第一页
 }
 
-// 列宽调整后的处理
-const handleHeaderDragEnd = () => {
-  nextTick(() => {
-    tableRef.value?.doLayout();
-  });
-};
-
 // 运行时间周期 全局校验方法(在submitForm中调用)
 const validateAllRunningTimes = (): boolean => {
   let isValid = true;
@@ -1047,65 +1039,12 @@ const queryParams = reactive({
   workOrderId: id
 })
 
-/** 获取当前所有设备ID集合 */
-const getCurrentDeviceIds = (): number[] => {
-  return [...new Set(list.value.map(item => item.deviceId))]
-}
-
-/** 更新责任人显示 */
-function updateDevicePersonsDisplay() {
-  const allNames = new Set<string>()
-  devicePersonsMap.value.forEach(names => {
-    names.forEach(name => allNames.add(name))
-  })
-  formData.value.devicePersons = Array.from(allNames).join(', ')
-}
-
-/**
- * 根据选择的设备查询所有设备关联的 责任人姓名 逗号分隔
- */
-async function getDevicePersons() {
-  // 获取当前已经选择的设备ID集合
-  const existDeviceIds = getCurrentDeviceIds()
-  if (existDeviceIds.length === 0) {
-    formData.value.devicePersons = ''
-    return
-  }
-  try {
-    // 调用接口获取数据
-    const params = {
-      deviceIds: existDeviceIds.join(',') // 明确传递数组参数
-    }
-    const res = await IotDevicePersonApi.getPersonsByDeviceIds(params)
-    const personsData = res || []
-    // 清空旧数据
-    devicePersonsMap.value.clear()
-    // 处理接口数据
-    personsData.forEach((item: { deviceId: number; personName: string }) => {
-      if (!devicePersonsMap.value.has(item.deviceId)) {
-        devicePersonsMap.value.set(item.deviceId, new Set())
-      }
-      devicePersonsMap.value.get(item.deviceId)?.add(item.personName)
-    })
-    // 生成展示字符串
-    updateDevicePersonsDisplay()
-  } catch (error) {
-    console.error('获取设备责任人失败:', error)
-  }
-}
-
 // 获取指定bomNodeId的物料数量
 const getMaterialCount = (bomNodeId: number) => {
   console.log('当前BOM节点:' + bomNodeId)
   return materialList.value.filter(item => item.bomNodeId === bomNodeId).length
 }
 
-const hasDelay = (row) => {
-  return row.delayKilometers > 0 ||
-    row.delayNaturalDate > 0 ||
-    row.delayDuration > 0
-}
-
 const handleDeleteMaterial = (material) => {
   // 根据唯一标识查找要删除的物料索引
   const index = materialList.value.findIndex(item =>
@@ -1281,7 +1220,7 @@ const calculateRemainKm = (row: IotMaintenanceBomVO) => {
     mileageValue > 0 &&
     row.nextRunningKilometers > 0;
 
-  if (!isValid) return null;
+  if (!isValid) return 0;
   const result = row.nextRunningKilometers - (mileageValue - row.lastRunningKilometers);
   return parseFloat(result.toFixed(2));
 };
@@ -1307,7 +1246,7 @@ const calculateRemainH = (row: IotMaintenanceBomVO) => {
     row.lastRunningTime > 0 &&
     runTimeValue > 0 &&
     row.nextRunningTime > 0;
-  if (!isValid) return null;
+  if (!isValid) return 0;
   const result = row.nextRunningTime - (runTimeValue - row.lastRunningTime);
   return parseFloat(result.toFixed(2));
 };
@@ -1333,7 +1272,7 @@ const calculateRemainDay = (row: IotMaintenanceBomVO) => {
     row.nextNaturalDate > 0;
 
   if (!isValid) {
-    return null;
+    return '-';
   }
 
   try {
@@ -1370,11 +1309,6 @@ const getStatusText = (row: any) => {
   return t('mainPlan.maintaining');
 };
 
-// 监听数据变化重新计算
-/* watch(() => list.value, () => {
-  calculateMaintItemsWidth()
-}, { deep: true }) */
-
 // 计算属性 - 检查当前页是否有开启的里程规则
 const hasMileageRuleInCurrentPage = computed(() => {
   return paginatedList.value.some(row => row.mileageRule === 0);