feat: 添加权限管理
This commit is contained in:
@@ -68,11 +68,14 @@ panel:MultiTagsCache: MultiTags Cache
|
|||||||
menus:Home: Home
|
menus:Home: Home
|
||||||
menus:Login: Login
|
menus:Login: Login
|
||||||
menus:Empty: Empty Page
|
menus:Empty: Empty Page
|
||||||
menus:SysManagement: System Manage
|
menus:SystemManagement: System Manage
|
||||||
menus:User: User Manage
|
menus:SystemUser: User Manage
|
||||||
menus:Role: Role Manage
|
menus:SystemRole: Role Manage
|
||||||
menus:SystemMenu: Menu Manage
|
menus:SystemPermission: Permission Manage
|
||||||
menus:Dept: Dept Manage
|
menus:SystemDepartment: Department Manage
|
||||||
|
menus:SystemI18n: i18n Manage
|
||||||
|
menus:SystemI18nLocale: Locale Manage
|
||||||
|
menus:SystemI18nLanguage: Language Manage
|
||||||
menus:SysMonitor: System Monitor
|
menus:SysMonitor: System Monitor
|
||||||
menus:OnlineUser: Online User
|
menus:OnlineUser: Online User
|
||||||
menus:LoginLog: Login Log
|
menus:LoginLog: Login Log
|
||||||
|
|||||||
@@ -68,11 +68,14 @@ panel:MultiTagsCache: 页签持久化
|
|||||||
menus:Home: 首页
|
menus:Home: 首页
|
||||||
menus:Login: 登录
|
menus:Login: 登录
|
||||||
menus:Empty: 无Layout页
|
menus:Empty: 无Layout页
|
||||||
menus:SysManagement: 系统管理
|
menus:SystemManagement: 系统管理
|
||||||
menus:User: 用户管理
|
menus:SystemUser: 用户管理
|
||||||
menus:Role: 角色管理
|
menus:SystemRole: 角色管理
|
||||||
menus:SystemMenu: 菜单管理
|
menus:SystemPermission: 权限管理
|
||||||
menus:Dept: 部门管理
|
menus:SystemDepartment: 部门管理
|
||||||
|
menus:SystemI18n: 国际化管理
|
||||||
|
menus:SystemI18nLocale: 类型管理
|
||||||
|
menus:SystemI18nLanguage: 语言管理
|
||||||
menus:SysMonitor: 系统监控
|
menus:SysMonitor: 系统监控
|
||||||
menus:OnlineUser: 在线用户
|
menus:OnlineUser: 在线用户
|
||||||
menus:LoginLog: 登录日志
|
menus:LoginLog: 登录日志
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { http } from "@/utils/http";
|
import { http } from "@/utils/http";
|
||||||
import type { DepartmentInfo } from "types/system";
|
import type { DepartmentInfo, PermissionInfo } from "types/system";
|
||||||
import { filterEmptyObject } from "./utils";
|
import { filterEmptyObject } from "./utils";
|
||||||
|
|
||||||
// ---------------------------部门相关-------------------------------------
|
// ---------------------------部门相关-------------------------------------
|
||||||
@@ -80,3 +80,147 @@ export const postAddDepartmentAPI = (data: AddDepartmentParams) => {
|
|||||||
export const deleteDepartmentAPI = (id: string) => {
|
export const deleteDepartmentAPI = (id: string) => {
|
||||||
return http.request<null>("post", `/api/department/delete/${id}`);
|
return http.request<null>("post", `/api/department/delete/${id}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ----------------------------权限相关------------------------------------
|
||||||
|
|
||||||
|
/** 获取权限列表参数 */
|
||||||
|
type GetPermissionListParams = {
|
||||||
|
/** 当前页码 */
|
||||||
|
page: number;
|
||||||
|
/** 每页条数 */
|
||||||
|
pageSize: number;
|
||||||
|
/** 主键 */
|
||||||
|
id?: string;
|
||||||
|
/** 权限名称 */
|
||||||
|
name?: string;
|
||||||
|
/** 父权限ID */
|
||||||
|
parentId?: string;
|
||||||
|
/** 权限路径 */
|
||||||
|
path?: string;
|
||||||
|
/** 排序权重 */
|
||||||
|
rank?: number;
|
||||||
|
/** 菜单类型(0菜单、1iframe、2外链、3按钮) */
|
||||||
|
menuType?: number;
|
||||||
|
/** 显示菜单 */
|
||||||
|
showLink?: boolean;
|
||||||
|
/** 显示父级菜单 */
|
||||||
|
showParent?: boolean;
|
||||||
|
/** 激活路径 */
|
||||||
|
activePath?: string;
|
||||||
|
/** 组件路径 */
|
||||||
|
component?: string;
|
||||||
|
/** 重定向路径 */
|
||||||
|
redirect?: string;
|
||||||
|
/** iframe路径 */
|
||||||
|
frameSrc?: string;
|
||||||
|
/** iframe加载动画 */
|
||||||
|
frameLoading?: boolean;
|
||||||
|
/** 缓存组件 */
|
||||||
|
keepAlive?: boolean;
|
||||||
|
/** 权限标识 */
|
||||||
|
auths?: string;
|
||||||
|
/** 菜单图标 */
|
||||||
|
icon?: string;
|
||||||
|
/** 右侧图标 */
|
||||||
|
extraIcon?: string;
|
||||||
|
/** 进场动画 */
|
||||||
|
enterTransition?: string;
|
||||||
|
/** 离场动画 */
|
||||||
|
leaveTransition?: string;
|
||||||
|
/** 固定标签页 */
|
||||||
|
fixedTag?: boolean;
|
||||||
|
/** 隐藏标签页 */
|
||||||
|
hiddenTag?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**获取权限列表 */
|
||||||
|
export const getPermissionListAPI = (params: GetPermissionListParams) => {
|
||||||
|
return http.request<QueryListResult<PermissionInfo[]>>(
|
||||||
|
"get",
|
||||||
|
`/api/permission/list`,
|
||||||
|
{
|
||||||
|
params: filterEmptyObject(params)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**添加权限参数 */
|
||||||
|
type AddPermissionParams = {
|
||||||
|
/** 路由名称 */
|
||||||
|
name: string;
|
||||||
|
/** 路由路径 */
|
||||||
|
path: string;
|
||||||
|
/** 菜单名称 */
|
||||||
|
title: string;
|
||||||
|
/** 组件路径 */
|
||||||
|
component?: string;
|
||||||
|
/** 菜单排序 */
|
||||||
|
rank: number;
|
||||||
|
/** 路由重定向 */
|
||||||
|
redirect?: string;
|
||||||
|
/** 菜单图标 */
|
||||||
|
icon?: string;
|
||||||
|
/** 右侧图标 */
|
||||||
|
extra_icon?: string;
|
||||||
|
/** 进场动画 */
|
||||||
|
enter_transition?: string;
|
||||||
|
/** 离场动画 */
|
||||||
|
leave_transition?: string;
|
||||||
|
/** 菜单激活路径 */
|
||||||
|
active_path?: string;
|
||||||
|
/** 权限标识 */
|
||||||
|
auths?: string;
|
||||||
|
/** iframe链接地址 */
|
||||||
|
frame_src?: string;
|
||||||
|
/** iframe加载动画 */
|
||||||
|
frame_loading: boolean;
|
||||||
|
/** 缓存页面 */
|
||||||
|
keep_alive: boolean;
|
||||||
|
/** 隐藏标签页 */
|
||||||
|
hidden_tag: boolean;
|
||||||
|
/** 固定标签页 */
|
||||||
|
fixed_tag: boolean;
|
||||||
|
/** 显示菜单 */
|
||||||
|
show_link: boolean;
|
||||||
|
/** 显示父级菜单 */
|
||||||
|
show_parent: boolean;
|
||||||
|
/** 父级菜单ID */
|
||||||
|
parent_id: string;
|
||||||
|
/** 菜单类型 */
|
||||||
|
menu_type: number;
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 添加权限
|
||||||
|
* @param data 添加权限参数
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const postAddPermissionAPI = (data: AddPermissionParams) => {
|
||||||
|
return http.request<null>("post", `/api/permission/add`, {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 删除权限
|
||||||
|
* @param id 权限ID
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const deletePermissionAPI = (id: string) => {
|
||||||
|
return http.request<null>("post", `/api/permission/delete/${id}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**更新权限信息 */
|
||||||
|
type UpdatePermissionParams = {} & AddPermissionParams;
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param id 权限ID
|
||||||
|
* @param data 更新权限信息参数
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const putUpdatePermissionAPI = (
|
||||||
|
id: string,
|
||||||
|
data: UpdatePermissionParams
|
||||||
|
) => {
|
||||||
|
return http.request<null>("post", `/api/permission/update/${id}`, {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|||||||
7
src/components/ReAnimateSelector/index.ts
Normal file
7
src/components/ReAnimateSelector/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import { withInstall } from "@pureadmin/utils";
|
||||||
|
import reAnimateSelector from "./src/index.vue";
|
||||||
|
|
||||||
|
/** [animate.css](https://animate.style/) 选择器组件 */
|
||||||
|
export const ReAnimateSelector = withInstall(reAnimateSelector);
|
||||||
|
|
||||||
|
export default ReAnimateSelector;
|
||||||
114
src/components/ReAnimateSelector/src/animate.ts
Normal file
114
src/components/ReAnimateSelector/src/animate.ts
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
export const animates = [
|
||||||
|
/* Attention seekers */
|
||||||
|
"bounce",
|
||||||
|
"flash",
|
||||||
|
"pulse",
|
||||||
|
"rubberBand",
|
||||||
|
"shakeX",
|
||||||
|
"headShake",
|
||||||
|
"swing",
|
||||||
|
"tada",
|
||||||
|
"wobble",
|
||||||
|
"jello",
|
||||||
|
"heartBeat",
|
||||||
|
/* Back entrances */
|
||||||
|
"backInDown",
|
||||||
|
"backInLeft",
|
||||||
|
"backInRight",
|
||||||
|
"backInUp",
|
||||||
|
/* Back exits */
|
||||||
|
"backOutDown",
|
||||||
|
"backOutLeft",
|
||||||
|
"backOutRight",
|
||||||
|
"backOutUp",
|
||||||
|
/* Bouncing entrances */
|
||||||
|
"bounceIn",
|
||||||
|
"bounceInDown",
|
||||||
|
"bounceInLeft",
|
||||||
|
"bounceInRight",
|
||||||
|
"bounceInUp",
|
||||||
|
/* Bouncing exits */
|
||||||
|
"bounceOut",
|
||||||
|
"bounceOutDown",
|
||||||
|
"bounceOutLeft",
|
||||||
|
"bounceOutRight",
|
||||||
|
"bounceOutUp",
|
||||||
|
/* Fading entrances */
|
||||||
|
"fadeIn",
|
||||||
|
"fadeInDown",
|
||||||
|
"fadeInDownBig",
|
||||||
|
"fadeInLeft",
|
||||||
|
"fadeInLeftBig",
|
||||||
|
"fadeInRight",
|
||||||
|
"fadeInRightBig",
|
||||||
|
"fadeInUp",
|
||||||
|
"fadeInUpBig",
|
||||||
|
"fadeInTopLeft",
|
||||||
|
"fadeInTopRight",
|
||||||
|
"fadeInBottomLeft",
|
||||||
|
"fadeInBottomRight",
|
||||||
|
/* Fading exits */
|
||||||
|
"fadeOut",
|
||||||
|
"fadeOutDown",
|
||||||
|
"fadeOutDownBig",
|
||||||
|
"fadeOutLeft",
|
||||||
|
"fadeOutLeftBig",
|
||||||
|
"fadeOutRight",
|
||||||
|
"fadeOutRightBig",
|
||||||
|
"fadeOutUp",
|
||||||
|
"fadeOutUpBig",
|
||||||
|
"fadeOutTopLeft",
|
||||||
|
"fadeOutTopRight",
|
||||||
|
"fadeOutBottomRight",
|
||||||
|
"fadeOutBottomLeft",
|
||||||
|
/* Flippers */
|
||||||
|
"flip",
|
||||||
|
"flipInX",
|
||||||
|
"flipInY",
|
||||||
|
"flipOutX",
|
||||||
|
"flipOutY",
|
||||||
|
/* Lightspeed */
|
||||||
|
"lightSpeedInRight",
|
||||||
|
"lightSpeedInLeft",
|
||||||
|
"lightSpeedOutRight",
|
||||||
|
"lightSpeedOutLeft",
|
||||||
|
/* Rotating entrances */
|
||||||
|
"rotateIn",
|
||||||
|
"rotateInDownLeft",
|
||||||
|
"rotateInDownRight",
|
||||||
|
"rotateInUpLeft",
|
||||||
|
"rotateInUpRight",
|
||||||
|
/* Rotating exits */
|
||||||
|
"rotateOut",
|
||||||
|
"rotateOutDownLeft",
|
||||||
|
"rotateOutDownRight",
|
||||||
|
"rotateOutUpLeft",
|
||||||
|
"rotateOutUpRight",
|
||||||
|
/* Specials */
|
||||||
|
"hinge",
|
||||||
|
"jackInTheBox",
|
||||||
|
"rollIn",
|
||||||
|
"rollOut",
|
||||||
|
/* Zooming entrances */
|
||||||
|
"zoomIn",
|
||||||
|
"zoomInDown",
|
||||||
|
"zoomInLeft",
|
||||||
|
"zoomInRight",
|
||||||
|
"zoomInUp",
|
||||||
|
/* Zooming exits */
|
||||||
|
"zoomOut",
|
||||||
|
"zoomOutDown",
|
||||||
|
"zoomOutLeft",
|
||||||
|
"zoomOutRight",
|
||||||
|
"zoomOutUp",
|
||||||
|
/* Sliding entrances */
|
||||||
|
"slideInDown",
|
||||||
|
"slideInLeft",
|
||||||
|
"slideInRight",
|
||||||
|
"slideInUp",
|
||||||
|
/* Sliding exits */
|
||||||
|
"slideOutDown",
|
||||||
|
"slideOutLeft",
|
||||||
|
"slideOutRight",
|
||||||
|
"slideOutUp"
|
||||||
|
];
|
||||||
136
src/components/ReAnimateSelector/src/index.vue
Normal file
136
src/components/ReAnimateSelector/src/index.vue
Normal file
@@ -0,0 +1,136 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import { animates } from "./animate";
|
||||||
|
import { cloneDeep } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "ReAnimateSelector"
|
||||||
|
});
|
||||||
|
|
||||||
|
defineProps({
|
||||||
|
placeholder: {
|
||||||
|
type: String,
|
||||||
|
default: "请选择动画"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputValue = defineModel({ type: String });
|
||||||
|
|
||||||
|
const searchVal = ref();
|
||||||
|
const animatesList = ref(animates);
|
||||||
|
const copyAnimatesList = cloneDeep(animatesList);
|
||||||
|
|
||||||
|
const animateClass = computed(() => {
|
||||||
|
return [
|
||||||
|
"mt-1",
|
||||||
|
"flex",
|
||||||
|
"border",
|
||||||
|
"w-[130px]",
|
||||||
|
"h-[100px]",
|
||||||
|
"items-center",
|
||||||
|
"cursor-pointer",
|
||||||
|
"transition-all",
|
||||||
|
"justify-center",
|
||||||
|
"border-[#e5e7eb]",
|
||||||
|
"hover:text-primary",
|
||||||
|
"hover:duration-[700ms]"
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const animateStyle = computed(
|
||||||
|
() => (i: string) =>
|
||||||
|
inputValue.value === i
|
||||||
|
? {
|
||||||
|
borderColor: "var(--el-color-primary)",
|
||||||
|
color: "var(--el-color-primary)"
|
||||||
|
}
|
||||||
|
: ""
|
||||||
|
);
|
||||||
|
|
||||||
|
function onChangeIcon(animate: string) {
|
||||||
|
inputValue.value = animate;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClear() {
|
||||||
|
inputValue.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterMethod(value: any) {
|
||||||
|
searchVal.value = value;
|
||||||
|
animatesList.value = copyAnimatesList.value.filter((i: string | any[]) =>
|
||||||
|
i.includes(value)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const animateMap = ref({});
|
||||||
|
function onMouseEnter(index: string | number) {
|
||||||
|
animateMap.value[index] = animateMap.value[index]?.loading
|
||||||
|
? Object.assign({}, animateMap.value[index], {
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
: Object.assign({}, animateMap.value[index], {
|
||||||
|
loading: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function onMouseleave() {
|
||||||
|
animateMap.value = {};
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-select
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:placeholder="placeholder"
|
||||||
|
popper-class="pure-animate-popper"
|
||||||
|
:model-value="inputValue"
|
||||||
|
:filter-method="filterMethod"
|
||||||
|
@clear="onClear"
|
||||||
|
>
|
||||||
|
<template #empty>
|
||||||
|
<div class="w-[280px]">
|
||||||
|
<el-scrollbar
|
||||||
|
noresize
|
||||||
|
height="212px"
|
||||||
|
:view-style="{ overflow: 'hidden' }"
|
||||||
|
class="border-t border-[#e5e7eb]"
|
||||||
|
>
|
||||||
|
<ul class="flex flex-wrap justify-around mb-1">
|
||||||
|
<li
|
||||||
|
v-for="(animate, index) in animatesList"
|
||||||
|
:key="index"
|
||||||
|
:class="animateClass"
|
||||||
|
:style="animateStyle(animate)"
|
||||||
|
@mouseenter.prevent="onMouseEnter(index)"
|
||||||
|
@mouseleave.prevent="onMouseleave"
|
||||||
|
@click="onChangeIcon(animate)"
|
||||||
|
>
|
||||||
|
<h4
|
||||||
|
:class="[
|
||||||
|
`animate__animated animate__${
|
||||||
|
animateMap[index]?.loading
|
||||||
|
? animate + ' animate__infinite'
|
||||||
|
: ''
|
||||||
|
} `
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
{{ animate }}
|
||||||
|
</h4>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<el-empty
|
||||||
|
v-show="animatesList.length === 0"
|
||||||
|
:description="`${searchVal} 动画不存在`"
|
||||||
|
:image-size="60"
|
||||||
|
/>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-select>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.pure-animate-popper {
|
||||||
|
min-width: 0 !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
3869
src/components/ReIcon/data.ts
Normal file
3869
src/components/ReIcon/data.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,12 +1,15 @@
|
|||||||
import iconifyIconOffline from "./src/iconifyIconOffline";
|
import iconifyIconOffline from "./src/iconifyIconOffline";
|
||||||
import iconifyIconOnline from "./src/iconifyIconOnline";
|
import iconifyIconOnline from "./src/iconifyIconOnline";
|
||||||
|
import iconSelect from "./src/Select.vue";
|
||||||
import fontIcon from "./src/iconfont";
|
import fontIcon from "./src/iconfont";
|
||||||
|
|
||||||
/** 本地图标组件 */
|
/** 本地图标组件 */
|
||||||
const IconifyIconOffline = iconifyIconOffline;
|
const IconifyIconOffline = iconifyIconOffline;
|
||||||
/** 在线图标组件 */
|
/** 在线图标组件 */
|
||||||
const IconifyIconOnline = iconifyIconOnline;
|
const IconifyIconOnline = iconifyIconOnline;
|
||||||
|
/** `IconSelect`图标选择器组件 */
|
||||||
|
const IconSelect = iconSelect;
|
||||||
/** `iconfont`组件 */
|
/** `iconfont`组件 */
|
||||||
const FontIcon = fontIcon;
|
const FontIcon = fontIcon;
|
||||||
|
|
||||||
export { IconifyIconOffline, IconifyIconOnline, FontIcon };
|
export { IconifyIconOffline, IconifyIconOnline, IconSelect, FontIcon };
|
||||||
|
|||||||
268
src/components/ReIcon/src/Select.vue
Normal file
268
src/components/ReIcon/src/Select.vue
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { IconJson } from "@/components/ReIcon/data";
|
||||||
|
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
||||||
|
import { ref, computed, CSSProperties, watch } from "vue";
|
||||||
|
import Search from "@iconify-icons/ri/search-eye-line";
|
||||||
|
|
||||||
|
type ParameterCSSProperties = (item?: string) => CSSProperties | undefined;
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "IconSelect"
|
||||||
|
});
|
||||||
|
|
||||||
|
const inputValue = defineModel({ type: String });
|
||||||
|
|
||||||
|
const iconList = ref(IconJson);
|
||||||
|
const icon = ref();
|
||||||
|
const currentActiveType = ref("ep:");
|
||||||
|
// 深拷贝图标数据,前端做搜索
|
||||||
|
const copyIconList = cloneDeep(iconList.value);
|
||||||
|
const totalPage = ref(0);
|
||||||
|
// 每页显示35个图标
|
||||||
|
const pageSize = ref(35);
|
||||||
|
const currentPage = ref(1);
|
||||||
|
|
||||||
|
// 搜索条件
|
||||||
|
const filterValue = ref("");
|
||||||
|
|
||||||
|
const tabsList = [
|
||||||
|
{
|
||||||
|
label: "Element Plus",
|
||||||
|
name: "ep:"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Remix Icon",
|
||||||
|
name: "ri:"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Font Awesome 5 Solid",
|
||||||
|
name: "fa-solid:"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const pageList = computed(() =>
|
||||||
|
copyIconList[currentActiveType.value]
|
||||||
|
.filter(i => i.includes(filterValue.value))
|
||||||
|
.slice(
|
||||||
|
(currentPage.value - 1) * pageSize.value,
|
||||||
|
currentPage.value * pageSize.value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const iconItemStyle = computed((): ParameterCSSProperties => {
|
||||||
|
return item => {
|
||||||
|
if (inputValue.value === currentActiveType.value + item) {
|
||||||
|
return {
|
||||||
|
borderColor: "var(--el-color-primary)",
|
||||||
|
color: "var(--el-color-primary)"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
function setVal() {
|
||||||
|
currentActiveType.value = inputValue.value.substring(
|
||||||
|
0,
|
||||||
|
inputValue.value.indexOf(":") + 1
|
||||||
|
);
|
||||||
|
icon.value = inputValue.value.substring(inputValue.value.indexOf(":") + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onBeforeEnter() {
|
||||||
|
if (isAllEmpty(icon.value)) return;
|
||||||
|
setVal();
|
||||||
|
// 寻找当前图标在第几页
|
||||||
|
const curIconIndex = copyIconList[currentActiveType.value].findIndex(
|
||||||
|
i => i === icon.value
|
||||||
|
);
|
||||||
|
currentPage.value = Math.ceil((curIconIndex + 1) / pageSize.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAfterLeave() {
|
||||||
|
filterValue.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleClick({ props }) {
|
||||||
|
currentPage.value = 1;
|
||||||
|
currentActiveType.value = props.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChangeIcon(item) {
|
||||||
|
icon.value = item;
|
||||||
|
inputValue.value = currentActiveType.value + item;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onCurrentChange(page) {
|
||||||
|
currentPage.value = page;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onClear() {
|
||||||
|
icon.value = "";
|
||||||
|
inputValue.value = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => pageList.value,
|
||||||
|
() =>
|
||||||
|
(totalPage.value = copyIconList[currentActiveType.value].filter(i =>
|
||||||
|
i.includes(filterValue.value)
|
||||||
|
).length),
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => inputValue.value,
|
||||||
|
val => val && setVal(),
|
||||||
|
{ immediate: true }
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => filterValue.value,
|
||||||
|
() => (currentPage.value = 1)
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="selector">
|
||||||
|
<el-input v-model="inputValue" disabled>
|
||||||
|
<template #append>
|
||||||
|
<el-popover
|
||||||
|
:width="350"
|
||||||
|
trigger="click"
|
||||||
|
popper-class="pure-popper"
|
||||||
|
:popper-options="{
|
||||||
|
placement: 'auto'
|
||||||
|
}"
|
||||||
|
@before-enter="onBeforeEnter"
|
||||||
|
@after-leave="onAfterLeave"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<div
|
||||||
|
class="w-[40px] h-[32px] cursor-pointer flex justify-center items-center"
|
||||||
|
>
|
||||||
|
<IconifyIconOffline v-if="!icon" :icon="Search" />
|
||||||
|
<IconifyIconOnline v-else :icon="inputValue" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-input
|
||||||
|
v-model="filterValue"
|
||||||
|
class="px-2 pt-2"
|
||||||
|
placeholder="搜索图标"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
|
||||||
|
<el-tabs v-model="currentActiveType" @tab-click="handleClick">
|
||||||
|
<el-tab-pane
|
||||||
|
v-for="(pane, index) in tabsList"
|
||||||
|
:key="index"
|
||||||
|
:label="pane.label"
|
||||||
|
:name="pane.name"
|
||||||
|
>
|
||||||
|
<el-scrollbar height="220px">
|
||||||
|
<ul class="flex flex-wrap px-2 ml-2">
|
||||||
|
<li
|
||||||
|
v-for="(item, key) in pageList"
|
||||||
|
:key="key"
|
||||||
|
:title="item"
|
||||||
|
class="icon-item p-2 cursor-pointer mr-2 mt-1 flex justify-center items-center border border-[#e5e7eb]"
|
||||||
|
:style="iconItemStyle(item)"
|
||||||
|
@click="onChangeIcon(item)"
|
||||||
|
>
|
||||||
|
<IconifyIconOnline
|
||||||
|
:icon="currentActiveType + item"
|
||||||
|
width="20px"
|
||||||
|
height="20px"
|
||||||
|
/>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<el-empty
|
||||||
|
v-show="pageList.length === 0"
|
||||||
|
:description="`${filterValue} 图标不存在`"
|
||||||
|
:image-size="60"
|
||||||
|
/>
|
||||||
|
</el-scrollbar>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="w-full h-9 flex items-center overflow-auto border-t border-[#e5e7eb]"
|
||||||
|
>
|
||||||
|
<el-pagination
|
||||||
|
class="flex-auto ml-2"
|
||||||
|
:total="totalPage"
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:pager-count="5"
|
||||||
|
layout="pager"
|
||||||
|
background
|
||||||
|
size="small"
|
||||||
|
@current-change="onCurrentChange"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
class="justify-end mr-2 ml-2"
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
text
|
||||||
|
bg
|
||||||
|
@click="onClear"
|
||||||
|
>
|
||||||
|
清空
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-popover>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.icon-item {
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
border-color: var(--el-color-primary);
|
||||||
|
transition: all 0.4s;
|
||||||
|
transform: scaleX(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-next) {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 32px;
|
||||||
|
box-shadow: -5px 0 5px -6px #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-prev) {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 32px;
|
||||||
|
box-shadow: 5px 0 5px -6px #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-input-group__append) {
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__item) {
|
||||||
|
height: 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__header),
|
||||||
|
:deep(.el-tabs__nav-wrap) {
|
||||||
|
position: static;
|
||||||
|
margin: 0;
|
||||||
|
box-shadow: 0 2px 5px rgb(0 0 0 / 6%);
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-wrap::after) {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__nav-wrap) {
|
||||||
|
padding: 0 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tabs__content) {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -27,8 +27,7 @@ export default defineComponent({
|
|||||||
return h(
|
return h(
|
||||||
"svg",
|
"svg",
|
||||||
{
|
{
|
||||||
class: "icon-svg",
|
class: "icon-svg"
|
||||||
"aria-hidden": true
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
default: () => [
|
default: () => [
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default defineComponent({
|
|||||||
IconifyIcon,
|
IconifyIcon,
|
||||||
{
|
{
|
||||||
icon: this.icon,
|
icon: this.icon,
|
||||||
|
"aria-hidden": false,
|
||||||
style: attrs?.style
|
style: attrs?.style
|
||||||
? Object.assign(attrs.style, { outline: "none" })
|
? Object.assign(attrs.style, { outline: "none" })
|
||||||
: { outline: "none" },
|
: { outline: "none" },
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ export default defineComponent({
|
|||||||
IconifyIcon,
|
IconifyIcon,
|
||||||
{
|
{
|
||||||
icon: `${this.icon}`,
|
icon: `${this.icon}`,
|
||||||
|
"aria-hidden": false,
|
||||||
style: attrs?.style
|
style: attrs?.style
|
||||||
? Object.assign(attrs.style, { outline: "none" })
|
? Object.assign(attrs.style, { outline: "none" })
|
||||||
: { outline: "none" },
|
: { outline: "none" },
|
||||||
|
|||||||
@@ -3,12 +3,68 @@ import { addIcon } from "@iconify/vue/dist/offline";
|
|||||||
|
|
||||||
// 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标
|
// 本地菜单图标,后端在路由的 icon 中返回对应的图标字符串并且前端在此处使用 addIcon 添加即可渲染菜单图标
|
||||||
// @iconify-icons/ep
|
// @iconify-icons/ep
|
||||||
|
import Menu from "@iconify-icons/ep/menu";
|
||||||
|
import Edit from "@iconify-icons/ep/edit";
|
||||||
|
import SetUp from "@iconify-icons/ep/set-up";
|
||||||
|
import Guide from "@iconify-icons/ep/guide";
|
||||||
|
import Monitor from "@iconify-icons/ep/monitor";
|
||||||
import Lollipop from "@iconify-icons/ep/lollipop";
|
import Lollipop from "@iconify-icons/ep/lollipop";
|
||||||
|
import Histogram from "@iconify-icons/ep/histogram";
|
||||||
import HomeFilled from "@iconify-icons/ep/home-filled";
|
import HomeFilled from "@iconify-icons/ep/home-filled";
|
||||||
|
addIcon("ep:menu", Menu);
|
||||||
|
addIcon("ep:edit", Edit);
|
||||||
|
addIcon("ep:set-up", SetUp);
|
||||||
|
addIcon("ep:guide", Guide);
|
||||||
|
addIcon("ep:monitor", Monitor);
|
||||||
addIcon("ep:lollipop", Lollipop);
|
addIcon("ep:lollipop", Lollipop);
|
||||||
|
addIcon("ep:histogram", Histogram);
|
||||||
addIcon("ep:home-filled", HomeFilled);
|
addIcon("ep:home-filled", HomeFilled);
|
||||||
// @iconify-icons/ri
|
// @iconify-icons/ri
|
||||||
|
import Tag from "@iconify-icons/ri/bookmark-2-line";
|
||||||
|
import Ppt from "@iconify-icons/ri/file-ppt-2-line";
|
||||||
|
import Card from "@iconify-icons/ri/bank-card-line";
|
||||||
|
import Role from "@iconify-icons/ri/admin-fill";
|
||||||
|
import Info from "@iconify-icons/ri/file-info-line";
|
||||||
|
import Dept from "@iconify-icons/ri/git-branch-line";
|
||||||
|
import Table from "@iconify-icons/ri/table-line";
|
||||||
|
import Links from "@iconify-icons/ri/links-fill";
|
||||||
import Search from "@iconify-icons/ri/search-line";
|
import Search from "@iconify-icons/ri/search-line";
|
||||||
|
import FlUser from "@iconify-icons/ri/admin-line";
|
||||||
|
import Setting from "@iconify-icons/ri/settings-3-line";
|
||||||
|
import MindMap from "@iconify-icons/ri/mind-map";
|
||||||
|
import BarChart from "@iconify-icons/ri/bar-chart-horizontal-line";
|
||||||
|
import LoginLog from "@iconify-icons/ri/window-line";
|
||||||
|
import Artboard from "@iconify-icons/ri/artboard-line";
|
||||||
|
import SystemLog from "@iconify-icons/ri/file-search-line";
|
||||||
|
import ListCheck from "@iconify-icons/ri/list-check";
|
||||||
|
import UbuntuFill from "@iconify-icons/ri/ubuntu-fill";
|
||||||
|
import OnlineUser from "@iconify-icons/ri/user-voice-line";
|
||||||
|
import EditBoxLine from "@iconify-icons/ri/edit-box-line";
|
||||||
|
import OperationLog from "@iconify-icons/ri/history-fill";
|
||||||
import InformationLine from "@iconify-icons/ri/information-line";
|
import InformationLine from "@iconify-icons/ri/information-line";
|
||||||
|
import TerminalWindowLine from "@iconify-icons/ri/terminal-window-line";
|
||||||
|
import CheckboxCircleLine from "@iconify-icons/ri/checkbox-circle-line";
|
||||||
|
addIcon("ri:bookmark-2-line", Tag);
|
||||||
|
addIcon("ri:file-ppt-2-line", Ppt);
|
||||||
|
addIcon("ri:bank-card-line", Card);
|
||||||
|
addIcon("ri:admin-fill", Role);
|
||||||
|
addIcon("ri:file-info-line", Info);
|
||||||
|
addIcon("ri:git-branch-line", Dept);
|
||||||
|
addIcon("ri:links-fill", Links);
|
||||||
|
addIcon("ri:table-line", Table);
|
||||||
addIcon("ri:search-line", Search);
|
addIcon("ri:search-line", Search);
|
||||||
|
addIcon("ri:admin-line", FlUser);
|
||||||
|
addIcon("ri:settings-3-line", Setting);
|
||||||
|
addIcon("ri:mind-map", MindMap);
|
||||||
|
addIcon("ri:bar-chart-horizontal-line", BarChart);
|
||||||
|
addIcon("ri:window-line", LoginLog);
|
||||||
|
addIcon("ri:file-search-line", SystemLog);
|
||||||
|
addIcon("ri:artboard-line", Artboard);
|
||||||
|
addIcon("ri:list-check", ListCheck);
|
||||||
|
addIcon("ri:ubuntu-fill", UbuntuFill);
|
||||||
|
addIcon("ri:user-voice-line", OnlineUser);
|
||||||
|
addIcon("ri:edit-box-line", EditBoxLine);
|
||||||
|
addIcon("ri:history-fill", OperationLog);
|
||||||
addIcon("ri:information-line", InformationLine);
|
addIcon("ri:information-line", InformationLine);
|
||||||
|
addIcon("ri:terminal-window-line", TerminalWindowLine);
|
||||||
|
addIcon("ri:checkbox-circle-line", CheckboxCircleLine);
|
||||||
|
|||||||
7
src/components/ReSelector/index.ts
Normal file
7
src/components/ReSelector/index.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
import reSelector from "./src";
|
||||||
|
import { withInstall } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
/** 选择器组件 */
|
||||||
|
export const ReSelector = withInstall(reSelector);
|
||||||
|
|
||||||
|
export default ReSelector;
|
||||||
28
src/components/ReSelector/src/index.css
Normal file
28
src/components/ReSelector/src/index.css
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
.hs-rate__icon {
|
||||||
|
font-size: 18px;
|
||||||
|
transition: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hs-item {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hs-on {
|
||||||
|
background-color: #409eff;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hs-range {
|
||||||
|
background-color: #f2f6fc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.both-left-sides {
|
||||||
|
border-radius: 50% 0 0 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.both-right-sides {
|
||||||
|
border-radius: 0 50% 50% 0;
|
||||||
|
}
|
||||||
327
src/components/ReSelector/src/index.tsx
Normal file
327
src/components/ReSelector/src/index.tsx
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
import "./index.css";
|
||||||
|
import {
|
||||||
|
unref,
|
||||||
|
computed,
|
||||||
|
nextTick,
|
||||||
|
onBeforeMount,
|
||||||
|
defineComponent,
|
||||||
|
getCurrentInstance
|
||||||
|
} from "vue";
|
||||||
|
import { addClass, removeClass, toggleClass } from "@pureadmin/utils";
|
||||||
|
|
||||||
|
const stayClass = "stay"; //鼠标点击
|
||||||
|
const activeClass = "hs-on"; //鼠标移动上去
|
||||||
|
const voidClass = "hs-off"; //鼠标移开
|
||||||
|
const inRange = "hs-range"; //当前选中的两个元素之间的背景
|
||||||
|
const bothLeftSides = "both-left-sides";
|
||||||
|
const bothRightSides = "both-right-sides";
|
||||||
|
let selectedDirection = "right"; //默认从左往右,索引变大
|
||||||
|
|
||||||
|
let overList = [];
|
||||||
|
// 存放第一个选中的元素和最后一个选中元素,只能存放这两个元素
|
||||||
|
let selectedList = [];
|
||||||
|
|
||||||
|
const props = {
|
||||||
|
HsKey: {
|
||||||
|
type: Number || String,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 回显数据的索引,长度必须是2
|
||||||
|
echo: {
|
||||||
|
type: Array,
|
||||||
|
default() {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
name: "ReSelector",
|
||||||
|
props,
|
||||||
|
emits: ["selectedVal"],
|
||||||
|
setup(props, { emit }) {
|
||||||
|
const instance = getCurrentInstance();
|
||||||
|
const currentValue = props.value;
|
||||||
|
|
||||||
|
const rateDisabled = computed(() => {
|
||||||
|
return props.disabled;
|
||||||
|
});
|
||||||
|
|
||||||
|
const classes = computed(() => {
|
||||||
|
const result = [];
|
||||||
|
let i = 0;
|
||||||
|
let threshold = currentValue;
|
||||||
|
if (currentValue !== Math.floor(currentValue)) {
|
||||||
|
threshold--;
|
||||||
|
}
|
||||||
|
for (; i < threshold; i++) {
|
||||||
|
result.push(activeClass);
|
||||||
|
}
|
||||||
|
for (; i < props.max.length; i++) {
|
||||||
|
result.push(voidClass);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 鼠标移入
|
||||||
|
const setCurrentValue = index => {
|
||||||
|
if (props.disabled) return;
|
||||||
|
// 当选中一个元素后,开始添加背景色
|
||||||
|
if (selectedList.length === 1) {
|
||||||
|
if (overList.length < 1) overList.push({ index });
|
||||||
|
|
||||||
|
let firstIndex = overList[0].index;
|
||||||
|
|
||||||
|
// 往右走,索引变大
|
||||||
|
if (index > firstIndex) {
|
||||||
|
selectedDirection = "right";
|
||||||
|
toggleClass(
|
||||||
|
false,
|
||||||
|
bothRightSides,
|
||||||
|
document.querySelector(".hs-select__item" + selectedList[0].index)
|
||||||
|
);
|
||||||
|
|
||||||
|
while (index >= firstIndex) {
|
||||||
|
addClass(
|
||||||
|
document.querySelector(".hs-select__item" + firstIndex),
|
||||||
|
inRange
|
||||||
|
);
|
||||||
|
firstIndex++;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
selectedDirection = "left";
|
||||||
|
toggleClass(
|
||||||
|
true,
|
||||||
|
bothRightSides,
|
||||||
|
document.querySelector(".hs-select__item" + selectedList[0].index)
|
||||||
|
);
|
||||||
|
|
||||||
|
while (index <= firstIndex) {
|
||||||
|
addClass(
|
||||||
|
document.querySelector(".hs-select__item" + firstIndex),
|
||||||
|
inRange
|
||||||
|
);
|
||||||
|
firstIndex--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addClass(document.querySelector("." + voidClass + index), activeClass);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标离开
|
||||||
|
const resetCurrentValue = index => {
|
||||||
|
if (props.disabled) return;
|
||||||
|
// 移除先检查是否选中 选中则返回false 不移除
|
||||||
|
const currentHsDom = document.querySelector("." + voidClass + index);
|
||||||
|
if (currentHsDom.className.includes(stayClass)) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
removeClass(currentHsDom, activeClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 当选中一个元素后,开始移除背景色
|
||||||
|
if (selectedList.length === 1) {
|
||||||
|
const firstIndex = overList[0].index;
|
||||||
|
if (index >= firstIndex) {
|
||||||
|
for (let i = 0; i <= index; i++) {
|
||||||
|
removeClass(
|
||||||
|
document.querySelector(".hs-select__item" + i),
|
||||||
|
inRange
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
while (index <= firstIndex) {
|
||||||
|
removeClass(
|
||||||
|
document.querySelector(".hs-select__item" + index),
|
||||||
|
inRange
|
||||||
|
);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 鼠标点击
|
||||||
|
const selectValue = (index, item) => {
|
||||||
|
if (props.disabled) return;
|
||||||
|
const len = selectedList.length;
|
||||||
|
|
||||||
|
if (len < 2) {
|
||||||
|
selectedList.push({ item, index });
|
||||||
|
addClass(document.querySelector("." + voidClass + index), stayClass);
|
||||||
|
|
||||||
|
addClass(
|
||||||
|
document.querySelector(".hs-select__item" + selectedList[0].index),
|
||||||
|
bothLeftSides
|
||||||
|
);
|
||||||
|
|
||||||
|
if (selectedList[1]) {
|
||||||
|
if (selectedDirection === "right") {
|
||||||
|
addClass(
|
||||||
|
document.querySelector(
|
||||||
|
".hs-select__item" + selectedList[1].index
|
||||||
|
),
|
||||||
|
bothRightSides
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
addClass(
|
||||||
|
document.querySelector(
|
||||||
|
".hs-select__item" + selectedList[1].index
|
||||||
|
),
|
||||||
|
bothLeftSides
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (len === 1) {
|
||||||
|
// 顺时针排序
|
||||||
|
if (selectedDirection === "right") {
|
||||||
|
emit("selectedVal", {
|
||||||
|
left: selectedList[0].item,
|
||||||
|
right: selectedList[1].item,
|
||||||
|
whole: selectedList
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
emit("selectedVal", {
|
||||||
|
left: selectedList[1].item,
|
||||||
|
right: selectedList[0].item,
|
||||||
|
whole: selectedList
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nextTick(() => {
|
||||||
|
selectedList.forEach(v => {
|
||||||
|
removeClass(
|
||||||
|
document.querySelector("." + voidClass + v.index),
|
||||||
|
activeClass,
|
||||||
|
stayClass
|
||||||
|
);
|
||||||
|
|
||||||
|
removeClass(
|
||||||
|
document.querySelector(".hs-select__item" + v.index),
|
||||||
|
bothLeftSides,
|
||||||
|
bothRightSides
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
selectedList = [];
|
||||||
|
overList = [];
|
||||||
|
for (let i = 0; i <= props.max.length; i++) {
|
||||||
|
const currentDom = document.querySelector(".hs-select__item" + i);
|
||||||
|
if (currentDom) {
|
||||||
|
removeClass(currentDom, inRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
selectedList.push({ item, index });
|
||||||
|
addClass(document.querySelector("." + voidClass + index), stayClass);
|
||||||
|
|
||||||
|
addClass(
|
||||||
|
document.querySelector(".hs-select__item" + selectedList[0].index),
|
||||||
|
bothLeftSides
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 回显数据
|
||||||
|
const echoView = item => {
|
||||||
|
if (item.length === 0) return;
|
||||||
|
|
||||||
|
if (item.length > 2 || item.length === 1) {
|
||||||
|
throw "传入的数组长度必须是2";
|
||||||
|
}
|
||||||
|
|
||||||
|
item.sort((a, b) => {
|
||||||
|
return a - b;
|
||||||
|
});
|
||||||
|
|
||||||
|
addClass(
|
||||||
|
instance.refs["hsdiv" + props.HsKey + item[0]] as Element,
|
||||||
|
activeClass,
|
||||||
|
stayClass
|
||||||
|
);
|
||||||
|
|
||||||
|
addClass(
|
||||||
|
instance.refs["hstd" + props.HsKey + item[0]] as Element,
|
||||||
|
bothLeftSides
|
||||||
|
);
|
||||||
|
|
||||||
|
addClass(
|
||||||
|
instance.refs["hsdiv" + props.HsKey + item[1]] as Element,
|
||||||
|
activeClass,
|
||||||
|
stayClass
|
||||||
|
);
|
||||||
|
|
||||||
|
addClass(
|
||||||
|
instance.refs["hstd" + props.HsKey + item[1]] as Element,
|
||||||
|
bothRightSides
|
||||||
|
);
|
||||||
|
|
||||||
|
while (item[1] >= item[0]) {
|
||||||
|
addClass(
|
||||||
|
instance.refs["hstd" + props.HsKey + item[0]] as Element,
|
||||||
|
inRange
|
||||||
|
);
|
||||||
|
item[0]++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
echoView(props.echo);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => (
|
||||||
|
<>
|
||||||
|
<table cellspacing="0" cellpadding="0">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
{props.max.map((item, key) => {
|
||||||
|
return (
|
||||||
|
<td
|
||||||
|
data-index={props.HsKey}
|
||||||
|
ref={`hstd${props.HsKey}${key}`}
|
||||||
|
class={`hs-select__item${key}`}
|
||||||
|
onMousemove={() => setCurrentValue(key)}
|
||||||
|
onMouseleave={() => resetCurrentValue(key)}
|
||||||
|
onClick={() => selectValue(key, item)}
|
||||||
|
style={{
|
||||||
|
cursor: unref(rateDisabled) ? "auto" : "pointer",
|
||||||
|
textAlign: "center"
|
||||||
|
}}
|
||||||
|
key={key}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={`hsdiv${props.HsKey}${key}`}
|
||||||
|
class={`hs-item ${[unref(classes)[key] + key]}`}
|
||||||
|
>
|
||||||
|
<span>{item}</span>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -12,7 +12,7 @@ defineOptions({
|
|||||||
* 表单
|
* 表单
|
||||||
*/
|
*/
|
||||||
interface FormItemProps {
|
interface FormItemProps {
|
||||||
higherDeptOptions: Record<string, unknown>[];
|
higherOptions: Record<string, unknown>[];
|
||||||
id: string;
|
id: string;
|
||||||
parent_id: string;
|
parent_id: string;
|
||||||
name: string;
|
name: string;
|
||||||
@@ -61,7 +61,7 @@ const formRules = reactive<FormRules>({
|
|||||||
const props = withDefaults(defineProps<FormProps>(), {
|
const props = withDefaults(defineProps<FormProps>(), {
|
||||||
formInline: () => ({
|
formInline: () => ({
|
||||||
id: "",
|
id: "",
|
||||||
higherDeptOptions: [],
|
higherOptions: [],
|
||||||
parent_id: "",
|
parent_id: "",
|
||||||
name: "",
|
name: "",
|
||||||
principal: "",
|
principal: "",
|
||||||
@@ -97,7 +97,7 @@ defineExpose({ getRef });
|
|||||||
<el-cascader
|
<el-cascader
|
||||||
v-model="newFormInline.parent_id"
|
v-model="newFormInline.parent_id"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:options="newFormInline.higherDeptOptions"
|
:options="newFormInline.higherOptions"
|
||||||
:props="{
|
:props="{
|
||||||
value: 'id',
|
value: 'id',
|
||||||
label: 'name',
|
label: 'name',
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import { handleTree } from "@/utils/tree";
|
|||||||
import { message } from "@/utils/message";
|
import { message } from "@/utils/message";
|
||||||
import { addDialog } from "@/components/ReDialog";
|
import { addDialog } from "@/components/ReDialog";
|
||||||
import { reactive, ref, onMounted, h } from "vue";
|
import { reactive, ref, onMounted, h } from "vue";
|
||||||
import type { FormItemProps } from "../utils/types";
|
|
||||||
import { cloneDeep } from "@pureadmin/utils";
|
import { cloneDeep } from "@pureadmin/utils";
|
||||||
import type { DepartmentInfo } from "types/system";
|
import type { DepartmentInfo } from "types/system";
|
||||||
import { usePublicHooks } from "../../hooks";
|
import { usePublicHooks } from "../../hooks";
|
||||||
@@ -51,7 +50,7 @@ export const useDepartment = () => {
|
|||||||
/**
|
/**
|
||||||
* 标签样式
|
* 标签样式
|
||||||
*/
|
*/
|
||||||
const { tagStyle, formatHigherDeptOptions } = usePublicHooks();
|
const { tagStyle } = usePublicHooks();
|
||||||
/**
|
/**
|
||||||
* 表格列设置
|
* 表格列设置
|
||||||
*/
|
*/
|
||||||
@@ -126,13 +125,23 @@ export const useDepartment = () => {
|
|||||||
formEl.resetFields();
|
formEl.resetFields();
|
||||||
onSearch();
|
onSearch();
|
||||||
};
|
};
|
||||||
|
const formatHigherOptions = treeList => {
|
||||||
const openDialog = (title = "新增", row?: FormItemProps) => {
|
// 根据返回数据的status字段值判断追加是否禁用disabled字段,返回处理后的树结构,用于上级部门级联选择器的展示(实际开发中也是如此,不可能前端需要的每个字段后端都会返回,这时需要前端自行根据后端返回的某些字段做逻辑处理)
|
||||||
|
if (!treeList || !treeList.length) return;
|
||||||
|
const newTreeList = [];
|
||||||
|
for (let i = 0; i < treeList.length; i++) {
|
||||||
|
treeList[i].disabled = treeList[i].status === 0 ? true : false;
|
||||||
|
formatHigherOptions(treeList[i].children);
|
||||||
|
newTreeList.push(treeList[i]);
|
||||||
|
}
|
||||||
|
return newTreeList;
|
||||||
|
};
|
||||||
|
const openDialog = (title = "新增", row?: DepartmentInfo) => {
|
||||||
addDialog({
|
addDialog({
|
||||||
title: `${title}部门`,
|
title: `${title}部门`,
|
||||||
props: {
|
props: {
|
||||||
formInline: {
|
formInline: {
|
||||||
higherDeptOptions: formatHigherDeptOptions(cloneDeep(dataList.value)),
|
higherDeptOptions: formatHigherOptions(cloneDeep(dataList.value)),
|
||||||
id: row?.id ?? "",
|
id: row?.id ?? "",
|
||||||
parent_id: row?.parent_id ?? "",
|
parent_id: row?.parent_id ?? "",
|
||||||
name: row?.name ?? "",
|
name: row?.name ?? "",
|
||||||
@@ -151,9 +160,7 @@ export const useDepartment = () => {
|
|||||||
contentRenderer: () =>
|
contentRenderer: () =>
|
||||||
h(editForm, {
|
h(editForm, {
|
||||||
formInline: {
|
formInline: {
|
||||||
higherDeptOptions: formatHigherDeptOptions(
|
higherOptions: formatHigherOptions(cloneDeep(dataList.value)),
|
||||||
cloneDeep(dataList.value)
|
|
||||||
),
|
|
||||||
id: row?.id ?? "",
|
id: row?.id ?? "",
|
||||||
parent_id: row?.parent_id ?? "",
|
parent_id: row?.parent_id ?? "",
|
||||||
name: row?.name ?? "",
|
name: row?.name ?? "",
|
||||||
@@ -168,7 +175,7 @@ export const useDepartment = () => {
|
|||||||
}),
|
}),
|
||||||
beforeSure: (done, { options }) => {
|
beforeSure: (done, { options }) => {
|
||||||
const FormRef = formRef.value.getRef();
|
const FormRef = formRef.value.getRef();
|
||||||
const curData = options.props.formInline as FormItemProps;
|
const curData = options.props.formInline as DepartmentInfo;
|
||||||
function chores() {
|
function chores() {
|
||||||
message(`您${title}了部门名称为${curData.name}的这条数据`, {
|
message(`您${title}了部门名称为${curData.name}的这条数据`, {
|
||||||
type: "success"
|
type: "success"
|
||||||
|
|||||||
@@ -28,26 +28,12 @@ export function usePublicHooks() {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const formatHigherDeptOptions = treeList => {
|
|
||||||
// 根据返回数据的status字段值判断追加是否禁用disabled字段,返回处理后的树结构,用于上级部门级联选择器的展示(实际开发中也是如此,不可能前端需要的每个字段后端都会返回,这时需要前端自行根据后端返回的某些字段做逻辑处理)
|
|
||||||
if (!treeList || !treeList.length) return;
|
|
||||||
const newTreeList = [];
|
|
||||||
for (let i = 0; i < treeList.length; i++) {
|
|
||||||
treeList[i].disabled = treeList[i].status === 0 ? true : false;
|
|
||||||
formatHigherDeptOptions(treeList[i].children);
|
|
||||||
newTreeList.push(treeList[i]);
|
|
||||||
}
|
|
||||||
return newTreeList;
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
/** 当前网页是否为`dark`模式 */
|
/** 当前网页是否为`dark`模式 */
|
||||||
isDark,
|
isDark,
|
||||||
/** 表现更鲜明的`el-switch`组件 */
|
/** 表现更鲜明的`el-switch`组件 */
|
||||||
switchStyle,
|
switchStyle,
|
||||||
/** 表现更鲜明的`el-tag`组件 */
|
/** 表现更鲜明的`el-tag`组件 */
|
||||||
tagStyle,
|
tagStyle
|
||||||
/** 获取上级部门级联选择器的数据结构*/
|
|
||||||
formatHigherDeptOptions
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,10 +44,10 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="onSearch"
|
@click="onSearch"
|
||||||
>
|
>
|
||||||
搜索
|
{{ t("buttons:Search") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
重置
|
{{ t("buttons:Reset") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -58,7 +58,7 @@
|
|||||||
:icon="useRenderIcon(AddFill)"
|
:icon="useRenderIcon(AddFill)"
|
||||||
@click="openDialog('新增')"
|
@click="openDialog('新增')"
|
||||||
>
|
>
|
||||||
新增
|
{{ t("buttons:Add") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
@@ -116,7 +116,7 @@
|
|||||||
:icon="useRenderIcon(EditPen)"
|
:icon="useRenderIcon(EditPen)"
|
||||||
@click="openDialog('修改', row)"
|
@click="openDialog('修改', row)"
|
||||||
>
|
>
|
||||||
修改
|
{{ t("buttons:Update") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-popconfirm
|
<el-popconfirm
|
||||||
:title="`是否确认删除国际化key为 ${row.key} 的这条数据`"
|
:title="`是否确认删除国际化key为 ${row.key} 的这条数据`"
|
||||||
@@ -130,7 +130,7 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(Delete)"
|
:icon="useRenderIcon(Delete)"
|
||||||
>
|
>
|
||||||
删除
|
{{ t("buttons:Delete") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-popconfirm>
|
</el-popconfirm>
|
||||||
@@ -147,12 +147,14 @@ defineOptions({
|
|||||||
});
|
});
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useI18n } from "./hook";
|
import { useI18n } from "./hook";
|
||||||
|
import { useI18n as i18n } from "vue-i18n";
|
||||||
import { PureTableBar } from "@/components/RePureTableBar";
|
import { PureTableBar } from "@/components/RePureTableBar";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
import Delete from "@iconify-icons/ep/delete";
|
import Delete from "@iconify-icons/ep/delete";
|
||||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||||
import Refresh from "@iconify-icons/ep/refresh";
|
import Refresh from "@iconify-icons/ep/refresh";
|
||||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||||
|
const { t } = i18n();
|
||||||
/**
|
/**
|
||||||
* 表格Ref
|
* 表格Ref
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -75,7 +75,7 @@ export const useLocale = (tableRef: Ref) => {
|
|||||||
{
|
{
|
||||||
label: "操作",
|
label: "操作",
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
width: 200,
|
width: 250,
|
||||||
slot: "operation"
|
slot: "operation"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -29,10 +29,10 @@
|
|||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="onSearch"
|
@click="onSearch"
|
||||||
>
|
>
|
||||||
搜索
|
{{ t("buttons:Search") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
重置
|
{{ t("buttons:Reset") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
@@ -43,7 +43,7 @@
|
|||||||
:icon="useRenderIcon(AddFill)"
|
:icon="useRenderIcon(AddFill)"
|
||||||
@click="openDialog('新增')"
|
@click="openDialog('新增')"
|
||||||
>
|
>
|
||||||
新增
|
{{ t("buttons:Add") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
@@ -101,7 +101,7 @@
|
|||||||
:icon="useRenderIcon(EditPen)"
|
:icon="useRenderIcon(EditPen)"
|
||||||
@click="openDialog('修改', row)"
|
@click="openDialog('修改', row)"
|
||||||
>
|
>
|
||||||
修改
|
{{ t("buttons:Update") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-popconfirm
|
<el-popconfirm
|
||||||
:title="`是否确认导出语言名称为 ${row.name} 的这条数据为yaml文件`"
|
:title="`是否确认导出语言名称为 ${row.name} 的这条数据为yaml文件`"
|
||||||
@@ -115,7 +115,7 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(Download)"
|
:icon="useRenderIcon(Download)"
|
||||||
>
|
>
|
||||||
导出
|
{{ t("buttons:Export") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-popconfirm>
|
</el-popconfirm>
|
||||||
@@ -131,7 +131,7 @@
|
|||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(Delete)"
|
:icon="useRenderIcon(Delete)"
|
||||||
>
|
>
|
||||||
删除
|
{{ t("buttons:Delete") }}
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
</el-popconfirm>
|
</el-popconfirm>
|
||||||
@@ -150,11 +150,13 @@ import { ref } from "vue";
|
|||||||
import { useLocale } from "./hook";
|
import { useLocale } from "./hook";
|
||||||
import { PureTableBar } from "@/components/RePureTableBar";
|
import { PureTableBar } from "@/components/RePureTableBar";
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
import Delete from "@iconify-icons/ep/delete";
|
import Delete from "@iconify-icons/ep/delete";
|
||||||
import EditPen from "@iconify-icons/ep/edit-pen";
|
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||||
import Refresh from "@iconify-icons/ep/refresh";
|
import Refresh from "@iconify-icons/ep/refresh";
|
||||||
import AddFill from "@iconify-icons/ri/add-circle-line";
|
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||||
import Download from "@iconify-icons/ri/file-download-line";
|
import Download from "@iconify-icons/ri/file-download-line";
|
||||||
|
const { t } = useI18n();
|
||||||
/**
|
/**
|
||||||
* 表格Ref
|
* 表格Ref
|
||||||
*/
|
*/
|
||||||
|
|||||||
511
src/views/system/permission/components/form.vue
Normal file
511
src/views/system/permission/components/form.vue
Normal file
@@ -0,0 +1,511 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { reactive, ref } from "vue";
|
||||||
|
import ReCol from "@/components/ReCol";
|
||||||
|
import { transformI18n } from "@/plugins/i18n";
|
||||||
|
import { IconSelect } from "@/components/ReIcon";
|
||||||
|
import Segmented, { OptionsType } from "@/components/ReSegmented";
|
||||||
|
import ReAnimateSelector from "@/components/ReAnimateSelector";
|
||||||
|
|
||||||
|
import type { FormRules } from "element-plus";
|
||||||
|
const menuTypeOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "菜单",
|
||||||
|
value: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "iframe",
|
||||||
|
value: 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "外链",
|
||||||
|
value: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "按钮",
|
||||||
|
value: 3
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const showLinkOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "显示",
|
||||||
|
tip: "会在菜单中显示",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "隐藏",
|
||||||
|
tip: "不会在菜单中显示",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const fixedTagOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "固定",
|
||||||
|
tip: "当前菜单名称固定显示在标签页且不可关闭",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "不固定",
|
||||||
|
tip: "当前菜单名称不固定显示在标签页且可关闭",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const keepAliveOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "缓存",
|
||||||
|
tip: "会保存该页面的整体状态,刷新后会清空状态",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "不缓存",
|
||||||
|
tip: "不会保存该页面的整体状态",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const hiddenTagOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "允许",
|
||||||
|
tip: "当前菜单名称或自定义信息允许添加到标签页",
|
||||||
|
value: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "禁止",
|
||||||
|
tip: "当前菜单名称或自定义信息禁止添加到标签页",
|
||||||
|
value: true
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const showParentOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "显示",
|
||||||
|
tip: "会显示父级菜单",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "隐藏",
|
||||||
|
tip: "不会显示父级菜单",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const frameLoadingOptions: Array<OptionsType> = [
|
||||||
|
{
|
||||||
|
label: "开启",
|
||||||
|
tip: "有首次加载动画",
|
||||||
|
value: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "关闭",
|
||||||
|
tip: "无首次加载动画",
|
||||||
|
value: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
interface FormItemProps {
|
||||||
|
/** 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)*/
|
||||||
|
id: string;
|
||||||
|
menu_type: number;
|
||||||
|
higherOptions: Record<string, unknown>[];
|
||||||
|
parent_id: string;
|
||||||
|
title: string;
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
component: string;
|
||||||
|
rank: number;
|
||||||
|
redirect: string;
|
||||||
|
icon: string;
|
||||||
|
extra_icon: string;
|
||||||
|
enter_transition: string;
|
||||||
|
leave_transition: string;
|
||||||
|
active_path: string;
|
||||||
|
auths: string | null;
|
||||||
|
frame_src: string;
|
||||||
|
frame_loading: boolean;
|
||||||
|
keep_alive: boolean;
|
||||||
|
hidden_tag: boolean;
|
||||||
|
fixed_tag: boolean;
|
||||||
|
show_link: boolean;
|
||||||
|
show_parent: boolean;
|
||||||
|
}
|
||||||
|
interface FormProps {
|
||||||
|
formInline: FormItemProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 自定义表单规则校验 */
|
||||||
|
const formRules = reactive<FormRules>({
|
||||||
|
title: [{ required: true, message: "菜单名称为必填项", trigger: "blur" }],
|
||||||
|
name: [{ required: true, message: "路由名称为必填项", trigger: "blur" }],
|
||||||
|
path: [{ required: true, message: "路由路径为必填项", trigger: "blur" }],
|
||||||
|
auths: [{ required: true, message: "权限标识为必填项", trigger: "blur" }]
|
||||||
|
});
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "SystemPermissionForm"
|
||||||
|
});
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<FormProps>(), {
|
||||||
|
formInline: () => ({
|
||||||
|
id: "",
|
||||||
|
menu_type: 0,
|
||||||
|
higherOptions: [],
|
||||||
|
parent_id: "",
|
||||||
|
title: "",
|
||||||
|
name: "",
|
||||||
|
path: "",
|
||||||
|
component: "",
|
||||||
|
rank: 99,
|
||||||
|
redirect: "",
|
||||||
|
icon: "",
|
||||||
|
extra_icon: "",
|
||||||
|
enter_transition: "",
|
||||||
|
leave_transition: "",
|
||||||
|
active_path: "",
|
||||||
|
auths: "",
|
||||||
|
frame_src: "",
|
||||||
|
frame_loading: true,
|
||||||
|
keep_alive: false,
|
||||||
|
hidden_tag: false,
|
||||||
|
fixed_tag: false,
|
||||||
|
show_link: true,
|
||||||
|
show_parent: false
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const ruleFormRef = ref();
|
||||||
|
const newFormInline = ref(props.formInline);
|
||||||
|
function getRef() {
|
||||||
|
return ruleFormRef.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ getRef });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormRef"
|
||||||
|
:model="newFormInline"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="82px"
|
||||||
|
>
|
||||||
|
<el-row :gutter="30">
|
||||||
|
<re-col>
|
||||||
|
<el-form-item label="权限类型">
|
||||||
|
<Segmented
|
||||||
|
v-model="newFormInline.menu_type"
|
||||||
|
:options="menuTypeOptions"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col>
|
||||||
|
<el-form-item label="上级菜单">
|
||||||
|
<el-cascader
|
||||||
|
v-model="newFormInline.parent_id"
|
||||||
|
class="w-full"
|
||||||
|
:options="newFormInline.higherOptions"
|
||||||
|
:props="{
|
||||||
|
value: 'id',
|
||||||
|
label: 'title',
|
||||||
|
emitPath: false,
|
||||||
|
checkStrictly: true
|
||||||
|
}"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
placeholder="请选择上级菜单"
|
||||||
|
>
|
||||||
|
<template #default="{ node, data }">
|
||||||
|
<span>{{ transformI18n(data.title) }}</span>
|
||||||
|
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||||
|
</template>
|
||||||
|
</el-cascader>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="菜单名称" prop="title">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.title"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入菜单名称"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col
|
||||||
|
v-if="newFormInline.menu_type !== 3"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="路由名称" prop="name">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.name"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入路由名称"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-if="newFormInline.menu_type !== 3"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="路由路径" prop="path">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.path"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入路由路径"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
|
<!-- 按钮级别权限设置 -->
|
||||||
|
<el-form-item label="权限标识" prop="auths">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.auths"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入权限标识"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type === 0"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="组件路径">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.component"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入组件路径"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="菜单排序">
|
||||||
|
<el-input-number
|
||||||
|
v-model="newFormInline.rank"
|
||||||
|
class="!w-full"
|
||||||
|
:min="1"
|
||||||
|
:max="9999"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type === 0"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="路由重定向">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.redirect"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入默认跳转地址"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type !== 3"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="菜单图标">
|
||||||
|
<IconSelect v-model="newFormInline.icon" class="w-full" />
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type !== 3"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="右侧图标">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.extra_icon"
|
||||||
|
clearable
|
||||||
|
placeholder="菜单名称右侧的额外图标"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type < 2"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="进场动画">
|
||||||
|
<ReAnimateSelector
|
||||||
|
v-model="newFormInline.enter_transition"
|
||||||
|
placeholder="请选择页面进场加载动画"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type < 2"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="离场动画">
|
||||||
|
<ReAnimateSelector
|
||||||
|
v-model="newFormInline.leave_transition"
|
||||||
|
placeholder="请选择页面离场加载动画"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type === 0"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="菜单激活">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.active_path"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入需要激活的菜单"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type === 1"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<!-- iframe -->
|
||||||
|
<el-form-item label="链接地址">
|
||||||
|
<el-input
|
||||||
|
v-model="newFormInline.frame_src"
|
||||||
|
clearable
|
||||||
|
placeholder="请输入 iframe 链接地址"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col
|
||||||
|
v-if="newFormInline.menu_type === 1"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="加载动画">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.frame_loading ? 0 : 1"
|
||||||
|
:options="frameLoadingOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.frame_loading = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type !== 3"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="菜单">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.show_link ? 0 : 1"
|
||||||
|
:options="showLinkOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.show_link = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type !== 3"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="父级菜单">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.show_parent ? 0 : 1"
|
||||||
|
:options="showParentOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.show_parent = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type < 2"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="缓存页面">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.keep_alive ? 0 : 1"
|
||||||
|
:options="keepAliveOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.keep_alive = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type < 2"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="标签页">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.hidden_tag ? 1 : 0"
|
||||||
|
:options="hiddenTagOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.hidden_tag = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col
|
||||||
|
v-show="newFormInline.menu_type < 2"
|
||||||
|
:value="12"
|
||||||
|
:xs="24"
|
||||||
|
:sm="24"
|
||||||
|
>
|
||||||
|
<el-form-item label="固定标签页">
|
||||||
|
<Segmented
|
||||||
|
:modelValue="newFormInline.fixed_tag ? 0 : 1"
|
||||||
|
:options="fixedTagOptions"
|
||||||
|
@change="
|
||||||
|
({ option: { value } }) => {
|
||||||
|
newFormInline.fixed_tag = value;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
167
src/views/system/permission/index.vue
Normal file
167
src/views/system/permission/index.vue
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { usePermission } from "./utils/hook";
|
||||||
|
import { transformI18n } from "@/plugins/i18n";
|
||||||
|
import { PureTableBar } from "@/components/RePureTableBar";
|
||||||
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
|
import { useI18n } from "vue-i18n";
|
||||||
|
import Delete from "@iconify-icons/ep/delete";
|
||||||
|
import EditPen from "@iconify-icons/ep/edit-pen";
|
||||||
|
import Refresh from "@iconify-icons/ep/refresh";
|
||||||
|
import AddFill from "@iconify-icons/ri/add-circle-line";
|
||||||
|
import { onBeforeRouteUpdate } from "vue-router";
|
||||||
|
const { t } = useI18n();
|
||||||
|
defineOptions({
|
||||||
|
name: "SystemPermission"
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* 权限Ref
|
||||||
|
*/
|
||||||
|
const formRef = ref();
|
||||||
|
/**
|
||||||
|
* 表格Ref
|
||||||
|
*/
|
||||||
|
const tableRef = ref();
|
||||||
|
const {
|
||||||
|
form,
|
||||||
|
loading,
|
||||||
|
columns,
|
||||||
|
dataList,
|
||||||
|
onSearch,
|
||||||
|
resetForm,
|
||||||
|
openDialog,
|
||||||
|
handleDelete
|
||||||
|
} = usePermission();
|
||||||
|
onBeforeRouteUpdate((to, from, next) => {
|
||||||
|
onSearch();
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="main">
|
||||||
|
<el-form
|
||||||
|
ref="formRef"
|
||||||
|
:inline="true"
|
||||||
|
:model="form"
|
||||||
|
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
|
||||||
|
>
|
||||||
|
<el-form-item label="权限名称:" prop="title">
|
||||||
|
<el-input
|
||||||
|
v-model="form.title"
|
||||||
|
placeholder="请输入权限名称"
|
||||||
|
clearable
|
||||||
|
class="!w-[180px]"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="useRenderIcon('ri:search-line')"
|
||||||
|
:loading="loading"
|
||||||
|
@click="onSearch"
|
||||||
|
>
|
||||||
|
{{ t("buttons:Search") }}
|
||||||
|
</el-button>
|
||||||
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
|
{{ t("buttons:Reset") }}
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<PureTableBar
|
||||||
|
title="权限管理"
|
||||||
|
:columns="columns"
|
||||||
|
:isExpandAll="false"
|
||||||
|
:tableRef="tableRef?.getTableRef()"
|
||||||
|
@refresh="onSearch"
|
||||||
|
>
|
||||||
|
<template #buttons>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="useRenderIcon(AddFill)"
|
||||||
|
@click="openDialog()"
|
||||||
|
>
|
||||||
|
{{ t("buttons:Add") }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
|
<pure-table
|
||||||
|
ref="tableRef"
|
||||||
|
adaptive
|
||||||
|
border
|
||||||
|
stripe
|
||||||
|
:adaptiveConfig="{ offsetBottom: 45 }"
|
||||||
|
align-whole="center"
|
||||||
|
row-key="id"
|
||||||
|
showOverflowTooltip
|
||||||
|
table-layout="auto"
|
||||||
|
:loading="loading"
|
||||||
|
:size="size"
|
||||||
|
:data="dataList"
|
||||||
|
:columns="dynamicColumns"
|
||||||
|
:header-cell-style="{
|
||||||
|
background: 'var(--el-fill-color-light)',
|
||||||
|
color: 'var(--el-text-color-primary)'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #operation="{ row }">
|
||||||
|
<el-button
|
||||||
|
class="reset-margin"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
:size="size"
|
||||||
|
:icon="useRenderIcon(EditPen)"
|
||||||
|
@click="openDialog('修改', row)"
|
||||||
|
>
|
||||||
|
{{ t("buttons:Update") }}
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-show="row.menuType !== 3"
|
||||||
|
class="reset-margin"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
:size="size"
|
||||||
|
:icon="useRenderIcon(AddFill)"
|
||||||
|
@click="openDialog('新增', { parent_id: row.id } as any)"
|
||||||
|
>
|
||||||
|
{{ t("buttons:Add") }}
|
||||||
|
</el-button>
|
||||||
|
<el-popconfirm
|
||||||
|
:title="`是否确认删除权限名称为${transformI18n(row.title)}的这条数据${row?.children?.length > 0 ? '。注意下级权限也会一并删除,请谨慎操作' : ''}`"
|
||||||
|
@confirm="handleDelete(row)"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-button
|
||||||
|
class="reset-margin"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
:size="size"
|
||||||
|
:icon="useRenderIcon(Delete)"
|
||||||
|
>
|
||||||
|
{{ t("buttons:Delete") }}
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</template>
|
||||||
|
</pure-table>
|
||||||
|
</template>
|
||||||
|
</PureTableBar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-table__inner-wrapper::before) {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
margin: 24px 24px 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-form {
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
344
src/views/system/permission/utils/hook.tsx
Normal file
344
src/views/system/permission/utils/hook.tsx
Normal file
@@ -0,0 +1,344 @@
|
|||||||
|
import editForm from "../components/form.vue";
|
||||||
|
import { handleTree } from "@/utils/tree";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
|
||||||
|
import {
|
||||||
|
deletePermissionAPI,
|
||||||
|
getPermissionListAPI,
|
||||||
|
postAddPermissionAPI,
|
||||||
|
putUpdatePermissionAPI
|
||||||
|
} from "@/api/system";
|
||||||
|
import { transformI18n } from "@/plugins/i18n";
|
||||||
|
import { addDialog } from "@/components/ReDialog";
|
||||||
|
import { reactive, ref, onMounted, h } from "vue";
|
||||||
|
import { cloneDeep, isAllEmpty, deviceDetection } from "@pureadmin/utils";
|
||||||
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
|
import type { PermissionInfo } from "types/system";
|
||||||
|
export const usePermission = () => {
|
||||||
|
/**
|
||||||
|
* 表单
|
||||||
|
*/
|
||||||
|
const form = reactive({
|
||||||
|
title: ""
|
||||||
|
});
|
||||||
|
/**
|
||||||
|
* 表单Ref
|
||||||
|
*/
|
||||||
|
const formRef = ref();
|
||||||
|
/**
|
||||||
|
* 数据列表
|
||||||
|
*/
|
||||||
|
const dataList = ref<PermissionInfo[]>([]);
|
||||||
|
/**
|
||||||
|
* 加载状态
|
||||||
|
*/
|
||||||
|
const loading = ref(true);
|
||||||
|
/**
|
||||||
|
* 获取菜单类型
|
||||||
|
* @param type 菜单类型
|
||||||
|
* @param text 文本
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const getMenuType = (type, text = false) => {
|
||||||
|
switch (type) {
|
||||||
|
case 0:
|
||||||
|
return text ? "菜单" : "primary";
|
||||||
|
case 1:
|
||||||
|
return text ? "iframe" : "warning";
|
||||||
|
case 2:
|
||||||
|
return text ? "外链" : "danger";
|
||||||
|
case 3:
|
||||||
|
return text ? "按钮" : "info";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 表格数据
|
||||||
|
*/
|
||||||
|
const columns: TableColumnList = [
|
||||||
|
{
|
||||||
|
label: "权限名称",
|
||||||
|
prop: "title",
|
||||||
|
align: "left",
|
||||||
|
cellRenderer: ({ row }) => (
|
||||||
|
<>
|
||||||
|
<span class="inline-block mr-1">
|
||||||
|
{h(useRenderIcon(row.icon), {
|
||||||
|
style: { paddingTop: "1px" }
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
<span>{transformI18n(row.title)}</span>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "权限类型",
|
||||||
|
prop: "menu_type",
|
||||||
|
cellRenderer: ({ row, props }) => (
|
||||||
|
<el-tag
|
||||||
|
size={props.size}
|
||||||
|
type={getMenuType(row.menu_type)}
|
||||||
|
effect="plain"
|
||||||
|
>
|
||||||
|
{getMenuType(row.menu_type, true)}
|
||||||
|
</el-tag>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "路由路径",
|
||||||
|
prop: "path"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "组件路径",
|
||||||
|
prop: "component",
|
||||||
|
formatter: ({ path, component }) =>
|
||||||
|
isAllEmpty(component) ? path : component
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "权限标识",
|
||||||
|
prop: "auths"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "排序",
|
||||||
|
prop: "rank",
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "隐藏",
|
||||||
|
prop: "show_link",
|
||||||
|
formatter: ({ show_link }) => (show_link ? "否" : "是"),
|
||||||
|
width: 100
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "操作",
|
||||||
|
fixed: "right",
|
||||||
|
width: 230,
|
||||||
|
slot: "operation"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
/**
|
||||||
|
* 搜索
|
||||||
|
*/
|
||||||
|
const onSearch = async () => {
|
||||||
|
loading.value = true; // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
|
||||||
|
const res = await getPermissionListAPI({
|
||||||
|
page: 1,
|
||||||
|
pageSize: 9999,
|
||||||
|
...form
|
||||||
|
});
|
||||||
|
if (res.success) {
|
||||||
|
let newData = res.data.result;
|
||||||
|
dataList.value = handleTree(newData, "id", "parent_id"); // 处理成树结构
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 500);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 重置表单
|
||||||
|
* @param formEl
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const resetForm = formEl => {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.resetFields();
|
||||||
|
onSearch();
|
||||||
|
};
|
||||||
|
const formatHigherOptions = treeList => {
|
||||||
|
if (!treeList || !treeList.length) return;
|
||||||
|
const newTreeList = [];
|
||||||
|
for (let i = 0; i < treeList.length; i++) {
|
||||||
|
treeList[i].title = transformI18n(treeList[i].title);
|
||||||
|
formatHigherOptions(treeList[i].children);
|
||||||
|
newTreeList.push(treeList[i]);
|
||||||
|
}
|
||||||
|
return newTreeList;
|
||||||
|
};
|
||||||
|
const openDialog = (title = "新增", row?: PermissionInfo) => {
|
||||||
|
addDialog({
|
||||||
|
title: `${title}权限`,
|
||||||
|
props: {
|
||||||
|
formInline: {
|
||||||
|
id: row?.id ?? "",
|
||||||
|
higherOptions: formatHigherOptions(cloneDeep(dataList.value)),
|
||||||
|
parent_id: row?.parent_id ?? "",
|
||||||
|
title: row?.title ?? "",
|
||||||
|
menu_type: row?.menu_type ?? 0,
|
||||||
|
name: row?.name ?? "",
|
||||||
|
path: row?.path ?? "",
|
||||||
|
component: row?.component ?? "",
|
||||||
|
rank: row?.rank ?? 99,
|
||||||
|
redirect: row?.redirect ?? "",
|
||||||
|
icon: row?.icon ?? "",
|
||||||
|
extra_icon: row?.extra_icon ?? "",
|
||||||
|
enter_transition: row?.enter_transition ?? "",
|
||||||
|
leave_transition: row?.leave_transition ?? "",
|
||||||
|
active_path: row?.active_path ?? "",
|
||||||
|
auths: row?.auths ?? "",
|
||||||
|
frame_src: row?.frame_src ?? "",
|
||||||
|
frame_loading: row?.frame_loading ?? true,
|
||||||
|
keep_alive: row?.keep_alive ?? false,
|
||||||
|
hidden_tag: row?.hidden_tag ?? false,
|
||||||
|
fixed_tag: row?.fixed_tag ?? false,
|
||||||
|
show_link: row?.show_link ?? true,
|
||||||
|
show_parent: row?.show_parent ?? false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
width: "45%",
|
||||||
|
draggable: true,
|
||||||
|
fullscreen: deviceDetection(),
|
||||||
|
fullscreenIcon: true,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
contentRenderer: () =>
|
||||||
|
h(editForm, {
|
||||||
|
ref: formRef,
|
||||||
|
formInline: {
|
||||||
|
id: row?.id ?? "",
|
||||||
|
higherOptions: formatHigherOptions(cloneDeep(dataList.value)),
|
||||||
|
parent_id: row?.parent_id ?? "",
|
||||||
|
title: row?.title ?? "",
|
||||||
|
menu_type: row?.menu_type ?? 0,
|
||||||
|
name: row?.name ?? "",
|
||||||
|
path: row?.path ?? "",
|
||||||
|
component: row?.component ?? "",
|
||||||
|
rank: row?.rank ?? 99,
|
||||||
|
redirect: row?.redirect ?? "",
|
||||||
|
icon: row?.icon ?? "",
|
||||||
|
extra_icon: row?.extra_icon ?? "",
|
||||||
|
enter_transition: row?.enter_transition ?? "",
|
||||||
|
leave_transition: row?.leave_transition ?? "",
|
||||||
|
active_path: row?.active_path ?? "",
|
||||||
|
frame_src: row?.frame_src ?? "",
|
||||||
|
auths: row?.auths ?? "",
|
||||||
|
frame_loading: row?.frame_loading ?? true,
|
||||||
|
keep_alive: row?.keep_alive ?? false,
|
||||||
|
hidden_tag: row?.hidden_tag ?? false,
|
||||||
|
fixed_tag: row?.fixed_tag ?? false,
|
||||||
|
show_link: row?.show_link ?? true,
|
||||||
|
show_parent: row?.show_parent ?? false
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
beforeSure: (done, { options }) => {
|
||||||
|
const FormRef = formRef.value.getRef();
|
||||||
|
const curData = options.props.formInline as PermissionInfo;
|
||||||
|
console.log(curData);
|
||||||
|
function chores() {
|
||||||
|
message(
|
||||||
|
`您${title}了权限名称为${transformI18n(curData.title)}的这条数据`,
|
||||||
|
{
|
||||||
|
type: "success"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
done(); // 关闭弹框
|
||||||
|
onSearch(); // 刷新表格数据
|
||||||
|
}
|
||||||
|
FormRef.validate(async valid => {
|
||||||
|
if (valid) {
|
||||||
|
console.log("curData", curData);
|
||||||
|
// 表单规则校验通过
|
||||||
|
if (title === "新增") {
|
||||||
|
let addForm = {
|
||||||
|
menu_type: 0,
|
||||||
|
parent_id: "",
|
||||||
|
title: "",
|
||||||
|
name: "",
|
||||||
|
path: "",
|
||||||
|
component: "",
|
||||||
|
rank: 99,
|
||||||
|
redirect: "",
|
||||||
|
icon: "",
|
||||||
|
extra_icon: "",
|
||||||
|
enter_transition: "",
|
||||||
|
leave_transition: "",
|
||||||
|
active_path: "",
|
||||||
|
auths: "",
|
||||||
|
frame_src: "",
|
||||||
|
frame_loading: true,
|
||||||
|
keep_alive: false,
|
||||||
|
hidden_tag: false,
|
||||||
|
fixed_tag: false,
|
||||||
|
show_link: true,
|
||||||
|
show_parent: false
|
||||||
|
};
|
||||||
|
for (let key in addForm) {
|
||||||
|
// 检查第二个字典是否包含相同的键
|
||||||
|
if (key in curData) {
|
||||||
|
// 将第二个字典中对应键的值赋给第一个字典
|
||||||
|
addForm[key] = curData[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const res = await postAddPermissionAPI(addForm);
|
||||||
|
if (res.code === 200) {
|
||||||
|
// 实际开发先调用新增接口,再进行下面操作
|
||||||
|
chores();
|
||||||
|
} else {
|
||||||
|
message(`添加失败!`, { type: "success" });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let updateForm = {
|
||||||
|
menu_type: 0,
|
||||||
|
parent_id: "",
|
||||||
|
title: "",
|
||||||
|
name: "",
|
||||||
|
path: "",
|
||||||
|
component: "",
|
||||||
|
rank: 99,
|
||||||
|
redirect: "",
|
||||||
|
icon: "",
|
||||||
|
extra_icon: "",
|
||||||
|
enter_transition: "",
|
||||||
|
leave_transition: "",
|
||||||
|
active_path: "",
|
||||||
|
auths: "",
|
||||||
|
frame_src: "",
|
||||||
|
frame_loading: true,
|
||||||
|
keep_alive: false,
|
||||||
|
hidden_tag: false,
|
||||||
|
fixed_tag: false,
|
||||||
|
show_link: true,
|
||||||
|
show_parent: false
|
||||||
|
};
|
||||||
|
for (let key in updateForm) {
|
||||||
|
// 检查第二个字典是否包含相同的键
|
||||||
|
if (key in curData) {
|
||||||
|
// 将第二个字典中对应键的值赋给第一个字典
|
||||||
|
updateForm[key] = curData[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const res = await putUpdatePermissionAPI(curData.id, updateForm);
|
||||||
|
if (res.code === 200) {
|
||||||
|
chores();
|
||||||
|
} else {
|
||||||
|
message(`更新失败!`, { type: "error" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const handleDelete = async (row: PermissionInfo) => {
|
||||||
|
const res = await deletePermissionAPI(row.id);
|
||||||
|
if (res.code === 200) {
|
||||||
|
message(`您删除了权限名称为${transformI18n(row.title)}的这条数据`, {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
onSearch();
|
||||||
|
} else {
|
||||||
|
message("权限删除失败!", { type: "error" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
formRef,
|
||||||
|
dataList,
|
||||||
|
loading,
|
||||||
|
columns,
|
||||||
|
onSearch,
|
||||||
|
resetForm,
|
||||||
|
openDialog,
|
||||||
|
handleDelete
|
||||||
|
};
|
||||||
|
};
|
||||||
40
types/i18n.d.ts
vendored
40
types/i18n.d.ts
vendored
@@ -1,40 +0,0 @@
|
|||||||
/** 语言信息类型 */
|
|
||||||
export type LanguageInfo = {
|
|
||||||
/** 语言ID */
|
|
||||||
id: string;
|
|
||||||
/** 语言代码 */
|
|
||||||
code: string;
|
|
||||||
/** 语言名称 */
|
|
||||||
name: string;
|
|
||||||
/** 创建时间 */
|
|
||||||
create_time: string;
|
|
||||||
/** 更新时间 */
|
|
||||||
update_time: string;
|
|
||||||
/** 创建人 */
|
|
||||||
create_by: string;
|
|
||||||
/** 更新人 */
|
|
||||||
update_by: string;
|
|
||||||
};
|
|
||||||
/** 翻译信息类型 */
|
|
||||||
export type TranslationInfo = {
|
|
||||||
/** 翻译记录ID */
|
|
||||||
id: string;
|
|
||||||
/** 键值 */
|
|
||||||
key: string;
|
|
||||||
/** 翻译内容 */
|
|
||||||
translation: string;
|
|
||||||
/** 语言ID */
|
|
||||||
locale_id: string;
|
|
||||||
/** 语言代码 */
|
|
||||||
locale_code: string;
|
|
||||||
/** 语言名称 */
|
|
||||||
locale_name: string;
|
|
||||||
/** 创建时间 */
|
|
||||||
create_time: string;
|
|
||||||
/** 修改时间 */
|
|
||||||
update_time: string;
|
|
||||||
/** 创建人 */
|
|
||||||
create_by: string;
|
|
||||||
/** 修改人 */
|
|
||||||
update_by: string;
|
|
||||||
};
|
|
||||||
58
types/system.d.ts
vendored
58
types/system.d.ts
vendored
@@ -51,7 +51,7 @@ export type DepartmentInfo = {
|
|||||||
/**部门名称 */
|
/**部门名称 */
|
||||||
name: string;
|
name: string;
|
||||||
/**父部门ID */
|
/**父部门ID */
|
||||||
parentId: string;
|
parent_id: string;
|
||||||
/**排序 */
|
/**排序 */
|
||||||
sort: number;
|
sort: number;
|
||||||
/**部门电话 */
|
/**部门电话 */
|
||||||
@@ -63,3 +63,59 @@ export type DepartmentInfo = {
|
|||||||
/**备注信息 */
|
/**备注信息 */
|
||||||
remark: string;
|
remark: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**权限信息类型 */
|
||||||
|
export type PermissionInfo = {
|
||||||
|
/** 菜单ID */
|
||||||
|
id: string;
|
||||||
|
/** 创建人 */
|
||||||
|
create_by: string;
|
||||||
|
/** 创建时间 */
|
||||||
|
create_time: string;
|
||||||
|
/** 更新人 */
|
||||||
|
update_by: string;
|
||||||
|
/** 更新时间 */
|
||||||
|
update_time: string;
|
||||||
|
/** 菜单类型(0代表菜单、1代表按钮) */
|
||||||
|
menu_type: number;
|
||||||
|
/** 父菜单ID */
|
||||||
|
parent_id: string;
|
||||||
|
/** 菜单标题 */
|
||||||
|
title: string;
|
||||||
|
/** 菜单名称(用于路由名称) */
|
||||||
|
name: string;
|
||||||
|
/** 菜单路径 */
|
||||||
|
path: string;
|
||||||
|
/** 组件路径 */
|
||||||
|
component: string;
|
||||||
|
/** 菜单排序 */
|
||||||
|
rank: number;
|
||||||
|
/** 重定向路径 */
|
||||||
|
redirect: string | null;
|
||||||
|
/** 菜单图标 */
|
||||||
|
icon: string;
|
||||||
|
/** 额外图标 */
|
||||||
|
extra_icon: string | null;
|
||||||
|
/** 进入页面过渡动画 */
|
||||||
|
enter_transition: string | null;
|
||||||
|
/** 离开页面过渡动画 */
|
||||||
|
leave_transition: string | null;
|
||||||
|
/** 激活路径 */
|
||||||
|
active_path: string | null;
|
||||||
|
/** 权限标识 */
|
||||||
|
auths: string;
|
||||||
|
/** 内嵌框架地址 */
|
||||||
|
frame_src: string | null;
|
||||||
|
/** 内嵌框架加载状态 */
|
||||||
|
frame_loading: boolean;
|
||||||
|
/** 是否缓存页面 */
|
||||||
|
keep_alive: boolean;
|
||||||
|
/** 是否隐藏标签页 */
|
||||||
|
hidden_tag: boolean;
|
||||||
|
/** 是否固定标签页 */
|
||||||
|
fixed_tag: boolean;
|
||||||
|
/** 是否显示菜单链接 */
|
||||||
|
show_link: boolean;
|
||||||
|
/** 是否显示父菜单 */
|
||||||
|
show_parent: boolean;
|
||||||
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user