liding
8 小时以前 3a63dff0d98c148c6b915cd762a50ed87aa6c3a5
feat:1.生产订单增加清场记录弹框
已添加1个文件
已修改2个文件
456 ■■■■■ 文件已修改
src/api/productionManagement/productionOrder.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/ClearanceRecordDialog.vue 400 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/productionOrder.js
@@ -137,3 +137,12 @@
    method: "patch",
  });
}
// ä¿å­˜æ¸…场记录
export function saveCleanRecord(id, data) {
  return request({
    url: `/productOrder/cleanRecord/${id}`,
    method: "patch",
    data: data,
  });
}
src/views/productionManagement/productionOrder/ClearanceRecordDialog.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,400 @@
<template>
  <el-dialog
    v-model="visible"
    title="清场记录"
    width="900px"
    :close-on-click-modal="false"
    destroy-on-close
  >
    <div class="clearance-record-form">
      <!-- åŸºæœ¬ä¿¡æ¯åŒºåŸŸ -->
      <div class="basic-info">
        <table class="info-table">
          <tr>
            <td class="label">产品名称</td>
            <td class="value" colspan="3">{{ formData.productName }}</td>
            <td class="label">生产日期</td>
            <td class="value">{{ formData.productionDate }}</td>
          </tr>
          <tr>
            <td class="label">规格</td>
            <td class="value">{{ formData.spec }}</td>
            <td class="label">批号</td>
            <td class="value">{{ formData.batchNo }}</td>
            <td class="label">生产车间</td>
            <td class="value">{{ formData.workshop }}</td>
          </tr>
        </table>
      </div>
      <!-- æ¸…场检查项目表格 -->
      <div class="check-items-section">
        <table class="check-table">
          <thead>
            <tr>
              <th class="item-no">序号</th>
              <th class="content">清场内容及要求</th>
              <th class="result" colspan="2">检查结果</th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="(item, index) in formData.checkItems" :key="index">
              <td class="item-no">{{ item.itemNo }}</td>
              <td class="content">{{ item.content }}</td>
              <td class="result-option">
                <el-radio-group v-model="item.result">
                  <el-radio label="符合规定">符合规定</el-radio>
                </el-radio-group>
              </td>
              <td class="result-option">
                <el-radio-group v-model="item.result">
                  <el-radio label="不符合规定">不符合规定</el-radio>
                </el-radio-group>
              </td>
            </tr>
          </tbody>
        </table>
      </div>
      <!-- ç­¾ååŒºåŸŸ -->
      <div class="signature-section">
        <table class="signature-table">
          <tr>
            <td class="label">清场人</td>
            <td class="value">
              <el-input v-model="formData.cleaner" placeholder="请输入清场人" clearable />
            </td>
            <td class="label">日期</td>
            <td class="value">
              <el-date-picker
                v-model="formData.cleanDate"
                type="datetime"
                placeholder="选择日期时间"
                format="YYYY-MM-DD HH:mm:ss"
                value-format="YYYY-MM-DD HH:mm:ss"
                style="width: 100%"
              />
            </td>
            <td class="label">检查人</td>
            <td class="value">
              <el-input v-model="formData.inspector" placeholder="请输入检查人" clearable />
            </td>
            <td class="label">日期</td>
            <td class="value">
              <el-date-picker
                v-model="formData.inspectDate"
                type="datetime"
                placeholder="选择日期时间"
                format="YYYY-MM-DD HH:mm:ss"
                value-format="YYYY-MM-DD HH:mm:ss"
                style="width: 100%"
              />
            </td>
          </tr>
        </table>
      </div>
    </div>
    <template #footer>
      <span class="dialog-footer">
        <el-button @click="handleCancel">取 æ¶ˆ</el-button>
        <el-button type="primary" :loading="saving" @click="handleSave">保 å­˜</el-button>
      </span>
    </template>
  </el-dialog>
