From 1d3a018e6d707179eef127424e3d78d3863fba71 Mon Sep 17 00:00:00 2001
From: huangxiaoqiang <huangxiaoqiang@hnkhzn.com>
Date: 星期一, 30 三月 2026 16:09:02 +0800
Subject: [PATCH] 实现MES过点记录与库存变动记录全流程功能 本次提交实现了MES过点记录和库存变动记录的前后端全流程,包括数据模型、仓储、服务、控制器、前端页面及扩展配置。新增精排显示页面,支持虚拟精排与车身信息弹窗。优化货位信息行页面的悬浮提示框,丰富展示内容并美化样式。后端完善了相关API、DTO、AutoMapper映射及业务逻辑,支持数据追溯与可视化。整体提升了系统的可维护性和用户体验。

---
 项目代码/WMS/WMSClient/src/views/widesea_wms/stock/locationInfoRow.vue |  461 ++++++++++++++++++++++++++++++++++++++++++++-------------
 1 files changed, 357 insertions(+), 104 deletions(-)

diff --git "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WMSClient/src/views/widesea_wms/stock/locationInfoRow.vue" "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WMSClient/src/views/widesea_wms/stock/locationInfoRow.vue"
index 2b6f034..a10d4f6 100644
--- "a/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WMSClient/src/views/widesea_wms/stock/locationInfoRow.vue"
+++ "b/\351\241\271\347\233\256\344\273\243\347\240\201/WMS/WMSClient/src/views/widesea_wms/stock/locationInfoRow.vue"
@@ -7,13 +7,13 @@
     <div class="content-wrapper">
       <!-- 鎺у埗闈㈡澘鍖哄煙 -->
       <div class="control-panel">
-        <div class="form-group">
+        <!-- <div class="form-group">
           <label>鍖哄煙锛�</label>
           <el-select size="mini" filterable v-model="Area.areaCode" placeholder="璇烽�夋嫨" class="full-width">
             <el-option v-for="item in slectData" :value="item.areaCode" :label="item.areaName"
               :key="item.areaName"></el-option>
           </el-select>
-        </div>
+        </div> -->
 
         <!-- <div class="form-group">
           <label>鎺掞細</label>
@@ -35,9 +35,7 @@
           </el-select>
         </div> -->
 
-        <el-button type="success" class="refresh-btn" @click="GetViewData">
-          鍒锋柊
-        </el-button>
+        <el-button type="success" class="refresh-btn" @click="GetViewData"> 鍒锋柊 </el-button>
 
         <div class="legend-section">
           <h4>璇存槑</h4>
@@ -63,153 +61,264 @@
         </div>
       </div>
       <!-- 鎮诞鎻愮ず妗� -->
