From 03a05815d7ea5612206b6cfe5d6537b4d4cedc0a Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 15 九月 2026 11:41:32 +0800
Subject: [PATCH] 天津潜宇塑胶app 1.添加采购需求、库存盘点、计件单价配置、产量统计、材料消耗核算、成本核算分析页面

---
 src/pages/inventoryManagement/stockManagement/index.vue |  264 +++++++++++++++++++++++++++++++++++++++++++++++++---
 1 files changed, 247 insertions(+), 17 deletions(-)

diff --git a/src/pages/inventoryManagement/stockManagement/index.vue b/src/pages/inventoryManagement/stockManagement/index.vue
index 45d27ad..49e1758 100644
--- a/src/pages/inventoryManagement/stockManagement/index.vue
+++ b/src/pages/inventoryManagement/stockManagement/index.vue
@@ -2,39 +2,141 @@
   <view class="app-container">
     <PageHeader title="搴撳瓨绠$悊"
                 @back="goBack" />
-    <view v-if="loading"
-          class="loading-state">
-      <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+    <!-- 搴撳瓨棰勮寮�鍏� -->
+    <view class="warn-bar">
+      <view class="warn-left">
+        <text class="warn-label">鍙湅棰勮</text>
+        <up-switch v-model="warnOnly"
+                   size="small"
+                   activeColor="#f56c6c"
+                   @change="handleWarnToggle"></up-switch>
+      </view>
     </view>
