Files
uni-pda/pages/inout/in.vue
T
2026-06-02 21:54:41 +08:00

493 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>
<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: 14px;
color: #666;
margin-left: 8px;
}
// 危险等级标签
.hazard-tag {
padding: 4rpx 16rpx;
border-radius: 4rpx;
font-size: 12px;
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: 13px;
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: 13px;
color: #007AFF;
}
.switch-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 16px;
background: #fff;
border-radius: 8px;
}
</style>