-      <div v-if="showTooltipFlag" class="location-tooltip" :style="{
-        left: tooltipPosition.x + 'px',
-        top: tooltipPosition.y + 'px',
-      }">
-        <div v-if="currentLocation">
-          <p><strong>璐т綅鍙�:</strong>{{ currentLocation.locationCode }}</p>
-          <!-- <p>
-            <strong>璐т綅鎺掑垪灞�:</strong> {{ currentLocation.row }}鎺抺{
-              currentLocation.layer
-            }}鍒梴{ currentLocation.layer }}灞�
-          </p> -->
-          <p><strong>鐘舵��:</strong> {{ getStatusText(currentLocation) }}</p>
-          <p>
-            <strong>绂佺敤:</strong>
-            {{ currentLocation.location_lock == 2 ? "鏄�" : "鍚�" }}
-          </p>
-          <!-- <p v-if="currentLocation.location_state > 0">
-            <strong>鐗╂枡缂栫爜:</strong>
-            {{ currentLocation.material_code || "鏃�" }}
-          </p>
-          <p v-if="currentLocation.location_state > 0">
-            <strong>鏁伴噺:</strong> {{ currentLocation.quantity || "鏃�" }}
-          </p> -->
+      <transition name="tooltip-fade">
+        <div v-if="showTooltipFlag" class="location-tooltip" :style="{
+          left: tooltipPosition.x + 'px',
+          top: tooltipPosition.y + 'px'
+        }">
+          <div v-if="currentLocation" class="tooltip-content">
+            <div class="tooltip-header">
+              <h4>{{ currentLocation.locationCode }}</h4>
+              <span class="status-badge" :class="getStatusClass(currentLocation)">
+                {{ getStatusText(currentLocation) }}
+              </span>
+            </div>
+            <div class="tooltip-body">
+              <div class="info-section">
+                <h5>鍩烘湰淇℃伅</h5>
+                <div class="info-row">
+                  <span class="label">璐т綅鍙�:</span>
+                  <span class="value">{{ currentLocation.locationCode }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">鐘舵��:</span>
+                  <span class="value status-highlight" :class="getStatusClass(currentLocation)">
+                    {{ getStatusText(currentLocation) }}
+                  </span>
+                </div>
+                <div class="info-row">
+                  <span class="label">绂佺敤:</span>
+                  <span class="value" :class="{ 'disabled': currentLocation.location_lock == 2 }">
+                    {{ currentLocation.location_lock == 2 ? '鏄�' : '鍚�' }}
+                  </span>
+                </div>
+              </div>
+              <div v-if="currentLocation.stockInfo != null" class="info-section">
+                <h5>搴撳瓨淇℃伅</h5>
+                <div class="info-row">
+                  <span class="label">PVI:</span>
+                  <span class="value">{{ currentLocation.stockInfo.pvi }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">婊戞﹪鍙�:</span>
+                  <span class="value">{{ currentLocation.stockInfo.palletCode }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">宸烽亾鍙�:</span>
+                  <span class="value">{{ currentLocation.stockInfo.roadwayNo }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">杞﹀瀷:</span>
+                  <span class="value">{{ currentLocation.stockInfo.vehicleCharacteristic }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">宸ュ崟绫诲瀷:</span>
+                  <span class="value">{{ currentLocation.stockInfo.workOrderType }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">杞﹁韩棰滆壊:</span>
+                  <span class="value">{{ currentLocation.stockInfo.carBodyCharacteristic }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">杞﹁韩绫诲瀷:</span>
+                  <span class="value">{{ getcarType(currentLocation.stockInfo.carType) }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">褰╄溅韬墿鏂欏彿:</span>
+                  <span class="value">{{ currentLocation.stockInfo.pbMaterial }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">鐧借溅韬墿鏂欏彿:</span>
+                  <span class="value">{{ currentLocation.stockInfo.biwMaterialCode }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">鐒婅涓婄嚎鏃堕棿:</span>
+                  <span class="value">{{ currentLocation.stockInfo.biwInPassTime }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">澶╃獥鐗瑰緛:</span>
+                  <span class="value">{{ currentLocation.stockInfo.skylightCharacteristic }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">鎷夊姩閿佸畾:</span>
+                  <span class="value">{{ getlockOrder(currentLocation.stockInfo.lockOrder) }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">閿佸畾鐘舵��:</span>
+                  <span class="value">{{ getstockStatus(currentLocation.stockInfo.stockStatus) }}</span>
+                </div>
+                <div class="info-row">
+                  <span class="label">浠诲姟鐘舵��:</span>
+                  <span class="value">{{ gettaskStatus(currentLocation.stockInfo.taskStatus) }}</span>
+                </div>
+              </div>
+            </div>
+          </div>
         </div>
-      </div>
+      </transition>
     </div>
   </div>
 </template>
 
 <script>
-import { ElButton } from "element-plus";
+import { ElButton } from 'element-plus'
 
 export default {
   data() {
     return {
       slectData: [],
       scList: [],
-      Area: { areaName: "", areaCode: "" },
-      mian_height: "",
+      Area: { areaName: '', areaCode: '' },
+      mian_height: '',
       infoMsg: [
-        { bgcolor: "lightgreen", msg: "绌鸿揣浣�", state: 0 },
-        { bgcolor: "orange", msg: "鏈夎揣", state: 2 },
-        { bgcolor: "#2BB3D5", msg: "閿佸畾", state: 1 },
-        { bgcolor: "#ccc", msg: "绂佺敤", state: 9 }, 
-        // { bgcolor: "red", msg: "绂佺敤", state: 3 },
-        { bgcolor: "blue", msg: "鍑哄簱", state: "3" },
-         { bgcolor: "pink", msg: "鍏ュ簱", state: "4" },
+        { bgcolor: 'lightgreen', msg: '绌鸿揣浣�', state: 0 },
+        { bgcolor: 'orange', msg: '鏈夎揣', state: 2 },
+        { bgcolor: '#2BB3D5', msg: '閿佸畾', state: 1 },
+        { bgcolor: '#ccc', msg: '绂佺敤', state: 9 },
+        { bgcolor: 'blue', msg: '鍑哄簱', state: '3' },
+        { bgcolor: 'pink', msg: '鍏ュ簱', state: '4' }
       ],
       locationData: [],
       showTooltipFlag: false,
       currentLocation: null,
-      tooltipPosition: { x: 0, y: 0 },
-    };
+      tooltipPosition: { x: 20, y: 20 }
+    }
   },
   computed: {
     GetBgColor(col) {
       return (col) => {
-        var bgColor ='';
+        var bgColor = ''
         //浼樺厛鏄剧ず绂佺敤鐘舵��
         if (col.location_lock == 2) {
           this.infoMsg.forEach((el) => {
             if (el.state == col.location_lock) {
-              bgColor = "#ccc";
+              bgColor = '#ccc'
             }
-          });
-        }
-         else {
+          })
+        } else {
           this.infoMsg.forEach((el) => {
-             if (el.state == col.location_state) {
-              bgColor = el.bgcolor;
+            if (el.state == col.location_state) {
+              bgColor = el.bgcolor
             }
-          });
+          })
         }
-        return bgColor;
-      };
-    },
+        return bgColor
+      }
+    }
   },
   watch: {
     //鍒囨崲搴撳尯
-    "Area.areaCode"(newValue, oldValue) {
-      this.scList = [];
+    'Area.areaCode'(newValue, oldValue) {
+      this.scList = []
       this.slectData.forEach((e) => {
         if (e.areaCode == newValue) {
-          this.Area.areaCode = e.areaCode[0];
+          this.Area.areaCode = e.areaCode[0]
           //this.scList = e.tunnel;
         }
-      });
-      this.GetViewData();
-    },
+      })
+      this.GetViewData()
+    }
   },
   methods: {
     GetViewData() {
-      var _this = this;
-      this.http
-        .post("/api/LocationInfo/GetLocationStatu", _this.Area, "鏌ヨ涓�")
-        .then((x) => {
-          _this.locationData = x;
-          console.log("鍚庣杩斿洖:", x);
-        });
+      var _this = this
+      this.http.post('/api/LocationInfo/GetLocationStatu', _this.Area, '鏌ヨ涓�').then((x) => {
+        _this.locationData = x
+        console.log('鍚庣杩斿洖:', x)
+      })
     },
     // 鍒囨崲鎺�
     SCChange() {
-      this.GetViewData();
+      this.GetViewData()
     },
     showTooltip(location, event) {
-      this.currentLocation = location;
-      this.showTooltipFlag = true;
+      this.currentLocation = location
+      this.showTooltipFlag = true
 
-      // 璁剧疆鎻愮ず妗嗕綅缃紝绋嶅井鍋忕Щ閬垮厤閬尅榧犳爣
-      this.tooltipPosition = {
-        x: event.clientX + 10,
-        y: event.clientY + 10,
-      };
+      // 鑾峰彇鎻愮ず妗嗙殑棰勪及瀹藉害
+      const tooltipWidth = 300
+      const tooltipHeight = 400
+      const offsetX = 15
+      const offsetY = 15
+
+      // 璁$畻鎻愮ず妗嗕綅缃紝浼樺厛鏄剧ず鍦ㄩ紶鏍囦笂鏂�
+      let x = event.clientX + offsetX
+      let y = event.clientY - tooltipHeight - offsetY
+
+      // 濡傛灉涓婃柟绌洪棿涓嶈冻锛屽垯鏄剧ず鍦ㄤ笅鏂�
+      if (y < 10) {
+        y = event.clientY + offsetY
+      }
+
+      // 妫�鏌ュ彸渚ц竟鐣�
+      if (x + tooltipWidth > window.innerWidth) {
+        x = event.clientX - tooltipWidth - offsetX
+      }
+
+      // 纭繚涓嶄細瓒呭嚭宸﹁竟鐣屽拰涓婅竟鐣�
+      x = Math.max(10, x)
+      y = Math.max(10, y)
+
+      this.tooltipPosition = { x, y }
     },
 
     hideTooltip() {
-      this.showTooltipFlag = false;
-      this.currentLocation = null;
+      this.showTooltipFlag = false
+      this.currentLocation = null
     },
 
     getStatusText(location) {
       // if (location.location_lock === 3) return "绂佺敤";
-      if (location.location_state === '0') return "绌鸿揣浣�";
-      if (location.location_state === '1') return "閿佸畾";
-      if (location.location_state === '3') return "鏈夎揣閿佸畾";
-      if (location.location_state === '4') return "绌洪棽閿佸畾";
-      if (location.location_state === '2') return "鏈夎揣";
+      if (location.location_state === '0') return '绌鸿揣浣�'
+      if (location.location_state === '1') return '閿佸畾'
+      if (location.location_state === '3') return '鏈夎揣閿佸畾'
+      if (location.location_state === '4') return '绌洪棽閿佸畾'
+      if (location.location_state === '2') return '鏈夎揣'
       // if (location.location_state > 0 && location.location_state < 100)
       //   return "閿佸畾";
-      return "鍏朵粬";
+      return '鍏朵粬'
     },
+    getStatusClass(location) {
+      if (location.location_state === '0') return 'status-empty'
+      if (location.location_state === '1') return 'status-locked'
+      if (location.location_state === '3') return 'status-locked-with-stock'
+      if (location.location_state === '4') return 'status-locked-empty'
+      if (location.location_state === '2') return 'status-with-stock'
+      return 'status-other'
+    },
+    getcarType(carType) {
+      if (carType == 1) return '鐧借溅韬�'
+      if (carType == 2) return '褰╄溅韬�'
+      if (carType == 3) return '绌烘粦姗�'
+    },
+    getlockOrder(lockOrder) {
+      if (lockOrder == 1) return '宸查攣杞�'
+      if (lockOrder == 0) return '鏈媺鍔�'
+    },
+    getstockStatus(stockStatus) {
+      if (stockStatus == 1) return '宸查攣瀹�'
+      if (stockStatus == 0) return '鏈攣瀹�'
+    },
+    gettaskStatus(taskStatus) {
+      if (taskStatus == 1) return '浠诲姟涓�'
+      if (taskStatus == 0) return '鏃犱换鍔�'
+    }
   },
   mounted() {
-    var mainHeight = document.getElementById("vol-main");
-    this.mian_height = mainHeight.offsetHeight - 40 + "px";
-    var _this = this;
+    var mainHeight = document.getElementById('vol-main')
+    this.mian_height = mainHeight.offsetHeight - 40 + 'px'
+    var _this = this
     //鍔犺浇涓嬫媺閫夐」
-    this.http.get("/api/dt_AreaInfo/GetAreaInfo", {}, "鏌ヨ涓�").then((x) => {
-      console.log(x);
-      _this.slectData = x;
+    this.http.get('/api/dt_AreaInfo/GetAreaInfo', {}, '鏌ヨ涓�').then((x) => {
+      console.log(x)
+      _this.slectData = x
       //鍔犺浇绗竴涓尯鍩燂紝绗竴鎺�
-      _this.Area.areaCode = _this.slectData[0].areaCode;
+      _this.Area.areaCode = _this.slectData[0].areaCode
       //_this.scList = _this.slectData[0].tunnel;
-    });
+    })
   },
-  components: { ElButton },
-};
+  components: { ElButton }
+}
 </script>
 
 <style scoped>
@@ -330,23 +439,167 @@
   position: fixed;
   z-index: 9999;
   background-color: white;
-  border: 1px solid #ddd;
-  border-radius: 4px;
-  padding: 10px;
-  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
+  border: 1px solid #e4e7ed;
+  border-radius: 8px;
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
   pointer-events: none;
-  max-width: 250px;
+  max-width: 320px;
+  min-width: 280px;
+  overflow: hidden;
 }
 
-.location-tooltip p {
-  margin: 5px 0;
+.tooltip-content {
+  display: flex;
+  flex-direction: column;
+}
+
+.tooltip-header {
+  padding: 12px 16px;
+  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  color: white;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.tooltip-header h4 {
+  margin: 0;
+  font-size: 15px;
+  font-weight: 600;
+}
+
+.status-badge {
+  padding: 4px 10px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: 500;
+  background-color: rgba(255, 255, 255, 0.2);
+  backdrop-filter: blur(4px);
+}
+
+.status-empty {
+  background-color: rgba(144, 238, 144, 0.9);
+}
+
+.status-locked {
+  background-color: rgba(43, 179, 213, 0.9);
+}
+
+.status-locked-with-stock {
+  background-color: rgba(255, 165, 0, 0.9);
+}
+
+.status-locked-empty {
+  background-color: rgba(255, 182, 193, 0.9);
+}
+
+.status-with-stock {
+  background-color: rgba(255, 99, 71, 0.9);
+}
+
+.status-other {
+  background-color: rgba(153, 153, 153, 0.9);
+}
+
+.tooltip-body {
+  padding: 16px;
+}
+
+.info-section {
+  margin-bottom: 16px;
+}
+
+.info-section:last-child {
+  margin-bottom: 0;
+}
+
+.info-section h5 {
+  margin: 0 0 10px 0;
   font-size: 13px;
-  line-height: 1.4;
+  color: #909399;
+  text-transform: uppercase;
+  letter-spacing: 0.5px;
+  padding-bottom: 6px;
+  border-bottom: 1px solid #ebeef5;
 }
 
-.location-tooltip strong {
-  display: inline-block;
-  width: 70px;
-  color: #666;
+.info-row {
+  display: flex;
+  margin-bottom: 8px;
+  align-items: center;
 }
-</style>
\ No newline at end of file
+
+.info-row:last-child {
+  margin-bottom: 0;
+}
+
+.info-row .label {
+  flex-shrink: 0;
+  width: 100px;
+  color: #606266;
+  font-size: 13px;
+}
+
+.info-row .value {
+  flex: 1;
+  color: #303133;
+  font-size: 13px;
+  word-break: break-all;
+}
+
+.info-row .value.disabled {
+  color: #f56c6c;
+  font-weight: 500;
+  background-color: #fef0f0;
+  padding: 2px 8px;
+  border-radius: 4px;
+}
+
+.info-row .value.status-highlight {
+  font-weight: 600;
+  padding: 2px 8px;
+  border-radius: 4px;
+}
+
+.status-highlight.status-empty {
+  color: #67c23a;
+  background-color: #f0f9ff;
+}
+
+.status-highlight.status-locked {
+  color: #409eff;
+  background-color: #ecf5ff;
+}
+
+.status-highlight.status-locked-with-stock {
+  color: #e6a23c;
+  background-color: #fdf6ec;
+}
+
+.status-highlight.status-locked-empty {
+  color: #f56c6c;
+  background-color: #fef0f0;
+}
+
+.status-highlight.status-with-stock {
+  color: #67c23a;
+  background-color: #f0f9ff;
+}
+
+.status-highlight.status-other {
+  color: #909399;
+  background-color: #f4f4f5;
+}
+
+/* 娣诲姞杩囨浮鍔ㄧ敾 */
+.tooltip-fade-enter-active,
+.tooltip-fade-leave-active {
+  transition: opacity 0.2s, transform 0.2s;
+}
+
+.tooltip-fade-enter-from,
+.tooltip-fade-leave-to {
+  opacity: 0;
+  transform: translateY(5px);
+}
+</style>

--
Gitblit v1.9.3