feat: 菜市场管理摊位列表

This commit is contained in:
lzhizhao 2025-10-29 16:38:32 +08:00
parent 038b6803fe
commit 1be3aac263
7 changed files with 2768 additions and 607 deletions

View File

@ -12,7 +12,7 @@
(function () {
window.SITE_CONFIG = {};
// api接口请求地址
window.SITE_CONFIG["baseUrl"] = "https://admin-test.damajishi.cn/";
window.SITE_CONFIG["baseUrl"] = "http://60.204.138.3:8899";
// 在线api接口请求地址
// window.SITE_CONFIG['baseUrl'] = 'http://119.29.103.250:8082/red-memory';
// 在线api接口请求地址

View File

@ -78,10 +78,10 @@ export const mer_admin = {
//商品详情
productDetail: data => {
return $http.request({
method: 'get',
url: '/merchant-api/product/detail',
method: "get",
url: "/merchant-api/product/detail",
params: data
})
});
},
//商户商品分类
getProductCategory: data => {
@ -411,5 +411,152 @@ export const mer_admin = {
url: `/merchant-api/backend/order/v2/overview`,
params: data
});
},
// ==================== 摊位管理相关接口 ====================
/**
* 经营者模式-摊位概览
* @param {object} data 查询参数
* @returns {Promise}
*/
overviewShop: data => {
return $http.request({
method: "get",
url: `/merchant-api/manager/shop/overview`,
params: data
});
},
/**
* 绑定收益人
* @param {object} data 绑定参数
* @param {number} data.marketId 市场ID
* @param {number} data.beneficiaryOperationType 操作类型1:市场经营者2:创建新收益人3:绑定已有供应商账号
* @param {string} data.beneficiaryName 收益人姓名
* @param {string} data.beneficiaryMobile 收益人账号
* @param {string} data.password 登录密码
* @param {string} data.remark 备注
* @param {number} data.shopId 店铺ID
* @returns {Promise}
*/
bindShopBeneficiary: data => {
return $http.post(`/merchant-api/shop/bind/beneficiary`, data);
},
/**
* 新增经营者店铺
* @param {object} data 店铺参数
* @returns {Promise}
*/
addShop: data => {
return $http.post(`/merchant-api/shop/add`, data);
},
/**
* 经营者模式-摊位列表
* @param {object} data 查询参数
* @param {number} data.pageNumber 页码
* @param {number} data.pageSize 每页数量
* @param {number} data.marketId 市场ID
* @param {string} data.shopName 摊位名称
* @param {number} data.status 业务状态-1:未配置 0:休息 1:营业
* @returns {Promise}
*/
pageShop: data => {
return $http.request({
method: "get",
url: `/merchant-api/manager/shop/page`,
params: data
});
},
/**
* 邀请摊主入驻
* @param {object} data 邀请参数
* @param {number} data.marketId 市场ID
* @param {number} data.beneficiaryOperationType 操作类型1:市场经营者2:创建新收益人3:绑定已有供应商账号
* @param {string} data.beneficiaryName 收益人姓名
* @param {string} data.beneficiaryMobile 收益人账号
* @param {string} data.password 登录密码
* @returns {Promise}
*/
inviteJoin: data => {
return $http.post(`/merchant-api/manager/shop/invite_join`, data);
},
/**
* 删除摊位
* @param {object} query 删除参数
* @param {number} query.shopId 摊位ID
* @returns {Promise}
*/
deleteShop: query => {
return $http.delete(`/merchant-api/manager/shop/delete`, { params: query });
},
/**
* 更新摊位状态
* @param {object} data 更新参数
* @param {number} data.shopId 摊位ID
* @param {number} data.status 状态
* @returns {Promise}
*/
updateShopStatus: data => {
return $http.put(`/merchant-api/manager/shop/status`, data);
},
/**
* 更新摊位信息
* @param {object} data 更新参数
* @param {number} data.shopId 摊位ID
* @returns {Promise}
*/
updateShop: data => {
return $http.put(`/merchant-api/manager/shop/update`, data);
},
/**
* 获取摊位详情经营者模式
* @param {object} query 查询参数
* @param {number} query.shopId 摊位ID
* @returns {Promise}
*/
getShopDetail: query => {
return $http.request({
method: "get",
url: `/merchant-api/manager/shop/detail`,
params: query
});
},
/**
* 获取摊位基础配置摊主模式
* @param {object} query 查询参数
* @param {string} query.shopId 摊位ID
* @returns {Promise}
*/
getShopBaseConfig: query => {
return $http.request({
method: "get",
url: `/merchant-api/shop/base_config`,
params: query
});
},
/**
* 更新摊位基础配置摊主模式
* @param {object} query 查询参数
* @param {string} query.shopId 摊位ID
* @param {object} data 更新数据
* @returns {Promise}
*/
updateShopBaseConfig: (query, data) => {
return $http.request({
method: "put",
url: `/merchant-api/shop/base_config`,
params: query,
data: data
});
}
};

View File

