45 Commits
7.1 ... v7.3

Author SHA1 Message Date
wintel
44d5b286aa 切换成SqlSugar 2025-03-16 23:39:30 +08:00
wintel
5664792162 新增测试数据源 2025-03-15 20:36:05 +08:00
wintel
3be8644769 routine update 2025-03-15 00:40:40 +08:00
wintel
32e0ae1a6b 完成外部数据源CRUD 2025-03-15 00:38:08 +08:00
yubaolee
ee396146d3 增加验证数据源 2025-03-14 17:30:22 +08:00
yubaolee
98808a65ea 优化vue3代码生成 2025-03-13 14:03:20 +08:00
wintel
4dd882cf90 增加外部数据源管理 2025-03-13 00:31:22 +08:00
wintel
be1c79799d 更新文档 2025-03-12 21:18:49 +08:00
wintel
e84e5d47ee 更新文档 2025-03-12 21:13:44 +08:00
wintel
0570a58743 update doc 2025-03-12 21:07:48 +08:00
wintel
889445bbcf routine update 2025-03-11 21:32:42 +08:00
yubaolee
2d108586e7 fix #I3RHPD 完成在api中对接口权限进行鉴权 2025-03-11 17:34:54 +08:00
yubaolee
18c6fbfaad fix #I3RHPD 在api中对用户接口权限进行鉴权 2025-03-11 11:21:56 +08:00
wintel
50769b94c6 增加同步API资源到资源列表接口 2025-03-11 01:58:35 +08:00
wintel
a5bbfad2ff 增加获取所有API的接口 2025-03-11 00:40:35 +08:00
wintel
89c3133889 fix #I5FK9U 2025-03-10 21:39:21 +08:00
yubaolee
7ad9b54fd2 完善文档 2025-03-10 14:47:56 +08:00
wintel
66e33c779c fix #IBRS0G 流程详情及审批界面需要统一 2025-03-08 15:58:11 +08:00
wintel
430ae8c377 fix #IBRS0G 流程详情及审批界面需要统一 2025-03-08 15:54:55 +08:00
wintel
a76aa99cb9 fix #I9KL18 当前审核人审核通过后,下个审核人还没审核,当前审核人可以撤回 2025-03-08 15:01:17 +08:00
yubaolee
4e6287808b 完成layui table点击行选中优化 2025-03-07 09:53:41 +08:00
wintel
12d327ddac 优化点击行可选中 2025-03-06 23:39:54 +08:00
yubaolee
ba511edf5b 修复底部遮挡 2025-03-05 17:45:37 +08:00
yubaolee
ea1cbb0f6b 修复pg存储的时候时间少8个小时 2025-03-05 14:33:29 +08:00
wintel
09c47a7a07 [try]字典管理调整为异步 2025-03-03 22:09:21 +08:00
wintel
0392a1ff30 修复字典管理缺少字段 2025-03-03 22:02:58 +08:00
yubaolee
6a290fe30f 完成pgsql脚本调整 2025-02-26 16:20:35 +08:00
yubaolee
92be71ac49 完成pgSql结构调整 2025-02-26 10:48:26 +08:00
wintel
7316e17d3c 修复因调整数据结构导致的获取流程通知用户异常 2025-02-26 07:24:19 +08:00
wintel
6fc4300521 加签记录到流程历史中 2025-02-25 23:54:53 +08:00
wintel
1369b08f70 fix #IBLLAG 加签人员驳回失败 2025-02-25 22:44:23 +08:00
yubaolee
04a557b6e6 优化CodeSmith代码生成 2025-02-25 15:52:30 +08:00
yubaolee
b909a98d96 完善文档 2025-02-25 11:10:00 +08:00
yubaolee
85d409f846 mysql调整为小写 2025-02-24 16:44:57 +08:00
yubaolee
6b3ae29a19 mysql转小写 2025-02-24 16:40:54 +08:00
yubaolee
5eec669cde 调整代码生成器生成变量命名方式 2025-02-24 12:18:45 +08:00
wintel
329c2c9f07 解决冲突 2025-02-23 21:21:36 +08:00
wintel
f6b7ba8b88 修复.NET bool类型转换为pgSql int2 2025-02-23 21:18:14 +08:00
wintel
b6a2707097 修复.NET bool类型转换为pgSql int2 2025-02-23 21:11:42 +08:00
wintel
8608aea1e9 修复因调整Comment字段导致的表头不显示问题 2025-02-23 11:18:09 +08:00
wintel
b8963d0557 调整pgj脚本 2025-02-23 11:01:00 +08:00
wintel
1db14b7973 去除pgSql驼峰 2025-02-23 08:28:24 +08:00
wintel
e244246028 修复模块添加菜单时异常 2025-02-22 20:16:46 +08:00
wintel
d07b75201d 优化代码生成器 2025-02-20 20:54:20 +08:00
wintel
b887ae372b 修复代码生成器size默认值 2025-02-20 20:43:54 +08:00
77 changed files with 4133 additions and 3189 deletions

View File

