| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444 |
- <!-- 拣货管理 - 智能仓储风格 -->
- <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>
- <!-- 主内容区域 -->
- <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="filterForm.inventoryType" :options="inventoryTypeList" :reduce="item => item.value"
- label="label" placeholder="选择类型" :clearable="true" class="filter-select dark-select"
- @update:model-value="getList"
- />
- </div>
- <div class="filter-item">
- <label class="filter-label">名称</label>
- <input
- v-model="filterForm.inventoryName" type="text" class="filter-input dark-input" placeholder="输入名称"
- @keyup.enter="getList"
- />
- </div>
- <!-- <div class="filter-item">
- <label class="filter-label">编号</label>
- <input
- v-model="filterForm.inventoryNo"
- type="text"
- class="filter-input dark-input"
- placeholder="输入编号"
- @keyup.enter="getList"
- />
- </div>
- <div class="filter-item">
- <label class="filter-label">仓库</label>
- <v-select
- v-model="filterForm.warehouseId"
- :options="warehouseList"
- :reduce="item => item.id"
- label="name"
- placeholder="选择仓库"
- :clearable="true"
- class="filter-select dark-select"
- />
- </div> -->
- <div class="filter-buttons">
- <button class="search-btn" @click="getList">
- 搜索
- </button>
- <button class="reset-btn" @click="handleReset">
- 重置
- </button>
- </div>
- </div>
- </div>
- <!-- 全选控制栏 -->
- <div v-if="materialList.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 class="card-grid-wrapper">
- <!-- 空状态 -->
- <div v-if="materialList.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 materialList" :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 v-if="selectedIds.includes(item.id)" class="selected-indicator">
- <i class="fas fa-check" />
- </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.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>
- </div>
- </div>
- </div>
- </main>
- <!-- 底部操作按钮 -->
- <div class="bottom-actions">
- <button class="submit-btn" :disabled="selectedIds.length === 0" @click="handleComplete">
- 拣货 ({{ selectedIds.length }})
- </button>
- </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>
- <!-- 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 { useRouter } from 'vue-router';
- import { ref, reactive, computed, onMounted } from 'vue';
- import { showNotify } from 'vant';
- import vSelect from 'vue-select';
- import 'vue-select/dist/vue-select.css';
- import { createPopper } from '@popperjs/core';
- import { list, createStockOut } from '../api/stockOut.js';
- import { getWarehouseList, queryIdleLocator } from '../api/stock.js';
- // 图片资源
- import bgImg from '../assets/images/bj.png';
- const router = useRouter();
- // 返回主页
- const goHome = () => {
- router.push('/home');
- };
- // Popper.js 配置
- const withPopper = (dropdownList, component, { width }) => {
- dropdownList.style.width = width;
- dropdownList.style.zIndex = '9999';
- const popper = createPopper(component.$refs.toggle, dropdownList, {
- placement: 'bottom',
- modifiers: [
- {
- name: 'offset',
- options: { offset: [0, -1] },
- },
- {
- name: 'toggleClass',
- enabled: true,
- phase: 'write',
- fn({ state }) {
- component.$el.classList.toggle('drop-up', state.placement === 'top');
- },
- },
- ],
- });
- return () => popper.destroy();
- };
- 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 completeModalVisible = ref(false);
- // 物料列表数据
- const materialList = ref([]);
- // 分页配置
- const pagination = reactive({
- start: 1,
- lang: 10,
- total: 0,
- });
- // 所选集合
- const selectedIds = ref([]);
- const selectedRows = ref([]);
- // 计算是否全选
- const isAllSelected = computed(() => {
- if (materialList.value.length === 0) return false;
- return materialList.value.every(item => selectedIds.value.includes(item.id));
- });
- // 可配送位置
- 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 = (checked, id) => {
- if (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 toggleSelectAll = checked => {
- if (checked) {
- // 全选当前页所有项(累加模式)
- materialList.value.forEach(item => {
- if (!selectedIds.value.includes(item.id)) {
- selectedIds.value.push(item.id);
- initDeliveryMethod(item);
- selectedRows.value.push(item);
- }
- });
- } else {
- // 取消全选当前页
- const currentPageIds = materialList.value.map(item => item.id);
- selectedIds.value = selectedIds.value.filter(id => !currentPageIds.includes(id));
- selectedRows.value = selectedRows.value.filter(row => !currentPageIds.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 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) {
- 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({
- stockOutPrepareLineId: item.id,
- deliveryMethod: item.deliveryMethod,
- positionEndNo: item.selectedLocation.value,
- });
- });
- console.log('提交参数:', params);
- await generateCFStockOut(params);
- };
- // 配送方式变更处理
- const changeDeliveryMethod = (item, method) => {
- item.deliveryMethod = method;
- // 当选择人工配送时,清空配送位置
- if (method === 'Manual_Delivery') {
- item.selectedLocation = '';
- }
- console.log('配送方式变更:', item.inventoryName, item.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 {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- } catch (error) {
- loading.value = false;
- console.error('获取物料列表API调用失败:', error);
- showNotify({ type: 'danger', message: '获取物料列表API调用失败' });
- return;
- }
- };
- // 生成CF出库单
- const generateCFStockOut = async params => {
- loading.value = true;
- try {
- const res = await createStockOut(params);
- if (res.errorCode === 0) {
- showNotify({ type: 'success', message: '领料完成' });
- selectedIds.value = [];
- selectedRows.value = [];
- // 显示完成确认弹窗
- completeModalVisible.value = true;
- } else {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- } catch (error) {
- console.error('生成CF出库单API调用失败:', error);
- showNotify({ type: 'danger', message: '生成CF出库单API调用失败' });
- } finally {
- loading.value = false;
- }
- };
- // 处理完成确认
- const handleCompleteConfirm = () => {
- completeModalVisible.value = false;
- router.push('/home');
- // showToast('领料申请已完成,配送任务已创建!');
- };
- // 处理完成取消
- const handleCompleteCancel = () => {
- completeModalVisible.value = false;
- // 留在当前页面,刷新列表
- getList();
- };
- // 获取仓库列表
- 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 {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- } catch (error) {
- console.error('获取仓库列表API调用失败:', error);
- showNotify({ type: 'danger', message: '获取仓库列表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 {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- } catch (error) {
- console.error('获取空闲货位API调用失败:', error);
- showNotify({ type: 'danger', message: '获取空闲货位API调用失败' });
- }
- };
- onMounted(() => {
- getWarehouse();
- getList();
- getIdleLocator();
- });
- </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: 15px;
- 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: 180px;
- }
- .filter-input.dark-input {
- width: 180px;
- 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;
- display: flex;
- align-items: center;
- gap: 8px;
- }
- .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);
- }
- .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);
- }
- /* 全选控制栏 */
- .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;
- }
- /* 空状态 */
- .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: 18px;
- }
- /* ========== 设备卡片样式 ========== */
- .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;
- font-size: 12px;
- margin-right: 6px;
- }
- /* 配送类型行样式 */
- .delivery-type-row {
- margin-top: 6px;
- padding-top: 6px;
- border-top: 1px solid rgba(30, 144, 255, 0.2);
- justify-content: center;
- }
- /* 选中状态指示 */
- .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;
- z-index: 2;
- box-shadow: 0 2px 8px rgba(0, 255, 136, 0.4);
- }
- /* ========== 配送方式选择区域(固定高度) ========== */
- .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;
- }
- /* 禁用状态的选择器 */
- .location-select.vs--disabled .vs__dropdown-toggle {
- opacity: 0.5;
- background: rgba(13, 58, 106, 0.4);
- }
- /* 统计信息 */
- .stats-info {
- text-align: center;
- padding: 15px 0;
- color: #5a8abf;
- font-size: 13px;
- }
- /* ========== 底部操作按钮 ========== */
- .bottom-actions {
- width: 100%;
- padding: 15px 30px;
- box-sizing: border-box;
- background: rgba(4, 28, 61, 0.95);
- z-index: 20;
- flex-shrink: 0;
- }
- .submit-btn {
- width: 100%;
- padding: 18px 0;
- background: #4a99e2;
- border: none;
- border-radius: 12px;
- font-size: 24px;
- font-weight: bold;
- color: #fff;
- cursor: pointer;
- transition: all 0.3s;
- letter-spacing: 4px;
- text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
- }
- .submit-btn:hover:not(:disabled) {
- box-shadow: 0 0 30px rgba(16, 185, 129, 0.6);
- transform: translateY(-2px);
- }
- .submit-btn:disabled {
- opacity: 0.5;
- cursor: not-allowed;
- background: linear-gradient(90deg, #6b7280 0%, #9ca3af 100%);
- }
- /* ========== 响应式 - 横屏 ========== */
- @media screen and (orientation: landscape) {
- .header-section {
- padding: 10px 20px;
- }
- .page-title {
- font-size: 22px;
- }
- .logout-btn {
- padding: 6px 14px;
- font-size: 13px;
- left: 20px;
- }
- .logout-btn i {
- font-size: 14px;
- }
- .main-content {
- padding: 0 20px;
- }
- .filter-section {
- padding: 10px 15px;
- margin-bottom: 10px;
- }
- .filter-row {
- gap: 12px;
- }
- .filter-label {
- font-size: 13px;
- }
- .filter-select {
- width: 140px;
- }
- .filter-input.dark-input {
- width: 140px;
- 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;
- }
- .select-all-bar {
- padding: 8px 15px;
- margin-bottom: 8px;
- }
- .select-all-text {
- font-size: 12px;
- }
- /* 卡片网格响应式 */
- .card-grid {
- grid-template-columns: repeat(6, 1fr);
- gap: 15px;
- }
- .card-index {
- width: 24px;
- height: 24px;
- font-size: 11px;
- top: 6px;
- left: 6px;
- }
- .selected-indicator {
- width: 20px;
- height: 20px;
- font-size: 10px;
- top: 6px;
- right: 6px;
- }
- .card-image {
- margin: 10px;
- margin-bottom: 8px;
- width: calc(100% - 20px);
- }
- .image-placeholder {
- font-size: 36px;
- }
- .card-info {
- padding: 6px 10px 8px;
- }
- .info-row {
- font-size: 11px;
- margin-bottom: 3px;
- }
- .location-icon {
- font-size: 10px;
- }
- .delivery-type-row {
- margin-top: 4px;
- padding-top: 4px;
- }
- .delivery-type-badge {
- font-size: 9px;
- padding: 2px 8px;
- }
- .card-delivery-section {
- padding: 8px 10px;
- min-height: 82px;
- }
- .delivery-placeholder {
- font-size: 10px;
- }
- .delivery-label {
- font-size: 10px;
- }
- .delivery-btn {
- padding: 3px 6px;
- font-size: 9px;
- gap: 2px;
- }
- .delivery-btn i {
- font-size: 8px;
- }
- .delivery-location-row {
- margin-top: 6px;
- }
- .bottom-actions {
- padding: 10px 20px;
- }
- .submit-btn {
- padding: 12px 0;
- font-size: 18px;
- letter-spacing: 4px;
- border-radius: 8px;
- }
- }
- /* 大弹窗样式 */
- :deep(.large-dialog) {
- width: 85vw !important;
- max-width: 600px !important;
- border-radius: 16px !important;
- }
- :deep(.large-dialog .van-dialog__header) {
- padding: 2rem 1.5rem 1rem !important;
- font-size: 1.5rem !important;
- font-weight: 700 !important;
- color: #111827 !important;
- }
- .large-dialog-content {
- padding: 1.5rem 2rem 2.5rem !important;
- }
- /* ========== 科技感弹窗样式 ========== */
- .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;
- position: relative;
- 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;
- }
- .modal-text h3 {
- font-size: 22px;
- font-weight: bold;
- color: #fff;
- margin: 0 0 12px 0;
- }
- .modal-text p {
- font-size: 15px;
- color: #7ec8ff;
- margin: 0;
- line-height: 1.6;
- }
- .modal-icon {
- flex-shrink: 0;
- }
- .icon-box {
- width: 80px;
- height: 80px;
- background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
- border-radius: 12px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 36px;
- color: #fff;
- position: relative;
- box-shadow: 0 10px 30px rgba(16, 185, 129, 0.3);
- }
- .icon-box .check-icon {
- position: absolute;
- bottom: -5px;
- right: -5px;
- font-size: 20px;
- color: #10b981;
- background: #041c3d;
- border-radius: 50%;
- padding: 4px;
- }
- .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, #10b981 0%, #34d399 100%);
- border: none;
- color: #fff;
- }
- .confirm-btn:hover {
- box-shadow: 0 0 20px rgba(16, 185, 129, 0.5);
- }
- </style>
|