Files
uni-pda/pages/weigh/generate.vue
T
2026-06-02 15:28:25 +08:00

469 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page-container">
<view class="page-body">
<view class="form-section">
<view class="form-group">
<view class="form-item">
<text class="form-label required">危废类型</text>
<picker class="form-value" mode="selector" :range="hwtypeOptions" range-key="alias" @change="onHwTypeChange">
<text>{{ form.hwTypeName || '请选择' }}</text>
<uni-icons type="right" size="14" color="#cccccc"></uni-icons>
</picker>
</view>
<view class="form-item">
<text class="form-label required">产生日期</text>
<view class="form-value">{{ form.productionDate }}</view>
</view>
<view class="form-item">
<text class="form-label required">重量(kg)</text>
<view class="weight-row">
<input class="form-input" type="number" placeholder="请输入重量" v-model="form.weight" />
<view class="form-input-extra" style="margin-left: 8px;">
<button class="btn btn-sm btn-outline" @click="getWeight">获取</button>
</view>
</view>
</view>
<view class="form-item">
<text class="form-label">经办人</text>
<picker class="form-value" mode="selector" :range="handlers" range-key="name" @change="onHandlerChange">
<text>{{ form.handlerName || '请选择' }}</text>
<uni-icons type="right" size="14" color="#cccccc"></uni-icons>
</picker>
</view>
<view class="form-item">
<text class="form-label">去向</text>
<picker class="form-value" mode="selector" :range="destinations" range-key="name" @change="onDestinationChange">
<text>{{ form.destinationName || '请选择' }}</text>
<uni-icons type="right" size="14" color="#cccccc"></uni-icons>
</picker>
</view>
<view class="form-item">
<text class="form-label">提交模式</text>
<radio-group class="radio-group" @change="onSubmitTypeChange">
<label class="radio-item">
<radio value="batch" :checked="form.submit_type === '1'" />
<text>仅产生</text>
</label>
<label class="radio-item">
<radio value="immediate" :checked="form.submit_type === '2'" />
<text>产生入库</text>
</label>
</radio-group>
</view>
</view>
</view>
<view class="tip-bar tip-bar-info">
请确保危废分类准确避免混入其他废弃物
</view>
<view class="btn-area">
<button class="btn btn-primary btn-block" @click="handleSubmit">提交并打印</button>
</view>
</view>
</view>
</template>
<script>
import * as iconv from 'iconv-lite'
import { api } from '@/api'
import { getDateTime } from '@/utils/common.js'
import printerService from '@/utils/BluetoothPrinterService.js';
// 【蓝牙秤】引入蓝牙称重服务,用于从电子秤读取重量
import scaleService from '@/utils/BluetoothScaleService.js';
export default {
data() {
return {
hwtypeOptions: [
{ id: 1, alias: '活性炭', fwdm: '900-039-49', jldw: '吨', sort_order: 0 },
{ id: 2, alias: '含汞荧光灯管', fwdm: '900-023-29', jldw: '吨', sort_order: 1 }
],
selectedHwType: null,
hwInfo: null,
handlers: [
{ id: 1, name: '李伟' },
{ id: 2, name: '张明' },
{ id: 3, name: '王芳' },
{ id: 4, name: '刘强' }
],
destinations: [
{ id: 1, name: '贮存' },
{ id: 2, name: '转移' },
{ id: 3, name: '自处置' }
],
form: {
hwTypeId: '',
hwTypeName: '',
productionDate: '',
weight: '',
handlerId: '',
handlerName: '',
destinationId: '',
destinationName: '',
submit_type: '1'
},
currentPrintData: null,
// 【蓝牙秤】蓝牙秤连接状态标记,避免重复连接/断开
scaleConnected: false
}
},
/**
* 页面加载生命周期
* 初始化产生日期为当前时间,并默认选中危废类型、经办人、去向的第一项
*/
onLoad() {
this.form.productionDate = getDateTime()
this.initData()
},
onUnload() {
// 退出页面时断开蓝牙打印机和称重设备连接
this.handleDisconnect()
},
methods: {
async initData() {
await this.gethwType()
await this.gethandler()
this.initOptions()
await this.getHwInfo()
},
// 危废类型
async gethwType() {
try {
const res = await api.hwType()
if (res.code == 1000)
this.hwtypeOptions = res.data
} catch (e) {
console.error('获取危废类型失败:', e)
}
},
// 危废详情
async getHwInfo() {
try {
const res = await api.hwTypeInfo({ tid: this.selectedHwType.id })
console.log('res', res)
if (res.code == 1000)
this.hwInfo = res.data
} catch (e) {
console.error('获取危废详情失败:', e)
}
},
// 产生经办人
async gethandler() {
const res = await api.handler({ type: 1 })
console.log('handler', res)
if (res.code == 1000) {
this.handlers = res.data.map(res => {
return {
id: res.handler_code,
name: res.handler_name
}
})
}
},
initOptions() {
// 默认选中第一项
const hwType = this.hwtypeOptions[0]
this.form.hwTypeId = hwType.id
this.form.hwTypeName = hwType.alias
this.selectedHwType = hwType
const dest = this.destinations[0]
this.form.destinationId = dest.id
this.form.destinationName = dest.name
const handler = this.handlers[0]
this.form.handlerId = handler.id
this.form.handlerName = handler.name
},
/**
* 获取重量
* 从蓝牙秤获取当前重量并填入表单
*
* 流程:
* 1. 检查是否已配置称重设备 → 未配置则引导去设置页
* 2. 检查蓝牙秤连接状态 → 未连接则自动连接
* 3. 发送 'R' 命令读取重量(5秒超时)
* 4. 解析返回数据填入 form.weight
*/
async getWeight() {
// ============================================================
// Step 1: 检查是否已配置称重设备
// 配置存储在 LocalStorage 的 'scaleConfig' 中
// 如果没有配置,提示用户去「设置 > 设备管理 > 称重设备」页面配置
// ============================================================
const scaleConfig = uni.getStorageSync('scaleConfig');
if (!scaleConfig || !scaleConfig.deviceId) {
uni.showModal({
title: '未配置称重设备',
content: '请先在设置中配置蓝牙称重设备,是否前往设置?',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/pages/settings/devices/scale' })
}
}
});
return;
}
uni.showToast({ title: '正在获取重量...', icon: 'none' });
try {
// ============================================================
// Step 2: 检查连接状态,未连接则自动连接
// autoConnect() 会从 LocalStorage 读取 deviceId/serviceId 等配置
// 然后调用底层 BluetoothManager 建立 BLE 连接
// ============================================================
if (!scaleService.isConnected || !this.scaleConnected) {
await scaleService.autoConnect();
this.scaleConnected = true;
}
// ============================================================
// Step 3: 发送读取命令 'R' (0x52),等待设备返回数据
// readWeight() 内部流程:
// - 向 WriteCharacteristic 写入 0x52
// - 启动 5 秒超时定时器
// - 等待 NotifyCharacteristic 收到设备响应
// - 调用 _parseCustom() 解析返回数据
//
// 【调试】如果解析失败(value=null),会显示原始HEX/ASCII数据,
// 你可以根据实际数据修改 _parseCustom() 中的协议解析逻辑
// ============================================================
const result = await scaleService.readWeight();
if (result.value !== null && !isNaN(result.value)) {
// 解析成功:将重量值填入表单
this.form.weight = String(result.value);
uni.showToast({ title: `重量: ${result.value} ${result.unit}`, icon: 'success' });
} else {
// 解析失败:显示原始数据便于调试
console.warn('【蓝牙秤】原始返回数据:', result);
uni.showModal({
title: '无法解析重量',
content: `原始数据:\nHEX: ${result.rawHex || '-'}\nASCII: ${result.rawAscii || '-'}\n\n请检查设备协议格式`,
showCancel: false
});
}
} catch (error) {
console.error('【蓝牙秤】获取重量失败:', error);
// 根据错误类型给出不同提示
let errMsg = '获取重量失败';
if (error.message.includes('未配置')) {
errMsg = '请先配置称重设备';
} else if (error.message.includes('未连接') || error.message.includes('连接失败')) {
errMsg = '蓝牙连接失败,请检查设备是否开机';
} else if (error.message.includes('超时')) {
errMsg = '读取超时,设备无响应';
}
uni.showToast({ title: errMsg, icon: 'none' });
}
},
/**
* 危废类型选择变更
* @param {Object} e - picker change 事件对象
*/
onHwTypeChange(e) {
const item = this.hwtypeOptions[e.detail.value]
this.form.hwTypeId = item.id
this.form.hwTypeName = item.alias
this.selectedHwType = item
// this.updateDestinationsByHwType(item)
this.getHwInfo()
},
/**
* 经办人选择变更
* @param {Object} e - picker change 事件对象
*/
onHandlerChange(e) {
const item = this.handlers[e.detail.value]
this.form.handlerId = item.id
this.form.handlerName = item.name
},
/**
* 去向选择变更
* @param {Object} e - picker change 事件对象
*/
onDestinationChange(e) {
const item = this.destinations[e.detail.value]
this.form.destinationId = item.id
this.form.destinationName = item.name
},
/**
* 提交模式切换
* @param {Object} e - radio-group change 事件对象
*/
onSubmitTypeChange(e) { this.form.submit_type = e.detail.value },
/**
* 提交登记/打印
*
*/
handleSubmit() {
if (!this.form.hwTypeId) return uni.showToast({ title: '请选择危废类型', icon: 'none' })
if (!this.form.weight) return uni.showToast({ title: '请输入重量', icon: 'none' })
const submitData = {
// ...this.form,
// fwdm: this.selectedHwType?.fwdm || '',
// jldw: this.selectedHwType?.jldw || ''
weight: this.form.weight,
weight_unit: this.selectedHwType.jldw,
direction: this.form.destinationId,
handler_code: this.form.handlerId,
submit_type: this.form.submit_type,
tid: this.form.hwTypeId,
time: getDateTime()
}
console.log('提交数据:', submitData)
// uni.showLoading({ title: '提交中...' })
api.hwGenerate({ ...submitData }).then(res => {
console.log('hwGenerate res', res)
// "flow_no": "019e5ceb-fc23-70a1-b0e4-06ed6bc7d951",
// "digital_id": "12510100580027911690003949202605250008",
// "label_qrcode": "https://www.cswm.org.cn/qr/?code=7G6MOYELnFHZfF1Y0F8e6h4LCHuJYm7dR2hXRJkeKK7UoS8KYXm5UyRKUnh430z2O&type=WFBQ",
// "produce_ledger_no": "CS20260525000008"
if (res.code == 1000) {
uni.showToast({ title: '登记成功', icon: 'success' })
// const data = res.data
const hwInfo = this.hwInfo
const result = res.data
const printData = {
'mingcheng': hwInfo.waste_name, // 危废名称
'leibie': hwInfo.waste_category, // 危废类别
'daima': hwInfo.waste_code, // 危废代码
'xingtai': hwInfo.waste_form || '-', // 废物形态
'zhuyaochengfen': hwInfo.main_component || '-', // 主要成分
'youhaichengfen': hwInfo.hazardous_components || '-', // 有害成分
'zhuyi': '-', // 注意事项
'shibiema': result.digital_id, // 数字识别码
'danwei': hwInfo.unit, // 单位
'lianxi': '', // 联系人
'riqi': getDate(submitData.time), // 日期
'zhongliang': submitData.weight, // 重量
'beizhu': '-', // 备注 台账编号,包装类型
'biaoshi': '', // 标识
'qrcode': result.label_qrcode // 二维码
}
this.currentPrintData = printData
this.doPrint()
}
}).finally(() => {
// uni.hideLoading()
})
},
async doPrint() {
// const printData = {
// 'mingcheng': '',
// 'leibie': '',
// 'daima': '',
// 'xingtai': '',
// 'zhuyaochengfen': '',
// 'youhaichengfen': '',
// 'zhuyi': '',
// 'shibiema': '',
// 'danwei': '',
// 'lianxi': '',
// 'riqi': '',
// 'zhongliang': '',
// 'beizhu': '-',
// 'biaoshi': ''
// }
if (!this.currentPrintData) {
return false
}
uni.showToast({ title: '打印中...', icon: 'none' })
// 初始化并连接
try {
await printerService.init();
await printerService.autoConnect();
await printerService.printWasteLabel(currentPrintData);
await this.handleDisconnect()
uni.showToast({ title: '打印成功', icon: 'success' })
this.currentPrintData = null
} catch (error) {
console.error('打印失败:', error)
uni.showToast({ title: '打印失败'+ error, icon: 'none' })
}
},
/**
* 断开所有蓝牙连接(打印机 + 称重设备)
*/
async handleDisconnect() {
// 断开蓝牙称重设备
if (this.scaleConnected) {
try {
await scaleService.disconnect();
this.scaleConnected = false;
} catch (err) {
console.warn('【蓝牙秤】断开连接失败:', err);
this.scaleConnected = false;
}
}
// 断开蓝牙打印机
if (this.connected) {
try {
await printerService.disconnect()
this.connected = false
} catch (err) {
console.warn('【蓝牙打印】断开连接失败:', err)
this.connected = false
}
}
}
}
}
</script>
<style lang="scss" scoped>
.weight-row {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
}
.btn-sm {
padding: 0 12px;
font-size: 13px;
height: 32px;
line-height: 32px;
}
.radio-group {
display: flex;
flex: 1;
align-items: center;
justify-content: flex-end;
}
.radio-item {
display: flex;
align-items: center;
margin-right: 16px;
text {
margin-left: 4px;
font-size: 14px;
color: #333;
}
}
.form-input-extra{
margin-left: 8px;
display: flex;
align-items: center;
}
</style>