| | |
| | | <template> |
| | | <view> |
| | | <scroll-view scroll-y catch:touchmove="touchmove"> |
| | | <!-- 托盘条码 --> |
| | | <view class="square-2"> |
| | | <view class="square-title"> |
| | | <view class="title-sign"><view class="sign"></view></view> |
| | | <view class="title-text"><text>托盘条码</text></view> |
| | | <view class="page-container"> |
| | | <scroll-view scroll-y class="page-scroll"> |
| | | |
| | | <!-- 1. Pallet Barcode Section --> |
| | | <view class="card-box"> |
| | | <view class="card-header"> |
| | | <view class="header-indicator"></view> |
| | | <text class="header-title">Pallet Barcode</text> |
| | | </view> |
| | | <view class="square-content"> |
| | | <view class="content-input"> |
| | | <view class="card-body"> |
| | | <view class="input-wrapper"> |
| | | <input |
| | | v-model="barcode" |
| | | type="text" |
| | | placeholder="扫码 / 输入" |
| | | maxlength="10" |
| | | placeholder="Scan Pallet Barcode" |
| | | maxlength="20" |
| | | :focus="barcodeFocus" |
| | | @input="barcodeInput" |
| | | placeholder-style="line-height: 85rpx;" |
| | | @confirm="barcodeInput" |
| | | class="custom-input" |
| | | /> |
| | | <uni-icons type="closeempty" size="20" color="#dadada" @click="removeBarcode" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <!-- 库位条码 --> |
| | | <view class="square-2"> |
| | | <view class="square-title"> |
| | | <view class="title-sign"><view class="sign"></view></view> |
| | | <view class="title-text"><text>库位条码</text></view> |
| | | </view> |
| | | <view class="square-content"> |
| | | <view class="content-input"> |
| | | <input |
| | | v-model="locNo" |
| | | type="text" |
| | | placeholder="扫码 / 输入" |
| | | maxlength="7" |
| | | :focus="locNoFocus" |
| | | @input="locNoInput" |
| | | placeholder-style="line-height: 85rpx;" |
| | | /> |
| | | <uni-icons type="closeempty" size="20" color="#dadada" @click="removeLocNo" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 商品条码 --> |
| | | <view class="square-2"> |
| | | <view class="square-title"> |
| | | <view class="title-sign"><view class="sign"></view></view> |
| | | <view class="title-text"><text>商品条码</text></view> |
| | | </view> |
| | | <view class="square-content"> |
| | | <view class="content-input"> |
| | | <input |
| | | v-model="code" |
| | | type="text" |
| | | placeholder="扫码 / 输入" |
| | | :focus="codeFocus" |
| | | @input="codeInput" |
| | | placeholder-style="line-height: 85rpx;" |
| | | /> |
| | | <uni-icons type="closeempty" size="20" color="#dadada" @click="removeCode" /> |
| | | <view class="icon-scan" @click="barcodeInput"> |
| | | <uni-icons type="scan" size="24" color="#007aff" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 商品列表头(含操作) --> |
| | | <view class="square-1"> |
| | | <view class="square-title"> |
| | | <view class="title-sign"><view class="sign"></view></view> |
| | | <view class="title-text" style="width: 200rpx;"><text>商品列表</text></view> |
| | | |
| | | <view v-show="matList.length" class="lable"> |
| | | <label class="label-btn" style="width: 170rpx;line-height: 95rpx;"> |
| | | <checkbox :checked="check" @click="allChecked">{{ checkText }}</checkbox> |
| | | <!-- 2. Scanning Options --> |
| | | <view class="card-box"> |
| | | <view class="card-header"> |
| | | <text class="header-title">Carton Scanning Option</text> |
| | | </view> |
| | | <view class="card-body"> |
| | | <radio-group @change="modeChange" class="radio-group-vertical"> |
| | | <label class="radio-row"> |
| | | <radio value="one" :checked="scanMode === 'one'" color="#007aff" style="transform:scale(0.8)" /> |
| | | <text class="radio-label">One Label per Carton</text> |
| | | </label> |
| | | <label class="label-btn"><text @click="reChecked">反选</text></label> |
| | | <label><uni-icons type="trash" size="25" color="#a5a5a5" @click="removeSelected" /></label> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="square-none" v-show="matList.length === 0"> |
| | | <view class="v-show">暂无更多数据...</view> |
| | | </view> |
| | | |
| | | <!-- 列表(使用 checkbox-group 统一管理) --> |
| | | <checkbox-group @change="checkboxChanged"> |
| | | <view |
| | | v-for="(item, index) in matList" |
| | | :key="item.id || index" |
| | | class="data-list bg-false" |
| | | :class="'bg-' + (item.checked ? 'true' : 'false')" |
| | | > |
| | | <label class="left-check-box"> |
| | | <checkbox :value="(item.id + '')" :checked="item.checked" style="display:block;" /> |
| | | </label> |
| | | |
| | | <view class="data-list-left"> |
| | | <view class="matnr"> |
| | | <text style="width: 500rpx;">编码:{{ item.matnr }}</text> |
| | | <text style="margin-left: 100rpx;">名称:{{ item.maktx }}</text> |
| | | </view> |
| | | <view> |
| | | <text style="width: 500rpx;">PO:{{ item.standby1 }}</text> |
| | | <text style="margin-left: 100rpx;">SKU:{{ item.standby3 }}</text> |
| | | </view> |
| | | <view><text style="width: 500rpx;">UPC:{{ item.standby2 }}</text></view> |
| | | <view> |
| | | <text style="width: 500rpx;">采购单:{{ item.boxType3 }}</text> |
| | | <text style="margin-left: 100rpx;"> |
| | | 数量:{{ item.current }} / {{ item.maxAnfme }} |
| | | </text> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="data-list-right"> |
| | | <label> |
| | | <uni-icons type="compose" size="20" color="#a5a5a5" @click="openRevise(item, index)" /> |
| | | <label class="radio-row"> |
| | | <radio value="multi" :checked="scanMode === 'multi'" color="#007aff" style="transform:scale(0.8)" /> |
| | | <text class="radio-label">Multiple Labels per Carton</text> |
| | | </label> |
| | | </radio-group> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 3. Scan Carton Label --> |
| | | <view class="card-box"> |
| | | <view class="card-header"> |
| | | <view class="header-indicator"></view> |
| | | <text class="header-title">Scan Carton Label</text> |
| | | </view> |
| | | <view class="card-body"> |
| | | <view class="input-row"> |
| | | <view class="input-wrapper flex-1"> |
| | | <input |
| | | v-model="code" |
| | | type="text" |
| | | placeholder="Scan Carton Label" |
| | | :focus="codeFocus" |
| | | @confirm="codeInput" |
| | | class="custom-input" |
| | | /> |
| | | </view> |
| | | <button class="cu-btn bg-blue shadow-blur sm margin-left-sm" @click="codeInput">Add</button> |
| | | </view> |
| | | </view> |
| | | </checkbox-group> |
| | | </view> |
| | | |
| | | <!-- 4. Zone Selection & Queue Action --> |
| | | <view class="card-box"> |
| | | <view class="card-body"> |
| | | <!-- Zone Selector --> |
| | | <view class="form-row"> |
| | | <text class="form-label">Zone:</text> |
| | | <picker @change="zoneChange" :value="zoneIndex" :range="zoneList" range-key="areaId" class="flex-1"> |
| | | <view class="picker-box"> |
| | | <text class="picker-text">{{ zoneList[zoneIndex] ? zoneList[zoneIndex].areaId : 'Select Zone' }}</text> |
| | | <uni-icons type="arrowdown" size="14" color="#666"></uni-icons> |
| | | </view> |
| | | </picker> |
| | | </view> |
| | | |
| | | <!-- Add Queue Button (Full Width) --> |
| | | <button class="cu-btn bg-blue block margin-top-sm lg shadow" @click="addToQueue"> |
| | | <text class="text-bold">ADD TO QUEUE</text> |
| | | </button> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 5. Data Tables --> |
| | | <view class="tables-section"> |
| | | |
| | | <!-- Left Table: Current Scanned --> |
| | | <view class="card-box no-padding"> |
| | | <view class="table-header-row"> |
| | | <text class="th col-2">Carton Label</text> |
| | | <text class="th col-1 text-center">Qty</text> |
| | | <text class="th col-05 text-center">Op</text> |
| | | </view> |
| | | <view class="table-body-scroll"> |
| | | <view v-for="(item, index) in matList" :key="index" class="table-row"> |
| | | <text class="td col-2 text-cut">{{ item.barcode }}</text> |
| | | <text class="td col-1 text-center">{{ item.anfme }}</text> |
| | | <view class="td col-05 text-center"> |
| | | <uni-icons type="trash" size="18" color="#dd524d" @click="removeCurrentItem(index)" /> |
| | | </view> |
| | | </view> |
| | | <view v-if="matList.length === 0" class="empty-state">No data</view> |
| | | </view> |
| | | <view class="table-footer-info"> |
| | | <text>Total Quantity: <text class="text-blue text-bold">{{ totalQty }}</text></text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- Right Table: Queue List --> |
| | | <view class="card-box no-padding margin-top-sm"> |
| | | <view class="card-header small-header"> |
| | | <text class="header-title text-sm">Queue List</text> |
| | | </view> |
| | | <scroll-view scroll-y class="queue-list-scroll"> |
| | | <view v-for="(qItem, qIndex) in queueList" :key="qIndex" class="queue-card"> |
| | | <view class="queue-header"> |
| | | <view class="flex align-center"> |
| | | <uni-icons type="box" size="16" color="#007aff" class="margin-right-xs"/> |
| | | <text class="text-bold text-dark">{{ qItem.barcode }}</text> |
| | | </view> |
| | | <view class="flex align-center"> |
| | | <text class="cu-tag bg-cyan light sm margin-right-xs">{{ qItem.zoneName }}</text> |
| | | <uni-icons type="closeempty" size="18" color="#999" @click="removeFromQueue(qIndex)" /> |
| | | </view> |
| | | </view> |
| | | <view class="queue-content"> |
| | | <view v-for="(cItem, cIndex) in qItem.cartons" :key="cIndex" class="queue-detail-row"> |
| | | <text class="text-grey">Label:{{ cItem.barcode }}</text> |
| | | <text class="text-grey">ItemNo:{{ cItem.matnr }}</text> |
| | | <text class="text-grey">x{{ cItem.anfme }}</text> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | <view v-if="queueList.length === 0" class="empty-state">Queue is empty</view> |
| | | </scroll-view> |
| | | </view> |
| | | |
| | | </view> |
| | | |
| | | <view style="height: 140rpx;"></view> |
| | | </scroll-view> |
| | | |
| | | <!-- 底部操作 --> |
| | | <view class="footer flex justify-around"> |
| | | <label class="label-btn flex justify-center align-center"> |
| | | <button class="cu-btn" @click="resst">重置</button> |
| | | </label> |
| | | <label class="label-btn flex justify-center align-center"> |
| | | <button class="cu-btn bg-blue" @click="comb">组托</button> |
| | | </label> |
| | | <!-- Bottom Footer --> |
| | | <view class="footer-actions"> |
| | | <button class="cu-btn bg-blue shadow-blur lg flex-1 margin-right-sm" @click="moveToASRS">MOVE TO ASRS</button> |
| | | <button class="cu-btn line-grey lg flex-1" @click="cancelAll">X CANCEL</button> |
| | | </view> |
| | | |
| | | <!-- 修改数量弹窗 --> |
| | | <uni-popup ref="revise" background-color="#fff" @change="change"> |
| | | <view class="revise-box"> |
| | | <view class="revise-box-top"> |
| | | <view class="color-block-blue"></view> |
| | | <text class="title">组托数量</text> |
| | | </view> |
| | | |
| | | <view class="text-box"> |
| | | <text>可组数量:{{ enableQty }}</text> |
| | | </view> |
| | | |
| | | <view class="changeBox flex justify-around"> |
| | | <view class="num-box"> |
| | | <uni-number-box v-model="count" :min="minCount" :max="maxCount" color="#747474" @change="changeValue" /> |
| | | </view> |
| | | </view> |
| | | |
| | | <view class="revise-box-buttom"> |
| | | <view> |
| | | <button class="cu-btn bg-blue" @click="confirm">确认</button> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </uni-popup> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | data() { |
| | | return { |
| | | commonUrl: null, |
| | | |
| | | // Pallet |
| | | barcode: "", |
| | | barcodeFocus: true, |
| | | focus: false, |
| | | type: "center", |
| | | order: null, |
| | | matList: [], // 每个 item: { id, matnr, maktx, standby1, standby2, standby3, boxType3, current, maxAnfme, checked } |
| | | count: 0, |
| | | minCount: 0, |
| | | maxCount: 0, |
| | | rowNum: null, |
| | | enableQty: 0, |
| | | check: false, |
| | | checkText: "全选", |
| | | orderNo: "", |
| | | orderNoList: [], |
| | | showDropdown: false, |
| | | |
| | | // Scan Mode |
| | | scanMode: "multi", // 'one' | 'multi' |
| | | |
| | | // Carton Input |
| | | code: "", |
| | | codeFocus: true, |
| | | locNo: "", |
| | | locNoFocus: true, |
| | | codeFocus: false, |
| | | |
| | | // Zone |
| | | zoneList: [], |
| | | zoneIndex: -1, |
| | | |
| | | // Current List (Left Table) |
| | | matList: [], // { matnr: string, qty: number } |
| | | |
| | | // Queue List (Right Table) |
| | | queueList: [], // { barcode: string, zoneId: string, zoneName: string, cartons: [] } |
| | | |
| | | // System |
| | | baseIP: "", |
| | | basePORT: "", |
| | | baseUrl: "", |
| | | }; |
| | | }, |
| | | computed: { |
| | | totalQty() { |
| | | return this.matList.reduce((sum, item) => sum + (Number(item.qty) || 0), 0); |
| | | } |
| | | }, |
| | | mounted() { |
| | | const UIP = uni.getStorageSync("UIP"); |
| | |
| | | const PROJ = uni.getStorageSync("UPROJ"); |
| | | this.baseUrl = PROJ; |
| | | this.getUrl(); |
| | | |
| | | // Load Zones |
| | | this.getZoneList(); |
| | | }, |
| | | methods: { |
| | | // 构建 base url |
| | | getUrl() { |
| | | this.commonUrl = this.baseHttp + this.baseIP + ":" + this.basePORT + "/" + this.baseUrl; |
| | | }, |
| | | |
| | | /* ------------------------- |
| | | 获取订单明细(接口返回 combMats) |
| | | 初始化每个 item 的 current / maxAnfme / checked |
| | | ------------------------- */ |
| | | getOrderDetlByOrderNo() { |
| | | if (!this.orderNo) { |
| | | uni.showToast({ title: "请选择订单", icon: "none" }); |
| | | return; |
| | | } |
| | | const that = this; |
| | | // 1. Get Zone List |
| | | getZoneList() { |
| | | uni.showLoading({ title: "Loading Zones..." }); |
| | | uni.request({ |
| | | url: that.commonUrl + "/mobile/order/search/orderNo/auth", |
| | | data: { orderNo: that.orderNo }, |
| | | header: { token: uni.getStorageSync("token") }, |
| | | success(result) { |
| | | const res = result.data; |
| | | if (res.code === 200 && res.data && res.data[0]) { |
| | | uni.showLoading(); |
| | | // 原始 combMats 可能包含 backend 的 anfme,作为 max 使用 |
| | | that.matList = (res.data[0].combMats || []).map(it => { |
| | | // 规范字段名(boxType3 / boxtype3) |
| | | it.boxType3 = it.boxType3 || it.boxtype3 || ""; |
| | | return { |
| | | ...it, |
| | | maxAnfme: Number(it.anfme || 0), // 后端最大可组托数量 |
| | | current: 0, // 当前已组数量(前端维护) |
| | | checked: false, |
| | | }; |
| | | }); |
| | | that.initAnfme(); |
| | | uni.hideLoading(); |
| | | } else if (res.code == 403) { |
| | | uni.showToast({ title: res.msg, icon: "none", position: "top" }); |
| | | setTimeout(() => uni.reLaunch({ url: "../login/login" }), 1000); |
| | | } else { |
| | | uni.showToast({ title: res.msg || "获取失败", icon: "none" }); |
| | | } |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | | // 初始化:把后端的 anfme 保存为 enableQty(显示用),并把 current 置 0 |
| | | initAnfme() { |
| | | for (let i = 0; i < this.matList.length; i++) { |
| | | this.matList[i].enableQty = this.matList[i].maxAnfme || 0; |
| | | // ensure current exists |
| | | if (typeof this.matList[i].current !== "number") this.matList[i].current = 0; |
| | | if (typeof this.matList[i].checked !== "boolean") this.matList[i].checked = false; |
| | | } |
| | | this.checkList(); |
| | | }, |
| | | |
| | | /* ------------------------- |
| | | 扫码/输入逻辑 |
| | | barcodeInput: 托盘码(校验长度) |
| | | codeInput: 商品码(解析并请求明细接口 / 合并到 matList) |
| | | ------------------------- */ |
| | | barcodeInput() { |
| | | const len = this.barcode.length; |
| | | if (len !== 6) { |
| | | // 6 位托盘码规则:若非 6 位则不给予进一步处理,但不要频繁弹框,只有明确错误时弹 |
| | | // 这里保持原判定逻辑:如果长度不是 6 则提示 |
| | | uni.showToast({ title: "托盘码有误请重试", icon: "none", position: "top" }); |
| | | this.barcodeFocuss(); |
| | | return; |
| | | } |
| | | // 若长度合规,可聚焦到商品输入 |
| | | this.focuss(); |
| | | }, |
| | | locNoInput() { |
| | | const len = this.locNo.length; |
| | | if (len !== 7) { |
| | | // 6 位托盘码规则:若非 6 位则不给予进一步处理,但不要频繁弹框,只有明确错误时弹 |
| | | // 这里保持原判定逻辑:如果长度不是 6 则提示 |
| | | uni.showToast({ title: "库位有误请重试", icon: "none", position: "top" }); |
| | | this.locNoFocuss(); |
| | | return; |
| | | } |
| | | // 若长度合规,可聚焦到商品输入 |
| | | this.focuss(); |
| | | }, |
| | | |
| | | codeInput() { |
| | | const that = this; |
| | | const m = (that.code || "").split(";"); |
| | | if (!m[1] || !m[5] || !m[7] || !m[9]) { |
| | | uni.showToast({ title: "条码有误", icon: "none", position: "top" }); |
| | | this.codeFocuss(); |
| | | return; |
| | | } |
| | | const supplier = m[10] === "supplier" ? m[11] : "1"; |
| | | |
| | | uni.request({ |
| | | url: that.commonUrl + "/mobile/order/search/orderDetl/auth", |
| | | method: "POST", |
| | | header: { token: uni.getStorageSync("token") }, |
| | | data: JSON.stringify({ |
| | | orderNo: m[1], |
| | | sku: m[5], |
| | | item: m[7], |
| | | upc: m[9], |
| | | supplier: supplier, // ✅ 永远有值 |
| | | }), |
| | | method: "POST", |
| | | header: { token: uni.getStorageSync("token") }, |
| | | success(result) { |
| | | const res = result.data; |
| | | if (res.code === 200 && res.data && Array.isArray(res.data.combMats)) { |
| | | |
| | | const firstBoxType = that.matList[0]?.boxType3; // 第一个物料的 boxType3 |
| | | let inconsistent = false; |
| | | |
| | | res.data.combMats.forEach(serverItem => { |
| | | serverItem.boxType3 = serverItem.boxType3 || serverItem.boxtype3 || ""; |
| | | |
| | | // ❌ 检查 boxType3 是否一致 |
| | | if (firstBoxType && serverItem.boxType3 !== firstBoxType) { |
| | | inconsistent = true; |
| | | return; |
| | | } |
| | | |
| | | const max = Number(serverItem.anfme || 0); |
| | | if (max <= 0) { |
| | | uni.showToast({ title: "当前物料单据无数据", icon: "none" }); |
| | | return; |
| | | } |
| | | |
| | | const existItem = that.matList.find( |
| | | m => |
| | | m.matnr === serverItem.matnr && |
| | | (m.standby2 || "") === (serverItem.standby2 || "") && |
| | | (m.standby3 || "") === (serverItem.standby3 || "") && |
| | | (m.boxType3 || "") === (serverItem.boxType3 || "") |
| | | ); |
| | | |
| | | if (existItem) { |
| | | existItem.current = Math.min(existItem.current + 1, existItem.maxAnfme); |
| | | } else { |
| | | that.matList.push({ |
| | | ...serverItem, |
| | | maxAnfme: max, |
| | | current: 1, |
| | | checked: false, |
| | | }); |
| | | } |
| | | }); |
| | | |
| | | if (inconsistent) { |
| | | uni.showToast({ title: "单据不一致,请检查", icon: "none", position: "top" }); |
| | | that.codeFocuss(); |
| | | return; |
| | | } |
| | | |
| | | that.codeFocuss(); |
| | | that.checkList(); |
| | | } else if (res.code == 403) { |
| | | uni.showToast({ title: res.msg, icon: "none", position: "top" }); |
| | | setTimeout(() => uni.reLaunch({ url: "../login/login" }), 1000); |
| | | } else { |
| | | uni.showToast({ title: res.msg || "获取条码商品失败", icon: "none" }); |
| | | } |
| | | }, |
| | | fail() { |
| | | uni.showToast({ title: "请求失败", icon: "none" }); |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | | |
| | | // 失焦 / 重置焦点的方法 |
| | | codeFocuss() { |
| | | this.codeFocus = false; |
| | | setTimeout(() => { |
| | | this.code = ""; |
| | | this.codeFocus = true; |
| | | }, 100); |
| | | }, |
| | | locNoFocuss() { |
| | | this.locNoFocus = false; |
| | | setTimeout(() => { |
| | | this.locNo = ""; |
| | | this.locNoFocus = true; |
| | | }, 100); |
| | | }, |
| | | barcodeFocuss() { |
| | | this.barcodeFocus = false; |
| | | setTimeout(() => { |
| | | this.barcode = ""; |
| | | this.barcodeFocus = true; |
| | | }, 100); |
| | | }, |
| | | focuss() { |
| | | this.focus = false; |
| | | setTimeout(() => { |
| | | this.matnrId = ""; |
| | | this.focus = true; |
| | | }, 100); |
| | | }, |
| | | |
| | | /* ------------------------- |
| | | 编辑数量弹窗(打开/确认) |
| | | ------------------------- */ |
| | | openRevise(item, index) { |
| | | // 更新 maxCount / enableQty / count |
| | | this.rowNum = index; |
| | | this.enableQty = item.maxAnfme || 0; |
| | | this.minCount = 0; |
| | | this.maxCount = item.maxAnfme || 0; |
| | | this.count = item.current || 0; |
| | | this.$refs.revise.open(); |
| | | }, |
| | | |
| | | confirm() { |
| | | // 限制范围然后写回 matList |
| | | const idx = this.rowNum; |
| | | if (idx == null || !this.matList[idx]) { |
| | | this.$refs.revise.close(); |
| | | return; |
| | | } |
| | | let v = Number(this.count) || 0; |
| | | v = Math.max(0, Math.min(v, this.matList[idx].maxAnfme || 0)); |
| | | this.matList[idx].current = v; |
| | | this.$refs.revise.close(); |
| | | }, |
| | | |
| | | changeValue() { |
| | | // 可在这里添加实时校验(当前使用 uni-number-box 自带 min/max) |
| | | }, |
| | | |
| | | /* ------------------------- |
| | | 组托(提交) |
| | | ------------------------- */ |
| | | comb() { |
| | | uni.vibrateShort(); |
| | | const that = this; |
| | | |
| | | if (!that.barcode) { |
| | | uni.showToast({ title: "请扫描托盘条码", icon: "none", position: "top" }); |
| | | return; |
| | | } |
| | | if (!that.locNo) { |
| | | uni.showToast({ title: "请扫描库位条码", icon: "none", position: "top" }); |
| | | return; |
| | | } |
| | | if (that.barcode.length !== 6) { |
| | | uni.showToast({ title: "托盘码必须为6位", icon: "none", position: "top" }); |
| | | return; |
| | | } |
| | | if (!that.matList.length) { |
| | | uni.showToast({ title: "请添加商品列表", icon: "none", position: "top" }); |
| | | return; |
| | | } |
| | | |
| | | // 只取 current > 0 的条目,并映射为后端需要的字段(注意这里仍使用后端期望字段名 anfme) |
| | | const validMats = that.matList |
| | | .filter(item => Number(item.current || 0) > 0) |
| | | .map(item => { |
| | | return { |
| | | ...item, |
| | | anfme: Number(item.current || 0), |
| | | }; |
| | | }); |
| | | |
| | | if (!validMats.length) { |
| | | uni.showToast({ title: "所有商品组托数量为0,无法组托", icon: "none", position: "top" }); |
| | | return; |
| | | } |
| | | |
| | | // 更新界面列表为仅剩有效条目(删除数量为0的) |
| | | that.matList = that.matList.filter(item => Number(item.current || 0) > 0); |
| | | |
| | | uni.showLoading(); |
| | | uni.request({ |
| | | url: that.commonUrl + "/mobile/comb/agv/auth", |
| | | data: JSON.stringify({ |
| | | billNo: that.orderNo, |
| | | orderNo: that.orderNo, |
| | | barcode: that.barcode, |
| | | locNo: that.locNo, |
| | | combMats: validMats, |
| | | }), |
| | | url: this.commonUrl + "/area/list/auth", |
| | | method: "POST", |
| | | data: { |
| | | }, |
| | | header: { token: uni.getStorageSync("token") }, |
| | | success(result) { |
| | | success: res => { |
| | | uni.hideLoading(); |
| | | const res = result.data; |
| | | if (res.code === 200) { |
| | | uni.showToast({ title: res.msg, position: "top", duration: 1000 }); |
| | | that.resst(); |
| | | } else if (res.code == 403) { |
| | | uni.showToast({ title: res.msg, icon: "none", position: "top" }); |
| | | setTimeout(() => uni.reLaunch({ url: "../login/login" }), 1000); |
| | | const r = res.data; |
| | | if (r.code === 200) { |
| | | this.zoneList = (r.data && r.data.records) ? r.data.records : []; |
| | | // Default select first if available |
| | | if (this.zoneList.length > 0) { |
| | | this.zoneIndex = 0; |
| | | } |
| | | } else if (r.code === 403) { |
| | | uni.showToast({ title: r.msg || "Re-login required", icon: "none" }); |
| | | setTimeout(() => { |
| | | uni.reLaunch({ url: "/pages/login/login" }); |
| | | }, 1000); |
| | | } else { |
| | | uni.showToast({ title: res.msg || "组托失败", icon: "none" }); |
| | | uni.showToast({ title: r.msg || "Fetch failed", icon: "none" }); |
| | | } |
| | | }, |
| | | fail() { |
| | | uni.hideLoading(); |
| | | uni.showToast({ title: "请求失败", icon: "none" }); |
| | | uni.showToast({ title: "Request failed", icon: "none" }); |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | | /* ------------------------- |
| | | 删除已选 / 重置 |
| | | ------------------------- */ |
| | | removeSelected() { |
| | | this.matList = this.matList.filter(item => !item.checked); |
| | | this.checkList(); |
| | | uni.vibrateShort(); |
| | | // 2. Zone Change |
| | | zoneChange(e) { |
| | | this.zoneIndex = e.detail.value; |
| | | }, |
| | | |
| | | resst() { |
| | | this.matList = []; |
| | | this.barcode = ""; |
| | | this.locNo = "" |
| | | this.order = ""; |
| | | this.orderNo = ""; |
| | | this.barcodeFocuss(); |
| | | uni.vibrateShort(); |
| | | this.checkList(); |
| | | // 3. Mode Change |
| | | modeChange(e) { |
| | | this.scanMode = e.detail.value; |
| | | // If switching to 'one' and we have > 1 items, warn or clear? |
| | | // Requirement says: "One label per carton... only allows one data" |
| | | if (this.scanMode === 'one' && this.matList.length > 1) { |
| | | uni.showToast({ title: "Switched to One Label mode. Clearing extra items.", icon: "none" }); |
| | | this.matList = [this.matList[0]]; |
| | | } |
| | | }, |
| | | |
| | | /* ------------------------- |
| | | checkbox 相关(统一管理:checkbox-group -> change) |
| | | ------------------------- */ |
| | | checkboxChanged(e) { |
| | | const values = e.detail.value || []; |
| | | this.matList.forEach(item => { |
| | | item.checked = values.includes(item.id + ""); |
| | | // 4. Pallet Barcode Input |
| | | barcodeInput() { |
| | | if (!this.barcode) return; |
| | | // Validate if needed |
| | | this.barcodeFocus = false; |
| | | this.$nextTick(() => { |
| | | this.codeFocus = true; // Jump to carton scan |
| | | }); |
| | | this.checkList(); |
| | | uni.vibrateShort(); |
| | | }, |
| | | |
| | | // 反选 |
| | | reChecked() { |
| | | if (!this.matList.length) return; |
| | | this.matList.forEach(item => { |
| | | item.checked = !item.checked; |
| | | }); |
| | | this.checkList(); |
| | | uni.vibrateShort(); |
| | | }, |
| | | |
| | | // 全选 / 取消全选 |
| | | allChecked() { |
| | | this.check = !this.check; |
| | | this.matList.forEach(item => (item.checked = this.check)); |
| | | this.checkText = this.check ? "取消全选" : "全选"; |
| | | uni.vibrateShort(); |
| | | }, |
| | | |
| | | // 更新页面顶部全选文案状态 |
| | | checkList() { |
| | | if (!this.matList.length) { |
| | | this.check = false; |
| | | this.checkText = "全选"; |
| | | // 5. Carton Code Input |
| | | codeInput() { |
| | | if (!this.code) { |
| | | this.codeFocus = false; |
| | | this.$nextTick(() => this.codeFocus = true); |
| | | return; |
| | | } |
| | | const all = this.matList.every(item => item.checked); |
| | | this.check = all; |
| | | this.checkText = all ? "取消全选" : "全选"; |
| | | }, |
| | | |
| | | /* ------------------------- |
| | | 根据输入查订单建议(未改动) |
| | | ------------------------- */ |
| | | getOrderDet(orderNo) { |
| | | if (!orderNo || orderNo.trim() === "") return; |
| | | // Check Mode Constraints |
| | | if (this.scanMode === 'one') { |
| | | if (this.matList.length >= 1) { |
| | | uni.showToast({ title: "One Label Mode: Only 1 carton allowed per pallet.", icon: "none" }); |
| | | this.code = ""; |
| | | this.codeFocus = false; |
| | | this.$nextTick(() => this.codeFocus = true); |
| | | return; |
| | | } |
| | | } |
| | | |
| | | if (this.queueList.some(q => q.barcode === this.barcode)) { |
| | | uni.showToast({ title: "Pallet Barcode has been in queue", icon: "none" }); |
| | | this.code = ""; |
| | | this.codeFocus = false; |
| | | this.$nextTick(() => this.codeFocus = true); |
| | | return; |
| | | } |
| | | |
| | | const cartonLabel = this.code; |
| | | if (this.queueList.some(q => (q.cartons || []).some(c => c.barcode === cartonLabel))) { |
| | | uni.showToast({ title: "Label has been in queue", icon: "none" }); |
| | | this.code = ""; |
| | | this.codeFocus = false; |
| | | this.$nextTick(() => this.codeFocus = true); |
| | | return; |
| | | } |
| | | uni.showLoading({ title: "Scanning..." }); |
| | | |
| | | uni.request({ |
| | | url: this.commonUrl + "/mobile/order/search/orderNoList/auth", |
| | | method: "GET", |
| | | data: { orderNo }, |
| | | header: { token: uni.getStorageSync("token") }, |
| | | success: res => { |
| | | const list = res.data.data || []; |
| | | this.orderNoList = list.map(o => o.orderNo); |
| | | url: this.commonUrl + "/mobile/cartonScan/auth", |
| | | method: "POST", |
| | | data: { |
| | | cartonLabel: cartonLabel |
| | | }, |
| | | }); |
| | | }, |
| | | |
| | | onOrderSelect(order) { |
| | | this.orderNo = order; |
| | | }, |
| | | |
| | | findOrder() { |
| | | if (!this.order) return; |
| | | const that = this; |
| | | uni.request({ |
| | | url: that.commonUrl + "/mobile/order/search/orderNo/auth", |
| | | data: { orderNo: that.order }, |
| | | header: { token: uni.getStorageSync("token") }, |
| | | success(result) { |
| | | const res = result.data; |
| | | if (res.code === 200 && res.data && res.data[0]) { |
| | | uni.showLoading(); |
| | | that.matList = (res.data[0].combMats || []).map(it => { |
| | | it.boxType3 = it.boxType3 || it.boxtype3 || ""; |
| | | return { |
| | | ...it, |
| | | maxAnfme: Number(it.anfme || 0), |
| | | current: 0, |
| | | checked: false, |
| | | }; |
| | | }); |
| | | that.orderNo = that.order; |
| | | that.initAnfme(); |
| | | success: (res) => { |
| | | uni.hideLoading(); |
| | | } else if (res.code == 403) { |
| | | uni.showToast({ title: res.msg, icon: "none", position: "top" }); |
| | | setTimeout(() => uni.reLaunch({ url: "../login/login" }), 1000); |
| | | } else { |
| | | uni.showToast({ title: res.msg || "查无数据", icon: "none" }); |
| | | } |
| | | const r = res.data; |
| | | if (r.code === 200) { |
| | | const data = r.data || {}; |
| | | const newItem = { |
| | | matnr: data.matnr || data.cartonLabel || cartonLabel, |
| | | barcode: data.barcode || cartonLabel, |
| | | cartonLabel:data.barcode || cartonLabel, |
| | | anfme: data.anfme || 0, |
| | | orderNo: data.orderNo, // Store orderNo from API |
| | | ...data // Store other fields |
| | | }; |
| | | |
| | | // Check if exists in current list |
| | | const exist = this.matList.find(m => m.barcode === newItem.barcode); |
| | | if (exist) { |
| | | exist.anfme = newItem.anfme; |
| | | } else { |
| | | this.matList.push(newItem); |
| | | } |
| | | |
| | | uni.vibrateShort(); |
| | | } else if (r.code === 403) { |
| | | uni.showToast({ title: r.msg || "Re-login required", icon: "none" }); |
| | | setTimeout(() => { |
| | | uni.reLaunch({ url: "/pages/login/login" }); |
| | | }, 1000); |
| | | } else { |
| | | uni.showToast({ title: r.msg || "Scan failed", icon: "none" }); |
| | | } |
| | | |
| | | // Clear and Refocus |
| | | this.code = ""; |
| | | this.codeFocus = false; |
| | | this.$nextTick(() => this.codeFocus = true); |
| | | }, |
| | | fail: () => { |
| | | uni.hideLoading(); |
| | | uni.showToast({ title: "Request failed", icon: "none" }); |
| | | this.code = ""; |
| | | this.codeFocus = false; |
| | | this.$nextTick(() => this.codeFocus = true); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | removeCurrentItem(index) { |
| | | this.matList.splice(index, 1); |
| | | }, |
| | | |
| | | // 6. Add to Queue |
| | | addToQueue() { |
| | | if (!this.barcode) { |
| | | uni.showToast({ title: "Please scan Pallet Barcode", icon: "none" }); |
| | | return; |
| | | } |
| | | if (this.matList.length === 0) { |
| | | uni.showToast({ title: "Please scan Carton Labels", icon: "none" }); |
| | | return; |
| | | } |
| | | if (this.zoneIndex < 0 || !this.zoneList[this.zoneIndex]) { |
| | | uni.showToast({ title: "Please select a Zone", icon: "none" }); |
| | | return; |
| | | } |
| | | |
| | | const zone = this.zoneList[this.zoneIndex]; |
| | | |
| | | // Extract orderNo from first carton if available (assuming same order per pallet or taking first) |
| | | const firstCarton = this.matList[0] || {}; |
| | | const orderNo = firstCarton.orderNo || ""; |
| | | |
| | | this.queueList.push({ |
| | | barcode: this.barcode, |
| | | zoneId: zone.id, // Submit ID |
| | | zoneName: zone.areaId, // Display areaId |
| | | orderNo: orderNo, // Add orderNo |
| | | cartons: JSON.parse(JSON.stringify(this.matList)) // Deep copy |
| | | }); |
| | | |
| | | // Clear Inputs |
| | | this.barcode = ""; |
| | | this.matList = []; |
| | | this.barcodeFocus = true; // Focus back to pallet |
| | | |
| | | uni.showToast({ title: "Added to Queue", icon: "success" }); |
| | | }, |
| | | |
| | | removeFromQueue(index) { |
| | | this.queueList.splice(index, 1); |
| | | }, |
| | | |
| | | // 7. Move to ASRS (Submit) |
| | | moveToASRS() { |
| | | if (this.queueList.length === 0) { |
| | | uni.showToast({ title: "Queue is empty", icon: "none" }); |
| | | return; |
| | | } |
| | | |
| | | const that = this; |
| | | uni.showLoading({ title: "Submitting..." }); |
| | | |
| | | // Submit to API |
| | | uni.request({ |
| | | url: that.commonUrl + "/mobile/cartonComb/auth", |
| | | method: "POST", |
| | | data: that.queueList, |
| | | header: { |
| | | token: uni.getStorageSync("token"), |
| | | 'content-type': 'application/json' |
| | | }, |
| | | success: (res) => { |
| | | uni.hideLoading(); |
| | | const r = res.data; |
| | | if (r.code === 200) { |
| | | uni.showToast({ title: "Moved to ASRS Successfully", icon: "success" }); |
| | | that.queueList = []; // Clear Queue |
| | | that.cancelAll(); // Reset form |
| | | } else if (r.code === 403) { |
| | | uni.showToast({ title: r.msg || "Re-login required", icon: "none" }); |
| | | setTimeout(() => { |
| | | uni.reLaunch({ url: "/pages/login/login" }); |
| | | }, 1000); |
| | | } else { |
| | | uni.showToast({ title: r.msg || "Submission failed", icon: "none" }); |
| | | } |
| | | }, |
| | | fail: () => { |
| | | uni.hideLoading(); |
| | | uni.showToast({ title: "Network request failed", icon: "none" }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 移除 code / barcode 等小工具 |
| | | removeBarcode() { |
| | | this.barcode = ""; |
| | | uni.vibrateShort(); |
| | | this.barcodeFocus = false; |
| | | this.$nextTick(() => (this.barcodeFocus = true)); |
| | | }, |
| | | removeLocNo() { |
| | | this.locNo = ""; |
| | | uni.vibrateShort(); |
| | | this.locNoFocus = false; |
| | | this.$nextTick(() => (this.locNoFocus = true)); |
| | | }, |
| | | removeCode() { |
| | | this.code = ""; |
| | | uni.vibrateShort(); |
| | | this.codeFocus = false; |
| | | this.$nextTick(() => (this.codeFocus = true)); |
| | | }, |
| | | }, |
| | | // 8. Cancel |
| | | cancelAll() { |
| | | this.barcode = ""; |
| | | this.matList = []; |
| | | this.queueList = []; // User said "Cancel ... clear queue list" |
| | | this.code = ""; |
| | | this.scanMode = "multi"; // Reset default? |
| | | this.barcodeFocus = true; |
| | | } |
| | | } |
| | | }; |
| | | </script> |
| | | |
| | | <style> |
| | | /* 你的样式我保留并做小幅清理 */ |
| | | .square-1 .lable { |
| | | display: inline-block; |
| | | float: right; |
| | | <style scoped> |
| | | .page-container { |
| | | background-color: #f1f1f1; |
| | | height: 100vh; |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .page-scroll { |
| | | flex: 1; |
| | | padding: 20rpx; |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | /* Card Styling */ |
| | | .card-box { |
| | | background-color: #ffffff; |
| | | border-radius: 12rpx; |
| | | margin-bottom: 20rpx; |
| | | box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.03); |
| | | overflow: hidden; |
| | | } |
| | | |
| | | .card-header { |
| | | padding: 20rpx; |
| | | border-bottom: 1rpx solid #f0f0f0; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .header-indicator { |
| | | width: 8rpx; |
| | | height: 28rpx; |
| | | background-color: #007aff; |
| | | border-radius: 4rpx; |
| | | margin-right: 16rpx; |
| | | } |
| | | |
| | | .header-title { |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .small-header { |
| | | padding: 15rpx 20rpx; |
| | | background-color: #fafafa; |
| | | } |
| | | |
| | | .card-body { |
| | | padding: 20rpx; |
| | | } |
| | | |
| | | /* Inputs */ |
| | | .input-wrapper { |
| | | background-color: #f5f7fa; |
| | | border-radius: 8rpx; |
| | | padding: 0 20rpx; |
| | | height: 80rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | border: 1rpx solid #e0e0e0; |
| | | } |
| | | |
| | | .custom-input { |
| | | flex: 1; |
| | | height: 100%; |
| | | width: 400rpx; |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | } |
| | | .square-1 .lable label { |
| | | display: inline-block; |
| | | float: left; |
| | | height: 100%; |
| | | width: 90rpx; |
| | | line-height: 100rpx; |
| | | } |
| | | .pak-seach-box { |
| | | background-color: #FFFFFF; |
| | | margin: 15rpx 15rpx 0rpx 15rpx; |
| | | width: 96%; |
| | | height: 150rpx; |
| | | border-radius: 20rpx; |
| | | } |
| | | .box-top { |
| | | display: block; |
| | | height: 60rpx; |
| | | width: 720rpx; |
| | | } |
| | | .color-block-blue { |
| | | background-color: #1E9FFF; |
| | | display: inline-block; |
| | | float: left; |
| | | margin: 15rpx 15rpx 0 15rpx; |
| | | width: 12rpx; |
| | | height: 40rpx; |
| | | border: 5rpx solid #1E9FFF; |
| | | border-radius: 20rpx; |
| | | } |
| | | .title { |
| | | display: inline-block; |
| | | float: left; |
| | | font-size: 34rpx; |
| | | font-weight: 700; |
| | | height: 50rpx; |
| | | line-height: 50rpx; |
| | | margin-top: 10rpx; |
| | | } |
| | | .box-buttom { |
| | | display: inline-block; |
| | | background-color: #ededed; |
| | | width: 96%; |
| | | height: 60rpx; |
| | | border-radius: 20rpx; |
| | | margin: 15rpx 15rpx 0rpx 15rpx; |
| | | } |
| | | .box-buttom input { |
| | | width: 75%; |
| | | float: left; |
| | | margin: 8rpx 10rpx 0rpx 25rpx; |
| | | } |
| | | .box-buttom .search-icon { |
| | | width: 60rpx; |
| | | height: 60rpx; |
| | | float: right; |
| | | margin-top: 5rpx; |
| | | margin-right: 10rpx; |
| | | } |
| | | .pak-seach-box button { |
| | | background-color: #1E9FFF; |
| | | color: #ffffff; |
| | | display: inline-block; |
| | | float: right; |
| | | width: 150rpx; |
| | | height: 60rpx; |
| | | margin: 15rpx 15rpx 0rpx 15rpx; |
| | | line-height: 60rpx; |
| | | } |
| | | .pakin-btn { |
| | | background-color: #1E9FFF; |
| | | } |
| | | .pak-data-box { |
| | | background-color: #F1F1F1; |
| | | margin: 15rpx 15rpx 0rpx 15rpx; |
| | | width: 96%; |
| | | height: 70rpx; |
| | | border-radius: 20rpx; |
| | | } |
| | | .pak-data-box .box-top { |
| | | background-color: #FFFFFF; |
| | | height: 70rpx; |
| | | border-radius: 20rpx 20rpx 20rpx 20rpx; |
| | | } |
| | | .bg-false { |
| | | background-color: #FFFFFF; |
| | | } |
| | | .bg-true { |
| | | background-color: #ebebeb; |
| | | } |
| | | .data-list { |
| | | border-bottom: 1px solid #d8d8d8; |
| | | height: 180rpx; |
| | | margin: 15rpx; |
| | | border-radius: 20rpx; |
| | | } |
| | | .data-list:first-child { |
| | | margin-top: 20rpx; |
| | | } |
| | | .data-list:last-child { |
| | | margin-bottom: 160rpx; |
| | | } |
| | | .left-check-box { |
| | | display: inline-block; |
| | | float: left; |
| | | height: 100%; |
| | | width: 100rpx; |
| | | text-align: center; |
| | | line-height: 170rpx; |
| | | } |
| | | .data-list-left { |
| | | display: inline-block; |
| | | float: left; |
| | | height: 180rpx; |
| | | width: 500rpx; |
| | | color: #676767; |
| | | } |
| | | .matnr { |
| | | padding-top: 10rpx; |
| | | } |
| | | .data-list-right { |
| | | display: inline-block; |
| | | float: right; |
| | | width: 100rpx; |
| | | height: 180rpx; |
| | | line-height: 180rpx; |
| | | } |
| | | .data-list-right label { |
| | | display: inline-block; |
| | | float: left; |
| | | width: 100rpx; |
| | | height: 180rpx; |
| | | } |
| | | .revise-box { |
| | | width: 500rpx; |
| | | height: 500rpx; |
| | | } |
| | | .revise-box-top { |
| | | width: 100%; |
| | | height: 100rpx; |
| | | background-color: #fff; |
| | | |
| | | .icon-scan { |
| | | padding: 10rpx; |
| | | } |
| | | .changeBox { |
| | | width: 100%; |
| | | height: 100rpx; |
| | | line-height: 120rpx; |
| | | |
| | | .input-row { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | /* Radio Group */ |
| | | .radio-group-vertical { |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .radio-row { |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 10rpx 0; |
| | | } |
| | | |
| | | .radio-label { |
| | | font-size: 28rpx; |
| | | margin-left: 10rpx; |
| | | color: #333; |
| | | } |
| | | |
| | | /* Zone & Form */ |
| | | .form-row { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .form-label { |
| | | font-weight: bold; |
| | | font-size: 28rpx; |
| | | margin-right: 20rpx; |
| | | color: #333; |
| | | } |
| | | |
| | | .picker-box { |
| | | background-color: #f5f7fa; |
| | | border: 1rpx solid #e0e0e0; |
| | | border-radius: 8rpx; |
| | | height: 70rpx; |
| | | padding: 0 20rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .picker-text { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | } |
| | | |
| | | /* Tables */ |
| | | .tables-section { |
| | | display: flex; |
| | | flex-direction: column; |
| | | } |
| | | |
| | | .no-padding { |
| | | padding: 0; |
| | | } |
| | | |
| | | .table-header-row { |
| | | display: flex; |
| | | background-color: #f0f2f5; |
| | | padding: 15rpx 20rpx; |
| | | font-weight: bold; |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | } |
| | | |
| | | .table-body-scroll { |
| | | max-height: 300rpx; |
| | | overflow-y: auto; |
| | | } |
| | | |
| | | .table-row { |
| | | display: flex; |
| | | padding: 20rpx; |
| | | border-bottom: 1rpx solid #eee; |
| | | align-items: center; |
| | | font-size: 26rpx; |
| | | } |
| | | |
| | | .col-2 { flex: 2; } |
| | | .col-1 { flex: 1; } |
| | | .col-05 { flex: 0.5; } |
| | | |
| | | .text-center { text-align: center; } |
| | | .text-cut { |
| | | overflow: hidden; |
| | | white-space: nowrap; |
| | | text-overflow: ellipsis; |
| | | } |
| | | |
| | | .empty-state { |
| | | padding: 40rpx; |
| | | text-align: center; |
| | | background-color: #FFF; |
| | | margin-top: 20rpx; |
| | | border-bottom: 1px solid #e3e3e3; |
| | | color: #999; |
| | | font-size: 26rpx; |
| | | } |
| | | .text-box { |
| | | width: 100%; |
| | | height: 100rpx; |
| | | line-height: 120rpx; |
| | | text-align: center; |
| | | background-color: #FFF; |
| | | margin-top: 20rpx; |
| | | border-bottom: 1px solid #e3e3e3; |
| | | |
| | | .table-footer-info { |
| | | padding: 15rpx 20rpx; |
| | | text-align: right; |
| | | font-size: 26rpx; |
| | | background-color: #fff; |
| | | border-top: 1rpx solid #f0f0f0; |
| | | } |
| | | .changeBox .num-box { |
| | | display: inline-block; |
| | | float: left; |
| | | |
| | | /* Queue List */ |
| | | .queue-list-scroll { |
| | | max-height: 400rpx; |
| | | padding: 10rpx; |
| | | background-color: #f8faff; |
| | | } |
| | | .changeBox button { |
| | | float: left; |
| | | |
| | | .queue-card { |
| | | background-color: #fff; |
| | | border: 1rpx solid #eee; |
| | | border-radius: 8rpx; |
| | | margin-bottom: 10rpx; |
| | | overflow: hidden; |
| | | } |
| | | .revise-box-buttom { |
| | | position: absolute; |
| | | width: 100%; |
| | | height: 100rpx; |
| | | line-height: 100rpx; |
| | | background-color: #FFFFFF; |
| | | |
| | | .queue-header { |
| | | padding: 15rpx; |
| | | background-color: #eef6ff; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | font-size: 26rpx; |
| | | } |
| | | |
| | | .queue-content { |
| | | padding: 10rpx 15rpx; |
| | | } |
| | | |
| | | .queue-detail-row { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | font-size: 24rpx; |
| | | padding: 5rpx 0; |
| | | } |
| | | |
| | | /* Footer Actions */ |
| | | .footer-actions { |
| | | position: fixed; |
| | | bottom: 0; |
| | | text-align: center; |
| | | left: 0; |
| | | right: 0; |
| | | height: 120rpx; |
| | | background-color: #fff; |
| | | display: flex; |
| | | align-items: center; |
| | | padding: 0 20rpx; |
| | | box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05); |
| | | z-index: 99; |
| | | } |
| | | |
| | | /* Utilities */ |
| | | .flex-1 { flex: 1; } |
| | | .margin-top-sm { margin-top: 20rpx; } |
| | | .margin-left-sm { margin-left: 20rpx; } |
| | | .margin-right-sm { margin-right: 20rpx; } |
| | | .margin-right-xs { margin-right: 10rpx; } |
| | | .text-bold { font-weight: bold; } |
| | | .text-blue { color: #007aff; } |
| | | .text-dark { color: #333; } |
| | | .text-grey { color: #888; } |
| | | .text-sm { font-size: 24rpx; } |
| | | |
| | | </style> |