Commit 76653b9a by wuqian

feat:权限相关bug修复

parent db9ed506
...@@ -77,7 +77,8 @@ import { ...@@ -77,7 +77,8 @@ import {
} from 'vue' } from 'vue'
import type { CustomColumn } from '@/types/table' import type { CustomColumn } from '@/types/table'
import RenderColumn from './RenderColumn.vue' import RenderColumn from './RenderColumn.vue'
import { ElTable } from 'element-plus' // 此处必须注释掉,不然使用该组件的表格样式会错乱
// import { ElTable } from 'element-plus'
import type { TableInstance } from 'element-plus' import type { TableInstance } from 'element-plus'
const tableRef = shallowRef<TableInstance>() const tableRef = shallowRef<TableInstance>()
......
<template> <template>
<div class="page_main h-100" :class="{ mobile: isMobile }"> <div class="page_main h-100">
<!-- 头部插槽 --> <!-- 头部插槽 -->
<div v-if="$slots.header" class="page_header"> <div v-if="$slots.header" class="page_header">
<slot name="header" /> <slot name="header" />
...@@ -41,7 +41,6 @@ ...@@ -41,7 +41,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, useAttrs } from 'vue' import { ref, computed, useAttrs } from 'vue'
import type { CSSProperties } from 'vue' import type { CSSProperties } from 'vue'
import { isMobile } from '@/utils/index.ts'
const props = defineProps({ const props = defineProps({
isleftshow: { isleftshow: {
type: Boolean, type: Boolean,
......
...@@ -181,6 +181,7 @@ export function buildDynamicRoutes( ...@@ -181,6 +181,7 @@ export function buildDynamicRoutes(
if (!loader) continue if (!loader) continue
records.push({ records.push({
path: p, path: p,
name: p,
// 使用异步加载的 component // 使用异步加载的 component
component: loader as unknown as RouteComponent, component: loader as unknown as RouteComponent,
meta: { title }, meta: { title },
......
...@@ -88,10 +88,8 @@ router.beforeEach( ...@@ -88,10 +88,8 @@ router.beforeEach(
return return
} }
// 把 getSysPermissionApi 从 afterEach 整体挪到 beforeEach 并 await: // 把 getSysPermissionApi 放到 beforeEach 并 await:
// 保证组件 onMounted 触发 getTreeNum 时,reqMenu.buttons 已就绪, // 进入新页面前先把 buttons 清空,避免上一页按钮残留。
// 避免 children 一上来就被 roleShow 一次性过滤成空数组。
// 进入新页面前先把 buttons 清空,避免上一页按钮残留(option 4 思路)。
if (token && to.path !== '/user/login') { if (token && to.path !== '/user/login') {
const reqMenu = (to.meta?.permissionRoute as string) || to.path const reqMenu = (to.meta?.permissionRoute as string) || to.path
if (reqMenu && reqMenu !== pendingReqMenu) { if (reqMenu && reqMenu !== pendingReqMenu) {
......
...@@ -33,7 +33,7 @@ const useMenuTreeStore = defineStore('menuTree', () => { ...@@ -33,7 +33,7 @@ const useMenuTreeStore = defineStore('menuTree', () => {
const setMenuTree = (data: MenuTreeNode[]) => { const setMenuTree = (data: MenuTreeNode[]) => {
menusTree.value = data menusTree.value = data
writeToStorage(data) // writeToStorage(data) // 这里不写入本地存储,避免覆盖 menusTreeNoBtn
} }
const clearMenuTree = () => { const clearMenuTree = () => {
......
.el-table thead { .el-table thead {
color: #333;
font-size: 14px; font-size: 14px;
height: 50px; height: 50px;
min-height: 50px; min-height: 50px;
} }
.el-table .cell {
white-space: nowrap;
}
.el-table thead th.el-table__cell { .el-table thead th.el-table__cell {
color: #333;
background-color: #fafafa; background-color: #fafafa;
} }
.el-table .cell {
white-space: nowrap;
}
.el-table__row { .el-table__row {
height: 40px; height: 40px;
min-height: 40px; min-height: 40px;
......
...@@ -64,8 +64,4 @@ export function downloadByUrl(url: string, filename?: string) { ...@@ -64,8 +64,4 @@ export function downloadByUrl(url: string, filename?: string) {
document.body.appendChild(a) document.body.appendChild(a)
a.click() a.click()
document.body.removeChild(a) document.body.removeChild(a)
} }
export const isMobile: boolean = \ No newline at end of file
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i.test(
navigator.userAgent,
)
\ No newline at end of file
...@@ -135,23 +135,34 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) { ...@@ -135,23 +135,34 @@ export function useOrderStatusTree(options: UseOrderStatusTreeOptions) {
const loadStatusTreeCounts = async () => { const loadStatusTreeCounts = async () => {
try { try {
const res = await getPodOrderStateGroupListApi() const res = await getPodOrderStateGroupListApi()
const GROUP_MARK = new Set(['PICKING']) // "配货中—" 这样的“分组容器”要自己加 const GROUP_MARK = new Set(['PICKING']) // 所有分组容器
const filterTree = (nodes: StatusTreeNode[]): StatusTreeNode[] => { const filterTree = (nodes: StatusTreeNode[]): StatusTreeNode[] => {
const result: StatusTreeNode[] = [] const result: StatusTreeNode[] = []
for (const node of nodes) { for (const node of nodes) {
if (GROUP_MARK.has(node.status) || roleShow(node.status)) { let filteredChildren: StatusTreeNode[] = []
const newNode = { ...node } if (node.children) {
if (newNode.children) { filteredChildren = filterTree(node.children)
newNode.children = filterTree(newNode.children) }
} const hasPermission = roleShow(node.status)
result.push(newNode) const isGroupContainer = GROUP_MARK.has(node.status)
if (
hasPermission ||
(isGroupContainer && filteredChildren.length > 0)
) {
result.push({ ...node, children: filteredChildren })
} }
} }
return result return result
} }
const result = filterTree(res.data) // 递归过滤掉没有权限的节点
const filteredData = filterTree(res.data)
statusTree.value = [ statusTree.value = [
{ status: 'ALL', statusName: '全部', quantity: 0, children: result }, {
status: 'ALL',
statusName: '全部',
quantity: 0,
children: filteredData,
},
] ]
nextTick(() => { nextTick(() => {
treeRef.value?.setCurrentKey(status.value, true) treeRef.value?.setCurrentKey(status.value, true)
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
padding: 10px 10px 0; padding: 10px 10px 0;
" "
> >
<div> <div v-if="currentRole.name" style="line-height: 40px">
<span style="font-weight: bold; font-size: 18px"> 角色名称: </span> <span style="font-weight: bold; font-size: 18px"> 角色名称: </span>
<span style="font-weight: bold; font-size: 20px; color: red"> <span style="font-weight: bold; font-size: 20px; color: red">
{{ currentRole.name }} {{ currentRole.name }}
...@@ -51,6 +51,7 @@ ...@@ -51,6 +51,7 @@
<div> <div>
<ElButton <ElButton
v-if="activeRoleId != '1'" v-if="activeRoleId != '1'"
:disabled="!currentRole.name"
type="primary" type="primary"
@click="editRole()" @click="editRole()"
> >
...@@ -58,6 +59,7 @@ ...@@ -58,6 +59,7 @@
</ElButton> </ElButton>
<ElButton <ElButton
v-if="activeRoleId != '1'" v-if="activeRoleId != '1'"
:disabled="!currentRole.name"
type="success" type="success"
@click="copyRole()" @click="copyRole()"
> >
...@@ -65,16 +67,28 @@ ...@@ -65,16 +67,28 @@
</ElButton> </ElButton>
<ElButton <ElButton
v-if="activeRoleId != '1'" v-if="activeRoleId != '1'"
:disabled="!currentRole.name"
type="danger" type="danger"
@click="removeRole()" @click="removeRole()"
> >
删除 删除
</ElButton> </ElButton>
<ElButton type="primary" @click="addRole()"> 添加 </ElButton> <ElButton type="primary" @click="addRole()"> 添加 </ElButton>
<ElButton type="success" @click="saveSelect()"> 保存 </ElButton> <ElButton
:disabled="!currentRole.name"
type="success"
@click="saveSelect()"
>
保存
</ElButton>
</div> </div>
</div> </div>
<ElTabs v-model="tabActive" @tab-click="tabClick"> <div v-if="!currentRole.name" class="no_role_tip">
<span style="color: red; font-weight: bold; font-size: 18px"
>请先添加角色吧!</span
>
</div>
<ElTabs v-else v-model="tabActive" @tab-click="tabClick">
<ElTabPane label="功能权限" name="second"> <ElTabPane label="功能权限" name="second">
<div <div
style=" style="
...@@ -458,11 +472,27 @@ function syncNodeCheckState(node: MenuTreeNode) { ...@@ -458,11 +472,27 @@ function syncNodeCheckState(node: MenuTreeNode) {
const anyIndeter = children.some((c) => c.indeter) const anyIndeter = children.some((c) => c.indeter)
node.indeter = (childSelectedCount > 0 && !allSelected) || anyIndeter node.indeter = (childSelectedCount > 0 && !allSelected) || anyIndeter
const idx = menuCheckList.value.indexOf(node.id) const idx = menuCheckList.value.indexOf(node.id)
if (allSelected && !anyIndeter) { if (childSelectedCount > 0) {
// 只要有任一子节点被选中,父节点 id 也应进入 checklist(视觉上仍由 indeter 决定半/全勾)
if (idx === -1) menuCheckList.value.push(node.id) if (idx === -1) menuCheckList.value.push(node.id)
} else if (childSelectedCount === 0 && !anyIndeter) { } else {
// 没有任何子节点被选中,移除父节点 id
if (idx !== -1) menuCheckList.value.splice(idx, 1) if (idx !== -1) menuCheckList.value.splice(idx, 1)
node.indeter = false
}
}
function findMenuNode(id: number | string): MenuTreeNode | undefined {
const walk = (list?: MenuTreeNode[]): MenuTreeNode | undefined => {
if (!list) return undefined
for (const node of list) {
if (node.id === id) return node
const found = walk(node.children)
if (found) return found
}
return undefined
} }
return walk(menuList.value)
} }
function updateParentsIndeter( function updateParentsIndeter(
...@@ -496,6 +526,8 @@ function selectChecked(item: MenuTreeNode) { ...@@ -496,6 +526,8 @@ function selectChecked(item: MenuTreeNode) {
} }
} }
walk(item.children) walk(item.children)
// 同步当前节点的 id 和 indeterminate
syncNodeCheckState(item)
// 更新父级 indeterminate // 更新父级 indeterminate
updateParentsIndeter(menuList.value, item) updateParentsIndeter(menuList.value, item)
} }
...@@ -512,16 +544,20 @@ function clearChecked(item: MenuTreeNode) { ...@@ -512,16 +544,20 @@ function clearChecked(item: MenuTreeNode) {
} }
} }
walk(item.children) walk(item.children)
// 同步当前节点的 id 和 indeterminate
syncNodeCheckState(item)
updateParentsIndeter(menuList.value, item) updateParentsIndeter(menuList.value, item)
} }
function checkChange(item: MenuTreeNode, value: boolean | string | number) { function checkChange(item: MenuTreeNode, value: boolean | string | number) {
if (value || item.indeter) { // 一级传入的是合成对象,需查找真实树节点,避免只改到临时对象的 indeter
selectChecked(item) const target = findMenuNode(item.id) ?? item
if (value || target.indeter) {
selectChecked(target)
} else { } else {
clearChecked(item) clearChecked(target)
} }
item.indeter = false target.indeter = false
} }
async function saveSelect() { async function saveSelect() {
...@@ -552,7 +588,7 @@ async function getActiveData() { ...@@ -552,7 +588,7 @@ async function getActiveData() {
const res = await getRolePermsApi(activeRoleId.value) const res = await getRolePermsApi(activeRoleId.value)
if (res.code == 200) { if (res.code == 200) {
menuCheckList.value = (res.data?.menuIds as number[]) || [] menuCheckList.value = (res.data?.menuIds as number[]) || []
checkMenus(menuCheckList.value, menuList.value) checkMenus(menuList.value)
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e)
...@@ -560,20 +596,12 @@ async function getActiveData() { ...@@ -560,20 +596,12 @@ async function getActiveData() {
await getUsers() await getUsers()
} }
function checkMenus(checklist: number[], menulist: MenuTreeNode[]) { function checkMenus(menulist: MenuTreeNode[]) {
const check = (list: MenuTreeNode[]) => { const check = (list: MenuTreeNode[]) => {
list.forEach((item) => { list.forEach((item) => {
if (item.children && item.children.length > 0) { if (item.children && item.children.length > 0) {
const hasSelectedChild = item.children.some((c) =>
checklist.includes(c.id),
)
if (!hasSelectedChild) {
const index = checklist.indexOf(item.id)
if (index !== -1) {
checklist.splice(index, 1)
}
}
check(item.children) check(item.children)
syncNodeCheckState(item)
} }
}) })
} }
...@@ -619,6 +647,12 @@ async function getList(role: RoleItem | null = null) { ...@@ -619,6 +647,12 @@ async function getList(role: RoleItem | null = null) {
currentActiveIndex >= 0 ? String(currentActiveIndex + 1) : '1' currentActiveIndex >= 0 ? String(currentActiveIndex + 1) : '1'
await getActiveData() await getActiveData()
} }
if (groupLists.value.length === 0) {
activeRoleId.value = ''
currentRole.value = {} as RoleItem
menuCheckList.value = []
userLists.value = []
}
return res return res
} catch (err) { } catch (err) {
console.log(err) console.log(err)
...@@ -687,6 +721,12 @@ onMounted(() => { ...@@ -687,6 +721,12 @@ onMounted(() => {
height: 100%; height: 100%;
display: flex; display: flex;
} }
.no_role_tip {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.menu_name { .menu_name {
display: inline-block; display: inline-block;
min-width: 160px; min-width: 160px;
......
...@@ -388,9 +388,10 @@ const save = async () => { ...@@ -388,9 +388,10 @@ const save = async () => {
const roleIdsStr = Array.isArray(editForm.value.roleIds) const roleIdsStr = Array.isArray(editForm.value.roleIds)
? editForm.value.roleIds.join(',') ? editForm.value.roleIds.join(',')
: editForm.value.roleIds : editForm.value.roleIds
const hasSuper = Array.isArray(editForm.value.roleIds) const hasSuper = getRoleName(editForm.value.roleIds)
? editForm.value.roleIds.includes(1) .split(',')
: editForm.value.roleIds.split(',').map(Number).includes(1) .filter(Boolean)
.includes('超级管理员')
try { try {
if (!editId.value) { if (!editId.value) {
await addUserApi({ await addUserApi({
......
...@@ -138,13 +138,16 @@ ...@@ -138,13 +138,16 @@
></ElPagination> ></ElPagination>
</template> </template>
<template #bottom> <template #bottom>
<StockingApplyOrderDetailTabs :selected-row="selectedRow" /> <StockingApplyOrderDetailTabs :selected-row="selectedRow ?? null" />
</template> </template>
</SplitDiv> </SplitDiv>
</div> </div>
</div> </div>
<!-- 提交入库弹框 --> <!-- 提交入库弹框 -->
<SubmitWarehousingDialog ref="submitWarehousingDialogRef" @refresh="onRefresh" /> <SubmitWarehousingDialog
ref="submitWarehousingDialogRef"
@refresh="onRefresh"
/>
</div> </div>
</template> </template>
<script setup lang="tsx"> <script setup lang="tsx">
...@@ -168,7 +171,8 @@ const treeData = ref<TreeData[]>([]) ...@@ -168,7 +171,8 @@ const treeData = ref<TreeData[]>([])
const treeRef = ref() const treeRef = ref()
const tableRef = ref() const tableRef = ref()
const selectedRow = ref<TableData | null>(null) const selectedRow = ref<TableData | null>(null)
const submitWarehousingDialogRef = ref<InstanceType<typeof SubmitWarehousingDialog>>() const submitWarehousingDialogRef =
ref<InstanceType<typeof SubmitWarehousingDialog>>()
const [searchForm, resetSearchForm] = useValue<SearchForm>({} as SearchForm) const [searchForm, resetSearchForm] = useValue<SearchForm>({} as SearchForm)
const status = ref<string>('TO_BE_RECEIPT') const status = ref<string>('TO_BE_RECEIPT')
const warehouseList = ref<WarehouseListData[]>([]) const warehouseList = ref<WarehouseListData[]>([])
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment