From 397dbcfdff999ff0ca24e35fa114db02db2da998 Mon Sep 17 00:00:00 2001 From: bunny <1319900154@qq.com> Date: Tue, 5 Nov 2024 02:07:20 +0800 Subject: [PATCH] =?UTF-8?q?optimize:=20=E2=99=BB=EF=B8=8F=20=E6=B7=BB?= =?UTF-8?q?=E5=8A=A0=E6=9D=83=E9=99=90=E9=AA=8C=E8=AF=81=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=A1=A8=E6=A0=BCbug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/v1/system/dept.ts | 2 +- src/components/ReVxeTableBar/index.ts | 5 + src/components/ReVxeTableBar/src/bar.tsx | 266 +++++++++++++++++++ src/components/TableBar/src/bar.tsx | 4 +- src/plugins/i18n.ts | 15 +- src/router/utils.ts | 4 + src/views/system/adminUser/index.vue | 102 +++---- src/views/system/adminUser/utils/auth.ts | 18 ++ src/views/system/adminUser/utils/columns.tsx | 2 +- src/views/system/adminUser/utils/hooks.tsx | 4 +- src/views/system/dept/index.vue | 59 ++-- src/views/system/dept/utils/auth.ts | 10 + src/views/system/dept/utils/columns.ts | 2 +- src/views/system/files/index.vue | 56 ++-- src/views/system/files/utils/auth.ts | 14 + src/views/system/files/utils/columns.ts | 2 +- src/views/system/files/utils/hooks.tsx | 4 +- src/views/system/menu/index.vue | 53 ++-- src/views/system/menu/menu-dialog.vue | 19 +- src/views/system/menu/utils/auth.ts | 20 ++ src/views/system/menu/utils/columns.tsx | 23 +- src/views/system/menu/utils/hooks.tsx | 6 +- src/views/system/power/index.vue | 50 ++-- src/views/system/power/utils/auth.ts | 14 + src/views/system/power/utils/columns.ts | 2 +- src/views/system/role/index.vue | 42 +-- src/views/system/role/utils/auth.ts | 12 + src/views/system/role/utils/columns.ts | 2 +- 28 files changed, 611 insertions(+), 201 deletions(-) create mode 100644 src/components/ReVxeTableBar/index.ts create mode 100644 src/components/ReVxeTableBar/src/bar.tsx create mode 100644 src/views/system/adminUser/utils/auth.ts create mode 100644 src/views/system/dept/utils/auth.ts create mode 100644 src/views/system/files/utils/auth.ts create mode 100644 src/views/system/menu/utils/auth.ts create mode 100644 src/views/system/power/utils/auth.ts create mode 100644 src/views/system/role/utils/auth.ts diff --git a/src/api/v1/system/dept.ts b/src/api/v1/system/dept.ts index af41d03..4f319c2 100644 --- a/src/api/v1/system/dept.ts +++ b/src/api/v1/system/dept.ts @@ -8,7 +8,7 @@ export const fetchGetDeptList = (data: any) => { /** 部门管理---获取所有部门管理列表 */ export const fetchGetAllDeptList = () => { - return http.request>('get', 'dept/getAllDeptList'); + return http.request>('get', 'dept/noManage/getAllDeptList'); }; /** 部门管理---添加部门管理 */ diff --git a/src/components/ReVxeTableBar/index.ts b/src/components/ReVxeTableBar/index.ts new file mode 100644 index 0000000..2615644 --- /dev/null +++ b/src/components/ReVxeTableBar/index.ts @@ -0,0 +1,5 @@ +import vxeTableBar from './src/bar'; +import { withInstall } from '@pureadmin/utils'; + +/** 配合 `vxe-table` 实现快速便捷的表格操作 */ +export const VxeTableBar = withInstall(vxeTableBar); diff --git a/src/components/ReVxeTableBar/src/bar.tsx b/src/components/ReVxeTableBar/src/bar.tsx new file mode 100644 index 0000000..bc31cf6 --- /dev/null +++ b/src/components/ReVxeTableBar/src/bar.tsx @@ -0,0 +1,266 @@ +import Sortable from 'sortablejs'; +import { transformI18n } from '@/plugins/i18n'; +import { useEpThemeStoreHook } from '@/store/modules/epTheme'; +import { cloneDeep, delay, getKeyList } from '@pureadmin/utils'; +import { computed, defineComponent, getCurrentInstance, nextTick, type PropType, ref, unref } from 'vue'; +import Fullscreen from '@iconify-icons/ri/fullscreen-fill'; +import ExitFullscreen from '@iconify-icons/ri/fullscreen-exit-fill'; +import DragIcon from '@/assets/table-bar/drag.svg?component'; +import ExpandIcon from '@/assets/table-bar/expand.svg?component'; +import RefreshIcon from '@/assets/table-bar/refresh.svg?component'; +import SettingIcon from '@/assets/table-bar/settings.svg?component'; +import CollapseIcon from '@/assets/table-bar/collapse.svg?component'; + +const props = { + /** 头部最左边的标题 */ + title: { + type: String, + default: '列表', + }, + vxeTableRef: { + type: Object as PropType, + }, + /** 需要展示的列 */ + columns: { + type: Array as PropType, + default: () => [], + }, + /** 是否为树列表 */ + tree: { + type: Boolean, + default: false, + }, + isExpandAll: { + type: Boolean, + default: true, + }, + tableKey: { + type: [String, Number] as PropType, + default: '0', + }, +}; + +export default defineComponent({ + name: 'VxeTableBar', + props, + emits: ['refresh', 'fullscreen'], + setup(props, { emit, slots, attrs }) { + const size = ref('small'); + const loading = ref(false); + const checkAll = ref(true); + const isFullscreen = ref(false); + const isIndeterminate = ref(false); + const instance = getCurrentInstance()!; + const isExpandAll = ref(props.isExpandAll); + let checkColumnList = getKeyList(cloneDeep(props?.columns), 'title'); + const checkedColumns = ref(getKeyList(cloneDeep(props?.columns), 'title')); + const dynamicColumns = ref(cloneDeep(props?.columns)); + + const getDropdownItemStyle = computed(() => { + return s => { + return { + background: s === size.value ? useEpThemeStoreHook().epThemeColor : '', + color: s === size.value ? '#fff' : 'var(--el-text-color-primary)', + }; + }; + }); + + const iconClass = computed(() => { + return ['text-black', 'dark:text-white', 'duration-100', 'hover:!text-primary', 'cursor-pointer', 'outline-none']; + }); + + const topClass = computed(() => { + return ['flex', 'justify-between', 'pt-[3px]', 'px-[11px]', 'border-b-[1px]', 'border-solid', 'border-[#dcdfe6]', 'dark:border-[#303030]']; + }); + + function onReFresh() { + loading.value = true; + emit('refresh'); + delay(500).then(() => (loading.value = false)); + } + + function onExpand() { + isExpandAll.value = !isExpandAll.value; + isExpandAll.value ? props.vxeTableRef.setAllTreeExpand(true) : props.vxeTableRef.clearTreeExpand(); + props.vxeTableRef.refreshColumn(); + } + + function onFullscreen() { + isFullscreen.value = !isFullscreen.value; + emit('fullscreen', isFullscreen.value); + } + + function reloadColumn() { + const curCheckedColumns = cloneDeep(dynamicColumns.value).filter(item => checkedColumns.value.includes(item.title)); + props.vxeTableRef.reloadColumn(curCheckedColumns); + } + + function handleCheckAllChange(val: boolean) { + checkedColumns.value = val ? checkColumnList : []; + isIndeterminate.value = false; + reloadColumn(); + } + + function handleCheckedColumnsChange(value: string[]) { + checkedColumns.value = value; + const checkedCount = value.length; + checkAll.value = checkedCount === checkColumnList.length; + isIndeterminate.value = checkedCount > 0 && checkedCount < checkColumnList.length; + } + + async function onReset() { + checkAll.value = true; + isIndeterminate.value = false; + dynamicColumns.value = cloneDeep(props?.columns); + checkColumnList = []; + checkColumnList = getKeyList(cloneDeep(props?.columns), 'title'); + checkedColumns.value = getKeyList(cloneDeep(props?.columns), 'title'); + props.vxeTableRef.refreshColumn(); + } + + function changeSize(curSize: string) { + size.value = curSize; + props.vxeTableRef.refreshColumn(); + } + + const dropdown = { + dropdown: () => ( + + changeSize('medium')}> + 宽松 + + changeSize('small')}> + 默认 + + changeSize('mini')}> + 紧凑 + + + ), + }; + + /** 列展示拖拽排序 */ + const rowDrop = (event: { preventDefault: () => void }) => { + event.preventDefault(); + nextTick(() => { + const wrapper: HTMLElement = (instance?.proxy?.$refs[`VxeGroupRef${unref(props.tableKey)}`] as any).$el.firstElementChild; + Sortable.create(wrapper, { + animation: 300, + handle: '.drag-btn', + onEnd: ({ newIndex, oldIndex, item }) => { + const targetThElem = item; + const wrapperElem = targetThElem.parentNode as HTMLElement; + const oldColumn = dynamicColumns.value[oldIndex]; + const newColumn = dynamicColumns.value[newIndex]; + if (oldColumn?.fixed || newColumn?.fixed) { + // 当前列存在fixed属性 则不可拖拽 + const oldThElem = wrapperElem.children[oldIndex] as HTMLElement; + if (newIndex > oldIndex) { + wrapperElem.insertBefore(targetThElem, oldThElem); + } else { + wrapperElem.insertBefore(targetThElem, oldThElem ? oldThElem.nextElementSibling : oldThElem); + } + return; + } + const currentRow = dynamicColumns.value.splice(oldIndex, 1)[0]; + dynamicColumns.value.splice(newIndex, 0, currentRow); + reloadColumn(); + }, + }); + }); + }; + + const isFixedColumn = (title: string) => { + return dynamicColumns.value.filter(item => transformI18n(item.title) === transformI18n(title))[0].fixed; + }; + + const rendTippyProps = (content: string) => { + // https://vue-tippy.netlify.app/props + return { + content, + offset: [0, 18], + duration: [300, 0], + followCursor: true, + hideOnClick: 'toggle', + }; + }; + + const reference = { + reference: () => , + }; + + return () => ( + <> +
+
+ {slots?.title ? slots.title() :

{props.title}

} +
+ {slots?.buttons ?
{slots.buttons()}
: null} + {props.tree ? ( + <> + onExpand()} + /> + + + ) : null} + onReFresh()} /> + + + + + + + +
+ handleCheckAllChange(value)} /> + onReset()}> + 重置 + +
+ +
+ + handleCheckedColumnsChange(value)}> + + {checkColumnList.map((item, index) => { + return ( +
+ void }) => rowDrop(event)} /> + + + {transformI18n(item)} + + +
+ ); + })} +
+
+
+
+
+ + + onFullscreen()} + /> +
+
+ {slots.default({ + size: size.value, + dynamicColumns: dynamicColumns.value, + })} +
+ + ); + }, +}); diff --git a/src/components/TableBar/src/bar.tsx b/src/components/TableBar/src/bar.tsx index 7b54280..e9a31f6 100644 --- a/src/components/TableBar/src/bar.tsx +++ b/src/components/TableBar/src/bar.tsx @@ -110,7 +110,7 @@ export default defineComponent({ } function handleCheckColumnListChange(val: boolean, label: string) { - dynamicColumns.value.filter(item => $t(item.label) === $t(label))[0].hide = !val; + dynamicColumns.value.filter(item => item.label === label)[0].hide = !val; } function onReset() { @@ -169,7 +169,7 @@ export default defineComponent({ }; const isFixedColumn = (label: string) => { - return !!dynamicColumns.value.filter(item => item.label === label)[0].fixed; + return dynamicColumns.value.filter(item => item.label === label)[0].fixed; }; const rendTippyProps = (content: string) => ({ diff --git a/src/plugins/i18n.ts b/src/plugins/i18n.ts index 91ce8cd..bb2b8e2 100644 --- a/src/plugins/i18n.ts +++ b/src/plugins/i18n.ts @@ -1,6 +1,7 @@ // 多组件库的国际化和本地项目国际化兼容 import { createI18n } from 'vue-i18n'; -import type { App } from 'vue'; +import type { App, WritableComputedRef } from 'vue'; +import { isObject } from '@pureadmin/utils'; // ? 从本地存储中获取数据 const languageData = localStorage.getItem('i18nStore'); @@ -17,7 +18,7 @@ export const i18n = createI18n({ messages: languageData ? JSON.parse(languageData).i18n : {}, }); -/*const siphonI18n = (function () { +const siphonI18n = (function () { // 仅初始化一次国际化配置 let cache = Object.fromEntries( Object.entries(import.meta.glob('../../locales/!*.y(a)?ml', { eager: true })).map(([key, value]: any) => { @@ -30,7 +31,7 @@ export const i18n = createI18n({ }; })(); -/!** 获取对象中所有嵌套对象的key键,并将它们用点号分割组成字符串 *!/ +/** 获取对象中所有嵌套对象的key键,并将它们用点号分割组成字符串 */ function getObjectKeys(obj) { const stack = []; const keys: Set = new Set(); @@ -54,7 +55,7 @@ function getObjectKeys(obj) { return keys; } -/!** 将展开的key缓存 *!/ +/** 将展开的key缓存 */ const keysCache: Map> = new Map(); const flatI18n = (prefix = 'zh') => { let cache = keysCache.get(prefix); @@ -65,11 +66,11 @@ const flatI18n = (prefix = 'zh') => { return cache; }; -/!** +/** * 国际化转换工具函数(自动读取根目录locales文件夹下文件进行国际化匹配) * @param message message * @returns 转化后的message - *!/ + */ export function transformI18n(message: any = '') { if (!message) { return ''; @@ -91,7 +92,7 @@ export function transformI18n(message: any = '') { } else { return message; } -}*/ +} export const $t: any = (i18n.global as any).t as any; diff --git a/src/router/utils.ts b/src/router/utils.ts index 7a71066..ac4c219 100644 --- a/src/router/utils.ts +++ b/src/router/utils.ts @@ -312,6 +312,10 @@ function hasAuth(value: string | Array): boolean { /** 从当前路由的`meta`字段里获取按钮级别的所有自定义`code`值 */ const metaAuths = getAuths(); if (!metaAuths) return false; + // 管理员权限 + if (metaAuths.includes('*::*::*') || metaAuths.includes('*::*') || metaAuths.includes('*')) { + return true; + } return isString(value) ? metaAuths.includes(value) : isIncludeAllChildren(value, metaAuths); } diff --git a/src/views/system/adminUser/index.vue b/src/views/system/adminUser/index.vue index f30fd33..8c7cd7e 100644 --- a/src/views/system/adminUser/index.vue +++ b/src/views/system/adminUser/index.vue @@ -38,6 +38,8 @@ import Airplane from '@/assets/svg/airplane.svg'; import { useDeptStore } from '@/store/system/dept'; import { FormInstance } from 'element-plus'; import { usePublicHooks } from '@/views/hooks'; +import { auth } from '@/views/system/adminUser/utils/auth'; +import { hasAuth } from '@/router/utils'; const adminUserStore = useAdminUserStore(); const deptStore = useDeptStore(); @@ -88,58 +90,62 @@ onMounted(() => {
- - - - - + + + + + + - - - - + + + + - - - - + + + + - - - - + + + + - - - - - - + + + + + + - - - - + + + + - - - - - - + + + + + + - - {{ $t('search') }} - {{ $t('buttons.reset') }} - - + + {{ $t('search') }} + {{ $t('buttons.reset') }} + + + @@ -212,10 +218,10 @@ onMounted(() => {