feat: 框架代码同步 (#139)

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-06-09 22:46:54 +08:00 committed by GitHub
parent 366ca0d237
commit 608a1ded5c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
61 changed files with 747 additions and 452 deletions

View File

@ -4,7 +4,6 @@
不包含 不包含
不排序 不排序
不等于 不等于
管理模块
丧偶 丧偶
中专 中专
中共党员 中共党员
@ -26,6 +25,7 @@
发送失败 发送失败
同步数据库结构 同步数据库结构
外国人居留证 外国人居留证
外部错误
大专 大专
大于 大于
大于等于 大于等于
@ -39,7 +39,6 @@
已校验 已校验
已读 已读
并且 并且
意外错误
成功 成功
或者 或者
手机 手机
@ -50,6 +49,7 @@
无效操作 无效操作
无效输入 无效输入
日期范围 日期范围
未处理异常
未婚 未婚
未读 未读
本人数据 本人数据
@ -68,6 +68,7 @@
空闲 空闲
等于 等于
等待发送 等待发送
管理模块
系统模块 系统模块
绑定手机号码 绑定手机号码
结果非预期 结果非预期

View File

@ -60,7 +60,6 @@ XML注释文件不存在
用户名长度4位以上 用户名长度4位以上
用户头像不能为空 用户头像不能为空
用户编号不存在 用户编号不存在
用户编号不能为空
目标设备不能为空 目标设备不能为空
短信验证请求不能为空 短信验证请求不能为空
站内信不存在 站内信不存在

View File

@ -1371,6 +1371,22 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
return default; return default;
} }
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task SetEnabledAsync(SetDeptEnabledReq req)
{
return default;
}
/// <inheritdoc />
[InlineData(default)]
[Theory]
public Task SetEnabledAsync(SetRoleEnabledReq req)
{
return default;
}
/// <inheritdoc /> /// <inheritdoc />
[InlineData(default)] [InlineData(default)]
[Theory] [Theory]

View File

@ -7,6 +7,8 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// 计划作业执行记录表 /// 计划作业执行记录表
/// </summary> /// </summary>
[Index($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(JobId)}_{nameof(TimeId)}", $"{nameof(JobId)},{nameof(TimeId)}", true)] [Index($"{Chars.FLG_DB_INDEX_PREFIX}{nameof(JobId)}_{nameof(TimeId)}", $"{nameof(JobId)},{nameof(TimeId)}", true)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(JobId), nameof(JobId), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))] [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_JobRecord))]
public record Sys_JobRecord : LiteImmutableEntity public record Sys_JobRecord : LiteImmutableEntity
{ {

View File

@ -8,6 +8,7 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// </summary> /// </summary>
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiId), nameof(ApiId), false)] [Index(Chars.FLG_DB_INDEX_PREFIX + nameof(ApiId), nameof(ApiId), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)] [Index(Chars.FLG_DB_INDEX_PREFIX + nameof(CreatedTime), nameof(CreatedTime), false)]
[Index(Chars.FLG_DB_INDEX_PREFIX + nameof(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))] [Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))]
public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient public record Sys_RequestLog : ImmutableEntity, IFieldCreatedClient
{ {

View File

@ -0,0 +1,23 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Dept;
/// <summary>
/// 请求:启用/禁用部门
/// </summary>
public sealed record SetDeptEnabledReq : Sys_Dept
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -0,0 +1,23 @@
using NetAdmin.Domain.DbMaps.Dependency;
using NetAdmin.Domain.DbMaps.Dependency.Fields;
using NetAdmin.Domain.DbMaps.Sys;
namespace NetAdmin.Domain.Dto.Sys.Role;
/// <summary>
/// 请求:启用/禁用角色
/// </summary>
public sealed record SetRoleEnabledReq : Sys_Role
{
/// <inheritdoc cref="IFieldEnabled.Enabled" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override bool Enabled { get; init; }
/// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
public override long Version { get; init; }
}

View File

@ -15,7 +15,6 @@ public sealed record SetUserEnabledReq : Sys_User
/// <inheritdoc cref="EntityBase{T}.Id" /> /// <inheritdoc cref="EntityBase{T}.Id" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)] [JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.用户编号不能为空))]
public override long Id { get; init; } public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" /> /// <inheritdoc cref="IFieldVersion.Version" />

View File

@ -15,9 +15,9 @@ public enum ErrorCodes
, ,
/// <summary> /// <summary>
/// 意外错误 /// 未处理异常
/// </summary> /// </summary>
[ResourceDescription<Ln>(nameof(Ln.意外错误))] [ResourceDescription<Ln>(nameof(Ln.未处理异常))]
Unhandled = 9000 Unhandled = 9000
, ,
@ -43,4 +43,12 @@ public enum ErrorCodes
/// </summary> /// </summary>
[ResourceDescription<Ln>(nameof(Ln.无效操作))] [ResourceDescription<Ln>(nameof(Ln.无效操作))]
InvalidOperation = 9300 InvalidOperation = 9300
,
/// <summary>
/// 外部错误
/// </summary>
[ResourceDescription<Ln>(nameof(Ln.外部错误))]
ExternalError = 9400
} }

View File

@ -3,6 +3,7 @@ namespace NetAdmin.Infrastructure.Enums;
/// <summary> /// <summary>
/// 日志等级 /// 日志等级
/// </summary> /// </summary>
[Export]
public enum LogLevels public enum LogLevels
{ {
/// <summary> /// <summary>

View File

@ -4,14 +4,9 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// NetAdmin异常基类 /// NetAdmin异常基类
/// </summary> /// </summary>
#pragma warning disable RCS1194 #pragma warning disable RCS1194
public abstract class NetAdminException : Exception public abstract class NetAdminException(string message, Exception innerException) : Exception(message, innerException)
#pragma warning restore RCS1194 #pragma warning restore RCS1194
{ {
/// <summary>
/// Initializes a new instance of the <see cref="NetAdminException" /> class.
/// </summary>
protected NetAdminException() { }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="NetAdminException" /> class. /// Initializes a new instance of the <see cref="NetAdminException" /> class.
/// </summary> /// </summary>
@ -21,12 +16,6 @@ public abstract class NetAdminException : Exception
Code = code; Code = code;
} }
/// <summary>
/// Initializes a new instance of the <see cref="NetAdminException" /> class.
/// </summary>
protected NetAdminException(string message, Exception innerException) //
: base(message, innerException) { }
/// <summary> /// <summary>
/// 错误码 /// 错误码
/// </summary> /// </summary>

View File

@ -0,0 +1,12 @@
namespace NetAdmin.Infrastructure.Exceptions;
/// <summary>
/// 外部错误异常
/// </summary>
/// <remarks>
/// 外部接口调用未得到预期的结果
/// </remarks>
#pragma warning disable RCS1194
public sealed class NetAdminExternalErrorException(string message, Exception innerException = null)
#pragma warning restore RCS1194
: NetAdminException(ErrorCodes.ExternalError, message, innerException) { }

View File

@ -3,6 +3,9 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// <summary> /// <summary>
/// 加锁失败异常 /// 加锁失败异常
/// </summary> /// </summary>
/// <remarks>
/// 并发执行时锁竞争失败
/// </remarks>
#pragma warning disable RCS1194 #pragma warning disable RCS1194
public sealed class NetAdminGetLockerException : NetAdminException; public sealed class NetAdminGetLockerException() : NetAdminInvalidOperationException(null) { }
#pragma warning restore RCS1194 #pragma warning restore RCS1194

View File

@ -8,6 +8,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// </remarks> /// </remarks>
#pragma warning disable RCS1194 #pragma warning disable RCS1194
public sealed class NetAdminInvalidInputException(string message = null, Exception innerException = null) public sealed class NetAdminInvalidInputException(string message = null, Exception innerException = null)
: NetAdminException(ErrorCodes.InvalidInput, message, innerException) #pragma warning restore RCS1194
#pragma warning restore RCS1194 : NetAdminException(ErrorCodes.InvalidInput, message, innerException) { }
{ }

View File

@ -6,19 +6,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// <remarks> /// <remarks>
/// 非正常的业务流程或逻辑 /// 非正常的业务流程或逻辑
/// </remarks> /// </remarks>
#pragma warning disable RCS1194, DesignedForInheritance #pragma warning disable DesignedForInheritance, RCS1194
public class NetAdminInvalidOperationException : NetAdminException public class NetAdminInvalidOperationException(string message, Exception innerException = null)
#pragma warning restore DesignedForInheritance, RCS1194 #pragma warning restore RCS1194, DesignedForInheritance
{ : NetAdminException(ErrorCodes.InvalidOperation, message, innerException) { }
/// <summary>
/// Initializes a new instance of the <see cref="NetAdminInvalidOperationException" /> class.
/// </summary>
public NetAdminInvalidOperationException(string message, Exception innerException = null) //
: this(ErrorCodes.InvalidOperation, message, innerException) { }
/// <summary>
/// Initializes a new instance of the <see cref="NetAdminInvalidOperationException" /> class.
/// </summary>
protected NetAdminInvalidOperationException(ErrorCodes errorCode, string message, Exception innerException) //
: base(errorCode, message, innerException) { }
}

View File

@ -6,25 +6,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// <remarks> /// <remarks>
/// 运行结果是非预期的,例如事务失败回滚 /// 运行结果是非预期的,例如事务失败回滚
/// </remarks> /// </remarks>
#pragma warning disable RCS1194, DesignedForInheritance #pragma warning disable RCS1194
public class NetAdminUnexpectedException : NetAdminException public sealed class NetAdminUnexpectedException(string message, Exception innerException = null)
#pragma warning restore DesignedForInheritance, RCS1194 #pragma warning restore RCS1194
{ : NetAdminException(ErrorCodes.Unexpected, message, innerException);
/// <summary>
/// Initializes a new instance of the <see cref="NetAdminUnexpectedException" /> class.
/// </summary>
public NetAdminUnexpectedException(string message) //
: this(ErrorCodes.Unexpected, message) { }
/// <summary>
/// Initializes a new instance of the <see cref="NetAdminUnexpectedException" /> class.
/// </summary>
public NetAdminUnexpectedException() //
: this(string.Empty) { }
/// <summary>
/// Initializes a new instance of the <see cref="NetAdminUnexpectedException" /> class.
/// </summary>
protected NetAdminUnexpectedException(ErrorCodes errorCode, string message) //
: base(errorCode, message) { }
}

View File

@ -8,8 +8,8 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// </remarks> /// </remarks>
#pragma warning disable RCS1194 #pragma warning disable RCS1194
public sealed class NetAdminValidateException(Dictionary<string, string[]> validateResults) public sealed class NetAdminValidateException(Dictionary<string, string[]> validateResults)
: NetAdminException(ErrorCodes.InvalidInput) #pragma warning restore RCS1194
#pragma warning restore RCS1194 : NetAdminInvalidOperationException(null)
{ {
/// <summary> /// <summary>
/// 验证结果 /// 验证结果

View File

@ -37,6 +37,6 @@ public static class HttpResponseMessageExtensions
Header = me?.ToString() Header = me?.ToString()
, RequestHeader = me?.RequestMessage?.Headers , RequestHeader = me?.RequestMessage?.Headers
, Body = bodyHandle is null ? body : bodyHandle(body) , Body = bodyHandle is null ? body : bodyHandle(body)
}.ToJson(); }.Json();
} }
} }

