|
|
@@ -0,0 +1,830 @@
|
|
|
+<!-- 拣货管理 -->
|
|
|
+<template>
|
|
|
+ <div class="page-container">
|
|
|
+ <!-- 顶部信息栏 -->
|
|
|
+ <PageHeader />
|
|
|
+
|
|
|
+ <!-- 主内容区域 -->
|
|
|
+ <main class="main-content">
|
|
|
+ <!-- 页面标题 -->
|
|
|
+ <div class="page-title">
|
|
|
+ <h2>拣货管理</h2>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 筛选区域 -->
|
|
|
+ <FilterPanel :default-collapsed="false" :active-count="getActiveFilterCount()">
|
|
|
+ <a-form layout="inline" class="filter-form">
|
|
|
+ <a-form-item label="设备名称">
|
|
|
+ <a-input
|
|
|
+ v-model:value="filterForm.inventoryName" placeholder="请输入" style="width: 180px"
|
|
|
+ @keyup.enter="getList"
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="设备编号">
|
|
|
+ <a-input
|
|
|
+ v-model:value="filterForm.inventoryNo" placeholder="请输入" style="width: 180px"
|
|
|
+ @keyup.enter="getList"
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="设备类型">
|
|
|
+ <a-select
|
|
|
+ v-model:value="filterForm.inventoryType" placeholder="选择类型" allow-clear style="width: 180px"
|
|
|
+ :options="inventoryTypeList" @change="getList"
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="仓库名称">
|
|
|
+ <a-select
|
|
|
+ v-model:value="filterForm.warehouseId" placeholder="选择仓库" allow-clear style="width: 180px"
|
|
|
+ @change="getList"
|
|
|
+ >
|
|
|
+ <a-select-option v-for="item in warehouseList" :key="item.id" :value="item.id">
|
|
|
+ {{ item.name }}
|
|
|
+ </a-select-option>
|
|
|
+ </a-select>
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item label="货位名称">
|
|
|
+ <a-input
|
|
|
+ v-model:value="filterForm.positionName" placeholder="请输入" style="width: 180px"
|
|
|
+ @keyup.enter="getList"
|
|
|
+ />
|
|
|
+ </a-form-item>
|
|
|
+ <a-form-item>
|
|
|
+ <a-button type="primary" @click="getList">
|
|
|
+ <i class="fas fa-search mr-1" /> 搜索
|
|
|
+ </a-button>
|
|
|
+ <a-button class="ml-2" @click="handleReset">
|
|
|
+ <i class="fas fa-redo mr-1" /> 重置
|
|
|
+ </a-button>
|
|
|
+ </a-form-item>
|
|
|
+ </a-form>
|
|
|
+ </FilterPanel>
|
|
|
+
|
|
|
+ <!-- 卡片容器 -->
|
|
|
+ <div class="card-container">
|
|
|
+ <!-- 卡片列表区域(可滚动) -->
|
|
|
+ <div class="card-list-wrapper">
|
|
|
+ <a-empty v-if="materialList.length === 0" description="暂无数据" :image="Empty.PRESENTED_IMAGE_SIMPLE" />
|
|
|
+
|
|
|
+ <!-- 卡片列表 -->
|
|
|
+ <div v-else class="card-list">
|
|
|
+ <a-card
|
|
|
+ v-for="(item, index) in materialList" :key="item.id || index" :hoverable="true"
|
|
|
+ :class="{ 'selected-card': selectedIds.includes(item.id) }" class="inventory-card"
|
|
|
+ @click="toggleSelect(item.id)"
|
|
|
+ >
|
|
|
+ <template #title>
|
|
|
+ <div class="card-header">
|
|
|
+ <div class="card-header-left">
|
|
|
+ <a-checkbox
|
|
|
+ :checked="selectedIds.includes(item.id)" @click.stop
|
|
|
+ @change="e => handleCheckboxChange(e, item.id)"
|
|
|
+ />
|
|
|
+ <a-avatar :size="42" :style="{ backgroundColor: '#3b82f6' }" class="ml-3">
|
|
|
+ <template #icon>
|
|
|
+ <i :class="getInventoryIcon(item.inventoryType)" style="font-size: 20px;" />
|
|
|
+ </template>
|
|
|
+ </a-avatar>
|
|
|
+ <div class="ml-4 card-title-info">
|
|
|
+ <div class="card-name">{{ item.inventoryName }}</div>
|
|
|
+ <div class="card-subtitle">编号: {{ item.inventoryNo }}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="card-header-right">
|
|
|
+ <div class="card-location">
|
|
|
+ <i class="fas fa-map-marker-alt mr-2" />
|
|
|
+ <span>{{ item.positionName || '-' }}</span>
|
|
|
+ <span class="mx-2">/</span>
|
|
|
+ <span>{{ item.warehouseName || '-' }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <!-- 卡片展开内容:配送方式选择(只有选中时显示) -->
|
|
|
+ <template v-if="selectedIds.includes(item.id)">
|
|
|
+ <div class="card-delivery-section" @click.stop>
|
|
|
+ <!-- <div class="delivery-section-title">
|
|
|
+ <i class="fas fa-truck mr-2" />
|
|
|
+ 配送方式设置
|
|
|
+ </div> -->
|
|
|
+
|
|
|
+ <div class="delivery-options">
|
|
|
+ <!-- 配送方式选择 -->
|
|
|
+ <div class="delivery-option-item">
|
|
|
+ <label class="option-label">配送方式:</label>
|
|
|
+ <a-radio-group
|
|
|
+ v-model:value="item.deliveryMethod" button-style="solid"
|
|
|
+ @change="handleDeliveryMethodChange(item)"
|
|
|
+ >
|
|
|
+ <a-radio-button value="AGV_Delivery" :disabled="item.deliveryType === '人工配送'">
|
|
|
+ <i class="fas fa-robot mr-1" /> AGV 配送
|
|
|
+ </a-radio-button>
|
|
|
+ <a-radio-button value="Manual_Delivery" :disabled="item.deliveryType === '强制AGV配送'">
|
|
|
+ <i class="fas fa-user mr-1" /> 人工配送
|
|
|
+ </a-radio-button>
|
|
|
+ </a-radio-group>
|
|
|
+ <span
|
|
|
+ v-if="item.deliveryType" class="delivery-type-badge"
|
|
|
+ :class="getDeliveryTypeBadgeClass(item.deliveryType)"
|
|
|
+ >
|
|
|
+ {{ item.deliveryType }}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 配送位置选择(仅 AGV 配送时显示) -->
|
|
|
+ <div v-if="item.deliveryMethod === 'AGV_Delivery'" class="delivery-location-item">
|
|
|
+ <label class="option-label">配送位置:</label>
|
|
|
+ <a-select
|
|
|
+ v-model:value="item.selectedLocation" placeholder="请选择" style="width: 200px"
|
|
|
+ :options="locator"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </a-card>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 分页区域(固定底部) -->
|
|
|
+ <div v-if="materialList.length > 0" class="pagination-wrapper">
|
|
|
+ <span class="text-gray-600">共 {{ materialList.length }} 条数据</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 底部完成按钮 -->
|
|
|
+ <div class="bottom-actions">
|
|
|
+ <a-button
|
|
|
+ type="primary" size="large" :disabled="selectedIds.length === 0" class="shadow-sm"
|
|
|
+ @click="handleComplete"
|
|
|
+ >
|
|
|
+ <template #icon>
|
|
|
+ <i class="fas fa-check-circle mr-2" />
|
|
|
+ </template>
|
|
|
+ 拣货 ({{ selectedIds.length }})
|
|
|
+ </a-button>
|
|
|
+ </div>
|
|
|
+ </main>
|
|
|
+ </div>
|
|
|
+ <Loading v-if="loading" />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+import { useRouter } from 'vue-router';
|
|
|
+import { ref, reactive, onMounted } from 'vue';
|
|
|
+import { message, Empty } from 'ant-design-vue';
|
|
|
+import PageHeader from '../common/PageHeader.vue';
|
|
|
+import FilterPanel from '../common/FilterPanel.vue';
|
|
|
+import { list, createStockOut } from '../api/stockOut.js';
|
|
|
+import { getWarehouseList, queryIdleLocator } from '../api/stock.js';
|
|
|
+
|
|
|
+const router = useRouter();
|
|
|
+
|
|
|
+const warehouseList = ref([]);
|
|
|
+
|
|
|
+// 筛选表单
|
|
|
+const filterForm = reactive({
|
|
|
+ inventoryName: '',
|
|
|
+ inventoryNo: '',
|
|
|
+ inventoryType: undefined,
|
|
|
+ warehouseId: undefined,
|
|
|
+ positionName: '',
|
|
|
+});
|
|
|
+const inventoryTypeList = ref([
|
|
|
+ { value: 'Clamp', label: '工装' },
|
|
|
+ { value: 'Instrument', label: '设备' },
|
|
|
+ { value: 'FinishProduct', label: '成品' },
|
|
|
+]);
|
|
|
+// 表格加载状态
|
|
|
+const loading = ref(false);
|
|
|
+
|
|
|
+// 物料列表数据
|
|
|
+const materialList = ref([]);
|
|
|
+
|
|
|
+// 分页配置
|
|
|
+const pagination = reactive({
|
|
|
+ start: 1,
|
|
|
+ lang: 10,
|
|
|
+ total: 0,
|
|
|
+});
|
|
|
+
|
|
|
+// 所选集合
|
|
|
+const selectedIds = ref([]);
|
|
|
+const selectedRows = ref([]);
|
|
|
+
|
|
|
+// 可配送位置
|
|
|
+const locator = ref([]);
|
|
|
+
|
|
|
+// 计算激活的筛选条件数量
|
|
|
+const getActiveFilterCount = () => {
|
|
|
+ let count = 0;
|
|
|
+ if (filterForm.inventoryName) count++;
|
|
|
+ if (filterForm.inventoryNo) count++;
|
|
|
+ if (filterForm.inventoryType) count++;
|
|
|
+ if (filterForm.warehouseId) count++;
|
|
|
+ if (filterForm.positionName) count++;
|
|
|
+ return count;
|
|
|
+};
|
|
|
+
|
|
|
+// 根据 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 = materialList.value.find(item => item.id === id);
|
|
|
+ if (item) {
|
|
|
+ // 初始化配送字段
|
|
|
+ initDeliveryMethod(item);
|
|
|
+ selectedRows.value.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 处理复选框变化
|
|
|
+const handleCheckboxChange = (e, id) => {
|
|
|
+ if (e.target.checked) {
|
|
|
+ if (!selectedIds.value.includes(id)) {
|
|
|
+ selectedIds.value.push(id);
|
|
|
+ const item = materialList.value.find(item => item.id === id);
|
|
|
+ if (item) {
|
|
|
+ // 初始化配送字段
|
|
|
+ initDeliveryMethod(item);
|
|
|
+ selectedRows.value.push(item);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ const index = selectedIds.value.indexOf(id);
|
|
|
+ if (index > -1) {
|
|
|
+ selectedIds.value.splice(index, 1);
|
|
|
+ selectedRows.value = selectedRows.value.filter(item => item.id !== 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 handleReset = () => {
|
|
|
+ filterForm.inventoryName = '';
|
|
|
+ filterForm.inventoryNo = '';
|
|
|
+ filterForm.inventoryType = undefined;
|
|
|
+ filterForm.warehouseId = undefined;
|
|
|
+ filterForm.positionName = '';
|
|
|
+ getList();
|
|
|
+};
|
|
|
+
|
|
|
+// 加入领料申请
|
|
|
+const addToRequisition = record => {
|
|
|
+ if (!selectedIds.value.includes(record.id)) {
|
|
|
+ selectedIds.value.push(record.id);
|
|
|
+ // 初始化配送字段
|
|
|
+ initDeliveryMethod(record);
|
|
|
+ selectedRows.value.push(record);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 领料申请,直接验证并提交
|
|
|
+const handleComplete = async () => {
|
|
|
+ if (selectedIds.value.length === 0) {
|
|
|
+ message.warning('请选择物料加入领料申请!');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 验证选择了 AGV 配送的物料是否都选择了配送位置
|
|
|
+ const agvItems = selectedRows.value.filter(item => item.deliveryMethod === 'AGV_Delivery');
|
|
|
+ const hasEmptyLocation = agvItems.some(item => !item.selectedLocation);
|
|
|
+
|
|
|
+ if (hasEmptyLocation) {
|
|
|
+ message.warning('请为所有 AGV 配送的物料选择配送位置');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const params = [];
|
|
|
+ selectedRows.value.forEach(item => {
|
|
|
+ params.push({
|
|
|
+ stockOutPrepareLineId: item.id,
|
|
|
+ deliveryMethod: item.deliveryMethod,
|
|
|
+ positionEndNo: item.selectedLocation,
|
|
|
+ });
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('提交参数:', params);
|
|
|
+ await generateCFStockOut(params);
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// 配送方式变更处理
|
|
|
+const handleDeliveryMethodChange = record => {
|
|
|
+ // 当选择人工配送时,清空配送位置
|
|
|
+ if (record.deliveryMethod === 'Manual_Delivery') {
|
|
|
+ record.selectedLocation = '';
|
|
|
+ }
|
|
|
+ console.log('配送方式变更:', record.inventoryName, record.deliveryMethod);
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// 获取物料列表
|
|
|
+const getList = async () => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ ...filterForm,
|
|
|
+ userId: JSON.parse(localStorage.getItem('#LoginInfo')).userId,
|
|
|
+ };
|
|
|
+ const res = await list(params);
|
|
|
+ loading.value = false;
|
|
|
+
|
|
|
+ if (res.errorCode === 0) {
|
|
|
+ if (res.datas && res.datas.length > 0) {
|
|
|
+ const testData = res.datas;
|
|
|
+ // ========== 测试数据模拟开始 ==========
|
|
|
+ // const testData = res.datas.map((item, index) => {
|
|
|
+ // const types = ['人工配送', '强制AGV配送', '可选AGV配送'];
|
|
|
+ // return {
|
|
|
+ // ...item,
|
|
|
+ // // 循环分配不同的配送类型用于测试
|
|
|
+ // deliveryType: types[index % 3] || item.deliveryType,
|
|
|
+ // };
|
|
|
+ // });
|
|
|
+ // ========== 测试数据模拟结束 ==========
|
|
|
+
|
|
|
+ // 保留已选中物料的配送方式设置
|
|
|
+ materialList.value = testData.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: '',
|
|
|
+ };
|
|
|
+ }
|
|
|
+ });
|
|
|
+ pagination.total = materialList.value.length;
|
|
|
+ } else {
|
|
|
+ materialList.value = [];
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ message.error(res.errorMessage);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ loading.value = false;
|
|
|
+ console.error('获取物料列表API调用失败:', error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+};
|
|
|
+// 生成CF出库单
|
|
|
+const generateCFStockOut = async params => {
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const res = await createStockOut(params);
|
|
|
+
|
|
|
+ if (res.errorCode === 0) {
|
|
|
+ message.success('领料申请已完成,配送任务已创建!');
|
|
|
+ selectedIds.value = [];
|
|
|
+ selectedRows.value = [];
|
|
|
+ // getList();
|
|
|
+ router.push('/home');
|
|
|
+ } else {
|
|
|
+ message.error(res.errorMessage);
|
|
|
+ }
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('生成CF出库单API调用失败:', error);
|
|
|
+ message.error('生成CF出库单API调用失败');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取仓库列表
|
|
|
+const getWarehouse = 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 {
|
|
|
+ message.error(res.errorMessage);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取仓库列表API调用失败:', error);
|
|
|
+ message.error('获取仓库列表API调用失败');
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+// 获取空闲货位
|
|
|
+const getIdleLocator = async () => {
|
|
|
+ try {
|
|
|
+ const res = await queryIdleLocator();
|
|
|
+
|
|
|
+ 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 {
|
|
|
+ message.error(res.errorMessage);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取空闲货位API调用失败:', error);
|
|
|
+ message.error('获取空闲货位API调用失败');
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+onMounted(() => {
|
|
|
+ getWarehouse();
|
|
|
+ getList();
|
|
|
+ getIdleLocator();
|
|
|
+});
|
|
|
+
|
|
|
+
|
|
|
+// 获取分页参数
|
|
|
+// const getPageParams = (current, pageSize) => {
|
|
|
+// pagination.start = current;
|
|
|
+// pagination.lang = pageSize;
|
|
|
+// getList();
|
|
|
+// };
|
|
|
+
|
|
|
+// 查询回到第一页
|
|
|
+// const getDatas = () => {
|
|
|
+// commonTableRef.value.backFirstPage();
|
|
|
+// };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+/* 页面容器 - 固定高度布局 */
|
|
|
+.page-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 100vh;
|
|
|
+ background-color: #f9fafb;
|
|
|
+ overflow: hidden;
|
|
|
+}
|
|
|
+
|
|
|
+/* 主内容区 - 可滚动 */
|
|
|
+.main-content {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 1.5rem;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+}
|
|
|
+
|
|
|
+/* 页面标题 */
|
|
|
+.page-title {
|
|
|
+ margin-bottom: 1.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+.page-title h2 {
|
|
|
+ font-size: 1.5rem;
|
|
|
+ font-weight: 700;
|
|
|
+ color: #111827;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片容器 */
|
|
|
+.card-container {
|
|
|
+ flex: 1;
|
|
|
+ background-color: white;
|
|
|
+ border-radius: 0.5rem;
|
|
|
+ box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ overflow: hidden;
|
|
|
+ min-height: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片列表包装器(可滚动区域) */
|
|
|
+.card-list-wrapper {
|
|
|
+ flex: 1;
|
|
|
+ overflow-y: auto;
|
|
|
+ padding: 1.5rem;
|
|
|
+ min-height: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片列表(单列布局) */
|
|
|
+.card-list {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+/* 分页包装器(固定底部) */
|
|
|
+.pagination-wrapper {
|
|
|
+ padding: 1rem 1.5rem;
|
|
|
+ border-top: 1px solid #e5e7eb;
|
|
|
+ border-bottom: 1px solid #e5e7eb;
|
|
|
+ background-color: #fafafa;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+/* 底部操作按钮 */
|
|
|
+.bottom-actions {
|
|
|
+ position: sticky;
|
|
|
+ bottom: 0;
|
|
|
+ padding: 1rem 1rem 0 1rem;
|
|
|
+ background-color: #f9fafb;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ z-index: 10;
|
|
|
+}
|
|
|
+
|
|
|
+/* 筛选表单 */
|
|
|
+.filter-form {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ gap: 0.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-form-item) {
|
|
|
+ margin-bottom: 0 !important;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-form-item-label > label) {
|
|
|
+ font-size: 14px !important;
|
|
|
+ font-weight: 600 !important;
|
|
|
+}
|
|
|
+
|
|
|
+/* 库存卡片样式 */
|
|
|
+.inventory-card {
|
|
|
+ cursor: pointer;
|
|
|
+ transition: all 0.25s ease;
|
|
|
+ border: 2px solid #e5e7eb;
|
|
|
+ background-color: #ffffff;
|
|
|
+ border-radius: 0.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+.inventory-card:hover {
|
|
|
+ border-color: #93c5fd;
|
|
|
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1);
|
|
|
+}
|
|
|
+
|
|
|
+.inventory-card.selected-card {
|
|
|
+ border-color: #3b82f6 !important;
|
|
|
+ background-color: #eff6ff;
|
|
|
+ box-shadow: 0 4px 16px rgba(59, 130, 246, 0.2);
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片标题区域 */
|
|
|
+.card-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ width: 100%;
|
|
|
+ gap: 1.5rem;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header-left {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.card-header-right {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片标题信息 */
|
|
|
+.card-title-info {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ gap: 0.25rem;
|
|
|
+ min-width: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.card-name {
|
|
|
+ font-size: 1.125rem;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #111827;
|
|
|
+ line-height: 1.5;
|
|
|
+ white-space: nowrap;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+}
|
|
|
+
|
|
|
+.card-subtitle {
|
|
|
+ font-size: 0.875rem;
|
|
|
+ color: #6b7280;
|
|
|
+ line-height: 1.4;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片位置信息 */
|
|
|
+.card-location {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 0.5rem 1rem;
|
|
|
+ background-color: #f3f4f6;
|
|
|
+ border-radius: 0.375rem;
|
|
|
+ font-size: 0.875rem;
|
|
|
+ color: #374151;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+.card-location i {
|
|
|
+ color: #3b82f6;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片标题样式 */
|
|
|
+:deep(.ant-card-head) {
|
|
|
+ border-bottom: none;
|
|
|
+ padding: 1rem 1.5rem;
|
|
|
+ min-height: auto;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-card-head-title) {
|
|
|
+ padding: 0;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片内容区域 - 选中时显示 */
|
|
|
+:deep(.ant-card-body) {
|
|
|
+ padding: 0;
|
|
|
+ display: block;
|
|
|
+}
|
|
|
+
|
|
|
+/* 卡片配送方式选择区域 */
|
|
|
+.card-delivery-section {
|
|
|
+ padding: 1rem 1.5rem;
|
|
|
+ background-color: #f8fafc;
|
|
|
+ border-top: 1px solid #e5e7eb;
|
|
|
+ margin-top: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.delivery-section-title {
|
|
|
+ font-size: 0.9375rem;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #1f2937;
|
|
|
+ margin-bottom: 1rem;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+}
|
|
|
+
|
|
|
+.delivery-section-title i {
|
|
|
+ color: #3b82f6;
|
|
|
+ font-size: 1rem;
|
|
|
+}
|
|
|
+
|
|
|
+.delivery-options {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: row;
|
|
|
+ align-items: center;
|
|
|
+ gap: 2rem;
|
|
|
+ flex-wrap: wrap;
|
|
|
+}
|
|
|
+
|
|
|
+.delivery-option-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 0.75rem;
|
|
|
+}
|
|
|
+
|
|
|
+.delivery-location-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 0.75rem;
|
|
|
+}
|
|
|
+
|
|
|
+.option-label {
|
|
|
+ font-size: 0.875rem;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #374151;
|
|
|
+ min-width: 80px;
|
|
|
+ margin: 0;
|
|
|
+}
|
|
|
+
|
|
|
+.delivery-type-badge {
|
|
|
+ margin-left: 0.75rem;
|
|
|
+ padding: 0.25rem 0.625rem;
|
|
|
+ border-radius: 0.375rem;
|
|
|
+ font-size: 0.8125rem;
|
|
|
+ font-weight: 500;
|
|
|
+ white-space: nowrap;
|
|
|
+}
|
|
|
+
|
|
|
+/* 人工配送徽章 - 灰色 */
|
|
|
+.delivery-type-badge.badge-manual {
|
|
|
+ background-color: #eff6ff;
|
|
|
+ color: #4b5563;
|
|
|
+}
|
|
|
+
|
|
|
+/* 强制AGV配送徽章 - 蓝色 */
|
|
|
+.delivery-type-badge.badge-agv-force {
|
|
|
+ background-color: #82b0ec;
|
|
|
+ color: #1e40af;
|
|
|
+}
|
|
|
+
|
|
|
+/* 可选AGV配送徽章 - 绿色 */
|
|
|
+.delivery-type-badge.badge-agv-optional {
|
|
|
+ background-color: #adf8d1;
|
|
|
+ color: #065f46;
|
|
|
+}
|
|
|
+
|
|
|
+/* Radio Group 样式优化 */
|
|
|
+:deep(.ant-radio-button-wrapper) {
|
|
|
+ display: inline-flex;
|
|
|
+ align-items: center;
|
|
|
+ height: 32px;
|
|
|
+ padding: 0 15px;
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-radio-button-wrapper i) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+/* Select 样式优化 */
|
|
|
+:deep(.ant-select) {
|
|
|
+ font-size: 14px;
|
|
|
+}
|
|
|
+
|
|
|
+/* 按钮样式 */
|
|
|
+:deep(.ant-btn-primary) {
|
|
|
+ background-color: #3b82f6;
|
|
|
+ border-color: #3b82f6;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-btn-primary:hover) {
|
|
|
+ background-color: #2563eb;
|
|
|
+ border-color: #2563eb;
|
|
|
+}
|
|
|
+
|
|
|
+:deep(.ant-btn[disabled]) {
|
|
|
+ opacity: 0.5;
|
|
|
+ cursor: not-allowed;
|
|
|
+}
|
|
|
+</style>
|