470 lines
16 KiB
Vue
470 lines
16 KiB
Vue
<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>
|