View File

@ -24,6 +24,11 @@ public static class GlobalStatic
#endif #endif
; ;
/// <summary>
/// 日志保存跳过的API编号
/// </summary>
public static string[] LogSavingSkipApiIds => ["api/probe/health.check", "api/adm/device.log/create"];
/// <summary> /// <summary>
/// 系统内部密钥 /// 系统内部密钥
/// </summary> /// </summary>

View File

@ -16,4 +16,9 @@ public interface IDeptModule : ICrudModule<CreateDeptReq, QueryDeptRsp // 创建
/// 编辑部门 /// 编辑部门
/// </summary> /// </summary>
Task<QueryDeptRsp> EditAsync(EditDeptReq req); Task<QueryDeptRsp> EditAsync(EditDeptReq req);
/// <summary>
/// 启用/禁用部门
/// </summary>
Task SetEnabledAsync(SetDeptEnabledReq req);
} }

View File

@ -16,4 +16,9 @@ public interface IRoleModule : ICrudModule<CreateRoleReq, QueryRoleRsp // 创建
/// 编辑角色 /// 编辑角色
/// </summary> /// </summary>
Task<QueryRoleRsp> EditAsync(EditRoleReq req); Task<QueryRoleRsp> EditAsync(EditRoleReq req);
/// <summary>
/// 启用/禁用角色
/// </summary>
Task SetEnabledAsync(SetRoleEnabledReq req);
} }

View File

@ -119,6 +119,13 @@ public sealed class DeptService(BasicRepository<Sys_Dept, long> rpo) //
.ConfigureAwait(false)).Adapt<IEnumerable<QueryDeptRsp>>(); .ConfigureAwait(false)).Adapt<IEnumerable<QueryDeptRsp>>();
} }
/// <inheritdoc />
public Task SetEnabledAsync(SetDeptEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.Enabled)]);
}
private ISelect<Sys_Dept> QueryInternal(QueryReq<QueryDeptReq> req, bool asTreeCte = false) private ISelect<Sys_Dept> QueryInternal(QueryReq<QueryDeptReq> req, bool asTreeCte = false)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter) var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)

View File

@ -75,8 +75,8 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
.Set(a => a.RequestBody == req.RequestBody) .Set(a => a.RequestBody == req.RequestBody)
.Set(a => a.RequestUrl == req.RequestUrl) .Set(a => a.RequestUrl == req.RequestUrl)
.Set(a => a.UserId == req.UserId) .Set(a => a.UserId == req.UserId)
.Where(a => a.Id == req.Id) .Set(a => a.Summary == req.Summary)
.Where(a => a.Version == req.Version); .Where(a => a.Id == req.Id);
#if DBTYPE_SQLSERVER #if DBTYPE_SQLSERVER
return (await update.ExecuteUpdatedAsync().ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryJobRsp>(); return (await update.ExecuteUpdatedAsync().ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryJobRsp>();
@ -276,11 +276,22 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
} }
/// <inheritdoc /> /// <inheritdoc />
public Task<int> ReleaseStuckTaskAsync() public async Task<int> ReleaseStuckTaskAsync()
{ {
return UpdateAsync( // var ret1 = await UpdateAsync( // 运行中,运行时间超过超时设定;置为空闲状态
new Sys_Job { Status = JobStatues.Idle }, [nameof(Sys_Job.Status)], null new Sys_Job { Status = JobStatues.Idle }, [nameof(Sys_Job.Status)], null
, a => a.Status == JobStatues.Running && a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB)); , a => a.Status == JobStatues.Running &&
a.LastExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB)
, true)
.ConfigureAwait(false);
var ret2 = await UpdateAsync( // 空闲中,下次执行时间在当前时间减去超时时间以前;将下次执行时间调整到现在
new Sys_Job { NextExecTime = DateTime.Now, NextTimeId = DateTime.Now.TimeUnixUtc() }
, [nameof(Sys_Job.NextExecTime), nameof(Sys_Job.NextTimeId)], null
, a => a.Status == JobStatues.Idle && a.NextExecTime < DateTime.Now.AddSeconds(-Numbers.SECS_TIMEOUT_JOB)
, true)
.ConfigureAwait(false);
return ret1 + ret2;
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -123,6 +123,13 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo) //
return ret.Adapt<IEnumerable<QueryRoleRsp>>(); return ret.Adapt<IEnumerable<QueryRoleRsp>>();
} }
/// <inheritdoc />
public Task SetEnabledAsync(SetRoleEnabledReq req)
{
req.ThrowIfInvalid();
return UpdateAsync(req, [nameof(req.Enabled)]);
}
private ISelect<Sys_Role> QueryInternal(QueryReq<QueryRoleReq> req) private ISelect<Sys_Role> QueryInternal(QueryReq<QueryRoleReq> req)
{ {
var ret = Rpo.Select.IncludeMany(a => a.Depts.Select(b => new Sys_Dept { Id = b.Id })) var ret = Rpo.Select.IncludeMany(a => a.Depts.Select(b => new Sys_Dept { Id = b.Id }))

View File

@ -63,4 +63,10 @@ public sealed class DeptCache(IDistributedCache cache, IDeptService service) //
{ {
return Service.QueryAsync(req); return Service.QueryAsync(req);
} }
/// <inheritdoc />
public Task SetEnabledAsync(SetDeptEnabledReq req)
{
return Service.SetEnabledAsync(req);
}
} }

View File

@ -63,4 +63,10 @@ public sealed class RoleCache(IDistributedCache cache, IRoleService service) //
{ {
return Service.QueryAsync(req); return Service.QueryAsync(req);
} }
/// <inheritdoc />
public Task SetEnabledAsync(SetRoleEnabledReq req)
{
return Service.SetEnabledAsync(req);
}
} }

View File

@ -91,4 +91,12 @@ public sealed class DeptController(IDeptCache cache) : ControllerBase<IDeptCache
{ {
return Cache.QueryAsync(req); return Cache.QueryAsync(req);
} }
/// <summary>
/// 启用/禁用部门
/// </summary>
public Task SetEnabledAsync(SetDeptEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}
} }

View File

@ -90,4 +90,12 @@ public sealed class RoleController(IRoleCache cache) : ControllerBase<IRoleCache
{ {
return Cache.QueryAsync(req); return Cache.QueryAsync(req);
} }
/// <summary>
/// 启用/禁用角色
/// </summary>
public Task SetEnabledAsync(SetRoleEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}
} }

View File

@ -20,8 +20,10 @@ public sealed class OperationLogger : IEventSubscriber
return; return;
} }
// 跳过心跳请求 // 跳过指定的请求
if (operationEvent.Data.ApiId.Equals("api/probe/health.check", StringComparison.OrdinalIgnoreCase)) { if (Array.Exists( //
GlobalStatic.LogSavingSkipApiIds
, x => x.Equals(operationEvent.Data.ApiId, StringComparison.OrdinalIgnoreCase))) {
return; return;
} }

View File

@ -81,4 +81,15 @@ export default {
return await http.post(this.url, data, config) return await http.post(this.url, data, config)
}, },
}, },
/**
* 启用/禁用部门
*/
setEnabled: {
url: `${config.API_URL}/api/sys/dept/set.enabled`,
name: `启用/禁用部门`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
} }

View File

@ -92,4 +92,15 @@ export default {
return await http.post(this.url, data, config) return await http.post(this.url, data, config)
}, },
}, },
/**
* 启用/禁用角色
*/
setEnabled: {
url: `${config.API_URL}/api/sys/role/set.enabled`,
name: `启用/禁用角色`,
post: async function (data = {}, config = {}) {
return await http.post(this.url, data, config)
},
},
} }

View File

@ -14,8 +14,8 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
<el-table-column :label="$t('地区')" prop="key" width="400"></el-table-column> <el-table-column :label="$t('地区')" prop="key" width="400" />
<el-table-column :label="$t('代码')" prop="value"></el-table-column> <el-table-column :label="$t('代码')" prop="value" />
</sc-table-select> </sc-table-select>
</template> </template>
<style scoped></style> <style scoped></style>

View File

