Эх сурвалжийг харах

删除拣货,领料拣货2合一

ZhangJunQuan 5 сар өмнө
parent
commit
a277bd8cc3

+ 8 - 0
src/api/stock.js

@@ -15,3 +15,11 @@ export function queryIdleLocator() {
         method: 'get',
     });
 }
+
+// 查询送料区全部货位
+export function queryFeedAreaStatus() {
+    return request({
+        url: '/api/LocatorResource/queryFeedAreaStatus',
+        method: 'get',
+    });
+}

+ 9 - 0
src/api/stockOut.js

@@ -85,6 +85,15 @@ export function createStockOut(params) {
     });
 }
 
+// 闸机外侧选择物料明细生成领料单、领料明细、出库单
+export function generatePickPaper(params) {
+    return request({
+        url: '/api/StockOutResource/generatePickPaper',
+        method: 'post',
+        data: params,
+    });
+}
+
 // 闸机内侧人工领料出库验证工装设备是否一致
 export function cfStockOut(params) {
     return request({

+ 1 - 1
src/hardware/Light.js

@@ -39,7 +39,7 @@ const LIGHT_OPEN = {
     'lightEffect': 'FLOW_RESERVE', // 反向流动灯效果
     'flowParams': {
         'color': 0xFFFFFF,      // 流动灯颜色 (白色: #FFFFFF)
-        'repeat': false,        // 是否重复播放 (false: 单次播放)
+        'repeat': true,        // 是否重复播放 (false: 单次播放)
         'flowInterval': 1,      // 流动间隔 (数值越小流动越快)
     },
 };

+ 7 - 6
src/login/UserHome.vue

@@ -449,14 +449,14 @@ const statistics = ref([
         changeText: '',
     },
     {
-        title: '今日入库',
+        title: '今日归还',
         value: '-',
         bg: cardIcon2,
         trend: 1,
         changeText: '',
     },
     {
-        title: '今日出库',
+        title: '今日借用',
         value: '-',
         bg: cardIcon3,
         trend: -1,
@@ -494,7 +494,7 @@ const goToAbnormalArea = () => {
 const outButtons = reactive([
     // { label: '领料', action: 'materialOut' },
     { label: '领料', action: 'materialReturn' },
-    { label: '入库', action: 'materialIn' },
+    { label: '归还', action: 'materialIn' },
     // { label: 'AGV RFID\n校验', action: 'agvRfidRecognition' },
     { label: '成品\n入库', action: 'finishedProductIn' },
     { label: '大件\n运输', action: 'bigProductTransport' },
@@ -596,7 +596,8 @@ const handleAction = action => {
         router.push('/stock-requisition');
         break;
     case 'materialReturn':
-        pickingConfirmVisible.value = true;
+        // pickingConfirmVisible.value = true;
+        router.push('/apply-material');
         break;
     case 'materialIn':
         router.push('/in-confirm');
@@ -663,8 +664,8 @@ const getInfo = async () => {
             statistics.value[2].value = stockOutQuantity;
             if (cfInAndOutResponses && cfInAndOutResponses.length > 0) {
                 config.value.data = cfInAndOutResponses.map(item => {
-                    const type = item.type === '出库' ? '<span style="color:#FF4D4F;">出库</span>' :
-                        '<span style="color:#25EC87;">入库</span>';
+                    const type = item.type === '借用' ? '<span style="color:#FF4D4F;">借用</span>' :
+                        '<span style="color:#25EC87;">归还</span>';
                     return [item.name, item.no, item.inventoryType, type, item.time, item.userName];
                 });
             }

+ 4 - 1
src/router/routes.js

@@ -4,6 +4,8 @@ const UserLogin = () => import('../login/UserLogin.vue');
 const UserHome = () => import('../login/UserHome.vue');
 // 领料管理
 const StockRequisition = () => import('../stock/StockRequisition.vue');
+// 领料
+const ApplyMaterial = () => import('../stock/ApplyMaterial.vue');
 // 常用领料
 const RegularRequisition = () => import('../stock/RegularRequisition.vue');
 // 领料车
@@ -60,6 +62,7 @@ const routes = [
     // { path: '/returned-leave', component: ReturnedLeave, meta: { title: '还料离开' } },    
     { path: '/stock-picking-car', component: StockPickingCar, meta: { title: '领料车' } },
     { path: '/stock-requisition', component: StockRequisition, meta: { title: '领料管理' } },
+    { path: '/apply-material', component: ApplyMaterial, meta: { title: '领料' } },
     { path: '/regular-requisition', component: RegularRequisition, meta: { title: '常用领料' } },
     { path: '/agv-rfid-recognition', component: AgvRfidRecognition, meta: { title: 'AGV RFID识别' } },
     { path: '/in-confirm', component: InConfirm, meta: { title: '入库确认' } },
@@ -73,4 +76,4 @@ const routes = [
     { path: '/feeding-area', component: FeedingArea, meta: { title: '上架区' } },
     { path: '/control-gate', component: ControlGate, meta: { title: '闸机控制' } },
 ];
-export default routes;
+export default routes;

+ 1798 - 0
src/stock/ApplyMaterial.vue

@@ -0,0 +1,1798 @@
+<!-- 领料管理 - 智能仓储风格 -->
+<template>
+  <div class="stock-requisition-page">
+    <!-- 背景层 -->
+    <div class="bg-layer" :style="{ backgroundImage: `url(${bgImg})` }" />
+
+    <!-- 顶部标题区域 -->
+    <div class="header-section">
+      <button class="logout-btn" @click="goHome">
+        <i class="fas fa-home" />
+        <span>主页</span>
+      </button>
+      <h1 class="page-title">领料</h1>
+      <!-- 右侧操作按钮 -->
+      <div class="header-actions">
+        <button class="action-btn regular-btn" @click="openRegularRequisition">
+          <i class="fas fa-star" />
+          <span>常用领料</span>
+        </button>
+        <button class="action-btn cart-btn" @click="openStockOutCar">
+          <van-badge :content="count" :show-zero="true">
+            <i class="fas fa-shopping-cart" />
+          </van-badge>
+          <span>领料车</span>
+        </button>
+      </div>
+    </div>
+
+    <!-- 主内容区域 -->
+    <main class="main-content">
+      <!-- 筛选区域 -->
+      <div class="filter-section">
+        <div class="filter-row">
+          <div class="filter-item">
+            <label class="filter-label">类型</label>
+            <v-select
+              v-model="inventoryType" :options="inventoryTypeList" :reduce="item => item.value" label="label"
+              placeholder="请输入类型" :clearable="true" :filterable="true" class="filter-select dark-select"
+              @update:model-value="getDatas"
+            >
+              <template #no-options>
+                <span>无该选项数据</span>
+              </template>
+            </v-select>
+          </div>
+          <div class="filter-item">
+            <label class="filter-label">名称</label>
+            <input
+              v-model="inventoryName" type="text" placeholder="请输入名称" class="filter-input dark-input"
+              @keyup.enter="getDatas"
+            />
+          </div>
+          <!-- <div class="filter-item">
+            <label class="filter-label">编号</label>
+            <input
+              v-model="inventoryNo"
+              type="text"
+              placeholder="请输入编号"
+              class="filter-input dark-input"
+              @keyup.enter="getDatas"
+            />
+          </div>
+          <div class="filter-item">
+            <label class="filter-label">仓库</label>
+            <v-select
+              v-model="warehouseId"
+              :options="warehouseList"
+              :reduce="item => item.id"
+              label="name"
+              placeholder="选择仓库"
+              :clearable="true"
+              :filterable="true"
+              class="filter-select dark-select"
+              @update:model-value="getDatas"
+            >
+              <template #no-options>
+                <span>无该选项数据</span>
+              </template>
+            </v-select>
+          </div> -->
+          <div class="filter-buttons">
+            <button class="search-btn" @click="getDatas">
+              搜索
+            </button>
+            <button class="reset-btn" @click="handleReset">
+              重置
+            </button>
+          </div>
+        </div>
+      </div>
+
+      <!-- 全选控制栏 -->
+      <div v-if="stockRequisitionList.length > 0" class="select-all-bar">
+        <van-checkbox :model-value="isAllSelected" @update:model-value="toggleSelectAll">
+          <span class="select-all-text">全选当前页(已选 {{ selectedIds.length }} 项)</span>
+        </van-checkbox>
+      </div>
+
+      <!-- 卡片网格区域 -->
+      <div ref="cardGridWrapper" class="card-grid-wrapper" @scroll="handleScroll">
+        <!-- 空状态 -->
+        <div v-if="stockRequisitionList.length === 0 && !loading" class="empty-state">
+          <i class="fas fa-inbox empty-icon" />
+          <p>暂无数据</p>
+        </div>
+
+        <!-- 卡片网格 -->
+        <div v-else class="card-grid">
+          <div
+            v-for="(item, index) in stockRequisitionList" :key="item.id || index" class="inventory-card"
+            :class="{ 'selected': selectedIds.includes(item.id) }" @click="toggleSelect(item.id)"
+          >
+            <!-- 卡片序号 -->
+            <div class="card-index">{{ index + 1 }}</div>
+
+            <!-- 图片区域 -->
+            <div class="card-image">
+              <img v-if="item.imageUrl" :src="item.imageUrl" alt="设备图片" />
+              <div v-else class="image-placeholder">
+                <i :class="getInventoryIcon(item.inventoryType)" />
+              </div>
+            </div>
+
+            <!-- 信息区域 -->
+            <div class="card-info">
+              <div class="info-row">
+                <span class="info-label">名称:</span>
+                <span class="info-value">{{ item.inventoryName || '-' }}</span>
+              </div>
+              <div class="info-row">
+                <span class="info-label">编号:</span>
+                <span class="info-value">{{ item.inventoryNo || '-' }}</span>
+              </div>
+              <div class="info-row location-row">
+                <i class="fas fa-map-marker-alt location-icon" />
+                <span class="info-value">{{ item.inventoryActulPosition || item.inventoryPosition || '-' }} / {{
+                  item.inventoryWarehouse || '-' }}</span>
+              </div>
+              <!-- 配送类型徽章 -->
+              <div class="info-row delivery-type-row">
+                <span class="delivery-type-badge" :class="getDeliveryTypeBadgeClass(item.deliveryType)">
+                  {{ item.deliveryType || '未指定' }}
+                </span>
+              </div>
+            </div>
+
+            <!-- 配送选择区域(固定高度,始终存在) -->
+            <div class="card-delivery-section" @click.stop>
+              <!-- 选中时显示配送选择 -->
+              <template v-if="selectedIds.includes(item.id)">
+                <div class="delivery-row">
+                  <span class="delivery-label">配送:</span>
+                  <div class="delivery-method-buttons">
+                    <button
+                      :class="['delivery-btn', { 'active': item.deliveryMethod === 'AGV_Delivery', 'disabled': item.deliveryType === '人工配送' }]"
+                      :disabled="item.deliveryType === '人工配送'" @click="changeDeliveryMethod(item, 'AGV_Delivery')"
+                    >
+                      <i class="fas fa-robot" /> AGV
+                    </button>
+                    <button
+                      :class="['delivery-btn', { 'active': item.deliveryMethod === 'Manual_Delivery', 'disabled': item.deliveryType === '强制AGV配送' }]"
+                      :disabled="item.deliveryType === '强制AGV配送'"
+                      @click="changeDeliveryMethod(item, 'Manual_Delivery')"
+                    >
+                      <i class="fas fa-user" /> 人工
+                    </button>
+                  </div>
+                </div>
+                <!-- AGV配送位置选择 -->
+                <div class="delivery-location-row">
+                  <span class="delivery-label">位置:</span>
+                  <v-select
+                    v-if="item.deliveryMethod === 'AGV_Delivery'" v-model="item.selectedLocation"
+                    :options="locator" :clearable="false" class="location-select dark-select" :append-to-body="true"
+                    :calculate-position="withPopper"
+                  />
+                  <span v-else style="font-size: 16px;">人工配送</span>
+                </div>
+              </template>
+              <!-- 未选中时显示占位 -->
+              <template v-else>
+                <div class="delivery-placeholder">
+                  <span>点击选择后设置配送方式</span>
+                </div>
+              </template>
+            </div>
+
+            <!-- 选中状态指示 -->
+            <div v-if="selectedIds.includes(item.id)" class="selected-indicator">
+              <i class="fas fa-check" />
+            </div>
+          </div>
+        </div>
+
+        <!-- 拣货完成确认弹窗 - 科技感风格 -->
+        <div v-if="completeModalVisible" class="tech-modal-overlay" @click.self="handleCompleteCancel">
+          <div class="tech-modal success-modal">
+            <div class="modal-content-row">
+              <div class="modal-text">
+                <h3>领料完成</h3>
+                <p>领料申请已完成,配送任务已创建!<br />请确认是否返回主页?</p>
+              </div>
+              <div class="modal-icon">
+                <div class="icon-box success-box">
+                  <i class="fas fa-truck-loading" />
+                  <i class="fas fa-check check-icon" />
+                </div>
+              </div>
+            </div>
+            <div class="modal-footer">
+              <button class="modal-btn cancel-btn" @click="handleCompleteCancel">取消</button>
+              <button class="modal-btn confirm-btn" @click="handleCompleteConfirm">确认</button>
+            </div>
+          </div>
+        </div>
+
+        <!-- 加载更多提示 -->
+        <div v-if="loadingMore" class="loading-more">
+          <div class="loading-more-spinner" />
+          <span>加载中...</span>
+        </div>
+        <div v-else-if="noMoreData && stockRequisitionList.length > 0" class="no-more-data">
+          <span>没有更多数据了</span>
+        </div>
+      </div>
+    </main>
+
+    <!-- 底部操作按钮 -->
+    <div class="bottom-actions">
+      <button class="submit-btn" :disabled="selectedIds.length === 0" @click="submitStock">
+        加入领料车
+      </button>
+      <button class="pick-btn" :disabled="selectedIds.length === 0" @click="handleComplete">
+        领料 ({{ selectedIds.length }})
+      </button>
+    </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, nextTick, computed } from 'vue';
+import { useRouter } from 'vue-router';
+import { showNotify } from 'vant';
+import vSelect from 'vue-select';
+import 'vue-select/dist/vue-select.css';
+
+// 图片资源
+import bgImg from '../assets/images/bj.png';
+
+import { getWarehouseList, queryFeedAreaStatus } from '../api/stock.js';
+import { findInventory, createStockOutPrepareLine, queryPickingCarNumber, generatePickPaper } from '../api/stockOut.js';
+import { gateController } from '../hardware/GateOperate.js';
+
+const router = useRouter();
+
+// 滚动容器ref
+const cardGridWrapper = ref(null);
+
+const warehouseId = ref(undefined);
+const inventoryName = ref('');
+const inventoryNo = ref('');
+const inventoryType = ref(undefined);
+const count = ref(0);
+const warehouseList = ref([]);
+const stockRequisitionList = ref([]);
+const selectedIds = ref([]);
+
+const loading = ref(false);
+const loadingMore = ref(false);
+const noMoreData = ref(false);
+
+const selectedRows = ref([]);
+
+const inventoryTypeList = ref([
+    { value: 'Clamp', label: '工装' },
+    { value: 'Instrument', label: '设备' },
+    // { value: 'FinishProduct', label: '成品' },
+]);
+
+// 无限滚动参数
+const pageSize = 20;
+const currentStart = ref(0);
+const total = ref(0);
+
+// 可配送位置
+const locator = ref([]);
+
+// 根据 deliveryType 初始化配送方式
+const initDeliveryMethod = item => {
+    if (item.deliveryType === '人工配送') {
+    // 人工配送:只能人工,不可切换
+        item.deliveryMethod = 'Manual_Delivery';
+        item.selectedLocation = '';
+    } else if (item.deliveryType === '强制AGV配送') {
+    // 强制AGV配送:只能AGV,不可切换为人工
+        item.deliveryMethod = 'AGV_Delivery';
+        if (!item.selectedLocation) {
+            item.selectedLocation = '';
+        }
+    } else if (item.deliveryType === '可选AGV配送') {
+    // 可选AGV配送:默认人工,可切换为AGV
+        item.deliveryMethod = item.deliveryMethod || 'Manual_Delivery';
+        item.selectedLocation = item.selectedLocation || '';
+    }
+};
+
+// 切换选择
+const toggleSelect = id => {
+    const index = selectedIds.value.indexOf(id);
+    if (index > -1) {
+        selectedIds.value.splice(index, 1);
+        // 同时移除 selectedRows 中对应的项
+        selectedRows.value = selectedRows.value.filter(item => item.id !== id);
+    } else {
+        selectedIds.value.push(id);
+        // 添加到 selectedRows 并初始化配送字段
+        const item = stockRequisitionList.value.find(item => item.id === id);
+        if (item) {
+            // 初始化配送字段
+            initDeliveryMethod(item);
+            selectedRows.value.push(item);
+        }
+    }
+};
+
+// 打开常用领料
+const openRegularRequisition = () => {
+    router.push('/regular-requisition');
+};
+
+// 打开领料车
+const openStockOutCar = () => {
+    router.push('/stock-picking-car?isRegular=false');
+};
+
+// 计算是否全选
+const isAllSelected = computed(() => {
+    if (stockRequisitionList.value.length === 0) return false;
+    return stockRequisitionList.value.every(item => selectedIds.value.includes(item.id));
+});
+
+
+// 全选/取消全选
+const toggleSelectAll = checked => {
+    if (checked) {
+    // 全选当前列表所有项
+        stockRequisitionList.value.forEach(item => {
+            if (!selectedIds.value.includes(item.id)) {
+                selectedIds.value.push(item.id);
+                initDeliveryMethod(item);
+                selectedRows.value.push(item);
+            }
+        });
+    } else {
+    // 取消全选当前列表
+        const currentListIds = stockRequisitionList.value.map(item => item.id);
+        selectedIds.value = selectedIds.value.filter(id => !currentListIds.includes(id));
+        selectedRows.value = selectedRows.value.filter(row => !currentListIds.includes(row.id));
+    }
+};
+
+// 获取设备类型图标
+const getInventoryIcon = type => {
+    const iconMap = {
+        '工装': 'fas fa-cube',
+        '设备': 'fas fa-cogs',
+        '成品': 'fas fa-box',
+    };
+    return iconMap[type] || 'fas fa-cube';
+};
+
+// 获取配送类型徽章样式
+const getDeliveryTypeBadgeClass = deliveryType => {
+    const classMap = {
+        '人工配送': 'badge-manual',
+        '强制AGV配送': 'badge-agv-force',
+        '可选AGV配送': 'badge-agv-optional',
+    };
+    return classMap[deliveryType] || '';
+};
+
+// 滚动事件处理 - 无限滚动
+const handleScroll = e => {
+    const container = e.target;
+    const { scrollTop, scrollHeight, clientHeight } = container;
+
+    // 距离底部100px时触发加载
+    if (scrollHeight - scrollTop - clientHeight < 100) {
+        loadMore();
+    }
+};
+
+// 检查容器是否需要继续加载(当内容不足以产生滚动条时自动加载更多)
+const checkAndLoadMore = async () => {
+    await nextTick();
+    const container = cardGridWrapper.value;
+    if (!container) return;
+
+    // 如果内容高度不足以产生滚动条,且还有更多数据,继续加载
+    if (container.scrollHeight <= container.clientHeight && !noMoreData.value && !loadingMore.value) {
+        loadMore();
+    }
+};
+
+// 是否正在执行初次加载(防止重复请求)
+let isInitialLoading = false;
+
+// 完成弹窗控制
+const completeModalVisible = ref(false);
+
+// 加载更多数据
+const loadMore = async () => {
+    // 增加 isInitialLoading 检查,防止初次加载时触发 loadMore
+    if (loadingMore.value || noMoreData.value || loading.value || isInitialLoading) return;
+
+    loadingMore.value = true;
+
+    const params = {
+        inventoryName: inventoryName.value,
+        inventoryNo: inventoryNo.value,
+        inventoryType: inventoryType.value,
+        warehouseId: warehouseId.value,
+        range: {
+            start: currentStart.value,
+            length: pageSize,
+        },
+    };
+
+    try {
+        const res = await findInventory(params);
+        if (res.errorCode == 0) {
+            if (res.datas && res.datas.length > 0) {
+                stockRequisitionList.value = [...stockRequisitionList.value, ...res.datas];
+                currentStart.value += res.datas.length;
+                total.value = res.total;
+
+                // 检查是否还有更多数据
+                if (stockRequisitionList.value.length >= res.total) {
+                    noMoreData.value = true;
+                } else {
+                    // 加载完成后检查是否需要继续加载
+                    checkAndLoadMore();
+                }
+            } else {
+                noMoreData.value = true;
+            }
+        }
+    } catch (error) {
+        console.error('加载更多数据失败:', error);
+    } finally {
+        loadingMore.value = false;
+    }
+};
+
+// 配送方式变更处理
+const changeDeliveryMethod = (item, method) => {
+    item.deliveryMethod = method;
+    // 当选择人工配送时,清空配送位置
+    if (method === 'Manual_Delivery') {
+        item.selectedLocation = '';
+    }
+    console.log('配送方式变更:', item.inventoryName, item.deliveryMethod);
+};
+
+// 获取送料区货位
+const queryFeedArea = async () => {
+    try {
+        const res = await queryFeedAreaStatus();
+
+        if (res.errorCode === 0) {
+            if (res.datas && res.datas.length > 0) {
+                locator.value = res.datas.map(item => {
+                    return {
+                        label: item.positionName,
+                        value: item.positionNo,
+                    };
+                });
+            } else {
+                locator.value = [];
+            }
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage });
+        }
+    } catch (error) {
+        console.error('获取送料区货位API调用失败:', error);
+        showNotify({ type: 'danger', message: '获取送料区货位API调用失败' });
+    }
+};
+
+// 初次加载物料数据
+const getStockRequisitionList = async () => {
+    // 防止重复调用
+    if (isInitialLoading) return;
+    isInitialLoading = true;
+
+    loading.value = true;
+    currentStart.value = 0;
+    noMoreData.value = false;
+    // 立即清空列表,防止数据重复
+    stockRequisitionList.value = [];
+
+    const params = {
+        inventoryName: inventoryName.value,
+        inventoryNo: inventoryNo.value,
+        inventoryType: inventoryType.value,
+        warehouseId: warehouseId.value,
+        range: {
+            start: 0,
+            length: pageSize,
+        },
+    };
+
+    try {
+        const res = await findInventory(params);
+        if (res.errorCode == 0) {
+            if (res.datas && res.datas.length > 0) {
+                stockRequisitionList.value = res.datas;
+                currentStart.value = res.datas.length;
+                total.value = res.total;
+
+                // 保留已选中物料的配送方式设置
+                stockRequisitionList.value = stockRequisitionList.value.map(item => {
+                    // 查找该物料是否在已选中列表中
+                    const selectedItem = selectedRows.value.find(selected => selected.id === item.id);
+
+                    if (selectedItem) {
+                        // 如果已选中,保留其配送方式和位置设置,并更新 selectedRows 中的引用
+                        const updatedItem = {
+                            ...item,
+                            deliveryMethod: selectedItem.deliveryMethod,
+                            selectedLocation: selectedItem.selectedLocation,
+                        };
+
+                        // 更新 selectedRows 中的对应项,保持引用同步
+                        const index = selectedRows.value.findIndex(row => row.id === item.id);
+                        if (index !== -1) {
+                            selectedRows.value[index] = updatedItem;
+                        }
+
+                        return updatedItem;
+                    } else {
+                        // 未选中,初始化为空
+                        return {
+                            ...item,
+                            deliveryMethod: '',
+                            selectedLocation: '',
+                        };
+                    }
+                });
+                if (stockRequisitionList.value.length >= res.total) {
+                    noMoreData.value = true;
+                } else {
+                    // 初次加载完成后检查是否需要继续加载
+                    checkAndLoadMore();
+                }
+            } else {
+                stockRequisitionList.value = [];
+                total.value = 0;
+                noMoreData.value = true;
+            }
+        }
+    } catch (error) {
+        console.error('获取物料数据失败:', error);
+        showNotify({ type: 'danger', message: '获取库存数据失败' });
+    } finally {
+        loading.value = false;
+        isInitialLoading = false;
+    }
+};
+
+// 获取仓库列表
+const getWarehouses = async () => {
+    try {
+        const res = await getWarehouseList();
+        if (res.errorCode == 0) {
+            if (res.datas && res.datas.length > 0) {
+                warehouseList.value = res.datas;
+            } else {
+                warehouseList.value = [];
+            }
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage });
+        }
+
+    } catch (error) {
+        console.error('获取仓库数据失败:', error);
+        showNotify({ type: 'danger', message: '获取仓库数据失败' });
+    }
+};
+
+// 返回主页
+const goHome = () => {
+    router.push('/home');
+};
+
+// 重置筛选条件
+const handleReset = () => {
+    warehouseId.value = undefined;
+    inventoryName.value = '';
+    inventoryNo.value = '';
+    inventoryType.value = undefined;
+    getDatas();
+};
+
+// 查询(重新搜索时重置列表)
+const getDatas = async () => {
+    // 滚动到顶部
+    if (cardGridWrapper.value) {
+        cardGridWrapper.value.scrollTop = 0;
+    }
+    await getStockRequisitionList();
+};
+
+// 提交中状态(与列表加载分开管理)
+const submitting = ref(false);
+
+// 加入领料车
+const submitStock = async () => {
+    if (selectedIds.value.length == 0) {
+        showNotify({ type: 'danger', message: '请至少选择一个物料' });
+        return;
+    }
+    submitting.value = true;
+    const params = {
+        inventoryIds: selectedIds.value,
+    };
+
+    try {
+        const res = await createStockOutPrepareLine(params);
+        if (res.errorCode == 0) {
+            selectedIds.value = [];
+            showNotify({ type: 'success', message: '已添加到领料车' });
+            // 先等待提交完成,再刷新列表
+            await getDatas();
+            queryPickingCarCount();
+        }
+    } catch (error) {
+        console.error('添加领料车失败:', error);
+        showNotify({ type: 'danger', message: '添加到领料车失败' });
+    } finally {
+        submitting.value = false;
+    }
+};
+
+// 领料申请,直接验证并提交
+const handleComplete = async () => {
+    if (selectedIds.value.length === 0) {
+        showNotify({ type: 'danger', message: '请至少选择一个物料' });
+        return;
+    }
+
+    // 验证选择了 AGV 配送的物料是否都选择了配送位置
+    const agvItems = selectedRows.value.filter(item => item.deliveryMethod === 'AGV_Delivery');
+    const hasEmptyLocation = agvItems.some(item => !item.selectedLocation);
+
+    if (hasEmptyLocation) {
+        showNotify({ type: 'danger', message: '请为所有 AGV 配送的物料选择配送位置' });
+        return;
+    }
+
+    const params = [];
+    selectedRows.value.forEach(item => {
+        params.push({
+            inventoryId: item.id,
+            deliveryMethod: item.deliveryMethod,
+            positionEndNo: item.selectedLocation.value,
+        });
+    });
+
+    console.log('提交参数:', params);
+    const res = await generatePickPaper(params);
+    if (res.errorCode == 0) {
+        showNotify({ type: 'success', message: '领料申请成功' });
+        selectedIds.value = [];
+        selectedRows.value = [];
+        // 显示完成确认弹窗
+        gateController('SHOTOPEN');
+        router.push('/');
+    } else {
+        showNotify({ type: 'danger', message: res.errorMessage });
+    }
+};
+
+// 处理完成确认
+const handleCompleteConfirm = () => {
+    // 调用开门操作
+    gateController('SHOTOPEN');
+    completeModalVisible.value = false;
+    router.push('/');
+    // showToast('领料申请已完成,配送任务已创建!');
+};
+
+// 处理完成取消
+const handleCompleteCancel = () => {
+    completeModalVisible.value = false;
+    // 留在当前页面,刷新列表
+    getDatas();
+};
+
+/**
+     * 查询领料车中的数量
+     */
+const queryPickingCarCount = async () => {
+    try {
+        const res = await queryPickingCarNumber();
+        if (res.errorCode == 0) {
+            if (res.data) {
+                count.value = res.data;
+            } else {
+                count.value = 0;
+            }
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage });
+        }
+
+    } catch (error) {
+        console.error('查询领料车数量失败:', error);
+        showNotify({ type: 'danger', message: '查询领料车数量失败' });
+    }
+};
+onMounted(() => {
+    getDatas();
+    getWarehouses();
+    queryPickingCarCount();
+    queryFeedArea();
+});
+</script>
+
+<style scoped>
+/* ========== 基础样式 - 所有屏幕固定布局 ========== */
+.stock-requisition-page {
+  width: 100%;
+  height: 100vh;
+  max-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;
+  background-repeat: no-repeat;
+  z-index: 0;
+}
+
+/* ========== 主内容区域 ========== */
+.main-content {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  padding: 0 30px;
+  position: relative;
+  z-index: 10;
+  min-height: 0;
+  overflow: hidden;
+}
+
+/* ========== 筛选区域 ========== */
+.filter-section {
+  background: rgba(9, 61, 140, 0.5);
+  border: 1px solid #049FD8;
+  border-radius: 12px;
+  padding: 20px;
+  margin-bottom: 20px;
+  flex-shrink: 0;
+}
+
+.filter-row {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 20px;
+}
+
+.filter-item {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+}
+
+.filter-label {
+  font-size: 16px;
+  color: #7ec8ff;
+  white-space: nowrap;
+  font-weight: 500;
+}
+
+.filter-select {
+  width: 200px;
+}
+
+.filter-input.dark-input {
+  width: 200px;
+  padding: 10px 15px;
+  background: rgba(13, 58, 106, 0.8);
+  border: 1px solid #2a7fff;
+  border-radius: 6px;
+  color: #fff;
+  font-size: 14px;
+  outline: none;
+  transition: all 0.3s;
+}
+
+.filter-input.dark-input::placeholder {
+  color: #7ec8ff;
+  opacity: 0.7;
+}
+
+.filter-input.dark-input:focus {
+  border-color: #00bfff;
+  box-shadow: 0 0 10px rgba(0, 191, 255, 0.3);
+}
+
+.filter-buttons {
+  display: flex;
+  gap: 15px;
+  margin-left: auto;
+}
+
+.search-btn,
+.reset-btn {
+  padding: 10px 30px;
+  border-radius: 6px;
+  font-size: 16px;
+  font-weight: 500;
+  cursor: pointer;
+  transition: all 0.3s;
+  border: none;
+}
+
+.search-btn {
+  background: linear-gradient(90deg, #1e90ff 0%, #00bfff 100%);
+  color: #fff;
+}
+
+.search-btn:hover {
+  box-shadow: 0 0 15px rgba(30, 144, 255, 0.6);
+  transform: translateY(-2px);
+}
+
+.reset-btn {
+  background: rgba(13, 58, 106, 0.8);
+  border: 1px solid #2a7fff;
+  color: #fff;
+}
+
+.reset-btn:hover {
+  background: rgba(26, 74, 122, 0.8);
+  box-shadow: 0 0 10px rgba(30, 144, 255, 0.3);
+}
+
+/* ========== 全选控制栏 ========== */
+.select-all-bar {
+  background: rgba(9, 61, 140, 0.3);
+  border: 1px solid #049FD8;
+  border-radius: 8px;
+  padding: 10px 20px;
+  margin-bottom: 10px;
+  display: flex;
+  align-items: center;
+  flex-shrink: 0;
+}
+
+.select-all-text {
+  font-size: 14px;
+  color: #7ec8ff;
+  font-weight: 500;
+}
+
+:deep(.select-all-bar .van-checkbox__label) {
+  color: #7ec8ff;
+}
+
+/* ========== 卡片网格区域 ========== */
+.card-grid-wrapper {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  min-height: 0;
+  overflow-y: auto;
+  overflow-x: hidden;
+  padding-bottom: 10px;
+  /* 隐藏滚动条 */
+  scrollbar-width: none;
+  -ms-overflow-style: none;
+}
+
+.card-grid-wrapper::-webkit-scrollbar {
+  display: none;
+}
+
+/* 加载更多样式 */
+.loading-more {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 10px;
+  padding: 20px 0;
+  color: #7ec8ff;
+  font-size: 14px;
+}
+
+.loading-more-spinner {
+  width: 20px;
+  height: 20px;
+  border: 2px solid rgba(30, 144, 255, 0.3);
+  border-top-color: #00bfff;
+  border-radius: 50%;
+  animation: spin 1s linear infinite;
+}
+
+.no-more-data {
+  text-align: center;
+  padding: 15px 0;
+  color: #5a8abf;
+  font-size: 13px;
+}
+
+/* 空状态 */
+.empty-state {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  color: #7ec8ff;
+  padding: 60px 0;
+}
+
+.empty-icon {
+  font-size: 80px;
+  margin-bottom: 20px;
+  opacity: 0.5;
+}
+
+.empty-state p {
+  font-size: 18px;
+  margin: 0;
+}
+
+/* 卡片网格 - 4列布局 */
+.card-grid {
+  display: grid;
+  grid-template-columns: repeat(4, 1fr);
+  gap: 20px;
+}
+
+/* ========== 设备卡片样式 ========== */
+.inventory-card {
+  background: rgba(5, 30, 60, 0.8);
+  border: 2px solid #0d4a8a;
+  border-radius: 12px;
+  overflow: hidden;
+  cursor: pointer;
+  transition: all 0.3s ease;
+  position: relative;
+}
+
+.inventory-card:hover {
+  border-color: #1e90ff;
+  box-shadow: 0 0 20px rgba(30, 144, 255, 0.3);
+  transform: translateY(-3px);
+}
+
+.inventory-card.selected {
+  border-color: #00bfff;
+  box-shadow: 0 0 25px rgba(0, 191, 255, 0.5);
+  background: rgba(10, 50, 100, 0.9);
+}
+
+/* 卡片序号 - 左上角 */
+.card-index {
+  position: absolute;
+  top: 10px;
+  left: 10px;
+  width: 36px;
+  height: 36px;
+  background: linear-gradient(135deg, #1e90ff 0%, #00bfff 100%);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 16px;
+  font-weight: bold;
+  color: #fff;
+  z-index: 2;
+  box-shadow: 0 2px 8px rgba(0, 191, 255, 0.4);
+}
+
+/* 图片区域 */
+.card-image {
+  width: 100%;
+  aspect-ratio: 3 / 4;
+  background: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  overflow: hidden;
+  margin: 15px;
+  margin-bottom: 10px;
+  border-radius: 8px;
+  width: calc(100% - 30px);
+}
+
+.card-image img {
+  width: 100%;
+  height: 100%;
+  object-fit: cover;
+}
+
+.image-placeholder {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 100%;
+  height: 100%;
+  background: linear-gradient(135deg, #f0f4f8 0%, #e8ecf0 100%);
+  color: #94a3b8;
+  font-size: 48px;
+}
+
+/* 信息区域 */
+.card-info {
+  padding: 10px 15px 15px;
+}
+
+.info-row {
+  display: flex;
+  align-items: baseline;
+  margin-bottom: 6px;
+  font-size: 14px;
+}
+
+.info-row:last-child {
+  margin-bottom: 0;
+}
+
+.info-label {
+  color: #7ec8ff;
+  white-space: nowrap;
+  margin-right: 5px;
+}
+
+.info-value {
+  color: #fff;
+  flex: 1;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+/* 位置行样式 */
+.location-row {
+  display: flex;
+  align-items: center;
+  margin-top: 4px;
+  padding-top: 4px;
+  border-top: 1px solid rgba(30, 144, 255, 0.2);
+}
+
+.location-icon {
+  color: #00bfff;
+  margin-right: 6px;
+  font-size: 12px;
+}
+
+.separator {
+  color: #7ec8ff;
+  margin: 0 6px;
+}
+
+/* 选中状态指示 - 右上角 */
+.selected-indicator {
+  position: absolute;
+  top: 10px;
+  right: 10px;
+  width: 28px;
+  height: 28px;
+  background: linear-gradient(135deg, #00ff88 0%, #00cc6a 100%);
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 14px;
+  color: #fff;
+  box-shadow: 0 2px 8px rgba(0, 255, 136, 0.4);
+}
+
+/* ========== 底部操作按钮 ========== */
+.bottom-actions {
+  width: 100%;
+  padding: 15px 30px;
+  box-sizing: border-box;
+  background: rgba(4, 28, 61, 0.95);
+  z-index: 20;
+  flex-shrink: 0;
+  display: flex;
+  gap: 20px;
+}
+
+
+.pick-btn,
+.submit-btn {
+  flex: 1;
+  padding: 18px 0;
+  border: none;
+  border-radius: 12px;
+  font-size: 24px;
+  font-weight: bold;
+  color: #fff;
+  cursor: pointer;
+  transition: all 0.3s;
+  letter-spacing: 8px;
+  text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
+}
+
+.pick-btn {
+  background: linear-gradient(90deg, #26dc8a 0%, #44efa2 100%);
+}
+
+.pick-btn:hover:not(:disabled) {
+  box-shadow: 0 0 30px rgba(42, 220, 138, 0.6);
+  transform: translateY(-2px);
+}
+
+.submit-btn {
+  background: #4a99e2;
+}
+
+.submit-btn:hover:not(:disabled) {
+  box-shadow: 0 0 30px rgba(74, 153, 226, 0.6);
+  transform: translateY(-2px);
+}
+
+.pick-btn:disabled,
+.submit-btn:disabled {
+  opacity: 0.5;
+  cursor: not-allowed;
+  background: linear-gradient(90deg, #6b7280 0%, #9ca3af 100%);
+}
+
+  /* ========== 配送方式选择区域(固定高度) ========== */
+  .card-delivery-section {
+    padding: 10px 15px;
+    border-top: 1px solid rgba(30, 144, 255, 0.3);
+    background: rgba(9, 61, 140, 0.3);
+    min-height: 94px;
+    /* 固定最小高度 */
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    margin-bottom: 6px;
+  }
+
+  /* 未选中时的占位样式 */
+  .delivery-placeholder {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 100%;
+    color: #5a8abf;
+    font-size: 12px;
+    opacity: 0.7;
+  }
+
+  .delivery-row {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin-bottom: 8px;
+  }
+
+  .delivery-row:last-child {
+    margin-bottom: 0;
+  }
+
+  .delivery-location-row {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    height: 30px;
+    margin-top: 6px;
+  }
+
+  .delivery-label {
+    font-size: 12px;
+    color: #7ec8ff;
+    white-space: nowrap;
+  }
+
+  /* 配送方式按钮 */
+  .delivery-method-buttons {
+    display: flex;
+    gap: 4px;
+  }
+
+  .delivery-btn {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    gap: 4px;
+    padding: 5px 10px;
+    font-size: 11px;
+    font-weight: 500;
+    border: 1px solid #2a7fff;
+    background: rgba(13, 58, 106, 0.8);
+    color: #7ec8ff;
+    cursor: pointer;
+    transition: all 0.2s;
+    border-radius: 4px;
+  }
+
+  .delivery-btn i {
+    font-size: 10px;
+  }
+
+  .delivery-btn.active {
+    background: linear-gradient(90deg, #1e90ff 0%, #00bfff 100%);
+    color: #fff;
+    border-color: #00bfff;
+  }
+
+  .delivery-btn.disabled {
+    opacity: 0.4;
+    cursor: not-allowed;
+  }
+
+  .delivery-btn:hover:not(.disabled):not(.active) {
+    background: rgba(30, 144, 255, 0.3);
+  }
+
+  /* 配送类型徽章 */
+  .delivery-type-badge {
+    padding: 3px 10px;
+    border-radius: 12px;
+    font-size: 10px;
+    font-weight: 500;
+    display: inline-block;
+  }
+
+  .delivery-type-badge.badge-manual {
+    background: rgba(107, 114, 128, 0.4);
+    border: 1px solid #6b7280;
+    color: #d1d5db;
+  }
+
+  .delivery-type-badge.badge-agv-force {
+    background: rgba(234, 88, 12, 0.4);
+    border: 1px solid #ea580c;
+    color: #fdba74;
+  }
+
+  .delivery-type-badge.badge-agv-optional {
+    background: rgba(16, 185, 129, 0.4);
+    border: 1px solid #10b981;
+    color: #6ee7b7;
+  }
+
+  /* 配送位置选择器 */
+  .location-select {
+    flex: 1;
+    min-width: 0;
+  }
+/* ========== 响应式 - 1920x1080 横屏 ========== */
+@media screen and (orientation: landscape) {
+
+  /* 横屏固定布局,不出现页面滚动条 */
+  .stock-requisition-page {
+    height: 100vh;
+    min-height: 100vh;
+    max-height: 100vh;
+    overflow: hidden;
+  }
+
+  .header-section {
+    padding: 10px 20px;
+    flex-shrink: 0;
+  }
+
+  .page-title {
+    font-size: 22px;
+  }
+
+  .logout-btn {
+    padding: 6px 14px;
+    font-size: 13px;
+    left: 20px;
+  }
+
+  .logout-btn i {
+    font-size: 14px;
+  }
+
+  .header-actions {
+    right: 20px;
+    gap: 10px;
+  }
+
+  .action-btn {
+    padding: 6px 14px;
+    font-size: 13px;
+    gap: 6px;
+  }
+
+  .action-btn i {
+    font-size: 14px;
+  }
+
+  .main-content {
+    padding: 0 20px;
+    flex: 1;
+    min-height: 0;
+    overflow: hidden;
+  }
+
+  .filter-section {
+    padding: 10px 15px;
+    margin-bottom: 10px;
+    flex-shrink: 0;
+  }
+
+  .filter-row {
+    gap: 12px;
+  }
+
+  .filter-label {
+    font-size: 13px;
+  }
+
+  .filter-select {
+    width: 150px;
+  }
+
+  .filter-input.dark-input {
+    width: 150px;
+    padding: 6px 10px;
+    font-size: 12px;
+  }
+
+  :deep(.dark-select .vs__dropdown-toggle) {
+    min-height: 32px;
+    padding: 4px 8px;
+  }
+
+  .search-btn,
+  .reset-btn {
+    padding: 6px 20px;
+    font-size: 13px;
+  }
+
+  /* 卡片区域可滚动,隐藏滚动条 */
+  .card-grid-wrapper {
+    flex: 1;
+    min-height: 0;
+    overflow-y: auto;
+    overflow-x: hidden;
+    padding-bottom: 10px;
+    /* 隐藏滚动条 */
+    scrollbar-width: none;
+    -ms-overflow-style: none;
+  }
+
+  .card-grid-wrapper::-webkit-scrollbar {
+    display: none;
+  }
+
+  /* 横屏5列布局,显示20个卡片 */
+  .card-grid {
+    grid-template-columns: repeat(5, 1fr);
+    gap: 10px;
+  }
+
+  .inventory-card {
+    border-radius: 8px;
+    border-width: 1px;
+  }
+
+  .card-image {
+    margin: 8px;
+    width: calc(100% - 16px);
+    aspect-ratio: 4 / 3;
+  }
+
+  .card-index {
+    width: 24px;
+    height: 24px;
+    font-size: 12px;
+    top: 6px;
+    right: 6px;
+  }
+
+  .card-info {
+    padding: 6px 8px 8px;
+  }
+
+  .info-row {
+    font-size: 14px;
+    margin-bottom: 2px;
+  }
+
+  .location-row {
+    margin-top: 2px;
+    padding-top: 2px;
+  }
+
+  .location-icon {
+    font-size: 10px;
+    margin-right: 4px;
+  }
+
+  .separator {
+    margin: 0 4px;
+  }
+
+  .image-placeholder {
+    font-size: 28px;
+  }
+
+  .selected-indicator {
+    width: 20px;
+    height: 20px;
+    font-size: 10px;
+    top: 6px;
+    right: 6px;
+  }
+
+  .card-index {
+    width: 24px;
+    height: 24px;
+    font-size: 12px;
+    top: 6px;
+    left: 6px;
+  }
+
+  .loading-more {
+    padding: 15px 0;
+    font-size: 12px;
+  }
+
+  .loading-more-spinner {
+    width: 16px;
+    height: 16px;
+  }
+
+  .no-more-data {
+    padding: 10px 0;
+    font-size: 11px;
+  }
+
+  /* 底部按钮固定 */
+  .bottom-actions {
+    padding: 10px 20px;
+    flex-shrink: 0;
+    background: rgba(4, 28, 61, 0.95);
+  }
+
+  .submit-btn {
+    padding: 12px 0;
+    font-size: 18px;
+    letter-spacing: 6px;
+    border-radius: 8px;
+  }
+
+}
+
+/* ========== 响应式 - 中等屏幕(横屏但宽度较小) ========== */
+@media screen and (orientation: landscape) and (max-width: 1400px) {
+  .card-grid {
+    grid-template-columns: repeat(4, 1fr);
+    gap: 10px;
+  }
+
+  .filter-select {
+    width: 130px;
+  }
+
+  .filter-input.dark-input {
+    width: 130px;
+  }
+}
+
+/* ========== 响应式 - 中等屏幕(竖屏) ========== */
+@media screen and (orientation: portrait) and (max-width: 900px) {
+  .stock-requisition-page {
+    height: 100vh;
+    overflow: hidden;
+  }
+
+  .card-grid {
+    grid-template-columns: repeat(3, 1fr);
+    gap: 15px;
+  }
+
+  .card-grid-wrapper {
+    scrollbar-width: none;
+    -ms-overflow-style: none;
+  }
+
+  .card-grid-wrapper::-webkit-scrollbar {
+    display: none;
+  }
+
+  .filter-row {
+    flex-direction: column;
+    align-items: stretch;
+    gap: 15px;
+  }
+
+  .filter-item {
+    width: 100%;
+  }
+
+  .filter-select,
+  .filter-input.dark-input {
+    flex: 1;
+  }
+
+  .filter-buttons {
+    margin-left: 0;
+    justify-content: center;
+  }
+
+  .header-actions {
+    position: static;
+    transform: none;
+    margin-top: 10px;
+  }
+
+  .header-section {
+    flex-wrap: wrap;
+    justify-content: space-between;
+  }
+}
+
+/* ========== 响应式 - 小屏幕 ========== */
+@media screen and (max-width: 600px) {
+  .stock-requisition-page {
+    height: 100vh;
+    overflow: hidden;
+  }
+
+  .header-section {
+    padding: 15px 20px;
+    flex-direction: column;
+    gap: 10px;
+  }
+
+  .logout-btn {
+    position: static;
+    transform: none;
+  }
+
+  .header-actions {
+    position: static;
+    transform: none;
+  }
+
+  .page-title {
+    font-size: 20px;
+  }
+
+  .main-content {
+    padding: 0 15px;
+    flex: 1;
+    min-height: 0;
+    overflow: hidden;
+  }
+
+  .card-grid-wrapper {
+    scrollbar-width: none;
+    -ms-overflow-style: none;
+  }
+
+  .card-grid-wrapper::-webkit-scrollbar {
+    display: none;
+  }
+
+  .card-grid {
+    grid-template-columns: repeat(2, 1fr);
+    gap: 12px;
+  }
+
+  .card-image {
+    margin: 8px;
+    width: calc(100% - 16px);
+  }
+
+  .card-index {
+    width: 26px;
+    height: 26px;
+    font-size: 12px;
+  }
+
+  .info-row {
+    font-size: 11px;
+  }
+
+  .image-placeholder {
+    font-size: 32px;
+  }
+
+  .bottom-actions {
+    padding: 15px;
+    flex-shrink: 0;
+    background: rgba(4, 28, 61, 0.95);
+  }
+
+  .submit-btn {
+    padding: 14px 0;
+    font-size: 18px;
+    letter-spacing: 4px;
+  }
+}
+
+/* ========== 响应式 - 大屏竖屏 (1080x1920) ========== */
+@media screen and (orientation: portrait) and (min-width: 900px) {
+  .header-section {
+    padding: 30px 40px;
+  }
+
+  .page-title {
+    font-size: 36px;
+    letter-spacing: 4px;
+  }
+
+  .logout-btn {
+    padding: 14px 28px;
+    font-size: 20px;
+    border-radius: 10px;
+    left: 40px;
+  }
+
+  .logout-btn i {
+    font-size: 22px;
+  }
+
+  .header-actions {
+    right: 40px;
+    gap: 20px;
+  }
+
+  .action-btn {
+    padding: 14px 24px;
+    font-size: 18px;
+    gap: 10px;
+    border-radius: 10px;
+  }
+
+  .action-btn i {
+    font-size: 20px;
+  }
+
+  .main-content {
+    padding: 0 40px;
+  }
+
+  .filter-section {
+    padding: 25px 30px;
+    margin-bottom: 25px;
+  }
+
+  .filter-label {
+    font-size: 20px;
+  }
+
+  .filter-input.dark-input {
+    width: 240px;
+    padding: 14px 18px;
+    font-size: 16px;
+  }
+
+  .search-btn,
+  .reset-btn {
+    padding: 14px 40px;
+    font-size: 18px;
+  }
+
+  .card-grid {
+    grid-template-columns: repeat(4, 1fr);
+    gap: 25px;
+  }
+
+  .card-index {
+    width: 42px;
+    height: 42px;
+    font-size: 18px;
+  }
+
+  .card-image {
+    margin: 18px;
+    width: calc(100% - 36px);
+  }
+
+  .card-info {
+    padding: 12px 18px 18px;
+  }
+
+  .info-row {
+    font-size: 16px;
+    margin-bottom: 8px;
+  }
+
+  .location-row {
+    margin-top: 6px;
+    padding-top: 6px;
+  }
+
+  .location-icon {
+    font-size: 14px;
+    margin-right: 8px;
+  }
+
+  .separator {
+    margin: 0 8px;
+  }
+
+  .image-placeholder {
+    font-size: 56px;
+  }
+
+  .selected-indicator {
+    width: 34px;
+    height: 34px;
+    font-size: 16px;
+    right: 12px;
+  }
+
+  .card-index {
+    width: 42px;
+    height: 42px;
+    font-size: 18px;
+    left: 12px;
+  }
+
+  .bottom-actions {
+    padding: 25px 40px;
+  }
+
+  .submit-btn {
+    padding: 22px 0;
+    font-size: 28px;
+    letter-spacing: 10px;
+    border-radius: 15px;
+  }
+
+  :deep(.dark-select .vs__dropdown-toggle) {
+    min-height: 48px;
+    padding: 8px 14px;
+  }
+
+  :deep(.dark-select .vs__dropdown-option) {
+    padding: 12px 18px;
+    font-size: 16px;
+  }
+
+  .filter-select {
+    width: 240px;
+  }
+
+  .loading-more {
+    padding: 30px 0;
+    font-size: 16px;
+  }
+
+  .loading-more-spinner {
+    width: 24px;
+    height: 24px;
+  }
+
+  .no-more-data {
+    padding: 20px 0;
+    font-size: 15px;
+  }
+}
+</style>

+ 369 - 5
src/stock/StockPickingCar.vue

@@ -109,6 +109,53 @@
                 <i class="fas fa-map-marker-alt location-icon" />
                 <span class="info-value">{{ item.positionName || '-' }} / {{ item.warehouseName || '-' }}</span>
               </div>
+              <!-- 配送类型徽章 -->
+              <div class="info-row delivery-type-row">
+                <span class="delivery-type-badge" :class="getDeliveryTypeBadgeClass(item.deliveryType)">
+                  {{ item.deliveryType || '未指定' }}
+                </span>
+              </div>
+            </div>
+
+            <!-- 配送选择区域(固定高度,始终存在) -->
+            <div class="card-delivery-section" @click.stop>
+              <!-- 选中时显示配送选择 -->
+              <template v-if="selectedIds.includes(item.id)">
+                <div class="delivery-row">
+                  <span class="delivery-label">配送:</span>
+                  <div class="delivery-method-buttons">
+                    <button
+                      :class="['delivery-btn', { 'active': item.deliveryMethod === 'AGV_Delivery', 'disabled': item.deliveryType === '人工配送' }]"
+                      :disabled="item.deliveryType === '人工配送'" @click="changeDeliveryMethod(item, 'AGV_Delivery')"
+                    >
+                      <i class="fas fa-robot" /> AGV
+                    </button>
+                    <button
+                      :class="['delivery-btn', { 'active': item.deliveryMethod === 'Manual_Delivery', 'disabled': item.deliveryType === '强制AGV配送' }]"
+                      :disabled="item.deliveryType === '强制AGV配送'"
+                      @click="changeDeliveryMethod(item, 'Manual_Delivery')"
+                    >
+                      <i class="fas fa-user" /> 人工
+                    </button>
+                  </div>
+                </div>
+                <!-- AGV配送位置选择 -->
+                <div class="delivery-location-row">
+                  <span class="delivery-label">位置:</span>
+                  <v-select
+                    v-if="item.deliveryMethod === 'AGV_Delivery'" v-model="item.selectedLocation"
+                    :options="locator" :clearable="false" class="location-select dark-select" :append-to-body="true"
+                    :calculate-position="withPopper"
+                  />
+                  <span v-else style="font-size: 16px;">人工配送</span>
+                </div>
+              </template>
+              <!-- 未选中时显示占位 -->
+              <template v-else>
+                <div class="delivery-placeholder">
+                  <span>点击选择后设置配送方式</span>
+                </div>
+              </template>
             </div>
             
             <!-- 选中状态指示 -->
@@ -118,6 +165,28 @@
           </div>
         </div>
 
+        <!-- 拣货完成确认弹窗 - 科技感风格 -->
+        <div v-if="completeModalVisible" class="tech-modal-overlay" @click.self="handleCompleteCancel">
+          <div class="tech-modal success-modal">
+            <div class="modal-content-row">
+              <div class="modal-text">
+                <h3>领料完成</h3>
+                <p>领料申请已完成,配送任务已创建!<br />请确认是否返回主页?</p>
+              </div>
+              <div class="modal-icon">
+                <div class="icon-box success-box">
+                  <i class="fas fa-truck-loading" />
+                  <i class="fas fa-check check-icon" />
+                </div>
+              </div>
+            </div>
+            <div class="modal-footer">
+              <button class="modal-btn cancel-btn" @click="handleCompleteCancel">取消</button>
+              <button class="modal-btn confirm-btn" @click="handleCompleteConfirm">确认</button>
+            </div>
+          </div>
+        </div>
+
         <!-- 加载更多提示 -->
         <div v-if="loadingMore" class="loading-more">
           <div class="loading-more-spinner" />
@@ -141,9 +210,9 @@
       <button
         class="submit-btn"
         :disabled="selectedIds.length === 0"
-        @click="submitStock"
+        @click="handleComplete"
       >
-        提交领料
+        领料 ({{ selectedIds.length }})
       </button>
     </div>
 
@@ -211,8 +280,9 @@ import 'vue-select/dist/vue-select.css';
 // 图片资源
 import bgImg from '../assets/images/bj.png';
 
-import { getWarehouseList } from '../api/stock.js';
-import { queryCFPickCar, deleteCFPickCar, saveCFStockOutPrepare } from '../api/stockOut.js';
+import { getWarehouseList,queryFeedAreaStatus } from '../api/stock.js';
+import { queryCFPickCar, deleteCFPickCar, saveCFStockOutPrepare, generatePickPaper } from '../api/stockOut.js';
+import { gateController } from '../hardware/GateOperate.js';
 
 const router = useRouter();
 
@@ -231,6 +301,7 @@ const inventoryType = ref(undefined);
 const warehouseList = ref([]);
 const carList = ref([]);
 const selectedIds = ref([]);
+const selectedRows = ref([]);
 
 const loading = ref(false);
 const loadingMore = ref(false);
@@ -248,6 +319,31 @@ const pageSize = 20;
 const currentStart = ref(0);
 const total = ref(0);
 
+// 完成弹窗控制
+const completeModalVisible = ref(false);
+
+// 可配送位置
+const locator = ref([]);
+
+// 根据 deliveryType 初始化配送方式
+const initDeliveryMethod = item => {
+    if (item.deliveryType === '人工配送') {
+    // 人工配送:只能人工,不可切换
+        item.deliveryMethod = 'Manual_Delivery';
+        item.selectedLocation = '';
+    } else if (item.deliveryType === '强制AGV配送') {
+    // 强制AGV配送:只能AGV,不可切换为人工
+        item.deliveryMethod = 'AGV_Delivery';
+        if (!item.selectedLocation) {
+            item.selectedLocation = '';
+        }
+    } else if (item.deliveryType === '可选AGV配送') {
+    // 可选AGV配送:默认人工,可切换为AGV
+        item.deliveryMethod = item.deliveryMethod || 'Manual_Delivery';
+        item.selectedLocation = item.selectedLocation || '';
+    }
+};
+
 // 计算是否全选
 const isAllSelected = computed(() => {
     if (carList.value.length === 0) return false;
@@ -259,22 +355,36 @@ const toggleSelect = id => {
     const index = selectedIds.value.indexOf(id);
     if (index > -1) {
         selectedIds.value.splice(index, 1);
+        // 同时移除 selectedRows 中对应的项
+        selectedRows.value = selectedRows.value.filter(item => item.id !== id);
     } else {
         selectedIds.value.push(id);
+        // 添加到 selectedRows 并初始化配送字段
+        const item = carList.value.find(item => item.id === id);
+        if (item) {
+            // 初始化配送字段
+            initDeliveryMethod(item);
+            selectedRows.value.push(item);
+        }
     }
 };
 
 // 全选/取消全选
 const toggleSelectAll = checked => {
     if (checked) {
+    // 全选当前列表所有项
         carList.value.forEach(item => {
             if (!selectedIds.value.includes(item.id)) {
                 selectedIds.value.push(item.id);
+                initDeliveryMethod(item);
+                selectedRows.value.push(item);
             }
         });
     } else {
+    // 取消全选当前列表
         const currentListIds = carList.value.map(item => item.id);
         selectedIds.value = selectedIds.value.filter(id => !currentListIds.includes(id));
+        selectedRows.value = selectedRows.value.filter(row => !currentListIds.includes(row.id));
     }
 };
 
@@ -288,6 +398,16 @@ const getInventoryIcon = type => {
     return iconMap[type] || 'fas fa-cube';
 };
 
+// 获取配送类型徽章样式
+const getDeliveryTypeBadgeClass = deliveryType => {
+    const classMap = {
+        '人工配送': 'badge-manual',
+        '强制AGV配送': 'badge-agv-force',
+        '可选AGV配送': 'badge-agv-optional',
+    };
+    return classMap[deliveryType] || '';
+};
+
 // 滚动事件处理 - 无限滚动
 const handleScroll = e => {
     const container = e.target;
@@ -363,6 +483,41 @@ const loadMore = async () => {
     }
 };
 
+// 配送方式变更处理
+const changeDeliveryMethod = (item, method) => {
+    item.deliveryMethod = method;
+    // 当选择人工配送时,清空配送位置
+    if (method === 'Manual_Delivery') {
+        item.selectedLocation = '';
+    }
+    console.log('配送方式变更:', item.inventoryName, item.deliveryMethod);
+};
+
+// 获取送料区货位
+const queryFeedArea = async () => {
+    try {
+        const res = await queryFeedAreaStatus();
+
+        if (res.errorCode === 0) {
+            if (res.datas && res.datas.length > 0) {
+                locator.value = res.datas.map(item => {
+                    return {
+                        label: item.positionName,
+                        value: item.positionNo,
+                    };
+                });
+            } else {
+                locator.value = [];
+            }
+        } else {
+            showNotify({ type: 'danger', message: res.errorMessage });
+        }
+    } catch (error) {
+        console.error('获取送料区货位API调用失败:', error);
+        showNotify({ type: 'danger', message: '获取送料区货位API调用失败' });
+    }
+};
+
 // 初次加载物料数据
 const getPickerCarList = async () => {
     // 防止重复调用
@@ -398,6 +553,36 @@ const getPickerCarList = async () => {
                 carList.value = res.datas;
                 currentStart.value = res.datas.length;
                 total.value = res.total;
+
+                // 保留已选中物料的配送方式设置
+                carList.value = carList.value.map(item => {
+                    // 查找该物料是否在已选中列表中
+                    const selectedItem = selectedRows.value.find(selected => selected.id === item.id);
+
+                    if (selectedItem) {
+                        // 如果已选中,保留其配送方式和位置设置,并更新 selectedRows 中的引用
+                        const updatedItem = {
+                            ...item,
+                            deliveryMethod: selectedItem.deliveryMethod,
+                            selectedLocation: selectedItem.selectedLocation,
+                        };
+
+                        // 更新 selectedRows 中的对应项,保持引用同步
+                        const index = selectedRows.value.findIndex(row => row.id === item.id);
+                        if (index !== -1) {
+                            selectedRows.value[index] = updatedItem;
+                        }
+
+                        return updatedItem;
+                    } else {
+                        // 未选中,初始化为空
+                        return {
+                            ...item,
+                            deliveryMethod: '',
+                            selectedLocation: '',
+                        };
+                    }
+                });
                 
                 if (carList.value.length >= res.total) {
                     noMoreData.value = true;
@@ -422,12 +607,67 @@ const getPickerCarList = async () => {
     }
 };
 
+// 领料申请,直接验证并提交
+const handleComplete = async () => {
+    if (selectedIds.value.length === 0) {
+        showNotify({ type: 'danger', message: '请至少选择一个物料' });
+        return;
+    }
+
+    // 验证选择了 AGV 配送的物料是否都选择了配送位置
+    const agvItems = selectedRows.value.filter(item => item.deliveryMethod === 'AGV_Delivery');
+    const hasEmptyLocation = agvItems.some(item => !item.selectedLocation);
+
+    if (hasEmptyLocation) {
+        showNotify({ type: 'danger', message: '请为所有 AGV 配送的物料选择配送位置' });
+        return;
+    }
+
+    const params = [];
+    selectedRows.value.forEach(item => {
+        params.push({
+            inventoryId: item.inventoryId,
+            deliveryMethod: item.deliveryMethod,
+            positionEndNo: item.selectedLocation.value,
+        });
+    });
+
+    console.log('提交参数:', params);
+    const res = await generatePickPaper(params);
+    if (res.errorCode == 0) {
+        showNotify({ type: 'success', message: '领料申请成功' });
+        selectedIds.value = [];
+        selectedRows.value = [];
+        // 显示完成确认弹窗
+        gateController('SHOTOPEN');
+        router.push('/');
+    } else {
+        showNotify({ type: 'danger', message: res.errorMessage });
+    }
+};
+
+// 处理完成确认
+const handleCompleteConfirm = () => {
+    // 调用开门操作
+    gateController('SHOTOPEN');
+    completeModalVisible.value = false;
+    router.push('/');
+    // showToast('领料申请已完成,配送任务已创建!');
+};
+
+// 处理完成取消
+const handleCompleteCancel = () => {
+    completeModalVisible.value = false;
+    // 留在当前页面,刷新列表
+    getDatas();
+};
+
 // 返回领料
 const goToHome = () => {
     if (isRegular.value == 'true') {
         router.push('/regular-requisition');
     } else {
-        router.push('/stock-requisition');
+        router.push('/apply-material');
     }
 };
 
@@ -556,6 +796,7 @@ const submitStock = async () => {
 onMounted(() => {
     getDatas();
     getWarehouses();
+    queryFeedArea();
     isRegular.value = router.currentRoute.value.query.isRegular;
 });
 </script>
@@ -1029,6 +1270,129 @@ onMounted(() => {
   color: white !important;
 }
 
+/* ========== 配送方式选择区域(固定高度) ========== */
+  .card-delivery-section {
+    padding: 10px 15px;
+    border-top: 1px solid rgba(30, 144, 255, 0.3);
+    background: rgba(9, 61, 140, 0.3);
+    min-height: 94px;
+    /* 固定最小高度 */
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    margin-bottom: 6px;
+  }
+
+  /* 未选中时的占位样式 */
+  .delivery-placeholder {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    height: 100%;
+    color: #5a8abf;
+    font-size: 12px;
+    opacity: 0.7;
+  }
+
+  .delivery-row {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    margin-bottom: 8px;
+  }
+
+  .delivery-row:last-child {
+    margin-bottom: 0;
+  }
+
+  .delivery-location-row {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    height: 30px;
+    margin-top: 6px;
+  }
+
+  .delivery-label {
+    font-size: 12px;
+    color: #7ec8ff;
+    white-space: nowrap;
+  }
+
+  /* 配送方式按钮 */
+  .delivery-method-buttons {
+    display: flex;
+    gap: 4px;
+  }
+
+  .delivery-btn {
+    display: inline-flex;
+    align-items: center;
+    justify-content: center;
+    gap: 4px;
+    padding: 5px 10px;
+    font-size: 11px;
+    font-weight: 500;
+    border: 1px solid #2a7fff;
+    background: rgba(13, 58, 106, 0.8);
+    color: #7ec8ff;
+    cursor: pointer;
+    transition: all 0.2s;
+    border-radius: 4px;
+  }
+
+  .delivery-btn i {
+    font-size: 10px;
+  }
+
+  .delivery-btn.active {
+    background: linear-gradient(90deg, #1e90ff 0%, #00bfff 100%);
+    color: #fff;
+    border-color: #00bfff;
+  }
+
+  .delivery-btn.disabled {
+    opacity: 0.4;
+    cursor: not-allowed;
+  }
+
+  .delivery-btn:hover:not(.disabled):not(.active) {
+    background: rgba(30, 144, 255, 0.3);
+  }
+
+  /* 配送类型徽章 */
+  .delivery-type-badge {
+    padding: 3px 10px;
+    border-radius: 12px;
+    font-size: 10px;
+    font-weight: 500;
+    display: inline-block;
+  }
+
+  .delivery-type-badge.badge-manual {
+    background: rgba(107, 114, 128, 0.4);
+    border: 1px solid #6b7280;
+    color: #d1d5db;
+  }
+
+  .delivery-type-badge.badge-agv-force {
+    background: rgba(234, 88, 12, 0.4);
+    border: 1px solid #ea580c;
+    color: #fdba74;
+  }
+
+  .delivery-type-badge.badge-agv-optional {
+    background: rgba(16, 185, 129, 0.4);
+    border: 1px solid #10b981;
+    color: #6ee7b7;
+  }
+
+  /* 配送位置选择器 */
+  .location-select {
+    flex: 1;
+    min-width: 0;
+  }
+
 /* ========== 响应式 - 1920x1080 横屏 ========== */
 @media screen and (orientation: landscape) {
   .stock-requisition-page {