From 927f9a4e4d26a2024c797ca0f29125556a514095 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 30 四月 2026 15:16:44 +0800
Subject: [PATCH] 生产排产

---
 src/pages.json                                                |    9 +
 src/api/productionManagement/workOrder.js                     |   14 ++
 src/pages/works.vue                                           |   23 ++-
 src/pages/productionManagement/productionScheduling/index.vue |  241 ++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 274 insertions(+), 13 deletions(-)

diff --git a/src/api/productionManagement/workOrder.js b/src/api/productionManagement/workOrder.js
index 7e8bd86..ffdd6a4 100644
--- a/src/api/productionManagement/workOrder.js
+++ b/src/api/productionManagement/workOrder.js
@@ -2,7 +2,7 @@
 
 export function productWorkOrderPage(query) {
   return request({
-    url: "/productWorkOrder/page",
+    url: "/productionOperationTask/page",
     method: "get",
     params: query,
   });
@@ -10,7 +10,7 @@
 
 export function updateProductWorkOrder(data) {
   return request({
-    url: "/productWorkOrder/updateProductWorkOrder",
+    url: "/productionOperationTask/updateProductWorkOrder",
     method: "post",
     data: data,
   });
@@ -24,10 +24,18 @@
   });
 }
 
+export function assignProductWorkOrder(data) {
+  return request({
+    url: "/productionOperationTask/assign",
+    method: "post",
+    data: data,
+  });
+}
+
 // 涓嬭浇宸ュ崟娴佽浆鍗★紙杩斿洖鏂囦欢娴侊級
 export function downProductWorkOrder(id) {
   return request({
-    url: "/productWorkOrder/down",
+    url: "/productionOperationTask/down",
     method: "post",
     data: { id },
     responseType: "blob",
diff --git a/src/pages.json b/src/pages.json
index ce4058c..2677dc6 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -840,7 +840,14 @@
     {
       "path": "pages/productionManagement/mainProductionPlan/detail",
       "style": {
-        "navigationBarTitleText": "璁″垝璇︽儏",
+        "navigationBarTitleText": "鐢熶骇璁″垝璇︽儏",
+        "navigationStyle": "custom"
+      }
+    },
+    {
+      "path": "pages/productionManagement/productionScheduling/index",
+      "style": {
+        "navigationBarTitleText": "鐢熶骇鎺掍骇",
         "navigationStyle": "custom"
       }
     },
diff --git a/src/pages/productionManagement/productionScheduling/index.vue b/src/pages/productionManagement/productionScheduling/index.vue
new file mode 100644
index 0000000..5cc2c0c
--- /dev/null
+++ b/src/pages/productionManagement/productionScheduling/index.vue
@@ -0,0 +1,241 @@
+<template>
+  <view class="production-scheduling">
+    <!-- 閫氱敤椤甸潰澶撮儴 -->
+    <PageHeader title="鐢熶骇鎺掍骇"
+                @back="goBack" />
+    <!-- 鎼滅储鍖哄煙 -->
+    <view class="search-section">
+      <view class="search-bar">
+        <view class="search-input">
+          <up-input class="search-text"
+                    placeholder="璇疯緭鍏ュ伐鍗曠紪鍙�"
+                    v-model="searchForm.workOrderNo"
+                    @confirm="handleQuery"
+                    clearable />
+        </view>
+        <view class="filter-button"
+              @click="handleQuery">
+          <up-icon name="search"
+                   size="24"
+                   color="#999"></up-icon>
+        </view>
+      </view>
+    </view>
+    <!-- 鍒楄〃 -->
+    <scroll-view scroll-y
+                 class="ledger-list"
+                 v-if="tableData.length > 0"
+                 @scrolltolower="loadMore">
+      <view v-for="(item, index) in tableData"
+            :key="item.id || index"
+            class="ledger-item">
+        <view class="item-header">
+          <view class="item-left">
+            <view class="document-icon">
+              <up-icon name="file-text"
+                       size="16"
+                       color="#ffffff"></up-icon>
+            </view>
+            <text class="item-id">{{ item.workOrderNo }}</text>
+          </view>
+          <view class="item-right">
+            <up-tag :text="item.workOrderType"
+                    size="mini"
+                    type="primary"
+                    plain></up-tag>
+          </view>
+        </view>
+        <up-divider></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.operationName || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">闇�姹傛暟閲�</text>
+            <text class="detail-value">{{ item.planQuantity || 0 }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">瀹屾垚鏁伴噺</text>
+            <text class="detail-value">{{ item.completeQuantity || 0 }}</text>
+          </view>
+          <view class="progress-section">
+            <text class="detail-label">瀹屾垚杩涘害</text>
+            <view class="progress-bar">
+              <up-line-progress :percentage="toProgressPercentage(item.completionStatus)"
+                                :activeColor="progressColor(item.completionStatus)"
+                                :showText="true"></up-line-progress>
+            </view>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">璁″垝寮�濮�</text>
+            <text class="detail-value">{{ item.planStartTime || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">璁″垝缁撴潫</text>
+            <text class="detail-value">{{ item.planEndTime || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">瀹為檯寮�濮�</text>
+            <text class="detail-value">{{ item.actualStartTime || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">瀹為檯缁撴潫</text>
+            <text class="detail-value">{{ item.actualEndTime || '-' }}</text>
+          </view>
+          <view class="detail-row">
+            <text class="detail-label">鎸囧畾鎶ュ伐浜�</text>
+            <view class="detail-value tags-box">
+              <template v-if="item.userNames">
+                <up-tag v-for="(name, idx) in item.userNames.split(',')"
+                        :key="idx"
+                        :text="name"
+                        size="mini"
+                        type="info"
+                        plain
+                        class="user-tag"></up-tag>
+              </template>
+              <text v-else>-</text>
+            </view>
+          </view>
+        </view>
+      </view>
+      <up-loadmore :status="loadStatus" />
+    </scroll-view>
+    <view v-else-if="!loading"
+          class="no-data">
+      <up-empty mode="data"
+                text="鏆傛棤鏁版嵁"></up-empty>
+    </view>
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, toRefs, getCurrentInstance } from "vue";
+  import { onShow } from "@dcloudio/uni-app";
+  import { productWorkOrderPage } from "@/api/productionManagement/workOrder.js";
+  import PageHeader from "@/components/PageHeader.vue";
+
+  const { proxy } = getCurrentInstance();
+
+  const loading = ref(false);
+  const tableData = ref([]);
+  const loadStatus = ref("loadmore");
+
+  const page = reactive({
+    current: 1,
+    size: 10,
+    total: 0,
+  });
+
+  const data = reactive({
+    searchForm: {
+      workOrderNo: "",
+    },
+  });
+  const { searchForm } = toRefs(data);
+
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  const handleQuery = () => {
+    page.current = 1;
+    tableData.value = [];
+    getList();
+  };
+
+  const getList = () => {
+    if (loading.value) return;
+    loading.value = true;
+
+    const params = {
+      ...searchForm.value,
+      ...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;
+
+        if (tableData.value.length >= page.total) {
+          loadStatus.value = "nomore";
+        } else {
+          loadStatus.value = "loadmore";
+        }
+      })
+      .catch(() => {
+        loading.value = false;
+        uni.showToast({ title: "鍔犺浇澶辫触", icon: "error" });
+      });
+  };
+
+  const loadMore = () => {
+    if (loadStatus.value === "nomore" || loading.value) return;
+    page.current++;
+    getList();
+  };
+
+  const toProgressPercentage = val => {
+    const n = Number(val);
+    if (!Number.isFinite(n)) return 0;
+    if (n <= 0) return 0;
+    if (n >= 100) return 100;
+    return Math.round(n);
+  };
+
+  const progressColor = percentage => {
+    const p = toProgressPercentage(percentage);
+    if (p < 30) return "#f56c6c";
+    if (p < 50) return "#e6a23c";
+    if (p < 80) return "#409eff";
+    return "#67c23a";
+  };
+
+  onShow(() => {
+    handleQuery();
+  });
+</script>
+
+<style scoped lang="scss">
+  @import "@/styles/sales-common.scss";
+
+  .production-scheduling {
+    padding-bottom: 20rpx;
+  }
+  .progress-bar {
+    margin-top: 20rpx;
+    margin-bottom: 20rpx;
+  }
+
+  .tags-box {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 8rpx;
+    justify-content: flex-end;
+  }
+
+  .user-tag {
+    margin-bottom: 4rpx;
+  }
+</style>
diff --git a/src/pages/works.vue b/src/pages/works.vue
index 202d9ca..bb7eeab 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -570,17 +570,17 @@
   // 鐢熶骇绠℃帶鍔熻兘鏁版嵁
   const productionItems = reactive([
     {
-      icon: "/static/images/icon/shengchandingdan@2x.svg",
+      icon: "/static/images/icon/shengchanbaogong.svg",
       label: "鐢熶骇璁㈠崟",
     },
     // {
     //   icon: "/static/images/icon/shengchanpaigong@2x.svg",
     //   label: "鐢熶骇娲惧伐",
     // },
-    // {
-    //   icon: "/static/images/icon/shengchanpaichan@2x.svg",
-    //   label: "宸ュ簭鎺掍骇",
-    // },
+    {
+      icon: "/static/images/icon/shengchanpaichan@2x.svg",
+      label: "鐢熶骇鎺掍骇",
+    },
     {
       icon: "/static/images/icon/shengchanbaogong.svg",
       label: "涓荤敓浜ц鍒�",
@@ -589,10 +589,10 @@
       icon: "/static/images/icon/shengchanbaogong.svg",
       label: "鐢熶骇鎶ュ伐",
     },
-    {
-      icon: "/static/images/icon/shengchanbaogong.svg",
-      label: "鐢熶骇宸ュ崟",
-    },
+    // {
+    //   icon: "/static/images/icon/shengchanbaogong.svg",
+    //   label: "鐢熶骇宸ュ崟",
+    // },
     // {
     //   icon: "/static/images/icon/shengchanhesuan@2x.svg",
     //   label: "鐢熶骇鏍哥畻",
@@ -852,6 +852,11 @@
           url: "/pages/productionManagement/mainProductionPlan/index",
         });
         break;
+      case "鐢熶骇鎺掍骇":
+        uni.navigateTo({
+          url: "/pages/productionManagement/productionScheduling/index",
+        });
+        break;
       case "鐢熶骇鎶ュ伐":
         getcode();
         break;

--
Gitblit v1.9.3