feat: 发布商品增加发布摊位步骤

This commit is contained in:
lzhizhao 2025-10-29 18:06:58 +08:00
parent 553bd5cd89
commit 446dcf9579
2 changed files with 364 additions and 60 deletions

View File

@ -1,9 +1,10 @@
const state = {
isMerchant: false, //是否是经营者还是摊铺 不是就是为false
marketList: [], //经营的市场
storeList: [], //经营的店铺
storeList: [], //经营的店铺 旧字段兼容
shopList: [], //经营的店铺
marketId: "",
shopId: "",
shopId: ""
};
// actions
@ -16,15 +17,16 @@ const mutations = {
state.isMerchant = data.isMerchant;
state.marketList = data.marketList;
state.storeList = data.storeList;
state.storeList = data.shopList;
state.shopList = data.shopList;
state.marketId = data.marketId;
state.shopId = data.shopId;
},
}
};
export default {
namespaced: true,
state,
mutations,
actions,
actions
};

View File

@ -8,8 +8,63 @@
@close="handleClose"
class="obj-modal"
>
<el-form ref="dataForm" :model="modalData" :rules="rules" label-width="120px">
<el-form
ref="dataForm"
:model="modalData"
:rules="rules"
label-width="120px"
>
<el-tabs :before-leave="beforeTabLeave" v-model="currentPanel">
<el-tab-pane label="发布摊位" name="发布摊位">
<el-form-item label="选择摊位" prop="shopId" required>
<!-- 如果是商户角色使用本地摊位列表 -->
<el-select
v-if="userRole === 'ROLE_MERCHANT'"
v-model="modalData.shopId"
placeholder="请选择需要发布的摊位"
style="width: 50%;"
clearable
>
<el-option
v-for="item in shopList"
:key="item.shopId"
:label="item.shopName"
:value="item.shopId"
></el-option>
</el-select>
<!-- 如果是经营者角色使用远程搜索 -->
<el-select
v-else
v-model="modalData.shopId"
placeholder="请输入摊位名称搜索"
style="width: 50%;"
filterable
remote
:remote-method="remoteSearchShop"
:loading="shopSearchLoading"
clearable
>
<el-option
v-for="item in remoteShopList"
:key="item.shopId"
:label="item.shopName"
:value="item.shopId"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="摊位说明">
<div class="shop-tips">
<i class="el-icon-info"></i>
<div class="tips-content">
<div> 请先选择要发布商品的摊位</div>
<div> 商品发布后将在所选摊位中展示</div>
<div> 同一商品可以发布到多个摊位</div>
</div>
</div>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="基础信息" name="基础信息">
<el-form-item label="商品图片" prop="productPhotoList">
<el-upload
@ -22,11 +77,17 @@
list-type="picture-card"
>
<i class="el-icon-plus"></i>
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件且不超过500kb</div>
<div slot="tip" class="el-upload__tip">
只能上传jpg/png文件且不超过500kb
</div>
</el-upload>
</el-form-item>
<el-form-item label="商品类目" prop="productCategoryId">
<el-select style="width: 50%;" placeholder="请选择商品类目" v-model="modalData.productCategoryId">
<el-select
style="width: 50%;"
placeholder="请选择商品类目"
v-model="modalData.productCategoryId"
>
<el-option
v-for="item in getProductCategory"
:key="item.id"
@ -36,7 +97,11 @@
</el-select>
</el-form-item>
<el-form-item label="商品名称" prop="name">
<el-input v-model="modalData.name" placeholder="请输入商品名称" maxlength="30"></el-input>
<el-input
v-model="modalData.name"
placeholder="请输入商品名称"
maxlength="30"
></el-input>
</el-form-item>
<el-form-item label="商品描述" prop="description">
<el-input
@ -49,7 +114,11 @@
</el-tab-pane>
<el-tab-pane label="销售信息" name="销售信息">
<el-form-item label="销售单位" prop="productUnit">
<el-select style="width: 50%;" v-model="modalData.productUnit" placeholder="请选择销售单位">
<el-select
style="width: 50%;"
v-model="modalData.productUnit"
placeholder="请选择销售单位"
>
<el-option
v-for="item in getSaleUnit"
:key="item.name"
@ -63,7 +132,11 @@
<el-form-item label="成本" prop="costPrice">
<el-input
:readonly="modalData.specType !== 0"
:placeholder="modalData.specType !== 0 ? '多规格商品在规格中设置' : '请输入价格'"
:placeholder="
modalData.specType !== 0
? '多规格商品在规格中设置'
: '请输入价格'
"
v-model="modalData.costPrice"
>
<template slot="append">单位</template>
@ -74,7 +147,11 @@
<el-form-item label="市场价" prop="marketPrice">
<el-input
:readonly="modalData.specType !== 0"
:placeholder="modalData.specType !== 0 ? '多规格商品在规格中设置' : '请输入市场价'"
:placeholder="
modalData.specType !== 0
? '多规格商品在规格中设置'
: '请输入市场价'
"
v-model="modalData.marketPrice"
>
<template slot="append">单位</template>
@ -87,7 +164,11 @@
<el-form-item label="库存" prop="stockNum">
<el-input
:readonly="modalData.specType !== 0"
:placeholder="modalData.specType !== 0 ? '多规格商品在规格中设置' : '请输入库存'"
:placeholder="
modalData.specType !== 0
? '多规格商品在规格中设置'
: '请输入库存'
"
v-model="modalData.stockNum"
></el-input>
</el-form-item>
@ -96,7 +177,11 @@
<el-form-item label="重量" prop="weight">
<el-input
:readonly="modalData.specType !== 0"
:placeholder="modalData.specType !== 0 ? '多规格商品在规格中设置' : '请输入重量'"
:placeholder="
modalData.specType !== 0
? '多规格商品在规格中设置'
: '请输入重量'
"
v-model="modalData.weight"
>
<template slot="append">KG</template>
@ -109,7 +194,11 @@
<el-form-item label="体积" prop="volume">
<el-input
:readonly="modalData.specType !== 0"
:placeholder="modalData.specType !== 0 ? '多规格商品在规格中设置' : '请输入体积'"
:placeholder="
modalData.specType !== 0
? '多规格商品在规格中设置'
: '请输入体积'
"
v-model="modalData.volume"
>
<template slot="append">立方米</template>
@ -120,20 +209,46 @@
<el-form-item label="规格" prop="productSpecificationList">
<el-button @click="addSpecs">修改规格配置</el-button>
<el-table
v-if="modalData.specType === 1 && modalData.productSpecificationList.length > 0"
v-if="
modalData.specType === 1 &&
modalData.productSpecificationList.length > 0
"
:data="modalData.productSpecificationList"
border
style="width: 100%; margin-top: 10px;"
>
<el-table-column prop="attributeValue" label="属性" align="center"></el-table-column>
<el-table-column prop="costPrice" label="成本(元)" align="center"></el-table-column>
<el-table-column prop="marketPrice" label="市场价(元)" align="center"></el-table-column>
<el-table-column prop="stockNum" label="库存" align="center"></el-table-column>
<el-table-column prop="weight" label="重量(kg)" align="center"></el-table-column>
<el-table-column prop="volume" label="体积(m³)" align="center"></el-table-column>
<el-table-column
prop="attributeValue"
label="属性"
align="center"
></el-table-column>
<el-table-column
prop="costPrice"
label="成本(元)"
align="center"
></el-table-column>
<el-table-column
prop="marketPrice"
label="市场价(元)"
align="center"
></el-table-column>
<el-table-column
prop="stockNum"
label="库存"
align="center"
></el-table-column>
<el-table-column
prop="weight"
label="重量(kg)"
align="center"
></el-table-column>
<el-table-column
prop="volume"
label="体积(m³)"
align="center"
></el-table-column>
</el-table>
</el-form-item>
</el-tab-pane>
<el-tab-pane label="其他信息" name="其他信息">
<el-form-item label="产地" prop="productPlace">
@ -147,7 +262,11 @@
></el-cascader>
</el-form-item>
<el-form-item label="保质期" prop="shelfLife">
<el-input style="width:50%;" placeholder="请填写保质期" v-model="modalData.shelfLife">
<el-input
style="width:50%;"
placeholder="请填写保质期"
v-model="modalData.shelfLife"
>
<div slot="suffix"></div>
</el-input>
</el-form-item>
@ -162,7 +281,9 @@
multiple
>
<i class="el-icon-plus"></i>
<div class="el-upload__tip" slot="tip">只能上传jpg/png文件且不超过500kb</div>
<div class="el-upload__tip" slot="tip">
只能上传jpg/png文件且不超过500kb
</div>
</el-upload>
</el-form-item>
<el-form-item label="商品视频" prop="productVideo">
@ -177,19 +298,31 @@
:headers="{ token: 'Bearer ' + $cookie.get('token') }"
>
<el-button size="small" type="primary">点击上传</el-button>
<div class="el-upload__tip" slot="tip">支持mp4avimov等视频格式建议不超过50MB</div>
<div class="el-upload__tip" slot="tip">
支持mp4avimov等视频格式建议不超过50MB
</div>
</el-upload>
</el-form-item>
</el-tab-pane>
</el-tabs>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="handleNext" type="primary">{{ currentPanel === '其他信息' ? '保存并上架' : '下一步' }}</el-button>
<el-button @click="handleSaveToWarehouse" type="info" :disabled="currentPanel !== '其他信息'">保存并放入仓库</el-button>
<el-button @click="handleNext" type="primary">{{
currentPanel === "其他信息" ? "保存并上架" : "下一步"
}}</el-button>
<el-button
@click="handleSaveToWarehouse"
type="info"
:disabled="currentPanel !== '其他信息'"
>保存并放入仓库</el-button
>
</span>
</el-dialog>
<!-- 添加规格 -->
<addSpecifications @getSpecs="getSpecs" ref="addSpecifications"></addSpecifications>
<addSpecifications
@getSpecs="getSpecs"
ref="addSpecifications"
></addSpecifications>
</div>
</template>
<script>
@ -199,7 +332,9 @@ export default {
components: { addSpecifications },
data() {
return {
currentPanel: "基础信息",
currentPanel: "发布摊位",
remoteShopList: [], //
shopSearchLoading: false, //
modalData: {
merchantId: null,
shopId: null,
@ -316,19 +451,49 @@ export default {
};
},
computed: {
// sessionStorage
userRole() {
const userInfo = JSON.parse(sessionStorage.getItem("userInfo") || "{}");
return userInfo.role || "";
},
shopList() {
return this.$store.state.userData.shopList || [];
},
marketId() {
return this.$store.state.userData.marketId;
},
rules() {
const isSingleSpec = this.modalData.specType === 0;
return {
productPhotoList: [{ required: true, message: "请上传商品图片", trigger: "blur" }],
productCategoryId: [{ required: true, message: "请选择商品类目", trigger: "change" }],
shopId: [{ required: true, message: "请选择摊位", trigger: "change" }],
productPhotoList: [
{ required: true, message: "请上传商品图片", trigger: "blur" }
],
productCategoryId: [
{ required: true, message: "请选择商品类目", trigger: "change" }
],
name: [{ required: true, message: "请输入商品名称", trigger: "blur" }],
description: [{ required: true, message: "请填写商品描述", trigger: "blur" }],
productUnit: [{ required: true, message: "请选择销售单位", trigger: "change" }],
costPrice: [{ required: isSingleSpec, message: "请输入成本价格", trigger: "blur" }],
marketPrice: [{ required: isSingleSpec, message: "请输入市场价", trigger: "blur" }],
stockNum: [{ required: isSingleSpec, message: "请输入库存", trigger: "blur" }],
weight: [{ required: isSingleSpec, message: "请输入重量", trigger: "blur" }],
volume: [{ required: isSingleSpec, message: "请输入体积", trigger: "blur" }]
description: [
{ required: true, message: "请填写商品描述", trigger: "blur" }
],
productUnit: [
{ required: true, message: "请选择销售单位", trigger: "change" }
],
costPrice: [
{ required: isSingleSpec, message: "请输入成本价格", trigger: "blur" }
],
marketPrice: [
{ required: isSingleSpec, message: "请输入市场价", trigger: "blur" }
],
stockNum: [
{ required: isSingleSpec, message: "请输入库存", trigger: "blur" }
],
weight: [
{ required: isSingleSpec, message: "请输入重量", trigger: "blur" }
],
volume: [
{ required: isSingleSpec, message: "请输入体积", trigger: "blur" }
]
};
}
},
@ -336,7 +501,7 @@ export default {
watch: {
"modalConfig.show"(newVal) {
if (!newVal) {
this.currentPanel = "基础信息";
this.currentPanel = "发布摊位";
this.fileList = [];
this.fileListOne = [];
this.fileListTwo = [];
@ -345,6 +510,36 @@ export default {
}
},
methods: {
//
async remoteSearchShop(query) {
try {
this.shopSearchLoading = true;
const params = {
pageNumber: 1,
pageSize: 20,
marketId: this.marketId
};
//
if (query) {
params.shopName = query;
}
const res = await this.$api.mer_admin.pageShop(params);
if (res.data && res.data.data) {
this.remoteShopList = res.data.data.data || [];
} else {
this.remoteShopList = [];
}
} catch (error) {
console.error("搜索摊位失败:", error);
this.remoteShopList = [];
} finally {
this.shopSearchLoading = false;
}
},
handleClose() {
this.modalConfig.show = false;
},
@ -357,9 +552,13 @@ export default {
this.updateProductPhotoList();
},
updateProductPhotoList() {
if (!this.fileList || !Array.isArray(this.fileList)) {
this.modalData.productPhotoList = [];
return;
}
this.modalData.productPhotoList = this.fileList.map((item, index) => ({
isMain: index === 0 ? 1 : 0,
url: item.response ? item.response.data : item.url,
url: item.response ? item.response.data : item.url
}));
},
handlePlaceChange(e) {
@ -367,6 +566,10 @@ export default {
this.modalData.productPlace = e.join("-");
},
handleIntroducePhotoSuccess(res, file, fileList) {
if (!fileList || !Array.isArray(fileList)) {
this.modalData.productIntroducePhoto = "";
return;
}
const urls = fileList
.map(item => (item.response ? item.response.data : item.url))
.filter(Boolean);
@ -374,6 +577,10 @@ export default {
console.log(this.modalData.productIntroducePhoto);
},
handleIntroducePhotoRemove(file, fileList) {
if (!fileList || !Array.isArray(fileList)) {
this.modalData.productIntroducePhoto = "";
return;
}
const urls = fileList
.map(item => (item.response ? item.response.data : item.url))
.filter(Boolean);
@ -388,11 +595,23 @@ export default {
},
handleNext: debounce(function() {
const validations = {
"基础信息": ["productPhotoList", "productCategoryId", "name", "description"],
"销售信息": () => {
发布摊位: ["shopId"],
基础信息: [
"productPhotoList",
"productCategoryId",
"name",
"description"
],
销售信息: () => {
const fields = ["productUnit"];
if (this.modalData.specType === 0) {
fields.push("costPrice", "marketPrice", "stockNum", "weight", "volume");
fields.push(
"costPrice",
"marketPrice",
"stockNum",
"weight",
"volume"
);
}
return fields;
}
@ -409,6 +628,15 @@ export default {
});
return;
}
if (!fieldsToValidate || !Array.isArray(fieldsToValidate)) {
console.error(
"fieldsToValidate is undefined for panel:",
this.currentPanel
);
return;
}
const validatePromises = fieldsToValidate.map(field => {
return new Promise((resolve, reject) => {
this.$refs.dataForm.validateField(field, errorMessage => {
@ -422,7 +650,9 @@ export default {
});
Promise.all(validatePromises)
.then(() => {
if (this.currentPanel === "基础信息") {
if (this.currentPanel === "发布摊位") {
this.currentPanel = "基础信息";
} else if (this.currentPanel === "基础信息") {
this.currentPanel = "销售信息";
} else if (this.currentPanel === "销售信息") {
this.currentPanel = "其他信息";
@ -481,10 +711,17 @@ export default {
return {
add: shopId => {
this.shopId = shopId;
this.getData();
// getDatatab
console.log(this.shopId);
// shopId
if (this.userRole === "ROLE_MANAGER" && !shopId) {
this.remoteSearchShop(""); //
}
this.$nextTick(() => {
this.modalConfig.title = "添加商品";
this.currentPanel = "发布摊位"; // tab
this.modalData = {
description: "",
merchantId: "",
@ -498,7 +735,7 @@ export default {
productUnit: "",
productVideo: "",
shelfLife: "",
shopId: "",
shopId: shopId || "",
singleStock: "",
specType: 0,
minCostPrice: "",
@ -508,7 +745,7 @@ export default {
this.modalData.merchantId = JSON.parse(
sessionStorage.getItem("userInfo")
).merchantId;
this.modalData.shopId = shopId;
this.modalData.shopId = shopId || "";
this.passCheck = [];
this.place = [];
});
@ -603,14 +840,24 @@ export default {
getSpecs(tableData, AttributeData) {
console.log(tableData);
// specType10
this.modalData.specType = (tableData && tableData.length > 0) || (AttributeData && AttributeData.length > 0) ? 1 : 0;
let AttributeList = AttributeData.map(item => {
this.modalData.specType =
(tableData && tableData.length > 0) ||
(AttributeData && AttributeData.length > 0)
? 1
: 0;
//
let AttributeList = [];
if (AttributeData && Array.isArray(AttributeData)) {
AttributeList = AttributeData.map(item => {
return {
attributeName: item.attributeName,
attributeValue: item.attributeValue.join(",")
};
});
this.$set(this.modalData, "productSpecificationList", tableData);
}
this.$set(this.modalData, "productSpecificationList", tableData || []);
this.$set(this.modalData, "productAttributeList", AttributeList);
},
beforeTabLeave(activeName, oldActiveName) {
@ -618,15 +865,27 @@ export default {
return true;
}
const validations = {
"基础信息": ["productPhotoList", "productCategoryId", "name", "description"],
"销售信息": () => {
发布摊位: ["shopId"],
基础信息: [
"productPhotoList",
"productCategoryId",
"name",
"description"
],
销售信息: () => {
const fields = ["productUnit"];
if (this.modalData.specType === 0) {
fields.push("costPrice", "marketPrice", "stockNum", "weight", "volume");
fields.push(
"costPrice",
"marketPrice",
"stockNum",
"weight",
"volume"
);
}
return fields;
},
"其他信息": []
其他信息: []
};
const fieldsToValidate =
typeof validations[oldActiveName] === "function"
@ -654,6 +913,17 @@ export default {
if (!this.passCheck.includes(oldActiveName)) {
this.passCheck.push(oldActiveName);
}
// """"
if (
oldActiveName === "发布摊位" &&
activeName === "基础信息" &&
this.modalData.shopId
) {
this.shopId = this.modalData.shopId;
this.getData();
}
return true;
})
.catch(() => {
@ -694,4 +964,36 @@ export default {
padding: 0 30px;
}
}
.shop-tips {
display: flex;
align-items: flex-start;
padding: 12px 16px;
background-color: #f4f4f5;
border-radius: 4px;
border: 1px solid #e9e9eb;
.el-icon-info {
font-size: 16px;
color: #909399;
margin-right: 10px;
margin-top: 2px;
flex-shrink: 0;
}
.tips-content {
flex: 1;
font-size: 14px;
color: #606266;
line-height: 1.8;
div {
margin-bottom: 4px;
&:last-child {
margin-bottom: 0;
}
}
}
}
</style>