@ -1,17 +1,17 @@
<template> <template>
<el-table-column :label="label" :min-width="minWidth" :prop="prop" :sortable="customSort ? `custom` : true" align="center"> <el-table-column v-bind:="$attrs">
<template #default="scope"> <template #default="{ row }">
<template v-for="(item, i) in options" :key="i"> <template v-for="(item, i) in options" :key="i">
<div v-if="tool.getNestedProperty(scope.row, prop) === item.value" class="indicator"> <div v-if="tool.getNestedProperty(row, this.$attrs.prop) === item.value">
<sc-status-indicator <sc-status-indicator
:pulse="item.pulse" :pulse="item.pulse"
:style="item.type ? '' : `background: #${Math.abs(this.$TOOL.crypto.hashCode(item.value)).toString(16).substring(0, 6)}`" :style="item.type ? '' : `background: #${Math.abs(this.$TOOL.crypto.hashCode(item.value)).toString(16).substring(0, 6)}`"
:type="item.type" /> :type="item.type" />
<span v-if="!$slots.default"> {{ item.text }}</span> <span v-if="!$slots.default">&nbsp;{{ item.text }}</span>
<slot :row="scope.row" :text="item.text"></slot> <slot :row="row" :text="item.text"></slot>
</div> </div>
</template> </template>
<slot :row="scope.row" name="info"></slot> <slot :row="row" name="info"></slot>
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
@ -21,11 +21,7 @@ import tool from '@/utils/tool'
export default { export default {
emits: [], emits: [],
props: { props: {
minWidth: { type: Number, default: 80 },
options: { type: Array }, options: { type: Array },
prop: { type: String },
label: { type: String },
customSort: { type: Boolean, default: true },
}, },
data() { data() {
return {} return {}

View File

@ -18,9 +18,9 @@
</el-form-item> </el-form-item>
</el-form> </el-form>
</template> </template>
<el-table-column :label="$t('用户编号')" prop="id"></el-table-column> <el-table-column :label="$t('用户编号')" prop="id" />
<el-table-column :label="$t('用户名')" prop="userName"></el-table-column> <el-table-column :label="$t('用户名')" prop="userName" />
<el-table-column :label="$t('手机号')" prop="mobile"></el-table-column> <el-table-column :label="$t('手机号')" prop="mobile" />
</sc-table-select> </sc-table-select>
</template> </template>
<style scoped></style> <style scoped></style>

View File

@ -6,12 +6,7 @@
:props="item.options.props" :props="item.options.props"
:table-width="60" :table-width="60"
style="width: 100%"> style="width: 100%">
<el-table-column <el-table-column v-for="(_item, _index) in item.options.column" :key="_index" :label="_item.label" :prop="_item.prop" :width="_item.width" />
v-for="(_item, _index) in item.options.column"
:key="_index"
:label="_item.label"
:prop="_item.prop"
:width="_item.width"></el-table-column>
</sc-table-select> </sc-table-select>
</template> </template>

View File

@ -46,7 +46,7 @@
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
<el-table-column min-width="1"></el-table-column> <el-table-column min-width="1" />
<template #empty> <template #empty>
<el-empty :description="emptyText" :image-size="100"></el-empty> <el-empty :description="emptyText" :image-size="100"></el-empty>
</template> </template>

View File

@ -36,7 +36,7 @@
@select-all="selectAll" @select-all="selectAll"
max-height="30rem" max-height="30rem"
ref="table"> ref="table">
<el-table-column v-if="multiple" type="selection" width="45"></el-table-column> <el-table-column v-if="multiple" type="selection" width="45" />
<el-table-column v-else type="index" width="45"> <el-table-column v-else type="index" width="45">
<template #default="scope" <template #default="scope"
><span>{{ scope.$index + (currentPage - 1) * pageSize + 1 }}</span></template ><span>{{ scope.$index + (currentPage - 1) * pageSize + 1 }}</span></template

View File

@ -484,6 +484,15 @@ textarea {
.mt-4 { .mt-4 {
margin-top: 1rem; margin-top: 1rem;
} }
.mr-2 {
margin-right: 0.5rem;
}
.mr-4 {
margin-right: 1rem;
}
.mt-8 { .mt-8 {
margin-top: 2rem; margin-top: 2rem;
} }
@ -511,10 +520,3 @@ textarea {
.justify-content-center { .justify-content-center {
justify-content: center; justify-content: center;
} }
.indicator {
display: flex;
align-items: center;
justify-content: center;
gap: 0.5rem;
}

View File

@ -165,6 +165,18 @@
height: 1px; height: 1px;
} }
.el-table {
.el-link:after {
border-bottom: 1px solid var(--el-link-text-color);
bottom: 0;
content: '';
height: 0;
left: 0;
position: absolute;
right: 0;
}
}
.el-table th.is-sortable { .el-table th.is-sortable {
transition: 0.1s; transition: 0.1s;
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<el-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" destroy-on-close> <el-dialog v-model="visible" :title="`${titleMap[mode]}${form?.id ?? '...'}`" :width="800" @closed="$emit('closed')" destroy-on-close>
<el-form :model="form" :rules="rules" label-position="top" ref="form"> <el-form :model="form" :rules="rules" label-position="top" ref="form">
<el-row class="items-center justify-content-center"> <el-row class="items-center justify-content-center">
<el-col v-if="mode === 'edit'" :lg="10"> <el-col v-if="mode === 'edit'" :lg="10">

View File

@ -16,8 +16,8 @@
row-key="id" row-key="id"
show-summary show-summary
stripe> stripe>
<el-table-column :label="$t('接口路径')" prop="id"></el-table-column> <el-table-column :label="$t('接口路径')" prop="id" />
<el-table-column :label="$t('接口名称')" prop="name"></el-table-column> <el-table-column :label="$t('接口名称')" prop="name" />
<na-col-indicator <na-col-indicator
:label="$t('请求方式')" :label="$t('请求方式')"
:options=" :options="
@ -25,10 +25,11 @@
return { value: x[0].toUpperCase(), text: x[1][1] } return { value: x[0].toUpperCase(), text: x[1][1] }
}) })
" "
align="center"
prop="method" prop="method"
width="100"> sortable="custom"
</na-col-indicator> width="100" />
<el-table-column :label="$t('接口描述')" prop="summary"></el-table-column> <el-table-column :label="$t('接口描述')" prop="summary" />
</sc-table> </sc-table>
</el-main> </el-main>
</el-container> </el-container>

View File

@ -5,7 +5,7 @@
<el-row :gutter="15"> <el-row :gutter="15">
<el-col :lg="4"> <el-col :lg="4">
<el-card shadow="never"> <el-card shadow="never">
<sc-statistic :value="statistics.version" groupSeparator title="Redis 版本"></sc-statistic> <sc-statistic :value="statistics.version" group-separator title="Redis 版本"></sc-statistic>
</el-card> </el-card>
</el-col> </el-col>
<el-col :lg="4"> <el-col :lg="4">
@ -13,7 +13,7 @@
<sc-statistic <sc-statistic
:suffix="$t('天')" :suffix="$t('天')"
:value="parseInt(statistics.upTime / 86400)" :value="parseInt(statistics.upTime / 86400)"
groupSeparator group-separator
title="Redis 运行时间"></sc-statistic> title="Redis 运行时间"></sc-statistic>
</el-card> </el-card>
</el-col> </el-col>
@ -21,7 +21,7 @@
<el-card shadow="never"> <el-card shadow="never">
<sc-statistic <sc-statistic
:value="statistics.upTime ? (statistics.usedCpu / statistics.upTime).toFixed(2) : 0" :value="statistics.upTime ? (statistics.usedCpu / statistics.upTime).toFixed(2) : 0"
groupSeparator group-separator
suffix="%" suffix="%"
title="CPU 使用率"></sc-statistic> title="CPU 使用率"></sc-statistic>
</el-card> </el-card>
@ -31,13 +31,13 @@
<sc-statistic <sc-statistic
:title="$t('内存使用量')" :title="$t('内存使用量')"
:value="(statistics.usedMemory / 1024 / 1024).toFixed(2)" :value="(statistics.usedMemory / 1024 / 1024).toFixed(2)"
groupSeparator group-separator
suffix="MiB"></sc-statistic> suffix="MiB"></sc-statistic>
</el-card> </el-card>
</el-col> </el-col>
<el-col :lg="4"> <el-col :lg="4">
<el-card shadow="never"> <el-card shadow="never">
<sc-statistic :title="$t('缓存数量')" :value="statistics.dbSize" groupSeparator></sc-statistic> <sc-statistic :title="$t('缓存数量')" :value="statistics.dbSize" group-separator></sc-statistic>
</el-card> </el-card>
</el-col> </el-col>
<el-col :lg="4"> <el-col :lg="4">
@ -45,7 +45,7 @@
<sc-statistic <sc-statistic
:title="$t('缓存命中率')" :title="$t('缓存命中率')"
:value="((statistics.keyspaceHits / (statistics.keyspaceMisses + statistics.keyspaceHits)) * 100).toFixed(2)" :value="((statistics.keyspaceHits / (statistics.keyspaceMisses + statistics.keyspaceHits)) * 100).toFixed(2)"
groupSeparator group-separator
suffix="%"></sc-statistic> suffix="%"></sc-statistic>
</el-card> </el-card>
</el-col> </el-col>

View File

@ -1,31 +1,39 @@
<template> <template>
<el-container> <el-container>
<el-header style="height: auto; padding: 0 1rem">
<sc-select-filter
:data="[
{
title: $t('启用状态'),
key: 'enabled',
options: [
{ label: $t('全部'), value: '' },
{ label: $t('启用'), value: true },
{ label: $t('禁用'), value: false },
],
},
]"
:label-width="6"
@on-change="filterChange"
ref="selectFilter"></sc-select-filter>
</el-header>
<el-header> <el-header>
<div class="left-panel"> <div class="left-panel">
<na-search <na-search
:controls="[ :controls="[]"
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '是否启用',
style: 'width:10rem',
},
]"
:vue="this" :vue="this"
@reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@search="onSearch" @search="onSearch"
ref="search" /> ref="search" />
</div> </div>
<div class="right-panel"> <div class="right-panel">
<na-button-add :vue="this"></na-button-add> <na-button-add :vue="this" />
<el-button :disabled="selection.length === 0" @click="batchDel" icon="el-icon-delete" plain type="danger"></el-button> <el-button :disabled="selection.length === 0" @click="batchDel" icon="el-icon-delete" plain type="danger"></el-button>
</div> </div>
</el-header> </el-header>
<el-main class="nopadding"> <el-main class="nopadding">
<sc-table <sc-table
v-loading="loading"
:apiObj="$API.sys_config.pagedQuery" :apiObj="$API.sys_config.pagedQuery"
:context-menus="['id', 'userRegisterConfirm', 'enabled', 'createdTime']" :context-menus="['id', 'userRegisterConfirm', 'enabled', 'createdTime']"
:params="query" :params="query"
@ -38,11 +46,11 @@
ref="table" ref="table"
row-key="id" row-key="id"
stripe> stripe>
<el-table-column align="center" type="selection"></el-table-column> <el-table-column type="selection" />
<el-table-column :label="$t('配置编号')" align="center" prop="id" width="170"></el-table-column> <el-table-column :label="$t('配置编号')" align="center" prop="id" width="170" />
<el-table-column :label="$t('用户注册')" align="center"> <el-table-column :label="$t('用户注册')" align="center">
<el-table-column :label="$t('默认部门')" align="center" prop="userRegisterDept.name" width="150"></el-table-column> <el-table-column :label="$t('默认部门')" align="center" prop="userRegisterDept.name" width="150" />
<el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.name" width="150"></el-table-column> <el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.name" width="150" />
<el-table-column :label="$t('人工审核')" align="center" prop="userRegisterConfirm" width="100"> <el-table-column :label="$t('人工审核')" align="center" prop="userRegisterConfirm" width="100">
<template #default="scope"> <template #default="scope">
<el-switch v-model="scope.row.userRegisterConfirm" @change="changeSwitch($event, scope.row)"></el-switch> <el-switch v-model="scope.row.userRegisterConfirm" @change="changeSwitch($event, scope.row)"></el-switch>
@ -54,7 +62,7 @@
<el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch> <el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('创建时间')" align="center" prop="createdTime" width="170"></el-table-column> <el-table-column :label="$t('创建时间')" align="center" prop="createdTime" width="170" />
<na-col-operation <na-col-operation
:buttons=" :buttons="
naColOperation.buttons.concat({ naColOperation.buttons.concat({
@ -85,47 +93,84 @@ import table from '@/config/table'
import tool from '@/utils/tool' import tool from '@/utils/tool'
export default { export default {
inject: ['reload'],
computed: {
table() {
return table
},
naColOperation() {
return naColOperation
},
},
components: { components: {
saveDialog, saveDialog,
}, },
computed: {
naColOperation() {
return naColOperation
},
table() {
return table
},
},
created() {},
data() { data() {
return { return {
dialog: { dialog: {
save: false,
info: false, info: false,
save: false,
}, },
selection: [], loading: false,
query: { query: {
dynamicFilter: { dynamicFilter: {
filters: [], filters: [],
}, },
filter: {}, filter: {},
}, },
selection: [],
} }
}, },
mounted() {}, inject: ['reload'],
methods: { methods: {
// async batchDel() {
let loading
try {
await this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning',
})
loading = this.$loading()
const res = await this.$API.sys_config.bulkDelete.post({
items: this.selection,
})
this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
loading?.close()
},
async changeSwitch(event, row) {
try {
await this.$API.sys_config.edit.post(row)
this.$refs.table.refresh()
this.$message.success(`操作成功`)
} catch {
//
}
},
filterChange(data) {
Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
this.$refs.search.search()
})
},
async rowDel(row) {
try {
const res = await this.$API.sys_config.delete.post({ id: row.id })
this.$message.success(`删除 ${res.data}`)
this.$refs.table.refresh()
} catch {
//
}
},
onSearch(form) { onSearch(form) {
if (Array.isArray(form.dy.createdTime)) { if (Array.isArray(form.dy.createdTime)) {
const start = new Date(form.dy.createdTime[0])
const end = new Date(form.dy.createdTime[1])
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
field: 'createdTime', field: 'createdTime',
operator: 'dateRange', operator: 'dateRange',
value: [ value: form.dy.createdTime,
tool.dateFormat(start.setDate(start.getDate() + 1)).substring(0, 10),
tool.dateFormat(end.setDate(end.getDate() + 1)).substring(0, 10),
],
}) })
} }
@ -139,51 +184,9 @@ export default {
this.$refs.table.upData() this.$refs.table.upData()
}, },
//
async changeSwitch(event, row) {
try {
await this.$API.sys_config.edit.post(row)
this.$message.success(`操作成功`)
} catch {
//
}
this.$refs.table.refresh()
},
//
async rowDel(row) {
try {
const res = await this.$API.sys_config.delete.post({ id: row.id })
this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
},
//
async batchDel() {
const confirmRes = await this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning',
confirmButtonText: '删除',
confirmButtonClass: 'el-button--danger',
}).catch(() => {})
if (!confirmRes) {
return false
}
try {
await this.$API.sys_config.bulkDelete.post({
items: this.selection,
})
this.$refs.table.removeKeys(this.selection.map((x) => x.id))
this.$message.success('操作成功')
} catch {
//
}
},
}, },
mounted() {},
} }
</script> </script>
<style></style> <style scoped></style>