</template>
<script setup>
import { ref, reactive, watch, computed } from 'vue';
import dayjs from 'dayjs';
const props = defineProps({
  modelValue: {
    type: Boolean,
    default: false
  },
  orderData: {
    type: Object,
    default: () => ({})
  }
});
const emit = defineEmits(['update:modelValue', 'save']);
const visible = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
});
const saving = ref(false);
// é»˜è®¤æ£€æŸ¥é¡¹ç›®
const defaultCheckItems = [
  { itemNo: 1, content: '车间内无前批遗留物、废弃物。', result: '符合规定', remark: '' },
  { itemNo: 2, content: '车间内无无关指令、规程、记录等。', result: '符合规定', remark: '' },
  { itemNo: 3, content: '桌面、工作台应清洁,无尘、无垢。', result: '符合规定', remark: '' },
  { itemNo: 4, content: '地面清洁,无积尘、杂物。', result: '符合规定', remark: '' },
  { itemNo: 5, content: '设备及部件内、外应清洁,无异物。', result: '符合规定', remark: '' },
  { itemNo: 6, content: '容器具应清洁无异物,并置指定位置', result: '符合规定', remark: '' },
  { itemNo: 7, content: '卫生洁具清洁,并置指定位置', result: '符合规定', remark: '' },
  { itemNo: 8, content: '其它', result: '符合规定', remark: '' }
];
const formData = reactive({
  orderId: '',
  productName: '',
  productionDate: '',
  spec: '',
  batchNo: '',
  workshop: '',
  checkItems: JSON.parse(JSON.stringify(defaultCheckItems)),
  cleaner: '',
  cleanDate: '',
  inspector: '',
  inspectDate: ''
});
// ç›‘听弹框打开,初始化数据
watch(() => props.modelValue, (val) => {
  if (val && props.orderData) {
    initFormData();
  }
});
const initFormData = () => {
  const order = props.orderData;
  const cleanRecord = order.cleanRecord || order.clearanceRecord;
  if (cleanRecord) {
    try {
      const record = typeof cleanRecord === 'string'
        ? JSON.parse(cleanRecord)
        : cleanRecord;
      // åŠ è½½åŸºæœ¬å­—æ®µ
      formData.orderId = record.orderId || order.npsNo || '';
      formData.productName = record.productName || order.productCategory || '';
      formData.productionDate = record.productionDate || (order.startTime ? dayjs(order.startTime).format('YYYY-MM-DD') : dayjs().format('YYYY-MM-DD'));
      formData.spec = record.spec || order.specificationModel || '';
      formData.batchNo = record.batchNo || order.batchNo || order.uidNo || '';
      formData.workshop = record.workshop || order.workshop || order.manufacturingTeam || '';
      // åŠ è½½æ£€æŸ¥é¡¹ç›®ï¼Œä¿æŒé»˜è®¤ç»“æž„ä½†æ›´æ–°ç»“æžœ
      if (record.checkItems && Array.isArray(record.checkItems)) {
        formData.checkItems = defaultCheckItems.map((defaultItem, index) => {
          const savedItem = record.checkItems.find(item => item.itemNo === defaultItem.itemNo);
          return {
            ...defaultItem,
            result: savedItem?.result || '符合规定',
            remark: savedItem?.remark || ''
          };
        });
      } else {
        formData.checkItems = JSON.parse(JSON.stringify(defaultCheckItems));
      }
      // åŠ è½½ç­¾åä¿¡æ¯
      formData.cleaner = record.cleaner || '';
      formData.cleanDate = record.cleanDate || dayjs().format('YYYY-MM-DD HH:mm:ss');
      formData.inspector = record.inspector || '';
      formData.inspectDate = record.inspectDate || dayjs().format('YYYY-MM-DD HH:mm:ss');
    } catch (e) {
      console.error('解析清场记录数据失败:', e);
      resetFormData(order);
    }
  } else {
    resetFormData(order);
  }
};
const resetFormData = (order) => {
  // é‡ç½®æ£€æŸ¥é¡¹ç›®
  formData.checkItems = JSON.parse(JSON.stringify(defaultCheckItems));
  // è‡ªåŠ¨å¸¦å…¥è®¢å•æ•°æ®
  formData.orderId = order.npsNo || '';
  formData.productName = order.productCategory || '';
  formData.productionDate = order.startTime ? dayjs(order.startTime).format('YYYY-MM-DD') : dayjs().format('YYYY-MM-DD');
  formData.spec = order.specificationModel || '';
  formData.batchNo = order.batchNo || order.uidNo || '';
  formData.workshop = order.workshop || order.manufacturingTeam || '';
  // æ¸…场人和检查人默认为空,日期默认为当前时间
  formData.cleaner = '';
  formData.cleanDate = dayjs().format('YYYY-MM-DD HH:mm:ss');
  formData.inspector = '';
  formData.inspectDate = dayjs().format('YYYY-MM-DD HH:mm:ss');
};
const handleCancel = () => {
  visible.value = false;
};
const handleSave = () => {
  // éªŒè¯å¿…填项
  if (!formData.cleaner) {
    ElMessage.warning('请输入清场人');
    return;
  }
  if (!formData.cleanDate) {
    ElMessage.warning('请选择清场日期');
    return;
  }
  if (!formData.inspector) {
    ElMessage.warning('请输入检查人');
    return;
  }
  if (!formData.inspectDate) {
    ElMessage.warning('请选择检查日期');
    return;
  }
  // æž„造保存的JSON数据
  const saveData = {
    orderId: formData.orderId,
    productName: formData.productName,
    productionDate: formData.productionDate,
    spec: formData.spec,
    batchNo: formData.batchNo,
    workshop: formData.workshop,
    checkItems: formData.checkItems.map(item => ({
      itemNo: item.itemNo,
      content: item.content,
      result: item.result,
      remark: item.remark || undefined
    })),
    cleaner: formData.cleaner,
    cleanDate: formData.cleanDate,
    inspector: formData.inspector,
    inspectDate: formData.inspectDate
  };
  // ç§»é™¤undefined字段
  saveData.checkItems = saveData.checkItems.map(item => {
    if (!item.remark) delete item.remark;
    return item;
  });
  saving.value = true;
  emit('save', saveData, () => {
    saving.value = false;
    visible.value = false;
  });
};
</script>
<style scoped lang="scss">
.clearance-record-form {
  .basic-info {
    margin-bottom: 20px;
  }
  .info-table {
    width: 100%;
    border-collapse: collapse;
    border: 1px solid #dcdfe6;
    td {
      border: 1px solid #dcdfe6;
      padding: 10px;
      font-size: 14px;
    }
    .label {
      background-color: #f5f7fa;
      width: 100px;
      text-align: center;
      font-weight: 500;
    }
    .value {
      background-color: #fff;
      min-width: 150px;
    }
  }
  .check-items-section {
    margin-bottom: 10px;
    .check-table {
      width: 100%;
      border-collapse: collapse;
      border: 1px solid #dcdfe6;
      th, td {
        border: 1px solid #dcdfe6;
        padding: 12px 10px;
        font-size: 14px;
      }
      th {
        background-color: #f5f7fa;
        font-weight: 500;
        text-align: center;
      }
      .item-no {
        width: 60px;
        text-align: center;
      }
      .content {
        text-align: left;
        padding-left: 15px;
      }
      .result {
        width: 200px;
        text-align: center;
      }
      .result-option {
        width: 150px;
        text-align: center;
        :deep(.el-radio-group) {
          display: flex;
          justify-content: center;
        }
      }
    }
  }
  .remark-section {
    margin-bottom: 20px;
    padding: 0 10px;
  }
  .signature-section {
    .signature-table {
      width: 100%;
      border-collapse: collapse;
      border: 1px solid #dcdfe6;
      td {
        border: 1px solid #dcdfe6;
        padding: 10px;
        font-size: 14px;
      }
      .label {
        background-color: #f5f7fa;
        width: 80px;
        text-align: center;
        font-weight: 500;
      }
      .value {
        background-color: #fff;
        min-width: 150px;
      }
    }
  }
}
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 10px;
}
</style>
src/views/productionManagement/productionOrder/index.vue
@@ -95,6 +95,13 @@
    <new-product-order v-if="isShowNewModal"
                       v-model:visible="isShowNewModal"
                       @completed="handleQuery"/>
    <!-- æ¸…场记录弹框 -->
    <clearance-record-dialog
      v-model="clearanceDialogVisible"
      :order-data="currentOrderData"
      @save="handleSaveClearanceRecord"
    />
  </div>
