From 7ab50761e7b6649fc30930cfd604d663b2ea4e00 Mon Sep 17 00:00:00 2001
From: tuqin <tuqin@tuqin.cn>
Date: 星期三, 21 十月 2026 15:16:10 +0800
Subject: [PATCH] feat(芯导): 仓储库存管理/协同审批会议提醒/采购付款查询导出/营销回款流水导出/生产设备补字段

---
 src/views/collaborativeApproval/notificationManagement/summary/index.vue     |   71 +++-
 src/views/procurementManagement/paymentHistory/index.vue                     |    6 
 src/views/inventoryManagement/stockCenter/index.vue                          |   51 +++
 src/views/collaborativeApproval/approvalProcess/index.vue                    |    6 
 src/views/productionManagement/productionDispatching/components/formDia.vue  |   32 ++
 src/views/procurementManagement/paymentEntry/index.vue                       |   58 +++
 src/views/inventoryManagement/stockWarningLedger/index.vue                   |   21 +
 src/views/salesManagement/invoiceLedger/index.vue                            |   23 -
 src/views/inventoryManagement/receiptManagement/index.vue                    |    1 
 src/views/equipmentManagement/calibration/index.vue                          |    5 
 src/views/collaborativeApproval/meetingManagement/index.vue                  |  110 ++++++
 src/api/inventoryManagement/stockTaking.js                                   |   35 ++
 src/views/inventoryManagement/dispatchLog/index.vue                          |    1 
 src/api/inventoryManagement/stockWarningLedger.js                            |    8 
 src/views/equipmentManagement/upkeep/index.vue                               |   11 
 src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue |   10 
 src/views/collaborativeApproval/meetingBoard/index.vue                       |    8 
 src/views/inventoryManagement/issueManagement/index.vue                      |    6 
 src/views/productionManagement/operationScheduling/index.vue                 |   10 
 src/views/inventoryManagement/stockMovement/index.vue                        |   80 +++++
 src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue |   10 
 src/views/productionManagement/productionOrder/index.vue                     |   40 ++
 src/views/inventoryManagement/stockTaking/index.vue                          |  136 ++++++++
 src/views/inventoryManagement/stockTimeSetting/index.vue                     |  132 ++++++++
 src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue |    6 
 src/views/equipmentManagement/upkeep/Form/PlanForm.vue                       |   19 +
 src/views/salesManagement/receiptPaymentHistory/index.vue                    |    7 
 27 files changed, 845 insertions(+), 58 deletions(-)

diff --git a/src/api/inventoryManagement/stockTaking.js b/src/api/inventoryManagement/stockTaking.js
new file mode 100644
index 0000000..d789d05
--- /dev/null
+++ b/src/api/inventoryManagement/stockTaking.js
@@ -0,0 +1,35 @@
+import request from "@/utils/request";
+
+// 搴撳瓨鐩樼偣-鍒嗛〉
+export const getStockTakingPage = (params) => {
+  return request({ url: "/stockTaking/listPage", method: "get", params });
+};
+// 搴撳瓨鐩樼偣-鏂板
+export const addStockTaking = (data) => {
+  return request({ url: "/stockTaking", method: "post", data });
+};
+// 搴撳瓨鐩樼偣-淇敼
+export const updateStockTaking = (data) => {
+  return request({ url: "/stockTaking", method: "put", data });
+};
+// 搴撳瓨鐩樼偣-鍒犻櫎
+export const delStockTaking = (ids) => {
+  return request({ url: "/stockTaking/" + ids.join(","), method: "delete" });
+};
+
+// 搴撳瓨鏃堕棿璁剧疆-鍒楄〃
+export const getStockTimeSettingList = () => {
+  return request({ url: "/stockTimeSetting/list", method: "get" });
+};
+// 搴撳瓨鏃堕棿璁剧疆-鏂板
+export const addStockTimeSetting = (data) => {
+  return request({ url: "/stockTimeSetting", method: "post", data });
+};
+// 搴撳瓨鏃堕棿璁剧疆-淇敼
+export const updateStockTimeSetting = (data) => {
+  return request({ url: "/stockTimeSetting", method: "put", data });
+};
+// 搴撳瓨鏃堕棿璁剧疆-鍒犻櫎
+export const delStockTimeSetting = (ids) => {
+  return request({ url: "/stockTimeSetting/" + ids.join(","), method: "delete" });
+};
\ No newline at end of file
diff --git a/src/api/inventoryManagement/stockWarningLedger.js b/src/api/inventoryManagement/stockWarningLedger.js
index 0cdeb2d..81c54ac 100644
--- a/src/api/inventoryManagement/stockWarningLedger.js
+++ b/src/api/inventoryManagement/stockWarningLedger.js
@@ -8,3 +8,11 @@
     params,
   });
 };
+// 鐢熸垚閲囪喘寤鸿 / 婊為攢澶勭悊鎻愮ず
+export const generateSuggestion = (data) => {
+  return request({
+    url: "/customStorageWarning/generateSuggestion",
+    method: "post",
+    data,
+  });
+};
diff --git a/src/views/collaborativeApproval/approvalProcess/index.vue b/src/views/collaborativeApproval/approvalProcess/index.vue
index 3815b70..7b97f23 100644
--- a/src/views/collaborativeApproval/approvalProcess/index.vue
+++ b/src/views/collaborativeApproval/approvalProcess/index.vue
@@ -3,12 +3,13 @@
     <!-- 鏍囩椤靛垏鎹笉鍚岀殑瀹℃壒绫诲瀷 -->
     <el-tabs v-if="!fixedApproveType" v-model="activeTab" @tab-change="handleTabChange" class="approval-tabs">
       <el-tab-pane label="鑷敱鍗忓悓瀹℃壒" name="1"></el-tab-pane>
-      <el-tab-pane label="璇峰亣绠$悊" name="2"></el-tab-pane>
+      <el-tab-pane label="璇峰亣瀹℃壒" name="2"></el-tab-pane>
       <el-tab-pane label="閿�鍞鎵�" name="3"></el-tab-pane>
       <el-tab-pane label="鎶ラ攢瀹℃壒" name="4"></el-tab-pane>
       <el-tab-pane label="閲囪喘瀹℃壒" name="5"></el-tab-pane>
       <el-tab-pane label="鎶ヤ环瀹℃壒" name="6"></el-tab-pane>
       <el-tab-pane label="鍑哄簱瀹℃壒" name="7"></el-tab-pane>
+      <el-tab-pane label="鍚堝悓瀹℃壒" name="8"></el-tab-pane>
     </el-tabs>
     
     <div class="search_form">
@@ -301,12 +302,13 @@
   const nameMap = {
     0: "鑷敱鍗忓悓瀹℃壒琛�",
     1: "鑷敱鍗忓悓瀹℃壒琛�",
-    2: "璇峰亣绠$悊瀹℃壒琛�",
+    2: "璇峰亣瀹℃壒琛�",
     3: "閿�鍞悎鍚屽鎵硅〃",
     4: "鎶ラ攢绠$悊瀹℃壒琛�",
     5: "閲囪喘璁″垝瀹℃壒琛�",
     6: "鎶ヤ环瀹℃壒琛�",
     7: "鍑哄簱瀹℃壒琛�",
+    8: "鍚堝悓瀹℃壒琛�",
   }
   const fileName = nameMap[type] || nameMap[0]
   proxy.download(url, {}, `${fileName}.xlsx`)
diff --git a/src/views/collaborativeApproval/meetingBoard/index.vue b/src/views/collaborativeApproval/meetingBoard/index.vue
index dfbb922..caf92f7 100644
--- a/src/views/collaborativeApproval/meetingBoard/index.vue
+++ b/src/views/collaborativeApproval/meetingBoard/index.vue
@@ -165,7 +165,13 @@
   meetings.value = resp2.data.map(item => {
     return {
       ...item,
-      participants: JSON.parse(item.participants)
+      participants: (() => {
+        if (Array.isArray(item.participants)) return item.participants
+        const s = String(item.participants || '').trim()
+        if (!s) return []
+        if (s.startsWith('[')) { try { return JSON.parse(s) } catch (e) { return [] } }
+        return s.split(',').map(v => v.trim()).filter(Boolean)
+      })()
     }
   })
   console.log('浼氳鐪嬫澘椤甸潰鍔犺浇瀹屾垚')