View File

@ -1,5 +1,5 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" @closed="$emit('closed')" destroy-on-close> <sc-dialog v-model="visible" :title="`${titleMap[mode]}${form?.id ?? '...'}`" @closed="$emit('closed')" destroy-on-close>
<div v-loading="loading"> <div v-loading="loading">
<el-tabs v-if="!loading" tab-position="top"> <el-tabs v-if="!loading" tab-position="top">
<el-tab-pane :label="$t('基本信息')"> <el-tab-pane :label="$t('基本信息')">
@ -112,4 +112,8 @@ export default {
} }
</script> </script>
<style></style> <style scoped>
.el-collapse {
border-top: none;
}
</style>

View File

@ -1,5 +1,22 @@
<template> <template>
<el-container> <el-container>
<el-header style="height: auto; padding: 0 1rem">
<sc-select-filter
:data="[
{
title: $t('启用状态'),
key: 'enabled',
options: [
{ label: $t('全部'), value: '' },
{ label: $t('启用'), value: true },
{ label: $t('禁用'), value: false },
],
},
]"
:label-width="6"
@on-change="filterChange"
ref="selectFilter"></sc-select-filter>
</el-header>
<el-header> <el-header>
<div class="left-panel"> <div class="left-panel">
<na-search <na-search
@ -10,27 +27,20 @@
placeholder: '部门编号 / 部门名称 / 备注', placeholder: '部门编号 / 部门名称 / 备注',
style: 'width:25rem', style: 'width:25rem',
}, },
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '状态',
style: 'width:15rem',
},
]" ]"
:vue="this" :vue="this"
@search="onSearch" /> @reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@search="onSearch"
ref="search" />
</div> </div>
<div class="right-panel"> <div class="right-panel">
<na-button-add :vue="this"></na-button-add> <na-button-add :vue="this" />
<el-button :disabled="selection.length === 0" @click="batchDel" icon="el-icon-delete" plain type="danger"></el-button> <el-button :disabled="selection.length === 0" @click="batchDel" icon="el-icon-delete" plain type="danger"></el-button>
</div> </div>
</el-header> </el-header>
<el-main class="nopadding"> <el-main class="nopadding">
<sc-table <sc-table
v-loading="loading"
:apiObj="$API.sys_dept.query" :apiObj="$API.sys_dept.query"
:context-menus="['id', 'name', 'sort', 'enabled', 'createdTime', 'summary']" :context-menus="['id', 'name', 'sort', 'enabled', 'createdTime', 'summary']"
:default-sort="{ prop: 'sort', order: 'descending' }" :default-sort="{ prop: 'sort', order: 'descending' }"
@ -48,19 +58,17 @@
remote-sort remote-sort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column type="selection" width="50"></el-table-column> <el-table-column type="selection" width="50" />
<el-table-column :label="$t('部门编号')" prop="id" sortable="custom"></el-table-column> <el-table-column :label="$t('部门编号')" prop="id" sortable="custom" />
<el-table-column :label="$t('部门名称')" prop="name" sortable="custom"></el-table-column> <el-table-column :label="$t('部门名称')" prop="name" sortable="custom" />
<el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom"></el-table-column> <el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom" />
<na-col-indicator <el-table-column :label="$t('启用')" align="center" prop="enabled" sortable="custom" width="80">
:label="$t('状态')" <template #default="scope">
:options="[ <el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
{ text: '启用', type: 'success', value: true }, </template>
{ text: '禁用', type: 'danger', value: false, pulse: true }, </el-table-column>
]" <el-table-column :label="$t('备注')" prop="summary" />
prop="enabled"></na-col-indicator> <el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" />
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom"></el-table-column>
<el-table-column :label="$t('备注')" prop="summary"></el-table-column>
<na-col-operation <na-col-operation
:buttons=" :buttons="
naColOperation.buttons.concat({ naColOperation.buttons.concat({
@ -89,44 +97,44 @@ import naColOperation from '@/config/naColOperation'
import table from '@/config/table' import table from '@/config/table'
export default { export default {
computed: {
table() {
return table
},
naColOperation() {
return naColOperation
},
},
components: { components: {
saveDialog, saveDialog,
}, },
inject: ['reload'], computed: {
naColOperation() {
return naColOperation
},
table() {
return table
},
},
created() {},
data() { data() {
return { return {
dialog: {
save: false,
},
loading: false,
query: { query: {
dynamicFilter: { dynamicFilter: {
filters: [], filters: [],
}, },
filter: {}, filter: {},
}, },
dialog: {
save: false,
},
selection: [], selection: [],
} }
}, },
inject: ['reload'],
methods: { methods: {
// async changeSwitch(event, row) {
async rowDel(row) {
try { try {
const res = await this.$API.sys_dept.delete.post({ id: row.id }) await this.$API.sys_dept.setEnabled.post(row)
this.$refs.table.refresh() this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`) this.$message.success(`操作成功`)
} catch { } catch {
// //
} }
}, },
//
async batchDel() { async batchDel() {
let loading let loading
try { try {
@ -144,8 +152,21 @@ export default {
} }
loading?.close() loading?.close()
}, },
filterChange(data) {
// Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
this.$refs.search.search()
})
},
async rowDel(row) {
try {
const res = await this.$API.sys_dept.delete.post({ id: row.id })
this.$message.success(`删除 ${res.data}`)
this.$refs.table.refresh()
} catch {
//
}
},
onSearch(form) { onSearch(form) {
if (Array.isArray(form.dy.createdTime)) { if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
@ -166,6 +187,8 @@ export default {
this.$refs.table.upData() this.$refs.table.upData()
}, },
}, },
mounted() {},
watch: {},
} }
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="500" @closed="$emit('closed')" destroy-on-close> <sc-dialog v-model="visible" :title="`${titleMap[mode]}${form?.id ?? '...'}`" :width="500" @closed="$emit('closed')" destroy-on-close>
<div v-loading="loading"> <div v-loading="loading">
<el-tabs tab-position="top"> <el-tabs tab-position="top">
<el-tab-pane :label="$t('基本信息')"> <el-tab-pane :label="$t('基本信息')">

View File

@ -37,10 +37,10 @@
remote-sort remote-sort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column type="selection" width="50"></el-table-column> <el-table-column type="selection" width="50" />
<el-table-column :label="$t('项名')" prop="key" sortable="custom"></el-table-column> <el-table-column :label="$t('项名')" prop="key" sortable="custom" />
<el-table-column :label="$t('项值')" prop="value" sortable="custom"></el-table-column> <el-table-column :label="$t('项值')" prop="value" sortable="custom" />
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom"></el-table-column> <el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" />
<na-col-operation <na-col-operation
:buttons=" :buttons="
naColOperation.buttons.concat({ naColOperation.buttons.concat({
@ -51,7 +51,7 @@
type: 'danger', type: 'danger',
}) })
" "
:vue="this"></na-col-operation> :vue="this" />
</sc-table> </sc-table>
</el-main> </el-main>
</el-container> </el-container>

View File

@ -1,5 +1,5 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="400" @closed="$emit('closed')" destroy-on-close> <sc-dialog v-model="visible" :title="`${titleMap[mode]}${form?.id ?? '...'}`" :width="800" @closed="$emit('closed')" destroy-on-close>
<div v-loading="loading"> <div v-loading="loading">
<el-tabs tab-position="top"> <el-tabs tab-position="top">
<el-tab-pane :label="$t('基本信息')"> <el-tab-pane :label="$t('基本信息')">
@ -41,9 +41,9 @@ export default {
return { return {
mode: 'add', mode: 'add',
titleMap: { titleMap: {
view: '查看字典', view: '查看字典',
add: '新增字典', add: '新增字典',
edit: '编辑字典', edit: '编辑字典',
}, },
visible: false, visible: false,
loading: false, loading: false,

View File

@ -1,5 +1,5 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="400" @closed="$emit('closed')" destroy-on-close> <sc-dialog v-model="visible" :title="`${titleMap[mode]}${form?.id ?? '...'}`" :width="800" @closed="$emit('closed')" destroy-on-close>
<el-form v-loading="loading" :model="form" :rules="rules" label-width="10rem" ref="dialogForm"> <el-form v-loading="loading" :model="form" :rules="rules" label-width="10rem" ref="dialogForm">
<el-form-item :label="$t('字典名称')" prop="name"> <el-form-item :label="$t('字典名称')" prop="name">
<el-input v-model="form.name" :placeholder="$t('字典显示名称')" clearable></el-input> <el-input v-model="form.name" :placeholder="$t('字典显示名称')" clearable></el-input>

View File

@ -13,6 +13,15 @@
}), }),
], ],
}, },
{
title: $t('启用状态'),
key: 'enabled',
options: [
{ label: $t('全部'), value: '' },
{ label: $t('启用'), value: true },
{ label: $t('禁用'), value: false },
],
},
]" ]"
:label-width="6" :label-width="6"
@on-change="filterChange" @on-change="filterChange"
@ -37,16 +46,6 @@
placeholder: $t('请求方式'), placeholder: $t('请求方式'),
style: 'width:10rem', style: 'width:10rem',
}, },
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '状态',
style: 'width:10rem',
},
]" ]"
:vue="this" :vue="this"
@reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))" @reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@ -88,7 +87,7 @@
remote-sort remote-sort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column type="selection"></el-table-column> <el-table-column type="selection" />
<el-table-column :label="$t('作业编号')" prop="id" sortable="custom" width="150" /> <el-table-column :label="$t('作业编号')" prop="id" sortable="custom" width="150" />
<el-table-column :label="$t('作业名称')" prop="jobName" show-overflow-tooltip sortable="custom" /> <el-table-column :label="$t('作业名称')" prop="jobName" show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('执行计划')" align="center" prop="executionCron" sortable="custom" width="150" /> <el-table-column :label="$t('执行计划')" align="center" prop="executionCron" sortable="custom" width="150" />
@ -98,8 +97,10 @@
{ text: '空闲', type: 'success', value: 'idle' }, { text: '空闲', type: 'success', value: 'idle' },
{ text: '运行', type: 'warning', value: 'running' }, { text: '运行', type: 'warning', value: 'running' },
]" ]"
align="center"
prop="status" prop="status"
width="100"></na-col-indicator> sortable="custom"
width="100" />
<na-col-indicator <na-col-indicator
:label="$t('请求方式')" :label="$t('请求方式')"
:options=" :options="
@ -107,19 +108,19 @@
return { value: x[0], text: x[1][1] } return { value: x[0], text: x[1][1] }
}) })
" "
align="center"
prop="httpMethod" prop="httpMethod"
sortable="custom"
width="100" /> width="100" />
<el-table-column :label="$t('上次执行')" align="center"> <el-table-column :label="$t('上次执行')" align="center">
<el-table-column :label="$t('状态')" align="center" prop="lastExecTime" sortable="custom" width="150"> <el-table-column :label="$t('状态')" align="center" prop="lastExecTime" sortable="custom" width="150">
<template #default="scope"> <template #default="scope">
<div class="indicator">
<sc-status-indicator :type="scope.row.lastStatusCode === 'ok' ? 'success' : 'danger'" /> <sc-status-indicator :type="scope.row.lastStatusCode === 'ok' ? 'success' : 'danger'" />
<span>{{ {{
this.$GLOBAL.enums.httpStatusCodes[scope.row.lastStatusCode] this.$GLOBAL.enums.httpStatusCodes[scope.row.lastStatusCode]
? this.$GLOBAL.enums.httpStatusCodes[scope.row.lastStatusCode][1] ? this.$GLOBAL.enums.httpStatusCodes[scope.row.lastStatusCode][1]
: scope.row.lastStatusCode : scope.row.lastStatusCode
}}</span> }}
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('时间')" align="right" prop="lastExecTime" sortable="custom" width="100"> <el-table-column :label="$t('时间')" align="right" prop="lastExecTime" sortable="custom" width="100">
@ -231,7 +232,7 @@ export default {
methods: { methods: {
filterChange(data) { filterChange(data) {
Object.entries(data).forEach(([key, value]) => { Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
}) })
this.$refs.search.search() this.$refs.search.search()
}, },
@ -240,10 +241,10 @@ export default {
try { try {
await this.$API.sys_job.setEnabled.post(row) await this.$API.sys_job.setEnabled.post(row)
this.$message.success(`操作成功`) this.$message.success(`操作成功`)
this.$refs.table.refresh()
} catch { } catch {
// //
} }
this.$refs.table.refresh()
}, },
async execute(row) { async execute(row) {
try { try {

View File

@ -36,6 +36,9 @@
]" ]"
:vue="this" :vue="this"
@search="onSearch" @search="onSearch"
dateFormat="YYYY-MM-DD HH:mm:ss"
dateType="datetimerange"
dateValueFormat="YYYY-MM-DD HH:mm:ss"
ref="search" /> ref="search" />
</div> </div>
<div class="right-panel"></div> <div class="right-panel"></div>
@ -44,13 +47,10 @@
<sc-table <sc-table
v-loading="loading" v-loading="loading"
:apiObj="$API.sys_job.recordPagedQuery" :apiObj="$API.sys_job.recordPagedQuery"
:context-menus="['id', 'duration', 'httpMethod', 'requestUrl', 'httpStatusCode', 'createdTime']"
:default-sort="{ prop: 'createdTime', order: 'descending' }" :default-sort="{ prop: 'createdTime', order: 'descending' }"
:params="query" :params="query"
@selection-change=" :vue="this"
(items) => {
selection = items
}
"
ref="table" ref="table"
remote-filter remote-filter
remote-sort remote-sort
@ -71,18 +71,18 @@
return { value: x[0], text: x[1][1] } return { value: x[0], text: x[1][1] }
}) })
" "
align="center"
prop="httpMethod" prop="httpMethod"
sortable="custom"
width="100" /> width="100" />
<el-table-column :label="$t('响应状态码')" align="center" prop="httpStatusCode" sortable="custom" width="200"> <el-table-column :label="$t('响应状态码')" align="center" prop="httpStatusCode" sortable="custom" width="200">
<template #default="scope"> <template #default="scope">
<div class="indicator">
<sc-status-indicator :type="scope.row.httpStatusCode === 'ok' ? 'success' : 'danger'" /> <sc-status-indicator :type="scope.row.httpStatusCode === 'ok' ? 'success' : 'danger'" />
<span>{{ {{
this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode] this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode]
? this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode][1] ? this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode][1]
: scope.row.httpStatusCode : scope.row.httpStatusCode
}}</span> }}
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('请求的网络地址')" prop="requestUrl" sortable="custom" /> <el-table-column :label="$t('请求的网络地址')" prop="requestUrl" sortable="custom" />
@ -110,19 +110,25 @@ export default {
components: { components: {
saveDialog, saveDialog,
}, },
inject: ['reload'],
data() { data() {
return { return {
loading: false, loading: false,
query: { query: {
dynamicFilter: { dynamicFilter: {
filters: [], filters: [
{
field: 'createdTime',
operator: 'dateRange',
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
},
],
}, },
filter: {}, filter: {},
}, },
dialog: { dialog: {
save: false, save: false,
}, },
selection: [],
} }
}, },
watch: {}, watch: {},
@ -142,6 +148,10 @@ export default {
this.$refs.search.form.root.keywords = this.keywords this.$refs.search.form.root.keywords = this.keywords
this.$refs.search.keepKeywords = this.keywords this.$refs.search.keepKeywords = this.keywords
} }
this.$refs.search.form.dy.createdTime = this.$refs.search.keepCreatedTime = [
`${tool.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
`${tool.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
]
}, },
created() { created() {
if (this.keywords) { if (this.keywords) {
@ -149,52 +159,13 @@ export default {
} }
}, },
methods: { methods: {
//
async changeSwitch(event, row) {
try {
await this.$API.sys_job.setEnabled.post(row)
this.$message.success(`操作成功`)
} catch {
//
}
this.$refs.table.refresh()
},
//
async rowDel(row) {
try {
const res = await this.$API.sys_job.delete.post({ id: row.id })
this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
},
//
async batchDel() {
let loading
try {
await this.$confirm(`确定删除选中的 ${this.selection.length} 项吗?`, '提示', {
type: 'warning',
})
loading = this.$loading()
const res = await this.$API.sys_job.bulkDelete.post({
items: this.selection,
})
this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`)
} catch {
//
}
loading?.close()
},
// //
onSearch(form) { onSearch(form) {
if (Array.isArray(form.dy.createdTime)) { if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
field: 'createdTime', field: 'createdTime',
operator: 'dateRange', operator: 'dateRange',
value: form.dy.createdTime, value: form.dy.createdTime.map((x) => x.replace(/ 00:00:00$/, '')),
}) })
} }
@ -220,6 +191,13 @@ export default {
filters: filters, filters: filters,
}) })
} }
if (typeof form.dy.httpMethod === 'string' && form.dy.httpMethod.trim() !== '') {
this.query.dynamicFilter.filters.push({
field: 'httpMethod',
operator: 'eq',
value: form.dy.httpMethod,
})
}
this.$refs.table.upData() this.$refs.table.upData()
}, },

