406 lines
11 KiB
Vue
406 lines
11 KiB
Vue
<template>
|
||
<view class="destroy-container">
|
||
<common-header title="销毁酒类" theme="common" @back="goBack" />
|
||
|
||
<view class="destroy-content page-content">
|
||
<!-- 扫描区域 -->
|
||
<view class="scan-section">
|
||
<view class="scan-card">
|
||
<uni-icons type="scan" size="80" color="#f44336" />
|
||
<text class="scan-title">扫描二维码销毁酒类</text>
|
||
<text class="scan-desc">请扫描酒类包装上的二维码</text>
|
||
<uni-button type="primary" @click="scanQRCode">
|
||
<uni-icons type="scan" size="20" color="#fff" />
|
||
开始扫描
|
||
</uni-button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 手动输入区域 -->
|
||
<view class="manual-section">
|
||
<uni-card title="手动输入" :is-shadow="false">
|
||
<uni-forms ref="form" :model="formData" :rules="rules" label-width="120">
|
||
<uni-forms-item label="SKU编码" required name="skuSn">
|
||
<uni-easyinput
|
||
v-model="formData.skuSn"
|
||
placeholder="请输入或扫描SKU编码"
|
||
maxlength="20"
|
||
@input="validateSkuSn" />
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item v-if="liquorInfo.name" label="酒类名称">
|
||
<text class="info-text">{{ liquorInfo.name }}</text>
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item v-if="liquorInfo.batch" label="生产批次">
|
||
<text class="info-text">{{ liquorInfo.batch }}</text>
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item v-if="liquorInfo.productDate" label="生产日期">
|
||
<text class="info-text">{{ liquorInfo.productDate }}</text>
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item v-if="liquorInfo.volume" label="规格容量">
|
||
<text class="info-text">{{ liquorInfo.volume }}</text>
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item label="销毁原因" required name="reason">
|
||
<uni-data-select
|
||
v-model="formData.reason"
|
||
:localdata="reasonOptions"
|
||
placeholder="请选择销毁原因" />
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item label="销毁数量" required name="quantity">
|
||
<uni-easyinput
|
||
v-model="formData.quantity"
|
||
type="number"
|
||
placeholder="请输入销毁数量"
|
||
:min="1" />
|
||
</uni-forms-item>
|
||
|
||
<uni-forms-item label="销毁备注" name="remark">
|
||
<uni-easyinput
|
||
v-model="formData.remark"
|
||
type="textarea"
|
||
placeholder="请输入销毁备注"
|
||
:auto-height="true" />
|
||
</uni-forms-item>
|
||
</uni-forms>
|
||
</uni-card>
|
||
</view>
|
||
|
||
<!-- 销毁确认 -->
|
||
<view class="confirm-section">
|
||
<uni-card title="销毁确认" :is-shadow="false">
|
||
<view class="warning-box">
|
||
<uni-icons type="warning" size="24" color="#f44336" />
|
||
<text class="warning-text">销毁操作不可逆,请谨慎操作!</text>
|
||
</view>
|
||
|
||
<view class="form-actions">
|
||
<uni-button type="default" @click="goBack">取消</uni-button>
|
||
<uni-button
|
||
type="primary"
|
||
@click="confirmDestroy"
|
||
:loading="submitting"
|
||
:disabled="!formData.skuSn || !formData.reason || !formData.quantity">
|
||
确认销毁
|
||
</uni-button>
|
||
</view>
|
||
</uni-card>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import CommonHeader from '@/components/common-header/common-header.vue'
|
||
import { getLiquorInfo, destroyLiquor } from '@/api/common/destroy'
|
||
|
||
export default {
|
||
name: 'DestroyLiquor',
|
||
components: {
|
||
CommonHeader
|
||
},
|
||
|
||
data() {
|
||
return {
|
||
formData: {
|
||
skuSn: '',
|
||
reason: '',
|
||
quantity: 1,
|
||
remark: ''
|
||
},
|
||
liquorInfo: {
|
||
name: '',
|
||
batch: '',
|
||
productDate: '',
|
||
volume: ''
|
||
},
|
||
rules: {
|
||
skuSn: {
|
||
rules: [
|
||
{ required: true, errorMessage: '请输入SKU编码' },
|
||
{ pattern: /^\d{14}$/, errorMessage: 'SKU编码必须为14位数字' }
|
||
]
|
||
},
|
||
reason: {
|
||
rules: [{ required: true, errorMessage: '请选择销毁原因' }]
|
||
},
|
||
quantity: {
|
||
rules: [
|
||
{ required: true, errorMessage: '请输入销毁数量' },
|
||
{ pattern: /^[1-9]\d*$/, errorMessage: '销毁数量必须为正整数' }
|
||
]
|
||
}
|
||
},
|
||
reasonOptions: [
|
||
{ value: '1', text: '过期变质' },
|
||
{ value: '2', text: '质量问题' },
|
||
{ value: '3', text: '包装破损' },
|
||
{ value: '4', text: '批次召回' },
|
||
{ value: '5', text: '库存清理' },
|
||
{ value: '6', text: '其他原因' }
|
||
],
|
||
submitting: false
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
// 扫描二维码
|
||
scanQRCode() {
|
||
// #ifdef APP-PLUS
|
||
uni.scanCode({
|
||
success: (res) => {
|
||
console.log('扫码结果:', res.result)
|
||
this.parseQRCode(res.result)
|
||
},
|
||
fail: (err) => {
|
||
console.error('扫码失败:', err)
|
||
this.$modal.showToast('扫码失败,请重试')
|
||
}
|
||
})
|
||
// #endif
|
||
|
||
// #ifdef H5 || MP-WEIXIN
|
||
uni.scanCode({
|
||
success: (res) => {
|
||
console.log('扫码结果:', res.result)
|
||
this.parseQRCode(res.result)
|
||
},
|
||
fail: (err) => {
|
||
console.error('扫码失败:', err)
|
||
this.$modal.showToast('扫码失败,请重试')
|
||
}
|
||
})
|
||
// #endif
|
||
},
|
||
|
||
// 解析二维码
|
||
parseQRCode(qrResult) {
|
||
try {
|
||
// 解析二维码链接: ma.zdtap.com/scan?skuSn=25070296207007
|
||
let url
|
||
if (qrResult.startsWith('http://') || qrResult.startsWith('https://')) {
|
||
url = new URL(qrResult)
|
||
} else {
|
||
// 处理不带协议的链接
|
||
url = new URL(`https://${qrResult}`)
|
||
}
|
||
|
||
const skuSn = url.searchParams.get('skuSn')
|
||
|
||
if (!skuSn) {
|
||
this.$modal.showToast('二维码格式错误,未找到SKU编码')
|
||
return
|
||
}
|
||
|
||
// 验证SKU编码格式(14位数字)
|
||
if (!/^\d{14}$/.test(skuSn)) {
|
||
this.$modal.showToast('SKU编码格式错误,应为14位数字')
|
||
return
|
||
}
|
||
|
||
this.formData.skuSn = skuSn
|
||
this.getLiquorInfoBySkuSn(skuSn)
|
||
this.$modal.showToast('扫码成功!')
|
||
|
||
} catch (error) {
|
||
console.error('解析二维码失败:', error)
|
||
this.$modal.showToast('二维码格式错误,请重新扫描')
|
||
}
|
||
},
|
||
|
||
// 验证SKU编码
|
||
validateSkuSn() {
|
||
if (this.formData.skuSn && /^\d{14}$/.test(this.formData.skuSn)) {
|
||
this.getLiquorInfoBySkuSn(this.formData.skuSn)
|
||
} else {
|
||
this.clearLiquorInfo()
|
||
}
|
||
},
|
||
|
||
// 获取酒类信息
|
||
async getLiquorInfoBySkuSn(skuSn) {
|
||
try {
|
||
const response = await getLiquorInfo(skuSn)
|
||
if (response.code === 200) {
|
||
this.liquorInfo = response.data || {}
|
||
} else {
|
||
// 如果API返回错误,显示默认信息
|
||
this.showDefaultLiquorInfo(skuSn)
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('获取酒类信息失败:', error)
|
||
this.showDefaultLiquorInfo(skuSn)
|
||
}
|
||
},
|
||
|
||
// 显示默认酒类信息
|
||
showDefaultLiquorInfo(skuSn) {
|
||
this.liquorInfo = {
|
||
name: '茅台酒(53度)',
|
||
batch: `MT${skuSn.substring(0, 8)}`,
|
||
productDate: `20${skuSn.substring(0, 2)}-${skuSn.substring(2, 4)}-${skuSn.substring(4, 6)}`,
|
||
volume: '500ml'
|
||
}
|
||
},
|
||
|
||
// 清空酒类信息
|
||
clearLiquorInfo() {
|
||
this.liquorInfo = {
|
||
name: '',
|
||
batch: '',
|
||
productDate: '',
|
||
volume: ''
|
||
}
|
||
},
|
||
|
||
// 确认销毁
|
||
async confirmDestroy() {
|
||
try {
|
||
// 表单验证
|
||
const valid = await this.$refs.form.validate()
|
||
if (!valid) return
|
||
|
||
// 二次确认
|
||
const confirmRes = await this.$modal.showConfirm(
|
||
`确定要销毁SKU编码为 ${this.formData.skuSn} 的酒类吗?\n数量:${this.formData.quantity} 瓶\n此操作不可逆!`
|
||
)
|
||
if (!confirmRes.confirm) return
|
||
|
||
this.submitting = true
|
||
|
||
// 提交销毁请求
|
||
const destroyData = {
|
||
skuSn: this.formData.skuSn,
|
||
reason: this.formData.reason,
|
||
quantity: parseInt(this.formData.quantity),
|
||
remark: this.formData.remark,
|
||
destroyTime: new Date().toISOString(),
|
||
liquorInfo: this.liquorInfo
|
||
}
|
||
|
||
const response = await destroyLiquor(destroyData)
|
||
|
||
if (response.code === 200) {
|
||
this.$modal.showToast('销毁成功!')
|
||
|
||
// 重置表单
|
||
this.resetForm()
|
||
|
||
// 可选:返回上一页
|
||
// setTimeout(() => {
|
||
// uni.navigateBack()
|
||
// }, 1500)
|
||
} else {
|
||
this.$modal.showToast(response.message || '销毁失败')
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('销毁失败:', error)
|
||
this.$modal.showToast('销毁失败,请重试')
|
||
} finally {
|
||
this.submitting = false
|
||
}
|
||
},
|
||
|
||
// 重置表单
|
||
resetForm() {
|
||
this.formData = {
|
||
skuSn: '',
|
||
reason: '',
|
||
quantity: 1,
|
||
remark: ''
|
||
}
|
||
this.clearLiquorInfo()
|
||
},
|
||
|
||
goBack() {
|
||
uni.navigateBack()
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.destroy-container {
|
||
min-height: 100vh;
|
||
background: #f5f7fa;
|
||
}
|
||
|
||
.destroy-content {
|
||
padding: 20rpx;
|
||
}
|
||
|
||
.scan-section {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.scan-card {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 60rpx 30rpx;
|
||
text-align: center;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
|
||
}
|
||
|
||
.scan-title {
|
||
display: block;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
margin: 30rpx 0 10rpx;
|
||
}
|
||
|
||
.scan-desc {
|
||
display: block;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
margin-bottom: 40rpx;
|
||
}
|
||
|
||
.manual-section {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.info-text {
|
||
font-size: 28rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.confirm-section {
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.warning-box {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 20rpx;
|
||
background: #fff5f5;
|
||
border: 1px solid #fecaca;
|
||
border-radius: 8rpx;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.warning-text {
|
||
font-size: 28rpx;
|
||
color: #dc2626;
|
||
margin-left: 10rpx;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.form-actions {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
margin-top: 30rpx;
|
||
}
|
||
|
||
.form-actions uni-button {
|
||
flex: 1;
|
||
}
|
||
</style>
|
||
|