diff --git a/src/views/collaborativeApproval/meetingManagement/index.vue b/src/views/collaborativeApproval/meetingManagement/index.vue
index 69b0275..1908797 100644
--- a/src/views/collaborativeApproval/meetingManagement/index.vue
+++ b/src/views/collaborativeApproval/meetingManagement/index.vue
@@ -1,5 +1,18 @@
 <template>
   <div class="app-container">
+    <!-- 绔欏唴鎻愰啋妯箙锛氬嵆灏嗗紑濮嬬殑浼氳 -->
+    <el-alert
+      v-if="upcomingMeetings.length"
+      class="remind-banner"
+      type="warning"
+      :closable="false"
+      show-icon
+    >
+      <template #title>
+        浼氳鎻愰啋锛歿{ upcomingText }}
+      </template>
+    </el-alert>
+
     <div class="tabs-wrapper">
       <el-tabs
         v-model="activeTab"
@@ -24,7 +37,10 @@
 </template>
 
 <script setup>
-import { ref, computed } from 'vue'
+import { ref, computed, onMounted, onUnmounted } from 'vue'
+import { ElNotification } from 'element-plus'
+import dayjs from 'dayjs'
+import { getMeetingPublish } from '@/api/collaborativeApproval/meeting.js'
 import MeetSetting from '../notificationManagement/meetSetting/index.vue'
 import MeetIndex from '../notificationManagement/meetIndex/index.vue'
 import MeetApplication from '../notificationManagement/meetApplication/index.vue'
@@ -48,9 +64,100 @@
 function handleTabChange(name) {
   activeTab.value = name
 }
