feat: 整体pure,核心功能对接完成

This commit is contained in:
橙子
2024-09-05 23:10:40 +08:00
parent 4ed44a2a8f
commit 3339e30014
31 changed files with 459 additions and 535 deletions

View File

@@ -1,6 +1,8 @@
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
using Lazy.Captcha.Core; using Lazy.Captcha.Core;
using Mapster;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
@@ -25,6 +27,7 @@ using Yi.Framework.Rbac.Domain.Repositories;
using Yi.Framework.Rbac.Domain.Shared.Caches; using Yi.Framework.Rbac.Domain.Shared.Caches;
using Yi.Framework.Rbac.Domain.Shared.Consts; using Yi.Framework.Rbac.Domain.Shared.Consts;
using Yi.Framework.Rbac.Domain.Shared.Dtos; using Yi.Framework.Rbac.Domain.Shared.Dtos;
using Yi.Framework.Rbac.Domain.Shared.Etos;
using Yi.Framework.Rbac.Domain.Shared.Options; using Yi.Framework.Rbac.Domain.Shared.Options;
using Yi.Framework.SqlSugarCore.Abstractions; using Yi.Framework.SqlSugarCore.Abstractions;
@@ -40,7 +43,7 @@ namespace Yi.Framework.Rbac.Application.Services
private readonly IAliyunManger _aliyunManger; private readonly IAliyunManger _aliyunManger;
private IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> _userCache; private IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> _userCache;
private UserManager _userManager; private UserManager _userManager;
private IHttpContextAccessor _httpContextAccessor;
public AccountService(IUserRepository userRepository, public AccountService(IUserRepository userRepository,
ICurrentUser currentUser, ICurrentUser currentUser,
IAccountManager accountManager, IAccountManager accountManager,
@@ -51,7 +54,7 @@ namespace Yi.Framework.Rbac.Application.Services
IGuidGenerator guidGenerator, IGuidGenerator guidGenerator,
IOptions<RbacOptions> options, IOptions<RbacOptions> options,
IAliyunManger aliyunManger, IAliyunManger aliyunManger,
UserManager userManager) UserManager userManager, IHttpContextAccessor httpContextAccessor)
{ {
_userRepository = userRepository; _userRepository = userRepository;
_currentUser = currentUser; _currentUser = currentUser;
@@ -64,6 +67,7 @@ namespace Yi.Framework.Rbac.Application.Services
_aliyunManger = aliyunManger; _aliyunManger = aliyunManger;
_userCache = userCache; _userCache = userCache;
_userManager = userManager; _userManager = userManager;
_httpContextAccessor = httpContextAccessor;
} }
@@ -109,10 +113,21 @@ namespace Yi.Framework.Rbac.Application.Services
//校验 //校验
await _accountManager.LoginValidationAsync(input.UserName, input.Password, x => user = x); await _accountManager.LoginValidationAsync(input.UserName, input.Password, x => user = x);
var userInfo = new UserRoleMenuDto();
//获取token //获取token
var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id); var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id, (info) => userInfo = info);
var refreshToken = _accountManager.CreateRefreshToken(user.Id); var refreshToken = _accountManager.CreateRefreshToken(user.Id);
//这里抛出一个登录的事件,也可以在全部流程走完,在应用层组装
if (_httpContextAccessor.HttpContext is not null)
{
var loginEntity = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContextAccessor.HttpContext);
var loginEto = loginEntity.Adapt<LoginEventArgs>();
loginEto.UserName = userInfo.User.UserName;
loginEto.UserId = userInfo.User.Id;
await LocalEventBus.PublishAsync(loginEto);
}
return new { Token = accessToken, RefreshToken = refreshToken }; return new { Token = accessToken, RefreshToken = refreshToken };
} }

View File

