fix: 市场基础配置模块

This commit is contained in:
lzhizhao 2025-10-29 17:13:48 +08:00
parent c800b97d90
commit ff60bd5719
1 changed files with 912 additions and 1 deletions

View File

@ -1,3 +1,914 @@
<template>
<div>市场基础配置</div>
<div class="page-container">
<div class="form-container">
<el-form
ref="formRef"
:model="formModel"
:rules="formRules"
label-width="200px"
size="small"
>
<!-- 基础配置 -->
<div class="section-header">
<el-divider content-position="left">
<span class="section-title">基础配置</span>
</el-divider>
<el-button
type="text"
size="small"
class="help-btn"
@click="openDescModal('Base')"
>
<i class="el-icon-question"></i> 字段说明
</el-button>
</div>
<el-form-item label="菜市场背景图" prop="background">
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
:file-list="formModel.backgroundList"
list-type="picture-card"
:on-success="handleBackgroundSuccess"
:on-remove="handleBackgroundRemove"
:before-upload="beforeUpload"
:limit="1"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="菜市场营业执照(顾客可见)" prop="license">
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
:file-list="formModel.licenseList"
list-type="picture-card"
:on-success="handleLicenseSuccess"
:on-remove="handleLicenseRemove"
:before-upload="beforeUpload"
:limit="1"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="菜市场实拍图(顾客可见)" prop="livePhoto">
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
:file-list="formModel.livePhotoList"
list-type="picture-card"
:on-success="handleLivePhotoSuccess"
:on-remove="handleLivePhotoRemove"
:before-upload="beforeUpload"
:limit="5"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="市场联系电话(顾客可见)" prop="contactPhone">
<el-input
v-model="formModel.contactPhone"
placeholder="请输入联系电话/手机号"
maxlength="11"
style="width: 300px"
/>
</el-form-item>
<el-form-item label="市场位置" prop="location">
<el-input
:value="locationText"
placeholder="请在地图上选择市场位置"
readonly
style="width: 300px"
@focus="handleLocationClick"
>
<el-button
slot="append"
icon="el-icon-location"
@click="handleLocationClick"
>
选择位置
</el-button>
</el-input>
</el-form-item>
<el-form-item label="市场详细地址" prop="detailAddress">
<el-input
v-model="formModel.detailAddress"
type="textarea"
:rows="2"
placeholder="请输入市场详细地址"
style="width: 400px"
/>
</el-form-item>
<el-form-item label="是否自动营业" prop="isAutoBusiness">
<el-switch
v-model="formModel.isAutoBusiness"
active-text="开启"
inactive-text="关闭"
/>
<el-button
v-if="formModel.isAutoBusiness"
type="text"
size="small"
style="margin-left: 10px"
@click="openBusinessHourDialog"
>
设置营业时间
</el-button>
</el-form-item>
<!-- 自提点配置 -->
<div class="section-header">
<el-divider content-position="left">
<span class="section-title">自提点配置</span>
</el-divider>
<el-button
type="text"
size="small"
class="help-btn"
@click="openDescModal('Pick')"
>
<i class="el-icon-question"></i> 字段说明
</el-button>
</div>
<el-form-item label="顾客自提点位置" prop="pickLocation">
<el-input
:value="pickLocationText"
placeholder="请在地图上选择自提点位置"
readonly
style="width: 300px"
@focus="handlePickLocationClick"
>
<el-button
slot="append"
icon="el-icon-location"
@click="handlePickLocationClick"
>
选择位置
</el-button>
</el-input>
</el-form-item>
<el-form-item label="自提点详细地址" prop="pickAddress">
<el-input
v-model="formModel.pickAddress"
type="textarea"
:rows="2"
placeholder="请输入自提点详细地址"
style="width: 400px"
/>
</el-form-item>
<el-form-item label="自提点位置示意图" prop="pickImg">
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
:file-list="formModel.pickImgList"
list-type="picture-card"
:on-success="handlePickImgSuccess"
:on-remove="handlePickImgRemove"
:before-upload="beforeUpload"
:limit="1"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="承诺送达自提点时间" prop="promisePickDeliveryTime">
<el-input-number
v-model="formModel.promisePickDeliveryTime"
:min="0"
:max="24"
controls-position="right"
/>
<span style="margin-left: 10px">小时以内</span>
</el-form-item>
<!-- 摊主入驻配置 -->
<div class="section-header">
<el-divider content-position="left">
<span class="section-title">摊主入驻配置</span>
</el-divider>
<el-button
type="text"
size="small"
class="help-btn"
@click="openDescModal('Join')"
>
<i class="el-icon-question"></i> 字段说明
</el-button>
</div>
<el-form-item
label="若有摊主想入驻菜市场是否允许摊主联系您"
prop="openJoinContact"
>
<el-switch
v-model="formModel.openJoinContact"
active-text="允许"
inactive-text="禁止"
/>
</el-form-item>
<el-form-item class="form-actions">
<el-button @click="handleCancel" size="medium">取消</el-button>
<el-button
type="primary"
@click="handleSubmit"
:loading="loading"
size="medium"
>
提交
</el-button>
</el-form-item>
</el-form>
</div>
<!-- 地图选择弹窗 -->
<el-dialog
title="选择位置"
:visible.sync="showMapDialog"
width="80%"
append-to-body
@close="showMapDialog = false"
>
<InputMap
v-if="showMapDialog && currentMapType === 'market'"
v-model="marketLocationValue"
@input="handleMapLocationChange"
/>
<InputMap
v-if="showMapDialog && currentMapType === 'pick'"
v-model="pickLocationValue"
@input="handleMapLocationChange"
/>
</el-dialog>
<!-- 营业时间设置弹窗 -->
<el-dialog
title="设置营业时间"
:visible.sync="businessHourVisible"
width="500px"
:close-on-click-modal="false"
>
<el-form label-width="100px" size="small">
<el-form-item label="营业日期">
<el-checkbox-group v-model="businessDaysArray">
<el-checkbox :label="1">周一</el-checkbox>
<el-checkbox :label="2">周二</el-checkbox>
<el-checkbox :label="3">周三</el-checkbox>
<el-checkbox :label="4">周四</el-checkbox>
<el-checkbox :label="5">周五</el-checkbox>
<el-checkbox :label="6">周六</el-checkbox>
<el-checkbox :label="7">周日</el-checkbox>
</el-checkbox-group>
</el-form-item>
<el-form-item label="营业时段">
<el-time-picker
v-model="startTime"
placeholder="开始时间"
format="HH:mm"
value-format="HH:mm"
style="width: 150px"
/>
<span style="margin: 0 10px"></span>
<el-time-picker
v-model="endTime"
placeholder="结束时间"
format="HH:mm"
value-format="HH:mm"
style="width: 150px"
/>
</el-form-item>
</el-form>
<div slot="footer">
<el-button @click="businessHourVisible = false">取消</el-button>
<el-button type="primary" @click="handleBusinessHourSubmit">
确定
</el-button>
</div>
</el-dialog>
<!-- 字段说明弹窗 -->
<el-dialog :title="descTitle" :visible.sync="descVisible" width="600px">
<el-descriptions :column="1" border>
<el-descriptions-item
v-for="(item, index) in descList"
:key="index"
:label="item.label"
:label-class-name="'desc-label'"
>
<div style="line-height: 1.6" v-html="item.desc"></div>
</el-descriptions-item>
</el-descriptions>
<div slot="footer">
<el-button type="primary" @click="descVisible = false">
知道了
</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import InputMap from "@/components/form/InputMap/index.vue";
import { mer_admin } from "@/api/modules/mer_admin.js";
export default {
name: "MarketConfig",
components: {
InputMap
},
data() {
return {
loading: false,
formModel: {
background: "",
backgroundList: [],
license: "",
licenseList: [],
livePhoto: "",
livePhotoList: [],
contactPhone: "",
lng: "",
lat: "",
address: "",
detailAddress: "",
isAutoBusiness: true,
businessDays: "1111111",
startBusinessTime: 0,
endBusinessTime: 1440,
pickName: "",
pickLng: "",
pickLat: "",
pickAddress: "",
pickImg: "",
pickImgList: [],
promisePickDeliveryTime: 2,
openJoinContact: false
},
formRules: {
background: [
{ required: true, message: "请上传菜市场背景图", trigger: "change" }
],
license: [
{ required: true, message: "请上传营业执照", trigger: "change" }
],
livePhoto: [
{ required: true, message: "请上传菜市场实拍图", trigger: "change" }
],
contactPhone: [
{ required: true, message: "请输入联系电话", trigger: "blur" },
{
pattern: /^1[3-9]\d{9}$/,
message: "请输入正确的手机号",
trigger: "blur"
}
],
location: [
{
required: true,
validator: this.validateLocation,
trigger: "change"
}
],
detailAddress: [
{ required: true, message: "请输入详细地址", trigger: "blur" }
],
pickLocation: [
{
required: true,
validator: this.validatePickLocation,
trigger: "change"
}
],
pickAddress: [
{ required: true, message: "请输入自提点详细地址", trigger: "blur" }
],
promisePickDeliveryTime: [
{ required: true, message: "请输入承诺送达时间", trigger: "blur" }
]
},
//
showMapDialog: false,
currentMapType: "", // market pick
marketLocationValue: [],
pickLocationValue: [],
//
businessHourVisible: false,
businessDaysArray: [1, 2, 3, 4, 5, 6, 7],
startTime: "00:00",
endTime: "23:59",
//
descVisible: false,
descTitle: "",
descList: []
};
},
computed: {
marketId() {
return this.$store.state.userData.marketId;
},
locationText() {
if (!this.formModel.lng || !this.formModel.lat) {
return "";
}
return `经度:${this.formModel.lng},纬度:${this.formModel.lat}`;
},
pickLocationText() {
if (!this.formModel.pickLng || !this.formModel.pickLat) {
return "";
}
return `经度:${this.formModel.pickLng},纬度:${this.formModel.pickLat}`;
},
uploadUrl() {
return this.$api.mer_admin.uploadFile();
},
uploadHeaders() {
return {
token: "Bearer " + this.$cookie.get("token")
};
}
},
mounted() {
this.fetchData();
},
methods: {
//
async fetchData() {
try {
this.loading = true;
const res = await mer_admin.getMarketBaseConfig({
marketId: this.marketId
});
const data = res.data?.data || res.data || {};
Object.assign(this.formModel, {
background: data.background || "",
backgroundList: data.background
? [{ url: data.background, name: data.background.split("/").pop() }]
: [],
license: data.license || "",
licenseList: data.license
? [{ url: data.license, name: data.license.split("/").pop() }]
: [],
livePhoto: data.livePhoto || "",
livePhotoList: data.livePhoto
? data.livePhoto
.split(",")
.filter(Boolean)
.map(url => ({ url, name: url.split("/").pop() }))
: [],
contactPhone: data.contactPhone || "",
lng: data.lng || "",
lat: data.lat || "",
address: data.address || "",
detailAddress: data.detailAddress || "",
isAutoBusiness: data.isAutoBusiness ?? true,
businessDays: data.businessDays || "1111111",
startBusinessTime: data.startBusinessTime || 0,
endBusinessTime: data.endBusinessTime || 1440,
pickName: data.pickName || "",
pickLng: data.pickLng || "",
pickLat: data.pickLat || "",
pickAddress: data.pickAddress || "",
pickImg: data.pickImg || "",
pickImgList: data.pickImg
? [{ url: data.pickImg, name: data.pickImg.split("/").pop() }]
: [],
promisePickDeliveryTime: data.promisePickDeliveryTime || 2,
openJoinContact: !!data.openJoinContact
});
//
this.businessDaysArray = this.businessDaysToArray(
data.businessDays || "1111111"
);
this.startTime = this.minutesToTime(data.startBusinessTime || 0);
this.endTime = this.minutesToTime(data.endBusinessTime || 1440);
//
if (data.lng && data.lat) {
this.marketLocationValue = [data.lng, data.lat, data.address || ""];
}
if (data.pickLng && data.pickLat) {
this.pickLocationValue = [
data.pickLng,
data.pickLat,
data.pickName || ""
];
}
} catch (error) {
console.error("获取数据失败:", error);
this.$message.error("获取数据失败");
} finally {
this.loading = false;
}
},
//
handleSubmit() {
this.$refs.formRef.validate(async valid => {
if (!valid) return;
try {
this.loading = true;
const params = {
...this.formModel,
livePhoto: this.formModel.livePhotoList
.map(item => item.url)
.join(",")
};
await mer_admin.updateMarketBaseConfig(
{ marketId: this.marketId },
params
);
this.$message.success("保存成功");
this.fetchData();
} catch (error) {
console.error("保存失败:", error);
this.$message.error("保存失败");
} finally {
this.loading = false;
}
});
},
//
handleCancel() {
this.$confirm("退出将不对此页面内容进行保存,是否确认退出?", "提示", {
confirmButtonText: "确定",
cancelButtonText: "取消",
type: "warning"
})
.then(() => {
this.$router.go(-1);
})
.catch(() => {});
},
//
validateLocation(rule, value, callback) {
if (!this.formModel.lng || !this.formModel.lat) {
callback(new Error("请选择市场位置"));
} else {
callback();
}
},
validatePickLocation(rule, value, callback) {
if (!this.formModel.pickLng || !this.formModel.pickLat) {
callback(new Error("请选择自提点位置"));
} else {
callback();
}
},
//
handleLocationClick() {
this.currentMapType = "market";
this.showMapDialog = true;
},
handlePickLocationClick() {
this.currentMapType = "pick";
this.showMapDialog = true;
},
handleMapLocationChange(value) {
if (value && value.length === 3) {
if (this.currentMapType === "market") {
this.formModel.lng = value[0];
this.formModel.lat = value[1];
this.formModel.address = value[2];
this.marketLocationValue = value;
} else if (this.currentMapType === "pick") {
this.formModel.pickLng = value[0];
this.formModel.pickLat = value[1];
this.formModel.pickName = value[2];
this.pickLocationValue = value;
}
}
this.showMapDialog = false;
},
//
openBusinessHourDialog() {
this.businessHourVisible = true;
},
handleBusinessHourSubmit() {
if (!this.businessDaysArray || this.businessDaysArray.length === 0) {
this.$message.warning("请选择营业日期");
return;
}
this.formModel.businessDays = this.businessDaysToString(
this.businessDaysArray
);
this.formModel.startBusinessTime = this.timeToMinutes(this.startTime);
this.formModel.endBusinessTime = this.timeToMinutes(this.endTime);
this.businessHourVisible = false;
this.$message.success("营业时间设置成功");
},
//
businessDaysToArray(businessDays) {
if (!businessDays) return [];
const days = [];
for (let i = 0; i < businessDays.length; i++) {
if (businessDays[i] === "1") {
days.push(i + 1);
}
}
return days;
},
businessDaysToString(daysArray) {
const result = ["0", "0", "0", "0", "0", "0", "0"];
daysArray.forEach(day => {
if (day >= 1 && day <= 7) {
result[day - 1] = "1";
}
});
return result.join("");
},
minutesToTime(minutes) {
if (minutes === null || minutes === undefined) return "00:00";
const hours = Math.floor(minutes / 60);
const mins = minutes % 60;
return `${String(hours).padStart(2, "0")}:${String(mins).padStart(
2,
"0"
)}`;
},
timeToMinutes(timeStr) {
if (!timeStr) return 0;
const [hours, minutes] = timeStr.split(":").map(Number);
return hours * 60 + minutes;
},
//
beforeUpload(file) {
const isJPG = file.type === "image/jpeg" || file.type === "image/png";
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error("只能上传 JPG/PNG 格式的图片!");
}
if (!isLt2M) {
this.$message.error("图片大小不能超过 2MB!");
}
return isJPG && isLt2M;
},
handleBackgroundSuccess(res, file) {
if (res.code === 200) {
this.formModel.background = res.data.url;
this.formModel.backgroundList = [
{ name: file.name, url: res.data.url }
];
} else {
this.$message.error("上传失败");
}
},
handleBackgroundRemove() {
this.formModel.background = "";
this.formModel.backgroundList = [];
},
handleLicenseSuccess(res, file) {
if (res.code === 200) {
this.formModel.license = res.data.url;
this.formModel.licenseList = [{ name: file.name, url: res.data.url }];
} else {
this.$message.error("上传失败");
}
},
handleLicenseRemove() {
this.formModel.license = "";
this.formModel.licenseList = [];
},
handleLivePhotoSuccess(res, file, fileList) {
if (res.code === 200) {
this.formModel.livePhotoList.push({
name: file.name,
url: res.data.url
});
} else {
this.$message.error("上传失败");
}
},
handleLivePhotoRemove(file, fileList) {
this.formModel.livePhotoList = fileList;
},
handlePickImgSuccess(res, file) {
if (res.code === 200) {
this.formModel.pickImg = res.data.url;
this.formModel.pickImgList = [{ name: file.name, url: res.data.url }];
} else {
this.$message.error("上传失败");
}
},
handlePickImgRemove() {
this.formModel.pickImg = "";
this.formModel.pickImgList = [];
},
//
openDescModal(type) {
const descEnum = {
Base: {
title: "市场基础管理-基础配置-字段说明",
descList: [
{ label: "菜市场背景图", desc: "顾客看到的菜市场背景图。" },
{
label: "是否自动营业",
desc:
'营业时间内,菜市场状态为"营业中",顾客可下单;营业时间外,菜市场状态为"休息中",顾客可查看、加购商品,但无法下单。自动营业开启状态下,系统将根据配置的营业时间自动切换菜市场状态;自动营业关闭状态下,营业和休息都需您手动切换设置。'
},
{
label: "菜市场联系电话(顾客可见)",
desc: "顾客查看菜市场详情时,可以查看该电话号码并拨打该电话号码。"
}
]
},
Pick: {
title: "市场基础管理-自提点配置-字段说明",
descList: [
{
label: "顾客自提点位置",
desc:
"顾客通过【自提点自提】方式下单时看到的自提点位置,尽量以通俗易懂的语言进行描述。"
},
{
label: "自提点详细地址",
desc: "若自提点位置不好找,可以通过填写详细地址。"
},
{
label: "自提点位置示意图",
desc:
"若自提点位置不好找,可以通过照片帮助顾客快速找到自提点位置。"
},
{
label: "承诺送达自提点时间",
desc:
"顾客通过【自提点自提】方式下单后,承诺在此配置时间内送达自提点,请根据实际情况配置。"
}
]
},
Join: {
title: "摊主入驻配置-字段说明",
descList: [
{
label: "若有摊主想入驻菜市场是否允许摊主联系您",
desc:
"在【允许】状态下菜市场摊主可以在APP上查看并联系您设置的电话号码向您了解、协商入驻相关事项。"
}
]
}
};
const config = descEnum[type];
this.descTitle = config.title;
this.descList = config.descList;
this.descVisible = true;
}
}
};
</script>
<style lang="scss" scoped>
.page-container {
padding: 0;
background-color: #f5f7fa;
min-height: 100vh;
}
.form-container {
background-color: #fff;
padding: 30px 40px;
margin: 0;
}
.section-header {
position: relative;
margin-top: 30px;
margin-bottom: 20px;
&:first-child {
margin-top: 0;
}
.el-divider {
margin: 0;
}
.section-title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
.help-btn {
position: absolute;
right: 0;
top: 50%;
transform: translateY(-50%);
padding: 0;
color: #909399;
&:hover {
color: #409eff;
}
i {
margin-right: 4px;
}
}
}
::v-deep .el-form-item {
margin-bottom: 24px;
.el-form-item__label {
font-weight: 500;
color: #606266;
}
.el-form-item__content {
line-height: 32px;
}
}
::v-deep .el-upload--picture-card {
width: 120px;
height: 120px;
line-height: 120px;
}
::v-deep .el-upload-list--picture-card .el-upload-list__item {
width: 120px;
height: 120px;
}
.form-actions {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
::v-deep .el-form-item__content {
text-align: center;
}
.el-button {
min-width: 100px;
margin: 0 10px;
}
}
.desc-label {
width: 180px;
font-weight: 500;
}
::v-deep .el-descriptions-item__label {
font-weight: 500;
background-color: #fafafa;
}
::v-deep .el-switch {
.el-switch__label {
color: #606266;
font-weight: normal;
&.is-active {
color: #409eff;
}
}
}
::v-deep .el-input-number {
width: 150px;
}
</style>