Some checks failed
CI / init (pull_request) Has been cancelled
CI / Frontend node 18.16.0 (pull_request) Has been cancelled
CI / Backend go (1.22) (pull_request) Has been cancelled
CI / release-pr (pull_request) Has been cancelled
CI / devops-test (1.22, 18.16.0) (pull_request) Has been cancelled
CI / release-please (pull_request) Has been cancelled
CI / devops-prod (1.22, 18.x) (pull_request) Has been cancelled
CI / docker (pull_request) Has been cancelled
158 lines
4.7 KiB
JavaScript
158 lines
4.7 KiB
JavaScript
/**
|
||
* 书籍后台管理菜单角色授权脚本
|
||
*
|
||
* 使用方式:
|
||
* 1. 先执行 01-create-book-admin-menus.browser-console.js。
|
||
* 2. 登录后台管理前端,打开浏览器控制台。
|
||
* 3. 如需给多个角色授权,先设置角色 ID:
|
||
* window.__XUANZHI_BOOK_AUTHORITY_IDS__ = [888, 8881]
|
||
* 4. 如前端接口代理不是 /api,先执行:
|
||
* window.__XUANZHI_BASE_API__ = 'http://127.0.0.1:8888'
|
||
* 5. 粘贴本文件全部内容并执行。
|
||
*
|
||
* 说明:
|
||
* - /menu/addMenuAuthority 是全量覆盖角色菜单。
|
||
* - 本脚本会先读取角色已有菜单,再合并书籍菜单,避免覆盖原有权限。
|
||
*/
|
||
(async () => {
|
||
const BASE_API = window.__XUANZHI_BASE_API__ || '/api'
|
||
const TOKEN = window.__XUANZHI_TOKEN__ || localStorage.getItem('token') || getCookie('x-token')
|
||
const AUTHORITY_IDS = window.__XUANZHI_BOOK_AUTHORITY_IDS__ || [888]
|
||
const TARGET_MENU_NAMES = [
|
||
'book',
|
||
'bookManage',
|
||
'bookChapterManage',
|
||
'bookAuthorManage',
|
||
'bookSeriesManage',
|
||
'bookCommentManage',
|
||
'bookReadRecordManage',
|
||
'bookFavoriteRecordManage',
|
||
'bookCommentLikeRecordManage'
|
||
]
|
||
let userId = window.__XUANZHI_USER_ID__ || localStorage.getItem('userId') || ''
|
||
|
||
if (!TOKEN) {
|
||
throw new Error('未找到 token,请先登录后台,或手动设置 window.__XUANZHI_TOKEN__')
|
||
}
|
||
if (!Array.isArray(AUTHORITY_IDS) || AUTHORITY_IDS.length === 0) {
|
||
throw new Error('请设置 window.__XUANZHI_BOOK_AUTHORITY_IDS__,例如:[888, 8881]')
|
||
}
|
||
|
||
userId = userId || await getCurrentUserId()
|
||
|
||
const allMenus = flattenMenus(await getBaseMenuTree())
|
||
const targetMenus = TARGET_MENU_NAMES.map((name) => {
|
||
const menu = allMenus.find((item) => item.name === name)
|
||
if (!menu) throw new Error(`未找到菜单 ${name},请先执行创建菜单脚本。`)
|
||
return menu
|
||
})
|
||
|
||
for (const authorityId of AUTHORITY_IDS) {
|
||
const currentMenus = await getMenuAuthority(authorityId)
|
||
const merged = mergeMenusById(currentMenus, targetMenus)
|
||
await apiPost('/menu/addMenuAuthority', {
|
||
authorityId,
|
||
menus: merged
|
||
})
|
||
console.log(`[authority] ${authorityId} 已授权书籍后台菜单,合并后菜单数量:${merged.length}`)
|
||
}
|
||
|
||
console.log('书籍后台菜单角色授权完成。刷新页面或重新登录后可查看菜单。')
|
||
|
||
async function getBaseMenuTree() {
|
||
const res = await apiPost('/menu/getBaseMenuTree', {})
|
||
return res.data?.menus || []
|
||
}
|
||
|
||
async function getMenuAuthority(authorityId) {
|
||
const res = await apiPost('/menu/getMenuAuthority', { authorityId })
|
||
return res.data?.menus || []
|
||
}
|
||
|
||
function mergeMenusById(currentMenus, targetMenus) {
|
||
const map = new Map()
|
||
;[...(currentMenus || []), ...(targetMenus || [])].forEach((item) => {
|
||
const id = getMenuId(item)
|
||
if (!id) return
|
||
map.set(Number(id), normalizeMenu(item))
|
||
})
|
||
return [...map.values()]
|
||
}
|
||
|
||
function normalizeMenu(item) {
|
||
return {
|
||
ID: getMenuId(item),
|
||
path: item.path,
|
||
name: item.name,
|
||
hidden: item.hidden,
|
||
parentId: item.parentId,
|
||
component: item.component,
|
||
sort: item.sort,
|
||
meta: item.meta,
|
||
parameters: item.parameters || [],
|
||
menuBtn: item.menuBtn || []
|
||
}
|
||
}
|
||
|
||
async function getCurrentUserId() {
|
||
try {
|
||
const res = await apiGet('/user/getUserInfo')
|
||
return res.data?.userInfo?.ID || res.data?.userInfo?.id || ''
|
||
} catch (error) {
|
||
console.warn('读取当前用户 ID 失败,将不带 x-user-id 调用菜单接口。必要时可手动设置 window.__XUANZHI_USER_ID__。', error)
|
||
return ''
|
||
}
|
||
}
|
||
|
||
async function apiGet(path) {
|
||
return apiRequest(path, { method: 'GET' })
|
||
}
|
||
|
||
async function apiPost(path, data) {
|
||
return apiRequest(path, {
|
||
method: 'POST',
|
||
body: JSON.stringify(data)
|
||
})
|
||
}
|
||
|
||
async function apiRequest(path, options = {}) {
|
||
const res = await fetch(`${BASE_API}${path}`, {
|
||
...options,
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
'x-token': TOKEN,
|
||
'x-user-id': String(userId || ''),
|
||
...(options.headers || {})
|
||
}
|
||
})
|
||
const json = await res.json().catch(() => ({}))
|
||
if (!res.ok || json.code !== 0) {
|
||
throw new Error(`${path} 调用失败:${json.msg || res.statusText}`)
|
||
}
|
||
return json
|
||
}
|
||
|
||
function flattenMenus(list) {
|
||
const result = []
|
||
const walk = (items) => {
|
||
;(items || []).forEach((item) => {
|
||
result.push(item)
|
||
walk(item.children)
|
||
})
|
||
}
|
||
walk(list)
|
||
return result
|
||
}
|
||
|
||
function getMenuId(menu) {
|
||
return menu?.ID || menu?.id || menu?.menuId
|
||
}
|
||
|
||
function getCookie(name) {
|
||
return document.cookie
|
||
.split('; ')
|
||
.find((row) => row.startsWith(`${name}=`))
|
||
?.split('=')[1] || ''
|
||
}
|
||
})()
|