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位以上
用户头像不能为空
用户编号不存在
用户编号不能为空
目标设备不能为空
短信验证请求不能为空
站内信不存在

View File

@ -1371,6 +1371,22 @@ public class AllTests(WebApplicationFactory<Startup> factory, ITestOutputHelper
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 />
[InlineData(default)]
[Theory]

View File

@ -7,6 +7,8 @@ namespace NetAdmin.Domain.DbMaps.Sys;
/// 计划作业执行记录表
/// </summary>
[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))]
public record Sys_JobRecord : LiteImmutableEntity
{

View File

@ -6,8 +6,9 @@ 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(CreatedTime), nameof(CreatedTime), 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(HttpStatusCode), nameof(HttpStatusCode), false)]
[Table(Name = Chars.FLG_DB_TABLE_NAME_PREFIX + nameof(Sys_RequestLog))]
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" />
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
[Required(ErrorMessageResourceType = typeof(Ln), ErrorMessageResourceName = nameof(Ln.用户编号不能为空))]
public override long Id { get; init; }
/// <inheritdoc cref="IFieldVersion.Version" />

View File

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

View File

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

View File

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

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>
/// <remarks>
/// 并发执行时锁竞争失败
/// </remarks>
#pragma warning disable RCS1194
public sealed class NetAdminGetLockerException : NetAdminException;
public sealed class NetAdminGetLockerException() : NetAdminInvalidOperationException(null) { }
#pragma warning restore RCS1194

View File

@ -8,6 +8,5 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// </remarks>
#pragma warning disable RCS1194
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>
#pragma warning disable RCS1194, DesignedForInheritance
public class NetAdminInvalidOperationException : NetAdminException
#pragma warning restore DesignedForInheritance, RCS1194
{
/// <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) { }
}
#pragma warning disable DesignedForInheritance, RCS1194
public class NetAdminInvalidOperationException(string message, Exception innerException = null)
#pragma warning restore RCS1194, DesignedForInheritance
: NetAdminException(ErrorCodes.InvalidOperation, message, innerException) { }

View File

@ -6,25 +6,7 @@ namespace NetAdmin.Infrastructure.Exceptions;
/// <remarks>
/// 运行结果是非预期的,例如事务失败回滚
/// </remarks>
#pragma warning disable RCS1194, DesignedForInheritance
public class NetAdminUnexpectedException : NetAdminException
#pragma warning restore DesignedForInheritance, RCS1194
{
/// <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) { }
}
#pragma warning disable RCS1194
public sealed class NetAdminUnexpectedException(string message, Exception innerException = null)
#pragma warning restore RCS1194
: NetAdminException(ErrorCodes.Unexpected, message, innerException);

View File

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

View File

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

View File

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

View File

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

View File

@ -16,4 +16,9 @@ public interface IRoleModule : ICrudModule<CreateRoleReq, QueryRoleRsp // 创建
/// 编辑角色
/// </summary>
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>>();
}
/// <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)
{
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.RequestUrl == req.RequestUrl)
.Set(a => a.UserId == req.UserId)
.Where(a => a.Id == req.Id)
.Where(a => a.Version == req.Version);
.Set(a => a.Summary == req.Summary)
.Where(a => a.Id == req.Id);
#if DBTYPE_SQLSERVER
return (await update.ExecuteUpdatedAsync().ConfigureAwait(false)).FirstOrDefault()?.Adapt<QueryJobRsp>();
@ -276,11 +276,22 @@ public sealed class JobService(BasicRepository<Sys_Job, long> rpo, IJobRecordSer
}
/// <inheritdoc />
public Task<int> ReleaseStuckTaskAsync()
public async Task<int> ReleaseStuckTaskAsync()
{
return UpdateAsync( //
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));
var ret1 = await UpdateAsync( // 运行中,运行时间超过超时设定;置为空闲状态
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)
, 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 />

View File

@ -123,6 +123,13 @@ public sealed class RoleService(BasicRepository<Sys_Role, long> rpo) //
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)
{
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);
}
/// <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);
}
/// <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);
}
/// <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);
}
/// <summary>
/// 启用/禁用角色
/// </summary>
public Task SetEnabledAsync(SetRoleEnabledReq req)
{
return Cache.SetEnabledAsync(req);
}
}