@@ -7,27 +7,22 @@ Description: 列表页面
<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context"
Description="连接的数据库" %>
<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %>
<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %>
<%@ Assembly Name="SchemaExplorer" %>
<%@ Import Namespace="SchemaExplorer" %>
<%@ Assembly Src="Util.cs" %>
<%@ Import Namespace="Util" %>
@section header
{
<link rel="stylesheet" href="/css/treetable.css" />
}
<blockquote class="layui-elem-quote news_search toolList" id="menus">
</blockquote>
<div class="layui-card">
<table class="layui-table" id="mainList"
lay-data="{height: 'full-80', page:true, id:'mainList'}"
lay-filter="list" lay-size="sm">
</table>
<script type="text/html" id="Disable">
{{# if(d.Disable){ }}
<span class="layui-badge">已禁用</span>
@@ -35,41 +30,46 @@ Description="连接的数据库" %>
<span class="layui-badge layui-bg-green">正常</span>
{{# } }}
</script>
</div>
<!--添加/编辑窗口-->
<div id="divEdit" style="display: none">
<form class="layui-form" action="" id="formEdit">
<form class="layui-form" action="" id="formEdit" lay-filter="formEdit">
<% foreach (ColumnSchema column in this.SourceTable.Columns) {
var typename = CSharpAlias[column.SystemType.FullName];
//Response.WriteLine(typename);
if(column.IsPrimaryKeyMember){%>
<input type="hidden" name="<%=column.Name%>" v-model="tmp.<%=column.Name%>" />
<input type="hidden" name="<%=column.Name%>" />
<%}else if(CSharpAlias[column.SystemType.FullName] == "bool") {%>
<div class="layui-form-item">
<label class="layui-form-label"><%=Tools.GetDescription(column)%></label>
<div class="layui-input-block">
<input type="checkbox" name="<%=column.Name%>" v-model="tmp.<%=column.Name%>" lay-skin="switch" value="1">
<input type="checkbox" name="<%=column.Name%>" lay-skin="switch" value="1">
</div>
</div>
<%}else if(CSharpAlias[column.SystemType.FullName] == "System.DateTime") {%>
<div class="layui-form-item">
<label class="layui-form-label"><%=Tools.GetDescription(column)%></label>
<div class="layui-input-block">
<input type="text" name="<%=column.Name%>" id="<%=column.Name%>" lay-verify="date" placeholder="yyyy-MM-dd" autocomplete="off" class="layui-input">
</div>
</div>
<%}else if(CSharpAlias[column.SystemType.FullName] == "int" ) {%>
<div class="layui-form-item">
<label class="layui-form-label"><%=Tools.GetDescription(column)%></label>
<div class="layui-input-block">
<input type="radio" name="<%=column.Name%>" value="1" title="value1" checked>
<input type="radio" name="<%=column.Name%>" value="0" title="value2">
<input type="number" name="<%=column.Name%>" placeholder="" autocomplete="off" class="layui-input" min="0" step="1" lay-affix="number">
</div>
</div>
<%} else {%>
<div class="layui-form-item">
<label class="layui-form-label"><%=Tools.GetDescription(column)%></label>
<div class="layui-input-block">
<input type="text" name="<%=column.Name%>" v-model="tmp.<%=column.Name%>" required lay-verify="required"
<input type="text" name="<%=column.Name%>" required lay-verify="required"
placeholder="<%=Tools.GetDescription(column)%>" autocomplete="off" class="layui-input">
</div>
</div>
<%} %>
<%} %>
<%if(Tools.NeedCascade(SourceTable)){ %>
<div class="layui-form-item">
<label class="layui-form-label">所属部门</label>
@@ -82,7 +82,6 @@ Description="连接的数据库" %>
</div>
</div>
<%} %>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn" lay-submit lay-filter="formSubmit">立即提交</button>
@@ -91,8 +90,5 @@ Description="连接的数据库" %>
</div>
</form>
</div>
<script type="text/javascript" src="/layui/layui.js"></script>
<script type="text/javascript" src="/userJs/<%=ModuleName.ToLower()%>s.js"></script>

View File

@@ -4,6 +4,8 @@ Author: yubaolee
--%>
<%@ CodeTemplate Language="C#" TargetLanguage="C#" Debug="False" Encoding="utf-8" Description="添加模块" %>
<%@ Property Name="ModuleName" Type="String" Category="Context" Description="模块名称" %>
<%@ Property Name="SourceTable" Type="SchemaExplorer.TableSchema" Category="Context"
Description="连接的数据库" %>
<%@ Map Name="CSharpAlias" Src="System-CSharpAlias" Description="System to C# Type Map" %>
<%@ Assembly Name="SchemaExplorer" %>
@@ -13,9 +15,10 @@ Author: yubaolee
layui.config({
base: "/js/"
}).use(['form', 'vue', 'ztree', 'layer', 'jquery', 'table', 'droptree', 'openauth', 'utils'], function () {
}).use(['form', 'laydate', 'ztree', 'layer', 'jquery', 'table', 'droptree', 'openauth', 'utils'], function () {
var form = layui.form,
layer = layui.layer,
laydate = layui.laydate,
$ = layui.jquery;
var table = layui.table;
var openauth = layui.openauth;
@@ -23,30 +26,49 @@ layui.config({
$("#menus").loadMenus("<%=ModuleName%>");
<% foreach (ColumnSchema column in this.SourceTable.Columns) {
if(CSharpAlias[column.SystemType.FullName] == "System.DateTime") { %>
laydate.render({
elem: '#<%=column.Name%>'
});
<%} }%>
var initVal = { //初始化的值
<% foreach (ColumnSchema column in this.SourceTable.Columns) {
if(CSharpAlias[column.SystemType.FullName] == "bool") { %>
<%=column.Name%>: false,
<%}else if(CSharpAlias[column.SystemType.FullName] == "int" ) {%>
<%=column.Name%>: 0,
<%}else if(CSharpAlias[column.SystemType.FullName] == "System.DateTime") {%>
<%=column.Name%>: new Date().toISOString().split('T')[0],
<%} else {%>
<%=column.Name%>: '',
<%} }%>
}
//加载表头
$.getJSON('/<%=ModuleName%>s/Load',
{ page: 1, limit: 1 },
function (data) {
var columns = data.columnFields.filter(u => u.IsList ===true).map(function (e) {
return {
field: e.ColumnName,
title: e.Comment
};
});
columns.unshift({
type: 'checkbox',
fixed: 'left'
});
table.render({
elem: '#mainList',
page: true,
url: '/<%=ModuleName%>s/Load',
cols: [columns]
, response: {
statusCode: 200 //规定成功的状态码默认0
}
});
{ page: 1, limit: 1 },
function (data) {
var columns = data.columnFields.filter(u => u.IsList ===true).map(function (e) {
return {
field: e.ColumnName,
title: e.Remark
};
});
columns.unshift({
type: 'checkbox',
fixed: 'left'
});
table.render({
elem: '#mainList',
page: true,
url: '/<%=ModuleName%>s/Load',
cols: [columns]
, response: {
statusCode: 200 //规定成功的状态码默认0
}
});
});
@@ -69,9 +91,7 @@ layui.config({
//添加(编辑)对话框
var editDlg = function () {
var vm;
var update = false; //是否为更新
var show = function (data) {
var show = function (update, data) {
var title = update ? "编辑信息" : "添加";
layer.open({
title: title,
@@ -79,32 +99,11 @@ layui.config({
type: 1,
content: $('#divEdit'),
success: function () {
if(vm == undefined){
vm = new Vue({
el: "#formEdit",
data(){
return {
tmp:data //使用一个tmp封装一下后面可以直接用vm.tmp赋值
}
},
watch:{
tmp(val){
this.$nextTick(function () {
form.render(); //刷新select等
layui.droptree("/Applications/GetList", "#AppName", "#AppId", false);
})
}
},
mounted(){
form.render();
layui.droptree("/Applications/GetList", "#AppName", "#AppId", false);
}
});
}else{
vm.tmp = Object.assign({}, vm.tmp,data)
}
if(data == undefined){
form.val("formEdit", initVal);
}else{
form.val("formEdit", data);
}
},
end: mainList
});
@@ -125,15 +124,11 @@ layui.config({
});
}
return {
add: function () { //弹出添加
update = false;
show({
Id: ''
});
add: function () { //弹出添加
show(false);
},
update: function (data) { //弹出编辑框
update = true;
show(data);
show(true, data);
}
};
}();

View File

@@ -37,6 +37,8 @@
//流程实例知会用户
public const string INSTANCE_NOTICE_USER = "INSTANCE_NOTICE_USER";
//流程实例知会角色
public const string INSTANCE_NOTICE_ROLE = "INSTANCE_NOTICE_ROLE";
public const string INSTANCE_NOTICE_ROLE = "INSTANCE_NOTICE_ROLE";
public const string API = "API_RESOURCE";
}
}

View File

@@ -35,10 +35,6 @@ namespace OpenAuth.App
public T Get(string id)
{
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
{
return SugarClient.Queryable<T>().Where("\"Id\"=@id", new {id = id}).First();
}
return SugarClient.Queryable<T>().Where("Id=@id", new {id = id}).First();
}

View File

@@ -60,14 +60,14 @@ namespace OpenAuth.App
return result;
}
public void Add(AddOrUpdateCategoryReq req)
public async Task Add(AddOrUpdateCategoryReq req)
{
var obj = req.MapTo<Category>();
obj.CreateTime = DateTime.Now;
var user = _auth.GetCurrentUser().User;
obj.CreateUserId = user.Id;
obj.CreateUserName = user.Name;
Repository.Add(obj);
await Repository.AddAsync(obj);
}
public void Update(AddOrUpdateCategoryReq obj)
@@ -82,8 +82,8 @@ namespace OpenAuth.App
TypeId = obj.TypeId,
UpdateTime = DateTime.Now,
UpdateUserId = user.Id,
UpdateUserName = user.Name
//todo:要修改的字段赋值
UpdateUserName = user.Name,
SortNo = obj.SortNo
});
}

View File

@@ -3,34 +3,31 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Linq;
using Autofac.Extensions.DependencyInjection;
using Humanizer;
using Infrastructure;
using Infrastructure.Utilities;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Options;
using OpenAuth.Repository;
using OpenAuth.Repository.Core;
using OpenAuth.Repository.Domain;
using OpenAuth.Repository.QueryObj;
using SqlSugar;
namespace OpenAuth.App
{
public class DbExtension
{
private List<DbContext> _contexts = new List<DbContext>();
protected ISqlSugarClient SugarClient;
private IOptions<AppSetting> _appConfiguration;
private IHttpContextAccessor _httpContextAccessor;
public DbExtension(IOptions<AppSetting> appConfiguration, OpenAuthDBContext openAuthDbContext, IHttpContextAccessor httpContextAccessor)
public DbExtension(IOptions<AppSetting> appConfiguration, ISqlSugarClient sugarClient, IHttpContextAccessor httpContextAccessor)
{
_appConfiguration = appConfiguration;
_httpContextAccessor = httpContextAccessor;
_contexts.Add(openAuthDbContext); //如果有多个DBContext可以按OpenAuthDBContext同样的方式添加到_contexts中
SugarClient = sugarClient;
}
/// <summary>
@@ -42,27 +39,26 @@ namespace OpenAuth.App
public List<BuilderTableColumn> GetTableColumnsFromDb(string moduleName)
{
var result = new List<BuilderTableColumn>();
const string domain = "openauth.repository.domain.";
IEntityType entity = null;
_contexts.ForEach(u =>
{
entity = u.Model.GetEntityTypes()
.FirstOrDefault(u => u.Name.ToLower() == domain + moduleName.ToLower());
});
if (entity == null)
//获取所有继承了StringEntity的类型
//单元测试时,这个一直报错????奇怪
var entiTypes = typeof(StringEntity).Assembly.GetTypes().Where(u => typeof(StringEntity).IsAssignableFrom(u) && !u.IsAbstract).ToList();
var entityType = entiTypes.FirstOrDefault(u => u.Name.ToLower() == moduleName.ToLower());
if (entityType == null)
{
throw new Exception($"未能找到{moduleName}对应的实体类");
}
foreach (var property in entity.ClrType.GetProperties())
foreach (var property in entityType.GetProperties())
{
object[] objs = property.GetCustomAttributes(typeof(DescriptionAttribute), true);
object[] browsableObjs = property.GetCustomAttributes(typeof(BrowsableAttribute), true);
var description = objs.Length > 0 ? ((DescriptionAttribute) objs[0]).Description : property.Name;
var description = objs.Length > 0 ? ((DescriptionAttribute)objs[0]).Description : property.Name;
if (string.IsNullOrEmpty(description)) description = property.Name;
//如果没有BrowsableAttribute或 [Browsable(true)]表示可见,其他均为不可见,需要前端配合显示
bool browsable = browsableObjs == null || browsableObjs.Length == 0 ||
((BrowsableAttribute) browsableObjs[0]).Browsable;
((BrowsableAttribute)browsableObjs[0]).Browsable;
var typeName = property.PropertyType.Name;
if (Nullable.GetUnderlyingType(property.PropertyType) != null)
{
@@ -81,29 +77,6 @@ namespace OpenAuth.App
return result;
}
/// <summary>
/// 获取数据库DbContext中所有的实体名称。
/// <para>注意!并不能获取数据库中的所有表</para>
/// </summary>
public List<string> GetDbEntityNames()
{
var names = new List<string>();
var models = _contexts.Select(u =>u.Model);
foreach (var model in models)
{
// Get all the entity types information contained in the DbContext class, ...
var entityTypes = model.GetEntityTypes();
foreach (var entityType in entityTypes)
{
var tableNameAnnotation = entityType.GetAnnotation("Relational:TableName");
names.Add(tableNameAnnotation.Value.ToString());
}
}
return names;
}
/// <summary>
/// 获取数据库表结构信息
/// </summary>
@@ -135,63 +108,62 @@ namespace OpenAuth.App
/// </summary>
private IList<SysTableColumn> GetOracleStructure(string tableName)
{
tableName = tableName.ToUpper();
var sql = $@"
select utc.column_name as columnname
, utc.data_type columntype
, utc.data_length maxlength
, case utc.nullable when 'N' then 0 else 1 end isnull
, utc.data_default defaultval
, ucc.comments ""COMMENT""
, utc.table_name tablename
, case utc.column_name
when (select col.column_name
from user_constraints con
, user_cons_columns col
where con.constraint_name = col.constraint_name
and con.constraint_type = 'P'
and col.table_name = '{tableName}') then 1
else 0 end as iskey
, case
when utc.column_name in ('CreateUserId', 'UpdateUserId', 'Id')
then 0
else 1
end as isdisplay
, case
when data_type in ('BIT', 'BOOL') then
'bool'
when data_type in ('SMALLINT') then 'short'
when data_type in ('TINYINT') then 'bool'
when data_type in ('NUMBER', 'CHAR', 'INT', 'Year') then
'int'
when data_type in ('BIGINT') then
'bigint'
when data_type in ('FLOAT', 'DOUBLE', 'DECIMAL') then
'decimal'
when data_type in
('CHAR', 'VARCHAR', 'TINY TEXT', 'TEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TINYBLOB', 'BLOB',
'MEDIUMBLOB', 'LONGBLOB', 'Time') then
'string'
when data_type in ('Date', 'DateTime', 'TIMESTAMP(6)') then
'DateTime'
else 'string'
end as entitytype
from user_tab_columns utc
, user_col_comments ucc
where utc.table_name = ucc.table_name
and utc.column_name = ucc.column_name
and utc.table_name = '{tableName}'
order by column_id; ";
foreach (var context in _contexts)
select utc.column_name as columnname
, utc.data_type columntype
, utc.data_length maxlength
, case utc.nullable when 'N' then 0 else 1 end isnull
, utc.data_default defaultval
, ucc.comments ""COMMENT""
, utc.table_name tablename
, case utc.column_name
when (select col.column_name
from user_constraints con
, user_cons_columns col
where con.constraint_name = col.constraint_name
and con.constraint_type = 'P'
and col.table_name = '{tableName}') then 1
else 0 end as iskey
, case
when utc.column_name in ('CreateUserId', 'UpdateUserId', 'Id')
then 0
else 1
end as isdisplay
, case
when data_type in ('BIT', 'BOOL') then
'bool'
when data_type in ('SMALLINT') then 'short'
when data_type in ('TINYINT') then 'bool'
when data_type in ('NUMBER', 'CHAR', 'INT', 'Year') then
'int'
when data_type in ('BIGINT') then
'bigint'
when data_type in ('FLOAT', 'DOUBLE', 'DECIMAL') then
'decimal'
when data_type in
('CHAR', 'VARCHAR', 'TINY TEXT', 'TEXT', 'MEDIUMTEXT', 'LONGTEXT', 'TINYBLOB', 'BLOB',
'MEDIUMBLOB', 'LONGBLOB', 'Time') then
'string'
when data_type in ('DATE', 'DATETIME', 'TIMESTAMP(6)') then
'DateTime'
else 'string'
end as entitytype
from user_tab_columns utc
, user_col_comments ucc
where utc.table_name = ucc.table_name
and utc.column_name = ucc.column_name
and utc.table_name = '{tableName}'
order by column_id; ";
var columns = SugarClient.SqlQueryable<SysTableColumn>(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
var columns = context.Set<SysTableColumn>().FromSqlRaw(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
return columnList;
}
columnList.ForEach(u => u.ColumnName = u.ColumnName.Transform(To.LowerCase, To.TitleCase));
return columnList;
}
return new List<SysTableColumn>();
}
@@ -200,9 +172,9 @@ order by column_id; ";
/// </summary>
private IList<SysTableColumn> GetMySqlStructure(string tableName)
{
var sql = $@"SELECT DISTINCT
var sql = $@"SELECT DISTINCT
Column_Name AS ColumnName,
'{ tableName}' as tableName,
'{tableName}' as tableName,
Column_Comment AS Comment,
data_type as ColumnType,
CASE
@@ -247,105 +219,104 @@ order by column_id; ";
WHERE
table_name = '{tableName}'";
foreach (var context in _contexts)
{
var columns = context.Set<SysTableColumn>().FromSqlRaw(sql);
var columns = SugarClient.SqlQueryable<SysTableColumn>(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
columnList.ForEach(u => u.ColumnName = u.ColumnName.Transform(To.LowerCase, To.TitleCase));
return columnList;
}
}
return new List<SysTableColumn>();
}
/// <summary>
/// 获取Mysql表结构信息
/// <summary>
/// 获取pgsql表结构信息
/// </summary>
private IList<SysTableColumn> GetPostgreStructure(string tableName)
{
var sql = $@"select attr.attrelid
, schema.nspname as schemaname
, class.relname as tablename --表名
, attr.attname as columnname --
, attr.atttypid --类型ID
, attr.atttypmod
, case when con.conname is not null then 1 else 0 end as iskey --是否主键
, case when attr.attnotnull is true then 0 else 1 end as isnull --可空
, t.typname columntype --类型名称
, case
when typname in ('BIT', 'BOOL', 'bit', 'bool') then
'bool'
when typname in ('smallint', 'SMALLINT') then 'short'
when typname in ('tinyint', 'TINYINT') then 'bool'
when typname in ('int4', 'int', 'INT') then
'int'
when typname in ('BIGINT', 'bigint') then
'bigint'
when typname in ('FLOAT', 'DOUBLE', 'DECIMAL', 'float', 'double', 'decimal') then
'decimal'
when typname in ('varchar', 'text') then
'string'
when typname in ('Date', 'DateTime', 'TimeStamp', 'date', 'datetime', 'timestamp') then
'DateTime'
else 'string'
end as entitytype
, comment.description as comment
, 1 as iscolumndata
, case
when t.typname = 'varchar' or t.typname = 'bpchar' then attr.atttypmod - 4
else 10
end as columnwidth
, 0 as orderno
, case
when t.typname = 'varchar' or t.typname = 'bpchar' then attr.atttypmod - 4
else 10
end as maxlength
, case
when attname in ('createid', 'modifyid', '')
or con.conname is null then 0
else 1
end as isdisplay --是否显示
from pg_catalog.pg_class class
inner join pg_catalog.pg_attribute attr on attr.attrelid = class.oid
left join pg_catalog.pg_constraint con
on con.conrelid = class.oid and attr.attnum = any (con.conkey) and con.contype = 'p'
left join pg_catalog.pg_type t on attr.atttypid = t.oid
inner join pg_catalog.pg_description comment
on comment.objoid = attr.attrelid and comment.objsubid = attr.attnum
inner join pg_catalog.pg_namespace schema on schema.oid = class.relnamespace
where attr.attnum > 0
and not attr.attisdropped
and schema.nspname = 'public' -- replace 'your_schema' with your schema name
and class.relname = '{tableName}'";
tableName = tableName.ToLower();
var sql = $@"select attr.attrelid
, schema.nspname as schemaname
, class.relname as tablename --
, attr.attname as columnname --列名
, attr.atttypid --类型ID
, attr.atttypmod
, case when con.conname is not null then 1 else 0 end as iskey --是否主键
, case when attr.attnotnull is true then 0 else 1 end as isnull --可空
, t.typname columntype --类型名称
, case
when typname in ('BIT', 'BOOL', 'bit', 'bool') then
'bool'
when typname in ('smallint', 'SMALLINT') then 'short'
when typname in ('tinyint', 'TINYINT') then 'bool'
when typname in ('int4', 'int', 'INT') then
'int'
when typname in ('BIGINT', 'bigint') then
'bigint'
when typname in ('FLOAT', 'DOUBLE', 'DECIMAL', 'float', 'double', 'decimal') then
'decimal'
when typname in ('varchar', 'text') then
'string'
when typname in ('Date', 'DateTime', 'TimeStamp', 'date', 'datetime', 'timestamp') then
'DateTime'
else 'string'
end as entitytype
, comment.description as comment
, 1 as iscolumndata
, case
when t.typname = 'varchar' or t.typname = 'bpchar' then attr.atttypmod - 4
else 10
end as columnwidth
, 0 as orderno
, case
when t.typname = 'varchar' or t.typname = 'bpchar' then attr.atttypmod - 4
else 10
end as maxlength
, case
when attname in ('createid', 'modifyid', '')
or con.conname is null then 0
else 1
end as isdisplay --是否显示
from pg_catalog.pg_class class
inner join pg_catalog.pg_attribute attr on attr.attrelid = class.oid
left join pg_catalog.pg_constraint con
on con.conrelid = class.oid and attr.attnum = any (con.conkey) and con.contype = 'p'
left join pg_catalog.pg_type t on attr.atttypid = t.oid
inner join pg_catalog.pg_description comment
on comment.objoid = attr.attrelid and comment.objsubid = attr.attnum
inner join pg_catalog.pg_namespace schema on schema.oid = class.relnamespace
where attr.attnum > 0
and not attr.attisdropped
and schema.nspname = 'public' -- replace 'your_schema' with your schema name
and class.relname = '{tableName}'";
foreach (var context in _contexts)
var columns = SugarClient.SqlQueryable<SysTableColumn>(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
var columns = context.Set<SysTableColumn>().FromSqlRaw(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
return columnList;
}
columnList.ForEach(u => u.ColumnName = u.ColumnName.Transform(To.LowerCase, To.TitleCase));
return columnList;
}
return new List<SysTableColumn>();
}
/// <summary>
/// <summary>
/// 获取SqlServer表结构信息
/// </summary>
/// <param name="tableName"></param>
/// <returns></returns>
private IList<SysTableColumn> GetSqlServerStructure(string tableName)
{
var sql = $@"
var sql = $@"
SELECT TableName,
LTRIM(RTRIM(ColumnName)) AS ColumnName,
Comment,
@@ -426,17 +397,14 @@ where attr.attnum > 0
LEFT JOIN sys.extended_properties epTwo ON obj.id = epTwo.major_id
AND epTwo.minor_id = 0
AND epTwo.name = 'MS_Description'
WHERE obj.name = '{ tableName}') AS t
WHERE obj.name = '{tableName}') AS t
ORDER BY t.colorder";
foreach (var context in _contexts)
var columns = SugarClient.SqlQueryable<SysTableColumn>(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
var columns = context.Set<SysTableColumn>().FromSqlRaw(sql);
var columnList = columns?.ToList();
if (columnList != null && columnList.Any())
{
return columnList;
}
return columnList;
}
return new List<SysTableColumn>();
}

View File

@@ -0,0 +1,115 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Infrastructure;
using OpenAuth.App.Interface;
using OpenAuth.App.Request;
using OpenAuth.App.Response;
using OpenAuth.Repository.Domain;
using SqlSugar;
namespace OpenAuth.App
{
public class ExtDataSourceApp : SqlSugarBaseApp<ExternalDataSource>
{
/// <summary>
/// 加载列表
/// </summary>
public async Task<TableData> Load(QueryExternalDataSourceListReq request)
{
var loginContext = _auth.GetCurrentUser();
if (loginContext == null)
{
throw new CommonException("登录已过期", Define.INVALID_TOKEN);
}
var result = new TableData();
var objs = GetDataPrivilege("u");
if (!string.IsNullOrEmpty(request.key))
{
objs = objs.Where(u => u.Name.Contains(request.key));
}
result.data = objs.OrderBy(u => u.Name)
.Skip((request.page - 1) * request.limit)
.Take(request.limit).ToList();
result.count = await objs.CountAsync();
return result;
}
/// <summary>
/// 获取数据库类型
/// </summary>
/// <returns></returns>
public TableData GetDbTypes()
{
var result = new TableData();
// 创建包含键值对的列表
var dbTypeList = Enum.GetValues(typeof(DbType))
.Cast<DbType>()
.Select(item => new { label = item.ToString(), value = (int)item })
.ToList();
result.data = dbTypeList;
return result;
}
/// <summary>
/// 测试数据源连接
/// </summary>
/// <param name="id">数据源ID</param>
/// <returns>是否连接成功</returns>
public bool TestConnection(TestConnectionReq req)
{
var conn = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = req.Connectionstring,
DbType = (DbType)Enum.Parse(typeof(DbType), req.Dbtype.ToString()),
IsAutoCloseConnection = true,
});
conn.Open();
return true;
}
public void Add(AddOrUpdateExternalDataSourceReq req)
{
var obj = req.MapTo<ExternalDataSource>();
obj.Id = Guid.NewGuid().ToString();
obj.Createtime = DateTime.Now;
var user = _auth.GetCurrentUser().User;
obj.Createuserid = user.Id;
obj.Createusername = user.Name;
Repository.Insert(obj);
}
public void Update(AddOrUpdateExternalDataSourceReq obj)
{
var user = _auth.GetCurrentUser().User;
Repository.Update(u => new ExternalDataSource
{
Name = obj.Name,
Dbtype = obj.Dbtype,
Databasename = obj.Databasename,
Server = obj.Server,
Port = obj.Port,
Username = obj.Username,
Password = obj.Password,
Description = obj.Description,
Enabled = obj.Enabled,
Testsuccess = obj.Testsuccess,
Connectionstring = obj.Connectionstring,
Updatetime = DateTime.Now,
Updateuserid = user.Id,
Updateusername = user.Name
},u => u.Id == obj.Id);
}
public ExtDataSourceApp(ISqlSugarClient client, IAuth auth) : base(client, auth)
{
}
}
public record TestConnectionReq(string Connectionstring, int Dbtype);
}

View File

@@ -0,0 +1,83 @@
//------------------------------------------------------------------------------
// This code was generated by a CodeSmith Template.
//
// DO NOT MODIFY contents of this file. Changes to this
// file will be lost if the code is regenerated.
// Author:Yubao Li
//------------------------------------------------------------------------------
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using OpenAuth.Repository.Core;
namespace OpenAuth.App.Request
{
/// <summary>
///
/// </summary>
public class AddOrUpdateExternalDataSourceReq
{
/// <summary>
///是否测试成功
/// </summary>
public bool? Testsuccess { get; set; }
/// <summary>
///主键
/// </summary>
public string Id { get; set; }
/// <summary>
///是否启用
/// </summary>
public bool Enabled { get; set; }
/// <summary>
///连接字符串
/// </summary>
public string Connectionstring { get; set; }
/// <summary>
///描述
/// </summary>
public string Description { get; set; }
/// <summary>
///密码
/// </summary>
public string Password { get; set; }
/// <summary>
///用户名
/// </summary>
public string Username { get; set; }
/// <summary>
///端口
/// </summary>
public int? Port { get; set; }
/// <summary>
///服务器地址
/// </summary>
public string Server { get; set; }
/// <summary>
///数据库名称
/// </summary>
public string Databasename { get; set; }
/// <summary>
///数据库类型
/// </summary>
public int Dbtype { get; set; }
/// <summary>
///数据源名称
/// </summary>
public string Name { get; set; }
}
}

View File

@@ -0,0 +1,7 @@
namespace OpenAuth.App.Request
{
public class QueryExternalDataSourceListReq : PageReq
{
}
}

View File

@@ -538,6 +538,57 @@ namespace OpenAuth.App.Flow
}
}
/// <summary>
/// 撤销当前节点的审批
/// </summary>
public void UndoVerification()
{
// 已结束的流程不能撤销
if (flowInstance.IsFinish == FlowInstanceStatus.Finished
|| flowInstance.IsFinish == FlowInstanceStatus.Rejected)
{
throw new Exception("流程已结束,不能撤销");
}
if(Nodes[previousId].type == FlowNode.START)
{
throw new Exception("没有任何审批,不能撤销!你可以删除或召回这个流程");
}
// 恢复到上一个节点
currentNodeId = flowInstance.PreviousId;
flowInstance.ActivityId = currentNodeId;
flowInstance.ActivityType = GetNodeType(currentNodeId);
flowInstance.ActivityName = Nodes[currentNodeId].name;
//向前查找ActivityId的前一个结点即连线指向ActivityId的节点
flowInstance.PreviousId = GetPreNode().id;
flowInstance.MakerList = GetNodeMarkers(Nodes[currentNodeId]);
// 清除当前节点的审批状态
currentNode.setInfo.Taged = null;
currentNode.setInfo.UserId = "";
currentNode.setInfo.UserName = "";
currentNode.setInfo.Description = "";
currentNode.setInfo.TagedTime = "";
//删除当前节点的扭转记录
var user = AutofacContainerModule.GetService<IAuth>().GetCurrentUser().User;
var SugarClient = AutofacContainerModule.GetService<ISqlSugarClient>();
SugarClient.Deleteable<FlowInstanceTransitionHistory>().Where(u => u.InstanceId == flowInstanceId && u.CreateUserId == user.Id && u.FromNodeId == currentNodeId).ExecuteCommand();
//删除当前节点的审批记录(只删除最新的一条)
var latestRecord = SugarClient.Queryable<FlowInstanceOperationHistory>()
.Where(u => u.InstanceId == flowInstanceId && u.CreateUserId == user.Id)
.OrderByDescending(u => u.CreateDate)
.First();
if (latestRecord != null)
{
SugarClient.Deleteable<FlowInstanceOperationHistory>()
.Where(u => u.Id == latestRecord.Id)
.ExecuteCommand();
}
}
#endregion
#region

View File

@@ -7,7 +7,6 @@ using OpenAuth.App.Interface;
using OpenAuth.App.Request;
using OpenAuth.App.Response;
using OpenAuth.Repository.Domain;
using Quartz.Listener;
using SqlSugar;
@@ -51,6 +50,15 @@ namespace OpenAuth.App
}
Repository.InsertRange(objs);
SugarClient.Insertable(new FlowInstanceOperationHistory
{
InstanceId = obj.InstanceId,
CreateUserId = loginContext.User.Id,
CreateUserName = loginContext.User.Name,
CreateDate = DateTime.Now,
Content = $"【加签】{loginContext.User.Name}加签给:{string.Join(",", objs.Select(u => u.ApproverName))}"
}).ExecuteCommand();
}
public void Update(AddApproverReq application)

View File

@@ -2,7 +2,7 @@
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
* @Date: 2024-12-13 16:55:17
* @Description: 工作流实例表操作
* @LastEditTime: 2025-02-19 11:43:24
* @LastEditTime: 2025-03-08 13:59:47
* Copyright (c) 2024 by yubaolee | ahfu~ , All Rights Reserved.
*/
@@ -332,6 +332,40 @@ namespace OpenAuth.App
return true;
}
/// <summary>
/// 撤销当前节点的审批
/// </summary>
public void UndoVerification(UndoVerificationReq request)
{
var flowInstance = Get(request.FlowInstanceId);
var user = _auth.GetCurrentUser().User;
// 获取运行实例
var wfruntime = new FlowRuntime(flowInstance);
SugarClient.Ado.BeginTran();
try
{
wfruntime.UndoVerification();
// 更新流程实例状态
flowInstance.IsFinish = FlowInstanceStatus.Running;
flowInstance.SchemeContent = JsonHelper.Instance.Serialize(wfruntime.ToSchemeObj());
SugarClient.Updateable(flowInstance).ExecuteCommand();
// 记录撤销操作
// wfruntime.SaveOperationHis($"【撤销审批】{user.Name}撤销了对【{wfruntime.currentNode.name}】的审批。备注:{request.Description}");
SugarClient.Ado.CommitTran();
}
catch (Exception ex)
{
SugarClient.Ado.RollbackTran();
throw new Exception("撤销审批失败:" + ex.Message);
}
}
/// <summary>
/// 会签
/// </summary>
@@ -507,7 +541,15 @@ namespace OpenAuth.App
{
var user = _auth.GetCurrentUser().User;
FlowInstance flowInstance = Get(reqest.FlowInstanceId);
if (flowInstance.MakerList != "1" && !flowInstance.MakerList.Contains(user.Id))
var approvers = _flowApproverApp.GetApproverIds(new QueryApproverReq
{
FlowInstanceId = reqest.FlowInstanceId,
ActivityId = flowInstance.ActivityId
});
if (flowInstance.MakerList != "1" && !flowInstance.MakerList.Contains(user.Id) &&
!approvers.Contains(user.Id))
{
throw new Exception("当前用户没有驳回该节点权限");
}
@@ -582,7 +624,32 @@ namespace OpenAuth.App
resp.NextNodeDesignateType = runtime.nextNode.setInfo.NodeDesignate;
resp.CanWriteFormItemIds = runtime.currentNode.setInfo.CanWriteFormItemIds;
}
var user = _auth.GetCurrentUser();
var query = SugarClient.Queryable<FlowInstanceOperationHistory>()
.OrderByDescending(u => u.CreateDate)
.First(u => u.InstanceId == id);
if (query != null)
{
//最后一个审批人是当前用户,可以撤销
resp.CanUndoVerify = query.CreateUserId == user.User.Id;
}
var approvers = _flowApproverApp.GetApproverIds(new QueryApproverReq
{
FlowInstanceId = id,
ActivityId = flowinstance.ActivityId
});
if (flowinstance.MakerList != "1" && !flowinstance.MakerList.Contains(user.User.Id) &&
!approvers.Contains(user.User.Id))
{
resp.CanVerify = false;
}
else
{
resp.CanVerify = true; //当前登录用户可以审批当前流程
}
return resp;
}
@@ -609,6 +676,12 @@ namespace OpenAuth.App
from SysUser
where fi.MakerList like '%' || Id || '%') ";
}
else if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
{
groupConcatSql = $@" (select string_agg(Account, ',')
from SysUser
where fi.MakerList like '%' || Id || '%') ";
}
string sql = String.Empty;
@@ -697,17 +770,17 @@ namespace OpenAuth.App
select distinct FirstId as InstanceId
from Relevance rel
inner join FlowInstance flow on rel.FirstId = flow.Id and flow.IsFinish = 1
where `Key` = '{Define.INSTANCE_NOTICE_USER}'
where `key` = '{Define.INSTANCE_NOTICE_USER}'
and SecondId = '{user.User.Id}'
union
select distinct a.FirstId as InstanceId
from Relevance a
inner join (select SecondId as RoleId
from Relevance
where `Key` = 'UserRole'
where `key` = 'UserRole'
and FirstId = '{user.User.Id}') b on a.SecondId = b.RoleId
inner join FlowInstance flow on a.FirstId = flow.Id and flow.IsFinish = 1
where a.`Key` = '{Define.INSTANCE_NOTICE_ROLE}') UniqueInstanceIds
where a.`key` = '{Define.INSTANCE_NOTICE_ROLE}') UniqueInstanceIds
ON fi.Id = UniqueInstanceIds.InstanceId
";
}
@@ -715,11 +788,14 @@ namespace OpenAuth.App
switch (SugarClient.CurrentConnectionConfig.DbType)
{
case DbType.SqlServer:
sql = sql.Replace("`Key`", "[Key]");
sql = sql.Replace("`key`", "[Key]");
sql = sql.Replace("from dual", "");
break;
case DbType.Oracle:
sql = sql.Replace("`Key`", "\"KEY\"");
sql = sql.Replace("`key`", "\"KEY\"");
break;
case DbType.PostgreSQL:
sql = sql.Replace("`key`", "key");
break;
}

