From 648003a577ef7a03046269c77fc5cc64199945e7 Mon Sep 17 00:00:00 2001
From: zouyu <2723363702@qq.com>
Date: 星期二, 20 一月 2026 17:06:47 +0800
Subject: [PATCH] Merge branch 'dev_tide' into dev_tide_zlglxt_xinlan

---
 src/views/inventoryManagement/stockWarningLedger/index.vue |  347 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 347 insertions(+), 0 deletions(-)

diff --git a/src/views/inventoryManagement/stockWarningLedger/index.vue b/src/views/inventoryManagement/stockWarningLedger/index.vue
new file mode 100644
index 0000000..d78ea30
--- /dev/null
+++ b/src/views/inventoryManagement/stockWarningLedger/index.vue
@@ -0,0 +1,347 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <el-form :model="searchForm" :inline="true">
+        <el-form-item label="浜у搧澶х被锛�">
+          <el-input
+            v-model="searchForm.productCategory"
+            placeholder="璇疯緭鍏ヤ骇鍝佸ぇ绫�"
+            clearable
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item label="瑙勬牸鍨嬪彿锛�">
+          <el-input
+            v-model="searchForm.specificationModel"
+            placeholder="璇疯緭鍏ヨ鏍煎瀷鍙�"
+            clearable
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item label="棰勮鐘舵�侊細">
+          <el-select
+            v-model="searchForm.warningStatus"
+            placeholder="璇烽�夋嫨棰勮鐘舵��"
+            clearable
+            style="width: 150px"
+          >
+            <el-option label="宸查璀�" value="宸查璀�" />
+            <el-option label="姝e父" value="姝e父" />
+          </el-select>
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleQuery">鎼滅储</el-button>
+          <el-button @click="resetQuery">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <div class="table_list">
+      <div class="actions"></div>
+      <el-table
+        :data="tableData"
+        border
+        v-loading="tableLoading"
+        style="width: 100%"
+        height="calc(100vh - 280px)"
+      >
+        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+        <el-table-column label="鎵规鍙�" prop="code" width="130" show-overflow-tooltip />
+        <el-table-column label="浜у搧澶х被" prop="productCategory" show-overflow-tooltip />
+        <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" show-overflow-tooltip />
+        <el-table-column label="褰撳墠搴撳瓨" prop="currentStock" width="120" show-overflow-tooltip>
+          <template #default="scope">
+            <span :class="getStockClass(scope.row)">{{ scope.row.currentStock || 0 }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鏈�浣庡簱瀛�" prop="warnNum" width="120" show-overflow-tooltip />
+        <el-table-column label="棰勮绾у埆" prop="warningLevel" width="100" show-overflow-tooltip>
+          <template #default="scope">
+            <el-tag :type="getWarningLevelTag(scope.row.warningLevel)">
+              {{ scope.row.warningLevel || '-' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="棰勮鐘舵��" prop="warningStatus" width="100" show-overflow-tooltip>
+          <template #default="scope">
+            <el-tag :type="scope.row.warningStatus === '宸查璀�' ? 'danger' : 'success'">
+              {{ scope.row.warningStatus || '姝e父' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="棰勮鏃堕棿" prop="warningTime" width="150" show-overflow-tooltip />
+        <el-table-column label="棰勮缂鸿揣鏃堕棿" prop="expectedShortageTime" width="150" show-overflow-tooltip>
+          <template #default="scope">
+            <div v-if="scope.row.expectedShortageTime">
+              <div v-if="getCountdown(scope.row.expectedShortageTime).isExpired" class="countdown-expired">
+                <el-tag type="danger">宸茬己璐�</el-tag>
+              </div>
+              <div v-else class="countdown-timer">
+                <span :class="getCountdownClass(scope.row.expectedShortageTime)">
+                  {{ getCountdown(scope.row.expectedShortageTime).text }}
+                </span>
+              </div>
+            </div>
+            <span v-else>-</span>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination
+        v-show="total > 0"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+        :page="page.current"
+        :limit="page.size"
+        @pagination="paginationChange"
+      />
+    </div>
+
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from 'vue'
+import { ElMessage } from 'element-plus'
+import pagination from '@/components/PIMTable/Pagination.vue'
+import {
+  getStockWarningLedgerPage
+} from '@/api/inventoryManagement/stockWarningLedger.js'
+
+// 鍝嶅簲寮忔暟鎹�
+const tableData = ref([])
+const tableLoading = ref(false)
+const total = ref(0)
+
+// 鍒嗛〉鍙傛暟
+const page = reactive({
+  current: 1,
+  size: 100
+})
+
+// 鎼滅储琛ㄥ崟
+const searchForm = reactive({
+  productCategory: '',
+  specificationModel: '',
+  warningStatus: ''
+})
+
+// 鑾峰彇鍒楄〃鏁版嵁
+const getList = () => {
+  tableLoading.value = true
+  const params = {
+    ...page,
+    ...searchForm
+  }
+  getStockWarningLedgerPage(params)
+    .then(res => {
+      tableLoading.value = false
+      if (res.code === 200) {
+        tableData.value = res.data.records || []
+        total.value = res.data.total || 0
+
+        // 璁$畻棰勮绾у埆鍜岀姸鎬�
+        tableData.value = tableData.value.map(item => {
+          const currentStock = parseFloat(item.inboundNum0 || item.currentStock || 0)
+          const warnNum = parseFloat(item.warnNum || 0)
+          const safetyStock = parseFloat(item.safetyStock || warnNum * 1.2)
+
+          // 璁$畻棰勮绾у埆
+          if (currentStock <= 0) {
+            item.warningLevel = '绱ф��'
+            item.warningStatus = '宸查璀�'
+          } else if (currentStock < warnNum) {
+            item.warningLevel = '閲嶈'
+            item.warningStatus = '宸查璀�'
+          } else if (currentStock < safetyStock) {
+            item.warningLevel = '涓�鑸�'
+            item.warningStatus = '宸查璀�'
+          } else {
+            item.warningLevel = ''
+            item.warningStatus = '姝e父'
+          }
+
+          // 璁$畻棰勮缂鸿揣鏃堕棿锛堝熀浜庢棩鍧囨秷鑰楅噺锛岃繖閲岀畝鍖栧鐞嗭級
+          if (item.warningStatus === '宸查璀�' && currentStock > 0 && warnNum > 0) {
+            const dailyConsumption = warnNum / 30 // 鍋囪30澶╂秷鑰楀畬鏈�浣庡簱瀛�
+            const daysRemaining = Math.floor(currentStock / dailyConsumption)
+            if (daysRemaining > 0) {
+              const date = new Date()
+              date.setDate(date.getDate() + daysRemaining)
+              item.expectedShortageTime = date.toISOString().split('T')[0]
+            }
+          }
+
+          item.currentStock = currentStock
+          item.safetyStock = safetyStock
+
+          return item
+        })
+      }
+    })
+    .catch(err => {
+      tableLoading.value = false
+      ElMessage.error(err.msg || '鑾峰彇鏁版嵁澶辫触')
+    })
+}
+
+// 鎼滅储
+const handleQuery = () => {
+  page.current = 1
+  getList()
+}
+
+// 閲嶇疆鎼滅储
+const resetQuery = () => {
+  Object.keys(searchForm).forEach(key => {
+    searchForm[key] = ''
+  })
+  handleQuery()
+}
+
+// 鍒嗛〉鍙樺寲
+const paginationChange = (obj) => {
+  page.current = obj.page
+  page.size = obj.limit
+  getList()
+}
+
+// 鑾峰彇搴撳瓨鏍峰紡绫�
+const getStockClass = (row) => {
+  const currentStock = parseFloat(row.currentStock || row.inboundNum0 || 0)
+  const warnNum = parseFloat(row.warnNum || 0)
+
+  if (currentStock <= 0) {
+    return 'text-danger'
+  } else if (currentStock < warnNum) {
+    return 'text-warning'
+  }
+  return 'text-success'
+}
+
+// 鑾峰彇棰勮绾у埆鏍囩鏍峰紡
+const getWarningLevelTag = (level) => {
+  const levelMap = {
+    '绱ф��': 'danger',
+    '閲嶈': 'warning',
+    '涓�鑸�': 'info'
+  }
+  return levelMap[level] || 'info'
+}
+
+// 鑾峰彇鍊掕鏃朵俊鎭�
+const getCountdown = (expectedTime) => {
+  if (!expectedTime) return { text: '-', isExpired: false }
+
+  const now = new Date().getTime()
+  const expected = new Date(expectedTime).getTime()
+  const diff = expected - now
+
+  if (diff <= 0) {
+    return { text: '宸茬己璐�', isExpired: true }
+  }
+
+  const days = Math.floor(diff / (1000 * 60 * 60 * 24))
+  const hours = Math.floor((diff % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60))
+  const minutes = Math.floor((diff % (1000 * 60 * 60)) / (1000 * 60))
+
+  if (days > 0) {
+    return { text: `${days}澶�${hours}灏忔椂`, isExpired: false }
+  } else if (hours > 0) {
+    return { text: `${hours}灏忔椂${minutes}鍒嗛挓`, isExpired: false }
+  } else {
+    return { text: `${minutes}鍒嗛挓`, isExpired: false }
+  }
+}
+
+// 鑾峰彇鍊掕鏃舵牱寮忕被
+const getCountdownClass = (expectedTime) => {
+  if (!expectedTime) return ''
+
+  const now = new Date().getTime()
+  const expected = new Date(expectedTime).getTime()
+  const diff = expected - now
+
+  if (diff <= 0) {
+    return 'countdown-expired'
+  } else if (diff <= 24 * 60 * 60 * 1000) { // 24灏忔椂鍐�
+    return 'countdown-urgent'
+  } else if (diff <= 7 * 24 * 60 * 60 * 1000) { // 7澶╁唴
+    return 'countdown-warning'
+  } else {
+    return 'countdown-normal'
+  }
+}
+
+// 椤甸潰鍔犺浇
+onMounted(() => {
+  getList()
+})
+</script>
+
+<style scoped lang="scss">
+.app-container {
+  padding: 20px;
+
+  .search_form {
+    background: #fff;
+    padding: 20px;
+    border-radius: 4px;
+    margin-bottom: 20px;
+  }
+
+  .table_list {
+    background: #fff;
+    border-radius: 4px;
+    padding: 20px;
+    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+
+    .actions {
+      display: flex;
+      justify-content: space-between;
+      margin-bottom: 20px;
+    }
+  }
+
+  .text-danger {
+    color: #f56c6c;
+    font-weight: bold;
+  }
+
+  .text-warning {
+    color: #e6a23c;
+    font-weight: bold;
+  }
+
+  .text-success {
+    color: #67c23a;
+    font-weight: bold;
+  }
+
+  .countdown-timer {
+    font-weight: bold;
+  }
+
+  .countdown-normal {
+    color: #67c23a;
+  }
+
+  .countdown-warning {
+    color: #e6a23c;
+  }
+
+  .countdown-urgent {
+    color: #f56c6c;
+    animation: blink 1s infinite;
+  }
+
+  .countdown-expired {
+    color: #f56c6c;
+    font-weight: bold;
+  }
+
+  @keyframes blink {
+    0%, 50% { opacity: 1; }
+    51%, 100% { opacity: 0.5; }
+  }
+}
+</style>

--
Gitblit v1.9.3