</template>
@@ -108,12 +115,14 @@
  listProcessRoute,
  bindingRoute,
  delProductOrder, finishOrder,
  saveCleanRecord,
} from "@/api/productionManagement/productionOrder.js";
import {listMain as getOrderProcessRouteMain} from "@/api/productionManagement/productProcessRoute.js";
import {fileDel} from "@/api/financialManagement/revenueManagement.js";
import PIMTable from "@/components/PIMTable/PIMTable.vue";
const NewProductOrder = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/New.vue"));
const ClearanceRecordDialog = defineAsyncComponent(() => import("@/views/productionManagement/productionOrder/ClearanceRecordDialog.vue"));
const {proxy} = getCurrentInstance();
@@ -227,6 +236,14 @@
          handleFinishOrder(row);
        },
      },
      {
        name: "清场记录",
        type: "text",
        showHide: row => !row.isEnd,
        clickFun: row => {
          handleClearanceRecord(row);
        },
      },
    ],
  },
]);
@@ -289,6 +306,10 @@
const bindRouteLoading = ref(false);
const bindRouteSaving = ref(false);
const routeOptions = ref([]);
// æ¸…场记录弹框
const clearanceDialogVisible = ref(false);
const currentOrderData = ref({});
const bindForm = reactive({
  orderId: null,
  routeId: null,
@@ -478,6 +499,32 @@
        proxy.$modal.msg("已取消");
      });
};
// æ‰“开清场记录弹框
const handleClearanceRecord = (row) => {
  currentOrderData.value = row;
  clearanceDialogVisible.value = true;
};
// ä¿å­˜æ¸…场记录
const handleSaveClearanceRecord = async (saveData, callback) => {
  const orderId = currentOrderData.value?.id;
  if (!orderId) {
    proxy.$modal.msgError("订单ID不存在");
    return;
  }
  try {
    await saveCleanRecord(orderId, saveData);
    proxy.$modal.msgSuccess("清场记录保存成功");
    currentOrderData.value.cleanRecord = saveData;
    if (callback) callback();
    getList();
  } catch (error) {
    console.error('保存清场记录失败:', error);
    proxy.$modal.msgError("清场记录保存失败");
  }
};
onMounted(() => {
  getList();
});