guicheng 7 mesiacov pred
rodič
commit
dcdfcfbe0f

+ 47 - 42
src/api/system.ts

@@ -1,8 +1,8 @@
 /*
  * @Author: Gui
  * @Date: 2023-03-01 19:20:44
- * @LastEditors: 潘凤涛 904755879@qq.com
- * @LastEditTime: 2024-01-15 16:29:07
+ * @LastEditors: guicheng 1625811865@qq.com
+ * @LastEditTime: 2024-04-10 19:09:06
  * @Description: kxs files
  * @filePath:
  */
@@ -21,67 +21,72 @@ type Result = {
 
 /** 获取用户管理列表 */
 export const getUserList = (data?: object) => {
-  console.log(data)
-  return http.get(baseUrlApi("/v1/qrcodeplatemain/sysadmin/sysadminlist"), data);
+  return http.get(
+    "http://test.apigateway.shuangkebang.com//v1/skb/sysServer/user/page",
+    data
+  );
 };
 /** 删除用户 */
 export const deleteUserList = (data?: object) => {
-  console.log(data)
-  return http.delete(baseUrlApi("/v1/qrcodeplatemain/sysadmin/deletesysadminuserinfo"), data);
+  return http.post(baseUrlApi("/v1/cybmain/system/deleteAdmin"), data);
 };
 /** 新增用户 */
 export const postUserList = (data?: object) => {
-  console.log(data)
-  return http.post(baseUrlApi("/v1/qrcodeplatemain/sysadmin/addsysadminuserinfo"), data);
+  return http.post(baseUrlApi("/v1/cybmain/system/addAdmin"), data);
 };
 /** 编辑用户 */
 export const editUserList = (data?: object) => {
-  console.log(data)
-  return http.post(baseUrlApi("/v1/qrcodeplatemain/sysadmin/editsysadminuserinfo"), data);
+  return http.post(baseUrlApi("/v1/cybmain/system/updateAdmin"), data);
 };
 
 /** 获取角色管理列表 */
 export const getRoleList = (data?: object) => {
-  console.log(data)
-  return http.get(baseUrlApi("/v1/qrcodeplatemain/sysadminrole/sysadminrolelist"), data);
+  return http.get(baseUrlApi("/v1/cybmain/system/getRoleList"), data);
+};
+/** 获取当前角色权限列表 */
+export const getRoleRightList = (data?: object) => {
+  return http.get(
+    baseUrlApi("/v1/cybmain/system/getMenuTreeByAdminList"),
+    data
+  );
 };
 /** 添加系统角色 */
 export const postRoleList = (data?: object) => {
-  console.log(data)
-  return http.post(baseUrlApi("/v1/qrcodeplatemain/sysadminrole/addsysadminroleinfo"), data);
+  return http.post(baseUrlApi("/v1/cybmain/system/addRole"), data);
 };
 /** 编辑系统角色 */
 export const putRoleList = (data?: object) => {
-  console.log(data)
-  return http.put(baseUrlApi("/v1/qrcodeplatemain/sysadminrole/editsysadminroleinfo"), data);
+  return http.post(baseUrlApi("/v1/cybmain/system/updateRole"), data);
 };
 /** 删除系统角色 */
 export const deleteRoleList = (data?: object) => {
-  console.log(data)
-  return http.delete(baseUrlApi("/v1/qrcodeplatemain/sysadminrole/deletesysadminroleinfo"), data);
+  return http.post(baseUrlApi("/v1/cybmain/system/deleteRole"), data);
 };
-/** 获取角色权限列表 */
+/** 获取所有角色权限列表 */
 export const getRoleTreeList = (data?: object) => {
-  console.log(data)
-  return http.get(baseUrlApi("/api/v1/sysadmin/authstree"), data);
-};
-/** 编辑协议 */
-export const edit_Protocol = (data?: object) => {
-  console.log(data)
-  return http.put(baseUrlApi("/v1/qrcodeplatemain/pageinfo/editagreementinfo"), data);
-};
-/** 删除协议 */
-export const delete_Protocol = (data?: object) => {
-  console.log(data)
-  return http.delete(baseUrlApi("/v1/qrcodeplatemain/pageinfo/deleteagreementinfo"), data);
-};
-/** 获取协议列表 */
-export const protocol_List = (data?: object) => {
-  console.log(data)
-  return http.get(baseUrlApi("/v1/qrcodeplatemain/pageinfo/agreementinfolist"), data);
-};
-/** 新增协议 */
-export const add_Protocol = (data?: object) => {
-  console.log(data)
-  return http.post(baseUrlApi("/v1/qrcodeplatemain/pageinfo/addagreementinfo"), data);
-};
+  return http.get(baseUrlApi("/v1/cybmain/system/getMenuTreeList"), data);
+};
+
+/** 获取权限列表 */
+export const getPermissionList = (data?: object) => {
+  return http.get(baseUrlApi("/v1/cybmain/system/getPermissionList"), data);
+};
+/** 获取菜单列表 */
+export const getMenuList = (data?: object) => {
+  return http.get(
+    "http://test.apigateway.shuangkebang.com/v1/skb/sysServer/menu/getById",
+    data
+  );
+};
+/** 添加菜单 */
+export const postMenuList = (data?: object) => {
+  return http.post(baseUrlApi("/v1/cybmain/system/addMenu"), data);
+};
+/** 编辑菜单 */
+export const putMenuList = (data?: object) => {
+  return http.post(baseUrlApi("/v1/cybmain/system/updateMenu"), data);
+};
+/** 删除菜单 */
+export const deleteMenuList = (data?: object) => {
+  return http.post(baseUrlApi("/v1/cybmain/system/deleteMenu"), data);
+};

+ 17 - 9
src/api/user.ts

@@ -1,8 +1,8 @@
 /*
  * @Author: Gui
  * @Date: 2023-03-01 19:20:44
- * @LastEditors: Please set LastEditors
- * @LastEditTime: 2024-01-03 14:17:14
+ * @LastEditors: guicheng 1625811865@qq.com
+ * @LastEditTime: 2024-04-10 18:09:03
  * @Description: kxs files
  * @filePath:
  */
