From 66a61e5084be68c8f7aa72f28607ef7d1664fb06 Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期三, 06 五月 2026 14:41:12 +0800
Subject: [PATCH] 生产核算模块开发

---
 src/pages.json                                                |   14 
 src/pages/productionManagement/productionAccounting/index.vue |  498 +++++++++++++++++++++++++++++++++++++++++++++++++
 src/pages/works.vue                                           |   20 -
 src/api/productionManagement/productionCosting.js             |   18 +
 4 files changed, 531 insertions(+), 19 deletions(-)

diff --git a/src/api/productionManagement/productionCosting.js b/src/api/productionManagement/productionCosting.js
index 8cc0251..ed969f6 100644
--- a/src/api/productionManagement/productionCosting.js
+++ b/src/api/productionManagement/productionCosting.js
@@ -8,4 +8,22 @@
     method: "get",
     params: query,
   });
+}
+
+// 宸﹁竟琛ㄦ牸鐨勬帴鍙� (姹囨��)
+export function salesLedgerProductionAccountingList(query) {
+  return request({
+    url: "/productionAccount/listPage",
+    method: "get",
+    params: query,
+  });
+}
+
+// 鍙宠竟琛ㄦ牸鐨勬帴鍙� (鏄庣粏)
+export function salesLedgerProductionAccountingListProductionDetails(query) {
+  return request({
+    url: "/productionAccount/listProductionDetails",
+    method: "get",
+    params: query,
+  });
 }
\ No newline at end of file
diff --git a/src/pages.json b/src/pages.json
index ee0b3e0..55521e4 100644
--- a/src/pages.json
+++ b/src/pages.json
@@ -858,13 +858,13 @@
         "navigationStyle": "custom"
       }
     },