View File

@@ -0,0 +1,17 @@
public class UndoVerificationReq
{
/// <summary>
/// 流程实例ID
/// </summary>
public string FlowInstanceId { get; set; }
/// <summary>
/// 撤销的节点ID
/// </summary>
public string NodeId { get; set; }
/// <summary>
/// 撤销说明
/// </summary>
public string Description { get; set; }
}

View File

@@ -37,5 +37,17 @@ namespace OpenAuth.App.Response
/// 当前节点的可写表单Id
/// </summary>
public string[] CanWriteFormItemIds { get; set; }
/// <summary>
/// 是否可以撤销审核
///<param>流程最后一个已审批节点的审批人是当前登录人</param>
/// </summary>
public bool CanUndoVerify { get; set; }
/// <summary>
/// 是否可以审核
///<param>待审批的节点需要当前登录人审批</param>
/// </summary>
public bool CanVerify{ get; set; }
}
}

View File

@@ -19,6 +19,7 @@
<PackageReference Include="Autofac" Version="8.1.1" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="Autofac.Extras.Quartz" Version="5.1.0" />
<PackageReference Include="Humanizer.Core" Version="2.14.1" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.DependencyModel" Version="5.0.0" />
@@ -26,6 +27,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.23" />
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="NUnit" Version="3.13.1" />
<PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />

View File

@@ -0,0 +1,17 @@
namespace OpenAuth.App.Request
{
/// <summary>
/// 角色分配资源
/// </summary>
public class AssignRoleResources
{
/// <summary>
/// 角色id
/// </summary>
public string RoleId { get; set; }
/// <summary>
/// 资源id列表
/// </summary>
public string[] ResourceIds { get; set; }
}
}

View File

@@ -234,5 +234,28 @@ namespace OpenAuth.App
UnitWork.Save();
});
}
/// <summary>
/// 为角色分配资源,需要统一提交,会删除以前该角色的所有资源
/// </summary>
/// <param name="request"></param>
public void AssignRoleResources(AssignRoleResources request)
{
UnitWork.ExecuteWithTransaction(() =>
{
//删除以前的所有资源
UnitWork.Delete<Relevance>(u => u.FirstId == request.RoleId && u.Key == Define.ROLERESOURCE);
//批量分配角色资源
UnitWork.BatchAdd((from firstId in request.ResourceIds
select new Relevance
{
Key = Define.ROLERESOURCE,
FirstId = request.RoleId,
SecondId = firstId,
OperateTime = DateTime.Now
}).ToArray());
UnitWork.Save();
});
}
}
}

View File

@@ -1,8 +1,10 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.AccessControl;
using System.Threading.Tasks;
using Infrastructure;
using NUnit.Framework;
using OpenAuth.App.Interface;
using OpenAuth.App.Request;
using OpenAuth.App.Response;
@@ -17,6 +19,16 @@ namespace OpenAuth.App
public class ResourceApp:SqlSugarBaseApp<SysResource>
{
private RevelanceManagerApp _revelanceApp;
private ApiService _apiService;
private readonly IAuth _auth;
public ResourceApp(ISqlSugarClient client, IAuth auth, RevelanceManagerApp revelanceApp, ApiService apiService) : base(client, auth)
{
_revelanceApp = revelanceApp;
_apiService = apiService;
_auth = auth;
}
public void Add(AddOrUpdateResReq resource)
{
@@ -85,10 +97,7 @@ namespace OpenAuth.App
}
var columnnames = columnFields.Select(u => u.ColumnName);
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
{
columnnames = columnFields.Select(u => "\"" + u.ColumnName +"\"");
}
var propertyStr = string.Join(',', columnnames);
result.columnFields = columnFields;
result.data = resources.OrderBy(u => u.TypeId)
@@ -98,10 +107,92 @@ namespace OpenAuth.App
return result;
}
public ResourceApp(ISqlSugarClient client, IAuth auth, RevelanceManagerApp revelanceApp) : base(client, auth)
/// <summary>
/// 获取资源类型
/// </summary>
/// <returns></returns>
public async Task<List<SysResourceApp>> GetResourceApps()
{
_revelanceApp = revelanceApp;
var types = await SugarClient.Queryable<SysResource>()
.Distinct()
.Select(u => new {u.AppId,u.AppName})
.ToListAsync();
return types.Select(u => new SysResourceApp(u.AppId, u.AppName)).ToList();
}
/// <summary>
/// 同步站点API到资源列表
/// <para>读取站点API信息如果资源列表中不存在则添加</para>
/// </summary>
public async Task Sync()
{
var apis = await _apiService.GetSwaggerEndpoints();
var user = _auth.GetCurrentUser().User;
foreach (var api in apis)
{
//检查资源是否存在
var resource = Repository.GetFirst(u => u.Name == api.Path && u.TypeId == Define.API);
if (resource != null)
{
continue;
}
resource = new SysResource
{
Id = api.Path,
Name = api.Path,
Disable = false,
SortNo = 0,
AppId = $"{Define.API}_{api.Tag}",
AppName = $"API接口-{api.Tag}",
TypeId = Define.API,
TypeName = "API接口",
Description = api.Summary??"",
CreateTime = DateTime.Now,
CreateUserId = user.Id,
CreateUserName = user.Name
};
CaculateCascade(resource);
Repository.Insert(resource);
}
}
/// <summary>
/// 判断当前登录用户是否拥有访问该API的权限
/// <para>如果角色没有做任何分配,则默认拥有权限。这个可以根据实际需要修改。</para>
/// </summary>
/// <param name="apiPath">API路径</param>
/// <returns>true:拥有权限,false:没有权限</returns>
public bool CanAccess(string apiPath)
{
var loginContext = _auth.GetCurrentUser();
if (loginContext == null)
{
throw new CommonException("登录已过期", Define.INVALID_TOKEN);
}
//如果当前登录用户是管理员,则拥有所有权限
if(loginContext.User.Account == Define.SYSTEM_USERNAME){
return true;
}
var elementIds = _revelanceApp.Get(Define.ROLERESOURCE, true, loginContext.Roles.Select(u => u.Id).ToArray());
//如果角色没有做任何分配,则默认拥有权限。这个可以根据实际需要修改。
if(elementIds.Count == 0)
{
return true;
}
//如果分配了资源,则判断是否拥有权限
var resource = Repository.GetFirst(u => u.Name.Contains(apiPath) && u.TypeId == Define.API && elementIds.Contains(u.Id));
if(resource == null)
{
return false;
}
return true;
}
}
/// <summary>
/// 资源类型
/// </summary>
public record SysResourceApp(string Id, string Name);
}

View File

@@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Nodes;
using System.Threading.Tasks;
using Infrastructure;
using OpenAuth.App.Interface;
@@ -41,10 +40,7 @@ namespace OpenAuth.App
}
var columnnames = columnFields.Select(u => u.ColumnName);
if (SugarClient.CurrentConnectionConfig.DbType == DbType.PostgreSQL)
{
columnnames = columnFields.Select(u => "\"" + u.ColumnName +"\"");
}
var propertyStr = string.Join(',', columnnames);
result.columnFields = columnFields;
result.data = objs.OrderByDescending(u => u.CreateTime)

View File

@@ -0,0 +1,99 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.OpenApi.Readers;
public class ApiService
{
private readonly IHttpClientFactory _httpClientFactory;
private readonly IConfiguration _configuration;
public ApiService(IHttpClientFactory httpClientFactory, IConfiguration configuration)
{
_httpClientFactory = httpClientFactory;
_configuration = configuration;
}
/// <summary>
/// 获取所有API接口信息
/// <para>这个方法单元测试必须启动WebApi站点</para>
/// </summary>
public async Task<List<SwaggerEndpointInfo>> GetSwaggerEndpoints()
{
var reader = new OpenApiStringReader();
var client = _httpClientFactory.CreateClient();
var baseUrl = _configuration["AppSetting:HttpHost"]?.Replace("*", "localhost");
var apis = new List<SwaggerEndpointInfo>();
var controllers = GetControllers();
foreach (var controller in controllers)
{
var groupname = GetSwaggerGroupName(controller);
var swaggerJsonUrl = $"{baseUrl}/swagger/{groupname}/swagger.json";
var response = await client.GetAsync(swaggerJsonUrl).ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var document = reader.Read(content, out var diagnostic);
//获取所有api
var controllerApis = document.Paths
.SelectMany(path => path.Value.Operations
.Select(op => new SwaggerEndpointInfo(
path.Key,
op.Key.ToString(),
op.Value.Summary,
op.Value.Description,
op.Value.Tags.FirstOrDefault()?.Name)));
apis.AddRange(controllerApis);
}
return apis;
}
/// <summary>
/// 获取控制器对应的swagger分组值
/// </summary>
private string GetSwaggerGroupName(Type controller)
{
var groupname = controller.Name.Replace("Controller", "");
var apisetting = controller.GetCustomAttribute(typeof(ApiExplorerSettingsAttribute));
if (apisetting != null)
{
groupname = ((ApiExplorerSettingsAttribute)apisetting).GroupName;
}
return groupname;
}
/// <summary>
/// 获取所有的控制器
/// </summary>
private List<Type> GetControllers()
{
var webApiAssembly = AppDomain.CurrentDomain.GetAssemblies()
.FirstOrDefault(a => a.GetName().Name.Contains("OpenAuth.WebApi"));
var controlleractionlist = webApiAssembly.GetTypes()
.Where(type => typeof(Microsoft.AspNetCore.Mvc.ControllerBase).IsAssignableFrom(type))
.ToList();
return controlleractionlist;
}
}
public record SwaggerEndpointInfo(
string Path,
string HttpMethod,
string Summary,
string Description,
string Tag);

View File

@@ -77,13 +77,27 @@ namespace OpenAuth.App.Test
{
DbType = dbType.Value,
ConnectionString = connectionString,
IsAutoCloseConnection = true,
MoreSettings=new ConnMoreSettings() {
PgSqlIsAutoToLower = false,//增删查改支持驼峰表
PgSqlIsAutoToLowerCodeFirst = false, // 建表建驼峰表。5.1.3.30
IsAutoToUpper=false //禁用自动转成大写表
}
IsAutoCloseConnection = true
});
if(dbType.Value != SqlSugar.DbType.PostgreSQL){
return sqlSugar;
}
// 配置bool类型转换为smallint
sqlSugar.Aop.OnExecutingChangeSql = (sql, parameters) =>
{
foreach (var param in parameters)
{
if (param.Value is bool boolValue)
{
param.DbType = System.Data.DbType.Int16;
// 将 bool 转换为 smallint
param.Value = boolValue ? (short)1 : (short)0;
}
}
// 返回修改后的 SQL 和参数
return new System.Collections.Generic.KeyValuePair<string, SugarParameter[]>(sql, parameters);
};
return sqlSugar;
});

View File

@@ -19,15 +19,6 @@ namespace OpenAuth.App.Test
var result = app.GetTableColumnsFromDb("Category");
Console.WriteLine(JsonHelper.Instance.Serialize(result));
}
[Test]
public void GetDbEntityNames()
{
var app = _autofacServiceProvider.GetService<DbExtension>();
var result = app.GetDbEntityNames();
Console.WriteLine(JsonHelper.Instance.Serialize(result));
}
[Test]
public void TestGetTables()

View File

@@ -324,29 +324,31 @@ namespace OpenAuth.App
{
var sql = $@"
select u.*
from `USER` u
from sysuser u
join (select distinct SecondId as UserId
from Relevance
where `Key` = '{Define.INSTANCE_NOTICE_USER}'
where `key` = '{Define.INSTANCE_NOTICE_USER}'
and FirstId = '{instanceId}'
union
select distinct FirstId as UserId
from Relevance a
inner join (select SecondId as RoleId
from Relevance
where `Key` = '{Define.INSTANCE_NOTICE_ROLE}'
where `key` = '{Define.INSTANCE_NOTICE_ROLE}'
and FirstId = '{instanceId}') b on a.SecondId = b.RoleId
where `Key` = 'UserRole') userids on u.Id = userids.UserId";
where `key` = 'UserRole') userids on u.Id = userids.UserId";
if (UnitWork.GetDbContext().Database.GetDbConnection().GetType().Name == "SqlConnection")
{
sql = sql.Replace(" `USER` ", " [USER] ");
sql = sql.Replace("`Key`", "[Key]");
sql = sql.Replace("`key`", "[Key]");
}
else if (UnitWork.GetDbContext().Database.GetDbConnection().GetType().Name == "OracleConnection")
{
sql = sql.Replace(" `USER` ", " \"USER\" ");
sql = sql.Replace("`Key`", "\"Key\"");
sql = sql.Replace("`key`", "\"KEY\"");
}
else if (UnitWork.GetDbContext().Database.GetDbConnection().GetType().Name == "NpgsqlConnection")
{
sql = sql.Replace("`key`", "\"key\"");
}
var users = UnitWork.FromSql<SysUser>(sql);

View File

@@ -104,13 +104,27 @@ namespace OpenAuth.IdentityServer
{
DbType = sugarDbtype.Value,
ConnectionString = connectionString,
IsAutoCloseConnection = true,
MoreSettings=new ConnMoreSettings() {
PgSqlIsAutoToLower = false,//增删查改支持驼峰表
PgSqlIsAutoToLowerCodeFirst = false, // 建表建驼峰表。5.1.3.30
IsAutoToUpper=false //禁用自动转成大写表
}
IsAutoCloseConnection = true
});
if(sugarDbtype.Value != SqlSugar.DbType.PostgreSQL){
return sqlSugar;
}
// 配置bool类型转换为smallint
sqlSugar.Aop.OnExecutingChangeSql = (sql, parameters) =>
{
foreach (var param in parameters)
{
if (param.Value is bool boolValue)
{
param.DbType = System.Data.DbType.Int16;
// 将 bool 转换为 smallint
param.Value = boolValue ? (short)1 : (short)0;
}
}
// 返回修改后的 SQL 和参数
return new System.Collections.Generic.KeyValuePair<string, SugarParameter[]>(sql, parameters);
};
return sqlSugar;
});

View File