@@ -47,15 +47,23 @@ export type RefreshTokenResult = {
 
 /** 登录 */
 export const getLogin = (data?: object) => {
-  console.log(data)
-  return http.post("/login", data);
-  // return http.get(baseUrlApi("/api/v1/sysadmin/login"), data);
+  console.log(data);
+  return http.login(
+    "http://test.apigateway.shuangkebang.com/v1/skb/sysServer/oauth2/token",
+    data
+  );
+};
+/** 获取当前用户树形菜单 */
+export const getUserMenu = () => {
+  return http.get(
+    "http://test.apigateway.shuangkebang.com/v1/skb/sysServer/menu",
+    {}
+  );
 };
 
 /** 刷新token */
 export const refreshTokenApi = (data?: object) => {
-  return http.request<RefreshTokenResult>(
-    "post", "/refreshtoken",
-    { data: { value: encryptByDES(JSON.stringify(data)) } }
-  );
+  return http.request<RefreshTokenResult>("post", "/refreshtoken", {
+    data: { value: encryptByDES(JSON.stringify(data)) }
+  });
 };

+ 1 - 1
src/router/utils.ts

@@ -301,7 +301,7 @@ function addAsyncRoutes(arrRoutes: Array<RouteRecordRaw>) {
   const modulesRoutesKeys = Object.keys(modulesRoutes);
   arrRoutes.forEach((v: RouteRecordRaw) => {
     // 后端获取路径和前端路径相匹配
-    v.path = v.path + "/index";
+    // v.path = v.path + "/index";
     // 所有路由都需要保持
     v.meta.keepAlive = true;
     // 将backstage属性加入meta,标识此路由为后端返回路由

+ 56 - 31
src/store/modules/user.ts

@@ -5,20 +5,38 @@ import { routerArrays } from "@/layout/types";
 import { resetRouter } from "@/router";
 import router from "@/router";
 import { storageSession } from "@pureadmin/utils";
-import { getLogin, refreshTokenApi } from "@/api/user";
+import { getLogin, getUserMenu, refreshTokenApi } from "@/api/user";
 import { UserResult, RefreshTokenResult } from "@/api/user";
 import { useMultiTagsStoreHook } from "@/store/modules/multiTags";
-import { type DataInfo, setToken, removeToken, sessionKey } from "@/utils/auth";
+import {
+  type DataInfo,
+  setToken,
+  removeToken,
+  sessionKey,
+  setUserinfo
+} from "@/utils/auth";
 import { ShakeDownDecrypt } from "@/utils/CryptoJS";
 
-type InData = {
-  realName?: string;
-  SysAdminId?: string;
-  roles?: any;
-  apiToken?: string;
-  refreshToken?: string;
-  rightList?: any;
-  apiTokenExpiredDate?: any;
+// 菜单格式化函数
+const formateRouter = (data, role = 1) => {
+  return data.map(item => {
+    return {
+      path: item.path,
+      name: item.name,
+      component: null,
+      meta: {
+        title: item.meta.title,
+        icon: item.meta.icon,
+        roles: [role],
+        auths: item.permission
+      },
+      children: item.children
+        ? item.children.length != 0
+          ? formateRouter(item.children, role)
+          : null
+        : null
+    };
+  });
 };
 export const useUserStore = defineStore({
   id: "pure-user",
@@ -67,20 +85,30 @@ export const useUserStore = defineStore({
     async loginByUsername(data) {
       return new Promise<UserResult>((resolve, reject) => {
         getLogin(data)
-          .then((data: UserResult) => {
-            if (data.status === "1") {
-              const indata: InData = data.data;
+          .then((data: any) => {
+            if (data.status === 1) {
+              console.log("[ data ] >", data);
+              const token = data.data;
               setToken({
-                SysAdminId: indata.SysAdminId,
-                username: indata.realName,
-                // 一个用户可能有多个角色
-                roles: indata.roles,
-                accessToken: indata.apiToken,
-                refreshToken: indata.refreshToken,
-                routeList: indata.rightList,
-                expires: new Date(indata.apiTokenExpiredDate)
+                accessToken: token.access_token,
+                refreshToken: token.refresh_token,
+                expires: new Date(token.exp)
+              });
+              getUserMenu().then((res: any) => {
+                if (res.status === 1) {
+                  console.log(1);
+                  setUserinfo({
+                    SysAdminId: token.user_info.user_id,
+                    username: token.user_info.username,
+                    // 一个用户可能有多个角色
+                    roles: 1,
+                    routeList: formateRouter(res.data)
+                  });
+                  resolve(data);
+                } else {
+                  resolve(data);
+                }
               });
-              resolve(data);
             } else {
               resolve(data);
             }
@@ -103,17 +131,14 @@ export const useUserStore = defineStore({
     async handRefreshToken(data) {
       return new Promise<RefreshTokenResult>((resolve, reject) => {
         refreshTokenApi(data)
-          .then(data => {
-            if (data) {
+          .then(res => {
+            if (res) {
+              const data: any = res.data;
               // 重新分配token
               setToken({
-                SysAdminId: "",
-                username: "",
-                roles: [],
-                accessToken: data.data.apiToken,
-                refreshToken: data.data.refreshToken,
-                routeList: [],
-                expires: new Date(data.data.apiTokenExpiredDate)
+                accessToken: data.access_token,
+                refreshToken: data.refresh_token,
+                expires: new Date(data.exp)
               });
               resolve(data);
             }

+ 13 - 10
src/utils/auth.ts

@@ -1,8 +1,8 @@
 /*
  * @Author: Gui
  * @Date: 2023-03-01 19:20:44
- * @LastEditors: Please set LastEditors
- * @LastEditTime: 2023-11-07 13:35:40
+ * @LastEditors: guicheng 1625811865@qq.com
+ * @LastEditTime: 2024-04-10 17:00:29
  * @Description: kxs files
  * @filePath:
  */
@@ -11,15 +11,17 @@ import { storageSession } from "@pureadmin/utils";
 import { useUserStoreHook } from "@/store/modules/user";
 import { ShakeDownEncrypt, ShakeDownDecrypt } from "@/utils/CryptoJS";
 
-export interface DataInfo<T> {
-  /** 用户ID **/
-  SysAdminId: string;
+export interface TokenInfo<T> {
   /** token */
   accessToken: string;
   /** `accessToken`的过期时间(时间戳) */
   expires: T;
   /** 用于调用刷新accessToken的接口时所需的token */
   refreshToken: string;
+}
+export interface DataInfo {
+  /** 用户ID **/
+  SysAdminId: string;
   /** 用户名 */
   username?: string;
   // 权限列表;
@@ -33,7 +35,7 @@ export const sessionKey = "user-info";
 export const TokenKey = "authorized-token";
 
 /** 获取`token` */
-export function getToken(): DataInfo<number> {
+export function getToken(): TokenInfo<number> {
   // 此处与`TokenKey`相同,此写法解决初始化时`Cookies`中不存在`TokenKey`报错
   return Cookies.get(TokenKey)
     ? JSON.parse(Cookies.get(TokenKey))
@@ -46,18 +48,20 @@ export function getToken(): DataInfo<number> {
  * 将`accessToken`、`expires`这两条信息放在key值为authorized-token的cookie里(过期自动销毁)
  * 将`username`、`roles`、`refreshToken`、`expires`这四条信息放在key值为`user-info`的sessionStorage里(浏览器关闭自动销毁)
  */
-export function setToken(data: DataInfo<Date>) {
+export function setToken(data) {
   let expires = 0;
-  const { accessToken, refreshToken, SysAdminId } = data;
+  const { accessToken, refreshToken } = data;
   expires = new Date(data.expires).getTime(); // 如果后端直接设置时间戳,将此处代码改为expires = data.expires,然后把上面的DataInfo<Date>改成DataInfo<number>即可
   const cookieString = JSON.stringify({ accessToken, refreshToken, expires });
-
   expires > 0
     ? Cookies.set(TokenKey, cookieString, {
       expires: (expires - Date.now()) / 86400000
     })
     : Cookies.set(TokenKey, cookieString);
+}
 
+export function setUserinfo(data: DataInfo<Date>) {
+  const { refreshToken, SysAdminId } = data;
   function setSessionKey(
     username: string,
     roles: Array<string>,
@@ -71,7 +75,6 @@ export function setToken(data: DataInfo<Date>) {
       ShakeDownEncrypt({
         SysAdminId,
         refreshToken,
-        expires,
         username,
         roles,
         routeList

+ 14 - 5
src/utils/encryptByDES.ts

@@ -1,12 +1,21 @@
+/*
+ * @Author: guicheng 1625811865@qq.com
+ * @Date: 2023-02-21 15:36:29
+ * @LastEditors: guicheng 1625811865@qq.com
+ * @LastEditTime: 2024-04-10 16:09:57
+ * @FilePath: /admin/src/utils/encryptByDES.ts
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+ */
 import CryptoJS from "crypto-js";
-const encryptByDES = function (message) {
-  const keyHex = CryptoJS.enc.Utf8.parse("*ga34|^7");
-  const ivHex = CryptoJS.enc.Utf8.parse("*ga34|^7");
-  const encrypted = CryptoJS.DES.encrypt(message, keyHex, {
+const encryptByDES = message => {
+  const keyHex = CryptoJS.enc.Utf8.parse("CBTU1dD4Kd5pyiGWTsI10jRQ3SvKusSV");
+  const ivHex = CryptoJS.enc.Utf8.parse("DYgjCEIMVrj2W9xN");
+  const encrypted = CryptoJS.AES.encrypt(message, keyHex, {
     iv: ivHex,
     mode: CryptoJS.mode.CBC,
     padding: CryptoJS.pad.Pkcs7
   });
-  return encrypted.toString();
+  const str = CryptoJS.enc.Utf8.parse(encrypted.toString());
+  return CryptoJS.enc.Base64.stringify(str);
 };
 export default encryptByDES;

+ 50 - 37
src/utils/http/index.ts

@@ -1,8 +1,8 @@
 /*
  * @Author: Gui
  * @Date: 2023-03-01 19:20:44
- * @LastEditors: Please set LastEditors
- * @LastEditTime: 2024-03-13 17:37:52
+ * @LastEditors: guicheng 1625811865@qq.com
+ * @LastEditTime: 2024-04-10 16:02:09
  * @Description: kxs files
  * @filePath:
  */
@@ -184,6 +184,40 @@ class PureHttp {
     );
   }
 
+  /** 登录请求工具函数 */
+  public login<T>(
+    url: string,
+    param?: any,
+    axiosConfig?: PureHttpRequestConfig
+  ): Promise<T> {
+    const config = {
+      method: "post",
+      url:
+        url +
+        "?scope=server&grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer",
+      data: encryptByDES(JSON.stringify(param)),
+      auth: {
+        username: "admin",
+        password: "kmdlqkyuvl"
+      },
+      headers: {
+        "Content-Type": "application/json"
+      },
+      ...axiosConfig
+    } as PureHttpRequestConfig;
+    // 单独处理自定义请求/响应回掉
+    return new Promise((resolve, reject) => {
+      PureHttp.axiosInstance
+        .request(config)
+        .then((response: undefined) => {
+          resolve(response);
+        })
+        .catch(error => {
+          reject(error);
+        });
+    });
+  }
+
   /** 通用请求工具函数 */
   public request<T>(
     method: RequestMethods,
@@ -196,7 +230,7 @@ class PureHttp {
       url,
       ...param,
       headers: {
-        "Content-Type": "application/x-www-form-urlencoded"
+        "Content-Type": "application/json"
       },
       ...axiosConfig
     } as PureHttpRequestConfig;
@@ -232,9 +266,7 @@ class PureHttp {
       }
     }
     if (legal) {
-      const data = new URLSearchParams();
-      data.append("value", encryptByDES(JSON.stringify(params)));
-      const param = { data };
+      const param = { data: encryptByDES(JSON.stringify(params)) };
       return this.request<P>("post", url, param, config);
     } else {
       ElMessage({
@@ -250,9 +282,7 @@ class PureHttp {
     params?: AxiosRequestConfig<T>,
     config?: PureHttpRequestConfig
   ): Promise<P> {
-    const data = new URLSearchParams();
-    data.append("value", encryptByDES(JSON.stringify(params)));
-    const param = { data };
+    const param = { data: encryptByDES(JSON.stringify(params)) };
     return this.request<P>("post", url, param, config);
   }
 
@@ -275,7 +305,9 @@ class PureHttp {
       }
     }
     if (legal) {
-      const param = { params: { value: encryptByDES(JSON.stringify(params)) } };
+      const params = new URLSearchParams();
+      params.append("value", encryptByDES(JSON.stringify(params)));
+      const param = { params };
       return this.request<P>("get", url, param, config);
     } else {
       ElMessage({
@@ -304,9 +336,7 @@ class PureHttp {
       }
     }
     if (legal) {
-      const data = new URLSearchParams();
-      data.append("value", encryptByDES(JSON.stringify(params)));
-      const param = { data };
+      const param = { data: encryptByDES(JSON.stringify(params)) };
       return this.request<P>("put", url, param, config);
     } else {
       ElMessage({
@@ -322,30 +352,13 @@ class PureHttp {
     params?: AxiosRequestConfig<T>,
     config?: PureHttpRequestConfig
   ): Promise<P> {
-    let legal = true;
-    const paramskey = [];
-    for (const key in params) {
-      if (
-        params[key] &&
-        typeof params[key] === "string" &&
-        regExpTest(params[key])
-      ) {
-        paramskey.push(key);
-        legal = false;
-      }
-    }
-    if (legal) {
-      const data = new URLSearchParams();
-      data.append("value", encryptByDES(JSON.stringify(params)));
-      const param = { data };
-      return this.request<P>("delete", url, param, config);
-    } else {
-      ElMessage({
-        message: `params is illegal`,
-        type: "error"
-      });
-      return Promise.reject(`${paramskey.join("、")} is illegal params`);
-    }
+    const param = { data: {} };
+    return this.request<P>(
+      "delete",
+      `${url}/${encryptByDES(JSON.stringify(params))}`,
+      param,
+      config
+    );
   }
 }
 

+ 336 - 0
src/views/admin/menu/hook.tsx

@@ -0,0 +1,336 @@
+import {
+  deleteMenuList,
+  getMenuList,
+  getPermissionList,
+  postMenuList,
+  putMenuList
+} from "@/api/system";
+import { ListDate } from "@/utils/formateDate";
+import {
+  UseFormatDatePickerHook,
+  UseValidTimeRangeHook
+} from "@/utils/hooks/format";
+import { type PaginationProps } from "@pureadmin/table";
+import { number } from "echarts";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { reactive, ref, computed, onMounted } from "vue";
+
+export function useUser() {
+  const UpdateForm = ref({
+    name: "", // 菜单名
+    path: "", // 组件路径
+    title: "",
+    descs: "", // 描述
+    icon: "", // 图标
+    parentId: "", //父id
+    permissionId: "", //权限id
+    menuStatus: "1", // 按钮标识
+    version: "1", // 是否按钮
+    id: ""
+  });
+  const dialogType = ref(1);
+  const dataList = ref([]);
+  const menuList = ref([]);
+  const permissionList = ref([]);
+  const loading = ref(false);
+  const dialogUpdateVisible = ref(false);
+  const pagination = reactive<PaginationProps>({
+    total: 0,
+    pageSize: 10,
+    currentPage: 1,
+    background: true
+  });
+  const columns: TableColumnList = [
+    {
+      type: "selection",
+      width: 55,
+      align: "left",
+      hide: ({ checkList }) => !checkList.includes("勾选列")
+    },
+    {
+      label: "序号",
+      type: "index",
+      width: 70,
+      hide: ({ checkList }) => !checkList.includes("序号列")
+    },
+    {
+      label: "ID",
+      prop: "id",
+      minWidth: 130
+    },
+    {
+      label: "菜单名",
+      prop: "name",
+      minWidth: 130
+    },
+    {
+      label: "组件路径",
+      prop: "path",
+      minWidth: 130
+    },
+    {
+      label: "图标",
+      prop: "icon",
+      minWidth: 130
+    },
+    {
+      label: "描述",
+      prop: "descs",
+      minWidth: 130
+    },
+    // {
+    //   label: "状态",
+    //   prop: "status",
+    //   minWidth: 130
+    // },
+    {
+      label: "操作",
+      fixed: "right",
+      width: 200,
+      slot: "operation"
+    }
+  ];
+  const buttonClass = computed(() => {
+    return [
+      "!h-[20px]",
+      "reset-margin",
+      "!text-gray-500",
+      "dark:!text-white",
+      "dark:hover:!text-primary"
+    ];
+  });
+
+  // 获取权限列表
+  async function permission() {
+    const { data } = await getPermissionList({});
+    const permission = data.map(item => {
+      return {
+        label: item.name,
+        Id: item.id
+      };
+    });
+    permissionList.value = permission;
+    permissionList.value.unshift({
+      label: "作为菜单",
+      Id: ""
+    });
+  }
+  permission();
+
+  // 树形结构格式化
+  function TreeFormatter(data, flag = false) {
+    const result = data.map(item => {
+      if (item.children.length != 0) {
+        return {
+          label: item.name,
+          value: item.id,
+          children: TreeFormatter(item.children, true)
+        };
+      } else if (item.version != 0) {
+        return {
+          disabled: true,
+          label: item.name,
+          value: item.id
+        };
+      } else {
+        return {
+          label: item.name,
+          value: item.id
+        };
+      }
+    });
+    return result;
+  }
+  // 获取全部菜单列表
+  async function Menu() {
+    const { data } = await getMenuList({ id: "" });
+    menuList.value = TreeFormatter(data);
+    console.log(menuList.value);
+    menuList.value.unshift({
+      label: "作为顶级菜单",
+      value: "null"
+    });
+  }
+  Menu();
+  async function UpdateFormSubmit() {
+    if (dialogType.value == 1) {
+      const { status, message } = await postMenuList({
+        name: UpdateForm.value.name, // 菜单名
+        path: UpdateForm.value.path, // 组件路径
+        descs: UpdateForm.value.descs, // 描述
+        title: UpdateForm.value.title, // 名称
+        icon: UpdateForm.value.icon, // 图标
+        parentId:
+          UpdateForm.value.parentId === "null"
+            ? null
+            : UpdateForm.value.parentId, //父id
+        permissionId: UpdateForm.value.permissionId, //权限id
+        menuStatus: UpdateForm.value.permissionId === "" ? "0" : "1" // 按钮标识
+      });
+      if (status == 1) {
+        ElMessage({
+          message: "新增菜单成功",
+          type: "success"
+        });
+        dialogUpdateVisible.value = false;
+        onSearch();
+        permission();
+        Menu();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      }
+    } else {
+      const { status, message } = await putMenuList({
+        name: UpdateForm.value.name, // 菜单名
+        path: UpdateForm.value.path, // 组件路径
+        descs: UpdateForm.value.descs, // 描述
+        title: UpdateForm.value.title, // 名称
+        icon: UpdateForm.value.icon, // 图标
+        parentId:
+          UpdateForm.value.parentId === "null"
+            ? null
+            : UpdateForm.value.parentId, //父id
+        permissionId: UpdateForm.value.permissionId, //权限id
+        menuStatus: UpdateForm.value.permissionId === "" ? "0" : "1", // 按钮标识
+        id: UpdateForm.value.id
+      });
+      if (status == 1) {
+        ElMessage({
+          message: "菜单修改成功",
+          type: "success"
+        });
+        dialogUpdateVisible.value = false;
+        onSearch();
+        permission();
+        Menu();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      }
+    }
+  }
+  function handleDelete(row) {
+    ElMessageBox.confirm(`是否删除名称为:${row.name}的菜单? `, "提示", {
+      confirmButtonText: "删除",
+      cancelButtonText: "取消",
+      type: "warning"
+    }).then(async () => {
+      const { status, message } = await deleteMenuList({ id: row.id });
+      if (status == 1) {
+        ElMessage({
+          message: "删除成功",
+          type: "success"
+        });
+        onSearch();
+        permission();
+        Menu();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      }
+    });
+  }
+  function AddUser(row) {
+    UpdateForm.value = {
+      name: "", // 菜单名
+      path: "", // 组件路径
+      descs: "", // 描述
+      title: "", // 名称
+      icon: "", // 图标
+      parentId: "", //父id
+      permissionId: "", //权限id
+      menuStatus: "1", // 按钮标识
+      version: "" // 是否按钮
+    };
+    dialogType.value = 1;
+    dialogUpdateVisible.value = true;
+  }
+  function handleUpdate(row) {
+    console.log(row);
+
+    dialogType.value = 2;
+    dialogUpdateVisible.value = true;
+    UpdateForm.value = {
+      name: row.name, // 菜单名
+      path: row.path, // 组件路径
+      title: row.title, // 菜单名称
+      descs: row.descs, // 描述
+      icon: row.icon, // 图标
+      parentId: row.parentId ? row.parentId : "null", //父id
+      permissionId: row.permissionId === null ? "" : row.permissionId, //权限id
+      menuStatus: row.version === 0 ? "0" : "1", // 按钮标识
+      version: row.version, // 是否按钮
+      id: row.id // 是否按钮
+    };
+  }
+
+  function handleSizeChange(val: number) {
+    console.log(`${val} items per page`);
+    onSearch();
+  }
+
+  function handleCurrentChange(val: number) {
+    if (typeof val !== "number") return;
+    console.log(`current page: ${val}`);
+    onSearch();
+  }
+
+  function handleSelectionChange(val) {
+    console.log("handleSelectionChange", val);
+    onSearch();
+  }
+
+  // 搜索函数(请求表格数据列表)
+  function submit(e: { keyCode: number }) {
+    if (e.keyCode == 229) {
+      return;
+    } else {
+      onSearch();
+    }
+  }
+  async function onSearch() {
+    loading.value = true;
+    const { data, other } = await getMenuList({
+      pageSize: pagination.pageSize,
+      pageNum: pagination.currentPage
+    });
+    dataList.value = data;
+    pagination.total = other;
+    setTimeout(() => {
+      loading.value = false;
+    }, 500);
+  }
+
+  onMounted(() => {
+    onSearch();
+  });
+
+  return {
+    loading,
+    columns,
+    dataList,
+    pagination,
+    buttonClass,
+    dialogUpdateVisible,
+    UpdateFormSubmit,
+    submit,
+    UpdateForm,
+    menuList,
+    permissionList,
+    dialogType,
+    AddUser,
+    onSearch,
+    handleUpdate,
+    handleDelete,
+    handleSizeChange,
+    handleCurrentChange,
+    handleSelectionChange
+  };
+}

+ 184 - 0
src/views/admin/menu/index.vue

@@ -0,0 +1,184 @@
+<script lang="ts">
+// 声明额外的选项
+export default {
+  name: "Menu"
+};
+</script>
+<script setup lang="ts">
+import { ref } from "vue";
+import { useUser } from "./hook";
+import { hasAuth } from "@/router/utils";
+import { PureTableBar } from "@/components/RePureTableBar";
+import { useRenderIcon } from "@/components/ReIcon/src/hooks";
+import IconSelect from '@/components/ReIcon/src/Select.vue';
+
+import Delete from "@iconify-icons/ep/delete";
+import Search from "@iconify-icons/ep/search";
+import Refresh from "@iconify-icons/ep/refresh";
+import AddFill from "@iconify-icons/ri/add-circle-line";
+
+const {
+  loading,
+  columns,
+  dataList,
+  pagination,
+  dialogUpdateVisible,
+  UpdateFormSubmit,
+  UpdateForm,
+  onSearch,
+  submit,
+  dialogType,
+  menuList,
+  permissionList,
+  AddUser,
+  handleUpdate,
+  handleDelete,
+  handleSizeChange,
+  handleCurrentChange,
+  handleSelectionChange
+} = useUser();
+defineExpose({
+  loading,
+  columns,
+  dataList,
+  pagination,
+  dialogUpdateVisible,
+  UpdateFormSubmit,
+  UpdateForm,
+  submit,
+  onSearch,
+  dialogType,
+  menuList,
+  permissionList,
+  AddUser,
+  handleUpdate,
+  handleDelete,
+  handleSizeChange,
+  handleCurrentChange,
+  handleSelectionChange
+});
+</script>
+
+<template lang="pug">
+.main
+  //- 表格组件
+  PureTableBar.mt12(title="后台菜单管理", @refresh="onSearch" )
+    template(#buttons)
+      el-button(type="primary" :icon="useRenderIcon(AddFill)" @click="AddUser"  v-if="hasAuth(['menuAdd'])") 新增菜单
+    template(v-slot="{ size, checkList }")
+      pure-table(stripe 
+        border,
+        row-key="id",
+        align-whole="center",
+        table-layout="auto",
+        :loading="loading",
+        :size="size",
+        :data="dataList",
+        :columns="columns",
+        :checkList="checkList",
+        :pagination="pagination",
+        :paginationSmall="size === 'default' ? true : false",
+        :header-cell-style="{ background: 'var(--el-table-row-hover-bg-color)', color: 'var(--el-text-color-primary)' }",
+        @selection-change="handleSelectionChange",
+        @size-change="handleSizeChange",
+        @current-change="handleCurrentChange"
+      )          
+        template(#operation="{ row }")
+          el-button.reset-margin(
+            link
+            type="primary"
+            :size="size"
+            @click="handleUpdate(row)"
+            :icon="useRenderIcon(EditPen)"
+            v-if="hasAuth(['menuEdit'])"
+          ) 编辑
+          el-button.reset-margin(
+            link
+            type="primary"
+            :size="size"
+            @click="handleDelete(row)"
+            :icon="useRenderIcon(Delete)"
+            v-if="hasAuth(['menuDelete'])"
+          ) 删除
+
+  el-dialog(v-model='dialogUpdateVisible' width="40%" :title="dialogType == 1 ?'添加菜单' : '修改菜单'" :close-on-click-modal="false")
+    el-form(:model='UpdateForm' 
+      label-position="left"
+      label-width="100px")
+      el-form-item(label='菜单标题' prop="name")
+        el-input(v-model='UpdateForm.name' autocomplete='off' class="!w-[300px]"
+          placeholder="请输入菜单标题")
+      el-form-item(label='菜单名称' prop="title")
+        el-input(v-model='UpdateForm.title' autocomplete='off' class="!w-[300px]"
+          placeholder="请输入菜单名称(英文字母名称)")
+      el-form-item(label='菜单描述' prop="descs")
+        el-input(v-model='UpdateForm.descs' autocomplete='off' class="!w-[300px]"
+          placeholder="请输入菜单描述")
+      el-form-item(label='菜单路径' prop="path")
+        el-input(v-model='UpdateForm.path' autocomplete='off' class="!w-[300px]"
+          placeholder="请输入菜单路径")
+      el-form-item(label='图标' prop="icon")
+        IconSelect(v-model="UpdateForm.icon")
+      el-form-item(label='父级菜单')
+        el-tree-select(
+          v-model="UpdateForm.parentId" 
+          :data="menuList" 
+          check-strictly
+          :render-after-expand="false"
+          show-checkbox
+          check-on-click-node
+          class="!w-[300px]")
+      el-form-item(label="按钮权限:", prop="permissionId")
+        el-select(
+          v-model="UpdateForm.permissionId",
+          placeholder="请选择按钮权限",
+          clearable,
+          filterable
+          class="!w-[300px]"
+        )
+          el-option(:label="item.label", :value="item.Id" v-for="(item,index) in permissionList")
+    template(#footer)
+      span.dialog-footer
+        el-button(@click='dialogUpdateVisible = false') 关闭
+        el-button(type='primary' @click='UpdateFormSubmit') 确认提交
+</template>
+
+<style scoped lang="scss">
+:deep(.el-dropdown-menu__item i) {
+  margin: 0;
+}
+
+:deep(.el-form-item__label) {
+  font-weight: 700;
+}
+
+:deep(.el-pagination) {
+  flex-flow: wrap;
+}
+
+:deep(.is-draggable) {
+  max-height: 80vh;
+  overflow: auto;
+}
+
+:deep(.el-dialog__header) {
+  position: sticky;
+  top: 0;
+  z-index: 2;
+  background: #fff;
+}
+
+.collapsedom {
+  padding: 0 20px;
+  background-color: #fff;
+}
+
+.ovh-x {
+  height: 40vh;
+  overflow-y: auto;
+}
+
+:deep(.el-descriptions__header) {
+  margin: 16px 0 !important;
+}
+</style>

+ 1 - 0
src/views/admin/menu/svg/expand.svg

@@ -0,0 +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.5V4h9Z"/></svg>

+ 1 - 0
src/views/admin/menu/svg/unexpand.svg

@@ -0,0 +1 @@
+<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>

+ 297 - 0
src/views/admin/role/hook.tsx

@@ -0,0 +1,297 @@
+import { deleteRoleList, getRoleList, getRoleRightList, getRoleTreeList, postRoleList, putRoleList } from "@/api/system";
+import { type PaginationProps } from "@pureadmin/table";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { reactive, ref, computed, onMounted } from "vue";
+
+export function useUser() {
+  let form = reactive({
+    name: ""
+  });
+  let AddForm = ref([]);
+  let AddAllForm = ref([]);
+  let AddFormdata = ref({
+    name: "",
+    sn: "",
+    menus: "",
+    permissions: "",
+  });
+  let dialogType = ref(1);
+  const AddFormvalueStrictly = ref([]);
+  const dataList = ref([]);
+  const loading = ref(true);
+  const dialogAddVisible = ref(false);
+  const pagination = reactive<PaginationProps>({
+    total: 0,
+    pageSize: 10,
+    currentPage: 1,
+    background: true
+  });
+  const columns: TableColumnList = [
+    {
+      type: "selection",
+      width: 55,
+      align: "left",
+      hide: ({ checkList }) => !checkList.includes("勾选列")
+    },
+    {
+      label: "序号",
+      type: "index",
+      width: 70,
+      hide: ({ checkList }) => !checkList.includes("序号列")
+    },
+    {
+      label: "角色ID",
+      prop: "id",
+      minWidth: 130
+    },
+    {
+      label: "角色名称",
+      prop: "name",
+      minWidth: 130
+    },
+    {
+      label: "角色描述",
+      prop: "sn",
+      minWidth: 130
+    },
+    {
+      label: "操作",
+      fixed: "right",
+      width: 200,
+      slot: "operation"
+    }
+  ];
+  const buttonClass = computed(() => {
+    return [
+      "!h-[20px]",
+      "reset-margin",
+      "!text-gray-500",
+      "dark:!text-white",
+      "dark:hover:!text-primary"
+    ];
+  });
+  async function AddFormSubmit() {
+    if (dialogType.value == 1) {
+      AddFormdata.value.menus = ResultFormatter(AddFormvalueStrictly.value, 0);
+      AddFormdata.value.permissions = ResultFormatter(AddFormvalueStrictly.value, 1);
+      const { status, message } = await postRoleList(AddFormdata.value);
+      if (status == 1) {
+        ElMessage({
+          message: "添加角色成功",
+          type: "success"
+        });
+        dialogAddVisible.value = false;
+        AddFormvalueStrictly.value = [];
+        AddFormdata.value = {
+          name: "",
+          sn: "",
+          menus: "",
+          permissions: "",
+        };
+        onSearch();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      };
+    } else {
+      AddFormdata.value.menus = ResultFormatter(AddFormvalueStrictly.value, 0);
+      AddFormdata.value.permissions = ResultFormatter(AddFormvalueStrictly.value, 1);
+      const { status, message } = await putRoleList(AddFormdata.value);
+      if (status == 1) {
+        ElMessage({
+          message: "编辑角色成功",
+          type: "success"
+        });
+        dialogAddVisible.value = false;
+        AddFormvalueStrictly.value = [];
+        AddFormdata.value = {
+          name: "",
+          sn: "",
+          menus: "",
+          permissions: "",
+        };
+        onSearch();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      };
+
+    }
+  }
+  // 删除角色
+  function handleDelete(row) {
+    ElMessageBox.confirm(
+      `是否删除该角色?  角色名称为:${row.name
+      }`,
+      "提示",
+      {
+        confirmButtonText: "删除",
+        cancelButtonText: "取消",
+        type: "warning"
+      }
+    ).then(async () => {
+      const { status, message } = await deleteRoleList({ id: row.id });
+      if (status == 1) {
+        ElMessage({
+          message: "删除成功",
+          type: "success"
+        });
+        onSearch();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      };
+    })
+  }
+  // 添加角色
+  function AddUser() {
+    dialogType.value = 1;
+    AddFormvalueStrictly.value = [];
+    AddFormdata.value = {
+      name: "",
+      sn: "",
+      menus: "",
+      permissions: "",
+    };
+    dialogAddVisible.value = true;
+  }
+  // 编辑角色
+  async function handleUpdate(row) {
+    dialogType.value = 2;
+
+    const arr = await getrolelisttree(row)
+    AddFormvalueStrictly.value = RoleTreeFormatter(arr);
+    loading.value = false;
+    AddFormdata.value.id = row.id;
+    AddFormdata.value.name = row.name;
+    AddFormdata.value.sn = row.sn;
+    dialogAddVisible.value = true;
+  }
+  // 当前页数量切换
+  function handleSizeChange(val: number) {
+    onSearch()
+  }
+  // 当前页码切换
+  function handleCurrentChange(val: number) {
+    onSearch()
+  }
+  // 选择表格项
+  function handleSelectionChange(val) {
+    onSearch()
+  }
+  // 搜索函数(请求表格数据列表)
+  function submit(e: { keyCode: number }) {
+    if (e.keyCode == 229) {
+      return;
+    } else {
+      onSearch('search');
+    }
+  }
+  // 搜索列表
+  async function onSearch(type = '') {
+
+    if (type == 'search' && !Object.values(form).some(item => !!item)) {
+      return ElMessage({
+        message: "请输入查询条件",
+        type: "error"
+      });
+    };
+    if (type == 'all') {
+      form.name = "";
+    }
+    loading.value = true;
+    const { data, other } = await getRoleList({ ...form, pageSize: pagination.pageSize, pageNum: pagination.currentPage });
+    dataList.value = data;
+    pagination.total = other;
+    setTimeout(() => {
+      loading.value = false;
+    }, 500);
+  }
+  getroletree();
+  // 获取权限树
+  async function getroletree() {
+    loading.value = true;
+    const { data } = await getRoleTreeList({});
+    AddForm.value = TreeFormatter(data);
+    setTimeout(() => {
+      loading.value = false;
+    }, 500);
+  }
+  // 获取角色权限树
+  async function getrolelisttree(row) {
+    loading.value = true;
+    const { data } = await getRoleRightList({ id: row.id });
+    return data;
+
+  }
+  // 树形结构格式化
+  function TreeFormatter(data) {
+    const result = data.map(item => {
+      if (item.children) {
+        return {
+          label: item.name,
+          value: item.id + "," + item.permissionId,
+          children: TreeFormatter(item.children)
+        }
+      } else {
+        return {
+          label: item.name,
+          value: item.id + "," + item.permissionId
+        }
+      }
+    })
+    return result
+  }
+  // 个人权限树形结构格式化
+  function RoleTreeFormatter(data) {
+    let arr = [];
+    data.forEach(item => {
+      if (item.children && item.children.length !== 0) {
+        arr.push(item.id + "," + item.permissionId)
+        arr.push(...RoleTreeFormatter(item.children))
+      } else {
+        arr.push(item.id + "," + item.permissionId)
+      };
+    });
+    return arr;
+  }
+  // 传输权限格式化
+  function ResultFormatter(data, type) {
+      return data.map(item => {
+        return item.split(',')[type]
+      })
+  }
+
+  onMounted(() => {
+    // onSearch();
+  });
+
+  return {
+    form,
+    loading,
+    columns,
+    dataList,
+    pagination,
+    buttonClass,
+    dialogAddVisible,
+    AddFormSubmit,
+    submit,
+    AddFormvalueStrictly,
+    AddForm,
+    AddFormdata,
+    dialogType,
+    AddUser,
+    onSearch,
+    handleUpdate,
+    handleDelete,
+    handleSizeChange,
+    handleCurrentChange,
+    handleSelectionChange
+  };
+}

+ 201 - 0
src/views/admin/role/index.vue

@@ -0,0 +1,201 @@
+<script lang="ts">
+// 声明额外的选项
+export default {
+  name: "Role"
+};
+</script>
+<script setup lang="ts">
+import { ref } from "vue";
+import { useUser } from "./hook";
+import { hasAuth } from "@/router/utils";
+import { PureTableBar } from "@/components/RePureTableBar";
+import { useRenderIcon } from "@/components/ReIcon/src/hooks";
+
+import Role from "@iconify-icons/ri/admin-line";
+import Password from "@iconify-icons/ri/lock-password-line";
+import More from "@iconify-icons/ep/more-filled";
+import Delete from "@iconify-icons/ep/delete";
+import EditPen from "@iconify-icons/ep/edit-pen";
+import Search from "@iconify-icons/ep/search";
+import Refresh from "@iconify-icons/ep/refresh";
+import AddFill from "@iconify-icons/ri/add-circle-line";
+const formRef = ref();
+const {
+  form,
+  loading,
+  columns,
+  dataList,
+  pagination,
+  dialogAddVisible,
+  AddFormSubmit,
+  submit,
+  dialogType,
+  AddForm,
+  AddFormdata,
+  AddFormvalueStrictly,
+  onSearch,
+  AddUser,
+  handleUpdate,
+  handleDelete,
+  handleSizeChange,
+  handleCurrentChange,
+  handleSelectionChange
+} = useUser();
+defineExpose({
+  form,
+  loading,
+  columns,
+  dataList,
+  pagination,
+  dialogAddVisible,
+  dialogType,
+  AddFormSubmit,
+  submit,
+  AddFormvalueStrictly,
+  AddForm,
+  AddFormdata,
+  onSearch,
+  AddUser,
+  handleUpdate,
+  handleDelete,
+  handleSizeChange,
+  handleCurrentChange,
+  handleSelectionChange
+});
+</script>
+
+<template lang="pug">
+.main
+  div
+    el-form.bg-bg_color.pl-8.pt-4.pr-8(
+      label-position="left"
+      label-width="100px"
+      ref="formRef",
+      :inline="true",
+      :model="form",
+      :rules="rules",
+      class="w-[99/100]"
+    )
+      el-form-item(label="角色名称:", prop="AdminName")
+        el-input(
+          v-model="form.Name",
+          placeholder="请输入角色名称",
+          clearable,@keydown.enter="submit"
+          class="!w-[230px]"
+        )
+      el-form-item
+        el-button(
+          type="primary",
+          :icon="useRenderIcon(Search)",
+          :loading="loading",
+          @click="onSearch('search')"
+          v-if="hasAuth(['getRoleList'])"
+        ) 查询
+      el-form-item
+        el-button(
+          type="primary",
+          :icon="useRenderIcon(Refresh)",
+          :loading="loading",
+          @click="onSearch('all')"
+          v-if="hasAuth(['getRoleList'])"
+        ) 全部
+    //- 表格组件
+    PureTableBar.mt12(title="后台角色管理", @refresh="onSearch" )
+      template(#buttons)
+        //- el-button(v-if="hasAuth(['add'])" type="primary" :icon="useRenderIcon(AddFill)" @click="AddUser" 
+        el-button(type="primary" :icon="useRenderIcon(AddFill)" @click="AddUser" v-if="hasAuth(['addRole'])" ) 新增角色
+      template(v-slot="{ size, checkList }")
+        pure-table(
+          stripe 
+          border,
+          align-whole="center",
+          table-layout="auto",
+          :loading="loading",
+          :size="size",
+          :data="dataList",
+          :columns="columns",
+          :checkList="checkList",
+          :pagination="pagination",
+          :paginationSmall="size === 'default' ? true : false",
+          :header-cell-style="{ background: 'var(--el-table-row-hover-bg-color)', color: 'var(--el-text-color-primary)' }",
+          @selection-change="handleSelectionChange",
+          @size-change="handleSizeChange",
+          @current-change="handleCurrentChange"
+        )          
+          template(#operation="{ row }")
+            .flex.flex-wrap.items-center
+              el-button(
+                class="reset-margin"
+                link
+                type="primary"
+                :size="size"
+                :icon="useRenderIcon(EditPen)"
+                @click="handleUpdate(row)"
+                v-if="hasAuth(['RoleEdit'])"
+              ) 编辑
+              el-button(
+                class="reset-margin"
+                link
+                type="primary"
+                :size="size"
+                :icon="useRenderIcon(Delete)"
+                @click="handleDelete(row)"
+                v-if="hasAuth(['RoleDelete'])"
+              ) 删除
+  el-dialog(v-model='dialogAddVisible' width="60%" :title="dialogType == 1? '新增角色':'修改角色'")
+    el-form(:model='AddFormdata' 
+      label-position="left"
+      label-width="100px")
+      el-form-item(label='角色名称' prop="name")
+        el-input(v-model='AddFormdata.name' autocomplete='off' class="!w-[230px]"
+          placeholder="请输入角色名称")
+      el-form-item(label='角色描述' prop="sn")
+        el-input(v-model='AddFormdata.sn' autocomplete='off' class="!w-[230px]"
+          placeholder="请输入角色描述")
+      el-form-item(label='权限' required)
+        el-tree-select(v-model="AddFormvalueStrictly" :data="AddForm" multiple size="large" collapse-tags collapse-tags-tooltip :max-collapse-tags="3" :render-after-expand="false" show-checkbox check-strictly check-on-click-node class="!w-[230px]")
+    template(#footer)
+      span.dialog-footer
+        el-button(@click='dialogAddVisible = false') 关闭
+        el-button(type='primary' @click='AddFormSubmit') 确认提交
+</template>
+
+<style scoped lang="scss">
+:deep(.el-dropdown-menu__item i) {
+  margin: 0;
+}
+
+:deep(.el-form-item__label) {
+  font-weight: 700;
+}
+
+:deep(.el-pagination) {
+  flex-flow: wrap;
+}
+
+:deep(.is-draggable) {
+  max-height: 80vh;
+  overflow: auto;
+}
+
+:deep(.el-dialog__header) {
+  position: sticky;
+  top: 0;
+  z-index: 2;
+  background: #fff;
+}
+
+.collapsedom {
+  padding: 0 20px;
+  background-color: #fff;
+}
+
+.ovh-x {
+  height: 40vh;
+  overflow-y: auto;
+}
+
+:deep(.el-descriptions__header) {
+  margin: 16px 0 !important;
+}
+</style>

+ 1 - 0
src/views/admin/role/svg/expand.svg

@@ -0,0 +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.5V4h9Z"/></svg>

+ 1 - 0
src/views/admin/role/svg/unexpand.svg

@@ -0,0 +1 @@
+<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>

+ 303 - 0
src/views/admin/user/hook.tsx

@@ -0,0 +1,303 @@
+import {
+  deleteUserList,
+  editUserList,
+  getRoleList,
+  getUserList,
+  postUserList
+} from "@/api/system";
+import { ListDate } from "@/utils/formateDate";
+import {
+  UseFormatDatePickerHook,
+  UseValidTimeRangeHook
+} from "@/utils/hooks/format";
+import { type PaginationProps } from "@pureadmin/table";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { reactive, ref, computed, onMounted } from "vue";
+
+export function useUser() {
+  const form = reactive({
+    username: "",
+    phone: "",
+    deptId: ""
+  });
+  const UpdateForm = ref({
+    account: "",
+    username: "",
+    password: "",
+    roleIds: [],
+    phone: "",
+    adminType: "1",
+    id: ""
+  });
+  const dialogType = ref(1);
+  const dataList = ref([]);
+  const optionList = ref([]);
+  const loading = ref(false);
+  const dialogUpdateVisible = ref(false);
+  const pagination = reactive<PaginationProps>({
+    total: 0,
+    pageSize: 10,
+    currentPage: 1,
+    background: true
+  });
+  const columns: TableColumnList = [
+    {
+      type: "selection",
+      width: 55,
+      align: "left",
+      hide: ({ checkList }) => !checkList.includes("勾选列")
+    },
+    {
+      label: "序号",
+      type: "index",
+      width: 70,
+      hide: ({ checkList }) => !checkList.includes("序号列")
+    },
+    {
+      label: "用户ID",
+      prop: "id",
+      minWidth: 130
+    },
+    {
+      label: "角色名称",
+      prop: "roles",
+      minWidth: 130,
+      formatter: ({ roles }) => {
+        let RoleName = "";
+        optionList.value.forEach(item => {
+          if (item.Id === roles[0].id) {
+            RoleName = item.label;
+          }
+        });
+        return RoleName;
+      }
+    },
+    {
+      label: "用户名",
+      prop: "account",
+      minWidth: 130
+    },
+    {
+      label: "名称",
+      prop: "username",
+      minWidth: 130
+    },
+    {
+      label: "操作",
+      fixed: "right",
+      width: 200,
+      slot: "operation"
+    }
+  ];
+  const buttonClass = computed(() => {
+    return [
+      "!h-[20px]",
+      "reset-margin",
+      "!text-gray-500",
+      "dark:!text-white",
+      "dark:hover:!text-primary"
+    ];
+  });
+
+  async function UpdateFormSubmit() {
+    if (dialogType.value == 1) {
+      const { status, message } = await postUserList({
+        account: UpdateForm.value.account,
+        username: UpdateForm.value.username,
+        password: UpdateForm.value.password,
+        roleIds: [UpdateForm.value.roleIds],
+        phone: UpdateForm.value.phone,
+        adminType: "1"
+      });
+      if (status == 1) {
+        ElMessage({
+          message: "新增账号成功",
+          type: "success"
+        });
+        dialogUpdateVisible.value = false;
+        UpdateForm.value = {
+          account: "",
+          username: "",
+          password: "",
+          roleIds: [],
+          phone: "",
+          adminType: "1"
+        };
+        onSearch();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      }
+    } else {
+      const { status, message } = await editUserList({
+        account: UpdateForm.value.account,
+        username: UpdateForm.value.username,
+        password: UpdateForm.value.password,
+        roleIds: [UpdateForm.value.roleIds],
+        phone: UpdateForm.value.phone,
+        adminType: "1",
+        id: UpdateForm.value.id
+      });
+      if (status == 1) {
+        ElMessage({
+          message: "修改账号成功",
+          type: "success"
+        });
+        dialogUpdateVisible.value = false;
+        UpdateForm.value = {
+          account: "",
+          username: "",
+          password: "",
+          roleIds: [],
+          phone: "",
+          adminType: "1",
+          id: ""
+        };
+        onSearch();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      }
+    }
+  }
+  // 获取角色列表
+  async function RoleList() {
+    const { data } = await getRoleList({ name: "", pageSize: 100, pageNum: 1 });
+    const option = data.map(item => {
+      return {
+        label: item.name,
+        Id: item.id
+      };
+    });
+    optionList.value = option;
+  }
+  RoleList();
+  function handleDelete(row) {
+    ElMessageBox.confirm(
+      `是否删除该用户?  用户名称为:${row.username}`,
+      "提示",
+      {
+        confirmButtonText: "删除",
+        cancelButtonText: "取消",
+        type: "warning"
+      }
+    ).then(async () => {
+      const { status, message } = await deleteUserList({ id: row.id });
+      if (status == 1) {
+        ElMessage({
+          message: "删除成功",
+          type: "success"
+        });
+        onSearch();
+      } else {
+        ElMessageBox.alert(message, "提示", {
+          confirmButtonText: "关闭",
+          type: "warning"
+        });
+      }
+    });
+  }
+  function AddUser(row) {
+    UpdateForm.value = {
+      account: "",
+      username: "",
+      password: "",
+      roleIds: [],
+      phone: "",
+      adminType: "1",
+      id: ""
+    };
+    dialogUpdateVisible.value = true;
+    dialogType.value = 1;
+  }
+  function handleUpdate(row) {
+    dialogType.value = 2;
+    dialogUpdateVisible.value = true;
+    UpdateForm.value = {
+      account: row.account,
+      username: row.username,
+      password: "",
+      roleIds: row.roles[0].id,
+      phone: row.phone,
+      adminType: "1",
+      id: row.id
+    };
+  }
+
+  function handleSizeChange(val: number) {
+    console.log(`${val} items per page`);
+    onSearch();
+  }
+
+  function handleCurrentChange(val: number) {
+    console.log(`current page: ${val}`);
+    onSearch();
+  }
+
+  function handleSelectionChange(val) {
+    console.log("handleSelectionChange", val);
+    onSearch();
+  }
+
+  // 搜索函数(请求表格数据列表)
+  function submit(e: { keyCode: number }) {
+    if (e.keyCode == 229) {
+      return;
+    } else {
+      onSearch("search");
+    }
+  }
+  async function onSearch(type = "") {
+    if (type == "search" && !Object.values(form).some(item => !!item)) {
+      return ElMessage({
+        message: "请输入查询条件",
+        type: "error"
+      });
+    }
+    if (type == "all") {
+      form.account = "";
+      form.username = "";
+    }
+    loading.value = true;
+    const { data, other } = await getUserList({
+      ...form,
+      pageSize: pagination.pageSize,
+      pageNum: pagination.currentPage
+    });
+    dataList.value = data;
+    pagination.total = other;
+    setTimeout(() => {
+      loading.value = false;
+    }, 500);
+  }
+
+  onMounted(() => {
+    // onSearch();
+  });
+
+  return {
+    form,
+    loading,
+    columns,
+    dataList,
+    pagination,
+    buttonClass,
+    dialogUpdateVisible,
+    UpdateFormSubmit,
+    submit,
+    UpdateForm,
+    optionList,
+    dialogType,
+    AddUser,
+    onSearch,
+    handleUpdate,
+    handleDelete,
+    handleSizeChange,
+    handleCurrentChange,
+    handleSelectionChange
+  };
+}

+ 229 - 0
src/views/admin/user/index.vue

@@ -0,0 +1,229 @@
+<script lang="ts">
+// 声明额外的选项
+export default {
+  name: "User"
+};
+</script>
+<script setup lang="ts">
+import { ref } from "vue";
+import { useUser } from "./hook";
+import { hasAuth } from "@/router/utils";
+import { PureTableBar } from "@/components/RePureTableBar";
+import { useRenderIcon } from "@/components/ReIcon/src/hooks";
+
+import Role from "@iconify-icons/ri/admin-line";
+import Password from "@iconify-icons/ri/lock-password-line";
+import More from "@iconify-icons/ep/more-filled";
+import Delete from "@iconify-icons/ep/delete";
+import EditPen from "@iconify-icons/ep/edit-pen";
+import Search from "@iconify-icons/ep/search";
+import Refresh from "@iconify-icons/ep/refresh";
+import AddFill from "@iconify-icons/ri/add-circle-line";
+
+const formRef = ref();
+const {
+  form,
+  loading,
+  columns,
+  dataList,
+  pagination,
+  dialogUpdateVisible,
+  UpdateFormSubmit,
+  UpdateForm,
+  onSearch,
+  submit,
+  dialogType,
+  optionList,
+  AddUser,
+  handleUpdate,
+  handleDelete,
+  handleSizeChange,
+  handleCurrentChange,
+  handleSelectionChange
+} = useUser();
+defineExpose({
+  form,
+  loading,
+  columns,
+  dataList,
+  pagination,
+  dialogUpdateVisible,
+  UpdateFormSubmit,
+  UpdateForm,
+  submit,
+  onSearch,
+  dialogType,
+  optionList,
+  AddUser,
+  handleUpdate,
+  handleDelete,
+  handleSizeChange,
+  handleCurrentChange,
+  handleSelectionChange
+});
+</script>
+
+<template lang="pug">
+.main
+  div
+    el-form.bg-bg_color.pl-8.pt-4.pr-8(
+      label-position="left"
+      label-width="100px"
+      ref="formRef",
+      :inline="true",
+      :model="form",
+      :rules="rules",
+      class="w-[99/100]"
+    )
+      el-form-item(label="账号:", prop="phone")
+        el-input(
+          v-model="form.phone",
+          placeholder="请输入账号",
+          clearable,@keydown.enter="submit"
+          class="!w-[230px]"
+        )
+      el-form-item(label="管理员名称:", prop="username")
+        el-input(
+          v-model="form.username",
+          placeholder="请输入管理员名称",
+          clearable,@keydown.enter="submit"
+          class="!w-[230px]"
+        )
+      el-form-item(label="部门ID:", prop="deptId")
+        el-input(
+          v-model="form.deptId",
+          placeholder="请输入部门ID",
+          clearable,@keydown.enter="submit"
+          class="!w-[230px]"
+        )
+      //- el-form-item(label="角色:", prop="RoleId")
+        el-select(
+          v-model="form.RoleId",
+          placeholder="请选择用户角色",
+          clearable,
+          class="!w-[230px]"
+        )
+          el-option(:label="item.label", :value="item.Id" v-for="(item,index) in optionList")
+      el-form-item
+        el-button(
+          type="primary",
+          :icon="useRenderIcon(Search)",
+          :loading="loading",
+          @click="onSearch('search')"
+        ) 查询
+      el-form-item
+        el-button(
+          type="primary",
+          :icon="useRenderIcon(Refresh)",
+          :loading="loading",
+          @click="onSearch('all')"
+        ) 全部
+    //- 表格组件
+    PureTableBar.mt12(title="后台用户管理", @refresh="onSearch" )
+      template(#buttons)
+        //- el-button(type="primary" :icon="useRenderIcon(AddFill)" @click="AddUser" v-if="hasAuth(['add'])") 新增用户
+        el-button(type="primary" :icon="useRenderIcon(AddFill)" @click="AddUser") 新增用户
+      template(v-slot="{ size, checkList }")
+        pure-table(stripe 
+          border,
+          align-whole="center",
+          table-layout="auto",
+          :loading="loading",
+          :size="size",
+          :data="dataList",
+          :columns="columns",
+          :checkList="checkList",
+          :pagination="pagination",
+          :paginationSmall="size === 'default' ? true : false",
+          :header-cell-style="{ background: 'var(--el-table-row-hover-bg-color)', color: 'var(--el-text-color-primary)' }",
+          @selection-change="handleSelectionChange",
+          @size-change="handleSizeChange",
+          @current-change="handleCurrentChange"
+        )          
+          template(#operation="{ row }")
+            el-button.reset-margin(
+              link
+              type="primary"
+              :size="size"
+              @click="handleUpdate(row)"
+              :icon="useRenderIcon(EditPen)"
+            ) 编辑
+              //- v-if="hasAuth(['edit'])"
+            el-button.reset-margin(
+              link
+              type="primary"
+              :size="size"
+              @click="handleDelete(row)"
+              :icon="useRenderIcon(Delete)"
+            ) 删除
+              //- v-if="hasAuth(['delete'])"
+
+  el-dialog(v-model='dialogUpdateVisible' width="40%" :title="dialogType == 1 ?'添加系统用户' : '修改系统用户信息'")
+    el-form(:model='UpdateForm' 
+      label-position="left"
+      label-width="100px")
+      el-form-item(label='管理员账号' prop="account")
+        el-input(v-model='UpdateForm.account' autocomplete='off' class="!w-[230px]"
+          placeholder="请输入管理员账号")
+      el-form-item(label='管理员密码' prop="password")
+        el-input(v-model='UpdateForm.password' autocomplete='off' class="!w-[230px]"
+          placeholder="请输入管理员密码")
+      el-form-item(label='用户名' prop="username")
+        el-input(v-model='UpdateForm.username' autocomplete='off' class="!w-[230px]"
+          placeholder="请输入用户名")
+      el-form-item(label='管理员电话' prop="phone")
+        el-input(v-model='UpdateForm.phone' autocomplete='off' class="!w-[230px]"
+          placeholder="请输入管理员电话")
+      el-form-item(label="角色:", prop="roleIds")
+        el-select(
+          v-model="UpdateForm.roleIds",
+          placeholder="请选择用户角色",
+          clearable,
+          class="!w-[230px]"
+        )
+          el-option(:label="item.label", :value="item.Id" v-for="(item,index) in optionList")
+    template(#footer)
+      span.dialog-footer
+        el-button(@click='dialogUpdateVisible = false') 关闭
+        el-button(type='primary' @click='UpdateFormSubmit') 确认提交
+</template>
+
+<style scoped lang="scss">
+:deep(.el-dropdown-menu__item i) {
+  margin: 0;
+}
+
+:deep(.el-form-item__label) {
+  font-weight: 700;
+}
+
+:deep(.el-pagination) {
+  flex-flow: wrap;
+}
+
+:deep(.is-draggable) {
+  max-height: 80vh;
+  overflow: auto;
+}
+
+:deep(.el-dialog__header) {
+  position: sticky;
+  top: 0;
+  z-index: 2;
+  background: #fff;
+}
+
+.collapsedom {
+  padding: 0 20px;
+  background-color: #fff;
+}
+
+.ovh-x {
+  height: 40vh;
+  overflow-y: auto;
+}
+
+:deep(.el-descriptions__header) {
+  margin: 16px 0 !important;
+}
+</style>

+ 1 - 0
src/views/admin/user/svg/expand.svg

@@ -0,0 +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.5V4h9Z"/></svg>

+ 1 - 0
src/views/admin/user/svg/unexpand.svg

@@ -0,0 +1 @@
+<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>

+ 6 - 7
src/views/login/index.vue

@@ -72,8 +72,8 @@ const ruleForm = reactive({
 const onLogin = async (formEl: FormInstance | undefined) => {
   loading.value = true;
   const value = {
-    userName: ruleForm.username,
-    pwd: ruleForm.password
+    username: ruleForm.username,
+    password: ruleForm.password
   };
   checked.value
     ? Cookies.set("isRememberPassword", "true") &&
@@ -88,17 +88,16 @@ const onLogin = async (formEl: FormInstance | undefined) => {
     if (valid) {
       useUserStoreHook()
         .loginByUsername(value)
-        .then(res => {
-          if (res.status === "1") {
-            // 获取后端路
+        .then((res: any) => {
+          if (res.status === 1) {
+            // 获取后端路
             initRouter().then((router: Router) => {
-              // router.push("/empty");
               router.push("/");
               message("登录成功", { type: "success" });
             });
           } else {
             loading.value = false;
-            message(res.info, { type: "error" });
+            message(res.msg, { type: "error" });
           }
         });
     } else {

+ 3 - 3
vite.config.ts

@@ -1,8 +1,8 @@
 /*
  * @Author: Gui
  * @Date: 2023-03-01 19:20:44
- * @LastEditors: Please set LastEditors
- * @LastEditTime: 2023-08-02 15:20:45
+ * @LastEditors: guicheng 1625811865@qq.com
+ * @LastEditTime: 2024-04-10 15:54:56
  * @Description: kxs files
  * @filePath:
  */
@@ -54,7 +54,7 @@ export default ({ command, mode }: ConfigEnv): UserConfigExport => {
       proxy: {
         // "/api": {
         //   // 这里填写后端地址
-        //   target: "https://kxs-admin.kexiaoshuang.com/",
+        //   target: "http://test.apigateway.shuangkebang.com",
         //   changeOrigin: true,
         //   rewrite: path => path.replace(/^\/api/, "")
         // }