@ -1,322 +1,335 @@
<template>
<div>
<el-input ref="main"
v-model="address"
:clearable="disabled"
:disabled="disabled"
placeholder="请选择地址"
@clear="handleClear"
@focus="handleShow"
/>
<div>
<el-input
ref="main"
v-model="address"
:clearable="disabled"
:disabled="disabled"
placeholder="请选择地址"
@clear="handleClear"
@focus="handleShow"
/>
<el-dialog
:visible.sync="box"
append-to-body
title="选择位置"
width="80%"
@close="handleClose"
<el-dialog
:visible.sync="box"
append-to-body
title="选择位置"
width="80%"
@close="handleClose"
>
<div v-if="box">
<el-input
id="map__input"
v-model="formattedAddress"
class="input-map-content-input"
:readonly="disabled"
clearable
placeholder="输入关键字选取地点"
@clear="clear"
/>
<div>
<div
id="map__container"
class="input-map-content-container"
tabindex="0"
/>
<div id="map__result" class="input-map-content-result" />
</div>
</div>
<span slot="footer" class="dialog-footer">
<el-button
v-if="!disabled"
icon="el-icon-check"
type="primary"
@click="handleSubmit"
> </el-button
>
<div v-if="box">
<el-input id="map__input"
v-model="formattedAddress"
class="input-map-content-input"
:readonly="disabled"
clearable
placeholder="输入关键字选取地点"
@clear="clear"
/>
<div>
<div id="map__container"
class="input-map-content-container"
tabindex="0"
/>
<div id="map__result"
class="input-map-content-result"
/>
</div>
</div>
<span slot="footer"
class="dialog-footer"
>
<el-button v-if="!(disabled )"
icon="el-icon-check"
type="primary"
@click="handleSubmit"
> </el-button>
</span>
</el-dialog>
</div>
</span>
</el-dialog>
</div>
</template>
<script>
import MapLoader from '@/components/form/InputMap/amap.js'
import MapLoader from "@/components/form/InputMap/amap.js";
export default {
name: 'InputMap',
props: {
disabled: {
type: Boolean,
default: false
}
},
data() {
return {
formattedAddress: '',
address: '',
poi: {},
text: '',
marker: null,
map: null,
box: false
}
},
watch: {
poi(val) {
this.formattedAddress = val.formattedAddress
},
value(val) {
if (!val) {
this.poi = {}
}
},
text(val) {
if (val) {
this.poi = {
longitude: val[0],
latitude: val[1],
formattedAddress: val[2]
}
this.address = val[2]
}
},
box: {
handler() {
if (this.box) {
this.$nextTick(() =>
this.init(() => {
if (this.longitude && this.latitude) {
this.addMarker(this.longitude, this.latitude)
this.getAddress(this.longitude, this.latitude)
}
})
)
}
},
immediate: true
}
},
methods: {
clear() {
this.poi = {}
this.clearMarker()
},
handleSubmit() {
this.setVal()
this.$emit('input', this.text)
this.box = false
},
handleClear() {
this.text = []
this.poi = {}
},
setVal() {
this.text = [this.poi.longitude, this.poi.latitude, this.poi.formattedAddress]
},
handleShow() {
this.$refs.main.blur()
this.box = true
},
//
addMarker(lng, lat) {
this.clearMarker()
this.marker = new window.AMap.Marker({
position: [lng, lat]
})
this.map.add(this.marker)
},
//
clearMarker() {
if (this.marker) {
this.map.remove(this.marker)
this.marker = null
}
},
//
getAddress(lng, lat) {
let that = this
window.AMap.plugin('AMap.Geocoder', function() {
//
let geocoder = new window.AMap.Geocoder({})
geocoder.getAddress([lng, lat], (status, result) => {
if (status === 'complete' && result.info === 'OK') {
const regeocode = result.regeocode
that.poi = Object.assign(regeocode, {
longitude: lng,
latitude: lat
})
//
that.clearMarker()
let content = `<div><img class="amap-marker-img" src="//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png">
<span class="input-map-marker">${that.poi.formattedAddress}</span></div>`
that.marker = new window.AMap.Marker({
content: content, //
position: [lng, lat],
title: that.poi.formattedAddress,
zoom: 13
})
that.map.add(that.marker)
}
})
})
},
//
getCurrentLocation() {
let that = this
window.AMap.plugin('AMap.Geolocation', function() {
let geolocation = new window.AMap.Geolocation({
// 使true
enableHighAccuracy: true,
//
timeout: 10000,
// Pixel(10, 20)
buttonOffset: new window.AMap.Pixel(10, 20),
// 使false
zoomToAccuracy: true,
// , RB
buttonPosition: 'RB'
})
geolocation.getCurrentPosition(function(status, result) {
if (status == 'complete') {
onComplete(result)
} else {
onError(result)
}
})
function onComplete(data) {
// data
console.log(data)
let {lng, lat} = data.position
that.getAddress(lng, lat)
}
function onError(data) {
//
console.log(data)
}
})
},
handleClose() {
window.poiPicker.clearSearchResults()
this.box = false
},
addClick() {
this.map.on('click', e => {
if (this.disabled) return
console.log(e)
const lnglat = e.lnglat
//
const lng = lnglat.lng
//
const lat = lnglat.lat
this.addMarker(lng, lat)
this.getAddress(lng, lat)
})
},
init() {
MapLoader().then(AMap => {
console.log('地图加载成功')
this.map = new AMap.Map('map__container', Object.assign({
zoom: 13,
//
resizeEnable: true,
center: (() => {
if (this.longitude && this.latitude) return [this.longitude, this.latitude]
})()
}, this.params))
this.getCurrentLocation()
this.initPoip()
this.addClick()
}, e => {
console.log('地图加载失败', e)
})
},
initPoip() {
// https://lbs.amap.com/api/amap-ui/reference-amap-ui/other/poipicker
if (!window.AMapUI) {
return
}
// PoiPickerloadUI 'ui/'
window.AMapUI.loadUI(['misc/PoiPicker'], PoiPicker => {
var poiPicker = new PoiPicker({
input: 'map__input',
placeSearchOptions: {
map: this.map,
pageSize: 10
},
searchResultsContainer: 'map__result'
})
// poiPicker
this.poiPickerReady(poiPicker)
})
},
poiPickerReady(poiPicker) {
window.poiPicker = poiPicker
// POI
poiPicker.on('poiPicked', poiResult => {
this.clearMarker()
let source = poiResult.source,
poi = poiResult.item
console.log(poiResult)
this.poi = Object.assign(poi, {
formattedAddress: poi.name,
longitude: poi.location.lng,
latitude: poi.location.lat
})
if (source !== 'search') {
poiPicker.searchByKeyword(poi.name)
}
})
}
name: "InputMap",
props: {
disabled: {
type: Boolean,
default: false
}
}
},
data() {
return {
formattedAddress: "",
address: "",
poi: {},
text: "",
marker: null,
map: null,
box: false
};
},
watch: {
poi(val) {
this.formattedAddress = val.formattedAddress;
},
value(val) {
if (!val) {
this.poi = {};
}
},
text(val) {
if (val) {
this.poi = {
longitude: val[0],
latitude: val[1],
formattedAddress: val[2]
};
this.address = val[2];
}
},
box: {
handler() {
if (this.box) {
this.$nextTick(() =>
this.init(() => {
if (this.longitude && this.latitude) {
this.addMarker(this.longitude, this.latitude);
this.getAddress(this.longitude, this.latitude);
}
})
);
}
},
immediate: true
}
},
methods: {
clear() {
this.poi = {};
this.clearMarker();
},
handleSubmit() {
this.setVal();
this.$emit("input", this.text);
this.box = false;
},
handleClear() {
this.text = [];
this.poi = {};
},
setVal() {
this.text = [
this.poi.longitude,
this.poi.latitude,
this.poi.formattedAddress
];
},
handleShow() {
this.$refs.main.blur();
this.box = true;
},
//
addMarker(lng, lat) {
this.clearMarker();
this.marker = new window.AMap.Marker({
position: [lng, lat]
});
this.map.add(this.marker);
},
//
clearMarker() {
if (this.marker) {
this.map.remove(this.marker);
this.marker = null;
}
},
//
getAddress(lng, lat) {
let that = this;
window.AMap.plugin("AMap.Geocoder", function() {
//
let geocoder = new window.AMap.Geocoder({});
geocoder.getAddress([lng, lat], (status, result) => {
if (status === "complete" && result.info === "OK") {
const regeocode = result.regeocode;
that.poi = Object.assign(regeocode, {
longitude: lng,
latitude: lat
});
//
that.clearMarker();
let content = `<div><img class="amap-marker-img" src="//a.amap.com/jsapi_demos/static/demo-center/icons/poi-marker-default.png">
<span class="input-map-marker">${that.poi.formattedAddress}</span></div>`;
that.marker = new window.AMap.Marker({
content: content, //
position: [lng, lat],
title: that.poi.formattedAddress,
zoom: 13
});
that.map.add(that.marker);
}
});
});
},
//
getCurrentLocation() {
let that = this;
window.AMap.plugin("AMap.Geolocation", function() {
let geolocation = new window.AMap.Geolocation({
// 使true
enableHighAccuracy: true,
//
timeout: 10000,
// Pixel(10, 20)
buttonOffset: new window.AMap.Pixel(10, 20),
// 使false
zoomToAccuracy: true,
// , RB
buttonPosition: "RB"
});
geolocation.getCurrentPosition(function(status, result) {
if (status == "complete") {
onComplete(result);
} else {
onError(result);
}
});
function onComplete(data) {
// data
console.log(data);
let { lng, lat } = data.position;
that.getAddress(lng, lat);
}
function onError(data) {
//
console.log(data);
}
});
},
handleClose() {
window.poiPicker.clearSearchResults();
this.box = false;
},
addClick() {
this.map.on("click", e => {
if (this.disabled) return;
console.log(e);
const lnglat = e.lnglat;
//
const lng = lnglat.lng;
//
const lat = lnglat.lat;
this.addMarker(lng, lat);
this.getAddress(lng, lat);
});
},
init() {
MapLoader().then(
AMap => {
console.log("地图加载成功");
this.map = new AMap.Map(
"map__container",
Object.assign(
{
zoom: 13,
//
resizeEnable: true,
center: (() => {
if (this.longitude && this.latitude)
return [this.longitude, this.latitude];
})()
},
this.params
)
);
this.getCurrentLocation();
this.initPoip();
this.addClick();
},
e => {
console.log("地图加载失败", e);
}
);
},
initPoip() {
// https://lbs.amap.com/api/amap-ui/reference-amap-ui/other/poipicker
if (!window.AMapUI) {
return;
}
// PoiPickerloadUI 'ui/'
window.AMapUI.loadUI(["misc/PoiPicker"], PoiPicker => {
var poiPicker = new PoiPicker({
input: "map__input",
placeSearchOptions: {
map: this.map,
pageSize: 10
},
searchResultsContainer: "map__result"
});
// poiPicker
this.poiPickerReady(poiPicker);
});
},
poiPickerReady(poiPicker) {
window.poiPicker = poiPicker;
// POI
poiPicker.on("poiPicked", poiResult => {
this.clearMarker();
let source = poiResult.source,
poi = poiResult.item;
console.log(poiResult);
this.poi = Object.assign(poi, {
formattedAddress: poi.name,
longitude: poi.location.lng,
latitude: poi.location.lat
});
if (source !== "search") {
poiPicker.searchByKeyword(poi.name);
}
});
}
}
};
</script>
<style scoped>
::v-deep .amap-marker-img {
width: 25px !important;
height: 34px !important;
width: 25px !important;
height: 34px !important;
}
::v-deep .input-map-marker {
position: absolute !important;
top: -20px !important;
right: -118px !important;
color: #fff !important;
padding: 4px 10px !important;
-webkit-box-shadow: 1px 1px 1px rgba(10, 10, 10, 0.2);
box-shadow: 1px 1px 1px rgba(10, 10, 10, 0.2);
white-space: nowrap;
font-size: 12px;
background-color: #25a5f7 !important;
border-radius: 3px;
position: absolute !important;
top: -20px !important;
right: -118px !important;
color: #fff !important;
padding: 4px 10px !important;
-webkit-box-shadow: 1px 1px 1px rgba(10, 10, 10, 0.2);
box-shadow: 1px 1px 1px rgba(10, 10, 10, 0.2);
white-space: nowrap;
font-size: 12px;
background-color: #25a5f7 !important;
border-radius: 3px;
}
.input-map-content-container {
width: 100%;
height: 450px;
width: 100%;
height: 450px;
}
.input-map-content-result {
display: block !important;
position: absolute;
top: 0;
right: -8px;
width: 250px;
height: 450px;
overflow-y: auto;
display: block !important;
position: absolute;
top: 0;
right: -8px;
width: 250px;
height: 450px;
overflow-y: auto;
}
.input-map-content-input {
margin-bottom: 10px;
margin-bottom: 10px;
}
::v-deep .el-dialog__headerbtn {

View File

@ -4,230 +4,191 @@
* 建议:
* 1. 代码中路由统一使用name属性跳转(不使用path属性)
*/
import Vue from "vue";
import Router from "vue-router";
import http from "@/utils/httpRequest";
import { isURL } from "@/utils/validate";
import { clearLoginInfo } from "@/utils";
import FULL_ROUTERS from "./full-routers";
import $api from "@/api/index.js";
import store from "@/store";
import { Loading } from "element-ui";
import Vue from 'vue'
import Router from 'vue-router'
import http from '@/utils/httpRequest'
import { isURL } from '@/utils/validate'
import { clearLoginInfo } from '@/utils'
import FULL_ROUTERS from './full-routers'
import $api from '@/api/index.js'
import store from '@/store'
import { Loading } from 'element-ui'
Vue.use(Router);
Vue.use(Router)
// 开发环境不使用懒加载, 因为懒加载页面太多的话会造成webpack热更新太慢, 所以只有生产环境使用懒加载
const _import = require("./import-" + process.env.NODE_ENV);
const _import = require('./import-' + process.env.NODE_ENV)
// 全局路由(无需嵌套上左右整体布局)
const globalRoutes = [
{
path: "/404",
component: _import("common/404"),
name: "404",
meta: { title: "404未找到" },
path: '/404',
component: _import('common/404'),
name: '404',
meta: { title: '404未找到' }
},
{
path: "/login",
component: _import("common/login"),
name: "login",
meta: { title: "登录" },
path: '/login',
component: _import('common/login'),
name: 'login',
meta: { title: '登录' }
},
{
path: "/test-member-detail",
component: _import("test-member-detail"),
name: "test-member-detail",
meta: { title: "会员详情样式测试" },
},
path: '/test-member-detail',
component: _import('test-member-detail'),
name: 'test-member-detail',
meta: { title: '会员详情样式测试' }
}
//test用会员管理
];
]
// 主入口路由(需嵌套上左右整体布局)
const mainRoutes = {
path: "/",
component: _import("main"),
name: "main",
redirect: { name: "home" },
meta: { title: "主入口整体布局" },
path: '/',
component: _import('main'),
name: 'main',
redirect: { name: 'home' },
meta: { title: '主入口整体布局' },
children: [
// 通过meta对象设置路由展示方式
// 1. isTab: 是否通过tab展示内容, true: 是, false: 否
// 2. iframeUrl: 是否通过iframe嵌套展示内容, '以http[s]://开头': 是, '': 否
// 提示: 如需要通过iframe嵌套展示内容, 但不通过tab打开, 请自行创建组件使用iframe处理!
{
path: "/home",
component: _import("common/home"),
name: "home",
meta: { title: "首页" },
path: '/home',
component: _import('common/home'),
name: 'home',
meta: { title: '首页' }
},
{
path: "/theme",
component: _import("common/theme"),
name: "theme",
meta: { title: "主题" },
path: '/theme',
component: _import('common/theme'),
name: 'theme',
meta: { title: '主题' }
},
{
path: "/demo-echarts",
component: _import("demo/echarts"),
name: "demo-echarts",
meta: { title: "demo-echarts", isTab: true },
},
path: '/demo-echarts',
component: _import('demo/echarts'),
name: 'demo-echarts',
meta: { title: 'demo-echarts', isTab: true }
}
],
beforeEnter(to, from, next) {
let token = Vue.cookie.get("token");
let token = Vue.cookie.get('token')
if (!token || !/\S/.test(token)) {
clearLoginInfo();
next({ name: "login" });
clearLoginInfo()
next({ name: 'login' })
}
next();
},
};
next()
}
}
const router = new Router({
mode: "hash",
mode: 'hash',
// mode:"history",
scrollBehavior: () => ({ y: 0 }),
isAddDynamicMenuRoutes: false, // 是否已经添加动态(菜单)路由
routes: globalRoutes.concat(mainRoutes),
});
routes: globalRoutes.concat(mainRoutes)
})
router.beforeEach((to, from, next) => {
console.log("beforeEach", to);
console.log('beforeEach', to)
// 添加动态(菜单)路由
// 1. 已经添加 or 全局路由, 直接访问
// 2. 获取菜单列表, 添加并保存本地存储
if (
router.options.isAddDynamicMenuRoutes ||
fnCurrentRouteType(to, globalRoutes) === "global"
fnCurrentRouteType(to, globalRoutes) === 'global'
) {
next();
next()
} else {
let loadingInstance = Loading.service({
text: "正在初始化中...",
background: "rgba(255,255,255,0.7)",
});
text: '正在初始化中...',
background: 'rgba(255,255,255,0.7)'
})
$api
.getUserInfo()
.then(async ({ data }) => {
// 从后端获取的权限码 (只包含叶子节点)
const permissionCodes = data.data.permissions?.permissionCodes || [];
const permissionCodes = data.data.permissions?.permissionCodes || []
const filterTreeData = (menuData, permissions) => {
const result = [];
menuData.forEach((node) => {
const result = []
menuData.forEach(node => {
// 检查当前节点是否为叶子节点
const isLeaf = !node.list || node.list.length === 0;
const isLeaf = !node.list || node.list.length === 0
if (isLeaf) {
// 如果是叶子节点,检查其权限是否存在于权限码中
if (permissions.includes(node.perms)) {
result.push({ ...node });
result.push({ ...node })
}
} else {
// 如果是父节点,递归过滤其子节点
const filteredChildren = filterTreeData(node.list, permissions);
const filteredChildren = filterTreeData(node.list, permissions)
// 如果过滤后的子节点列表不为空,则保留该父节点
if (filteredChildren.length > 0) {
result.push({
...node,
list: filteredChildren,
});
list: filteredChildren
})
}
}
});
return result;
};
console.log(data, "用户信息");
sessionStorage.setItem("role", JSON.stringify(data.data.role));
if (data.data.markets?.length > 0) {
// 存在多个市场
$api.mer_admin
.storeList({ marketId: data.data.markets[0].marketId })
.then((res) => {
store.commit("userData/setState", {
isMerchant: true,
marketList: data.data.markets,
storeList: res.data.data,
marketId: data.data.markets[0].marketId,
shopId: res.data.data[0].shopId,
});
});
console.log(data.data.markets);
} else if (data.data.shopId) {
// 存在单个店铺或云店
if (data.data.marketId) {
store.commit("userData/setState", {
isMerchant: true,
marketList: [],
storeList: [],
marketId: data.data.marketId,
shopId: data.data.shopId,
});
} else {
store.commit("userData/setState", {
isMerchant: true,
marketList: [],
storeList: [],
marketId: -1,
shopId: data.data.shopId,
});
}
} else {
// 不存在店铺
store.commit("userData/setState", {
isMerchant: false,
marketList: [],
storeList: [],
marketId: "",
shopId: "",
});
})
return result
}
sessionStorage.setItem("userInfo", JSON.stringify(data.data));
sessionStorage.setItem(
"permissions",
JSON.stringify(permissionCodes || "[]")
);
let _menu = filterTreeData(FULL_ROUTERS.menuList, permissionCodes);
fnAddDynamicMenuRoutes(_menu);
sessionStorage.setItem("menuList", JSON.stringify(_menu));
router.options.isAddDynamicMenuRoutes = true;
next({ ...to, replace: true });
console.log(data, '用户信息')
sessionStorage.setItem('role', JSON.stringify(data.data.role))
store.commit('userData/setState', {
isMerchant: true,
...data.data
})
sessionStorage.setItem('userInfo', JSON.stringify(data.data))
sessionStorage.setItem(
'permissions',
JSON.stringify(permissionCodes || '[]')
)
let _menu = filterTreeData(FULL_ROUTERS.menuList, permissionCodes)
fnAddDynamicMenuRoutes(_menu)
sessionStorage.setItem('menuList', JSON.stringify(_menu))
router.options.isAddDynamicMenuRoutes = true
next({ ...to, replace: true })
})
.catch((e) => {
sessionStorage.setItem("menuList", "[]");
sessionStorage.setItem("permissions", "[]");
.catch(e => {
sessionStorage.setItem('menuList', '[]')
sessionStorage.setItem('permissions', '[]')
console.log(
`%c${e} 请求菜单列表和权限失败,跳转至登录页!!`,
"color:blue"
);
router.push({ name: "login" });
next();
'color:blue'
)
router.push({ name: 'login' })
next()
})
.finally(() => {
loadingInstance.close();
});
loadingInstance.close()
})
}
});
})
/**
* 判断当前路由类型, global: 全局路由, main: 主入口路由
* @param {*} route 当前路由
*/
function fnCurrentRouteType(route, globalRoutes = []) {
var temp = [];
var temp = []
for (var i = 0; i < globalRoutes.length; i++) {
if (route.path === globalRoutes[i].path) {
return "global";
return 'global'
} else if (
globalRoutes[i].children &&
globalRoutes[i].children.length >= 1
) {
temp = temp.concat(globalRoutes[i].children);
temp = temp.concat(globalRoutes[i].children)
}
}
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : "main";
return temp.length >= 1 ? fnCurrentRouteType(route, temp) : 'main'
}
/**
@ -236,59 +197,59 @@ function fnCurrentRouteType(route, globalRoutes = []) {
* @param {*} routes 递归创建的动态(菜单)路由
*/
function fnAddDynamicMenuRoutes(menuList = [], routes = []) {
var temp = [];
var temp = []
for (var i = 0; i < menuList.length; i++) {
if (menuList[i].list && menuList[i].list.length >= 1) {
temp = temp.concat(menuList[i].list);
temp = temp.concat(menuList[i].list)
} else if (menuList[i].url && /\S/.test(menuList[i].url)) {
menuList[i].url = menuList[i].url.replace(/^\//, "");
menuList[i].url = menuList[i].url.replace(/^\//, '')
var route = {
path: menuList[i].url.replace("/", "-"),
path: menuList[i].url.replace('/', '-'),
component: null,
name: menuList[i].url.replace("/", "-"),
name: menuList[i].url.replace('/', '-'),
meta: {
menuId: menuList[i].menuId,
title: menuList[i].name,
isDynamic: true,
isTab: true,
iframeUrl: "",
isMicroApp: menuList[i].isMicroApp,
},
};
iframeUrl: '',
isMicroApp: menuList[i].isMicroApp
}
}
// url以http[s]://开头, 通过iframe展示,或者启用microApp框架
if (isURL(menuList[i].url)) {
route["path"] = `i-${menuList[i].menuId}`;
route["name"] = `i-${menuList[i].menuId}`;
route["meta"]["iframeUrl"] = menuList[i].url;
route['path'] = `i-${menuList[i].menuId}`
route['name'] = `i-${menuList[i].menuId}`
route['meta']['iframeUrl'] = menuList[i].url
} else {
try {
route["component"] = _import(`modules/${menuList[i].url}`) || null;
route['component'] = _import(`modules/${menuList[i].url}`) || null
} catch (e) {}
}
routes.push(route);
routes.push(route)
}
}
if (temp.length >= 1) {
fnAddDynamicMenuRoutes(temp, routes);
fnAddDynamicMenuRoutes(temp, routes)
} else {
mainRoutes.name = "main-dynamic";
mainRoutes.children = routes;
router.addRoutes([mainRoutes, { path: "*", redirect: { name: "404" } }]);
mainRoutes.name = 'main-dynamic'
mainRoutes.children = routes
router.addRoutes([mainRoutes, { path: '*', redirect: { name: '404' } }])
sessionStorage.setItem(
"dynamicMenuRoutes",
JSON.stringify(mainRoutes.children || "[]")
);
console.log("\n");
'dynamicMenuRoutes',
JSON.stringify(mainRoutes.children || '[]')
)
console.log('\n')
console.log(
"%c!<-------------------- 动态(菜单)路由 s -------------------->",
"color:blue"
);
console.log(mainRoutes.children);
'%c!<-------------------- 动态(菜单)路由 s -------------------->',
'color:blue'
)
console.log(mainRoutes.children)
console.log(
"%c!<-------------------- 动态(菜单)路由 e -------------------->",
"color:blue"
);
'%c!<-------------------- 动态(菜单)路由 e -------------------->',
'color:blue'
)
}
}
export default router;
export default router

