Files
klp-mono/apps/hand-factory/components/scan-coil/scan-coil.vue
砂糖 fb5ee8356b feat(hand-factory): release v1.3.34
- 扫描钢卷功能支持维护异常信息
- 隐藏「设备巡检」菜单入口
- 版本号升级至 1.3.34
2026-06-09 09:04:52 +08:00

1191 lines
31 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="container">
<view
v-for="item in quickActions"
:key="item.key"
class="action-card"
:class="item.cardClass"
@click="handleQuickAction(item.key)"
>
<view class="action-body">
<text class="action-name">{{ item.label }}</text>
<text class="action-desc">{{ item.description }}</text>
</view>
<text class="action-arrow"></text>
</view>
<uni-popup ref="tranferPopup" type="bottom">
<view style="background-color: white; padding: 20rpx;">
<view class="info-card" v-if="form.coilId">
<view class="card-title">
<text class="title-dot"></text>
<text class="title-text">原钢卷信息</text>
<view class="more-btn" @click.stop="showBomDialog"
v-if="coilDetail.bomItemList && coilDetail.bomItemList.length > 0">
<text class="icon-more"></text>
<text class="more-text">参数</text>
</view>
</view>
<view class="info-grid">
<view class="info-item">
<text class="item-label">入场钢卷号</text>
<text class="item-value">{{ coilDetail.enterCoilNo || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">当前钢卷号</text>
<text class="item-value">{{ coilDetail.currentCoilNo || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">当前库区</text>
<text class="item-value">{{ coilDetail.actualWarehouseName || '-' }}</text>
</view>
</view>
</view>
<!-- 移库表单区域扫码选择目标库区 + 回显 -->
<view class="form-card" v-if="form.coilId">
<view class="form-item form-item-optional">
<text class="form-label-optional">目标库区</text>
<!-- 未选择目标库区显示扫码按钮 -->
<view v-if="!targetWarehouse" class="scan-btn-wrapper" @click="handleScanWarehouseCode">
<view class="scan-icon">
<text class="icon-camera">📷</text>
</view>
<text class="scan-tip">扫描库区码</text>
<text class="scan-hint">请扫描目标库区的二维码</text>
</view>
<!-- 已选择目标库区回显信息 + 重新扫码按钮 -->
<view v-else class="selected-warehouse">
<view class="warehouse-info">
<text class="warehouse-label">已选中</text>
<text class="warehouse-value">{{ targetWarehouse.actualWarehouseName }}</text>
</view>
<button class="re-scan-btn" @click="handleReScan">重新选择</button>
</view>
</view>
<view class="action-buttons">
<button class="btn btn-secondary" @click="handleClosePopup">取消</button>
<!-- 已选择目标库区才启用提交按钮 -->
<button class="btn btn-primary" @click="handleConfirm" :disabled="loading || !targetWarehouse">
{{ loading ? '提交中...' : '确认移库' }}
</button>
</view>
</view>
</view>
</uni-popup>
<!-- <klp-scaner></klp-scaner> -->
<qs-scanlistener ref='pda'></qs-scanlistener>
<uni-popup ref="shipPopup" type="bottom">
<view style="background-color: white; padding: 20rpx;">
<view class="info-card" v-if="coilDetail.coilId">
<view class="info-grid">
<view class="info-item">
<text class="item-label">入场钢卷号</text>
<text class="item-value">{{ coilDetail.enterCoilNo || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">当前钢卷号</text>
<text class="item-value">{{ coilDetail.currentCoilNo || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">逻辑库区</text>
<text class="item-value">{{ coilDetail.warehouseName || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">实际库区</text>
<text class="item-value">{{ coilDetail.actualWarehouseName || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">重量</text>
<text class="item-value">{{ coilDetail.netWeight || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">厂家</text>
<text class="item-value">{{ (coilDetail.manufacturer) || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">材质</text>
<text class="item-value">{{ (coilDetail.material) || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">规格</text>
<text class="item-value">{{ (coilDetail.specification) || '-' }}</text>
</view>
<view class="info-item">
<text class="item-label">品名</text>
<text class="item-value">{{ (coilDetail.itemName) || '-' }}</text>
</view>
</view>
</view>
<button
v-if="currentAction == 'withdrawShip'"
:disabled="buttonLoading"
:loading="buttonLoading"
@click="handleCancelShipSubmit">
撤回发货
</button>
<button
v-if="currentAction == 'ship'"
:disabled="buttonLoading"
:loading="buttonLoading"
@click="handleShipSubmit">
发货
</button>
<button
v-if="currentAction == 'release'"
:disabled="buttonLoading"
:loading="buttonLoading"
@click="handleReleaseSubmit">
释放库位
</button>
</view>
</uni-popup>
</view>
</template>
<script>
import {
getDicts
} from '@/api/system/dict/data.js'
import {
getGenerateRecord
} from '@/api/wms/code.js'
import {
getMaterialCoil,
updateMaterialCoilSimple,
listMaterialCoil,
updateMaterialCoil,
exportCoil,
cancelExportCoil
} from '@/api/wms/coil.js'
import {
addPendingAction
} from '@/api/wms/pendingAction.js'
import {
getActualWarehouse,
forceReleaseLocation
} from '@/api/wms/actualWarehouse.js'
export default {
data() {
return {
types: [],
loading: false,
coilDetail: {},
form: {},
targetWarehouse: null,
bomDialogShow: false,
mode: 'pda',
currentAction: '',
buttonLoading: false,
quickActions: [
{ key: 'ship', label: '发货', description: '扫描钢卷二维码,执行发货操作', cardClass: 'card-ship' },
{ key: 'cancelShip', label: '撤回发货', description: '撤回已发货的钢卷', cardClass: 'card-cancelShip' },
{ key: 'transfer', label: '移库', description: '将钢卷移动到目标库区', cardClass: 'card-transfer' },
{ key: 'scanCoil', label: '扫描钢卷', description: '直接扫描钢卷码,查看钢卷详细信息,并维护异常信息', cardClass: 'card-scanCoil' },
{ key: 'see', label: '查看存储钢卷', description: '扫描库区码,查看库区内存储的钢卷信息', cardClass: 'card-see' },
{ key: 'release', label: '释放库位', description: '释放当前库位占用', cardClass: 'card-release' },
],
}
},
computed: {
splitTypes() {
return this.types.filter(item => {
const value = parseInt(item.dictValue);
return value >= 100 && value <= 199;
});
},
otherTypes() {
return this.types.filter(item => {
const value = parseInt(item.dictValue);
return value < 100 || (value > 199 && value < 400);
});
},
},
methods: {
handleQuickAction(key) {
const handlerMap = {
ship: 'handleShip',
cancelShip: 'handleCancelShip',
transfer: 'handleTranfer',
scanCoil: 'handleScanCoil',
see: 'handleSee',
release: 'handleRelease',
};
this[handlerMap[key]]();
},
handleLogout() {
this.$modal.confirm('确定注销并退出系统吗?').then(() => {
this.$store.dispatch('LogOut').then(() => {}).finally(() => {
this.$tab.reLaunch('/pages/login')
})
})
},
async handleCancelShip() {
// 撤回发货
this.currentAction = 'withdrawShip';
const content = await this.getQRCodeContent()
let coilId = content.current_coil_id && content.current_coil_id !== 'null' ? content
.current_coil_id : null;
if (!coilId) {
uni.showToast({
title: '二维码异常',
icon: 'none'
})
return;
}
const coilRes = await getMaterialCoil(coilId);
if (coilRes.data.status == 0) {
uni.showToast({
title: '钢卷还未发货',
icon: 'error'
})
return;
}
this.coilDetail = coilRes.data;
this.$refs.shipPopup.open('bottom');
},
async handleCancelShipSubmit() {
if (!this.coilDetail.coilId) {
uni.showToast({
title: '钢卷标识缺失',
icon: 'none'
})
}
const res = await cancelExportCoil(this.coilDetail.coilId)
console.log(res)
if (res.code != 200) {
uni.showToast({
icon: 'error',
title: res.message || '撤回失败请重试'
})
return;
}
uni.showToast({
title: this.coilDetail.currentCoilNo + '发货已撤回',
icon: 'none'
})
this.$refs.shipPopup.close()
},
scan(mode = 'camera') {
return new Promise((resolve, reject) => {
if (mode == 'camera') {
uni.scanCode({
success(res) {
console.log('扫码成功')
resolve(res.result)
},
fail() {
reject()
}
})
} else if (mode == 'pda') {
this.$refs.pda.open().then(code => {
resolve(code);
}).catch(() => {
reject()
})
}
})
},
async handlePack() {
const content = await this.getQRCodeContent()
let coilId = content.current_coil_id && content.current_coil_id !== 'null' ? content
.current_coil_id : null;
if (!coilId) {
uni.showToast({
title: '二维码异常',
icon: 'none'
})
return;
}
uni.navigateTo({
url: '/pages/easycode/packing?coilId=' + coilId
})
},
async handleSee() {
const actualWarehouseId = await this.scan()
// 查询真实库区在此处的钢卷
const res = await listMaterialCoil({
actualWarehouseId,
dataType: 1
})
if (res.total == 0) {
uni.showToast({
title: '该库区未发现钢卷',
icon: 'none'
});
return;
}
if (res.rows.length == 1) {
const coilId = res.rows[0].coilId;
uni.navigateTo({
url: '/pages/coil-detail/index?coilId=' + coilId
})
} else {
uni.showToast({
title: '数据异常',
icon: 'none'
})
}
},
async handleScanCoil() {
const content = await this.getQRCodeContent();
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 (!coilId) {
uni.showToast({ title: '二维码异常', icon: 'none' });
return;
}
uni.navigateTo({
url: '/pages/coil-detail/index?coilId=' + coilId
});
},
async handleRelease() {
const actualWarehouseId = await this.scan();
// 查询真实库区在此处的钢卷
const res = await listMaterialCoil({
actualWarehouseId,
dataType: 1
})
if (res.total == 0) {
uni.showToast({
title: '该库区未发现钢卷',
icon: 'none'
});
return;
};
if (res.rows.length == 1) {
this.coilDetail = res.rows[0]
this.currentAction = 'release';
this.$refs.shipPopup.open('bottom');
} else {
uni.showToast({
title: '数据异常',
icon: 'none'
})
}
},
async handleReleaseSubmit() {
this.buttonLoading = true;
try {
const res = await forceReleaseLocation(this.coilDetail.actualWarehouseId);
if (res.code == 200) {
uni.showToast({
title: '释放成功',
icon: 'none'
})
this.$refs.shipPopup.close()
} else {
throw new Error()
}
} catch {
uni.showToast({
title: '库区释放失败',
icon: 'none'
})
} finally {
this.buttonLoading = false;
}
},
// 扫码并创建待操作(原有方法不变)
async handleScan(actionType) {
const content = await this.getQRCodeContent()
// 优先使用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;
}
console.log('提取钢卷ID - current_coil_id:', content.current_coil_id, 'coil_id:', content.coil_id,
'最终使用:', coilId);
if (!coilId) {
throw new Error('二维码中未包含有效的钢卷ID');
}
// 3. 直接通过钢卷ID获取钢卷详情
console.log('3. 获取钢卷详情钢卷ID:', coilId);
const coilRes = await getMaterialCoil(coilId);
console.log('钢卷详情响应:', coilRes);
if (coilRes.code !== 200) {
throw new Error(coilRes.msg || '查询钢卷信息失败');
}
if (!coilRes.data) {
throw new Error('未找到钢卷信息');
}
const coilData = coilRes.data;
console.log('4. 钢卷数据:', coilData);
// 4. 创建待操作记录
const pendingData = {
coilId: coilData.coilId,
currentCoilNo: coilData.currentCoilNo,
actionType: parseInt(actionType),
actionStatus: 0, // 待处理
sourceType: 'scan', // 扫码来源
scanTime: new Date().toISOString(),
scanDevice: this.getDeviceInfo(),
warehouseId: coilData.warehouseId,
priority: 0, // 默认普通优先级
remark: `移动端扫码创建-${this.getActionTypeName(actionType)}`
};
console.log('5. 创建待操作记录,数据:', pendingData);
const addRes = await addPendingAction(pendingData);
console.log('创建待操作响应:', addRes);
if (addRes.code !== 200) {
throw new Error(addRes.msg || '创建待操作失败');
}
uni.hideLoading();
console.log('=== 扫码流程完成 ===');
uni.navigateTo({
url: '/pages/scansuccess/scansuccess'
})
},
// 通用扫码获取二维码内容(原有方法不变)
async getQRCodeContent() {
return new Promise((resolve, reject) => {
this.scan().then(async res => {
uni.showLoading({
title: '处理中...'
});
try {
const qrcodeId = res;
// 1. 通过二维码ID获取二维码详情
console.log('1. 获取二维码详情ID:', qrcodeId);
const qrcodeRes = await getGenerateRecord(qrcodeId);
console.log('二维码响应:', qrcodeRes);
if (qrcodeRes.code !== 200) {
throw new Error('未找到二维码记录');
}
// 2. 解析二维码的content获取coil_id
const qrcodeRecord = qrcodeRes.data;
console.log('2. 二维码记录:', qrcodeRecord);
// 检查二维码状态0=失效1=有效)
if (qrcodeRecord.status === 0) {
uni.hideLoading();
uni.showModal({
title: '提示',
content: '该二维码已失效,无法创建待操作任务',
showCancel: false
});
return;
}
uni.hideLoading();
const content = JSON.parse(qrcodeRecord.content);
console.log('解析后的内容:', content);
resolve(content)
} catch (err) {
console.error('=== 扫码处理失败 ===');
console.error('错误信息:', err);
console.error('错误堆栈:', err.stack);
uni.hideLoading();
uni.showToast({
title: err.message || '处理失败',
icon: 'none',
duration: 3000
});
reject()
}
}).catch(() => {
reject()
})
})
},
// 发货操作(原有方法不变)
async handleShip() {
const content = await this.getQRCodeContent();
// 优先使用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;
}
console.log('提取钢卷ID - current_coil_id:', content.current_coil_id, 'coil_id:', content.coil_id,
'最终使用:', coilId);
if (!coilId) {
throw new Error('二维码中未包含有效的钢卷ID');
}
// 3. 直接通过钢卷ID获取钢卷详情
console.log('3. 获取钢卷详情钢卷ID:', coilId);
const coilRes = await getMaterialCoil(coilId);
console.log('钢卷详情响应:', coilRes);
if (coilRes.code !== 200) {
throw new Error(coilRes.msg || '查询钢卷信息失败');
}
if (!coilRes.data) {
throw new Error('未找到钢卷信息');
}
const coilData = coilRes.data;
if (coilRes.data.status == 1) {
uni.showToast({
title: '钢卷已经发货,无需再次发货'
})
return;
}
this.$refs.shipPopup.open('bottom');
this.currentAction = 'ship';
this.coilDetail = coilRes.data
},
async handleShipSubmit() {
try {
// 判断钢卷的质量状态必须是A+, AA-, B+,B,B-其中之一
// if (!['A+', 'A', 'A-', 'B+', 'B', 'B-'].includes(this.coilDetail.qualityStatus)
// && !(this.coilDetail.qualityStatus == null
// || this.coilDetail.qualityStatus == undefined
// || this.coilDetail.qualityStatus == '')
// ) {
// uni.showToast({
// title: '只能发货B-以上品质的钢卷',
// icon: 'none'
// });
// return;
// }
// 1. 更新钢卷状态为已发货
const res = await exportCoil(this.coilDetail.coilId);
// 2. 插入一条已完成的待操作记录
await addPendingAction({
coilId: this.coilDetail.coilId,
currentCoilNo: this.coilDetail.currentCoilNo,
actionType: 402, // 402=发货
actionStatus: 2, // 直接标记为完成状态
scanTime: new Date(),
scanDevice: this.getDeviceInfo(),
priority: 0, // 0=普通
sourceType: 'scan',
warehouseId: this.coilDetail.warehouseId,
processTime: new Date(),
completeTime: new Date()
});
if (res.code != 200) {
uni.showToast({
icon: 'error',
title: res.message || '发货失败请重试'
})
return;
}
uni.showToast({
title: '发货成功',
icon: 'success'
});
this.$refs.shipPopup.close()
} catch (error) {
console.error('发货失败:', error);
uni.showToast({
title: error?.message || '发货失败',
icon: 'none'
});
}
},
// 移库操作:第一步 - 扫描钢卷,打开移库弹窗(原有方法优化)
async handleTranfer() {
try {
const content = await this.getQRCodeContent()
// 优先使用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;
}
console.log('提取钢卷ID - current_coil_id:', content.current_coil_id, 'coil_id:', content.coil_id,
'最终使用:', coilId);
if (!coilId) {
throw new Error('二维码中未包含有效的钢卷ID');
}
// 3. 直接通过钢卷ID获取钢卷详情
console.log('3. 获取钢卷详情钢卷ID:', coilId);
const coilRes = await getMaterialCoil(coilId);
console.log('钢卷详情响应:', coilRes);
if (coilRes.code !== 200) {
throw new Error(coilRes.msg || '查询钢卷信息失败');
}
if (!coilRes.data) {
throw new Error('未找到钢卷信息');
}
// 重置移库状态
this.targetWarehouse = null;
this.coilDetail = coilRes.data;
this.form = {
...coilRes.data
};
// 打开移库弹窗
this.$refs.tranferPopup.open();
uni.hideLoading()
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error?.message || '打开移库失败',
icon: 'none',
duration: 2000
});
}
},
// 移库操作:第二步 - 扫描目标库区码(核心补充)
async handleScanWarehouseCode() {
try {
uni.showLoading({
title: '扫码中...'
});
// 扫描库区二维码
const scanRes = await this.scan()
uni.hideLoading();
const warehouseQrCode = scanRes;
if (!warehouseQrCode) {
throw new Error('未识别到库区信息');
}
// 解析库区二维码假设二维码内容是库区ID若为JSON格式需调整解析逻辑
const targetWarehouseId = warehouseQrCode;
console.log('扫描到的目标库区ID', targetWarehouseId);
// 调用接口获取库区详情关键通过库区ID查询名称等信息
uni.showLoading({
title: '验证库区...'
});
const warehouseRes = await getActualWarehouse(targetWarehouseId);
uni.hideLoading();
if (warehouseRes.code !== 200 || !warehouseRes.data) {
throw new Error('获取库区信息失败,请确认库区码有效');
}
const targetWarehouse = warehouseRes.data;
// 校验:目标库区不能与原库区相同
if (targetWarehouse.actualWarehouseId === this.coilDetail.actualWarehouseId) {
throw new Error('目标库区不能与当前库区相同');
}
// 存储目标库区信息(用于回显和提交)
this.targetWarehouse = targetWarehouse;
// 更新表单中的目标库区ID提交时需要
this.form.actualWarehouseId = targetWarehouse.actualWarehouseId;
this.form.actualWarehouseName = targetWarehouse.actualWarehouseName;
uni.showToast({
title: `已选中库区:${targetWarehouse.actualWarehouseName}`,
icon: 'none',
duration: 1500
});
} catch (error) {
uni.hideLoading();
uni.showToast({
title: error?.message || '扫描库区失败',
icon: 'none',
duration: 2500
});
}
},
// 移库操作:重新扫描目标库区
handleReScan() {
this.targetWarehouse = null;
this.form.warehouseId = this.coilDetail.warehouseId; // 重置为原库区ID
this.form.actualWarehouseName = this.coilDetail.actualWarehouseName;
},
// 移库操作:关闭弹窗
handleClosePopup() {
this.$refs.tranferPopup.close();
// 重置状态
this.targetWarehouse = null;
this.form = {};
this.coilDetail = {};
},
// 移库操作:第三步 - 确认移库(核心补充)
async handleConfirm() {
try {
// 再次校验:防止异常场景
if (!this.targetWarehouse || !this.form.warehouseId) {
uni.showToast({
title: '请先选择目标库区',
icon: 'none'
});
return;
}
if (this.targetWarehouse.actualWarehouseId === this.coilDetail.warehouseId) {
uni.showToast({
title: '目标库区不能与当前库区相同',
icon: 'none'
});
return;
}
this.loading = true;
uni.showLoading({
title: '移库中...'
});
// 1. 核心:更新钢卷的库区信息
const updateRes = await updateMaterialCoilSimple(this.form);
if (updateRes.code !== 200) {
throw new Error(updateRes.msg || '更新钢卷库区失败');
}
// 2. 创建移库操作记录(标记为已完成)
const pendingData = {
coilId: this.form.coilId,
currentCoilNo: this.form.currentCoilNo,
actionType: 403, // 403=移库需与字典表action_type的移库编码一致
actionStatus: 2, // 2=已完成
scanTime: new Date().toISOString(),
scanDevice: this.getDeviceInfo(),
priority: 0, // 普通优先级
sourceType: 'scan', // 扫码来源
processTime: new Date().toISOString(),
completeTime: new Date().toISOString(),
remark: `移库操作:${this.coilDetail.actualWarehouseName}${this.form.actualWarehouseName}`
};
const addRes = await addPendingAction(pendingData);
if (addRes.code !== 200) {
throw new Error(addRes.msg || '创建移库记录失败');
}
// 操作成功
uni.hideLoading();
uni.showToast({
title: '移库成功',
icon: 'success',
duration: 1500
});
// 关闭弹窗并重置状态
setTimeout(() => {
this.handleClosePopup();
}, 1500);
} catch (error) {
this.loading = false;
uni.hideLoading();
console.error('移库失败详情:', error);
uni.showToast({
title: error?.message || '移库失败,请重试',
icon: 'none',
duration: 3000
});
} finally {
this.loading = false;
}
},
// 显示BOM参数弹窗原有功能补充实现
showBomDialog() {
this.bomDialogShow = true;
// 可扩展BOM参数弹窗内容此处省略弹窗组件实现
},
// 获取设备信息(原有方法不变)
getDeviceInfo() {
try {
const systemInfo = uni.getSystemInfoSync();
return `${systemInfo.platform} ${systemInfo.model}`;
} catch (e) {
return 'Unknown Device';
}
},
// 获取操作类型名称(原有方法不变)
getActionTypeName(actionType) {
const type = this.types.find(t => t.dictValue === String(actionType));
return type ? type.dictLabel : '未知操作';
}
},
mounted() {
getDicts('action_type').then(res => {
console.log(res.data)
this.types = res.data
})
}
}
</script>
<style scoped>
.container {
padding: 20rpx;
height: 100vh;
background-color: #f5f7fa;
box-sizing: border-box;
overflow: hidden;
}
/* 操作卡片 */
.action-card {
display: flex;
align-items: center;
border-radius: 12rpx;
padding: 32rpx 28rpx;
margin-bottom: 20rpx;
border-left: 8rpx solid transparent;
}
.action-card:last-child {
margin-bottom: 0;
}
.action-card:active {
opacity: 0.85;
}
.action-body {
flex: 1;
display: flex;
flex-direction: column;
gap: 6rpx;
}
.action-name {
font-size: 28rpx;
font-weight: 600;
}
.action-desc {
font-size: 22rpx;
}
.action-arrow {
font-size: 36rpx;
margin-left: 16rpx;
}
/* 发货 - 绿色 */
.card-ship {
background: #e8f5e9;
border-left-color: #4caf50;
}
.card-ship .action-name { color: #2e7d32; }
.card-ship .action-desc { color: #66bb6a; }
.card-ship .action-arrow { color: #a5d6a7; }
/* 撤回发货 - 橙色 */
.card-cancelShip {
background: #fff3e0;
border-left-color: #ff9800;
}
.card-cancelShip .action-name { color: #e65100; }
.card-cancelShip .action-desc { color: #ffa726; }
.card-cancelShip .action-arrow { color: #ffcc80; }
/* 移库 - 蓝色 */
.card-transfer {
background: #e3f2fd;
border-left-color: #2196f3;
}
.card-transfer .action-name { color: #1565c0; }
.card-transfer .action-desc { color: #42a5f5; }
.card-transfer .action-arrow { color: #90caf9; }
/* 扫描钢卷 - 粉色 */
.card-scanCoil {
background: #fce4ec;
border-left-color: #e91e63;
}
.card-scanCoil .action-name { color: #880e4f; }
.card-scanCoil .action-desc { color: #ec407a; }
.card-scanCoil .action-arrow { color: #f48fb1; }
/* 查看 - 青色 */
.card-see {
background: #e0f7fa;
border-left-color: #0097a7;
}
.card-see .action-name { color: #006064; }
.card-see .action-desc { color: #26c6da; }
.card-see .action-arrow { color: #80deea; }
/* 释放 - 紫色 */
.card-release {
background: #f3e5f5;
border-left-color: #9c27b0;
}
.card-release .action-name { color: #6a1b9a; }
.card-release .action-desc { color: #ab47bc; }
.card-release .action-arrow { color: #ce93d8; }
/* 卡片样式 */
.info-card,
.material-card,
.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-item:last-of-type {
margin-bottom: 0;
}
.form-label {
display: block;
font-size: 28rpx;
color: #333;
margin-bottom: 15rpx;
font-weight: 500;
}
.form-label::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;
}
.form-input:focus {
background: #fff;
border-color: #007aff;
}
.form-input-disabled {
background: #f5f5f5;
color: #999;
cursor: not-allowed;
}
.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);
}
.scan-icon:active {
transform: scale(0.95);
}
.icon-camera {
font-size: 70rpx;
color: #fff;
}
.scan-tip {
font-size: 28rpx;
color: #666;
}
.scan-hint {
font-size: 24rpx;
color: #999;
}
.selected-warehouse {
display: flex;
flex-direction: column;
align-items: center;
gap: 15rpx;
padding: 20rpx;
background-color: #f0f9ff;
border-radius: 12rpx;
width: 100%;
box-sizing: border-box;
}
.warehouse-info {
display: flex;
align-items: center;
gap: 10rpx;
font-size: 28rpx;
}
.warehouse-label {
color: #666;
}
.warehouse-value {
color: #1976d2;
font-weight: 600;
}
.re-scan-btn {
padding: 10rpx 30rpx;
font-size: 24rpx;
color: #409eff;
background-color: transparent;
border: 2rpx solid #409eff;
border-radius: 8rpx;
}
.action-buttons {
display: flex;
gap: 20rpx;
margin-top: 40rpx;
}
.btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
font-size: 28rpx;
border-radius: 12rpx;
border: none;
}
.btn-secondary {
background-color: #f5f5f5;
color: #666;
}
.btn-primary {
background-color: #409eff;
color: #fff;
}
.btn:disabled {
opacity: 0.6;
}
.info-grid {
display: flex;
flex-direction: column;
gap: 20rpx;
margin-top: 20rpx;
}
.info-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10rpx 0;
border-bottom: 2rpx solid #f5f5f5;
}
.item-label {
font-size: 26rpx;
color: #666;
width: 30%;
}
.item-value {
font-size: 26rpx;
color: #333;
width: 70%;
text-align: right;
word-break: break-all;
}
.card-title {
display: flex;
align-items: center;
justify-content: space-between;
}
.title-dot {
display: inline-block;
width: 12rpx;
height: 12rpx;
background-color: #409eff;
border-radius: 50%;
margin-right: 10rpx;
}
.title-text {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
.more-btn {
display: flex;
align-items: center;
gap: 5rpx;
color: #409eff;
font-size: 24rpx;
}
.icon-more {
font-size: 26rpx;
}
</style>