View File

@ -1,5 +1,11 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" destroy-on-close full-screen> <sc-dialog
v-model="visible"
:title="`${titleMap[mode]}${form?.id ?? '...'}`"
:width="800"
@closed="$emit('closed')"
destroy-on-close
full-screen>
<el-form <el-form
v-loading="loading" v-loading="loading"
:disabled="mode === 'view'" :disabled="mode === 'view'"

View File

@ -1,5 +1,11 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" destroy-on-close full-screen> <sc-dialog
v-model="visible"
:title="`${titleMap[mode]}${form?.id ?? '...'}`"
:width="800"
@closed="$emit('closed')"
destroy-on-close
full-screen>
<el-tabs v-model="tabIndex" tab-position="top"> <el-tabs v-model="tabIndex" tab-position="top">
<el-tab-pane :label="$t('基本信息')" :name="0"> <el-tab-pane :label="$t('基本信息')" :name="0">
<el-form <el-form
@ -24,6 +30,9 @@
<el-form-item :label="$t('作业名称')" prop="jobName"> <el-form-item :label="$t('作业名称')" prop="jobName">
<el-input v-model="form.jobName" clearable /> <el-input v-model="form.jobName" clearable />
</el-form-item> </el-form-item>
<el-form-item :label="$t('备注')" prop="summary">
<el-input v-model="form.summary" clearable type="textarea" />
</el-form-item>
<el-form-item v-if="mode === 'view'" :label="$t('上次执行时间')" prop="lastExecTime"> <el-form-item v-if="mode === 'view'" :label="$t('上次执行时间')" prop="lastExecTime">
<el-input v-model="form.lastExecTime" clearable /> <el-input v-model="form.lastExecTime" clearable />
</el-form-item> </el-form-item>
@ -42,6 +51,7 @@
:theme="this.$TOOL.data.get('APP_DARK') ? 'github_dark' : 'github'" :theme="this.$TOOL.data.get('APP_DARK') ? 'github_dark' : 'github'"
lang="json" lang="json"
style="height: 5rem; width: 100%" /> style="height: 5rem; width: 100%" />
<el-button @click="form.requestHeader = jsonFormat(form.requestHeader)" type="text">JSON格式化</el-button>
</el-form-item> </el-form-item>
<el-form-item :label="$t('请求体')" prop="requestBody"> <el-form-item :label="$t('请求体')" prop="requestBody">
<v-ace-editor <v-ace-editor
@ -49,6 +59,7 @@
:theme="this.$TOOL.data.get('APP_DARK') ? 'github_dark' : 'github'" :theme="this.$TOOL.data.get('APP_DARK') ? 'github_dark' : 'github'"
lang="json" lang="json"
style="height: 10rem; width: 100%" /> style="height: 10rem; width: 100%" />
<el-button @click="form.requestBody = jsonFormat(form.requestBody)" type="text">JSON格式化</el-button>
</el-form-item> </el-form-item>
<el-form-item :label="$t('请求的网络地址')" prop="requestUrl"> <el-form-item :label="$t('请求的网络地址')" prop="requestUrl">
<el-input v-model="form.requestUrl" clearable /> <el-input v-model="form.requestUrl" clearable />
@ -109,6 +120,7 @@
<script> <script>
import scEditor from '@/components/scEditor/index.vue' import scEditor from '@/components/scEditor/index.vue'
import Record from '@/views/sys/job/record/index.vue' import Record from '@/views/sys/job/record/index.vue'
import vkbeautify from 'vkbeautify/index'
export default { export default {
components: { components: {
@ -191,6 +203,17 @@ export default {
}, },
mounted() {}, mounted() {},
methods: { methods: {
jsonFormat(obj) {
try {
obj = this.vkbeautify().json(obj, 2)
} catch {
this.$message.error(this.$t('非JSON格式'))
}
return obj
},
vkbeautify() {
return vkbeautify
},
// //
async open(mode = 'add', data, tabIndex = 0) { async open(mode = 'add', data, tabIndex = 0) {
this.visible = true this.visible = true

View File

@ -1,17 +1,30 @@
<template> <template>
<el-container> <el-container>
<el-header style="height: auto; padding: 0 1rem">
<sc-select-filter
:data="[
{
title: $t('登录结果'),
key: 'loginResult',
options: [
{ label: $t('全部'), value: '' },
{ label: $t('成功'), value: true },
{ label: $t('失败'), value: false },
],
},
]"
:label-width="6"
@on-change="filterChange"
ref="selectFilter"></sc-select-filter>
</el-header>
<el-header> <el-header>
<div class="left-panel"> <div class="left-panel">
<na-search <na-search
:controls="[ :controls="[
{ {
type: 'select', type: 'input',
field: ['dy', 'httpStatusCode'], field: ['dy', 'id'],
options: [ placeholder: '日志编号',
{ label: '成功', value: '200,299' },
{ label: '失败', value: '300,999' },
],
placeholder: '登录结果',
style: 'width:15rem', style: 'width:15rem',
}, },
{ {
@ -28,13 +41,18 @@
}, },
]" ]"
:vue="this" :vue="this"
@reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@search="onSearch" @search="onSearch"
dateFormat="YYYY-MM-DD HH:mm:ss"
dateType="datetimerange"
dateValueFormat="YYYY-MM-DD HH:mm:ss"
ref="search" /> ref="search" />
</div> </div>
<div class="right-panel"></div> <div class="right-panel"></div>
</el-header> </el-header>
<el-main class="nopadding"> <el-main class="nopadding">
<sc-table <sc-table
v-loading="loading"
:apiObj="$API.sys_log.pagedQuery" :apiObj="$API.sys_log.pagedQuery"
:context-menus="['id', 'httpStatusCode', 'extraData', 'createdClientIp', 'os', 'createdUserAgent', 'createdTime']" :context-menus="['id', 'httpStatusCode', 'extraData', 'createdClientIp', 'os', 'createdUserAgent', 'createdTime']"
:context-opers="['view']" :context-opers="['view']"
@ -43,26 +61,26 @@
:vue="this" :vue="this"
@row-click="rowClick" @row-click="rowClick"
ref="table" ref="table"
remoteFilter remote-filter
remoteSort remote-sort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column :label="$t('日志编号')" prop="id" sortable="custom" width="150"></el-table-column> <el-table-column :label="$t('日志编号')" prop="id" sortable="custom" width="150" />
<el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" width="170" />
<el-table-column :label="$t('结果')" align="center" prop="httpStatusCode" sortable="custom" width="80"> <el-table-column :label="$t('结果')" align="center" prop="httpStatusCode" sortable="custom" width="80">
<template #default="scope"> <template #default="scope">
<sc-status-indicator :type="scope.row.httpStatusCode === 200 ? 'success' : 'danger'" /> <sc-status-indicator :type="scope.row.httpStatusCode === 200 ? 'success' : 'danger'" />
{{ scope.row.httpStatusCode === 200 ? '成功' : '失败' }} {{ scope.row.httpStatusCode === 200 ? '成功' : '失败' }}
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('登录名')" prop="extraData" sortable="custom" width="200"></el-table-column> <el-table-column :label="$t('登录名')" prop="extraData" sortable="custom" width="200" />
<el-table-column :label="$t('客户端IP')" prop="createdClientIp" sortable="custom" width="200"> <el-table-column :label="$t('客户端IP')" prop="createdClientIp" sortable="custom" width="200">
<template #default="scope"> <template #default="scope">
<na-ip :ip="scope.row.createdClientIp"></na-ip> <na-ip :ip="scope.row.createdClientIp"></na-ip>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('操作系统')" prop="os" width="150"></el-table-column> <el-table-column :label="$t('操作系统')" prop="os" width="150" />
<el-table-column :label="$t('用户代理')" prop="createdUserAgent" show-overflow-tooltip sortable="custom"></el-table-column> <el-table-column :label="$t('用户代理')" prop="createdUserAgent" show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" width="170"></el-table-column>
</sc-table> </sc-table>
</el-main> </el-main>
</el-container> </el-container>
@ -84,9 +102,16 @@ export default {
inject: ['reload'], inject: ['reload'],
data() { data() {
return { return {
loading: false,
query: { query: {
dynamicFilter: { dynamicFilter: {
filters: [], filters: [
{
field: 'createdTime',
operator: 'dateRange',
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
},
],
}, },
filter: {}, filter: {},
}, },
@ -97,6 +122,10 @@ export default {
}, },
mounted() { mounted() {
this.$refs.search.form.dy.apiId = 'api/sys/user/login.by.pwd' this.$refs.search.form.dy.apiId = 'api/sys/user/login.by.pwd'
this.$refs.search.form.dy.createdTime = this.$refs.search.keepCreatedTime = [
`${tool.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
`${tool.dateFormat(new Date(), 'yyyy-MM-dd')} 00:00:00`,
]
}, },
created() { created() {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
@ -106,13 +135,26 @@ export default {
}) })
}, },
methods: { methods: {
filterChange(data) {
Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
})
this.$refs.search.search()
},
// //
onSearch(form) { onSearch(form) {
if (this.query.dynamicFilter.filters.findIndex((x) => x.field === 'apiId') < 0) {
this.query.dynamicFilter.filters.push({
field: 'apiId',
operator: 'eq',
value: 'api/sys/user/login.by.pwd',
})
}
if (Array.isArray(form.dy.createdTime)) { if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
field: 'createdTime', field: 'createdTime',
operator: 'dateRange', operator: 'dateRange',
value: form.dy.createdTime, value: form.dy.createdTime.map((x) => x.replace(/ 00:00:00$/, '')),
}) })
} }
if (form.dy.httpStatusCode) { if (form.dy.httpStatusCode) {
@ -129,6 +171,13 @@ export default {
value: form.dy.apiId, value: form.dy.apiId,
}) })
} }
if (form.dy.id) {
this.query.dynamicFilter.filters.push({
field: 'id',
operator: 'eq',
value: form.dy.id,
})
}
if (form.dy.extraData) { if (form.dy.extraData) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
field: 'extraData', field: 'extraData',
@ -143,6 +192,22 @@ export default {
value: form.dy.createdClientIp, value: form.dy.createdClientIp,
}) })
} }
if (typeof form.dy.loginResult === 'boolean') {
this.query.dynamicFilter.filters.push(
form.dy.loginResult
? {
field: 'httpStatusCode',
operator: 'range',
value: '200,299',
}
: {
field: 'httpStatusCode',
operator: 'range',
value: '300,999',
},
)
}
this.$refs.table.upData() this.$refs.table.upData()
}, },

View File

@ -68,14 +68,12 @@
remoteSort remoteSort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column :label="$t('日志编号')" align="center" prop="id" sortable="custom" width="150"></el-table-column> <el-table-column :label="$t('日志编号')" prop="id" sortable="custom" width="150"> </el-table-column
><el-table-column :label="$t('创建时间')" prop="createdTime" sortable="custom" width="170" />
<el-table-column :label="$t('响应码')" align="center" prop="httpStatusCode" sortable="custom" width="100"> <el-table-column :label="$t('响应码')" align="center" prop="httpStatusCode" sortable="custom" width="100">
<template #default="scope"> <template #default="{ row }">
<div class="indicator"> <sc-status-indicator :type="row.httpStatusCode >= 200 && row.httpStatusCode < 300 ? 'success' : 'danger'" />
<sc-status-indicator {{ row.httpStatusCode }}
:style="`background: #${Math.abs(this.$TOOL.crypto.hashCode(scope.row.httpStatusCode)).toString(16).substring(0, 6)}`" />
<span> {{ scope.row.httpStatusCode }}</span>
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('请求服务')" align="center"> <el-table-column :label="$t('请求服务')" align="center">
@ -87,11 +85,9 @@
</el-table-column> </el-table-column>
<el-table-column :label="$t('方法')" align="center" prop="method" sortable="custom" width="100"> <el-table-column :label="$t('方法')" align="center" prop="method" sortable="custom" width="100">
<template #default="scope"> <template #default="scope">
<div class="indicator">
<sc-status-indicator <sc-status-indicator
:style="`background: #${Math.abs(this.$TOOL.crypto.hashCode(scope.row.method)).toString(16).substring(0, 6)}`" /> :style="`background: #${Math.abs(this.$TOOL.crypto.hashCode(scope.row.method)).toString(16).substring(0, 6)}`" />
<span> {{ scope.row.method }}</span> {{ scope.row.method }}
</div>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column <el-table-column
@ -113,8 +109,7 @@
<na-ip :ip="scope.row.createdClientIp"></na-ip> <na-ip :ip="scope.row.createdClientIp"></na-ip>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('操作系统')" align="center" prop="os" width="150"></el-table-column> <el-table-column :label="$t('操作系统')" align="center" prop="os" width="150" />
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" width="170"></el-table-column>
</sc-table> </sc-table>
</el-main> </el-main>
</el-container> </el-container>
@ -126,6 +121,7 @@
import naInfo from '@/components/naInfo/index.vue' import naInfo from '@/components/naInfo/index.vue'
import tool from '@/utils/tool' import tool from '@/utils/tool'
import ScTable from '@/components/scTable/index.vue' import ScTable from '@/components/scTable/index.vue'
import ScStatusIndicator from '@/components/scMini/scStatusIndicator.vue'
export default { export default {
inject: ['reload'], inject: ['reload'],
@ -135,6 +131,7 @@ export default {
}, },
}, },
components: { components: {
ScStatusIndicator,
ScTable, ScTable,
naInfo, naInfo,
}, },

View File

@ -57,7 +57,7 @@
remote-sort remote-sort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column type="selection"></el-table-column> <el-table-column type="selection" />
<el-table-column :label="$t('消息编号')" prop="id" sortable="custom" width="150" /> <el-table-column :label="$t('消息编号')" prop="id" sortable="custom" width="150" />
<na-col-avatar :label="$t('用户名')" prop="createdUserName" /> <na-col-avatar :label="$t('用户名')" prop="createdUserName" />
<na-col-indicator <na-col-indicator
@ -67,9 +67,10 @@
return { value: x[0], text: x[1][1] } return { value: x[0], text: x[1][1] }
}) })
" "
align="center"
prop="msgType" prop="msgType"
width="100"> sortable="custom"
</na-col-indicator> width="100" />
<el-table-column :label="$t('消息主题')" prop="title" show-overflow-tooltip sortable="custom" /> <el-table-column :label="$t('消息主题')" prop="title" show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('消息摘要')" prop="summary" show-overflow-tooltip sortable="custom" /> <el-table-column :label="$t('消息摘要')" prop="summary" show-overflow-tooltip sortable="custom" />
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" width="170" /> <el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" width="170" />

View File

@ -1,5 +1,11 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" destroy-on-close full-screen> <sc-dialog
v-model="visible"
:title="`${titleMap[mode]}${form?.id ?? '...'}`"
:width="800"
@closed="$emit('closed')"
destroy-on-close
full-screen>
<el-form <el-form
v-loading="loading" v-loading="loading"
:disabled="mode === 'view'" :disabled="mode === 'view'"

View File

@ -1,5 +1,22 @@
<template> <template>
<el-container> <el-container>
<el-header style="height: auto; padding: 0 1rem">
<sc-select-filter
:data="[
{
title: $t('启用状态'),
key: 'enabled',
options: [
{ label: $t('全部'), value: '' },
{ label: $t('启用'), value: true },
{ label: $t('禁用'), value: false },
],
},
]"
:label-width="6"
@on-change="filterChange"
ref="selectFilter"></sc-select-filter>
</el-header>
<el-header> <el-header>
<div class="left-panel"> <div class="left-panel">
<na-search <na-search
@ -10,16 +27,6 @@
placeholder: '角色编号 / 角色名称 / 备注', placeholder: '角色编号 / 角色名称 / 备注',
style: 'width:20rem', style: 'width:20rem',
}, },
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '状态',
style: 'width:15rem',
},
{ {
type: 'select', type: 'select',
field: ['dy', 'ignorePermissionControl'], field: ['dy', 'ignorePermissionControl'],
@ -42,15 +49,18 @@
}, },
]" ]"
:vue="this" :vue="this"
@search="onSearch" /> @reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@search="onSearch"
ref="search" />
</div> </div>
<div class="right-panel"> <div class="right-panel">
<na-button-add :vue="this"></na-button-add> <na-button-add :vue="this" />
<el-button :disabled="selection.length === 0" @click="batchDel" icon="el-icon-delete" plain type="danger"></el-button> <el-button :disabled="selection.length === 0" @click="batchDel" icon="el-icon-delete" plain type="danger"></el-button>
</div> </div>
</el-header> </el-header>
<el-main class="nopadding"> <el-main class="nopadding">
<sc-table <sc-table
v-loading="loading"
:apiObj="$API.sys_role.pagedQuery" :apiObj="$API.sys_role.pagedQuery"
:context-menus="['id', 'name', 'sort', 'enabled', 'ignorePermissionControl', 'dataScope', 'displayDashboard', 'createdTime']" :context-menus="['id', 'name', 'sort', 'enabled', 'ignorePermissionControl', 'dataScope', 'displayDashboard', 'createdTime']"
:default-sort="{ prop: 'sort', order: 'descending' }" :default-sort="{ prop: 'sort', order: 'descending' }"
@ -66,24 +76,24 @@
remote-sort remote-sort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column type="selection" width="50"></el-table-column> <el-table-column type="selection" />
<el-table-column :label="$t('角色编号')" prop="id" sortable="custom"></el-table-column> <el-table-column :label="$t('角色编号')" prop="id" sortable="custom" />
<el-table-column :label="$t('角色名称')" prop="name" sortable="custom"></el-table-column> <el-table-column :label="$t('角色名称')" prop="name" sortable="custom" />
<el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom"></el-table-column> <el-table-column :label="$t('排序')" align="right" prop="sort" sortable="custom" />
<na-col-indicator <el-table-column :label="$t('启用')" align="center" prop="enabled" sortable="custom" width="80">
:label="$t('状态')" <template #default="scope">
:options="[ <el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
{ text: '启用', type: 'success', value: true }, </template>
{ text: '禁用', type: 'danger', value: false, pulse: true }, </el-table-column>
]"
prop="enabled"></na-col-indicator>
<na-col-indicator <na-col-indicator
:label="$t('无限权限')" :label="$t('无限权限')"
:options="[ :options="[
{ text: '是', type: 'success', value: true, pulse: true }, { text: '是', type: 'success', value: true, pulse: true },
{ text: '否', type: 'danger', value: false }, { text: '否', type: 'danger', value: false },
]" ]"
prop="ignorePermissionControl"></na-col-indicator> align="center"
prop="ignorePermissionControl"
sortable="custom"></na-col-indicator>
<na-col-indicator <na-col-indicator
:label="$t('数据范围')" :label="$t('数据范围')"
:options=" :options="
@ -102,9 +112,11 @@
{ text: '是', type: 'success', value: true }, { text: '是', type: 'success', value: true },
{ text: '否', type: 'danger', value: false }, { text: '否', type: 'danger', value: false },
]" ]"
prop="displayDashboard"></na-col-indicator> align="center"
prop="displayDashboard"
sortable="custom" />
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom"></el-table-column> <el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" />
<na-col-operation <na-col-operation
:buttons=" :buttons="
naColOperation.buttons.concat({ naColOperation.buttons.concat({
@ -115,7 +127,7 @@
type: 'danger', type: 'danger',
}) })
" "
:vue="this"></na-col-operation> :vue="this" />
</sc-table> </sc-table>
</el-main> </el-main>
</el-container> </el-container>
@ -133,44 +145,44 @@ import naColOperation from '@/config/naColOperation'
import table from '@/config/table' import table from '@/config/table'
export default { export default {
computed: {
table() {
return table
},
naColOperation() {
return naColOperation
},
},
components: { components: {
saveDialog, saveDialog,
}, },
inject: ['reload'], computed: {
naColOperation() {
return naColOperation
},
table() {
return table
},
},
created() {},
data() { data() {
return { return {
dialog: {
save: false,
},
loading: false,
query: { query: {
dynamicFilter: { dynamicFilter: {
filters: [], filters: [],
}, },
filter: {}, filter: {},
}, },
dialog: {
save: false,
},
selection: [], selection: [],
} }
}, },
inject: ['reload'],
methods: { methods: {
// async changeSwitch(event, row) {
async rowDel(row) {
try { try {
const res = await this.$API.sys_role.delete.post({ id: row.id }) await this.$API.sys_role.setEnabled.post(row)
this.$refs.table.refresh() this.$refs.table.refresh()
this.$message.success(`删除 ${res.data}`) this.$message.success(`操作成功`)
} catch { } catch {
// //
} }
}, },
//
async batchDel() { async batchDel() {
let loading let loading
try { try {
@ -188,8 +200,21 @@ export default {
} }
loading?.close() loading?.close()
}, },
filterChange(data) {
// Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
this.$refs.search.search()
})
},
async rowDel(row) {
try {
const res = await this.$API.sys_role.delete.post({ id: row.id })
this.$message.success(`删除 ${res.data}`)
this.$refs.table.refresh()
} catch {
//
}
},
onSearch(form) { onSearch(form) {
if (Array.isArray(form.dy.createdTime)) { if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
@ -222,10 +247,11 @@ export default {
value: form.dy.displayDashboard, value: form.dy.displayDashboard,
}) })
} }
this.$refs.table.upData() this.$refs.table.upData()
}, },
}, },
mounted() {},
watch: {},
} }
</script> </script>

