493 lines
15 KiB
Vue
493 lines
15 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>
|
||
<view class="input-row">
|
||
<input class="form-input" v-model="formData.digitalId" placeholder="输入或扫码" />
|
||
<view class="input-extra" style="margin-left: 8px;">
|
||
<button class="btn btn-sm btn-outline" @click="scanCode">扫码</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label">入库时间</text>
|
||
<view class="form-value">{{ formData.entryDateTime }}</view>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label required">贮存设施</text>
|
||
<picker class="form-value" :range="storageFacilities" range-key="label" :value="storageFacilityIndex" @change="onStorageFacilityChange">
|
||
<text>{{ formData.storageFacilityName || '选择贮存设施' }}</text>
|
||
<uni-icons type="right" size="14" color="#cccccc"></uni-icons>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label required">贮存经办人</text>
|
||
<picker class="form-value" :range="operators" range-key="name" :value="storageManagerIndex" @change="onStorageManagerChange">
|
||
<text>{{ formData.storageManagerName || '选择贮存经办人' }}</text>
|
||
<uni-icons type="right" size="14" color="#cccccc"></uni-icons>
|
||
</picker>
|
||
</view>
|
||
|
||
<view class="form-item">
|
||
<text class="form-label required">运送经办人</text>
|
||
<picker class="form-value" :range="operators" range-key="name" :value="transportManagerIndex" @change="onTransportManagerChange">
|
||
<text>{{ formData.transportManagerName || '选择运送经办人' }}</text>
|
||
<uni-icons type="right" size="14" color="#cccccc"></uni-icons>
|
||
</picker>
|
||
</view>
|
||
|
||
</view>
|
||
</view>
|
||
<!-- 自动扫码入库开关 -->
|
||
<view class="auto-scan-section">
|
||
<view class="form-item switch-row">
|
||
<text class="form-label">自动扫码入库</text>
|
||
<switch :checked="autoScanSubmit" @change="onAutoScanSwitchChange" color="#007AFF" />
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 按钮 -->
|
||
<view class="btn-area" v-if="!autoScanSubmit">
|
||
<button class="btn btn-primary" :disabled="!canSubmit" @click="handleSubmit(false)">确认入库</button>
|
||
</view>
|
||
|
||
|
||
<!-- 扫码入库记录 -->
|
||
<scan-records
|
||
:records="scanRecords"
|
||
title="扫码记录"
|
||
empty-text="暂无扫码记录"
|
||
@clear="clearScanRecords"
|
||
/>
|
||
|
||
<!-- 入库历史 -->
|
||
<inout-history
|
||
:records="inRecords"
|
||
title="入库历史"
|
||
empty-text="暂无入库历史"
|
||
/>
|
||
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { api } from '@/api'
|
||
import { getDateTime, getTime } from '@/utils/common.js'
|
||
// #ifdef APP-PLUS
|
||
import scan from '@/utils/scan.js'
|
||
// #endif
|
||
import ScanRecords from '@/components/common/scan-records.vue'
|
||
import InoutHistory from '@/components/common/inout-history.vue'
|
||
import { mapState } from 'vuex'
|
||
|
||
/**
|
||
* 入库页
|
||
*
|
||
* 提交数据:数字识别码(digitalId)、入库时间(entryDateTime)、
|
||
* 贮存经办人(storageManagerCode)、运送经办人(transportManagerCode)
|
||
*
|
||
* 扫码方式:通过全局统一扫码模块 scan.js,
|
||
* 由 store/scan.scanMode 决定使用 iData 专用扫码 还是 通用 Android 扫码
|
||
* 扫码 -> 自动填充数字识别码 -> 开启自动提交时延时入库
|
||
*/
|
||
export default {
|
||
components: {
|
||
ScanRecords,
|
||
InoutHistory
|
||
},
|
||
data() {
|
||
return {
|
||
autoScanSubmit: true, // 是否开启自动扫码提交
|
||
isAutoSubmitting: false, // 自动提交防重锁
|
||
scanRecords: [], // 扫码入库记录列表
|
||
formData: {
|
||
digitalId: '', // 数字识别码(扫码或手动输入)
|
||
entryDateTime: '', // 入库时间
|
||
storageFacility: '', // 贮存设施 code(提交用)
|
||
storageFacilityName: '', // 贮存设施名称(显示用)
|
||
storageManagerId: '', // 贮存经办人 id
|
||
storageManagerName: '', // 贮存经办人姓名
|
||
storageManagerCode: '', // 贮存经办人编码
|
||
transportManagerId: '', // 运送经办人 id
|
||
transportManagerName: '', // 运送经办人姓名
|
||
transportManagerCode: '' // 运送经办人编码
|
||
},
|
||
// 贮存设施选项列表
|
||
storageFacilities: [],
|
||
storageFacilityIndex: 0, // 当前选中的贮存设施下标
|
||
// 操作人员选项
|
||
operators: [
|
||
// { id: 1, name: '李伟', code: 'H_ZC_001' },
|
||
// { id: 2, name: '张明', code: 'H_ZC_002' },
|
||
// { id: 3, name: '王芳', code: 'H_ZC_003' },
|
||
// { id: 4, name: '刘强', code: 'H_ZC_004' }
|
||
],
|
||
storageManagerIndex: 0, // 当前选中的贮存经办人下标
|
||
transportManagerIndex: 0, // 当前选中的运送经办人下标
|
||
inRecords: [] // 入库历史记录
|
||
}
|
||
},
|
||
computed: {
|
||
// 从 store 获取当前扫码模式(idata / android)
|
||
...mapState('scan', ['scanMode']),
|
||
// 提交按钮可用判定:数字识别码、贮存设施、两个经办人、入库时间,缺一不可
|
||
canSubmit() {
|
||
return this.formData.digitalId &&
|
||
// this.formData.storageFacility &&
|
||
this.formData.storageManagerCode && this.formData.transportManagerCode &&
|
||
this.formData.entryDateTime
|
||
}
|
||
},
|
||
onLoad() {
|
||
// #ifdef APP-PLUS
|
||
this.initScanner() // 初始化扫码模块
|
||
// #endif
|
||
},
|
||
async mounted() {
|
||
await this.initData()
|
||
this.initDefaultValues() // 初始化默认值(时间、设施、经办人)
|
||
},
|
||
onUnload() {
|
||
// #ifdef APP-PLUS
|
||
this.cleanupScanner() // 页面卸载时注销扫码回调
|
||
// #endif
|
||
},
|
||
methods: {
|
||
async initData() {
|
||
await this.getInRecords()
|
||
await this.getOperators()
|
||
await this.getFacilitys()
|
||
},
|
||
// 获取入库历史记录
|
||
async getInRecords() {
|
||
this.inRecords = await api.hwInlist().then(res => {
|
||
return res.data.list
|
||
})
|
||
},
|
||
async getFacilitys() {
|
||
const res = await api.facility()
|
||
console.log('facility', res)
|
||
if (res.code == 1000) {
|
||
// 解析 list 字符串数组为对象数组
|
||
this.storageFacilities = res.data.list.map(item => {
|
||
const obj = {}
|
||
item.split('; ').forEach(pair => {
|
||
const [key, val] = pair.split('=')
|
||
obj[key] = val
|
||
})
|
||
return {
|
||
sfid: obj.sfid,
|
||
label: obj.alias,
|
||
code: obj.code,
|
||
order: parseInt(obj.order) || 999
|
||
}
|
||
}).sort((a, b) => a.order - b.order)
|
||
|
||
// 设置默认值(从 default 字段解析)
|
||
if (res.data.default) {
|
||
const defaultObj = {}
|
||
res.data.default.split('; ').forEach(pair => {
|
||
const [key, val] = pair.split('=')
|
||
defaultObj[key] = val
|
||
})
|
||
const defaultIndex = this.storageFacilities.findIndex(
|
||
f => f.code === defaultObj.code
|
||
)
|
||
if (defaultIndex !== -1) {
|
||
this.storageFacilityIndex = defaultIndex
|
||
this.formData.storageFacility = this.storageFacilities[defaultIndex].code
|
||
this.formData.storageFacilityName = this.storageFacilities[defaultIndex].label
|
||
}
|
||
}
|
||
}
|
||
},
|
||
// 获取经办人
|
||
async getOperators() {
|
||
// 1.产生,2.贮存,3.运送,4.出库
|
||
const res = await api.handler({ type: 2 })
|
||
console.log('handler', res)
|
||
if (res.code == 1000) {
|
||
this.operators = res.data.map(res => {
|
||
return {
|
||
id: res.handler_code,
|
||
name: res.handler_name,
|
||
code: res.handler_code
|
||
}
|
||
})
|
||
}
|
||
},
|
||
|
||
// ==================== 初始化 ====================
|
||
/** 初始化全部默认值 */
|
||
initDefaultValues() {
|
||
this.setDefaultOperators()
|
||
this.formData.entryDateTime = getDateTime()
|
||
},
|
||
/** 默认选中第一个贮存经办人和运送经办人 */
|
||
setDefaultOperators() {
|
||
if (this.operators.length > 0) {
|
||
this.storageManagerIndex = 0
|
||
this.transportManagerIndex = 0
|
||
this.formData.storageManagerId = this.operators[0].id
|
||
this.formData.storageManagerName = this.operators[0].name
|
||
this.formData.storageManagerCode = this.operators[0].code
|
||
this.formData.transportManagerId = this.operators[0].id
|
||
this.formData.transportManagerName = this.operators[0].name
|
||
this.formData.transportManagerCode = this.operators[0].code
|
||
}
|
||
},
|
||
|
||
// #ifdef APP-PLUS
|
||
// ==================== APP原生扫码模块 ====================
|
||
/**
|
||
* 初始化扫码
|
||
* 根据 store 中的 scanMode 设置 idata/android 模式,并注册扫码回调
|
||
*/
|
||
initScanner() {
|
||
try {
|
||
scan.setMode(this.scanMode)
|
||
scan.init()
|
||
scan.register((codeStr) => {
|
||
this.onBarcodeScanned(codeStr)
|
||
})
|
||
} catch (e) {
|
||
console.error('[入库] 扫码初始化失败:', e)
|
||
}
|
||
},
|
||
/** 页面卸载时注销扫码回调,防止内存泄漏 */
|
||
cleanupScanner() {
|
||
scan.unregister()
|
||
},
|
||
/** APP端扫码按钮:点一次开始扫描,再点一次停止扫描 */
|
||
scanCode() {
|
||
if (scan.isScanning()) {
|
||
scan.stopScan()
|
||
} else {
|
||
scan.startScan()
|
||
}
|
||
},
|
||
// #endif
|
||
|
||
// #ifdef MP
|
||
// ==================== 小程序兼容扫码模块 ====================
|
||
/** 小程序端扫码按钮:调用uni跨平台扫码API */
|
||
scanCode() {
|
||
uni.scanCode({
|
||
success: (res) => {
|
||
this.onBarcodeScanned(res.result)
|
||
},
|
||
fail: (err) => {
|
||
console.error('[入库] 小程序扫码失败:', err)
|
||
if (err.errMsg && err.errMsg.indexOf('cancel') === -1) {
|
||
uni.showToast({ title: '扫码失败', icon: 'none' })
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// #endif
|
||
/** 扫码结果回调:填充数字识别码 -> 自动提交(如果开启) */
|
||
onBarcodeScanned(barcode) {
|
||
this.formData.digitalId = barcode
|
||
uni.showToast({ title: '扫码成功 ' + barcode, icon: 'none' })
|
||
if (this.autoScanSubmit && !this.isAutoSubmitting) {
|
||
setTimeout(() => {
|
||
if (this.canSubmit) {
|
||
this.handleSubmit(true)
|
||
}
|
||
}, 300)
|
||
}
|
||
},
|
||
// ==================== 表单选择器 ====================
|
||
|
||
/** 贮存经办人 picker 变更 */
|
||
onStorageManagerChange(e) {
|
||
this.storageManagerIndex = e.detail.value
|
||
const operator = this.operators[e.detail.value]
|
||
this.formData.storageManagerId = operator.id
|
||
this.formData.storageManagerName = operator.name
|
||
this.formData.storageManagerCode = operator.code
|
||
},
|
||
/** 运送经办人 picker 变更 */
|
||
onTransportManagerChange(e) {
|
||
this.transportManagerIndex = e.detail.value
|
||
const operator = this.operators[e.detail.value]
|
||
this.formData.transportManagerId = operator.id
|
||
this.formData.transportManagerName = operator.name
|
||
this.formData.transportManagerCode = operator.code
|
||
},
|
||
/** 贮存设施 picker 变更 */
|
||
onStorageFacilityChange(e) {
|
||
this.storageFacilityIndex = e.detail.value
|
||
const facility = this.storageFacilities[e.detail.value]
|
||
this.formData.storageFacility = facility.code
|
||
this.formData.storageFacilityName = facility.label
|
||
},
|
||
|
||
// ==================== 入库记录 ====================
|
||
/** 添加一条入库记录(最多保留 10 条) */
|
||
addScanRecord(digitalId, status, message) {
|
||
const time = getTime()
|
||
this.scanRecords.unshift({ digitalId, status, message, time })
|
||
if (this.scanRecords.length > 10) {
|
||
this.scanRecords.pop()
|
||
}
|
||
},
|
||
/** 清空入库记录 */
|
||
clearScanRecords() {
|
||
this.scanRecords = []
|
||
},
|
||
|
||
// ==================== 提交 & 重置 ====================
|
||
/**
|
||
* 提交入库
|
||
* @param {Boolean} isAuto 是否自动提交(扫码触发)
|
||
*/
|
||
async handleSubmit(isAuto = false) {
|
||
if (this.isAutoSubmitting) return
|
||
if (isAuto) {
|
||
this.isAutoSubmitting = true
|
||
}
|
||
try {
|
||
const submitData = {
|
||
code: this.formData.digitalId,
|
||
time: this.formData.entryDateTime,
|
||
storage_facility: this.formData.storageFacility,
|
||
storage_handler: this.formData.storageManagerCode,
|
||
transport_handler: this.formData.transportManagerCode
|
||
}
|
||
|
||
const res = await api.deviceHwIn(submitData)
|
||
console.log('hwin', res)
|
||
if (isAuto) {
|
||
this.addScanRecord(this.formData.digitalId, 'success', '入库成功')
|
||
}
|
||
uni.showToast({ title: '入库成功', icon: 'success' })
|
||
// 刷新入库历史
|
||
this.getInRecords()
|
||
this.resetForm()
|
||
} catch (error) {
|
||
console.error('入库失败:', error)
|
||
if (isAuto) {
|
||
this.addScanRecord(this.formData.digitalId, 'error', '入库失败')
|
||
}
|
||
} finally {
|
||
if (isAuto) {
|
||
this.isAutoSubmitting = false
|
||
}
|
||
}
|
||
},
|
||
/** 重置表单,恢复默认值 */
|
||
resetForm() {
|
||
this.formData = {
|
||
digitalId: '',
|
||
entryDateTime: '',
|
||
storageFacility: '',
|
||
storageFacilityName: '',
|
||
storageManagerId: '',
|
||
storageManagerName: '',
|
||
storageManagerCode: '',
|
||
transportManagerId: '',
|
||
transportManagerName: '',
|
||
transportManagerCode: ''
|
||
}
|
||
this.initDefaultValues()
|
||
},
|
||
/** 自动提交开关切换 */
|
||
onAutoScanSwitchChange(e) {
|
||
this.autoScanSubmit = e.detail.value
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
// 日期并排布局
|
||
.date-row {
|
||
display: flex;
|
||
gap: 12px;
|
||
}
|
||
|
||
.date-picker {
|
||
min-width: 120px;
|
||
}
|
||
|
||
// 输入行(带按钮)
|
||
.input-row {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
// gap: 8px;
|
||
}
|
||
|
||
.input-extra {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
// 单位文本
|
||
.unit-text {
|
||
font-size: $font-size-md;
|
||
color: #666;
|
||
margin-left: 8px;
|
||
}
|
||
|
||
// 危险等级标签
|
||
.hazard-tag {
|
||
padding: 4rpx 16rpx;
|
||
border-radius: 4rpx;
|
||
font-size: $font-size-xs;
|
||
color: #fff;
|
||
}
|
||
|
||
.hazard-tag.level1 { background: #4CAF50; }
|
||
.hazard-tag.level2 { background: #8BC34A; }
|
||
.hazard-tag.level3 { background: #FFC107; }
|
||
.hazard-tag.level4 { background: #FF9800; }
|
||
.hazard-tag.level5 { background: #F44336; }
|
||
|
||
.btn-sm {
|
||
padding: 0 12px;
|
||
font-size: $font-size-base;
|
||
height: 32px;
|
||
line-height: 32px;
|
||
}
|
||
|
||
// 自动扫码入库开关
|
||
.auto-scan-section {
|
||
padding: 0 16px;
|
||
// margin-bottom: 16px;
|
||
}
|
||
|
||
// 扫码状态提示
|
||
.scan-status {
|
||
padding: 8px 16px;
|
||
margin-top: 8px;
|
||
background: #f0f9ff;
|
||
border-radius: 4px;
|
||
text-align: center;
|
||
}
|
||
|
||
.scan-status-text {
|
||
font-size: $font-size-base;
|
||
color: #007AFF;
|
||
}
|
||
|
||
.switch-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 12px 16px;
|
||
background: #fff;
|
||
border-radius: 8px;
|
||
}
|
||
|
||
</style>
|