<template>
|
<div class="app-container">
|
<div class="search_form">
|
<div>
|
<span class="search_title ml10">产品名称:</span>
|
<el-input v-model="searchForm.productName"
|
style="width: 240px"
|
placeholder="请输入"
|
clearable/>
|
<span class="search_title ml10">产品大类:</span>
|
<el-select v-model="searchForm.parentId"
|
style="width: 240px"
|
placeholder="请输入"
|
clearable>
|
<el-option v-for="item of selectList" :key="item.id" :label="item.name" :value="item.id"></el-option>
|
</el-select>
|
<el-button type="primary" @click="handleQuery" style="margin-left: 10px">搜索</el-button>
|
</div>
|
<div>
|
<el-button type="primary" @click="isShowNewModal = true">新增库存</el-button>
|
<el-button type="info" plain icon="Upload" @click="isShowImportModal = true">
|
导入库存
|
</el-button>
|
<el-button @click="handleOut">导出</el-button>
|
</div>
|
</div>
|
<div class="table_list">
|
<el-table :data="tableData" border v-loading="tableLoading" @selection-change="handleSelectionChange"
|
:expand-row-keys="expandedRowKeys" :row-key="row => row.id" style="width: 100%"
|
:row-class-name="tableRowClassName" height="calc(100vh - 18.5em)">
|
<el-table-column align="center" type="selection" width="55" />
|
<el-table-column align="center" label="序号" type="index" width="60" />
|
<el-table-column label="产品名称" prop="productName" show-overflow-tooltip />
|
<el-table-column label="规格型号" prop="model" show-overflow-tooltip />
|
<el-table-column label="单位" prop="unit" show-overflow-tooltip />
|
<el-table-column label="产品图片" prop="url" show-overflow-tooltip>
|
<template #default="scope">
|
<img :src="scope.row.url" alt="产品图片" style="width: 50px; height: 50px;">
|
</template>
|
</el-table-column>
|
<el-table-column label="高度" prop="height" show-overflow-tooltip />
|
<el-table-column label="每件数量(支)" prop="boxNum" show-overflow-tooltip />
|
<el-table-column label="单价(美元/件)" prop="dollarPrice" show-overflow-tooltip />
|
<el-table-column label="单价(元/件)" prop="taxInclusiveUnitPrice" show-overflow-tooltip />
|
<el-table-column label="库存数量" prop="qualitity" show-overflow-tooltip />
|
<el-table-column label="冻结数量" prop="lockedQuantity" show-overflow-tooltip />
|
<el-table-column label="库存预警数量" prop="warnNum" show-overflow-tooltip />
|
<el-table-column label="备注" prop="remark" show-overflow-tooltip />
|
<el-table-column label="最近更新时间" prop="updateTime" show-overflow-tooltip />
|
<el-table-column fixed="right" label="操作" width="160" align="center">
|
<template #default="scope">
|
<el-button link type="primary" size="small" @click="showSubtractModal(scope.row)" :disabled="scope.row.unLockedQuantity === 0">领用</el-button>
|
<el-button link type="primary" size="small" v-if="scope.row.unLockedQuantity > 0" @click="showFrozenModal(scope.row)">冻结</el-button>
|
<el-button link type="primary" size="small" v-if="scope.row.lockedQuantity > 0" @click="showThawModal(scope.row)">解冻</el-button>
|
<el-button link type="success" size="small" @click="showQRCode(scope.row,2)">生成条形码</el-button>
|
<el-button link type="success" size="small" @click="showERCode(scope.row,2)">生成二维码</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
<pagination v-show="total > 0" :total="total" layout="total, sizes, prev, pager, next, jumper"
|
:page="page.current" :limit="page.size" @pagination="paginationChange" />
|
</div>
|
<new-stock-inventory v-if="isShowNewModal"
|
v-model:visible="isShowNewModal"
|
type="qualified"
|
@completed="handleQuery" />
|
|
<subtract-stock-inventory v-if="isShowSubtractModal"
|
v-model:visible="isShowSubtractModal"
|
:record="record"
|
type="qualified"
|
@completed="handleQuery" />
|
<!-- 导入库存-->
|
<import-stock-inventory v-if="isShowImportModal"
|
v-model:visible="isShowImportModal"
|
type="qualified"
|
@uploadSuccess="handleQuery" />
|
<!-- 冻结/解冻库存-->
|
<frozen-and-thaw-stock-inventory v-if="isShowFrozenAndThawModal"
|
v-model:visible="isShowFrozenAndThawModal"
|
:record="record"
|
:operation-type="operationType"
|
type="qualified"
|
@completed="handleQuery" />
|
|
<el-dialog
|
v-model="qrCodeDialogVisible"
|
title="商品条形码"
|
width="400px"
|
center
|
>
|
<div style="text-align: center;">
|
<img id="barcode" style="width:200px;height: 50px;"/>
|
<!-- <img :src="qrCodeUrl" alt="二维码" style="width:200px;height:200px;" /> -->
|
<div style="margin: 20px;">
|
<el-button type="primary" @click="downloadQRCode">下载条形码</el-button>
|
</div>
|
</div>
|
</el-dialog>
|
<!-- 二维码显示对话框 -->
|
<el-dialog
|
v-model="erCodeDialogVisible"
|
title="商品二维码"
|
width="400px"
|
center
|
>
|
<div style="text-align: center;">
|
<img :src="erCodeUrl" alt="二维码" style="width:200px;height:200px;" />
|
<div style="margin: 20px;">
|
<el-button type="primary" @click="downloadERCode">下载二维码图片</el-button>
|
</div>
|
</div>
|
</el-dialog>
|
<el-dialog v-model="barcodeDia" title="产品信息" width="40%" @close="closeBarcodeDia">
|
<div>
|
<el-row v-if="barcodeDetail.url" :gutter="30">
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">图片</div>
|
<img class="barcode-img" :src="javaApiUrl+barcodeDetail.url"></img>
|
</div>
|
</el-col>
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">名称</div>
|
<div class="barcode-value">{{barcodeDetail.productCategory}}</div>
|
</div>
|
</el-col>
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">产品高度</div>
|
<div class="barcode-value">{{barcodeDetail.height}}</div>
|
</div>
|
</el-col>
|
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">单价(美元)/件</div>
|
<div class="barcode-value">{{barcodeDetail.dollarPrice}}</div>
|
</div>
|
</el-col>
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">单价(元)/件</div>
|
<div class="barcode-value">{{barcodeDetail.taxInclusiveUnitPrice}}</div>
|
</div>
|
</el-col>
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">数量/件</div>
|
<div class="barcode-value">{{barcodeDetail.qualitity}}</div>
|
</div>
|
</el-col>
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">每件数量/支</div>
|
<div class="barcode-value">{{barcodeDetail.boxNum}}</div>
|
</div>
|
</el-col>
|
</el-row>
|
<el-row :gutter="30">
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">规格</div>
|
<div class="barcode-value">{{barcodeDetail.model}}</div>
|
</div>
|
</el-col>
|
<el-col :span="12">
|
<div class="barcode-item">
|
<div class="barcode-label">单位</div>
|
<div class="barcode-value">{{barcodeDetail.unit}}</div>
|
</div>
|
</el-col>
|
</el-row>
|
</div>
|
<template #footer>
|
<div class="dialog-footer">
|
<el-button @click="closeBarcodeDia">关闭</el-button>
|
</div>
|
</template>
|
</el-dialog>
|
</div>
|
</template>
|
|
<script setup>
|
import pagination from '@/components/PIMTable/Pagination.vue'
|
import { ref, reactive, toRefs, onMounted, getCurrentInstance } from 'vue'
|
import {ElMessage, ElMessageBox} from "element-plus";
|
import {
|
getStockInventoryListPage,
|
getProductList,
|
getStockInventoryById
|
} from "@/api/inventoryManagement/stockInventory.js";
|
import QRCode from "qrcode";
|
import JsBarcode from "jsbarcode";
|
|
const NewStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/New.vue"));
|
const SubtractStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Subtract.vue"));
|
const ImportStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/Import.vue"));
|
const FrozenAndThawStockInventory = defineAsyncComponent(() => import("@/views/inventoryManagement/stockManagement/FrozenAndThaw.vue"));
|
const { proxy } = getCurrentInstance()
|
const tableData = ref([])
|
const selectedRows = ref([])
|
const record = ref({})
|
const tableLoading = ref(false)
|
const page = reactive({
|
current: 1,
|
size: 100,
|
})
|
const total = ref(0)
|
// 是否显示新增弹框
|
const isShowNewModal = ref(false)
|
// 是否显示领用弹框
|
const isShowSubtractModal = ref(false)
|
// 是否显示冻结/解冻弹框
|
const isShowFrozenAndThawModal = ref(false)
|
// 操作类型
|
const operationType = ref('frozen')
|
// 是否显示导入弹框
|
const isShowImportModal = ref(false)
|
const data = reactive({
|
searchForm: {
|
productName: '',
|
},
|
selectList:[
|
{
|
id:1,
|
label:1
|
},{
|
id:2,
|
label:2
|
},{
|
id:3,
|
label:3
|
}
|
]
|
})
|
const { searchForm, selectList } = toRefs(data)
|
|
// 查询列表
|
/** 搜索按钮操作 */
|
const handleQuery = () => {
|
page.current = 1
|
getList()
|
}
|
const paginationChange = (obj) => {
|
page.current = obj.page;
|
page.size = obj.limit;
|
getList()
|
}
|
const getList = () => {
|
tableLoading.value = true
|
getStockInventoryListPage({ ...searchForm.value, ...page }).then(res => {
|
tableLoading.value = false
|
tableData.value = res.data.records
|
total.value = res.data.total
|
// 数据加载完成后检查库存
|
// checkStockAndCreatePurchase();
|
}).catch(() => {
|
tableLoading.value = false
|
})
|
}
|
|
const handleFileSuccess = (response) => {
|
const { code, msg } = response;
|
if (code == 200) {
|
ElMessage({ message: "导入成功", type: "success" });
|
upload.open = false;
|
emits("uploadSuccess");
|
} else {
|
ElMessage({ message: msg, type: "error" });
|
}
|
};
|
|
// 点击领用
|
const showSubtractModal = (row) => {
|
record.value = row
|
isShowSubtractModal.value = true
|
}
|
|
// 点击冻结
|
const showFrozenModal = (row) => {
|
record.value = row
|
isShowFrozenAndThawModal.value = true
|
operationType.value = 'frozen'
|
}
|
|
// 点击解冻
|
const showThawModal = (row) => {
|
record.value = row
|
isShowFrozenAndThawModal.value = true
|
operationType.value = 'thaw'
|
}
|
|
// 表格选择数据
|
const handleSelectionChange = (selection) => {
|
// 过滤掉子数据
|
selectedRows.value = selection.filter(item => item.id);
|
console.log('selection', selectedRows.value)
|
}
|
const expandedRowKeys = ref([])
|
|
// 表格行类名
|
const tableRowClassName = ({ row }) => {
|
const stock = Number(row?.unLockedQuantity ?? 0);
|
const warn = Number(row?.warnNum ?? 0);
|
if (!Number.isFinite(stock) || !Number.isFinite(warn)) {
|
return '';
|
}
|
return stock < warn ? 'row-low-stock' : '';
|
};
|
|
// 导出
|
const handleOut = () => {
|
ElMessageBox.confirm(
|
'是否确认导出?',
|
'导出', {
|
confirmButtonText: '确认',
|
cancelButtonText: '取消',
|
type: 'warning',
|
}
|
).then(() => {
|
proxy.download("/stockInventory/exportStockInventory", {}, '合格库存信息.xlsx')
|
}).catch(() => {
|
proxy.$modal.msg("已取消")
|
})
|
}
|
// 获取产品大类下拉
|
const productCategorySelectList =async ()=>{
|
let res =await getProductList()
|
console.log(res)
|
if(res?.code === 200 && res?.data) {
|
selectList.value = res.data
|
}
|
}
|
|
|
// 二维码相关变量
|
const qrCodeDialogVisible = ref(false);
|
const qrCodeUrl = ref("");
|
const showQRCode = async (row,type) => {
|
if(barcodeDia.value)return
|
try {
|
// 构建二维码内容,只包含采购合同号(纯文本)
|
let qrContent = row.id || '';
|
// 检查内容是否为空
|
if (!qrContent) {
|
proxy.$modal.msgWarning("该行商品id,无法生成条形码");
|
return;
|
}
|
qrContent+=`,${type}`
|
qrCodeDialogVisible.value = true;
|
await nextTick();
|
JsBarcode("#barcode", qrContent+'', {
|
width:10,
|
height:100,
|
displayValue: false
|
});
|
} catch (error) {
|
console.error('生成条形码失败:', error);
|
proxy.$modal.msgError("生成条形码失败:" + error.message);
|
}
|
};
|
|
const erCodeDialogVisible = ref(false);
|
const erCodeUrl = ref("");
|
const showERCode = async (row,type) => {
|
if(barcodeDia.value)return
|
let qrContent = row.id || '';
|
// 检查内容是否为空
|
if (!qrContent) {
|
proxy.$modal.msgWarning("该行商品id,无法生成二维码");
|
return;
|
}
|
qrContent+=`,${type}`
|
try {
|
erCodeUrl.value = await QRCode.toDataURL(qrContent+'', {
|
width: 200,
|
margin: 2,
|
color: {
|
dark: '#000000',
|
light: '#FFFFFF'
|
}
|
});
|
erCodeDialogVisible.value = true;
|
} catch (error) {
|
console.error('生成二维码失败:', error);
|
proxy.$modal.msgError("生成二维码失败:" + error.message);
|
}
|
};
|
const downloadQRCode = () => {
|
const imgSrc = document.getElementById('barcode').src
|
const a = document.createElement('a');
|
if(!imgSrc){
|
proxy.$modal.msgWarning('暂无条形码')
|
return
|
}
|
a.href = imgSrc;
|
a.download = `商品条形码_${new Date().getTime()}.png`;
|
document.body.appendChild(a);
|
a.click();
|
document.body.removeChild(a);
|
proxy.$modal.msgSuccess("下载成功");
|
};
|
|
// 下载二维码
|
const downloadERCode = () => {
|
if (!erCodeUrl.value) {
|
proxy.$modal.msgWarning("二维码未生成");
|
return;
|
}
|
|
const a = document.createElement('a');
|
a.href = erCodeUrl.value;
|
a.download = `商品二维码_${new Date().getTime()}.png`;
|
document.body.appendChild(a);
|
a.click();
|
document.body.removeChild(a);
|
proxy.$modal.msgSuccess("下载成功");
|
};
|
|
//扫码相关参数
|
const barcodeDia = ref(false);
|
const scanBarcodeInput = ref('');
|
const barcodeDetail = ref({})
|
// 扫码函数
|
function scanBarcode (e){
|
if(!e||!e.target||!e.target.tagName){
|
return;
|
}
|
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') {
|
return
|
}
|
if (e.key === 'Enter') {
|
let _value = scanBarcodeInput.value
|
getDetail(_value)
|
scanBarcodeInput.value = ""
|
} else {
|
scanBarcodeInput.value += e.key
|
}
|
}
|
const getDetail = (barcode)=>{
|
if(barcode.indexOf(",")==-1){
|
proxy.$modal.msgWarning("请扫描正确的条形码")
|
return
|
}
|
let barcodeList = barcode.split(",")
|
let barcodeId = barcodeList[0]
|
let detailApi = getStockInventoryById
|
|
if(!detailApi){
|
proxy.$modal.msgWarning("请扫描正确的条形码")
|
return
|
}
|
detailApi({id:barcodeId}).then((resp) => {
|
if(!resp.data){
|
proxy.$modal.msgError("商品不存在")
|
return
|
}
|
if(resp.code!=200){
|
proxy.$modal.msgError(res.msg)
|
return
|
}
|
barcodeDetail.value = resp.data
|
barcodeDia.value = true
|
}).catch(() => {
|
proxy.$modal.msgError("查看详情失败")
|
})
|
|
}
|
|
|
const closeBarcodeDia = () => {
|
barcodeDia.value = false
|
}
|
|
onMounted(async() => {
|
// 添加扫码枪监听事件
|
document.removeEventListener('keypress',scanBarcode)
|
document.addEventListener('keypress', scanBarcode)
|
await productCategorySelectList()
|
await getList()
|
})
|
</script>
|
|
<style scoped lang="scss">
|
:deep(.row-low-stock td) {
|
background-color: #fde2e2;
|
color: #c45656;
|
}
|
|
:deep(.row-low-stock:hover > td) {
|
background-color: #fcd4d4;
|
}
|
|
.barcode-item{
|
display: flex;
|
justify-content: space-between;
|
padding: 5px 0;
|
|
}
|
|
.barcode-img{
|
width: 80px;
|
height: 80px;
|
border-radius: 5px;
|
background-color: #F5F5F5;
|
}
|
</style>
|