View File

@ -1,5 +1,5 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" destroy-on-close> <sc-dialog v-model="visible" :title="`${titleMap[mode]}${form?.id ?? '...'}`" @closed="$emit('closed')" destroy-on-close full-screen>
<div v-loading="loading"> <div v-loading="loading">
<el-tabs tab-position="top"> <el-tabs tab-position="top">
<el-tab-pane :label="$t('基本信息')"> <el-tab-pane :label="$t('基本信息')">

View File

@ -1,5 +1,22 @@
<template> <template>
<el-container> <el-container>
<el-header style="height: auto; padding: 0 1rem">
<sc-select-filter
:data="[
{
title: $t('启用状态'),
key: 'enabled',
options: [
{ label: $t('全部'), value: '' },
{ label: $t('启用'), value: true },
{ label: $t('禁用'), value: false },
],
},
]"
:label-width="6"
@on-change="filterChange"
ref="selectFilter"></sc-select-filter>
</el-header>
<el-header> <el-header>
<div class="left-panel"> <div class="left-panel">
<na-search <na-search
@ -26,19 +43,11 @@
placeholder: '所属部门', placeholder: '所属部门',
style: 'width:15rem', style: 'width:15rem',
}, },
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '是否启用',
style: 'width:10rem',
},
]" ]"
:vue="this" :vue="this"
@search="onSearch" /> @reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@search="onSearch"
ref="search" />
</div> </div>
<div class="right-panel"> <div class="right-panel">
<na-button-add :vue="this" /> <na-button-add :vue="this" />
@ -63,11 +72,11 @@
remote-sort remote-sort
row-key="id" row-key="id"
stripe> stripe>
<el-table-column type="selection"></el-table-column> <el-table-column type="selection" />
<el-table-column :label="$t('用户编号')" prop="id" sortable="custom" width="150"></el-table-column> <el-table-column :label="$t('用户编号')" prop="id" sortable="custom" width="150" />
<na-col-avatar :label="$t('用户名')" prop="userName" /> <na-col-avatar :label="$t('用户名')" prop="userName" />
<el-table-column :label="$t('手机号')" align="center" prop="mobile" sortable="custom" width="120"></el-table-column> <el-table-column :label="$t('手机号')" align="center" prop="mobile" sortable="custom" width="120" />
<el-table-column :label="$t('邮箱')" prop="email" sortable="custom"></el-table-column> <el-table-column :label="$t('邮箱')" prop="email" sortable="custom" />
<na-col-tags :label="$t('所属角色')" @click="(item) => openDialog('sys_role', item.id, 'roleSave')" field="name" prop="roles" /> <na-col-tags :label="$t('所属角色')" @click="(item) => openDialog('sys_role', item.id, 'roleSave')" field="name" prop="roles" />
<na-col-tags :label="$t('所属部门')" @click="(item) => openDialog('sys_dept', item.id, 'deptSave')" field="name" prop="dept" /> <na-col-tags :label="$t('所属部门')" @click="(item) => openDialog('sys_dept', item.id, 'deptSave')" field="name" prop="dept" />
<el-table-column :label="$t('启用')" align="center" prop="enabled" sortable="custom" width="80"> <el-table-column :label="$t('启用')" align="center" prop="enabled" sortable="custom" width="80">
@ -75,8 +84,8 @@
<el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch> <el-switch v-model="scope.row.enabled" @change="changeSwitch($event, scope.row)"></el-switch>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom"></el-table-column> <el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" />
<na-col-operation :vue="this"></na-col-operation> <na-col-operation :vue="this" />
</sc-table> </sc-table>
</el-main> </el-main>
</el-container> </el-container>
@ -98,13 +107,23 @@ import table from '@/config/table'
export default { export default {
components: { components: {
saveDialog,
roleSaveDialog,
deptSaveDialog, deptSaveDialog,
roleSaveDialog,
saveDialog,
}, },
inject: ['reload'], computed: {
table() {
return table
},
},
created() {},
data() { data() {
return { return {
dialog: {
deptSave: false,
roleSave: false,
save: false,
},
loading: false, loading: false,
query: { query: {
dynamicFilter: { dynamicFilter: {
@ -112,32 +131,25 @@ export default {
}, },
filter: {}, filter: {},
}, },
dialog: {
roleSave: false,
deptSave: false,
save: false,
},
selection: [], selection: [],
} }
}, },
watch: {}, inject: ['reload'],
computed: {
table() {
return table
},
},
mounted() {},
created() {},
methods: { methods: {
//
async changeSwitch(event, row) { async changeSwitch(event, row) {
try { try {
await this.$API.sys_user.setEnabled.post(row) await this.$API.sys_user.setEnabled.post(row)
this.$refs.table.refresh()
this.$message.success(`操作成功`) this.$message.success(`操作成功`)
} catch { } catch {
// //
} }
this.$refs.table.refresh() },
filterChange(data) {
Object.entries(data).forEach(([key, value]) => {
this.$refs.search.form.dy[key] = value === 'true' ? true : value === 'false' ? false : value
this.$refs.search.search()
})
}, },
async openDialog(api, id, dialog) { async openDialog(api, id, dialog) {
this.loading = true this.loading = true
@ -149,8 +161,6 @@ export default {
await this.$nextTick() await this.$nextTick()
this.$refs[`${dialog}Dialog`].open('view', res.data[0]) this.$refs[`${dialog}Dialog`].open('view', res.data[0])
}, },
//
onSearch(form) { onSearch(form) {
if (Array.isArray(form.dy.createdTime)) { if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
@ -171,6 +181,8 @@ export default {
this.$refs.table.upData() this.$refs.table.upData()
}, },
}, },
mounted() {},
watch: {},
} }
</script> </script>

View File

@ -1,5 +1,11 @@
<template> <template>
<sc-dialog v-model="visible" :title="titleMap[mode]" :width="800" @closed="$emit('closed')" append-to-body destroy-on-close> <sc-dialog
v-model="visible"
:title="`${titleMap[mode]}${form?.id ?? '...'}`"
:width="800"
@closed="$emit('closed')"
append-to-body
destroy-on-close>
<el-form <el-form
v-loading="loading" v-loading="loading"
:disabled="mode === 'view'" :disabled="mode === 'view'"