spring
2026-05-12 09f98ab6b6ac0779f12bd003443e1e6065cad256
fix: 支持按照工单进行输入报工信息报工。也可以扫码报工
已添加1个文件
已修改4个文件
465 ■■■■ 文件已修改
src/manifest.json 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages.json 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/index.vue 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/productionManagement/productionReport/workOrderList.vue 345 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/pages/works.vue 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/manifest.json
@@ -1,5 +1,5 @@
{
    "name" : "信息管理",
  "name": "青铝绿行",
    "appid" : "__UNI__099A590",
    "description" : "",
    "versionName" : "1.1.5",
src/pages.json
@@ -824,6 +824,13 @@
      }
    },
    {
      "path": "pages/productionManagement/productionReport/workOrderList",
      "style": {
        "navigationBarTitleText": "生产报工",
        "navigationStyle": "custom"
      }
    },
    {
      "path": "pages/productionManagement/productionReporting/ledger",
      "style": {
        "navigationBarTitleText": "报工台账",
src/pages/index.vue
@@ -206,7 +206,7 @@
  {
    label: "生产报工",
    icon: "/static/images/icon/shengchanbaogong.svg",
    action: "scan",
    route: "/pages/productionManagement/productionReport/workOrderList",
  },
  {
    label: "设备巡检",
@@ -242,28 +242,6 @@
}
function handleQuickTool(item) {
  if (item?.action === "scan") {
    // ç”Ÿäº§æŠ¥å·¥ - è°ƒç”¨æ‰«ç 
    uni.scanCode({
      success: (res) => {
        console.log("扫码结果:", res);
        // è§£æžæ‰«ç ç»“果并跳转到生产报工页面
        try {
          const scanResult = JSON.parse(res.result);
          uni.navigateTo({
            url: `/pages/productionManagement/productionReport/index?orderRow=${encodeURIComponent(JSON.stringify(scanResult))}`
          });
        } catch (e) {
          console.error("扫码结果解析失败:", e);
          uni.showToast({ title: "无效的二维码", icon: "none" });
        }
      },
      fail: (err) => {
        console.error("扫码失败:", err);
      }
    });
    return;
  }
  if (!item?.route) return;
  uni.navigateTo({ url: item.route });
}
src/pages/productionManagement/productionReport/workOrderList.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,345 @@
<template>
  <view class="report-entry-page">
    <PageHeader title="生产报工"
                @back="goBack" />
    <view class="scan-section">
      <view class="scan-card"
            @click="startScan">
        <up-icon name="scan"
                 size="28"
                 color="#2979ff" />
        <view class="scan-text-wrap">
          <text class="scan-title">扫码报工</text>
        </view>
        <up-icon name="arrow-right"
                 size="18"
                 color="#c0c4cc" />
      </view>
    </view>
    <scroll-view v-if="tableData.length > 0"
                 scroll-y
                 class="list-body"
                 @scrolltolower="loadMore">
      <view v-for="(item, index) in tableData"
            :key="item.id || index"
            class="ledger-item"
            @click="onCardClick(item)">
        <view class="item-header">
          <view class="item-left">
            <view class="document-icon">
              <up-icon name="file-text"
                       size="16"
                       color="#ffffff" />
            </view>
            <text class="item-id">{{ item.workOrderNo || "-" }}</text>
          </view>
          <view class="item-right operation-header-right">
            <view class="operation-pill">
              <text class="operation-pill-text">{{ operationNameOf(item) }}</text>
            </view>
            <view v-if="item.endOrder"
                  class="end-pill">
              <text class="end-pill-text">已结束</text>
            </view>
          </view>
        </view>
        <up-divider />
        <view class="item-details">
          <view class="detail-row">
            <text class="detail-label">生产订单号</text>
            <text class="detail-value">{{ item.npsNo || "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">产品名称</text>
            <text class="detail-value">{{ item.productName || "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">规格</text>
            <text class="detail-value">{{ item.model || "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">单位</text>
            <text class="detail-value">{{ item.unit || "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">需求数量</text>
            <text class="detail-value">{{ item.planQuantity ?? "-" }}</text>
          </view>
          <view class="detail-row">
            <text class="detail-label">完成数量</text>
            <text class="detail-value">{{ item.completeQuantity ?? "-" }}</text>
          </view>
        </view>
      </view>
      <up-loadmore :status="loadStatus" />
    </scroll-view>
    <view v-else-if="!loading"
          class="no-data">
      <up-empty mode="data"
                text="暂无工单数据" />
    </view>
  </view>
</template>
<script setup>
  import { ref, reactive } from "vue";
  import { onShow } from "@dcloudio/uni-app";
  import PageHeader from "@/components/PageHeader.vue";
  import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
  import { getProductWorkOrderById } from "@/api/productionManagement/productionReporting";
  import modal from "@/plugins/modal";
  import useUserStore from "@/store/modules/user";
  const userStore = useUserStore();
  const loading = ref(false);
  const tableData = ref([]);
  const loadStatus = ref("loadmore");
  const page = reactive({
    current: 1,
    size: 10,
    total: 0,
  });
  const goBack = () => {
    uni.navigateBack();
  };
  const operationNameOf = item =>
    item?.operationName || item?.processName || "-";
  const reportPermissionMessage = row => {
    if (row.endOrder) return "该订单已结束,无法报工";
    const pq = Number(row.planQuantity);
    if (Number.isFinite(pq) && pq <= 0) return "待生产数量为0,无法报工";
    // if (row.userIds) {
    //   try {
    //     const userIds =
    //       typeof row.userIds === "string" ? JSON.parse(row.userIds) : row.userIds;
    //     if (
    //       Array.isArray(userIds) &&
    //       userIds.length > 0 &&
    //       !userIds.some(id => String(id) === String(userStore.id))
    //     ) {
    //       return "您不在该工单的指定报工人范围内";
    //     }
    //   } catch {
    //     return "工单报工权限校验失败";
    //   }
    // }
    return "";
  };
  const navigateToReport = orderRowStr => {
    uni.navigateTo({
      url: `/pages/productionManagement/productionReport/index?orderRow=${encodeURIComponent(orderRowStr)}`,
    });
  };
  const onCardClick = row => {
    const msg = reportPermissionMessage(row);
    if (msg) {
      uni.showToast({ title: msg, icon: "none" });
      return;
    }
    navigateToReport(JSON.stringify(row));
  };
  const handleQuery = () => {
    page.current = 1;
    tableData.value = [];
    getList();
  };
  const getList = () => {
    if (loading.value) return;
    loading.value = true;
    const params = { ...page };
    productWorkOrderPage(params)
      .then(res => {
        loading.value = false;
        const records = res.data?.records || [];
        tableData.value =
          page.current === 1 ? records : [...tableData.value, ...records];
        page.total = res.data?.total ?? 0;
        loadStatus.value =
          tableData.value.length >= page.total ? "nomore" : "loadmore";
      })
      .catch(() => {
        loading.value = false;
        uni.showToast({ title: "加载失败", icon: "none" });
      });
  };
  const loadMore = () => {
    if (loadStatus.value === "nomore" || loading.value) return;
    page.current++;
    getList();
  };
  const startScan = () => {
    uni.scanCode({
      success: async res => {
        const scanResult = res.result;
        let orderRow = "";
        const isNumericId = /^\d+$/.test(String(scanResult).trim());
        if (isNumericId) {
          const workOrderId = String(scanResult).trim();
          modal.loading("正在获取工单信息...");
          try {
            const workRes = await getProductWorkOrderById({ id: workOrderId });
            modal.closeLoading();
            if (workRes.code === 200 && workRes.data) {
              const workData = workRes.data;
              if (workData.endOrder === true) {
                modal.msgError("该订单已结束,无法报工");
                return;
              }
              orderRow = JSON.stringify(workData);
            } else {
              modal.msgError("未找到对应的工单信息");
              return;
            }
          } catch (error) {
            modal.closeLoading();
            modal.msgError(
              "获取工单信息失败: " + (error?.message || "未知错误")
            );
            return;
          }
        } else {
          try {
            const orderRowStart = scanResult.indexOf("orderRow={");
            if (orderRowStart !== -1) {
              orderRow = scanResult.substring(orderRowStart + 9);
            } else {
              orderRow = scanResult;
            }
            JSON.parse(orderRow);
          } catch {
            try {
              const parsed = JSON.parse(scanResult);
              orderRow = JSON.stringify(parsed);
            } catch {
              modal.msgError("订单解析失败,请检查二维码格式");
              return;
            }
          }
        }
        navigateToReport(orderRow);
      },
      fail: () => {
        uni.showToast({ title: "扫码失败", icon: "none" });
      },
    });
  };
  onShow(() => {
    handleQuery();
  });
</script>
<style scoped lang="scss">
  @import "@/styles/sales-common.scss";
  .report-entry-page {
    min-height: 100vh;
    background: #f8f9fa;
    display: flex;
    flex-direction: column;
  }
  .scan-section {
    padding: 12px 16px 8px;
  }
  .scan-card {
    display: flex;
    align-items: center;
    gap: 12px;
    background: #fff;
    border-radius: 12px;
    padding: 16px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
  }
  .scan-text-wrap {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 4px;
  }
  .scan-title {
    font-size: 16px;
    font-weight: 600;
    color: #303133;
  }
  .scan-desc {
    font-size: 12px;
    color: #909399;
  }
  .list-body {
    flex: 1;
    height: 0;
    padding: 12px 16px 20px;
    box-sizing: border-box;
  }
  .operation-header-right {
    flex: 1;
    min-width: 0;
    justify-content: flex-end;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;
  }
  .operation-pill {
    max-width: 58%;
    min-width: 0;
    padding: 5px 12px;
    border-radius: 20px;
    background: #e8f1ff;
    flex-shrink: 0;
  }
  .operation-pill-text {
    font-size: 12px;
    line-height: 1.35;
    color: #2979ff;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
  }
  .end-pill {
    padding: 5px 10px;
    border-radius: 20px;
    background: #fef0f0;
    flex-shrink: 0;
  }
  .end-pill-text {
    font-size: 12px;
    line-height: 1.35;
    color: #f56c6c;
    font-weight: 500;
  }
  .no-data {
    flex: 1;
    padding-top: 80px;
  }
</style>
src/pages/works.vue
@@ -306,7 +306,6 @@
<script setup>
  import { ref, onMounted, nextTick, reactive, computed } from "vue";
  import { userLoginFacotryList } from "@/api/login";
  import { getProductWorkOrderById } from "@/api/productionManagement/productionReporting";
  import DownloadProgressMask from "@/components/DownloadProgressMask.vue";
  import modal from "@/plugins/modal";
  import useUserStore from "@/store/modules/user";
@@ -868,7 +867,9 @@
        });
        break;
      case "生产报工":
        getcode();
        uni.navigateTo({
          url: "/pages/productionManagement/productionReport/workOrderList",
        });
        break;
      case "报工台账":
        uni.navigateTo({
@@ -1076,88 +1077,6 @@
        factoryList.value = [];
      });
  }
  const getcode = async () => {
    uni.scanCode({
      success: async res => {
        // è§£æžäºŒç»´ç å†…容
        const scanResult = res.result;
        let orderRow = "";
        // åˆ¤æ–­æ‰«æç»“果是否为纯数字(id)
        const isNumericId = /^\d+$/.test(scanResult.trim());
        if (isNumericId) {
          // å¦‚果是纯数字,根据 id èŽ·å–å·¥å•æ•°æ®
          const workOrderId = scanResult.trim();
          modal.loading("正在获取工单信息...");
          try {
            const workRes = await getProductWorkOrderById({ id: workOrderId });
            modal.closeLoading();
            console.log("工单查询结果:", workRes);
            if (workRes.code === 200 && workRes.data) {
              // æ–°æŽ¥å£è¿”回的是单个对象,不是数组
              const workData = workRes.data;
              console.log("工单数据:", workData);
              if (workData.endOrder === true) {
                modal.msgError("该订单已结束,无法报工");
                return;
              }
              orderRow = JSON.stringify(workData);
              console.log("构造的orderRow:", orderRow);
            } else {
              modal.msgError("未找到对应的工单信息");
              return;
            }
          } catch (error) {
            modal.closeLoading();
            console.error("获取工单信息失败:", error);
            modal.msgError("获取工单信息失败: " + (error.message || "未知错误"));
            return;
          }
        } else {
          // å¦‚果不是纯数字,尝试从扫码结果中提取orderRow参数
          try {
            // å¤„理混合格式: http://...?orderRow={...}
            const orderRowStart = scanResult.indexOf("orderRow={");
            if (orderRowStart !== -1) {
              // æå–从orderRow={开始的JSON内容
              const jsonPart = scanResult.substring(orderRowStart + 9); // 9是"orderRow=".length
              orderRow = jsonPart;
            } else {
              // å¦‚果直接是JSON字符串,尝试解析
              orderRow = scanResult;
            }
          } catch (e) {
            console.error(e, "解析失败====????=====");
            orderRow = "";
          }
          // éªŒè¯æ˜¯å¦ä¸ºæœ‰æ•ˆçš„JSON
          try {
            JSON.parse(orderRow);
          } catch (error) {
            modal.msgError("订单解析失败,请检查二维码格式");
            return;
          }
        }
        // æ‰«ç æˆåŠŸåŽè·³è½¬åˆ°ç”Ÿäº§æŠ¥å·¥é¡µé¢ï¼Œå¹¶ä¼ é€’orderRow参数
        uni.navigateTo({
          url: `/pages/productionManagement/productionReport/index?orderRow=${orderRow}`,
        });
      },
      fail: err => {
        uni.showToast({
          title: "扫码失败",
          icon: "none",
        });
      },
    });
  };
  const changeFactory = async arr => {
    show.value = false;
    const factoryId = factoryListTem.value[arr.indexs[0]].deptId;