修复BUG:

1.角色管理没有分页问题;
2.自定义指令引用问题
This commit is contained in:
猿小天
2024-01-18 20:25:02 +08:00
parent e210b7dd17
commit 9bd79b9dc6
11 changed files with 222 additions and 499 deletions

View File

@@ -123,7 +123,7 @@ const getDict = async () => {
const params = {
page: pageConfig.page,
limit: pageConfig.limit,
search: search
search: search.value
}
const dicts = dict({url: url, params: params})
await dicts.reloadDict()

View File

@@ -1,7 +1,6 @@
import type { App } from 'vue';
import { useUserInfo } from '/@/stores/userInfo';
import { judementSameArr } from '/@/utils/arrayOperation';
import {BtnPermissionStore} from "/@/stores/btnPermission";
/**
* 用户权限指令
* @directive 单个权限验证v-auth="xxx"
@@ -12,16 +11,16 @@ export function authDirective(app: App) {
// 单个权限验证v-auth="xxx"
app.directive('auth', {
mounted(el, binding) {
const stores = useUserInfo();
if (!stores.userInfos.authBtnList.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
const stores = BtnPermissionStore();
if (!stores.data.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
},
});
// 多个权限验证满足一个则显示v-auths="[xxx,xxx]"
app.directive('auths', {
mounted(el, binding) {
let flag = false;
const stores = useUserInfo();
stores.userInfos.authBtnList.map((val: string) => {
const stores = BtnPermissionStore();
stores.data.map((val: string) => {
binding.value.map((v: string) => {
if (val === v) flag = true;
});
@@ -32,8 +31,8 @@ export function authDirective(app: App) {
// 多个权限验证全部满足则显示v-auth-all="[xxx,xxx]"
app.directive('auth-all', {
mounted(el, binding) {
const stores = useUserInfo();
const flag = judementSameArr(binding.value, stores.userInfos.authBtnList);
const stores = BtnPermissionStore();
const flag = judementSameArr(binding.value, stores.data);
if (!flag) el.parentNode.removeChild(el);
},
});

View File

@@ -1,7 +1,7 @@
import type { App } from 'vue';
import { authDirective } from '/@/directive/authDirective';
import { wavesDirective, dragDirective } from '/@/directive/customDirective';
import {resizeObDirective} from '/@/directive/sizeDirective'
/**
* 导出指令方法v-xxx
* @methods authDirective 用户权限指令用法v-auth
@@ -15,4 +15,6 @@ export function directive(app: App) {
wavesDirective(app);
// 自定义拖动指令
dragDirective(app);
// 监听窗口大小变化
resizeObDirective(app)
}

View File

@@ -0,0 +1,23 @@
import {App} from "vue/dist/vue";
const map = new WeakMap()
const ob = new ResizeObserver((entries) => {
for(const entry of entries){
const handler = map.get(entry.target);
handler && handler({
width: entry.borderBoxSize[0].inlineSize,
height: entry.borderBoxSize[0].blockSize
});
}
});
export function resizeObDirective(app: App){
app.directive('resizeOb', {
mounted(el,binding) {
map.set(el,binding.value);
ob.observe(el); // 监听目标元素
},
unmounted(el) {
ob.unobserve(el); // 停止监听
},
})
}

View File

@@ -1,7 +1,7 @@
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import { directive } from '/@/utils/directive';
import { directive } from '/@/directive/index';
import { i18n } from '/@/i18n';
import other from '/@/utils/other';
import '/@/assets/style/tailwind.css'; // 先引入tailwind css, 以免element-plus冲突

View File

@@ -1,39 +0,0 @@
import type { App } from 'vue';
import { judementSameArr } from '/@/utils/arrayOperation';
import {BtnPermissionStore} from "/@/stores/btnPermission";
/**
* 用户权限指令
* @directive 单个权限验证v-auth="xxx"
* @directive 多个权限验证满足一个则显示v-auths="[xxx,xxx]"
* @directive 多个权限验证全部满足则显示v-auth-all="[xxx,xxx]"
*/
export function authDirective(app: App) {
// 单个权限验证v-auth="xxx"
app.directive('auth', {
mounted(el, binding) {
const stores = BtnPermissionStore();
if (!stores.data.some((v: string) => v === binding.value)) el.parentNode.removeChild(el);
},
});
// 多个权限验证满足一个则显示v-auths="[xxx,xxx]"
app.directive('auths', {
mounted(el, binding) {
let flag = false;
const stores = BtnPermissionStore();
stores.data.map((val: string) => {
binding.value.map((v: string) => {
if (val === v) flag = true;
});
});
if (!flag) el.parentNode.removeChild(el);
},
});
// 多个权限验证全部满足则显示v-auth-all="[xxx,xxx]"
app.directive('auth-all', {
mounted(el, binding) {
const stores = BtnPermissionStore();
const flag = judementSameArr(binding.value, stores.data);
if (!flag) el.parentNode.removeChild(el);
},
});
}

View File

@@ -1,178 +0,0 @@
import type { App } from 'vue';
/**
* 按钮波浪指令
* @directive 默认方式v-waves如 `<div v-waves></div>`
* @directive 参数方式v-waves=" |light|red|orange|purple|green|teal",如 `<div v-waves="'light'"></div>`
*/
export function wavesDirective(app: App) {
app.directive('waves', {
mounted(el, binding) {
el.classList.add('waves-effect');
binding.value && el.classList.add(`waves-${binding.value}`);
function setConvertStyle(obj: { [key: string]: unknown }) {
let style: string = '';
for (let i in obj) {
if (obj.hasOwnProperty(i)) style += `${i}:${obj[i]};`;
}
return style;
}
function onCurrentClick(e: { [key: string]: unknown }) {
let elDiv = document.createElement('div');
elDiv.classList.add('waves-ripple');
el.appendChild(elDiv);
let styles = {
left: `${e.layerX}px`,
top: `${e.layerY}px`,
opacity: 1,
transform: `scale(${(el.clientWidth / 100) * 10})`,
'transition-duration': `750ms`,
'transition-timing-function': `cubic-bezier(0.250, 0.460, 0.450, 0.940)`,
};
elDiv.setAttribute('style', setConvertStyle(styles));
setTimeout(() => {
elDiv.setAttribute(
'style',
setConvertStyle({
opacity: 0,
transform: styles.transform,
left: styles.left,
top: styles.top,
})
);
setTimeout(() => {
elDiv && el.removeChild(elDiv);
}, 750);
}, 450);
}
el.addEventListener('mousedown', onCurrentClick, false);
},
unmounted(el) {
el.addEventListener('mousedown', () => {});
},
});
}
/**
* 自定义拖动指令
* @description 使用方式v-drag="[dragDom,dragHeader]",如 `<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']"></div>`
* @description dragDom 要拖动的元素dragHeader 要拖动的 Header 位置
* @link 注意https://github.com/element-plus/element-plus/issues/522
* @lick 参考https://blog.csdn.net/weixin_46391323/article/details/105228020?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-10&spm=1001.2101.3001.4242
*/
export function dragDirective(app: App) {
app.directive('drag', {
mounted(el, binding) {
if (!binding.value) return false;
const dragDom = document.querySelector(binding.value[0]) as HTMLElement;
const dragHeader = document.querySelector(binding.value[1]) as HTMLElement;
dragHeader.onmouseover = () => (dragHeader.style.cursor = `move`);
function down(e: any, type: string) {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = type === 'pc' ? e.clientX - dragHeader.offsetLeft : e.touches[0].clientX - dragHeader.offsetLeft;
const disY = type === 'pc' ? e.clientY - dragHeader.offsetTop : e.touches[0].clientY - dragHeader.offsetTop;
// body当前宽度
const screenWidth = document.body.clientWidth;
// 可见区域高度(应为body高度可某些环境下无法获取)
const screenHeight = document.documentElement.clientHeight;
// 对话框宽度
const dragDomWidth = dragDom.offsetWidth;
// 对话框高度
const dragDomheight = dragDom.offsetHeight;
const minDragDomLeft = dragDom.offsetLeft;
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
const minDragDomTop = dragDom.offsetTop;
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
// 获取到的值带px 正则匹配替换
let styL: any = getComputedStyle(dragDom).left;
let styT: any = getComputedStyle(dragDom).top;
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
} else {
styL = +styL.replace(/\px/g, '');
styT = +styT.replace(/\px/g, '');
}
return {
disX,
disY,
minDragDomLeft,
maxDragDomLeft,
minDragDomTop,
maxDragDomTop,
styL,
styT,
};
}
function move(e: any, type: string, obj: any) {
let { disX, disY, minDragDomLeft, maxDragDomLeft, minDragDomTop, maxDragDomTop, styL, styT } = obj;
// 通过事件委托,计算移动的距离
let left = type === 'pc' ? e.clientX - disX : e.touches[0].clientX - disX;
let top = type === 'pc' ? e.clientY - disY : e.touches[0].clientY - disY;
// 边界处理
if (-left > minDragDomLeft) {
left = -minDragDomLeft;
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft;
}
if (-top > minDragDomTop) {
top = -minDragDomTop;
} else if (top > maxDragDomTop) {
top = maxDragDomTop;
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
}
/**
* pc端
* onmousedown 鼠标按下触发事件
* onmousemove 鼠标按下时持续触发事件
* onmouseup 鼠标抬起触发事件
*/
dragHeader.onmousedown = (e) => {
const obj = down(e, 'pc');
document.onmousemove = (e) => {
move(e, 'pc', obj);
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
};
/**
* 移动端
* ontouchstart 当按下手指时触发ontouchstart
* ontouchmove 当移动手指时触发ontouchmove
* ontouchend 当移走手指时触发ontouchend
*/
dragHeader.ontouchstart = (e) => {
const obj = down(e, 'app');
document.ontouchmove = (e) => {
move(e, 'app', obj);
};
document.ontouchend = () => {
document.ontouchmove = null;
document.ontouchend = null;
};
};
},
});
}

View File

@@ -1,18 +0,0 @@
import type { App } from 'vue';
import { authDirective } from '/@/utils/authDirective';
import { wavesDirective, dragDirective } from '/@/utils/customDirective';
/**
* 导出指令方法v-xxx
* @methods authDirective 用户权限指令用法v-auth
* @methods wavesDirective 按钮波浪指令用法v-waves
* @methods dragDirective 自定义拖动指令用法v-drag
*/
export function directive(app: App) {
// 用户权限指令
authDirective(app);
// 按钮波浪指令
wavesDirective(app);
// 自定义拖动指令
dragDirective(app);
}

View File

@@ -6,7 +6,7 @@
<template #header>
<el-row>
<el-col :span="4">
<div>当前角色:
<div>当前授权角色:
<el-tag>{{ props.roleName }}</el-tag>
</div>
</el-col>

View File

@@ -1,255 +1,189 @@
import { CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose, compute } from '@fast-crud/fast-crud';
import {CrudOptions, AddReq, DelReq, EditReq, dict, CrudExpose, compute} from '@fast-crud/fast-crud';
import * as api from './api';
import { dictionary } from '/@/utils/dictionary';
import { columnPermission } from '../../../utils/columnPermission';
import { successMessage } from '../../../utils/message';
import {dictionary} from '/@/utils/dictionary';
import {columnPermission} from '../../../utils/columnPermission';
import {successMessage} from '../../../utils/message';
import {auth} from '/@/utils/authFunction'
interface CreateCrudOptionsTypes {
output: any;
crudOptions: CrudOptions;
output: any;
crudOptions: CrudOptions;
}
//此处为crudOptions配置
export const createCrudOptions = function ({
crudExpose,
rolePermission,
handleDrawerOpen,
}: {
crudExpose: CrudExpose;
rolePermission: any;
handleDrawerOpen: Function;
crudExpose,
rolePermission,
handleDrawerOpen,
}: {
crudExpose: CrudExpose;
rolePermission: any;
handleDrawerOpen: Function;
}): CreateCrudOptionsTypes {
const pageRequest = async (query: any) => {
return await api.GetList(query);
};
const editRequest = async ({ form, row }: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({ row }: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({ form }: AddReq) => {
return await api.AddObj(form);
};
const pageRequest = async (query: any) => {
return await api.GetList(query);
};
const editRequest = async ({form, row}: EditReq) => {
form.id = row.id;
return await api.UpdateObj(form);
};
const delRequest = async ({row}: DelReq) => {
return await api.DelObj(row.id);
};
const addRequest = async ({form}: AddReq) => {
return await api.AddObj(form);
};
//权限判定
//权限判定
// @ts-ignore
// @ts-ignore
return {
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
actionbar: {
buttons: {
add: {
show: auth('role:Create')
}
}
},
rowHandle: {
//固定右侧
fixed: 'right',
width: 320,
buttons: {
view: {
show: true,
},
edit: {
show: auth('role:Update'),
},
remove: {
show: auth('role:Delete'),
},
permission: {
type: 'primary',
text: '权限配置',
show: auth('role:Permission'),
tooltip: {
placement: 'top',
content: '权限配置',
},
click: (context: any): void => {
const { row } = context;
handleDrawerOpen(row);
},
},
},
},
form: {
col: { span: 24 },
labelWidth: '100px',
wrapper: {
is: 'el-dialog',
width: '600px',
},
},
columns: {
_index: {
title: '序号',
form: { show: false },
column: {
type: 'index',
align: 'center',
width: '70px',
columnSetDisabled: true, //禁止在列设置中选择
},
},
id: {
title: 'ID',
type: 'text',
column: { show: false },
search: { show: false },
form: { show: false },
},
name: {
title: '角色名称',
type: 'text',
search: { show: true },
column: {
minWidth: 120,
sortable: 'custom',
show: columnPermission('name', 'is_query'),
},
// addForm: {
// show: columnPermission('name', 'is_create'),
// },
editForm: {
show: columnPermission('name', 'is_update'),
},
form: {
rules: [{ required: true, message: '角色名称必填' }],
component: {
placeholder: '请输入角色名称',
},
},
},
key: {
title: '权限标识',
type: 'text',
search: { show: false },
column: {
minWidth: 120,
sortable: 'custom',
show: columnPermission('key', 'is_query'),
columnSetDisabled: true,
},
addForm: {
show: columnPermission('key', 'is_create'),
},
editForm: {
show: columnPermission('key', 'is_update'),
},
form: {
rules: [{ required: true, message: '权限标识必填' }],
component: {
placeholder: '输入权限标识',
},
},
valueBuilder(context){
const {row,key} = context
return row[key]
}
},
sort: {
title: '排序',
search: { show: false },
type: 'number',
column: {
minWidth: 90,
sortable: 'custom',
},
addForm: {
show: columnPermission('sort', 'is_create'),
},
editForm: {
show: columnPermission('sort', 'is_update'),
},
form: {
rules: [{ required: true, message: '排序必填' }],
value: 1,
},
},
status: {
title: '状态',
search: { show: true },
type: 'dict-radio',
column: {
width: 100,
component: {
name: 'fs-dict-switch',
activeText: '',
inactiveText: '',
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
onChange: compute((context) => {
return () => {
api.UpdateObj(context.row).then((res: APIResponseData) => {
successMessage(res.msg as string);
});
};
}),
},
show: columnPermission('status', 'is_query'),
},
addForm: {
show: columnPermission('status', 'is_create'),
},
editForm: {
show: columnPermission('status', 'is_update'),
},
dict: dict({
data: dictionary('button_status_bool'),
}),
},
update_datetime: {
title: '更新时间',
type: 'text',
search: { show: false },
column: {
minWidth: 170,
sortable: 'custom',
show: columnPermission('update_datetime', 'is_query'),
},
form: {
show: false,
component: {
placeholder: '输入关键词搜索',
},
},
},
create_datetime: {
title: '创建时间',
type: 'text',
search: { show: false },
column: {
sortable: 'custom',
minWidth: 170,
show: columnPermission('create_datetime', 'is_query'),
},
form: {
show: false,
component: {
placeholder: '输入关键词搜索',
},
},
},
// description: {
// title: '备注',
// type: 'textarea',
// search: {show: false},
// form: {
// component: {
// maxlength: 200,
// placeholder: '输入备注',
// },
// },
// },
},
},
};
// @ts-ignore
// @ts-ignore
return {
crudOptions: {
request: {
pageRequest,
addRequest,
editRequest,
delRequest,
},
pagination: {
show: true
},
actionbar: {
buttons: {
add: {
show: auth('role:Create')
}
}
},
rowHandle: {
//固定右侧
fixed: 'right',
width: 320,
buttons: {
view: {
show: true,
},
edit: {
show: auth('role:Update'),
},
remove: {
show: auth('role:Delete'),
},
permission: {
type: 'primary',
text: '权限配置',
show: auth('role:Permission'),
tooltip: {
placement: 'top',
content: '权限配置',
},
click: (context: any): void => {
const {row} = context;
handleDrawerOpen(row);
},
},
},
},
form: {
col: {span: 24},
labelWidth: '100px',
wrapper: {
is: 'el-dialog',
width: '600px',
},
},
columns: {
_index: {
title: '序号',
form: {show: false},
column: {
type: 'index',
align: 'center',
width: '70px',
columnSetDisabled: true, //禁止在列设置中选择
},
},
id: {
title: 'ID',
type: 'text',
column: {show: false},
search: {show: false},
form: {show: false},
},
name: {
title: '角色名称',
type: 'text',
search: {show: true},
column: {
minWidth: 120,
sortable: 'custom',
},
form: {
rules: [{required: true, message: '角色名称必填'}],
component: {
placeholder: '请输入角色名称',
},
},
},
key: {
title: '权限标识',
type: 'text',
search: {show: false},
column: {
minWidth: 120,
sortable: 'custom',
columnSetDisabled: true,
},
form: {
rules: [{required: true, message: '权限标识必填'}],
component: {
placeholder: '输入权限标识',
},
},
valueBuilder(context) {
const {row, key} = context
return row[key]
}
},
sort: {
title: '排序',
search: {show: false},
type: 'number',
column: {
minWidth: 90,
sortable: 'custom',
},
form: {
rules: [{required: true, message: '排序必填'}],
value: 1,
},
},
status: {
title: '状态',
search: {show: true},
type: 'dict-radio',
column: {
width: 100,
component: {
name: 'fs-dict-switch',
activeText: '',
inactiveText: '',
style: '--el-switch-on-color: var(--el-color-primary); --el-switch-off-color: #dcdfe6',
onChange: compute((context) => {
return () => {
api.UpdateObj(context.row).then((res: APIResponseData) => {
successMessage(res.msg as string);
});
};
}),
},
},
dict: dict({
data: dictionary('button_status_bool'),
}),
}
},
},
};
};

View File

@@ -47,21 +47,21 @@ const { crudExpose } = useExpose({ crudRef, crudBinding });
// 你的crud配置
const { crudOptions } = createCrudOptions({ crudExpose, rolePermission, handleDrawerOpen });
// 初始化crud配置
const { resetCrudOptions } = useCrud({
crudExpose,
crudOptions,
context: {},
});
// 页面打开后获取列表数据
onMounted( async () => {
const newOptions = await handleColumnPermission(GetPermission,crudOptions)
// 初始化crud配置
const { resetCrudOptions } = useCrud({
crudExpose,
crudOptions,
context: {},
});
//重置crudBinding
resetCrudOptions(newOptions);
//resetCrudOptions(newOptions);
crudExpose.doRefresh();
});