Files
klp-mono/apps/hand-factory/components/panels/code/merge.vue

808 lines
17 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="merge-container">
<!-- 扫码提示 -->
<view class="scan-section">
<view class="scan-btn-wrapper" @click="handleScan">
<view class="scan-icon">
<text class="icon-camera">📷</text>
</view>
<text class="scan-tip">扫描要合卷的钢卷</text>
<text class="scan-hint">已扫描 {{ scannedCoils.length }} 个钢卷</text>
</view>
</view>
<!-- 已扫描的钢卷列表 -->
<view class="coil-list" v-if="scannedCoils.length > 0">
<view class="card-title">
<text class="title-dot"></text>
<text class="title-text">待合卷列表</text>
</view>
<view class="coil-item" v-for="(coil, index) in scannedCoils" :key="index">
<view class="coil-content">
<view class="coil-index">{{ index + 1 }}</view>
<view class="coil-info">
<view class="coil-row">
<text class="coil-label">入场钢卷号</text>
<text class="coil-value">{{ coil.enterCoilNo }}</text>
</view>
<view class="coil-row">
<text class="coil-label">当前钢卷号</text>
<text class="coil-value">{{ coil.currentCoilNo }}</text>
</view>
<view class="coil-row" v-if="coil.warehouse">
<text class="coil-label">库区</text>
<text class="coil-value">{{ coil.warehouse.warehouseName }}</text>
</view>
</view>
</view>
<view class="coil-remove" @click="removeCoil(index)">
<text></text>
</view>
</view>
</view>
<!-- 合卷配置 -->
<view class="form-card" v-if="scannedCoils.length >= 2" @click="closeWarehouseList">
<view class="card-title">
<text class="title-dot"></text>
<text class="title-text">合卷配置</text>
</view>
<!-- 新钢卷号 -->
<view class="form-item">
<text class="form-label">新钢卷号</text>
<input
v-model="mergedCoilNo"
placeholder="请输入合卷后的新钢卷号"
class="form-input"
/>
</view>
<!-- 班组 -->
<view class="form-item">
<text class="form-label">班组</text>
<input
v-model="team"
placeholder="请输入班组名称"
class="form-input"
/>
</view>
<!-- 目标库区 -->
<view class="form-item form-item-optional">
<text class="form-label-optional">目标库位</text>
<klp-warehouse-picker v-model="warehouseId" :disabled="coilDetail.dataType === 0" placeholder="请选择目标库区" />
</view>
<!-- 实际库区 -->
<view class="form-item form-item-optional">
<text class="form-label-optional">实际库区</text>
<klp-warehouse-picker v-model="actualWarehouseId" placeholder="请选择目标库区"
ware-type="actual" />
</view>
<klp-material-picker
:item-type.sync="itemType"
:item-id.sync="itemId"
:page-size="2000"
/>
<!-- 毛重 -->
<view class="form-item form-item-optional">
<text class="form-label-optional">毛重 ()</text>
<input
v-model="grossWeight"
type="digit"
placeholder="请输入毛重(选填)"
class="form-input"
/>
</view>
<!-- 净重 -->
<view class="form-item form-item-optional">
<text class="form-label-optional">净重 ()</text>
<input
v-model="netWeight"
type="digit"
placeholder="请输入净重(选填)"
class="form-input"
/>
</view>
<!-- 操作者信息 -->
<view class="operator-info">
<text class="operator-label">操作人</text>
<text class="operator-name">{{ operatorName }}</text>
</view>
<!-- 操作按钮 -->
<view class="action-buttons">
<button class="btn btn-secondary" @click="handleReset">清空重新扫</button>
<button class="btn btn-primary" @click="handleConfirm" :disabled="loading">
{{ loading ? '提交中...' : '确认合卷' }}
</button>
</view>
</view>
<!-- 提示信息 -->
<view class="tip-card" v-if="scannedCoils.length === 1">
<text class="tip-text">至少需要扫描 2 个钢卷才能进行合卷操作</text>
</view>
</view>
</template>
<script>
import { getGenerateRecord } from '@/api/wms/code.js'
import { updateMaterialCoil, getMaterialCoil, getMaterialCoilTrace } from '@/api/wms/coil.js'
export default {
data() {
return {
scannedCoils: [],
mergedCoilNo: '',
team: '',
warehouseId: undefined,
actualWarehouseId: undefined,
warehouseName: '',
warehouseKeyword: '',
warehouses: [],
filteredWarehouses: [],
showWarehouseList: false,
warehouseSearchKeyword: '',
filteredWarehousesInPicker: [],
products: [], // 产品列表
productSearchKeyword: '',
filteredProductsInPicker: [],
rawMaterials: [], // 原材料列表
rawMaterialSearchKeyword: '',
filteredRawMaterialsInPicker: [],
itemType: '', // 合卷后的物品类型
itemId: undefined, // 合卷后的物品ID
selectedProductName: '', // 已选择的产品名称
selectedRawMaterialName: '', // 已选择的原材料名称
grossWeight: '',
netWeight: '',
loading: false
}
},
computed: {
operatorName() {
return this.$store.state.user.nickName || this.$store.state.user.name || '未知'
}
},
methods: {
// 扫码
handleScan() {
uni.scanCode({
success: async (res) => {
console.log('扫码结果:', res.result);
try {
const qrcodeId = res.result;
// 1. 获取二维码详情
const qrcodeRes = await getGenerateRecord(qrcodeId);
if (qrcodeRes.code !== 200) {
throw new Error('未找到二维码记录');
}
// 2. 解析content获取current_coil_id
const qrcodeRecord = qrcodeRes.data;
const content = JSON.parse(qrcodeRecord.content);
// 优先使用current_coil_id当前有效的钢卷ID如果没有则使用coil_id兼容旧数据
let coilId = content.current_coil_id && content.current_coil_id !== 'null' ? content.current_coil_id : null;
if (!coilId) {
coilId = content.coil_id && content.coil_id !== 'null' ? content.coil_id : null;
}
// 检查二维码状态
if (qrcodeRecord.status === 0) {
uni.showModal({
title: '提示',
content: '该二维码已失效,无法进行合卷操作',
showCancel: false
});
return;
}
if (!coilId) {
throw new Error('二维码中未包含有效的钢卷ID');
}
// 3. 直接通过钢卷ID获取钢卷详情
const coilRes = await getMaterialCoil(coilId);
if (coilRes.code !== 200 || !coilRes.data) {
throw new Error('未找到钢卷信息');
}
const coilData = coilRes.data;
// 6. 检查是否已扫描过
const exists = this.scannedCoils.some(coil => coil.coilId === coilData.coilId);
if (exists) {
uni.showToast({
title: '该钢卷已扫描',
icon: 'none'
});
return;
}
this.scannedCoils.push(coilData);
console.log('钢卷详情:', coilData);
uni.showToast({
title: `${this.scannedCoils.length} 个钢卷扫描成功`,
icon: 'success'
});
} catch (err) {
console.error('扫码处理失败:', err);
uni.showToast({
title: err.message || '扫码信息解析失败',
icon: 'none',
duration: 2000
});
}
},
fail: (err) => {
console.error('扫码失败:', err);
uni.showToast({
title: '扫码失败,请重试',
icon: 'none'
});
}
});
},
// 移除钢卷
removeCoil(index) {
uni.showModal({
title: '确认移除',
content: '是否要移除此钢卷?',
success: (res) => {
if (res.confirm) {
this.scannedCoils.splice(index, 1);
}
}
});
},
// 重置
handleReset() {
uni.showModal({
title: '确认重置',
content: '是否要清空所有已扫描的钢卷?',
success: (res) => {
if (res.confirm) {
this.scannedCoils = [];
this.mergedCoilNo = '';
this.team = '';
this.warehouseId = undefined;
this.actualWarehouseId = undefined;
this.warehouseName = '';
this.warehouseKeyword = '';
this.itemType = '';
this.itemId = undefined;
this.selectedProductName = '';
this.selectedRawMaterialName = '';
this.grossWeight = '';
this.netWeight = '';
this.showWarehouseList = false;
}
}
});
},
// 提交合卷
handleConfirm() {
// 检查是否包含历史数据
const hasHistoryData = this.scannedCoils.some(coil => coil.dataType === 0);
if (hasHistoryData) {
uni.showModal({
title: '包含历史数据',
content: '列表中包含历史记录,不允许进行合卷操作,请移除后重试',
showCancel: false
});
return;
}
// 验证
if (this.scannedCoils.length < 2) {
uni.showToast({
title: '至少需要2个钢卷',
icon: 'none'
});
return;
}
if (!this.mergedCoilNo) {
uni.showToast({
title: '请输入新钢卷号',
icon: 'none'
});
return;
}
if (!this.team) {
uni.showToast({
title: '请输入班组名称',
icon: 'none'
});
return;
}
this.loading = true;
// 构建提交数据
// 根据后端逻辑bo是合卷后的新钢卷bo.newCoils是参与合卷的原始钢卷
const submitData = {
coilId: null, // 合卷时coilId为null
enterCoilNo: this.scannedCoils[0].enterCoilNo, // 使用第一个钢卷的入场号
supplierCoilNo: this.scannedCoils[0].supplierCoilNo, // 保留厂家原料卷号
currentCoilNo: this.mergedCoilNo,
team: this.team,
hasMergeSplit: 2, // 2表示合卷
itemType: this.itemType || this.scannedCoils[0].itemType, // 优先使用选择的itemType
itemId: this.itemId || this.scannedCoils[0].itemId, // 优先使用选择的itemId
warehouseId: this.warehouseId || this.scannedCoils[0].warehouseId, // 使用选择的库区或第一个钢卷的库区
actualWarehouseId: this.actualWarehouseId || this.scannedCoils[0].actualWarehouseId,
grossWeight: this.grossWeight ? Number(this.grossWeight) : null,
netWeight: this.netWeight ? Number(this.netWeight) : null,
newCoils: this.scannedCoils.map(coil => ({
coilId: coil.coilId,
currentCoilNo: coil.currentCoilNo
}))
};
updateMaterialCoil(submitData).then(res => {
if (res.code === 200) {
uni.showToast({
title: '合卷成功',
icon: 'success'
});
setTimeout(() => {
this.handleReset();
}, 1500);
} else {
uni.showToast({
title: res.msg || '合卷失败',
icon: 'none'
});
}
}).catch(err => {
console.error('提交失败:', err);
uni.showToast({
title: '合卷失败,请重试',
icon: 'none'
});
}).finally(() => {
this.loading = false;
});
}
}
}
</script>
<style scoped lang="scss">
.merge-container {
padding-bottom: 30rpx;
}
/* 扫码区域 */
.scan-section {
padding: 60rpx 40rpx;
background: #fff;
border-radius: 16rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
.scan-btn-wrapper {
display: flex;
flex-direction: column;
align-items: center;
gap: 20rpx;
.scan-icon {
width: 160rpx;
height: 160rpx;
border-radius: 50%;
background: linear-gradient(135deg, #34c759 0%, #30b350 100%);
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 8rpx 24rpx rgba(52, 199, 89, 0.3);
&:active {
transform: scale(0.95);
}
}
.icon-camera {
font-size: 70rpx;
}
.scan-tip {
font-size: 28rpx;
color: #666;
}
.scan-hint {
font-size: 24rpx;
color: #34c759;
font-weight: 500;
}
}
}
/* 钢卷列表 */
.coil-list {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
.card-title {
display: flex;
align-items: center;
margin-bottom: 25rpx;
padding-bottom: 20rpx;
border-bottom: 1rpx solid #f0f0f0;
.title-dot {
width: 8rpx;
height: 28rpx;
background: #34c759;
border-radius: 4rpx;
margin-right: 12rpx;
}
.title-text {
flex: 1;
font-size: 32rpx;
font-weight: 600;
color: #333;
}
}
.coil-item {
display: flex;
align-items: center;
background: #f8f9fa;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
position: relative;
&:last-child {
margin-bottom: 0;
}
.coil-content {
display: flex;
flex: 1;
gap: 20rpx;
.coil-index {
width: 60rpx;
height: 60rpx;
background: #34c759;
color: #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 24rpx;
font-weight: bold;
flex-shrink: 0;
&.index-history {
background: #999;
}
}
.coil-info {
flex: 1;
.coil-row {
display: flex;
align-items: center;
margin-bottom: 8rpx;
&:last-child {
margin-bottom: 0;
}
.coil-label {
font-size: 24rpx;
color: #666;
min-width: 150rpx;
}
.coil-value {
font-size: 24rpx;
color: #333;
font-weight: 500;
}
.history-tag {
font-size: 20rpx;
padding: 2rpx 10rpx;
background: #f8d7da;
color: #721c24;
border-radius: 8rpx;
margin-left: 10rpx;
}
}
}
}
.coil-remove {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
color: #ff4d4f;
font-size: 32rpx;
margin-left: 10rpx;
&:active {
opacity: 0.6;
}
}
}
/* 表单卡片 */
.form-card {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.04);
}
.form-item {
margin-bottom: 30rpx;
.form-label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: 500;
&::before {
content: '*';
color: #ff4d4f;
margin-right: 6rpx;
}
}
.form-label-optional {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: 500;
}
.form-input {
width: 100%;
height: 88rpx;
padding: 0 24rpx;
background: #f8f9fa;
border: 2rpx solid #e8e8e8;
border-radius: 12rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
transition: all 0.3s;
&:focus {
background: #fff;
border-color: #34c759;
}
}
.picker-input {
width: 100%;
height: 88rpx;
padding: 0 24rpx;
background: #f8f9fa;
border: 2rpx solid #e8e8e8;
border-radius: 12rpx;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
transition: all 0.3s;
&:active {
background: #fff;
border-color: #34c759;
}
.picker-text {
flex: 1;
font-size: 28rpx;
color: #333;
&.picker-placeholder {
color: #999;
}
}
.picker-arrow {
font-size: 24rpx;
color: #999;
margin-left: 10rpx;
}
}
}
/* 操作者信息 */
.operator-info {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx 0;
margin-top: 20rpx;
.operator-label {
font-size: 26rpx;
color: #999;
}
.operator-name {
font-size: 26rpx;
color: #34c759;
font-weight: 500;
}
}
/* 操作按钮 */
.action-buttons {
display: flex;
gap: 20rpx;
margin-top: 30rpx;
.btn {
flex: 1;
height: 88rpx;
border-radius: 12rpx;
font-size: 30rpx;
font-weight: 500;
border: none;
transition: all 0.2s;
&:active {
transform: scale(0.98);
}
&[disabled] {
opacity: 0.6;
}
}
.btn-secondary {
background: #f5f5f5;
color: #666;
}
.btn-primary {
background: linear-gradient(135deg, #34c759 0%, #30b350 100%);
color: #fff;
box-shadow: 0 4rpx 16rpx rgba(52, 199, 89, 0.3);
}
}
/* 提示卡片 */
.tip-card {
background: #fff3cd;
border-radius: 12rpx;
padding: 30rpx;
text-align: center;
.tip-text {
font-size: 26rpx;
color: #856404;
}
}
/* 库区选择弹窗 */
.warehouse-popup {
background: #fff;
border-radius: 24rpx 24rpx 0 0;
max-height: 70vh;
.popup-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.popup-title {
font-size: 32rpx;
color: #333;
font-weight: 600;
}
.popup-close {
font-size: 40rpx;
color: #999;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
}
}
.popup-search {
padding: 20rpx 30rpx;
border-bottom: 1rpx solid #f0f0f0;
.search-input {
width: 100%;
height: 80rpx;
padding: 0 20rpx;
background: #f8f9fa;
border: 2rpx solid #e8e8e8;
border-radius: 10rpx;
font-size: 28rpx;
color: #333;
box-sizing: border-box;
&:focus {
background: #fff;
border-color: #34c759;
}
}
}
.popup-body {
max-height: 400rpx;
.warehouse-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
&:active {
background: #e8f5e8;
}
.warehouse-name {
flex: 1;
font-size: 28rpx;
color: #333;
}
.warehouse-check {
font-size: 32rpx;
color: #34c759;
font-weight: bold;
}
}
.empty-tip {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
}
}
</style>