|
|
@@ -0,0 +1,1203 @@
|
|
|
+<!-- 出库确认 - 智能仓储风格 -->
|
|
|
+<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();
|
|
|
+
|
|
|
+// 操作的id
|
|
|
+const operationId = ref(null);
|
|
|
+
|
|
|
+// 返回主页
|
|
|
+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 => {
|
|
|
+ operationId.value = record.inventoryId;
|
|
|
+ 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: '申请领料成功' });
|
|
|
+ operationId.value = record.inventoryId;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ await handleComplete();
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ materialList.value.map(item => {
|
|
|
+ return {
|
|
|
+ stockOutPrepareLineId: item.stockOutPrepareLineId,
|
|
|
+ stockOutId: item.stockOutId,
|
|
|
+ inventoryId: item.inventoryId,
|
|
|
+ };
|
|
|
+ });
|
|
|
+ loading.value = true;
|
|
|
+ try {
|
|
|
+ const res = await cfStockOutLeave(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('领料离开API调用失败:', error);
|
|
|
+ showNotify({ type: 'danger', message: '领料离开API调用失败' });
|
|
|
+ } finally {
|
|
|
+ loading.value = false;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// 获取扫描到的领料数据
|
|
|
+const getStockOutList = async () => {
|
|
|
+ const params = {
|
|
|
+ epcList: [],
|
|
|
+ };
|
|
|
+
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ 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.status = 1;
|
|
|
+ j.queryStatus = 2;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ });
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ showNotify({ type: 'danger', message: res.errorMessage });
|
|
|
+ }
|
|
|
+ computerErrorData();
|
|
|
+ } catch (error) {
|
|
|
+ console.error('获取扫描到的领料数据API调用失败:', error);
|
|
|
+ showNotify({ type: 'danger', message: '获取扫描到的领料数据API调用失败' });
|
|
|
+ } 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 handleComplete = async () => {
|
|
|
+ const params = needConfirmStockInMaterialList.value.map(item => {
|
|
|
+ return item.stockInId;
|
|
|
+ });
|
|
|
+
|
|
|
+ console.log('提交参数:', params);
|
|
|
+ if(params.length > 0){
|
|
|
+ await generateCFStockIn(params);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+// 还料离开(回填归还入库单入库时间)
|
|
|
+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 || needConfirmStockInMaterialList.value.length === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!materialList.value || materialList.value.length === 0) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ needConfirmStockInMaterialList.value.forEach(item => {
|
|
|
+ item.isValid = true;
|
|
|
+ });
|
|
|
+ materialList.value.forEach(i => {
|
|
|
+ i.isValid = true;
|
|
|
+ });
|
|
|
+
|
|
|
+ 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>
|