@@ -27,14 +27,14 @@ namespace OpenAuth.Mvc.Controllers
return View();
}
public async Task<string> All([FromQuery]QueryCategoryListReq request)
public async Task<string> All([FromQuery] QueryCategoryListReq request)
{
TableData data = new TableData();
data = await _app.Load(request);
return JsonHelper.Instance.Serialize(data);
}
[HttpPost]
[HttpPost]
public string Delete(string[] ids)
{
Response resp = new Response();
@@ -50,13 +50,13 @@ namespace OpenAuth.Mvc.Controllers
return JsonHelper.Instance.Serialize(resp);
}
[HttpPost]
public string Add(AddOrUpdateCategoryReq obj)
[HttpPost]
public async Task<string> Add(AddOrUpdateCategoryReq obj)
{
Response resp = new Response();
try
{
_app.Add(obj);
await _app.Add(obj);
}
catch (Exception e)
{
@@ -66,7 +66,7 @@ namespace OpenAuth.Mvc.Controllers
return JsonHelper.Instance.Serialize(resp);
}
[HttpPost]
[HttpPost]
public string Update(AddOrUpdateCategoryReq obj)
{
Response resp = new Response();
@@ -99,7 +99,7 @@ namespace OpenAuth.Mvc.Controllers
resp.Message = e.Message;
}
return JsonHelper.Instance.Serialize(resp);
}
}

View File

@@ -109,12 +109,28 @@ namespace OpenAuth.Mvc
{
DbType = dbType.Value,
ConnectionString = connectionString,
IsAutoCloseConnection = true,
MoreSettings=new ConnMoreSettings() {
PgSqlIsAutoToLower = false,//增删查改支持驼峰表
PgSqlIsAutoToLowerCodeFirst = false // 建表建驼峰表。5.1.3.30
}
IsAutoCloseConnection = true
}, db => { db.Aop.OnLogExecuting = (sql, pars) => { logger.LogInformation(sql); }; });
if(dbType.Value != SqlSugar.DbType.PostgreSQL){
return sqlSugar;
}
// 配置bool类型转换为smallint
sqlSugar.Aop.OnExecutingChangeSql = (sql, parameters) =>
{
foreach (var param in parameters)
{
if (param.Value is bool boolValue)
{
param.DbType = System.Data.DbType.Int16;
// 将 bool 转换为 smallint
param.Value = boolValue ? (short)1 : (short)0;
}
}
// 返回修改后的 SQL 和参数
return new System.Collections.Generic.KeyValuePair<string, SugarParameter[]>(sql, parameters);
};
return sqlSugar;
});

View File

@@ -1,16 +1,15 @@
@section header
{
<link rel="stylesheet" href="/css/treetable.css"/>
<link rel="stylesheet" href="/js/dtree/dtree.css"/>
<link rel="stylesheet" href="/js/dtree/font/dtreefont.css"/>
<link rel="stylesheet" href="/css/treetable.css" />
<link rel="stylesheet" href="/js/dtree/dtree.css" />
<link rel="stylesheet" href="/js/dtree/font/dtreefont.css" />
}
<blockquote class="layui-elem-quote news_search toolList" id="menus">
</blockquote>
<table id="mainList"
lay-data="{height: 'full-80',cellMinWidth:80, page:true, id:'mainList'}"
lay-filter="list" lay-size="sm">
<table id="mainList" lay-data="{height: 'full-80',cellMinWidth:80, page:true, id:'mainList'}" lay-filter="list"
lay-size="sm">
</table>
<script type="text/html" id="barList">
@@ -22,27 +21,33 @@
<!--用户添加/编辑窗口-->
<div id="divEdit" style="display: none">
<form class="layui-form layui-form-pane" action="" id="formEdit" lay-filter="formEdit">
<input type="hidden" name="Id" />
<div class="layui-form-item">
<label class="layui-form-label">类型标识</label>
<div class="layui-input-block">
<input type="text" name="Id"
placeholder="请输入类型标识" autocomplete="off" class="layui-input">
<input type="text" name="DtCode" placeholder="请输入类型标识" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">名称</label>
<div class="layui-input-block">
<input type="text" name="Name" required lay-verify="required"
placeholder="请输入名称" autocomplete="off" class="layui-input">
<input type="text" name="Name" required lay-verify="required" placeholder="请输入名称" autocomplete="off"
class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">值</label>
<div class="layui-input-block">
<input type="text" name="DtValue" placeholder="请输入值" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">类型分类</label>
<div class="layui-input-block">
<input id="TypeName" name="TypeName" required lay-verify="required" class="layui-input"/>
<input id="TypeId" name="TypeId" required lay-verify="required" type="hidden"/>
<input id="TypeName" name="TypeName" required lay-verify="required" class="layui-input" />
<input id="TypeId" name="TypeId" required lay-verify="required" type="hidden" />
</div>
</div>
@@ -50,8 +55,7 @@
<div class="layui-form-item">
<label class="layui-form-label">描述</label>
<div class="layui-input-block">
<input type="text" name="Description"
placeholder="请输入描述" autocomplete="off" class="layui-input">
<input type="text" name="Description" placeholder="请输入描述" autocomplete="off" class="layui-input">
</div>
</div>

View File