@@ -34,13 +34,11 @@ namespace Yi.Framework.Rbac.Domain.Managers
private readonly ILocalEventBus _localEventBus; private readonly ILocalEventBus _localEventBus;
private readonly JwtOptions _jwtOptions; private readonly JwtOptions _jwtOptions;
private readonly RbacOptions _options; private readonly RbacOptions _options;
private IHttpContextAccessor _httpContextAccessor;
private UserManager _userManager; private UserManager _userManager;
private ISqlSugarRepository<RoleAggregateRoot> _roleRepository; private ISqlSugarRepository<RoleAggregateRoot> _roleRepository;
private RefreshJwtOptions _refreshJwtOptions; private RefreshJwtOptions _refreshJwtOptions;
public AccountManager(IUserRepository repository public AccountManager(IUserRepository repository
, IHttpContextAccessor httpContextAccessor
, IOptions<JwtOptions> jwtOptions , IOptions<JwtOptions> jwtOptions
, ILocalEventBus localEventBus , ILocalEventBus localEventBus
, UserManager userManager , UserManager userManager
@@ -49,7 +47,6 @@ namespace Yi.Framework.Rbac.Domain.Managers
, IOptions<RbacOptions> options) , IOptions<RbacOptions> options)
{ {
_repository = repository; _repository = repository;
_httpContextAccessor = httpContextAccessor;
_jwtOptions = jwtOptions.Value; _jwtOptions = jwtOptions.Value;
_localEventBus = localEventBus; _localEventBus = localEventBus;
_userManager = userManager; _userManager = userManager;
@@ -62,9 +59,10 @@ namespace Yi.Framework.Rbac.Domain.Managers
/// 根据用户id获取token /// 根据用户id获取token
/// </summary> /// </summary>
/// <param name="userId"></param> /// <param name="userId"></param>
/// <param name="getUserInfo"></param>
/// <returns></returns> /// <returns></returns>
/// <exception cref="UserFriendlyException"></exception> /// <exception cref="UserFriendlyException"></exception>
public async Task<string> GetTokenByUserIdAsync(Guid userId) public async Task<string> GetTokenByUserIdAsync(Guid userId,Action<UserRoleMenuDto>? getUserInfo=null)
{ {
//获取用户信息 //获取用户信息
var userInfo = await _userManager.GetInfoAsync(userId); var userInfo = await _userManager.GetInfoAsync(userId);
@@ -79,23 +77,18 @@ namespace Yi.Framework.Rbac.Domain.Managers
{ {
throw new UserFriendlyException(UserConst.No_Role); throw new UserFriendlyException(UserConst.No_Role);
} }
if (userInfo.PermissionCodes.Count() == 0) if (!userInfo.PermissionCodes.Any())
{ {
throw new UserFriendlyException(UserConst.No_Permission); throw new UserFriendlyException(UserConst.No_Permission);
} }
//这里抛出一个登录的事件,也可以在全部流程走完,在应用层组装
if (_httpContextAccessor.HttpContext is not null) if (getUserInfo is not null)
{ {
var loginEntity = new LoginLogAggregateRoot().GetInfoByHttpContext(_httpContextAccessor.HttpContext); getUserInfo(userInfo);
var loginEto = loginEntity.Adapt<LoginEventArgs>();
loginEto.UserName = userInfo.User.UserName;
loginEto.UserId = userInfo.User.Id;
await _localEventBus.PublishAsync(loginEto);
} }
var accessToken = CreateToken(this.UserInfoToClaim(userInfo)); var accessToken = CreateToken(this.UserInfoToClaim(userInfo));
//将用户信息添加到缓存中,需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新,看具体业务进行选择 //将用户信息添加到缓存中,需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新,看具体业务进行选择
return accessToken; return accessToken;
} }

View File

@@ -5,14 +5,15 @@ using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Volo.Abp.Domain.Services; using Volo.Abp.Domain.Services;
using Yi.Framework.Rbac.Domain.Entities; using Yi.Framework.Rbac.Domain.Entities;
using Yi.Framework.Rbac.Domain.Shared.Dtos;
namespace Yi.Framework.Rbac.Domain.Managers namespace Yi.Framework.Rbac.Domain.Managers
{ {
public interface IAccountManager : IDomainService public interface IAccountManager : IDomainService
{ {
string CreateRefreshToken(Guid userId); string CreateRefreshToken(Guid userId);
Task<string> GetTokenByUserIdAsync(Guid userId); Task<string> GetTokenByUserIdAsync(Guid userId,Action<UserRoleMenuDto>? getUserInfo=null);
Task LoginValidationAsync(string userName, string password, Action<UserAggregateRoot> userAction = null); Task LoginValidationAsync(string userName, string password, Action<UserAggregateRoot>? userAction = null);
Task RegisterAsync(string userName, string password, long phone); Task RegisterAsync(string userName, string password, long phone);
Task<bool> RestPasswordAsync(Guid userId, string password); Task<bool> RestPasswordAsync(Guid userId, string password);
Task UpdatePasswordAsync(Guid userId, string newPassword, string oldPassword); Task UpdatePasswordAsync(Guid userId, string newPassword, string oldPassword);

View File

@@ -162,14 +162,23 @@ namespace Yi.Framework.Rbac.Domain.Managers
} }
/// <summary> /// <summary>
/// 查询用户信息,缓存 /// 查询用户信息,取消缓存
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task<UserRoleMenuDto> GetInfoAsync(Guid userId) public async Task<UserRoleMenuDto> GetInfoAsync(Guid userId)
{ {
var user = await _userRepository.GetUserAllInfoAsync(userId);
var output = await GetInfoByCacheAsync(userId); var data = EntityMapToDto(user);
return output; //系统用户数据被重置,老前端访问重新授权
if (data is null)
{
throw new AbpAuthorizationException();
}
//data.Menus.Clear();
// output = data;
return data;
// var output = await GetInfoByCacheAsync(userId);
// return output;
} }
private async Task<UserRoleMenuDto> GetInfoByCacheAsync(Guid userId) private async Task<UserRoleMenuDto> GetInfoByCacheAsync(Guid userId)
{ {

View File

@@ -1,6 +1,6 @@
// 模拟后端动态生成路由 // 模拟后端动态生成路由
import { defineFakeRoute } from "vite-plugin-fake-server/client"; import { defineFakeRoute } from "vite-plugin-fake-server/client";
import { system, monitor, permission, frame, tabs } from "@/router/enums"; import { system, monitor } from "@/router/enums";
/** /**
* roles页面级别权限这里模拟二种 "admin"、"common" * roles页面级别权限这里模拟二种 "admin"、"common"
@@ -21,7 +21,7 @@ const systemManagementRouter = {
name: "SystemUser", name: "SystemUser",
meta: { meta: {
icon: "ri:admin-line", icon: "ri:admin-line",
title: "menus.pureUser", title: "menus.pureUser"
//roles: ["admin"] //roles: ["admin"]
} }
}, },
@@ -30,7 +30,7 @@ const systemManagementRouter = {
name: "SystemRole", name: "SystemRole",
meta: { meta: {
icon: "ri:admin-fill", icon: "ri:admin-fill",
title: "menus.pureRole", title: "menus.pureRole"
//roles: ["admin"] //roles: ["admin"]
} }
}, },
@@ -39,7 +39,7 @@ const systemManagementRouter = {
name: "SystemMenu", name: "SystemMenu",
meta: { meta: {
icon: "ep:menu", icon: "ep:menu",
title: "menus.pureSystemMenu", title: "menus.pureSystemMenu"
//roles: ["admin"] //roles: ["admin"]
} }
}, },
@@ -48,7 +48,7 @@ const systemManagementRouter = {
name: "SystemDept", name: "SystemDept",
meta: { meta: {
icon: "ri:git-branch-line", icon: "ri:git-branch-line",
title: "menus.pureDept", title: "menus.pureDept"
//roles: ["admin"] //roles: ["admin"]
} }
}, },
@@ -57,7 +57,7 @@ const systemManagementRouter = {
name: "SystemPost", name: "SystemPost",
meta: { meta: {
icon: "ant-design:deployment-unit-outlined", icon: "ant-design:deployment-unit-outlined",
title: "menus.purePost", title: "menus.purePost"
//roles: ["admin"] //roles: ["admin"]
} }
} }
@@ -78,7 +78,7 @@ const systemMonitorRouter = {
name: "OnlineUser", name: "OnlineUser",
meta: { meta: {
icon: "ri:user-voice-line", icon: "ri:user-voice-line",
title: "menus.pureOnlineUser", title: "menus.pureOnlineUser"
//roles: ["admin"] //roles: ["admin"]
} }
}, },
@@ -88,7 +88,7 @@ const systemMonitorRouter = {
name: "LoginLog", name: "LoginLog",
meta: { meta: {
icon: "ri:window-line", icon: "ri:window-line",
title: "menus.pureLoginLog", title: "menus.pureLoginLog"
//roles: ["admin"] //roles: ["admin"]
} }
}, },
@@ -98,7 +98,7 @@ const systemMonitorRouter = {
name: "OperationLog", name: "OperationLog",
meta: { meta: {
icon: "ri:history-fill", icon: "ri:history-fill",
title: "menus.pureOperationLog", title: "menus.pureOperationLog"
//roles: ["admin"] //roles: ["admin"]
} }
} }
@@ -114,220 +114,220 @@ const systemMonitorRouter = {
// } // }
] ]
}; };
//
const permissionRouter = { // const permissionRouter = {
path: "/permission", // path: "/permission",
meta: { // meta: {
title: "menus.purePermission", // title: "menus.purePermission",
icon: "ep:lollipop", // icon: "ep:lollipop",
rank: permission // rank: permission
}, // },
children: [ // children: [
{ // {
path: "/permission/page/index", // path: "/permission/page/index",
name: "PermissionPage", // name: "PermissionPage",
meta: { // meta: {
title: "menus.purePermissionPage", // title: "menus.purePermissionPage"
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/permission/button", // path: "/permission/button",
meta: { // meta: {
title: "menus.purePermissionButton", // title: "menus.purePermissionButton"
//roles: ["admin", "common"] // //roles: ["admin", "common"]
}, // },
children: [ // children: [
{ // {
path: "/permission/button/router", // path: "/permission/button/router",
component: "permission/button/index", // component: "permission/button/index",
name: "PermissionButtonRouter", // name: "PermissionButtonRouter",
meta: { // meta: {
title: "menus.purePermissionButtonRouter", // title: "menus.purePermissionButtonRouter",
auths: [ // auths: [
"permission:btn:add", // "permission:btn:add",
"permission:btn:edit", // "permission:btn:edit",
"permission:btn:delete" // "permission:btn:delete"
] // ]
} // }
}, // },
{ // {
path: "/permission/button/login", // path: "/permission/button/login",
component: "permission/button/perms", // component: "permission/button/perms",
name: "PermissionButtonLogin", // name: "PermissionButtonLogin",
meta: { // meta: {
title: "menus.purePermissionButtonLogin" // title: "menus.purePermissionButtonLogin"
} // }
} // }
] // ]
} // }
] // ]
}; // };
//
const frameRouter = { // const frameRouter = {
path: "/iframe", // path: "/iframe",
meta: { // meta: {
icon: "ri:links-fill", // icon: "ri:links-fill",
title: "menus.pureExternalPage", // title: "menus.pureExternalPage",
rank: frame // rank: frame
}, // },
children: [ // children: [
{ // {
path: "/iframe/embedded", // path: "/iframe/embedded",
meta: { // meta: {
title: "menus.pureEmbeddedDoc" // title: "menus.pureEmbeddedDoc"
}, // },
children: [ // children: [
{ // {
path: "/iframe/colorhunt", // path: "/iframe/colorhunt",
name: "FrameColorHunt", // name: "FrameColorHunt",
meta: { // meta: {
title: "menus.pureColorHuntDoc", // title: "menus.pureColorHuntDoc",
frameSrc: "https://colorhunt.co/", // frameSrc: "https://colorhunt.co/",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/iframe/uigradients", // path: "/iframe/uigradients",
name: "FrameUiGradients", // name: "FrameUiGradients",
meta: { // meta: {
title: "menus.pureUiGradients", // title: "menus.pureUiGradients",
frameSrc: "https://uigradients.com/", // frameSrc: "https://uigradients.com/",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/iframe/ep", // path: "/iframe/ep",
name: "FrameEp", // name: "FrameEp",
meta: { // meta: {
title: "menus.pureEpDoc", // title: "menus.pureEpDoc",
frameSrc: "https://element-plus.org/zh-CN/", // frameSrc: "https://element-plus.org/zh-CN/",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/iframe/tailwindcss", // path: "/iframe/tailwindcss",
name: "FrameTailwindcss", // name: "FrameTailwindcss",
meta: { // meta: {
title: "menus.pureTailwindcssDoc", // title: "menus.pureTailwindcssDoc",
frameSrc: "https://tailwindcss.com/docs/installation", // frameSrc: "https://tailwindcss.com/docs/installation",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/iframe/vue3", // path: "/iframe/vue3",
name: "FrameVue", // name: "FrameVue",
meta: { // meta: {
title: "menus.pureVueDoc", // title: "menus.pureVueDoc",
frameSrc: "https://cn.vuejs.org/", // frameSrc: "https://cn.vuejs.org/",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/iframe/vite", // path: "/iframe/vite",
name: "FrameVite", // name: "FrameVite",
meta: { // meta: {
title: "menus.pureViteDoc", // title: "menus.pureViteDoc",
frameSrc: "https://cn.vitejs.dev/", // frameSrc: "https://cn.vitejs.dev/",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/iframe/pinia", // path: "/iframe/pinia",
name: "FramePinia", // name: "FramePinia",
meta: { // meta: {
title: "menus.purePiniaDoc", // title: "menus.purePiniaDoc",
frameSrc: "https://pinia.vuejs.org/zh/index.html", // frameSrc: "https://pinia.vuejs.org/zh/index.html",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/iframe/vue-router", // path: "/iframe/vue-router",
name: "FrameRouter", // name: "FrameRouter",
meta: { // meta: {
title: "menus.pureRouterDoc", // title: "menus.pureRouterDoc",
frameSrc: "https://router.vuejs.org/zh/", // frameSrc: "https://router.vuejs.org/zh/",
keepAlive: true, // keepAlive: true
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
} // }
] // ]
}, // },
{ // {
path: "/iframe/external", // path: "/iframe/external",
meta: { // meta: {
title: "menus.pureExternalDoc" // title: "menus.pureExternalDoc"
}, // },
children: [ // children: [
{ // {
path: "/external", // path: "/external",
name: "https://pure-admin.github.io/pure-admin-doc", // name: "https://pure-admin.github.io/pure-admin-doc",
meta: { // meta: {
title: "menus.pureExternalLink", // title: "menus.pureExternalLink"
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
{ // {
path: "/pureUtilsLink", // path: "/pureUtilsLink",
name: "https://pure-admin-utils.netlify.app/", // name: "https://pure-admin-utils.netlify.app/",
meta: { // meta: {
title: "menus.pureUtilsLink", // title: "menus.pureUtilsLink"
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
} // }
] // ]
} // }
] // ]
}; // };
//
const tabsRouter = { // const tabsRouter = {
path: "/tabs", // path: "/tabs",
meta: { // meta: {
icon: "ri:bookmark-2-line", // icon: "ri:bookmark-2-line",
title: "menus.pureTabs", // title: "menus.pureTabs",
rank: tabs // rank: tabs
}, // },
children: [ // children: [
{ // {
path: "/tabs/index", // path: "/tabs/index",
name: "Tabs", // name: "Tabs",
meta: { // meta: {
title: "menus.pureTabs", // title: "menus.pureTabs"
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
// query 传参模式 // // query 传参模式
{ // {
path: "/tabs/query-detail", // path: "/tabs/query-detail",
name: "TabQueryDetail", // name: "TabQueryDetail",
meta: { // meta: {
// 不在menu菜单中显示 // // 不在menu菜单中显示
showLink: false, // showLink: false,
activePath: "/tabs/index", // activePath: "/tabs/index"
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
}, // },
// params 传参模式 // // params 传参模式
{ // {
path: "/tabs/params-detail/:id", // path: "/tabs/params-detail/:id",
component: "params-detail", // component: "params-detail",
name: "TabParamsDetail", // name: "TabParamsDetail",
meta: { // meta: {
// 不在menu菜单中显示 // // 不在menu菜单中显示
showLink: false, // showLink: false,
activePath: "/tabs/index", // activePath: "/tabs/index"
//roles: ["admin", "common"] // //roles: ["admin", "common"]
} // }
} // }
] // ]
}; // };
export default defineFakeRoute([ export default defineFakeRoute([
{ {
@@ -336,7 +336,7 @@ export default defineFakeRoute([
response: () => { response: () => {
return [ return [
systemManagementRouter, systemManagementRouter,
systemMonitorRouter, systemMonitorRouter
//permissionRouter, //permissionRouter,
// frameRouter, // frameRouter,
// tabsRouter // tabsRouter

View File

@@ -0,0 +1,12 @@
import { http } from "@/utils/http";
import type { ResultFile } from "@/api/result";
/** 上传文件*/
export const uploadFile = (data?: object) => {
return http.request<ResultFile>("post", "/file", {
headers: {
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
},
data
});
};

View File

@@ -17,3 +17,10 @@ export type ResultPage = {
totalCount?: number; totalCount?: number;
}; };
}; };
export type ResultFile = {
status: number;
data?: Array<{
id: string;
}>;
};

View File

@@ -28,7 +28,7 @@ export const updatePostStatus = (id, state) => {
/** 删除岗位 */ /** 删除岗位 */
export const delPost = ids => { export const delPost = ids => {
return http.request<Result>("delete", `/post`, { params: { id:ids } }); return http.request<Result>("delete", `/post`, { params: { id: ids } });
}; };
/** 获取岗位选择框列表 */ /** 获取岗位选择框列表 */

View File

@@ -42,7 +42,7 @@ export const addUser = (data: any) => {
/** 查询用户个人信息 */ /** 查询用户个人信息 */
export const getUserProfile = () => { export const getUserProfile = () => {
return http.request<Result>("get", `/account`, {}); return http.request<UserInfoResult>("get", `/account`, {});
}; };
/** 修改用户个人信息 */ /** 修改用户个人信息 */
@@ -63,3 +63,29 @@ export const updateUserPwd = (oldPassword, newPassword) => {
}; };
return http.request<Result>("put", `/account/password`, { data }); return http.request<Result>("put", `/account/password`, { data });
}; };
export type UserInfoResult = {
status: number;
data: UserInfo;
};
export type UserInfo = {
user: {
/** 头像 */
icon: string;
/** 用户名 */
userName: string;
/** 昵称 */
nick: string;
/** 邮箱 */
email: string;
/** 联系电话 */
phone: string;
/** 简介 */
introduction: string;
};
/** 当前登录用户的角色 */
roleCodes: Array<string>;
/** 按钮级别权限 */
permissions: Array<string>;
};

View File

@@ -55,40 +55,6 @@ export type RefreshTokenResult = {
}; };
}; };
export type UserInfo = {
/** 头像 */
avatar: string;
/** 用户名 */
username: string;
/** 昵称 */
nickname: string;
/** 邮箱 */
email: string;
/** 联系电话 */
phone: string;
/** 简介 */
description: string;
};
export type UserInfoResult = {
success: boolean;
data: UserInfo;
};
type ResultTable = {
success: boolean;
data?: {
/** 列表数据 */
list: Array<any>;
/** 总条目数 */
total?: number;
/** 每页显示条目个数 */
pageSize?: number;
/** 当前页数 */
currentPage?: number;
};
};
/** 登录 */ /** 登录 */
export const getLogin = (data?: object) => { export const getLogin = (data?: object) => {
return http.request<LoginResult>("post", "/account/login", { data }); return http.request<LoginResult>("post", "/account/login", { data });
@@ -107,13 +73,3 @@ export const getCodeImg = () => {
export const refreshTokenApi = (data?: object) => { export const refreshTokenApi = (data?: object) => {
return http.request<RefreshTokenResult>("post", "/refresh-token", { data }); return http.request<RefreshTokenResult>("post", "/refresh-token", { data });
}; };
/** 账户设置-个人信息 */
export const getMine = (data?: object) => {
return http.request<UserInfoResult>("get", "/mine", { data });
};
/** 账户设置-个人安全日志 */
export const getMineLogs = (data?: object) => {
return http.request<ResultTable>("get", "/mine-logs", { data });
};

View File

@@ -14,7 +14,7 @@ import { useUserStoreHook } from "@/store/modules/user";
import { useGlobal, isAllEmpty } from "@pureadmin/utils"; import { useGlobal, isAllEmpty } from "@pureadmin/utils";
import { useEpThemeStoreHook } from "@/store/modules/epTheme"; import { useEpThemeStoreHook } from "@/store/modules/epTheme";
import { usePermissionStoreHook } from "@/store/modules/permission"; import { usePermissionStoreHook } from "@/store/modules/permission";
import {getFileUrl} from "@/utils/file" import { getFileUrl } from "@/utils/file";
import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill"; import ExitFullscreen from "@iconify-icons/ri/fullscreen-exit-fill";
import Fullscreen from "@iconify-icons/ri/fullscreen-fill"; import Fullscreen from "@iconify-icons/ri/fullscreen-fill";
@@ -41,7 +41,7 @@ export function useNav() {
/** 头像(如果头像为空则使用 src/assets/user.jpg */ /** 头像(如果头像为空则使用 src/assets/user.jpg */
const userAvatar = computed(() => { const userAvatar = computed(() => {
return getFileUrl(useUserStoreHook()?.avatar,Avatar); return getFileUrl(useUserStoreHook()?.avatar, Avatar);
}); });
/** 昵称(如果昵称为空则显示用户名) */ /** 昵称(如果昵称为空则显示用户名) */

View File

@@ -1,4 +1,4 @@
import {isAllEmpty} from "@pureadmin/utils"; import { isAllEmpty } from "@pureadmin/utils";
export function getFileUrl(fileId: string, tryPath: string): string { export function getFileUrl(fileId: string, tryPath: string): string {
if (isAllEmpty(fileId)) { if (isAllEmpty(fileId)) {

View File

@@ -3,17 +3,13 @@ import Axios, {
type AxiosRequestConfig, type AxiosRequestConfig,
type CustomParamsSerializer type CustomParamsSerializer
} from "axios"; } from "axios";
import type { import type { RequestMethods, PureHttpRequestConfig } from "./types.d";
PureHttpError,
RequestMethods,
PureHttpRequestConfig
} from "./types.d";
import { stringify } from "qs"; import { stringify } from "qs";
import NProgress from "../progress"; import NProgress from "../progress";
import { getToken, formatToken } from "@/utils/auth"; import { getToken, formatToken } from "@/utils/auth";
import { useUserStoreHook } from "@/store/modules/user"; import { useUserStoreHook } from "@/store/modules/user";
import {message} from "@/utils/message"; import { message } from "@/utils/message";
import { transformI18n } from "@/plugins/i18n"; // import { transformI18n } from "@/plugins/i18n";
// 相关配置请参考www.axios-js.com/zh-cn/docs/#axios-request-config-1 // 相关配置请参考www.axios-js.com/zh-cn/docs/#axios-request-config-1
const defaultConfig: AxiosRequestConfig = { const defaultConfig: AxiosRequestConfig = {
baseURL: import.meta.env.VITE_APP_BASE_API, baseURL: import.meta.env.VITE_APP_BASE_API,
@@ -142,7 +138,7 @@ class PureHttp {
// 关闭进度条动画 // 关闭进度条动画
NProgress.done(); NProgress.done();
// 所有的响应异常 区分来源为取消请求/非取消请求 // 所有的响应异常 区分来源为取消请求/非取消请求
return Promise.reject($error); return Promise.reject($error);
} }
); );
} }

View File

@@ -12,27 +12,30 @@ const list = ref([
title: "账户密码", title: "账户密码",
illustrate: "当前密码强度:强", illustrate: "当前密码强度:强",
button: "修改" button: "修改"
},
{
title: "密保手机",
illustrate: "已经绑定手机158****6789",
button: "修改"
},
{
title: "密保问题",
illustrate: "未设置密保问题,密保问题可有效保护账户安全",
button: "修改"
},
{
title: "备用邮箱",
illustrate: "已绑定邮箱pure***@163.com",
button: "修改"
} }
// {
// title: "密保手机",
// illustrate: "已经绑定手机158****6789",
// button: "修改"
// },
// {
// title: "密保问题",
// illustrate: "未设置密保问题,密保问题可有效保护账户安全",
// button: "修改"
// },
// {
// title: "备用邮箱",
// illustrate: "已绑定邮箱pure***@163.com",
// button: "修改"
// }
]); ]);
function onClick(item) { function onClick(item) {
console.log("onClick", item.title); switch (item.title) {
message("请根据具体业务自行实现", { type: "success" }); case "账户密码":
message("密码更改成功", { type: "success" });
break;
}
} }
</script> </script>

