6 天以前 22229c67d87f0fae4c4bc90db106b8a1e2c49b53
src/views/productionManagement/workOrder/index.vue
@@ -24,13 +24,7 @@
                :tableData="tableData"
                :page="page"
                :tableLoading="tableLoading"
                @pagination="pagination">
        <template #completionStatus="{ row }">
          <el-progress :percentage="toProgressPercentage(row?.completionStatus)"
                       :color="progressColor(toProgressPercentage(row?.completionStatus))"
                       :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''" />
        </template>
      </PIMTable>
                @pagination="pagination" />
    </div>
    <el-dialog v-model="editDialogVisible"
               title="编辑时间"
@@ -233,6 +227,7 @@
            <el-form-item label="班组信息"
                          prop="teamList">
              <el-select v-model="reportForm.teamList"
                         ref="teamSelectRef"
                         multiple
                         filterable
                         allow-create
@@ -242,8 +237,8 @@
                         value-key="userName"
                         placeholder="请选择或输入班组成员"
                         @change="handleTeamListChange">
                <el-option v-for="user in reportForm.userIdsList"
                           :key="user.userId"
                <el-option v-for="user in teamSelectOptions"
                           :key="user.userId || `custom-${user.nickName}`"
                           :label="user.nickName"
                           :value="{ userId: user.userId, userName: user.nickName }" />
              </el-select>
@@ -288,6 +283,19 @@
          <!--          </el-col>-->
        </el-row>
      </el-form>
      <el-table :data="personList" border style="margin-top: 12px;">
        <el-table-column label="生产人" prop="userName" min-width="100" align="center" />
        <el-table-column label="产量" min-width="120" align="center">
          <template #default="scope">
            <el-input-number v-model="scope.row.quantity" :min="0" :controls="false" placeholder="产量" style="width: 100%" />
          </template>
        </el-table-column>
        <el-table-column label="生产时长(分钟)" min-width="140" align="center">
          <template #default="scope">
            <el-input-number v-model="scope.row.workHour" :min="0" :controls="false" placeholder="时长(分钟)" style="width: 100%" />
          </template>
        </el-table-column>
      </el-table>
      <template #footer>
        <span class="dialog-footer">
          <el-button type="primary"
@@ -489,6 +497,7 @@
    productWorkOrderPage,
    updateProductWorkOrder,
    addProductMain,
    getProductWorkOrderById,
    downProductWorkOrder,
    addProductionMachineRecord,
    productionMachineRecordListPage,
