feat: 整体pure,核心功能对接完成
This commit is contained in:
@@ -1,6 +1,8 @@
|
||||
using System.Text.RegularExpressions;
|
||||
using Lazy.Captcha.Core;
|
||||
using Mapster;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
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.Consts;
|
||||
using Yi.Framework.Rbac.Domain.Shared.Dtos;
|
||||
using Yi.Framework.Rbac.Domain.Shared.Etos;
|
||||
using Yi.Framework.Rbac.Domain.Shared.Options;
|
||||
using Yi.Framework.SqlSugarCore.Abstractions;
|
||||
|
||||
@@ -40,7 +43,7 @@ namespace Yi.Framework.Rbac.Application.Services
|
||||
private readonly IAliyunManger _aliyunManger;
|
||||
private IDistributedCache<UserInfoCacheItem, UserInfoCacheKey> _userCache;
|
||||
private UserManager _userManager;
|
||||
|
||||
private IHttpContextAccessor _httpContextAccessor;
|
||||
public AccountService(IUserRepository userRepository,
|
||||
ICurrentUser currentUser,
|
||||
IAccountManager accountManager,
|
||||
@@ -51,7 +54,7 @@ namespace Yi.Framework.Rbac.Application.Services
|
||||
IGuidGenerator guidGenerator,
|
||||
IOptions<RbacOptions> options,
|
||||
IAliyunManger aliyunManger,
|
||||
UserManager userManager)
|
||||
UserManager userManager, IHttpContextAccessor httpContextAccessor)
|
||||
{
|
||||
_userRepository = userRepository;
|
||||
_currentUser = currentUser;
|
||||
@@ -64,6 +67,7 @@ namespace Yi.Framework.Rbac.Application.Services
|
||||
_aliyunManger = aliyunManger;
|
||||
_userCache = userCache;
|
||||
_userManager = userManager;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
}
|
||||
|
||||
|
||||
@@ -109,10 +113,21 @@ namespace Yi.Framework.Rbac.Application.Services
|
||||
//校验
|
||||
await _accountManager.LoginValidationAsync(input.UserName, input.Password, x => user = x);
|
||||
|
||||
var userInfo = new UserRoleMenuDto();
|
||||
//获取token
|
||||
var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id);
|
||||
var accessToken = await _accountManager.GetTokenByUserIdAsync(user.Id, (info) => userInfo = info);
|
||||
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 };
|
||||
}
|
||||
|
||||
|
||||
@@ -34,13 +34,11 @@ namespace Yi.Framework.Rbac.Domain.Managers
|
||||
private readonly ILocalEventBus _localEventBus;
|
||||
private readonly JwtOptions _jwtOptions;
|
||||
private readonly RbacOptions _options;
|
||||
private IHttpContextAccessor _httpContextAccessor;
|
||||
private UserManager _userManager;
|
||||
private ISqlSugarRepository<RoleAggregateRoot> _roleRepository;
|
||||
private RefreshJwtOptions _refreshJwtOptions;
|
||||
|
||||
public AccountManager(IUserRepository repository
|
||||
, IHttpContextAccessor httpContextAccessor
|
||||
, IOptions<JwtOptions> jwtOptions
|
||||
, ILocalEventBus localEventBus
|
||||
, UserManager userManager
|
||||
@@ -49,7 +47,6 @@ namespace Yi.Framework.Rbac.Domain.Managers
|
||||
, IOptions<RbacOptions> options)
|
||||
{
|
||||
_repository = repository;
|
||||
_httpContextAccessor = httpContextAccessor;
|
||||
_jwtOptions = jwtOptions.Value;
|
||||
_localEventBus = localEventBus;
|
||||
_userManager = userManager;
|
||||
@@ -62,9 +59,10 @@ namespace Yi.Framework.Rbac.Domain.Managers
|
||||
/// 根据用户id获取token
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <param name="getUserInfo"></param>
|
||||
/// <returns></returns>
|
||||
/// <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);
|
||||
@@ -79,23 +77,18 @@ namespace Yi.Framework.Rbac.Domain.Managers
|
||||
{
|
||||
throw new UserFriendlyException(UserConst.No_Role);
|
||||
}
|
||||
if (userInfo.PermissionCodes.Count() == 0)
|
||||
if (!userInfo.PermissionCodes.Any())
|
||||
{
|
||||
throw new UserFriendlyException(UserConst.No_Permission);
|
||||
}
|
||||
//这里抛出一个登录的事件,也可以在全部流程走完,在应用层组装
|
||||
if (_httpContextAccessor.HttpContext is not null)
|
||||
|
||||
if (getUserInfo 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);
|
||||
getUserInfo(userInfo);
|
||||
}
|
||||
|
||||
var accessToken = CreateToken(this.UserInfoToClaim(userInfo));
|
||||
//将用户信息添加到缓存中,需要考虑的是更改了用户、角色、菜单等整个体系都需要将缓存进行刷新,看具体业务进行选择
|
||||
|
||||
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
|
||||
@@ -5,14 +5,15 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Volo.Abp.Domain.Services;
|
||||
using Yi.Framework.Rbac.Domain.Entities;
|
||||
using Yi.Framework.Rbac.Domain.Shared.Dtos;
|
||||
|
||||
namespace Yi.Framework.Rbac.Domain.Managers
|
||||
{
|
||||
public interface IAccountManager : IDomainService
|
||||
{
|
||||
string CreateRefreshToken(Guid userId);
|
||||
Task<string> GetTokenByUserIdAsync(Guid userId);
|
||||
Task LoginValidationAsync(string userName, string password, Action<UserAggregateRoot> userAction = null);
|
||||
Task<string> GetTokenByUserIdAsync(Guid userId,Action<UserRoleMenuDto>? getUserInfo=null);
|
||||
Task LoginValidationAsync(string userName, string password, Action<UserAggregateRoot>? userAction = null);
|
||||
Task RegisterAsync(string userName, string password, long phone);
|
||||
Task<bool> RestPasswordAsync(Guid userId, string password);
|
||||
Task UpdatePasswordAsync(Guid userId, string newPassword, string oldPassword);
|
||||
|
||||
@@ -162,14 +162,23 @@ namespace Yi.Framework.Rbac.Domain.Managers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 查询用户信息,已缓存
|
||||
/// 查询用户信息,取消缓存
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<UserRoleMenuDto> GetInfoAsync(Guid userId)
|
||||
{
|
||||
|
||||
var output = await GetInfoByCacheAsync(userId);
|
||||
return output;
|
||||
var user = await _userRepository.GetUserAllInfoAsync(userId);
|
||||
var data = EntityMapToDto(user);
|
||||
//系统用户数据被重置,老前端访问重新授权
|
||||
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)
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
// 模拟后端动态生成路由
|
||||
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"
|
||||
@@ -21,7 +21,7 @@ const systemManagementRouter = {
|
||||
name: "SystemUser",
|
||||
meta: {
|
||||
icon: "ri:admin-line",
|
||||
title: "menus.pureUser",
|
||||
title: "menus.pureUser"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
},
|
||||
@@ -30,7 +30,7 @@ const systemManagementRouter = {
|
||||
name: "SystemRole",
|
||||
meta: {
|
||||
icon: "ri:admin-fill",
|
||||
title: "menus.pureRole",
|
||||
title: "menus.pureRole"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
},
|
||||
@@ -39,7 +39,7 @@ const systemManagementRouter = {
|
||||
name: "SystemMenu",
|
||||
meta: {
|
||||
icon: "ep:menu",
|
||||
title: "menus.pureSystemMenu",
|
||||
title: "menus.pureSystemMenu"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
},
|
||||
@@ -48,7 +48,7 @@ const systemManagementRouter = {
|
||||
name: "SystemDept",
|
||||
meta: {
|
||||
icon: "ri:git-branch-line",
|
||||
title: "menus.pureDept",
|
||||
title: "menus.pureDept"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
},
|
||||
@@ -57,7 +57,7 @@ const systemManagementRouter = {
|
||||
name: "SystemPost",
|
||||
meta: {
|
||||
icon: "ant-design:deployment-unit-outlined",
|
||||
title: "menus.purePost",
|
||||
title: "menus.purePost"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,7 @@ const systemMonitorRouter = {
|
||||
name: "OnlineUser",
|
||||
meta: {
|
||||
icon: "ri:user-voice-line",
|
||||
title: "menus.pureOnlineUser",
|
||||
title: "menus.pureOnlineUser"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
},
|
||||
@@ -88,7 +88,7 @@ const systemMonitorRouter = {
|
||||
name: "LoginLog",
|
||||
meta: {
|
||||
icon: "ri:window-line",
|
||||
title: "menus.pureLoginLog",
|
||||
title: "menus.pureLoginLog"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
},
|
||||
@@ -98,7 +98,7 @@ const systemMonitorRouter = {
|
||||
name: "OperationLog",
|
||||
meta: {
|
||||
icon: "ri:history-fill",
|
||||
title: "menus.pureOperationLog",
|
||||
title: "menus.pureOperationLog"
|
||||
//roles: ["admin"]
|
||||
}
|
||||
}
|
||||
@@ -114,220 +114,220 @@ const systemMonitorRouter = {
|
||||
// }
|
||||
]
|
||||
};
|
||||
|
||||
const permissionRouter = {
|
||||
path: "/permission",
|
||||
meta: {
|
||||
title: "menus.purePermission",
|
||||
icon: "ep:lollipop",
|
||||
rank: permission
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/permission/page/index",
|
||||
name: "PermissionPage",
|
||||
meta: {
|
||||
title: "menus.purePermissionPage",
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/permission/button",
|
||||
meta: {
|
||||
title: "menus.purePermissionButton",
|
||||
//roles: ["admin", "common"]
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/permission/button/router",
|
||||
component: "permission/button/index",
|
||||
name: "PermissionButtonRouter",
|
||||
meta: {
|
||||
title: "menus.purePermissionButtonRouter",
|
||||
auths: [
|
||||
"permission:btn:add",
|
||||
"permission:btn:edit",
|
||||
"permission:btn:delete"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/permission/button/login",
|
||||
component: "permission/button/perms",
|
||||
name: "PermissionButtonLogin",
|
||||
meta: {
|
||||
title: "menus.purePermissionButtonLogin"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const frameRouter = {
|
||||
path: "/iframe",
|
||||
meta: {
|
||||
icon: "ri:links-fill",
|
||||
title: "menus.pureExternalPage",
|
||||
rank: frame
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/iframe/embedded",
|
||||
meta: {
|
||||
title: "menus.pureEmbeddedDoc"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/iframe/colorhunt",
|
||||
name: "FrameColorHunt",
|
||||
meta: {
|
||||
title: "menus.pureColorHuntDoc",
|
||||
frameSrc: "https://colorhunt.co/",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/iframe/uigradients",
|
||||
name: "FrameUiGradients",
|
||||
meta: {
|
||||
title: "menus.pureUiGradients",
|
||||
frameSrc: "https://uigradients.com/",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/iframe/ep",
|
||||
name: "FrameEp",
|
||||
meta: {
|
||||
title: "menus.pureEpDoc",
|
||||
frameSrc: "https://element-plus.org/zh-CN/",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/iframe/tailwindcss",
|
||||
name: "FrameTailwindcss",
|
||||
meta: {
|
||||
title: "menus.pureTailwindcssDoc",
|
||||
frameSrc: "https://tailwindcss.com/docs/installation",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/iframe/vue3",
|
||||
name: "FrameVue",
|
||||
meta: {
|
||||
title: "menus.pureVueDoc",
|
||||
frameSrc: "https://cn.vuejs.org/",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/iframe/vite",
|
||||
name: "FrameVite",
|
||||
meta: {
|
||||
title: "menus.pureViteDoc",
|
||||
frameSrc: "https://cn.vitejs.dev/",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/iframe/pinia",
|
||||
name: "FramePinia",
|
||||
meta: {
|
||||
title: "menus.purePiniaDoc",
|
||||
frameSrc: "https://pinia.vuejs.org/zh/index.html",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/iframe/vue-router",
|
||||
name: "FrameRouter",
|
||||
meta: {
|
||||
title: "menus.pureRouterDoc",
|
||||
frameSrc: "https://router.vuejs.org/zh/",
|
||||
keepAlive: true,
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: "/iframe/external",
|
||||
meta: {
|
||||
title: "menus.pureExternalDoc"
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/external",
|
||||
name: "https://pure-admin.github.io/pure-admin-doc",
|
||||
meta: {
|
||||
title: "menus.pureExternalLink",
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
{
|
||||
path: "/pureUtilsLink",
|
||||
name: "https://pure-admin-utils.netlify.app/",
|
||||
meta: {
|
||||
title: "menus.pureUtilsLink",
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const tabsRouter = {
|
||||
path: "/tabs",
|
||||
meta: {
|
||||
icon: "ri:bookmark-2-line",
|
||||
title: "menus.pureTabs",
|
||||
rank: tabs
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/tabs/index",
|
||||
name: "Tabs",
|
||||
meta: {
|
||||
title: "menus.pureTabs",
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
// query 传参模式
|
||||
{
|
||||
path: "/tabs/query-detail",
|
||||
name: "TabQueryDetail",
|
||||
meta: {
|
||||
// 不在menu菜单中显示
|
||||
showLink: false,
|
||||
activePath: "/tabs/index",
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
},
|
||||
// params 传参模式
|
||||
{
|
||||
path: "/tabs/params-detail/:id",
|
||||
component: "params-detail",
|
||||
name: "TabParamsDetail",
|
||||
meta: {
|
||||
// 不在menu菜单中显示
|
||||
showLink: false,
|
||||
activePath: "/tabs/index",
|
||||
//roles: ["admin", "common"]
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
//
|
||||
// const permissionRouter = {
|
||||
// path: "/permission",
|
||||
// meta: {
|
||||
// title: "menus.purePermission",
|
||||
// icon: "ep:lollipop",
|
||||
// rank: permission
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: "/permission/page/index",
|
||||
// name: "PermissionPage",
|
||||
// meta: {
|
||||
// title: "menus.purePermissionPage"
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/permission/button",
|
||||
// meta: {
|
||||
// title: "menus.purePermissionButton"
|
||||
// //roles: ["admin", "common"]
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: "/permission/button/router",
|
||||
// component: "permission/button/index",
|
||||
// name: "PermissionButtonRouter",
|
||||
// meta: {
|
||||
// title: "menus.purePermissionButtonRouter",
|
||||
// auths: [
|
||||
// "permission:btn:add",
|
||||
// "permission:btn:edit",
|
||||
// "permission:btn:delete"
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/permission/button/login",
|
||||
// component: "permission/button/perms",
|
||||
// name: "PermissionButtonLogin",
|
||||
// meta: {
|
||||
// title: "menus.purePermissionButtonLogin"
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// };
|
||||
//
|
||||
// const frameRouter = {
|
||||
// path: "/iframe",
|
||||
// meta: {
|
||||
// icon: "ri:links-fill",
|
||||
// title: "menus.pureExternalPage",
|
||||
// rank: frame
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: "/iframe/embedded",
|
||||
// meta: {
|
||||
// title: "menus.pureEmbeddedDoc"
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: "/iframe/colorhunt",
|
||||
// name: "FrameColorHunt",
|
||||
// meta: {
|
||||
// title: "menus.pureColorHuntDoc",
|
||||
// frameSrc: "https://colorhunt.co/",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/uigradients",
|
||||
// name: "FrameUiGradients",
|
||||
// meta: {
|
||||
// title: "menus.pureUiGradients",
|
||||
// frameSrc: "https://uigradients.com/",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/ep",
|
||||
// name: "FrameEp",
|
||||
// meta: {
|
||||
// title: "menus.pureEpDoc",
|
||||
// frameSrc: "https://element-plus.org/zh-CN/",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/tailwindcss",
|
||||
// name: "FrameTailwindcss",
|
||||
// meta: {
|
||||
// title: "menus.pureTailwindcssDoc",
|
||||
// frameSrc: "https://tailwindcss.com/docs/installation",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/vue3",
|
||||
// name: "FrameVue",
|
||||
// meta: {
|
||||
// title: "menus.pureVueDoc",
|
||||
// frameSrc: "https://cn.vuejs.org/",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/vite",
|
||||
// name: "FrameVite",
|
||||
// meta: {
|
||||
// title: "menus.pureViteDoc",
|
||||
// frameSrc: "https://cn.vitejs.dev/",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/pinia",
|
||||
// name: "FramePinia",
|
||||
// meta: {
|
||||
// title: "menus.purePiniaDoc",
|
||||
// frameSrc: "https://pinia.vuejs.org/zh/index.html",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/vue-router",
|
||||
// name: "FrameRouter",
|
||||
// meta: {
|
||||
// title: "menus.pureRouterDoc",
|
||||
// frameSrc: "https://router.vuejs.org/zh/",
|
||||
// keepAlive: true
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// {
|
||||
// path: "/iframe/external",
|
||||
// meta: {
|
||||
// title: "menus.pureExternalDoc"
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: "/external",
|
||||
// name: "https://pure-admin.github.io/pure-admin-doc",
|
||||
// meta: {
|
||||
// title: "menus.pureExternalLink"
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// path: "/pureUtilsLink",
|
||||
// name: "https://pure-admin-utils.netlify.app/",
|
||||
// meta: {
|
||||
// title: "menus.pureUtilsLink"
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// ]
|
||||
// };
|
||||
//
|
||||
// const tabsRouter = {
|
||||
// path: "/tabs",
|
||||
// meta: {
|
||||
// icon: "ri:bookmark-2-line",
|
||||
// title: "menus.pureTabs",
|
||||
// rank: tabs
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: "/tabs/index",
|
||||
// name: "Tabs",
|
||||
// meta: {
|
||||
// title: "menus.pureTabs"
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// // query 传参模式
|
||||
// {
|
||||
// path: "/tabs/query-detail",
|
||||
// name: "TabQueryDetail",
|
||||
// meta: {
|
||||
// // 不在menu菜单中显示
|
||||
// showLink: false,
|
||||
// activePath: "/tabs/index"
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// },
|
||||
// // params 传参模式
|
||||
// {
|
||||
// path: "/tabs/params-detail/:id",
|
||||
// component: "params-detail",
|
||||
// name: "TabParamsDetail",
|
||||
// meta: {
|
||||
// // 不在menu菜单中显示
|
||||
// showLink: false,
|
||||
// activePath: "/tabs/index"
|
||||
// //roles: ["admin", "common"]
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// };
|
||||
|
||||
export default defineFakeRoute([
|
||||
{
|
||||
@@ -336,7 +336,7 @@ export default defineFakeRoute([
|
||||
response: () => {
|
||||
return [
|
||||
systemManagementRouter,
|
||||
systemMonitorRouter,
|
||||
systemMonitorRouter
|
||||
//permissionRouter,
|
||||
// frameRouter,
|
||||
// tabsRouter
|
||||
|
||||
12
Yi.Pure.Vue3/src/api/file.ts
Normal file
12
Yi.Pure.Vue3/src/api/file.ts
Normal 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
|
||||
});
|
||||
};
|
||||
@@ -17,3 +17,10 @@ export type ResultPage = {
|
||||
totalCount?: number;
|
||||
};
|
||||
};
|
||||
|
||||
export type ResultFile = {
|
||||
status: number;
|
||||
data?: Array<{
|
||||
id: string;
|
||||
}>;
|
||||
};
|
||||
|
||||
@@ -28,7 +28,7 @@ export const updatePostStatus = (id, state) => {
|
||||
|
||||
/** 删除岗位 */
|
||||
export const delPost = ids => {
|
||||
return http.request<Result>("delete", `/post`, { params: { id:ids } });
|
||||
return http.request<Result>("delete", `/post`, { params: { id: ids } });
|
||||
};
|
||||
|
||||
/** 获取岗位选择框列表 */
|
||||
|
||||
@@ -42,7 +42,7 @@ export const addUser = (data: any) => {
|
||||
|
||||
/** 查询用户个人信息 */
|
||||
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 });
|
||||
};
|
||||
|
||||
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>;
|
||||
};
|
||||
|
||||
@@ -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) => {
|
||||
return http.request<LoginResult>("post", "/account/login", { data });
|
||||
@@ -107,13 +73,3 @@ export const getCodeImg = () => {
|
||||
export const refreshTokenApi = (data?: object) => {
|
||||
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 });
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import { useUserStoreHook } from "@/store/modules/user";
|
||||
import { useGlobal, isAllEmpty } from "@pureadmin/utils";
|
||||
import { useEpThemeStoreHook } from "@/store/modules/epTheme";
|
||||
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 Fullscreen from "@iconify-icons/ri/fullscreen-fill";
|
||||
|
||||
@@ -41,7 +41,7 @@ export function useNav() {
|
||||
|
||||
/** 头像(如果头像为空则使用 src/assets/user.jpg ) */
|
||||
const userAvatar = computed(() => {
|
||||
return getFileUrl(useUserStoreHook()?.avatar,Avatar);
|
||||
return getFileUrl(useUserStoreHook()?.avatar, Avatar);
|
||||
});
|
||||
|
||||
/** 昵称(如果昵称为空则显示用户名) */
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {isAllEmpty} from "@pureadmin/utils";
|
||||
import { isAllEmpty } from "@pureadmin/utils";
|
||||
|
||||
export function getFileUrl(fileId: string, tryPath: string): string {
|
||||
if (isAllEmpty(fileId)) {
|
||||
|
||||
@@ -3,17 +3,13 @@ import Axios, {
|
||||
type AxiosRequestConfig,
|
||||
type CustomParamsSerializer
|
||||
} from "axios";
|
||||
import type {
|
||||
PureHttpError,
|
||||
RequestMethods,
|
||||
PureHttpRequestConfig
|
||||
} from "./types.d";
|
||||
import type { RequestMethods, PureHttpRequestConfig } from "./types.d";
|
||||
import { stringify } from "qs";
|
||||
import NProgress from "../progress";
|
||||
import { getToken, formatToken } from "@/utils/auth";
|
||||
import { useUserStoreHook } from "@/store/modules/user";
|
||||
import {message} from "@/utils/message";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { message } from "@/utils/message";
|
||||
// import { transformI18n } from "@/plugins/i18n";
|
||||
// 相关配置请参考:www.axios-js.com/zh-cn/docs/#axios-request-config-1
|
||||
const defaultConfig: AxiosRequestConfig = {
|
||||
baseURL: import.meta.env.VITE_APP_BASE_API,
|
||||
@@ -142,7 +138,7 @@ class PureHttp {
|
||||
// 关闭进度条动画
|
||||
NProgress.done();
|
||||
// 所有的响应异常 区分来源为取消请求/非取消请求
|
||||
return Promise.reject($error);
|
||||
return Promise.reject($error);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -12,27 +12,30 @@ const list = ref([
|
||||
title: "账户密码",
|
||||
illustrate: "当前密码强度:强",
|
||||
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) {
|
||||
console.log("onClick", item.title);
|
||||
message("请根据具体业务自行实现", { type: "success" });
|
||||
switch (item.title) {
|
||||
case "账户密码":
|
||||
message("密码更改成功", { type: "success" });
|
||||
break;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -2,11 +2,19 @@
|
||||
import { reactive, ref } from "vue";
|
||||
import { formUpload } from "@/api/mock";
|
||||
import { message } from "@/utils/message";
|
||||
import { type UserInfo, getMine } from "@/api/user";
|
||||
import type { FormInstance, FormRules } from "element-plus";
|
||||
import ReCropperPreview from "@/components/ReCropperPreview";
|
||||
import { createFormData, deviceDetection } from "@pureadmin/utils";
|
||||
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({
|
||||
name: "Profile"
|
||||
@@ -20,15 +28,16 @@ const isShow = ref(false);
|
||||
const userInfoFormRef = ref<FormInstance>();
|
||||
|
||||
const userInfos = reactive({
|
||||
avatar: "",
|
||||
nickname: "",
|
||||
icon: "",
|
||||
nick: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
description: ""
|
||||
introduction: ""
|
||||
});
|
||||
|
||||
const rules = reactive<FormRules<UserInfo>>({
|
||||
nickname: [{ required: true, message: "昵称必填", trigger: "blur" }]
|
||||
const rules = reactive<FormRules>({
|
||||
userName: [{ required: true, message: "用户名必填", trigger: "blur" }],
|
||||
nick: [{ required: true, message: "昵称必填", trigger: "blur" }]
|
||||
});
|
||||
|
||||
function queryEmail(queryString, callback) {
|
||||
@@ -70,11 +79,13 @@ const onCropper = ({ blob }) => (cropperBlob.value = blob);
|
||||
|
||||
const handleSubmitImage = () => {
|
||||
const formData = createFormData({
|
||||
files: new File([cropperBlob.value], "avatar")
|
||||
files: new File([cropperBlob.value], "file")
|
||||
});
|
||||
formUpload(formData)
|
||||
.then(({ success, data }) => {
|
||||
if (success) {
|
||||
uploadFile(formData)
|
||||
.then(async ({ status, data }) => {
|
||||
if (status == 200) {
|
||||
await updateUserIcon(data[0].id);
|
||||
useUserStoreHook().SET_AVATAR(data[0].id);
|
||||
message("更新头像成功", { type: "success" });
|
||||
handleClose();
|
||||
} else {
|
||||
@@ -88,18 +99,17 @@ const handleSubmitImage = () => {
|
||||
|
||||
// 更新信息
|
||||
const onSubmit = async (formEl: FormInstance) => {
|
||||
await formEl.validate((valid, fields) => {
|
||||
await formEl.validate(async (valid, fields) => {
|
||||
if (valid) {
|
||||
console.log(userInfos);
|
||||
await updateUserProfile(userInfos);
|
||||
message("更新信息成功", { type: "success" });
|
||||
} else {
|
||||
console.log("error submit!", fields);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
getMine().then(res => {
|
||||
Object.assign(userInfos, res.data);
|
||||
getUserProfile().then(res => {
|
||||
Object.assign(userInfos, res.data.user);
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -118,7 +128,7 @@ getMine().then(res => {
|
||||
:model="userInfos"
|
||||
>
|
||||
<el-form-item label="头像">
|
||||
<el-avatar :size="80" :src="userInfos.avatar" />
|
||||
<el-avatar :size="80" :src="getFileUrl(userInfos.icon, userAvatar)" />
|
||||
<el-upload
|
||||
ref="uploadRef"
|
||||
accept="image/*"
|
||||
@@ -135,7 +145,7 @@ getMine().then(res => {
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<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 label="邮箱" prop="email">
|
||||
<el-autocomplete
|
||||
@@ -156,7 +166,7 @@ getMine().then(res => {
|
||||
</el-form-item>
|
||||
<el-form-item label="简介">
|
||||
<el-input
|
||||
v-model="userInfos.description"
|
||||
v-model="userInfos.introduction"
|
||||
placeholder="请输入简介"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 6, maxRows: 8 }"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import dayjs from "dayjs";
|
||||
import { getMineLogs } from "@/api/user";
|
||||
// import { getMineLogs } from "@/api/user";
|
||||
import { reactive, ref, onMounted } from "vue";
|
||||
import { deviceDetection } from "@pureadmin/utils";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
@@ -55,11 +55,11 @@ const columns: TableColumnList = [
|
||||
|
||||
async function onSearch() {
|
||||
loading.value = true;
|
||||
const { data } = await getMineLogs();
|
||||
dataList.value = data.list;
|
||||
pagination.total = data.total;
|
||||
pagination.pageSize = data.pageSize;
|
||||
pagination.currentPage = data.currentPage;
|
||||
// const { data } = await getMineLogs();
|
||||
// dataList.value = data.list;
|
||||
// pagination.total = data.total;
|
||||
// pagination.pageSize = data.pageSize;
|
||||
// pagination.currentPage = data.currentPage;
|
||||
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<script setup lang="ts">
|
||||
import { getMine } from "@/api/user";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ref, onBeforeMount } from "vue";
|
||||
import { ReText } from "@/components/ReText";
|
||||
@@ -11,11 +10,14 @@ import AccountManagement from "./components/AccountManagement.vue";
|
||||
import { useDataThemeChange } from "@/layout/hooks/useDataThemeChange";
|
||||
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 ProfileIcon from "@iconify-icons/ri/user-3-line";
|
||||
import PreferencesIcon from "@iconify-icons/ri/settings-3-line";
|
||||
import SecurityLogIcon from "@iconify-icons/ri/window-line";
|
||||
import AccountManagementIcon from "@iconify-icons/ri/profile-line";
|
||||
import { getUserProfile } from "@/api/system/user";
|
||||
|
||||
defineOptions({
|
||||
name: "AccountSettings"
|
||||
@@ -29,7 +31,7 @@ onBeforeMount(() => {
|
||||
});
|
||||
|
||||
const userInfo = ref({
|
||||
avatar: "",
|
||||
icon: "",
|
||||
userName: "",
|
||||
nick: ""
|
||||
});
|
||||
@@ -40,12 +42,12 @@ const panes = [
|
||||
icon: ProfileIcon,
|
||||
component: Profile
|
||||
},
|
||||
{
|
||||
key: "preferences",
|
||||
label: "偏好设置",
|
||||
icon: PreferencesIcon,
|
||||
component: Preferences
|
||||
},
|
||||
// {
|
||||
// key: "preferences",
|
||||
// label: "偏好设置",
|
||||
// icon: PreferencesIcon,
|
||||
// component: Preferences
|
||||
// },
|
||||
{
|
||||
key: "securityLog",
|
||||
label: "安全日志",
|
||||
@@ -61,8 +63,8 @@ const panes = [
|
||||
];
|
||||
const witchPane = ref("profile");
|
||||
|
||||
getMine().then(res => {
|
||||
userInfo.value = res.data;
|
||||
getUserProfile().then(res => {
|
||||
userInfo.value = res.data.user;
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -84,13 +86,13 @@ getMine().then(res => {
|
||||
</div>
|
||||
</el-menu-item>
|
||||
<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]">
|
||||
<ReText class="font-bold !self-baseline">
|
||||
{{ userInfo.nickname }}
|
||||
{{ userInfo.nick }}
|
||||
</ReText>
|
||||
<ReText class="!self-baseline" type="info">
|
||||
{{ userInfo.username }}
|
||||
{{ userInfo.nick }}
|
||||
</ReText>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -248,7 +248,7 @@ getCode();
|
||||
:src="codeUrl"
|
||||
width="120"
|
||||
height="40"
|
||||
style="width: 120px; height: 40px;max-width: none; "
|
||||
style="width: 120px; height: 40px; max-width: none"
|
||||
class="cursor-pointer"
|
||||
alt=""
|
||||
@click="getCode"
|
||||
|
||||
@@ -66,7 +66,7 @@ export function useRole(tableRef: Ref) {
|
||||
label: "登录状态",
|
||||
prop: "state",
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
cellRenderer: ({ props }) => (
|
||||
<el-tag size={props.size} style={tagStyle.value(1)}>
|
||||
{1 === 1 ? "成功" : "失败"}
|
||||
</el-tag>
|
||||
|
||||
@@ -85,11 +85,7 @@ const {
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar
|
||||
title="登录日志"
|
||||
:columns="columns"
|
||||
@refresh="onSearch"
|
||||
>
|
||||
<PureTableBar title="登录日志" :columns="columns" @refresh="onSearch">
|
||||
<template #buttons>
|
||||
<el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll">
|
||||
<template #reference>
|
||||
|
||||
@@ -73,7 +73,7 @@ export function useRole(tableRef: Ref) {
|
||||
label: "操作状态",
|
||||
prop: "status",
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
cellRenderer: ({ props }) => (
|
||||
<el-tag size={props.size} style={tagStyle.value(1)}>
|
||||
{1 === 1 ? "成功" : "失败"}
|
||||
</el-tag>
|
||||
@@ -81,10 +81,10 @@ export function useRole(tableRef: Ref) {
|
||||
},
|
||||
{
|
||||
label: "操作时间",
|
||||
prop: "operatingTime",
|
||||
prop: "creationTime",
|
||||
minWidth: 180,
|
||||
formatter: ({ operatingTime }) =>
|
||||
dayjs(operatingTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
formatter: ({ creationTime }) =>
|
||||
dayjs(creationTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
},
|
||||
{
|
||||
label: "操作",
|
||||
|
||||
@@ -18,7 +18,6 @@ const tableRef = ref();
|
||||
|
||||
const {
|
||||
form,
|
||||
timeQuery,
|
||||
loading,
|
||||
columns,
|
||||
dataList,
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import dayjs from "dayjs";
|
||||
import { message } from "@/utils/message";
|
||||
import { getOnlineLogsList } from "@/api/system";
|
||||
import { reactive, ref, onMounted, toRaw } from "vue";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
import {forceLogout, getOnlineList} from "@/api/monitor/online";
|
||||
import { forceLogout, getOnlineList } from "@/api/monitor/online";
|
||||
|
||||
export function useRole() {
|
||||
const form = reactive({
|
||||
|
||||
@@ -59,7 +59,6 @@ defineExpose({ getRef });
|
||||
<Segmented
|
||||
v-model="newFormInline.menuType"
|
||||
:options="menuTypeOptions"
|
||||
@change="changeSegmented"
|
||||
/>
|
||||
</el-form-item>
|
||||
</re-col>
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import editForm from "../form.vue";
|
||||
import { handleTree } from "@/utils/tree";
|
||||
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 { addDialog } from "@/components/ReDialog";
|
||||
import { reactive, ref, onMounted, h } from "vue";
|
||||
|
||||
@@ -48,27 +48,15 @@ const treeHeight = ref();
|
||||
const {
|
||||
form,
|
||||
isShow,
|
||||
curRow,
|
||||
loading,
|
||||
columns,
|
||||
rowStyle,
|
||||
dataList,
|
||||
treeData,
|
||||
treeProps,
|
||||
isLinkage,
|
||||
pagination,
|
||||
isExpandAll,
|
||||
isSelectAll,
|
||||
treeSearchValue,
|
||||
onSearch,
|
||||
resetForm,
|
||||
openDialog,
|
||||
handleMenu,
|
||||
handleSave,
|
||||
handleDelete,
|
||||
filterMethod,
|
||||
transformI18n,
|
||||
onQueryChanged,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange
|
||||
@@ -209,69 +197,6 @@ onMounted(() => {
|
||||
</pure-table>
|
||||
</template>
|
||||
</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>
|
||||
</template>
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import dayjs from "dayjs";
|
||||
import editForm from "../form.vue";
|
||||
import {message} from "@/utils/message";
|
||||
import {ElMessageBox} from "element-plus";
|
||||
import {usePublicHooks} from "../../hooks";
|
||||
import {transformI18n} from "@/plugins/i18n";
|
||||
import {addDialog} from "@/components/ReDialog";
|
||||
import type {FormItemProps} from "../utils/types";
|
||||
import type {PaginationProps} from "@pureadmin/table";
|
||||
import {getKeyList, deviceDetection} from "@pureadmin/utils";
|
||||
import { message } from "@/utils/message";
|
||||
import { ElMessageBox } from "element-plus";
|
||||
import { usePublicHooks } from "../../hooks";
|
||||
import { transformI18n } from "@/plugins/i18n";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
import type { FormItemProps } from "../utils/types";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
import { deviceDetection } from "@pureadmin/utils";
|
||||
import {
|
||||
getPostList,
|
||||
addPost,
|
||||
@@ -15,20 +15,11 @@ import {
|
||||
delPost,
|
||||
getPost,
|
||||
updatePostStatus
|
||||
} from "@/api/system/post"
|
||||
} from "@/api/system/post";
|
||||
|
||||
import {
|
||||
type Ref,
|
||||
reactive,
|
||||
ref,
|
||||
onMounted,
|
||||
h,
|
||||
toRaw,
|
||||
watch,
|
||||
nextTick
|
||||
} from "vue";
|
||||
import { reactive, ref, onMounted, h, toRaw } from "vue";
|
||||
|
||||
export function usePost(treeRef: Ref) {
|
||||
export function usePost() {
|
||||
const form = reactive({
|
||||
postName: "",
|
||||
postCode: "",
|
||||
@@ -39,7 +30,6 @@ export function usePost(treeRef: Ref) {
|
||||
const curRow = ref();
|
||||
const formRef = ref();
|
||||
const dataList = ref([]);
|
||||
const treeIds = ref([]);
|
||||
const treeData = ref([]);
|
||||
const isShow = ref(false);
|
||||
const loading = ref(true);
|
||||
@@ -48,7 +38,7 @@ export function usePost(treeRef: Ref) {
|
||||
const switchLoadMap = ref({});
|
||||
const isExpandAll = ref(false);
|
||||
const isSelectAll = ref(false);
|
||||
const {switchStyle} = usePublicHooks();
|
||||
const { switchStyle } = usePublicHooks();
|
||||
const pagination = reactive<PaginationProps>({
|
||||
total: 0,
|
||||
pageSize: 10,
|
||||
@@ -86,7 +76,7 @@ export function usePost(treeRef: Ref) {
|
||||
),
|
||||
minWidth: 90
|
||||
},
|
||||
{label:"排序",prop:"orderNum"},
|
||||
{ label: "排序", prop: "orderNum" },
|
||||
{
|
||||
label: "备注",
|
||||
prop: "remark",
|
||||
@@ -96,7 +86,7 @@ export function usePost(treeRef: Ref) {
|
||||
label: "创建时间",
|
||||
prop: "creationTime",
|
||||
minWidth: 160,
|
||||
formatter: ({creationTime}) =>
|
||||
formatter: ({ creationTime }) =>
|
||||
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(
|
||||
`确认要<strong>${
|
||||
row.state === false ? "停用" : "启用"
|
||||
@@ -152,7 +142,7 @@ export function usePost(treeRef: Ref) {
|
||||
|
||||
async function handleDelete(row) {
|
||||
await delPost([row.id]);
|
||||
message(`您删除了角色名称为${row.roleName}的这条数据`, {type: "success"});
|
||||
message(`您删除了角色名称为${row.roleName}的这条数据`, { type: "success" });
|
||||
onSearch();
|
||||
}
|
||||
|
||||
@@ -172,7 +162,7 @@ export function usePost(treeRef: Ref) {
|
||||
|
||||
async function onSearch() {
|
||||
loading.value = true;
|
||||
const {data} = await getPostList(toRaw(form));
|
||||
const { data } = await getPostList(toRaw(form));
|
||||
dataList.value = data.items;
|
||||
pagination.total = data.totalCount;
|
||||
loading.value = false;
|
||||
@@ -196,7 +186,7 @@ export function usePost(treeRef: Ref) {
|
||||
postName: row?.postName ?? "",
|
||||
postCode: row?.postCode ?? "",
|
||||
remark: row?.remark ?? "",
|
||||
orderNum: data?.orderNum ?? 0,
|
||||
orderNum: data?.orderNum ?? 0
|
||||
}
|
||||
},
|
||||
width: "40%",
|
||||
@@ -204,8 +194,8 @@ export function usePost(treeRef: Ref) {
|
||||
fullscreen: deviceDetection(),
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(editForm, {ref: formRef}),
|
||||
beforeSure: (done, {options}) => {
|
||||
contentRenderer: () => h(editForm, { ref: formRef }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const FormRef = formRef.value.getRef();
|
||||
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 {
|
||||
cursor: "pointer",
|
||||
background: id === curRow.value?.id ? "var(--el-fill-color-light)" : ""
|
||||
@@ -244,11 +234,7 @@ export function usePost(treeRef: Ref) {
|
||||
}
|
||||
|
||||
/** 数据权限 可自行开发 */
|
||||
// function handleDatabase() {}
|
||||
|
||||
const onQueryChanged = (query: string) => {
|
||||
treeRef.value!.filter(query);
|
||||
};
|
||||
// function handleDatabase() {}
|
||||
|
||||
const filterMethod = (query: string, node) => {
|
||||
return transformI18n(node.title)!.includes(query);
|
||||
@@ -258,18 +244,6 @@ export function usePost(treeRef: Ref) {
|
||||
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 {
|
||||
form,
|
||||
isShow,
|
||||
@@ -290,7 +264,6 @@ export function usePost(treeRef: Ref) {
|
||||
handleDelete,
|
||||
filterMethod,
|
||||
transformI18n,
|
||||
onQueryChanged,
|
||||
handleSizeChange,
|
||||
handleCurrentChange,
|
||||
handleSelectionChange
|
||||
|
||||
@@ -9,7 +9,6 @@ interface FormItemProps {
|
||||
/** 备注 */
|
||||
remark: string;
|
||||
orderNum: number;
|
||||
|
||||
}
|
||||
interface FormProps {
|
||||
formInline: FormItemProps;
|
||||
|
||||
@@ -16,8 +16,7 @@ import {
|
||||
updateRole,
|
||||
changeRoleStatus,
|
||||
delRole,
|
||||
getRoleMenuSelect,
|
||||
updataDataScope
|
||||
getRoleMenuSelect
|
||||
} from "@/api/system/role";
|
||||
import { getMenuOption } from "@/api/system/menu";
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
|
||||
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 More from "@iconify-icons/ep/more-filled";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
import "./reset.css";
|
||||
import dayjs from "dayjs";
|
||||
import editForm from "../form/index.vue";
|
||||
import {zxcvbn} from "@zxcvbn-ts/core";
|
||||
import {handleTree} from "@/utils/tree";
|
||||
import {message} from "@/utils/message";
|
||||
import { zxcvbn } from "@zxcvbn-ts/core";
|
||||
import { handleTree } from "@/utils/tree";
|
||||
import { message } from "@/utils/message";
|
||||
import userAvatar from "@/assets/user.jpg";
|
||||
import {getFileUrl} from "@/utils/file"
|
||||
import {usePublicHooks} from "../../hooks";
|
||||
import {addDialog} from "@/components/ReDialog";
|
||||
import type {PaginationProps} from "@pureadmin/table";
|
||||
import { getFileUrl } from "@/utils/file";
|
||||
import { usePublicHooks } from "../../hooks";
|
||||
import { addDialog } from "@/components/ReDialog";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
import ReCropperPreview from "@/components/ReCropperPreview";
|
||||
import type {FormItemProps} from "../utils/types";
|
||||
import type { FormItemProps } from "../utils/types";
|
||||
import {
|
||||
getKeyList,
|
||||
isAllEmpty,
|
||||
@@ -26,8 +26,8 @@ import {
|
||||
updateUser,
|
||||
getUserList
|
||||
} from "@/api/system/user";
|
||||
import {getRoleOption} from "@/api/system/role";
|
||||
import {getDeptList} from "@/api/system/dept";
|
||||
import { getRoleOption } from "@/api/system/role";
|
||||
import { getDeptList } from "@/api/system/dept";
|
||||
import {
|
||||
ElForm,
|
||||
ElInput,
|
||||
@@ -63,7 +63,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
// 上传头像信息
|
||||
const avatarInfo = ref();
|
||||
const switchLoadMap = ref({});
|
||||
const {switchStyle} = usePublicHooks();
|
||||
const { switchStyle } = usePublicHooks();
|
||||
const higherDeptOptions = ref();
|
||||
const treeData = ref([]);
|
||||
const treeLoading = ref(true);
|
||||
@@ -89,12 +89,12 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
{
|
||||
label: "用户头像",
|
||||
prop: "avatar",
|
||||
cellRenderer: ({row}) => (
|
||||
cellRenderer: ({ row }) => (
|
||||
<el-image
|
||||
fit="cover"
|
||||
preview-teleported={true}
|
||||
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"
|
||||
/>
|
||||
),
|
||||
@@ -114,7 +114,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
label: "性别",
|
||||
prop: "sex",
|
||||
minWidth: 90,
|
||||
cellRenderer: ({row, props}) => (
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag
|
||||
size={props.size}
|
||||
type={row.sex === "Woman" ? "danger" : null}
|
||||
@@ -133,8 +133,8 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
label: "手机号码",
|
||||
prop: "phone",
|
||||
minWidth: 90,
|
||||
formatter: ({phone}) =>
|
||||
phone == null ? "-" : hideTextAtIndex(phone, {start: 3, end: 6})
|
||||
formatter: ({ phone }) =>
|
||||
phone == null ? "-" : hideTextAtIndex(phone, { start: 3, end: 6 })
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
@@ -159,7 +159,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
label: "创建时间",
|
||||
minWidth: 90,
|
||||
prop: "creationTime",
|
||||
formatter: ({creationTime}) =>
|
||||
formatter: ({ creationTime }) =>
|
||||
dayjs(creationTime).format("YYYY-MM-DD HH:mm:ss")
|
||||
},
|
||||
{
|
||||
@@ -183,17 +183,17 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
password: ""
|
||||
});
|
||||
const pwdProgress = [
|
||||
{color: "#e74242", text: "非常弱"},
|
||||
{color: "#EFBD47", text: "弱"},
|
||||
{color: "#ffa500", text: "一般"},
|
||||
{color: "#1bbf1b", text: "强"},
|
||||
{color: "#008000", text: "非常强"}
|
||||
{ color: "#e74242", text: "非常弱" },
|
||||
{ color: "#EFBD47", text: "弱" },
|
||||
{ color: "#ffa500", text: "一般" },
|
||||
{ color: "#1bbf1b", text: "强" },
|
||||
{ color: "#008000", text: "非常强" }
|
||||
];
|
||||
// 当前密码强度(0-4)
|
||||
const curScore = ref();
|
||||
const roleOptions = ref([]);
|
||||
|
||||
function onChange({row, index}) {
|
||||
function onChange({ row, index }) {
|
||||
ElMessageBox.confirm(
|
||||
`确认要<strong>${
|
||||
row.state === 0 ? "停用" : "启用"
|
||||
@@ -242,7 +242,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
|
||||
async function handleDelete(row) {
|
||||
await delUser([row.id]);
|
||||
message(`您删除了用户编号为${row.id}的这条数据`, {type: "success"});
|
||||
message(`您删除了用户编号为${row.id}的这条数据`, { type: "success" });
|
||||
onSearch();
|
||||
}
|
||||
|
||||
@@ -286,7 +286,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
|
||||
async function onSearch() {
|
||||
loading.value = true;
|
||||
const {data} = await getUserList(toRaw(form));
|
||||
const { data } = await getUserList(toRaw(form));
|
||||
dataList.value = data.items;
|
||||
pagination.total = data.totalCount;
|
||||
// pagination.pageSize = data.pageSize;
|
||||
@@ -305,7 +305,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
onSearch();
|
||||
};
|
||||
|
||||
function onTreeSelect({id, selected}) {
|
||||
function onTreeSelect({ id, selected }) {
|
||||
form.deptId = selected ? id : "";
|
||||
onSearch();
|
||||
}
|
||||
@@ -353,8 +353,8 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
fullscreen: deviceDetection(),
|
||||
fullscreenIcon: true,
|
||||
closeOnClickModal: false,
|
||||
contentRenderer: () => h(editForm, {ref: formRef}),
|
||||
beforeSure: (done, {options}) => {
|
||||
contentRenderer: () => h(editForm, { ref: formRef }),
|
||||
beforeSure: (done, { options }) => {
|
||||
const FormRef = formRef.value.getRef();
|
||||
const curData = options.props.formInline as FormItemProps;
|
||||
|
||||
@@ -411,7 +411,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
|
||||
watch(
|
||||
pwdForm,
|
||||
({password}) =>
|
||||
({ password }) =>
|
||||
(curScore.value = isAllEmpty(password) ? -1 : zxcvbn(password).score)
|
||||
);
|
||||
|
||||
@@ -446,10 +446,10 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
</ElFormItem>
|
||||
</ElForm>
|
||||
<div class="mt-4 flex">
|
||||
{pwdProgress.map(({color, text}, idx) => (
|
||||
{pwdProgress.map(({ color, text }, idx) => (
|
||||
<div
|
||||
class="w-[19vw]"
|
||||
style={{marginLeft: idx !== 0 ? "4px" : 0}}
|
||||
style={{ marginLeft: idx !== 0 ? "4px" : 0 }}
|
||||
>
|
||||
<ElProgress
|
||||
striped
|
||||
@@ -462,7 +462,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
/>
|
||||
<p
|
||||
class="text-center"
|
||||
style={{color: curScore.value === idx ? color : ""}}
|
||||
style={{ color: curScore.value === idx ? color : "" }}
|
||||
>
|
||||
{text}
|
||||
</p>
|
||||
@@ -494,7 +494,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) {
|
||||
onSearch();
|
||||
|
||||
// 归属部门
|
||||
const {data} = await getDeptList();
|
||||
const { data } = await getDeptList();
|
||||
higherDeptOptions.value = handleTree(data.items);
|
||||
treeData.value = handleTree(data.items);
|
||||
treeLoading.value = false;
|
||||
|
||||
Reference in New Issue
Block a user