+    <!-- 棰勮鍒楄〃 -->
+    <template v-if="warnOnly">
+      <view class="warn-search">
+        <view class="search-input">
+          <up-input class="search-text"
+                    v-model="warnSearch.productName"
+                    placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+                    clearable
+                    @confirm="handleWarnQuery" />
+        </view>
+        <view class="filter-button"
+              @click="handleWarnQuery">
+          <up-icon name="search"
+                   size="24"
+                   color="#999"></up-icon>
+        </view>
+      </view>
+      <scroll-view scroll-y
+                   class="warn-list"
+                   v-if="warnList.length > 0"
+                   @scrolltolower="loadWarnMore">
+        <view v-for="item in warnList"
+              :key="item.id"
+              class="ledger-item warn-item"
+              :class="{ 'is-warn': isWarnRow(item) }">
+          <view class="item-header">
+            <view class="item-left">
+              <view class="document-icon warn-icon">
+                <up-icon name="warning"
+                         size="16"
+                         color="#ffffff"></up-icon>
+              </view>
+              <text class="item-id">{{ item.productName || '-' }}</text>
+            </view>
+            <up-tag text="棰勮"
+                    type="error"
+                    size="mini"
+                    v-if="isWarnRow(item)" />
+          </view>
+          <up-divider></up-divider>
+          <view class="item-details">
+            <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">{{ formatQty(item.qualitity) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">閿佸畾鏁伴噺</text>
+              <text class="detail-value">{{ formatQty(item.lockedQuantity) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鍙敤鏁伴噺</text>
+              <text class="detail-value"
+                    :class="{ 'warn-value': isWarnRow(item) }">{{ formatQty(item.un_locked_quantity ?? item.unLockedQuantity) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">棰勮鍊�</text>
+              <text class="detail-value">{{ formatQty(item.warnNum) }}</text>
+            </view>
+            <view class="detail-row">
+              <text class="detail-label">鎵瑰彿</text>
+              <text class="detail-value">{{ item.batchNo || '-' }}</text>
+            </view>
+          </view>
+        </view>
+        <up-loadmore :status="warnStatus" />
+      </scroll-view>
+      <view v-else-if="!warnLoading"
+            class="no-data">
+        <up-empty mode="data"
+                  text="鏆傛棤棰勮搴撳瓨"></up-empty>
+      </view>
+    </template>
+    <!-- 搴撳瓨鍒楄〃 -->
     <template v-else>
-      <up-tabs :list="tabs"
-               @click="handleTabClick"
-               :current="activeTab" />
-      <swiper class="swiper-box"
-              :current="activeTab"
-              @change="handleSwiperChange">
-        <swiper-item class="swiper-item"
-                     v-for="tab in products"
-                     :key="tab.id">
-          <record :product-id="tab.id"
-                  v-if="activeTab === products.indexOf(tab) || initializedTabs.includes(tab.id)" />
-        </swiper-item>
-      </swiper>
+      <view v-if="loading"
+            class="loading-state">
+        <up-loading-icon text="鍔犺浇涓�..."></up-loading-icon>
+      </view>
+      <template v-else>
+        <up-tabs :list="tabs"
+                 @click="handleTabClick"
+                 :current="activeTab" />
+        <swiper class="swiper-box"
+                :current="activeTab"
+                @change="handleSwiperChange">
+          <swiper-item class="swiper-item"
+                       v-for="tab in products"
+                       :key="tab.id">
+            <record :product-id="tab.id"
+                    v-if="activeTab === products.indexOf(tab) || initializedTabs.includes(tab.id)" />
+          </swiper-item>
+        </swiper>
+      </template>
     </template>
   </view>
 </template>
 
 <script setup>
-  import { ref, onMounted } from "vue";
+  import { ref, reactive, onMounted } from "vue";
   import PageHeader from "@/components/PageHeader.vue";
   import Record from "./Record.vue";
   import { productTreeList } from "@/api/basicData/product.js";
+  import { getStockWarnPage } from "@/api/inventoryManagement/stockInventory.js";
 
   const activeTab = ref(0);
   const tabs = ref([]);
   const products = ref([]);
   const loading = ref(false);
   const initializedTabs = ref([]);
+
+  // 棰勮
+  const warnOnly = ref(false);
+  const warnLoading = ref(false);
+  const warnStatus = ref("loadmore");
+  const warnList = ref([]);
+  const warnPage = reactive({ current: 1, size: 10, total: 0 });
+  const warnSearch = reactive({ productName: "" });
 
   const handleTabClick = item => {
     activeTab.value = item.index;
@@ -74,12 +176,74 @@
     uni.navigateBack();
   };
 
+  // 棰勮鐩稿叧
+  const formatQty = val => {
+    if (val === null || val === undefined || val === "") return "-";
+    return Number(val) || 0;
+  };
+
+  const getAvailable = item => {
+    const val = item.un_locked_quantity ?? item.unLockedQuantity;
+    return Number(val) || 0;
+  };
+
+  // 鍙敤鏁伴噺 鈮� 棰勮鍊� 涓� 棰勮鍊�>0
+  const isWarnRow = item => {
+    const warnNum = Number(item.warnNum) || 0;
+    if (warnNum <= 0) return false;
+    return getAvailable(item) <= warnNum;
+  };
+
+  const getWarnList = () => {
+    warnLoading.value = true;
+    warnStatus.value = "loading";
+    getStockWarnPage({
+      current: warnPage.current,
+      size: warnPage.size,
+      productName: warnSearch.productName,
+    })
+      .then(res => {
+        const records = res?.data?.records || [];
+        warnList.value =
+          warnPage.current === 1 ? records : [...warnList.value, ...records];
+        warnPage.total = res?.data?.total || 0;
+        warnStatus.value =
+          warnList.value.length >= warnPage.total ? "nomore" : "loadmore";
+      })
+      .catch(() => {
+        warnStatus.value = "loadmore";
+      })
+      .finally(() => {
+        warnLoading.value = false;
+      });
+  };
+
+  const handleWarnQuery = () => {
+    warnPage.current = 1;
+    warnList.value = [];
+    getWarnList();
+  };
+
+  const loadWarnMore = () => {
+    if (warnStatus.value === "nomore" || warnLoading.value) return;
+    warnPage.current++;
+    getWarnList();
+  };
+
+  const handleWarnToggle = () => {
+    if (warnOnly.value && !warnList.value.length) {
+      handleWarnQuery();
+    }
+  };
+
   onMounted(() => {
     fetchProducts();
   });
 </script>
 
 <style scoped lang="scss">
+  @import "@/styles/procurement-common.scss";
+
   .app-container {
     display: flex;
     flex-direction: column;
@@ -101,4 +265,70 @@
   :deep(.up-tabs) {
     background-color: #fff;
   }
+
+  .warn-bar {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    background: #fff;
+    padding: 16rpx 30rpx;
+
+    .warn-left {
+      display: flex;
+      align-items: center;
+      gap: 12rpx;
+    }
+
+    .warn-label {
+      font-size: 28rpx;
+      color: #333;
+    }
+  }
+
+  .warn-search {
+    display: flex;
+    align-items: center;
+    gap: 12rpx;
+    background: #fff;
+    padding: 16rpx 30rpx;
+
+    .search-input {
+      flex: 1;
+      background: #f5f5f5;
+      border-radius: 40rpx;
+      padding: 0 24rpx;
+      height: 72rpx;
+      display: flex;
+      align-items: center;
+    }
+  }
+
+  .warn-list {
+    flex: 1;
+    height: 0;
+    padding: 20rpx;
+    box-sizing: border-box;
+  }
+
+  .warn-item {
+    padding: 24rpx;
+
+    &.is-warn {
+      border: 2rpx solid #f56c6c;
+    }
+
+    .warn-icon {
+      background: linear-gradient(135deg, #f56c6c, #c0392b);
+    }
+
+    .warn-value {
+      color: #f56c6c;
+      font-weight: 600;
+    }
+  }
+
+  .no-data {
+    flex: 1;
+    padding-top: 150rpx;
+  }
 </style>

--
Gitblit v1.9.3