feat(需求): ①创建/编辑用户菜单权限勾选父菜单自动全选子菜单(el-tree去check-strictly走父子联动,提交前压缩为最小直接授权集,父勾选时后代不重复落库) ②修复管理员勾选店铺数据菜单仍提示'无权查看店铺数据任务'——admin分区授权后若有效授权含店铺数据任务/重复检查菜单(含经'店铺管理'分组展开),自动补内部数据权限 admin_shop_data_crawl_task_data(镜像V84惯例只增不删,回收仍走超管数据范围授权UI)
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import type { MenuOptionNode } from './user-menu-auth'
|
||||
import { compactDirectGrantIds } from './user-menu-auth'
|
||||
import { fetchGrantableMenus } from './user-menu-auth-api'
|
||||
|
||||
const props = defineProps<{ checked?: number[] }>()
|
||||
@@ -32,7 +33,10 @@ function onCheck(): void {
|
||||
return (Array.isArray(keys) ? keys : []).map((key) => Number(key)).filter((id) => id > 0)
|
||||
}
|
||||
const ids = [...collect(adminTree.value), ...collect(appTree.value)]
|
||||
emit('update:checked', ids)
|
||||
// 父子联动模式(未启用严格勾选)下勾选父菜单会自动全选全部子菜单;
|
||||
// 提交前压缩为最小直接授权集:父级已勾选时后代不再重复提交(后端按“父级授权
|
||||
// 展开全部后代”判断权限,与旧 admin.html 勾父清后代语义一致)。
|
||||
emit('update:checked', compactDirectGrantIds(ids, data.value))
|
||||
}
|
||||
|
||||
async function load(): Promise<void> {
|
||||
@@ -62,12 +66,13 @@ watch(
|
||||
<div class="menu-group-columns">
|
||||
<div v-if="adminNodes.length" class="menu-group-block">
|
||||
<div class="menu-group-title">后台</div>
|
||||
<!-- 父子联动模式(未启用严格勾选):勾选父菜单自动全选全部子菜单,
|
||||
勾父即代表全部后代,与后端“父级授权展开后代”的权限语义一致。 -->
|
||||
<el-tree
|
||||
ref="adminTree"
|
||||
:data="adminNodes"
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
default-expand-all
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
@check="onCheck"
|
||||
@@ -80,7 +85,6 @@ watch(
|
||||
:data="appNodes"
|
||||
node-key="id"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
default-expand-all
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
@check="onCheck"
|
||||
|
||||
@@ -105,3 +105,29 @@ export function normalizeMenuIds(raw: unknown): number[] {
|
||||
export function buildUserColumnPermissionPayload(raw: unknown): { columnIds: number[] } {
|
||||
return { columnIds: normalizeMenuIds(raw) }
|
||||
}
|
||||
|
||||
/**
|
||||
* 把权限树勾选 id 压缩为后端意义上的“直接授权最小集”:父级已勾选时后代无需
|
||||
* 重复提交(后端读取/校验时父级授权会向全部后代展开,与旧 admin.html“勾父清
|
||||
* 后代”语义一致,避免 user_column_permission 存储冗余行)。未勾选父级但单独
|
||||
* 勾选子级的部分授权原样保留。
|
||||
*/
|
||||
export function compactDirectGrantIds(checkedIds: unknown, tree: MenuOptionNode[]): number[] {
|
||||
const ids = normalizeMenuIds(checkedIds)
|
||||
if (ids.length === 0 || tree.length === 0) return ids
|
||||
const idSet = new Set(ids)
|
||||
/** 因“有已勾选祖先”而被父级覆盖、无需直接授权的 id。 */
|
||||
const coveredByAncestor = new Set<number>()
|
||||
const walk = (nodes: MenuOptionNode[], ancestorChecked: boolean): void => {
|
||||
for (const node of nodes) {
|
||||
if (ancestorChecked) {
|
||||
coveredByAncestor.add(node.id)
|
||||
if (node.children?.length) walk(node.children, true)
|
||||
continue
|
||||
}
|
||||
if (node.children?.length) walk(node.children, idSet.has(node.id))
|
||||
}
|
||||
}
|
||||
walk(tree, false)
|
||||
return ids.filter((id) => !coveredByAncestor.has(id))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user