君歌
1.不需要bom、生产核算
2.新增销售台账后自动生成一个生产订单,然后对订单进行排产
3.生产排产要求可以选择计划时间,时间格式为年月日+时分秒;也要可以多选报工人
4.web和app生产报工中要细分报工开始和报工结束两个操作,根据操作的提交时间记录实际报工时长展示报工台账中
5.报工人选择的谁,只有在相关账号才能看到可报工的数据
6.不合格管理字段按照单据进行修改。
7.生产工时、质量问题汇总、客户档案、销售及回款都能从各模块中导出数据
已修改15个文件
已删除1个文件
2898 ■■■■■ 文件已修改
src/api/productionManagement/workOrder.js 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/nonconformingManagement.js 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/qualityManagement/qualityUnqualifiedOrder.js 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/Edit.vue 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/New.vue 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/index.vue 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/processRoute/processRouteItem/index.vue 607 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionOrder/index.vue 207 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionReporting/index.vue 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productionTraceability/index.vue 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrderEdit/index.vue 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/workOrderManagement/index.vue 641 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue 239 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/components/orderFormDia.vue 428 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/qualityManagement/nonconformingManagement/index.vue 439 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/salesManagement/indicatorStats/index.vue 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/api/productionManagement/workOrder.js
@@ -16,6 +16,15 @@
  });
}
// å¼€å§‹æŠ¥å·¥
export function startWork(data) {
  return request({
    url: "/productionProductMain/startWork",
    method: "post",
    data: data,
  });
}
export function addProductMain(data) {
  return request({
    url: "/productionProductMain/addProductMain",
src/api/qualityManagement/nonconformingManagement.js
@@ -1,5 +1,7 @@
import request from "@/utils/request";
// ==================== ä¸åˆæ ¼ç®¡ç†ï¼ˆæ—§æ¨¡å—) ====================
// æŸ¥è¯¢ä¸åˆæ ¼ç®¡ç†åˆ—表
export function qualityUnqualifiedListPage(query) {
  return request({
@@ -8,7 +10,7 @@
    params: query,
  });
}
// æ–°å¢žä¸åˆæ ¼ç®¡ç†åˆ—表
// æ–°å¢žä¸åˆæ ¼ç®¡ç†
export function qualityUnqualifiedAdd(query) {
  return request({
    url: "/quality/qualityUnqualified/add",
@@ -16,7 +18,7 @@
    data: query,
  });
}
// ä¿®æ”¹ä¸åˆæ ¼ç®¡ç†åˆ—表
// ä¿®æ”¹ä¸åˆæ ¼ç®¡ç†
export function qualityUnqualifiedUpdate(query) {
  return request({
    url: "/quality/qualityUnqualified/update",
@@ -32,7 +34,7 @@
    data: query,
  });
}
// åˆ é™¤ä¸åˆæ ¼ç®¡ç†åˆ—表
// åˆ é™¤ä¸åˆæ ¼ç®¡ç†
export function qualityUnqualifiedDel(query) {
  return request({
    url: "/quality/qualityUnqualified/del",
@@ -40,7 +42,7 @@
    data: query,
  });
}
// æŸ¥è¯¢ä¸åˆæ ¼ç®¡ç†ä¿¡æ¯
// æŸ¥è¯¢ä¸åˆæ ¼ç®¡ç†è¯¦æƒ…
export function getQualityUnqualifiedInfo(query) {
  return request({
    url: "/quality/qualityUnqualified/" + query,
@@ -48,3 +50,61 @@
    data: query,
  });
}
// ==================== ä¸åˆæ ¼å“å¤„理单(新模块) ====================
// åˆ†é¡µæŸ¥è¯¢ä¸åˆæ ¼å“å¤„理单
export function qualityUnqualifiedOrderListPage(query) {
  return request({
    url: "/qualityUnqualifiedOrder/listPage",
    method: "get",
    params: query,
  });
}
// æŸ¥çœ‹å¤„理单详情
export function getQualityUnqualifiedOrderDetail(id) {
  return request({
    url: `/qualityUnqualifiedOrder/${id}`,
    method: "get",
  });
}
// æ–°å¢žå¤„理单
export function saveQualityUnqualifiedOrder(data) {
  return request({
    url: "/qualityUnqualifiedOrder/save",
    method: "post",
    data,
  });
}
// ä¿®æ”¹å¤„理单
export function updateQualityUnqualifiedOrder(data) {
  return request({
    url: "/qualityUnqualifiedOrder/update",
    method: "put",
    data,
  });
}
// åˆ é™¤å¤„理单
export function deleteQualityUnqualifiedOrder(ids) {
  return request({
    url: "/qualityUnqualifiedOrder/delete",
    method: "delete",
    data: ids,
  });
}
// å¤„理(处置)处理单
export function dealQualityUnqualifiedOrder(data) {
  return request({
    url: "/qualityUnqualifiedOrder/deal",
    method: "post",
    data,
  });
}
// å¯¼å‡ºå¤„理单
export function exportQualityUnqualifiedOrder(id) {
  return request({
    url: `/qualityUnqualifiedOrder/export/${id}`,
    method: "get",
    responseType: "blob",
  });
}
src/api/qualityManagement/qualityUnqualifiedOrder.js
ÎļþÒÑɾ³ý
src/views/productionManagement/processRoute/Edit.vue
@@ -25,33 +25,6 @@
          </el-button>
        </el-form-item>
        <el-form-item
            label="BOM"
            prop="bomId"
            :rules="[
                {
                required: true,
                message: '请选择BOM',
                trigger: 'change',
              }
            ]"
        >
          <el-select
              v-model="formState.bomId"
              placeholder="请选择BOM"
              clearable
              :disabled="!formState.productModelId || bomOptions.length === 0"
              style="width: 100%"
          >
            <el-option
                v-for="item in bomOptions"
                :key="item.id"
                :label="item.bomNo || `BOM-${item.id}`"
                :value="item.id"
            />
          </el-select>
        </el-form-item>
        <el-form-item label="备注" prop="description">
          <el-input v-model="formState.description" type="textarea" />
        </el-form-item>
@@ -76,7 +49,6 @@
<script setup>
import {ref, computed, getCurrentInstance, onMounted, nextTick, watch} from "vue";
import {update} from "@/api/productionManagement/processRoute.js";
import {getByModel} from "@/api/productionManagement/productBom.js";
import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
const props = defineProps({
@@ -99,7 +71,6 @@
  productModelId: undefined,
  productName: "",
  productModelName: "",
  bomId: undefined,
  description: '',
});
@@ -113,7 +84,6 @@
});
const showProductSelectDialog = ref(false);
const bomOptions = ref([]);
let { proxy } = getCurrentInstance()
@@ -129,92 +99,29 @@
      productId: props.record.productId,
      productModelId: props.record.productModelId,
      productName: props.record.productName || "",
      // æ³¨æ„ï¼šrecord中的字段是model,需要映射到productModelName
      productModelName: props.record.model || props.record.productModelName || "",
      bomId: props.record.bomId,
      description: props.record.description || '',
    };
    // å¦‚果有产品型号ID,加载BOM列表
    if (props.record.productModelId) {
      loadBomList(props.record.productModelId);
    }
  }
}
// åŠ è½½BOM列表
const loadBomList = async (productModelId) => {
  if (!productModelId) {
    bomOptions.value = [];
    return;
  }
  try {
    const res = await getByModel(productModelId);
    // å¤„理返回的BOM数据:可能是数组、对象或包含data字段
    let bomList = [];
    if (Array.isArray(res)) {
      bomList = res;
    } else if (res && res.data) {
      bomList = Array.isArray(res.data) ? res.data : [res.data];
    } else if (res && typeof res === 'object') {
      bomList = [res];
    }
    bomOptions.value = bomList;
  } catch (error) {
    console.error("加载BOM列表失败:", error);
    bomOptions.value = [];
  }
};
// äº§å“é€‰æ‹©å¤„理
const handleProductSelect = async (products) => {
const handleProductSelect = (products) => {
  if (products && products.length > 0) {
    const product = products[0];
    // å…ˆæŸ¥è¯¢BOM列表(必选)
    try {
      const res = await getByModel(product.id);
      // å¤„理返回的BOM数据:可能是数组、对象或包含data字段
      let bomList = [];
      if (Array.isArray(res)) {
        bomList = res;
      } else if (res && res.data) {
        bomList = Array.isArray(res.data) ? res.data : [res.data];
      } else if (res && typeof res === 'object') {
        bomList = [res];
      }
      if (bomList.length > 0) {
        formState.value.productModelId = product.id;
        formState.value.productName = product.productName;
        formState.value.productModelName = product.model;
        // å¦‚果当前选择的BOM不在新列表中,则重置BOM选择
        const currentBomExists = bomList.some(bom => bom.id === formState.value.bomId);
        if (!currentBomExists) {
          formState.value.bomId = undefined;
        }
        bomOptions.value = bomList;
        showProductSelectDialog.value = false;
        // è§¦å‘表单验证更新
        proxy.$refs["formRef"]?.validateField('productModelId');
      } else {
        proxy.$modal.msgError("该产品没有BOM,请先创建BOM");
      }
    } catch (error) {
      // å¦‚果接口返回404或其他错误,说明没有BOM
      proxy.$modal.msgError("该产品没有BOM,请先创建BOM");
    }
    formState.value.productModelId = product.id;
    formState.value.productName = product.productName;
    formState.value.productModelName = product.model;
    showProductSelectDialog.value = false;
    proxy.$refs["formRef"]?.validateField('productModelId');
  }
};
const handleSubmit = () => {
  proxy.$refs["formRef"].validate(valid => {
    if (valid) {
      // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“å’ŒBOM
      if (!formState.value.productModelId) {
        proxy.$modal.msgError("请选择产品");
        return;
      }
      if (!formState.value.bomId) {
        proxy.$modal.msgError("请选择BOM");
        return;
      }
      update(formState.value).then(res => {
src/views/productionManagement/processRoute/New.vue
@@ -24,26 +24,6 @@
              : '选择产品' }}
          </el-button>
        </el-form-item>
        <el-form-item label="BOM"
                      prop="bomId"
                      :rules="[
                {
                required: true,
                message: '请选择BOM',
                trigger: 'change',
              }
            ]">
          <el-select v-model="formState.bomId"
                     placeholder="请选择BOM"
                     clearable
                     :disabled="!formState.productModelId || bomOptions.length === 0"
                     style="width: 100%">
            <el-option v-for="item in bomOptions"
                       :key="item.id"
                       :label="item.bomNo || `BOM-${item.id}`"
                       :value="item.id" />
          </el-select>
        </el-form-item>
        <el-form-item label="备注"
                      prop="description">
          <el-input v-model="formState.description"
@@ -68,7 +48,6 @@
<script setup>
  import { ref, computed, getCurrentInstance } from "vue";
  import { add } from "@/api/productionManagement/processRoute.js";
  import { getByModel } from "@/api/productionManagement/productBom.js";
  import ProductSelectDialog from "@/views/basicData/product/ProductSelectDialog.vue";
  const props = defineProps({
@@ -86,7 +65,6 @@
    productModelId: undefined,
    productName: "",
    productModelName: "",
    bomId: undefined,
    description: "",
  });
@@ -100,7 +78,6 @@
  });
  const showProductSelectDialog = ref(false);
  const bomOptions = ref([]);
  let { proxy } = getCurrentInstance();
@@ -111,59 +88,28 @@
      productModelId: undefined,
      productName: "",
      productModelName: "",
      bomId: undefined,
      description: "",
    };
    bomOptions.value = [];
    isShow.value = false;
  };
  // äº§å“é€‰æ‹©å¤„理
  const handleProductSelect = async products => {
  const handleProductSelect = products => {
    if (products && products.length > 0) {
      const product = products[0];
      // å…ˆæŸ¥è¯¢BOM列表(必选)
      try {
        const res = await getByModel(product.id);
        // å¤„理返回的BOM数据:可能是数组、对象或包含data字段
        let bomList = [];
        if (Array.isArray(res)) {
          bomList = res;
        } else if (res && res.data) {
          bomList = Array.isArray(res.data) ? res.data : [res.data];
        } else if (res && typeof res === "object") {
          bomList = [res];
        }
        if (bomList.length > 0) {
          formState.value.productModelId = product.id;
          formState.value.productName = product.productName;
          formState.value.productModelName = product.model;
          formState.value.bomId = undefined; // é‡ç½®BOM选择
          bomOptions.value = bomList;
          showProductSelectDialog.value = false;
          // è§¦å‘表单验证更新
          proxy.$refs["formRef"]?.validateField("productModelId");
        } else {
          proxy.$modal.msgError("该产品没有BOM,请先创建BOM");
        }
      } catch (error) {
        // å¦‚果接口返回404或其他错误,说明没有BOM
        proxy.$modal.msgError("该产品没有BOM,请先创建BOM");
      }
      formState.value.productModelId = product.id;
      formState.value.productName = product.productName;
      formState.value.productModelName = product.model;
      showProductSelectDialog.value = false;
      proxy.$refs["formRef"]?.validateField("productModelId");
    }
  };
  const handleSubmit = () => {
    proxy.$refs["formRef"].validate(valid => {
      if (valid) {
        // éªŒè¯æ˜¯å¦é€‰æ‹©äº†äº§å“å’ŒBOM
        if (!formState.value.productModelId) {
          proxy.$modal.msgError("请选择产品");
          return;
        }
        if (!formState.value.bomId) {
          proxy.$modal.msgError("请选择BOM");
          return;
        }
        console.log(formState.value, "formState.value====");
src/views/productionManagement/processRoute/index.vue
@@ -89,10 +89,6 @@
      prop: "model",
    },
    {
      label: "BOM编号",
      prop: "bomNo",
    },
    {
      label: "描述",
      prop: "description",
    },
@@ -263,8 +259,6 @@
        processRouteCode: row.processRouteCode || "",
        productName: row.productName || "",
        model: row.model || "",
        bomNo: row.bomNo || "",
        bomId: row.bomId || "",
        description: row.description || "",
        type: "route",
      },
src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -30,14 +30,6 @@
            <span class="info-value">{{ routeInfo.model || '-' }}</span>
          </div>
        </div>
        <div class="info-item">
          <div class="info-label-wrapper">
            <span class="info-label">BOM编号</span>
          </div>
          <div class="info-value-wrapper">
            <span class="info-value">{{ routeInfo.bomNo || '-' }}</span>
          </div>
        </div>
        <div class="info-item"
             v-if="routeInfo.quantity && routeInfo.quantity !== 0">
          <div class="info-label-wrapper">
@@ -253,164 +245,6 @@
        </div>
      </div>
    </template>
    <!-- bom模块 -->
    <div class="section-header"
         style="margin-top: 20px;">
      <div class="section-title">BOM ç»“æž„</div>
      <div class="section-actions"
           v-if="pageType === 'order' && editable">
        <el-button v-if="!bomDataValue.isEdit"
                   type="primary"
                   @click="bomDataValue.isEdit = true">
          ç¼–辑
        </el-button>
        <el-button v-if="bomDataValue.isEdit"
                   @click="cancelEditBom">
          å–消
        </el-button>
        <el-button v-if="bomDataValue.isEdit"
                   type="primary"
                   @click="handleSaveBom"
                   :loading="bomDataValue.loading">
          ä¿å­˜BOM
        </el-button>
      </div>
    </div>
    <el-table :data="bomTableData"
              border
              :preserve-expanded-content="false"
              :default-expand-all="true"
              style="width: 100%">
      <el-table-column type="expand">
        <template #default>
          <el-form ref="bomFormRef"
                   :model="bomDataValue">
            <el-table :data="bomDataValue.dataList"
                      row-key="tempId"
                      default-expand-all
                      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
                      style="width: 100%">
              <el-table-column prop="productName"
                               label="产品" />
              <el-table-column prop="model"
                               label="规格">
                <template #default="{ row }">
                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
                                :rules="[{ required: true, message: '请选择规格', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-select v-model="row.model"
                               placeholder="请选择规格"
                               clearable
                               :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)"
                               style="width: 100%"
                               @visible-change="(v) => { if (v) openBomDialog(row.tempId) }">
                      <el-option v-if="row.model"
                                 :label="row.model"
                                 :value="row.model" />
                    </el-select>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="processName"
                               label="消耗工序">
                <template #default="{ row }">
                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
                                :rules="bomDataValue.dataList.some(item => (item).tempId === row.tempId) ? [] : [{ required: true, message: '请选择消耗工序', trigger: 'change' }]"
                                style="margin: 0">
                    <el-select v-model="row.processId"
                               placeholder="请选择"
                               filterable
                               clearable
                               style="width: 100%"
                               @change="value => handleBomProcessChange(row, value)"
                               :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)">
                      <el-option v-for="item in bomDataValue.processOptions"
                                 :key="item.id"
                                 :label="item.name"
                                 :value="item.id" />
                    </el-select>
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="unitQuantity"
                               label="单位产出所需数量">
                <template #default="{ row }">
                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
                                :rules="[{ required: true, message: '请输入单位产出所需数量', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input-number v-model="row.unitQuantity"
                                     :min="0"
                                     :precision="2"
                                     :step="1"
                                     controls-position="right"
                                     style="width: 100%"
                                     @change="handleUnitQuantityChange"
                                     :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column v-if="pageType === 'order'"
                               prop="demandedQuantity"
                               label="需求总量">
                <template #default="{ row }">
                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
                                :rules="[{ required: true, message: '请输入需求总量', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input-number v-model="row.demandedQuantity"
                                     :min="0"
                                     :precision="2"
                                     :step="1"
                                     controls-position="right"
                                     style="width: 100%"
                                     :disabled="true" />
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column prop="unit"
                               label="单位">
                <template #default="{ row }">
                  <el-form-item v-if="pageType === 'order' && bomDataValue.isEdit"
                                :rules="[{ required: true, message: '请输入单位', trigger: ['blur','change'] }]"
                                style="margin: 0">
                    <el-input v-model="row.unit"
                              placeholder="请输入单位"
                              clearable
                              :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
                  </el-form-item>
                </template>
              </el-table-column>
              <el-table-column label="操作"
                               fixed="right"
                               width="200"
                               v-if="pageType === 'order' && bomDataValue.isEdit">
                <template #default="{ row }">
                  <el-button v-if="bomDataValue.isEdit && !bomDataValue.dataList.some(item => (item).tempId === row.tempId)"
                             type="danger"
                             text
                             @click="removeBomItem(row.tempId)">删除
                  </el-button>
                  <el-button v-if="bomDataValue.isEdit"
                             type="primary"
                             text
                             @click="addBomItem(row.tempId)">添加
                  </el-button>
                </template>
              </el-table-column>
            </el-table>
          </el-form>
        </template>
      </el-table-column>
      <el-table-column label="BOM编号"
                       prop="bomNo" />
      <el-table-column label="产品名称"
                       prop="productName" />
      <el-table-column label="规格型号"
                       prop="model" />
    </el-table>
    <ProductSelectDialog v-if="bomDataValue.showProductDialog"
                         v-model="bomDataValue.showProductDialog"
                         :single="true"
                         @confirm="handleBomProduct" />
    <!-- ä¸Šä¼ ç»„件弹窗 -->
    <el-dialog v-model="uploadDialogVisible"
               title="上传附件"
@@ -553,12 +387,6 @@
    sortRouteItem,
  } from "@/api/productionManagement/productProcessRoute.js";
  import { processList } from "@/api/productionManagement/productionProcess.js";
  import { listProcessBom } from "@/api/productionManagement/productionOrder.js";
  import {
    queryList,
    queryList2,
    add2,
  } from "@/api/productionManagement/productStructure.js";
  import AttachmentUpload from "@/components/AttachmentUpload/file/index.vue";
  import {
    attachmentList,
@@ -584,7 +412,6 @@
  const dialogVisible = ref(false);
  const operationType = ref("add"); // add | edit
  const formRef = ref(null);
  const bomFormRef = ref(null);
  const submitLoading = ref(false);
  const cardsContainer = ref(null);
  const tableRef = ref(null);
@@ -593,7 +420,6 @@
    processRouteCode: "",
    productName: "",
    model: "",
    bomNo: "",
    description: "",
    quantity: 0,
    technologyRoutingId: "",
@@ -770,7 +596,6 @@
    processList({ size: -1, current: -1 })
      .then(res => {
        processOptions.value = res.data.records || [];
        bomDataValue.value.processOptions = processOptions.value;
      })
      .catch(err => {
        console.error("获取工序失败:", err);
@@ -783,16 +608,11 @@
      processRouteCode: route.query.processRouteCode || "",
      productName: route.query.productName || "",
      model: route.query.model || "",
      bomNo: route.query.bomNo || "",
      bomId: route.query.bomId || "",
      description: route.query.description || "",
      quantity: route.query.quantity || 0,
      technologyRoutingId: route.query.technologyRoutingId || "",
      status: !(route.query.status == 1 || route.query.status === "false"),
    };
    bomTableData.value[0].productName = routeInfo.value.productName;
    bomTableData.value[0].model = routeInfo.value.model;
    bomTableData.value[0].bomNo = routeInfo.value.bomNo;
  };
  // æ–°å¢ž
@@ -1147,103 +967,6 @@
    }
  };
  // BOM相关状态和方法
  const bomTableData = ref([
    {
      productName: "",
      model: "",
      bomNo: "",
    },
  ]);
  const bomDataValue = ref({
    dataList: [],
    processOptions: [],
    showProductDialog: false,
    currentRowName: null,
    loading: false,
    isEdit: false,
  });
  const syncProcessOperationFields = item => {
    const processId =
      item.processId ?? item.operationId ?? item.technologyOperationId ?? "";
    if (!processId) {
      item.processId = "";
      return;
    }
    const option = bomDataValue.value.processOptions.find(
      p => p.id === processId
    );
    const processName =
      option?.name || item.processName || item.operationName || "";
    item.processId = processId;
    if (pageType.value === "order") {
      item.technologyOperationId = processId;
    } else {
      item.operationId = processId;
    }
    item.processName = processName;
    item.operationName = processName;
  };
  const normalizeTreeData = items => {
    items.forEach(item => {
      item.tempId = item.tempId || item.id || `${Date.now()}_${Math.random()}`;
      syncProcessOperationFields(item);
      if (Array.isArray(item.children) && item.children.length > 0) {
        normalizeTreeData(item.children);
      }
    });
  };
  const toQuantityNumber = value => {
    const numberValue = Number(value);
    if (!Number.isFinite(numberValue)) {
      return 0;
    }
    return Number(numberValue.toFixed(2));
  };
  const syncDemandedQuantityTree = (items, parentDemandedQuantity = null) => {
    items.forEach(item => {
      if (parentDemandedQuantity !== null) {
        item.demandedQuantity = toQuantityNumber(
          parentDemandedQuantity * toQuantityNumber(item.unitQuantity)
        );
      }
      if (Array.isArray(item.children) && item.children.length > 0) {
        syncDemandedQuantityTree(
          item.children,
          toQuantityNumber(item.demandedQuantity)
        );
      }
    });
  };
  const recalculateDemandedQuantities = () => {
    if (pageType.value !== "order") {
      return;
    }
    const rootDemandedQuantity = routeInfo.value.quantity;
    if (
      rootDemandedQuantity === undefined ||
      rootDemandedQuantity === null ||
      rootDemandedQuantity === ""
    ) {
      syncDemandedQuantityTree(bomDataValue.value.dataList);
      return;
    }
    syncDemandedQuantityTree(
      bomDataValue.value.dataList,
      toQuantityNumber(rootDemandedQuantity)
    );
  };
  const processChange = value => {
    processOptions.value.forEach(item => {
      if (item.id == value) {
@@ -1254,340 +977,10 @@
    });
  };
  const findSiblings = (items, tempId) => {
    if (!items || items.length === 0) return null;
    // æ£€æŸ¥å½“前层级
    if (items.some(item => item.tempId === tempId)) {
      return items;
    }
    // é€’归查找子级
    for (const item of items) {
      if (item.children && item.children.length > 0) {
        const result = findSiblings(item.children, tempId);
        if (result) return result;
      }
    }
    return null;
  };
  const handleBomProcessChange = (row, value) => {
    row.processId = value || "";
    syncProcessOperationFields(row);
    // æ£€æŸ¥åŒä¸€å±‚级是否已经有其他不同的工序被选中
    const siblings = findSiblings(bomDataValue.value.dataList, row.tempId);
    if (siblings && value) {
      const hasDifferentProcess = siblings.some(sibling => {
        return (
          sibling.tempId !== row.tempId &&
          sibling.processId &&
          sibling.processId !== value
        );
      });
      if (hasDifferentProcess) {
        ElMessage.warning("同一层级已存在不同的工序,请先统一工序后再进行修改");
      }
    }
  };
  const openBomDialog = tempId => {
    bomDataValue.value.currentRowName = tempId;
    bomDataValue.value.showProductDialog = true;
  };
  const fetchBomData = async () => {
    try {
      const isOrderPage = pageType.value === "order";
      const { data } = await (isOrderPage ? queryList2 : queryList)(
        routeInfo.value.bomId
      );
      bomDataValue.value.dataList = data || [];
      normalizeTreeData(bomDataValue.value.dataList);
      recalculateDemandedQuantities();
    } catch (err) {
      console.error("获取BOM数据失败:", err);
    }
  };
  const childItem = (item, tempId, productData) => {
    if (item.tempId === tempId) {
      item.productName = productData.productName;
      item.model = productData.model;
      item.productModelId = productData.id;
      item.unit = productData.unit || "";
      return true;
    }
    if (item.children && item.children.length > 0) {
      for (let child of item.children) {
        if (childItem(child, tempId, productData)) {
          return true;
        }
      }
    }
    return false;
  };
  const handleBomProduct = row => {
    if (!Array.isArray(row) || row.length === 0) {
      ElMessage.warning("请选择一个产品");
      return;
    }
    const productData = row[row.length - 1];
    const isTopLevel = bomDataValue.value.dataList.some(
      item => item.tempId === bomDataValue.value.currentRowName
    );
    if (isTopLevel) {
      if (
        productData.productName === bomTableData.value[0].productName &&
        productData.model === bomTableData.value[0].model
      ) {
        const hasOther = bomDataValue.value.dataList.some(
          item =>
            item.tempId !== bomDataValue.value.currentRowName &&
            item.productName === bomTableData.value[0].productName &&
            item.model === bomTableData.value[0].model
        );
        if (hasOther) {
          ElMessage.warning("最外层和当前产品一样的一级只能有一个");
          return;
        }
      }
    }
    bomDataValue.value.dataList.forEach(item => {
      if (item.tempId === bomDataValue.value.currentRowName) {
        item.productName = productData.productName;
        item.model = productData.model;
        item.productModelId = productData.id;
        item.unit = productData.unit || "";
        return;
      }
      childItem(item, bomDataValue.value.currentRowName, productData);
    });
    bomDataValue.value.showProductDialog = false;
  };
  const removeBomItem = tempId => {
    const topIndex = bomDataValue.value.dataList.findIndex(
      item => item.tempId === tempId
    );
    if (topIndex !== -1) {
      bomDataValue.value.dataList.splice(topIndex, 1);
      return;
    }
    const delchildItem = (items, tempId) => {
      for (let i = 0; i < items.length; i++) {
        const item = items[i];
        if (item.tempId === tempId) {
          items.splice(i, 1);
          return true;
        }
        if (item.children && item.children.length > 0) {
          if (delchildItem(item.children, tempId)) {
            return true;
          }
        }
      }
      return false;
    };
    bomDataValue.value.dataList.forEach(item => {
      if (item.children && item.children.length > 0) {
        delchildItem(item.children, tempId);
      }
    });
  };
  const handleUnitQuantityChange = () => {
    recalculateDemandedQuantities();
  };
  const addchildItem = (item, tempId) => {
    if (item.tempId === tempId) {
      if (!item.children) {
        item.children = [];
      }
      item.children.push({
        parentId: item.id || "",
        parentTempId: item.tempId || "",
        productName: "",
        productId: "",
        model: undefined,
        productModelId: undefined,
        processId: "",
        processName: "",
        [pageType.value === "order" ? "technologyOperationId" : "operationId"]:
          "",
        operationName: "",
        unitQuantity: 1,
        demandedQuantity: 0,
        children: [],
        unit: "",
        tempId: new Date().getTime(),
      });
      recalculateDemandedQuantities();
      return true;
    }
    if (item.children && item.children.length > 0) {
      for (let child of item.children) {
        if (addchildItem(child, tempId)) {
          return true;
        }
      }
    }
    return false;
  };
  const addBomItem = tempId => {
    bomDataValue.value.dataList.forEach(item => {
      if (item.tempId === tempId) {
        if (!item.children) {
          item.children = [];
        }
        item.children.push({
          parentId: item.id || "",
          parentTempId: item.tempId || "",
          productName: "",
          productId: "",
          model: undefined,
          productModelId: undefined,
          processId: "",
          processName: "",
          [pageType.value === "order" ? "technologyOperationId" : "operationId"]:
            "",
          operationName: "",
          unitQuantity: 1,
          demandedQuantity: 0,
          unit: "",
          children: [],
          tempId: new Date().getTime(),
        });
        recalculateDemandedQuantities();
        return;
      }
      addchildItem(item, tempId);
    });
  };
  const validateAllBom = () => {
    let isValid = true;
    const isOrderPage = pageType.value === "order";
    const validateItem = (item, isTopLevel = false) => {
      if (!item.model) {
        ElMessage.error("请选择规格");
        isValid = false;
        return;
      }
      if (!isTopLevel && !item.processId) {
        ElMessage.error("请选择消耗工序");
        isValid = false;
        return;
      }
      if (!item.unitQuantity) {
        ElMessage.error("请输入单位产出所需数量");
        isValid = false;
        return;
      }
      if (isOrderPage && !item.demandedQuantity) {
        ElMessage.error("请输入需求总量");
        isValid = false;
        return;
      }
      if (item.children && item.children.length > 0) {
        item.children.forEach(child => {
          validateItem(child, false);
        });
      }
    };
    // æ ¡éªŒåŒä¸€å±‚级的工序是否一致
    const validateProcessConsistency = items => {
      if (!items || items.length === 0) return;
      // æ£€æŸ¥å½“前层级
      const processes = items
        .filter(item => item.processId)
        .map(item => item.processId);
      if (processes.length > 1) {
        const uniqueProcesses = [...new Set(processes)];
        if (uniqueProcesses.length > 1) {
          ElMessage.error("同一层级的工序必须一致");
          isValid = false;
          return;
        }
      }
      // é€’归检查子级
      items.forEach(item => {
        if (item.children && item.children.length > 0) {
          validateProcessConsistency(item.children);
        }
      });
    };
    bomDataValue.value.dataList.forEach(item => {
      validateItem(item, true);
    });
    validateProcessConsistency(bomDataValue.value.dataList);
    return isValid;
  };
  const buildSubmitTree = items => {
    return items.map(item => {
      const current = { ...item };
      syncProcessOperationFields(current);
      current.children = Array.isArray(current.children)
        ? buildSubmitTree(current.children)
        : [];
      return current;
    });
  };
  const cancelEditBom = () => {
    bomDataValue.value.isEdit = false;
    fetchBomData();
  };
  const handleSaveBom = () => {
    bomDataValue.value.loading = true;
    console.log(bomDataValue.value.dataList, "bomDataValue.value.dataList");
    normalizeTreeData(bomDataValue.value.dataList);
    recalculateDemandedQuantities();
    const valid = validateAllBom();
    if (valid) {
      add2({
        // bomId: Number(routeInfo.value.bomId),
        productionOrderBomId: Number(routeInfo.value.bomId) || null,
        children: buildSubmitTree(bomDataValue.value.dataList || []),
      })
        .then(() => {
          ElMessage.success("BOM保存成功");
          bomDataValue.value.isEdit = false;
          refreshCurrentPage();
        })
        .catch(() => {
          ElMessage.error("BOM保存失败");
        })
        .finally(() => {
          bomDataValue.value.loading = false;
        });
    } else {
      bomDataValue.value.loading = false;
    }
  };
  const refreshCurrentPage = () => {
    getRouteInfo();
    getList();
    getProcessList();
    fetchBomData();
    if (pageType.value === "order") {
      getAttachmentList();
    }
src/views/productionManagement/productionOrder/index.vue
@@ -192,7 +192,8 @@
        </div>
      </div>
    </el-dialog>
    <MaterialLedgerDialog v-model="materialDialogVisible"
    <!-- é¢†æ–™/补料/领料详情功能已隐藏 -->
    <!-- <MaterialLedgerDialog v-model="materialDialogVisible"
                          :order-row="currentMaterialOrder"
                          @saved="getList" />
    <MaterialDetailDialog v-model="materialDetailDialogVisible"
@@ -200,16 +201,16 @@
                          @confirmed="getList" />
    <MaterialSupplementDialog v-model="materialSupplementDialogVisible"
                              :order-row="currentMaterialSupplementOrder"
                              @saved="getList" />
                              @saved="getList" /> -->
    <new-product-order v-if="isShowNewModal"
                       v-model:visible="isShowNewModal"
                       @completed="handleQuery" />
    <!-- æ‰“印领料单组件 -->
    <div class="print-requisition-wrapper">
    <!-- æ‰“印领料单功能已隐藏 -->
    <!-- <div class="print-requisition-wrapper">
      <PrintMaterialRequisition ref="printRef"
                                :order-row="printOrderRow"
                                :material-list="printMaterialList" />
    </div>
    </div> -->
  </div>
</template>
@@ -237,16 +238,18 @@
  } from "@/api/productionManagement/productionOrder.js";
  import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
  import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
  import MaterialLedgerDialog from "@/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue";
  import MaterialDetailDialog from "@/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue";
  import MaterialSupplementDialog from "@/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue";
  import PrintMaterialRequisition from "@/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue";
  // é¢†æ–™/补料/领料详情/打印领料单功能已隐藏
  // import MaterialLedgerDialog from "@/views/productionManagement/productionOrder/components/MaterialLedgerDialog.vue";
  // import MaterialDetailDialog from "@/views/productionManagement/productionOrder/components/MaterialDetailDialog.vue";
  // import MaterialSupplementDialog from "@/views/productionManagement/productionOrder/components/MaterialSupplementDialog.vue";
  // import PrintMaterialRequisition from "@/views/productionManagement/productionOrder/components/PrintMaterialRequisition.vue";
  import PIMTable from "@/components/PIMTable/PIMTable.vue";
  import { listPage } from "@/api/productionManagement/processRoute.js";
  import {
    listMaterialPickingDetail,
    listMaterialPickingBom,
  } from "@/api/productionManagement/productionOrder.js";
  // é¢†æ–™åŠŸèƒ½å·²éšè—
  // import {
  //   listMaterialPickingDetail,
  //   listMaterialPickingBom,
  // } from "@/api/productionManagement/productionOrder.js";
  const NewProductOrder = defineAsyncComponent(() =>
    import("@/views/productionManagement/productionOrder/New.vue")
  );
@@ -398,41 +401,42 @@
            showSourceData(row);
          },
        },
        {
          name: "领料",
          type: "text",
          color: "#5EC7AB",
          showHide: row => !row.endOrder && !row.returned,
          clickFun: row => {
            openMaterialDialog(row);
          },
        },
        {
          name: "补料",
          type: "text",
          color: "#5EC7AB",
          showHide: row => !row.endOrder && !row.returned,
          clickFun: row => {
            openMaterialSupplementDialog(row);
          },
        },
        {
          name: "领料详情",
          type: "text",
          color: "#5EC7AB",
          clickFun: row => {
            openMaterialDetailDialog(row);
          },
        },
        {
          name: "打印领料单",
          type: "text",
          color: "#5EC7AB",
          showHide: row => !row.endOrder,
          clickFun: row => {
            handlePrint(row);
          },
        },
        // é¢†æ–™/补料/领料详情/打印领料单功能已隐藏
        // {
        //   name: "领料",
        //   type: "text",
        //   color: "#5EC7AB",
        //   showHide: row => !row.endOrder && !row.returned,
        //   clickFun: row => {
        //     openMaterialDialog(row);
        //   },
        // },
        // {
        //   name: "补料",
        //   type: "text",
        //   color: "#5EC7AB",
        //   showHide: row => !row.endOrder && !row.returned,
        //   clickFun: row => {
        //     openMaterialSupplementDialog(row);
        //   },
        // },
        // {
        //   name: "领料详情",
        //   type: "text",
        //   color: "#5EC7AB",
        //   clickFun: row => {
        //     openMaterialDetailDialog(row);
        //   },
        // },
        // {
        //   name: "打印领料单",
        //   type: "text",
        //   color: "#5EC7AB",
        //   showHide: row => !row.endOrder,
        //   clickFun: row => {
        //     handlePrint(row);
        //   },
        // },
        {
          name: "生产追溯",
          type: "text",
@@ -525,48 +529,48 @@
    orderId: null,
    routeId: null,
  });
  const materialDialogVisible = ref(false);
  const currentMaterialOrder = ref(null);
  const materialDetailDialogVisible = ref(false);
  const currentMaterialDetailOrder = ref(null);
  const materialSupplementDialogVisible = ref(false);
  const currentMaterialSupplementOrder = ref(null);
  // é¢†æ–™/补料/领料详情功能已隐藏
  // const materialDialogVisible = ref(false);
  // const currentMaterialOrder = ref(null);
  // const materialDetailDialogVisible = ref(false);
  // const currentMaterialDetailOrder = ref(null);
  // const materialSupplementDialogVisible = ref(false);
  // const currentMaterialSupplementOrder = ref(null);
  // æ‰“印相关
  const printOrderRow = ref(null);
  const printMaterialList = ref([]);
  const handlePrint = async row => {
    printOrderRow.value = row;
    proxy.$modal.loading("正在获取领料数据...");
    try {
      printMaterialList.value = [];
      const detailRes = await listMaterialPickingDetail(row.id);
      const detailList = Array.isArray(detailRes?.data)
        ? detailRes.data
        : detailRes?.data?.records || [];
      if (detailList.length > 0) {
        printMaterialList.value = detailList;
      }
      if (printMaterialList.value.length === 0) {
        proxy.$modal.msgWarning("暂无领料数据");
        return;
      }
      // ç­‰å¾… DOM æ›´æ–°åŽæ‰§è¡Œæ‰“印
      proxy.$nextTick(() => {
        setTimeout(() => {
          window.print();
        }, 800);
      });
    } catch (e) {
      console.error("获取领料数据失败:", e);
      proxy.$modal.msgError("获取领料数据失败");
    } finally {
      proxy.$modal.closeLoading();
    }
  };
  // æ‰“印领料单功能已隐藏
  // const printOrderRow = ref(null);
  // const printMaterialList = ref([]);
  // const handlePrint = async row => {
  //   printOrderRow.value = row;
  //   proxy.$modal.loading("正在获取领料数据...");
  //   try {
  //     printMaterialList.value = [];
  //     const detailRes = await listMaterialPickingDetail(row.id);
  //     const detailList = Array.isArray(detailRes?.data)
  //       ? detailRes.data
  //       : detailRes?.data?.records || [];
  //
  //     if (detailList.length > 0) {
  //       printMaterialList.value = detailList;
  //     }
  //
  //     if (printMaterialList.value.length === 0) {
  //       proxy.$modal.msgWarning("暂无领料数据");
  //       return;
  //     }
  //
  //     proxy.$nextTick(() => {
  //       setTimeout(() => {
  //         window.print();
  //       }, 800);
  //     });
  //   } catch (e) {
  //     console.error("获取领料数据失败:", e);
  //     proxy.$modal.msgError("获取领料数据失败");
  //   } finally {
  //     proxy.$modal.closeLoading();
  //   }
  // };
  const openBindRouteDialog = async (row, type) => {
    bindForm.orderId = row.id;
@@ -612,20 +616,21 @@
    }
  };
  const openMaterialDialog = row => {
    currentMaterialOrder.value = row;
    materialDialogVisible.value = true;
  };
  // é¢†æ–™/补料/领料详情功能已隐藏
  // const openMaterialDialog = row => {
  //   currentMaterialOrder.value = row;
  //   materialDialogVisible.value = true;
  // };
  const openMaterialDetailDialog = async row => {
    currentMaterialDetailOrder.value = row;
    materialDetailDialogVisible.value = true;
  };
  // const openMaterialDetailDialog = async row => {
  //   currentMaterialDetailOrder.value = row;
  //   materialDetailDialogVisible.value = true;
  // };
  const openMaterialSupplementDialog = row => {
    currentMaterialSupplementOrder.value = row;
    materialSupplementDialogVisible.value = true;
  };
  // const openMaterialSupplementDialog = row => {
  //   currentMaterialSupplementOrder.value = row;
  //   materialSupplementDialogVisible.value = true;
  // };
  const handleReset = () => {
    searchForm.value = {
src/views/productionManagement/productionReporting/index.vue
@@ -244,13 +244,13 @@
      fixed: "right",
      width: 250,
      operation: [
        {
          name: "查看投入",
          type: "text",
          clickFun: row => {
            showInput(row);
          },
        },
        // {
        //   name: "查看投入",
        //   type: "text",
        //   clickFun: row => {
        //     showInput(row);
        //   },
        // },
        {
          name: "参数详情",
          type: "text",
src/views/productionManagement/productionTraceability/index.vue
@@ -173,9 +173,9 @@
                             align="center"
                             width="200">
              <template #default="{ row }">
                <el-button type="primary"
                           link
                           @click="showInput(row.id)">查看投入</el-button>
<!--                <el-button type="primary"-->
<!--                           link-->
<!--                           @click="showInput(row.id)">查看投入</el-button>-->
                <el-button type="primary"
                           link
                           @click="showParamDetail(row.productionOperationParamList)">参数详情</el-button>
src/views/productionManagement/workOrderEdit/index.vue
@@ -47,16 +47,16 @@
               label-width="120px">
        <el-form-item label="计划开始时间">
          <el-date-picker v-model="editrow.planStartTime"
                          type="date"
                          type="datetime"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          value-format="YYYY-MM-DD HH:mm:ss"
                          style="width: 300px" />
        </el-form-item>
        <el-form-item label="计划结束时间">
          <el-date-picker v-model="editrow.planEndTime"
                          type="date"
                          type="datetime"
                          placeholder="请选择"
                          value-format="YYYY-MM-DD"
                          value-format="YYYY-MM-DD HH:mm:ss"
                          style="width: 300px" />
        </el-form-item>
      </el-form>
src/views/productionManagement/workOrderManagement/index.vue
@@ -24,6 +24,11 @@
          <el-button type="primary"
                     @click="handleQuery">搜索</el-button>
        </div>
        <div class="search-item">
          <el-switch v-model="filterMine"
                     active-text="仅看我的"
                     @change="handleQuery" />
        </div>
      </div>
    </div>
    <div class="table_list">
@@ -115,9 +120,9 @@
                   @click="printTransferCard">打印流转卡</el-button>
      </div>
    </el-dialog>
    <!-- æŠ¥å·¥å¼¹çª— -->
    <!-- æŠ¥å·¥å¼¹çª—(结束报工用) -->
    <el-dialog v-model="reportDialogVisible"
               title="报工"
               title="结束报工"
               width="500px">
      <el-form ref="reportFormRef"
               :model="reportForm"
@@ -161,7 +166,6 @@
                       :value="user.userId" />
          </el-select>
        </el-form-item>
        <!-- å·¥æ—¶ -->
        <el-form-item label="工时"
                      v-if="currentReportRowData?.type == 0"
                      prop="workHour">
@@ -245,7 +249,7 @@
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
                     @click="handleReport">确定</el-button>
                     @click="handleFinishWork">确定</el-button>
          <el-button @click="reportDialogVisible = false">取消</el-button>
        </span>
      </template>
@@ -268,9 +272,9 @@
  import {
    productWorkOrderPage,
    addProductMain,
    startWork,
    downProductWorkOrder,
  } from "@/api/productionManagement/workOrder.js";
  import { listMaterialPickingDetail } from "@/api/productionManagement/productionOrder.js";
  import { findProcessParamListOrder } from "@/api/productionManagement/productProcessRoute.js";
  import { getUserProfile, userListNoPageByTenantId } from "@/api/system/user.js";
  import { getDicts } from "@/api/system/dict/data";
@@ -285,124 +289,59 @@
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
  const isCompleted = row => {
    const status = Number(row?.completionStatus);
    return Number.isFinite(status) && status >= 100;
  };
  const canOperate = row => !row.endOrder && !isCompleted(row);
  const tableColumn = ref([
    { label: "工单类型", prop: "workOrderType", width: "80" },
    { label: "工单编号", prop: "workOrderNo", width: "140" },
    { label: "生产订单号", prop: "npsNo", width: "140" },
    { label: "产品名称", prop: "productName", width: "140" },
    { label: "规格", prop: "model" },
    { label: "单位", prop: "unit" },
    { label: "工序名称", prop: "operationName", width: "100" },
    { label: "需求数量", prop: "planQuantity", width: "140" },
    { label: "完成数量", prop: "completeQuantity", width: "140" },
    { label: "完成进度", prop: "completionStatus", dataType: "slot", slot: "completionStatus", width: "140" },
    { label: "计划开始时间", prop: "planStartTime", width: "140" },
    { label: "计划结束时间", prop: "planEndTime", width: "140" },
    { label: "实际开始时间", prop: "actualStartTime", width: "140" },
    { label: "实际结束时间", prop: "actualEndTime", width: "140" },
    {
      label: "工单类型",
      prop: "workOrderType",
      width: "80",
    },
    {
      label: "工单编号",
      prop: "workOrderNo",
      width: "140",
    },
    {
      label: "生产订单号",
      prop: "npsNo",
      width: "140",
    },
    {
      label: "产品名称",
      prop: "productName",
      width: "140",
    },
    {
      label: "规格",
      prop: "model",
    },
    {
      label: "单位",
      prop: "unit",
    },
    {
      label: "工序名称",
      prop: "operationName",
      width: "100",
    },
    {
      label: "需求数量",
      prop: "planQuantity",
      width: "140",
    },
    {
      label: "完成数量",
      prop: "completeQuantity",
      width: "140",
    },
    {
      label: "完成进度",
      prop: "completionStatus",
      dataType: "slot",
      slot: "completionStatus",
      width: "140",
    },
    {
      label: "计划开始时间",
      prop: "planStartTime",
      width: "140",
    },
    {
      label: "计划结束时间",
      prop: "planEndTime",
      width: "140",
    },
    {
      label: "实际开始时间",
      prop: "actualStartTime",
      width: "140",
    },
    {
      label: "实际结束时间",
      prop: "actualEndTime",
      width: "140",
    },
    {
      label: "操作",
      width: "260",
      align: "center",
      dataType: "action",
      fixed: "right",
      label: "操作", width: "260", align: "center", dataType: "action", fixed: "right",
      operation: [
        {
          name: "流转卡",
          clickFun: row => {
            downloadAndPrintWorkOrder(row);
          },
          clickFun: row => { downloadAndPrintWorkOrder(row); },
        },
        {
          name: "附件",
          clickFun: row => {
            openWorkOrderFiles(row);
          },
          clickFun: row => { openWorkOrderFiles(row); },
        },
        // {
        //   name: "物料",
        //   clickFun: row => {
        //     openMaterialDialog(row);
        //   },
        // },
        {
          name: "报工",
          clickFun: row => {
            showReportDialog(row);
          },
          showHide: row => !row.endOrder,
          name: "开始报工",
          showHide: row => canOperate(row) && !row.actualStartTime,
          disabled: row => {
            if (row.planQuantity <= 0) return true;
            if (!row.userIds) return false;
            try {
              const userIds =
                typeof row.userIds === "string"
                  ? JSON.parse(row.userIds)
                  : row.userIds;
              const userIds = typeof row.userIds === "string" ? JSON.parse(row.userIds) : row.userIds;
              if (Array.isArray(userIds)) {
                return !userIds.some(id => String(id) === String(userStore.id));
              }
              return true;
            } catch (e) {
              return true;
            }
            } catch (e) { return true; }
          },
          clickFun: row => { handleStartWork(row); },
        },
        {
          name: "结束报工",
          showHide: row => canOperate(row) && !!row.actualStartTime,
          clickFun: row => { showReportDialog(row); },
        },
      ],
    },
@@ -410,8 +349,6 @@
  const tableData = ref([]);
  const tableLoading = ref(false);
  const transferCardVisible = ref(false);
  const transferCardData = ref([]);
  const transferCardQrUrl = ref("");
  const transferCardRowData = ref(null);
  const reportDialogVisible = ref(false);
  const fileDialogVisible = ref(false);
@@ -438,112 +375,69 @@
  const dictOptions = ref({});
  const paramLoading = ref(false);
  // ç”Ÿäº§åˆæ ¼æ•°é‡éªŒè¯è§„则
  const validateQuantity = (rule, value, callback) => {
    if (value === null || value === undefined || value === "") {
      callback(new Error("请输入生产合格数量"));
      return;
    }
    const num = Number(value);
    // æ•´æ•°ä¸”大于等于1
    if (isNaN(num) || !Number.isInteger(num) || num < 0) {
      callback(new Error("生产合格数量必须大于等于0"));
      return;
    }
    callback();
  };
  // æŠ¥åºŸæ•°é‡éªŒè¯è§„则
  const validateScrapQty = (rule, value, callback) => {
    if (value === null || value === undefined || value === "") {
      callback();
      return;
    }
    const num = Number(value);
    // æ•´æ•°ä¸”大于等于0
    if (isNaN(num) || !Number.isInteger(num) || num < 0) {
      callback(new Error("报废数量必须大于等于0"));
      return;
    }
    callback();
  };
  // éªŒè¯è§„则
  const reportFormRules = {
    quantity: [{ required: true, validator: validateQuantity, trigger: "blur" }],
    scrapQty: [{ validator: validateScrapQty, trigger: "blur" }],
  };
  // å¤„理生产合格数量输入,限制必须大于等于0
  const handleQuantityInput = value => {
    if (value === "" || value === null || value === undefined) {
      reportForm.quantity = null;
      return;
    }
    if (value === "" || value === null || value === undefined) { reportForm.quantity = null; return; }
    const num = Number(value);
    if (isNaN(num)) {
      return;
    }
    // å¦‚果小于1,清除
    if (num < 0) {
      reportForm.quantity = null;
      return;
    }
    // å¦‚果是小数取整数部分
    if (isNaN(num)) return;
    if (num < 0) { reportForm.quantity = null; return; }
    if (!Number.isInteger(num)) {
      const intValue = Math.floor(num);
      // å¦‚果取整后小于1,清除
      if (intValue < 0) {
        reportForm.quantity = null;
        return;
      }
      if (intValue < 0) { reportForm.quantity = null; return; }
      reportForm.quantity = intValue;
      return;
    }
    reportForm.quantity = num;
  };
  // å¤„理报废数量
  const handleScrapQtyInput = value => {
    if (value === "" || value === null || value === undefined) {
      reportForm.scrapQty = null;
      return;
    }
    if (value === "" || value === null || value === undefined) { reportForm.scrapQty = null; return; }
    const num = Number(value);
    // å¦‚果是NaN,保持原值
    if (isNaN(num)) {
      return;
    }
    // å¦‚果是负数,清除输入
    if (num < 0) {
      reportForm.scrapQty = null;
      return;
    }
    // å¦‚果是小数,取整数部分
    if (!Number.isInteger(num)) {
      reportForm.scrapQty = Math.floor(num);
      return;
    }
    // æœ‰æ•ˆçš„非负整数(包括0)
    if (isNaN(num)) return;
    if (num < 0) { reportForm.scrapQty = null; return; }
    if (!Number.isInteger(num)) { reportForm.scrapQty = Math.floor(num); return; }
    reportForm.scrapQty = num;
  };
  const filterMine = ref(false);
  const currentReportRowData = ref(null);
  const materialDialogVisible = ref(false);
  const currentMaterialOrderRow = ref(null);
  const page = reactive({
    current: 1,
    size: 100,
    total: 0,
  });
  const page = reactive({ current: 1, size: 100, total: 0 });
  const data = reactive({
    searchForm: {
      workOrderNo: "",
      npsNo: "",
    },
    searchForm: { workOrderNo: "", npsNo: "" },
  });
  const { searchForm } = toRefs(data);
  const toProgressPercentage = val => {
    const n = Number(val);
    if (!Number.isFinite(n)) return 0;
@@ -559,78 +453,41 @@
    return "#67c23a";
  };
  // æŸ¥è¯¢åˆ—表
  /** æœç´¢æŒ‰é’®æ“ä½œ */
  const handleQuery = () => {
    page.current = 1;
    getList();
  };
  const pagination = obj => {
    page.current = obj.page;
    page.size = obj.limit;
    getList();
  };
  const handleQuery = () => { page.current = 1; getList(); };
  const pagination = obj => { page.current = obj.page; page.size = obj.limit; getList(); };
  const getList = () => {
    tableLoading.value = true;
    const params = { ...searchForm.value, ...page };
    if (filterMine.value) { params.filterMine = true; }
    productWorkOrderPage(params)
      .then(res => {
        tableLoading.value = false;
        tableData.value = res.data.records;
        page.total = res.data.total;
      })
      .catch(() => {
        tableLoading.value = false;
      });
      .catch(() => { tableLoading.value = false; });
  };
  // ä¸‹è½½å¹¶æ‰“印工单流转卡(文件流)
  const downloadAndPrintWorkOrder = async row => {
    if (!row || !row.id) {
      proxy.$modal.msgError("缺少工单ID,无法下载流转卡");
      return;
    }
    const fileName = row.workOrderNo
      ? `工单流转卡_${row.workOrderNo}.xlsx`
      : "工单流转卡.xlsx";
    try {
      // è°ƒç”¨æŽ¥å£ï¼Œä»¥ responseType: 'blob' èŽ·å–æ–‡ä»¶æµ
      const blob = await downProductWorkOrder(row.id);
      if (!blob) {
        proxy.$modal.msgError("未获取到流转卡文件");
        return;
      }
      // åˆ›å»º Blob URL
      const fileBlob =
        blob instanceof Blob
          ? blob
          : new Blob([blob], { type: blob.type || "application/octet-stream" });
      if (!blob) { proxy.$modal.msgError("未获取到流转卡文件"); return; }
      const fileBlob = blob instanceof Blob ? blob : new Blob([blob], { type: blob.type || "application/octet-stream" });
      const url = window.URL.createObjectURL(fileBlob);
      // åˆ›å»ºéšè— iframe,用于触发浏览器打印
      const iframe = document.createElement("iframe");
      iframe.style.position = "fixed";
      iframe.style.right = "0";
      iframe.style.bottom = "0";
      iframe.style.width = "0";
      iframe.style.height = "0";
      iframe.style.border = "0";
      iframe.style.right = "0"; iframe.style.bottom = "0";
      iframe.style.width = "0"; iframe.style.height = "0"; iframe.style.border = "0";
      iframe.src = url;
      document.body.appendChild(iframe);
      iframe.onload = () => {
        try {
          iframe.contentWindow?.focus();
          iframe.contentWindow?.print();
        } catch (e) {
          console.error("自动调用打印失败", e);
          // é€€è€Œæ±‚其次,打开新窗口由用户手动打印
          window.open(url);
        }
        try { iframe.contentWindow?.focus(); iframe.contentWindow?.print(); }
        catch (e) { window.open(url); }
      };
    } catch (e) {
      console.error("下载工单流转卡失败", e);
@@ -638,17 +495,7 @@
    }
  };
  const showTransferCard = async row => {
    transferCardRowData.value = row;
    const qrContent = String(row.id);
    transferCardQrUrl.value = await QRCode.toDataURL(qrContent);
    transferCardVisible.value = true;
  };
  const printTransferCard = () => {
    window.print();
  };
  const printTransferCard = () => { window.print(); };
  const currentWorkOrderRow = ref(null);
  const openWorkOrderFiles = row => {
@@ -657,58 +504,57 @@
    fileDialogVisible.value = true;
  };
  const showReportDialog = async row => {
    if (row.productionOrderId) {
      try {
        const res = await listMaterialPickingDetail(row.productionOrderId);
        const records = Array.isArray(res.data)
          ? res.data
          : res.data?.records || [];
        if (res.code === 200 && records.length === 0) {
          proxy.$modal.msgError("未领料无法报工");
          return;
  const handleStartWork = row => {
    proxy.$modal
      .confirm("确认开始报工?", "提示", { type: "info" })
      .then(async () => {
        try {
          await startWork({
            productionOperationTaskId: row.id,
            userId: userStore.id,
          });
          proxy.$modal.msgSuccess("开始报工成功");
          getList();
        } catch (e) {
          proxy.$modal.msgError("开始报工失败");
        }
      } catch (error) {
        console.error("查询领料详情失败:", error);
      }
    }
      })
      .catch(() => {});
  };
  const showReportDialog = async row => {
    currentReportRowData.value = row;
    const planQuantity = Number(row.planQuantity || 0);
    const completeQuantity = Number(row.completeQuantity || 0);
    const remainingQuantity = Math.max(0, planQuantity - completeQuantity);
    reportForm.planQuantity = remainingQuantity;
    reportForm.quantity =
      row.quantity !== undefined && row.quantity !== null ? row.quantity : null;
    reportForm.quantity = row.quantity !== undefined && row.quantity !== null ? row.quantity : null;
    reportForm.productProcessRouteItemId = row.productProcessRouteItemId;
    reportForm.workOrderId = row.id;
    reportForm.reportWork = row.reportWork;
    reportForm.productMainId = row.productMainId;
    reportForm.scrapQty =
      row.scrapQty !== undefined && row.scrapQty !== null ? row.scrapQty : null;
    reportForm.productionOrderRoutingOperationId =
      row.productionOrderRoutingOperationId;
    reportForm.scrapQty = row.scrapQty !== undefined && row.scrapQty !== null ? row.scrapQty : null;
    reportForm.productionOrderRoutingOperationId = row.productionOrderRoutingOperationId;
    reportForm.productionOrderId = row.productionOrderId;
    if (row.type == 0) {
      reportForm.workHour = row.workHour || 0;
    } else {
      reportForm.workHour = 0;
    }
    reportForm.workHour = row.type == 0 ? (row.workHour || 0) : 0;
    nextTick(() => {
      reportFormRef.value?.clearValidate();
      if (row.productionOrderRoutingOperationId && row.productionOrderId) {
        loadParams(row.productionOrderRoutingOperationId, row.productionOrderId);
      }
    });
    if (userStore.id) {
      reportForm.userId = userStore.id;
      reportForm.userName = userStore.name || userStore.nickName || "";
    }
    getUserProfile()
      .then(res => {
        if (res.code === 200) {
        if (res.code === 200 && res.data) {
          reportForm.userId = res.data.userId;
          reportForm.userName = res.data.nickName;
          reportForm.userName = res.data.nickName || res.data.userName || "";
        }
      })
      .catch(err => {
        console.error("获取用户信息失败", err);
      });
      .catch(err => { console.error("获取用户信息失败", err); });
    reportDialogVisible.value = true;
  };
@@ -718,70 +564,36 @@
    materialDialogVisible.value = true;
  };
  const handleReport = () => {
  const handleFinishWork = () => {
    reportFormRef.value?.validate(valid => {
      if (!valid) {
        return false;
      }
      if (!valid) return;
      if (reportForm.planQuantity <= 0) {
        ElMessageBox.alert("待生产数量为0,无法报工", "提示", {
          confirmButtonText: "确定",
        });
        ElMessageBox.alert("待生产数量为0,无法报工", "提示", { confirmButtonText: "确定" });
        return;
      }
      // éªŒè¯ç”Ÿäº§åˆæ ¼æ•°é‡
      if (
        reportForm.quantity === null ||
        reportForm.quantity === undefined ||
        reportForm.quantity === ""
      ) {
        ElMessageBox.alert("请输入生产合格数量", "提示", {
          confirmButtonText: "确定",
        });
      if (reportForm.quantity === null || reportForm.quantity === undefined || reportForm.quantity === "") {
        ElMessageBox.alert("请输入生产合格数量", "提示", { confirmButtonText: "确定" });
        return;
      }
      const quantity = Number(reportForm.quantity);
      if (isNaN(quantity) || quantity < 0) {
        ElMessageBox.alert("生产合格数量必须大于等于0", "提示", {
          confirmButtonText: "确定",
        });
        ElMessageBox.alert("生产合格数量必须大于等于0", "提示", { confirmButtonText: "确定" });
        return;
      }
      // if (quantity > reportForm.planQuantity) {
      //   ElMessageBox.alert("本次生产数量不能超过待生产数量", "提示", {
      //     confirmButtonText: "确定",
      //   });
      //   return;
      // }
      // éªŒè¯æŠ¥åºŸæ•°é‡
      const scrapQty = Number(reportForm.scrapQty);
      if (!isNaN(scrapQty) && scrapQty < 0) {
        ElMessageBox.alert("报废数量不能小于0", "提示", {
          confirmButtonText: "确定",
        });
        ElMessageBox.alert("报废数量不能小于0", "提示", { confirmButtonText: "确定" });
        return;
      }
      // if (!isNaN(scrapQty) && scrapQty > quantity) {
      //   ElMessageBox.alert("报废数量不能大于本次生产数量", "提示", {
      //     confirmButtonText: "确定",
      //   });
      //   return;
      // }
      const productionOperationParamList = params.value.map(param => ({
        ...param,
        inputValue: reportForm.paramGroups[param.id] ?? "",
      }));
      const submitParams = {
        quantity: quantity,
      addProductMain({
        quantity,
        scrapQty: isNaN(scrapQty) ? 0 : scrapQty,
        userId: reportForm.userId,
        userName: reportForm.userName,
@@ -789,23 +601,18 @@
        productProcessRouteItemId: reportForm.productProcessRouteItemId,
        reportWork: reportForm.reportWork,
        productMainId: reportForm.productMainId,
        productionOrderRoutingOperationId:
          reportForm.productionOrderRoutingOperationId,
        productionOrderRoutingOperationId: reportForm.productionOrderRoutingOperationId,
        productionOrderId: reportForm.productionOrderId,
        workHour: reportForm.workHour,
        productionOperationParamList: productionOperationParamList,
      };
      addProductMain(submitParams)
        .then(res => {
          proxy.$modal.msgSuccess("报工成功");
      })
        .then(() => {
          proxy.$modal.msgSuccess("结束报工成功");
          reportDialogVisible.value = false;
          getList();
        })
        .catch(() => {
          ElMessageBox.alert("报工失败", "提示", {
            confirmButtonText: "确定",
          });
          ElMessageBox.alert("结束报工失败", "提示", { confirmButtonText: "确定" });
        });
    });
  };
@@ -820,221 +627,81 @@
    if (dictOptions.value[dictType]) return dictOptions.value[dictType];
    try {
      const res = await getDicts(dictType);
      if (res.code === 200) {
        dictOptions.value[dictType] = res.data;
        return res.data;
      }
      if (res.code === 200) { dictOptions.value[dictType] = res.data; return res.data; }
      return [];
    } catch (error) {
      console.error("获取字典数据失败:", error);
      return [];
    }
    } catch (error) { console.error("获取字典数据失败:", error); return []; }
  };
  const loadParams = (productionOrderRoutingOperationId, productionOrderId) => {
    paramLoading.value = true;
    findProcessParamListOrder({
      productionOrderRoutingOperationId,
      productionOrderId,
    })
    findProcessParamListOrder({ productionOrderRoutingOperationId, productionOrderId })
      .then(res => {
        if (res.code === 200) {
          const paramList = res.data || [];
          params.value = paramList;
          reportForm.paramGroups = {};
          paramList.forEach(param => {
            if (!reportForm.paramGroups[param.id]) {
              reportForm.paramGroups[param.id] = "";
            }
            if (param.paramType == "3" && param.paramFormat) {
              getDictOptions(param.paramFormat);
            }
            if (!reportForm.paramGroups[param.id]) { reportForm.paramGroups[param.id] = ""; }
            if (param.paramType == "3" && param.paramFormat) { getDictOptions(param.paramFormat); }
          });
        }
      })
      .catch(err => {
        console.error("获取工序参数失败:", err);
      })
      .finally(() => {
        paramLoading.value = false;
      });
      .catch(err => { console.error("获取工序参数失败:", err); })
      .finally(() => { paramLoading.value = false; });
  };
  onMounted(() => {
    userStore.getInfo();
    getList();
    // èŽ·å–ç”¨æˆ·åˆ—è¡¨
    userListNoPageByTenantId().then(res => {
      if (res.code === 200) {
        userOptions.value = res.data;
      }
      if (res.code === 200) { userOptions.value = res.data; }
    });
  });
</script>
<style scoped lang="scss">
  .search-row {
    display: flex;
    align-items: center;
    gap: 12px;
  }
  .search-item {
    display: flex;
    align-items: center;
  }
  .search_title {
    margin-right: 8px;
    font-size: 14px;
    color: #606266;
  }
  .transfer-card-title {
    text-align: center;
    font-size: 24px;
    font-weight: bold;
    margin-bottom: 20px;
    color: #303133;
  }
  .transfer-card-container {
    display: flex;
    justify-content: space-between;
    padding: 20px;
  }
  .transfer-card-info {
    flex: 1;
    margin-right: 20px;
  }
  .info-group {
    margin-bottom: 20px;
  }
  .info-item {
    display: flex;
    margin-bottom: 10px;
  }
  .info-label {
    width: 100px;
    font-weight: bold;
    color: #606266;
  }
  .info-value {
    flex: 1;
    color: #303133;
  }
  .transfer-card-qr {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  .qr-container {
    text-align: center;
  }
  .print-button-container {
    text-align: center;
    margin-top: 20px;
  }
  .param-grid {
    margin-top: 10px;
    border-top: 1px solid #ebe9f3;
    padding-top: 10px;
  }
  .param-item {
    margin-bottom: 12px;
  }
  .param-input-group {
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .param-input {
    flex: 1;
  }
  .param-select {
    flex: 1;
  }
  .param-unit {
    color: #909399;
    font-size: 12px;
    min-width: 30px;
  }
  .search-row { display: flex; align-items: center; gap: 12px; }
  .search-item { display: flex; align-items: center; }
  .search_title { margin-right: 8px; font-size: 14px; color: #606266; }
  .transfer-card-title { text-align: center; font-size: 24px; font-weight: bold; margin-bottom: 20px; color: #303133; }
  .transfer-card-container { display: flex; justify-content: space-between; padding: 20px; }
  .transfer-card-info { flex: 1; margin-right: 20px; }
  .info-group { margin-bottom: 20px; }
  .info-item { display: flex; margin-bottom: 10px; }
  .info-label { width: 100px; font-weight: bold; color: #606266; }
  .info-value { flex: 1; color: #303133; }
  .transfer-card-qr { display: flex; flex-direction: column; align-items: center; }
  .qr-container { text-align: center; }
  .print-button-container { text-align: center; margin-top: 20px; }
  .param-grid { margin-top: 10px; border-top: 1px solid #ebe9f3; padding-top: 10px; }
  .param-item { margin-bottom: 12px; }
  .param-input-group { display: flex; align-items: center; gap: 8px; }
  .param-input { flex: 1; }
  .param-select { flex: 1; }
  .param-unit { color: #909399; font-size: 12px; min-width: 30px; }
</style>
<style  lang="scss">
<style lang="scss">
  @media print {
    @page {
      size: landscape;
    }
    body * {
      visibility: hidden;
    }
    .el-dialog__wrapper,
    .el-dialog,
    .el-dialog__body,
    .transfer-card-title,
    .transfer-card-container,
    .transfer-card-container *,
    .info-item,
    .info-label,
    .info-value {
      visibility: visible;
    }
    .print-button-container {
      visibility: hidden;
    }
    .el-dialog__wrapper {
      position: absolute;
      top: 0;
      left: 0;
      right: 0;
      margin: 0;
    }
    .el-dialog {
      width: 100% !important;
      max-width: 800px;
      margin: 0 auto !important;
    }
    .el-dialog__header,
    .el-dialog__footer {
      display: none;
    }
    .el-dialog__body {
      padding: 20px;
    }
    .transfer-card-container {
      height: auto;
      display: flex;
      gap: 20px;
    }
    @page { size: landscape; }
    body * { visibility: hidden; }
    .el-dialog__wrapper, .el-dialog, .el-dialog__body,
    .transfer-card-title, .transfer-card-container, .transfer-card-container *,
    .info-item, .info-label, .info-value { visibility: visible; }
    .print-button-container { visibility: hidden; }
    .el-dialog__wrapper { position: absolute; top: 0; left: 0; right: 0; margin: 0; }
    .el-dialog { width: 100% !important; max-width: 800px; margin: 0 auto !important; }
    .el-dialog__header, .el-dialog__footer { display: none; }
    .el-dialog__body { padding: 20px; }
    .transfer-card-container { height: auto; display: flex; gap: 20px; }
    .transfer-card-info {
      flex: 1;
      .info-group {
        width: 100%;
        float: none;
        margin-bottom: 20px;
      }
      .info-item {
        display: flex;
        margin-bottom: 10px;
        .info-label {
          width: 100px;
          font-weight: bold;
          margin-right: 15px;
          white-space: nowrap;
        }
        .info-value {
          flex: 1;
          word-break: break-word;
        }
      .info-group { width: 100%; float: none; margin-bottom: 20px; }
      .info-item { display: flex; margin-bottom: 10px;
        .info-label { width: 100px; font-weight: bold; margin-right: 15px; white-space: nowrap; }
        .info-value { flex: 1; word-break: break-word; }
      }
    }
    .transfer-card-qr {
      width: 160px;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: flex-start;
    }
    .qr-container img {
      width: 140px !important;
      height: 140px !important;
    }
    .transfer-card-qr { width: 160px; display: flex; flex-direction: column; align-items: center; justify-content: flex-start; }
    .qr-container img { width: 140px !important; height: 140px !important; }
  }
</style>
src/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue
@@ -1,117 +1,120 @@
<template>
  <div>
    <el-dialog
        v-model="dialogFormVisible"
        :title="operationType === 'add' ? '新增不合格处理' : '处理不合格'"
        width="70%"
        @close="closeDia"
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增不合格处理' : '处理不合格'"
      width="75%"
      @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
      <el-form :model="form" label-width="130px" :rules="rules" ref="formRef">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="类别:" prop="inspectType">
              <el-select v-model="form.inspectType" disabled>
            <el-form-item label="类别">
              <el-select v-model="form.inspectType" disabled style="width: 100%">
                <el-option label="原材料检验" :value="0" />
                <el-option label="过程检验" :value="1" />
                <el-option label="出厂检验" :value="2" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="产品名称:" prop="productId">
            <el-form-item label="产品名称">
              <el-tree-select
                  v-model="form.productId"
                  placeholder="请选择"
                  clearable
                  disabled
                  check-strictly
                  @change="getModels"
                  :data="productOptions"
                  :render-after-expand="false"
                  style="width: 100%"
                v-model="form.productId"
                placeholder="请选择"
                clearable
                disabled
                check-strictly
                @change="getModels"
                :data="productOptions"
                :render-after-expand="false"
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="规格型号:" prop="model">
              <el-input v-model="form.model" placeholder="请输入" clearable disabled/>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="规格型号">
              <el-input v-model="form.model" placeholder="请输入" clearable disabled />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="单位">
              <el-input v-model="form.unit" placeholder="请输入" clearable disabled />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="数量">
              <el-input v-model="form.quantity" placeholder="请输入" clearable disabled />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="单位:" prop="unit">
              <el-input v-model="form.unit" placeholder="请输入" clearable disabled/>
            <el-form-item label="检验员">
              <el-input v-model="form.checkName" placeholder="请输入" clearable disabled />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="数量:" prop="quantity">
              <el-input v-model="form.quantity" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="检验员:" prop="checkName">
              <el-input v-model="form.checkName" placeholder="请输入" clearable disabled/>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检测日期:" prop="checkTime">
            <el-form-item label="检测日期">
              <el-date-picker
                  v-model="form.checkTime"
                  type="date"
                  disabled
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                v-model="form.checkTime"
                type="date"
                disabled
                placeholder="请选择日期"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                clearable
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="不合格现象:" prop="defectivePhenomena">
              <el-input v-model="form.defectivePhenomena" placeholder="请输入" clearable/>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="不合格现象" prop="defectivePhenomena">
              <el-input v-model="form.defectivePhenomena" type="textarea" :rows="2" placeholder="请输入" clearable />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="处理结果:" prop="dealResult">
              <el-select v-model="form.dealResult" placeholder="请选择" clearable>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="处理结果" prop="dealResult">
              <el-select v-model="form.dealResult" placeholder="请选择" clearable style="width: 100%">
                <el-option :label="item.label" :value="item.value" v-for="item in filteredRejectionHandling" :key="item.value" />
              </el-select>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="处理人:" prop="dealName">
          <el-col :span="8">
            <el-form-item label="处理人" prop="dealName">
              <el-select v-model="form.dealName" placeholder="请选择" clearable style="width: 100%">
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName"/>
                <el-option v-for="item in userList" :key="item.nickName" :label="item.nickName" :value="item.nickName" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="处理日期:" prop="dealTime">
          <el-col :span="8">
            <el-form-item label="处理日期" prop="dealTime">
              <el-date-picker
                  v-model="form.dealTime"
                  type="date"
                  placeholder="请选择日期"
                  value-format="YYYY-MM-DD"
                  format="YYYY-MM-DD"
                  clearable
                  style="width: 100%"
                v-model="form.dealTime"
                type="date"
                placeholder="请选择日期"
                value-format="YYYY-MM-DD"
                format="YYYY-MM-DD"
                clearable
                style="width: 100%"
              />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="附件:">
            <el-form-item label="附件">
              <FileUpload v-model:file-list="form.storageBlobDTOs" :file-list="form.storageBlobVOs" />
            </el-form-item>
          </el-col>
@@ -128,20 +131,22 @@
</template>
<script setup>
import {ref, reactive, toRefs, computed} from "vue";
import {productTreeList} from "@/api/basicData/product.js";
import { ref, reactive, toRefs, computed } from "vue";
import { productTreeList } from "@/api/basicData/product.js";
import {
  getQualityUnqualifiedInfo,
  qualityUnqualifiedDeal
} from "@/api/qualityManagement/nonconformingManagement.js";
import {userListNoPage} from "@/api/system/user.js";
import { userListNoPage } from "@/api/system/user.js";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
const { proxy } = getCurrentInstance()
const emit = defineEmits(['close'])
const { rejection_handling } = proxy.useDict("rejection_handling")
const { proxy } = getCurrentInstance();
const emit = defineEmits(["close"]);
const { rejection_handling } = proxy.useDict("rejection_handling");
const dialogFormVisible = ref(false);
const operationType = ref('')
const operationType = ref("");
const data = reactive({
  form: {
    checkTime: "",
@@ -154,48 +159,36 @@
    quantity: "",
    checkCompany: "",
    checkResult: "",
    inspectType: '',
    defectivePhenomena: '',
    dealResult: '',
    dealName: '',
    dealTime: '',
    inspectType: "",
    defectivePhenomena: "",
    dealResult: "",
    dealName: "",
    dealTime: "",
    method: undefined,
    storageBlobDTOs: [],
    storageBlobVOs: [],
  },
  rules: {
    checkTime: [{ required: false, message: "请输入", trigger: "blur" },],
    process: [{ required: false, message: "请输入", trigger: "blur" }],
    checkName: [{ required: false, message: "请输入", trigger: "blur" }],
    productId: [{ required: false, message: "请输入", trigger: "blur" }],
    model: [{ required: false, message: "请输入", trigger: "blur" }],
    unit: [{ required: false, message: "请输入", trigger: "blur" }],
    quantity: [{ required: false, message: "请输入", trigger: "blur" }],
    checkCompany: [{ required: false, message: "请输入", trigger: "blur" }],
    checkResult: [{ required: false, message: "请输入", trigger: "blur" }],
    defectivePhenomena: [{ required: true, message: "请输入", trigger: "blur" }],
    dealResult: [{ required: true, message: "请输入", trigger: "blur" }],
    defectivePhenomena: [{ required: true, message: "请输入不合格现象", trigger: "blur" }],
    dealResult: [{ required: true, message: "请选择处理结果", trigger: "change" }],
    dealName: [{ required: true, message: "请选择处理人", trigger: "change" }],
    dealTime: [{ required: true, message: "请输入", trigger: "change" }],
    dealTime: [{ required: true, message: "请选择处理日期", trigger: "change" }],
  },
});
const { form, rules } = toRefs(data);
const productOptions = ref([]);
const userList = ref([]); // å¤„理人下拉列表
const userList = ref([]);
const filteredRejectionHandling = computed(() => {
  const data = rejection_handling.value;
  if (form.value.method) {
    return data.filter(item => item && item.label && item.label !== '返工' && item.label !== '返修')
    return data.filter(item => item && item.label && item.label !== "返工" && item.label !== "返修");
  }
  return data
})
  return data;
});
// æ‰“开弹框
const openDialog = async (type, row) => {
  operationType.value = type;
  // å¤„理人下拉列表
  try {
    const userRes = await userListNoPage();
    userList.value = userRes.data || [];
@@ -206,74 +199,74 @@
  dialogFormVisible.value = true;
  form.value = {};
  getProductOptions();
  if (operationType.value === 'edit') {
  if (operationType.value === "edit") {
    getQualityUnqualifiedInfo(row.id).then(res => {
      const { inspectState, ...rest } = (res.data || {})
      // æœ‰æ•°æ®å°±æ˜¾ç¤ºé»˜è®¤å€¼ï¼Œæ²¡æœ‰å°±ä¸æ˜¾ç¤º
      form.value = { ...rest }
    })
      const { inspectState, ...rest } = (res.data || {});
      form.value = { ...rest };
    });
  }
}
};
const getProductOptions = () => {
  productTreeList().then((res) => {
    productOptions.value = convertIdToValue(res);
  });
};
const getModels = (value) => {
  form.value.productName = findNodeById(productOptions.value, value);
};
const findNodeById = (nodes, productId) => {
  for (let i = 0; i < nodes.length; i++) {
    if (nodes[i].value === productId) {
      return nodes[i].label; // æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
      return nodes[i].label;
    }
    if (nodes[i].children && nodes[i].children.length > 0) {
      const foundNode = findNodeById(nodes[i].children, productId);
      if (foundNode) {
        return foundNode; // åœ¨å­èŠ‚ç‚¹ä¸­æ‰¾åˆ°ï¼Œè¿”å›žè¯¥èŠ‚ç‚¹
        return foundNode;
      }
    }
  }
  return null; // æ²¡æœ‰æ‰¾åˆ°èŠ‚ç‚¹ï¼Œè¿”å›žnull
  return null;
};
function convertIdToValue(data) {
  return data.map((item) => {
    const { id, children, ...rest } = item;
    const newItem = {
      ...rest,
      value: id, // å°† id æ”¹ä¸º value
      value: id,
    };
    if (children && children.length > 0) {
      newItem.children = convertIdToValue(children);
    }
    return newItem;
  });
}
// æäº¤äº§å“è¡¨å•
const submitForm = () => {
  proxy.$refs.formRef.validate(valid => {
    if (valid) {
      // çŠ¶æ€å­—æ®µä¸åœ¨è¡¨å•å¡«å†™ï¼Œä¹Ÿä¸ä¼ ç»™åŽç«¯ï¼›å¤„ç†ç»Ÿä¸€èµ° /deal æŽ¥å£
      const { inspectState, ...payload } = (form.value || {})
      const { inspectState, ...payload } = (form.value || {});
      qualityUnqualifiedDeal(payload).then(() => {
        proxy.$modal.msgSuccess("提交成功");
        closeDia();
      })
      });
    }
  })
}
// å…³é—­å¼¹æ¡†
  });
};
const closeDia = () => {
  proxy.resetForm("formRef");
  dialogFormVisible.value = false;
  emit('close')
  emit("close");
};
defineExpose({
  openDialog,
});
</script>
<style scoped>
</style>
<style scoped></style>
src/views/qualityManagement/nonconformingManagement/components/orderFormDia.vue
@@ -2,166 +2,220 @@
  <div>
    <el-dialog
      v-model="dialogFormVisible"
      :title="operationType === 'add' ? '新增不合格品处理单' : '编辑不合格品处理单'"
      width="70%"
      :title="operationType === 'add' ? '新增不合格品处理单' : operationType === 'view' ? '不合格品处理单详情' : operationType === 'dispose' ? '处置不合格品处理单' : '编辑不合格品处理单'"
      width="75%"
      @close="closeDia"
    >
      <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
        <el-row :gutter="30">
      <el-form :model="form" label-width="130px" :rules="rules" ref="formRef">
        <!-- å…³è”不合格品(仅新增时) -->
        <el-row :gutter="20" v-if="operationType === 'add'">
          <el-col :span="12">
            <el-form-item label="项目名称" prop="projectName">
              <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="项目编号" prop="projectNo">
              <el-input v-model="form.projectNo" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="设备名称" prop="equipmentName">
              <el-input v-model="form.equipmentName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="设备图号" prop="equipmentDrawingNo">
              <el-input v-model="form.equipmentDrawingNo" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="物料/部件名称" prop="materialName">
              <el-input v-model="form.materialName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="物料图号" prop="materialDrawingNo">
              <el-input v-model="form.materialDrawingNo" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="型号规格" prop="specificationModel">
              <el-input v-model="form.specificationModel" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="材质" prop="materialQuality">
              <el-input v-model="form.materialQuality" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="总数量" prop="quantity">
              <el-input-number v-model="form.quantity" :min="0" style="width: 100%" :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="不合格数量" prop="unqualifiedQuantity">
              <el-input-number v-model="form.unqualifiedQuantity" :min="0" style="width: 100%" :disabled="operationType === 'view'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="不合格工序" prop="unqualifiedProcess">
              <el-select v-model="form.unqualifiedProcess" placeholder="请选择" clearable :disabled="operationType === 'view'" style="width: 100%">
                <el-option label="来料" :value="1" />
                <el-option label="制程" :value="2" />
                <el-option label="成品" :value="3" />
            <el-form-item label="关联不合格品" prop="unqualifiedId">
              <el-select v-model="form.unqualifiedId" placeholder="选择不合格品记录" filterable clearable
                @change="handleUnqualifiedSelect" style="width: 100%">
                <el-option v-for="item in unqualifiedList" :key="item.id"
                  :label="item.productName + ' ' + item.model + ' (' + item.quantity + ')'"
                  :value="item.id" />
              </el-select>
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="供应商名称" prop="supplierName">
              <el-input v-model="form.supplierName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            <el-form-item label="不合格工序">
              <el-radio-group v-model="form.unqualifiedProcess" :disabled="operationType === 'view' || operationType === 'dispose'">
                <el-radio :value="1">来料</el-radio>
                <el-radio :value="2">制程</el-radio>
                <el-radio :value="3">成品</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-divider>基本信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="检验员" prop="inspectorName">
              <el-input v-model="form.inspectorName" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            <el-form-item label="项目名称">
              <el-input v-model="form.projectName" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="检验日期" prop="inspectDate">
              <el-date-picker v-model="form.inspectDate" type="date" value-format="YYYY-MM-DD" format="YYYY-MM-DD" placeholder="请选择" clearable style="width: 100%" :disabled="operationType === 'view'" />
            <el-form-item label="项目编号">
              <el-input v-model="form.projectNo" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="责任人" prop="responsiblePerson">
              <el-input v-model="form.responsiblePerson" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            <el-form-item label="设备名称">
              <el-input v-model="form.equipmentName" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="责任部门" prop="responsibleDept">
              <el-input v-model="form.responsibleDept" placeholder="请输入" clearable :disabled="operationType === 'view'" />
            <el-form-item label="设备图号">
              <el-input v-model="form.equipmentDrawingNo" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="12">
            <el-form-item label="物料/部件名称">
              <el-input v-model="form.materialName" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="物料图号">
              <el-input v-model="form.materialDrawingNo" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="型号规格">
              <el-input v-model="form.specificationModel" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="材质">
              <el-input v-model="form.materialQuality" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="供应商">
              <el-input v-model="form.supplierName" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-divider>不合格信息</el-divider>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="总数量">
              <el-input-number v-model="form.quantity" :min="0" :precision="2" style="width: 100%" :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="不合格数量">
              <el-input-number v-model="form.unqualifiedQuantity" :min="0" :precision="2" style="width: 100%" :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="检验日期">
              <el-date-picker v-model="form.inspectDate" type="date" value-format="YYYY-MM-DD" placeholder="请选择" clearable style="width: 100%" :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="8">
            <el-form-item label="检验员">
              <el-input v-model="form.inspectorName" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="责任人">
              <el-input v-model="form.responsiblePerson" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
          <el-col :span="8">
            <el-form-item label="责任部门">
              <el-input v-model="form.responsibleDept" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="问题描述" prop="problemDescription">
              <el-input type="textarea" v-model="form.problemDescription" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="3" />
            <el-form-item label="问题描述">
              <el-input type="textarea" v-model="form.problemDescription" placeholder="请输入" clearable :disabled="operationType === 'view' || operationType === 'dispose'" :rows="3" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-divider>处置决策</el-divider>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="原因分析及建议" prop="reasonAnalysis">
            <el-form-item label="处置方式" prop="disposalMethod">
              <el-radio-group v-model="form.disposalMethod" :disabled="operationType === 'view'">
                <el-radio :value="1">让步接收</el-radio>
                <el-radio :value="2">厂内维修</el-radio>
                <el-radio :value="3">返厂维修</el-radio>
                <el-radio :value="4">换货</el-radio>
                <el-radio :value="5">退货</el-radio>
                <el-radio :value="6">报废</el-radio>
              </el-radio-group>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20" v-if="form.disposalMethod === 2 || form.disposalMethod === 3">
          <el-col :span="24">
            <el-form-item label="厂内/返厂维修评估">
              <el-input type="textarea" v-model="form.repairEvaluation" placeholder="评估维修可行性、所需工时、物料等" clearable :disabled="operationType === 'view'" :rows="3" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="原因分析及建议">
              <el-input type="textarea" v-model="form.reasonAnalysis" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="3" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
          <el-col :span="12">
            <el-form-item label="纠正措施" prop="correctionAction">
              <el-input type="textarea" v-model="form.correctionAction" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="3" />
            </el-form-item>
          </el-col>
          <el-col :span="12">
            <el-form-item label="处置方式" prop="disposalMethod">
              <el-select v-model="form.disposalMethod" placeholder="请选择" clearable :disabled="operationType === 'view'" style="width: 100%">
                <el-option label="让步接收" :value="1" />
                <el-option label="厂内维修" :value="2" />
                <el-option label="返厂维修" :value="3" />
                <el-option label="换货" :value="4" />
                <el-option label="退货" :value="5" />
                <el-option label="报废" :value="6" />
              </el-select>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="纠正措施">
              <el-input type="textarea" v-model="form.correctionAction" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="2" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="维修评估" prop="repairEvaluation">
              <el-input type="textarea" v-model="form.repairEvaluation" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="3" />
            <el-form-item label="预防措施">
              <el-input type="textarea" v-model="form.preventiveAction" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="2" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-divider>审批意见</el-divider>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="预防措施" prop="preventiveAction">
              <el-input type="textarea" v-model="form.preventiveAction" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="3" />
            <el-form-item label="责任部门主管意见">
              <el-input type="textarea" v-model="form.deptOpinion" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="2" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="备注" prop="remark">
            <el-form-item label="公司处理决定">
              <el-input type="textarea" v-model="form.companyDecision" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="2" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="总经理意见">
              <el-input type="textarea" v-model="form.generalManagerOpinion" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="2" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="备注">
              <el-input type="textarea" v-model="form.remark" placeholder="请输入" clearable :disabled="operationType === 'view'" :rows="2" />
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="30">
        <el-divider>附件</el-divider>
        <el-row :gutter="20">
          <el-col :span="24">
            <el-form-item label="附件">
              <FileUpload v-model:file-list="form.storageBlobDTOs" :file-list="form.storageBlobVOs" :disabled="operationType === 'view'" />
@@ -181,7 +235,13 @@
<script setup>
import { ref, reactive, toRefs, getCurrentInstance } from "vue";
import { save, update, getDetail } from "@/api/qualityManagement/qualityUnqualifiedOrder.js";
import {
  saveQualityUnqualifiedOrder,
  updateQualityUnqualifiedOrder,
  getQualityUnqualifiedOrderDetail,
  qualityUnqualifiedListPage,
  dealQualityUnqualifiedOrder,
} from "@/api/qualityManagement/nonconformingManagement.js";
import FileUpload from "@/components/AttachmentUpload/file/index.vue";
const { proxy } = getCurrentInstance();
@@ -189,77 +249,82 @@
const dialogFormVisible = ref(false);
const operationType = ref("");
const unqualifiedList = ref([]);
const emptyForm = {
  unqualifiedId: undefined,
  projectName: "",
  projectNo: "",
  equipmentId: undefined,
  equipmentName: "",
  equipmentDrawingNo: "",
  materialName: "",
  productModelId: undefined,
  materialDrawingNo: "",
  specificationModel: "",
  materialQuality: "",
  quantity: undefined,
  unqualifiedQuantity: undefined,
  unqualifiedProcess: undefined,
  supplierName: "",
  inspectorName: "",
  inspectDate: "",
  responsiblePerson: "",
  responsibleDept: "",
  problemDescription: "",
  reasonAnalysis: "",
  correctionAction: "",
  disposalMethod: undefined,
  repairEvaluation: "",
  preventiveAction: "",
  deptOpinion: "",
  companyDecision: "",
  generalManagerOpinion: "",
  remark: "",
  storageBlobDTOs: [],
  storageBlobVOs: [],
};
const data = reactive({
  form: {
    projectName: "",
    projectNo: "",
    equipmentName: "",
    equipmentDrawingNo: "",
    materialName: "",
    materialDrawingNo: "",
    specificationModel: "",
    materialQuality: "",
    quantity: undefined,
    unqualifiedQuantity: undefined,
    unqualifiedProcess: undefined,
    supplierName: "",
    inspectorName: "",
    inspectDate: "",
    responsiblePerson: "",
    responsibleDept: "",
    problemDescription: "",
    reasonAnalysis: "",
    correctionAction: "",
    disposalMethod: undefined,
    repairEvaluation: "",
    preventiveAction: "",
    remark: "",
    storageBlobDTOs: [],
    storageBlobVOs: [],
  },
  form: { ...emptyForm },
  rules: {
    projectName: [{ required: false, message: "请输入", trigger: "blur" }],
    unqualifiedId: [{ required: true, message: "请选择关联不合格品", trigger: "change" }],
    disposalMethod: [{ required: true, message: "请选择处置方式", trigger: "change" }],
  },
});
const { form, rules } = toRefs(data);
const loadUnqualifiedList = () => {
  qualityUnqualifiedListPage({ inspectState: 0, page: 1, size: 999 }).then(res => {
    unqualifiedList.value = res.data?.records || [];
  });
};
const handleUnqualifiedSelect = (id) => {
  const record = unqualifiedList.value.find(item => item.id === id);
  if (!record) return;
  form.value.materialName = record.productName || "";
  form.value.specificationModel = record.model || "";
  form.value.quantity = record.quantity;
  form.value.inspectorName = record.checkName || "";
  form.value.inspectDate = record.checkTime || "";
  form.value.problemDescription = record.defectivePhenomena || "";
  form.value.unqualifiedProcess = record.inspectType != null ? record.inspectType + 1 : undefined;
  form.value.unqualifiedQuantity = record.quantity;
};
const openDialog = async (type, row) => {
  operationType.value = type;
  dialogFormVisible.value = true;
  if (type === "add") {
    form.value = {
      projectName: "",
      projectNo: "",
      equipmentName: "",
      equipmentDrawingNo: "",
      materialName: "",
      materialDrawingNo: "",
      specificationModel: "",
      materialQuality: "",
      quantity: undefined,
      unqualifiedQuantity: undefined,
      unqualifiedProcess: undefined,
      supplierName: "",
      inspectorName: "",
      inspectDate: "",
      responsiblePerson: "",
      responsibleDept: "",
      problemDescription: "",
      reasonAnalysis: "",
      correctionAction: "",
      disposalMethod: undefined,
      repairEvaluation: "",
      preventiveAction: "",
      remark: "",
      storageBlobDTOs: [],
      storageBlobVOs: [],
    };
    loadUnqualifiedList();
    Object.assign(data.form, { ...emptyForm });
  } else {
    form.value = {};
    Object.assign(data.form, { ...emptyForm });
    if (row?.id) {
      getDetail(row.id).then((res) => {
        form.value = res.data || res || {};
      getQualityUnqualifiedOrderDetail(row.id).then((res) => {
        const detail = res.data || res || {};
        Object.assign(data.form, { ...emptyForm }, detail);
      });
    }
  }
@@ -268,14 +333,31 @@
const submitForm = () => {
  proxy.$refs.formRef.validate((valid) => {
    if (valid) {
      const { status, ...payload } = form.value || {};
      const { status, storageBlobVOs, ...payload } = form.value || {};
      if (operationType.value === "add") {
        save(payload).then(() => {
        saveQualityUnqualifiedOrder(payload).then(() => {
          proxy.$modal.msgSuccess("提交成功");
          closeDia();
        });
      } else if (operationType.value === "dispose") {
        const dealFields = {
          id: payload.id,
          disposalMethod: payload.disposalMethod,
          repairEvaluation: payload.repairEvaluation,
          reasonAnalysis: payload.reasonAnalysis,
          correctionAction: payload.correctionAction,
          preventiveAction: payload.preventiveAction,
          remark: payload.remark,
          deptOpinion: payload.deptOpinion,
          companyDecision: payload.companyDecision,
          generalManagerOpinion: payload.generalManagerOpinion,
        };
        dealQualityUnqualifiedOrder(dealFields).then(() => {
          proxy.$modal.msgSuccess("处置成功");
          closeDia();
        });
      } else {
        update(payload).then(() => {
        updateQualityUnqualifiedOrder(payload).then(() => {
          proxy.$modal.msgSuccess("修改成功");
          closeDia();
        });
src/views/qualityManagement/nonconformingManagement/index.vue
@@ -1,314 +1,265 @@
<template>
  <div class="app-container">
    <div class="search_form">
      <el-form :model="searchForm" inline style="margin-bottom: 0;">
        <el-form-item label="类型:">
          <el-select v-model="searchForm.inspectType" clearable style="width: 200px" @change="handleQuery">
            <el-option label="原材料检验" :value="0" />
            <el-option label="过程检验" :value="1" />
            <el-option label="出厂检验" :value="2" />
          </el-select>
      <el-form :model="queryParams" inline style="margin-bottom: 0;">
        <el-form-item label="处理单编号">
          <el-input v-model="queryParams.orderNo" style="width: 200px" placeholder="输入编号" clearable
            :prefix-icon="Search" @change="handleQuery" />
        </el-form-item>
        <el-form-item label="状态:">
          <el-select v-model="searchForm.inspectState" clearable style="width: 200px" @change="handleQuery">
            <el-option label="待处理" :value="0" />
            <el-option label="已处理" :value="1" />
          </el-select>
        <el-form-item label="项目名称">
          <el-input v-model="queryParams.projectName" style="width: 200px" placeholder="输入项目" clearable
            :prefix-icon="Search" @change="handleQuery" />
        </el-form-item>
        <el-form-item label="产品名称:">
          <el-input
              v-model="searchForm.productName"
              style="width: 200px"
              placeholder="请输入产品名称搜索"
              @change="handleQuery"
              clearable
              :prefix-icon="Search"
          />
        </el-form-item>
        <el-form-item label="检测日期:">
          <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
                          style="width: 300px"
                          placeholder="请选择" clearable @change="changeDaterange" />
<!--        <el-form-item label="状态">&ndash;&gt;-->
<!--          <el-select v-model="queryParams.status" clearable style="width: 150px" @change="handleQuery" placeholder="全部">&ndash;&gt;-->
<!--            <el-option label="草稿" :value="0" />&ndash;&gt;-->
<!--            <el-option label="待审批" :value="1" />&ndash;&gt;-->
<!--            <el-option label="审批中" :value="2" />&ndash;&gt;-->
<!--            <el-option label="已完成" :value="3" />&ndash;&gt;-->
<!--            <el-option label="已驳回" :value="4" />&ndash;&gt;-->
<!--          </el-select>&ndash;&gt;-->
<!--        </el-form-item>&ndash;&gt;-->
        <el-form-item label="创建时间">
          <el-date-picker v-model="dateRange" value-format="YYYY-MM-DD" type="daterange"
            style="width: 260px" placeholder="请选择" clearable @change="changeDaterange" />
        </el-form-item>
        <el-form-item>
          <el-button type="primary" @click="handleQuery">搜索</el-button>
          <el-button @click="resetQuery">重置</el-button>
        </el-form-item>
      </el-form>
    </div>
    <div class="mb20" style="text-align: right;">
<!--        <el-button type="primary" @click="openForm('add')">新增</el-button>-->
        <el-button type="primary" @click="openOrderForm">处理单</el-button>
        <el-button @click="handleOut">导出</el-button>
        <el-button type="danger" plain @click="handleDelete">删除</el-button>
      </div>
    <div class="table_list">
      <PIMTable
          rowKey="id"
          :column="tableColumn"
          :tableData="tableData"
          :page="page"
          :isSelection="true"
          @selection-change="handleSelectionChange"
          :tableLoading="tableLoading"
          @pagination="pagination"
          :total="page.total"
      ></PIMTable>
      <el-button type="primary" @click="handleAdd">新增处理单</el-button>
      <el-button type="danger" plain :disabled="!selectedRows.length" @click="handleDelete">删除</el-button>
    </div>
    <FormDia ref="formDia" @close="handleQuery"></FormDia>
    <InspectionFormDia ref="inspectionFormDia" @close="handleQuery"></InspectionFormDia>
    <OrderFormDia ref="orderFormDia" @close="handleQuery"></OrderFormDia>
    <div class="table_list">
      <PIMTable rowKey="id" :column="tableColumn" :tableData="tableData" :page="page"
        :isSelection="true" @selection-change="handleSelectionChange"
        :tableLoading="tableLoading" @pagination="pagination" :total="page.total" />
    </div>
    <OrderFormDia ref="orderFormDia" @close="handleQuery" />
  </div>
</template>
<script setup>
import { Search } from "@element-plus/icons-vue";
import {onMounted, ref, reactive, toRefs, nextTick, getCurrentInstance} from "vue";
import FormDia from "@/views/qualityManagement/nonconformingManagement/components/formDia.vue";
import {ElMessageBox} from "element-plus";
import {qualityUnqualifiedDel, qualityUnqualifiedListPage} from "@/api/qualityManagement/nonconformingManagement.js";
import InspectionFormDia from "@/views/qualityManagement/nonconformingManagement/components/inspectionFormDia.vue";
import { onMounted, ref, reactive, nextTick, getCurrentInstance } from "vue";
import { ElMessageBox } from "element-plus";
import {
  qualityUnqualifiedOrderListPage,
  deleteQualityUnqualifiedOrder,
} from "@/api/qualityManagement/nonconformingManagement.js";
import OrderFormDia from "@/views/qualityManagement/nonconformingManagement/components/orderFormDia.vue";
import dayjs from "dayjs";
const data = reactive({
  searchForm: {
    inspectType: "",
    inspectState: "",
    productName: "",
    entryDate: undefined, // å½•入日期
    entryDateStart: undefined,
    entryDateEnd: undefined,
  },
const { proxy } = getCurrentInstance();
const disposalMethodMap = { 1: "让步接收", 2: "厂内维修", 3: "返厂维修", 4: "换货", 5: "退货", 6: "报废" };
const statusMap = { 0: "草稿", 1: "待审批", 2: "审批中", 3: "已完成", 4: "已驳回" };
const queryParams = reactive({
  orderNo: "",
  projectName: "",
  status: null,
  entryDateStart: undefined,
  entryDateEnd: undefined,
});
const { searchForm } = toRefs(data);
const tableColumn = ref([
  {
    label: "状态",
    prop: "inspectState",
    dataType: "tag",
    formatData: (params) => {
      if (params == 0) {
        return "待处理";
      } else if (params == 1) {
        return "已处理";
      } else {
        return null;
      }
    },
    formatType: (params) => {
      if (params == '不合格') {
        return "danger";
      } else if (params == '合格') {
        return "success";
      } else {
        return 'danger';
      }
    },
  },
  {
    label: "检测日期",
    prop: "checkTime",
    width: 120
  },
  {
    label: "类别",
    prop: "inspectType",
    dataType: "tag",
    width: 120,
    formatData: (params) => {
      if (params == 0) {
        return "原材料检验";
      } else if (params == 1) {
        return "过程检验";
      } else {
        return '出厂检验';
      }
    },
    formatType: (params) => {
      if (params == '不合格') {
        return "info";
      } else if (params == '合格') {
        return "success";
      } else {
        return 'primary';
      }
    },
  },
  {
    label: "检验员",
    prop: "checkName",
  },
  {
    label: "产品名称",
    prop: "productName",
  },
  {
    label: "规格型号",
    prop: "model",
  },
  {
    label: "单位",
    prop: "unit",
  },
  {
    label: "数量",
    prop: "quantity",
    width: 100
  },
  {
    label: "不合格现象",
    prop: "defectivePhenomena",
    width: 120
  },
  {
    label: "处理结果",
    prop: "dealResult",
    width: 120
  },
  {
    label: "处理人",
    prop: "dealName",
    width: 120
  },
  {
    label: "处理日期",
    prop: "dealTime",
    width: 120
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 100,
    operation: [
      {
        name: "处理",
        type: "text",
        clickFun: (row) => {
          openInspectionForm("edit", row);
        },
        disabled: (row) => row.inspectState === 1,
      },
    ],
  },
]);
const dateRange = ref(undefined);
const tableData = ref([]);
const selectedRows = ref([]);
const tableLoading = ref(false);
const page = reactive({
  current: 1,
  size: 100,
  total: 0
  total: 0,
});
const formDia = ref()
const inspectionFormDia = ref()
const orderFormDia = ref()
const { proxy } = getCurrentInstance()
const orderFormDia = ref();
const tableColumn = ref([
  {
    label: "处理单编号",
    prop: "orderNo",
    width: 160,
  },
  {
    label: "项目名称",
    prop: "projectName",
  },
  {
    label: "型号规格",
    prop: "specificationModel",
    width: 120,
  },
  {
    label: "不合格数量",
    prop: "unqualifiedQuantity",
    width: 100,
  },
  {
    label: "处置方式",
    prop: "disposalMethod",
    width: 110,
    dataType: "tag",
    formatData: (val) => disposalMethodMap[val] || "",
  },
  // {
  //   label: "状态",
  //   prop: "status",
  //   width: 80,
  //   dataType: "tag",
  //   formatData: (val) => statusMap[val] || "",
  //   formatType: (val) => {
  //     const map = { 0: "info", 1: "warning", 2: "", 3: "success", 4: "danger" };
  //     return map[val] || "info";
  //   },
  // },
  {
    label: "检验员",
    prop: "inspectorName",
    width: 100,
  },
  {
    label: "检验日期",
    prop: "inspectDate",
    width: 110,
  },
  {
    label: "创建时间",
    prop: "createTime",
    width: 160,
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 160,
    operation: [
      {
        name: "导出",
        type: "text",
        clickFun: (row) => {
          handleExport(row);
        },
      },
      {
        name: "处置",
        type: "text",
        showHide: (row) => row.status === 0,
        clickFun: (row) => {
          nextTick(() => {
            orderFormDia.value?.openDialog("dispose", row);
          });
        },
      },
      {
        name: "编辑",
        type: "text",
        clickFun: (row) => {
          nextTick(() => {
            orderFormDia.value?.openDialog("edit", row);
          });
        },
      },
      {
        name: "详情",
        type: "text",
        clickFun: (row) => {
          nextTick(() => {
            orderFormDia.value?.openDialog("view", row);
          });
        },
      },
    ],
  },
]);
const changeDaterange = (value) => {
  searchForm.value.entryDateStart = undefined;
  searchForm.value.entryDateEnd = undefined;
  queryParams.entryDateStart = undefined;
  queryParams.entryDateEnd = undefined;
  if (value) {
    searchForm.value.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    searchForm.value.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
    queryParams.entryDateStart = dayjs(value[0]).format("YYYY-MM-DD");
    queryParams.entryDateEnd = dayjs(value[1]).format("YYYY-MM-DD");
  }
  getList();
};
// æŸ¥è¯¢åˆ—表
/** æœç´¢æŒ‰é’®æ“ä½œ */
const handleQuery = () => {
  page.current = 1;
  getList();
};
const resetQuery = () => {
  queryParams.orderNo = "";
  queryParams.projectName = "";
  queryParams.status = null;
  dateRange.value = undefined;
  queryParams.entryDateStart = undefined;
  queryParams.entryDateEnd = undefined;
  handleQuery();
};
const pagination = (obj) => {
  page.current = obj.page;
  page.size = obj.limit;
  getList();
};
const getList = () => {
  tableLoading.value = true;
  const params = { ...searchForm.value, ...page };
  params.entryDate = undefined
  qualityUnqualifiedListPage(params).then(res => {
    tableLoading.value = false;
    tableData.value = res.data.records
    page.total = res.data.total;
  }).catch(err => {
    tableLoading.value = false;
  })
  const params = { ...queryParams, page: page.current, size: page.size };
  qualityUnqualifiedOrderListPage(params)
    .then((res) => {
      tableLoading.value = false;
      tableData.value = res.data?.records || [];
      page.total = res.data?.total || 0;
    })
    .catch(() => {
      tableLoading.value = false;
    });
};
// è¡¨æ ¼é€‰æ‹©æ•°æ®
const handleSelectionChange = (selection) => {
  selectedRows.value = selection;
};
// æ‰“开弹框
const openForm = (type, row) => {
  if (type !== 'add' && row?.inspectState === 1) {
    proxy.$modal.msgWarning("已处理的数据不能再编辑");
    return;
  }
  nextTick(() => {
    formDia.value?.openDialog(type, row)
  })
};
// æ‰“开处理弹框
const openInspectionForm = (type, row) => {
  if (row?.inspectState === 1) {
    proxy.$modal.msgWarning("已处理的数据不能再处理");
    return;
  }
  nextTick(() => {
    inspectionFormDia.value?.openDialog(type, row)
  })
};
// æ‰“开处理单弹框
const openOrderForm = () => {
const handleAdd = () => {
  nextTick(() => {
    orderFormDia.value?.openDialog("add", null);
  });
};
// åˆ é™¤
const handleDelete = () => {
  let ids = [];
  if (selectedRows.value.length > 0) {
    ids = selectedRows.value.map((item) => item.id);
  } else {
  if (selectedRows.value.length === 0) {
    proxy.$modal.msgWarning("请选择数据");
    return;
  }
  ElMessageBox.confirm("选中的内容将被删除,是否确认删除?", "导出", {
  const ids = selectedRows.value.map((item) => item.id);
  ElMessageBox.confirm("选中的处理单将被删除,是否确认?", "提示", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        qualityUnqualifiedDel(ids).then((res) => {
          proxy.$modal.msgSuccess("删除成功");
          getList();
        });
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
    .then(() => {
      deleteQualityUnqualifiedOrder(ids).then(() => {
        proxy.$modal.msgSuccess("删除成功");
        getList();
      });
    })
    .catch(() => {
      proxy.$modal.msg("已取消");
    });
};
// å¯¼å‡º
const handleOut = () => {
  ElMessageBox.confirm("选中的内容将被导出,是否确认导出?", "导出", {
    confirmButtonText: "确认",
    cancelButtonText: "取消",
    type: "warning",
  })
      .then(() => {
        proxy.download("/quality/qualityUnqualified/export", {}, "不合格管理.xlsx");
      })
      .catch(() => {
        proxy.$modal.msg("已取消");
      });
const handleExport = (row) => {
  proxy.$download.zip(
    `/qualityUnqualifiedOrder/export/${row.id}`,
    `不合格品处理单_${row.orderNo || row.id}.xlsx`
  );
};
onMounted(() => {
  getList();
});
src/views/salesManagement/indicatorStats/index.vue
@@ -25,7 +25,7 @@
          </div>
          <div class="stat-content">
            <div class="stat-value">Â¥{{ indicatorKpis.salesAmount.toLocaleString() }}</div>
            <div class="stat-label">销售额</div>
            <div class="stat-label">合同金额</div>
          </div>
          <div class="stat-bg-decoration"></div>
        </div>
@@ -287,7 +287,7 @@
  const option = {
    title: { text: '多维度销售指标趋势', left: 'center' },
    tooltip: { trigger: 'axis' },
    legend: { data: ['订单数', '销售额'], top: 30 },
    legend: { data: ['订单数', '合同金额'], top: 30 },
    grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true },
    xAxis: { 
      type: 'category', 
@@ -317,7 +317,7 @@
        itemStyle: { color: '#409eff' } 
      },
      { 
        name: '销售额',
        name: '合同金额',
        type: 'bar', 
        yAxisIndex: 0,
        data: chartData.salesAmountList || [], 
@@ -335,7 +335,7 @@
  const option = {
    title: { text: '多维度销售指标趋势', left: 'center' },
    tooltip: { trigger: 'axis' },
    legend: { data: ['订单数', '销售额'], top: 30 },
    legend: { data: ['订单数', '合同金额'], top: 30 },
    grid: { left: '3%', right: '8%', bottom: '3%', containLabel: true },
    xAxis: { type: 'category', data: [] },
    yAxis: [
@@ -355,7 +355,7 @@
    ],
    series: [
      { name: '订单数', type: 'line', yAxisIndex: 1, data: [], itemStyle: { color: '#409eff' } },
      { name: '销售额', type: 'bar', yAxisIndex: 0, data: [], itemStyle: { color: '#67c23a' } }
      { name: '合同金额', type: 'bar', yAxisIndex: 0, data: [], itemStyle: { color: '#67c23a' } }
    ]
  }
  indicatorChart.setOption(option)