<template>
|
<view class="scan-container">
|
<PageHeader title="扫码出库"
|
@back="goBack" />
|
<view class="module-selector"
|
v-if="!showForm">
|
<view class="module-card"
|
@click="startScan('qualified')">
|
<view class="module-icon qualified">
|
<u-icon name="checkbox-mark"
|
color="#fff"
|
size="40"></u-icon>
|
</view>
|
<view class="module-info">
|
<text class="module-label">合格出库</text>
|
<text class="module-desc">扫描合格品进行领用出库</text>
|
</view>
|
</view>
|
<view class="module-card"
|
@click="startScan('unqualified')">
|
<view class="module-icon unqualified">
|
<u-icon name="close"
|
color="#fff"
|
size="40"></u-icon>
|
</view>
|
<view class="module-info">
|
<text class="module-label">不合格出库</text>
|
<text class="module-desc">记录不合格品的出库流向</text>
|
</view>
|
</view>
|
</view>
|
<view class="form-content"
|
v-if="showForm">
|
<u-form ref="formRef"
|
:model="form"
|
:rules="formRules"
|
label-width="100px">
|
<u-form-item label="出库类型"
|
border-bottom>
|
<u-tag :text="type === 'qualified' ? '合格出库' : '不合格出库'"
|
:type="type === 'qualified' ? 'success' : 'error'"></u-tag>
|
</u-form-item>
|
<u-form-item label="产品名称"
|
border-bottom>
|
<u-input v-model="form.productName"
|
readonly
|
border="none"></u-input>
|
</u-form-item>
|
<u-form-item label="规格型号"
|
border-bottom>
|
<u-input v-model="form.model"
|
readonly
|
border="none"></u-input>
|
</u-form-item>
|
<u-form-item label="可用库存"
|
border-bottom>
|
<u-input v-model="form.unLockedQuantity"
|
readonly
|
border="none"></u-input>{{form.unit}}
|
</u-form-item>
|
<u-form-item label="出库数量"
|
prop="qualitity"
|
required
|
border-bottom>
|
<u-number-box v-model="form.qualitity"
|
:min="1"
|
:max="form.unLockedQuantity"
|
:step="1"></u-number-box>
|
<text class="limit-tip">最大可领用: {{form.unLockedQuantity}}</text>
|
</u-form-item>
|
<u-form-item label="备注"
|
prop="remark"
|
border-bottom>
|
<u-textarea v-model="form.remark"
|
placeholder="请输入备注"
|
count></u-textarea>
|
</u-form-item>
|
</u-form>
|
<view class="footer-btns">
|
<u-button class="cancel-btn"
|
@click="cancelForm">取消</u-button>
|
<u-button class="save-btn"
|
@click="handleSubmit"
|
:loading="loading">确认出库</u-button>
|
</view>
|
</view>
|
</view>
|
</template>
|
|
<script setup>
|
import { ref, reactive } from "vue";
|
import PageHeader from "@/components/PageHeader.vue";
|
import {
|
subtractStockInventory,
|
getStockInventoryListPage,
|
} from "@/api/inventoryManagement/stockInventory.js";
|
import {
|
subtractStockUnInventory,
|
getStockUninventoryListPage,
|
} from "@/api/inventoryManagement/stockUninventory.js";
|
import modal from "@/plugins/modal";
|
|
const showForm = ref(false);
|
const type = ref("qualified"); // qualified | unqualified
|
const loading = ref(false);
|
const formRef = ref(null);
|
|
const form = ref({
|
id: undefined,
|
productId: undefined,
|
productModelId: undefined,
|
productName: "",
|
model: "",
|
unit: "",
|
qualitity: 1,
|
unLockedQuantity: 0,
|
remark: "",
|
});
|
|
const formRules = {
|
qualitity: [
|
{
|
required: true,
|
type: "number",
|
message: "请输入出库数量",
|
trigger: ["blur", "change"],
|
},
|
{
|
validator: (rule, value, callback) => {
|
if (value > form.value.unLockedQuantity) {
|
callback(new Error("不能超过可用库存"));
|
} else {
|
callback();
|
}
|
},
|
trigger: ["blur", "change"],
|
},
|
],
|
};
|
|
const goBack = () => {
|
if (showForm.value) {
|
showForm.value = false;
|
} else {
|
uni.navigateBack();
|
}
|
};
|
|
const cancelForm = () => {
|
showForm.value = false;
|
};
|
|
const startScan = scanType => {
|
type.value = scanType;
|
uni.scanCode({
|
success: res => {
|
handleScanResult(res.result);
|
},
|
fail: err => {
|
modal.msgError("扫码失败");
|
},
|
});
|
};
|
|
const handleScanResult = async result => {
|
try {
|
// 解析二维码数据
|
const scanData = JSON.parse(result);
|
if (!scanData.id) {
|
modal.msgError("无效的二维码数据");
|
return;
|
}
|
|
// 获取实时库存详情
|
modal.loading("获取产品库存详情...");
|
const apiCall =
|
type.value === "qualified"
|
? getStockInventoryListPage
|
: getStockUninventoryListPage;
|
|
const res = await apiCall({ productModelId: scanData.id });
|
modal.closeLoading();
|
|
if (res.code === 200 && res.data.records && res.data.records.length > 0) {
|
const detail = res.data.records[0];
|
form.value.id = detail.id;
|
form.value.productId = detail.productId;
|
form.value.productName = detail.productName;
|
form.value.productModelId = detail.productModelId;
|
form.value.model = detail.model;
|
form.value.unit = detail.unit;
|
form.value.unLockedQuantity = detail.unLockedQuantity;
|
form.value.qualitity = 1;
|
form.value.remark = "";
|
|
if (form.value.unLockedQuantity <= 0) {
|
modal.msgError("当前库存不足,无法出库");
|
return;
|
}
|
|
showForm.value = true;
|
} else {
|
modal.msgError("未找到该产品型号的库存记录");
|
}
|
} catch (error) {
|
modal.closeLoading();
|
console.error("处理扫码结果失败", error);
|
modal.msgError("扫码处理失败,请重试");
|
}
|
};
|
|
const handleSubmit = async () => {
|
try {
|
const valid = await formRef.value.validate();
|
if (!valid) return;
|
|
loading.value = true;
|
const apiCall =
|
type.value === "qualified"
|
? subtractStockInventory
|
: subtractStockUnInventory;
|
|
const res = await apiCall(form.value);
|
if (res.code === 200) {
|
modal.msgSuccess("出库成功");
|
setTimeout(() => {
|
showForm.value = false;
|
}, 1500);
|
}
|
} catch (error) {
|
console.error("提交失败", error);
|
} finally {
|
loading.value = false;
|
}
|
};
|
</script>
|
|
<style scoped lang="scss">
|
.scan-container {
|
min-height: 100vh;
|
background-color: #f5f7fa;
|
}
|
|
.module-selector {
|
display: flex;
|
flex-direction: column;
|
padding: 40rpx;
|
height: 80vh;
|
justify-content: center;
|
}
|
|
.module-card {
|
display: flex;
|
align-items: center;
|
background-color: #fff;
|
padding: 80rpx 50rpx;
|
border-radius: 32rpx;
|
box-shadow: 0 10rpx 30rpx rgba(0, 0, 0, 0.05);
|
margin-bottom: 50rpx;
|
transition: all 0.3s ease;
|
border: 2rpx solid transparent;
|
|
&:active {
|
transform: scale(0.98);
|
background-color: #f9f9f9;
|
}
|
}
|
|
.module-icon {
|
width: 140rpx;
|
height: 140rpx;
|
border-radius: 32rpx;
|
display: flex;
|
justify-content: center;
|
align-items: center;
|
margin-right: 40rpx;
|
|
&.qualified {
|
background: linear-gradient(135deg, #52c41a, #73d13d);
|
box-shadow: 0 10rpx 20rpx rgba(82, 196, 26, 0.2);
|
}
|
|
&.unqualified {
|
background: linear-gradient(135deg, #ff4d4f, #ff7875);
|
box-shadow: 0 10rpx 20rpx rgba(255, 77, 79, 0.2);
|
}
|
}
|
|
.module-info {
|
display: flex;
|
flex-direction: column;
|
}
|
|
.module-label {
|
font-size: 40rpx;
|
font-weight: 700;
|
color: #1a1a1a;
|
margin-bottom: 12rpx;
|
}
|
|
.module-desc {
|
font-size: 28rpx;
|
color: #999;
|
}
|
|
.form-content {
|
background-color: #fff;
|
margin: 20rpx;
|
padding: 30rpx;
|
border-radius: 16rpx;
|
}
|
|
.limit-tip {
|
font-size: 24rpx;
|
color: #999;
|
margin-left: 20rpx;
|
}
|
|
.footer-btns {
|
margin-top: 60rpx;
|
display: flex;
|
justify-content: space-between;
|
padding-bottom: 40rpx;
|
}
|
|
.cancel-btn {
|
width: 30%;
|
background-color: #f5f5f5;
|
color: #666;
|
border: none;
|
}
|
|
.save-btn {
|
width: 65%;
|
background: linear-gradient(140deg, #00baff 0%, #006cfb 100%);
|
color: #fff;
|
border: none;
|
}
|
</style>
|