+
+/* ==================== 浼氳鎻愰啋锛堢珯鍐� + 娴忚鍣�/鎵嬫満绔脊绐楋級 ==================== */
+const REMIND_WINDOW_MIN = 30 // 浼氳寮�濮嬪墠 30 鍒嗛挓鍐呮彁閱�
+const REMIND_STORAGE_KEY = 'meeting_remind_sent'
+const POLL_INTERVAL = 60 * 1000 // 姣� 60 绉掓鏌ヤ竴娆�
+
+const upcomingMeetings = ref([])
+let remindTimer = null
+
+const getSentMap = () => {
+  try {
+    return JSON.parse(localStorage.getItem(REMIND_STORAGE_KEY) || '{}')
+  } catch (e) {
+    return {}
+  }
+}
+const markSent = (key) => {
+  const m = getSentMap()
+  m[key] = Date.now()
+  // 鍙繚鐣欐渶杩� 200 鏉★紝閬垮厤 localStorage 鏃犻檺澧為暱
+  const entries = Object.entries(m).sort((a, b) => b[1] - a[1]).slice(0, 200)
+  localStorage.setItem(REMIND_STORAGE_KEY, JSON.stringify(Object.fromEntries(entries)))
+}
+
+// 璁$畻浼氳寮�濮嬫椂闂达紙鍏煎 startTime 涓� datetime / 绾椂闂翠袱绉嶆牸寮忥級
+const parseStart = (m) => {
+  const raw = m.startTime
+  if (!raw) return null
+  const s = String(raw)
+  if (s.length > 10) return dayjs(s)
+  if (!m.meetingDate) return null
+  return dayjs(`${m.meetingDate} ${s}`)
+}
+
+const checkRemind = async () => {
+  try {
+    const resp = await getMeetingPublish({ current: 1, size: 100 })
+    const records = (resp && resp.data && resp.data.records) || []
+    const now = dayjs()
+    const sent = getSentMap()
+    const upcoming = []
+
+    records.forEach((m) => {
+      const start = parseStart(m)
+      if (!start || !start.isValid()) return
+      const diffMin = start.diff(now, 'minute', true)
+      // 鍙彁閱掋�屽嵆灏嗗紑濮嬨�嶏細0 ~ 30 鍒嗛挓
+      if (diffMin < 0 || diffMin > REMIND_WINDOW_MIN) return
+      const flagKey = `${m.id}_${start.format('YYYYMMDDHHmm')}`
+      upcoming.push({ title: m.title || '浼氳', start, diffMin })
+
+      if (sent[flagKey]) return
+      markSent(flagKey)
+      ElNotification({
+        title: '浼氳鎻愰啋',
+        message: `銆�${m.title || '浼氳'}銆戝皢浜� ${start.format('HH:mm')} 寮�濮�${
+          diffMin <= 1 ? '锛堝嵆灏嗗紑濮嬶級' : `锛堢害 ${Math.round(diffMin)} 鍒嗛挓鍚庯級`
+        }`,
+        type: 'warning',
+        duration: 15000,
+        position: 'bottom-right'
+      })
+    })
+
+    upcoming.sort((a, b) => a.start.valueOf() - b.start.valueOf())
+    upcomingMeetings.value = upcoming
+  } catch (e) {
+    // 鎻愰啋澶辫触涓嶅奖鍝嶄富娴佺▼
+  }
+}
+
+const upcomingText = computed(() =>
+  upcomingMeetings.value
+    .map((x) => `${x.title}锛�${x.start.format('MM-DD HH:mm')}锛塦)
+    .join('锛�')
+)
+
+onMounted(() => {
+  checkRemind()
+  remindTimer = setInterval(checkRemind, POLL_INTERVAL)
+})
+
+onUnmounted(() => {
+  if (remindTimer) {
+    clearInterval(remindTimer)
+    remindTimer = null
+  }
+})
 </script>
 
 <style scoped lang="scss">
+.remind-banner {
+  margin-bottom: 12px;
+}
 
 .tabs-wrapper {
   margin-bottom: 10px;
@@ -60,4 +167,3 @@
   min-height: 400px;
 }
 </style>
-
diff --git a/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
index 83747a0..c5f0d2e 100644
--- a/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/meetExamine/index.vue
@@ -232,12 +232,12 @@
   loading.value = true
   let resp = await getExamineList({...searchForm, ...queryParams})
   approvalList.value = resp.data.records.map(it => {
-    let room = roomEnum.value.find(room => it.roomId === room.id)
-    it.location = `${room.name}(${room.location})`
-    let staffs = JSON.parse(it.participants)
-    it.staffCount = staffs.size
+    const room = (roomEnum.value || []).find(r => r.id === it.roomId)
+    it.location = room ? `${room.name}(${room.location})` : (it.location || '-')
+    let staffs = []; if (Array.isArray(it.participants)) { staffs = it.participants } else if (it.participants) { const ps = String(it.participants).trim(); if (ps.startsWith('[')) { try { staffs = JSON.parse(ps) } catch (e) { staffs = [] } } else { staffs = ps.split(',').map(v => v.trim()).filter(Boolean) } }; if (!Array.isArray(staffs)) staffs = []; staffs = staffs.map(v => String(v));
+    it.staffCount = staffs.length
     it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
-    it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => {
+    it.participants = (staffList.value || []).filter(staff => staffs.some(id => String(id) === String(staff.id))).map(staff => {
       return {
         id: staff.id,
         name: `${staff.staffName}(${staff.postJob})`
diff --git a/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
index 2351293..d09a148 100644
--- a/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/meetPublish/index.vue
@@ -230,13 +230,13 @@
   loading.value = true
   let resp = await getMeetingPublish({...searchForm, ...queryParams})
   approvalList.value = resp.data.records.map(it => {
-    let room = roomEnum.value.find(room => it.roomId === room.id)
-    it.location = `${room.name}(${room.location})`
-    let staffs = JSON.parse(it.participants)
-    it.staffCount = staffs.size
+    const room = (roomEnum.value || []).find(r => r.id === it.roomId)
+    it.location = room ? `${room.name}(${room.location})` : (it.location || '-')
+    let staffs = []; if (Array.isArray(it.participants)) { staffs = it.participants } else if (it.participants) { const ps = String(it.participants).trim(); if (ps.startsWith('[')) { try { staffs = JSON.parse(ps) } catch (e) { staffs = [] } } else { staffs = ps.split(',').map(v => v.trim()).filter(Boolean) } }; if (!Array.isArray(staffs)) staffs = []; staffs = staffs.map(v => String(v));
+    it.staffCount = staffs.length
     it.status = it.publishStatus
     it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
-    it.participants = staffList.value.filter(staff => staffs.some(id=>id === staff.id)).map(staff => {
+    it.participants = (staffList.value || []).filter(staff => staffs.some(id => String(id) === String(staff.id))).map(staff => {
       return {
         id: staff.id,
         name: `${staff.staffName}(${staff.postJob})`
diff --git a/src/views/collaborativeApproval/notificationManagement/summary/index.vue b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
index a00316d..5d1bee6 100644
--- a/src/views/collaborativeApproval/notificationManagement/summary/index.vue
+++ b/src/views/collaborativeApproval/notificationManagement/summary/index.vue
@@ -204,24 +204,48 @@
 // 鏌ヨ鏁版嵁
 const getList = async () => {
   loading.value = true
-  let resp = await getMeetingPublish({ ...searchForm, ...queryParams })
-  meetingList.value = resp.data.records.map(it => {
-    let room = roomEnum.value.find(room => it.roomId === room.id)
-    it.location = `${room.name}(${room.location})`
-    let staffs = JSON.parse(it.participants)
-    it.staffCount = staffs.size
-    it.meetingTime = `${it.meetingDate} ${dayjs(it.startTime).format('HH:mm:ss')} ~ ${dayjs(it.endTime).format('HH:mm:ss')}`
-    it.participants = staffList.value.filter(staff => staffs.some(id => id === staff.id)).map(staff => {
-      return {
-        id: staff.id,
-        name: `${staff.staffName}(${staff.postJob})`
+  try {
+    const resp = await getMeetingPublish({ ...searchForm, ...queryParams })
+    const records = resp && resp.data && resp.data.records ? resp.data.records : []
+    meetingList.value = records.map(it => {
+      const room = (roomEnum.value || []).find(r => r.id === it.roomId)
+      it.location = room ? `${room.name}(${room.location})` : (it.location || '鈥�')
+      let staffIds = []
+      const rawParticipants = it.participants
+      if (Array.isArray(rawParticipants)) {
+        staffIds = rawParticipants
+      } else if (rawParticipants) {
+        const s = String(rawParticipants).trim()
+        if (s.startsWith('[')) {
+          try {
+            staffIds = JSON.parse(s)
+          } catch (e) {
+            staffIds = []
+          }
+        } else {
+          // 鍏煎 "130,131,132" 閫楀彿鍒嗛殧鏍煎紡
+          staffIds = s.split(',').map(v => v.trim()).filter(Boolean)
+        }
       }
+      if (!Array.isArray(staffIds)) staffIds = []
+      staffIds = staffIds.map(v => String(v))
+      it.staffCount = staffIds.length
+      const s = it.startTime ? dayjs(it.startTime).format('HH:mm:ss') : ''
+      const e2 = it.endTime ? dayjs(it.endTime).format('HH:mm:ss') : ''
+      it.meetingTime = `${it.meetingDate || ''} ${s} ~ ${e2}`.trim()
+      it.participants = (staffList.value || [])
+        .filter(staff => staffIds.some(id => String(id) === String(staff.id)))
+        .map(staff => ({ id: staff.id, name: `${staff.staffName}(${staff.postJob})` }))
+      return it
     })
-
-    return it
-  })
-  total.value = resp.data.total
-  loading.value = false
+    total.value = (resp && resp.data && resp.data.total) || 0
+  } catch (err) {
+    console.error('浼氳绾鍒楄〃鍔犺浇澶辫触', err)
+    meetingList.value = []
+    total.value = 0
+  } finally {
+    loading.value = false
+  }
 }
 
 // 鎼滅储鎸夐挳鎿嶄綔
@@ -337,10 +361,17 @@
 
 // 椤甸潰鍔犺浇鏃惰幏鍙栨暟鎹�
 onMounted(async () => {
-  const [resp1, resp2] = await Promise.all([getRoomEnum(), getStaffOnJob()])
-  roomEnum.value = resp1.data
-  staffList.value = resp2.data
-
+  try {
+    const [resp1, resp2] = await Promise.all([
+      getRoomEnum().catch(() => ({ data: [] })),
+      getStaffOnJob().catch(() => ({ data: [] }))
+    ])
+    roomEnum.value = (resp1 && resp1.data) || []
+    staffList.value = (resp2 && resp2.data) || []
+  } catch (e) {
+    roomEnum.value = []
+    staffList.value = []
+  }
   await getList()
 })
 </script>
diff --git a/src/views/equipmentManagement/calibration/index.vue b/src/views/equipmentManagement/calibration/index.vue
index 7dfd405..bc65408 100644
--- a/src/views/equipmentManagement/calibration/index.vue
+++ b/src/views/equipmentManagement/calibration/index.vue
@@ -123,6 +123,11 @@
 		width: 100,
 	},
 	{
+		label: "涓嬫妫�瀹氭棩鏈�",
+		prop: "nextDate",
+		width: 130,
+	},
+	{
 		label: "褰曞叆浜�",
 		prop: "userName",
 	},
diff --git a/src/views/equipmentManagement/upkeep/Form/PlanForm.vue b/src/views/equipmentManagement/upkeep/Form/PlanForm.vue
index 1d94b68..8e707b5 100644
--- a/src/views/equipmentManagement/upkeep/Form/PlanForm.vue
+++ b/src/views/equipmentManagement/upkeep/Form/PlanForm.vue
@@ -32,6 +32,21 @@
         clearable
       />
     </el-form-item>
+    <el-form-item label="淇濆吇鍛ㄦ湡">
+      <el-input
+        v-model="form.maintenanceCycle"
+        placeholder="璇疯緭鍏ヤ繚鍏诲懆鏈燂紙濡傦細姣忔湀涓�娆� / 姣忓搴� / 30澶╋級"
+        clearable
+      />
+    </el-form-item>
+    <el-form-item label="淇濆吇鍐呭">
+      <el-input
+        v-model="form.maintenanceContent"
+        type="textarea"
+        :rows="3"
+        placeholder="璇疯緭鍏ヤ繚鍏诲唴瀹�"
+      />
+    </el-form-item>
   </el-form>
 </template>
 
@@ -56,6 +71,8 @@
   deviceName: undefined, // 璁惧鍚嶇О
   deviceModel: undefined, // 瑙勬牸鍨嬪彿
   maintenancePlanTime: undefined, // 璁″垝淇濆吇鏃ユ湡
+  maintenanceCycle: undefined, // 淇濆吇鍛ㄦ湡
+  maintenanceContent: undefined, // 淇濆吇鍐呭
 });
 
 const setDeviceModel = (id) => {
@@ -78,6 +95,8 @@
   form.maintenancePlanTime = dayjs(data.maintenancePlanTime).format(
     "YYYY-MM-DD HH:mm:ss"
   );
+  form.maintenanceCycle = data.maintenanceCycle;
+  form.maintenanceContent = data.maintenanceContent;
 };
 
 const loadForm = () => {};
diff --git a/src/views/equipmentManagement/upkeep/index.vue b/src/views/equipmentManagement/upkeep/index.vue
index 484538c..5895e51 100644
--- a/src/views/equipmentManagement/upkeep/index.vue
+++ b/src/views/equipmentManagement/upkeep/index.vue
@@ -178,6 +178,17 @@
     formatData: (cell) => dayjs(cell).format("YYYY-MM-DD"),
   },
   {
+    label: "淇濆吇鍛ㄦ湡",
+    align: "center",
+    prop: "maintenanceCycle",
+  },
+  {
+    label: "淇濆吇鍐呭",
+    align: "center",
+    prop: "maintenanceContent",
+    showOverflowTooltip: true,
+  },
+  {
     label: "褰曞叆浜�",
     align: "center",
     prop: "createUserName",
diff --git a/src/views/inventoryManagement/dispatchLog/index.vue b/src/views/inventoryManagement/dispatchLog/index.vue
index c15e73f..e2f7304 100644
--- a/src/views/inventoryManagement/dispatchLog/index.vue
+++ b/src/views/inventoryManagement/dispatchLog/index.vue
@@ -48,6 +48,7 @@
 				<el-table-column label="鍗曚綅" prop="unit" show-overflow-tooltip />
 				<el-table-column label="鐗╁搧绫诲瀷" prop="itemType" show-overflow-tooltip />
 				<el-table-column label="鍙戞枡鏁伴噺" prop="inboundNum" show-overflow-tooltip />
+				<el-table-column label="棰嗙敤鏁伴噺" prop="requisitionNum" show-overflow-tooltip />
 				<el-table-column label="鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" width="150"></el-table-column>
 				<el-table-column label="鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" width="150"></el-table-column>
 				<el-table-column label="鍙戞枡浜�" prop="createBy" show-overflow-tooltip />
diff --git a/src/views/inventoryManagement/issueManagement/index.vue b/src/views/inventoryManagement/issueManagement/index.vue
index c5a1236..9ec5730 100644
--- a/src/views/inventoryManagement/issueManagement/index.vue
+++ b/src/views/inventoryManagement/issueManagement/index.vue
@@ -69,6 +69,9 @@
         <el-form-item :label="cfg.qtyLabel" prop="inboundQuantity">
           <el-input-number :step="0.01" :min="0" :max="currentRowNum" style="width: 100%" v-model="form.inboundQuantity" placeholder="璇疯緭鍏�" clearable />
         </el-form-item>
+        <el-form-item label="棰嗙敤鏁伴噺锛�">
+          <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="form.requisitionNum" placeholder="璇疯緭鍏�" clearable />
+        </el-form-item>
         <el-form-item :label="cfg.dateLabel" prop="inboundTime">
           <el-date-picker style="width: 100%" v-model="form.inboundTime" value-format="YYYY-MM-DD" format="YYYY-MM-DD"
             type="date" placeholder="璇烽�夋嫨" clearable />
@@ -237,6 +240,7 @@
     form.value = {
       productrecordId: '',
       inboundQuantity: currentRowNum.value,
+      requisitionNum: 0,
       inboundTime: getCurrentDate(),
       nickName: defaultUserId,
     }
@@ -244,6 +248,7 @@
     form.value = {
       productrecordId: '',
       inboundQuantity: currentRowNum.value,
+      requisitionNum: 0,
       inboundTime: getCurrentDate(),
       nickName: '',
     }
@@ -262,6 +267,7 @@
         id: currentRow.value.id, // 鏉ユ簮鍏ュ簱璁板綍ID
         salesLedgerProductId: currentRow.value.salesLedgerProductId || 0,
         quantity: form.value.inboundQuantity, // 鍑哄簱鏁伴噺
+        requisitionNum: form.value.requisitionNum, // 棰嗙敤鏁伴噺
         time: form.value.inboundTime, // 鍑哄簱鏃堕棿
         userId: form.value.nickName, // 缁忔墜浜�
         type: cfg.value.type, // 鍑哄簱绫诲瀷锛�3鍙戞枡 / 2閿�鍞嚭搴� / 5閫�璐у嚭搴�
diff --git a/src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue b/src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue
index 7561257..aaf0a2f 100644
--- a/src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue
+++ b/src/views/inventoryManagement/receiptManagement/components/formDiaManual.vue
@@ -2,6 +2,10 @@
   <el-dialog v-model="dialogFormVisible" :title="operationType === 'add' ? '鏂板鑷畾涔夊叆搴�' : '缂栬緫鑷畾涔夊叆搴�'" width="70%"
     @close="closeDia">
     <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
+      <div style="margin-bottom: 10px; display: flex; align-items: center; gap: 12px;">
+        <span>鐧昏浜猴細</span>
+        <el-input v-model="form.registrant" style="width: 220px" placeholder="璇疯緭鍏ョ櫥璁颁汉" clearable />
+      </div>
       <div style="margin-bottom: 10px;" v-if="operationType === 'add'">
         <el-button type="primary" @click="addProductRow">鏂板</el-button>
       </div>
@@ -142,6 +146,7 @@
     supplierName: '',       // 渚涘簲鍟嗗悕绉�
     recorderId: userStore.userId, // 褰曞叆浜篒D
     recorderName: userStore.name, // 褰曞叆浜哄鍚�
+    registrant: userStore.name, // 鐧昏浜�
     entryDate: getCurrentDate(),  // 褰曞叆鏃ユ湡
     remark: '',             // 澶囨敞
   },
@@ -224,6 +229,7 @@
 
     const payloadList = productList.value.map(product => ({
       id: product.id ?? null,
+      registrant: form.value.registrant || '',
 			inboundNum: Number(product.inboundNum),
       productCategory: product.productCategory,
       specificationModel: product.specificationModel,
diff --git a/src/views/inventoryManagement/receiptManagement/index.vue b/src/views/inventoryManagement/receiptManagement/index.vue
index f0e01c3..9b0c458 100644
--- a/src/views/inventoryManagement/receiptManagement/index.vue
+++ b/src/views/inventoryManagement/receiptManagement/index.vue
@@ -54,6 +54,7 @@
         <el-table-column label="鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" width="150" />
         <el-table-column label="鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" width="150" />
         <el-table-column label="鍏ュ簱浜�" prop="createBy" width="80" show-overflow-tooltip />
+        <el-table-column label="鐧昏浜�" prop="registrant" width="90" show-overflow-tooltip />
         <el-table-column fixed="right" label="鎿嶄綔" width="100" align="center">
           <template #default="scope">
             <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">缂栬緫</el-button>
diff --git a/src/views/inventoryManagement/stockCenter/index.vue b/src/views/inventoryManagement/stockCenter/index.vue
new file mode 100644
index 0000000..902c8a8
--- /dev/null
+++ b/src/views/inventoryManagement/stockCenter/index.vue
@@ -0,0 +1,51 @@
+<template>
+  <div class="stock-center">
+    <el-tabs v-model="activeTab" class="center-tabs">
+      <el-tab-pane label="瀹炴椂搴撳瓨" name="stock" />
+      <el-tab-pane label="搴撳瓨鍙樺姩鏄庣粏" name="movement" />
+      <el-tab-pane label="搴撳瓨鐩樼偣" name="taking" />
+      <el-tab-pane label="搴撳瓨鏃堕棿璁剧疆" name="setting" />
+    </el-tabs>
+    <div class="center-body">
+      <keep-alive>
+        <component :is="currentComponent" />
+      </keep-alive>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import StockRealTime from "../stockManagement/index.vue";
+import StockMovement from "../stockMovement/index.vue";
+import StockTaking from "../stockTaking/index.vue";
+import StockTimeSetting from "../stockTimeSetting/index.vue";
+
+defineOptions({
+  name: "搴撳瓨绠$悊",
+});
+
+const activeTab = ref("stock");
+
+const tabMap = {
+  stock: StockRealTime,
+  movement: StockMovement,
+  taking: StockTaking,
+  setting: StockTimeSetting,
+};
+
+const currentComponent = computed(() => tabMap[activeTab.value] || StockRealTime);
+</script>
+
+<style scoped lang="scss">
+.stock-center {
+  padding: 10px 12px;
+}
+.center-tabs {
+  margin-bottom: 6px;
+}
+/* 瀛愰〉闈㈣嚜甯︾殑 app-container 浼氬涓�灞傚唴杈硅窛锛岃繖閲屽幓鎺� */
+.center-body :deep(.app-container) {
+  padding: 0;
+}
+</style>
diff --git a/src/views/inventoryManagement/stockMovement/index.vue b/src/views/inventoryManagement/stockMovement/index.vue
new file mode 100644
index 0000000..876ea92
--- /dev/null
+++ b/src/views/inventoryManagement/stockMovement/index.vue
@@ -0,0 +1,80 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeTab" @tab-change="handleTabChange">
+      <el-tab-pane label="鍏ュ簱鏄庣粏" name="in" />
+      <el-tab-pane label="鍑哄簱鏄庣粏" name="out" />
+    </el-tabs>
+
+    <div class="search_form">
+      <div>
+        <span class="search_title">浜у搧澶х被锛�</span>
+        <el-input v-model="searchForm.productCategory" style="width: 220px" placeholder="璇疯緭鍏�" clearable @change="handleQuery" />
+        <el-button type="primary" style="margin-left: 10px" @click="handleQuery">鎼滅储</el-button>
+        <el-button @click="resetQuery">閲嶇疆</el-button>
+      </div>
+    </div>
+
+    <div class="table_list">
+      <el-table :data="tableData" border v-loading="tableLoading" height="calc(100vh - 20em)">
+        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+        <el-table-column v-if="activeTab === 'in'" label="鍏ュ簱鏃ユ湡" prop="inboundDate" width="120" />
+        <el-table-column v-else label="鍑哄簱鏃ユ湡" prop="createTime" width="160" />
+        <el-table-column label="鎵规鍙�" prop="code" width="140" 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="unit" width="70" />
+        <el-table-column v-if="activeTab === 'in'" label="鍏ュ簱鏁伴噺" prop="inboundNum" width="110" />
+        <el-table-column v-else label="鍑哄簱鏁伴噺" prop="inboundNum" width="110" />
+        <el-table-column v-if="activeTab === 'out'" label="棰嗙敤鏁伴噺" prop="requisitionNum" width="110" />
+        <el-table-column label="鍗曚环(鍏�)" prop="taxInclusiveUnitPrice" width="120" />
+        <el-table-column label="鎬讳环(鍏�)" prop="taxInclusiveTotalPrice" width="130" />
+        <el-table-column label="缁忔墜浜�" prop="createBy" width="100" show-overflow-tooltip />
+      </el-table>
+      <pagination v-show="page.total > 0" :total="page.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 pagination from "@/components/PIMTable/Pagination.vue";
+import { getInPageByCustom } from "@/api/inventoryManagement/stockIn.js";
+import { getStockOutPage } from "@/api/inventoryManagement/stockOut.js";
+import { getCurrentDate } from "@/utils/index.js";
+
+const activeTab = ref("in");
+const tableData = ref([]);
+const tableLoading = ref(false);
+const page = reactive({ current: 1, size: 100, total: 0 });
+const searchForm = reactive({ productCategory: "", timeStr: getCurrentDate() });
+
+const getList = () => {
+  tableLoading.value = true;
+  const params = { ...searchForm, ...page };
+  if (activeTab.value === "in") {
+    params.flag = false;
+    getInPageByCustom(params).then((res) => {
+      tableLoading.value = false;
+      tableData.value = (res.data && res.data.records) || [];
+      page.total = (res.data && res.data.total) || 0;
+    }).catch(() => { tableLoading.value = false; });
+  } else {
+    getStockOutPage(params).then((res) => {
+      tableLoading.value = false;
+      tableData.value = (res.data && res.data.records) || [];
+      page.total = (res.data && res.data.total) || 0;
+    }).catch(() => { tableLoading.value = false; });
+  }
+};
+const handleQuery = () => { page.current = 1; getList(); };
+const resetQuery = () => { searchForm.productCategory = ""; handleQuery(); };
+const paginationChange = (obj) => { page.current = obj.page; page.size = obj.limit; getList(); };
+const handleTabChange = () => { page.current = 1; getList(); };
+
+onMounted(() => { getList(); });
+</script>
+
+<style scoped lang="scss">
+.table_list { margin-top: unset; }
+</style>
diff --git a/src/views/inventoryManagement/stockTaking/index.vue b/src/views/inventoryManagement/stockTaking/index.vue
new file mode 100644
index 0000000..937d55d
--- /dev/null
+++ b/src/views/inventoryManagement/stockTaking/index.vue
@@ -0,0 +1,136 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div>
+        <span class="search_title">浜у搧澶х被锛�</span>
+        <el-input v-model="searchForm.productCategory" style="width: 220px" placeholder="璇疯緭鍏�" clearable @change="handleQuery" />
+        <span class="search_title ml10">瑙勬牸鍨嬪彿锛�</span>
+        <el-input v-model="searchForm.specificationModel" style="width: 220px" placeholder="璇疯緭鍏�" clearable @change="handleQuery" />
+        <el-button type="primary" style="margin-left: 10px" @click="handleQuery">鎼滅储</el-button>
+        <el-button @click="resetQuery">閲嶇疆</el-button>
+      </div>
+      <div>
+        <el-button type="primary" @click="openForm('add')">鏂板鐩樼偣</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <el-table :data="tableData" border v-loading="tableLoading" height="calc(100vh - 18.5em)">
+        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+        <el-table-column label="鐩樼偣鍗曞彿" prop="code" width="140" show-overflow-tooltip />
+        <el-table-column label="鎵规鍙�" prop="inboundBatches" width="140" 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="unit" width="70" />
+        <el-table-column label="璐﹂潰鏁伴噺" prop="bookNum" width="110" />
+        <el-table-column label="瀹炵洏鏁伴噺" prop="actualNum" width="110" />
+        <el-table-column label="宸紓鏁伴噺" prop="diffNum" width="110">
+          <template #default="scope">
+            <span :style="{ color: Number(scope.row.diffNum) === 0 ? '' : (Number(scope.row.diffNum) > 0 ? '#67c23a' : '#f56c6c') }">
+              {{ scope.row.diffNum == null ? '-' : scope.row.diffNum }}
+            </span>
+          </template>
+        </el-table-column>
+        <el-table-column label="鐩樼偣鏃ユ湡" prop="takingDate" width="120" />
+        <el-table-column label="鐩樼偣浜�" prop="takingUser" width="100" />
+        <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
+        <el-table-column fixed="right" label="鎿嶄綔" width="130" align="center">
+          <template #default="scope">
+            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">缂栬緫</el-button>
+            <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+      <pagination v-show="page.total > 0" :total="page.total" layout="total, sizes, prev, pager, next, jumper"
+        :page="page.current" :limit="page.size" @pagination="paginationChange" />
+    </div>
+
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="600px" @close="closeDialog">
+      <el-form :model="form" label-width="110px" label-position="top">
+        <el-row :gutter="20">
+          <el-col :span="12"><el-form-item label="浜у搧澶х被"><el-input v-model="form.productCategory" clearable /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="瑙勬牸鍨嬪彿"><el-input v-model="form.specificationModel" clearable /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="鍗曚綅"><el-input v-model="form.unit" clearable /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="鎵规鍙�"><el-input v-model="form.inboundBatches" clearable /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="璐﹂潰鏁伴噺"><el-input-number v-model="form.bookNum" :step="0.01" :min="0" style="width: 100%" /></el-form-item></el-col>
+          <el-col :span="12"><el-form-item label="瀹炵洏鏁伴噺"><el-input-number v-model="form.actualNum" :step="0.01" :min="0" style="width: 100%" /></el-form-item></el-col>
+          <el-col :span="12">
+            <el-form-item label="鐩樼偣鏃ユ湡">
+              <el-date-picker v-model="form.takingDate" type="date" value-format="YYYY-MM-DD" format="YYYY-MM-DD" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12"><el-form-item label="鐩樼偣浜�"><el-input v-model="form.takingUser" clearable /></el-form-item></el-col>
+          <el-col :span="24"><el-form-item label="澶囨敞"><el-input v-model="form.remark" type="textarea" :rows="2" /></el-form-item></el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭</el-button>
+        <el-button @click="closeDialog">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import pagination from "@/components/PIMTable/Pagination.vue";
+import { getStockTakingPage, addStockTaking, updateStockTaking, delStockTaking } from "@/api/inventoryManagement/stockTaking.js";
+import useUserStore from "@/store/modules/user";
+
+const userStore = useUserStore();
+const tableData = ref([]);
+const tableLoading = ref(false);
+const page = reactive({ current: 1, size: 100, total: 0 });
+const searchForm = reactive({ productCategory: "", specificationModel: "" });
+const dialogVisible = ref(false);
+const dialogTitle = ref("鏂板鐩樼偣");
+const dialogMode = ref("add");
+const emptyForm = () => ({
+  id: null, productCategory: "", specificationModel: "", unit: "", inboundBatches: "",
+  bookNum: 0, actualNum: 0, takingDate: "", takingUser: userStore.nickName || "", remark: "",
+});
+const form = reactive(emptyForm());
+
+const getList = () => {
+  tableLoading.value = true;
+  getStockTakingPage({ ...searchForm, ...page }).then((res) => {
+    tableLoading.value = false;
+    tableData.value = (res.data && res.data.records) || [];
+    page.total = (res.data && res.data.total) || 0;
+  }).catch(() => { tableLoading.value = false; });
+};
+const handleQuery = () => { page.current = 1; getList(); };
+const resetQuery = () => { searchForm.productCategory = ""; searchForm.specificationModel = ""; handleQuery(); };
+const paginationChange = (obj) => { page.current = obj.page; page.size = obj.limit; getList(); };
+
+const openForm = (mode, row) => {
+  dialogMode.value = mode;
+  dialogTitle.value = mode === "add" ? "鏂板鐩樼偣" : "缂栬緫鐩樼偣";
+  Object.assign(form, emptyForm());
+  if (mode === "edit" && row) {
+    Object.keys(form).forEach((k) => { if (row[k] !== undefined) form[k] = row[k]; });
+  }
+  dialogVisible.value = true;
+};
+const closeDialog = () => { dialogVisible.value = false; };
+const submitForm = () => {
+  const payload = { ...form };
+  const req = dialogMode.value === "add" ? addStockTaking(payload) : updateStockTaking(payload);
+  req.then(() => {
+    ElMessage.success(dialogMode.value === "add" ? "鏂板鎴愬姛" : "淇敼鎴愬姛");
+    closeDialog();
+    getList();
+  }).catch(() => ElMessage.error("鎿嶄綔澶辫触"));
+};
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ョ洏鐐硅褰曪紵", "鎻愮ず", { type: "warning" }).then(() => {
+    delStockTaking([row.id]).then(() => { ElMessage.success("鍒犻櫎鎴愬姛"); getList(); });
+  }).catch(() => {});
+};
+
+onMounted(() => { getList(); });
+</script>
+
+<style scoped lang="scss">
+.table_list { margin-top: unset; }
+</style>
diff --git a/src/views/inventoryManagement/stockTimeSetting/index.vue b/src/views/inventoryManagement/stockTimeSetting/index.vue
new file mode 100644
index 0000000..37babea
--- /dev/null
+++ b/src/views/inventoryManagement/stockTimeSetting/index.vue
@@ -0,0 +1,132 @@
+<template>
+  <div class="app-container">
+    <div class="search_form">
+      <div>
+        <span class="search_title">搴撳瓨鏃堕棿璁剧疆锛氶厤缃簱瀛橀璀︽娴嬫椂闂淬�佺洏鐐瑰懆鏈熴�佸懆杞巼棰勮澶╂暟绛�</span>
+      </div>
+      <div>
+        <el-button type="primary" @click="openForm('add')">鏂板璁剧疆</el-button>
+      </div>
+    </div>
+    <div class="table_list">
+      <el-table :data="tableData" border v-loading="tableLoading" height="calc(100vh - 18.5em)">
+        <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+        <el-table-column label="棰勮妫�娴嬫椂闂�" prop="warningCheckTime" width="140" />
+        <el-table-column label="鐩樼偣鍛ㄦ湡" prop="stockTakingCycle" width="120" />
+        <el-table-column label="鍛ㄨ浆鐜囬璀﹀ぉ鏁�" prop="turnoverWarnDays" width="150" />
+        <el-table-column label="姣忔湀鑷姩鐩樼偣鏃�" prop="autoTakingDay" width="150" />
+        <el-table-column label="鏄惁鍚敤" prop="enabled" width="100">
+          <template #default="scope">
+            <el-tag :type="scope.row.enabled === 1 ? 'success' : 'info'">{{ scope.row.enabled === 1 ? '鍚敤' : '鍋滅敤' }}</el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="澶囨敞" prop="remark" show-overflow-tooltip />
+        <el-table-column fixed="right" label="鎿嶄綔" width="130" align="center">
+          <template #default="scope">
+            <el-button link type="primary" size="small" @click="openForm('edit', scope.row)">缂栬緫</el-button>
+            <el-button link type="danger" size="small" @click="handleDelete(scope.row)">鍒犻櫎</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <el-dialog v-model="dialogVisible" :title="dialogTitle" width="560px" @close="closeDialog">
+      <el-form :model="form" label-width="140px" label-position="top">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="棰勮妫�娴嬫椂闂�(HH:mm)">
+              <el-time-picker v-model="form.warningCheckTime" format="HH:mm" value-format="HH:mm" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鐩樼偣鍛ㄦ湡">
+              <el-select v-model="form.stockTakingCycle" style="width: 100%" placeholder="璇烽�夋嫨">
+                <el-option label="姣忔棩" value="鏃�" />
+                <el-option label="姣忓懆" value="鍛�" />
+                <el-option label="姣忔湀" value="鏈�" />
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鍛ㄨ浆鐜囬璀﹀ぉ鏁�">
+              <el-input-number v-model="form.turnoverWarnDays" :min="1" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="姣忔湀鑷姩鐩樼偣鏃�">
+              <el-input-number v-model="form.autoTakingDay" :min="1" :max="31" style="width: 100%" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="鏄惁鍚敤">
+              <el-switch v-model="form.enabled" :active-value="1" :inactive-value="0" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="澶囨敞">
+              <el-input v-model="form.remark" type="textarea" :rows="2" />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <template #footer>
+        <el-button type="primary" @click="submitForm">纭</el-button>
+        <el-button @click="closeDialog">鍙栨秷</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted } from "vue";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { getStockTimeSettingList, addStockTimeSetting, updateStockTimeSetting, delStockTimeSetting } from "@/api/inventoryManagement/stockTaking.js";
+
+const tableData = ref([]);
+const tableLoading = ref(false);
+const dialogVisible = ref(false);
+const dialogTitle = ref("鏂板璁剧疆");
+const dialogMode = ref("add");
+const emptyForm = () => ({
+  id: null, warningCheckTime: "09:00", stockTakingCycle: "鏈�", turnoverWarnDays: 30, autoTakingDay: 1, enabled: 1, remark: "",
+});
+const form = reactive(emptyForm());
+
+const getList = () => {
+  tableLoading.value = true;
+  getStockTimeSettingList().then((res) => {
+    tableLoading.value = false;
+    tableData.value = res.data || [];
+  }).catch(() => { tableLoading.value = false; });
+};
+const openForm = (mode, row) => {
+  dialogMode.value = mode;
+  dialogTitle.value = mode === "add" ? "鏂板璁剧疆" : "缂栬緫璁剧疆";
+  Object.assign(form, emptyForm());
+  if (mode === "edit" && row) {
+    Object.keys(form).forEach((k) => { if (row[k] !== undefined) form[k] = row[k]; });
+  }
+  dialogVisible.value = true;
+};
+const closeDialog = () => { dialogVisible.value = false; };
+const submitForm = () => {
+  const payload = { ...form };
+  const req = dialogMode.value === "add" ? addStockTimeSetting(payload) : updateStockTimeSetting(payload);
+  req.then(() => {
+    ElMessage.success(dialogMode.value === "add" ? "鏂板鎴愬姛" : "淇敼鎴愬姛");
+    closeDialog();
+    getList();
+  }).catch(() => ElMessage.error("鎿嶄綔澶辫触"));
+};
+const handleDelete = (row) => {
+  ElMessageBox.confirm("纭鍒犻櫎璇ヨ缃紵", "鎻愮ず", { type: "warning" }).then(() => {
+    delStockTimeSetting([row.id]).then(() => { ElMessage.success("鍒犻櫎鎴愬姛"); getList(); });
+  }).catch(() => {});
+};
+
+onMounted(() => { getList(); });
+</script>
+
+<style scoped lang="scss">
+.table_list { margin-top: unset; }
+</style>
diff --git a/src/views/inventoryManagement/stockWarningLedger/index.vue b/src/views/inventoryManagement/stockWarningLedger/index.vue
index d78ea30..35150d4 100644
--- a/src/views/inventoryManagement/stockWarningLedger/index.vue
+++ b/src/views/inventoryManagement/stockWarningLedger/index.vue
@@ -85,6 +85,14 @@
             <span v-else>-</span>
           </template>
         </el-table-column>
+        <el-table-column label="寤鸿绫诲瀷" prop="suggestionType" width="110" show-overflow-tooltip />
+        <el-table-column label="寤鸿鍐呭" prop="suggestionContent" show-overflow-tooltip />
+        <el-table-column fixed="right" label="鎿嶄綔" width="210" align="center">
+          <template #default="scope">
+            <el-button link type="primary" size="small" @click="handleSuggestion(scope.row, '閲囪喘寤鸿')">閲囪喘寤鸿</el-button>
+            <el-button link type="warning" size="small" @click="handleSuggestion(scope.row, '婊為攢澶勭悊')">婊為攢鎻愮ず</el-button>
+          </template>
+        </el-table-column>
       </el-table>
       <pagination
         v-show="total > 0"
@@ -104,7 +112,8 @@
 import { ElMessage } from 'element-plus'
 import pagination from '@/components/PIMTable/Pagination.vue'
 import {
-  getStockWarningLedgerPage
+  getStockWarningLedgerPage,
+  generateSuggestion
 } from '@/api/inventoryManagement/stockWarningLedger.js'
 
 // 鍝嶅簲寮忔暟鎹�
@@ -273,6 +282,16 @@
 }
 
 // 椤甸潰鍔犺浇
+// 鐢熸垚閲囪喘寤鸿 / 婊為攢澶勭悊鎻愮ず
+const handleSuggestion = (row, type) => {
+  generateSuggestion({ id: row.id, type }).then((res) => {
+    ElMessage({ message: res.msg || res.data || '宸茬敓鎴�', type: 'success', duration: 6000, showClose: true })
+    getList()
+  }).catch(() => {
+    ElMessage.error('鐢熸垚澶辫触')
+  })
+}
+
 onMounted(() => {
   getList()
 })
diff --git a/src/views/procurementManagement/paymentEntry/index.vue b/src/views/procurementManagement/paymentEntry/index.vue
index 02fda1a..63363c3 100644
--- a/src/views/procurementManagement/paymentEntry/index.vue
+++ b/src/views/procurementManagement/paymentEntry/index.vue
@@ -14,6 +14,36 @@
                 @change="handleQuery"
               />
             </el-form-item>
+            <el-form-item label="鍙戠エ鏃ユ湡">
+              <el-date-picker
+                v-model="searchForm.issueDateRange"
+                style="width: 250px"
+                value-format="YYYY-MM-DD"
+                format="YYYY-MM-DD"
+                type="daterange"
+                start-placeholder="寮�濮嬫棩鏈�"
+                end-placeholder="缁撴潫鏃ユ湡"
+                clearable
+                @change="handleIssueDateChange"
+              />
+            </el-form-item>
+            <el-form-item label="鍙戠エ閲戦">
+              <el-input
+                v-model="searchForm.invoiceAmountStart"
+                style="width: 110px"
+                placeholder="鏈�灏�"
+                clearable
+                @change="handleQuery"
+              />
+              <span style="margin: 0 6px">-</span>
+              <el-input
+                v-model="searchForm.invoiceAmountEnd"
+                style="width: 110px"
+                placeholder="鏈�澶�"
+                clearable
+                @change="handleQuery"
+              />
+            </el-form-item>
             <el-form-item>
               <el-checkbox
                 v-model="searchForm.status"
@@ -309,6 +339,11 @@
     width:200
   },
   {
+    label: "鍙戠エ鏃ユ湡",
+    prop: "issueDate",
+    width: 120,
+  },
+  {
     label: "鍙戠エ閲戦(鍏�)",
     prop: "invoiceAmount",
     formatData: (params) => {
@@ -353,6 +388,11 @@
   searchForm: {
     supplierNameOrContractNo: "",
     status: false,
+    issueDateRange: [],
+    issueDateStart: undefined,
+    issueDateEnd: undefined,
+    invoiceAmountStart: undefined,
+    invoiceAmountEnd: undefined,
   },
   form: {
     purchaseContractNumber: "",
@@ -412,6 +452,17 @@
   page.current = 1;
   getList();
 };
+// 鍙戠エ鏃ユ湡鍖洪棿鍙樺寲
+const handleIssueDateChange = (val) => {
+  if (val && val.length === 2) {
+    searchForm.issueDateStart = val[0];
+    searchForm.issueDateEnd = val[1];
+  } else {
+    searchForm.issueDateStart = undefined;
+    searchForm.issueDateEnd = undefined;
+  }
+  handleQuery();
+};
 const pagination = (obj) => {
   page.current = obj.page;
   page.size = obj.limit;
@@ -419,7 +470,12 @@
 };
 const getList = () => {
   tableLoading.value = true;
-  invoiceListPage({ ...searchForm, ...page }).then((res) => {
+  const params = { ...searchForm, ...page };
+  // 鍙戠エ鏃ユ湡鍖洪棿鐢� start/end 浼犵粰鍚庣锛屽幓鎺変粎鍓嶇鐢ㄧ殑鏁扮粍
+  delete params.issueDateRange;
+  if (params.invoiceAmountStart === "" || params.invoiceAmountStart === null) delete params.invoiceAmountStart;
+  if (params.invoiceAmountEnd === "" || params.invoiceAmountEnd === null) delete params.invoiceAmountEnd;
+  invoiceListPage(params).then((res) => {
     tableLoading.value = false;
     tableData.value = res.records;
 		page.total = res.total;
diff --git a/src/views/procurementManagement/paymentHistory/index.vue b/src/views/procurementManagement/paymentHistory/index.vue
index 7c71979..7a55f7e 100644
--- a/src/views/procurementManagement/paymentHistory/index.vue
+++ b/src/views/procurementManagement/paymentHistory/index.vue
@@ -32,6 +32,7 @@
         >
           鎼滅储
         </el-button>
+        <el-button @click="handleExport">瀵煎嚭</el-button>
       </el-form-item>
     </el-form>
     <div class="table_list">
@@ -120,6 +121,11 @@
   page.current = 1;
   getList();
 };
+// 瀵煎嚭浠樻娴佹按锛堟寜褰撳墠鏌ヨ鏉′欢瀵煎嚭鍏ㄩ儴锛�
+const handleExport = () => {
+  const { paymentDate, ...rest } = searchForm;
+  proxy.download("/purchase/paymentRegistration/export", { ...rest }, "浠樻娴佹按.xlsx");
+};
 const pagination = (obj) => {
   page.current = obj.page;
   page.size = obj.limit;
diff --git a/src/views/productionManagement/operationScheduling/index.vue b/src/views/productionManagement/operationScheduling/index.vue
index 1427faf..c6a724f 100644
--- a/src/views/productionManagement/operationScheduling/index.vue
+++ b/src/views/productionManagement/operationScheduling/index.vue
@@ -120,6 +120,16 @@
 		prop: "pendingNum",
 		width: 100,
 	},
+	{
+		label: "鐢熶骇瑕佹眰",
+		prop: "productionRequirement",
+		showOverflowTooltip: true,
+	},
+	{
+		label: "瀹屾垚鏈熼檺",
+		prop: "completionDeadline",
+		width: 120,
+	},
 ]);
 const tableData = ref([]);
 const selectedRows = ref([]);
diff --git a/src/views/productionManagement/productionDispatching/components/formDia.vue b/src/views/productionManagement/productionDispatching/components/formDia.vue
index de04e07..47d7426 100644
--- a/src/views/productionManagement/productionDispatching/components/formDia.vue
+++ b/src/views/productionManagement/productionDispatching/components/formDia.vue
@@ -4,6 +4,7 @@
         v-model="dialogFormVisible"
         title="鐢熶骇娲惧伐"
         width="50%"
+        top="5vh"
         @close="closeDia"
     >
       <el-form :model="form" label-width="140px" label-position="top" :rules="rules" ref="formRef">
@@ -72,6 +73,33 @@
             </el-form-item>
           </el-col>
         </el-row>
+        <el-row :gutter="30">
+          <el-col :span="24">
+            <el-form-item label="鐢熶骇瑕佹眰锛�" prop="productionRequirement">
+              <el-input
+                  v-model="form.productionRequirement"
+                  type="textarea"
+                  :rows="2"
+                  placeholder="璇疯緭鍏ョ敓浜ц姹�"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="瀹屾垚鏈熼檺锛�" prop="completionDeadline">
+              <el-date-picker
+                  v-model="form.completionDeadline"
+                  type="date"
+                  placeholder="璇烽�夋嫨瀹屾垚鏈熼檺"
+                  value-format="YYYY-MM-DD"
+                  format="YYYY-MM-DD"
+                  clearable
+                  style="width: 100%"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
       </el-form>
       <template #footer>
         <div class="dialog-footer">
@@ -104,6 +132,8 @@
 		schedulingDate: "",
 		pendingQuantity: "",
 		salesLedgerProductId: "",
+		productionRequirement: "",
+		completionDeadline: "",
   },
   rules: {
 		schedulingNum: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" },],
@@ -130,6 +160,8 @@
 	form.value.schedulingNum = 0
 	form.value.schedulingUserId = userStore.id
 	form.value.schedulingDate = dayjs().format("YYYY-MM-DD");
+	form.value.productionRequirement = "";
+	form.value.completionDeadline = "";
 }
 
 //
diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 5dda65a..9cf589e 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -50,6 +50,20 @@
 					clearable
 				/>
 			</el-form-item>
+			<el-form-item label="浜у搧鍚嶇О" prop="productName">
+				<el-input
+					v-model="form.productName"
+					placeholder="璇疯緭鍏ヤ骇鍝佸悕绉�"
+					clearable
+				/>
+			</el-form-item>
+			<el-form-item label="椤圭洰鍚嶇О" prop="projectName">
+				<el-input
+					v-model="form.projectName"
+					placeholder="璇疯緭鍏ラ」鐩悕绉�"
+					clearable
+				/>
+			</el-form-item>
 			<el-form-item label="瑙勬牸鍨嬪彿" prop="specificationModel">
 				<el-input
 					v-model="form.specificationModel"
@@ -101,6 +115,14 @@
 		prop: "productCategory",
 	},
 	{
+		label: "浜у搧鍚嶇О",
+		prop: "productName",
+	},
+	{
+		label: "椤圭洰鍚嶇О",
+		prop: "projectName",
+	},
+	{
 		label: "瑙勬牸鍨嬪彿",
 		prop: "specificationModel",
 	},
@@ -112,11 +134,11 @@
 		label: "鏁伴噺",
 		prop: "quantity",
 	},
-	// {
-	// 	label: "鎺掍骇鏁伴噺",
-	// 	prop: "schedulingNum",
-	// 	width: 100,
-	// },
+	{
+		label: "鎺掍骇鏁伴噺",
+		prop: "schedulingNum",
+		width: 100,
+	},
 	// {
 	// 	label: "瀹屽伐鏁伴噺",
 	// 	prop: "successNum",
@@ -148,6 +170,8 @@
 	id: null,
 	registerDate: dayjs().format("YYYY-MM-DD"),
 	productCategory: "",
+	productName: "",
+	projectName: "",
 	specificationModel: "",
 	unit: "",
 	quantity: null,
@@ -185,6 +209,8 @@
 		form.id = row.id;
 		form.registerDate = row.registerDate;
 		form.productCategory = row.productCategory;
+		form.productName = row.productName;
+		form.projectName = row.projectName;
 		form.specificationModel = row.specificationModel;
 		form.unit = row.unit;
 		form.quantity = row.quantity;
@@ -201,6 +227,8 @@
 	form.id = null;
 	form.registerDate = dayjs().format("YYYY-MM-DD");
 	form.productCategory = "";
+	form.productName = "";
+	form.projectName = "";
 	form.specificationModel = "";
 	form.unit = "";
 	form.quantity = null;
@@ -213,6 +241,8 @@
 			const payload = {
 				registerDate: form.registerDate,
 				productCategory: form.productCategory,
+				productName: form.productName,
+				projectName: form.projectName,
 				specificationModel: form.specificationModel,
 				unit: form.unit,
 				quantity: form.quantity,
diff --git a/src/views/salesManagement/invoiceLedger/index.vue b/src/views/salesManagement/invoiceLedger/index.vue
index 4e8c274..30fbaab 100644
--- a/src/views/salesManagement/invoiceLedger/index.vue
+++ b/src/views/salesManagement/invoiceLedger/index.vue
@@ -15,13 +15,17 @@
           <el-date-picker style="width: 100%" v-model="searchForm.createTimeStart" value-format="YYYY-MM-DD HH:mm:ss"
             format="YYYY-MM-DD" type="date" placeholder="璇烽�夋嫨" clearable @change="handleQuery" />
         </el-form-item>
+      <el-form-item label="鍙戠エ閲戦">
+        <el-input v-model="searchForm.invoiceAmountStart" style="width: 110px" placeholder="鏈�灏�" clearable @change="handleQuery" />
+        <span style="margin: 0 6px">-</span>
+        <el-input v-model="searchForm.invoiceAmountEnd" style="width: 110px" placeholder="鏈�澶�" clearable @change="handleQuery" />
+      </el-form-item>
         <el-form-item label="涓嶆樉绀烘湁鍙戠エ琛�">
           <el-checkbox v-model="searchForm.status" @change="handleQuery" />
         </el-form-item>
         <el-form-item>
           <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
           <el-button @click="resetForm"> 閲嶇疆 </el-button>
-          <el-button @click="handleOut" type="primary">瀵煎嚭</el-button>
         </el-form-item>
       </el-form>
     </div>
@@ -192,6 +196,8 @@
     invoiceDateStart: dayjs().startOf("month").format("YYYY-MM-DD"),
     invoiceDateEnd: dayjs().endOf("month").format("YYYY-MM-DD"),
     createTimeStart: "", // 褰曞叆鏃ユ湡
+    invoiceAmountStart: undefined,
+    invoiceAmountEnd: undefined,
   },
   form: {
     salesLedgerId: "",
@@ -351,21 +357,6 @@
   proxy.resetForm("formRef");
   dialogFormVisible.value = false;
 };
-// 瀵煎嚭
-const handleOut = () => {
-  ElMessageBox.confirm("閫変腑鐨勫唴瀹瑰皢琚鍑猴紝鏄惁纭瀵煎嚭锛�", "瀵煎嚭", {
-    confirmButtonText: "纭",
-    cancelButtonText: "鍙栨秷",
-    type: "warning",
-  })
-    .then(() => {
-      proxy.download("/invoiceLedger/export", {}, "寮�绁ㄥ彴璐�.xlsx");
-    })
-    .catch(() => {
-      proxy.$modal.msg("宸插彇娑�");
-    });
-};
-
 // 鎵撳紑闄勪欢涓婁紶寮圭獥
 const handleDownload = (val) => {
   fileList.value = [];
diff --git a/src/views/salesManagement/receiptPaymentHistory/index.vue b/src/views/salesManagement/receiptPaymentHistory/index.vue
index d7ef8b2..4888926 100644
--- a/src/views/salesManagement/receiptPaymentHistory/index.vue
+++ b/src/views/salesManagement/receiptPaymentHistory/index.vue
@@ -44,6 +44,7 @@
       </el-form-item>
       <el-form-item>
         <el-button type="primary" @click="handleQuery"> 鎼滅储 </el-button>
+        <el-button @click="handleOut">瀵煎嚭</el-button>
       </el-form-item>
     </el-form>
     <div class="table_list">
@@ -156,6 +157,12 @@
   page.size = obj.limit;
   getList();
 };
+// 瀵煎嚭鍥炴娴佹按锛堟寜褰撳墠鏌ヨ鏉′欢锛�
+const handleOut = () => {
+  const { receiptPaymentDate, ...rest } = searchForm;
+  proxy.download("/receiptPayment/exportOne", { ...rest }, "鍥炴娴佹按.xlsx");
+};
+
 const getList = () => {
   tableLoading.value = true;
   const { receiptPaymentDate, ...rest } = searchForm;

--
Gitblit v1.9.3