mirror of
https://gitee.com/beijing_hongye_huicheng/lilishop-ui.git
synced 2026-11-10 02:49:03 +08:00
feat(seller): 商品库存页支持下载导入模板与按增减修改库存
This commit is contained in:
@@ -222,6 +222,11 @@ export const queryExportStock = (params) => {
|
||||
return getRequest(`/goods/goods/queryExportStock`, params,'blob')
|
||||
}
|
||||
|
||||
// 下载商品库存导入模板
|
||||
export const downloadStockTemplate = () => {
|
||||
return getRequest(`/goods/goods/downloadStockTemplate`, {}, 'blob');
|
||||
};
|
||||
|
||||
// 上传商品库存列表
|
||||
export const importStockExcel = params => {
|
||||
return postRequestWithNoForm(`/goods/goods/importStockExcel`, params );
|
||||
|
||||
@@ -62,6 +62,18 @@
|
||||
<el-table-column label="库存" width="100" align="right">
|
||||
<template #default="{ row }">{{ row.quantity || 0 }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120" align="right" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-tooltip
|
||||
v-if="row.goodsType === 'E_COUPON'"
|
||||
content="电子卡券库存由卡密池自动同步,不可手动修改"
|
||||
placement="top"
|
||||
>
|
||||
<span class="disabled-link-text">修改库存</span>
|
||||
</el-tooltip>
|
||||
<a v-else class="link-text" @click="openUpdateStockModal(row)">修改库存</a>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
|
||||
@@ -79,30 +91,97 @@
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="importModal" title="导入商品信息" :close-on-click-modal="false">
|
||||
<el-dialog v-model="importModal" title="导入商品信息" :close-on-click-modal="false" width="520px">
|
||||
<div class="template-tip-bar">
|
||||
<span class="template-tip-text">请按标准模板格式整理库存数据后再上传</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
:loading="templateLoading"
|
||||
@click="handleDownloadTemplate"
|
||||
>
|
||||
<el-icon class="mr_5"><Download /></el-icon>下载导入模板
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-loading="spinShow" style="text-align: center">
|
||||
<el-upload drag :before-upload="handleUpload" :show-file-list="false" accept=".xlsx,.xls">
|
||||
<div style="padding: 50px 0">
|
||||
<div style="font-size: 48px; color: #3399ff">↑</div>
|
||||
<h2>选择或拖拽文件上传</h2>
|
||||
<div style="padding: 40px 0">
|
||||
<el-icon :size="48" style="color: #3399ff"><UploadFilled /></el-icon>
|
||||
<h2 style="font-size: 16px; margin-top: 10px">选择或拖拽文件上传</h2>
|
||||
<p style="font-size: 12px; color: #909399; margin-top: 8px">支持 .xlsx / .xls 格式文件</p>
|
||||
</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="importModal = false">确定</el-button>
|
||||
<el-button @click="importModal = false">取消</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog
|
||||
v-model="updateStockModalVisible"
|
||||
title="修改商品库存"
|
||||
width="500px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
ref="stockFormRef"
|
||||
:model="stockForm"
|
||||
:rules="stockRules"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="商品名称">
|
||||
<div class="dialog-value-text">{{ selectedSku.goodsName || "-" }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="SKU信息">
|
||||
<div class="dialog-value-text">{{ selectedSku.simpleSpecs || "-" }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="当前库存">
|
||||
<div class="dialog-value-text">{{ selectedSku.quantity || 0 }}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="修改方向" prop="type">
|
||||
<el-radio-group v-model="stockForm.type" @change="handleStockTypeChange">
|
||||
<el-radio value="ADD">增</el-radio>
|
||||
<el-radio value="SUB">减</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="变更数量" prop="quantity">
|
||||
<el-input-number
|
||||
v-model="stockForm.quantity"
|
||||
:min="1"
|
||||
:max="99999999"
|
||||
:step="1"
|
||||
:precision="0"
|
||||
controls-position="right"
|
||||
style="width: 200px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="变更后库存">
|
||||
<span :class="['preview-stock', { 'is-insufficient': previewStock < 0 }]">
|
||||
{{ previewStock }}
|
||||
</span>
|
||||
<span v-if="previewStock < 0" class="insufficient-tip">(库存不足以扣减)</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="updateStockModalVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="updateStockLoading" @click="submitUpdateStock">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import { useTable } from "@/utils/useTable";
|
||||
import { Download, UploadFilled } from "@element-plus/icons-vue";
|
||||
import { downloadBlob } from "@/utils/downloadBlob";
|
||||
import {
|
||||
downloadStockTemplate,
|
||||
getGoodsCategoryAll,
|
||||
getGoodsSkuListDataSeller,
|
||||
importStockExcel,
|
||||
queryExportStock,
|
||||
updateGoodsSkuStocks,
|
||||
} from "@/api/goods";
|
||||
import { Message } from "@/utils/message";
|
||||
|
||||
@@ -116,6 +195,43 @@ const spinShow = ref(false);
|
||||
const categoryList = ref([]);
|
||||
const category = ref([]);
|
||||
const file = ref(null);
|
||||
const templateLoading = ref(false);
|
||||
const updateStockModalVisible = ref(false);
|
||||
const updateStockLoading = ref(false);
|
||||
const selectedSku = ref({});
|
||||
const stockFormRef = ref(null);
|
||||
const stockForm = ref({
|
||||
type: "ADD",
|
||||
quantity: 1,
|
||||
});
|
||||
|
||||
const previewStock = computed(() => {
|
||||
const current = Number(selectedSku.value.quantity) || 0;
|
||||
const change = Number(stockForm.value.quantity) || 0;
|
||||
return stockForm.value.type === "ADD" ? current + change : current - change;
|
||||
});
|
||||
|
||||
const stockRules = {
|
||||
type: [{ required: true, message: "请选择库存修改方向", trigger: "change" }],
|
||||
quantity: [
|
||||
{ required: true, message: "请输入变更数量", trigger: "blur" },
|
||||
{
|
||||
validator: (_rule, value, callback) => {
|
||||
const num = Number(value);
|
||||
if (!Number.isInteger(num) || num <= 0) {
|
||||
callback(new Error("变更数量必须为正整数"));
|
||||
return;
|
||||
}
|
||||
if (stockForm.value.type === "SUB" && num > (Number(selectedSku.value.quantity) || 0)) {
|
||||
callback(new Error("扣减数量不能大于当前库存"));
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
},
|
||||
trigger: ["blur", "change"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const { loading, data: stockAllData, total, searchForm, getDataList, handleSearch, resetSearch } = useTable(
|
||||
getGoodsSkuListDataSeller,
|
||||
@@ -193,6 +309,64 @@ function openImportStock() {
|
||||
importModal.value = true;
|
||||
}
|
||||
|
||||
/** 打开单条商品库存增减弹窗 */
|
||||
function openUpdateStockModal(row) {
|
||||
selectedSku.value = { ...row };
|
||||
stockForm.value = {
|
||||
type: "ADD",
|
||||
quantity: 1,
|
||||
};
|
||||
updateStockModalVisible.value = true;
|
||||
stockFormRef.value?.clearValidate();
|
||||
}
|
||||
|
||||
/** 切换库存修改方向时重新校验数量 */
|
||||
function handleStockTypeChange() {
|
||||
stockFormRef.value?.validateField("quantity", () => {});
|
||||
}
|
||||
|
||||
/** 提交商品库存增减修改 */
|
||||
function submitUpdateStock() {
|
||||
stockFormRef.value?.validate((valid) => {
|
||||
if (!valid) return;
|
||||
updateStockLoading.value = true;
|
||||
updateGoodsSkuStocks([
|
||||
{
|
||||
goodsId: selectedSku.value.goodsId,
|
||||
skuId: selectedSku.value.id,
|
||||
type: stockForm.value.type,
|
||||
quantity: Number(stockForm.value.quantity),
|
||||
},
|
||||
])
|
||||
.then((res) => {
|
||||
if (res.success) {
|
||||
Message.success("修改库存成功");
|
||||
updateStockModalVisible.value = false;
|
||||
getDataList();
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
updateStockLoading.value = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/** 下载商品库存导入空白模板 */
|
||||
function handleDownloadTemplate() {
|
||||
templateLoading.value = true;
|
||||
downloadStockTemplate()
|
||||
.then((res) => {
|
||||
downloadBlob(res, "商品库存导入模板.xlsx");
|
||||
Message.success("模板下载成功");
|
||||
})
|
||||
.catch(() => {
|
||||
Message.error("模板下载失败");
|
||||
})
|
||||
.finally(() => {
|
||||
templateLoading.value = false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleUpload(f) {
|
||||
file.value = f;
|
||||
upload();
|
||||
@@ -250,4 +424,47 @@ onMounted(() => {
|
||||
white-space: nowrap;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.template-tip-bar {
|
||||
margin-bottom: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.template-tip-text {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.mr_5 {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.disabled-link-text {
|
||||
color: #c0c4cc;
|
||||
cursor: not-allowed;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.dialog-value-text {
|
||||
color: #303133;
|
||||
line-height: 22px;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.preview-stock {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
|
||||
&.is-insufficient {
|
||||
color: #f56c6c;
|
||||
}
|
||||
}
|
||||
|
||||
.insufficient-tip {
|
||||
color: #f56c6c;
|
||||
font-size: 12px;
|
||||
margin-left: 4px;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user