File diff suppressed because it is too large Load Diff

View File

@ -9,7 +9,7 @@
<el-input v-model="formInline.name" placeholder="请输入商品名称" clearable></el-input>
</el-form-item>
</el-col>
<el-col :span="6" v-if="marketList.length > 0">
<el-col :span="6" v-if="marketList?.length > 0">
<el-form-item label="店铺" prop="shopId">
<el-select v-model="formInline.shopId" placeholder="请选择店铺" style="width: 100%;">
<el-option v-for="item in storeList" :key="item.shopId" :label="item.shopName" :value="item.shopId"></el-option>
@ -74,7 +74,7 @@
<div class="table-container">
<div style="margin-bottom: 15px;">
<el-button type="primary" size="small" @click="addProduct">添加商品</el-button>
<el-button :disabled="selectList.length <= 0" type="danger" size="small" @click="deleteProduct">批量删除</el-button>
<el-button :disabled="selectList?.length <= 0" type="danger" size="small" @click="deleteProduct">批量删除</el-button>
</div>
<el-table
:data="dataList"
@ -380,4 +380,4 @@ export default {
font-size: 12px;
line-height: 20px;
}
</style>
</style>

View File

@ -8,10 +8,18 @@
:rules="rules"
style="max-width: 600px"
>
<el-form-item v-if="!marketId && markets && markets.length > 0" label="选择市场" prop="selectedMarketId">
<el-select v-model="form.selectedMarketId" placeholder="请选择市场" style="width: 100%">
<el-form-item
v-if="!marketId && marketList && marketList.length > 0"
label="选择市场"
prop="selectedMarketId"
>
<el-select
v-model="form.selectedMarketId"
placeholder="请选择市场"
style="width: 100%"
>
<el-option
v-for="market in markets"
v-for="market in marketList"
:key="market.marketId"
:label="market.marketName"
:value="market.marketId"
@ -20,7 +28,11 @@
</el-form-item>
<el-form-item label="操作类型" prop="operationType">
<el-select v-model="form.operationType" placeholder="请选择操作类型" style="width: 100%">
<el-select
v-model="form.operationType"
placeholder="请选择操作类型"
style="width: 100%"
>
<el-option label="创建新账号" value="create"></el-option>
<el-option label="更新旧账号" value="update"></el-option>
</el-select>
@ -31,13 +43,22 @@
<el-input v-model="form.username" placeholder="请输入账号"></el-input>
</el-form-item>
<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>
</template>
<template v-if="form.operationType === 'update'">
<el-form-item label="选择账号" prop="accountId">
<el-select v-model="form.accountId" placeholder="请选择要更新的账号" @change="handleAccountChange" style="width: 100%">
<el-select
v-model="form.accountId"
placeholder="请选择要更新的账号"
@change="handleAccountChange"
style="width: 100%"
>
<el-option
v-for="item in subAccountList"
:key="item.accountId"
@ -47,10 +68,16 @@
</el-select>
</el-form-item>
<el-form-item>
<el-button type="warning" @click="openResetPasswordDialog">重置密码</el-button>
<el-button type="warning" @click="openResetPasswordDialog"
>重置密码</el-button
>
</el-form-item>
<el-form-item label="状态">
<el-switch v-model="form.enable" active-text="启用" inactive-text="禁用"></el-switch>
<el-switch
v-model="form.enable"
active-text="启用"
inactive-text="禁用"
></el-switch>
</el-form-item>
</template>
@ -61,7 +88,11 @@
<el-input v-model="form.mobile" placeholder="请输入手机号"></el-input>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注"></el-input>
<el-input
v-model="form.remark"
type="textarea"
placeholder="请输入备注"
></el-input>
</el-form-item>
<el-form-item label="权限分配" prop="permissionCodes">
@ -89,7 +120,11 @@
>
<el-form label-width="80px">
<el-form-item label="新密码">
<el-input v-model="newPassword" type="password" placeholder="请输入新密码"></el-input>
<el-input
v-model="newPassword"
type="password"
placeholder="请输入新密码"
></el-input>
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
@ -116,55 +151,64 @@ import { mapState } from 'vuex'
// Function to filter the full menu tree based on available permissions
const filterTreeByPerms = (tree, availablePerms) => {
if (!Array.isArray(tree)) {
return []; // Return empty array if tree is not an array
return [] // Return empty array if tree is not an array
}
return tree.reduce((acc, node) => {
// Recursively filter children
const children = node.list && node.list.length > 0
? filterTreeByPerms(node.list, availablePerms)
: [];
const children =
node.list && node.list.length > 0
? filterTreeByPerms(node.list, availablePerms)
: []
// A node is kept if its permission is in the available list,
// or if it has children that are kept.
if (availablePerms.includes(node.perms) || children.length > 0) {
acc.push({
...node,
list: children, // Use the filtered list of children
});
list: children // Use the filtered list of children
})
}
return acc;
}, []);
};
return acc
}, [])
}
// Function to map the filtered tree to the format required by el-tree
const mapTreeForElTree = (tree) => {
return tree.map((item) => ({
const mapTreeForElTree = tree => {
return tree.map(item => ({
id: item.perms, // Use perms as the id
label: item.name,
children: item.list && item.list.length > 0 ? mapTreeForElTree(item.list) : [],
}));
};
children:
item.list && item.list.length > 0 ? mapTreeForElTree(item.list) : []
}))
}
export default {
name: 'SubOperator',
data() {
const validateUsername = (rule, value, callback) => {
if (this.form.operationType !== 'create') {
return callback();
return callback()
}
if (!value) {
return callback(new Error('请输入账号'));
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('账号校验失败'));
});
};
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,
@ -189,121 +233,132 @@ export default {
},
rules: {
operationType: [
{ required: true, message: "请选择操作类型", trigger: "change" },
{ required: true, message: '请选择操作类型', trigger: 'change' }
],
username: [
{ required: true, validator: validateUsername, trigger: "blur" },
],
password: [
{ required: true, message: "请输入密码", trigger: "blur" },
],
name: [
{ required: true, message: "请输入姓名", trigger: "blur" },
],
mobile: [
{ required: true, message: "请输入手机号", trigger: "blur" },
{ required: true, validator: validateUsername, trigger: 'blur' }
],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
mobile: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
permissionCodes: [
{ required: true, message: "请选择菜单权限", trigger: "change", type: 'array' },
{
required: true,
message: '请选择菜单权限',
trigger: 'change',
type: 'array'
}
],
accountId: [
{ required: true, message: "请选择要更新的账号", trigger: "change" },
],
},
};
{ required: true, message: '请选择要更新的账号', trigger: 'change' }
]
}
}
},
computed: {
...mapState("userData", [
"isMerchant",
"marketList",
"storeList",
"marketId",
"shopId",
"markets"
...mapState('userData', [
'isMerchant',
'marketList',
'storeList',
'marketId',
'shopId'
])
},
watch: {
'form.operationType'(newType) {
// Manually reset form fields to avoid resetting the operationType
this.form.username = '';
this.form.password = '';
this.form.name = '';
this.form.mobile = '';
this.form.permissionCodes = [];
this.form.remark = '';
this.form.accountId = null;
this.form.username = ''
this.form.password = ''
this.form.name = ''
this.form.mobile = ''
this.form.permissionCodes = []
this.form.remark = ''
this.form.accountId = null
// Clear validation messages after the DOM has updated
this.$nextTick(() => {
this.$refs.ruleFormRef.clearValidate();
});
this.$refs.ruleFormRef.clearValidate()
})
// Reset the tree's visual state
if (this.$refs.permissionTree) {
this.$refs.permissionTree.setCheckedKeys([]);
this.$refs.permissionTree.setCheckedKeys([])
}
if (newType === 'update') {
this.loadSubAccountList();
this.loadSubAccountList()
}
}
},
created() {
this.getPermissions();
this.getPermissions()
},
methods: {
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);
});
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() {
if (!this.form.accountId) {
this.$message.error('请先选择要更新的账号');
return;
this.$message.error('请先选择要更新的账号')
return
}
this.resetPasswordDialogVisible = true;
this.resetPasswordDialogVisible = true
},
handleResetPassword() {
if (!this.newPassword) {
this.$message.error('请输入新密码');
return;
this.$message.error('请输入新密码')
return
}
resetPassword(this.form.accountId, this.newPassword, this.form.selectedMarketId || this.marketId).then(() => {
this.$message.success('密码重置成功');
this.resetPasswordDialogVisible = false;
});
resetPassword(
this.form.accountId,
this.newPassword,
this.form.selectedMarketId || this.marketId
).then(() => {
this.$message.success('密码重置成功')
this.resetPasswordDialogVisible = false
})
},
handleTreeCheck() {
// getCheckedKeys(true) will return an array of keys of the currently checked leaf nodes.
this.form.permissionCodes = this.$refs.permissionTree.getCheckedKeys(true);
this.form.permissionCodes = this.$refs.permissionTree.getCheckedKeys(true)
},
loadSubAccountList() {
getSubAccountList(this.form.selectedMarketId || this.marketId).then(res => {
this.subAccountList = res.data.data.data || [];
});
getSubAccountList(this.form.selectedMarketId || this.marketId).then(
res => {
this.subAccountList = res.data.data.data || []
}
)
},
handleAccountChange(accountId) {
if (!accountId) return;
getSubAccountDetail(accountId, this.form.selectedMarketId || this.marketId).then(res => {
const accountDetails = res.data.data;
this.form.name = accountDetails.name;
this.form.mobile = accountDetails.mobile;
this.form.remark = accountDetails.remark;
this.form.permissionCodes = accountDetails.permissionCodes || [];
this.form.enable = accountDetails.enabled;
if (!accountId) return
getSubAccountDetail(
accountId,
this.form.selectedMarketId || this.marketId
).then(res => {
const accountDetails = res.data.data
this.form.name = accountDetails.name
this.form.mobile = accountDetails.mobile
this.form.remark = accountDetails.remark
this.form.permissionCodes = accountDetails.permissionCodes || []
this.form.enable = accountDetails.enabled
this.$nextTick(() => {
this.$refs.permissionTree.setCheckedKeys(this.form.permissionCodes);
});
});
this.$refs.permissionTree.setCheckedKeys(this.form.permissionCodes)
})
})
},
save() {
this.$refs.ruleFormRef.validate((valid) => {
this.$refs.ruleFormRef.validate(valid => {
if (valid) {
const marketId = this.form.selectedMarketId || this.marketId;
const marketId = this.form.selectedMarketId || this.marketId
// if (!marketId) {
// this.$message.error('');
// return;
@ -311,32 +366,32 @@ export default {
console.log(this.form.operationType)
if (this.form.operationType === 'create') {
const createData = { ...this.form, marketId: marketId };
const createData = { ...this.form, marketId: marketId }
if (createData.accountId === null) {
delete createData.accountId;
delete createData.accountId
}
createSubAccount(createData).then(() => {
this.$message.success('创建成功');
this.$message.success('创建成功')
//
const operationType = this.form.operationType;
this.form.username = '';
this.form.password = '';
this.form.name = '';
this.form.mobile = '';
this.form.remark = '';
this.form.permissionCodes = [];
this.form.accountId = null;
this.form.enable = true;
this.form.operationType = operationType; //
const operationType = this.form.operationType
this.form.username = ''
this.form.password = ''
this.form.name = ''
this.form.mobile = ''
this.form.remark = ''
this.form.permissionCodes = []
this.form.accountId = null
this.form.enable = true
this.form.operationType = operationType //
//
if (this.$refs.permissionTree) {
this.$refs.permissionTree.setCheckedKeys([]);
this.$refs.permissionTree.setCheckedKeys([])
}
});
})
} else if (this.form.operationType === 'update') {
if (!this.form.accountId) {
this.$message.error('请先选择要更新的账号');
return;
this.$message.error('请先选择要更新的账号')
return
}
const updateData = {
subAccountId: this.form.accountId,
@ -346,25 +401,25 @@ export default {
remark: this.form.remark,
enabled: this.form.enable,
marketId
};
}
updateSubAccount(updateData).then(() => {
this.$message.success('更新成功');
this.$message.success('更新成功')
// accountIdoperationType
this.form.name = '';
this.form.mobile = '';
this.form.remark = '';
this.form.permissionCodes = [];
this.form.enable = true;
this.form.name = ''
this.form.mobile = ''
this.form.remark = ''
this.form.permissionCodes = []
this.form.enable = true
//
if (this.$refs.permissionTree) {
this.$refs.permissionTree.setCheckedKeys([]);
this.$refs.permissionTree.setCheckedKeys([])
}
});
})
}
}
});
})
}
},
}
}
</script>