|
|
<template>
|
|
|
<scroll-view class="index-bg" scroll-y @scrolltolower="loadMorePlans" lower-threshold="100"
|
|
|
@refresherrefresh="refreshPlans" refresher-enabled="true" :refresher-triggered="planLoading">
|
|
|
<view class="index-content">
|
|
|
<view class="btn-group">
|
|
|
<button class="main-btn" @click="scanInventory">扫码盘点</button>
|
|
|
<button class="main-btn outline" @click="scanView">扫码查看</button>
|
|
|
</view>
|
|
|
|
|
|
<!-- 标签盘点功能区域 -->
|
|
|
<view class="inventory-section">
|
|
|
<view class="inventory-header">
|
|
|
<text class="inventory-title">标签盘点</text>
|
|
|
<text class="tags-amount">标签数量: {{tagsAmount}}</text>
|
|
|
</view>
|
|
|
|
|
|
<!-- 标签列表 -->
|
|
|
<scroll-view class="tag-list" scroll-y @scrolltolower="loadmore" lower-threshold="50">
|
|
|
<view class="list-item-head">
|
|
|
<text class="list-item-text-id">序号</text>
|
|
|
<text class="list-item-text-epc">标签信息</text>
|
|
|
<text class="list-item-text-count">次数</text>
|
|
|
<text class="list-item-text-rssi">信号强度</text>
|
|
|
</view>
|
|
|
<view class="list-item" v-for="item in dataList" :key="item.id">
|
|
|
<text class="list-item-text-id">{{item.id+1}}</text>
|
|
|
<text class="list-item-text-epc">{{item.epc}}</text>
|
|
|
<text class="list-item-text-count">{{item.count}}</text>
|
|
|
<text class="list-item-text-rssi">{{item.rssi}}</text>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
|
|
|
<!-- 设置选项 -->
|
|
|
<view class="inventory-options">
|
|
|
<!-- <checkbox-group @change="onAsyncChange" class="option-item">
|
|
|
<label class="checkbox-label">
|
|
|
<checkbox value="async_checkbox" :disabled="cbDisabled" />
|
|
|
<text class="option-text">异步</text>
|
|
|
</label>
|
|
|
</checkbox-group> -->
|
|
|
<!-- <checkbox-group @change="onVoiceChange" class="option-item">
|
|
|
<label class="checkbox-label">
|
|
|
<checkbox value="voice_checkbox" :disabled="cbDisabled" :checked="voiceFlag" />
|
|
|
<text class="option-text">声音</text>
|
|
|
</label>
|
|
|
</checkbox-group> -->
|
|
|
<!-- #ifdef APP-PLUS -->
|
|
|
<!-- <checkbox-group @change="onBarcodeChange" class="option-item">
|
|
|
<label class="checkbox-label">
|
|
|
<checkbox value="barcode_checkbox" :disabled="cbDisabled" :checked="barcodeFlag" />
|
|
|
<text class="option-text">手柄扫码</text>
|
|
|
</label>
|
|
|
</checkbox-group> -->
|
|
|
<!-- #endif -->
|
|
|
</view>
|
|
|
|
|
|
<!-- 操作按钮 -->
|
|
|
<view class="inventory-btn-box">
|
|
|
<button type="primary" @click="handleInventory" class="inventory-btn">{{btn1Info}}</button>
|
|
|
<button type="primary" :disabled="btn2Disabled" @click="viewMaterials" class="inventory-btn">查看物资</button>
|
|
|
</view>
|
|
|
<view>
|
|
|
<button type="primary" :disabled="btn3Disabled" @click="clearTags" class="inventory-btn clear-btn">清空</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<view class="task-section">
|
|
|
<view class="task-title">物资列表</view>
|
|
|
<!-- 表头 -->
|
|
|
<view class="task-list">
|
|
|
<view class="task-item" style="font-weight:600;">
|
|
|
<view class="task-info">
|
|
|
<text class="task-name">物资名称</text>
|
|
|
</view>
|
|
|
<view class="task-info" style="flex-direction: row; align-items: center; gap: 8rpx;">
|
|
|
<text class="task-time">库存</text>
|
|
|
</view>
|
|
|
<view class="task-info" style="width: 120rpx; text-align: right;">
|
|
|
<text class="task-time">操作</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- 数据行 -->
|
|
|
<view class="task-item" v-for="(item, idx) in taskList" :key="idx">
|
|
|
<view class="task-info">
|
|
|
<text class="task-name">{{item.zichanmingcheng}}</text>
|
|
|
</view>
|
|
|
<view class="task-info" style="flex-direction: row; align-items: center; gap: 8rpx;">
|
|
|
<text class="task-time">{{item.total_num}}</text>
|
|
|
</view>
|
|
|
<view class="task-info" style="width: 120rpx; display:flex; justify-content:flex-end;">
|
|
|
<button size="mini" type="primary" @click.stop="goInventoryFromList(item)">盘点</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- 盘点计划 -->
|
|
|
<view class="inventory-plan-section">
|
|
|
<view class="inventory-plan-header">
|
|
|
<text class="inventory-plan-title">盘点计划</text>
|
|
|
<!-- <text class="plan-total">总计: {{planTotal}}</text> -->
|
|
|
</view>
|
|
|
|
|
|
<!-- 表头 -->
|
|
|
<view class="plan-list">
|
|
|
<view class="plan-item" style="font-weight:600;">
|
|
|
<view class="plan-info plan-name-col">
|
|
|
<text class="plan-name">计划名称</text>
|
|
|
</view>
|
|
|
<view class="plan-info plan-status-col">
|
|
|
<text class="plan-time">状态</text>
|
|
|
</view>
|
|
|
<view class="plan-info plan-date-col">
|
|
|
<text class="plan-time">开始日期</text>
|
|
|
</view>
|
|
|
<view class="plan-info plan-date-col">
|
|
|
<text class="plan-time">结束日期</text>
|
|
|
</view>
|
|
|
<view class="plan-info plan-action-col">
|
|
|
<text class="plan-time">操作</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- 数据行 -->
|
|
|
<view class="plan-item" v-for="item in inventoryPlanList" :key="item.id">
|
|
|
<view class="plan-info plan-name-col">
|
|
|
<text class="plan-name">{{item.name}}</text>
|
|
|
<!-- <text class="plan-no">{{item.no || '暂无'}}</text> -->
|
|
|
</view>
|
|
|
<view class="plan-info plan-status-col">
|
|
|
<text class="plan-status" :class="'status-' + item.status">{{getStatusText(item.status)}}</text>
|
|
|
</view>
|
|
|
<view class="plan-info plan-date-col">
|
|
|
<text class="plan-time">{{formatDate(item.start_date)}}</text>
|
|
|
</view>
|
|
|
<view class="plan-info plan-date-col">
|
|
|
<text class="plan-time">{{formatDate(item.end_date)}}</text>
|
|
|
</view>
|
|
|
<view class="plan-info plan-action-col">
|
|
|
<button size="mini" type="primary" @click.stop="viewPlanDetail(item)">查看</button>
|
|
|
</view>
|
|
|
</view>
|
|
|
</view>
|
|
|
|
|
|
<!-- 加载更多提示 -->
|
|
|
<view class="load-more" v-if="planLoading">
|
|
|
<view class="loading-spinner"></view>
|
|
|
<text class="loading-text">加载中...</text>
|
|
|
</view>
|
|
|
<view class="load-more" v-else-if="!planHasMore && inventoryPlanList.length > 0">
|
|
|
<text class="no-more-text">— 没有更多数据了 —</text>
|
|
|
</view>
|
|
|
</view>
|
|
|
<!-- H5扫码弹窗 -->
|
|
|
<div v-if="showH5Scan" class="h5-scan-modal">
|
|
|
<div id="reader" style="width:300px;height:300px;margin:0 auto;"></div>
|
|
|
<button @click="closeH5Scan">关闭</button>
|
|
|
</div>
|
|
|
</view>
|
|
|
</scroll-view>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
let keyDownTime = 1
|
|
|
let runtimeBarcodeFlag = false
|
|
|
let runtimeAsyncFlag = false
|
|
|
let runtimeVoiceFlag = true
|
|
|
import { getInventoryTaskList,getInventoryPlanList } from '@/api.js';
|
|
|
// #ifdef H5
|
|
|
import { Html5Qrcode, Html5QrcodeSupportedFormats } from 'html5-qrcode';
|
|
|
// #endif
|
|
|
|
|
|
export default {
|
|
|
data() {
|
|
|
return {
|
|
|
currentDate: '',
|
|
|
taskList: [],
|
|
|
showH5Scan: false,
|
|
|
html5QrCode: null,
|
|
|
scanType: '', // 'inventory' or 'view'
|
|
|
// 标签盘点相关数据
|
|
|
btn1Info: "开始盘点",
|
|
|
tagsAmount: 0, //读取到的标签总数
|
|
|
btn2Disabled: false, //禁用按键
|
|
|
btn3Disabled: false, //禁用按键
|
|
|
cbDisabled: false, //是否禁用
|
|
|
dataList: [], // list展示的标签列表,首次最多加载offset个,待上拉加载更多时,加载tempList中的数据
|
|
|
pageNum: 100, // 每页加载数量
|
|
|
offset: 50, // 第一页加载的数量/已加载的数量
|
|
|
epcList: [], // 过滤所用列表,只存标签的EPC信息
|
|
|
tempList: [], // 标签缓存列表,缓存标签信息,等待上拉至列表底部时,按页加载缓存列表中的数据
|
|
|
asyncFlag: false, // 异步盘点标志
|
|
|
voiceFlag: true, // 声音播放标志
|
|
|
barcodeFlag: false, // 二维码扫描标志
|
|
|
main: null, // Android主Activity
|
|
|
hhwUHFController: null, // UHF控制器
|
|
|
globalEvent: null, // 事件监听
|
|
|
receiver: null, // Android广播接收器
|
|
|
inventoryPlanList: [], // 盘点计划列表
|
|
|
planPage: 1, // 盘点计划当前页码
|
|
|
planPageSize: 5, // 盘点计划每页数量
|
|
|
planTotal: 0, // 盘点计划总数
|
|
|
planLoading: false, // 盘点计划加载状态
|
|
|
planHasMore: true // 是否还有更多数据
|
|
|
}
|
|
|
},
|
|
|
created() {
|
|
|
runtimeAsyncFlag = this.asyncFlag
|
|
|
runtimeVoiceFlag = this.voiceFlag
|
|
|
runtimeBarcodeFlag = this.barcodeFlag
|
|
|
},
|
|
|
onLoad() {
|
|
|
this.updateDate()
|
|
|
// #ifdef APP-PLUS
|
|
|
this.initAndroidComponents()
|
|
|
// #endif
|
|
|
this.initUHFEvent()
|
|
|
// 加载盘点计划列表
|
|
|
this.getInventoryPlanList(true)
|
|
|
},
|
|
|
onShow() {
|
|
|
// #ifdef APP-PLUS
|
|
|
console.log("inventory Show")
|
|
|
// 初始化二维码扫描,以防扫描服务处于关闭状态,而无法调用扫描
|
|
|
this.initBarcodeScan()
|
|
|
// 屏蔽二维码扫描扳机,以便app可以自定义触发
|
|
|
this.disableBarcodeScanKey()
|
|
|
// 监听功能按键,触发扫描
|
|
|
this.registerKeyReceiver()
|
|
|
// #endif
|
|
|
},
|
|
|
onHide() {
|
|
|
// #ifdef APP-PLUS
|
|
|
console.log("inventory Hide")
|
|
|
// 注销按键监听
|
|
|
if (this.main && this.receiver) {
|
|
|
this.main.unregisterReceiver(this.receiver)
|
|
|
}
|
|
|
// #endif
|
|
|
},
|
|
|
methods: {
|
|
|
updateDate() {
|
|
|
const now = new Date()
|
|
|
const year = now.getFullYear()
|
|
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
|
|
const day = String(now.getDate()).padStart(2, '0')
|
|
|
this.currentDate = `${year}-${month}-${day}`
|
|
|
},
|
|
|
handleScan(type) {
|
|
|
this.scanType = type;
|
|
|
// #ifdef H5
|
|
|
// 先请求摄像头权限
|
|
|
navigator.mediaDevices.getUserMedia({
|
|
|
video: {
|
|
|
facingMode: "environment",
|
|
|
width: { ideal: 1280 },
|
|
|
height: { ideal: 720 }
|
|
|
}
|
|
|
})
|
|
|
.then(() => {
|
|
|
this.showH5Scan = true;
|
|
|
this.$nextTick(() => {
|
|
|
if (!window.Html5Qrcode) {
|
|
|
window.Html5Qrcode = Html5Qrcode;
|
|
|
}
|
|
|
const config = {
|
|
|
fps: 10,
|
|
|
qrbox: { width: 250, height: 250 },
|
|
|
aspectRatio: 1.0,
|
|
|
formatsToSupport: [ Html5QrcodeSupportedFormats.QR_CODE ]
|
|
|
};
|
|
|
|
|
|
this.html5QrCode = new window.Html5Qrcode("reader");
|
|
|
this.html5QrCode.start(
|
|
|
{ facingMode: "environment" },
|
|
|
config,
|
|
|
qrCodeMessage => {
|
|
|
this.closeH5Scan();
|
|
|
// 直接使用扫码结果作为 id
|
|
|
let id = qrCodeMessage.trim();
|
|
|
console.log("id:", id)
|
|
|
// 规范化:允许类似 3715.000 的形式,转换为整数部分字符串
|
|
|
id = this.normalizeScannedId(id)
|
|
|
if (!id) {
|
|
|
uni.showToast({ title: '二维码无效', icon: 'none' });
|
|
|
return;
|
|
|
}
|
|
|
// 验证是否为数字(包括字符串类型的数字)
|
|
|
if (!/^\d+$/.test(id)) {
|
|
|
uni.showToast({ title: '二维码信息错误', icon: 'none' });
|
|
|
return;
|
|
|
}
|
|
|
if (this.scanType === 'inventory') {
|
|
|
uni.navigateTo({ url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}` });
|
|
|
} else {
|
|
|
uni.navigateTo({ url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}&view=1` });
|
|
|
}
|
|
|
},
|
|
|
errorMessage => {
|
|
|
console.log('扫码错误:', errorMessage);
|
|
|
}
|
|
|
).catch(err => {
|
|
|
console.error('启动扫码失败:', err);
|
|
|
uni.showToast({
|
|
|
title: '启动扫码失败,请检查摄像头权限',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
});
|
|
|
this.closeH5Scan();
|
|
|
});
|
|
|
});
|
|
|
})
|
|
|
.catch(err => {
|
|
|
console.error('摄像头权限错误:', err);
|
|
|
uni.showModal({
|
|
|
title: '提示',
|
|
|
content: '请允许访问摄像头以使用扫码功能',
|
|
|
confirmText: '确定',
|
|
|
showCancel: false,
|
|
|
success: () => {
|
|
|
// 引导用户去设置页面开启权限
|
|
|
if (uni.getSystemInfoSync().platform === 'android') {
|
|
|
uni.showToast({
|
|
|
title: '请在系统设置中开启摄像头权限',
|
|
|
icon: 'none',
|
|
|
duration: 2000
|
|
|
});
|
|
|
}
|
|
|
}
|
|
|
});
|
|
|
});
|
|
|
// #endif
|
|
|
// #ifndef H5
|
|
|
uni.scanCode({
|
|
|
success: (res) => {
|
|
|
// 直接使用扫码结果作为 id
|
|
|
let id = res.result.trim();
|
|
|
console.log("id2:", id)
|
|
|
// 规范化:允许类似 3715.000 的形式,转换为整数部分字符串
|
|
|
id = this.normalizeScannedId(id)
|
|
|
if (!id) {
|
|
|
uni.showToast({ title: '二维码无效', icon: 'none' });
|
|
|
return;
|
|
|
}
|
|
|
// 验证是否为数字(包括字符串类型的数字)
|
|
|
if (!/^\d+$/.test(id)) {
|
|
|
uni.showToast({ title: '二维码信息错误', icon: 'none' });
|
|
|
return;
|
|
|
}
|
|
|
if (type === 'inventory') {
|
|
|
uni.navigateTo({ url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}` });
|
|
|
} else {
|
|
|
uni.navigateTo({ url: `/pages/inventory/inventory?code=${encodeURIComponent(id)}&view=1` });
|
|
|
}
|
|
|
},
|
|
|
fail: () => {
|
|
|
uni.showToast({ title: '扫码失败', icon: 'none' });
|
|
|
}
|
|
|
});
|
|
|
// #endif
|
|
|
},
|
|
|
scanInventory() {
|
|
|
this.handleScan('inventory');
|
|
|
},
|
|
|
scanView() {
|
|
|
this.handleScan('view');
|
|
|
},
|
|
|
closeH5Scan() {
|
|
|
this.showH5Scan = false;
|
|
|
if (this.html5QrCode) {
|
|
|
this.html5QrCode.stop().then(() => {
|
|
|
this.html5QrCode.clear();
|
|
|
});
|
|
|
}
|
|
|
},
|
|
|
// 获取盘点计划列表
|
|
|
async getInventoryPlanList(isRefresh = false) {
|
|
|
if (this.planLoading) return;
|
|
|
|
|
|
try {
|
|
|
this.planLoading = true;
|
|
|
|
|
|
// 如果是刷新,重置页码
|
|
|
if (isRefresh) {
|
|
|
this.planPage = 1;
|
|
|
this.planHasMore = true;
|
|
|
}
|
|
|
|
|
|
const params = {
|
|
|
page: this.planPage,
|
|
|
page_size: this.planPageSize
|
|
|
};
|
|
|
|
|
|
const res = await getInventoryPlanList(params);
|
|
|
console.log("盘点计划列表响应:", res);
|
|
|
if(res.data && res.data.errcode===40001){
|
|
|
uni.showToast({
|
|
|
title: res.data?.errmsg || '获取盘点计划失败',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
uni.reLaunch({
|
|
|
url: '/pages/login/login'
|
|
|
});
|
|
|
return;
|
|
|
}
|
|
|
if (res.data && res.data.list) {
|
|
|
const data = res.data.list;
|
|
|
const newList = data.data || [];
|
|
|
|
|
|
if (isRefresh) {
|
|
|
this.inventoryPlanList = newList;
|
|
|
} else {
|
|
|
this.inventoryPlanList = [...this.inventoryPlanList, ...newList];
|
|
|
}
|
|
|
|
|
|
this.planTotal = data.total || 0;
|
|
|
this.planHasMore = newList.length === this.planPageSize;
|
|
|
|
|
|
if (this.planHasMore) {
|
|
|
this.planPage++;
|
|
|
}
|
|
|
} else {
|
|
|
uni.showToast({
|
|
|
title: res.data?.message || '获取盘点计划失败',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
}
|
|
|
} catch (error) {
|
|
|
console.error('获取盘点计划列表失败:', error);
|
|
|
uni.showToast({
|
|
|
title: '获取盘点计划失败',
|
|
|
icon: 'none'
|
|
|
});
|
|
|
} finally {
|
|
|
this.planLoading = false;
|
|
|
}
|
|
|
},
|
|
|
|
|
|
// 刷新盘点计划列表
|
|
|
refreshPlans() {
|
|
|
this.getInventoryPlanList(true);
|
|
|
},
|
|
|
|
|
|
// 加载更多盘点计划
|
|
|
loadMorePlans() {
|
|
|
if (!this.planHasMore || this.planLoading) return;
|
|
|
this.getInventoryPlanList(false);
|
|
|
},
|
|
|
|
|
|
// 获取状态文本
|
|
|
getStatusText(status) {
|
|
|
const statusMap = {
|
|
|
0: '未开始',
|
|
|
1: '进行中',
|
|
|
2: '已完成'
|
|
|
};
|
|
|
return statusMap[status] || '未知状态';
|
|
|
},
|
|
|
|
|
|
// 获取盘点类型文本
|
|
|
getTypeText(type) {
|
|
|
const typeMap = {
|
|
|
1: '年度',
|
|
|
2: '季度'
|
|
|
};
|
|
|
return typeMap[type] || '未知类型';
|
|
|
},
|
|
|
|
|
|
// 格式化日期
|
|
|
formatDate(dateStr) {
|
|
|
if (!dateStr) return '';
|
|
|
const date = new Date(dateStr);
|
|
|
const year = date.getFullYear();
|
|
|
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
const day = String(date.getDate()).padStart(2, '0');
|
|
|
return `${year}-${month}-${day}`;
|
|
|
},
|
|
|
|
|
|
// 查看计划详情
|
|
|
viewPlanDetail(item) {
|
|
|
console.log('查看计划详情:', item);
|
|
|
|
|
|
// 将计划信息作为参数传递
|
|
|
const planInfoStr = encodeURIComponent(JSON.stringify(item));
|
|
|
|
|
|
uni.navigateTo({
|
|
|
url: `/pages/plan-detail/plan-detail?planInfo=${planInfoStr}&planId=${item.id}`
|
|
|
});
|
|
|
},
|
|
|
|
|
|
// 计算计划进度
|
|
|
getProgress(item) {
|
|
|
if (item.status === 2) return 100; // 已完成
|
|
|
if (item.status === 0) return 0; // 未开始
|
|
|
|
|
|
// 进行中的计划,根据时间计算进度
|
|
|
const now = new Date();
|
|
|
const startDate = new Date(item.start_date);
|
|
|
const endDate = new Date(item.end_date);
|
|
|
|
|
|
if (now < startDate) return 0;
|
|
|
if (now > endDate) return 100;
|
|
|
|
|
|
const totalTime = endDate.getTime() - startDate.getTime();
|
|
|
const passedTime = now.getTime() - startDate.getTime();
|
|
|
|
|
|
return Math.round((passedTime / totalTime) * 100);
|
|
|
},
|
|
|
// 初始化Android组件
|
|
|
initAndroidComponents() {
|
|
|
// #ifdef APP-PLUS
|
|
|
try {
|
|
|
this.main = plus.android.runtimeMainActivity()
|
|
|
// UHF控制器,在App.vue中初始化
|
|
|
this.hhwUHFController = getApp().globalData.hhwUHFController
|
|
|
// 事件监听,在App.vue中初始化
|
|
|
this.globalEvent = getApp().globalData.globalEvent
|
|
|
} catch (e) {
|
|
|
console.error('初始化Android组件失败:', e)
|
|
|
}
|
|
|
// #endif
|
|
|
},
|
|
|
// 初始化UHF事件监听
|
|
|
initUHFEvent() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (this.globalEvent) {
|
|
|
this.globalEvent.addEventListener('uhf_tag_event', (e) => {
|
|
|
this.handleUHFEvent(e)
|
|
|
})
|
|
|
}
|
|
|
// #endif
|
|
|
},
|
|
|
// 处理UHF事件
|
|
|
handleUHFEvent(e) {
|
|
|
console.log(e.tag_info_list)
|
|
|
var result = e.tag_info_list
|
|
|
if (result == null) {
|
|
|
// 接收到停止盘点的回调消息
|
|
|
var event = e.inventory_event
|
|
|
if (event == "stopInventory") {
|
|
|
uni.showToast({
|
|
|
title: "停止盘点",
|
|
|
icon: "none"
|
|
|
})
|
|
|
this.btn2Disabled = false
|
|
|
this.btn3Disabled = false
|
|
|
this.cbDisabled = false
|
|
|
this.btn1Info = "开始盘点"
|
|
|
}
|
|
|
return
|
|
|
}
|
|
|
// 接收盘点到的标签信息
|
|
|
for (var i = 0; i < result.length; i++) {
|
|
|
var id = i
|
|
|
var epcHex = this.bytes2HexString(result[i].EpcId)
|
|
|
var epc = this.hexToString(epcHex)
|
|
|
var rssi = result[i].RSSI
|
|
|
var tag = {
|
|
|
id: id,
|
|
|
epc: epc,
|
|
|
count: 1,
|
|
|
rssi: rssi,
|
|
|
}
|
|
|
var index = this.epcList.indexOf(epc)
|
|
|
if (index == -1) {
|
|
|
tag.id = this.epcList.length
|
|
|
if (this.dataList.length < this.offset) {
|
|
|
this.dataList.push(tag)
|
|
|
}
|
|
|
this.tempList.push(tag)
|
|
|
this.epcList.push(epc)
|
|
|
} else {
|
|
|
tag.id = index
|
|
|
tag.count = this.tempList[index].count + 1
|
|
|
if (index < this.dataList.length) {
|
|
|
this.$set(this.dataList, index, tag)
|
|
|
}
|
|
|
this.$set(this.tempList, index, tag)
|
|
|
}
|
|
|
}
|
|
|
this.tagsAmount = this.epcList.length
|
|
|
},
|
|
|
// 注册按键接收器
|
|
|
registerKeyReceiver() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (!this.main) return
|
|
|
var IntentFilter = plus.android.importClass('android.content.IntentFilter')
|
|
|
var filter = new IntentFilter()
|
|
|
filter.addAction("android.rfid.FUN_KEY")
|
|
|
this.receiver = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
|
|
|
onReceive: (context, intent) => {
|
|
|
plus.android.importClass(intent)
|
|
|
var code = intent.getIntExtra("keyCode", 0)
|
|
|
var keyDown = intent.getBooleanExtra("keydown", false)
|
|
|
if (keyDown && keyDownTime == 1 && code == 137) {
|
|
|
console.log("inventory", "receive keyUp code: " + code)
|
|
|
if (runtimeBarcodeFlag) {
|
|
|
// 开始扫描
|
|
|
this.startBarcodeScan()
|
|
|
} else {
|
|
|
// 开始超高频
|
|
|
this.startInventory()
|
|
|
}
|
|
|
keyDownTime++
|
|
|
} else if (!keyDown) {
|
|
|
if (runtimeBarcodeFlag) {
|
|
|
// 停止扫描
|
|
|
this.stopBarcodeScan()
|
|
|
} else {
|
|
|
// 停止超高频
|
|
|
this.stopInventory()
|
|
|
}
|
|
|
keyDownTime = 1
|
|
|
}
|
|
|
}
|
|
|
})
|
|
|
this.main.registerReceiver(this.receiver, filter)
|
|
|
// #endif
|
|
|
},
|
|
|
// 屏蔽二维码扫描扳机
|
|
|
disableBarcodeScanKey() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (!this.main) return
|
|
|
var Intent = plus.android.importClass("android.content.Intent")
|
|
|
var intent = new Intent("com.rfid.KEY_SET")
|
|
|
var keyValueArray = ["137"]
|
|
|
intent.putExtra("keyValueArray", keyValueArray)
|
|
|
intent.putExtra("137", false)
|
|
|
this.main.sendBroadcast(intent)
|
|
|
// #endif
|
|
|
},
|
|
|
// 字节数组转十六进制字符
|
|
|
bytes2HexString(byteArray) {
|
|
|
return Array.from(byteArray, function(byte) {
|
|
|
return ('0' + (byte & 0xFF).toString(16)).slice(-2)
|
|
|
}).join('')
|
|
|
},
|
|
|
// 十六进制字符串转字节数组
|
|
|
hexString2Bytes(str) {
|
|
|
var pos = 0
|
|
|
var len = str.length
|
|
|
if (len % 2 != 0) {
|
|
|
return null
|
|
|
}
|
|
|
len /= 2
|
|
|
var hexA = new Array()
|
|
|
for (var i = 0; i < len; i++) {
|
|
|
var s = str.substr(pos, 2)
|
|
|
var v = parseInt(s, 16)
|
|
|
hexA.push(v)
|
|
|
pos += 2
|
|
|
}
|
|
|
return hexA
|
|
|
},
|
|
|
// 十六进制字符串转原始数字字符串(反向操作:parseInt(id, 10).toString(16))
|
|
|
hexToString(hexStr) {
|
|
|
if (!hexStr) return ''
|
|
|
// 去除空格并转为大写
|
|
|
let clean = hexStr.replace(/\s+/g, '').toUpperCase()
|
|
|
// 将十六进制字符串转换为十进制数字,再转为字符串
|
|
|
const num = parseInt(clean, 16)
|
|
|
if (isNaN(num)) {
|
|
|
return hexStr
|
|
|
}
|
|
|
return num.toString()
|
|
|
},
|
|
|
// 格式化物资列表数据
|
|
|
formatMaterialList(res) {
|
|
|
const list = res && res.data && res.data.list && res.data.list.data ? res.data.list.data : []
|
|
|
console.log("list:", list)
|
|
|
return list.map((item) => ({
|
|
|
// 用于跳转的ID,后端字段兼容
|
|
|
id: item.id || item.material_info_id || item.inventory_id || '',
|
|
|
// 表格展示字段
|
|
|
zichanmingcheng: item.zichanmingcheng || '',
|
|
|
total_num: item.total_num ?? item.inventorys_total ?? item.zaikushuliang ?? 0
|
|
|
}))
|
|
|
},
|
|
|
// 规范化扫码ID:允许 "3715.000" => "3715"
|
|
|
normalizeScannedId(value) {
|
|
|
if (!value) return ''
|
|
|
const v = String(value).trim()
|
|
|
// 若是纯数字,直接返回
|
|
|
if (/^\d+$/.test(v)) return v
|
|
|
// 数字(可带小数),取整数部分
|
|
|
if (/^\d+(?:\.\d+)?$/.test(v)) {
|
|
|
const num = Number(v)
|
|
|
if (!Number.isNaN(num) && Number.isFinite(num)) {
|
|
|
return Math.trunc(num).toString()
|
|
|
}
|
|
|
}
|
|
|
// 其他情况返回空,触发无效提示
|
|
|
return ''
|
|
|
},
|
|
|
// 从物资列表进入盘点页面
|
|
|
goInventoryFromList(item) {
|
|
|
const code = item && (item.id)
|
|
|
if (!code) {
|
|
|
uni.showToast({ title: '无法获取物资ID', icon: 'none' })
|
|
|
return
|
|
|
}
|
|
|
uni.navigateTo({ url: `/pages/inventory/inventory?code=${encodeURIComponent(code)}` });
|
|
|
},
|
|
|
// 初始化二维码扫描
|
|
|
initBarcodeScan() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (!this.main) return
|
|
|
var Intent = plus.android.importClass("android.content.Intent")
|
|
|
var intent = new Intent("com.rfid.SCAN_INIT")
|
|
|
this.main.sendBroadcast(intent)
|
|
|
// #endif
|
|
|
},
|
|
|
// 触发二维码扫描
|
|
|
startBarcodeScan() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (!this.main) return
|
|
|
var Intent = plus.android.importClass("android.content.Intent")
|
|
|
var intent = new Intent("com.rfid.SCAN_CMD")
|
|
|
this.main.sendBroadcast(intent)
|
|
|
// #endif
|
|
|
},
|
|
|
// 暂停二维码扫描
|
|
|
stopBarcodeScan() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (!this.main) return
|
|
|
var Intent = plus.android.importClass("android.content.Intent")
|
|
|
var intent = new Intent("com.rfid.STOP_SCAN")
|
|
|
this.main.sendBroadcast(intent)
|
|
|
// #endif
|
|
|
},
|
|
|
// 处理盘点按钮点击
|
|
|
handleInventory() {
|
|
|
if (this.btn1Info == "开始盘点") {
|
|
|
// 按钮点击始终进行超高频盘点,不受手柄扫码选项影响
|
|
|
this.startInventory()
|
|
|
} else {
|
|
|
// 停止超高频盘点
|
|
|
this.stopInventory()
|
|
|
}
|
|
|
},
|
|
|
// 开始盘点
|
|
|
startInventory() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (!this.hhwUHFController) {
|
|
|
uni.showToast({
|
|
|
title: "UHF控制器未初始化",
|
|
|
icon: "none"
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
// #endif
|
|
|
this.btn2Disabled = true
|
|
|
this.btn3Disabled = true
|
|
|
this.cbDisabled = true
|
|
|
this.btn1Info = "停止盘点"
|
|
|
// #ifdef APP-PLUS
|
|
|
this.hhwUHFController.setCancleInventoryFilter()
|
|
|
if (runtimeAsyncFlag) {
|
|
|
// 大量标签场景(200张标签以上),开始异步盘点,手动调用停止盘点后,停止盘点
|
|
|
this.hhwUHFController.startInventory(30, 0, true, 0, runtimeVoiceFlag, (result) => {
|
|
|
console.log("inventory inventory", "startInventory " + result)
|
|
|
})
|
|
|
} else {
|
|
|
// 少量标签场景(200张标签以下),开始同步盘点,手动调用停止盘点后,停止盘点
|
|
|
console.log("async_flag")
|
|
|
this.hhwUHFController.startInventory(30, 0, false, 0, runtimeVoiceFlag, (result) => {
|
|
|
console.log("inventory inventory", "startInventory " + result)
|
|
|
})
|
|
|
}
|
|
|
// #endif
|
|
|
},
|
|
|
// 停止盘点
|
|
|
stopInventory() {
|
|
|
// #ifdef APP-PLUS
|
|
|
if (!this.hhwUHFController) {
|
|
|
return
|
|
|
}
|
|
|
// 停止盘点,注意stopInventory中的参数值需要和startInventory第一个参数值对应
|
|
|
if (runtimeAsyncFlag) {
|
|
|
this.hhwUHFController.stopInventory(true)
|
|
|
} else {
|
|
|
this.hhwUHFController.stopInventory(false)
|
|
|
}
|
|
|
// #endif
|
|
|
},
|
|
|
// 清空标签
|
|
|
clearTags() {
|
|
|
this.dataList = []
|
|
|
this.tempList = []
|
|
|
this.epcList = []
|
|
|
this.tagsAmount = 0
|
|
|
this.offset = 50
|
|
|
this.taskList = []
|
|
|
},
|
|
|
// 查看物资
|
|
|
viewMaterials() {
|
|
|
const idsSource = this.epcList.length ? this.epcList : this.dataList.map(item => item.epc).filter(Boolean)
|
|
|
if (!idsSource.length) {
|
|
|
uni.showToast({
|
|
|
title: '请先获取标签数据',
|
|
|
icon: 'none'
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
const ids = idsSource.join(',')
|
|
|
uni.showLoading({
|
|
|
title: '查询中...'
|
|
|
})
|
|
|
getInventoryTaskList({ ids:ids,page:1,page_size:999 })
|
|
|
.then(res => {
|
|
|
uni.hideLoading()
|
|
|
console.log("res:", res)
|
|
|
const materialList = this.formatMaterialList(res)
|
|
|
if (materialList.length === 0) {
|
|
|
this.taskList = []
|
|
|
uni.showToast({
|
|
|
title: '未查询到物资信息',
|
|
|
icon: 'none'
|
|
|
})
|
|
|
return
|
|
|
}
|
|
|
this.taskList = materialList
|
|
|
})
|
|
|
.catch(err => {
|
|
|
console.error('viewMaterials error', err)
|
|
|
uni.hideLoading()
|
|
|
uni.showToast({
|
|
|
title: '获取物资失败',
|
|
|
icon: 'none'
|
|
|
})
|
|
|
})
|
|
|
},
|
|
|
// 加载更多
|
|
|
loadmore() {
|
|
|
console.log("inventory loadmore", "dataList size1: " + this.dataList.length, "temList size: " + this.tempList.length)
|
|
|
if (this.dataList.length >= this.tempList.length) {
|
|
|
console.log("inventory loadmore", "nomore")
|
|
|
return
|
|
|
}
|
|
|
// 每次加载pageNum个
|
|
|
var size
|
|
|
if (this.tempList.length - this.offset >= this.pageNum) {
|
|
|
size = this.pageNum
|
|
|
} else {
|
|
|
size = this.tempList.length - this.offset
|
|
|
}
|
|
|
for (var i = this.offset; i < size + this.offset; i++) {
|
|
|
this.dataList.push(this.tempList[i])
|
|
|
}
|
|
|
this.offset = this.offset + size
|
|
|
},
|
|
|
// 异步选项改变
|
|
|
onAsyncChange(e) {
|
|
|
this.asyncFlag = e.detail.value[0] === 'async_checkbox'
|
|
|
runtimeAsyncFlag = this.asyncFlag
|
|
|
},
|
|
|
// 声音选项改变
|
|
|
onVoiceChange(e) {
|
|
|
this.voiceFlag = e.detail.value[0] === 'voice_checkbox'
|
|
|
runtimeVoiceFlag = this.voiceFlag
|
|
|
},
|
|
|
// 二维码扫描选项改变
|
|
|
onBarcodeChange(e) {
|
|
|
this.barcodeFlag = e.detail.value[0] === 'barcode_checkbox'
|
|
|
runtimeBarcodeFlag = this.barcodeFlag
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style>
|
|
|
.index-bg {
|
|
|
height: 100vh;
|
|
|
background: linear-gradient(180deg, #eaf1fb 0%, #f7fafd 100%);
|
|
|
}
|
|
|
|
|
|
.index-content {
|
|
|
width: 100%;
|
|
|
max-width: 600px;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
padding: 4vw 0 6vw 0;
|
|
|
box-sizing: border-box;
|
|
|
margin: 0 auto;
|
|
|
}
|
|
|
|
|
|
.btn-group {
|
|
|
width: 90%;
|
|
|
max-width: 500px;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
margin: 0 auto 48rpx auto;
|
|
|
}
|
|
|
|
|
|
.main-btn {
|
|
|
height: 110rpx;
|
|
|
font-size: 38rpx;
|
|
|
font-weight: 800;
|
|
|
border-radius: 28rpx;
|
|
|
background: linear-gradient(135deg, #409eff 0%, #3b7cff 100%);
|
|
|
color: #fff;
|
|
|
box-shadow: 0 8px 24px rgba(64,158,255,0.25);
|
|
|
letter-spacing: 10rpx;
|
|
|
margin: 0;
|
|
|
border: none;
|
|
|
outline: none;
|
|
|
width: 100%;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
transition: all 0.3s ease;
|
|
|
margin-bottom: 36rpx;
|
|
|
position: relative;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.main-btn::before {
|
|
|
content: '';
|
|
|
position: absolute;
|
|
|
top: 0;
|
|
|
left: -100%;
|
|
|
width: 100%;
|
|
|
height: 100%;
|
|
|
background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
|
|
|
transition: left 0.6s;
|
|
|
}
|
|
|
|
|
|
.main-btn:active::before {
|
|
|
left: 100%;
|
|
|
}
|
|
|
|
|
|
.main-btn:last-child {
|
|
|
margin-bottom: 0;
|
|
|
}
|
|
|
|
|
|
.main-btn:active {
|
|
|
background: linear-gradient(135deg, #337ecc 0%, #2a5db0 100%);
|
|
|
box-shadow: 0 4px 16px rgba(64,158,255,0.3);
|
|
|
transform: scale(0.98);
|
|
|
}
|
|
|
|
|
|
.main-btn.outline {
|
|
|
background: #fff;
|
|
|
color: #409eff;
|
|
|
border: 3px solid #409eff;
|
|
|
box-shadow: 0 8px 24px rgba(64,158,255,0.15);
|
|
|
}
|
|
|
|
|
|
.main-btn.outline::before {
|
|
|
background: linear-gradient(90deg, transparent, rgba(64,158,255,0.1), transparent);
|
|
|
}
|
|
|
|
|
|
.main-btn.outline:active {
|
|
|
background: #f0f7ff;
|
|
|
transform: scale(0.98);
|
|
|
box-shadow: 0 4px 16px rgba(64,158,255,0.2);
|
|
|
}
|
|
|
|
|
|
.task-section {
|
|
|
width: 92%;
|
|
|
max-width: 520px;
|
|
|
margin: 0 auto 48rpx auto;
|
|
|
background: #fff;
|
|
|
border-radius: 22rpx;
|
|
|
box-shadow: 0 4px 18px rgba(64,158,255,0.07);
|
|
|
padding: 28rpx 20rpx 18rpx 20rpx;
|
|
|
}
|
|
|
|
|
|
.task-title {
|
|
|
font-size: 28rpx;
|
|
|
color: #222;
|
|
|
font-weight: 700;
|
|
|
margin-bottom: 18rpx;
|
|
|
}
|
|
|
|
|
|
.task-list {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 14rpx;
|
|
|
}
|
|
|
|
|
|
.task-item {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 14rpx 0;
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.task-item:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
.task-info {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.task-name {
|
|
|
font-size: 24rpx;
|
|
|
color: #333;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.task-time {
|
|
|
font-size: 20rpx;
|
|
|
color: #999;
|
|
|
margin-top: 2rpx;
|
|
|
}
|
|
|
|
|
|
.task-list {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 14rpx;
|
|
|
}
|
|
|
|
|
|
.task-item {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 14rpx 0;
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.task-item:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
.task-info {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.task-name {
|
|
|
font-size: 24rpx;
|
|
|
color: #333;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.task-time {
|
|
|
font-size: 20rpx;
|
|
|
color: #999;
|
|
|
margin-top: 2rpx;
|
|
|
}
|
|
|
|
|
|
.task-status {
|
|
|
font-size: 20rpx;
|
|
|
padding: 4rpx 14rpx;
|
|
|
border-radius: 16rpx;
|
|
|
}
|
|
|
|
|
|
.task-status.pending {
|
|
|
background-color: #fff3e0;
|
|
|
color: #ff9800;
|
|
|
}
|
|
|
|
|
|
.task-status.completed {
|
|
|
background-color: #e8f5e9;
|
|
|
color: #4caf50;
|
|
|
}
|
|
|
|
|
|
.task-status.in-progress {
|
|
|
background-color: #e8f5e9;
|
|
|
color: #4caf50;
|
|
|
}
|
|
|
|
|
|
|
|
|
.h5-scan-modal {
|
|
|
position: fixed;
|
|
|
left: 0; top: 0; right: 0; bottom: 0;
|
|
|
background: rgba(0,0,0,0.6);
|
|
|
z-index: 9999;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
}
|
|
|
|
|
|
.h5-scan-modal #reader {
|
|
|
width: 300px !important;
|
|
|
height: 300px !important;
|
|
|
margin: 0 auto;
|
|
|
background: #fff;
|
|
|
border-radius: 8px;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.h5-scan-modal #reader video {
|
|
|
width: 100% !important;
|
|
|
height: 100% !important;
|
|
|
object-fit: cover;
|
|
|
}
|
|
|
|
|
|
.h5-scan-modal #reader__scan_region {
|
|
|
width: 100% !important;
|
|
|
height: 100% !important;
|
|
|
}
|
|
|
|
|
|
.h5-scan-modal #reader__scan_region video {
|
|
|
width: 100% !important;
|
|
|
height: 100% !important;
|
|
|
}
|
|
|
|
|
|
.h5-scan-modal button {
|
|
|
margin-top: 20px;
|
|
|
padding: 8px 20px;
|
|
|
background: #409eff;
|
|
|
color: #fff;
|
|
|
border: none;
|
|
|
border-radius: 4px;
|
|
|
cursor: pointer;
|
|
|
}
|
|
|
|
|
|
.h5-scan-modal button:hover {
|
|
|
background: #66b1ff;
|
|
|
}
|
|
|
|
|
|
/* 标签盘点区域样式 */
|
|
|
.inventory-section {
|
|
|
width: 92%;
|
|
|
max-width: 520px;
|
|
|
margin: 0 auto 48rpx auto;
|
|
|
background: #fff;
|
|
|
border-radius: 22rpx;
|
|
|
box-shadow: 0 4px 18px rgba(64,158,255,0.07);
|
|
|
padding: 28rpx 20rpx 18rpx 20rpx;
|
|
|
}
|
|
|
|
|
|
.inventory-header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
margin-bottom: 18rpx;
|
|
|
}
|
|
|
|
|
|
.inventory-title {
|
|
|
font-size: 28rpx;
|
|
|
color: #222;
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
|
|
|
.tags-amount {
|
|
|
font-size: 24rpx;
|
|
|
color: #409eff;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.tag-list {
|
|
|
max-height: 200rpx;
|
|
|
background-color: #ebebeb;
|
|
|
border-radius: 12rpx;
|
|
|
margin-bottom: 18rpx;
|
|
|
}
|
|
|
|
|
|
.list-item-head {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
background-color: #f5f5f5;
|
|
|
border-radius: 12rpx 12rpx 0 0;
|
|
|
padding: 10rpx 0;
|
|
|
}
|
|
|
|
|
|
.list-item {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
background-color: #fff;
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.list-item:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
.list-item-text-id {
|
|
|
width: 65px;
|
|
|
padding: 8rpx 10rpx;
|
|
|
font-size: 24rpx;
|
|
|
text-align: center;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.list-item-text-epc {
|
|
|
flex: 1;
|
|
|
padding: 8rpx 10rpx;
|
|
|
font-size: 24rpx;
|
|
|
word-break: break-all;
|
|
|
min-width: 0;
|
|
|
}
|
|
|
|
|
|
.list-item-text-count {
|
|
|
width: 65px;
|
|
|
padding: 8rpx 10rpx;
|
|
|
font-size: 24rpx;
|
|
|
text-align: center;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.list-item-text-rssi {
|
|
|
width: 100px;
|
|
|
padding: 8rpx 10rpx;
|
|
|
font-size: 24rpx;
|
|
|
text-align: center;
|
|
|
flex-shrink: 0;
|
|
|
}
|
|
|
|
|
|
.inventory-options {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
align-items: center;
|
|
|
padding: 18rpx 0;
|
|
|
flex-wrap: wrap;
|
|
|
gap: 20rpx;
|
|
|
}
|
|
|
|
|
|
.option-item {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.checkbox-label {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.option-text {
|
|
|
margin-left: 8rpx;
|
|
|
font-size: 24rpx;
|
|
|
color: #333;
|
|
|
}
|
|
|
|
|
|
.inventory-btn-box {
|
|
|
display: flex;
|
|
|
flex-direction: row;
|
|
|
align-items: center;
|
|
|
gap: 10rpx;
|
|
|
margin-top: 5rpx;
|
|
|
}
|
|
|
|
|
|
.inventory-btn {
|
|
|
flex: 1;
|
|
|
/* height: 80rpx; */
|
|
|
font-size: 28rpx;
|
|
|
border-radius: 12rpx;
|
|
|
margin: 0;
|
|
|
}
|
|
|
|
|
|
.inventory-btn.clear-btn {
|
|
|
flex: 0 0 100rpx;
|
|
|
margin-top:10rpx;
|
|
|
}
|
|
|
|
|
|
/* 盘点计划区域样式 */
|
|
|
.inventory-plan-section {
|
|
|
width: 92%;
|
|
|
max-width: 520px;
|
|
|
margin: 0 auto 48rpx auto;
|
|
|
background: #fff;
|
|
|
border-radius: 22rpx;
|
|
|
box-shadow: 0 4px 18px rgba(64,158,255,0.07);
|
|
|
padding: 28rpx 20rpx 18rpx 20rpx;
|
|
|
}
|
|
|
|
|
|
.inventory-plan-header {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
margin-bottom: 18rpx;
|
|
|
}
|
|
|
|
|
|
.inventory-plan-title {
|
|
|
font-size: 28rpx;
|
|
|
color: #222;
|
|
|
font-weight: 700;
|
|
|
}
|
|
|
|
|
|
.plan-total {
|
|
|
font-size: 24rpx;
|
|
|
color: #409eff;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
|
|
|
.plan-list {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 14rpx;
|
|
|
}
|
|
|
|
|
|
.plan-item {
|
|
|
display: flex;
|
|
|
justify-content: space-between;
|
|
|
align-items: center;
|
|
|
padding: 14rpx 0;
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
}
|
|
|
|
|
|
.plan-item:last-child {
|
|
|
border-bottom: none;
|
|
|
}
|
|
|
|
|
|
.plan-info {
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
}
|
|
|
|
|
|
.plan-name-col {
|
|
|
flex: 1;
|
|
|
min-width: 0;
|
|
|
}
|
|
|
|
|
|
.plan-status-col {
|
|
|
width: 120rpx;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.plan-date-col {
|
|
|
width: 140rpx;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.plan-action-col {
|
|
|
width: 120rpx;
|
|
|
display: flex;
|
|
|
justify-content: flex-end;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
.plan-name {
|
|
|
font-size: 24rpx;
|
|
|
color: #333;
|
|
|
font-weight: 600;
|
|
|
}
|
|
|
|
|
|
.plan-no {
|
|
|
font-size: 20rpx;
|
|
|
color: #999;
|
|
|
margin-top: 4rpx;
|
|
|
}
|
|
|
|
|
|
.plan-time {
|
|
|
font-size: 20rpx;
|
|
|
color: #999;
|
|
|
margin-top: 2rpx;
|
|
|
}
|
|
|
|
|
|
.plan-status {
|
|
|
font-size: 20rpx;
|
|
|
padding: 4rpx 14rpx;
|
|
|
border-radius: 16rpx;
|
|
|
white-space: nowrap;
|
|
|
}
|
|
|
|
|
|
.plan-status.status-0 {
|
|
|
background-color: #fff3e0;
|
|
|
color: #ff9800;
|
|
|
}
|
|
|
|
|
|
.plan-status.status-1 {
|
|
|
background-color: #e3f2fd;
|
|
|
color: #2196f3;
|
|
|
}
|
|
|
|
|
|
.plan-status.status-2 {
|
|
|
background-color: #e8f5e9;
|
|
|
color: #4caf50;
|
|
|
}
|
|
|
|
|
|
/* 加载更多样式优化 */
|
|
|
.load-more {
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
gap: 12rpx;
|
|
|
padding: 30rpx 20rpx;
|
|
|
font-size: 24rpx;
|
|
|
color: #999;
|
|
|
}
|
|
|
|
|
|
.loading-spinner {
|
|
|
width: 32rpx;
|
|
|
height: 32rpx;
|
|
|
border: 3rpx solid #f0f0f0;
|
|
|
border-top: 3rpx solid #409eff;
|
|
|
border-radius: 50%;
|
|
|
animation: spin 1s linear infinite;
|
|
|
}
|
|
|
|
|
|
@keyframes spin {
|
|
|
0% { transform: rotate(0deg); }
|
|
|
100% { transform: rotate(360deg); }
|
|
|
}
|
|
|
|
|
|
.loading-text {
|
|
|
color: #409eff;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
.no-more-text {
|
|
|
color: #ccc;
|
|
|
font-size: 22rpx;
|
|
|
}
|
|
|
</style>
|