-    // {
-    //   "path": "pages/productionManagement/productionCosting/index",
-    //   "style": {
-    //     "navigationBarTitleText": "鐢熶骇鏍哥畻",
-    //     "navigationStyle": "custom"
-    //   }
-    // },
+    {
+      "path": "pages/productionManagement/productionAccounting/index",
+      "style": {
+        "navigationBarTitleText": "鐢熶骇鏍哥畻",
+        "navigationStyle": "custom"
+      }
+    },
     {
       "path": "pages/inventoryManagement/receiptManagement/index",
       "style": {
diff --git a/src/pages/productionManagement/productionAccounting/index.vue b/src/pages/productionManagement/productionAccounting/index.vue
new file mode 100644
index 0000000..b20b407
--- /dev/null
+++ b/src/pages/productionManagement/productionAccounting/index.vue
@@ -0,0 +1,498 @@
+<template>
+  <view class="production-accounting">
+    <PageHeader title="鐢熶骇鏍哥畻"
+                @back="goBack" />
+
+    <!-- 绛涢�夊尯鍩� -->
+    <view class="filter-section">
+      <view class="date-type-selector">
+        <up-tabs :list="dateTypeList"
+                 :current="currentDateTypeIndex"
+                 @change="handleDateTypeChange"
+                 :activeStyle="{ color: '#2979ff', fontWeight: 'bold' }"
+                 lineWidth="30"
+                 lineHeight="3" />
+      </view>
+
+      <view class="date-picker-bar"
+            @click="showDatePicker = true">
+        <view class="date-display">
+          <up-icon name="calendar"
+                   size="20"
+                   color="#2979ff"></up-icon>
+          <text class="date-text">{{ dateDisplayText }}</text>
+        </view>
+        <up-icon name="arrow-right"
+                 size="16"
+                 color="#999"></up-icon>
+      </view>
+    </view>
+
+    <!-- 姹囨�诲垪琛� -->
+    <view class="summary-section"
+          v-if="!showDetail">
+      <view class="section-header">
+        <text class="section-title">鐢熶骇浜哄憳姹囨��</text>
+      </view>
+      <view class="ledger-list"
+            v-if="summaryList.length > 0">
+        <view v-for="(item, index) in summaryList"
+              :key="index"
+              class="ledger-item"
+              @click="handleRowClick(item)">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="user-icon">
+                <up-icon name="account"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ item.schedulingUserName || '鏈煡' }}</text>
+            </view>
+            <view class="item-right">
+              <up-icon name="arrow-right"
+                       size="16"
+                       color="#999"></up-icon>
+            </view>
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <view class="detail-grid">
+              <view class="grid-item">
+                <text class="grid-label">浜ч噺</text>
+                <text class="grid-value">{{ item.finishedNum || 0 }}</text>
+              </view>
+              <view class="grid-item">
+                <text class="grid-label">宸ヨ祫</text>
+                <text class="grid-value highlight">楼{{ item.wages || 0 }}</text>
+              </view>
+              <view class="grid-item">
+                <text class="grid-label">鍚堟牸鐜�</text>
+                <text class="grid-value">{{ formatOutputRate(item.outputRate) }}</text>
+              </view>
+            </view>
+          </view>
+        </view>
+      </view>
+      <view v-else
+            class="no-data">
+        <up-empty mode="data"
+                  text="鏆傛棤姹囨�绘暟鎹�" />
+      </view>
+    </view>
+
+    <!-- 鏄庣粏鍒楄〃 (鐐瑰嚮姹囨�昏鍚庢樉绀�) -->
+    <view class="detail-section"
+          v-else>
+      <view class="section-header back-bar"
+            @click="showDetail = false">
+        <up-icon name="arrow-left"
+                 size="16"
+                 color="#2979ff"></up-icon>
+        <text class="back-text">杩斿洖姹囨�� ({{ currentUserName }})</text>
+      </view>
+
+      <view class="ledger-list"
+            v-if="detailList.length > 0">
+        <view v-for="(item, index) in detailList"
+              :key="index"
+              class="ledger-item no-click">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="product-icon">
+                <up-icon name="shopping-cart"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ item.productName }}</text>
+            </view>
+            <view class="item-tag">
+              <text class="tag-text">{{ item.schedulingDate }}</text>
+            </view>
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <view class="detail-row">
+              <text class="detail-label">瑙勬牸鍨嬪彿</text>
+              <text class="detail-value">{{ item.productModelName }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">宸ュ簭</text>
+              <text class="detail-value">{{ item.process }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鐢熶骇鏁伴噺</text>
+              <text class="detail-value">{{ item.quantity }} {{ item.unit }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">宸ユ椂瀹氶</text>
+              <text class="detail-value">{{ item.workHours }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">宸ヨ祫</text>
+              <text class="detail-value highlight">楼{{ item.wages }}</text>
+            </view>
+          </view>
+        </view>
+        <up-loadmore :status="loadStatus"
+                     @loadmore="getDetailList" />
+      </view>
+      <view v-else
+            class="no-data">
+        <up-empty mode="data"
+                  text="鏆傛棤鏄庣粏鏁版嵁" />
+      </view>
+    </view>
+
+    <!-- 鏃ユ湡閫夋嫨鍣� -->
+    <up-datetime-picker :show="showDatePicker"
+                        v-model="pickerValue"
+                        :mode="currentDateType === 'day' ? 'date' : 'year-month'"
+                        @confirm="handleDateConfirm"
+                        @cancel="showDatePicker = false" />
+  </view>
+</template>
+
+<script setup>
+  import { ref, reactive, computed, onMounted } from "vue";
+  import {
+    salesLedgerProductionAccountingList,
+    salesLedgerProductionAccountingListProductionDetails,
+  } from "@/api/productionManagement/productionCosting";
+  import PageHeader from "@/components/PageHeader.vue";
+  import dayjs from "dayjs";
+
+  // 绛涢�夌浉鍏�
+  const dateTypeList = [{ name: "鏃�" }, { name: "鏈�" }];
+  const currentDateTypeIndex = ref(0);
+  const currentDateType = computed(() =>
+    currentDateTypeIndex.value === 0 ? "day" : "month"
+  );
+  const showDatePicker = ref(false);
+  const pickerValue = ref(Date.now());
+  const selectedDate = ref(dayjs().format("YYYY-MM-DD"));
+
+  const dateDisplayText = computed(() => {
+    return currentDateType.value === "day"
+      ? selectedDate.value
+      : dayjs(selectedDate.value).format("YYYY-MM");
+  });
+
+  // 鏁版嵁鐩稿叧
+  const summaryList = ref([]);
+  const detailList = ref([]);
+  const showDetail = ref(false);
+  const currentUserName = ref("");
+  const loadStatus = ref("loadmore");
+
+  const page = reactive({
+    current: 1,
+    size: 20,
+    total: 0,
+  });
+
+  const page1 = reactive({
+    current: 1,
+    size: 20,
+    total: 0,
+  });
+
+  // 杩斿洖涓婁竴椤�
+  const goBack = () => {
+    uni.navigateBack();
+  };
+
+  // 鍒囨崲鏃ユ湡绫诲瀷
+  const handleDateTypeChange = index => {
+    currentDateTypeIndex.value = index.index;
+    if (currentDateType.value === "day") {
+      selectedDate.value = dayjs().format("YYYY-MM-DD");
+    } else {
+      selectedDate.value = dayjs().startOf("month").format("YYYY-MM-DD");
+    }
+    reloadData();
+  };
+
+  // 纭鏃ユ湡閫夋嫨
+  const handleDateConfirm = e => {
+    selectedDate.value = dayjs(e.value).format("YYYY-MM-DD");
+    showDatePicker.value = false;
+    reloadData();
+  };
+
+  // 鏍煎紡鍖栧悎鏍肩巼
+  const formatOutputRate = val => {
+    if (val == null || val === "") return "-";
+    return parseFloat(val).toFixed(2) + "%";
+  };
+
+  // 鍔犺浇姹囨�诲垪琛�
+  const getSummaryList = () => {
+    uni.showLoading({ title: "鍔犺浇涓�..." });
+    const params = {
+      dateType: currentDateType.value,
+      entryDate: currentDateType.value === "day" ? selectedDate.value : undefined,
+      entryDateStart:
+        currentDateType.value === "month"
+          ? dayjs(selectedDate.value).startOf("month").format("YYYY-MM-DD")
+          : undefined,
+      entryDateEnd:
+        currentDateType.value === "month"
+          ? dayjs(selectedDate.value).endOf("month").format("YYYY-MM-DD")
+          : undefined,
+      pageNum: page.current,
+      pageSize: page.size,
+    };
+
+    salesLedgerProductionAccountingList(params)
+      .then(res => {
+        summaryList.value = res.data.records || [];
+        page.total = res.data.total || 0;
+      })
+      .finally(() => {
+        uni.hideLoading();
+      });
+  };
+
+  // 鍔犺浇鏄庣粏鍒楄〃
+  const getDetailList = (isLoadMore = false) => {
+    if (!isLoadMore) {
+      page1.current = 1;
+      detailList.value = [];
+    }
+    loadStatus.value = "loading";
+
+    const params = {
+      schedulingUserName: currentUserName.value,
+      dateType: currentDateType.value,
+      entryDate: currentDateType.value === "day" ? selectedDate.value : undefined,
+      entryDateStart:
+        currentDateType.value === "month"
+          ? dayjs(selectedDate.value).startOf("month").format("YYYY-MM-DD")
+          : undefined,
+      entryDateEnd:
+        currentDateType.value === "month"
+          ? dayjs(selectedDate.value).endOf("month").format("YYYY-MM-DD")
+          : undefined,
+      pageNum: page1.current,
+      pageSize: page1.size,
+    };
+
+    salesLedgerProductionAccountingListProductionDetails(params)
+      .then(res => {
+        const records = res.data.records || [];
+        detailList.value = isLoadMore ? [...detailList.value, ...records] : records;
+        page1.total = res.data.total || 0;
+
+        if (detailList.value.length >= page1.total) {
+          loadStatus.value = "nomore";
+        } else {
+          loadStatus.value = "loadmore";
+          page1.current++;
+        }
+      })
+      .catch(() => {
+        loadStatus.value = "loadmore";
+      });
+  };
+
+  // 鐐瑰嚮姹囨�昏
+  const handleRowClick = item => {
+    currentUserName.value = item.schedulingUserName;
+    showDetail.value = true;
+    getDetailList();
+  };
+
+  // 閲嶆柊鍔犺浇鏁版嵁
+  const reloadData = () => {
+    page.current = 1;
+    showDetail.value = false;
+    getSummaryList();
+  };
+
+  onMounted(() => {
+    getSummaryList();
+  });
+</script>
+
+<style scoped lang="scss">
+  .production-accounting {
+    background-color: #f5f7fa;
+    min-height: 100vh;
+    padding-bottom: 30rpx;
+
+    .filter-section {
+      background-color: #ffffff;
+      padding: 20rpx 30rpx;
+      margin-bottom: 20rpx;
+
+      .date-type-selector {
+        margin-bottom: 20rpx;
+      }
+
+      .date-picker-bar {
+        display: flex;
+        justify-content: space-between;
+        align-items: center;
+        background-color: #f0f4ff;
+        padding: 16rpx 24rpx;
+        border-radius: 8rpx;
+
+        .date-display {
+          display: flex;
+          align-items: center;
+          gap: 12rpx;
+
+          .date-text {
+            font-size: 28rpx;
+            color: #2979ff;
+            font-weight: bold;
+          }
+        }
+      }
+    }
+
+    .section-header {
+      padding: 20rpx 30rpx;
+
+      .section-title {
+        font-size: 30rpx;
+        font-weight: bold;
+        color: #333;
+        border-left: 8rpx solid #2979ff;
+        padding-left: 16rpx;
+      }
+
+      &.back-bar {
+        display: flex;
+        align-items: center;
+        gap: 10rpx;
+        background-color: #ffffff;
+        margin-bottom: 20rpx;
+
+        .back-text {
+          font-size: 28rpx;
+          color: #2979ff;
+        }
+      }
+    }
+
+    .ledger-list {
+      padding: 0 20rpx;
+
+      .ledger-item {
+        background-color: #ffffff;
+        border-radius: 16rpx;
+        padding: 24rpx;
+        margin-bottom: 20rpx;
+        box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
+
+        &:active {
+          background-color: #f9f9f9;
+        }
+
+        &.no-click:active {
+          background-color: #ffffff;
+        }
+
+        .item-header {
+          display: flex;
+          justify-content: space-between;
+          align-items: center;
+          margin-bottom: 16rpx;
+
+          .item-left {
+            display: flex;
+            align-items: center;
+            gap: 12rpx;
+
+            .user-icon,
+            .product-icon {
+              width: 48rpx;
+              height: 48rpx;
+              background: linear-gradient(135deg, #2979ff, #64a1ff);
+              border-radius: 8rpx;
+              display: flex;
+              align-items: center;
+              justify-content: center;
+            }
+
+            .item-id {
+              font-size: 28rpx;
+              font-weight: bold;
+              color: #333;
+            }
+          }
+
+          .item-tag {
+            background-color: #f0f4ff;
+            padding: 4rpx 12rpx;
+            border-radius: 4rpx;
+
+            .tag-text {
+              font-size: 24rpx;
+              color: #2979ff;
+            }
+          }
+        }
+
+        .item-details {
+          padding-top: 10rpx;
+
+          .detail-grid {
+            display: flex;
+            justify-content: space-between;
+
+            .grid-item {
+              display: flex;
+              flex-direction: column;
+              align-items: center;
+              flex: 1;
+
+              .grid-label {
+                font-size: 24rpx;
+                color: #999;
+                margin-bottom: 8rpx;
+              }
+
+              .grid-value {
+                font-size: 28rpx;
+                color: #333;
+                font-weight: 500;
+
+                &.highlight {
+                  color: #ff5a5f;
+                }
+              }
+            }
+          }
+
+          .detail-row {
+            display: flex;
+            justify-content: space-between;
+            margin-bottom: 12rpx;
+
+            .detail-label {
+              font-size: 26rpx;
+              color: #999;
+            }
+
+            .detail-value {
+              font-size: 26rpx;
+              color: #333;
+
+              &.highlight {
+                color: #ff5a5f;
+                font-weight: bold;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .no-data {
+      padding-top: 100rpx;
+    }
+  }
+</style>
diff --git a/src/pages/works.vue b/src/pages/works.vue
index a647ada..04aeb3c 100644
--- a/src/pages/works.vue
+++ b/src/pages/works.vue
@@ -570,7 +570,7 @@
   // 鐢熶骇绠℃帶鍔熻兘鏁版嵁
   const productionItems = reactive([
     {
-      icon: "/static/images/icon/shengchanbaogong.svg",
+      icon: "/static/images/icon/shengchanbaogong1.svg",
       label: "鐢熶骇璁㈠崟",
     },
     // {
@@ -578,11 +578,11 @@
     //   label: "鐢熶骇娲惧伐",
     // },
     {
-      icon: "/static/images/icon/shengchanbaogong.svg",
+      icon: "/static/images/icon/shengchanbaogong1.svg",
       label: "鐢熶骇鎺掍骇",
     },
     {
-      icon: "/static/images/icon/shengchanbaogong.svg",
+      icon: "/static/images/icon/shengchanbaogong1.svg",
       label: "涓荤敓浜ц鍒�",
     },
     {
@@ -590,17 +590,13 @@
       label: "鐢熶骇鎶ュ伐",
     },
     {
-      icon: "/static/images/icon/xiaoshoutaizhang.svg",
+      icon: "/static/images/icon/shengchanbaogong1.svg",
       label: "鎶ュ伐鍙拌处",
     },
-    // {
-    //   icon: "/static/images/icon/shengchanbaogong.svg",
-    //   label: "鐢熶骇宸ュ崟",
-    // },
-    // {
-    //   icon: "/static/images/icon/shengchanhesuan@2x.svg",
-    //   label: "鐢熶骇鏍哥畻",
-    // },
+    {
+      icon: "/static/images/icon/shengchanbaogong1.svg",
+      label: "鐢熶骇鏍哥畻",
+    },
   ]);
 
   // 璁惧绠$悊鍔熻兘鏁版嵁

--
Gitblit v1.9.3