This commit is contained in:
2026-06-02 15:28:25 +08:00
commit b271b84f70
249 changed files with 42240 additions and 0 deletions
+468
View File
@@ -0,0 +1,468 @@
<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>