Files
uni-pda/pages/weigh/generate.vue
T
2026-06-27 20:58:16 +08:00

470 lines
16 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, getDate } from '@/utils/common.js'
import { mapGetters } from 'vuex';
import printerService from '@/utils/BluetoothPrinterService.new.js';
import scaleService from '@/utils/BluetoothScaleService.new.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,
// 【打印机】蓝牙打印机连接状态标记(新增)
printerConnected: false
}
},
computed: {
...mapGetters('scale', ['scaleDeviceId', 'scaleServiceId', 'scaleCharacteristicId'])
},
/**
* 页面加载生命周期
* 初始化产生日期为当前时间,并默认选中危废类型、经办人、去向的第一项
*/
onLoad() {
this.$store.dispatch('scale/initScaleConfig'); // 初始化称重配置
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() {
if (!this.scaleDeviceId || !this.scaleServiceId || !this.scaleCharacteristicId) {
uni.showModal({
title: '未配置称重设备',
content: '请先在设置中配置蓝牙称重设备,是否前往设置?',
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url: '/pages/settings/devices/scale' })
}
}
});
return;
}
uni.showToast({ title: '正在获取重量...', icon: 'none' });
try {
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);
// 提取错误信息(兼容 Error 对象和 uni-app 错误对象)
const errMsg = error.message || error.errMsg || JSON.stringify(error);
// 根据错误类型给出不同提示
let toastMsg = '获取重量失败';
if (errMsg.includes('未配置')) {
toastMsg = '请先配置称重设备';
} else if (errMsg.includes('未连接') || errMsg.includes('连接失败') || errMsg.includes('already connect')) {
toastMsg = '蓝牙连接失败,请检查设备是否开机';
} else if (errMsg.includes('超时') || errMsg.includes('time out')) {
toastMsg = '连接超时,请检查蓝牙设备';
} else if (errMsg.includes('invalid data')) {
toastMsg = '数据格式错误';
}
uni.showToast({ title: toastMsg, icon: 'none', duration: 2000 });
}
},
/**
* 危废类型选择变更
* @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_category_code, // 危废代码
'xingtai': hwInfo.waste_form || '-', // 废物形态
'zhuyaochengfen': hwInfo.main_components || '-', // 主要成分
'youhaichengfen': hwInfo.hazardous_components || '-', // 有害成分
'zhuyi': hwInfo.main_properties || '-', // 注意事项
'shibiema': result.digital_id, // 数字识别码
'danwei': hwInfo.unit, // 单位
'lianxi': '', // 联系人
'riqi': getDate(), // 日期
'zhongliang': submitData.weight, // 重量
'beizhu': result.produce_ledger_no || '-', // 备注 台账编号,包装类型
'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();
this.printerConnected = true
await printerService.printWasteLabel(this.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.printerConnected) { // ✅ 修改:this.connected → this.printerConnected
try {
await printerService.disconnect()
this.printerConnected = false // ✅ 修改:this.connected → this.printerConnected
} catch (err) {
console.warn('【蓝牙打印】断开连接失败:', err)
this.printerConnected = false // ✅ 修改:this.connected → this.printerConnected
}
}
}
}
}
</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>