View File

@ -20,8 +20,10 @@ public sealed class OperationLogger : IEventSubscriber
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;
}

View File

@ -81,4 +81,15 @@ export default {
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)
},
},
/**
* 启用/禁用角色
*/
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>
</template>
<el-table-column :label="$t('地区')" prop="key" width="400"></el-table-column>
<el-table-column :label="$t('代码')" prop="value"></el-table-column>
<el-table-column :label="$t('地区')" prop="key" width="400" />
<el-table-column :label="$t('代码')" prop="value" />
</sc-table-select>
</template>
<style scoped></style>

View File

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

View File

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

View File

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

View File

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

View File

@ -36,7 +36,7 @@
@select-all="selectAll"
max-height="30rem"
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">
<template #default="scope"
><span>{{ scope.$index + (currentPage - 1) * pageSize + 1 }}</span></template

View File

@ -484,6 +484,15 @@ textarea {
.mt-4 {
margin-top: 1rem;
}
.mr-2 {
margin-right: 0.5rem;
}
.mr-4 {
margin-right: 1rem;
}
.mt-8 {
margin-top: 2rem;
}
@ -510,11 +519,4 @@ textarea {
.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;
}
.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 {
transition: 0.1s;
}

View File

@ -1,5 +1,5 @@
<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-row class="items-center justify-content-center">
<el-col v-if="mode === 'edit'" :lg="10">

View File

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

View File

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

View File

@ -1,31 +1,39 @@
<template>
<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>
<div class="left-panel">
<na-search
:controls="[
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '是否启用',
style: 'width:10rem',
},
]"
:controls="[]"
:vue="this"
@reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@search="onSearch"
ref="search" />
</div>
<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>
</div>
</el-header>
<el-main class="nopadding">
<sc-table
v-loading="loading"
:apiObj="$API.sys_config.pagedQuery"
:context-menus="['id', 'userRegisterConfirm', 'enabled', 'createdTime']"
:params="query"
@ -38,11 +46,11 @@
ref="table"
row-key="id"
stripe>
<el-table-column align="center" type="selection"></el-table-column>
<el-table-column :label="$t('配置编号')" align="center" prop="id" width="170"></el-table-column>
<el-table-column type="selection" />
<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" prop="userRegisterDept.name" width="150"></el-table-column>
<el-table-column :label="$t('默认角色')" align="center" prop="userRegisterRole.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 :label="$t('人工审核')" align="center" prop="userRegisterConfirm" width="100">
<template #default="scope">
<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>
</template>
</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
:buttons="
naColOperation.buttons.concat({
@ -85,47 +93,84 @@ import table from '@/config/table'
import tool from '@/utils/tool'
export default {
inject: ['reload'],
computed: {
table() {
return table
},
naColOperation() {
return naColOperation
},
},
components: {
saveDialog,
},
computed: {
naColOperation() {
return naColOperation
},
table() {
return table
},
},
created() {},
data() {
return {
dialog: {
save: false,
info: false,
save: false,
},
selection: [],
loading: false,
query: {
dynamicFilter: {
filters: [],
},
filter: {},
},
selection: [],
}
},
mounted() {},
inject: ['reload'],
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) {
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({
field: 'createdTime',
operator: 'dateRange',
value: [
tool.dateFormat(start.setDate(start.getDate() + 1)).substring(0, 10),
tool.dateFormat(end.setDate(end.getDate() + 1)).substring(0, 10),
],
value: form.dy.createdTime,
})
}
@ -139,51 +184,9 @@ export default {
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>
<style></style>
<style scoped></style>

View File

@ -1,5 +1,5 @@
<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">
<el-tabs v-if="!loading" tab-position="top">
<el-tab-pane :label="$t('基本信息')">
@ -112,4 +112,8 @@ export default {
}
</script>
<style></style>
<style scoped>
.el-collapse {
border-top: none;
}
</style>

View File

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

View File

@ -1,5 +1,5 @@
<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">
<el-tabs tab-position="top">
<el-tab-pane :label="$t('基本信息')">

View File

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

View File

@ -1,5 +1,5 @@
<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">
<el-tabs tab-position="top">
<el-tab-pane :label="$t('基本信息')">
@ -41,9 +41,9 @@ export default {
return {
mode: 'add',
titleMap: {
view: '查看字典',
add: '新增字典',
edit: '编辑字典',
view: '查看字典',
add: '新增字典',
edit: '编辑字典',
},
visible: false,
loading: false,

View File

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

View File

@ -36,6 +36,9 @@
]"
:vue="this"
@search="onSearch"
dateFormat="YYYY-MM-DD HH:mm:ss"
dateType="datetimerange"
dateValueFormat="YYYY-MM-DD HH:mm:ss"
ref="search" />
</div>
<div class="right-panel"></div>
@ -44,13 +47,10 @@
<sc-table
v-loading="loading"
:apiObj="$API.sys_job.recordPagedQuery"
:context-menus="['id', 'duration', 'httpMethod', 'requestUrl', 'httpStatusCode', 'createdTime']"
:default-sort="{ prop: 'createdTime', order: 'descending' }"
:params="query"
@selection-change="
(items) => {
selection = items
}
"
:vue="this"
ref="table"
remote-filter
remote-sort
@ -71,18 +71,18 @@
return { value: x[0], text: x[1][1] }
})
"
align="center"
prop="httpMethod"
sortable="custom"
width="100" />
<el-table-column :label="$t('响应状态码')" align="center" prop="httpStatusCode" sortable="custom" width="200">
<template #default="scope">
<div class="indicator">
<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][1]
: scope.row.httpStatusCode
}}</span>
</div>
<sc-status-indicator :type="scope.row.httpStatusCode === 'ok' ? 'success' : 'danger'" />
{{
this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode]
? this.$GLOBAL.enums.httpStatusCodes[scope.row.httpStatusCode][1]
: scope.row.httpStatusCode
}}
</template>
</el-table-column>
<el-table-column :label="$t('请求的网络地址')" prop="requestUrl" sortable="custom" />
@ -110,19 +110,25 @@ export default {
components: {
saveDialog,
},
inject: ['reload'],
data() {
return {
loading: false,
query: {
dynamicFilter: {
filters: [],
filters: [
{
field: 'createdTime',
operator: 'dateRange',
value: [tool.dateFormat(new Date(), 'yyyy-MM-dd'), tool.dateFormat(new Date(), 'yyyy-MM-dd')],
},
],
},
filter: {},
},
dialog: {
save: false,
},
selection: [],
}
},
watch: {},
@ -142,6 +148,10 @@ export default {
this.$refs.search.form.root.keywords = 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() {
if (this.keywords) {
@ -149,52 +159,13 @@ export default {
}
},
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) {
if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({
field: 'createdTime',
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,
})
}
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()
},

View File

@ -1,5 +1,11 @@
<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
v-loading="loading"
:disabled="mode === 'view'"

View File

@ -1,5 +1,11 @@
<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-tab-pane :label="$t('基本信息')" :name="0">
<el-form
@ -24,6 +30,9 @@
<el-form-item :label="$t('作业名称')" prop="jobName">
<el-input v-model="form.jobName" clearable />
</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-input v-model="form.lastExecTime" clearable />
</el-form-item>
@ -42,6 +51,7 @@
:theme="this.$TOOL.data.get('APP_DARK') ? 'github_dark' : 'github'"
lang="json"
style="height: 5rem; width: 100%" />
<el-button @click="form.requestHeader = jsonFormat(form.requestHeader)" type="text">JSON格式化</el-button>
</el-form-item>
<el-form-item :label="$t('请求体')" prop="requestBody">
<v-ace-editor
@ -49,6 +59,7 @@
:theme="this.$TOOL.data.get('APP_DARK') ? 'github_dark' : 'github'"
lang="json"
style="height: 10rem; width: 100%" />
<el-button @click="form.requestBody = jsonFormat(form.requestBody)" type="text">JSON格式化</el-button>
</el-form-item>
<el-form-item :label="$t('请求的网络地址')" prop="requestUrl">
<el-input v-model="form.requestUrl" clearable />
@ -109,6 +120,7 @@
<script>
import scEditor from '@/components/scEditor/index.vue'
import Record from '@/views/sys/job/record/index.vue'
import vkbeautify from 'vkbeautify/index'
export default {
components: {
@ -191,6 +203,17 @@ export default {
},
mounted() {},
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) {
this.visible = true

View File

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

View File

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

View File

@ -57,7 +57,7 @@
remote-sort
row-key="id"
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" />
<na-col-avatar :label="$t('用户名')" prop="createdUserName" />
<na-col-indicator
@ -67,9 +67,10 @@
return { value: x[0], text: x[1][1] }
})
"
align="center"
prop="msgType"
width="100">
</na-col-indicator>
sortable="custom"
width="100" />
<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('创建时间')" align="right" prop="createdTime" sortable="custom" width="170" />

View File

@ -1,5 +1,11 @@
<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
v-loading="loading"
:disabled="mode === 'view'"

View File

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

View File

@ -1,5 +1,5 @@
<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">
<el-tabs tab-position="top">
<el-tab-pane :label="$t('基本信息')">

View File

@ -1,5 +1,22 @@
<template>
<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>
<div class="left-panel">
<na-search
@ -26,19 +43,11 @@
placeholder: '所属部门',
style: 'width:15rem',
},
{
type: 'select',
field: ['dy', 'enabled'],
options: [
{ label: '启用', value: true },
{ label: '禁用', value: false },
],
placeholder: '是否启用',
style: 'width:10rem',
},
]"
:vue="this"
@search="onSearch" />
@reset="Object.entries(this.$refs.selectFilter.selected).forEach(([key, _]) => (this.$refs.selectFilter.selected[key] = ['']))"
@search="onSearch"
ref="search" />
</div>
<div class="right-panel">
<na-button-add :vue="this" />
@ -63,11 +72,11 @@
remote-sort
row-key="id"
stripe>
<el-table-column type="selection"></el-table-column>
<el-table-column :label="$t('用户编号')" prop="id" sortable="custom" width="150"></el-table-column>
<el-table-column type="selection" />
<el-table-column :label="$t('用户编号')" prop="id" sortable="custom" width="150" />
<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('邮箱')" prop="email" sortable="custom"></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" />
<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" />
<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>
</template>
</el-table-column>
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom"></el-table-column>
<na-col-operation :vue="this"></na-col-operation>
<el-table-column :label="$t('创建时间')" align="right" prop="createdTime" sortable="custom" />
<na-col-operation :vue="this" />
</sc-table>
</el-main>
</el-container>
@ -98,13 +107,23 @@ import table from '@/config/table'
export default {
components: {
saveDialog,
roleSaveDialog,
deptSaveDialog,
roleSaveDialog,
saveDialog,
},
inject: ['reload'],
computed: {
table() {
return table
},
},
created() {},
data() {
return {
dialog: {
deptSave: false,
roleSave: false,
save: false,
},
loading: false,
query: {
dynamicFilter: {
@ -112,32 +131,25 @@ export default {
},
filter: {},
},
dialog: {
roleSave: false,
deptSave: false,
save: false,
},
selection: [],
}
},
watch: {},
computed: {
table() {
return table
},
},
mounted() {},
created() {},
inject: ['reload'],
methods: {
//
async changeSwitch(event, row) {
try {
await this.$API.sys_user.setEnabled.post(row)
this.$refs.table.refresh()
this.$message.success(`操作成功`)
} 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) {
this.loading = true
@ -149,8 +161,6 @@ export default {
await this.$nextTick()
this.$refs[`${dialog}Dialog`].open('view', res.data[0])
},
//
onSearch(form) {
if (Array.isArray(form.dy.createdTime)) {
this.query.dynamicFilter.filters.push({
@ -171,6 +181,8 @@ export default {
this.$refs.table.upData()
},
},
mounted() {},
watch: {},
}
</script>

View File

@ -1,5 +1,11 @@
<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
v-loading="loading"
:disabled="mode === 'view'"