| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200 |
- <!-- 出库确认 - 智能仓储风格 -->
- <template>
- <dv-border-box-8>
- <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 refresh-btn" @click="resetView()">
- <i class="fas fa-sync-alt" />
- <span>重新校验</span>
- </button>
- </div>
- </div>
- <!-- 主内容区域 -->
- <main class="main-content">
- <div v-if="needConfirmStockInMaterialList.length > 0" class="stats-section">
- <span class="stats-text">
- 您共有 {{ needConfirmStockInMaterialList.length }} 个物料入库,请确认物料已经放置到对应的货位,确认无误以后,再点击【入库离开】按钮。
- </span>
- <span v-if="confirmInvalidCount > 0" style="color: red; font-weight: bolder;">
- 其中 {{ confirmInvalidCount }} 个物料被闸机读取到了,请把物料放到对应的位置,再点击【重新校验】按钮。
- </span>
- </div>
- <!-- 卡片网格区域 -->
- <div class="card-grid-wrapper">
- <!-- 空状态 -->
- <div v-if=" needConfirmStockInMaterialList.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 needConfirmStockInMaterialList"
- :key="item.id || index"
- class="inventory-card"
- :class="getCardClass(item)"
- >
- <!-- 卡片序号 -->
- <div class="card-index" :class="getCardClass(item)">{{ index + 1 }}</div>
- <!-- 图片区域 -->
- <div class="card-image">
- <div class="image-placeholder" :class="getCardClass(item)">
- <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.inventoryType || '-' }}</span>
- </div>
- <div class="info-row">
- <span class="info-label">名称:</span>
- <span class="info-value">{{ item.inventoryName || '-' }}</span>
- </div>
- <div class="info-row location-row">
- <i class="fas fa-map-marker-alt location-icon" />
- <span class="info-value">{{ item.positionName || '-' }}</span>
- </div>
- </div>
- <!-- 状态区域(固定高度) -->
- <div class="card-status-section">
- <div class="status-badge" :class="getCardClass(item)">
- <i class="fas fa-exclamation-circle" />
- {{ item.remarks || '-' }}
- </div>
- </div>
- </div>
- </div>
- </div>
- <!-- 统计信息 -->
- <div v-if="materialList.length > 0" class="stats-section">
- <span class="stats-text">共 {{ materialList.length }} 条数据,已完成 {{ completedCount }} 条, 不识别 {{ unrecognizedCount }} 条</span>
- </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">
- <template v-for="(item, index) in materialList" :key="item.epc">
- <div
- v-if="item.inventoryId != null && item.isValid === true"
- class="inventory-card"
- :class="{ 'completed': item.status === 1 }"
- >
- <!-- 卡片序号 -->
- <div class="card-index" :class="{ 'completed': item.status === 1 }">{{ index + 1 }}</div>
- <!-- 图片区域 -->
- <div class="card-image">
- <div class="image-placeholder" :style="{ backgroundColor: getStatusColor(item.status) }">
- <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>
- <!-- 操作区域(固定高度) -->
- <div class="card-action-section">
- <template v-if="item.positionName !== '不在库' && item.inventoryName !== '未识别epc'">
- <button v-if="item.status === 1" class="status-btn completed-btn" disabled>
- <i class="fas fa-check" /> 领料完成
- </button>
- <button v-if="item.status === 2" class="status-btn start-btn" @click.stop="handleStart(item)">
- <i class="fas fa-play" /> 开始领料
- </button>
- <button v-if="item.status === 3" class="status-btn apply-btn" @click.stop="handleApply(item)">
- <i class="fas fa-hand-paper" /> 申请领料
- </button>
- </template>
- <template v-else>
- <span class="status-placeholder">无需操作</span>
- </template>
- </div>
- </div>
- </template>
- </div>
- </div>
- </main>
- <!-- 底部操作按钮 -->
- <div class="bottom-actions">
- <button class="submit-btn" :disabled="confirmInvalidCount > 0 || !isCanLeave" @click="handleLeave">
- 确认离开
- </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>
- </dv-border-box-8>
- </template>
- <script setup>
- import { useRouter } from 'vue-router';
- import { ref, computed, onMounted, onUnmounted } from 'vue';
- import { showNotify } from 'vant';
- import { cfStockOut, createStockOut, cfStockOutLeave,leaveCFWarehouse } from '../api/stockOut.js';
- import { areArraysEqual } from '../common/utils.js';
- import { gateController } from '../hardware/GateOperate.js';
- import { queryCFStockInByUser, backfillCFStockInTime, cfStockInLeave } from '../api/stockIn.js';
- // 图片资源
- import bgImg from '../assets/images/bj.png';
- // 路由
- const router = useRouter();
- // 返回主页
- const goHome = () => {
- router.push('/home');
- };
- // 表格加载状态
- const loading = ref(false);
- // 物料列表数据
- const materialList = ref([]);
- const epcs = ref([]);
- // 完成数量统计
- const confirmInvalidCount = computed(() => {
- return needConfirmStockInMaterialList.value.filter(item => item.isValid === false).length;
- });
- // 完成数量统计
- const completedCount = computed(() => {
- return materialList.value.filter(item => item.status === 1 && item.inventoryId != null).length;
- });
- // 不识别的数据统计
- const unrecognizedCount = computed(() => {
- return materialList.value.filter(item => item.inventoryId == null).length;
- });
- // 获取设备类型图标
- const getInventoryIcon = type => {
- const iconMap = {
- '工装': 'fas fa-cube',
- '设备': 'fas fa-cogs',
- '成品': 'fas fa-box',
- };
- return iconMap[type] || 'fas fa-cube';
- };
- // 获取状态颜色
- const getStatusColor = status => {
- const colorMap = {
- 1: '#10b981', // 完成 - 绿色
- 2: '#f59e0b', // 进行中 - 橙色
- 3: '#3b82f6', // 待开始 - 蓝色
- };
- return colorMap[status] || '#6b7280';
- };
- // 判断是否可以离开
- const isCanLeave = computed(() => {
- // 1. 先判断 materialList 是否存在且是数组
- // if (!materialList.value || !Array.isArray(materialList.value)) {
- // return false;
- // }
- // 2. 判断是否有数据(空数组不能离开)
- // if (materialList.value.length === 0) {
- // return false;
- // }
- // 3. 判断所有项的 status 是否都为 1
- // 使用 every 时需要确保 item 和 item.status 都存在
- // return materialList.value.every(item => {
- // return item && typeof item.status !== 'undefined' && item.status === 1;
- // });
- return true;
- });
- // 开始领料
- const handleStart = record => {
- generateCFStockOut(record.stockOutPrepareLineId);
- };
- // 申请领料
- const handleApply = async record => {
- showNotify({ type: 'success', message: '申请领料成功' });
- try {
- const res = await leaveCFWarehouse(record.inventoryId);
- if (res.errorCode === 0) {
- showNotify({ type: 'success', message: '申请领料成功' });
- getStockOutList();
- } else {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- } catch (error) {
- console.error('申请领料API调用失败:', error);
- showNotify({ type: 'danger', message: '申请领料API调用失败' });
- } finally {
- loading.value = false;
- }
- };
- // 领料完成后离开
- const handleLeave = async () => {
- //----------------------------------- 还料数据处理 -----------------------------------//
- await getCFStockInByUser();
- let invalidCount = 0;
- needConfirmStockInMaterialList.value.map(item => {
- if(item.isValid === false) {
- invalidCount++;
- }
- });
- if(invalidCount > 0) {
- showNotify({
- type: 'danger',
- message: '请检查有' + invalidCount + '条物料已经入库,但是被闸机扫描到了。请自己检查以后,点击【重新校验】按钮' });
- return;
- }
- // 还料离开,回填入库单中的离开时间
- const stockInIds = needConfirmStockInMaterialList.value.map(item => {
- return item.stockInId;
- });
- if(stockInIds.length > 0){
- await generateCFStockIn(stockInIds);
- }
- //----------------------------------- 领料数据处理 -----------------------------------//
- const params = [];
- materialList.value.map(item => {
- if(item.isValid === true && item.inventoryId != null){
- params.push({
- stockOutPrepareLineId: item.stockOutPrepareLineId,
- stockOutId: item.stockOutId,
- inventoryId: item.inventoryId,
- });
- }
- });
- if(params.length == 0){
- // 直接开门离开
- gateController('SHOTOPEN');
- router.push('/');
- return;
- }
- loading.value = true;
- try {
- const res = await cfStockOutLeave(params);
- if (res.errorCode === 0) {
- gateController('SHOTOPEN');
- showNotify({ type: 'success', message: '领料离开成功' });
- router.push('/');
- } else {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- } catch (error) {
- console.error('领料离开API调用失败:', error);
- showNotify({ type: 'danger', message: '领料离开API调用失败' });
- } finally {
- loading.value = false;
- }
- };
- // 扫描到EPC以后,获取领料的数据
- const getStockOutList = async () => {
- const params = {
- epcList: [],
- };
- const tempMaterialList = [];
- for(let i = 0; i < materialList.value.length; i++) {
- if(materialList.value[i].queryStatus === 0) {
- params.epcList.push(materialList.value[i].epc);
- materialList.value[i].queryStatus = 1;
- tempMaterialList.push(materialList.value[i]);
- }
- }
- if(params.epcList.length == 0){
- return;
- }
-
- try {
- const res = await cfStockOut(params);
- if (res.errorCode === 0) {
- if (res.datas && res.datas.length > 0) {
- res.datas.forEach(i => {
- materialList.value.forEach(j => {
- if (j.epc === i.epc) {
- j.stockOutId = i.stockOutId;
- j.inventoryName = i.inventoryName;
- j.inventoryNo = i.inventoryNo;
- j.inventoryType = i.inventoryType;
- j.warehouseName = i.warehouseName;
- j.positionName = i.positionName;
- j.stockOutPrepareNo = i.stockOutPrepareNo;
- j.stockOutNo = i.stockOutNo;
- j.inventoryId = i.inventoryId;
- j.stockOutPrepareLineId = i.stockOutPrepareLineId;
- j.queryStatus = 2;
- j.status = i.status;
- }
- });
- });
- }
- } else {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- computerErrorData();
- } catch (error) {
- console.error('获取扫描到的领料数据API调用失败:', error);
- showNotify({ type: 'danger', message: '获取扫描到的领料数据API调用失败' });
- // 处理查询失败的物料
- tempMaterialList.forEach(item => {
- item.queryStatus = 0;
- });
- } finally {
- loading.value = false;
- }
- };
- // 生成出库单
- const generateCFStockOut = async id => {
- loading.value = true;
- const params = [
- {
- stockOutPrepareLineId: id,
- deliveryMethod: 'Manual_Delivery',
- },
- ];
- try {
- const res = await createStockOut(params);
- if (res.errorCode === 0) {
- getStockOutList();
- } else {
- showNotify({ type: 'danger', message: res.errorMessage });
- }
- } catch (error) {
- console.error('生成出库单API调用失败:', error);
- showNotify({ type: 'danger', message: '生成出库单API调用失败' });
- } finally {
- loading.value = false;
- }
- };
- let timer = null; // 用于保存当前定时器引用
- const addEpc = data => {
- const newEpcs = data.map(item => item.epc);
-
- // 将新的EPC数据添加到临时数组中,使用Set进行去重
- const uniqueEpcs = new Set([...newEpcs]);
- if(newEpcs != null && newEpcs.length > 0) {
- newEpcs.forEach(item => {
- let exist = false;
- for(let i =0; i < materialList.value.length; i++) {
- if(materialList.value[i].epc === item) {
- exist = true;
- break;
- }
- }
- if(exist == false) {
- materialList.value.push({
- epc: item,
- queryStatus: 0, // 0: 未查询, 1: 查询中, 2: 已查询, 3: 未查询到
- });
- }
- });
- }
- };
- onMounted(() => {
- getCFStockInByUser();
- plugin.gateConfig.sendEpc = function(data){
- if (typeof(GATE_CONFIG) == 'undefined') {
- console.log('设备不支持读写器功能。');
- } else {
- addEpc(data);
- }
- };
- // 创建全局唯一定时器,每2秒执行一次
- timer = setInterval(() => {
- // 执行获取出库列表
- // addEpc([{
- // 'epc': 'FFF000000000000000000001',
- // }]);
- getStockOutList();
- }, 1000);
- });
- onUnmounted(() => {
- plugin.gateConfig.sendEpc = null;
- // 清除定时器
- if (timer) {
- clearInterval(timer);
- timer = null;
- }
- });
- // 已入库但是未确认的工装设备列表数据
- const needConfirmStockInMaterialList = ref([]);
- // 查询用户已经入库但是未确认的工装设备信息
- const getCFStockInByUser = async () => {
- const info = localStorage.getItem('#LoginInfo');
- const userId = JSON.parse(info).userId;
- try {
- const res = await queryCFStockInByUser(userId);
- if (res.errorCode === 0) {
- if (res.datas && res.datas.length > 0) {
- needConfirmStockInMaterialList.value = res.datas;
- computerErrorData();
- // userMaterialList.value = res.datas;
- // getInnerList();
- } else {
- needConfirmStockInMaterialList.value = [];
- computerErrorData();
- // userMaterialList.value = [];
- }
- } else {
- showNotify({ type: 'danger', message: res.errorMessage,zIndex: 99999999 });
- // getInnerList();
- }
- } catch (error) {
- console.error('获取物料列表API调用失败:', error);
- showNotify({ type: 'danger', message: '获取物料列表API调用失败' });
- }
- };
- // 还料离开(回填归还入库单入库时间)
- const generateCFStockIn = async params => {
- loading.value = true;
- try {
- const res = await backfillCFStockInTime(params);
- if (res.errorCode === 0) {
- // 调用开门操作
- //gateController('SHOTOPEN');
- //showNotify({ type: 'success', message: '还料离开已完成!' });
- //router.push('/home');
- } 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 computerErrorData = data => {
- if(needConfirmStockInMaterialList.value != null){
- needConfirmStockInMaterialList.value.forEach(item => {
- item.isValid = true;
- });
- }
- if(materialList.value != null){
- materialList.value.forEach(i => {
- i.isValid = true;
- });
- }
- if (!needConfirmStockInMaterialList.value || needConfirmStockInMaterialList.value.length === 0) {
- return;
- }
- if (!materialList.value || materialList.value.length === 0) {
- return;
- }
- needConfirmStockInMaterialList.value.forEach(item => {
- materialList.value.forEach(i => {
- if (i.epc === item.epc) {
- i.isValid = false;
- item.isValid = false;
- }
- });
- });
- };
- // 根据 isValid 返回卡片样式类
- const getCardClass = card => {
- if (card.isValid == null || card.isValid === true) {
- return 'in-stock-card';
- } else if (card.isValid === false) {
- return 'not-in-stock-card';
- }
- return '';
- };
- const resetView = () => {
- epcs.value = [];
- materialList.value = [];
- needConfirmStockInMaterialList.value = [];
- getCFStockInByUser();
- };
- </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;
- }
- /* 统计信息 */
- .stats-section {
- background: rgba(9, 61, 140, 0.5);
- border: 1px solid #049FD8;
- border-radius: 12px;
- padding: 15px 20px;
- margin-bottom: 15px;
- flex-shrink: 0;
- }
- .stats-text {
- color: #7ec8ff;
- font-size: 16px;
- }
- /* ========== 卡片网格区域 ========== */
- .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: 20px;
- }
- /* ========== 设备卡片样式 ========== */
- .inventory-card {
- background: rgba(5, 30, 60, 0.8);
- border: 2px solid #0d4a8a;
- border-radius: 12px;
- overflow: hidden;
- 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.completed {
- border-color: #10b981;
- box-shadow: 0 0 15px rgba(16, 185, 129, 0.3);
- }
- /* 卡片序号 - 左上角 */
- .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-index.completed {
- background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
- box-shadow: 0 2px 8px rgba(16, 185, 129, 0.4);
- }
- /* 图片区域 */
- .card-image {
- width: 100%;
- aspect-ratio: 3 / 4;
- display: flex;
- align-items: center;
- justify-content: center;
- overflow: hidden;
- margin: 15px;
- margin-bottom: 10px;
- border-radius: 8px;
- width: calc(100% - 30px);
- }
- .image-placeholder {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 100%;
- height: 100%;
- background: linear-gradient(135deg, #f0f4f8 0%, #e8ecf0 100%);
- color: #fff;
- 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;
- }
- /* ========== 操作区域(固定高度) ========== */
- .card-action-section {
- padding: 12px 15px;
- border-top: 1px solid rgba(30, 144, 255, 0.3);
- background: rgba(9, 61, 140, 0.3);
- min-height: 50px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .status-placeholder {
- color: #5a8abf;
- font-size: 12px;
- opacity: 0.7;
- }
- /* 状态按钮 */
- .status-btn {
- width: 100%;
- padding: 10px 15px;
- border-radius: 6px;
- font-size: 13px;
- font-weight: 500;
- cursor: pointer;
- transition: all 0.3s;
- border: none;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 6px;
- }
- .completed-btn {
- background: linear-gradient(90deg, #10b981 0%, #34d399 100%);
- color: #fff;
- opacity: 0.7;
- cursor: not-allowed;
- }
- .start-btn {
- background: linear-gradient(90deg, #f59e0b 0%, #fbbf24 100%);
- color: #fff;
- }
- .start-btn:hover {
- box-shadow: 0 0 15px rgba(245, 158, 11, 0.5);
- }
- .apply-btn {
- background: linear-gradient(90deg, #1e90ff 0%, #00bfff 100%);
- color: #fff;
- }
- .apply-btn:hover {
- box-shadow: 0 0 15px rgba(30, 144, 255, 0.5);
- }
- /* ========== 底部操作按钮 ========== */
- .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: 8px;
- 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: 30px 20px 10px 20px;
- }
- .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;
- }
- .action-btn {
- padding: 6px 14px;
- font-size: 13px;
- }
- .action-btn i {
- font-size: 14px;
- }
- .main-content {
- padding: 0 20px;
- }
- .stats-section {
- padding: 10px 15px;
- margin-bottom: 10px;
- }
- .stats-text {
- font-size: 14px;
- }
- /* 卡片网格响应式 */
- .card-grid {
- grid-template-columns: repeat(6, 1fr);
- gap: 15px;
- }
- .card-index {
- width: 24px;
- height: 24px;
- font-size: 11px;
- top: 6px;
- left: 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;
- }
- .card-action-section {
- padding: 8px 10px;
- min-height: 40px;
- }
- .status-btn {
- padding: 6px 10px;
- font-size: 11px;
- gap: 4px;
- }
- .status-placeholder {
- font-size: 10px;
- }
- .bottom-actions {
- padding: 10px 20px;
- }
- .submit-btn {
- padding: 12px 0;
- font-size: 18px;
- letter-spacing: 6px;
- border-radius: 8px;
- }
- }
- </style>
- <style scoped>
- /* ========== 设备卡片样式 ========== */
- .inventory-card {
- background: rgba(5, 30, 60, 0.8);
- border: 2px solid #0d4a8a;
- border-radius: 12px;
- overflow: hidden;
- 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.not-in-stock-card {
- border-color: #ef4444;
- }
- /* 校验通过卡片 - 绿色 */
- .inventory-card.in-stock-card {
- border-color: #10b981;
- }
- /* 卡片序号 - 左上角 */
- .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-index.not-in-stock-card {
- background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
- box-shadow: 0 2px 8px rgba(239, 68, 68, 0.4);
- }
- .card-index.in-stock-card {
- background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
- box-shadow: 0 2px 8px rgba(16, 185, 129, 0.4);
- }
- /* 图片区域 */
- .card-image {
- width: 100%;
- aspect-ratio: 3 / 4;
- display: flex;
- align-items: center;
- justify-content: center;
- overflow: hidden;
- margin: 15px;
- margin-bottom: 10px;
- border-radius: 8px;
- width: calc(100% - 30px);
- }
- .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;
- }
- .image-placeholder.not-in-stock-card {
- background: linear-gradient(135deg, #ef4444 0%, #f87171 100%);
- color: #fff;
- }
- .image-placeholder.in-stock-card {
- background: linear-gradient(135deg, #10b981 0%, #34d399 100%);
- color: #fff;
- }
- /* 信息区域 */
- .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;
- }
- /* ========== 状态区域(固定高度) ========== */
- .card-status-section {
- padding: 12px 15px;
- border-top: 1px solid rgba(30, 144, 255, 0.3);
- background: rgba(9, 61, 140, 0.3);
- min-height: 50px;
- display: flex;
- align-items: center;
- justify-content: center;
- }
- /* 状态标签 */
- .status-badge {
- padding: 8px 20px;
- border-radius: 20px;
- font-size: 13px;
- font-weight: 500;
- display: flex;
- align-items: center;
- gap: 6px;
- }
- .status-badge.not-in-stock-card {
- background: rgba(239, 68, 68, 0.3);
- border: 1px solid #ef4444;
- color: #fca5a5;
- }
- .status-badge.in-stock-card {
- background: rgba(16, 185, 129, 0.3);
- border: 1px solid #10b981;
- color: #6ee7b7;
- }
- </style>
|