mirror of
https://github.com/pure-admin/pure-admin-thin.git
synced 2025-04-25 07:57:18 +08:00
perf: 系统管理
This commit is contained in:
parent
7443ac3ac4
commit
9bac6333fb
@ -38,6 +38,7 @@ menus:
|
|||||||
hsRole: Role Manage
|
hsRole: Role Manage
|
||||||
hsSystemMenu: Menu Manage
|
hsSystemMenu: Menu Manage
|
||||||
hsDept: Dept Manage
|
hsDept: Dept Manage
|
||||||
|
hsJob: Job Manage
|
||||||
hssysMonitor: System Monitor
|
hssysMonitor: System Monitor
|
||||||
hsOnlineUser: Online User
|
hsOnlineUser: Online User
|
||||||
hsLoginLog: Login Log
|
hsLoginLog: Login Log
|
||||||
|
@ -35,9 +35,10 @@ menus:
|
|||||||
hsAbout: 关于
|
hsAbout: 关于
|
||||||
hssysManagement: 系统管理
|
hssysManagement: 系统管理
|
||||||
hsUser: 用户管理
|
hsUser: 用户管理
|
||||||
hsRole: 角色管理
|
hsRole: 权限管理
|
||||||
hsSystemMenu: 菜单管理
|
hsSystemMenu: 菜单管理
|
||||||
hsDept: 部门管理
|
hsDept: 部门管理
|
||||||
|
hsJob: 角色管理
|
||||||
hssysMonitor: 系统监控
|
hssysMonitor: 系统监控
|
||||||
hsOnlineUser: 在线用户
|
hsOnlineUser: 在线用户
|
||||||
hsLoginLog: 登录日志
|
hsLoginLog: 登录日志
|
||||||
|
@ -51,6 +51,15 @@ const systemManagementRouter = {
|
|||||||
title: "menus.hsDept",
|
title: "menus.hsDept",
|
||||||
roles: ["admin"]
|
roles: ["admin"]
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: "/system/job/index",
|
||||||
|
name: "SystemJob",
|
||||||
|
meta: {
|
||||||
|
icon: "ri:account-circle-fill",
|
||||||
|
title: "menus.hsDept",
|
||||||
|
roles: ["admin"]
|
||||||
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
73
src/api/system/dept.ts
Normal file
73
src/api/system/dept.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { http } from "@/utils/http";
|
||||||
|
import { PageQuery, type ApiAbstract } from "@/utils/http/ApiAbstract";
|
||||||
|
import { baseUrlApi } from "../utils";
|
||||||
|
|
||||||
|
export class Dept {
|
||||||
|
createBy: string;
|
||||||
|
createTime: Date;
|
||||||
|
deptSort: number;
|
||||||
|
enabled: boolean;
|
||||||
|
hasChildren: boolean;
|
||||||
|
id: number;
|
||||||
|
pid: number;
|
||||||
|
status: number;
|
||||||
|
label: string;
|
||||||
|
leaf: boolean;
|
||||||
|
name: string;
|
||||||
|
subCount: number;
|
||||||
|
updateBy: string;
|
||||||
|
updateTime: Date;
|
||||||
|
menus: any[];
|
||||||
|
}
|
||||||
|
export class DeptQueryCriteria extends PageQuery {
|
||||||
|
name: string;
|
||||||
|
enabled: boolean;
|
||||||
|
pid: number;
|
||||||
|
pidIsNull: boolean;
|
||||||
|
createTime: Array<Date>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getDepts = (params: DeptQueryCriteria | any) => {
|
||||||
|
return http.request<ApiAbstract<Dept>>("get", baseUrlApi("dept"), {
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const getDeptTree = (params?: DeptQueryCriteria | any) => {
|
||||||
|
return http.request<ApiAbstract<Dept>>("get", baseUrlApi("dept/treeAll"), {
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getDeptSuperior = (ids: Number[]) => {
|
||||||
|
const data = ids.length || ids.length === 0 ? ids : Array.of(ids);
|
||||||
|
return http.request<ApiAbstract<Dept>>("post", baseUrlApi("dept/superior"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const add = (data: Partial<Dept>) => {
|
||||||
|
return http.request<ApiAbstract<Dept>>("post", baseUrlApi("dept"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const del = (ids: number[] | any) => {
|
||||||
|
return http.request("delete", baseUrlApi("dept"), {
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const edit = (data: Partial<Dept>) => {
|
||||||
|
return http.request<ApiAbstract<Dept>>("put", baseUrlApi("dept"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const download = (data: Partial<DeptQueryCriteria>) => {
|
||||||
|
return http.request<Blob>(
|
||||||
|
"get",
|
||||||
|
baseUrlApi("dept/download"),
|
||||||
|
{
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{ responseType: "blob" }
|
||||||
|
);
|
||||||
|
};
|
64
src/api/system/job.ts
Normal file
64
src/api/system/job.ts
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { http } from "@/utils/http";
|
||||||
|
import {
|
||||||
|
type ApiAbstract,
|
||||||
|
type Page,
|
||||||
|
PageQuery,
|
||||||
|
VersionEntity
|
||||||
|
} from "@/utils/http/ApiAbstract";
|
||||||
|
import { baseUrlApi } from "../utils";
|
||||||
|
|
||||||
|
export class Job extends VersionEntity {
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* 岗位名称
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 岗位排序
|
||||||
|
*/
|
||||||
|
jobSort: number;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
enabled: boolean;
|
||||||
|
}
|
||||||
|
export class JobQueryCriteria extends PageQuery {
|
||||||
|
name: string;
|
||||||
|
enabled: boolean;
|
||||||
|
createTime: Date[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const get = (params?: number | any) => {
|
||||||
|
return http.request<ApiAbstract<Page<Job>>>("get", baseUrlApi("job"), {
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const add = (data: Partial<Job>) => {
|
||||||
|
return http.request("post", baseUrlApi("job"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const del = (ids: number[] | any) => {
|
||||||
|
return http.request("delete", baseUrlApi("job"), {
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const edit = (data: Partial<Job>) => {
|
||||||
|
return http.request("put", baseUrlApi("job"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const download = (data: Partial<JobQueryCriteria>) => {
|
||||||
|
return http.request<Blob>(
|
||||||
|
"get",
|
||||||
|
baseUrlApi("job/download"),
|
||||||
|
{
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{ responseType: "blob" }
|
||||||
|
);
|
||||||
|
};
|
78
src/api/system/menu.ts
Normal file
78
src/api/system/menu.ts
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import { http } from "@/utils/http";
|
||||||
|
import { PageQuery, type ApiAbstract } from "@/utils/http/ApiAbstract";
|
||||||
|
import { baseUrlApi } from "../utils";
|
||||||
|
|
||||||
|
export class Menu {
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
children: Menu[];
|
||||||
|
|
||||||
|
type: number;
|
||||||
|
|
||||||
|
permission: string;
|
||||||
|
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
menuSort: number;
|
||||||
|
|
||||||
|
path: string;
|
||||||
|
|
||||||
|
component: string;
|
||||||
|
|
||||||
|
pid: number;
|
||||||
|
|
||||||
|
subCount: number;
|
||||||
|
|
||||||
|
iframe: boolean;
|
||||||
|
|
||||||
|
cache: boolean;
|
||||||
|
|
||||||
|
hidden: boolean;
|
||||||
|
|
||||||
|
componentName: string;
|
||||||
|
|
||||||
|
icon: string;
|
||||||
|
}
|
||||||
|
export class MenuQueryCriteria extends PageQuery {
|
||||||
|
blurry: string;
|
||||||
|
pid: number;
|
||||||
|
createTime: Date[];
|
||||||
|
}
|
||||||
|
export const menuTree = (ids: number[]) => {
|
||||||
|
return http.request<ApiAbstract<Menu>>("post", baseUrlApi("menus/tree"), {
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get = (params: number | any) => {
|
||||||
|
return http.request<ApiAbstract<Menu>>("get", baseUrlApi("menus"), {
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const add = (data: Partial<Menu>) => {
|
||||||
|
return http.request("post", baseUrlApi("menus"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const del = (ids: number[] | any) => {
|
||||||
|
return http.request("delete", baseUrlApi("menus"), {
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const edit = (data: Partial<Menu>) => {
|
||||||
|
return http.request<ApiAbstract<Menu>>("put", baseUrlApi("menus"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const download = (data: Partial<MenuQueryCriteria>) => {
|
||||||
|
return http.request<Blob>(
|
||||||
|
"get",
|
||||||
|
baseUrlApi("menus/download"),
|
||||||
|
{
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{ responseType: "blob" }
|
||||||
|
);
|
||||||
|
};
|
90
src/api/system/role.ts
Normal file
90
src/api/system/role.ts
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
import { http } from "@/utils/http";
|
||||||
|
import { type ApiAbstract, PageQuery } from "@/utils/http/ApiAbstract";
|
||||||
|
import { baseUrlApi } from "../utils";
|
||||||
|
import type { Dept } from "./dept";
|
||||||
|
|
||||||
|
export class Role {
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* 用户
|
||||||
|
*/
|
||||||
|
users: any;
|
||||||
|
/**
|
||||||
|
* 菜单
|
||||||
|
*/
|
||||||
|
menus: any;
|
||||||
|
/**
|
||||||
|
* 部门
|
||||||
|
*/
|
||||||
|
depts: Dept[] | any[];
|
||||||
|
/**
|
||||||
|
* 名称
|
||||||
|
*/
|
||||||
|
name: string;
|
||||||
|
/**
|
||||||
|
* 数据权限,全部 、 本级 、 自定义
|
||||||
|
*/
|
||||||
|
dataScope: string;
|
||||||
|
/**
|
||||||
|
* 级别,数值越小,级别越大
|
||||||
|
*/
|
||||||
|
level: number;
|
||||||
|
/**
|
||||||
|
* 描述
|
||||||
|
*/
|
||||||
|
description: string;
|
||||||
|
}
|
||||||
|
export class RoleQueryCriteria extends PageQuery {
|
||||||
|
blurry: string;
|
||||||
|
createTime: Date[];
|
||||||
|
}
|
||||||
|
export const getAll = (data: Partial<RoleQueryCriteria>) => {
|
||||||
|
return http.request<Role[]>("get", baseUrlApi("roles/all"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const get = (params?: number | any) => {
|
||||||
|
return http.request<ApiAbstract<Role>>("get", baseUrlApi("roles"), {
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getLevel = () => {
|
||||||
|
return http.request("get", baseUrlApi("roles/level"));
|
||||||
|
};
|
||||||
|
|
||||||
|
export const editMenu = data => {
|
||||||
|
return http.request("put", baseUrlApi("roles/menu"), { data });
|
||||||
|
};
|
||||||
|
export const add = (data: Partial<Role>) => {
|
||||||
|
return http.request("post", baseUrlApi("roles"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const del = (ids: number[] | any) => {
|
||||||
|
return http.request("delete", baseUrlApi("roles"), {
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const edit = (data: Partial<Role>) => {
|
||||||
|
return http.request<ApiAbstract<Role>>("put", baseUrlApi("roles"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const menus = (data: Partial<Dept>) => {
|
||||||
|
return http.request("put", baseUrlApi("roles/menu"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const download = (data: Partial<RoleQueryCriteria>) => {
|
||||||
|
return http.request<Blob>(
|
||||||
|
"get",
|
||||||
|
baseUrlApi("roles/download"),
|
||||||
|
{
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{ responseType: "blob" }
|
||||||
|
);
|
||||||
|
};
|
158
src/api/system/user.ts
Normal file
158
src/api/system/user.ts
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import { http } from "@/utils/http";
|
||||||
|
import {
|
||||||
|
type ApiAbstract,
|
||||||
|
PageQuery,
|
||||||
|
VersionEntity
|
||||||
|
} from "@/utils/http/ApiAbstract";
|
||||||
|
import { baseUrlApi } from "../utils";
|
||||||
|
import type { Role } from "./role";
|
||||||
|
import type { Job } from "./job";
|
||||||
|
import type { Dept } from "./dept";
|
||||||
|
import { encrypt } from "@/utils/rsaEncrypt";
|
||||||
|
|
||||||
|
export class User extends VersionEntity {
|
||||||
|
id: number;
|
||||||
|
/**
|
||||||
|
* 用户角色
|
||||||
|
*/
|
||||||
|
roles: Role[];
|
||||||
|
/**
|
||||||
|
* 用户岗位
|
||||||
|
*/
|
||||||
|
jobs: Job[];
|
||||||
|
/**
|
||||||
|
* 用户部门
|
||||||
|
*/
|
||||||
|
dept: Dept;
|
||||||
|
/**
|
||||||
|
* 用户名称
|
||||||
|
*/
|
||||||
|
username: string;
|
||||||
|
/**
|
||||||
|
* 用户昵称
|
||||||
|
*/
|
||||||
|
nickName: string;
|
||||||
|
/**
|
||||||
|
* 邮箱
|
||||||
|
*/
|
||||||
|
email: string;
|
||||||
|
/**
|
||||||
|
* 电话号码
|
||||||
|
*/
|
||||||
|
phone: string;
|
||||||
|
/**
|
||||||
|
* 用户性别
|
||||||
|
*/
|
||||||
|
gender: string;
|
||||||
|
/**
|
||||||
|
* 头像真实名称
|
||||||
|
*/
|
||||||
|
avatarName: string;
|
||||||
|
/**
|
||||||
|
* 头像存储的路径
|
||||||
|
*/
|
||||||
|
avatarPath: string;
|
||||||
|
/**
|
||||||
|
* 密码
|
||||||
|
*/
|
||||||
|
password: string;
|
||||||
|
/**
|
||||||
|
* 是否启用
|
||||||
|
*/
|
||||||
|
enabled: string;
|
||||||
|
/**
|
||||||
|
* 是否为admin账号
|
||||||
|
*/
|
||||||
|
isAdmin: boolean;
|
||||||
|
/**
|
||||||
|
* 最后修改密码的时间
|
||||||
|
*/
|
||||||
|
pwdResetTime: Date;
|
||||||
|
}
|
||||||
|
export class UserQueryCriteria extends PageQuery {
|
||||||
|
name: string;
|
||||||
|
deptId: number;
|
||||||
|
deptIds: number[];
|
||||||
|
enabled: boolean;
|
||||||
|
createTime: Date[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export const get = (params: number | any) => {
|
||||||
|
return http.request<ApiAbstract<User>>("get", baseUrlApi("users"), {
|
||||||
|
params
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const add = (data: Partial<User>) => {
|
||||||
|
return http.request("post", baseUrlApi("users"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const del = (ids: number[] | any) => {
|
||||||
|
return http.request("delete", baseUrlApi("users"), {
|
||||||
|
data: ids
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const edit = (data: Partial<User>) => {
|
||||||
|
return http.request("put", baseUrlApi("users"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
};
|
||||||
|
export const download = (data: Partial<UserQueryCriteria>) => {
|
||||||
|
return http.request<Blob>(
|
||||||
|
"get",
|
||||||
|
baseUrlApi("users/download"),
|
||||||
|
{
|
||||||
|
data
|
||||||
|
},
|
||||||
|
{ responseType: "blob" }
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateAvatarByid = ({ id, avatar }) => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append("avatar", avatar, "avatar.jpg");
|
||||||
|
return http.request("post", baseUrlApi("/users/updateAvatar/" + id), {
|
||||||
|
data: formData,
|
||||||
|
// 请求超时时间
|
||||||
|
timeout: 60000,
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "multipart/form-data",
|
||||||
|
filename: "avatar.png"
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export function updatePass({ oldPass, newPass }) {
|
||||||
|
const data = {
|
||||||
|
oldPass: encrypt(oldPass),
|
||||||
|
newPass: encrypt(newPass)
|
||||||
|
};
|
||||||
|
return http.request("post", baseUrlApi("users/updatePass"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function resetEmail(data) {
|
||||||
|
return http.request("post", baseUrlApi("/code/resetEmail?email=" + data));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function updateEmail(form) {
|
||||||
|
const data = {
|
||||||
|
password: encrypt(form.pass),
|
||||||
|
email: form.email
|
||||||
|
};
|
||||||
|
return http.request("post", baseUrlApi("users/updateEmail/" + form.code), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
export function editUser(data) {
|
||||||
|
return http.request("put", baseUrlApi("users/center"), {
|
||||||
|
data
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getLog() {
|
||||||
|
return http.request("get", baseUrlApi("logs/user"));
|
||||||
|
}
|
53
src/views/components/date-picker.vue
Normal file
53
src/views/components/date-picker.vue
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
<script setup lang="tsx">
|
||||||
|
import { ref } from "vue";
|
||||||
|
const props = defineProps({
|
||||||
|
createTime: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const createTime = ref(props.createTime);
|
||||||
|
|
||||||
|
const shortcuts = [
|
||||||
|
{
|
||||||
|
text: "最近1周",
|
||||||
|
value: () => {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 7);
|
||||||
|
return [start, end];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "最近1个月",
|
||||||
|
value: () => {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 30);
|
||||||
|
return [start, end];
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
text: "最近3个月",
|
||||||
|
value: () => {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setTime(start.getTime() - 3600 * 1000 * 24 * 90);
|
||||||
|
return [start, end];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="createTime"
|
||||||
|
type="datetimerange"
|
||||||
|
:shortcuts="shortcuts"
|
||||||
|
range-separator="-"
|
||||||
|
start-placeholder="开始时间"
|
||||||
|
end-placeholder="结束时间"
|
||||||
|
value-format="YYYY-MM-DD h:m:s"
|
||||||
|
/>
|
||||||
|
</template>
|
@ -58,7 +58,7 @@ const { locale, translationCh, translationEn } = useTranslationLang();
|
|||||||
|
|
||||||
const ruleForm = reactive({
|
const ruleForm = reactive({
|
||||||
username: "admin",
|
username: "admin",
|
||||||
password: "admin123",
|
password: "123456",
|
||||||
verifyCode: ""
|
verifyCode: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import ReCol from "@/components/ReCol";
|
import ReCol from "@/components/ReCol";
|
||||||
import { formRules } from "./utils/rule";
|
import { formRules } from "./utils/rule";
|
||||||
import { FormProps } from "./utils/types";
|
import { FormProps } from "./utils/types";
|
||||||
@ -8,13 +8,17 @@ import { usePublicHooks } from "../hooks";
|
|||||||
const props = withDefaults(defineProps<FormProps>(), {
|
const props = withDefaults(defineProps<FormProps>(), {
|
||||||
formInline: () => ({
|
formInline: () => ({
|
||||||
higherDeptOptions: [],
|
higherDeptOptions: [],
|
||||||
|
higherDeptOptions2: {},
|
||||||
parentId: 0,
|
parentId: 0,
|
||||||
|
id: 0,
|
||||||
|
pid: 0,
|
||||||
|
deptSort: 0,
|
||||||
|
enabled: false,
|
||||||
name: "",
|
name: "",
|
||||||
principal: "",
|
principal: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
email: "",
|
email: "",
|
||||||
sort: 0,
|
sort: 0,
|
||||||
status: 1,
|
|
||||||
remark: ""
|
remark: ""
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
@ -26,7 +30,9 @@ const newFormInline = ref(props.formInline);
|
|||||||
function getRef() {
|
function getRef() {
|
||||||
return ruleFormRef.value;
|
return ruleFormRef.value;
|
||||||
}
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
// 在这里编写页面加载后要执行的代码
|
||||||
|
});
|
||||||
defineExpose({ getRef });
|
defineExpose({ getRef });
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -41,22 +47,16 @@ defineExpose({ getRef });
|
|||||||
<re-col>
|
<re-col>
|
||||||
<el-form-item label="上级部门">
|
<el-form-item label="上级部门">
|
||||||
<el-cascader
|
<el-cascader
|
||||||
v-model="newFormInline.parentId"
|
v-model="newFormInline.pid"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
:options="newFormInline.higherDeptOptions"
|
:props="newFormInline.higherDeptOptions2"
|
||||||
:props="{
|
|
||||||
value: 'id',
|
|
||||||
label: 'name',
|
|
||||||
emitPath: false,
|
|
||||||
checkStrictly: true
|
|
||||||
}"
|
|
||||||
clearable
|
clearable
|
||||||
filterable
|
filterable
|
||||||
placeholder="请选择上级部门"
|
placeholder="请选择上级部门"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<span>{{ data.name }}</span>
|
<span>{{ data.label }}</span>
|
||||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
<span v-if="!node.isLeaf"> ({{ data.subCount }}) </span>
|
||||||
</template>
|
</template>
|
||||||
</el-cascader>
|
</el-cascader>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@ -103,8 +103,7 @@ defineExpose({ getRef });
|
|||||||
<re-col :value="12" :xs="24" :sm="24">
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
<el-form-item label="排序">
|
<el-form-item label="排序">
|
||||||
<el-input-number
|
<el-input-number
|
||||||
v-model="newFormInline.sort"
|
v-model="newFormInline.deptSort"
|
||||||
class="!w-full"
|
|
||||||
:min="0"
|
:min="0"
|
||||||
:max="9999"
|
:max="9999"
|
||||||
controls-position="right"
|
controls-position="right"
|
||||||
@ -114,10 +113,10 @@ defineExpose({ getRef });
|
|||||||
<re-col :value="12" :xs="24" :sm="24">
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
<el-form-item label="部门状态">
|
<el-form-item label="部门状态">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="newFormInline.status"
|
v-model="newFormInline.enabled"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
:active-value="1"
|
:active-value="true"
|
||||||
:inactive-value="0"
|
:inactive-value="false"
|
||||||
active-text="启用"
|
active-text="启用"
|
||||||
inactive-text="停用"
|
inactive-text="停用"
|
||||||
:style="switchStyle"
|
:style="switchStyle"
|
||||||
|
@ -3,28 +3,94 @@ import { ref } from "vue";
|
|||||||
import { useDept } from "./utils/hook";
|
import { useDept } from "./utils/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 datePicker from "@/views/components/date-picker.vue";
|
||||||
|
import * as Dept from "@/api/system/dept";
|
||||||
|
|
||||||
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 { message } from "@/utils/message";
|
||||||
|
import { ElMessageBox } from "element-plus";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SystemDept"
|
name: "Dept"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const handleSelectionChange = val => {
|
||||||
|
multipleSelection.value = val;
|
||||||
|
if (val != null && val.length > 0) {
|
||||||
|
value2.value = false;
|
||||||
|
} else {
|
||||||
|
value2.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function deleteAll() {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`确认要<strong>删除所选的</strong><strong style='color:var(--el-color-primary)'>${multipleSelection.value.length}</strong>个部门吗?`,
|
||||||
|
"系统提示",
|
||||||
|
{
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
draggable: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
Dept.del(multipleSelection.value.map(dept => dept.id));
|
||||||
|
message("已删除所选的部门", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
onSearch();
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const load = (
|
||||||
|
row: Partial<Dept.Dept>,
|
||||||
|
treeNode: unknown,
|
||||||
|
resolve: (date: Partial<Dept.Dept>[]) => void
|
||||||
|
) => {
|
||||||
|
let queryDept = { pid: row?.id };
|
||||||
|
if (!row?.id) {
|
||||||
|
queryDept = null;
|
||||||
|
}
|
||||||
|
Dept.getDepts(queryDept).then(contentData => {
|
||||||
|
// 调用' resolve '回调以返回子节点数据并指示加载完成。
|
||||||
|
resolve(contentData.data.content);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const exportClick = async () => {
|
||||||
|
const response: Blob = await Dept.download(null);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
const url = window.URL.createObjectURL(response); // 创建媒体流 url ,详细了解可自己查 URL.createObjectURL(推荐 MDN )
|
||||||
|
|
||||||
|
a.href = url;
|
||||||
|
a.style.display = "none";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.parentNode.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url); // 删除创建的媒体流 url 对象
|
||||||
|
message("导出成功", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
};
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const tableRef = ref();
|
const tableRef = ref();
|
||||||
|
const value2 = ref(true);
|
||||||
const {
|
const {
|
||||||
form,
|
form,
|
||||||
loading,
|
loading,
|
||||||
columns,
|
columns,
|
||||||
dataList,
|
dataList,
|
||||||
|
multipleSelection,
|
||||||
onSearch,
|
onSearch,
|
||||||
resetForm,
|
resetForm,
|
||||||
openDialog,
|
openDialog,
|
||||||
handleDelete,
|
handleDelete
|
||||||
handleSelectionChange
|
|
||||||
} = useDept();
|
} = useDept();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -41,24 +107,27 @@ const {
|
|||||||
v-model="form.name"
|
v-model="form.name"
|
||||||
placeholder="请输入部门名称"
|
placeholder="请输入部门名称"
|
||||||
clearable
|
clearable
|
||||||
class="!w-[180px]"
|
class="!w-[200px]"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态:" prop="status">
|
<el-form-item label="状态:" prop="status">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.status"
|
v-model="form.enabled"
|
||||||
placeholder="请选择状态"
|
placeholder="请选择状态"
|
||||||
clearable
|
clearable
|
||||||
class="!w-[180px]"
|
class="!w-[180px]"
|
||||||
>
|
>
|
||||||
<el-option label="启用" :value="1" />
|
<el-option label="启用" :value="true" />
|
||||||
<el-option label="停用" :value="0" />
|
<el-option label="停用" :value="false" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="createTime">
|
||||||
|
<datePicker v-model="form.createTime" />
|
||||||
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:icon="useRenderIcon('ri:search-line')"
|
:icon="useRenderIcon('search')"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="onSearch"
|
@click="onSearch"
|
||||||
>
|
>
|
||||||
@ -71,12 +140,12 @@ const {
|
|||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<PureTableBar
|
<PureTableBar
|
||||||
title="部门管理(仅演示,操作后不生效)"
|
title="部门列表"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:tableRef="tableRef?.getTableRef()"
|
:tableRef="tableRef?.getTableRef()"
|
||||||
@refresh="onSearch"
|
@refresh="onSearch"
|
||||||
>
|
>
|
||||||
<template #buttons>
|
<template #add>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:icon="useRenderIcon(AddFill)"
|
:icon="useRenderIcon(AddFill)"
|
||||||
@ -85,11 +154,30 @@ const {
|
|||||||
新增部门
|
新增部门
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
<template #delete>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:disabled="value2"
|
||||||
|
:icon="useRenderIcon(Delete)"
|
||||||
|
@click="deleteAll()"
|
||||||
|
>
|
||||||
|
删除部门
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #export>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
:icon="useRenderIcon('solar:upload-bold')"
|
||||||
|
@click="exportClick()"
|
||||||
|
>
|
||||||
|
导出数据
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
<pure-table
|
<pure-table
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
adaptive
|
adaptive
|
||||||
:adaptiveConfig="{ offsetBottom: 45 }"
|
:adaptiveConfig="{ offsetBottom: 32 }"
|
||||||
align-whole="center"
|
align-whole="center"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
showOverflowTooltip
|
showOverflowTooltip
|
||||||
@ -97,12 +185,14 @@ const {
|
|||||||
default-expand-all
|
default-expand-all
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:size="size"
|
:size="size"
|
||||||
:data="dataList"
|
|
||||||
:columns="dynamicColumns"
|
:columns="dynamicColumns"
|
||||||
|
:data="dataList"
|
||||||
:header-cell-style="{
|
:header-cell-style="{
|
||||||
background: 'var(--el-fill-color-light)',
|
background: 'var(--el-fill-color-light)',
|
||||||
color: 'var(--el-text-color-primary)'
|
color: 'var(--el-text-color-primary)'
|
||||||
}"
|
}"
|
||||||
|
lazy
|
||||||
|
:load="load"
|
||||||
@selection-change="handleSelectionChange"
|
@selection-change="handleSelectionChange"
|
||||||
>
|
>
|
||||||
<template #operation="{ row }">
|
<template #operation="{ row }">
|
||||||
@ -112,19 +202,9 @@ const {
|
|||||||
type="primary"
|
type="primary"
|
||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(EditPen)"
|
:icon="useRenderIcon(EditPen)"
|
||||||
@click="openDialog('修改', row)"
|
@click="openDialog('编辑', row)"
|
||||||
>
|
>
|
||||||
修改
|
编辑
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(AddFill)"
|
|
||||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
|
||||||
>
|
|
||||||
新增
|
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-popconfirm
|
<el-popconfirm
|
||||||
:title="`是否确认删除部门名称为${row.name}的这条数据`"
|
:title="`是否确认删除部门名称为${row.name}的这条数据`"
|
||||||
@ -150,14 +230,6 @@ const {
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
:deep(.el-table__inner-wrapper::before) {
|
|
||||||
height: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.main-content {
|
|
||||||
margin: 24px 24px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form {
|
.search-form {
|
||||||
:deep(.el-form-item) {
|
:deep(.el-form-item) {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
@ -1,26 +1,41 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import editForm from "../form.vue";
|
import editForm from "../form.vue";
|
||||||
import { handleTree } from "@/utils/tree";
|
|
||||||
import { message } from "@/utils/message";
|
import { message } from "@/utils/message";
|
||||||
import { getDeptList } from "@/api/system";
|
import * as Dept from "@/api/system/dept";
|
||||||
import { usePublicHooks } from "../../hooks";
|
|
||||||
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 type { FormItemProps } from "../utils/types";
|
||||||
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
||||||
|
import { usePublicHooks } from "../../hooks";
|
||||||
|
import { ElMessageBox, type CascaderProps } from "element-plus";
|
||||||
|
|
||||||
export function useDept() {
|
export function useDept() {
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: "",
|
name: "",
|
||||||
status: null
|
enabled: null,
|
||||||
|
createTime: ""
|
||||||
});
|
});
|
||||||
|
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const dataList = ref([]);
|
const dataList = reactive([]);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const { tagStyle } = usePublicHooks();
|
const multipleSelection = ref([]);
|
||||||
|
const switchLoadMap = ref({});
|
||||||
|
const { switchStyle } = usePublicHooks();
|
||||||
|
|
||||||
|
/** 表格索引 */
|
||||||
|
const indexMethod = (index: number) => {
|
||||||
|
return index + 1;
|
||||||
|
};
|
||||||
|
|
||||||
const columns: TableColumnList = [
|
const columns: TableColumnList = [
|
||||||
|
{
|
||||||
|
type: "selection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "index",
|
||||||
|
index: indexMethod
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "部门名称",
|
label: "部门名称",
|
||||||
prop: "name",
|
prop: "name",
|
||||||
@ -29,17 +44,26 @@ export function useDept() {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "排序",
|
label: "排序",
|
||||||
prop: "sort",
|
prop: "deptSort",
|
||||||
minWidth: 70
|
minWidth: 70
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "状态",
|
label: "状态",
|
||||||
prop: "status",
|
prop: "enabled",
|
||||||
minWidth: 100,
|
minWidth: 100,
|
||||||
cellRenderer: ({ row, props }) => (
|
cellRenderer: scope => (
|
||||||
<el-tag size={props.size} style={tagStyle.value(row.status)}>
|
<el-switch
|
||||||
{row.status === 1 ? "启用" : "停用"}
|
v-model={scope.row.enabled}
|
||||||
</el-tag>
|
size={scope.props.size === "small" ? "small" : "default"}
|
||||||
|
loading={switchLoadMap.value[scope.row.index]?.loading}
|
||||||
|
style={switchStyle.value}
|
||||||
|
inline-prompt
|
||||||
|
active-value={true}
|
||||||
|
inactive-value={false}
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="停用"
|
||||||
|
onChange={() => onChange(scope as any)}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -57,15 +81,11 @@ export function useDept() {
|
|||||||
{
|
{
|
||||||
label: "操作",
|
label: "操作",
|
||||||
fixed: "right",
|
fixed: "right",
|
||||||
width: 210,
|
width: 160,
|
||||||
slot: "operation"
|
slot: "operation"
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
function handleSelectionChange(val) {
|
|
||||||
console.log("handleSelectionChange", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetForm(formEl) {
|
function resetForm(formEl) {
|
||||||
if (!formEl) return;
|
if (!formEl) return;
|
||||||
formEl.resetFields();
|
formEl.resetFields();
|
||||||
@ -74,17 +94,24 @@ export function useDept() {
|
|||||||
|
|
||||||
async function onSearch() {
|
async function onSearch() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const { data } = await getDeptList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
|
const queryType = new Dept.DeptQueryCriteria();
|
||||||
let newData = data;
|
|
||||||
if (!isAllEmpty(form.name)) {
|
if (!isAllEmpty(form.name)) {
|
||||||
// 前端搜索部门名称
|
queryType.name = form.name;
|
||||||
newData = newData.filter(item => item.name.includes(form.name));
|
|
||||||
}
|
}
|
||||||
if (!isAllEmpty(form.status)) {
|
if (!isAllEmpty(form.enabled)) {
|
||||||
// 前端搜索状态
|
queryType.enabled = form.enabled;
|
||||||
newData = newData.filter(item => item.status === form.status);
|
|
||||||
}
|
}
|
||||||
dataList.value = handleTree(newData); // 处理成树结构
|
if (!isAllEmpty(form.createTime)) {
|
||||||
|
queryType.createTime = form.createTime;
|
||||||
|
}
|
||||||
|
const depts = (await Dept.getDepts(queryType)).data; // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
|
||||||
|
let newData = depts.content;
|
||||||
|
//dataList.value = handleTree2(newData); // 处理成树结构
|
||||||
|
dataList.splice(0, dataList.length); // 清空数组
|
||||||
|
newData.forEach(x => {
|
||||||
|
dataList.push(x);
|
||||||
|
});
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
}, 500);
|
}, 500);
|
||||||
@ -95,26 +122,51 @@ export function useDept() {
|
|||||||
if (!treeList || !treeList.length) return;
|
if (!treeList || !treeList.length) return;
|
||||||
const newTreeList = [];
|
const newTreeList = [];
|
||||||
for (let i = 0; i < treeList.length; i++) {
|
for (let i = 0; i < treeList.length; i++) {
|
||||||
treeList[i].disabled = treeList[i].status === 0 ? true : false;
|
treeList[i].disabled = treeList[i].enabled;
|
||||||
formatHigherDeptOptions(treeList[i].children);
|
formatHigherDeptOptions(treeList[i].children);
|
||||||
newTreeList.push(treeList[i]);
|
newTreeList.push(treeList[i]);
|
||||||
}
|
}
|
||||||
return newTreeList;
|
return newTreeList;
|
||||||
}
|
}
|
||||||
|
const higherDeptOptions2: CascaderProps = {
|
||||||
|
lazy: true,
|
||||||
|
checkStrictly: true,
|
||||||
|
lazyLoad(node, resolve) {
|
||||||
|
setTimeout(() => {
|
||||||
|
let queryDept = { pid: node?.data?.value };
|
||||||
|
if (!node.data) {
|
||||||
|
queryDept = null;
|
||||||
|
}
|
||||||
|
Dept.getDepts(queryDept).then(contentData => {
|
||||||
|
const nodes = contentData.data.content.map(item => ({
|
||||||
|
value: item.id,
|
||||||
|
label: item.name,
|
||||||
|
subCount: item.subCount,
|
||||||
|
leaf: item.subCount === 0
|
||||||
|
}));
|
||||||
|
// 调用' resolve '回调以返回子节点数据并指示加载完成。
|
||||||
|
resolve(nodes);
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
};
|
||||||
function openDialog(title = "新增", row?: FormItemProps) {
|
function openDialog(title = "新增", row?: FormItemProps) {
|
||||||
addDialog({
|
addDialog({
|
||||||
title: `${title}部门`,
|
title: `${title}部门`,
|
||||||
props: {
|
props: {
|
||||||
formInline: {
|
formInline: {
|
||||||
higherDeptOptions: formatHigherDeptOptions(cloneDeep(dataList.value)),
|
higherDeptOptions: formatHigherDeptOptions(cloneDeep(dataList)),
|
||||||
|
higherDeptOptions2: higherDeptOptions2,
|
||||||
parentId: row?.parentId ?? 0,
|
parentId: row?.parentId ?? 0,
|
||||||
|
id: row?.id ?? 0,
|
||||||
|
pid: row?.pid ?? 0,
|
||||||
name: row?.name ?? "",
|
name: row?.name ?? "",
|
||||||
|
deptSort: row?.deptSort ?? 0,
|
||||||
principal: row?.principal ?? "",
|
principal: row?.principal ?? "",
|
||||||
phone: row?.phone ?? "",
|
phone: row?.phone ?? "",
|
||||||
email: row?.email ?? "",
|
email: row?.email ?? "",
|
||||||
sort: row?.sort ?? 0,
|
sort: row?.sort ?? 0,
|
||||||
status: row?.status ?? 1,
|
enabled: row?.enabled ?? false,
|
||||||
remark: row?.remark ?? ""
|
remark: row?.remark ?? ""
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -135,13 +187,27 @@ export function useDept() {
|
|||||||
}
|
}
|
||||||
FormRef.validate(valid => {
|
FormRef.validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
console.log("curData", curData);
|
|
||||||
// 表单规则校验通过
|
// 表单规则校验通过
|
||||||
if (title === "新增") {
|
if (title === "新增") {
|
||||||
// 实际开发先调用新增接口,再进行下面操作
|
// 实际开发先调用新增接口,再进行下面操作
|
||||||
|
Dept.add({
|
||||||
|
name: curData.name,
|
||||||
|
pid: curData.pid === 0 ? null : curData.pid[0],
|
||||||
|
deptSort: curData.deptSort,
|
||||||
|
enabled: curData.enabled
|
||||||
|
});
|
||||||
|
chores();
|
||||||
|
} else if (title === "修改") {
|
||||||
|
Dept.edit({
|
||||||
|
id: curData.id,
|
||||||
|
name: curData.name,
|
||||||
|
pid: curData.pid === 0 ? null : curData.pid,
|
||||||
|
deptSort: curData.deptSort,
|
||||||
|
enabled: curData.enabled
|
||||||
|
});
|
||||||
|
// 实际开发先调用编辑接口,再进行下面操作
|
||||||
chores();
|
chores();
|
||||||
} else {
|
} else {
|
||||||
// 实际开发先调用修改接口,再进行下面操作
|
|
||||||
chores();
|
chores();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -151,10 +217,60 @@ export function useDept() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete(row) {
|
function handleDelete(row) {
|
||||||
|
Dept.del([row.id]).then(() => {
|
||||||
message(`您删除了部门名称为${row.name}的这条数据`, { type: "success" });
|
message(`您删除了部门名称为${row.name}的这条数据`, { type: "success" });
|
||||||
|
});
|
||||||
onSearch();
|
onSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onChange({ row, index }) {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`确认要<strong>${
|
||||||
|
!row.enabled ? "停用" : "启用"
|
||||||
|
}</strong><strong style='color:var(--el-color-primary)'>${
|
||||||
|
row.name
|
||||||
|
}</strong>用户吗?`,
|
||||||
|
"系统提示",
|
||||||
|
{
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
draggable: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
switchLoadMap.value[index] = Object.assign(
|
||||||
|
{},
|
||||||
|
switchLoadMap.value[index],
|
||||||
|
{
|
||||||
|
loading: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
switchLoadMap.value[index] = Object.assign(
|
||||||
|
{},
|
||||||
|
switchLoadMap.value[index],
|
||||||
|
{
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Dept.edit({
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
pid: row.pid === 0 ? null : row.pid,
|
||||||
|
deptSort: row.deptSort,
|
||||||
|
enabled: row.enabled
|
||||||
|
});
|
||||||
|
message("已成功修改部门状态", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
row.enabled ? (row.enabled = false) : (row.enabled = true);
|
||||||
|
});
|
||||||
|
}
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
onSearch();
|
onSearch();
|
||||||
});
|
});
|
||||||
@ -164,14 +280,14 @@ export function useDept() {
|
|||||||
loading,
|
loading,
|
||||||
columns,
|
columns,
|
||||||
dataList,
|
dataList,
|
||||||
|
multipleSelection,
|
||||||
/** 搜索 */
|
/** 搜索 */
|
||||||
onSearch,
|
onSearch,
|
||||||
/** 重置 */
|
/** 重置 */
|
||||||
resetForm,
|
resetForm,
|
||||||
/** 新增、修改部门 */
|
/** 新增、编辑部门 */
|
||||||
openDialog,
|
openDialog,
|
||||||
/** 删除部门 */
|
/** 删除部门 */
|
||||||
handleDelete,
|
handleDelete
|
||||||
handleSelectionChange
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
|
import type { CascaderProps } from "element-plus";
|
||||||
interface FormItemProps {
|
interface FormItemProps {
|
||||||
higherDeptOptions: Record<string, unknown>[];
|
higherDeptOptions: Record<string, unknown>[];
|
||||||
|
higherDeptOptions2: CascaderProps;
|
||||||
parentId: number;
|
parentId: number;
|
||||||
|
id: number;
|
||||||
|
pid: number;
|
||||||
|
deptSort: number;
|
||||||
name: string;
|
name: string;
|
||||||
principal: string;
|
principal: string;
|
||||||
phone: string | number;
|
phone: string | number;
|
||||||
email: string;
|
email: string;
|
||||||
sort: number;
|
sort: number;
|
||||||
status: number;
|
enabled: boolean;
|
||||||
remark: string;
|
remark: string;
|
||||||
}
|
}
|
||||||
interface FormProps {
|
interface FormProps {
|
||||||
|
@ -13,8 +13,8 @@ export function usePublicHooks() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const tagStyle = computed(() => {
|
const tagStyle = computed(() => {
|
||||||
return (status: number) => {
|
return (enabled: boolean) => {
|
||||||
return status === 1
|
return enabled
|
||||||
? {
|
? {
|
||||||
"--el-tag-text-color": isDark.value ? "#6abe39" : "#389e0d",
|
"--el-tag-text-color": isDark.value ? "#6abe39" : "#389e0d",
|
||||||
"--el-tag-bg-color": isDark.value ? "#172412" : "#f6ffed",
|
"--el-tag-bg-color": isDark.value ? "#172412" : "#f6ffed",
|
||||||
|
80
src/views/system/job/form.vue
Normal file
80
src/views/system/job/form.vue
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, onMounted } from "vue";
|
||||||
|
import ReCol from "@/components/ReCol";
|
||||||
|
import { formRules } from "./utils/rule";
|
||||||
|
import { FormProps } from "./utils/types";
|
||||||
|
import { usePublicHooks } from "../hooks";
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<FormProps>(), {
|
||||||
|
formInline: () => ({
|
||||||
|
higherDeptOptions: [],
|
||||||
|
jobSort: 0,
|
||||||
|
id: 0,
|
||||||
|
deptSort: 0,
|
||||||
|
enabled: false,
|
||||||
|
name: "",
|
||||||
|
principal: "",
|
||||||
|
phone: "",
|
||||||
|
email: "",
|
||||||
|
version: 0,
|
||||||
|
sort: 0,
|
||||||
|
remark: ""
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
const ruleFormRef = ref();
|
||||||
|
const { switchStyle } = usePublicHooks();
|
||||||
|
const newFormInline = ref(props.formInline);
|
||||||
|
|
||||||
|
function getRef() {
|
||||||
|
return ruleFormRef.value;
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
// 在这里编写页面加载后要执行的代码
|
||||||
|
});
|
||||||
|
defineExpose({ getRef });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormRef"
|
||||||
|
:model="newFormInline"
|
||||||
|
:rules="formRules"
|
||||||
|
label-width="82px"
|
||||||
|
>
|
||||||
|
<el-row :gutter="30">
|
||||||
|
<re-col :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 :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="排序">
|
||||||
|
<el-input-number
|
||||||
|
v-model="newFormInline.jobSort"
|
||||||
|
:min="0"
|
||||||
|
:max="9999"
|
||||||
|
controls-position="right"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="岗位状态">
|
||||||
|
<el-switch
|
||||||
|
v-model="newFormInline.enabled"
|
||||||
|
inline-prompt
|
||||||
|
:active-value="true"
|
||||||
|
:inactive-value="false"
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="停用"
|
||||||
|
:style="switchStyle"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
229
src/views/system/job/index.vue
Normal file
229
src/views/system/job/index.vue
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { useDept } from "./utils/hook";
|
||||||
|
import { PureTableBar } from "@/components/RePureTableBar";
|
||||||
|
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||||
|
import datePicker from "@/views/components/date-picker.vue";
|
||||||
|
import * as Job from "@/api/system/job";
|
||||||
|
|
||||||
|
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 { message } from "@/utils/message";
|
||||||
|
import { ElMessageBox } from "element-plus";
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: "Dept"
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleSelectionChange = val => {
|
||||||
|
multipleSelection.value = val;
|
||||||
|
if (val != null && val.length > 0) {
|
||||||
|
value2.value = false;
|
||||||
|
} else {
|
||||||
|
value2.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function deleteAll() {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`确认要<strong>删除所选的</strong><strong style='color:var(--el-color-primary)'>${multipleSelection.value.length}</strong>个岗位吗?`,
|
||||||
|
"系统提示",
|
||||||
|
{
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
draggable: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
Job.del(multipleSelection.value.map(dept => dept.id)).then(() => {
|
||||||
|
message("已删除所选的岗位", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const exportClick = async () => {
|
||||||
|
const response: Blob = await Job.download(null);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
const url = window.URL.createObjectURL(response); // 创建媒体流 url ,详细了解可自己查 URL.createObjectURL(推荐 MDN )
|
||||||
|
|
||||||
|
a.href = url;
|
||||||
|
a.style.display = "none";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.parentNode.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url); // 删除创建的媒体流 url 对象
|
||||||
|
message("导出成功", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const formRef = ref();
|
||||||
|
const tableRef = ref();
|
||||||
|
const value2 = ref(true);
|
||||||
|
const {
|
||||||
|
form,
|
||||||
|
loading,
|
||||||
|
columns,
|
||||||
|
dataList,
|
||||||
|
multipleSelection,
|
||||||
|
pagination,
|
||||||
|
onSearch,
|
||||||
|
resetForm,
|
||||||
|
openDialog,
|
||||||
|
handleDelete,
|
||||||
|
handleSizeChange,
|
||||||
|
handleCurrentChange
|
||||||
|
} = useDept();
|
||||||
|
</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="name">
|
||||||
|
<el-input
|
||||||
|
v-model="form.name"
|
||||||
|
placeholder="请输入岗位名称"
|
||||||
|
clearable
|
||||||
|
class="!w-[200px]"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态:" prop="status">
|
||||||
|
<el-select
|
||||||
|
v-model="form.enabled"
|
||||||
|
placeholder="请选择状态"
|
||||||
|
clearable
|
||||||
|
class="!w-[180px]"
|
||||||
|
>
|
||||||
|
<el-option label="启用" :value="true" />
|
||||||
|
<el-option label="停用" :value="false" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="" prop="createTime">
|
||||||
|
<datePicker v-model="form.createTime" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="useRenderIcon('search')"
|
||||||
|
:loading="loading"
|
||||||
|
@click="onSearch"
|
||||||
|
>
|
||||||
|
搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
|
重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<PureTableBar
|
||||||
|
title="岗位列表"
|
||||||
|
:columns="columns"
|
||||||
|
:tableRef="tableRef?.getTableRef()"
|
||||||
|
@refresh="onSearch"
|
||||||
|
>
|
||||||
|
<template #add>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
:icon="useRenderIcon(AddFill)"
|
||||||
|
@click="openDialog()"
|
||||||
|
>
|
||||||
|
新增岗位
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #delete>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
:disabled="value2"
|
||||||
|
:icon="useRenderIcon(Delete)"
|
||||||
|
@click="deleteAll()"
|
||||||
|
>
|
||||||
|
删除岗位
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #export>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
:icon="useRenderIcon('solar:upload-bold')"
|
||||||
|
@click="exportClick()"
|
||||||
|
>
|
||||||
|
导出数据
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
|
<pure-table
|
||||||
|
adaptive
|
||||||
|
:adaptiveConfig="{ offsetBottom: 32 }"
|
||||||
|
align-whole="center"
|
||||||
|
row-key="id"
|
||||||
|
showOverflowTooltip
|
||||||
|
table-layout="auto"
|
||||||
|
default-expand-all
|
||||||
|
:loading="loading"
|
||||||
|
:size="size"
|
||||||
|
:columns="dynamicColumns"
|
||||||
|
:data="dataList"
|
||||||
|
:pagination="pagination"
|
||||||
|
:paginationSmall="size === 'small' ? true : false"
|
||||||
|
:header-cell-style="{
|
||||||
|
background: 'var(--el-fill-color-light)',
|
||||||
|
color: 'var(--el-text-color-primary)'
|
||||||
|
}"
|
||||||
|
@selection-change="handleSelectionChange"
|
||||||
|
@page-size-change="handleSizeChange"
|
||||||
|
@page-current-change="handleCurrentChange"
|
||||||
|
>
|
||||||
|
<template #operation="{ row }">
|
||||||
|
<el-button
|
||||||
|
class="reset-margin"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
:size="size"
|
||||||
|
:icon="useRenderIcon(EditPen)"
|
||||||
|
@click="openDialog('编辑', row)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-popconfirm
|
||||||
|
:title="`是否确认删除岗位名称为${row.name}的这条数据`"
|
||||||
|
@confirm="handleDelete(row)"
|
||||||
|
>
|
||||||
|
<template #reference>
|
||||||
|
<el-button
|
||||||
|
class="reset-margin"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
:size="size"
|
||||||
|
:icon="useRenderIcon(Delete)"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-popconfirm>
|
||||||
|
</template>
|
||||||
|
</pure-table>
|
||||||
|
</template>
|
||||||
|
</PureTableBar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.search-form {
|
||||||
|
:deep(.el-form-item) {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
271
src/views/system/job/utils/hook.tsx
Normal file
271
src/views/system/job/utils/hook.tsx
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
import dayjs from "dayjs";
|
||||||
|
import editForm from "../form.vue";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
import * as Job from "@/api/system/job";
|
||||||
|
import { addDialog } from "@/components/ReDialog";
|
||||||
|
import type { PaginationProps } from "@pureadmin/table";
|
||||||
|
import { reactive, ref, onMounted, h } from "vue";
|
||||||
|
import type { FormItemProps } from "../utils/types";
|
||||||
|
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
||||||
|
import { usePublicHooks } from "../../hooks";
|
||||||
|
import { ElMessageBox } from "element-plus";
|
||||||
|
|
||||||
|
export function useDept() {
|
||||||
|
const form = reactive({
|
||||||
|
name: "",
|
||||||
|
enabled: null,
|
||||||
|
createTime: ""
|
||||||
|
});
|
||||||
|
|
||||||
|
const formRef = ref();
|
||||||
|
const dataList = reactive([]);
|
||||||
|
const loading = ref(true);
|
||||||
|
const multipleSelection = ref([]);
|
||||||
|
const switchLoadMap = ref({});
|
||||||
|
const { switchStyle } = usePublicHooks();
|
||||||
|
|
||||||
|
/** 分页配置 */
|
||||||
|
const pagination = reactive<PaginationProps>({
|
||||||
|
total: 10,
|
||||||
|
pageSize: 2,
|
||||||
|
pageSizes: [10, 20, 50],
|
||||||
|
currentPage: 1,
|
||||||
|
align: "left",
|
||||||
|
background: true
|
||||||
|
});
|
||||||
|
/** 表格索引 */
|
||||||
|
const indexMethod = (index: number) => {
|
||||||
|
return index + 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns: TableColumnList = [
|
||||||
|
{
|
||||||
|
type: "selection"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "index",
|
||||||
|
index: indexMethod
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "岗位名称",
|
||||||
|
prop: "name",
|
||||||
|
width: 180,
|
||||||
|
align: "left"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "排序",
|
||||||
|
prop: "jobSort",
|
||||||
|
minWidth: 70
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "状态",
|
||||||
|
prop: "enabled",
|
||||||
|
minWidth: 100,
|
||||||
|
cellRenderer: scope => (
|
||||||
|
<el-switch
|
||||||
|
v-model={scope.row.enabled}
|
||||||
|
size={scope.props.size === "small" ? "small" : "default"}
|
||||||
|
loading={switchLoadMap.value[scope.row.index]?.loading}
|
||||||
|
style={switchStyle.value}
|
||||||
|
inline-prompt
|
||||||
|
active-value={true}
|
||||||
|
inactive-value={false}
|
||||||
|
active-text="启用"
|
||||||
|
inactive-text="停用"
|
||||||
|
onChange={() => onChange(scope as any)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "创建时间",
|
||||||
|
minWidth: 200,
|
||||||
|
prop: "createTime",
|
||||||
|
formatter: ({ createTime }) =>
|
||||||
|
dayjs(createTime).format("YYYY-MM-DD HH:mm:ss")
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "操作",
|
||||||
|
fixed: "right",
|
||||||
|
width: 160,
|
||||||
|
slot: "operation"
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
function resetForm(formEl) {
|
||||||
|
if (!formEl) return;
|
||||||
|
formEl.resetFields();
|
||||||
|
onSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
async function onSearch() {
|
||||||
|
loading.value = true;
|
||||||
|
const queryType = new Job.JobQueryCriteria();
|
||||||
|
if (!isAllEmpty(form.name)) {
|
||||||
|
queryType.name = form.name;
|
||||||
|
}
|
||||||
|
if (!isAllEmpty(form.enabled)) {
|
||||||
|
queryType.enabled = form.enabled;
|
||||||
|
}
|
||||||
|
if (!isAllEmpty(form.createTime)) {
|
||||||
|
queryType.createTime = form.createTime;
|
||||||
|
}
|
||||||
|
queryType.page = pagination.currentPage - 1;
|
||||||
|
queryType.size = pagination.pageSize;
|
||||||
|
const depts = (await Job.get(queryType)).data;
|
||||||
|
pagination.total = depts.totalElements;
|
||||||
|
let newData = depts.content;
|
||||||
|
dataList.splice(0, dataList.length); // 清空数组
|
||||||
|
newData.forEach(x => {
|
||||||
|
dataList.push(x);
|
||||||
|
});
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(title = "新增", row?: FormItemProps) {
|
||||||
|
addDialog({
|
||||||
|
title: `${title}岗位`,
|
||||||
|
props: {
|
||||||
|
formInline: {
|
||||||
|
higherDeptOptions: cloneDeep(dataList),
|
||||||
|
id: row?.id ?? 0,
|
||||||
|
name: row?.name ?? "",
|
||||||
|
jobSort: row?.jobSort ?? 0,
|
||||||
|
principal: row?.principal ?? "",
|
||||||
|
phone: row?.phone ?? "",
|
||||||
|
email: row?.email ?? "",
|
||||||
|
sort: row?.sort ?? 0,
|
||||||
|
version: row?.version ?? 0,
|
||||||
|
enabled: row?.enabled ?? false,
|
||||||
|
remark: row?.remark ?? ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
width: "40%",
|
||||||
|
draggable: true,
|
||||||
|
fullscreenIcon: true,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
contentRenderer: () => h(editForm, { ref: formRef }),
|
||||||
|
beforeSure: (done, { options }) => {
|
||||||
|
const FormRef = formRef.value.getRef();
|
||||||
|
const curData = options.props.formInline as FormItemProps;
|
||||||
|
function chores() {
|
||||||
|
message(`您${title}了岗位名称为${curData.name}的这条数据`, {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
done(); // 关闭弹框
|
||||||
|
onSearch(); // 刷新表格数据
|
||||||
|
}
|
||||||
|
FormRef.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
// 表单规则校验通过
|
||||||
|
if (title === "新增") {
|
||||||
|
// 实际开发先调用新增接口,再进行下面操作
|
||||||
|
Job.add({
|
||||||
|
name: curData.name,
|
||||||
|
enabled: curData.enabled,
|
||||||
|
version: curData.version,
|
||||||
|
jobSort: curData.jobSort
|
||||||
|
}).finally(() => chores());
|
||||||
|
} else if (title === "编辑") {
|
||||||
|
Job.edit({
|
||||||
|
id: curData.id,
|
||||||
|
name: curData.name,
|
||||||
|
enabled: curData.enabled,
|
||||||
|
version: curData.version,
|
||||||
|
jobSort: curData.jobSort
|
||||||
|
}).finally(() => chores());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDelete(row) {
|
||||||
|
Job.del([row.id]).then(() => {
|
||||||
|
message(`您删除了岗位名称为${row.name}的这条数据`, { type: "success" });
|
||||||
|
});
|
||||||
|
onSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onChange({ row, index }) {
|
||||||
|
ElMessageBox.confirm(
|
||||||
|
`确认要<strong>${
|
||||||
|
!row.enabled ? "停用" : "启用"
|
||||||
|
}</strong><strong style='color:var(--el-color-primary)'>${
|
||||||
|
row.name
|
||||||
|
}</strong>用户吗?`,
|
||||||
|
"系统提示",
|
||||||
|
{
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning",
|
||||||
|
dangerouslyUseHTMLString: true,
|
||||||
|
draggable: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.then(() => {
|
||||||
|
switchLoadMap.value[index] = Object.assign(
|
||||||
|
{},
|
||||||
|
switchLoadMap.value[index],
|
||||||
|
{
|
||||||
|
loading: true
|
||||||
|
}
|
||||||
|
);
|
||||||
|
setTimeout(() => {
|
||||||
|
switchLoadMap.value[index] = Object.assign(
|
||||||
|
{},
|
||||||
|
switchLoadMap.value[index],
|
||||||
|
{
|
||||||
|
loading: false
|
||||||
|
}
|
||||||
|
);
|
||||||
|
Job.edit({
|
||||||
|
id: row.id,
|
||||||
|
name: row.name,
|
||||||
|
enabled: row.enabled,
|
||||||
|
version: row.version,
|
||||||
|
jobSort: row.jobSort
|
||||||
|
});
|
||||||
|
message("已成功修改岗位状态", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
}, 300);
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
row.enabled ? (row.enabled = false) : (row.enabled = true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function handleSizeChange(val: number) {
|
||||||
|
pagination.pageSize = val;
|
||||||
|
onSearch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleCurrentChange(val: number) {
|
||||||
|
pagination.currentPage = val;
|
||||||
|
onSearch();
|
||||||
|
}
|
||||||
|
onMounted(() => {
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
loading,
|
||||||
|
columns,
|
||||||
|
dataList,
|
||||||
|
multipleSelection,
|
||||||
|
pagination,
|
||||||
|
/** 搜索 */
|
||||||
|
onSearch,
|
||||||
|
/** 重置 */
|
||||||
|
resetForm,
|
||||||
|
/** 新增、编辑岗位 */
|
||||||
|
openDialog,
|
||||||
|
/** 删除岗位 */
|
||||||
|
handleDelete,
|
||||||
|
handleSizeChange,
|
||||||
|
handleCurrentChange
|
||||||
|
};
|
||||||
|
}
|
8
src/views/system/job/utils/rule.ts
Normal file
8
src/views/system/job/utils/rule.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { reactive } from "vue";
|
||||||
|
import type { FormRules } from "element-plus";
|
||||||
|
|
||||||
|
/** 自定义表单规则校验 */
|
||||||
|
export const formRules = reactive(<FormRules>{
|
||||||
|
name: [{ required: true, message: "岗位名称为必填项", trigger: "blur" }],
|
||||||
|
jobSort: [{ required: true, message: "岗位排序为必填项", trigger: "blur" }]
|
||||||
|
});
|
19
src/views/system/job/utils/types.ts
Normal file
19
src/views/system/job/utils/types.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
interface FormItemProps {
|
||||||
|
higherDeptOptions: Record<string, unknown>[];
|
||||||
|
parentId?: number;
|
||||||
|
id: number;
|
||||||
|
jobSort: number;
|
||||||
|
name: string;
|
||||||
|
principal: string;
|
||||||
|
phone: string | number;
|
||||||
|
email: string;
|
||||||
|
sort: number;
|
||||||
|
version: number;
|
||||||
|
enabled: boolean;
|
||||||
|
remark: string;
|
||||||
|
}
|
||||||
|
interface FormProps {
|
||||||
|
formInline: FormItemProps;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type { FormItemProps, FormProps };
|
@ -1,26 +0,0 @@
|
|||||||
<!-- 初版,持续完善中 -->
|
|
||||||
|
|
||||||
## 字段含义
|
|
||||||
|
|
||||||
| 字段 | 说明 |
|
|
||||||
| :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
||||||
| `menuType` | 菜单类型(`0`代表菜单、`1`代表`iframe`、`2`代表外链、`3`代表按钮) |
|
|
||||||
| `parentId` | |
|
|
||||||
| `title` | 菜单名称(兼容国际化、非国际化,如果用国际化的写法就必须在根目录的`locales`文件夹下对应添加) |
|
|
||||||
| `name` | 路由名称(必须唯一并且和当前路由`component`字段对应的页面里用`defineOptions`包起来的`name`保持一致) |
|
|
||||||
| `path` | 路由路径 |
|
|
||||||
| `component` | 组件路径(传`component`组件路径,那么`path`可以随便写,如果不传,`component`组件路径会跟`path`保持一致) |
|
|
||||||
| `rank` | 菜单排序(平台规定只有`home`路由的`rank`才能为`0`,所以后端在返回`rank`的时候需要从非`0`开始 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E8%8F%9C%E5%8D%95%E6%8E%92%E5%BA%8F-rank)) |
|
|
||||||
| `redirect` | 路由重定向 |
|
|
||||||
| `icon` | 菜单图标 |
|
|
||||||
| `extraIcon` | 右侧图标 |
|
|
||||||
| `enterTransition` | 进场动画(页面加载动画) |
|
|
||||||
| `leaveTransition` | 离场动画(页面加载动画) |
|
|
||||||
| `activePath` | 菜单激活(将某个菜单激活,主要用于通过`query`或`params`传参的路由,当它们通过配置`showLink: false`后不在菜单中显示,就不会有任何菜单高亮,而通过设置`activePath`指定激活菜单即可获得高亮,`activePath`为指定激活菜单的`path`) |
|
|
||||||
| `auths` | 权限标识(按钮级别权限设置) |
|
|
||||||
| `frameSrc` | 链接地址(需要内嵌的`iframe`链接地址) |
|
|
||||||
| `frameLoading` | 加载动画(内嵌的`iframe`页面是否开启首次加载动画) |
|
|
||||||
| `keepAlive` | 缓存页面(是否缓存该路由页面,开启后会保存该页面的整体状态,刷新后会清空状态) |
|
|
||||||
| `hiddenTag` | 标签页(当前菜单名称或自定义信息禁止添加到标签页) |
|
|
||||||
| `showLink` | 菜单(是否显示该菜单) |
|
|
||||||
| `showParent` | 父级菜单(是否显示父级菜单 [点击查看更多](https://yiming_chang.gitee.io/pure-admin-doc/pages/routerMenu/#%E7%AC%AC%E4%B8%80%E7%A7%8D-%E8%AF%A5%E6%A8%A1%E5%BC%8F%E9%92%88%E5%AF%B9%E7%88%B6%E7%BA%A7%E8%8F%9C%E5%8D%95%E4%B8%8B%E5%8F%AA%E6%9C%89%E4%B8%80%E4%B8%AA%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84%E6%83%85%E5%86%B5-%E5%9C%A8%E5%AD%90%E8%8F%9C%E5%8D%95%E7%9A%84-meta-%E5%B1%9E%E6%80%A7%E4%B8%AD%E5%8A%A0%E4%B8%8A-showparent-true-%E5%8D%B3%E5%8F%AF)) |
|
|
@ -1,326 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "vue";
|
|
||||||
import ReCol from "@/components/ReCol";
|
|
||||||
import { formRules } from "./utils/rule";
|
|
||||||
import { FormProps } from "./utils/types";
|
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { IconSelect } from "@/components/ReIcon";
|
|
||||||
import Segmented from "@/components/ReSegmented";
|
|
||||||
import ReAnimateSelector from "@/components/ReAnimateSelector";
|
|
||||||
import {
|
|
||||||
menuTypeOptions,
|
|
||||||
showLinkOptions,
|
|
||||||
keepAliveOptions,
|
|
||||||
hiddenTagOptions,
|
|
||||||
showParentOptions,
|
|
||||||
frameLoadingOptions
|
|
||||||
} from "./utils/enums";
|
|
||||||
|
|
||||||
const props = withDefaults(defineProps<FormProps>(), {
|
|
||||||
formInline: () => ({
|
|
||||||
menuType: 0,
|
|
||||||
higherMenuOptions: [],
|
|
||||||
parentId: 0,
|
|
||||||
title: "",
|
|
||||||
name: "",
|
|
||||||
path: "",
|
|
||||||
component: "",
|
|
||||||
rank: 99,
|
|
||||||
redirect: " ",
|
|
||||||
icon: "",
|
|
||||||
extraIcon: "",
|
|
||||||
enterTransition: "",
|
|
||||||
leaveTransition: "",
|
|
||||||
activePath: "",
|
|
||||||
auths: "",
|
|
||||||
frameSrc: "",
|
|
||||||
frameLoading: true,
|
|
||||||
keepAlive: false,
|
|
||||||
hiddenTag: false,
|
|
||||||
showLink: true,
|
|
||||||
showParent: 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.menuType"
|
|
||||||
:options="menuTypeOptions"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col>
|
|
||||||
<el-form-item label="上级菜单">
|
|
||||||
<el-cascader
|
|
||||||
v-model="newFormInline.parentId"
|
|
||||||
class="w-full"
|
|
||||||
:options="newFormInline.higherMenuOptions"
|
|
||||||
: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.menuType !== 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.menuType !== 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
|
|
||||||
v-show="newFormInline.menuType === 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.menuType === 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.menuType !== 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.menuType !== 3"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="右侧图标">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.extraIcon"
|
|
||||||
clearable
|
|
||||||
placeholder="菜单名称右侧的额外图标"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="进场动画">
|
|
||||||
<ReAnimateSelector
|
|
||||||
v-model="newFormInline.enterTransition"
|
|
||||||
placeholder="请选择页面进场加载动画"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="离场动画">
|
|
||||||
<ReAnimateSelector
|
|
||||||
v-model="newFormInline.leaveTransition"
|
|
||||||
placeholder="请选择页面离场加载动画"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType === 0"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="菜单激活">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.activePath"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入需要激活的菜单"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-if="newFormInline.menuType === 3" :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.menuType === 1"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<!-- iframe -->
|
|
||||||
<el-form-item label="链接地址">
|
|
||||||
<el-input
|
|
||||||
v-model="newFormInline.frameSrc"
|
|
||||||
clearable
|
|
||||||
placeholder="请输入 iframe 链接地址"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-if="newFormInline.menuType === 1" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="加载动画">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.frameLoading ? 0 : 1"
|
|
||||||
:options="frameLoadingOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.frameLoading = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="缓存页面">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.keepAlive ? 0 : 1"
|
|
||||||
:options="keepAliveOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.keepAlive = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col v-show="newFormInline.menuType < 2" :value="12" :xs="24" :sm="24">
|
|
||||||
<el-form-item label="标签页">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.hiddenTag ? 1 : 0"
|
|
||||||
:options="hiddenTagOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.hiddenTag = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType !== 3"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="菜单">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.showLink ? 0 : 1"
|
|
||||||
:options="showLinkOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.showLink = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
<re-col
|
|
||||||
v-show="newFormInline.menuType !== 3"
|
|
||||||
:value="8"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="父级菜单">
|
|
||||||
<Segmented
|
|
||||||
:modelValue="newFormInline.showParent ? 0 : 1"
|
|
||||||
:options="showParentOptions"
|
|
||||||
@change="
|
|
||||||
({ option: { value } }) => {
|
|
||||||
newFormInline.showParent = value;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</re-col>
|
|
||||||
</el-row>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
|
@ -1,157 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref } from "vue";
|
|
||||||
import { useMenu } from "./utils/hook";
|
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { PureTableBar } from "@/components/RePureTableBar";
|
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
|
||||||
|
|
||||||
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";
|
|
||||||
|
|
||||||
defineOptions({
|
|
||||||
name: "SystemMenu"
|
|
||||||
});
|
|
||||||
|
|
||||||
const formRef = ref();
|
|
||||||
const tableRef = ref();
|
|
||||||
const {
|
|
||||||
form,
|
|
||||||
loading,
|
|
||||||
columns,
|
|
||||||
dataList,
|
|
||||||
onSearch,
|
|
||||||
resetForm,
|
|
||||||
openDialog,
|
|
||||||
handleDelete,
|
|
||||||
handleSelectionChange
|
|
||||||
} = useMenu();
|
|
||||||
</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"
|
|
||||||
>
|
|
||||||
搜索
|
|
||||||
</el-button>
|
|
||||||
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
|
||||||
重置
|
|
||||||
</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()"
|
|
||||||
>
|
|
||||||
新增菜单
|
|
||||||
</el-button>
|
|
||||||
</template>
|
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
|
||||||
<pure-table
|
|
||||||
ref="tableRef"
|
|
||||||
adaptive
|
|
||||||
: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)'
|
|
||||||
}"
|
|
||||||
@selection-change="handleSelectionChange"
|
|
||||||
>
|
|
||||||
<template #operation="{ row }">
|
|
||||||
<el-button
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(EditPen)"
|
|
||||||
@click="openDialog('修改', row)"
|
|
||||||
>
|
|
||||||
修改
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
v-show="row.menuType !== 3"
|
|
||||||
class="reset-margin"
|
|
||||||
link
|
|
||||||
type="primary"
|
|
||||||
:size="size"
|
|
||||||
:icon="useRenderIcon(AddFill)"
|
|
||||||
@click="openDialog('新增', { parentId: row.id } as any)"
|
|
||||||
>
|
|
||||||
新增
|
|
||||||
</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)"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</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>
|
|
@ -1,94 +0,0 @@
|
|||||||
import type { OptionsType } from "@/components/ReSegmented";
|
|
||||||
|
|
||||||
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 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
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export {
|
|
||||||
menuTypeOptions,
|
|
||||||
showLinkOptions,
|
|
||||||
keepAliveOptions,
|
|
||||||
hiddenTagOptions,
|
|
||||||
showParentOptions,
|
|
||||||
frameLoadingOptions
|
|
||||||
};
|
|
@ -1,223 +0,0 @@
|
|||||||
import editForm from "../form.vue";
|
|
||||||
import { handleTree } from "@/utils/tree";
|
|
||||||
import { message } from "@/utils/message";
|
|
||||||
import { getMenuList } from "@/api/system";
|
|
||||||
import { transformI18n } from "@/plugins/i18n";
|
|
||||||
import { addDialog } from "@/components/ReDialog";
|
|
||||||
import { reactive, ref, onMounted, h } from "vue";
|
|
||||||
import type { FormItemProps } from "../utils/types";
|
|
||||||
import { cloneDeep, isAllEmpty } from "@pureadmin/utils";
|
|
||||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
|
||||||
|
|
||||||
export function useMenu() {
|
|
||||||
const form = reactive({
|
|
||||||
title: ""
|
|
||||||
});
|
|
||||||
|
|
||||||
const formRef = ref();
|
|
||||||
const dataList = ref([]);
|
|
||||||
const loading = ref(true);
|
|
||||||
|
|
||||||
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: "menuType",
|
|
||||||
width: 100,
|
|
||||||
cellRenderer: ({ row, props }) => (
|
|
||||||
<el-tag
|
|
||||||
size={props.size}
|
|
||||||
type={getMenuType(row.menuType)}
|
|
||||||
effect="plain"
|
|
||||||
>
|
|
||||||
{getMenuType(row.menuType, 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: "showLink",
|
|
||||||
formatter: ({ showLink }) => (showLink ? "否" : "是"),
|
|
||||||
width: 100
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: "操作",
|
|
||||||
fixed: "right",
|
|
||||||
width: 210,
|
|
||||||
slot: "operation"
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
function handleSelectionChange(val) {
|
|
||||||
console.log("handleSelectionChange", val);
|
|
||||||
}
|
|
||||||
|
|
||||||
function resetForm(formEl) {
|
|
||||||
if (!formEl) return;
|
|
||||||
formEl.resetFields();
|
|
||||||
onSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
async function onSearch() {
|
|
||||||
loading.value = true;
|
|
||||||
const { data } = await getMenuList(); // 这里是返回一维数组结构,前端自行处理成树结构,返回格式要求:唯一id加父节点parentId,parentId取父节点id
|
|
||||||
let newData = data;
|
|
||||||
if (!isAllEmpty(form.title)) {
|
|
||||||
// 前端搜索菜单名称
|
|
||||||
newData = newData.filter(item =>
|
|
||||||
transformI18n(item.title).includes(form.title)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
dataList.value = handleTree(newData); // 处理成树结构
|
|
||||||
setTimeout(() => {
|
|
||||||
loading.value = false;
|
|
||||||
}, 500);
|
|
||||||
}
|
|
||||||
|
|
||||||
function formatHigherMenuOptions(treeList) {
|
|
||||||
if (!treeList || !treeList.length) return;
|
|
||||||
const newTreeList = [];
|
|
||||||
for (let i = 0; i < treeList.length; i++) {
|
|
||||||
treeList[i].title = transformI18n(treeList[i].title);
|
|
||||||
formatHigherMenuOptions(treeList[i].children);
|
|
||||||
newTreeList.push(treeList[i]);
|
|
||||||
}
|
|
||||||
return newTreeList;
|
|
||||||
}
|
|
||||||
|
|
||||||
function openDialog(title = "新增", row?: FormItemProps) {
|
|
||||||
addDialog({
|
|
||||||
title: `${title}菜单`,
|
|
||||||
props: {
|
|
||||||
formInline: {
|
|
||||||
menuType: row?.menuType ?? 0,
|
|
||||||
higherMenuOptions: formatHigherMenuOptions(cloneDeep(dataList.value)),
|
|
||||||
parentId: row?.parentId ?? 0,
|
|
||||||
title: row?.title ?? "",
|
|
||||||
name: row?.name ?? "",
|
|
||||||
path: row?.path ?? "",
|
|
||||||
component: row?.component ?? "",
|
|
||||||
rank: row?.rank ?? 99,
|
|
||||||
redirect: row?.redirect ?? "",
|
|
||||||
icon: row?.icon ?? "",
|
|
||||||
extraIcon: row?.extraIcon ?? "",
|
|
||||||
enterTransition: row?.enterTransition ?? "",
|
|
||||||
leaveTransition: row?.leaveTransition ?? "",
|
|
||||||
activePath: row?.activePath ?? "",
|
|
||||||
auths: row?.auths ?? "",
|
|
||||||
frameSrc: row?.frameSrc ?? "",
|
|
||||||
frameLoading: row?.frameLoading ?? true,
|
|
||||||
keepAlive: row?.keepAlive ?? false,
|
|
||||||
hiddenTag: row?.hiddenTag ?? false,
|
|
||||||
showLink: row?.showLink ?? true,
|
|
||||||
showParent: row?.showParent ?? false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
width: "45%",
|
|
||||||
draggable: true,
|
|
||||||
fullscreenIcon: true,
|
|
||||||
closeOnClickModal: false,
|
|
||||||
contentRenderer: () => h(editForm, { ref: formRef }),
|
|
||||||
beforeSure: (done, { options }) => {
|
|
||||||
const FormRef = formRef.value.getRef();
|
|
||||||
const curData = options.props.formInline as FormItemProps;
|
|
||||||
function chores() {
|
|
||||||
message(
|
|
||||||
`您${title}了菜单名称为${transformI18n(curData.title)}的这条数据`,
|
|
||||||
{
|
|
||||||
type: "success"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
done(); // 关闭弹框
|
|
||||||
onSearch(); // 刷新表格数据
|
|
||||||
}
|
|
||||||
FormRef.validate(valid => {
|
|
||||||
if (valid) {
|
|
||||||
console.log("curData", curData);
|
|
||||||
// 表单规则校验通过
|
|
||||||
if (title === "新增") {
|
|
||||||
// 实际开发先调用新增接口,再进行下面操作
|
|
||||||
chores();
|
|
||||||
} else {
|
|
||||||
// 实际开发先调用修改接口,再进行下面操作
|
|
||||||
chores();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDelete(row) {
|
|
||||||
message(`您删除了菜单名称为${transformI18n(row.title)}的这条数据`, {
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
onSearch();
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
onSearch();
|
|
||||||
});
|
|
||||||
|
|
||||||
return {
|
|
||||||
form,
|
|
||||||
loading,
|
|
||||||
columns,
|
|
||||||
dataList,
|
|
||||||
/** 搜索 */
|
|
||||||
onSearch,
|
|
||||||
/** 重置 */
|
|
||||||
resetForm,
|
|
||||||
/** 新增、修改菜单 */
|
|
||||||
openDialog,
|
|
||||||
/** 删除菜单 */
|
|
||||||
handleDelete,
|
|
||||||
handleSelectionChange
|
|
||||||
};
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import { reactive } from "vue";
|
|
||||||
import type { FormRules } from "element-plus";
|
|
||||||
|
|
||||||
/** 自定义表单规则校验 */
|
|
||||||
export 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" }]
|
|
||||||
});
|
|
@ -1,29 +0,0 @@
|
|||||||
interface FormItemProps {
|
|
||||||
/** 菜单类型(0代表菜单、1代表iframe、2代表外链、3代表按钮)*/
|
|
||||||
menuType: number;
|
|
||||||
higherMenuOptions: Record<string, unknown>[];
|
|
||||||
parentId: number;
|
|
||||||
title: string;
|
|
||||||
name: string;
|
|
||||||
path: string;
|
|
||||||
component: string;
|
|
||||||
rank: number;
|
|
||||||
redirect: string;
|
|
||||||
icon: string;
|
|
||||||
extraIcon: string;
|
|
||||||
enterTransition: string;
|
|
||||||
leaveTransition: string;
|
|
||||||
activePath: string;
|
|
||||||
auths: string;
|
|
||||||
frameSrc: string;
|
|
||||||
frameLoading: boolean;
|
|
||||||
keepAlive: boolean;
|
|
||||||
hiddenTag: boolean;
|
|
||||||
showLink: boolean;
|
|
||||||
showParent: boolean;
|
|
||||||
}
|
|
||||||
interface FormProps {
|
|
||||||
formInline: FormItemProps;
|
|
||||||
}
|
|
||||||
|
|
||||||
export type { FormItemProps, FormProps };
|
|
@ -5,15 +5,23 @@ import { FormProps } from "./utils/types";
|
|||||||
|
|
||||||
const props = withDefaults(defineProps<FormProps>(), {
|
const props = withDefaults(defineProps<FormProps>(), {
|
||||||
formInline: () => ({
|
formInline: () => ({
|
||||||
|
id: 0,
|
||||||
name: "",
|
name: "",
|
||||||
code: "",
|
description: "",
|
||||||
remark: ""
|
dataScope: "全部",
|
||||||
|
level: 0,
|
||||||
|
depts: [],
|
||||||
|
deptIds: []
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const ruleFormRef = ref();
|
const ruleFormRef = ref();
|
||||||
const newFormInline = ref(props.formInline);
|
const newFormInline = ref(props.formInline);
|
||||||
|
const props1 = {
|
||||||
|
multiple: true,
|
||||||
|
value: "id",
|
||||||
|
label: "name",
|
||||||
|
checkStrictly: true
|
||||||
|
};
|
||||||
function getRef() {
|
function getRef() {
|
||||||
return ruleFormRef.value;
|
return ruleFormRef.value;
|
||||||
}
|
}
|
||||||
@ -28,6 +36,8 @@ defineExpose({ getRef });
|
|||||||
:rules="formRules"
|
:rules="formRules"
|
||||||
label-width="82px"
|
label-width="82px"
|
||||||
>
|
>
|
||||||
|
<el-row>
|
||||||
|
<el-col>
|
||||||
<el-form-item label="角色名称" prop="name">
|
<el-form-item label="角色名称" prop="name">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newFormInline.name"
|
v-model="newFormInline.name"
|
||||||
@ -35,21 +45,59 @@ defineExpose({ getRef });
|
|||||||
placeholder="请输入角色名称"
|
placeholder="请输入角色名称"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
<el-form-item label="角色标识" prop="code">
|
<el-col :span="12">
|
||||||
<el-input
|
<el-form-item label="角色级别" prop="level">
|
||||||
v-model="newFormInline.code"
|
<el-input-number
|
||||||
|
v-model="newFormInline.level"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入角色标识"
|
:min="0"
|
||||||
|
placeholder="请输入角色级别"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="数据范围" prop="dataScope">
|
||||||
|
<el-select
|
||||||
|
v-model="newFormInline.dataScope"
|
||||||
|
filterable
|
||||||
|
placeholder="请选择数据范围"
|
||||||
|
>
|
||||||
|
<el-option key="全部" value="全部" label="全部" default />
|
||||||
|
<el-option key="本级" value="本级" label="本级" />
|
||||||
|
<el-option key="自定义" value="自定义" label="自定义" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col v-show="newFormInline.dataScope === '自定义'">
|
||||||
|
<el-form-item label="数据权限" prop="deptIds">
|
||||||
|
<el-cascader
|
||||||
|
v-model="newFormInline.deptIds"
|
||||||
|
placeholder="请选择部门"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
:props="props1"
|
||||||
|
:show-all-levels="false"
|
||||||
|
:options="newFormInline.depts"
|
||||||
|
>
|
||||||
|
<template #default="{ node, data }">
|
||||||
|
<span>{{ data.name }}</span>
|
||||||
|
<span v-if="!node.isLeaf && data.children?.length > 0">
|
||||||
|
({{ data.children?.length }})
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-cascader>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col>
|
||||||
<el-form-item label="备注">
|
<el-form-item label="备注">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newFormInline.remark"
|
v-model="newFormInline.description"
|
||||||
placeholder="请输入备注信息"
|
placeholder="请输入备注信息"
|
||||||
type="textarea"
|
type="textarea"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,137 +1,130 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from "vue";
|
import { ref } from "vue";
|
||||||
import { useRole } from "./utils/hook";
|
import { useRole } from "./utils/hook";
|
||||||
|
import tree from "./tree.vue";
|
||||||
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 * as Role from "@/api/system/role";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
import datePicker from "@/views/components/date-picker.vue";
|
||||||
|
|
||||||
|
// import Database from "@iconify-icons/ri/database-2-line";
|
||||||
|
// import More from "@iconify-icons/ep/more-filled";
|
||||||
import Delete from "@iconify-icons/ep/delete";
|
import Delete from "@iconify-icons/ep/delete";
|
||||||
import Edit from "@iconify-icons/ep/edit";
|
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/ep/download";
|
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SystemRole"
|
name: "Role"
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const exportClick = async () => {
|
||||||
|
const response: Blob = await Role.download(null);
|
||||||
|
const a = document.createElement("a");
|
||||||
|
const url = window.URL.createObjectURL(response); // 创建媒体流 url ,详细了解可自己查 URL.createObjectURL(推荐 MDN )
|
||||||
|
|
||||||
|
a.href = url;
|
||||||
|
a.style.display = "none";
|
||||||
|
document.body.appendChild(a);
|
||||||
|
a.click();
|
||||||
|
a.parentNode.removeChild(a);
|
||||||
|
window.URL.revokeObjectURL(url); // 删除创建的媒体流 url 对象
|
||||||
|
message("导出成功", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const treeRef = ref();
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
|
const tableRef = ref();
|
||||||
const {
|
const {
|
||||||
form,
|
form,
|
||||||
loading,
|
loading,
|
||||||
columns,
|
columns,
|
||||||
dataList,
|
dataList,
|
||||||
pagination,
|
pagination,
|
||||||
disabledDelete,
|
treeData,
|
||||||
disabledEdit,
|
treeLoading,
|
||||||
disabledEditFrom,
|
currentRow,
|
||||||
|
deptId,
|
||||||
|
// buttonClass,
|
||||||
|
onTreeSelect,
|
||||||
|
// buttonClass,
|
||||||
onSearch,
|
onSearch,
|
||||||
resetForm,
|
resetForm,
|
||||||
openDialog,
|
openDialog,
|
||||||
handleDelete,
|
handleDelete,
|
||||||
|
// handleDatabase,
|
||||||
handleSizeChange,
|
handleSizeChange,
|
||||||
handleCurrentChange,
|
handleCurrentChange,
|
||||||
handleSelectionChange
|
handleCurrentChange1
|
||||||
} = useRole();
|
} = useRole(tableRef, treeRef);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="main">
|
<div class="flex justify-between">
|
||||||
<!-- 搜索状态栏 -->
|
<div class="w-[calc(90%-180px)]">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:inline="true"
|
:inline="true"
|
||||||
:model="form"
|
:model="form"
|
||||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
|
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px]"
|
||||||
>
|
>
|
||||||
<el-form-item label="角色名称:" prop="name">
|
<el-form-item label="角色名称:" prop="blurry">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.name"
|
v-model="form.blurry"
|
||||||
placeholder="请输入角色名称"
|
placeholder="请输入角色名称"
|
||||||
clearable
|
clearable
|
||||||
class="!w-[180px]"
|
class="!w-[200px]"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态:" prop="status">
|
<el-form-item label="" prop="createTime">
|
||||||
<el-select
|
<datePicker v-model="form.createTime" />
|
||||||
v-model="form.status"
|
|
||||||
placeholder="请选择状态"
|
|
||||||
clearable
|
|
||||||
class="!w-[180px]"
|
|
||||||
>
|
|
||||||
<el-option label="已启用" value="1" />
|
|
||||||
<el-option label="已停用" value="0" />
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:icon="useRenderIcon('ri:search-line')"
|
:icon="useRenderIcon('search')"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="onSearch"
|
@click="onSearch"
|
||||||
>
|
>
|
||||||
搜索
|
搜索
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button :icon="useRenderIcon(Refresh)" @click="resetForm(formRef)">
|
||||||
type="info"
|
|
||||||
:icon="useRenderIcon(Refresh)"
|
|
||||||
@click="resetForm(formRef)"
|
|
||||||
>
|
|
||||||
重置
|
重置
|
||||||
</el-button>
|
</el-button>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<!-- 表格 -->
|
|
||||||
<PureTableBar
|
<PureTableBar title="角色列表" :columns="columns" @refresh="onSearch">
|
||||||
title="角色管理(仅演示,操作后不生效)"
|
<template #add>
|
||||||
:columns="columns"
|
|
||||||
@refresh="onSearch"
|
|
||||||
>
|
|
||||||
<!-- 表头按钮 -->
|
|
||||||
<template #buttons>
|
|
||||||
<el-button
|
<el-button
|
||||||
type="success"
|
type="primary"
|
||||||
:icon="useRenderIcon(AddFill)"
|
:icon="useRenderIcon(AddFill)"
|
||||||
@click="openDialog()"
|
@click="openDialog()"
|
||||||
>
|
>
|
||||||
新增
|
新增角色
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
</template>
|
||||||
type="primary"
|
<template #export>
|
||||||
:icon="useRenderIcon(Edit)"
|
<el-button
|
||||||
:disabled="disabledEdit"
|
type="success"
|
||||||
@click="openDialog('修改', disabledEditFrom)"
|
:icon="useRenderIcon('solar:upload-bold')"
|
||||||
>
|
@click="exportClick()"
|
||||||
修改
|
>
|
||||||
</el-button>
|
导出数据
|
||||||
<el-button
|
|
||||||
type="danger"
|
|
||||||
:icon="useRenderIcon(Delete)"
|
|
||||||
:disabled="disabledDelete"
|
|
||||||
@click="openDialog()"
|
|
||||||
>
|
|
||||||
删除
|
|
||||||
</el-button>
|
|
||||||
<el-button
|
|
||||||
type="warning"
|
|
||||||
:icon="useRenderIcon(Download)"
|
|
||||||
@click="openDialog()"
|
|
||||||
>
|
|
||||||
导出
|
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
<!-- 表格内容 -->
|
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
<pure-table
|
<pure-table
|
||||||
|
ref="tableRef"
|
||||||
align-whole="center"
|
align-whole="center"
|
||||||
showOverflowTooltip
|
showOverflowTooltip
|
||||||
|
adaptive
|
||||||
table-layout="auto"
|
table-layout="auto"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:size="size"
|
:size="size"
|
||||||
adaptive
|
|
||||||
stripe
|
|
||||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
|
||||||
:data="dataList"
|
:data="dataList"
|
||||||
row-key="id"
|
|
||||||
:columns="dynamicColumns"
|
:columns="dynamicColumns"
|
||||||
:pagination="pagination"
|
:pagination="pagination"
|
||||||
:paginationSmall="size === 'small' ? true : false"
|
:paginationSmall="size === 'small' ? true : false"
|
||||||
@ -139,19 +132,20 @@ const {
|
|||||||
background: 'var(--el-fill-color-light)',
|
background: 'var(--el-fill-color-light)',
|
||||||
color: 'var(--el-text-color-primary)'
|
color: 'var(--el-text-color-primary)'
|
||||||
}"
|
}"
|
||||||
@selection-change="handleSelectionChange"
|
highlight-current-row
|
||||||
|
@selection-change="handleCurrentChange"
|
||||||
@page-size-change="handleSizeChange"
|
@page-size-change="handleSizeChange"
|
||||||
@page-current-change="handleCurrentChange"
|
@page-current-change="handleCurrentChange"
|
||||||
|
@current-change="handleCurrentChange1"
|
||||||
>
|
>
|
||||||
<!-- 表格数据操作按钮 -->
|
|
||||||
<template #operation="{ row }">
|
<template #operation="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
class="reset-margin"
|
class="reset-margin"
|
||||||
link
|
link
|
||||||
type="primary"
|
type="primary"
|
||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(Edit)"
|
:icon="useRenderIcon(EditPen)"
|
||||||
@click="openDialog('修改', row)"
|
@click="openDialog('编辑', row)"
|
||||||
>
|
>
|
||||||
修改
|
修改
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -163,7 +157,7 @@ const {
|
|||||||
<el-button
|
<el-button
|
||||||
class="reset-margin"
|
class="reset-margin"
|
||||||
link
|
link
|
||||||
type="danger"
|
type="primary"
|
||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(Delete)"
|
:icon="useRenderIcon(Delete)"
|
||||||
>
|
>
|
||||||
@ -176,6 +170,15 @@ const {
|
|||||||
</template>
|
</template>
|
||||||
</PureTableBar>
|
</PureTableBar>
|
||||||
</div>
|
</div>
|
||||||
|
<tree
|
||||||
|
v-model:currentRow="currentRow"
|
||||||
|
v-model:deptId="deptId"
|
||||||
|
class="min-w-[300px] mr-2"
|
||||||
|
:treeData="treeData"
|
||||||
|
:treeLoading="treeLoading"
|
||||||
|
@tree-select="onTreeSelect"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -183,10 +186,6 @@ const {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
|
||||||
margin: 24px 24px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form {
|
.search-form {
|
||||||
:deep(.el-form-item) {
|
:deep(.el-form-item) {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
213
src/views/system/role/tree.vue
Normal file
213
src/views/system/role/tree.vue
Normal file
@ -0,0 +1,213 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, watch, defineModel, getCurrentInstance } from "vue";
|
||||||
|
|
||||||
|
import Dept from "@iconify-icons/ri/git-branch-line";
|
||||||
|
// import Reset from "@iconify-icons/ri/restart-line";
|
||||||
|
import More2Fill from "@iconify-icons/ri/more-2-fill";
|
||||||
|
import OfficeBuilding from "@iconify-icons/ep/office-building";
|
||||||
|
import LocationCompany from "@iconify-icons/ep/add-location";
|
||||||
|
import { TreeKey } from "element-plus/es/components/tree/src/tree.type.mjs";
|
||||||
|
|
||||||
|
interface Tree {
|
||||||
|
id: number;
|
||||||
|
label: string;
|
||||||
|
highlight?: boolean;
|
||||||
|
children?: Tree[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
treeLoading: Boolean,
|
||||||
|
treeData: Array
|
||||||
|
});
|
||||||
|
const currentRow = defineModel<TreeKey[]>("currentRow");
|
||||||
|
const deptId = defineModel<Number>("deptId");
|
||||||
|
|
||||||
|
const emit = defineEmits(["tree-select"]);
|
||||||
|
|
||||||
|
const treeRef = ref();
|
||||||
|
const treeRef2 = ref();
|
||||||
|
const isExpand = ref(true);
|
||||||
|
const searchValue = ref("");
|
||||||
|
const highlightMap = ref({});
|
||||||
|
const { proxy } = getCurrentInstance();
|
||||||
|
const defaultProps = {
|
||||||
|
children: "children",
|
||||||
|
label: "label"
|
||||||
|
};
|
||||||
|
const buttonClass = computed(() => {
|
||||||
|
return [
|
||||||
|
"!h-[20px]",
|
||||||
|
"reset-margin",
|
||||||
|
"!text-gray-500",
|
||||||
|
"dark:!text-white",
|
||||||
|
"dark:hover:!text-primary"
|
||||||
|
];
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterNode = (value: string, data: Tree) => {
|
||||||
|
if (!value) return true;
|
||||||
|
return data.label.includes(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
function toggleRowExpansionAll(status) {
|
||||||
|
isExpand.value = status;
|
||||||
|
const nodes = (proxy.$refs["treeRef"] as any).store._getAllNodes();
|
||||||
|
for (let i = 0; i < nodes.length; i++) {
|
||||||
|
nodes[i].expanded = status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** 重置部门树状态(选中状态、搜索框值、树初始化) */
|
||||||
|
function onTreeReset() {
|
||||||
|
highlightMap.value = {};
|
||||||
|
searchValue.value = "";
|
||||||
|
toggleRowExpansionAll(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testClick() {
|
||||||
|
emit("tree-select", { id: deptId, menuIds: treeRef.value.getCheckedKeys() });
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(searchValue, val => {
|
||||||
|
treeRef.value!.filter(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
defineExpose({ onTreeReset });
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-loading="props.treeLoading"
|
||||||
|
class="h-full bg-bg_color overflow-auto"
|
||||||
|
:style="{ minHeight: `calc(100vh - 133px)` }"
|
||||||
|
>
|
||||||
|
<div class="flex items-center h-[34px]">
|
||||||
|
<el-row :gutter="24">
|
||||||
|
<el-col :span="16"
|
||||||
|
><div class="grid-content ep-bg-purple" />
|
||||||
|
菜单分配</el-col
|
||||||
|
>
|
||||||
|
<el-col :span="6"
|
||||||
|
><div class="grid-content ep-bg-purple" />
|
||||||
|
<el-button type="primary" @click="testClick">✔保存</el-button>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex items-center h-[34px]">
|
||||||
|
<el-input
|
||||||
|
v-model="searchValue"
|
||||||
|
class="ml-2"
|
||||||
|
size="small"
|
||||||
|
placeholder="请输入部门名称"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<template #suffix>
|
||||||
|
<el-icon v-show="searchValue.length === 0" class="el-input__icon">
|
||||||
|
<IconifyIconOffline icon="search" />
|
||||||
|
</el-icon>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<el-dropdown :hide-on-click="false">
|
||||||
|
<IconifyIconOffline
|
||||||
|
class="w-[28px] cursor-pointer"
|
||||||
|
width="18px"
|
||||||
|
:icon="More2Fill"
|
||||||
|
/>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button
|
||||||
|
:class="buttonClass"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="toggleRowExpansionAll(isExpand ? false : true)"
|
||||||
|
>
|
||||||
|
{{ isExpand ? "折叠全部" : "展开全部" }}
|
||||||
|
</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button
|
||||||
|
:class="buttonClass"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="onTreeReset"
|
||||||
|
>
|
||||||
|
全选
|
||||||
|
</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-button
|
||||||
|
:class="buttonClass"
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="onTreeReset"
|
||||||
|
>
|
||||||
|
取消全选
|
||||||
|
</el-button>
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
|
</div>
|
||||||
|
<el-divider />
|
||||||
|
<el-tree
|
||||||
|
ref="treeRef"
|
||||||
|
node-key="id"
|
||||||
|
size="small"
|
||||||
|
show-checkbox
|
||||||
|
:data="treeData"
|
||||||
|
:props="defaultProps"
|
||||||
|
:default-checked-keys="currentRow"
|
||||||
|
:current-node-key="treeRef2"
|
||||||
|
:expand-on-click-node="false"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
>
|
||||||
|
<template #default="{ node, data }">
|
||||||
|
<span
|
||||||
|
:class="[
|
||||||
|
'pl-1',
|
||||||
|
'pr-1',
|
||||||
|
'rounded',
|
||||||
|
'flex',
|
||||||
|
'items-center',
|
||||||
|
'select-none',
|
||||||
|
'hover:text-primary',
|
||||||
|
searchValue.trim().length > 0 &&
|
||||||
|
node.label.includes(searchValue) &&
|
||||||
|
'text-red-500',
|
||||||
|
highlightMap[node.id]?.highlight ? 'dark:text-primary' : ''
|
||||||
|
]"
|
||||||
|
:style="{
|
||||||
|
color: highlightMap[node.id]?.highlight
|
||||||
|
? 'var(--el-color-primary)'
|
||||||
|
: '',
|
||||||
|
background: highlightMap[node.id]?.highlight
|
||||||
|
? 'var(--el-color-primary-light-7)'
|
||||||
|
: 'transparent'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<IconifyIconOffline
|
||||||
|
:icon="
|
||||||
|
data.type === 1
|
||||||
|
? OfficeBuilding
|
||||||
|
: data.type === 2
|
||||||
|
? LocationCompany
|
||||||
|
: Dept
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
{{ node.label }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
:deep(.el-divider) {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-tree) {
|
||||||
|
--el-tree-node-hover-bg-color: transparent;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1,68 +1,61 @@
|
|||||||
import dayjs from "dayjs";
|
import dayjs from "dayjs";
|
||||||
import editForm from "../form.vue";
|
import editForm from "../form.vue";
|
||||||
import { message } from "@/utils/message";
|
import { message } from "@/utils/message";
|
||||||
import { getRoleList } from "@/api/system";
|
import * as Role from "@/api/system/role";
|
||||||
import { ElMessageBox } from "element-plus";
|
|
||||||
import { usePublicHooks } from "../../hooks";
|
|
||||||
import { addDialog } from "@/components/ReDialog";
|
import { addDialog } from "@/components/ReDialog";
|
||||||
import type { FormItemProps } from "../utils/types";
|
import type { FormItemProps } from "../utils/types";
|
||||||
import type { PaginationProps } from "@pureadmin/table";
|
import type { PaginationProps } from "@pureadmin/table";
|
||||||
import { reactive, ref, onMounted, h, toRaw } from "vue";
|
import { reactive, ref, onMounted, h, toRaw, type Ref } from "vue";
|
||||||
|
import { handleTree } from "@/utils/tree";
|
||||||
|
import * as Dept from "@/api/system/dept";
|
||||||
|
import * as Menu from "@/api/system/menu";
|
||||||
|
import { cloneDeep } from "@pureadmin/utils";
|
||||||
|
import type { ApiAbstract } from "@/utils/http/ApiAbstract";
|
||||||
|
//import { cloneDeep } from "@pureadmin/utils";
|
||||||
|
|
||||||
export function useRole() {
|
export function useRole(tableRef?: Ref, treeRef?: Ref) {
|
||||||
|
const deptList = ref();
|
||||||
|
console.log(tableRef, treeRef);
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
name: "",
|
blurry: "",
|
||||||
code: "",
|
createTime: "",
|
||||||
status: ""
|
size: 10,
|
||||||
|
page: 0
|
||||||
});
|
});
|
||||||
const formRef = ref();
|
const formRef = ref();
|
||||||
const dataList = ref([]);
|
const dataList = ref([]);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
const switchLoadMap = ref({});
|
const treeData = ref([]);
|
||||||
const { switchStyle } = usePublicHooks();
|
const treeLoading = ref(true);
|
||||||
|
const currentRow = ref([]);
|
||||||
|
const deptId = ref<Number>();
|
||||||
|
const nenus = ref<ApiAbstract<Menu.Menu>>();
|
||||||
const pagination = reactive<PaginationProps>({
|
const pagination = reactive<PaginationProps>({
|
||||||
total: 0,
|
total: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
pageSizes: [10, 20, 50, 100],
|
||||||
background: true
|
background: true
|
||||||
});
|
});
|
||||||
const columns: TableColumnList = [
|
const columns: TableColumnList = [
|
||||||
{
|
|
||||||
label: "角色编号",
|
|
||||||
prop: "id",
|
|
||||||
minWidth: 100
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
label: "角色名称",
|
label: "角色名称",
|
||||||
prop: "name",
|
prop: "name",
|
||||||
minWidth: 120
|
minWidth: 120
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "角色标识",
|
label: "数据权限",
|
||||||
prop: "code",
|
prop: "dataScope",
|
||||||
minWidth: 150
|
minWidth: 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "状态",
|
label: "角色级别",
|
||||||
minWidth: 130,
|
prop: "level",
|
||||||
cellRenderer: scope => (
|
minWidth: 150
|
||||||
<el-switch
|
|
||||||
size={scope.props.size === "small" ? "small" : "default"}
|
|
||||||
loading={switchLoadMap.value[scope.index]?.loading}
|
|
||||||
v-model={scope.row.status}
|
|
||||||
active-value={1}
|
|
||||||
inactive-value={0}
|
|
||||||
active-text="已启用"
|
|
||||||
inactive-text="已停用"
|
|
||||||
inline-prompt
|
|
||||||
style={switchStyle.value}
|
|
||||||
onChange={() => onChange(scope as any)}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "备注",
|
label: "描述",
|
||||||
prop: "remark",
|
prop: "description",
|
||||||
minWidth: 150
|
minWidth: 150
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -89,72 +82,37 @@ export function useRole() {
|
|||||||
// ];
|
// ];
|
||||||
// });
|
// });
|
||||||
|
|
||||||
function onChange({ row, index }) {
|
|
||||||
ElMessageBox.confirm(
|
|
||||||
`确认要<strong>${
|
|
||||||
row.status === 0 ? "停用" : "启用"
|
|
||||||
}</strong><strong style='color:var(--el-color-primary)'>${
|
|
||||||
row.name
|
|
||||||
}</strong>吗?`,
|
|
||||||
"系统提示",
|
|
||||||
{
|
|
||||||
confirmButtonText: "确定",
|
|
||||||
cancelButtonText: "取消",
|
|
||||||
type: "warning",
|
|
||||||
dangerouslyUseHTMLString: true,
|
|
||||||
draggable: true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.then(() => {
|
|
||||||
switchLoadMap.value[index] = Object.assign(
|
|
||||||
{},
|
|
||||||
switchLoadMap.value[index],
|
|
||||||
{
|
|
||||||
loading: true
|
|
||||||
}
|
|
||||||
);
|
|
||||||
setTimeout(() => {
|
|
||||||
switchLoadMap.value[index] = Object.assign(
|
|
||||||
{},
|
|
||||||
switchLoadMap.value[index],
|
|
||||||
{
|
|
||||||
loading: false
|
|
||||||
}
|
|
||||||
);
|
|
||||||
message(`已${row.status === 0 ? "停用" : "启用"}${row.name}`, {
|
|
||||||
type: "success"
|
|
||||||
});
|
|
||||||
}, 300);
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
row.status === 0 ? (row.status = 1) : (row.status = 0);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleDelete(row) {
|
function handleDelete(row) {
|
||||||
message(`您删除了角色名称为${row.name}的这条数据`, { type: "success" });
|
message(`您删除了角色名称为${row.name}的这条数据`, { type: "success" });
|
||||||
onSearch();
|
Role.del([row.id]).finally(() => onSearch());
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSizeChange(val: number) {
|
function handleSizeChange(val: number) {
|
||||||
console.log(`${val} items per page`);
|
form.size = val - 1;
|
||||||
|
onSearch();
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleCurrentChange(val: number) {
|
function handleCurrentChange(val: number) {
|
||||||
console.log(`current page: ${val}`);
|
form.page = val - 1;
|
||||||
|
onSearch();
|
||||||
}
|
}
|
||||||
|
function handleCurrentChange1(value: Dept.Dept) {
|
||||||
function handleSelectionChange(val) {
|
deptId.value = value?.id;
|
||||||
console.log("handleSelectionChange", val);
|
const { data } = nenus.value;
|
||||||
|
treeData.value = cloneDeep(handleTree(data, "id", "pid"));
|
||||||
|
currentRow.value = value?.menus?.map(item => item.id) ?? [-1];
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSearch() {
|
async function onSearch() {
|
||||||
|
getDeptTree();
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const { data } = await getRoleList(toRaw(form));
|
const { data } = await Role.get(
|
||||||
dataList.value = data.list;
|
Object.entries(toRaw(form))
|
||||||
pagination.total = data.total;
|
.filter(([_, value]) => value !== null && value !== "")
|
||||||
pagination.pageSize = data.pageSize;
|
.reduce((acc, [key, value]) => ({ ...acc, [key]: value }), {})
|
||||||
pagination.currentPage = data.currentPage;
|
);
|
||||||
|
dataList.value = data.content;
|
||||||
|
pagination.total = data.totalElements;
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
loading.value = false;
|
loading.value = false;
|
||||||
@ -172,9 +130,14 @@ export function useRole() {
|
|||||||
title: `${title}角色`,
|
title: `${title}角色`,
|
||||||
props: {
|
props: {
|
||||||
formInline: {
|
formInline: {
|
||||||
|
id: row?.id ?? 0,
|
||||||
name: row?.name ?? "",
|
name: row?.name ?? "",
|
||||||
code: row?.code ?? "",
|
description: row?.description ?? "",
|
||||||
remark: row?.remark ?? ""
|
level: row?.level ?? 0,
|
||||||
|
dataScope: row?.dataScope ?? "全部",
|
||||||
|
dataIds: row?.deptIds ?? [],
|
||||||
|
deptIds: row?.depts.map(person => person.id) ?? [],
|
||||||
|
depts: cloneDeep(deptList.value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
width: "40%",
|
width: "40%",
|
||||||
@ -194,14 +157,24 @@ export function useRole() {
|
|||||||
}
|
}
|
||||||
FormRef.validate(valid => {
|
FormRef.validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
console.log("curData", curData);
|
|
||||||
// 表单规则校验通过
|
// 表单规则校验通过
|
||||||
if (title === "新增") {
|
if (title === "新增") {
|
||||||
// 实际开发先调用新增接口,再进行下面操作
|
const obj = {
|
||||||
chores();
|
...curData,
|
||||||
} else {
|
depts: curData.deptIds.map(person => ({
|
||||||
// 实际开发先调用修改接口,再进行下面操作
|
id: Object.values(person)[Object.values(person).length - 1]
|
||||||
chores();
|
}))
|
||||||
|
}; // 复制对象
|
||||||
|
delete obj.id; // 删除指定字段
|
||||||
|
Role.add(obj).finally(() => chores());
|
||||||
|
} else if (title === "编辑") {
|
||||||
|
const roleOne = {
|
||||||
|
...curData,
|
||||||
|
depts: curData.deptIds.map(person => ({
|
||||||
|
id: Object.values(person)[Object.values(person).length - 1]
|
||||||
|
}))
|
||||||
|
};
|
||||||
|
Role.edit(roleOne).finally(() => chores());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -209,15 +182,35 @@ export function useRole() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 菜单权限 */
|
function getDeptTree() {
|
||||||
function handleMenu() {
|
Dept.getDeptTree([]).then(data => {
|
||||||
message("等菜单管理页面开发后完善");
|
if (data.status) {
|
||||||
|
deptList.value = handleTree(data.data, "id", "pid");
|
||||||
}
|
}
|
||||||
|
});
|
||||||
/** 数据权限 可自行开发 */
|
}
|
||||||
// function handleDatabase() {}
|
function onTreeSelect({ id, menuIds }) {
|
||||||
|
if (
|
||||||
onMounted(() => {
|
id.value !== null &&
|
||||||
|
id.value !== undefined &&
|
||||||
|
id.value === deptId.value
|
||||||
|
) {
|
||||||
|
Role.menus({
|
||||||
|
id: id.value,
|
||||||
|
menus: menuIds.map(person => ({
|
||||||
|
id: person
|
||||||
|
}))
|
||||||
|
}).then(() => {
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
onMounted(async () => {
|
||||||
|
// 归属部门
|
||||||
|
nenus.value = await Menu.menuTree([]);
|
||||||
|
const { data } = nenus.value;
|
||||||
|
treeData.value = cloneDeep(handleTree(data, "id", "pid"));
|
||||||
|
treeLoading.value = false;
|
||||||
onSearch();
|
onSearch();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -226,18 +219,21 @@ export function useRole() {
|
|||||||
loading,
|
loading,
|
||||||
columns,
|
columns,
|
||||||
dataList,
|
dataList,
|
||||||
|
deptList,
|
||||||
pagination,
|
pagination,
|
||||||
|
treeData,
|
||||||
|
treeLoading,
|
||||||
|
currentRow,
|
||||||
|
deptId,
|
||||||
// buttonClass,
|
// buttonClass,
|
||||||
switchStyle,
|
onTreeSelect,
|
||||||
onChange,
|
|
||||||
onSearch,
|
onSearch,
|
||||||
resetForm,
|
resetForm,
|
||||||
openDialog,
|
openDialog,
|
||||||
handleMenu,
|
|
||||||
handleDelete,
|
handleDelete,
|
||||||
// handleDatabase,
|
// handleDatabase,
|
||||||
handleSizeChange,
|
handleSizeChange,
|
||||||
handleCurrentChange,
|
handleCurrentChange,
|
||||||
handleSelectionChange
|
handleCurrentChange1
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -4,5 +4,19 @@ import type { FormRules } from "element-plus";
|
|||||||
/** 自定义表单规则校验 */
|
/** 自定义表单规则校验 */
|
||||||
export const formRules = reactive(<FormRules>{
|
export const formRules = reactive(<FormRules>{
|
||||||
name: [{ required: true, message: "角色名称为必填项", trigger: "blur" }],
|
name: [{ required: true, message: "角色名称为必填项", trigger: "blur" }],
|
||||||
code: [{ required: true, message: "角色标识为必填项", trigger: "blur" }]
|
code: [{ required: true, message: "角色标识为必填项", trigger: "blur" }],
|
||||||
|
deptIds: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === "" || value.lenght < 1) {
|
||||||
|
callback(new Error("手机号为必填项"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: "blur"
|
||||||
|
// trigger: "click" // 如果想在点击确定按钮时触发这个校验,trigger 设置成 click 即可
|
||||||
|
}
|
||||||
|
]
|
||||||
});
|
});
|
||||||
|
@ -1,12 +1,18 @@
|
|||||||
// 虽然字段很少 但是抽离出来 后续有扩展字段需求就很方便了
|
// 虽然字段很少 但是抽离出来 后续有扩展字段需求就很方便了
|
||||||
|
|
||||||
interface FormItemProps {
|
interface FormItemProps {
|
||||||
|
id: number;
|
||||||
/** 角色名称 */
|
/** 角色名称 */
|
||||||
name: string;
|
name: string;
|
||||||
/** 角色编号 */
|
/** 角色编号 */
|
||||||
code: string;
|
level: number;
|
||||||
|
/** 数据范围 */
|
||||||
|
dataScope: string;
|
||||||
/** 备注 */
|
/** 备注 */
|
||||||
remark: string;
|
description: string;
|
||||||
|
/** 备注 */
|
||||||
|
depts: any[];
|
||||||
|
deptIds: number[];
|
||||||
}
|
}
|
||||||
interface FormProps {
|
interface FormProps {
|
||||||
formInline: FormItemProps;
|
formInline: FormItemProps;
|
||||||
|
@ -9,15 +9,20 @@ const props = withDefaults(defineProps<FormProps>(), {
|
|||||||
formInline: () => ({
|
formInline: () => ({
|
||||||
title: "新增",
|
title: "新增",
|
||||||
higherDeptOptions: [],
|
higherDeptOptions: [],
|
||||||
|
id: null,
|
||||||
parentId: 0,
|
parentId: 0,
|
||||||
nickname: "",
|
nickName: "",
|
||||||
username: "",
|
username: "",
|
||||||
password: "",
|
password: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
email: "",
|
email: "",
|
||||||
sex: "",
|
gender: "男",
|
||||||
status: 1,
|
enabled: false,
|
||||||
remark: ""
|
remark: "",
|
||||||
|
roleOptionsId: [],
|
||||||
|
roleOptions: [],
|
||||||
|
jobOptionsId: [],
|
||||||
|
jobOptions: []
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -51,9 +56,9 @@ defineExpose({ getRef });
|
|||||||
>
|
>
|
||||||
<el-row :gutter="30">
|
<el-row :gutter="30">
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
<el-form-item label="用户昵称" prop="nickname">
|
<el-form-item label="用户昵称" prop="nickName">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="newFormInline.nickname"
|
v-model="newFormInline.nickName"
|
||||||
clearable
|
clearable
|
||||||
placeholder="请输入用户昵称"
|
placeholder="请输入用户昵称"
|
||||||
/>
|
/>
|
||||||
@ -104,24 +109,20 @@ defineExpose({ getRef });
|
|||||||
</re-col>
|
</re-col>
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
<el-form-item label="用户性别">
|
<el-form-item label="用户性别">
|
||||||
<el-select
|
<el-radio-group v-model="newFormInline.gender">
|
||||||
v-model="newFormInline.sex"
|
<el-radio
|
||||||
placeholder="请选择用户性别"
|
|
||||||
class="w-full"
|
|
||||||
clearable
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="(item, index) in sexOptions"
|
v-for="(item, index) in sexOptions"
|
||||||
:key="index"
|
:key="index"
|
||||||
:label="item.label"
|
:label="item.label"
|
||||||
:value="item.value"
|
:value="item.value"
|
||||||
|
border
|
||||||
/>
|
/>
|
||||||
</el-select>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</re-col>
|
</re-col>
|
||||||
|
|
||||||
<re-col :value="12" :xs="24" :sm="24">
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
<el-form-item label="归属部门">
|
<el-form-item label="归属部门" prop="parentId">
|
||||||
<el-cascader
|
<el-cascader
|
||||||
v-model="newFormInline.parentId"
|
v-model="newFormInline.parentId"
|
||||||
class="w-full"
|
class="w-full"
|
||||||
@ -143,25 +144,59 @@ defineExpose({ getRef });
|
|||||||
</el-cascader>
|
</el-cascader>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</re-col>
|
</re-col>
|
||||||
<re-col
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
v-if="newFormInline.title === '新增'"
|
|
||||||
:value="12"
|
|
||||||
:xs="24"
|
|
||||||
:sm="24"
|
|
||||||
>
|
|
||||||
<el-form-item label="用户状态">
|
<el-form-item label="用户状态">
|
||||||
<el-switch
|
<el-switch
|
||||||
v-model="newFormInline.status"
|
v-model="newFormInline.enabled"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
:active-value="1"
|
:active-value="true"
|
||||||
:inactive-value="0"
|
:inactive-value="false"
|
||||||
active-text="启用"
|
active-text="激活"
|
||||||
inactive-text="停用"
|
inactive-text="锁定"
|
||||||
:style="switchStyle"
|
:style="switchStyle"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</re-col>
|
</re-col>
|
||||||
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="角色" prop="roleOptionsId">
|
||||||
|
<el-select
|
||||||
|
v-model="newFormInline.roleOptionsId"
|
||||||
|
placeholder="请选择"
|
||||||
|
class="w-full"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in newFormInline.roleOptions"
|
||||||
|
:key="index"
|
||||||
|
:value="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col :value="12" :xs="24" :sm="24">
|
||||||
|
<el-form-item label="岗位" prop="jobOptionsId">
|
||||||
|
<el-select
|
||||||
|
v-model="newFormInline.jobOptionsId"
|
||||||
|
placeholder="请选择"
|
||||||
|
class="w-full"
|
||||||
|
clearable
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="(item, index) in newFormInline.jobOptions"
|
||||||
|
:key="index"
|
||||||
|
:value="item.id"
|
||||||
|
:label="item.name"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
<re-col>
|
<re-col>
|
||||||
<el-form-item label="备注">
|
<el-form-item label="备注">
|
||||||
<el-input
|
<el-input
|
||||||
|
@ -6,7 +6,7 @@ import { RoleFormProps } from "../utils/types";
|
|||||||
const props = withDefaults(defineProps<RoleFormProps>(), {
|
const props = withDefaults(defineProps<RoleFormProps>(), {
|
||||||
formInline: () => ({
|
formInline: () => ({
|
||||||
username: "",
|
username: "",
|
||||||
nickname: "",
|
nickName: "",
|
||||||
roleOptions: [],
|
roleOptions: [],
|
||||||
ids: []
|
ids: []
|
||||||
})
|
})
|
||||||
@ -18,14 +18,14 @@ const newFormInline = ref(props.formInline);
|
|||||||
<template>
|
<template>
|
||||||
<el-form :model="newFormInline">
|
<el-form :model="newFormInline">
|
||||||
<el-row :gutter="30">
|
<el-row :gutter="30">
|
||||||
<!-- <re-col>
|
|
||||||
<el-form-item label="用户名称" prop="username">
|
|
||||||
<el-input disabled v-model="newFormInline.username" />
|
|
||||||
</el-form-item>
|
|
||||||
</re-col> -->
|
|
||||||
<re-col>
|
<re-col>
|
||||||
<el-form-item label="用户昵称" prop="nickname">
|
<el-form-item label="用户名称" prop="username">
|
||||||
<el-input v-model="newFormInline.nickname" disabled />
|
<el-input v-model="newFormInline.username" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
</re-col>
|
||||||
|
<re-col>
|
||||||
|
<el-form-item label="用户昵称" prop="nickName">
|
||||||
|
<el-input v-model="newFormInline.nickName" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</re-col>
|
</re-col>
|
||||||
<re-col>
|
<re-col>
|
||||||
|
@ -4,6 +4,7 @@ import tree from "./tree.vue";
|
|||||||
import { useUser } from "./utils/hook";
|
import { useUser } from "./utils/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 datePicker from "@/views/components/date-picker.vue";
|
||||||
|
|
||||||
import Upload from "@iconify-icons/ri/upload-line";
|
import Upload from "@iconify-icons/ri/upload-line";
|
||||||
import Role from "@iconify-icons/ri/admin-line";
|
import Role from "@iconify-icons/ri/admin-line";
|
||||||
@ -15,7 +16,7 @@ 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";
|
||||||
|
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "SystemUser"
|
name: "User"
|
||||||
});
|
});
|
||||||
|
|
||||||
const treeRef = ref();
|
const treeRef = ref();
|
||||||
@ -68,34 +69,29 @@ const {
|
|||||||
<el-form-item label="用户名称:" prop="username">
|
<el-form-item label="用户名称:" prop="username">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="form.username"
|
v-model="form.username"
|
||||||
placeholder="请输入用户名称"
|
placeholder="请输入用户名或邮箱"
|
||||||
clearable
|
clearable
|
||||||
class="!w-[180px]"
|
class="!w-[160px]"
|
||||||
/>
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="手机号码:" prop="phone">
|
<el-form-item label="" prop="createTime">
|
||||||
<el-input
|
<datePicker v-model="form.createTime" />
|
||||||
v-model="form.phone"
|
|
||||||
placeholder="请输入手机号码"
|
|
||||||
clearable
|
|
||||||
class="!w-[180px]"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="状态:" prop="status">
|
<el-form-item label="状态:" prop="status">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="form.status"
|
v-model="form.status"
|
||||||
placeholder="请选择"
|
placeholder="请选择"
|
||||||
clearable
|
clearable
|
||||||
class="!w-[180px]"
|
class="!w-[160px]"
|
||||||
>
|
>
|
||||||
<el-option label="已开启" value="1" />
|
<el-option label="激活" value="1" />
|
||||||
<el-option label="已关闭" value="0" />
|
<el-option label="锁定" value="0" />
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item>
|
<el-form-item>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:icon="useRenderIcon('ri:search-line')"
|
:icon="useRenderIcon('search')"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@click="onSearch"
|
@click="onSearch"
|
||||||
>
|
>
|
||||||
@ -107,12 +103,8 @@ const {
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
|
|
||||||
<PureTableBar
|
<PureTableBar title="用户管理" :columns="columns" @refresh="onSearch">
|
||||||
title="用户管理(仅演示,操作后不生效)"
|
<template #add>
|
||||||
:columns="columns"
|
|
||||||
@refresh="onSearch"
|
|
||||||
>
|
|
||||||
<template #buttons>
|
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
:icon="useRenderIcon(AddFill)"
|
:icon="useRenderIcon(AddFill)"
|
||||||
@ -121,6 +113,25 @@ const {
|
|||||||
新增用户
|
新增用户
|
||||||
</el-button>
|
</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
<template #export>
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
:icon="useRenderIcon('solar:upload-bold')"
|
||||||
|
@click="openDialog()"
|
||||||
|
>
|
||||||
|
导出数据
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
<template #reset>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
disabled
|
||||||
|
:icon="useRenderIcon(Refresh)"
|
||||||
|
@click="openDialog()"
|
||||||
|
>
|
||||||
|
重置密码
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
<template v-slot="{ size, dynamicColumns }">
|
<template v-slot="{ size, dynamicColumns }">
|
||||||
<div
|
<div
|
||||||
v-if="selectedNum > 0"
|
v-if="selectedNum > 0"
|
||||||
@ -150,7 +161,6 @@ const {
|
|||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
row-key="id"
|
row-key="id"
|
||||||
adaptive
|
adaptive
|
||||||
:adaptiveConfig="{ offsetBottom: 108 }"
|
|
||||||
align-whole="center"
|
align-whole="center"
|
||||||
table-layout="auto"
|
table-layout="auto"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
@ -174,7 +184,7 @@ const {
|
|||||||
type="primary"
|
type="primary"
|
||||||
:size="size"
|
:size="size"
|
||||||
:icon="useRenderIcon(EditPen)"
|
:icon="useRenderIcon(EditPen)"
|
||||||
@click="openDialog('修改', row)"
|
@click="openDialog('编辑', row)"
|
||||||
>
|
>
|
||||||
修改
|
修改
|
||||||
</el-button>
|
</el-button>
|
||||||
@ -261,10 +271,6 @@ const {
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.main-content {
|
|
||||||
margin: 24px 24px 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-form {
|
.search-form {
|
||||||
:deep(.el-form-item) {
|
:deep(.el-form-item) {
|
||||||
margin-bottom: 12px;
|
margin-bottom: 12px;
|
||||||
|
241
src/views/system/user/info.vue
Normal file
241
src/views/system/user/info.vue
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from "vue";
|
||||||
|
import type { TabsPaneContext } from "element-plus";
|
||||||
|
import { useUser } from "./utils/info";
|
||||||
|
import { isPhone } from "@pureadmin/utils";
|
||||||
|
import type { FormInstance } from "element-plus";
|
||||||
|
|
||||||
|
import Check from "@iconify-icons/ep/avatar";
|
||||||
|
import SignIn from "@iconify-icons/ri/login-box-line";
|
||||||
|
import NodeTree from "@iconify-icons/ri/node-tree";
|
||||||
|
import Phone from "@iconify-icons/ep/iphone";
|
||||||
|
import Mail from "@iconify-icons/ri/mail-fill";
|
||||||
|
import Secure from "@iconify-icons/ri/secure-payment-fill";
|
||||||
|
|
||||||
|
const activeName = ref("first");
|
||||||
|
|
||||||
|
const handleClick = (tab: TabsPaneContext, event: Event) => {
|
||||||
|
console.log("tab", tab.paneName);
|
||||||
|
console.log("event", event);
|
||||||
|
};
|
||||||
|
|
||||||
|
const treeRef = ref();
|
||||||
|
const tableRef = ref();
|
||||||
|
const ruleFormRef = ref<FormInstance>();
|
||||||
|
const {
|
||||||
|
userInfo,
|
||||||
|
handleUpload,
|
||||||
|
handleReset,
|
||||||
|
handleResetEmail,
|
||||||
|
submitEditUser
|
||||||
|
} = useUser(tableRef, treeRef);
|
||||||
|
|
||||||
|
const user = reactive(userInfo.value.user);
|
||||||
|
defineOptions({
|
||||||
|
name: "UserInfo"
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="justify-between">
|
||||||
|
<el-row :gutter="30">
|
||||||
|
<el-col :xs="8" :sm="6" :md="4" :lg="6" :xl="5">
|
||||||
|
<div class="grid-content">
|
||||||
|
<el-card class="box-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>个人信息</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="el-upload">
|
||||||
|
<el-avatar
|
||||||
|
:size="80"
|
||||||
|
src="https://empty"
|
||||||
|
@click="handleUpload(userInfo.user)"
|
||||||
|
>
|
||||||
|
<img :src="'/avatar/' + userInfo.user.avatarName" />
|
||||||
|
</el-avatar>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ul class="user-info">
|
||||||
|
<li>
|
||||||
|
<div style="height: 100%">
|
||||||
|
<IconifyIconOffline class="check-zh" :icon="SignIn" />
|
||||||
|
登录账号
|
||||||
|
<div class="user-right">{{ userInfo.user.username }}</div>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IconifyIconOffline class="check-zh" :icon="Check" />
|
||||||
|
用户昵称
|
||||||
|
<div class="user-right">{{ userInfo.user.nickName }}</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IconifyIconOffline class="check-zh" :icon="NodeTree" />
|
||||||
|
所属部门
|
||||||
|
<div class="user-right">{{ userInfo.user.dept.name }}</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IconifyIconOffline class="check-zh" :icon="Phone" />
|
||||||
|
手机号码
|
||||||
|
<div class="user-right">{{ userInfo.user.phone }}</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IconifyIconOffline class="check-zh" :icon="Mail" />
|
||||||
|
用户邮箱
|
||||||
|
<div class="user-right">{{ userInfo.user.email }}</div>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<IconifyIconOffline class="check-zh" :icon="Secure" />
|
||||||
|
安全设置
|
||||||
|
<div class="user-right">
|
||||||
|
<a @click="handleReset">修改密码 </a>
|
||||||
|
|
||||||
|
<a @click="handleResetEmail"> 修改邮箱</a>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="8" :sm="6" :md="8" :lg="18" :xl="11"
|
||||||
|
><div class="grid-content">
|
||||||
|
<el-tabs
|
||||||
|
v-model="activeName"
|
||||||
|
class="demo-tabs grid-content"
|
||||||
|
@tab-click="handleClick"
|
||||||
|
>
|
||||||
|
<el-tab-pane label="用户资料" name="first">
|
||||||
|
<el-form
|
||||||
|
ref="ruleFormRef"
|
||||||
|
:model="user"
|
||||||
|
style="margin-top: 10px"
|
||||||
|
size="small"
|
||||||
|
label-width="65px"
|
||||||
|
>
|
||||||
|
<el-form-item
|
||||||
|
label="昵称"
|
||||||
|
prop="nickName"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '昵称为必填项',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
min: 3,
|
||||||
|
max: 12,
|
||||||
|
message: 'Length should be 4 to 12',
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-input
|
||||||
|
v-model="user.nickName"
|
||||||
|
clearable
|
||||||
|
style="width: 35%"
|
||||||
|
/>
|
||||||
|
<span style=" margin-left: 10px;color: #e6a23c"
|
||||||
|
>⚠️用户昵称不作为登录使用</span
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="手机号"
|
||||||
|
prop="phone"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '手机号为必填项',
|
||||||
|
trigger: 'blur'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === '') {
|
||||||
|
callback(new Error('手机号为必填项'));
|
||||||
|
} else if (!isPhone(value)) {
|
||||||
|
callback(new Error('请输入正确的手机号码格式'));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: 'blur'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-input v-model="user.phone" clearable style="width: 35%" />
|
||||||
|
<span style=" margin-left: 10px;color: #e6a23c"
|
||||||
|
>⚠️手机号码不能重复</span
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="性别" prop="gender">
|
||||||
|
<el-radio-group v-model="user.gender" style="width: 178px">
|
||||||
|
<el-radio label="男" value="男">男</el-radio>
|
||||||
|
<el-radio label="女" value="女">女</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="submitEditUser(ruleFormRef, user)"
|
||||||
|
>保存配置</el-button
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="操作日志" name="second">操作日志</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div></el-col
|
||||||
|
>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.el-col {
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.grid-content {
|
||||||
|
min-height: 36px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.demo-tabs > .el-tabs__content {
|
||||||
|
padding: 32px;
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #6b778c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-upload {
|
||||||
|
display: inline-block;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
padding-left: 0;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
padding: 11px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
border-bottom: 1px solid #f0f3f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-right {
|
||||||
|
float: right;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #317ef3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-zh {
|
||||||
|
display: unset;
|
||||||
|
}
|
||||||
|
</style>
|
@ -1 +1 @@
|
|||||||
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M22 4V2H2v2h9v14.17l-5.5-5.5-1.42 1.41L12 22l7.92-7.92-1.42-1.41-5.5 5.5V4z"/></svg>
|
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M22 4V2H2v2h9v14.17l-5.5-5.5-1.42 1.41L12 22l7.92-7.92-1.42-1.41-5.5 5.5V4h9Z"/></svg>
|
Before Width: | Height: | Size: 161 B After Width: | Height: | Size: 163 B |
@ -1 +1 @@
|
|||||||
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M4 2H2v20h2v-9h14.17l-5.5 5.5 1.41 1.42L22 12l-7.92-7.92-1.41 1.42 5.5 5.5H4z"/></svg>
|
<svg width="32" height="32" viewBox="0 0 24 24"><path fill="currentColor" d="M4 2H2v20h2v-9h14.17l-5.5 5.5l1.41 1.42L22 12l-7.92-7.92l-1.41 1.42l5.5 5.5H4V2Z"/></svg>
|
Before Width: | Height: | Size: 163 B After Width: | Height: | Size: 166 B |
@ -96,7 +96,7 @@ defineExpose({ onTreeReset });
|
|||||||
<div
|
<div
|
||||||
v-loading="props.treeLoading"
|
v-loading="props.treeLoading"
|
||||||
class="h-full bg-bg_color overflow-auto"
|
class="h-full bg-bg_color overflow-auto"
|
||||||
:style="{ minHeight: `calc(100vh - 145px)` }"
|
:style="{ minHeight: `calc(100vh - 133px)` }"
|
||||||
>
|
>
|
||||||
<div class="flex items-center h-[34px]">
|
<div class="flex items-center h-[34px]">
|
||||||
<el-input
|
<el-input
|
||||||
@ -110,7 +110,7 @@ defineExpose({ onTreeReset });
|
|||||||
<el-icon class="el-input__icon">
|
<el-icon class="el-input__icon">
|
||||||
<IconifyIconOffline
|
<IconifyIconOffline
|
||||||
v-show="searchValue.length === 0"
|
v-show="searchValue.length === 0"
|
||||||
icon="ri:search-line"
|
icon="search"
|
||||||
/>
|
/>
|
||||||
</el-icon>
|
</el-icon>
|
||||||
</template>
|
</template>
|
||||||
|
@ -10,7 +10,6 @@ const props = defineProps({
|
|||||||
const emit = defineEmits(["cropper"]);
|
const emit = defineEmits(["cropper"]);
|
||||||
|
|
||||||
const infos = ref();
|
const infos = ref();
|
||||||
const popoverRef = ref();
|
|
||||||
const refCropper = ref();
|
const refCropper = ref();
|
||||||
const showPopover = ref(false);
|
const showPopover = ref(false);
|
||||||
const cropperImg = ref<string>("");
|
const cropperImg = ref<string>("");
|
||||||
@ -20,22 +19,11 @@ function onCropper({ base64, blob, info }) {
|
|||||||
cropperImg.value = base64;
|
cropperImg.value = base64;
|
||||||
emit("cropper", { base64, blob, info });
|
emit("cropper", { base64, blob, info });
|
||||||
}
|
}
|
||||||
|
|
||||||
function hidePopover() {
|
|
||||||
popoverRef.value.hide();
|
|
||||||
}
|
|
||||||
|
|
||||||
defineExpose({ hidePopover });
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div v-loading="!showPopover" element-loading-background="transparent">
|
<div v-loading="!showPopover" element-loading-background="transparent">
|
||||||
<el-popover
|
<el-popover :visible="showPopover" placement="right-end" width="18vw">
|
||||||
ref="popoverRef"
|
|
||||||
:visible="showPopover"
|
|
||||||
placement="right"
|
|
||||||
width="18vw"
|
|
||||||
>
|
|
||||||
<template #reference>
|
<template #reference>
|
||||||
<div class="w-[18vw]">
|
<div class="w-[18vw]">
|
||||||
<ReCropper
|
<ReCropper
|
||||||
|
@ -10,13 +10,17 @@ import { usePublicHooks } from "../../hooks";
|
|||||||
import { addDialog } from "@/components/ReDialog";
|
import { addDialog } from "@/components/ReDialog";
|
||||||
import type { PaginationProps } from "@pureadmin/table";
|
import type { PaginationProps } from "@pureadmin/table";
|
||||||
import type { FormItemProps, RoleFormItemProps } from "../utils/types";
|
import type { FormItemProps, RoleFormItemProps } from "../utils/types";
|
||||||
import { hideTextAtIndex, getKeyList, isAllEmpty } from "@pureadmin/utils";
|
|
||||||
import {
|
import {
|
||||||
getRoleIds,
|
hideTextAtIndex,
|
||||||
getDeptList,
|
getKeyList,
|
||||||
getUserList,
|
isAllEmpty,
|
||||||
getAllRoleList
|
cloneDeep
|
||||||
} from "@/api/system";
|
} from "@pureadmin/utils";
|
||||||
|
import { baseUrlAvatar } from "@/api/utils";
|
||||||
|
import * as User from "@/api/system/user";
|
||||||
|
import * as Dept from "@/api/system/dept";
|
||||||
|
import * as Job from "@/api/system/job";
|
||||||
|
import * as Role from "@/api/system/role";
|
||||||
import {
|
import {
|
||||||
ElForm,
|
ElForm,
|
||||||
ElInput,
|
ElInput,
|
||||||
@ -24,22 +28,14 @@ import {
|
|||||||
ElProgress,
|
ElProgress,
|
||||||
ElMessageBox
|
ElMessageBox
|
||||||
} from "element-plus";
|
} from "element-plus";
|
||||||
import {
|
import { type Ref, h, ref, watch, computed, reactive, onMounted } from "vue";
|
||||||
type Ref,
|
|
||||||
h,
|
|
||||||
ref,
|
|
||||||
toRaw,
|
|
||||||
watch,
|
|
||||||
computed,
|
|
||||||
reactive,
|
|
||||||
onMounted
|
|
||||||
} from "vue";
|
|
||||||
|
|
||||||
export function useUser(tableRef: Ref, treeRef: Ref) {
|
export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
// 左侧部门树的id
|
// 左侧部门树的id
|
||||||
deptId: "",
|
deptId: "",
|
||||||
username: "",
|
username: "",
|
||||||
|
createTime: "",
|
||||||
phone: "",
|
phone: "",
|
||||||
status: ""
|
status: ""
|
||||||
});
|
});
|
||||||
@ -55,10 +51,13 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
const treeData = ref([]);
|
const treeData = ref([]);
|
||||||
const treeLoading = ref(true);
|
const treeLoading = ref(true);
|
||||||
const selectedNum = ref(0);
|
const selectedNum = ref(0);
|
||||||
|
/** 分页配置 */
|
||||||
const pagination = reactive<PaginationProps>({
|
const pagination = reactive<PaginationProps>({
|
||||||
total: 0,
|
total: 0,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
|
pageSizes: [10, 15, 20],
|
||||||
currentPage: 1,
|
currentPage: 1,
|
||||||
|
align: "left",
|
||||||
background: true
|
background: true
|
||||||
});
|
});
|
||||||
const columns: TableColumnList = [
|
const columns: TableColumnList = [
|
||||||
@ -75,15 +74,21 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "用户头像",
|
label: "用户头像",
|
||||||
prop: "avatar",
|
prop: "avatarName",
|
||||||
cellRenderer: ({ row }) => (
|
cellRenderer: ({ row }) => (
|
||||||
<el-image
|
<el-image
|
||||||
fit="cover"
|
fit="cover"
|
||||||
preview-teleported={true}
|
preview-teleported={true}
|
||||||
src={row.avatar}
|
src={baseUrlAvatar(row.avatarName)}
|
||||||
preview-src-list={Array.of(row.avatar)}
|
preview-src-list={Array.of(baseUrlAvatar(row.avatarName))}
|
||||||
class="w-[24px] h-[24px] rounded-full align-middle"
|
class="w-[24px] h-[24px] rounded-full align-middle"
|
||||||
/>
|
>
|
||||||
|
{{
|
||||||
|
error: () => (
|
||||||
|
<el-image src="https://element-plus.org/images/element-plus-logo.svg" />
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</el-image>
|
||||||
),
|
),
|
||||||
width: 90
|
width: 90
|
||||||
},
|
},
|
||||||
@ -94,20 +99,20 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "用户昵称",
|
label: "用户昵称",
|
||||||
prop: "nickname",
|
prop: "nickName",
|
||||||
minWidth: 130
|
minWidth: 130
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "性别",
|
label: "性别",
|
||||||
prop: "sex",
|
prop: "gender",
|
||||||
minWidth: 90,
|
minWidth: 90,
|
||||||
cellRenderer: ({ row, props }) => (
|
cellRenderer: ({ row, props }) => (
|
||||||
<el-tag
|
<el-tag
|
||||||
size={props.size}
|
size={props.size}
|
||||||
type={row.sex === 1 ? "danger" : null}
|
type={row.sex === 1 ? "danger" : "success"}
|
||||||
effect="plain"
|
effect="plain"
|
||||||
>
|
>
|
||||||
{row.sex === 1 ? "女" : "男"}
|
{row.gender}
|
||||||
</el-tag>
|
</el-tag>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
@ -122,19 +127,24 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
minWidth: 90,
|
minWidth: 90,
|
||||||
formatter: ({ phone }) => hideTextAtIndex(phone, { start: 3, end: 6 })
|
formatter: ({ phone }) => hideTextAtIndex(phone, { start: 3, end: 6 })
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
label: "邮箱",
|
||||||
|
prop: "email",
|
||||||
|
minWidth: 90
|
||||||
|
},
|
||||||
{
|
{
|
||||||
label: "状态",
|
label: "状态",
|
||||||
prop: "status",
|
prop: "enabled",
|
||||||
minWidth: 90,
|
minWidth: 90,
|
||||||
cellRenderer: scope => (
|
cellRenderer: scope => (
|
||||||
<el-switch
|
<el-switch
|
||||||
size={scope.props.size === "small" ? "small" : "default"}
|
size={scope.props.size === "small" ? "small" : "default"}
|
||||||
loading={switchLoadMap.value[scope.index]?.loading}
|
loading={switchLoadMap.value[scope.index]?.loading}
|
||||||
v-model={scope.row.status}
|
v-model={scope.row.enabled}
|
||||||
active-value={1}
|
active-value={true}
|
||||||
inactive-value={0}
|
inactive-value={false}
|
||||||
active-text="已启用"
|
active-text="激活"
|
||||||
inactive-text="已停用"
|
inactive-text="锁定"
|
||||||
inline-prompt
|
inline-prompt
|
||||||
style={switchStyle.value}
|
style={switchStyle.value}
|
||||||
onChange={() => onChange(scope as any)}
|
onChange={() => onChange(scope as any)}
|
||||||
@ -164,6 +174,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
"dark:hover:!text-primary"
|
"dark:hover:!text-primary"
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重置的新密码
|
// 重置的新密码
|
||||||
const pwdForm = reactive({
|
const pwdForm = reactive({
|
||||||
newPwd: ""
|
newPwd: ""
|
||||||
@ -178,11 +189,12 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
// 当前密码强度(0-4)
|
// 当前密码强度(0-4)
|
||||||
const curScore = ref();
|
const curScore = ref();
|
||||||
const roleOptions = ref([]);
|
const roleOptions = ref([]);
|
||||||
|
const jobOptions = ref([]);
|
||||||
|
|
||||||
function onChange({ row, index }) {
|
function onChange({ row, index }) {
|
||||||
ElMessageBox.confirm(
|
ElMessageBox.confirm(
|
||||||
`确认要<strong>${
|
`确认要<strong>${
|
||||||
row.status === 0 ? "停用" : "启用"
|
row.enabled ? "激活" : "锁定"
|
||||||
}</strong><strong style='color:var(--el-color-primary)'>${
|
}</strong><strong style='color:var(--el-color-primary)'>${
|
||||||
row.username
|
row.username
|
||||||
}</strong>用户吗?`,
|
}</strong>用户吗?`,
|
||||||
@ -203,7 +215,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
loading: true
|
loading: true
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
setTimeout(() => {
|
User.edit(row).finally(() => {
|
||||||
switchLoadMap.value[index] = Object.assign(
|
switchLoadMap.value[index] = Object.assign(
|
||||||
{},
|
{},
|
||||||
switchLoadMap.value[index],
|
switchLoadMap.value[index],
|
||||||
@ -214,7 +226,8 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
message("已成功修改用户状态", {
|
message("已成功修改用户状态", {
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
}, 300);
|
onSearch();
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
row.status === 0 ? (row.status = 1) : (row.status = 0);
|
row.status === 0 ? (row.status = 1) : (row.status = 0);
|
||||||
@ -226,8 +239,10 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleDelete(row) {
|
function handleDelete(row) {
|
||||||
message(`您删除了用户编号为${row.id}的这条数据`, { type: "success" });
|
User.del([row.id]).then(() => {
|
||||||
|
message(`您删除了用户编号为${row.id}的这条数据!`, { type: "success" });
|
||||||
onSearch();
|
onSearch();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function handleSizeChange(val: number) {
|
function handleSizeChange(val: number) {
|
||||||
@ -256,25 +271,42 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
function onbatchDel() {
|
function onbatchDel() {
|
||||||
// 返回当前选中的行
|
// 返回当前选中的行
|
||||||
const curSelected = tableRef.value.getTableRef().getSelectionRows();
|
const curSelected = tableRef.value.getTableRef().getSelectionRows();
|
||||||
|
User.del(getKeyList(curSelected, "id")).then(() => {
|
||||||
// 接下来根据实际业务,通过选中行的某项数据,比如下面的id,调用接口进行批量删除
|
// 接下来根据实际业务,通过选中行的某项数据,比如下面的id,调用接口进行批量删除
|
||||||
message(`已删除用户编号为 ${getKeyList(curSelected, "id")} 的数据`, {
|
message(`已删除用户编号为 ${getKeyList(curSelected, "id")} 的数据`, {
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
tableRef.value.getTableRef().clearSelection();
|
tableRef.value.getTableRef().clearSelection();
|
||||||
onSearch();
|
onSearch();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onSearch() {
|
async function onSearch() {
|
||||||
loading.value = true;
|
loading.value = true;
|
||||||
const { data } = await getUserList(toRaw(form));
|
const queryType = new User.UserQueryCriteria();
|
||||||
dataList.value = data.list;
|
if (!isAllEmpty(form.username)) {
|
||||||
pagination.total = data.total;
|
queryType.name = form.username;
|
||||||
pagination.pageSize = data.pageSize;
|
}
|
||||||
pagination.currentPage = data.currentPage;
|
if (
|
||||||
|
form.deptId !== null &&
|
||||||
setTimeout(() => {
|
form.deptId !== "0" &&
|
||||||
loading.value = false;
|
form.deptId !== "" &&
|
||||||
}, 500);
|
form.deptId !== " "
|
||||||
|
) {
|
||||||
|
queryType.deptId = Number(form.deptId);
|
||||||
|
}
|
||||||
|
queryType.page = pagination.currentPage - 1;
|
||||||
|
queryType.size = pagination.pageSize;
|
||||||
|
User.get(queryType)
|
||||||
|
.then(data => {
|
||||||
|
data.data.content.forEach(userFor => {
|
||||||
|
userFor["roleOptionsId"] = userFor.roles.map(x => x.id);
|
||||||
|
userFor["jobOptionsId"] = userFor.jobs.map(x => x.id);
|
||||||
|
});
|
||||||
|
dataList.value = data.data.content;
|
||||||
|
pagination.total = data.data.totalElements;
|
||||||
|
})
|
||||||
|
.finally(() => (loading.value = false));
|
||||||
}
|
}
|
||||||
|
|
||||||
const resetForm = formEl => {
|
const resetForm = formEl => {
|
||||||
@ -309,15 +341,20 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
formInline: {
|
formInline: {
|
||||||
title,
|
title,
|
||||||
higherDeptOptions: formatHigherDeptOptions(higherDeptOptions.value),
|
higherDeptOptions: formatHigherDeptOptions(higherDeptOptions.value),
|
||||||
|
id: row?.id,
|
||||||
parentId: row?.dept.id ?? 0,
|
parentId: row?.dept.id ?? 0,
|
||||||
nickname: row?.nickname ?? "",
|
nickName: row?.nickName ?? "",
|
||||||
username: row?.username ?? "",
|
username: row?.nickName ?? "",
|
||||||
password: row?.password ?? "",
|
password: row?.password ?? "",
|
||||||
phone: row?.phone ?? "",
|
phone: row?.phone ?? "",
|
||||||
email: row?.email ?? "",
|
email: row?.email ?? "",
|
||||||
sex: row?.sex ?? "",
|
gender: row?.gender ?? "男",
|
||||||
status: row?.status ?? 1,
|
enabled: row?.enabled ?? 1,
|
||||||
remark: row?.remark ?? ""
|
remark: row?.remark ?? "",
|
||||||
|
jobOptionsId: row?.jobOptionsId ?? [],
|
||||||
|
roleOptionsId: row?.roleOptionsId ?? [],
|
||||||
|
roleOptions: roleOptions?.value ?? [],
|
||||||
|
jobOptions: jobOptions?.value ?? []
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
width: "46%",
|
width: "46%",
|
||||||
@ -329,7 +366,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
const FormRef = formRef.value.getRef();
|
const FormRef = formRef.value.getRef();
|
||||||
const curData = options.props.formInline as FormItemProps;
|
const curData = options.props.formInline as FormItemProps;
|
||||||
function chores() {
|
function chores() {
|
||||||
message(`您${title}了用户名称为${curData.username}的这条数据`, {
|
message(`您${title}了用户名称为${curData.nickName}的这条数据`, {
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
done(); // 关闭弹框
|
done(); // 关闭弹框
|
||||||
@ -337,14 +374,30 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
}
|
}
|
||||||
FormRef.validate(valid => {
|
FormRef.validate(valid => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
console.log("curData", curData);
|
const userClone = cloneDeep(curData);
|
||||||
|
userClone["dept"] = { id: userClone.parentId };
|
||||||
|
userClone["roles"] = userClone.roleOptionsId.map(x => ({
|
||||||
|
id: x
|
||||||
|
}));
|
||||||
|
userClone["jobs"] = userClone.jobOptionsId.map(x => ({
|
||||||
|
id: x
|
||||||
|
}));
|
||||||
|
delete userClone.title;
|
||||||
|
delete userClone.higherDeptOptions;
|
||||||
|
delete userClone.parentId;
|
||||||
|
delete userClone.roleOptions;
|
||||||
|
delete userClone.jobOptions;
|
||||||
|
delete userClone.roleOptionsId;
|
||||||
|
delete userClone.jobOptionsId;
|
||||||
// 表单规则校验通过
|
// 表单规则校验通过
|
||||||
if (title === "新增") {
|
if (title === "新增") {
|
||||||
// 实际开发先调用新增接口,再进行下面操作
|
User.add(userClone);
|
||||||
chores();
|
chores();
|
||||||
|
console.log("curData", userClone);
|
||||||
} else {
|
} else {
|
||||||
// 实际开发先调用修改接口,再进行下面操作
|
User.edit(userClone).finally(() => {
|
||||||
chores();
|
chores();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -352,7 +405,6 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const cropRef = ref();
|
|
||||||
/** 上传头像 */
|
/** 上传头像 */
|
||||||
function handleUpload(row) {
|
function handleUpload(row) {
|
||||||
addDialog({
|
addDialog({
|
||||||
@ -362,17 +414,15 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
closeOnClickModal: false,
|
closeOnClickModal: false,
|
||||||
contentRenderer: () =>
|
contentRenderer: () =>
|
||||||
h(croppingUpload, {
|
h(croppingUpload, {
|
||||||
ref: cropRef,
|
imgSrc: baseUrlAvatar(row.avatarName),
|
||||||
imgSrc: row.avatar,
|
|
||||||
onCropper: info => (avatarInfo.value = info)
|
onCropper: info => (avatarInfo.value = info)
|
||||||
}),
|
}),
|
||||||
beforeSure: done => {
|
beforeSure: done => {
|
||||||
console.log("裁剪后的图片信息:", avatarInfo.value);
|
User.updateAvatarByid({ id: row.id, avatar: avatarInfo.value.blob });
|
||||||
// 根据实际业务使用avatarInfo.value和row里的某些字段去调用上传头像接口即可
|
// 根据实际业务使用avatarInfo.value和row里的某些字段去调用上传头像接口即可
|
||||||
done(); // 关闭弹框
|
done(); // 关闭弹框
|
||||||
onSearch(); // 刷新表格数据
|
onSearch(); // 刷新表格数据
|
||||||
},
|
}
|
||||||
closeCallBack: () => cropRef.value.hidePopover()
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,7 +495,6 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
message(`已成功重置 ${row.username} 用户的密码`, {
|
message(`已成功重置 ${row.username} 用户的密码`, {
|
||||||
type: "success"
|
type: "success"
|
||||||
});
|
});
|
||||||
console.log(pwdForm.newPwd);
|
|
||||||
// 根据实际业务使用pwdForm.newPwd和row里的某些字段去调用重置用户密码接口即可
|
// 根据实际业务使用pwdForm.newPwd和row里的某些字段去调用重置用户密码接口即可
|
||||||
done(); // 关闭弹框
|
done(); // 关闭弹框
|
||||||
onSearch(); // 刷新表格数据
|
onSearch(); // 刷新表格数据
|
||||||
@ -457,14 +506,15 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
|
|
||||||
/** 分配角色 */
|
/** 分配角色 */
|
||||||
async function handleRole(row) {
|
async function handleRole(row) {
|
||||||
|
const roleIds = row.roles.map(x => x.id);
|
||||||
// 选中的角色列表
|
// 选中的角色列表
|
||||||
const ids = (await getRoleIds({ userId: row.id })).data ?? [];
|
const ids = roleIds ?? [];
|
||||||
addDialog({
|
addDialog({
|
||||||
title: `分配 ${row.username} 用户的角色`,
|
title: `分配 ${row.username} 用户的角色`,
|
||||||
props: {
|
props: {
|
||||||
formInline: {
|
formInline: {
|
||||||
username: row?.username ?? "",
|
username: row?.username ?? "",
|
||||||
nickname: row?.nickname ?? "",
|
nickName: row?.nickName ?? "",
|
||||||
roleOptions: roleOptions.value ?? [],
|
roleOptions: roleOptions.value ?? [],
|
||||||
ids
|
ids
|
||||||
}
|
}
|
||||||
@ -476,9 +526,25 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
contentRenderer: () => h(roleForm),
|
contentRenderer: () => h(roleForm),
|
||||||
beforeSure: (done, { options }) => {
|
beforeSure: (done, { options }) => {
|
||||||
const curData = options.props.formInline as RoleFormItemProps;
|
const curData = options.props.formInline as RoleFormItemProps;
|
||||||
console.log("curIds", curData.ids);
|
const userClone = cloneDeep(row);
|
||||||
// 根据实际业务使用curData.ids和row里的某些字段去调用修改角色接口即可
|
userClone["dept"] = { id: userClone.parentId };
|
||||||
done(); // 关闭弹框
|
userClone["roles"] = curData.ids.map(x => ({
|
||||||
|
id: x
|
||||||
|
}));
|
||||||
|
userClone["jobs"] = userClone.jobOptionsId.map(x => ({
|
||||||
|
id: x
|
||||||
|
}));
|
||||||
|
delete userClone.title;
|
||||||
|
delete userClone.higherDeptOptions;
|
||||||
|
delete userClone.parentId;
|
||||||
|
delete userClone.roleOptions;
|
||||||
|
delete userClone.jobOptions;
|
||||||
|
delete userClone.roleOptionsId;
|
||||||
|
delete userClone.jobOptionsId;
|
||||||
|
User.edit(userClone).finally(() => {
|
||||||
|
done();
|
||||||
|
onSearch();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -488,13 +554,15 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
|||||||
onSearch();
|
onSearch();
|
||||||
|
|
||||||
// 归属部门
|
// 归属部门
|
||||||
const { data } = await getDeptList();
|
// const { data } = await getDeptList();
|
||||||
higherDeptOptions.value = handleTree(data);
|
const { data } = await Dept.getDeptTree({ enabled: true });
|
||||||
treeData.value = handleTree(data);
|
higherDeptOptions.value = handleTree(data, "id", "pid");
|
||||||
|
treeData.value = handleTree(data, "id", "pid");
|
||||||
treeLoading.value = false;
|
treeLoading.value = false;
|
||||||
|
|
||||||
// 角色列表
|
// 角色列表
|
||||||
roleOptions.value = (await getAllRoleList()).data;
|
roleOptions.value = (await Role.get()).data.content;
|
||||||
|
jobOptions.value = (await Job.get()).data.content;
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
321
src/views/system/user/utils/info.tsx
Normal file
321
src/views/system/user/utils/info.tsx
Normal file
@ -0,0 +1,321 @@
|
|||||||
|
import { type Ref, h, ref, watch, reactive, onMounted, computed } from "vue";
|
||||||
|
import croppingUpload from "../upload.vue";
|
||||||
|
import { baseUrlAvatar } from "@/api/utils";
|
||||||
|
import { zxcvbn } from "@zxcvbn-ts/core";
|
||||||
|
import { isAllEmpty, isNull, isEmail } from "@pureadmin/utils";
|
||||||
|
import { addDialog } from "@/components/ReDialog";
|
||||||
|
import Cookies from "js-cookie";
|
||||||
|
import * as User from "@/api/system/user";
|
||||||
|
import {
|
||||||
|
ElForm,
|
||||||
|
ElInput,
|
||||||
|
ElFormItem,
|
||||||
|
ElProgress,
|
||||||
|
ElButton
|
||||||
|
} from "element-plus";
|
||||||
|
import { message } from "@/utils/message";
|
||||||
|
import { formRulesPwd, formRulesEmail } from "./rule";
|
||||||
|
import type { FormInstance } from "element-plus";
|
||||||
|
|
||||||
|
export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||||
|
console.log(tableRef, treeRef);
|
||||||
|
// 上传头像信息
|
||||||
|
const avatarInfo = ref();
|
||||||
|
const ruleFormRef = ref();
|
||||||
|
// 重置的新密码
|
||||||
|
const pwdForm = reactive({
|
||||||
|
newPwd: "",
|
||||||
|
oldPwd: "",
|
||||||
|
newPwdCop: ""
|
||||||
|
});
|
||||||
|
const pwdProgress = [
|
||||||
|
{ color: "#e74242", text: "非常弱" },
|
||||||
|
{ color: "#EFBD47", text: "弱" },
|
||||||
|
{ color: "#ffa500", text: "一般" },
|
||||||
|
{ color: "#1bbf1b", text: "强" },
|
||||||
|
{ color: "#008000", text: "非常强" }
|
||||||
|
];
|
||||||
|
const ruleEmailFormRef = ref();
|
||||||
|
|
||||||
|
// 重置的新邮箱
|
||||||
|
const emailForm = reactive({
|
||||||
|
pwd: "",
|
||||||
|
code: "",
|
||||||
|
email: ""
|
||||||
|
});
|
||||||
|
// 当前密码强度(0-4)
|
||||||
|
const curScore = ref();
|
||||||
|
const form = reactive({
|
||||||
|
// 左侧部门树的id
|
||||||
|
deptId: "",
|
||||||
|
username: "",
|
||||||
|
createTime: "",
|
||||||
|
phone: "",
|
||||||
|
status: ""
|
||||||
|
});
|
||||||
|
/** 用户信息 */
|
||||||
|
const userInfo = computed(() => {
|
||||||
|
return JSON.parse(Cookies.get("userInfo"));
|
||||||
|
});
|
||||||
|
/** 获取邮箱验证码 */
|
||||||
|
const getEmailCode = email => {
|
||||||
|
if (isNull(email)) {
|
||||||
|
message("email必填", {
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
} else if (!isEmail(email)) {
|
||||||
|
message("email格式错误", {
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("email", email);
|
||||||
|
User.resetEmail(email)
|
||||||
|
.then(() => {
|
||||||
|
message("更换邮箱成功", {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
message(`${error}`, {
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
onMounted(async () => {
|
||||||
|
console.log(userInfo);
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 上传头像 */
|
||||||
|
function handleUpload(row) {
|
||||||
|
addDialog({
|
||||||
|
title: "裁剪、上传头像",
|
||||||
|
width: "50%",
|
||||||
|
draggable: false,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
contentRenderer: () =>
|
||||||
|
h(croppingUpload, {
|
||||||
|
imgSrc: baseUrlAvatar(row.avatarName),
|
||||||
|
onCropper: info => (avatarInfo.value = info)
|
||||||
|
}),
|
||||||
|
beforeSure: done => {
|
||||||
|
console.log("avatarInfo.value", avatarInfo.value);
|
||||||
|
User.updateAvatarByid({ id: row.id, avatar: avatarInfo.value.blob });
|
||||||
|
// 根据实际业务使用avatarInfo.value和row里的某些字段去调用上传头像接口即可
|
||||||
|
done(); // 关闭弹框
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
watch(
|
||||||
|
pwdForm,
|
||||||
|
({ newPwd }) =>
|
||||||
|
(curScore.value = isAllEmpty(newPwd) ? -1 : zxcvbn(newPwd).score)
|
||||||
|
);
|
||||||
|
/** 重置密码 */
|
||||||
|
function handleReset() {
|
||||||
|
addDialog({
|
||||||
|
title: `重置 ${userInfo.value.user.nickName} 用户的密码`,
|
||||||
|
width: "30%",
|
||||||
|
draggable: true,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
contentRenderer: () => (
|
||||||
|
<>
|
||||||
|
<ElForm
|
||||||
|
ref={ruleFormRef}
|
||||||
|
model={pwdForm}
|
||||||
|
{...{ rules: formRulesPwd }}
|
||||||
|
>
|
||||||
|
<ElFormItem prop="oldPwd" label="请输入旧密码:">
|
||||||
|
<ElInput
|
||||||
|
clearable
|
||||||
|
show-password
|
||||||
|
type="password"
|
||||||
|
v-model={pwdForm.oldPwd}
|
||||||
|
placeholder="请输入旧密码"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem prop="newPwd" label="请输入新密码:">
|
||||||
|
<ElInput
|
||||||
|
clearable
|
||||||
|
show-password
|
||||||
|
type="password"
|
||||||
|
v-model={pwdForm.newPwd}
|
||||||
|
placeholder="请输入新密码"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
<div class="mt-4 flex">
|
||||||
|
{pwdProgress.map(({ color, text }, idx) => (
|
||||||
|
<div
|
||||||
|
class="w-[19vw]"
|
||||||
|
style={{ marginLeft: idx !== 0 ? "4px" : 0 }}
|
||||||
|
>
|
||||||
|
<ElProgress
|
||||||
|
striped
|
||||||
|
striped-flow
|
||||||
|
duration={curScore.value === idx ? 6 : 0}
|
||||||
|
percentage={curScore.value >= idx ? 100 : 0}
|
||||||
|
color={color}
|
||||||
|
stroke-width={10}
|
||||||
|
show-text={false}
|
||||||
|
/>
|
||||||
|
<p
|
||||||
|
class="text-center"
|
||||||
|
style={{ color: curScore.value === idx ? color : "" }}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
<ElFormItem prop="newPwdCop" label="请确认新密码:">
|
||||||
|
<ElInput
|
||||||
|
clearable
|
||||||
|
show-password
|
||||||
|
type="password"
|
||||||
|
v-model={pwdForm.newPwdCop}
|
||||||
|
placeholder="请确认新密码"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElForm>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
closeCallBack: () =>
|
||||||
|
Object.assign(pwdForm, {
|
||||||
|
newPwd: "",
|
||||||
|
oldPwd: "",
|
||||||
|
newPwdCop: ""
|
||||||
|
}),
|
||||||
|
beforeSure: done => {
|
||||||
|
ruleFormRef.value.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
if (pwdForm.newPwd !== pwdForm.newPwdCop) {
|
||||||
|
message(`两次密码不想等`, {
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 表单规则校验通过
|
||||||
|
message(`已成功重置 ${userInfo.value.user.nickName} 用户的密码`, {
|
||||||
|
type: "success"
|
||||||
|
});
|
||||||
|
// 根据实际业务使用pwdForm.newPwd和row里的某些字段去调用重置用户密码接口即可
|
||||||
|
done(); // 关闭弹框
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
/** 更换邮箱 */
|
||||||
|
function handleResetEmail() {
|
||||||
|
addDialog({
|
||||||
|
title: `更换 ${userInfo.value.user.nickName} 用户的邮箱`,
|
||||||
|
width: "30%",
|
||||||
|
draggable: true,
|
||||||
|
closeOnClickModal: false,
|
||||||
|
contentRenderer: () => (
|
||||||
|
<>
|
||||||
|
<ElForm
|
||||||
|
ref={ruleEmailFormRef}
|
||||||
|
model={emailForm}
|
||||||
|
{...{ rules: formRulesEmail }}
|
||||||
|
>
|
||||||
|
<ElFormItem prop="email" label="新邮箱:">
|
||||||
|
<ElInput
|
||||||
|
clearable
|
||||||
|
type="email"
|
||||||
|
v-model={emailForm.email}
|
||||||
|
placeholder="请输入邮箱"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem prop="code" label="验证码:">
|
||||||
|
<ElInput
|
||||||
|
clearable
|
||||||
|
type="text"
|
||||||
|
v-model={emailForm.code}
|
||||||
|
placeholder="请输入验证码"
|
||||||
|
v-slots={{
|
||||||
|
append: () => (
|
||||||
|
<ElButton onClick={() => getEmailCode(emailForm.email)}>
|
||||||
|
获取验证码
|
||||||
|
</ElButton>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
></ElInput>
|
||||||
|
</ElFormItem>
|
||||||
|
<ElFormItem prop="pwd" label="当前密码:">
|
||||||
|
<ElInput
|
||||||
|
clearable
|
||||||
|
show-password
|
||||||
|
type="password"
|
||||||
|
v-model={emailForm.pwd}
|
||||||
|
placeholder="请输入当前密码"
|
||||||
|
/>
|
||||||
|
</ElFormItem>
|
||||||
|
</ElForm>
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
closeCallBack: () =>
|
||||||
|
Object.assign(emailForm, {
|
||||||
|
pwd: "",
|
||||||
|
code: "",
|
||||||
|
email: ""
|
||||||
|
}),
|
||||||
|
beforeSure: done => {
|
||||||
|
ruleEmailFormRef.value.validate(valid => {
|
||||||
|
if (valid) {
|
||||||
|
console.log("emailForm", emailForm);
|
||||||
|
User.updateEmail({
|
||||||
|
code: emailForm.code,
|
||||||
|
pass: emailForm.pwd,
|
||||||
|
email: emailForm.email
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
// 表单规则校验通过
|
||||||
|
message(
|
||||||
|
`已成功更换 ${userInfo.value.user.nickName} 用户的邮箱`,
|
||||||
|
{
|
||||||
|
type: "success"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// 根据实际业务使用pwdForm.newPwd和row里的某些字段去调用重置用户密码接口即可
|
||||||
|
done(); // 关闭弹框
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
console.log("error", error.response);
|
||||||
|
console.log("error", error.response.data.message);
|
||||||
|
message(`验证码错误:${error.response.data.message}`, {
|
||||||
|
type: "error"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
//更新用户谢谢
|
||||||
|
const submitEditUser = async (formEl: FormInstance | undefined, userI) => {
|
||||||
|
if (!formEl) return;
|
||||||
|
await formEl.validate((valid, fields) => {
|
||||||
|
if (valid) {
|
||||||
|
console.log("userI!", userI);
|
||||||
|
User.editUser({
|
||||||
|
id: userI.id,
|
||||||
|
nickName: userI.nickName,
|
||||||
|
gender: userI.gender,
|
||||||
|
phone: userI.phone
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
console.log("error submit!", fields);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
form,
|
||||||
|
userInfo,
|
||||||
|
handleUpload,
|
||||||
|
handleReset,
|
||||||
|
handleResetEmail,
|
||||||
|
submitEditUser
|
||||||
|
};
|
||||||
|
}
|
@ -4,14 +4,15 @@ import { isPhone, isEmail } from "@pureadmin/utils";
|
|||||||
|
|
||||||
/** 自定义表单规则校验 */
|
/** 自定义表单规则校验 */
|
||||||
export const formRules = reactive(<FormRules>{
|
export const formRules = reactive(<FormRules>{
|
||||||
nickname: [{ required: true, message: "用户昵称为必填项", trigger: "blur" }],
|
nickName: [{ required: true, message: "用户昵称为必填项", trigger: "blur" }],
|
||||||
username: [{ required: true, message: "用户名称为必填项", trigger: "blur" }],
|
username: [{ required: true, message: "用户名称为必填项", trigger: "blur" }],
|
||||||
password: [{ required: true, message: "用户密码为必填项", trigger: "blur" }],
|
password: [{ required: true, message: "用户密码为必填项", trigger: "blur" }],
|
||||||
phone: [
|
phone: [
|
||||||
{
|
{
|
||||||
|
required: true,
|
||||||
validator: (rule, value, callback) => {
|
validator: (rule, value, callback) => {
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
callback();
|
callback(new Error("手机号为必填项"));
|
||||||
} else if (!isPhone(value)) {
|
} else if (!isPhone(value)) {
|
||||||
callback(new Error("请输入正确的手机号码格式"));
|
callback(new Error("请输入正确的手机号码格式"));
|
||||||
} else {
|
} else {
|
||||||
@ -24,9 +25,10 @@ export const formRules = reactive(<FormRules>{
|
|||||||
],
|
],
|
||||||
email: [
|
email: [
|
||||||
{
|
{
|
||||||
|
required: true,
|
||||||
validator: (rule, value, callback) => {
|
validator: (rule, value, callback) => {
|
||||||
if (value === "") {
|
if (value === "") {
|
||||||
callback();
|
callback(new Error("邮箱为必填项"));
|
||||||
} else if (!isEmail(value)) {
|
} else if (!isEmail(value)) {
|
||||||
callback(new Error("请输入正确的邮箱格式"));
|
callback(new Error("请输入正确的邮箱格式"));
|
||||||
} else {
|
} else {
|
||||||
@ -35,5 +37,69 @@ export const formRules = reactive(<FormRules>{
|
|||||||
},
|
},
|
||||||
trigger: "blur"
|
trigger: "blur"
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
parentId: [{ required: true, message: "部门为必填项", trigger: "blur" }],
|
||||||
|
roleOptionsId: [{ required: true, message: "角色为必填项", trigger: "blur" }],
|
||||||
|
jobOptionsId: [{ required: true, message: "岗位为必填项", trigger: "blur" }]
|
||||||
|
});
|
||||||
|
|
||||||
|
/** 自定义修改密码规则校验 */
|
||||||
|
export const formRulesPwd = reactive(<FormRules>{
|
||||||
|
oldPwd: [{ required: true, message: "旧密码为必填项", trigger: "blur" }],
|
||||||
|
newPwd: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("新密码为必填项"));
|
||||||
|
} else if (value.length < 6) {
|
||||||
|
callback(new Error("密码长度小于6"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
newPwdCop: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("确认新密码为必填项"));
|
||||||
|
} else if (value.length < 6) {
|
||||||
|
callback(new Error("密码长度小于6"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
/** 自定义修改邮箱规则校验 */
|
||||||
|
export const formRulesEmail = reactive(<FormRules>{
|
||||||
|
email: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value === "") {
|
||||||
|
callback(new Error("邮箱为必填项"));
|
||||||
|
} else if (!isEmail(value)) {
|
||||||
|
callback(new Error("请输入正确的邮箱格式"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
code: [
|
||||||
|
{ required: true, message: "验证码为必填项", trigger: "blur" },
|
||||||
|
{ min: 4, max: 8, message: "Length should be 4 to 8", trigger: "blur" }
|
||||||
|
],
|
||||||
|
pwd: [
|
||||||
|
{ required: true, message: "密码为必填项", trigger: "blur" },
|
||||||
|
{ min: 6, max: 18, message: "Length should be 4 to 8", trigger: "blur" }
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
@ -4,18 +4,22 @@ interface FormItemProps {
|
|||||||
title: string;
|
title: string;
|
||||||
higherDeptOptions: Record<string, unknown>[];
|
higherDeptOptions: Record<string, unknown>[];
|
||||||
parentId: number;
|
parentId: number;
|
||||||
nickname: string;
|
|
||||||
username: string;
|
username: string;
|
||||||
|
nickName: string;
|
||||||
password: string;
|
password: string;
|
||||||
phone: string | number;
|
phone: string | number;
|
||||||
email: string;
|
email: string;
|
||||||
sex: string | number;
|
gender: string | number;
|
||||||
status: number;
|
enabled: boolean;
|
||||||
dept?: {
|
dept?: {
|
||||||
id?: number;
|
id?: number;
|
||||||
name?: string;
|
name?: string;
|
||||||
};
|
};
|
||||||
remark: string;
|
remark: string;
|
||||||
|
roleOptionsId: number[];
|
||||||
|
roleOptions: Record<string, number>[];
|
||||||
|
jobOptionsId: number[];
|
||||||
|
jobOptions: Record<string, number>[];
|
||||||
}
|
}
|
||||||
interface FormProps {
|
interface FormProps {
|
||||||
formInline: FormItemProps;
|
formInline: FormItemProps;
|
||||||
@ -23,7 +27,7 @@ interface FormProps {
|
|||||||
|
|
||||||
interface RoleFormItemProps {
|
interface RoleFormItemProps {
|
||||||
username: string;
|
username: string;
|
||||||
nickname: string;
|
nickName: string;
|
||||||
/** 角色列表 */
|
/** 角色列表 */
|
||||||
roleOptions: any[];
|
roleOptions: any[];
|
||||||
/** 选中的角色列表 */
|
/** 选中的角色列表 */
|
||||||
|
Loading…
x
Reference in New Issue
Block a user