소스 검색

新增异常停泊区

ZhangJunQuan 6 달 전
부모
커밋
017f5e8b15

+ 732 - 0
src/agv-process/AbnormalManagement.vue

@@ -0,0 +1,732 @@
+<!-- 异常停泊区管理 - 深色科技风格 -->
+<template>
+  <div class="delivery-page">
+    <!-- 背景层 -->
+    <div class="bg-layer" :style="{ backgroundImage: `url(${bgImg})` }" />
+
+    <!-- 顶部标题区域 -->
+    <div class="header-section">
+      <button class="back-btn" @click="$router.push('/home')">
+        <i class="fas fa-arrow-left" />
+        <span>返回</span>
+      </button>
+      <h1 class="page-title">异常停泊区管理</h1>
+    </div>
+
+    <!-- 主内容区域 -->
+    <main class="main-content">
+      <div class="stations-grid">
+        <div
+          v-for="station in stations" :key="station.positionNo" class="station-card"
+          :class="getStationClass(station)" @click="openStation(station)"
+        >
+          <div class="card-header">
+            <div class="card-title">{{ station.positionName }}</div>
+            <span class="status-badge" :class="getStatusClass(station.positionStatus)">
+              {{ station.positionStatus }}
+            </span>
+          </div>
+
+          <!-- 卡片内容 -->
+          <div class="card-content">
+            <!-- 占用状态 - 显示物料图标 -->
+            <div v-if="station.positionStatus === '已占用'" class="material-display">
+              <div class="material-image">
+                <i class="fas fa-car" style="font-size: 60px; color: #fff;" />
+              </div>
+            </div>
+
+            <!-- 空闲状态 -->
+            <div v-else class="empty-state">
+              <i class="fas fa-inbox" />
+              <span>暂无物料车</span>
+            </div>
+          </div>
+        </div>
+      </div>
+    </main>
+
+    <!-- 确认取走弹窗 - 科技感风格 -->
+    <div v-if="visible" class="tech-modal-overlay" @click.self="cancelTask">
+      <div class="tech-modal">
+        <div class="modal-content-row">
+          <div class="modal-text">
+            <h3>人工核对</h3>
+            <p>您是否已检查<span class="highlight">【{{ positionName }}】</span>的工装/设备,<br />确认无误并准备取走料车?</p>
+          </div>
+          <div class="modal-icon">
+            <div class="icon-box">
+              <i class="fas fa-dolly" />
+            </div>
+          </div>
+        </div>
+        
+        <!-- RFID记录详细信息 -->
+        <div v-if="selectedStation && selectedStation.agvRfidRecordLogResponseList" class="rfid-details">
+          <h4 class="details-title">RFID校验记录</h4>
+          <div v-if="selectedStation && selectedStation.positionEndNo" class="endpoint-info">
+            <h3>料车目标终点货位:</h3>
+            <span class="endpoint-value">{{ selectedStation.positionEndNo }}</span>
+          </div>
+          <div class="record-list">
+            <div v-for="(record, index) in selectedStation.agvRfidRecordLogResponseList" :key="index" class="record-item">
+              <div class="record-header">
+                <span class="record-index">闸机第{{ record.countExecute }}次校验</span>
+                <span class="record-time">{{ record.requestTime }}</span>
+                <span class="record-status" :class="record.success ? 'success' : 'failed'">
+                  {{ record.success ? '成功' : '失败' }}
+                </span>
+              </div>
+              <div class="record-message">
+                <i class="fas fa-info-circle" />
+                <span>{{ record.message }}</span>
+              </div>
+              
+              <!-- RFID设备信息 -->
+              <div v-if="record.rfidInventoryResponseList && record.rfidInventoryResponseList.length > 0" class="inventory-section">
+                <h5>RFID读取的设备:</h5>
+                <div class="inventory-list">
+                  <div v-for="(item, idx) in record.rfidInventoryResponseList" :key="idx" class="inventory-item">
+                    <span class="inventory-no">{{ item.inventoryNo }}</span>
+                    <span class="inventory-name">{{ item.inventoryName }}</span>
+                    <span class="inventory-box">{{ item.feedBoxName }}</span>
+                  </div>
+                </div>
+              </div>
+              
+              <!-- 校验设备信息 -->
+              <div v-if="record.checkInventoryResponseList && record.checkInventoryResponseList.length > 0" class="check-section">
+                <h5>需要校验的设备:</h5>
+                <div class="inventory-list">
+                  <div v-for="(item, idx) in record.checkInventoryResponseList" :key="idx" class="inventory-item">
+                    <span class="inventory-no">{{ item.inventoryNo }}</span>
+                    <span class="inventory-name">{{ item.inventoryName }}</span>
+                    <span class="inventory-box">{{ item.feedBoxName }}</span>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </div>
+        </div>
+        
+        <div class="modal-footer">
+          <button class="modal-btn cancel-btn" @click="cancelTask">取消</button>
+          <button class="modal-btn confirm-btn" @click="takeAway">确认取走</button>
+        </div>
+      </div>
+    </div>
+
+    <!-- Loading -->
+    <div v-if="loading" class="loading-overlay">
+      <div class="loading-dots">
+        <div class="dot" />
+        <div class="dot" />
+        <div class="dot" />
+      </div>
+      <span class="loading-text">加载中...</span>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, onMounted } from 'vue';
+import { showNotify } from 'vant';
+import { getLocatorAbnormalAreaStatus, takeAwayAbnormalCar } from '../api/agv.js';
+
+// 图片资源
+import bgImg from '../assets/images/bj.png';
+
+// 加载状态
+const loading = ref(false);
+
+// 还料位数据
+const stations = ref([]);
+
+const visible = ref(false);
+
+const positionName = ref('');
+const positionNo = ref('');
+const taskId = ref('');
+const selectedStation = ref(null);
+
+// 获取卡片样式类
+const getStationClass = station => {
+    if (station.positionStatus === '已占用') return 'station-occupied';
+    if (station.positionStatus === '空闲中') return 'station-idle';
+    if (station.positionStatus === '待入库') return 'station-pending';
+    return '';
+};
+
+// 获取状态徽章类
+const getStatusClass = status => {
+    if (status === '已占用') return 'status-danger';
+    if (status === '空闲中') return 'status-success';
+    if (status === '待入库') return 'status-warning';
+    return '';
+};
+
+// 打开弹窗
+const openStation = station => {
+    if (station.positionStatus === '空闲中' || station.positionStatus === '待入库') {
+        showNotify({ type: 'danger', message: '请选择占用中的异常停泊区' });
+    } else {
+        visible.value = true;
+        positionName.value = station.positionName;
+        positionNo.value = station.positionNo;
+        taskId.value = station.taskId;
+        selectedStation.value = station;
+    }
+};
+
+// 根据还料货位和料车编号生成归还单和调度任务
+const takeAway = async () => {
+
+    loading.value = true;
+    try {
+        const params = {
+            positionNo: positionNo.value,
+            taskId: taskId.value,
+        };
+        const res = await takeAwayAbnormalCar(params);
+        if (res.errorCode === 0) {
+            showNotify({ type: 'success', message: '料车取走成功' });
+            visible.value = false;
+            selectedStation.value = null;
+            await getStations();
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage || '料车取走失败' });
+        }
+    } catch (error) {
+        console.error('料车取走失败:', error);
+        showNotify({ type: 'danger', message: '料车取走失败' });
+    } finally {
+        loading.value = false;
+    }
+};
+
+// 取消任务
+const cancelTask = () => {
+    visible.value = false;
+    selectedStation.value = null;
+};
+// 获取还料位数据
+const getStations = async () => {
+    loading.value = true;
+    try {
+        const res = await getLocatorAbnormalAreaStatus();
+        if (res.errorCode === 0) {
+            if (res.datas && res.datas.length > 0) {
+                stations.value = res.datas;
+            } else {
+                stations.value = [];
+            }
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage });
+        }
+
+    } catch (error) {
+        console.error('获取异常停泊区货位数据失败:', error);
+        showNotify({ type: 'danger', message: '获取异常停泊区货位数据失败' });
+    } finally {
+        loading.value = false;
+    }
+};
+
+onMounted(() => {
+    getStations();
+});
+</script>
+
+<style scoped>
+/* ========== 基础样式 ========== */
+.delivery-page {
+  width: 100%;
+  height: 100vh;
+  position: relative;
+  font-family: 'Microsoft YaHei', sans-serif;
+  color: #fff;
+  overflow: hidden;
+  display: flex;
+  flex-direction: column;
+}
+
+/* 背景层 */
+.bg-layer {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: #041c3d;
+  background-size: cover;
+  background-position: center;
+  z-index: 0;
+}
+
+/* ========== 主内容区域 ========== */
+.main-content {
+  flex: 1;
+  overflow-y: auto;
+  padding: 20px 30px;
+  position: relative;
+  z-index: 10;
+}
+
+/* 网格布局 */
+.stations-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+  gap: 20px;
+}
+
+/* ========== 卡片样式 ========== */
+.station-card {
+  background: rgba(9, 61, 140, 0.6);
+  border: 1px solid #049FD8;
+  border-radius: 12px;
+  padding: 20px;
+  min-height: 280px;
+  display: flex;
+  flex-direction: column;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.station-card:hover {
+  transform: translateY(-4px);
+  box-shadow: 0 0 25px rgba(4, 159, 216, 0.4);
+}
+
+.station-occupied {
+  border-color: #ef4444;
+  background: rgba(239, 68, 68, 0.15);
+}
+
+.station-idle {
+  border-color: #10b981;
+  background: rgba(16, 185, 129, 0.1);
+}
+
+.station-pending {
+  border-color: #f59e0b;
+  background: rgba(245, 158, 11, 0.1);
+}
+
+/* 卡片头部 */
+.card-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 15px;
+  padding-bottom: 15px;
+  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.card-title {
+  font-size: 20px;
+  font-weight: bold;
+  color: #fff;
+}
+
+/* 状态徽章 */
+.status-badge {
+  padding: 6px 14px;
+  border-radius: 20px;
+  font-size: 14px;
+  font-weight: 600;
+}
+
+/* 卡片内容 */
+.card-content {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.status-success {
+  background: rgba(16, 185, 129, 0.2);
+  color: #34d399;
+}
+
+.status-danger {
+  background: rgba(239, 68, 68, 0.2);
+  color: #f87171;
+}
+
+.status-warning {
+  background: rgba(245, 158, 11, 0.2);
+  color: #fbbf24;
+}
+
+/* 物料显示区域 */
+.material-display {
+  width: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+}
+
+.material-image {
+  width: 150px;
+  height: 150px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  background: rgba(255, 255, 255, 0.1);
+  border-radius: 10px;
+  padding: 15px;
+}
+
+.material-image img {
+  max-width: 100%;
+  max-height: 100%;
+  object-fit: contain;
+  filter: brightness(0) invert(1);
+}
+
+/* 空状态 */
+.empty-state {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 10px;
+  color: #7ec8ff;
+}
+
+.empty-state i {
+  font-size: 48px;
+  opacity: 0.5;
+}
+
+.empty-state span {
+  font-size: 14px;
+}
+
+/* ========== 科技感弹窗样式 ========== */
+.tech-modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background: rgba(4, 28, 61, 0.9);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 2000;
+}
+
+.tech-modal {
+  background: linear-gradient(135deg, #0a3d6d 0%, #041c3d 100%);
+  border: 1px solid #049FD8;
+  border-radius: 16px;
+  width: 90%;
+  max-width: 500px;
+  box-shadow: 0 0 40px rgba(4, 159, 216, 0.3);
+}
+
+.modal-content-row {
+  padding: 30px;
+  display: flex;
+  align-items: center;
+  gap: 30px;
+}
+
+.modal-text {
+  flex: 1;
+}
+
+.endpoint-info {
+  margin-bottom: 20px;
+  text-align: left;
+  display: flex;
+  align-items: center;
+  flex-wrap: wrap;
+}
+
+.endpoint-info h3 {
+  font-size: 22px;
+  font-weight: bold;
+  color: #fff;
+  margin: 0;
+  text-align: left;
+  display: inline;
+  margin-right: 10px;
+}
+
+.endpoint-info .endpoint-value {
+  color: #00bfff;
+  font-weight: bold;
+  font-size: 24px;
+  font-family: 'Courier New', monospace;
+  margin: 0;
+  text-align: left;
+  display: inline;
+}
+
+.modal-text h3 {
+  font-size: 22px;
+  font-weight: bold;
+  color: #fff;
+  margin: 0 0 12px 0;
+}
+
+.modal-text p {
+  font-size: 16px;
+  color: #7ec8ff;
+  margin: 0;
+  line-height: 1.6;
+}
+
+.modal-text .highlight {
+  color: #00bfff;
+  font-weight: bold;
+}
+
+.modal-icon {
+  flex-shrink: 0;
+}
+
+.icon-box {
+  width: 80px;
+  height: 80px;
+  background: linear-gradient(135deg, #1e90ff 0%, #00bfff 100%);
+  border-radius: 12px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 36px;
+  color: #fff;
+  box-shadow: 0 10px 30px rgba(0, 191, 255, 0.3);
+}
+
+.modal-footer {
+  padding: 20px 30px 30px;
+  display: flex;
+  gap: 15px;
+}
+
+.modal-btn {
+  flex: 1;
+  padding: 14px 0;
+  border-radius: 8px;
+  font-size: 16px;
+  font-weight: 600;
+  cursor: pointer;
+  transition: all 0.3s;
+}
+
+.cancel-btn {
+  background: transparent;
+  border: 1px solid #2a7fff;
+  color: #7ec8ff;
+}
+
+.cancel-btn:hover {
+  background: rgba(42, 127, 255, 0.2);
+}
+
+.confirm-btn {
+  background: linear-gradient(90deg, #1e90ff 0%, #00bfff 100%);
+  border: none;
+  color: #fff;
+}
+
+.confirm-btn:hover {
+  box-shadow: 0 0 20px rgba(30, 144, 255, 0.5);
+}
+
+/* ========== RFID详细信息样式 ========== */
+.rfid-details {
+  padding: 0 30px 20px;
+  border-top: 1px solid rgba(255, 255, 255, 0.1);
+  margin-top: 20px;
+}
+
+.details-title {
+  font-size: 18px;
+  font-weight: bold;
+  color: #fff;
+  margin: 0 0 15px 0;
+  display: flex;
+  align-items: center;
+  gap: 8px;
+}
+
+.details-title::before {
+  content: '';
+  display: inline-block;
+  width: 4px;
+  height: 20px;
+  background: linear-gradient(135deg, #1e90ff 0%, #00bfff 100%);
+  border-radius: 2px;
+}
+
+.record-list {
+  max-height: 300px;
+  overflow-y: auto;
+}
+
+.record-item {
+  background: rgba(255, 255, 255, 0.05);
+  border-radius: 8px;
+  padding: 15px;
+  margin-bottom: 10px;
+  border: 1px solid rgba(255, 255, 255, 0.1);
+}
+
+.record-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  margin-bottom: 10px;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.record-index {
+  font-weight: bold;
+  color: #00bfff;
+}
+
+.record-time {
+  color: #7ec8ff;
+  font-size: 14px;
+}
+
+.record-status {
+  padding: 4px 12px;
+  border-radius: 12px;
+  font-size: 12px;
+  font-weight: bold;
+}
+
+.record-status.success {
+  background: rgba(16, 185, 129, 0.2);
+  color: #34d399;
+}
+
+.record-status.failed {
+  background: rgba(239, 68, 68, 0.2);
+  color: #f87171;
+}
+
+.record-message {
+  display: flex;
+  align-items: flex-start;
+  gap: 8px;
+  color: #7ec8ff;
+  margin-bottom: 12px;
+  font-size: 14px;
+}
+
+.record-message i {
+  color: #00bfff;
+  margin-top: 2px;
+}
+
+.inventory-section, .check-section {
+  margin-top: 12px;
+  padding-top: 12px;
+  border-top: 1px dashed rgba(255, 255, 255, 0.1);
+}
+
+.inventory-section h5, .check-section h5 {
+  font-size: 14px;
+  font-weight: bold;
+  color: #fff;
+  margin: 0 0 8px 0;
+}
+
+.inventory-list {
+  background: rgba(255, 255, 255, 0.03);
+  border-radius: 6px;
+  padding: 8px;
+}
+
+.inventory-item {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 6px 8px;
+  margin-bottom: 4px;
+  background: rgba(255, 255, 255, 0.05);
+  border-radius: 4px;
+  font-size: 13px;
+}
+
+.inventory-item:last-child {
+  margin-bottom: 0;
+}
+
+.inventory-no {
+  color: #00bfff;
+  font-family: 'Courier New', monospace;
+  flex: 2;
+}
+
+.inventory-name {
+  color: #fff;
+  flex: 3;
+  text-align: center;
+}
+
+.inventory-box {
+  color: #7ec8ff;
+  flex: 2;
+  text-align: right;
+}
+
+/* 滚动条样式 */
+.record-list::-webkit-scrollbar {
+  width: 6px;
+}
+
+.record-list::-webkit-scrollbar-track {
+  background: rgba(255, 255, 255, 0.05);
+  border-radius: 3px;
+}
+
+.record-list::-webkit-scrollbar-thumb {
+  background: rgba(0, 191, 255, 0.3);
+  border-radius: 3px;
+}
+
+.record-list::-webkit-scrollbar-thumb:hover {
+  background: rgba(0, 191, 255, 0.5);
+}
+
+/* ========== 响应式 - 横屏 ========== */
+@media screen and (orientation: landscape) {
+  .header-section {
+    padding: 10px 20px;
+  }
+
+  .page-title {
+    font-size: 22px;
+  }
+
+  .main-content {
+    padding: 10px 20px;
+  }
+
+  .station-card {
+    min-height: 200px;
+    padding: 15px;
+  }
+  
+  .rfid-details {
+    padding: 0 20px 15px;
+  }
+  
+  .modal-content-row {
+    padding: 20px;
+  }
+  
+  .modal-footer {
+    padding: 15px 20px 20px;
+  }
+}
+</style>

+ 1 - 1
src/agv-process/DeliveryManagement.vue

@@ -32,7 +32,7 @@
             <!-- 占用状态 - 显示物料图片 -->
             <div v-if="station.positionStatus === '已占用'" class="material-display">
               <div class="material-image">
-                <img src="./car.svg" :alt="station.materialName" />
+                <i class="fas fa-car" style="font-size: 60px; color: #fff;" />
               </div>
             </div>
 

+ 1 - 1
src/agv-process/ReturnManagement.vue

@@ -32,7 +32,7 @@
             <!-- 占用状态 - 显示物料图片 -->
             <div v-if="station.positionStatus === '已占用'" class="material-display">
               <div class="material-image">
-                <img src="./car.svg" :alt="station.materialName" />
+                <i class="fas fa-car" style="font-size: 60px; color: #fff;" />
               </div>
             </div>
 

+ 17 - 0
src/api/agv.js

@@ -41,6 +41,23 @@ export function takeAwaySkip(positionNo) {
     });
 }
 
+// 查询异常停泊区货位状态
+export function getLocatorAbnormalAreaStatus() {
+    return request({
+        url: '/api/LocatorResource/queryBlankAbnormalParkStatus',
+        method: 'get',
+    });
+}
+
+// 取走异常停泊区的料车
+export function takeAwayAbnormalCar(params) {
+    return request({
+        url: '/api/LocatorResource/takeAwayAbnormalCar',
+        method: 'post',
+        data: params,
+    });
+}
+
 // 查询工装设备
 export function findDeviceByCondition(params) {
     return request({

+ 8 - 0
src/api/login.js

@@ -23,4 +23,12 @@ export function queryRemindTime() {
         url: '/api/RemindPasswordTimeResource/queryRemindTime',
         method: 'get',
     });
+}
+
+// 人员登录时推送尚未处理的异常停泊区任务
+export function queryAwayAbnormalCar() {
+    return request({
+        url: '/api/LocatorResource/checkAwayAbnormalCar',
+        method: 'get',
+    });
 }

+ 136 - 1
src/login/UserHome.vue

@@ -12,7 +12,7 @@
     <!-- 顶部标题区域 - 全宽 -->
     <div class="header-section">
       <img :src="headerImg" alt="" class="header-bg" />
-      <h1 class="title">智能仓储管理系统</h1>
+      <h1 class="title">无人线边库</h1>
       <!-- 左侧用户信息 -->
       <div class="user-badge">
         <i class="fas fa-user user-icon" />
@@ -65,6 +65,20 @@
           </div>
         </div>
       </div>
+      
+      <!-- 异常停泊区提示卡片 -->
+      <div v-if="showAbnormalCard" class="abnormal-card">
+        <div class="abnormal-card-header">
+          <h3>异常停泊区提示</h3>
+        </div>
+        <div class="abnormal-card-content">
+          <p>{{ abnormalMessage }}</p>
+        </div>
+        <div class="abnormal-card-footer">
+          <button class="abnormal-btn know-btn" @click="closeAbnormalCard">我已知晓</button>
+          <button class="abnormal-btn go-btn" @click="goToAbnormalArea">去看看</button>
+        </div>
+      </div>
 
       <!-- 操作按钮区域 -->
       <div class="buttons-section">
@@ -184,6 +198,11 @@ onMounted(() => {
 
     // 添加点击外部关闭下拉菜单的事件监听
     document.addEventListener('click', handleClickOutside);
+    
+    // 延时1秒检查异常信息,确保异常信息已经存储到localStorage
+    setTimeout(() => {
+        checkAbnormalMessage();
+    }, 1000);
 });
 
 onUnmounted(() => {
@@ -218,6 +237,32 @@ const statistics = ref([
     },
 ]);
 
+// 异常停泊区提示卡片
+const showAbnormalCard = ref(false);
+const abnormalMessage = ref('');
+
+// 检查异常信息
+const checkAbnormalMessage = () => {
+    const message = localStorage.getItem('abnormalCarMessage');
+    if (message) {
+        abnormalMessage.value = message;
+        showAbnormalCard.value = true;
+    }
+};
+
+// 关闭异常提示卡片
+const closeAbnormalCard = () => {
+    showAbnormalCard.value = false;
+    localStorage.removeItem('abnormalCarMessage');
+};
+
+// 跳转到异常停泊区页面
+const goToAbnormalArea = () => {
+    showAbnormalCard.value = false;
+    localStorage.removeItem('abnormalCarMessage');
+    router.push('/abnormal-anchorage-area');
+};
+
 // 外侧屏幕操作按钮
 const outButtons = reactive([
     { label: '领料', action: 'materialOut' },
@@ -676,6 +721,96 @@ const getInfo = async () => {
   margin-right: 4px;
 }
 
+/* 异常停泊区提示卡片样式 */
+.abnormal-card {
+  position: absolute;
+  top: 150px; 
+  width: 1030px;
+  background: linear-gradient(135deg, rgba(9, 45, 82, 0.95) 0%, rgba(5, 30, 60, 0.95) 100%);
+  border: 1px solid rgba(30, 144, 255, 0.5);
+  border-radius: 12px;
+  padding: 20px;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3), 0 0 20px rgba(30, 144, 255, 0.2);
+  animation: fadeInUp 0.5s ease-out;
+  z-index: 10;
+  box-sizing: border-box;
+}
+
+.abnormal-card-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16px;
+}
+
+.abnormal-card-header h3 {
+  color: #fff;
+  font-size: 20px;
+  font-weight: 600;
+  margin: 0;
+  text-shadow: 0 0 10px rgba(30, 144, 255, 0.5);
+}
+
+.abnormal-card-content {
+  margin-bottom: 24px;
+}
+
+.abnormal-card-content p {
+  color: #fff;
+  font-size: 16px;
+  line-height: 1.5;
+  margin: 0;
+}
+
+.abnormal-card-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 12px;
+}
+
+.abnormal-btn {
+  padding: 10px 20px;
+  border-radius: 6px;
+  font-size: 14px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  border: none;
+  outline: none;
+}
+
+.know-btn {
+  background: rgba(30, 144, 255, 0.2);
+  color: #7ec8ff;
+  border: 1px solid rgba(30, 144, 255, 0.3);
+}
+
+.know-btn:hover {
+  background: rgba(30, 144, 255, 0.3);
+  color: #00bfff;
+}
+
+.go-btn {
+  background: linear-gradient(90deg, #1e90ff 0%, #00bfff 100%);
+  color: #fff;
+}
+
+.go-btn:hover {
+  transform: translateY(-1px);
+  box-shadow: 0 5px 15px rgba(30, 144, 255, 0.4);
+}
+
+@keyframes fadeInUp {
+  from {
+    opacity: 0;
+    transform: translateY(30px);
+  }
+  to {
+    opacity: 1;
+    transform: translateY(0);
+  }
+}
+
 /* ========== 操作按钮区域 ========== */
 .buttons-section {
   display: flex;

+ 24 - 1
src/login/UserLogin.vue

@@ -97,7 +97,7 @@
 <script setup>
 import { ref, onMounted } from 'vue';
 import { useRouter, useRoute } from 'vue-router';
-import { loginApi, queryRemindTime } from '../api/login.js';
+import { loginApi, queryRemindTime,queryAwayAbnormalCar } from '../api/login.js';
 import { getFormattedDateTime } from '../common/Common.js';
 import { showNotify } from 'vant';
 
@@ -166,6 +166,7 @@ const handleLogin = async () => {
                 router.push('/home');
             }
             checkPasswordReminder();
+            checkAwayAbnormalCar();
         } else {
             showNotify({ type: 'danger', message: res.errorMessage });
         }
@@ -194,6 +195,28 @@ const checkPasswordReminder = async () => {
     }
 };
 
+const checkAwayAbnormalCar = async () => {
+    try {
+        const res = await queryAwayAbnormalCar();
+        if (res.errorCode !== 0) {
+            if (res.errorCode === -1) {
+                // 将异常信息存储到localStorage,以便在UserHome.vue中显示
+                localStorage.setItem('abnormalCarMessage', res.errorMessage);
+            } else {
+                // 如果错误码不是-1,说明没有异常信息,清除localStorage中的异常信息
+                localStorage.removeItem('abnormalCarMessage');
+            }
+        } else {
+            // 如果接口返回正常,清除localStorage中的异常信息
+            localStorage.removeItem('abnormalCarMessage');
+        }
+    } catch (error) {
+        console.error('提示检查用户异常停泊区Api调用失败', error);
+        // 存储错误信息到localStorage
+        localStorage.setItem('abnormalCarMessage', '提示检查用户异常停泊区Api调用失败');
+    }
+};
+
 const handleFingerprintLogin = () => {
     const isOut = localStorage.getItem('isOut') === 'true';
     if (isOut) {

+ 4 - 0
src/router/routes.js

@@ -25,6 +25,9 @@ const ReturnManagement = () => import('../agv-process/ReturnManagement.vue');
 // 送料区管理
 const DeliveryManagement = () => import('../agv-process/DeliveryManagement.vue');
 
+// 异常停泊区管理
+const AbnormalManagement = () => import('../agv-process/AbnormalManagement.vue');
+
 // 成品入库
 const FinishProductIn = () => import('../finishProduct/FinishProductIn.vue');
 
@@ -54,6 +57,7 @@ const routes = [
     { path: '/in-confirm', component: InConfirm, meta: { title: '入库确认' } },
     { path: '/return-management', component: ReturnManagement, meta: { title: '还料区管理' } },
     { path: '/delivery-management', component: DeliveryManagement, meta: { title: '送料区管理' } },
+    { path: '/abnormal-anchorage-area', component: AbnormalManagement, meta: { title: '异常停泊区管理' } },
     { path: '/finish-product-in', component: FinishProductIn, meta: { title: '成品入库' } },
     { path: '/finish-product-out', component: FinishProductOut, meta: { title: '成品出库管理' } },
     { path: '/fingerprint-enroll', component: FingerprintEnroll, meta: { title: '指纹录入' } },