@@ -122,7 +122,7 @@ tpwidget("init", {
</div>
<!-- 底部 -->
<div class="layui-footer footer">
<p>copyright @@2024 yubaolee  <a onclick="donation()" class="layui-btn layui-btn-danger l·ayui-btn-small">捐赠作者</a></p>
<p>copyright @@2025 yubaolee  <a onclick="donation()" class="layui-btn layui-btn-danger l·ayui-btn-small">捐赠作者</a></p>
</div>
</div>

View File

@@ -18,7 +18,7 @@
<div class="panel col">
<a href="javascript:;" data-url="/Rolemanager/index">
<div class="panel_icon" style="background-color:#FF5722;">
<i class="layui-icon layui-icon-vercode" ></i>
<i class="layui-icon layui-icon-vercode" ></i>
</div>
<div class="panel_word roles">
<span></span>
@@ -75,14 +75,14 @@
<p>官方博客:<a href="http://www.cnblogs.com/yubaolee/">http://www.cnblogs.com/yubaolee/</a> </p>
<p>
系统默认System账号登录可以查看所有权限如果用其他账号admin/test可以查看相应的授权/可见字段情况。
数据库密码明文存储不要问为什么不加密因为你要问这些账号的密码我也记不住啊O(∩_∩)O
</p>
<p>
<a href="https://gitee.com/dotnetchina/OpenAuth.Net" target="_blank" class="layui-btn layui-btn-xs layui-btn-danger">项目地址</a>
<a class="layui-btn layui-btn-xs" target="_blank" href="http://doc.openauth.net.cn">在线文档</a>
<a class="layui-btn layui-btn-xs layui-btn-danger" target="_blank" href="http://demo.openauth.net.cn:1803">企业版/高级版入口</a>
<a class="layui-btn layui-btn-xs layui-btn-danger" target="_blank" href="http://demo.openauth.net.cn:1803">企业Vue2演示</a>
<a class="layui-btn layui-btn-xs layui-btn-danger" target="_blank" href="http://demo.openauth.net.cn:1805">企业Vue3演示</a>
<a class="layui-btn layui-btn-xs layui-btn-danger" target="_blank" href="http://demo.openauth.net.cn:1804">企业版H5入口请使用移动模式或者直接手机查看</a>
<span style="color: #f00;">注:【本框架遵循LGPL开源协议企业单位如商用请联系作者授权谢谢】</span>
<span style="color: #f00;">注:【本框架遵循Apache开源协议可以免费商用】</span>
</p>
<p>技术交流QQ群484498493【已满】 &nbsp; &nbsp; 626433139【已满】 &nbsp;&nbsp; 566344079</p>
</blockquote>

View File

@@ -198,4 +198,4 @@
</div>
<script type="text/javascript" src="/layui/layui.js"></script>
<script type="text/javascript" src="/userJs/modules.js?v2"></script>
<script type="text/javascript" src="/userJs/modules.js?v2.1"></script>

View File

@@ -82,7 +82,7 @@
.layui-body{overflow:hidden; border-top:5px solid #1AA094;border-left:2px solid #1AA094;}
.layui-tab-content{ height:100%; padding:0; }
.layui-tab-item{ position: absolute; top: 41px; bottom:0; left: 0; right: 0; padding: 0; margin: 0; -webkit-overflow-scrolling:touch; overflow:auto;}
.layui-tab-item{ position: absolute; top: 41px; bottom:27px; left: 0; right: 0; padding: 0; margin: 0; -webkit-overflow-scrolling:touch; overflow:auto;}
.layui-tab-title .layui-this{ background-color:#18A093;}
.layui-tab-title .layui-this:after{ border:none; }
.layui-tab-title li cite{ font-style: normal; padding-left:5px; }

View File

@@ -1,15 +1,15 @@
var $,tab,skyconsWeather;
var $, tab, skyconsWeather;
layui.config({
base : "/js/"
}).use(['bodyTab','form','element','layer','jquery','cookie'],function(){
base: "/js/"
}).use(['bodyTab', 'form', 'element', 'layer', 'jquery', 'cookie'], function () {
var form = layui.form,
layer = layui.layer,
element = layui.element;
$ = layui.jquery;
tab = layui.bodyTab({
openTabNum : "50", //最大可打开窗口数量
url: "/UserSession/GetModulesTree" //获取菜单json地址
});
$ = layui.jquery;
tab = layui.bodyTab({
openTabNum: "50", //最大可打开窗口数量
url: "/UserSession/GetModulesTree" //获取菜单json地址
});
$(".menu_three").on("click", function () {
@@ -27,131 +27,131 @@ layui.config({
$(this).addClass('three_this'); // 添加当前元素的样式
})
$.get('/UserSession/GetUserName',
function(data) {
$("#username").html(data);
$("#usernametop").html(data);
});
$.get('/UserSession/GetUserName',
function (data) {
$("#username").html(data);
$("#usernametop").html(data);
});
//todo:默认登录后取第一个机构的id作为默认可以在【个人中心】界面修改默认
//todo:默认登录后取第一个机构的id作为默认可以在【个人中心】界面修改默认
//在大型业务系统中,应该让用户登录成功后弹出选择框选择操作的机构
$.get('/UserSession/GetOrgs',
function(data) {
var orgs = JSON.parse(data).Result;
function (data) {
var orgs = JSON.parse(data).Result;
var defaultorgId = orgs[0].Id;
$.cookie('defaultorgid', defaultorgId,{path: '/'});
$.cookie('defaultorgid', defaultorgId, { path: '/' });
});
//更换皮肤
function skins(){
function skins() {
var skin = window.sessionStorage.getItem("skin");
if (skin) { //如果更换过皮肤
if (window.sessionStorage.getItem("skinValue") != "自定义") {
$("body").addClass(window.sessionStorage.getItem("skin"));
} else {
$(".layui-layout-admin .layui-header").css("background-color", skin.split(',')[0]);
$(".layui-bg-black").css("background-color", skin.split(',')[1]);
$(".hideMenu").css("background-color", skin.split(',')[2]);
}
if (window.sessionStorage.getItem("skinValue") != "自定义") {
$("body").addClass(window.sessionStorage.getItem("skin"));
} else {
$(".layui-layout-admin .layui-header").css("background-color", skin.split(',')[0]);
$(".layui-bg-black").css("background-color", skin.split(',')[1]);
$(".hideMenu").css("background-color", skin.split(',')[2]);
}
} else {
$("body").addClass("blue");
$("body").addClass("blue");
}
}
skins();
$(".changeSkin").click(function(){
$(".changeSkin").click(function () {
layer.open({
title : "更换皮肤",
area : ["310px","280px"],
type : "1",
content : '<div class="skins_box">'+
'<form class="layui-form">'+
'<div class="layui-form-item">'+
'<input type="radio" name="skin" value="默认" title="默认" lay-filter="default" checked="">'+
'<input type="radio" name="skin" value="藏青" title="藏青" lay-filter="cyan">'+
'<input type="radio" name="skin" value="蓝色" title="蓝色" lay-filter="blue">'+
'<input type="radio" name="skin" value="自定义" title="自定义" lay-filter="custom">'+
'<div class="skinCustom">'+
'<input type="text" class="layui-input topColor" name="topSkin" placeholder="顶部颜色" />'+
'<input type="text" class="layui-input leftColor" name="leftSkin" placeholder="左侧颜色" />'+
'<input type="text" class="layui-input menuColor" name="btnSkin" placeholder="顶部菜单按钮" />'+
'</div>'+
'</div>'+
'<div class="layui-form-item skinBtn">'+
'<a href="javascript:;" class="layui-btn layui-btn-small layui-btn-normal" lay-submit="" lay-filter="changeSkin">确定更换</a>'+
'<a href="javascript:;" class="layui-btn layui-btn-small layui-btn-primary" lay-submit="" lay-filter="noChangeSkin">我再想想</a>'+
'</div>'+
'</form>'+
'</div>',
success : function(index, layero){
title: "更换皮肤",
area: ["310px", "280px"],
type: "1",
content: '<div class="skins_box">' +
'<form class="layui-form">' +
'<div class="layui-form-item">' +
'<input type="radio" name="skin" value="默认" title="默认" lay-filter="default" checked="">' +
'<input type="radio" name="skin" value="藏青" title="藏青" lay-filter="cyan">' +
'<input type="radio" name="skin" value="蓝色" title="蓝色" lay-filter="blue">' +
'<input type="radio" name="skin" value="自定义" title="自定义" lay-filter="custom">' +
'<div class="skinCustom">' +
'<input type="text" class="layui-input topColor" name="topSkin" placeholder="顶部颜色" />' +
'<input type="text" class="layui-input leftColor" name="leftSkin" placeholder="左侧颜色" />' +
'<input type="text" class="layui-input menuColor" name="btnSkin" placeholder="顶部菜单按钮" />' +
'</div>' +
'</div>' +
'<div class="layui-form-item skinBtn">' +
'<a href="javascript:;" class="layui-btn layui-btn-small layui-btn-normal" lay-submit="" lay-filter="changeSkin">确定更换</a>' +
'<a href="javascript:;" class="layui-btn layui-btn-small layui-btn-primary" lay-submit="" lay-filter="noChangeSkin">我再想想</a>' +
'</div>' +
'</form>' +
'</div>',
success: function (index, layero) {
var skin = window.sessionStorage.getItem("skin");
if(window.sessionStorage.getItem("skinValue")){
$(".skins_box input[value="+window.sessionStorage.getItem("skinValue")+"]").attr("checked","checked");
if (window.sessionStorage.getItem("skinValue")) {
$(".skins_box input[value=" + window.sessionStorage.getItem("skinValue") + "]").attr("checked", "checked");
};
if($(".skins_box input[value=自定义]").attr("checked")){
$(".skinCustom").css("visibility","inherit");
if ($(".skins_box input[value=自定义]").attr("checked")) {
$(".skinCustom").css("visibility", "inherit");
$(".topColor").val(skin.split(',')[0]);
$(".leftColor").val(skin.split(',')[1]);
$(".menuColor").val(skin.split(',')[2]);
};
form.render();
$(".skins_box").removeClass("layui-hide");
$(".skins_box .layui-form-radio").on("click",function(){
$(".skins_box .layui-form-radio").on("click", function () {
var skinColor;
if($(this).find("span").text() == "藏青"){
if ($(this).find("span").text() == "藏青") {
skinColor = "cyan";
}else if($(this).find("span").text() == "蓝色"){
} else if ($(this).find("span").text() == "蓝色") {
skinColor = "blue";
}else if($(this).find("span").text() == "默认"){
} else if ($(this).find("span").text() == "默认") {
skinColor = "";
}
if($(this).find("span").text() != "自定义"){
if ($(this).find("span").text() != "自定义") {
$(".topColor,.leftColor,.menuColor").val('');
$("body").removeAttr("class").addClass("main_body "+skinColor+"");
$("body").removeAttr("class").addClass("main_body " + skinColor + "");
$(".skinCustom").removeAttr("style");
$(".layui-bg-black,.hideMenu,.layui-layout-admin .layui-header").removeAttr("style");
}else{
$(".skinCustom").css("visibility","inherit");
} else {
$(".skinCustom").css("visibility", "inherit");
}
})
var skinStr,skinColor;
$(".topColor").blur(function(){
$(".layui-layout-admin .layui-header").css("background-color",$(this).val());
var skinStr, skinColor;
$(".topColor").blur(function () {
$(".layui-layout-admin .layui-header").css("background-color", $(this).val());
})
$(".leftColor").blur(function(){
$(".layui-bg-black").css("background-color",$(this).val());
$(".leftColor").blur(function () {
$(".layui-bg-black").css("background-color", $(this).val());
})
$(".menuColor").blur(function(){
$(".hideMenu").css("background-color",$(this).val());
$(".menuColor").blur(function () {
$(".hideMenu").css("background-color", $(this).val());
})
form.on("submit(changeSkin)",function(data){
if(data.field.skin != "自定义"){
if(data.field.skin == "藏青"){
form.on("submit(changeSkin)", function (data) {
if (data.field.skin != "自定义") {
if (data.field.skin == "藏青") {
skinColor = "cyan";
}else if(data.field.skin == "蓝色"){
} else if (data.field.skin == "蓝色") {
skinColor = "blue";
}else if(data.field.skin == "默认"){
} else if (data.field.skin == "默认") {
skinColor = "";
}
window.sessionStorage.setItem("skin",skinColor);
}else{
skinStr = $(".topColor").val()+','+$(".leftColor").val()+','+$(".menuColor").val();
window.sessionStorage.setItem("skin",skinStr);
window.sessionStorage.setItem("skin", skinColor);
} else {
skinStr = $(".topColor").val() + ',' + $(".leftColor").val() + ',' + $(".menuColor").val();
window.sessionStorage.setItem("skin", skinStr);
$("body").removeAttr("class").addClass("main_body");
}
window.sessionStorage.setItem("skinValue",data.field.skin);
window.sessionStorage.setItem("skinValue", data.field.skin);
layer.closeAll("page");
});
form.on("submit(noChangeSkin)",function(){
$("body").removeAttr("class").addClass("main_body "+window.sessionStorage.getItem("skin")+"");
form.on("submit(noChangeSkin)", function () {
$("body").removeAttr("class").addClass("main_body " + window.sessionStorage.getItem("skin") + "");
$(".layui-bg-black,.hideMenu,.layui-layout-admin .layui-header").removeAttr("style");
skins();
layer.closeAll("page");
});
},
cancel : function(){
$("body").removeAttr("class").addClass("main_body "+window.sessionStorage.getItem("skin")+"");
cancel: function () {
$("body").removeAttr("class").addClass("main_body " + window.sessionStorage.getItem("skin") + "");
$(".layui-bg-black,.hideMenu,.layui-layout-admin .layui-header").removeAttr("style");
skins();
}
@@ -159,14 +159,14 @@ layui.config({
})
//退出
$(".signOut").click(function(){
$(".signOut").click(function () {
window.sessionStorage.removeItem("menu");
menu = [];
window.sessionStorage.removeItem("curmenu");
})
//隐藏左侧导航
$(".hideMenu").click(function(){
$(".hideMenu").click(function () {
$(".layui-layout-admin").toggleClass("showMenu");
//渲染顶部窗口
tab.tabMove();
@@ -176,43 +176,43 @@ layui.config({
tab.render();
//锁屏
function lockPage(){
function lockPage() {
layer.open({
title : false,
type : 1,
content : ' <div class="admin-header-lock" id="lock-box">'+
'<div class="admin-header-lock-img"><img src="/images/face.jpg"/></div>'+
'<div class="admin-header-lock-name" id="lockUserName">admin</div>'+
'<div class="input_btn">'+
'<input type="password" class="admin-header-lock-input layui-input" autocomplete="off" placeholder="请输入密码解锁.." name="lockPwd" id="lockPwd" />'+
'<button class="layui-btn" id="unlock">解锁</button>'+
'</div>'+
'<p>请输入“123456”否则不会解锁成功哦</p>'+
'</div>',
closeBtn : 0,
shade : 0.9
title: false,
type: 1,
content: ' <div class="admin-header-lock" id="lock-box">' +
'<div class="admin-header-lock-img"><img src="/images/face.jpg"/></div>' +
'<div class="admin-header-lock-name" id="lockUserName">admin</div>' +
'<div class="input_btn">' +
'<input type="password" class="admin-header-lock-input layui-input" autocomplete="off" placeholder="请输入密码解锁.." name="lockPwd" id="lockPwd" />' +
'<button class="layui-btn" id="unlock">解锁</button>' +
'</div>' +
'<p>请输入“123456”否则不会解锁成功哦</p>' +
'</div>',
closeBtn: 0,
shade: 0.9
})
$(".admin-header-lock-input").focus();
}
$(".lockcms").on("click",function(){
window.sessionStorage.setItem("lockcms",true);
$(".lockcms").on("click", function () {
window.sessionStorage.setItem("lockcms", true);
lockPage();
})
// 判断是否显示锁屏
if(window.sessionStorage.getItem("lockcms") == "true"){
if (window.sessionStorage.getItem("lockcms") == "true") {
lockPage();
}
// 解锁
$("body").on("click","#unlock",function(){
if($(this).siblings(".admin-header-lock-input").val() == ''){
$("body").on("click", "#unlock", function () {
if ($(this).siblings(".admin-header-lock-input").val() == '') {
layer.msg("请输入解锁密码!");
$(this).siblings(".admin-header-lock-input").focus();
}else{
if($(this).siblings(".admin-header-lock-input").val() == "123456"){
window.sessionStorage.setItem("lockcms",false);
} else {
if ($(this).siblings(".admin-header-lock-input").val() == "123456") {
window.sessionStorage.setItem("lockcms", false);
$(this).siblings(".admin-header-lock-input").val('');
layer.closeAll("page");
}else{
} else {
layer.msg("密码错误,请重新输入!");
$(this).siblings(".admin-header-lock-input").val('').focus();
}
@@ -223,18 +223,18 @@ layui.config({
var treeMobile = $('.site-tree-mobile'),
shadeMobile = $('.site-mobile-shade')
treeMobile.on('click', function(){
treeMobile.on('click', function () {
$('body').addClass('site-mobile');
});
shadeMobile.on('click', function(){
shadeMobile.on('click', function () {
$('body').removeClass('site-mobile');
});
// 添加新窗口
$("body").on("click",".layui-nav .layui-nav-item a",function(){
$("body").on("click", ".layui-nav .layui-nav-item a", function () {
//如果不存在子级
if($(this).siblings().length == 0){
if ($(this).siblings().length == 0) {
addTab($(this));
$('body').removeClass('site-mobile'); //移动端点击菜单关闭菜单层
}
@@ -242,73 +242,73 @@ layui.config({
})
//公告层
function showNotice(){
function showNotice() {
layer.open({
type: 1,
title: "系统公告",
closeBtn: false,
area: '310px',
shade: 0.8,
id: 'LAY_layuipro',
btn: ['火速围观'],
moveType: 1,
content: '<div style="padding:15px 20px; text-align:justify; line-height: 22px; text-indent:2em;border-bottom:1px solid #e2e2e2;">' +
'<p>郑重提示OpenAuth.Net 6.0新版上线,如以前访问过本站点请清空缓存后访问</p>' +
'<p>喜欢的快快star吧</p></div>',
success: function(layero){
type: 1,
title: "系统公告",
closeBtn: false,
area: '310px',
shade: 0.8,
id: 'LAY_layuipro',
btn: ['火速围观'],
moveType: 1,
content: '<div style="padding:15px 20px; text-align:justify; line-height: 22px; text-indent:2em;border-bottom:1px solid #e2e2e2;">' +
'<p>郑重提示OpenAuth.Net 7.1新版上线,如以前访问过本站点请清空缓存后访问</p>' +
'<p>喜欢的快快star吧</p></div>',
success: function (layero) {
var btn = layero.find('.layui-layer-btn');
btn.css('text-align', 'center');
btn.on("click",function(){
window.sessionStorage.setItem("showNotice","true");
btn.on("click", function () {
window.sessionStorage.setItem("showNotice", "true");
})
if($(window).width() > 432){ //如果页面宽度不足以显示顶部“系统公告”按钮,则不提示
btn.on("click",function(){
if ($(window).width() > 432) { //如果页面宽度不足以显示顶部“系统公告”按钮,则不提示
btn.on("click", function () {
layer.tips('系统公告躲在了这里', '#showNotice', {
tips: 3
});
})
}
}
});
}
});
}
//判断是否处于锁屏状态(如果关闭以后则未关闭浏览器之前不再显示)
if(window.sessionStorage.getItem("lockcms") != "true" && window.sessionStorage.getItem("showNotice") != "true"){
if (window.sessionStorage.getItem("lockcms") != "true" && window.sessionStorage.getItem("showNotice") != "true") {
showNotice();
}
$(".showNotice").on("click",function(){
$(".showNotice").on("click", function () {
showNotice();
})
//刷新后还原打开的窗口
if(window.sessionStorage.getItem("menu") != null){
if (window.sessionStorage.getItem("menu") != null) {
menu = JSON.parse(window.sessionStorage.getItem("menu"));
curmenu = window.sessionStorage.getItem("curmenu");
var openTitle = '';
for(var i=0;i<menu.length;i++){
for (var i = 0; i < menu.length; i++) {
openTitle = '';
if(menu[i].icon){
if(menu[i].icon.split("-")[0] == 'icon'){
openTitle += '<i class="iconfont '+menu[i].icon+'"></i>';
}else{
openTitle += '<i class="layui-icon">'+menu[i].icon+'</i>';
if (menu[i].icon) {
if (menu[i].icon.split("-")[0] == 'icon') {
openTitle += '<i class="iconfont ' + menu[i].icon + '"></i>';
} else {
openTitle += '<i class="layui-icon">' + menu[i].icon + '</i>';
}
}
openTitle += '<cite>'+menu[i].title+'</cite>';
openTitle += '<i class="layui-icon layui-unselect layui-tab-close" data-id="'+menu[i].layId+'">&#x1006;</i>';
element.tabAdd("bodyTab",{
title : openTitle,
content :"<iframe src='"+menu[i].href+"' data-id='"+menu[i].layId+"'></frame>",
id : menu[i].layId
openTitle += '<cite>' + menu[i].title + '</cite>';
openTitle += '<i class="layui-icon layui-unselect layui-tab-close" data-id="' + menu[i].layId + '">&#x1006;</i>';
element.tabAdd("bodyTab", {
title: openTitle,
content: "<iframe src='" + menu[i].href + "' data-id='" + menu[i].layId + "'></frame>",
id: menu[i].layId
})
//定位到刷新前的窗口
if(curmenu != "undefined"){
if(curmenu == '' || curmenu == "null"){ //定位到后台首页
element.tabChange("bodyTab",'');
}else if(JSON.parse(curmenu).title == menu[i].title){ //定位到刷新前的页面
element.tabChange("bodyTab",menu[i].layId);
if (curmenu != "undefined") {
if (curmenu == '' || curmenu == "null") { //定位到后台首页
element.tabChange("bodyTab", '');
} else if (JSON.parse(curmenu).title == menu[i].title) { //定位到刷新前的页面
element.tabChange("bodyTab", menu[i].layId);
}
}else{
element.tabChange("bodyTab",menu[menu.length-1].layId);
} else {
element.tabChange("bodyTab", menu[menu.length - 1].layId);
}
}
//渲染顶部窗口
@@ -316,61 +316,61 @@ layui.config({
}
//刷新当前
$(".refresh").on("click",function(){ //此处添加禁止连续点击刷新一是为了降低服务器压力另外一个就是为了防止超快点击造成chrome本身的一些js文件的报错(不过貌似这个问题还是存在,不过概率小了很多)
if($(this).hasClass("refreshThis")){
$(".refresh").on("click", function () { //此处添加禁止连续点击刷新一是为了降低服务器压力另外一个就是为了防止超快点击造成chrome本身的一些js文件的报错(不过貌似这个问题还是存在,不过概率小了很多)
if ($(this).hasClass("refreshThis")) {
$(this).removeClass("refreshThis");
$(".clildFrame .layui-tab-item.layui-show").find("iframe")[0].contentWindow.location.reload(true);
}else{
} else {
layer.msg("您点击的速度超过了服务器的响应速度,还是等两秒再刷新吧!");
setTimeout(function(){
setTimeout(function () {
$(".refresh").addClass("refreshThis");
},2000)
}, 2000)
}
})
//关闭其他
$(".closePageOther").on("click",function(){
if($("#top_tabs li").length>2 && $("#top_tabs li.layui-this cite").text()!="后台首页"){
$(".closePageOther").on("click", function () {
if ($("#top_tabs li").length > 2 && $("#top_tabs li.layui-this cite").text() != "后台首页") {
var menu = JSON.parse(window.sessionStorage.getItem("menu"));
$("#top_tabs li").each(function(){
if($(this).attr("lay-id") != '' && !$(this).hasClass("layui-this")){
element.tabDelete("bodyTab",$(this).attr("lay-id")).init();
$("#top_tabs li").each(function () {
if ($(this).attr("lay-id") != '' && !$(this).hasClass("layui-this")) {
element.tabDelete("bodyTab", $(this).attr("lay-id")).init();
//此处将当前窗口重新获取放入session避免一个个删除来回循环造成的不必要工作量
for(var i=0;i<menu.length;i++){
if($("#top_tabs li.layui-this cite").text() == menu[i].title){
menu.splice(0,menu.length,menu[i]);
window.sessionStorage.setItem("menu",JSON.stringify(menu));
for (var i = 0; i < menu.length; i++) {
if ($("#top_tabs li.layui-this cite").text() == menu[i].title) {
menu.splice(0, menu.length, menu[i]);
window.sessionStorage.setItem("menu", JSON.stringify(menu));
}
}
}
})
}else if($("#top_tabs li.layui-this cite").text()=="后台首页" && $("#top_tabs li").length>1){
$("#top_tabs li").each(function(){
if($(this).attr("lay-id") != '' && !$(this).hasClass("layui-this")){
element.tabDelete("bodyTab",$(this).attr("lay-id")).init();
} else if ($("#top_tabs li.layui-this cite").text() == "后台首页" && $("#top_tabs li").length > 1) {
$("#top_tabs li").each(function () {
if ($(this).attr("lay-id") != '' && !$(this).hasClass("layui-this")) {
element.tabDelete("bodyTab", $(this).attr("lay-id")).init();
window.sessionStorage.removeItem("menu");
menu = [];
window.sessionStorage.removeItem("curmenu");
}
})
}else{
} else {
layer.msg("没有可以关闭的窗口了@_@");
}
//渲染顶部窗口
tab.tabMove();
})
//关闭全部
$(".closePageAll").on("click",function(){
if($("#top_tabs li").length > 1){
$("#top_tabs li").each(function(){
if($(this).attr("lay-id") != ''){
element.tabDelete("bodyTab",$(this).attr("lay-id")).init();
$(".closePageAll").on("click", function () {
if ($("#top_tabs li").length > 1) {
$("#top_tabs li").each(function () {
if ($(this).attr("lay-id") != '') {
element.tabDelete("bodyTab", $(this).attr("lay-id")).init();
window.sessionStorage.removeItem("menu");
menu = [];
window.sessionStorage.removeItem("curmenu");
}
})
}else{
} else {
layer.msg("没有可以关闭的窗口了@_@");
}
//渲染顶部窗口
@@ -379,20 +379,20 @@ layui.config({
})
//打开新窗口
function addTab(_this){
function addTab(_this) {
tab.tabAdd(_this);
}
//捐赠弹窗
function donation(){
function donation() {
layer.tab({
area : ['260px', '367px'],
tab : [{
title : "微信",
content : "<div style='padding:30px;overflow:hidden;background:#d2d0d0;'><img src='/images/wechat.jpg'></div>"
},{
title : "支付宝",
content : "<div style='padding:30px;overflow:hidden;background:#d2d0d0;'><img src='/images/alipay.jpg'></div>"
area: ['260px', '367px'],
tab: [{
title: "微信",
content: "<div style='padding:30px;overflow:hidden;background:#d2d0d0;'><img src='/images/wechat.jpg'></div>"
}, {
title: "支付宝",
content: "<div style='padding:30px;overflow:hidden;background:#d2d0d0;'><img src='/images/alipay.jpg'></div>"
}]
})
}

View File

@@ -1,4 +1,11 @@

/*
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
* @Date: 2024-11-29 22:16:54
* @LastEditTime: 2025-03-07 09:49:51
* @Description:
* Copyright (c) 2025 by yubaolee | ahfu~ , All Rights Reserved.
*/
layui.define("jquery", function (exports) {
var jQuery = layui.jquery,
$ = layui.jquery;
@@ -29,30 +36,62 @@ layui.define("jquery", function (exports) {
loadMenus: function (modulecode) {
var dom = $(this);
$.ajax("/ModuleManager/LoadAuthorizedMenus?modulecode=" + modulecode,
{
async: false
, success: function (data) {
var obj = JSON.parse(data);
var sb = '';
$.each(obj,
function () {
var element = this;
sb += ("<button " + " data-type='" + element.DomId +
"' " + " class='layui-btn layui-btn-small " + element.Class +
"' " + element.Attr + ">");
{
async: false
, success: function (data) {
var obj = JSON.parse(data);
var sb = '';
$.each(obj,
function () {
var element = this;
sb += ("<button " + " data-type='" + element.DomId +
"' " + " class='layui-btn layui-btn-small " + element.Class +
"' " + element.Attr + ">");
if (element.Icon != null && element.Icon != '') {
sb += ("<i class='layui-icon "+element.Icon+"'></i>");
}
sb += (element.Name + "</button>");
});
if (element.Icon != null && element.Icon != '') {
sb += ("<i class='layui-icon " + element.Icon + "'></i>");
}
sb += (element.Name + "</button>");
});
dom.html(sb);
}
});
dom.html(sb);
}
});
}
});
// 修改laytable点击行选中checkbox
clickTableRow = function (table, id, filter, callback) {
// 监听行单击事件
table.on('row(' + filter + ')', function (obj) {
// 先取消所有行的checkbox的选中状态
var tableBox = obj.tr.parents('.layui-table-box');
tableBox.find('.layui-table-body tr').each(function () {
$(this).find('input[type="checkbox"]').prop('checked', false);
var index = $(this).data('index');
table.cache[id][index].LAY_CHECKED = false;
});
// 取消选中行
table.setRowChecked(id, {
index: 'all', // 所有行
checked: false // 此处若设置 true则表示全选
});
// 设置当前行选中,这个方法可以让checkStatus获取到值但它不能改变checkbox的选中状态😓
table.setRowChecked(id, {
index: obj.tr.data('index')
});
// 设置当前行checkbox 选中
obj.tr.find('input[type="checkbox"]').prop('checked', true);
obj.data.LAY_CHECKED = true;
if (callback) {
callback(obj);
}
});
}
exports('utils');
});

View File

@@ -24,7 +24,7 @@ layui.config({
var columns = data.columnFields.filter(u => u.IsList === true).map(function (e) {
return {
field: e.ColumnName,
title: e.Comment
title: e.Remark
};
});
columns.unshift({
@@ -102,6 +102,7 @@ layui.config({
};
}();
clickTableRow(table,'mainList', 'list');
//监听表格内部按钮
table.on('tool(list)', function (obj) {
var data = obj.data;

View File

@@ -27,7 +27,7 @@
var columns = data.columnFields.filter(u => u.IsList ===true).map(function (e) {
return {
field: e.ColumnName,
title: e.Comment
title: e.Remark
};
});
columns.unshift({

View File

@@ -68,7 +68,7 @@
}();
$("#tree").height($("div.layui-table-view").height());
clickTableRow(table,'mainList', 'list');
//监听表格内部按钮
table.on('tool(list)', function (obj) {
var data = obj.data;

View File

@@ -68,7 +68,7 @@
}();
$("#tree").height($("div.layui-table-view").height());
clickTableRow(table,'mainList', 'list');
//监听表格内部按钮
table.on('tool(list)', function (obj) {
var data = obj.data;

View File

@@ -108,6 +108,7 @@
};
}();
clickTableRow(table,'mainList', 'list');
//监听表格内部按钮
table.on('tool(list)', function (obj) {
var data = obj.data;

View File

@@ -109,6 +109,7 @@
};
}();
clickTableRow(table,'mainList', 'list');
//监听表格内部按钮
table.on('tool(list)', function (obj) {
var data = obj.data;

View File

@@ -108,7 +108,8 @@
}
};
}();
clickTableRow(table,'mainList', 'list');
//监听表格内部按钮
table.on('tool(list)', function (obj) {
var data = obj.data;

View File

@@ -1,32 +1,29 @@
/*
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
* @Date: 2023-12-25 14:43:53
* @LastEditTime: 2024-12-03 14:00:32
* @LastEditTime: 2025-02-25 19:46:11
* @Description:
* Copyright (c) 2024 by yubaolee | ahfu~ , All Rights Reserved.
*/
layui.config({
base : "/js/"
}).use(['form','element','layer','jquery'],function(){
var form = layui.form,
layer = parent.layer === undefined ? layui.layer : parent.layer,
element = layui.element,
$ = layui.jquery;
base: "/js/"
}).use(['form', 'element', 'layer', 'jquery'], function () {
var $ = layui.jquery;
$(".panel a").on("click",function(){
window.parent.addTab($(this));
})
$(".panel a").on("click", function () {
window.parent.addTab($(this));
})
//用户数
$.getJSON("/UserManager/Load?limit=1&page=1",
function(data){
$(".userAll span").text(data.count);
}
)
//用户数
$.getJSON("/UserManager/Load?limit=1&page=1",
function (data) {
$(".userAll span").text(data.count);
}
)
//机构
$.getJSON("/UserSession/GetOrgs",
$.getJSON("/UserSession/GetOrgs",
function (data) {
$(".orgs span").text(data.Result.length);
}
@@ -60,13 +57,13 @@ layui.config({
}
)
//数字格式化
$(".panel span").each(function(){
$(this).html($(this).text()>9999 ? ($(this).text()/10000).toFixed(2) + "<em>万</em>" : $(this).text());
})
//数字格式化
$(".panel span").each(function () {
$(this).html($(this).text() > 9999 ? ($(this).text() / 10000).toFixed(2) + "<em>万</em>" : $(this).text());
})
//系统基本参数
$(".version").text("v6.5"); //当前版本
//系统基本参数
$(".version").text("v7.1"); //当前版本
$(".author").text("yubaolee"); //开发作者
$(".homePage").text("/Home/Index"); //网站首页
$(".server").text("centos docker"); //服务器环境

View File

@@ -62,7 +62,9 @@ layui.config({
}
});
}
clickTableRow(table,'mainList', 'list', function (obj) {
menuList({moduleId: obj.data.Id});
});
//菜单列表
var menucon = {}; //table的参数如搜索key点击tree的id
var menuList = function (options) {
@@ -177,7 +179,7 @@ layui.config({
type: 1,
content: $('#divMenuEdit'),
success: function () {
if (data == undefined) {
if (!update) {
form.val("mfromEdit", menuInital);
} else {
form.val("mfromEdit", data);
@@ -204,6 +206,7 @@ layui.config({
}
return {
add: function (moduleId) { //弹出添加
menuInital.ModuleId = moduleId;
show(false);
},
update: function (data) { //弹出编辑框
@@ -212,10 +215,6 @@ layui.config({
};
}();
//监听行单击事件
table.on('row(list)', function (obj) {
menuList({moduleId: obj.data.Id});
});
//监听页面主按钮操作
var active = {

View File

@@ -35,7 +35,8 @@
});
};
mainList();
clickTableRow(table,'mainList', 'list');
//添加(编辑)对话框
var editDlg = function () {
var show = function (update, data) {

View File

@@ -31,6 +31,7 @@
}
});
}
clickTableRow(table,'mainList', 'list');
//左边树状机构列表
var ztree = function () {
var url = '/UserSession/GetOrgs';

View File

@@ -20,12 +20,12 @@
//加载表头
$.getJSON('/Resources/Load',
{page: 1, limit: 1},
{ page: 1, limit: 1 },
function (data) {
var columns = data.columnFields.filter(u => u.IsList === true).map(function (e) {
return {
field: e.ColumnName,
title: e.Comment
title: e.Remark
};
});
columns.unshift({
@@ -60,6 +60,7 @@
});
};
mainList();
clickTableRow(table,'mainList', 'list');
//添加(编辑)对话框
var editDlg = function () {
@@ -140,7 +141,7 @@
}
, search: function () { //搜索
mainList({key: $('#key').val()});
mainList({ key: $('#key').val() });
}
, btnRefresh: function () {
mainList();

View File

@@ -42,6 +42,7 @@ layui.config({
}
mainList();
clickTableRow(table,'mainList', 'list');
//添加(编辑)对话框
var editDlg = function() {

View File

@@ -34,6 +34,7 @@ layui.config({
}
});
}
clickTableRow(table,'mainList', 'list');
//左边树状机构列表
var ztree = function () {
var url = '/UserSession/GetOrgs';

View File

@@ -37,7 +37,7 @@
var columns = data.columnFields.filter(u => u.IsList === true).map(function (e) {
return {
field: e.ColumnName,
title: e.Comment
title: e.Remark
};
});
columns.unshift({

View File

@@ -0,0 +1,139 @@
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using OpenAuth.Repository.Core;
namespace OpenAuth.Repository.Domain
{
/// <summary>
///外部数据源
/// </summary>
[Table("ExternalDataSource")]
public class ExternalDataSource : StringEntity
{
public ExternalDataSource()
{
this.Createtime = DateTime.Now;
this.Createusername = "";
this.Createuserid = "";
this.Updateuserid = "";
this.Updateusername = "";
this.Testsuccess = false;
this.Updatetime = DateTime.Now;
this.Enabled = false;
this.Connectionstring = "";
this.Description = "";
this.Password = "";
this.Username = "";
this.Port = null;
this.Server = "";
this.Databasename = "";
this.Dbtype = 0;
this.Name = "";
}
/// <summary>
///创建时间
/// </summary>
[Description("创建时间")]
public DateTime Createtime { get; set; }
/// <summary>
///创建用户名
/// </summary>
[Description("创建用户名")]
public string Createusername { get; set; }
/// <summary>
///创建用户ID
/// </summary>
[Description("创建用户ID")]
public string Createuserid { get; set; }
/// <summary>
///更新用户ID
/// </summary>
[Description("更新用户ID")]
public string Updateuserid { get; set; }
/// <summary>
///更新用户名
/// </summary>
[Description("更新用户名")]
public string Updateusername { get; set; }
/// <summary>
///是否测试成功
/// </summary>
[Description("是否测试成功")]
public bool? Testsuccess { get; set; }
/// <summary>
///最后测试时间
/// </summary>
[Description("最后测试时间")]
public DateTime? Updatetime { get; set; }
/// <summary>
///是否启用
/// </summary>
[Description("是否启用")]
public bool Enabled { get; set; }
/// <summary>
///连接字符串
/// </summary>
[Description("连接字符串")]
public string Connectionstring { get; set; }
/// <summary>
///描述
/// </summary>
[Description("描述")]
public string Description { get; set; }
/// <summary>
///密码
/// </summary>
[Description("密码")]
public string Password { get; set; }
/// <summary>
///用户名
/// </summary>
[Description("用户名")]
public string Username { get; set; }
/// <summary>
///端口
/// </summary>
[Description("端口")]
public int? Port { get; set; }
/// <summary>
///服务器地址
/// </summary>
[Description("服务器地址")]
public string Server { get; set; }
/// <summary>
///数据库名称
/// </summary>
[Description("数据库名称")]
public string Databasename { get; set; }
/// <summary>
///数据库类型
/// </summary>
[Description("数据库类型")]
public int Dbtype { get; set; }
/// <summary>
///数据源名称
/// </summary>
[Description("数据源名称")]
public string Name { get; set; }
}
}

View File

@@ -77,7 +77,7 @@ namespace OpenAuth.Repository
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<DataPrivilegeRule>()
.HasKey(c => new {c.Id});
.HasKey(c => new { c.Id });
modelBuilder.Entity<SysTableColumn>().HasNoKey();
modelBuilder.Entity<QueryStringObj>().HasNoKey();
@@ -86,7 +86,7 @@ namespace OpenAuth.Repository
|| Database.ProviderName == "Oracle.EntityFrameworkCore")
{
var boolToSmallIntConverter = new ValueConverter<bool, short>(
v => v ? (short) 1 : (short) 0,
v => v ? (short)1 : (short)0,
v => v != 0);
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
@@ -102,26 +102,37 @@ namespace OpenAuth.Repository
property.ClrType == typeof(DateTime))
{
property.SetValueConverter(new ValueConverter<DateTime, DateTime>(
v => v.ToUniversalTime(), v => DateTime.SpecifyKind(v, DateTimeKind.Utc)));
v => v.ToLocalTime(), v => DateTime.SpecifyKind(v, DateTimeKind.Local)));
}
}
}
}
//如果数据库是Oracle则将所有表名和字段名转换为大写
if (Database.ProviderName == "Oracle.EntityFrameworkCore")
// Oracle和PostgreSQL将所有属性映射到大写/小写列名
foreach (var entity in modelBuilder.Model.GetEntityTypes())
{
foreach (var entity in modelBuilder.Model.GetEntityTypes())
if (Database.ProviderName == "Oracle.EntityFrameworkCore")
{
entity.SetTableName(entity.GetTableName().ToUpper());
// 将所有属性映射到大写列名
foreach (var property in entity.GetProperties())
}
else if (Database.ProviderName == "Npgsql.EntityFrameworkCore.PostgreSQL" || Database.ProviderName == "Pomelo.EntityFrameworkCore.MySql")
{
entity.SetTableName(entity.GetTableName().ToLower());
}
foreach (var property in entity.GetProperties())
{
var storeObject = StoreObjectIdentifier.Create(entity, StoreObjectType.Table);
if (storeObject.HasValue)
{
var storeObject = StoreObjectIdentifier.Create(entity, StoreObjectType.Table);
if (storeObject.HasValue)
if (Database.ProviderName == "Oracle.EntityFrameworkCore")
{
property.SetColumnName(property.GetColumnName(storeObject.Value).ToUpper());
}
else if (Database.ProviderName == "Npgsql.EntityFrameworkCore.PostgreSQL" || Database.ProviderName == "Pomelo.EntityFrameworkCore.MySql")
{
property.SetColumnName(property.GetColumnName(storeObject.Value).ToLower());
}
}
}
}

View File

@@ -83,13 +83,28 @@ namespace OpenAuth.Repository.Test
{
DbType = dbType.Value,
ConnectionString = connectionString,
IsAutoCloseConnection = true,
MoreSettings=new ConnMoreSettings() {
PgSqlIsAutoToLower = false,//增删查改支持驼峰表
PgSqlIsAutoToLowerCodeFirst = false, // 建表建驼峰表。5.1.3.30
IsAutoToUpper=false //禁用自动转成大写表
}
IsAutoCloseConnection = true
});
if(dbType.Value != SqlSugar.DbType.PostgreSQL){
return sqlSugar;
}
// 配置bool类型转换为smallint
sqlSugar.Aop.OnExecutingChangeSql = (sql, parameters) =>
{
foreach (var param in parameters)
{
if (param.Value is bool boolValue)
{
param.DbType = System.Data.DbType.Int16;
// 将 bool 转换为 smallint
param.Value = boolValue ? (short)1 : (short)0;
}
}
// 返回修改后的 SQL 和参数
return new System.Collections.Generic.KeyValuePair<string, SugarParameter[]>(sql, parameters);
};
return sqlSugar;
});

View File

@@ -133,6 +133,26 @@ namespace OpenAuth.WebApi.Controllers
return result;
}
/// <summary>
/// 角色分配资源,整体提交,会覆盖之前的配置
/// </summary>
[HttpPost]
public Response AssignRoleResources([FromBody] AssignRoleResources request)
{
var result = new Response();
try
{
_app.AssignRoleResources(request);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
/// <summary>
/// 部门分配用户,整体提交,会覆盖之前的配置

View File

@@ -18,7 +18,7 @@ namespace OpenAuth.WebApi.Controllers
public class CategorysController : ControllerBase
{
private readonly CategoryApp _app;
/// <summary>
/// 获取分类详情
/// </summary>
@@ -45,14 +45,13 @@ namespace OpenAuth.WebApi.Controllers
/// 添加分类
/// </summary>
/// <returns></returns>
[HttpPost]
public Response Add([FromBody] AddOrUpdateCategoryReq obj)
[HttpPost]
public async Task<Response> Add([FromBody] AddOrUpdateCategoryReq obj)
{
var result = new Response();
try
{
_app.Add(obj);
await _app.Add(obj);
}
catch (Exception ex)
{
@@ -67,7 +66,7 @@ namespace OpenAuth.WebApi.Controllers
/// 修改分类(字典)
/// </summary>
/// <returns></returns>
[HttpPost]
[HttpPost]
public Response Update([FromBody] AddOrUpdateCategoryReq obj)
{
var result = new Response();
@@ -89,7 +88,7 @@ namespace OpenAuth.WebApi.Controllers
/// 加载列表
/// </summary>
[HttpGet]
public async Task<TableData> Load([FromQuery]QueryCategoryListReq request)
public async Task<TableData> Load([FromQuery] QueryCategoryListReq request)
{
return await _app.Load(request);
}
@@ -97,8 +96,8 @@ namespace OpenAuth.WebApi.Controllers
/// <summary>
/// 批量删除
/// </summary>
[HttpPost]
public Response Delete([FromBody]string[] ids)
[HttpPost]
public Response Delete([FromBody] string[] ids)
{
var result = new Response();
try
@@ -115,7 +114,7 @@ namespace OpenAuth.WebApi.Controllers
return result;
}
public CategorysController(CategoryApp app)
public CategorysController(CategoryApp app)
{
_app = app;
}

View File

@@ -0,0 +1,143 @@
using System;
using System.Threading.Tasks;
using Infrastructure;
using Microsoft.AspNetCore.Mvc;
using OpenAuth.App;
using OpenAuth.App.Request;
using OpenAuth.App.Response;
using OpenAuth.Repository.Domain;
namespace OpenAuth.WebApi.Controllers
{
/// <summary>
/// 外部数据源管理接口
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
[ApiExplorerSettings(GroupName = "外部数据源管理接口_ExternalDataSources")]
public class ExternalDataSourcesController : ControllerBase
{
private readonly ExtDataSourceApp _app;
//获取详情
[HttpGet]
public Response<ExternalDataSource> Get(string id)
{
var result = new Response<ExternalDataSource>();
try
{
result.Result = _app.Get(id);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
//添加
[HttpPost]
public Response Add([FromBody]AddOrUpdateExternalDataSourceReq obj)
{
var result = new Response();
try
{
_app.Add(obj);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
//测试连接
[HttpPost]
public Response TestConnection([FromBody]TestConnectionReq obj)
{
var result = new Response();
try
{
_app.TestConnection(obj);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
//修改
[HttpPost]
public Response Update([FromBody]AddOrUpdateExternalDataSourceReq obj)
{
var result = new Response();
try
{
_app.Update(obj);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
/// <summary>
/// 加载列表
/// </summary>
[HttpGet]
public async Task<TableData> Load([FromQuery]QueryExternalDataSourceListReq request)
{
return await _app.Load(request);
}
/// <summary>
/// 获取数据库类型
/// </summary>
/// <returns></returns>
[HttpGet]
public TableData GetDbTypes()
{
return _app.GetDbTypes();
}
/// <summary>
/// 批量删除
/// </summary>
[HttpPost]
public Response Delete([FromBody]string[] ids)
{
var result = new Response();
try
{
_app.Delete(ids);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
public ExternalDataSourcesController(ExtDataSourceApp app)
{
_app = app;
}
}
}

View File

@@ -167,6 +167,27 @@ namespace OpenAuth.WebApi.Controllers
return response;
}
/// <summary>
/// 撤销最后一个节点审批
/// </summary>
/// <returns></returns>
[HttpPost]
public Response UndoVerification([FromBody] UndoVerificationReq request)
{
var result = new Response();
try
{
_app.UndoVerification(request);
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
/// <summary>
/// 加载列表
/// </summary>
@@ -194,6 +215,9 @@ namespace OpenAuth.WebApi.Controllers
return result;
}
public FlowInstancesController(FlowInstanceApp app)
{
_app = app;

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -22,7 +22,7 @@ namespace OpenAuth.WebApi.Controllers
{
private readonly ResourceApp _app;
public ResourcesController(IAuth authUtil, ResourceApp app)
public ResourcesController(ResourceApp app)
{
_app = app;
}
@@ -32,6 +32,28 @@ namespace OpenAuth.WebApi.Controllers
return await _app.Load(request);
}
/// <summary>
/// 同步站点API到资源列表
/// <para>读取站点API信息如果资源列表中不存在则添加</para>
/// </summary>
[HttpPost]
public async Task<Response> Sync()
{
var result = new Response();
try
{
await _app.Sync();
return result;
}
catch (Exception ex)
{
result.Code = 500;
result.Message = ex.InnerException?.Message ?? ex.Message;
}
return result;
}
[HttpPost]
public Response Delete([FromBody]string[] ids)
{
@@ -104,5 +126,23 @@ namespace OpenAuth.WebApi.Controllers
return result;
}
/// <summary>
/// 获取资源所属应用
/// </summary>
[HttpGet]
public async Task<Response<List<SysResourceApp>>> GetResourceApps()
{
var result = new Response<List<SysResourceApp>>();
try
{
result.Result = await _app.GetResourceApps();
}
catch (Exception e)
{
result.Code = 500;
result.Message = e.InnerException?.Message ?? e.Message;
}
return result;
}
}
}

View File

@@ -14,10 +14,13 @@ namespace OpenAuth.WebApi.Model
private readonly IAuth _authUtil;
private readonly SysLogApp _logApp;
public OpenAuthFilter(IAuth authUtil, SysLogApp logApp)
private readonly ResourceApp _resourceApp;
public OpenAuthFilter(IAuth authUtil, SysLogApp logApp, ResourceApp resourceApp)
{
_authUtil = authUtil;
_logApp = logApp;
_resourceApp = resourceApp;
}
public void OnActionExecuting(ActionExecutingContext context)
@@ -28,9 +31,9 @@ namespace OpenAuth.WebApi.Model
var Controllername = description.ControllerName.ToLower();
var Actionname = description.ActionName.ToLower();
//匿名标识
var authorize = description.MethodInfo.GetCustomAttribute(typeof(AllowAnonymousAttribute));
if (authorize != null)
//匿名访问的不需要验证
var allowAnonymous = description.MethodInfo.GetCustomAttribute(typeof(AllowAnonymousAttribute));
if (allowAnonymous != null)
{
return;
}
@@ -41,14 +44,29 @@ namespace OpenAuth.WebApi.Model
context.Result = new JsonResult(new Response
{
Code = 401,
Message = "认证失败,请提供认证信息"
Message = "登录已过期,请重新登录"
});
return;
}
var apiPath = $"{Controllername}/{Actionname}";
//判断登录角色是否拥有访问该URL的权限
var resource = _resourceApp.CanAccess(apiPath);
if(!resource)
{
context.Result = new JsonResult(new Response
{
Code = 500,
Message = $"当前用户没有访问{apiPath}的权限,请在【角色管理】中分配资源"
});
return;
}
_logApp.Add(new SysLog
{
Content = $"用户访问",
Href = $"{Controllername}/{Actionname}",
Href = apiPath,
CreateName = _authUtil.GetUserName(),
CreateId = _authUtil.GetCurrentUser().User.Id,
TypeName = "访问日志"

View File

@@ -32,6 +32,7 @@
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.2" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="3.1.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="3.1.0" />
<PackageReference Include="Microsoft.OpenApi.Readers" Version="1.6.23" />
<PackageReference Include="Microsoft.VisualStudio.Web.CodeGeneration.Design" Version="3.1.0" />
<PackageReference Include="MiniProfiler.AspNetCore" Version="4.2.22" />
<PackageReference Include="MiniProfiler.AspNetCore.Mvc" Version="4.2.22" />

View File

@@ -184,12 +184,28 @@ namespace OpenAuth.WebApi
{
DbType = dbType.Value,
ConnectionString = connectionString,
IsAutoCloseConnection = true,
MoreSettings=new ConnMoreSettings() {
PgSqlIsAutoToLower = false,//增删查改支持驼峰表
PgSqlIsAutoToLowerCodeFirst = false// 建表建驼峰表。5.1.3.30
}
IsAutoCloseConnection = true
}, db => { db.Aop.OnLogExecuting = (sql, pars) => { logger.LogInformation(sql); }; });
if(dbType.Value != SqlSugar.DbType.PostgreSQL){
return sqlSugar;
}
// 配置bool类型转换为smallint
sqlSugar.Aop.OnExecutingChangeSql = (sql, parameters) =>
{
foreach (var param in parameters)
{
if (param.Value is bool boolValue)
{
param.DbType = System.Data.DbType.Int16;
// 将 bool 转换为 smallint
param.Value = boolValue ? (short)1 : (short)0;
}
}
// 返回修改后的 SQL 和参数
return new System.Collections.Generic.KeyValuePair<string, SugarParameter[]>(sql, parameters);
};
return sqlSugar;
});

View File

@@ -9,9 +9,9 @@
<div class="flex-column">
<sticky :className="'sub-navbar'">
<div class="filter-container">
<el-input @keyup.enter.native="handleFilter" size="mini" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="firstQuery.key"> </el-input>
<el-button class="filter-item" size="mini" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="mini" v-on:btn-event="onBtnClicked"></permission-btn>
<el-input @keyup.enter.native="handleFilter" size="small" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="firstQuery.key"> </el-input>
<el-button class="filter-item" size="small" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="small" v-on:btn-event="onBtnClicked"></permission-btn>
</div>
</sticky>
<div class="app-container flex-item flex-column">
@@ -40,8 +40,8 @@
<span v-if="radio == ''">明细</span>
<span v-else>{{ radio }}明细</span>
<div class="edit-buttons">
<el-button v-if="editModel" class="filter-item edit-button" size="mini" v-waves icon="el-icon-plus" @click="onBtnClicked('btnAddDetail')">添加</el-button>
<el-button v-if="editModel" class="filter-item edit-button delete-button" size="mini" v-waves icon="el-icon-delete" @click="onBtnClicked('btnDelDetail')">删除</el-button>
<el-button v-if="editModel" class="filter-item edit-button" size="small" v-waves icon="el-icon-plus" @click="onBtnClicked('btnAddDetail')">添加</el-button>
<el-button v-if="editModel" class="filter-item edit-button delete-button" size="small" v-waves icon="el-icon-delete" @click="onBtnClicked('btnDelDetail')">删除</el-button>
</div>
</div>
<auth-table style="height:calc(100% - 52px - 34px)" ref="secondTable" border :editModel="editModel" :table-fields="secondHeaderList" :data="secondList" @row-click="rowClickSecondTable" @selection-change="selChangeSecondTable"></auth-table>
@@ -52,9 +52,9 @@
<el-card shadow="nerver" v-if="editModel" style="text-align: right;padding-bottom: 10px;">
<el-row>
<el-col :span="24"
><el-button size="mini" @click="editModel = false">取消</el-button>
<el-button size="mini" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="mini" v-else type="primary" @click="updateData">确认</el-button></el-col
><el-button size="small" @click="editModel = false">取消</el-button>
<el-button size="small" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="small" v-else type="primary" @click="updateData">确认</el-button></el-col
>
</el-row>
</el-card>

View File

@@ -9,9 +9,9 @@
<div class="flex-column">
<sticky :className="'sub-navbar'">
<div class="filter-container">
<el-input @keyup.enter.native="handleFilter" size="mini" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="firstQuery.key"> </el-input>
<el-button class="filter-item" size="mini" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="mini" v-on:btn-event="onBtnClicked"></permission-btn>
<el-input @keyup.enter.native="handleFilter" size="small" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="firstQuery.key"> </el-input>
<el-button class="filter-item" size="small" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="small" v-on:btn-event="onBtnClicked"></permission-btn>
</div>
</sticky>
<div class="app-container flex-item flex-column">
@@ -40,8 +40,8 @@
<span v-if="radio == ''">明细</span>
<span v-else>{{ radio }}明细</span>
<div class="edit-buttons">
<el-button v-if="editModel" class="filter-item edit-button" size="mini" v-waves icon="el-icon-plus" @click="onBtnClicked('btnAddDetail')">添加</el-button>
<el-button v-if="editModel" class="filter-item edit-button delete-button" size="mini" v-waves icon="el-icon-delete" @click="onBtnClicked('btnDelDetail')">删除</el-button>
<el-button v-if="editModel" class="filter-item edit-button" size="small" v-waves icon="el-icon-plus" @click="onBtnClicked('btnAddDetail')">添加</el-button>
<el-button v-if="editModel" class="filter-item edit-button delete-button" size="small" v-waves icon="el-icon-delete" @click="onBtnClicked('btnDelDetail')">删除</el-button>
</div>
</div>
<auth-table style="height:calc(100% - 52px - 34px)" ref="secondTable" border :editModel="editModel" :table-fields="secondHeaderList" :data="secondList" @row-click="rowClickSecondTable" @selection-change="selChangeSecondTable"></auth-table>
@@ -52,9 +52,9 @@
<el-card shadow="nerver" v-if="editModel" style="text-align: right;padding-bottom: 10px;">
<el-row>
<el-col :span="24"
><el-button size="mini" @click="editModel = false">取消</el-button>
<el-button size="mini" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="mini" v-else type="primary" @click="updateData">确认</el-button></el-col
><el-button size="small" @click="editModel = false">取消</el-button>
<el-button size="small" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="small" v-else type="primary" @click="updateData">确认</el-button></el-col
>
</el-row>
</el-card>

View File

@@ -7,9 +7,9 @@
<div class="flex-column">
<sticky :className="'sub-navbar'">
<div class="filter-container">
<el-input @keyup.enter.native="handleFilter" size="mini" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="listQuery.key"> </el-input>
<el-button class="filter-item" size="mini" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="mini" v-on:btn-event="onBtnClicked"></permission-btn>
<el-input @keyup.enter.native="handleFilter" size="small" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="listQuery.key"> </el-input>
<el-button class="filter-item" size="small" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="small" v-on:btn-event="onBtnClicked"></permission-btn>
</div>
</sticky>
<div class="app-container flex-item">
@@ -29,9 +29,9 @@
<el-dialog v-el-drag-dialog class="dialog-mini" width="500px" :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<auth-form ref="dataForm" :rules="rules" :edit-model="true" :form-fields="headerList" v-model="temp" :col-num="2"></auth-form>
<div slot="footer">
<el-button size="mini" @click="dialogFormVisible = false">取消</el-button>
<el-button size="mini" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="mini" v-else type="primary" @click="updateData">确认</el-button>
<el-button size="small" @click="dialogFormVisible = false">取消</el-button>
<el-button size="small" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="small" v-else type="primary" @click="updateData">确认</el-button>
</div>
</el-dialog>
</div>

View File

@@ -1,71 +1,36 @@
<!--
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
* @Description: 代码生成器自动生成动态头部列表
* @Copyright (c) 2022 by yubaolee | ahfu~ , All Rights Reserved.
* @Description: OpenAuth.Pro代码生成器自动生成
* Copyright (c) 2025 by yubaolee | ahfu~ , All Rights Reserved.
-->
<template>
<div class="flex flex-column">
<sticky>
<el-input
@keyup.enter="handleFilter"
size="mini"
style="width: 200px"
class="filter-item"
:placeholder="'名称'"
v-model="listQuery.key"></el-input>
<el-button
class="filter-item"
size="mini"
icon="el-icon-search"
@click="handleFilter">
搜索
</el-button>
<permission-btn
size="mini"
v-on:btn-event="onBtnClicked"></permission-btn>
<div class="search-box">
<el-input @keyup.enter="handleFilter" size="small" class="custom-input w-200" :placeholder="'名称'"
v-model="listQuery.key"></el-input>
<el-button size="small" class="custom-button filter-item" icon="el-icon-search"
@click="handleFilter">搜索</el-button>
</div>
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
</sticky>
<auth-table
ref="mainTable"
:select-type="'checkbox'"
:table-fields="headerList"
:data="list"
:v-loading="listLoading"
@row-click="rowClick"
@selection-change="handleSelectionChange"></auth-table>
<el-pagination
v-show="total > 0"
:total="total"
v-model:currentPage="listQuery.page"
v-model:page-size="listQuery.limit"
@current-change="handleCurrentChange" />
<auth-table ref="mainTableRef" :select-type="'checkbox'" :table-fields="headerList" :data="list"
:v-loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
<el-pagination v-show="total > 0" :total="total" v-model:currentPage="listQuery.page"
v-model:page-size="listQuery.limit" @current-change="handleCurrentChange" />
</div>
<el-dialog
draggable
class="dialog-mini"
width="500px"
:title="txtDlgTitle[dialogStatus]"
v-model="dialogFormVisible">
<auth-form
ref="dataForm"
:rules="rules"
:edit-model="true"
:form-fields="headerList"
:data="temp"
<el-dialog draggable class="dialog-mini" width="500px" :title="dialogStatus==0?'新增':'编辑'" v-model="dialogFormVisible">
<auth-form ref="dataFormRef" :rules="rules" :edit-model="true" :form-fields="headerList" :data="temp"
:col-num="2"></auth-form>
<template v-slot:footer>
<div>
<el-button size="mini" @click="dialogFormVisible = false">
<el-button size="small" @click="dialogFormVisible = false">
取消
</el-button>
<el-button
size="mini"
v-if="dialogStatus == 'create'"
type="primary"
@click="createData">
<el-button size="small" v-if="dialogStatus == 0" type="primary" @click="createData">
确认
</el-button>
<el-button size="mini" v-else type="primary" @click="updateData">
<el-button size="small" v-else type="primary" @click="updateData">
确认
</el-button>
</div>
@@ -86,46 +51,35 @@ import Sticky from '@/components/Sticky/index.vue'
import permissionBtn from '@/components/PermissionBtn/index.vue'
import AuthForm from '../../components/Base/AuthForm.vue'
import AuthTable from '../../components/Base/AuthTable.vue'
const headerList = ref([]) //列表头
const multipleSelection = ref([]) //列表checkbox选中的值
const list = ref([]) //列表值
const total = ref(0) //总条数
const listLoading = ref(true) //进度条
const dialogFormVisible = ref(false) //是否显示编辑框
const dialogStatus = ref('')
const dialogStatus = ref(0) //0:新增 1:编辑
let temp = reactive({}) //新增(编辑)绑定对话框
const listQuery = reactive({
// 查询条件
page: 1,
limit: 20,
key: undefined,
})
const txtDlgTitle = reactive({
//对话框标题
update: '编辑',
create: '添加',
})
const rules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
})
//组件refs
const mainTable = ref(null)
const dataForm = ref(null)
const mainTableRef = ref(null)
const dataFormRef = ref(null)
onMounted(() => {
headerList.value = [
{HeaderList}
{HeaderList}
]
getList()
})
const rowClick = function (row) {
mainTable.value.clearSelection()
mainTable.value.toggleRowSelection(row)
mainTableRef.value.clearSelection()
mainTableRef.value.toggleRowSelection(row)
}
const handleSelectionChange = function (val) {
multipleSelection.value = val
@@ -137,27 +91,18 @@ const onBtnClicked = function (domId, callback) {
break
case 'btnEdit':
if (multipleSelection.value.length !== 1) {
ElMessage.error({
message: '只能选中一个进行编辑',
type: 'error',
})
ElMessage.error('只能选中一个进行编辑')
return
}
handleUpdate(multipleSelection.value[0])
break
case 'btnDel':
if (multipleSelection.value.length < 1) {
ElMessage.error({
message: '至少删除一个',
type: 'error',
})
ElMessage.error('至少删除一个')
return
}
handleDelete(multipleSelection.value)
break
case 'btnExport':
mainTable.value.exportExcel('资源文件', callback)
break
default:
break
}
@@ -174,22 +119,12 @@ const handleFilter = function () {
listQuery.page = 1
getList()
}
const handleSizeChange = function (val) {
listQuery.limit = val
getList()
}
const handleCurrentChange = function (val) {
listQuery.page = val
getList()
}
const handleModifyStatus = function (row, disable) {
// 模拟修改状态
ElMessage.success({
message: '操作成功',
type: 'success',
})
row.disable = disable
}
const resetTemp = function () {
let obj = {}
headerList.value.forEach(item => {
@@ -198,39 +133,30 @@ const resetTemp = function () {
Object.assign(temp, obj)
}
const handleCreate = async function () {
// 弹出添加框
resetTemp()
dialogStatus.value = 'create'
dialogStatus.value = 0
dialogFormVisible.value = true
await nextTick()
dataForm.value.clearValidate()
dataFormRef.value.clearValidate()
}
const createData = function () {
// 保存提交
dataForm.value.validate(() => {
dataFormRef.value.validate(() => {
{TableName}s.add(temp).then(() => {
list.value.unshift(temp)
dialogFormVisible.value = false
ElNotification({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000,
})
ElNotification.success('创建成功')
})
})
}
const handleUpdate = async function (row) {
// 弹出编辑框
Object.assign(temp, row) //必需这样赋值才能响应式
dialogStatus.value = 'update'
dialogStatus.value = 1
dialogFormVisible.value = true
await nextTick()
dataForm.value.clearValidate()
dataFormRef.value.clearValidate()
}
const updateData = function () {
// 更新提交
dataForm.value.validate(() => {
dataFormRef.value.validate(() => {
const tempData = Object.assign({}, temp)
{TableName}s.update(tempData).then(() => {
for (const v of list.value) {
@@ -241,17 +167,11 @@ const updateData = function () {
}
}
dialogFormVisible.value = false
ElNotification({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000,
})
ElNotification.success('更新成功')
})
})
}
const handleDelete = function (rows) {
// 多行删除
delrows({TableName}s, rows)
delrows({TableName}s, rows, getList)
}
</script>

View File

@@ -1,71 +1,36 @@
<!--
* @Author: yubaolee <yubaolee@163.com> | ahfu~ <954478625@qq.com>
* @Description: 代码生成器自动生成动态头部列表
* @Copyright (c) 2022 by yubaolee | ahfu~ , All Rights Reserved.
* @Description: OpenAuth.Pro代码生成器自动生成
* Copyright (c) 2025 by yubaolee | ahfu~ , All Rights Reserved.
-->
<template>
<div class="flex flex-column">
<sticky>
<el-input
@keyup.enter="handleFilter"
size="mini"
style="width: 200px"
class="filter-item"
:placeholder="'名称'"
v-model="listQuery.key"></el-input>
<el-button
class="filter-item"
size="mini"
icon="el-icon-search"
@click="handleFilter">
搜索
</el-button>
<permission-btn
size="mini"
v-on:btn-event="onBtnClicked"></permission-btn>
<div class="search-box">
<el-input @keyup.enter="handleFilter" size="small" class="custom-input w-200" :placeholder="'名称'"
v-model="listQuery.key"></el-input>
<el-button size="small" class="custom-button filter-item" icon="el-icon-search"
@click="handleFilter">搜索</el-button>
</div>
<permission-btn v-on:btn-event="onBtnClicked"></permission-btn>
</sticky>
<auth-table
ref="mainTable"
:select-type="'checkbox'"
:table-fields="headerList"
:data="list"
:v-loading="listLoading"
@row-click="rowClick"
@selection-change="handleSelectionChange"></auth-table>
<el-pagination
v-show="total > 0"
:total="total"
v-model:currentPage="listQuery.page"
v-model:page-size="listQuery.limit"
@current-change="handleCurrentChange" />
<auth-table ref="mainTableRef" :select-type="'checkbox'" :table-fields="headerList" :data="list"
:v-loading="listLoading" @row-click="rowClick" @selection-change="handleSelectionChange"></auth-table>
<el-pagination v-show="total > 0" :total="total" v-model:currentPage="listQuery.page"
v-model:page-size="listQuery.limit" @current-change="handleCurrentChange" />
</div>
<el-dialog
draggable
class="dialog-mini"
width="500px"
:title="txtDlgTitle[dialogStatus]"
v-model="dialogFormVisible">
<auth-form
ref="dataForm"
:rules="rules"
:edit-model="true"
:form-fields="headerList"
:data="temp"
<el-dialog draggable class="dialog-mini" width="500px" :title="dialogStatus==0?'新增':'编辑'" v-model="dialogFormVisible">
<auth-form ref="dataFormRef" :rules="rules" :edit-model="true" :form-fields="headerList" :data="temp"
:col-num="2"></auth-form>
<template v-slot:footer>
<div>
<el-button size="mini" @click="dialogFormVisible = false">
<el-button size="small" @click="dialogFormVisible = false">
取消
</el-button>
<el-button
size="mini"
v-if="dialogStatus == 'create'"
type="primary"
@click="createData">
<el-button size="small" v-if="dialogStatus == 0" type="primary" @click="createData">
确认
</el-button>
<el-button size="mini" v-else type="primary" @click="updateData">
<el-button size="small" v-else type="primary" @click="updateData">
确认
</el-button>
</div>
@@ -80,48 +45,41 @@ import { onMounted, ref, reactive, nextTick } from 'vue'
import * as {TableName}s from '@/api/{TableName}s'
import { defaultVal } from '@/utils/index'
import { delrows } from '@/utils/delRows.js'
import ColumnDefine from '@/interface/columnDefine'
//引入组件
import Sticky from '@/components/Sticky/index.vue'
import permissionBtn from '@/components/PermissionBtn/index.vue'
import AuthForm from '../../components/Base/AuthForm.vue'
import AuthTable from '../../components/Base/AuthTable.vue'
const headerList = ref([]) //列表头
const multipleSelection = ref([]) //列表checkbox选中的值
const list = ref([]) //列表值
const total = ref(0) //总条数
const listLoading = ref(true) //进度条
const dialogFormVisible = ref(false) //是否显示编辑框
const dialogStatus = ref('')
const dialogStatus = ref(0) //0:新增 1:编辑
let temp = reactive({}) //新增(编辑)绑定对话框
const listQuery = reactive({
// 查询条件
page: 1,
limit: 20,
key: undefined,
})
const txtDlgTitle = reactive({
//对话框标题
update: '编辑',
create: '添加',
})
const rules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
})
//组件refs
const mainTable = ref(null)
const dataForm = ref(null)
const mainTableRef = ref(null)
const dataFormRef = ref(null)
onMounted(() => {
headerList.value = [
{HeaderList}
]
getList()
})
const rowClick = function (row) {
mainTable.value.clearSelection()
mainTable.value.toggleRowSelection(row)
mainTableRef.value.clearSelection()
mainTableRef.value.toggleRowSelection(row)
}
const handleSelectionChange = function (val) {
multipleSelection.value = val
@@ -133,27 +91,18 @@ const onBtnClicked = function (domId, callback) {
break
case 'btnEdit':
if (multipleSelection.value.length !== 1) {
ElMessage.error({
message: '只能选中一个进行编辑',
type: 'error',
})
ElMessage.error('只能选中一个进行编辑')
return
}
handleUpdate(multipleSelection.value[0])
break
case 'btnDel':
if (multipleSelection.value.length < 1) {
ElMessage.error({
message: '至少删除一个',
type: 'error',
})
ElMessage.error('至少删除一个')
return
}
handleDelete(multipleSelection.value)
break
case 'btnExport':
mainTable.value.exportExcel('资源文件', callback)
break
default:
break
}
@@ -177,22 +126,12 @@ const handleFilter = function () {
listQuery.page = 1
getList()
}
const handleSizeChange = function (val) {
listQuery.limit = val
getList()
}
const handleCurrentChange = function (val) {
listQuery.page = val
getList()
}
const handleModifyStatus = function (row, disable) {
// 模拟修改状态
ElMessage.success({
message: '操作成功',
type: 'success',
})
row.disable = disable
}
const resetTemp = function () {
let obj = {}
headerList.value.forEach(item => {
@@ -201,39 +140,30 @@ const resetTemp = function () {
Object.assign(temp, obj)
}
const handleCreate = async function () {
// 弹出添加框
resetTemp()
dialogStatus.value = 'create'
dialogStatus.value = 0
dialogFormVisible.value = true
await nextTick()
dataForm.value.clearValidate()
dataFormRef.value.clearValidate()
}
const createData = function () {
// 保存提交
dataForm.value.validate(() => {
dataFormRef.value.validate(() => {
{TableName}s.add(temp).then(() => {
list.value.unshift(temp)
dialogFormVisible.value = false
ElNotification({
title: '成功',
message: '创建成功',
type: 'success',
duration: 2000,
})
ElNotification.success('创建成功')
})
})
}
const handleUpdate = async function (row) {
// 弹出编辑框
Object.assign(temp, row) //必需这样赋值才能响应式
dialogStatus.value = 'update'
dialogStatus.value = 1
dialogFormVisible.value = true
await nextTick()
dataForm.value.clearValidate()
dataFormRef.value.clearValidate()
}
const updateData = function () {
// 更新提交
dataForm.value.validate(() => {
dataFormRef.value.validate(() => {
const tempData = Object.assign({}, temp)
{TableName}s.update(tempData).then(() => {
for (const v of list.value) {
@@ -244,17 +174,11 @@ const updateData = function () {
}
}
dialogFormVisible.value = false
ElNotification({
title: '成功',
message: '更新成功',
type: 'success',
duration: 2000,
})
ElNotification.success('更新成功')
})
})
}
const handleDelete = function (rows) {
// 多行删除
delrows({TableName}s, rows)
delrows({TableName}s, rows, getList)
}
</script>

View File

@@ -7,9 +7,9 @@
<div class="flex-column">
<sticky :className="'sub-navbar'">
<div class="filter-container">
<el-input @keyup.enter.native="handleFilter" size="mini" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="listQuery.key"> </el-input>
<el-button class="filter-item" size="mini" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="mini" v-on:btn-event="onBtnClicked"></permission-btn>
<el-input @keyup.enter.native="handleFilter" size="small" style="width: 200px;" class="filter-item" :placeholder="'名称'" v-model="listQuery.key"> </el-input>
<el-button class="filter-item" size="small" v-waves icon="el-icon-search" @click="handleFilter">搜索</el-button>
<permission-btn size="small" v-on:btn-event="onBtnClicked"></permission-btn>
</div>
</sticky>
<div class="app-container flex-item">
@@ -29,9 +29,9 @@
<el-dialog v-el-drag-dialog class="dialog-mini" width="500px" :title="textMap[dialogStatus]" :visible.sync="dialogFormVisible">
<auth-form ref="dataForm" :rules="rules" :edit-model="true" :form-fields="headerList" v-model="temp" :col-num="2"></auth-form>
<div slot="footer">
<el-button size="mini" @click="dialogFormVisible = false">取消</el-button>
<el-button size="mini" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="mini" v-else type="primary" @click="updateData">确认</el-button>
<el-button size="small" @click="dialogFormVisible = false">取消</el-button>
<el-button size="small" v-if="dialogStatus == 'create'" type="primary" @click="createData">确认</el-button>
<el-button size="small" v-else type="primary" @click="updateData">确认</el-button>
</div>
</el-dialog>
</div>

View File

@@ -1,4 +1,4 @@
🔥.Net权限管理及快速开发框架、最好用的权限工作流系统。源于Martin Fowler企业级应用开发思想及最新技术组合SqlSugar、EF、Quartz、AutoFac、WebAPI、Swagger、Mock、NUnit、Vue2/3、Element-ui/plus、IdentityServer等。核心模块包括角色授权、代码生成、智能打印、表单设计、工作流、定时任务等。架构易扩展是中小企业的首选。
🔥.Net权限管理及快速开发框架、最好用的权限工作流系统。源于Martin Fowler企业级应用开发思想及最新技术组合SqlSugar、EF、Quartz、AutoFac、WebAPI、Swagger、Mock、NUnit、Vue2/3、Element-ui/plus、IdentityServer等。核心模块包括角色授权、代码生成、API鉴权、智能打印、表单设计、工作流、定时任务等。架构易扩展,是中小企业的首选。
![LOGO](https://gitee.com/uploads/images/2018/0425/163228_7077c3fd_362401.png "1.png")
@@ -93,7 +93,7 @@
* 超强的自定义权限控制功能,请参考:[通用权限设计与实现](https://www.cnblogs.com/yubaolee/p/DataPrivilege.html)
* 完整的字段权限控制,可以控制字段可见及API是否返回字段值
* 完整API鉴权可以控制角色可访问的API资源及模块功能字段可见及是否返回,请参考:[按角色授权API资源](http://doc.openauth.net.cn/core/apiauth.html#%E6%8C%89%E8%A7%92%E8%89%B2%E6%8E%88%E6%9D%83api%E8%B5%84%E6%BA%90) 及 [字段权限](http://doc.openauth.net.cn/core/datapropertyrule.html)
* 可拖拽的表单设计。详情:[可拖拽表单](http://doc.openauth.net.cn/pro/dragform.html)
@@ -101,7 +101,7 @@
* 全网最好用的打印解决方案。详情:[智能打印](http://doc.openauth.net.cn/pro/printerplan.html)
* 基于Quartz.Net的定时任务控制,可随时启/停可视化配置Cron表达式功能
* 基于Quartz.Net的定时任务控制,可随时启/停可视化配置Cron表达式功能,请参考:[定时任务](http://doc.openauth.net.cn/core/job.html)
* 基于CodeSmith的代码生成功能可快速生成带有头/明细结构的页面
@@ -111,7 +111,7 @@
* 支持多租户
* 集成IdentityServer4实现基于OAuth2的登录体系
* 支持搭建自己的IdentityServer服务器实现基于OAuth2的登录体系,请参考:[登录认证及OAuth集成](http://doc.openauth.net.cn/core/identity.html)
* 建立三方对接规范,已有系统可以无缝对接流程引擎

View File

@@ -15,10 +15,10 @@ features:
- title: 紧随潮流
details: 最新的.net sdk配合最炫的vue框架。
- title: 功能强大
details: 角色授权、代码生成、智能打印、数据权限、拖拽表单、工作流引擎、定时任务。
details: 角色授权、代码生成、API鉴权、智能打印、数据权限、拖拽表单、工作流引擎、定时任务。
- title: 主流技术
details: IdentityServer、EF、SqlSugar、Quartz、AutoFac、WebAPI、Swagger、Mock、NUnit、VUE2、VUE3、Element-ui、Element-plus。
footer: Copyright © 2024- yubaolee
footer: Copyright © 2025- yubaolee
---

View File

@@ -1,7 +1,7 @@
![LOGO](/logocore.png "1.png")
OpenAuth.Net是基于最新版.Net的开源权限工作流快速开发框架。源于Martin Fowler企业级应用开发思想及最新技术组合SqlSugar、EF、Quartz、AutoFac、WebAPI、Swagger、Mock、NUnit、Vue2/3、Element-ui/plus、IdentityServer等。核心模块包括组织机构、角色用户、权限授权、表单设计、工作流等。
OpenAuth.Net是基于最新版.Net的开源权限工作流快速开发框架。源于Martin Fowler企业级应用开发思想及最新技术组合SqlSugar、EF、Quartz、AutoFac、WebAPI、Swagger、Mock、NUnit、Vue2/3、Element-ui/plus、IdentityServer等。核心模块包括角色授权、代码生成、API鉴权、智能打印、表单设计、工作流、定时任务等。
开源版本演示:[http://demo.openauth.net.cn:1802/](http://demo.openauth.net.cn:1802/)
@@ -33,7 +33,7 @@ gitee上面两个版本。其中
* 超强的自定义权限控制功能,请参考:[通用权限设计与实现](https://www.cnblogs.com/yubaolee/p/DataPrivilege.html)
* 完整的字段权限控制,可以控制字段可见及API是否返回字段值
* 完整API鉴权可以控制角色可访问的API资源及模块功能字段可见及是否返回,请参考:[按角色授权API资源](http://doc.openauth.net.cn/core/apiauth.html#%E6%8C%89%E8%A7%92%E8%89%B2%E6%8E%88%E6%9D%83api%E8%B5%84%E6%BA%90) 及 [字段权限](http://doc.openauth.net.cn/core/datapropertyrule.html)
* 可拖拽的表单设计。详情:[可拖拽表单](http://doc.openauth.net.cn/pro/dragform.html)
@@ -41,7 +41,7 @@ gitee上面两个版本。其中
* 全网最好用的打印解决方案。详情:[智能打印](http://doc.openauth.net.cn/pro/printerplan.html)
* 基于Quartz.Net的定时任务控制,可随时启/停可视化配置Cron表达式功能
* 基于Quartz.Net的定时任务控制,可随时启/停可视化配置Cron表达式功能,请参考:[定时任务](http://doc.openauth.net.cn/core/job.html)
* 基于CodeSmith的代码生成功能可快速生成带有头/明细结构的页面
@@ -51,7 +51,7 @@ gitee上面两个版本。其中
* 支持多租户
* 集成IdentityServer4实现基于OAuth2的登录体系
* 支持搭建自己的IdentityServer服务器实现基于OAuth2的登录体系,请参考:[登录认证及OAuth集成](http://doc.openauth.net.cn/core/identity.html)
* 建立三方对接规范,已有系统可以无缝对接流程引擎

View File

@@ -30,6 +30,15 @@ Host: localhost:52789
X-Token: e4a5aa00
```
## 按角色授权API资源
目前主流的接口平台都提供按角色或账号授权访问API的功能OpenAuth.Net也不例外。在OpenAuth.Net中接口API被当作资源处理。如图
![2025-03-11-11-18-31](http://img.openauth.net.cn/2025-03-11-11-18-31.png)
如果后端新增或删除API点击【同步系统API资源】按钮即可同步到资源列表中。在角色管理功能中可以对登录的角色进行API资源授权。
![](http://img.openauth.net.cn/2025-03-11-11-13-54.png)
## 不登录直接访问

View File

@@ -1,3 +1,4 @@
# 字段权限
::: warning 注意
@@ -59,7 +60,7 @@ return result;
var columns = data.columnFields.filter(u => u.IsList ===true).map(function (e) {
return {
field: e.ColumnName,
title: e.Comment
title: e.Remark
};
});
columns.unshift({

View File

@@ -63,11 +63,11 @@ CodeSmith Generator Studio 8.0或以上
如下图使用CodeSmith文件夹中的模板右击【WebGenerate.cst】--【Execute】选择需要生成的表本文以Stock为例及相关的上下文命名空间点击【Generate】
![](http://pj.openauth.net.cn/zentao/file-read-47.png)
![](http://img.openauth.net.cn/2025-02-25-10-56-08.png)
生成成功后在CodeSmith/Csharp文件夹下面会有相关的界面代码如下图
![](http://pj.openauth.net.cn/zentao/file-read-50.png)
![](http://img.openauth.net.cn/2025-02-25-10-57-44.png)
Controllers、Views直接覆盖到OpenAuth.Mvc项目中对应的文件夹即可
@@ -83,12 +83,12 @@ userJs直接覆盖到OpenAuth.Mvc/wwwroot中
因为生成的Controller名称类似XXXsController所以模块的Url地址应该是XXXs/Index
:::
并为它添加菜单,这里我添加一个菜单【btnAdd】,如下图:
添加模块时,系统会自动添加三个默认菜单【添加】【编辑】【删除】。可根据需要调整。这里我添加一个菜单【btnCancel】,如下图:
![](http://pj.openauth.net.cn/zentao/file-read-51.png)
![](http://img.openauth.net.cn/2025-02-25-11-05-08.png)
重新登录系统,即可看到新加的仓储管理模块。
![](http://pj.openauth.net.cn/zentao/file-read-52.png)
![](http://img.openauth.net.cn/2025-02-25-11-08-26.png)

View File

@@ -4,7 +4,7 @@
#### IsFinish流程的当前状态
- -1 草稿/召回:流程发起人主动撤销流程;【新增】
- -1 草稿/召回:流程发起人主动召回流程;
- 0 正在运行;
@@ -15,6 +15,8 @@
- 4 驳回:流程结束,可能发起的流程内容有问题,要求被驳回重新提交;
#### ActivityId: 当前活动节点,即待审批的节点
#### ActivityType当前节点的类型
- -1 无法运行,
@@ -121,3 +123,11 @@
- 3开始节点,即type为start
- 4流程运行结束即type为end
与流程实例密切相关的还有两个表流程实例的操作记录FlowInstanceOperationHistory及流转记录FlowInstanceTransitionHistory。它们有不同的作用
### 操作记录FlowInstanceOperationHistory
该表记录了流程实例的所有操作记录,包括流程的创建、撤回、驳回、同意、不同意等操作。
### 流转记录FlowInstanceTransitionHistory
记录某个流程实例所有已审批的从一个活动节点到下一个活动节点的操作人、操作时间。

View File

@@ -38,6 +38,25 @@ OpenAuth.Net支持两种登录认证方式自定义token认证和基于Identi
当启用了Identity时客户端调用API需要先通过OpenAuth.IdentityServer服务器的OAuth验证才能调用接口。OpenAuth.Net调用API的客户端有两种
#### OpenAuth.Pro vue3
在使用企业版vue界面访问OpenAuth.WebApi时已经在文件`.env.dev`中配置好相关选项,可以直接使用,无需其他处理。
```python
VITE_OIDC_AUTHORITY = http://localhost:12796 #Identity服务器地址
VITE_OIDC_CLIENTID = OpenAuth.Pro #客户端名称
VITE_OIDC_REDIRECTURI = http://localhost:1803 #登录回调
VITE_OIDC_RESPONSETYPE = code #认证方式
VITE_OIDC_SCOPE = openid profile openauthapi #认证范围
VITE_OIDC_AUTOMATICSILENTRENEW = true #自动续期
```
如果服务端启用了Identity认证则打开登录界面如下
![2025-03-10-14-46-47](http://img.openauth.net.cn/2025-03-10-14-46-47.png)
这时点击登录超链接操作同OpenAuth.Mvc一样。
#### SwaggerUI
当我们启动了OpenAuth.WebApi在浏览器打开[http://localhost:52789/swagger/index.html](http://localhost:52789/swagger/index.html)时其实是Swagger框架给我们生成的一个调动客户端对于启用了Identity的服务Swagger会有一个Authorise的操作按钮
@@ -54,21 +73,6 @@ OpenAuth.Net支持两种登录认证方式自定义token认证和基于Identi
![20220119180608](http://img.openauth.net.cn/20220119180608.png)
#### OpenAuth.Pro
在使用企业版vue界面访问OpenAuth.WebApi时已经在文件`.env.dev`中配置好相关选项,可以直接使用,无需其他处理。
```python
VUE_APP_OIDC_AUTHORITY = http://localhost:12796 #Identity服务器地址
VUE_APP_OIDC_CLIENTID = OpenAuth.Pro #客户端名称
VUE_APP_OIDC_REDIRECTURI = http://localhost:1803/#/oidc-callback #登录回调
VUE_APP_OIDC_POSTLOGOUTREDIRECTURI = http://localhost:1803 #退出登录回调
VUE_APP_OIDC_RESPONSETYPE = code #认证方式
VUE_APP_OIDC_SCOPE = openid profile openauthapi #认证范围
VUE_APP_OIDC_AUTOMATICSILENTRENEW = true
VUE_APP_OIDC_SILENTREDIRECTURI = http://localhost:1803/silent-renew-oidc.html
```

View File

@@ -1,23 +1,17 @@
# 开发规范
# 后端开发规范
## 新增数据库名称规范
## 数据库表及字段命
子系统名称+业务名称+表尾,其中表尾名称规则如下:
SqlServer采用PascalCase命名Oracle采用全大写命名其他数据库采用camelCase命名。
::: tip 提示
开源版代码生成时通过表结尾Dtbl来判断是否是生成明细表代码。因此建议数据库表命名时按子系统名称+业务名称+表尾,其中表尾名称规则:
- 基础主数据以Mst结尾
- 普通业务表以Tbl结尾
- 业务明细表以Dtbl结尾
比如:
- WMS系统客户主数据表WmsCustomerMst
- WMS系统入库订单头表WmsInboundOrderTbl
- WMS系统入库订单明细表WmsInboundOrderDtbl
WMS系统入库订单明细表WmsInboundOrderDtbl
:::
## 数据库字段类型

View File

@@ -10,11 +10,11 @@ OpenAuth.Pro封装了一些组件方便开发使用组件全部在`src\com
静态字典。根据【字典分类】模块的`分类标识`关键字获取,如:
```html
<auth-select :isEdit="isEdit" @change="change" :data-source="'SYS_STATUS'" v-model="val" size="mini"></auth-select>
<auth-select :isEdit="isEdit" @change="change" :data-source="'SYS_STATUS'" v-model="val" size="small"></auth-select>
```
动态列表。从指定Url获取value/label结构的数据进行渲染
```html
<auth-select :default-props="{label:'name', value:'id'}" :isEdit="isEdit" @change="change" :type="'dynamic'" :data-source="'/CategoryTypes/Load'" v-model="val" size="mini"></auth-select>
<auth-select :default-props="{label:'name', value:'id'}" :isEdit="isEdit" @change="change" :type="'dynamic'" :data-source="'/CategoryTypes/Load'" v-model="val" size="small"></auth-select>
```
该组件有以下几个参数:

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long