mirror of
https://gitee.com/dotnetchina/OpenAuth.Net.git
synced 2026-02-15 12:56:28 +08:00
Merge branch 'main' of https://gitee.com/dotnetchina/OpenAuth.Net
This commit is contained in:
237
.cursor/rules/openauth.mdc
Normal file
237
.cursor/rules/openauth.mdc
Normal file
@@ -0,0 +1,237 @@
|
||||
---
|
||||
alwaysApply: false
|
||||
---
|
||||
# OpenAuth.Net Cursor Rules
|
||||
|
||||
## 项目概述
|
||||
OpenAuth.Net是一个基于.NET 9的企业级权限管理和快速开发框架,采用Martin Fowler企业级应用开发思想,集成了最新的技术栈。
|
||||
|
||||
## 技术栈
|
||||
- **后端**: .NET 9, ASP.NET Core WebAPI
|
||||
- **ORM**: SqlSugar (主要) + Entity Framework Core (兼容)
|
||||
- **依赖注入**: Autofac
|
||||
- **数据库**: 支持SqlServer、MySQL、Oracle、PostgreSQL
|
||||
- **定时任务**: Quartz.NET
|
||||
- **缓存**: Redis, MemoryCache
|
||||
- **前端**: Vue2 + Element-UI
|
||||
- **测试**: NUnit
|
||||
- **文档**: Swagger
|
||||
|
||||
## 项目架构
|
||||
```
|
||||
📦OpenAuth.Net
|
||||
┣ 📂Infrastructure # 基础设施层 - 工具类、扩展方法、帮助类
|
||||
┣ 📂OpenAuth.Repository # 数据访问层 - 实体定义、数据访问
|
||||
┣ 📂OpenAuth.App # 应用服务层 - 业务逻辑
|
||||
┣ 📂OpenAuth.WebApi # 表示层 - WebAPI控制器
|
||||
┣ 📂OpenAuth.Identity # 身份认证服务 - IdentityServer4
|
||||
┣ 📂Vue2 # 前端项目
|
||||
┗ 📂数据库脚本 # 数据库初始化脚本
|
||||
```
|
||||
|
||||
## 编码规范
|
||||
|
||||
### 通用规范
|
||||
- 使用C# 9+语法特性
|
||||
- 遵循Microsoft C#编码规范
|
||||
- 类名使用PascalCase,方法名使用PascalCase
|
||||
- 私有字段使用_camelCase,公共属性使用PascalCase
|
||||
- 常量使用UPPER_CASE
|
||||
- 异步方法必须添加Async后缀
|
||||
|
||||
### 命名规范
|
||||
- **Controller**: 以Controller结尾,如`UsersController`
|
||||
- **Service/App**: 以App结尾,如`UserManagerApp`
|
||||
- **Repository**: 以Repository结尾,如`UserRepository`
|
||||
- **Entity**: 实体类直接使用名词,如`SysUser`
|
||||
- **Request**: 请求类以Req结尾,如`QueryUserListReq`
|
||||
- **Response**: 响应类以Resp结尾,如`PagedListDataResp`
|
||||
- **DTO**: 视图模型以View结尾,如`UserView`
|
||||
|
||||
### 注释规范
|
||||
- 所有公共方法必须添加XML注释
|
||||
- 复杂业务逻辑必须添加行内注释
|
||||
- 使用/// <summary>标记方法说明
|
||||
- 参数使用/// <param name="参数名">说明</param>
|
||||
- 返回值使用/// <returns>说明</returns>
|
||||
|
||||
## 分层架构规则
|
||||
|
||||
### Infrastructure层 (基础设施层)
|
||||
- **职责**: 提供通用工具类、扩展方法、帮助类、常量定义
|
||||
- **规则**:
|
||||
- 不依赖其他业务层
|
||||
- 提供可复用的基础功能
|
||||
- 包含缓存、配置、工具类等
|
||||
- **文件组织**:
|
||||
- `/Cache` - 缓存相关
|
||||
- `/Extensions` - 扩展方法
|
||||
- `/Helpers` - 帮助类
|
||||
- `/Const` - 常量定义
|
||||
|
||||
### Repository层 (数据访问层)
|
||||
- **职责**: 数据访问、实体定义、数据库操作
|
||||
- **规则**:
|
||||
- 继承自`BaseRepository<T,TContext>`
|
||||
- 实体类继承自`StringEntity`、`LongEntity`或`IntAutoGenEntity`
|
||||
- 使用SqlSugar或EF Core进行数据访问
|
||||
- 包含DbContext配置
|
||||
- **基类使用**:
|
||||
- `StringEntity`: 字符串主键实体
|
||||
- `LongEntity`: 长整型主键实体
|
||||
- `IntAutoGenEntity`: 自增整型主键实体
|
||||
|
||||
### App层 (应用服务层)
|
||||
- **职责**: 业务逻辑处理、数据传输对象、业务规则
|
||||
- **规则**:
|
||||
- 继承自`SqlSugarBaseApp<T>`、`BaseStringApp<T>`、`BaseLongApp<T>`等基类
|
||||
- 通过构造函数注入依赖
|
||||
- 实现业务逻辑,不直接操作数据库
|
||||
- 使用Request/Response模式
|
||||
- **依赖注入**:
|
||||
```csharp
|
||||
public UserManagerApp(ISqlSugarClient client, RevelanceManagerApp app, IAuth auth)
|
||||
: base(client, auth)
|
||||
```
|
||||
|
||||
### WebApi层 (表示层)
|
||||
- **职责**: HTTP请求处理、参数验证、响应格式化
|
||||
- **规则**:
|
||||
- 继承自`ControllerBase`
|
||||
- 使用`[ApiController]`特性
|
||||
- 统一返回`Response<T>`格式
|
||||
- 进行参数验证
|
||||
- 添加Swagger文档注释
|
||||
|
||||
## 数据库操作规范
|
||||
|
||||
### SqlSugar使用规范
|
||||
```csharp
|
||||
// 查询
|
||||
var users = SugarClient.Queryable<SysUser>()
|
||||
.Where(u => u.Status == 1)
|
||||
.ToList();
|
||||
|
||||
// 分页查询
|
||||
var result = new PagedListDataResp<Role>();
|
||||
var objs = SugarClient.Queryable<Role>();
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
objs = objs.Where(u => u.Name.Contains(request.key));
|
||||
}
|
||||
|
||||
result.Data = await objs.OrderBy(u => u.Name)
|
||||
.Skip((request.page - 1) * request.limit)
|
||||
.Take(request.limit).ToListAsync();
|
||||
result.Count = await objs.CountAsync();
|
||||
return result;
|
||||
|
||||
// 联表查询
|
||||
var result = SugarClient.Queryable<Relevance>()
|
||||
.LeftJoin<SysOrg>((u, o) => u.SecondId == o.Id)
|
||||
.Where((u, o) => u.FirstId == userId && u.RelKey == Define.USERORG)
|
||||
.Select((u, o) => o);
|
||||
return result.ToList();
|
||||
```
|
||||
|
||||
### 事务处理
|
||||
```csharp
|
||||
SugarClient.Ado.BeginTran();
|
||||
try
|
||||
{
|
||||
// 数据库操作
|
||||
SugarClient.Ado.CommitTran();
|
||||
}
|
||||
catch
|
||||
{
|
||||
SugarClient.Ado.RollbackTran();
|
||||
throw;
|
||||
}
|
||||
```
|
||||
|
||||
## 权限和安全规范
|
||||
|
||||
### 权限验证
|
||||
- 使用`IAuth`接口获取当前用户信息
|
||||
- 通过`_auth.GetCurrentUser()`获取登录用户
|
||||
- 数据权限通过机构级联控制
|
||||
|
||||
## API设计规范
|
||||
|
||||
### 控制器设计
|
||||
```csharp
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private readonly UserManagerApp _app;
|
||||
|
||||
public UsersController(UserManagerApp app)
|
||||
{
|
||||
_app = app;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取用户列表
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public async Task<Response<PagedListDataResp<UserView>>> Get([FromQuery] QueryUserListReq request)
|
||||
{
|
||||
var result = await _app.Load(request);
|
||||
return Response.Ok(result);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 依赖注入规范
|
||||
|
||||
### Autofac配置
|
||||
- 在`AutofacExt.cs`中配置依赖注入
|
||||
- 实现`IDependency`接口的类自动注册
|
||||
- 使用构造函数注入
|
||||
|
||||
## 测试规范
|
||||
|
||||
### 单元测试
|
||||
- 使用NUnit框架
|
||||
- 继承自`TestBase`基类
|
||||
- 测试类以Test结尾
|
||||
- 测试方法使用描述性名称
|
||||
|
||||
### 集成测试
|
||||
- 使用`AutofacWebApplicationFactory`
|
||||
- 模拟HTTP请求
|
||||
- 验证完整的业务流程
|
||||
|
||||
## 工作流规范
|
||||
|
||||
### 流程定义
|
||||
- 使用`FlowScheme`定义流程模板
|
||||
- 通过`FlowInstance`管理流程实例
|
||||
- `FlowNode`表示流程节点
|
||||
|
||||
### 流程引擎
|
||||
- 使用`FlowRuntime`执行流程
|
||||
- 支持顺序、并行、条件等流程类型
|
||||
- 流程状态通过`FlowInstanceStatus`管理
|
||||
|
||||
## 定时任务规范
|
||||
|
||||
### Quartz使用
|
||||
- 继承自`IJob`接口
|
||||
- 在`QuartzService`中注册任务
|
||||
- 支持Cron表达式配置
|
||||
|
||||
## 文档规范
|
||||
|
||||
### 代码文档
|
||||
- 重要业务逻辑添加注释
|
||||
- 复杂算法添加说明
|
||||
- 配置文件添加说明注释
|
||||
|
||||
## 配置管理
|
||||
|
||||
### 配置文件
|
||||
- `appsettings.json`: 基础配置
|
||||
- `appsettings.Development.json`: 开发环境配置
|
||||
- `appsettings.Production.json`: 生产环境配置
|
||||
@@ -34,6 +34,6 @@ COPY --from=build /app/publish/webapi ./webapi
|
||||
# 复制 Identity 发布文件
|
||||
COPY --from=build /app/publish/identity ./identity
|
||||
|
||||
# 启动 WebApi, Mvc, 和 Identity,就算失败也保持运行,方便查询日志
|
||||
# 启动 WebApi 和 Identity,就算失败也保持运行,方便查询日志
|
||||
ENTRYPOINT ["sh", "-c", "cd webapi && dotnet OpenAuth.WebApi.dll & cd identity && dotnet OpenAuth.IdentityServer.dll || tail -f /dev/null"]
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\net5.0\Infrastructure.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\net9.0\Infrastructure.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;1573;1572;1570</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -5,10 +5,14 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\net5.0\OpenAuth.App.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\net9.0\OpenAuth.App.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;1573;1572;1570</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
|
||||
<DocumentationFile>bin\Release\net9.0\OpenAuth.App.xml</DocumentationFile>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Model\**" />
|
||||
<EmbeddedResource Remove="Model\**" />
|
||||
|
||||
@@ -4,6 +4,7 @@ using System.Linq;
|
||||
using Infrastructure;
|
||||
using OpenAuth.App.Interface;
|
||||
using OpenAuth.App.Request;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using OpenAuth.Repository.Interface;
|
||||
@@ -76,16 +77,18 @@ namespace OpenAuth.App
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载特定用户的部门
|
||||
/// 获取所有机构
|
||||
/// </summary>
|
||||
/// <param name="userId">The user unique identifier.</param>
|
||||
public List<SysOrg> LoadForUser(string userId)
|
||||
/// <returns></returns>
|
||||
public List<OrgView> LoadAll()
|
||||
{
|
||||
var result = SugarClient.Queryable<Relevance>()
|
||||
.LeftJoin<SysOrg>((u, o) => u.SecondId == o.Id)
|
||||
.Where((u, o) => u.FirstId == userId && u.RelKey == Define.USERORG)
|
||||
.Select((u, o) => o);
|
||||
return result.ToList();
|
||||
return SugarClient.Queryable<SysOrg>()
|
||||
.LeftJoin<SysUser>((org, user) => org.ChairmanId ==user.Id)
|
||||
.Select((org,user)=>new OrgView
|
||||
{
|
||||
Id = org.Id.SelectAll(),
|
||||
ChairmanName = user.Name
|
||||
}).ToList();
|
||||
}
|
||||
|
||||
public OrgManagerApp(ISqlSugarClient client, IAuth auth,
|
||||
|
||||
@@ -5,9 +5,15 @@ namespace OpenAuth.App.SSO
|
||||
|
||||
public class PassportLoginRequest
|
||||
{
|
||||
/// <summary>
|
||||
/// 登录账号
|
||||
/// </summary>
|
||||
/// <example>System</example>
|
||||
public string Account { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 登录密码
|
||||
/// </summary>
|
||||
/// <example>123456</example>
|
||||
public string Password { get; set; }
|
||||
|
||||
|
||||
@@ -100,6 +100,15 @@ namespace OpenAuth.App.Test
|
||||
Console.WriteLine($"添加数据库连接: {conn.Key} / {(dbtypes.ContainsKey(conn.Key) ? dbtypes[conn.Key] : "未指定类型")},连接字符串:{conn.Value}");
|
||||
}
|
||||
|
||||
//通过ConfigId为空判断是否有默认的连接字符串
|
||||
if(!connectionConfigs.Any(x => x.ConfigId == null))
|
||||
{
|
||||
throw new Exception($"没有找到默认的连接字符串:{Define.DEFAULT_TENANT_ID}");
|
||||
}
|
||||
|
||||
//把connectionConfigs排序,ConfigId为空的放在最前面,即默认的连接字符串必须排最前面
|
||||
connectionConfigs = connectionConfigs.OrderBy(x => x.ConfigId == null ? 0 : 1).ToList();
|
||||
|
||||
var sqlSugar = new SqlSugarClient(connectionConfigs);
|
||||
|
||||
// 配置PostgreSQL数据库处理
|
||||
|
||||
@@ -42,29 +42,42 @@ namespace OpenAuth.App
|
||||
var query = SugarClient.Queryable<SysUser>();
|
||||
if (!string.IsNullOrEmpty(request.key))
|
||||
{
|
||||
query = SugarClient.Queryable<SysUser>().Where(u => u.Name.Contains(request.key) || u.Account.Contains(request.key));
|
||||
query = query.Where(u => u.Name.Contains(request.key) || u.Account.Contains(request.key));
|
||||
}
|
||||
|
||||
var orgs = SugarClient.Queryable<SysOrg>();
|
||||
if(!ignoreAuth) //如果没有忽略权限,则只能访问自己所在的机构
|
||||
{
|
||||
var orgIds = loginUser.Orgs.Select(u => u.Id).ToArray();
|
||||
orgs = orgs.Where(u => orgIds.Contains(u.Id));
|
||||
}
|
||||
|
||||
if(!string.IsNullOrEmpty(request.orgId)) //如果请求的orgId不为空,加载这个机构及该机构下级的所有用户
|
||||
{
|
||||
var reqorg = SugarClient.Queryable<SysOrg>().First(u => u.Id == request.orgId);
|
||||
var cascadeId = reqorg.CascadeId;
|
||||
var orgIds = orgs.Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray();
|
||||
var userIds = SugarClient.Queryable<Relevance>().Where(r => r.RelKey == Define.USERORG
|
||||
&& orgIds.Contains(r.SecondId)).Select(r => r.FirstId).Distinct().ToList();
|
||||
query = query.Where(u => userIds.Contains(u.Id));
|
||||
|
||||
}else{
|
||||
if(!ignoreAuth) //如果没有忽略权限,则根据用户所在的机构获取用户
|
||||
{
|
||||
var orgIds = orgs.Select(o => o.Id).ToArray();
|
||||
var userIds = SugarClient.Queryable<Relevance>().Where(r => r.RelKey == Define.USERORG
|
||||
&& orgIds.Contains(r.SecondId)).Select(r => r.FirstId).Distinct().ToList();
|
||||
query = query.Where(u => userIds.Contains(u.Id));
|
||||
}
|
||||
|
||||
//没有限制权限、没有传入orgId,则query就是获取最原始的所有用户
|
||||
}
|
||||
|
||||
var userOrgs = query
|
||||
.LeftJoin<SysUser>((user, u) => user.ParentId == u.Id)
|
||||
.LeftJoin<Relevance>((user, u, r) => user.Id == r.FirstId && r.RelKey == Define.USERORG)
|
||||
.LeftJoin<SysOrg>((user, u, r, o) => r.SecondId == o.Id);
|
||||
|
||||
//如果请求的orgId不为空,加载用户可以看到的机构及下级的所有用户
|
||||
if (!string.IsNullOrEmpty(request.orgId))
|
||||
{
|
||||
var org = loginUser.Orgs.SingleOrDefault(u => u.Id == request.orgId);
|
||||
var cascadeId = org.CascadeId;
|
||||
var orgIds = loginUser.Orgs.Where(u => u.CascadeId.Contains(cascadeId)).Select(u => u.Id).ToArray();
|
||||
//只获取机构里面的用户
|
||||
userOrgs = userOrgs.Where((user, u, r, o) => r.RelKey == Define.USERORG && orgIds.Contains(o.Id));
|
||||
}
|
||||
else if (!ignoreAuth) //如果请求的orgId为空,即为跟节点,如果不忽略权限,只能获取到用户可以看到的机构及未分配机构的用户
|
||||
{
|
||||
var orgIds = loginUser.Orgs.Select(u => u.Id).ToArray();
|
||||
//获取用户可以访问的机构的用户和没有任何机构关联的用户(机构被删除后,没有删除这里面的关联关系)
|
||||
userOrgs = userOrgs.Where((user, u, r, o) => (r.RelKey == Define.USERORG && orgIds.Contains(o.Id)) || (o == null));
|
||||
}
|
||||
|
||||
var userOrgsResult = userOrgs.Select((user, u, r, o) => new
|
||||
{
|
||||
Account = user.Account,
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||
<DocumentationFile>bin\Debug\net5.0\OpenAuth.Repository.xml</DocumentationFile>
|
||||
<DocumentationFile>bin\Debug\net9.0\OpenAuth.Repository.xml</DocumentationFile>
|
||||
<NoWarn>1701;1702;1591;1573;1572;1570</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
using Infrastructure;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using OpenAuth.App;
|
||||
using OpenAuth.App.Response;
|
||||
using OpenAuth.Repository.Domain;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace OpenAuth.WebApi.Controllers
|
||||
{
|
||||
@@ -79,6 +81,37 @@ namespace OpenAuth.WebApi.Controllers
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有机构
|
||||
/// </summary>
|
||||
[HttpGet]
|
||||
public Response<List<OrgView>> LoadAll()
|
||||
{
|
||||
var result = new Response<List<OrgView>>();
|
||||
try
|
||||
{
|
||||
result.Data = _app.LoadAll();
|
||||
}
|
||||
catch (CommonException ex)
|
||||
{
|
||||
if (ex.Code == Define.INVALID_TOKEN)
|
||||
{
|
||||
result.Code = ex.Code;
|
||||
result.Message = ex.Message;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.Code = 500;
|
||||
result.Message = ex.InnerException != null
|
||||
? "OpenAuth.WebAPI数据库访问失败:" + ex.InnerException.Message
|
||||
: "OpenAuth.WebAPI数据库访问失败:" + ex.Message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 删除选中的部门及所有的子部门
|
||||
|
||||
@@ -1,81 +1,129 @@
|
||||
<template>
|
||||
<div class="flex-column">
|
||||
<div class="profile-page">
|
||||
|
||||
<div class="app-container flex-item">
|
||||
<el-row style="height: 100%;">
|
||||
<el-col :span="15" style="height: 100%;">
|
||||
<div class="bg-white" style="height: 50%;">
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">基本资料</el-button>
|
||||
</div>
|
||||
<el-form ref="dataForm" :model="temp" label-position="right" label-width="100px">
|
||||
<el-form-item size="small" :label="'账号'" prop="account">
|
||||
<span>{{temp.account}}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item size="small" :label="'姓名'" prop="name">
|
||||
<el-input v-model="temp.name"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item size="small" :label="'性别'">
|
||||
<el-radio v-model="temp.sex" :label="0">男</el-radio>
|
||||
<el-radio v-model="temp.sex" :label="1">女</el-radio>
|
||||
</el-form-item>
|
||||
<el-form-item size="small" :label="' '">
|
||||
<el-button size="mini" type="primary" @click="changeProfile">确认修改</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
</el-card>
|
||||
</div>
|
||||
<div class="bg-white" style="height: 50%;">
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">修改密码</el-button>
|
||||
</div>
|
||||
<el-form ref="dataForm" :model="temp" label-position="right" label-width="100px">
|
||||
<el-form-item size="small" :label="'新密码'" prop="name">
|
||||
<el-input v-model="newpwd" show-password></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item size="small" :label="' '">
|
||||
<el-button size="mini" type="primary" @click="changePassword">确认修改</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
|
||||
</el-card>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="4" style="height: 100%;">
|
||||
<div class="bg-white" style="height: 100%;">
|
||||
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">可访问的模块</el-button>
|
||||
<!-- 主内容区 -->
|
||||
<div class="profile-main">
|
||||
<div class="container">
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧用户信息区 -->
|
||||
<el-col :span="8">
|
||||
<div class="user-info-section">
|
||||
<!-- 用户头像卡片 -->
|
||||
<div class="user-avatar-card">
|
||||
<div class="avatar-container">
|
||||
<div class="user-avatar">
|
||||
<i class="el-icon-user-solid"></i>
|
||||
</div>
|
||||
<div class="user-basic-info">
|
||||
<h3 class="user-name">{{temp.name || temp.account}}</h3>
|
||||
<p class="user-account">@{{temp.account}}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-tree :data="modulesTree" :expand-on-click-node="false" default-expand-all :props="orgTreeProps">
|
||||
</el-tree>
|
||||
</el-card>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="5" style="height: 100%;border: 1px solid #EBEEF5;">
|
||||
<el-card shadow="never" class="body-small" style="height: 100%;overflow:auto;">
|
||||
<div slot="header" class="clearfix">
|
||||
<el-button type="text" style="padding: 0 11px">可访问的机构(✅为当前默认,点击可切换)</el-button>
|
||||
<!-- 基本信息编辑卡片 -->
|
||||
<div class="info-edit-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">基本信息</h4>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<el-form :model="temp" label-width="80px" class="user-form">
|
||||
<el-form-item label="账号">
|
||||
<div class="readonly-text">{{temp.account}}</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名">
|
||||
<el-input v-model="temp.name" placeholder="请输入姓名" size="medium"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="性别">
|
||||
<el-radio-group v-model="temp.sex">
|
||||
<el-radio :label="0">男</el-radio>
|
||||
<el-radio :label="1">女</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="changeProfile" size="medium">
|
||||
保存修改
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 密码修改卡片 -->
|
||||
<div class="password-edit-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">修改密码</h4>
|
||||
</div>
|
||||
<div class="card-content">
|
||||
<el-form label-width="80px" class="user-form">
|
||||
<el-form-item label="新密码">
|
||||
<el-input v-model="newpwd" type="password" placeholder="请输入新密码" size="medium" show-password></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="warning" @click="changePassword" size="medium">
|
||||
修改密码
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<el-tree :data="orgsTree" :expand-on-click-node="false" default-expand-all :props="orgTreeProps"
|
||||
@node-click="handleNodeClick"></el-tree>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
</el-row>
|
||||
<!-- 右侧功能区 -->
|
||||
<el-col :span="16">
|
||||
<div class="function-section">
|
||||
<el-row :gutter="20">
|
||||
<!-- 可访问模块 -->
|
||||
<el-col :span="12">
|
||||
<div class="function-card modules-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
<i class="el-icon-menu"></i>
|
||||
可访问模块
|
||||
</h4>
|
||||
</div>
|
||||
<div class="card-content tree-content">
|
||||
<el-tree
|
||||
:data="modulesTree"
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
:props="orgTreeProps"
|
||||
class="access-tree">
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 可访问机构 -->
|
||||
<el-col :span="12">
|
||||
<div class="function-card orgs-card">
|
||||
<div class="card-header">
|
||||
<h4 class="card-title">
|
||||
<i class="el-icon-office-building"></i>
|
||||
可访问机构
|
||||
</h4>
|
||||
<span class="card-tips">✅当前默认机构,点击切换</span>
|
||||
</div>
|
||||
<div class="card-content tree-content">
|
||||
<el-tree
|
||||
:data="orgsTree"
|
||||
:expand-on-click-node="false"
|
||||
default-expand-all
|
||||
:props="orgTreeProps"
|
||||
@node-click="handleNodeClick"
|
||||
class="access-tree clickable-tree">
|
||||
</el-tree>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -200,22 +248,259 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.clearfix:before,
|
||||
.clearfix:after {
|
||||
display: table;
|
||||
content: "";
|
||||
}
|
||||
/* 页面整体样式 */
|
||||
.profile-page {
|
||||
min-height: 100vh;
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.clearfix:after {
|
||||
clear: both
|
||||
}
|
||||
/* 顶部导航 */
|
||||
.profile-header {
|
||||
background: #fff;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.el-select.filter-item.el-select--small {
|
||||
width: 100%;
|
||||
}
|
||||
.header-content {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px 24px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
margin: 0;
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
/* 主内容区 */
|
||||
.profile-main {
|
||||
padding: 24px 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 0 24px;
|
||||
}
|
||||
|
||||
/* 左侧用户信息区 */
|
||||
.user-info-section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* 用户头像卡片 */
|
||||
.user-avatar-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.avatar-container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.user-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
border-radius: 50%;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.user-avatar i {
|
||||
font-size: 36px;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.user-name {
|
||||
margin: 0 0 8px 0;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.user-account {
|
||||
margin: 0;
|
||||
font-size: 14px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
/* 通用卡片样式 */
|
||||
.info-edit-card,
|
||||
.password-edit-card,
|
||||
.function-card {
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.card-header {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
color: #262626;
|
||||
}
|
||||
|
||||
.card-title i {
|
||||
margin-right: 8px;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.card-tips {
|
||||
font-size: 12px;
|
||||
color: #8c8c8c;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 表单样式 */
|
||||
.user-form .el-form-item {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.user-form .el-form-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.readonly-text {
|
||||
color: #8c8c8c;
|
||||
background: #fafafa;
|
||||
padding: 8px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.user-form .el-input .el-input__inner {
|
||||
border-radius: 4px;
|
||||
border: 1px solid #d9d9d9;
|
||||
}
|
||||
|
||||
.user-form .el-input .el-input__inner:focus {
|
||||
border-color: #1890ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
|
||||
.user-form .el-radio {
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.user-form .el-button {
|
||||
padding: 8px 20px;
|
||||
border-radius: 4px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
/* 右侧功能区 */
|
||||
.function-section {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.function-card {
|
||||
height: 600px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.tree-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
padding: 16px 20px 20px;
|
||||
}
|
||||
|
||||
/* 树形组件样式 */
|
||||
.access-tree {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.access-tree .el-tree-node__content {
|
||||
height: auto;
|
||||
padding: 6px 0;
|
||||
font-size: 14px;
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
.access-tree .el-tree-node__content:hover {
|
||||
background: #f5f5f5;
|
||||
}
|
||||
|
||||
.clickable-tree .el-tree-node__content {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.clickable-tree .el-tree-node__content:hover {
|
||||
background: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
/* 滚动条样式 */
|
||||
.access-tree::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
|
||||
.access-tree::-webkit-scrollbar-track {
|
||||
background: #f5f5f5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.access-tree::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.access-tree::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.container {
|
||||
padding: 0 16px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.profile-main {
|
||||
padding: 16px 0;
|
||||
}
|
||||
|
||||
.container {
|
||||
padding: 0 12px;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
padding: 16px 12px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -8,14 +8,14 @@ permalink: /core/identity/
|
||||
## 前言
|
||||
OpenAuth.Net支持两种登录认证方式:Token认证和==自己搭建=={.tip}的OpenAuth.IdentityServer认证。
|
||||
|
||||
这两种方式通过配置webapi或mvc的appsettings.json可以自由切换:
|
||||
这两种方式通过配置webapi的appsettings.json可以自由切换:
|
||||
|
||||
```json
|
||||
"IdentityServerUrl": "http://localhost:12796", //IdentityServer服务器地址。如果为空,则不启用OAuth认证
|
||||
```
|
||||
## Token认证
|
||||
|
||||
当我们启动OpenAuth.WebApi/Mvc时,如果IdentityServerUrl为空,则采用普通的token认证,这时不需要启动OpenAuth.Identity项目:
|
||||
当我们启动OpenAuth.WebApi时,如果IdentityServerUrl为空,则采用普通的token认证,这时不需要启动OpenAuth.Identity项目:
|
||||
```json
|
||||
"IdentityServerUrl": "", //如果为空,则采用普通的token认证
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user