@@ -499,12 +508,18 @@
  import { getCurrentInstance, reactive, toRefs } from "vue";
  import FilesDia from "./components/filesDia.vue";
  import { getDeviceLedger } from "@/api/equipmentManagement/ledger.js";
  import useUserStore from "@/store/modules/user";
  const { proxy } = getCurrentInstance();
  const userStore = useUserStore();
  const isAdminUser = computed(() =>
    ["admin", "普通2", "最高1"].some(r => (userStore.roles || []).includes(r))
  );
  const currentUserId = ref("");
  const deviceOptions = ref([]);
  const currentUserName = ref("");
  const teamSelectRef = ref(null);
  const ensureCurrentUser = async () => {
    if (currentUserId.value) return;
@@ -567,9 +582,13 @@
    return ids.includes(uid);
  };
  // 判断当前用户是否可以报工(工单报工人 或 工序报工人)
  // 判断当前用户是否可以报工(工单报工人 或 工序报工人,管理员不受限)
  const canCurrentUserReport = row => {
    return isCurrentUserInUserIds(row) || isCurrentUserInProcessUserIds(row);
    return (
      isAdminUser.value ||
      isCurrentUserInUserIds(row) ||
      isCurrentUserInProcessUserIds(row)
    );
  };
  const canOperateByReportWorker = computed(() => {
@@ -1073,25 +1092,13 @@
      width: "140",
    },
    {
      label: "加放数",
      prop: "processRouteAddNum",
      width: "100",
    },
    {
      label: "完成数量",
      prop: "completeQuantity",
      width: "140",
    },
    {
      label: "完成进度",
      prop: "completionStatus",
      dataType: "slot",
      slot: "completionStatus",
      width: "140",
    },
    {
      label: "计划开始时间",
      prop: "planStartTime",
      width: "140",
    },
    {
      label: "计划结束时间",
      prop: "planEndTime",
      width: "140",
    },
    {
@@ -1134,11 +1141,10 @@
          clickFun: row => {
            showReportDialog(row);
          },
          // 用户当前id在工单报工人或工序报工人中
          // 用户当前id在工单报工人或工序报工人中,且完成数量未达计划数量时可继续分批报工
          disabled: row =>
            row.completeQuantity >= row.planQuantity ||
            !canCurrentUserReport(row) ||
            row.hasUnreportedMachine,
            !canCurrentUserReport(row),
        },
        {
          name: "生产排产",
@@ -1215,6 +1221,31 @@
    productMainId: null,
    teamList: [],
    deviceId: null,
  });
  // 班组人员产量时长明细 [{ userId, userName, quantity, workHour }]
  const personList = ref([]);
  const teamSelectOptions = computed(() => {
    const base =
      Array.isArray(reportForm.userIdsList) && reportForm.userIdsList.length > 0
        ? reportForm.userIdsList.map(u => ({
            userId: String(u.userId ?? ""),
            nickName: String(u.nickName ?? "").trim(),
          }))
        : [];
    const baseNameSet = new Set(base.map(u => u.nickName));
    const selected = Array.isArray(reportForm.teamList)
      ? reportForm.teamList
      : [];
    const extraNames = selected
      .map(item => {
        if (typeof item === "string") return String(item).trim();
        const name = item?.userName ?? item?.nickName ?? "";
        return String(name).trim();
      })
      .filter(Boolean)
      .filter(name => !baseNameSet.has(name));
    const extras = extraNames.map(name => ({ userId: "", nickName: name }));
    return [...base, ...extras];
  });
  function removeLastFour(str) {
    if (!str) return ""; // 空值保护
@@ -1520,7 +1551,7 @@
      });
  };
  const showReportDialog = row => {
  const showReportDialog = async row => {
    currentReportRowData.value = row;
    reportForm.planQuantity = row.planQuantity - row.completeQuantity;
    reportForm.quantity = row.planQuantity - row.completeQuantity;
@@ -1533,7 +1564,7 @@
    reportForm.replenishQty = 0;
    reportForm.teamList = [];
    reportForm.scrapQty = 0;
    reportForm.addQty = 0;
    reportForm.addQty = row.processRouteAddNum ? Number(row.processRouteAddNum) : 0;
    reportForm.userIds = row.userIds || [];
    // 合并工单报工人和工序报工人,去重
@@ -1552,6 +1583,34 @@
        userId: item.userId,
        nickName: item.nickName,
      }));
    // 获取报工弹窗数据,带出默认班组成员(合并排产人员与机台操作员,去重)
    personList.value = [];
    try {
      const res = await getProductWorkOrderById(row.id);
      const reportPersons = (Array.isArray(res?.data?.reportPersons)
        ? res.data.reportPersons
        : []
      )
        .filter(p => p && typeof p === "object")
        .map(p => ({
          userId: p.userId ?? p.id ?? "",
          userName: String(p.userName || p.nickName || "").trim(),
        }))
        .filter(p => p.userName !== "");
      personList.value = reportPersons.map(p => ({
        userId: p.userId,
        userName: p.userName,
        quantity: null,
        workHour: null,
      }));
      reportForm.teamList = reportPersons.map(p => ({
        userId: p.userId,
        userName: p.userName,
      }));
    } catch (err) {
      console.error("获取报工弹窗数据失败", err);
    }
    nextTick(() => {
      reportFormRef.value?.clearValidate();
@@ -1624,10 +1683,23 @@
      //   return;
      // }
      // 校验各班组产量之和等于报工总产量
      const personQuantityTotal = personList.value.reduce(
        (sum, p) => sum + (Number(p.quantity) || 0),
        0
      );
      if (personQuantityTotal !== quantity) {
        ElMessageBox.alert("各班组产量之和必须等于报工总产量", "提示", {
          confirmButtonText: "确定",
        });
        return;
      }
      const submitData = {
        ...reportForm,
        quantity: quantity,
        scrapQty: scrapQty,
        personList: personList.value,
      };
      addProductMain(submitData).then(res => {
        if (res.code === 200) {
@@ -1675,13 +1747,39 @@
  const handleTeamListChange = val => {
    if (!Array.isArray(val)) return;
    if (!val.some(item => typeof item === "string")) return;
    reportForm.teamList = val.map(item => {
    let hasString = false;
    const newList = val.map(item => {
      if (typeof item === "string") {
        hasString = true;
        return { userName: item };
      }
      return item;
    });
    if (hasString) {
      reportForm.teamList = newList;
    }
    // 解决 allow-create 在 multiple 模式下输入框内容不自动清空的问题
    setTimeout(() => {
      if (teamSelectRef.value) {
        // 1. 清除内部 query 状态
        teamSelectRef.value.query = "";
        if (teamSelectRef.value.states) {
          teamSelectRef.value.states.query = "";
          teamSelectRef.value.states.inputValue = "";
        }
        // 2. 强制清除 DOM 输入框的值
        const input = teamSelectRef.value.$el?.querySelector("input");
        if (input) {
          input.value = "";
        }
        // 3. 针对某些版本,重置选中标签的内部偏移(防止输入框被挤占)
        if (typeof teamSelectRef.value.resetInputState === "function") {
          teamSelectRef.value.resetInputState();
        }
      }
    }, 50);
  };
  // 审核人
  const handleReviewerIdChange = userId => {
@@ -1711,6 +1809,8 @@
  // 判断当前用户是否能排产
  const canScheduleByWorkOrderNo = row => {
    // 管理员可排产
    if (isAdminUser.value) return true;
    if (!row) return false;
    const uid = String(currentUserId.value || "");