View File

@@ -2,11 +2,19 @@
import { reactive, ref } from "vue"; import { reactive, ref } from "vue";
import { formUpload } from "@/api/mock"; import { formUpload } from "@/api/mock";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { type UserInfo, getMine } from "@/api/user";
import type { FormInstance, FormRules } from "element-plus"; import type { FormInstance, FormRules } from "element-plus";
import ReCropperPreview from "@/components/ReCropperPreview"; import ReCropperPreview from "@/components/ReCropperPreview";
import { createFormData, deviceDetection } from "@pureadmin/utils"; import { createFormData, deviceDetection } from "@pureadmin/utils";
import uploadLine from "@iconify-icons/ri/upload-line"; import uploadLine from "@iconify-icons/ri/upload-line";
import { useUserStoreHook } from "@/store/modules/user";
import {
getUserProfile,
updateUserIcon,
updateUserProfile
} from "@/api/system/user";
import userAvatar from "@/assets/user.jpg";
import { getFileUrl } from "@/utils/file";
import { uploadFile } from "@/api/file";
defineOptions({ defineOptions({
name: "Profile" name: "Profile"
@@ -20,15 +28,16 @@ const isShow = ref(false);
const userInfoFormRef = ref<FormInstance>(); const userInfoFormRef = ref<FormInstance>();
const userInfos = reactive({ const userInfos = reactive({
avatar: "", icon: "",
nickname: "", nick: "",
email: "", email: "",
phone: "", phone: "",
description: "" introduction: ""
}); });
const rules = reactive<FormRules<UserInfo>>({ const rules = reactive<FormRules>({
nickname: [{ required: true, message: "昵称必填", trigger: "blur" }] userName: [{ required: true, message: "用户名必填", trigger: "blur" }],
nick: [{ required: true, message: "昵称必填", trigger: "blur" }]
}); });
function queryEmail(queryString, callback) { function queryEmail(queryString, callback) {
@@ -70,11 +79,13 @@ const onCropper = ({ blob }) => (cropperBlob.value = blob);
const handleSubmitImage = () => { const handleSubmitImage = () => {
const formData = createFormData({ const formData = createFormData({
files: new File([cropperBlob.value], "avatar") files: new File([cropperBlob.value], "file")
}); });
formUpload(formData) uploadFile(formData)
.then(({ success, data }) => { .then(async ({ status, data }) => {
if (success) { if (status == 200) {
await updateUserIcon(data[0].id);
useUserStoreHook().SET_AVATAR(data[0].id);
message("更新头像成功", { type: "success" }); message("更新头像成功", { type: "success" });
handleClose(); handleClose();
} else { } else {
@@ -88,18 +99,17 @@ const handleSubmitImage = () => {
// 更新信息 // 更新信息
const onSubmit = async (formEl: FormInstance) => { const onSubmit = async (formEl: FormInstance) => {
await formEl.validate((valid, fields) => { await formEl.validate(async (valid, fields) => {
if (valid) { if (valid) {
console.log(userInfos); await updateUserProfile(userInfos);
message("更新信息成功", { type: "success" }); message("更新信息成功", { type: "success" });
} else { } else {
console.log("error submit!", fields); console.log("error submit!", fields);
} }
}); });
}; };
getUserProfile().then(res => {
getMine().then(res => { Object.assign(userInfos, res.data.user);
Object.assign(userInfos, res.data);
}); });
</script> </script>
@@ -118,7 +128,7 @@ getMine().then(res => {
:model="userInfos" :model="userInfos"
> >
<el-form-item label="头像"> <el-form-item label="头像">
<el-avatar :size="80" :src="userInfos.avatar" /> <el-avatar :size="80" :src="getFileUrl(userInfos.icon, userAvatar)" />
<el-upload <el-upload
ref="uploadRef" ref="uploadRef"
accept="image/*" accept="image/*"
@@ -135,7 +145,7 @@ getMine().then(res => {
</el-upload> </el-upload>
</el-form-item> </el-form-item>
<el-form-item label="昵称" prop="nickname"> <el-form-item label="昵称" prop="nickname">
<el-input v-model="userInfos.nickname" placeholder="请输入昵称" /> <el-input v-model="userInfos.nick" placeholder="请输入昵称" />
</el-form-item> </el-form-item>
<el-form-item label="邮箱" prop="email"> <el-form-item label="邮箱" prop="email">
<el-autocomplete <el-autocomplete
@@ -156,7 +166,7 @@ getMine().then(res => {
</el-form-item> </el-form-item>
<el-form-item label="简介"> <el-form-item label="简介">
<el-input <el-input
v-model="userInfos.description" v-model="userInfos.introduction"
placeholder="请输入简介" placeholder="请输入简介"
type="textarea" type="textarea"
:autosize="{ minRows: 6, maxRows: 8 }" :autosize="{ minRows: 6, maxRows: 8 }"

View File

@@ -1,6 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import dayjs from "dayjs"; import dayjs from "dayjs";
import { getMineLogs } from "@/api/user"; // import { getMineLogs } from "@/api/user";
import { reactive, ref, onMounted } from "vue"; import { reactive, ref, onMounted } from "vue";
import { deviceDetection } from "@pureadmin/utils"; import { deviceDetection } from "@pureadmin/utils";
import type { PaginationProps } from "@pureadmin/table"; import type { PaginationProps } from "@pureadmin/table";
@@ -55,11 +55,11 @@ const columns: TableColumnList = [
async function onSearch() { async function onSearch() {
loading.value = true; loading.value = true;
const { data } = await getMineLogs(); // const { data } = await getMineLogs();
dataList.value = data.list; // dataList.value = data.list;
pagination.total = data.total; // pagination.total = data.total;
pagination.pageSize = data.pageSize; // pagination.pageSize = data.pageSize;
pagination.currentPage = data.currentPage; // pagination.currentPage = data.currentPage;
setTimeout(() => { setTimeout(() => {
loading.value = false; loading.value = false;

View File

@@ -1,5 +1,4 @@
<script setup lang="ts"> <script setup lang="ts">
import { getMine } from "@/api/user";
import { useRouter } from "vue-router"; import { useRouter } from "vue-router";
import { ref, onBeforeMount } from "vue"; import { ref, onBeforeMount } from "vue";
import { ReText } from "@/components/ReText"; import { ReText } from "@/components/ReText";
@@ -11,11 +10,14 @@ import AccountManagement from "./components/AccountManagement.vue";
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange"; import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
import LaySidebarTopCollapse from "@/layout/components/lay-sidebar/components/SidebarTopCollapse.vue"; import LaySidebarTopCollapse from "@/layout/components/lay-sidebar/components/SidebarTopCollapse.vue";
import userAvatar from "@/assets/user.jpg";
import { getFileUrl } from "@/utils/file";
import leftLine from "@iconify-icons/ri/arrow-left-s-line"; import leftLine from "@iconify-icons/ri/arrow-left-s-line";
import ProfileIcon from "@iconify-icons/ri/user-3-line"; import ProfileIcon from "@iconify-icons/ri/user-3-line";
import PreferencesIcon from "@iconify-icons/ri/settings-3-line"; import PreferencesIcon from "@iconify-icons/ri/settings-3-line";
import SecurityLogIcon from "@iconify-icons/ri/window-line"; import SecurityLogIcon from "@iconify-icons/ri/window-line";
import AccountManagementIcon from "@iconify-icons/ri/profile-line"; import AccountManagementIcon from "@iconify-icons/ri/profile-line";
import { getUserProfile } from "@/api/system/user";
defineOptions({ defineOptions({
name: "AccountSettings" name: "AccountSettings"
@@ -29,7 +31,7 @@ onBeforeMount(() => {
}); });
const userInfo = ref({ const userInfo = ref({
avatar: "", icon: "",
userName: "", userName: "",
nick: "" nick: ""
}); });
@@ -40,12 +42,12 @@ const panes = [
icon: ProfileIcon, icon: ProfileIcon,
component: Profile component: Profile
}, },
{ // {
key: "preferences", // key: "preferences",
label: "偏好设置", // label: "偏好设置",
icon: PreferencesIcon, // icon: PreferencesIcon,
component: Preferences // component: Preferences
}, // },
{ {
key: "securityLog", key: "securityLog",
label: "安全日志", label: "安全日志",
@@ -61,8 +63,8 @@ const panes = [
]; ];
const witchPane = ref("profile"); const witchPane = ref("profile");
getMine().then(res => { getUserProfile().then(res => {
userInfo.value = res.data; userInfo.value = res.data.user;
}); });
</script> </script>
@@ -84,13 +86,13 @@ getMine().then(res => {
</div> </div>
</el-menu-item> </el-menu-item>
<div class="flex items-center ml-8 mt-4 mb-4"> <div class="flex items-center ml-8 mt-4 mb-4">
<el-avatar :size="48" :src="userInfo.avatar" /> <el-avatar :size="48" :src="getFileUrl(userInfo.icon, userAvatar)" />
<div class="ml-4 flex flex-col max-w-[130px]"> <div class="ml-4 flex flex-col max-w-[130px]">
<ReText class="font-bold !self-baseline"> <ReText class="font-bold !self-baseline">
{{ userInfo.nickname }} {{ userInfo.nick }}
</ReText> </ReText>
<ReText class="!self-baseline" type="info"> <ReText class="!self-baseline" type="info">
{{ userInfo.username }} {{ userInfo.nick }}
</ReText> </ReText>
</div> </div>
</div> </div>

View File

@@ -248,7 +248,7 @@ getCode();
:src="codeUrl" :src="codeUrl"
width="120" width="120"
height="40" height="40"
style="width: 120px; height: 40px;max-width: none; " style="width: 120px; height: 40px; max-width: none"
class="cursor-pointer" class="cursor-pointer"
alt="" alt=""
@click="getCode" @click="getCode"

View File

@@ -66,7 +66,7 @@ export function useRole(tableRef: Ref) {
label: "登录状态", label: "登录状态",
prop: "state", prop: "state",
minWidth: 100, minWidth: 100,
cellRenderer: ({ row, props }) => ( cellRenderer: ({ props }) => (
<el-tag size={props.size} style={tagStyle.value(1)}> <el-tag size={props.size} style={tagStyle.value(1)}>
{1 === 1 ? "成功" : "失败"} {1 === 1 ? "成功" : "失败"}
</el-tag> </el-tag>

View File

@@ -85,11 +85,7 @@ const {
</el-form-item> </el-form-item>
</el-form> </el-form>
<PureTableBar <PureTableBar title="登录日志" :columns="columns" @refresh="onSearch">
title="登录日志"
:columns="columns"
@refresh="onSearch"
>
<template #buttons> <template #buttons>
<el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll"> <el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll">
<template #reference> <template #reference>

View File

@@ -73,7 +73,7 @@ export function useRole(tableRef: Ref) {
label: "操作状态", label: "操作状态",
prop: "status", prop: "status",
minWidth: 100, minWidth: 100,
cellRenderer: ({ row, props }) => ( cellRenderer: ({ props }) => (
<el-tag size={props.size} style={tagStyle.value(1)}> <el-tag size={props.size} style={tagStyle.value(1)}>
{1 === 1 ? "成功" : "失败"} {1 === 1 ? "成功" : "失败"}
</el-tag> </el-tag>
@@ -81,10 +81,10 @@ export function useRole(tableRef: Ref) {
}, },
{ {
label: "操作时间", label: "操作时间",
prop: "operatingTime", prop: "creationTime",
minWidth: 180, minWidth: 180,
formatter: ({ operatingTime }) => formatter: ({ creationTime }) =>
dayjs(operatingTime).format("YYYY-MM-DD HH:mm:ss") dayjs(creationTime).format("YYYY-MM-DD HH:mm:ss")
}, },
{ {
label: "操作", label: "操作",

View File

@@ -18,7 +18,6 @@ const tableRef = ref();
const { const {
form, form,
timeQuery,
loading, loading,
columns, columns,
dataList, dataList,

View File

@@ -1,9 +1,8 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import { getOnlineLogsList } from "@/api/system";
import { reactive, ref, onMounted, toRaw } from "vue"; import { reactive, ref, onMounted, toRaw } from "vue";
import type { PaginationProps } from "@pureadmin/table"; import type { PaginationProps } from "@pureadmin/table";
import {forceLogout, getOnlineList} from "@/api/monitor/online"; import { forceLogout, getOnlineList } from "@/api/monitor/online";
export function useRole() { export function useRole() {
const form = reactive({ const form = reactive({

View File

@@ -59,7 +59,6 @@ defineExpose({ getRef });
<Segmented <Segmented
v-model="newFormInline.menuType" v-model="newFormInline.menuType"
:options="menuTypeOptions" :options="menuTypeOptions"
@change="changeSegmented"
/> />
</el-form-item> </el-form-item>
</re-col> </re-col>

View File

@@ -1,7 +1,13 @@
import editForm from "../form.vue"; import editForm from "../form.vue";
import { handleTree } from "@/utils/tree"; import { handleTree } from "@/utils/tree";
import { message } from "@/utils/message"; import { message } from "@/utils/message";
import {addMenu, delMenu, getListMenu, getMenu, updateMenu} from "@/api/system/menu"; import {
addMenu,
delMenu,
getListMenu,
getMenu,
updateMenu
} from "@/api/system/menu";
import { transformI18n } from "@/plugins/i18n"; import { transformI18n } from "@/plugins/i18n";
import { addDialog } from "@/components/ReDialog"; import { addDialog } from "@/components/ReDialog";
import { reactive, ref, onMounted, h } from "vue"; import { reactive, ref, onMounted, h } from "vue";

View File

@@ -48,27 +48,15 @@ const treeHeight = ref();
const { const {
form, form,
isShow, isShow,
curRow,
loading, loading,
columns, columns,
rowStyle, rowStyle,
dataList, dataList,
treeData,
treeProps,
isLinkage,
pagination, pagination,
isExpandAll,
isSelectAll,
treeSearchValue,
onSearch, onSearch,
resetForm, resetForm,
openDialog, openDialog,
handleMenu,
handleSave,
handleDelete, handleDelete,
filterMethod,
transformI18n,
onQueryChanged,
handleSizeChange, handleSizeChange,
handleCurrentChange, handleCurrentChange,
handleSelectionChange handleSelectionChange
@@ -209,69 +197,6 @@ onMounted(() => {
</pure-table> </pure-table>
</template> </template>
</PureTableBar> </PureTableBar>
<div
v-if="isShow"
class="!min-w-[calc(100vw-60vw-268px)] w-full mt-2 px-2 pb-2 bg-bg_color ml-2 overflow-auto"
>
<div class="flex justify-between w-full px-3 pt-5 pb-4">
<div class="flex">
<span :class="iconClass">
<IconifyIconOffline
v-tippy="{
content: '关闭'
}"
class="dark:text-white"
width="18px"
height="18px"
:icon="Close"
@click="handleMenu"
/>
</span>
<span :class="[iconClass, 'ml-2']">
<IconifyIconOffline
v-tippy="{
content: '保存菜单权限'
}"
class="dark:text-white"
width="18px"
height="18px"
:icon="Check"
@click="handleSave"
/>
</span>
</div>
<p class="font-bold truncate">
菜单权限
{{ `${curRow?.name ? `${curRow.name}` : ""}` }}
</p>
</div>
<el-input
v-model="treeSearchValue"
placeholder="请输入菜单进行搜索"
class="mb-1"
clearable
@input="onQueryChanged"
/>
<div class="flex flex-wrap">
<el-checkbox v-model="isExpandAll" label="展开/折叠" />
<el-checkbox v-model="isSelectAll" label="全选/全不选" />
<el-checkbox v-model="isLinkage" label="父子联动" />
</div>
<el-tree-v2
ref="treeRef"
show-checkbox
:data="treeData"
:props="treeProps"
:height="treeHeight"
:check-strictly="!isLinkage"
:filter-method="filterMethod"
>
<template #default="{ node }">
<span>{{ transformI18n(node.label) }}</span>
</template>
</el-tree-v2>
</div>
</div> </div>
</div> </div>
</template> </template>

View File

@@ -1,13 +1,13 @@
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 {ElMessageBox} from "element-plus"; import { ElMessageBox } from "element-plus";
import {usePublicHooks} from "../../hooks"; import { usePublicHooks } from "../../hooks";
import {transformI18n} from "@/plugins/i18n"; import { transformI18n } from "@/plugins/i18n";
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 {getKeyList, deviceDetection} from "@pureadmin/utils"; import { deviceDetection } from "@pureadmin/utils";
import { import {
getPostList, getPostList,
addPost, addPost,
@@ -15,20 +15,11 @@ import {
delPost, delPost,
getPost, getPost,
updatePostStatus updatePostStatus
} from "@/api/system/post" } from "@/api/system/post";
import { import { reactive, ref, onMounted, h, toRaw } from "vue";
type Ref,
reactive,
ref,
onMounted,
h,
toRaw,
watch,
nextTick
} from "vue";
export function usePost(treeRef: Ref) { export function usePost() {
const form = reactive({ const form = reactive({
postName: "", postName: "",
postCode: "", postCode: "",
@@ -39,7 +30,6 @@ export function usePost(treeRef: Ref) {
const curRow = ref(); const curRow = ref();
const formRef = ref(); const formRef = ref();
const dataList = ref([]); const dataList = ref([]);
const treeIds = ref([]);
const treeData = ref([]); const treeData = ref([]);
const isShow = ref(false); const isShow = ref(false);
const loading = ref(true); const loading = ref(true);
@@ -48,7 +38,7 @@ export function usePost(treeRef: Ref) {
const switchLoadMap = ref({}); const switchLoadMap = ref({});
const isExpandAll = ref(false); const isExpandAll = ref(false);
const isSelectAll = ref(false); const isSelectAll = ref(false);
const {switchStyle} = usePublicHooks(); const { switchStyle } = usePublicHooks();
const pagination = reactive<PaginationProps>({ const pagination = reactive<PaginationProps>({
total: 0, total: 0,
pageSize: 10, pageSize: 10,
@@ -86,7 +76,7 @@ export function usePost(treeRef: Ref) {
), ),
minWidth: 90 minWidth: 90
}, },
{label:"排序",prop:"orderNum"}, { label: "排序", prop: "orderNum" },
{ {
label: "备注", label: "备注",
prop: "remark", prop: "remark",
@@ -96,7 +86,7 @@ export function usePost(treeRef: Ref) {
label: "创建时间", label: "创建时间",
prop: "creationTime", prop: "creationTime",
minWidth: 160, minWidth: 160,
formatter: ({creationTime}) => formatter: ({ creationTime }) =>
dayjs(creationTime).format("YYYY-MM-DD HH:mm:ss") dayjs(creationTime).format("YYYY-MM-DD HH:mm:ss")
}, },
{ {
@@ -107,7 +97,7 @@ export function usePost(treeRef: Ref) {
} }
]; ];
async function onChange({row, index}) { async function onChange({ row, index }) {
ElMessageBox.confirm( ElMessageBox.confirm(
`确认要<strong>${ `确认要<strong>${
row.state === false ? "停用" : "启用" row.state === false ? "停用" : "启用"
@@ -152,7 +142,7 @@ export function usePost(treeRef: Ref) {
async function handleDelete(row) { async function handleDelete(row) {
await delPost([row.id]); await delPost([row.id]);
message(`您删除了角色名称为${row.roleName}的这条数据`, {type: "success"}); message(`您删除了角色名称为${row.roleName}的这条数据`, { type: "success" });
onSearch(); onSearch();
} }
@@ -172,7 +162,7 @@ export function usePost(treeRef: Ref) {
async function onSearch() { async function onSearch() {
loading.value = true; loading.value = true;
const {data} = await getPostList(toRaw(form)); const { data } = await getPostList(toRaw(form));
dataList.value = data.items; dataList.value = data.items;
pagination.total = data.totalCount; pagination.total = data.totalCount;
loading.value = false; loading.value = false;
@@ -196,7 +186,7 @@ export function usePost(treeRef: Ref) {
postName: row?.postName ?? "", postName: row?.postName ?? "",
postCode: row?.postCode ?? "", postCode: row?.postCode ?? "",
remark: row?.remark ?? "", remark: row?.remark ?? "",
orderNum: data?.orderNum ?? 0, orderNum: data?.orderNum ?? 0
} }
}, },
width: "40%", width: "40%",
@@ -204,8 +194,8 @@ export function usePost(treeRef: Ref) {
fullscreen: deviceDetection(), fullscreen: deviceDetection(),
fullscreenIcon: true, fullscreenIcon: true,
closeOnClickModal: false, closeOnClickModal: false,
contentRenderer: () => h(editForm, {ref: formRef}), contentRenderer: () => h(editForm, { ref: formRef }),
beforeSure: (done, {options}) => { beforeSure: (done, { options }) => {
const FormRef = formRef.value.getRef(); const FormRef = formRef.value.getRef();
const curData = options.props.formInline as FormItemProps; const curData = options.props.formInline as FormItemProps;
@@ -236,7 +226,7 @@ export function usePost(treeRef: Ref) {
} }
/** 高亮当前权限选中行 */ /** 高亮当前权限选中行 */
function rowStyle({row: {id}}) { function rowStyle({ row: { id } }) {
return { return {
cursor: "pointer", cursor: "pointer",
background: id === curRow.value?.id ? "var(--el-fill-color-light)" : "" background: id === curRow.value?.id ? "var(--el-fill-color-light)" : ""
@@ -244,11 +234,7 @@ export function usePost(treeRef: Ref) {
} }
/** 数据权限 可自行开发 */ /** 数据权限 可自行开发 */
// function handleDatabase() {} // function handleDatabase() {}
const onQueryChanged = (query: string) => {
treeRef.value!.filter(query);
};
const filterMethod = (query: string, node) => { const filterMethod = (query: string, node) => {
return transformI18n(node.title)!.includes(query); return transformI18n(node.title)!.includes(query);
@@ -258,18 +244,6 @@ export function usePost(treeRef: Ref) {
onSearch(); onSearch();
}); });
watch(isExpandAll, val => {
val
? treeRef.value.setExpandedKeys(treeIds.value)
: treeRef.value.setExpandedKeys([]);
});
watch(isSelectAll, val => {
val
? treeRef.value.setCheckedKeys(treeIds.value)
: treeRef.value.setCheckedKeys([]);
});
return { return {
form, form,
isShow, isShow,
@@ -290,7 +264,6 @@ export function usePost(treeRef: Ref) {
handleDelete, handleDelete,
filterMethod, filterMethod,
transformI18n, transformI18n,
onQueryChanged,
handleSizeChange, handleSizeChange,
handleCurrentChange, handleCurrentChange,
handleSelectionChange handleSelectionChange

View File

@@ -9,7 +9,6 @@ interface FormItemProps {
/** 备注 */ /** 备注 */
remark: string; remark: string;
orderNum: number; orderNum: number;
} }
interface FormProps { interface FormProps {
formInline: FormItemProps; formInline: FormItemProps;

View File

@@ -16,8 +16,7 @@ import {
updateRole, updateRole,
changeRoleStatus, changeRoleStatus,
delRole, delRole,
getRoleMenuSelect, getRoleMenuSelect
updataDataScope
} from "@/api/system/role"; } from "@/api/system/role";
import { getMenuOption } from "@/api/system/menu"; import { getMenuOption } from "@/api/system/menu";

View File

@@ -6,7 +6,6 @@ import { PureTableBar } from "@/components/RePureTableBar";
import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { useRenderIcon } from "@/components/ReIcon/src/hooks";
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 Password from "@iconify-icons/ri/lock-password-line"; import Password from "@iconify-icons/ri/lock-password-line";
import More from "@iconify-icons/ep/more-filled"; import More from "@iconify-icons/ep/more-filled";
import Delete from "@iconify-icons/ep/delete"; import Delete from "@iconify-icons/ep/delete";

View File

@@ -1,16 +1,16 @@
import "./reset.css"; import "./reset.css";
import dayjs from "dayjs"; import dayjs from "dayjs";
import editForm from "../form/index.vue"; import editForm from "../form/index.vue";
import {zxcvbn} from "@zxcvbn-ts/core"; import { zxcvbn } from "@zxcvbn-ts/core";
import {handleTree} from "@/utils/tree"; import { handleTree } from "@/utils/tree";
import {message} from "@/utils/message"; import { message } from "@/utils/message";
import userAvatar from "@/assets/user.jpg"; import userAvatar from "@/assets/user.jpg";
import {getFileUrl} from "@/utils/file" import { getFileUrl } from "@/utils/file";
import {usePublicHooks} from "../../hooks"; 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 ReCropperPreview from "@/components/ReCropperPreview"; import ReCropperPreview from "@/components/ReCropperPreview";
import type {FormItemProps} from "../utils/types"; import type { FormItemProps } from "../utils/types";
import { import {
getKeyList, getKeyList,
isAllEmpty, isAllEmpty,
@@ -26,8 +26,8 @@ import {
updateUser, updateUser,
getUserList getUserList
} from "@/api/system/user"; } from "@/api/system/user";
import {getRoleOption} from "@/api/system/role"; import { getRoleOption } from "@/api/system/role";
import {getDeptList} from "@/api/system/dept"; import { getDeptList } from "@/api/system/dept";
import { import {
ElForm, ElForm,
ElInput, ElInput,
@@ -63,7 +63,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
// 上传头像信息 // 上传头像信息
const avatarInfo = ref(); const avatarInfo = ref();
const switchLoadMap = ref({}); const switchLoadMap = ref({});
const {switchStyle} = usePublicHooks(); const { switchStyle } = usePublicHooks();
const higherDeptOptions = ref(); const higherDeptOptions = ref();
const treeData = ref([]); const treeData = ref([]);
const treeLoading = ref(true); const treeLoading = ref(true);
@@ -89,12 +89,12 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
{ {
label: "用户头像", label: "用户头像",
prop: "avatar", prop: "avatar",
cellRenderer: ({row}) => ( cellRenderer: ({ row }) => (
<el-image <el-image
fit="cover" fit="cover"
preview-teleported={true} preview-teleported={true}
src={getFileUrl(row.avatar, userAvatar)} src={getFileUrl(row.avatar, userAvatar)}
preview-src-list={Array.of(getFileUrl(row.avatar , userAvatar))} preview-src-list={Array.of(getFileUrl(row.avatar, userAvatar))}
class="w-[24px] h-[24px] rounded-full align-middle" class="w-[24px] h-[24px] rounded-full align-middle"
/> />
), ),
@@ -114,7 +114,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
label: "性别", label: "性别",
prop: "sex", prop: "sex",
minWidth: 90, minWidth: 90,
cellRenderer: ({row, props}) => ( cellRenderer: ({ row, props }) => (
<el-tag <el-tag
size={props.size} size={props.size}
type={row.sex === "Woman" ? "danger" : null} type={row.sex === "Woman" ? "danger" : null}
@@ -133,8 +133,8 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
label: "手机号码", label: "手机号码",
prop: "phone", prop: "phone",
minWidth: 90, minWidth: 90,
formatter: ({phone}) => formatter: ({ phone }) =>
phone == null ? "-" : hideTextAtIndex(phone, {start: 3, end: 6}) phone == null ? "-" : hideTextAtIndex(phone, { start: 3, end: 6 })
}, },
{ {
label: "状态", label: "状态",
@@ -159,7 +159,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
label: "创建时间", label: "创建时间",
minWidth: 90, minWidth: 90,
prop: "creationTime", prop: "creationTime",
formatter: ({creationTime}) => formatter: ({ creationTime }) =>
dayjs(creationTime).format("YYYY-MM-DD HH:mm:ss") dayjs(creationTime).format("YYYY-MM-DD HH:mm:ss")
}, },
{ {
@@ -183,17 +183,17 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
password: "" password: ""
}); });
const pwdProgress = [ const pwdProgress = [
{color: "#e74242", text: "非常弱"}, { color: "#e74242", text: "非常弱" },
{color: "#EFBD47", text: "弱"}, { color: "#EFBD47", text: "弱" },
{color: "#ffa500", text: "一般"}, { color: "#ffa500", text: "一般" },
{color: "#1bbf1b", text: "强"}, { color: "#1bbf1b", text: "强" },
{color: "#008000", text: "非常强"} { color: "#008000", text: "非常强" }
]; ];
// 当前密码强度0-4 // 当前密码强度0-4
const curScore = ref(); const curScore = ref();
const roleOptions = ref([]); const roleOptions = ref([]);
function onChange({row, index}) { function onChange({ row, index }) {
ElMessageBox.confirm( ElMessageBox.confirm(
`确认要<strong>${ `确认要<strong>${
row.state === 0 ? "停用" : "启用" row.state === 0 ? "停用" : "启用"
@@ -242,7 +242,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
async function handleDelete(row) { async function handleDelete(row) {
await delUser([row.id]); await delUser([row.id]);
message(`您删除了用户编号为${row.id}的这条数据`, {type: "success"}); message(`您删除了用户编号为${row.id}的这条数据`, { type: "success" });
onSearch(); onSearch();
} }
@@ -286,7 +286,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
async function onSearch() { async function onSearch() {
loading.value = true; loading.value = true;
const {data} = await getUserList(toRaw(form)); const { data } = await getUserList(toRaw(form));
dataList.value = data.items; dataList.value = data.items;
pagination.total = data.totalCount; pagination.total = data.totalCount;
// pagination.pageSize = data.pageSize; // pagination.pageSize = data.pageSize;
@@ -305,7 +305,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
onSearch(); onSearch();
}; };
function onTreeSelect({id, selected}) { function onTreeSelect({ id, selected }) {
form.deptId = selected ? id : ""; form.deptId = selected ? id : "";
onSearch(); onSearch();
} }
@@ -353,8 +353,8 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
fullscreen: deviceDetection(), fullscreen: deviceDetection(),
fullscreenIcon: true, fullscreenIcon: true,
closeOnClickModal: false, closeOnClickModal: false,
contentRenderer: () => h(editForm, {ref: formRef}), contentRenderer: () => h(editForm, { ref: formRef }),
beforeSure: (done, {options}) => { beforeSure: (done, { options }) => {
const FormRef = formRef.value.getRef(); const FormRef = formRef.value.getRef();
const curData = options.props.formInline as FormItemProps; const curData = options.props.formInline as FormItemProps;
@@ -411,7 +411,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
watch( watch(
pwdForm, pwdForm,
({password}) => ({ password }) =>
(curScore.value = isAllEmpty(password) ? -1 : zxcvbn(password).score) (curScore.value = isAllEmpty(password) ? -1 : zxcvbn(password).score)
); );
@@ -446,10 +446,10 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
</ElFormItem> </ElFormItem>
</ElForm> </ElForm>
<div class="mt-4 flex"> <div class="mt-4 flex">
{pwdProgress.map(({color, text}, idx) => ( {pwdProgress.map(({ color, text }, idx) => (
<div <div
class="w-[19vw]" class="w-[19vw]"
style={{marginLeft: idx !== 0 ? "4px" : 0}} style={{ marginLeft: idx !== 0 ? "4px" : 0 }}
> >
<ElProgress <ElProgress
striped striped
@@ -462,7 +462,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
/> />
<p <p
class="text-center" class="text-center"
style={{color: curScore.value === idx ? color : ""}} style={{ color: curScore.value === idx ? color : "" }}
> >
{text} {text}
</p> </p>
@@ -494,7 +494,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
onSearch(); onSearch();
// 归属部门 // 归属部门
const {data} = await getDeptList(); const { data } = await getDeptList();
higherDeptOptions.value = handleTree(data.items); higherDeptOptions.value = handleTree(data.items);
treeData.value = handleTree(data.items); treeData.value = handleTree(data.items);
treeLoading.value = false; treeLoading.value = false;