feat: 创建新商户

This commit is contained in:
lzhizhao 2025-09-11 10:48:05 +08:00
parent 83078e70c7
commit 5abb93d202
2 changed files with 251 additions and 6 deletions

View File

@ -15,3 +15,20 @@ export function setMerchantPermissions(data) {
data data
}) })
} }
export function shopBaseConfig(query, data) {
return request({
url: '/shop/base_config',
method: 'put',
params: query,
data
})
}
export function listPlatformCategory(params) {
return request({
url: '/category/list',
method: 'get',
params
})
}

View File

@ -33,6 +33,106 @@
<el-form-item label="密码" prop="password"> <el-form-item label="密码" prop="password">
<el-input v-model="form.password" type="password" placeholder="请输入密码"></el-input> <el-input v-model="form.password" type="password" placeholder="请输入密码"></el-input>
</el-form-item> </el-form-item>
<h3>店铺基础信息</h3>
<el-form-item label="摊位名称" prop="name">
<el-input v-model="form.name" placeholder="请输入摊位名称"></el-input>
</el-form-item>
<el-form-item label="主营类目" prop="categoryId">
<el-select v-model="form.categoryId" placeholder="请选择主营类目" style="width: 100%">
<el-option
v-for="item in categoryList"
:key="item.id"
:label="item.name"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
<el-form-item label="摊位背景图" prop="background">
<el-upload
class="avatar-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="handleBackgroundSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="form.background" :src="form.background" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="摊位联系电话" prop="contactPhone">
<el-input v-model="form.contactPhone" placeholder="请输入联系电话/手机号"></el-input>
</el-form-item>
<el-form-item label="摊位照片" prop="fileList">
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
list-type="picture-card"
:file-list="form.fileList"
:on-success="handleFileListSuccess"
:before-upload="beforeAvatarUpload"
:on-remove="handleRemove"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="摊位位置" prop="address">
<el-input v-model="form.address" placeholder="请输入摊位位置"></el-input>
</el-form-item>
<el-form-item label="摊位详细地址" prop="detailAddress">
<el-input v-model="form.detailAddress" placeholder="请输入摊位详细地址"></el-input>
</el-form-item>
<el-form-item label="许可证" prop="permits">
<el-upload
:action="uploadUrl"
:headers="uploadHeaders"
list-type="picture-card"
:file-list="form.permits"
:on-success="handlePermitsSuccess"
:before-upload="beforeAvatarUpload"
:on-remove="handlePermitsRemove"
>
<i class="el-icon-plus"></i>
</el-upload>
</el-form-item>
<el-form-item label="是否自动营业" prop="isAutoBusiness">
<el-switch v-model="form.isAutoBusiness"></el-switch>
</el-form-item>
<el-form-item v-if="form.isAutoBusiness" label="营业时间">
<el-time-picker
is-range
v-model="businessHours"
range-separator="至"
start-placeholder="开始时间"
end-placeholder="结束时间"
placeholder="选择时间范围">
</el-time-picker>
</el-form-item>
<el-form-item label="顾客自提点位置" prop="pickName">
<el-input v-model="form.pickName" placeholder="请输入顾客自提点位置"></el-input>
</el-form-item>
<el-form-item label="自提点详细地址" prop="pickAddress">
<el-input v-model="form.pickAddress" placeholder="请输入自提点详细地址"></el-input>
</el-form-item>
<el-form-item label="自提点位置示意图" prop="pickImg">
<el-upload
class="avatar-uploader"
:action="uploadUrl"
:headers="uploadHeaders"
:show-file-list="false"
:on-success="handlePickImgSuccess"
:before-upload="beforeAvatarUpload"
>
<img v-if="form.pickImg" :src="form.pickImg" class="avatar">
<i v-else class="el-icon-plus avatar-uploader-icon"></i>
</el-upload>
</el-form-item>
<el-form-item label="承诺送达自提点时间" prop="promisePickDeliveryTime">
<el-input v-model.number="form.promisePickDeliveryTime" placeholder="请输入小时数">
<template slot="append">小时以内</template>
</el-input>
</el-form-item>
</template> </template>
<template v-if="form.operationType === 'update'"> <template v-if="form.operationType === 'update'">
@ -96,9 +196,11 @@ import {
checkUsername, checkUsername,
resetPassword resetPassword
} from '@/api/modules/subaccount' // Assuming some APIs can be reused } from '@/api/modules/subaccount' // Assuming some APIs can be reused
import { inviteJoin, setMerchantPermissions } from '@/api/modules/merchant' // New merchant API import { inviteJoin, setMerchantPermissions, shopBaseConfig, listPlatformCategory } from '@/api/modules/merchant' // New merchant API
import { mer_admin } from '@/api/modules/mer_admin'
import routerConfig from '@/router/full-routers' import routerConfig from '@/router/full-routers'
import { mapState } from 'vuex' import { mapState } from 'vuex'
import cookie from 'js-cookie'
// Function to filter the full menu tree based on available permissions // Function to filter the full menu tree based on available permissions
const filterTreeByPerms = (tree, availablePerms) => { const filterTreeByPerms = (tree, availablePerms) => {
@ -160,8 +262,30 @@ export default {
permissionCodes: [], permissionCodes: [],
accountId: null, accountId: null,
enable: true, enable: true,
selectedMarketId: '' selectedMarketId: '',
name: '',
categoryId: '',
background: '',
isAutoBusiness: true,
businessDays: '1111111',
startBusinessTime: 0,
endBusinessTime: 1440,
contactPhone: '',
fileList: [],
address: '',
permits: [],
lng: '',
lat: '',
detailAddress: '',
pickName: '',
pickLng: '',
pickAddress: '',
pickLat: '',
pickImg: '',
promisePickDeliveryTime: '',
}, },
businessHours: [new Date(2016, 9, 10, 0, 0), new Date(2016, 9, 10, 23, 59)],
categoryList: [],
merchantAccountList: [], // To be populated with merchant accounts merchantAccountList: [], // To be populated with merchant accounts
permissionList: [], permissionList: [],
defaultProps: { defaultProps: {
@ -178,6 +302,34 @@ export default {
password: [ password: [
{ required: true, message: "请输入密码", trigger: "blur" }, { required: true, message: "请输入密码", trigger: "blur" },
], ],
name: [
{ required: true, message: "请输入摊位名称", trigger: "blur" },
],
categoryId: [
{ required: true, message: "请选择主营类目", trigger: "change" },
],
background: [
{ required: true, message: "请上传摊位背景图", trigger: "change" },
],
fileList: [
{ required: true, message: "请上传摊位照片", trigger: "change" },
],
address: [
{ required: true, message: "请输入摊位位置", trigger: "blur" },
],
detailAddress: [
{ required: true, message: "请输入摊位详细地址", trigger: "blur" },
],
pickName: [
{ required: true, message: "请输入顾客自提点位置", trigger: "blur" },
],
pickAddress: [
{ required: true, message: "请输入自提点详细地址", trigger: "blur" },
],
promisePickDeliveryTime: [
{ required: true, message: "请输入承诺送达自提点时间", trigger: "blur" },
{ type: 'number', message: '必须为数字值'}
],
permissionCodes: [ permissionCodes: [
{ required: true, message: "请选择菜单权限", trigger: "change", type: 'array' }, { required: true, message: "请选择菜单权限", trigger: "change", type: 'array' },
], ],
@ -195,7 +347,15 @@ export default {
"marketId", "marketId",
"shopId", "shopId",
"markets" "markets"
]) ]),
uploadUrl() {
return mer_admin.uploadFile();
},
uploadHeaders() {
return {
token: cookie.get('token')
};
}
}, },
watch: { watch: {
'form.operationType'(newType) { 'form.operationType'(newType) {
@ -219,8 +379,14 @@ export default {
}, },
created() { created() {
this.getPermissions(); this.getPermissions();
this.getPlatformCategory();
}, },
methods: { methods: {
getPlatformCategory() {
listPlatformCategory({ type: this.isCloudShop ? 1 : 0 }).then(res => {
this.categoryList = res.data.data;
});
},
getPermissions() { getPermissions() {
getAvailablePermissions().then((res) => { getAvailablePermissions().then((res) => {
const availablePerms = (res && res.data && Array.isArray(res.data.data)) ? res.data.data : []; const availablePerms = (res && res.data && Array.isArray(res.data.data)) ? res.data.data : [];
@ -249,6 +415,36 @@ export default {
handleTreeCheck() { handleTreeCheck() {
this.form.permissionCodes = this.$refs.permissionTree.getCheckedKeys(true); this.form.permissionCodes = this.$refs.permissionTree.getCheckedKeys(true);
}, },
handleBackgroundSuccess(res) {
this.form.background = res.data;
},
handleFileListSuccess(res, file, fileList) {
this.form.fileList = fileList;
},
handlePermitsSuccess(res, file, fileList) {
this.form.permits = fileList;
},
handlePickImgSuccess(res) {
this.form.pickImg = res.data;
},
beforeAvatarUpload(file) {
const isJPG = file.type === 'image/jpeg';
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isJPG) {
this.$message.error('上传头像图片只能是 JPG 格式!');
}
if (!isLt2M) {
this.$message.error('上传头像图片大小不能超过 2MB!');
}
return isJPG && isLt2M;
},
handleRemove(file, fileList) {
this.form.fileList = fileList;
},
handlePermitsRemove(file, fileList) {
this.form.permits = fileList;
},
// To be implemented // To be implemented
// loadMerchantAccountList() { // loadMerchantAccountList() {
// getMerchantAccountList().then(res => { // getMerchantAccountList().then(res => {
@ -286,14 +482,24 @@ export default {
}; };
inviteJoin(createData).then((res) => { inviteJoin(createData).then((res) => {
const merchantAccountId = res.data.data.merchantId; // Assuming this is the merchant ID const merchantAccountId = res.data.data.merchantId; // Assuming this is the merchant ID
const shopId = res.data.data.shopId; // Assuming this is the shop ID
const permissionsData = { const permissionsData = {
merchantAccountId, merchantAccountId,
permissionCodes: this.form.permissionCodes, permissionCodes: this.form.permissionCodes,
marketId marketId
}; };
setMerchantPermissions(permissionsData).then(() => { setMerchantPermissions(permissionsData).then(() => {
this.$message.success('商户创建成功且权限已设置'); const shopData = {
this.$refs.ruleFormRef.resetFields(); ...this.form,
imgs: this.form.fileList.map(f => f.url).join(','),
permits: this.form.permits.map(f => f.url).join(','),
startBusinessTime: this.businessHours[0].getHours() * 60 + this.businessHours[0].getMinutes(),
endBusinessTime: this.businessHours[1].getHours() * 60 + this.businessHours[1].getMinutes(),
};
shopBaseConfig({ shopId }, shopData).then(() => {
this.$message.success('商户创建成功,权限及店铺信息已设置');
this.$refs.ruleFormRef.resetFields();
});
}); });
}); });
} else if (this.form.operationType === 'update') { } else if (this.form.operationType === 'update') {
@ -323,4 +529,26 @@ export default {
.app-container { .app-container {
padding: 20px; padding: 20px;
} }
</style> .avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409EFF;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}