2025-09-10 17:55:35 +00:00
< template >
2025-09-11 02:36:47 +00:00
< div class = "app-container" >
< el -form
ref = "ruleFormRef"
: model = "form"
label - position = "left"
label - width = "auto"
: rules = "rules"
style = "max-width: 600px"
>
< el -form -item v-if ="!marketId && markets.length > 0" label="选择市场" prop="selectedMarketId" >
< el -select v-model ="form.selectedMarketId" placeholder="请选择市场" style="width: 100%" >
< el -option
v - for = "market in markets"
: key = "market.marketId"
: label = "market.marketName"
: value = "market.marketId"
> < / e l - o p t i o n >
< / e l - s e l e c t >
< / e l - f o r m - i t e m >
< el -form -item label = "操作类型" prop = "operationType" >
< el -select v-model ="form.operationType" placeholder="请选择操作类型" style="width: 100%" >
< el -option label = "创建新商户" value = "create" > < / e l - o p t i o n >
< el -option label = "维护商户信息" value = "update" > < / e l - o p t i o n >
< / e l - s e l e c t >
< / e l - f o r m - i t e m >
< template v-if ="form.operationType === 'create'" >
< el -form -item label = "商户账号" prop = "mobile" >
< el -input v-model ="form.mobile" placeholder="请输入商户账号" > < / el -input >
< / e l - f o r m - i t e m >
< el -form -item label = "密码" prop = "password" >
< el -input v-model ="form.password" type="password" placeholder="请输入密码" > < / el -input >
< / e l - f o r m - i t e m >
2025-09-11 02:48:05 +00:00
< h3 > 店铺基础信息 < / h3 >
< el -form -item label = "摊位名称" prop = "name" >
< el -input v-model ="form.name" placeholder="请输入摊位名称" > < / el -input >
< / e l - f o r m - i t e m >
< 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"
> < / e l - o p t i o n >
< / e l - s e l e c t >
< / e l - f o r m - i t e m >
< 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 >
< / e l - u p l o a d >
< / e l - f o r m - i t e m >
< el -form -item label = "摊位联系电话" prop = "contactPhone" >
< el -input v-model ="form.contactPhone" placeholder="请输入联系电话/手机号" > < / el -input >
< / e l - f o r m - i t e m >
< 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 >
< / e l - u p l o a d >
< / e l - f o r m - i t e m >
< el -form -item label = "摊位位置" prop = "address" >
< el -input v-model ="form.address" placeholder="请输入摊位位置" > < / el -input >
< / e l - f o r m - i t e m >
< el -form -item label = "摊位详细地址" prop = "detailAddress" >
< el -input v-model ="form.detailAddress" placeholder="请输入摊位详细地址" > < / el -input >
< / e l - f o r m - i t e m >
< 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 >
< / e l - u p l o a d >
< / e l - f o r m - i t e m >
< el -form -item label = "是否自动营业" prop = "isAutoBusiness" >
< el -switch v-model ="form.isAutoBusiness" > < / el -switch >
< / e l - f o r m - i t e m >
< el -form -item v-if ="form.isAutoBusiness" label="营业时间" >
< el -time -picker
is - range
v - model = "businessHours"
range - separator = "至"
start - placeholder = "开始时间"
end - placeholder = "结束时间"
placeholder = "选择时间范围" >
< / e l - t i m e - p i c k e r >
< / e l - f o r m - i t e m >
< el -form -item label = "顾客自提点位置" prop = "pickName" >
< el -input v-model ="form.pickName" placeholder="请输入顾客自提点位置" > < / el -input >
< / e l - f o r m - i t e m >
< el -form -item label = "自提点详细地址" prop = "pickAddress" >
< el -input v-model ="form.pickAddress" placeholder="请输入自提点详细地址" > < / el -input >
< / e l - f o r m - i t e m >
< 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 >
< / e l - u p l o a d >
< / e l - f o r m - i t e m >
< el -form -item label = "承诺送达自提点时间" prop = "promisePickDeliveryTime" >
< el -input v -model .number = " form.promisePickDeliveryTime " placeholder = "请输入小时数" >
< template slot = "append" > 小时以内 < / template >
< / e l - i n p u t >
< / e l - f o r m - i t e m >
2025-09-11 02:36:47 +00:00
< / template >
< template v-if ="form.operationType === 'update'" >
2025-09-11 06:26:11 +00:00
< el -form -item label = "选择商户账号" prop = "merchantId" >
< el -select v-model ="form.merchantId" placeholder="请选择要更新的商户账号" @change="handleAccountChange" style="width: 100%" >
2025-09-11 02:36:47 +00:00
< el -option
v - for = "item in merchantAccountList"
2025-09-11 06:26:11 +00:00
: key = "item.merchantId"
2025-09-11 02:36:47 +00:00
: label = "item.name"
2025-09-11 06:26:11 +00:00
: value = "item.merchantId"
2025-09-11 02:36:47 +00:00
> < / e l - o p t i o n >
< / e l - s e l e c t >
< / e l - f o r m - i t e m >
< el -form -item >
< el -button type = "warning" @click ="openResetPasswordDialog" > 重置密码 < / el -button >
< / e l - f o r m - i t e m >
< el -form -item label = "状态" >
< el -switch v-model ="form.enable" active-text="启用" inactive-text="禁用" > < / el -switch >
< / e l - f o r m - i t e m >
< / template >
< el -form -item label = "权限分配" prop = "permissionCodes" >
< el -tree
ref = "permissionTree"
: data = "permissionList"
show - checkbox
node - key = "id"
: props = "defaultProps"
@ check = "handleTreeCheck"
>
< / e l - t r e e >
< / e l - f o r m - i t e m >
< el -form -item >
< el -button type = "primary" @click ="save" > 保存 < / el -button >
< / e l - f o r m - i t e m >
< / e l - f o r m >
< el -dialog
title = "重置密码"
: visible . sync = "resetPasswordDialogVisible"
width = "30%"
@ close = "newPassword = ''"
>
< el -form label -width = " 80px " >
< el -form -item label = "新密码" >
< el -input v-model ="newPassword" type="password" placeholder="请输入新密码" > < / el -input >
< / e l - f o r m - i t e m >
< / e l - f o r m >
< span slot = "footer" class = "dialog-footer" >
< el -button @ click = "resetPasswordDialogVisible = false" > 取 消 < / e l - b u t t o n >
< el -button type = "primary" @click ="handleResetPassword" > 确 定 < / el -button >
< / span >
< / e l - d i a l o g >
< / div >
2025-09-10 17:55:35 +00:00
< / template >
< script >
2025-09-11 02:36:47 +00:00
import {
getAvailablePermissions ,
checkUsername ,
} from '@/api/modules/subaccount' // Assuming some APIs can be reused
2025-09-11 06:26:11 +00:00
import { inviteJoin , setMerchantPermissions , shopBaseConfig , listPlatformCategory , getMerchantList , updatePassword } from '@/api/modules/merchant' // New merchant API
2025-09-11 02:48:05 +00:00
import { mer _admin } from '@/api/modules/mer_admin'
2025-09-11 02:36:47 +00:00
import routerConfig from '@/router/full-routers'
import { mapState } from 'vuex'
2025-09-11 02:48:05 +00:00
import cookie from 'js-cookie'
2025-09-11 02:36:47 +00:00
// Function to filter the full menu tree based on available permissions
const filterTreeByPerms = ( tree , availablePerms ) => {
if ( ! Array . isArray ( tree ) ) {
return [ ] ;
}
return tree . reduce ( ( acc , node ) => {
const children = node . list && node . list . length > 0
? filterTreeByPerms ( node . list , availablePerms )
: [ ] ;
if ( availablePerms . includes ( node . perms ) || children . length > 0 ) {
acc . push ( {
... node ,
list : children ,
} ) ;
}
return acc ;
} , [ ] ) ;
} ;
// Function to map the filtered tree to the format required by el-tree
const mapTreeForElTree = ( tree ) => {
return tree . map ( ( item ) => ( {
id : item . perms ,
label : item . name ,
children : item . list && item . list . length > 0 ? mapTreeForElTree ( item . list ) : [ ] ,
} ) ) ;
} ;
2025-09-10 17:55:35 +00:00
export default {
2025-09-11 02:36:47 +00:00
name : 'MerchantAccount' ,
data ( ) {
const validateMobile = ( rule , value , callback ) => {
if ( this . form . operationType !== 'create' ) {
return callback ( ) ;
}
if ( ! value ) {
return callback ( new Error ( '请输入商户账号' ) ) ;
}
checkUsername ( value , this . form . selectedMarketId || this . marketId ) . then ( res => {
if ( res && res . data && res . data . data && res . data . data . available === true ) {
callback ( ) ;
} else {
callback ( new Error ( '商户账号已被占用' ) ) ;
}
} ) . catch ( ( ) => {
callback ( new Error ( '商户账号校验失败' ) ) ;
} ) ;
} ;
return {
resetPasswordDialogVisible : false ,
newPassword : '' ,
form : {
operationType : 'create' ,
mobile : '' ,
password : '' ,
permissionCodes : [ ] ,
2025-09-11 06:26:11 +00:00
merchantId : null ,
2025-09-11 02:36:47 +00:00
enable : true ,
2025-09-11 02:48:05 +00:00
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 : '' ,
2025-09-11 02:36:47 +00:00
} ,
2025-09-11 02:48:05 +00:00
businessHours : [ new Date ( 2016 , 9 , 10 , 0 , 0 ) , new Date ( 2016 , 9 , 10 , 23 , 59 ) ] ,
categoryList : [ ] ,
2025-09-11 02:36:47 +00:00
merchantAccountList : [ ] , // To be populated with merchant accounts
permissionList : [ ] ,
defaultProps : {
children : 'children' ,
label : 'label'
} ,
rules : {
operationType : [
{ required : true , message : "请选择操作类型" , trigger : "change" } ,
] ,
mobile : [
{ required : true , validator : validateMobile , trigger : "blur" } ,
] ,
password : [
{ required : true , message : "请输入密码" , trigger : "blur" } ,
] ,
2025-09-11 02:48:05 +00:00
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 : '必须为数字值' }
] ,
2025-09-11 02:36:47 +00:00
permissionCodes : [
{ required : true , message : "请选择菜单权限" , trigger : "change" , type : 'array' } ,
] ,
2025-09-11 06:26:11 +00:00
merchantId : [
2025-09-11 02:36:47 +00:00
{ required : true , message : "请选择要更新的商户账号" , trigger : "change" } ,
] ,
} ,
} ;
} ,
computed : {
... mapState ( "userData" , [
"isMerchant" ,
"marketList" ,
"storeList" ,
"marketId" ,
"shopId" ,
"markets"
2025-09-11 02:48:05 +00:00
] ) ,
uploadUrl ( ) {
return mer _admin . uploadFile ( ) ;
} ,
uploadHeaders ( ) {
return {
token : cookie . get ( 'token' )
} ;
}
2025-09-11 02:36:47 +00:00
} ,
watch : {
'form.operationType' ( newType ) {
this . form . mobile = '' ;
this . form . password = '' ;
this . form . permissionCodes = [ ] ;
2025-09-11 06:26:11 +00:00
this . form . merchantId = null ;
2025-09-11 02:36:47 +00:00
this . $nextTick ( ( ) => {
this . $refs . ruleFormRef . clearValidate ( ) ;
} ) ;
if ( this . $refs . permissionTree ) {
this . $refs . permissionTree . setCheckedKeys ( [ ] ) ;
}
if ( newType === 'update' ) {
2025-09-11 06:26:11 +00:00
this . loadMerchantAccountList ( ) ;
2025-09-11 02:36:47 +00:00
}
}
} ,
created ( ) {
this . getPermissions ( ) ;
2025-09-11 02:48:05 +00:00
this . getPlatformCategory ( ) ;
2025-09-11 02:36:47 +00:00
} ,
methods : {
2025-09-11 02:48:05 +00:00
getPlatformCategory ( ) {
listPlatformCategory ( { type : this . isCloudShop ? 1 : 0 } ) . then ( res => {
this . categoryList = res . data . data ;
} ) ;
} ,
2025-09-11 02:36:47 +00:00
getPermissions ( ) {
getAvailablePermissions ( ) . then ( ( res ) => {
const availablePerms = ( res && res . data && Array . isArray ( res . data . data ) ) ? res . data . data : [ ] ;
const menuList = ( routerConfig && Array . isArray ( routerConfig . menuList ) ) ? routerConfig . menuList : [ ] ;
const filteredTree = filterTreeByPerms ( menuList , availablePerms ) ;
this . permissionList = mapTreeForElTree ( filteredTree ) ;
} ) ;
} ,
openResetPasswordDialog ( ) {
2025-09-11 06:26:11 +00:00
if ( ! this . form . merchantId ) {
2025-09-11 02:36:47 +00:00
this . $message . error ( '请先选择要更新的商户账号' ) ;
return ;
}
this . resetPasswordDialogVisible = true ;
} ,
handleResetPassword ( ) {
if ( ! this . newPassword ) {
this . $message . error ( '请输入新密码' ) ;
return ;
}
2025-09-11 06:26:11 +00:00
updatePassword ( { password : this . newPassword } ) . then ( ( ) => {
2025-09-11 02:36:47 +00:00
this . $message . success ( '密码重置成功' ) ;
this . resetPasswordDialogVisible = false ;
} ) ;
} ,
handleTreeCheck ( ) {
this . form . permissionCodes = this . $refs . permissionTree . getCheckedKeys ( true ) ;
} ,
2025-09-11 02:48:05 +00:00
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 ;
} ,
2025-09-11 06:26:11 +00:00
loadMerchantAccountList ( ) {
getMerchantList ( { marketId : this . form . selectedMarketId || this . marketId } ) . then ( res => {
this . merchantAccountList = res . data . data || [ ] ;
} ) ;
} ,
handleAccountChange ( merchantId ) {
if ( ! merchantId ) return ;
const accountDetails = this . merchantAccountList . find ( item => item . merchantId === merchantId ) ;
if ( accountDetails ) {
this . form . permissionCodes = accountDetails . permissionCodes || [ ] ;
this . form . enable = accountDetails . enabled ;
this . $nextTick ( ( ) => {
this . $refs . permissionTree . setCheckedKeys ( this . form . permissionCodes ) ;
} ) ;
}
2025-09-11 02:36:47 +00:00
} ,
save ( ) {
this . $refs . ruleFormRef . validate ( ( valid ) => {
if ( valid ) {
const marketId = this . form . selectedMarketId || this . marketId ;
if ( ! marketId ) {
this . $message . error ( '请选择市场' ) ;
return ;
}
if ( this . form . operationType === 'create' ) {
const createData = {
marketId ,
name : this . form . mobile ,
mobile : this . form . mobile ,
remark : '' ,
username : this . form . mobile ,
password : this . form . password
} ;
inviteJoin ( createData ) . then ( ( res ) => {
const merchantAccountId = res . data . data . merchantId ; // Assuming this is the merchant ID
2025-09-11 02:48:05 +00:00
const shopId = res . data . data . shopId ; // Assuming this is the shop ID
2025-09-11 02:36:47 +00:00
const permissionsData = {
merchantAccountId ,
permissionCodes : this . form . permissionCodes ,
marketId
} ;
setMerchantPermissions ( permissionsData ) . then ( ( ) => {
2025-09-11 02:48:05 +00:00
const shopData = {
... 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 ( ) ;
} ) ;
2025-09-11 02:36:47 +00:00
} ) ;
} ) ;
} else if ( this . form . operationType === 'update' ) {
2025-09-11 06:26:11 +00:00
if ( ! this . form . merchantId ) {
2025-09-11 02:36:47 +00:00
this . $message . error ( '请先选择要更新的商户账号' ) ;
return ;
}
2025-09-11 06:26:11 +00:00
const permissionsData = {
merchantAccountId : this . form . merchantId ,
2025-09-11 02:36:47 +00:00
permissionCodes : this . form . permissionCodes ,
marketId
} ;
2025-09-11 06:26:11 +00:00
setMerchantPermissions ( permissionsData ) . then ( ( ) => {
this . $message . success ( '商户信息更新成功' ) ;
this . $refs . ruleFormRef . resetFields ( ) ;
} ) ;
2025-09-11 02:36:47 +00:00
}
}
} ) ;
}
} ,
2025-09-10 17:55:35 +00:00
}
< / script >
< style scoped >
2025-09-11 02:36:47 +00:00
. app - container {
padding : 20 px ;
}
2025-09-11 02:48:05 +00:00
. avatar - uploader . el - upload {
border : 1 px dashed # d9d9d9 ;
border - radius : 6 px ;
cursor : pointer ;
position : relative ;
overflow : hidden ;
}
. avatar - uploader . el - upload : hover {
border - color : # 409 EFF ;
}
. avatar - uploader - icon {
font - size : 28 px ;
color : # 8 c939d ;
width : 178 px ;
height : 178 px ;
line - height : 178 px ;
text - align : center ;
}
. avatar {
width : 178 px ;
height : 178 px ;
display : block ;
2025-09-11 06:26:11 +00:00
}