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')
|
return getRequest(`/goods/goods/queryExportStock`, params,'blob')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 下载商品库存导入模板
|
||||||
|
export const downloadStockTemplate = () => {
|
||||||
|
return getRequest(`/goods/goods/downloadStockTemplate`, {}, 'blob');
|
||||||
|
};
|
||||||
|
|
||||||
// 上传商品库存列表
|
// 上传商品库存列表
|
||||||
export const importStockExcel = params => {
|
export const importStockExcel = params => {
|
||||||
return postRequestWithNoForm(`/goods/goods/importStockExcel`, params );
|
return postRequestWithNoForm(`/goods/goods/importStockExcel`, params );
|
||||||
|
|||||||
@@ -62,6 +62,18 @@
|
|||||||
<el-table-column label="库存" width="100" align="right">
|
<el-table-column label="库存" width="100" align="right">
|
||||||
<template #default="{ row }">{{ row.quantity || 0 }}</template>
|
<template #default="{ row }">{{ row.quantity || 0 }}</template>
|
||||||
</el-table-column>
|
</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>
|
</el-table>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -79,30 +91,97 @@
|
|||||||
</div>
|
</div>
|
||||||
</el-card>
|
</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">
|
<div v-loading="spinShow" style="text-align: center">
|
||||||
<el-upload drag :before-upload="handleUpload" :show-file-list="false" accept=".xlsx,.xls">
|
<el-upload drag :before-upload="handleUpload" :show-file-list="false" accept=".xlsx,.xls">
|
||||||
<div style="padding: 50px 0">
|
<div style="padding: 40px 0">
|
||||||
<div style="font-size: 48px; color: #3399ff">↑</div>
|
<el-icon :size="48" style="color: #3399ff"><UploadFilled /></el-icon>
|
||||||
<h2>选择或拖拽文件上传</h2>
|
<h2 style="font-size: 16px; margin-top: 10px">选择或拖拽文件上传</h2>
|
||||||
|
<p style="font-size: 12px; color: #909399; margin-top: 8px">支持 .xlsx / .xls 格式文件</p>
|
||||||
</div>
|
</div>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
</div>
|
</div>
|
||||||
<template #footer>
|
<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>
|
</template>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { onMounted, ref } from "vue";
|
import { computed, onMounted, ref } from "vue";
|
||||||
import { useTable } from "@/utils/useTable";
|
import { useTable } from "@/utils/useTable";
|
||||||
|
import { Download, UploadFilled } from "@element-plus/icons-vue";
|
||||||
|
import { downloadBlob } from "@/utils/downloadBlob";
|
||||||
import {
|
import {
|
||||||
|
downloadStockTemplate,
|
||||||
getGoodsCategoryAll,
|
getGoodsCategoryAll,
|
||||||
getGoodsSkuListDataSeller,
|
getGoodsSkuListDataSeller,
|
||||||
importStockExcel,
|
importStockExcel,
|
||||||
queryExportStock,
|
queryExportStock,
|
||||||
|
updateGoodsSkuStocks,
|
||||||
} from "@/api/goods";
|
} from "@/api/goods";
|
||||||
import { Message } from "@/utils/message";
|
import { Message } from "@/utils/message";
|
||||||
|
|
||||||
@@ -116,6 +195,43 @@ const spinShow = ref(false);
|
|||||||
const categoryList = ref([]);
|
const categoryList = ref([]);
|
||||||
const category = ref([]);
|
const category = ref([]);
|
||||||
const file = ref(null);
|
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(
|
const { loading, data: stockAllData, total, searchForm, getDataList, handleSearch, resetSearch } = useTable(
|
||||||
getGoodsSkuListDataSeller,
|
getGoodsSkuListDataSeller,
|
||||||
@@ -193,6 +309,64 @@ function openImportStock() {
|
|||||||
importModal.value = true;
|
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) {
|
function handleUpload(f) {
|
||||||
file.value = f;
|
file.value = f;
|
||||||
upload();
|
upload();
|
||||||
@@ -250,4 +424,47 @@ onMounted(() => {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
min-width: 0;
|
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>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user