496 lines
14 KiB
Vue
496 lines
14 KiB
Vue
<template>
|
||
<view>
|
||
<view class="uni-header">
|
||
<uni-stat-breadcrumb class="uni-stat-breadcrumb-on-phone"/>
|
||
</view>
|
||
<view class="uni-tabs__header">
|
||
<view class="uni-tabs__nav-wrap">
|
||
<view class="uni-tabs__nav-scroll">
|
||
<view class="uni-tabs__nav">
|
||
<view @click="switchTab('menus')" :class="{'is-active':currentTab==='menus'}"
|
||
class="uni-tabs__item">
|
||
{{ $t('menu.text.menuManager') }}
|
||
</view>
|
||
<view @click="switchTab('pluginMenus')" v-if="pluginMenus.length"
|
||
:class="{'is-active':currentTab==='pluginMenus'}" class="uni-tabs__item">
|
||
{{ $t('menu.text.additiveMenu') }}
|
||
<uni-badge class="menu-badge" :text="pluginMenus.length" type="error"></uni-badge>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view v-show="currentTab==='menus'">
|
||
<view class="uni-header" style="border-bottom: 0;margin-bottom: -15px;">
|
||
<view class="uni-group">
|
||
<button @click="navigateTo('./add')" size="mini" plain="true"
|
||
type="primary">{{ $t('menu.button.addFirstLevelMenu') }}
|
||
</button>
|
||
<button @click="updateBuiltInMenu" size="mini" plain="true" style="margin-left: 10px;"
|
||
type="warn">{{ $t('menu.button.updateBuiltInMenu') }}
|
||
</button>
|
||
</view>
|
||
<view class="uni-group">
|
||
|
||
</view>
|
||
</view>
|
||
<view class="uni-container">
|
||
<unicloud-db ref="udb" @load="onqueryload" collection="opendb-admin-menus" :options="options"
|
||
:where="where" page-data="replace" :orderby="orderby" :getcount="true"
|
||
:page-size="options.pageSize"
|
||
:page-current="options.pageCurrent" v-slot:default="{data,pagination,loading,error}">
|
||
<uni-table :loading="loading" class="table-pc" :emptyText="errMsg || $t('common.empty')" border
|
||
stripe>
|
||
<uni-tr>
|
||
<uni-th align="center">排序</uni-th>
|
||
<uni-th width="200" align="center">名称</uni-th>
|
||
<uni-th align="center">标识</uni-th>
|
||
<uni-th align="center">URL</uni-th>
|
||
<uni-th width="100" align="center">是否启用</uni-th>
|
||
<uni-th align="center">操作</uni-th>
|
||
</uni-tr>
|
||
<uni-tr v-for="(item,index) in data" :key="index">
|
||
<uni-td align="center">{{ item.sort }}</uni-td>
|
||
<uni-td>{{ item.name }}</uni-td>
|
||
<uni-td>{{ item.menu_id }}</uni-td>
|
||
<uni-td>{{ item.url }}</uni-td>
|
||
<uni-td align="center" :class="{'menu-disable':!item.enable}">
|
||
<switch :checked="item.enable" @change="enableChange(item)" />
|
||
<!-- {{ item.enable ? '已启用' : '未启用' }} -->
|
||
</uni-td>
|
||
<uni-td align="center">
|
||
<view class="uni-group" style="justify-content: left;">
|
||
<button @click="navigateTo('./edit?id='+item._id, false)" class="uni-button"
|
||
size="mini" type="primary">{{ $t('common.button.edit') }}
|
||
</button>
|
||
<button
|
||
v-if="item.menu_id !== 'system_menu' && item.menu_id !== 'system_management'"
|
||
@click="confirmDelete(item)" class="uni-button" size="mini"
|
||
type="warn">{{ $t('common.button.delete') }}
|
||
</button>
|
||
<button v-if="!item.url" @click="navigateTo('./add?parent_id='+item.menu_id, false)"
|
||
class="uni-button" size="mini"
|
||
type="primary">{{ $t('menu.button.addChildMenu') }}
|
||
</button>
|
||
</view>
|
||
</uni-td>
|
||
</uni-tr>
|
||
</uni-table>
|
||
</unicloud-db>
|
||
</view>
|
||
</view>
|
||
<view v-show="currentTab==='pluginMenus'">
|
||
<view class="uni-header" style="border-bottom: 0;margin-bottom: -15px;">
|
||
<view class="uni-group">
|
||
<button style="width: 130px;" @click="addPluginMenus" size="mini" type="primary">添加选中的菜单
|
||
</button>
|
||
</view>
|
||
<view class="uni-group"></view>
|
||
</view>
|
||
<view class="uni-container">
|
||
<uni-table ref="pluginMenusTable" type="selection" border stripe
|
||
@selection-change="pluginMenuSelectChange">
|
||
<uni-tr>
|
||
<uni-th align="center">名称(标识)</uni-th>
|
||
<uni-th align="center">URL</uni-th>
|
||
<uni-th align="center">插件菜单 json 文件</uni-th>
|
||
</uni-tr>
|
||
<uni-tr v-for="(item,index) in pluginMenus" :key="index">
|
||
<uni-td>{{ item.name }}({{ item.menu_id }})</uni-td>
|
||
<uni-td>{{ item.url }}</uni-td>
|
||
<uni-td>{{ item.json }}</uni-td>
|
||
</uni-tr>
|
||
</uni-table>
|
||
<view class="uni-sub-title" style="margin-top: 15px;">
|
||
以上待添加菜单来自于三方插件,添加后,将显示在菜单管理中,若不希望显示在上述表格中时,可手动删除项目中对应的`插件id-menu.json`文件。
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- #ifndef H5 -->
|
||
<fix-window/>
|
||
<!-- #endif -->
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
buildMenus
|
||
} from '../../../components/uni-data-menu/util.js'
|
||
|
||
import originalMenuList from './originalMenuList.json'
|
||
|
||
const db = uniCloud.database()
|
||
// 表查询配置
|
||
const dbOrderBy = 'create_date asc'
|
||
// 分页配置
|
||
const pageSize = 20000
|
||
const pageCurrent = 1
|
||
// 查找插件注册的菜单列表(目前仅在开发模式启用,仅限 admin 角色)
|
||
const pluginMenuJsons = []
|
||
|
||
if (process.env.NODE_ENV === 'development') {
|
||
// #ifdef VUE2
|
||
const rootModules = require.context(
|
||
'../../../',
|
||
false,
|
||
/-menu.json$/
|
||
)
|
||
rootModules.keys().forEach(function (key) {
|
||
const json = key.substr(2)
|
||
rootModules(key).forEach(item => {
|
||
item.json = json
|
||
pluginMenuJsons.push(item)
|
||
})
|
||
})
|
||
|
||
const pluginModules = require.context(
|
||
'../../../uni_modules/',
|
||
true,
|
||
/menu.json$/
|
||
)
|
||
pluginModules.keys().forEach(function (key) {
|
||
const json = 'uni_modules' + key.substr(1)
|
||
pluginModules(key).forEach(item => {
|
||
item.json = json
|
||
pluginMenuJsons.push(item)
|
||
})
|
||
})
|
||
// #endif
|
||
// #ifdef VUE3
|
||
const rootModules = import.meta.glob('../../../uni_modules/*/*-menu.json', {eager: true});
|
||
for (const modulePath in rootModules) {
|
||
const json = modulePath.replace(/^..\/..\/..\//, '');
|
||
let moduleItem = rootModules[modulePath];
|
||
if (typeof moduleItem === "function") {
|
||
// 兼容 HBX3.6.5或以下版本
|
||
moduleItem().then(module => {
|
||
module = module.default ? module.default : module
|
||
module.forEach(item => {
|
||
item.json = json
|
||
pluginMenuJsons.push(item)
|
||
});
|
||
})
|
||
} else {
|
||
// 兼容 HBX3.6.13或以上版本
|
||
let module = moduleItem.default ? moduleItem.default : moduleItem;
|
||
module.forEach(item => {
|
||
item.json = json
|
||
pluginMenuJsons.push(item)
|
||
});
|
||
}
|
||
}
|
||
|
||
const pluginModules = import.meta.glob('../../../uni_modules/**/menu.json', {eager: true});
|
||
for (const modulePath in pluginModules) {
|
||
const json = modulePath.replace(/^..\/..\/..\//, '');
|
||
let moduleItem = pluginModules[modulePath];
|
||
if (typeof moduleItem === "function") {
|
||
// 兼容 HBX3.6.5或以下版本
|
||
moduleItem().then(module => {
|
||
module = module.default ? module.default : module
|
||
module.forEach(item => {
|
||
item.json = json
|
||
pluginMenuJsons.push(item)
|
||
})
|
||
})
|
||
} else {
|
||
// 兼容 HBX3.6.13或以上版本
|
||
let module = moduleItem.default ? moduleItem.default : moduleItem;
|
||
module.forEach(item => {
|
||
item.json = json
|
||
pluginMenuJsons.push(item)
|
||
});
|
||
}
|
||
}
|
||
// #endif
|
||
}
|
||
|
||
|
||
// 获取父的个数
|
||
function getParents(menus, id, depth = 0) {
|
||
menus.forEach(menu => {
|
||
if (menu.menu_id === id && menu.parent_id) {
|
||
depth = depth + 1 + getParents(menus, menu.parent_id, depth)
|
||
}
|
||
})
|
||
return depth
|
||
}
|
||
|
||
// 获取子的 _id
|
||
function getChildren(menus, id, childrenIds = []) {
|
||
if (menus.find(menu => menu.parent_id === id)) {
|
||
menus.forEach(item => {
|
||
if (item.parent_id === id) {
|
||
childrenIds.push(item._id)
|
||
getChildren(menus, item.menu_id, childrenIds)
|
||
}
|
||
})
|
||
}
|
||
return childrenIds
|
||
}
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
query: '',
|
||
where: '',
|
||
orderby: dbOrderBy,
|
||
options: {
|
||
pageSize,
|
||
pageCurrent
|
||
},
|
||
selectedIndexs: [], //批量选中的项
|
||
loading: true,
|
||
menus: [],
|
||
errMsg: '',
|
||
currentTab: 'menus',
|
||
selectedPluginMenuIndexs: []
|
||
}
|
||
},
|
||
computed: {
|
||
pluginMenus() {
|
||
const menus = []
|
||
if (!this.$hasRole('admin')) {
|
||
return menus
|
||
}
|
||
const dbMenus = this.menus
|
||
if (!dbMenus.length) {
|
||
return menus
|
||
}
|
||
pluginMenuJsons.forEach(menu => {
|
||
// 查找尚未被注册到数据库中的菜单
|
||
if (!dbMenus.find(item => item.menu_id === menu.menu_id)) {
|
||
menus.push(menu)
|
||
}
|
||
})
|
||
return menus
|
||
},
|
||
},
|
||
watch: {
|
||
pluginMenus(val) {
|
||
if (!val.length) {
|
||
this.currentTab = 'menus'
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
enableChange(item){
|
||
item.enable = item.enable ? false : true;
|
||
db.collection("opendb-admin-menus").doc(item._id).update({
|
||
enable: item.enable
|
||
});
|
||
},
|
||
getSortMenu(menuList) {
|
||
// 标记叶子节点
|
||
menuList.map(item => {
|
||
if (!menuList.some(subMenuItem => subMenuItem.parent_id === item.menu_id)) {
|
||
item.isLeafNode = true
|
||
}
|
||
})
|
||
return buildMenus(menuList)
|
||
},
|
||
onqueryload(data) {
|
||
for (let i = 0; i < data.length; i++) {
|
||
let item = data[i]
|
||
const depth = getParents(data, item.menu_id)
|
||
item.name = (depth ? ' '.repeat(depth) + '|-' : '') + item.name
|
||
}
|
||
const menuTree = this.getSortMenu(data)
|
||
const sortMenus = []
|
||
this.patTree(menuTree, sortMenus)
|
||
data.length = 0;
|
||
data.push(...sortMenus)
|
||
this.menus = data //仅导出当前页
|
||
},
|
||
patTree(tree, sortMenus) {
|
||
tree.forEach(item => {
|
||
sortMenus.push(item)
|
||
if (item.children.length) {
|
||
this.patTree(item.children, sortMenus)
|
||
}
|
||
})
|
||
return sortMenus
|
||
},
|
||
switchTab(tab) {
|
||
this.currentTab = tab
|
||
},
|
||
loadData(clear = true) {
|
||
this.$refs.udb.loadData({
|
||
clear
|
||
})
|
||
},
|
||
navigateTo(url, clear) { // clear 表示刷新列表时是否清除当前页码,true 表示刷新并回到列表第 1 页,默认为 true
|
||
uni.navigateTo({
|
||
url,
|
||
events: {
|
||
refreshData: () => {
|
||
this.loadData(clear)
|
||
}
|
||
}
|
||
})
|
||
},
|
||
confirmDelete(menu) {
|
||
let ids = menu._id
|
||
let content = '是否删除该菜单?'
|
||
// 如有子菜单
|
||
const children = getChildren(this.menus, menu.menu_id)
|
||
if (children.length) content = '是否删除该菜单及其子菜单?'
|
||
ids = [ids, ...children]
|
||
uni.showModal({
|
||
title: '提示',
|
||
content,
|
||
success: (res) => {
|
||
if (!res.confirm) {
|
||
return
|
||
}
|
||
this.$refs.udb.remove(ids, {
|
||
needConfirm: false
|
||
})
|
||
}
|
||
})
|
||
},
|
||
pluginMenuSelectChange(e) {
|
||
this.selectedPluginMenuIndexs = e.detail.index
|
||
},
|
||
addPluginMenus(confirmContent) {
|
||
if (!this.selectedPluginMenuIndexs.length) {
|
||
return uni.showModal({
|
||
title: '提示',
|
||
content: '请选择要添加的菜单!',
|
||
showCancel: false
|
||
})
|
||
}
|
||
const pluginMenus = this.pluginMenus
|
||
const menus = []
|
||
this.selectedPluginMenuIndexs.forEach(i => {
|
||
const menu = pluginMenus[i]
|
||
if (menu) {
|
||
// 拷贝一份,移除 json 字段
|
||
const dbMenu = JSON.parse(JSON.stringify(menu))
|
||
dbMenu.enable = true;
|
||
delete dbMenu.json
|
||
menus.push(dbMenu)
|
||
}
|
||
})
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '您确认要添加已选中的菜单吗?',
|
||
success: (res) => {
|
||
if (!res.confirm) {
|
||
return
|
||
}
|
||
uni.showLoading({
|
||
mask: true
|
||
})
|
||
const checkAll = menus.length === pluginMenus.length
|
||
uniCloud.database().collection('opendb-admin-menus').add(menus).then(res => {
|
||
// this.init()
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '添加菜单成功!',
|
||
showCancel: false,
|
||
success: () => {
|
||
this.$refs.pluginMenusTable.clearSelection()
|
||
if (checkAll) {
|
||
this.currentTab = 'menus'
|
||
}
|
||
this.loadData()
|
||
}
|
||
})
|
||
}).catch(err => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: err.message,
|
||
showCancel: false
|
||
})
|
||
}).finally(() => {
|
||
uni.hideLoading()
|
||
})
|
||
}
|
||
})
|
||
},
|
||
// 更新内置菜单
|
||
async updateBuiltInMenu(){
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定更新内置菜单吗?\n(该操作不会影响现有的菜单)',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
const db = uniCloud.database();
|
||
const _ = db.command;
|
||
let menu_ids = originalMenuList.map((item, index) => {
|
||
return item.menu_id;
|
||
});
|
||
uni.showLoading({
|
||
title:"更新中...",
|
||
mask:true
|
||
});
|
||
try {
|
||
let addMenuList = [];
|
||
// 读取菜单
|
||
let oldMenuListRes = await db.collection("opendb-admin-menus").where({
|
||
menu_id: _.in[menu_ids]
|
||
}).limit(500).get();
|
||
let oldMenuList = oldMenuListRes.result.data;
|
||
originalMenuList.map((item, index) => {
|
||
let oldMenuItem = oldMenuList.find((item2, index2, arr2) => {
|
||
return item2.menu_id === item.menu_id;
|
||
});
|
||
if (!oldMenuItem) {
|
||
addMenuList.push({
|
||
...item,
|
||
create_date: undefined
|
||
});
|
||
}
|
||
});
|
||
if (addMenuList && addMenuList.length > 0) {
|
||
// 添加没有的菜单
|
||
let addRes = await db.collection("opendb-admin-menus").add(addMenuList);
|
||
uni.showToast({
|
||
title:`新增了${addRes.result.inserted}个菜单,即将刷新`,
|
||
icon:"none"
|
||
})
|
||
setTimeout(() => {
|
||
// #ifdef H5
|
||
window.location.reload();
|
||
// #endif
|
||
// #ifndef H5
|
||
this.loadData(true);
|
||
// #endif
|
||
}, 300);
|
||
} else {
|
||
uni.showToast({
|
||
title:"菜单无变动",
|
||
icon:"none"
|
||
})
|
||
}
|
||
} catch(err) {
|
||
console.error(err)
|
||
} finally {
|
||
uni.hideLoading();
|
||
}
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
<style>
|
||
/* #ifndef H5 */
|
||
page {
|
||
padding-top: 85px;
|
||
}
|
||
|
||
/* #endif */
|
||
.menu-disable {
|
||
color: red;
|
||
}
|
||
|
||
.menu-badge {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 5px;
|
||
}
|
||
</style>
|