You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1403 lines
35 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<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>