feat: 请求日志增加TraceId (#154)

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-07-08 20:50:53 +08:00 committed by GitHub
parent be5b9a160d
commit aaea28389a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
38 changed files with 150 additions and 138 deletions

View File

@ -123,6 +123,7 @@
请求日志导出 请求日志导出
调试 调试
跟踪 跟踪
跟踪编号
身份证 身份证
运行 运行
通知 通知

View File

@ -15,7 +15,7 @@
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.10.48"> <PackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.11.19-preview">
<PrivateAssets>all</PrivateAssets> <PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference> </PackageReference>

View File

@ -10,5 +10,5 @@ public sealed class IndicatorAttribute(string indicate) : Attribute
/// <summary> /// <summary>
/// 状态指示 /// 状态指示
/// </summary> /// </summary>
public string Indicate { get; init; } = indicate; public string Indicate { get; } = indicate;
} }

View File

@ -61,7 +61,7 @@ public record Sys_Job : VersionEntity, IFieldEnabled, IFieldSummary
[Column] [Column]
[Ignore] [Ignore]
[JsonIgnore] [JsonIgnore]
public virtual HttpStatusCode? LastStatusCode { get; init; } public HttpStatusCode? LastStatusCode { get; init; }
/// <summary> /// <summary>
/// 下次执行时间 /// 下次执行时间

View File

@ -172,6 +172,14 @@ public record Sys_RequestLog : SimpleEntity, IFieldCreatedTime, IFieldCreatedCli
[JsonIgnore] [JsonIgnore]
public virtual int? ServerIp { get; init; } public virtual int? ServerIp { get; init; }
/// <summary>
/// 请求跟踪标识
/// </summary>
[Column(DbType = Chars.FLG_DB_FIELD_TYPE_VARCHAR_31)]
[Ignore]
[JsonIgnore]
public virtual string TraceId { get; init; }
/// <summary> /// <summary>
/// 用户 /// 用户
/// </summary> /// </summary>

View File

@ -6,17 +6,17 @@ namespace NetAdmin.Domain.Dto.Dependency;
public sealed record PagedQueryRsp<T>(int Page, int PageSize, long Total, IEnumerable<T> Rows) : IPagedInfo public sealed record PagedQueryRsp<T>(int Page, int PageSize, long Total, IEnumerable<T> Rows) : IPagedInfo
where T : DataAbstraction where T : DataAbstraction
{ {
/// <summary>
/// 数据行
/// </summary>
public IEnumerable<T> Rows { get; } = Rows;
/// <inheritdoc cref="IPagedInfo.Page" /> /// <inheritdoc cref="IPagedInfo.Page" />
public int Page { get; init; } = Page; public int Page { get; init; } = Page;
/// <inheritdoc cref="IPagedInfo.PageSize" /> /// <inheritdoc cref="IPagedInfo.PageSize" />
public int PageSize { get; init; } = PageSize; public int PageSize { get; init; } = PageSize;
/// <summary>
/// 数据行
/// </summary>
public IEnumerable<T> Rows { get; init; } = Rows;
/// <summary> /// <summary>
/// 数据总条 /// 数据总条
/// </summary> /// </summary>

View File

@ -34,6 +34,7 @@ public sealed record GetAllEntriesRsp : DataAbstraction
/// <summary> /// <summary>
/// 绝对过期时间 /// 绝对过期时间
/// </summary> /// </summary>
[JsonInclude]
public long AbsExp { get; init; } public long AbsExp { get; init; }
/// <summary> /// <summary>
@ -49,5 +50,6 @@ public sealed record GetAllEntriesRsp : DataAbstraction
/// <summary> /// <summary>
/// 滑动过期时间 /// 滑动过期时间
/// </summary> /// </summary>
[JsonInclude]
public long SldExp { get; init; } public long SldExp { get; init; }
} }

View File

@ -29,6 +29,7 @@ public sealed record IconExportJsInfo : DataAbstraction
/// <summary> /// <summary>
/// Icons /// Icons
/// </summary> /// </summary>
[JsonInclude]
public ICollection<string> Icons { get; init; } public ICollection<string> Icons { get; init; }
/// <summary> /// <summary>

View File

@ -10,6 +10,7 @@ namespace NetAdmin.Domain.Dto.Sys.Job;
public record QueryJobRsp : Sys_Job public record QueryJobRsp : Sys_Job
{ {
/// <inheritdoc cref="Sys_Job.LastStatusCode" /> /// <inheritdoc cref="Sys_Job.LastStatusCode" />
[JsonInclude]
public new virtual string LastStatusCode => public new virtual string LastStatusCode =>
#pragma warning disable IDE0072 #pragma warning disable IDE0072
base.LastStatusCode switch { base.LastStatusCode switch {

View File

@ -9,6 +9,7 @@ namespace NetAdmin.Domain.Dto.Sys.JobRecord;
public record QueryJobRecordRsp : Sys_JobRecord public record QueryJobRecordRsp : Sys_JobRecord
{ {
/// <inheritdoc cref="Sys_JobRecord.HttpStatusCode" /> /// <inheritdoc cref="Sys_JobRecord.HttpStatusCode" />
[JsonInclude]
public new virtual string HttpStatusCode => public new virtual string HttpStatusCode =>
base.HttpStatusCode == Numbers.HTTP_STATUS_BIZ_FAIL base.HttpStatusCode == Numbers.HTTP_STATUS_BIZ_FAIL
? nameof(ErrorCodes.Unhandled).ToLowerCamelCase() ? nameof(ErrorCodes.Unhandled).ToLowerCamelCase()

View File

@ -31,36 +31,43 @@ public sealed record MetaInfo : DataAbstraction
/// <summary> /// <summary>
/// 背景颜色 /// 背景颜色
/// </summary> /// </summary>
[JsonInclude]
public string Color { get; init; } public string Color { get; init; }
/// <summary> /// <summary>
/// 是否整页路由 /// 是否整页路由
/// </summary> /// </summary>
[JsonInclude]
public bool FullPage { get; init; } public bool FullPage { get; init; }
/// <summary> /// <summary>
/// 是否隐藏 /// 是否隐藏
/// </summary> /// </summary>
[JsonInclude]
public bool Hidden { get; init; } public bool Hidden { get; init; }
/// <summary> /// <summary>
/// 是否隐藏面包屑 /// 是否隐藏面包屑
/// </summary> /// </summary>
[JsonInclude]
public bool HiddenBreadCrumb { get; init; } public bool HiddenBreadCrumb { get; init; }
/// <summary> /// <summary>
/// 图标 /// 图标
/// </summary> /// </summary>
[JsonInclude]
public string Icon { get; init; } public string Icon { get; init; }
/// <summary> /// <summary>
/// 标签 /// 标签
/// </summary> /// </summary>
[JsonInclude]
public string Tag { get; init; } public string Tag { get; init; }
/// <summary> /// <summary>
/// 标题 /// 标题
/// </summary> /// </summary>
[JsonInclude]
public string Title { get; init; } public string Title { get; init; }
/// <summary> /// <summary>
@ -68,5 +75,6 @@ public sealed record MetaInfo : DataAbstraction
/// </summary> /// </summary>
[EnumDataType(typeof(MenuTypes), ErrorMessageResourceType = typeof(Ln) [EnumDataType(typeof(MenuTypes), ErrorMessageResourceType = typeof(Ln)
, ErrorMessageResourceName = nameof(Ln.))] , ErrorMessageResourceName = nameof(Ln.))]
[JsonInclude]
public MenuTypes Type { get; init; } public MenuTypes Type { get; init; }
} }

View File

@ -59,6 +59,12 @@ public record ExportRequestLogRsp : QueryRequestLogRsp
[Name(nameof(Ln.请求方式))] [Name(nameof(Ln.请求方式))]
public override string Method { get; init; } public override string Method { get; init; }
/// <inheritdoc />
[CsvIndex(9)]
[Ignore(false)]
[Name(nameof(Ln.跟踪编号))]
public override string TraceId { get; init; }
/// <inheritdoc /> /// <inheritdoc />
[Ignore] [Ignore]
public override QueryUserRsp User { get; init; } public override QueryUserRsp User { get; init; }

View File

@ -10,16 +10,19 @@ public record QueryRequestLogRsp : Sys_RequestLog, IRegister
/// <summary> /// <summary>
/// 创建者客户端IP /// 创建者客户端IP
/// </summary> /// </summary>
[JsonInclude]
public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4(); public new virtual string CreatedClientIp => base.CreatedClientIp?.ToIpV4();
/// <summary> /// <summary>
/// 登录名 /// 登录名
/// </summary> /// </summary>
[JsonInclude]
public virtual string LoginName => RequestBody?.ToObject<LoginByPwdReq>()?.Account; public virtual string LoginName => RequestBody?.ToObject<LoginByPwdReq>()?.Account;
/// <summary> /// <summary>
/// 操作系统 /// 操作系统
/// </summary> /// </summary>
[JsonInclude]
public virtual string Os => UserAgentParser.Create(CreatedUserAgent)?.Platform; public virtual string Os => UserAgentParser.Create(CreatedUserAgent)?.Platform;
/// <inheritdoc cref="Sys_RequestLog.ApiId" /> /// <inheritdoc cref="Sys_RequestLog.ApiId" />
@ -91,6 +94,10 @@ public record QueryRequestLogRsp : Sys_RequestLog, IRegister
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override int? ServerIp { get; init; } public override int? ServerIp { get; init; }
/// <inheritdoc cref="Sys_RequestLog.TraceId" />
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
public override string TraceId { get; init; }
/// <inheritdoc cref="Sys_RequestLog.User" /> /// <inheritdoc cref="Sys_RequestLog.User" />
public new virtual QueryUserRsp User { get; init; } public new virtual QueryUserRsp User { get; init; }

View File

@ -68,7 +68,7 @@ public static class IMvcBuilderExtensions
/// <summary> /// <summary>
/// 设置Json选项 /// 设置Json选项
/// </summary> /// </summary>
public static void SetJsonOptions(bool enumToString, JsonOptions options) private static void SetJsonOptions(bool enumToString, JsonOptions options)
{ {
////////////////////////////// json -> object ////////////////////////////// json -> object

View File

@ -26,7 +26,7 @@ public abstract class Startup : AppStartup
/// <summary> /// <summary>
/// 打印Banner /// 打印Banner
/// </summary> /// </summary>
protected static void ShowBanner() private static void ShowBanner()
{ {
AnsiConsole.WriteLine(); AnsiConsole.WriteLine();
var gridInfo = new Grid().AddColumn(new GridColumn().NoWrap().Width(50).PadRight(10)) var gridInfo = new Grid().AddColumn(new GridColumn().NoWrap().Width(50).PadRight(10))

View File

@ -19,7 +19,6 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
{ {
// 从请求头中读取用户信息 // 从请求头中读取用户信息
var associatedUser = GetAssociatedUser(context); var associatedUser = GetAssociatedUser(context);
var auditData = new CreateRequestLogReq { var auditData = new CreateRequestLogReq {
Duration = duration Duration = duration
, Method = context.Request.Method , Method = context.Request.Method
@ -48,6 +47,7 @@ public sealed class RequestLogger(ILogger<RequestLogger> logger, IEventPublisher
?.MapToIPv4() ?.MapToIPv4()
.ToString() .ToString()
.IpV4ToInt32() .IpV4ToInt32()
, TraceId = context.TraceIdentifier
}; };
// 打印日志 // 打印日志

View File

@ -19,5 +19,5 @@ public abstract class NetAdminException(string message, Exception innerException
/// <summary> /// <summary>
/// 错误码 /// 错误码
/// </summary> /// </summary>
public ErrorCodes Code { get; init; } public ErrorCodes Code { get; }
} }

View File

@ -5,22 +5,22 @@ namespace NetAdmin.Infrastructure.Extensions;
/// </summary> /// </summary>
public static class HttpRequestMessageExtensions public static class HttpRequestMessageExtensions
{ {
/// <summary>
/// 将Http请求的Uri、Header、Body打包成Json字符串
/// </summary>
public static async Task<string> BuildJsonAsync(this HttpRequestMessage me)
{
var body = me?.Content == null ? null : await me.Content!.ReadAsStringAsync().ConfigureAwait(false);
return new { Uri = me?.RequestUri, Header = me?.ToString(), Body = body }.ToJson();
}
/// <summary> /// <summary>
/// 记录日志 /// 记录日志
/// </summary> /// </summary>
public static async Task<HttpRequestMessage> LogAsync<T>(this HttpRequestMessage me, ILogger<T> logger) public static async Task<HttpRequestMessage> LogAsync<T>(this HttpRequestMessage me, ILogger<T> logger)
{ {
logger.Info( logger.Info(
$"HTTP Request: {(await me.BuildJsonAsync().ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}"); $"HTTP Request {(await me.BuildJsonAsync().ConfigureAwait(false))?.Sub(0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}");
return me; return me;
} }
/// <summary>
/// 将Http请求的Uri、Header、Body打包成Json字符串
/// </summary>
private static async Task<string> BuildJsonAsync(this HttpRequestMessage me)
{
var body = me?.Content == null ? null : await me.Content!.ReadAsStringAsync().ConfigureAwait(false);
return new { Uri = me?.RequestUri, Header = me?.ToString(), Body = body }.ToJson();
}
} }

View File

@ -11,9 +11,8 @@ public static class HttpResponseMessageExtensions
public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger public static async Task LogAsync<T>(this HttpResponseMessage me, ILogger<T> logger
, Func<string, string> bodyPreHandle = null) , Func<string, string> bodyPreHandle = null)
{ {
logger.Info( logger.Info($"HTTP Response {(await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false))?.Sub(
(await me.BuildJsonAsync(bodyPreHandle).ConfigureAwait(false))?.Sub( 0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT)}");
0, Numbers.MAX_LIMIT_PRINT_LEN_CONTENT));
} }
/// <summary> /// <summary>

View File

@ -13,4 +13,9 @@ public interface IDicContentService : IService, IDicContentModule
/// 编辑字典内容 /// 编辑字典内容
/// </summary> /// </summary>
Task<QueryDicContentRsp> EditAsync(EditDicContentReq req); Task<QueryDicContentRsp> EditAsync(EditDicContentReq req);
/// <summary>
/// 通过分类键查询字典内容
/// </summary>
Task<List<QueryDicContentRsp>> QueryByCatalogCodeAsync(string catalogCode);
} }

View File

@ -141,6 +141,17 @@ public sealed class DicContentService(BasicRepository<Sys_DicContent, long> rpo)
return ret.Adapt<IEnumerable<QueryDicContentRsp>>(); return ret.Adapt<IEnumerable<QueryDicContentRsp>>();
} }
/// <inheritdoc />
public async Task<List<QueryDicContentRsp>> QueryByCatalogCodeAsync(string catalogCode)
{
var ret = await Rpo.Orm.Select<Sys_DicContent>()
.Include(a => a.Catalog)
.Where(a => a.Catalog.Code == catalogCode)
.ToListAsync()
.ConfigureAwait(false);
return ret.Adapt<List<QueryDicContentRsp>>();
}
private ISelect<Sys_DicContent> QueryInternal(QueryReq<QueryDicContentReq> req) private ISelect<Sys_DicContent> QueryInternal(QueryReq<QueryDicContentReq> req)
{ {
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter); var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);

View File

@ -279,19 +279,18 @@ public sealed class UserService(
public async Task<int> ResetPasswordAsync(ResetPasswordReq req) public async Task<int> ResetPasswordAsync(ResetPasswordReq req)
{ {
req.ThrowIfInvalid(); req.ThrowIfInvalid();
if (await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) { if (!await verifyCodeService.VerifyAsync(req.VerifySmsCodeReq).ConfigureAwait(false)) {
throw new NetAdminInvalidOperationException(Ln.);
}
var dto = (await Rpo.Where(a => a.Mobile == req.VerifySmsCodeReq.DestDevice) var dto = (await Rpo.Where(a => a.Mobile == req.VerifySmsCodeReq.DestDevice)
.ToOneAsync(a => new { a.Version, a.Id }) .ToOneAsync(a => new { a.Version, a.Id })
.ConfigureAwait(false)).Adapt<Sys_User>() with { .ConfigureAwait(false)).Adapt<Sys_User>() with {
Password = req.PasswordText.Pwd() Password = req.PasswordText.Pwd().Guid()
.Guid()
}; };
return await UpdateAsync(dto, [nameof(Sys_User.Password)]).ConfigureAwait(false); return await UpdateAsync(dto, [nameof(Sys_User.Password)]).ConfigureAwait(false);
} }
throw new NetAdminInvalidOperationException(Ln.);
}
/// <inheritdoc /> /// <inheritdoc />
public async Task<UserInfoRsp> SetAvatarAsync(SetAvatarReq req) public async Task<UserInfoRsp> SetAvatarAsync(SetAvatarReq req)
{ {

View File

@ -1,4 +1,5 @@
using NetAdmin.Cache; using NetAdmin.Cache;
using NetAdmin.Domain.Dto.Sys.Dic.Content;
using NetAdmin.SysComponent.Application.Modules.Sys; using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency; using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
@ -7,4 +8,10 @@ namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
/// <summary> /// <summary>
/// 字典内容缓存 /// 字典内容缓存
/// </summary> /// </summary>
public interface IDicContentCache : ICache<IDistributedCache, IDicContentService>, IDicContentModule; public interface IDicContentCache : ICache<IDistributedCache, IDicContentService>, IDicContentModule
{
/// <summary>
/// 通过分类键查询字典内容
/// </summary>
Task<List<QueryDicContentRsp>> QueryByCatalogCodeAsync(string catalogCode);
}

View File

@ -1,7 +1,4 @@
using NetAdmin.Cache; using NetAdmin.Cache;
using NetAdmin.Domain.Dto.Dependency;
using NetAdmin.Domain.Dto.Sys.User;
using NetAdmin.Domain.Dto.Sys.UserProfile;
using NetAdmin.SysComponent.Application.Modules.Sys; using NetAdmin.SysComponent.Application.Modules.Sys;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency; using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
@ -12,41 +9,6 @@ namespace NetAdmin.SysComponent.Cache.Sys.Dependency;
/// </summary> /// </summary>
public interface IUserCache : ICache<IDistributedCache, IUserService>, IUserModule public interface IUserCache : ICache<IDistributedCache, IUserService>, IUserModule
{ {
/// <summary>
/// 删除缓存 CheckMobileAvailableAsync
/// </summary>
Task RemoveCheckMobileAvailableAsync(CheckMobileAvailableReq req);
/// <summary>
/// 删除缓存 CheckUserNameAvailableAsync
/// </summary>
Task RemoveCheckUserNameAvailableAsync(CheckUserNameAvailableReq req);
/// <summary>
/// 删除缓存 LoginByPwdAsync
/// </summary>
Task RemoveLoginByPwdAsync(LoginByPwdReq req);
/// <summary>
/// 删除缓存 LoginBySmsAsync
/// </summary>
Task RemoveLoginBySmsAsync(LoginBySmsReq req);
/// <summary>
/// 删除缓存 QueryProfileAsync
/// </summary>
Task RemoveQueryProfileAsync(QueryReq<QueryUserProfileReq> req);
/// <summary>
/// 删除缓存 RegisterAsync
/// </summary>
Task RemoveRegisterAsync(RegisterUserReq req);
/// <summary>
/// 删除缓存 ResetPasswordAsync
/// </summary>
Task RemoveResetPasswordAsync(ResetPasswordReq req);
/// <summary> /// <summary>
/// 删除缓存 UserInfoAsync /// 删除缓存 UserInfoAsync
/// </summary> /// </summary>

View File

@ -63,4 +63,16 @@ public sealed class DicContentCache(IDistributedCache cache, IDicContentService
{ {
return Service.QueryAsync(req); return Service.QueryAsync(req);
} }
/// <inheritdoc />
public Task<List<QueryDicContentRsp>> QueryByCatalogCodeAsync(string catalogCode)
{
#if !DEBUG
return GetOrCreateAsync( //
GetCacheKey(catalogCode), () => Service.QueryByCatalogCodeAsync(catalogCode)
, TimeSpan.FromSeconds(Numbers.SECS_CACHE_DIC_CATALOG_CODE));
#else
return Service.QueryByCatalogCodeAsync(catalogCode);
#endif
}
} }

View File

@ -119,48 +119,6 @@ public sealed class UserCache(IDistributedCache cache, IUserService service, IVe
return Service.RegisterAsync(req); return Service.RegisterAsync(req);
} }
/// <inheritdoc />
public Task RemoveCheckMobileAvailableAsync(CheckMobileAvailableReq req)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task RemoveCheckUserNameAvailableAsync(CheckUserNameAvailableReq req)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task RemoveLoginByPwdAsync(LoginByPwdReq req)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task RemoveLoginBySmsAsync(LoginBySmsReq req)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task RemoveQueryProfileAsync(QueryReq<QueryUserProfileReq> req)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task RemoveRegisterAsync(RegisterUserReq req)
{
throw new NotImplementedException();
}
/// <inheritdoc />
public Task RemoveResetPasswordAsync(ResetPasswordReq req)
{
throw new NotImplementedException();
}
/// <inheritdoc /> /// <inheritdoc />
public Task RemoveUserInfoAsync() public Task RemoveUserInfoAsync()
{ {

View File

@ -1,5 +1,6 @@
<template> <template>
<el-cascader <el-cascader
v-bind="$attrs"
v-model="data" v-model="data"
:options="options" :options="options"
:placeholder="placeholder" :placeholder="placeholder"

View File

@ -1,7 +1,7 @@
<template> <template>
<el-drawer v-model="visible" :size="size" :title="title" @closed="$emit('closed')" destroy-on-close> <el-drawer v-model="visible" :size="size" :title="title" @closed="$emit('closed')" destroy-on-close>
<el-main> <el-main>
<el-descriptions :column="1" border size="small"> <el-descriptions :column="1" border class="font-monospace" size="small">
<el-descriptions-item v-for="(item, i) in data" :key="i" :label="i" label-class-name="w15"> <el-descriptions-item v-for="(item, i) in data" :key="i" :label="i" label-class-name="w15">
{{ item }} {{ item }}
</el-descriptions-item> </el-descriptions-item>

View File

@ -497,6 +497,10 @@ textarea {
margin-top: 2rem; margin-top: 2rem;
} }
.mb-8 {
margin-bottom: 2rem;
}
.w100p { .w100p {
width: 100%; width: 100%;
} }

View File

@ -121,13 +121,13 @@
margin-top: 1rem; margin-top: 1rem;
border-bottom: 1px solid var(--el-border-color-light); border-bottom: 1px solid var(--el-border-color-light);
} }
.adminui-main > .el-container > .el-container > .el-header { .adminui-main > .el-container .el-header {
@extend .headerPublic; @extend .headerPublic;
} }
.adminui-main > .el-container > .el-container > .el-header .left-panel { .adminui-main > .el-container .el-header .left-panel {
display: block; display: block;
} }
.adminui-main > .el-container > .el-container > .el-header .right-panel { .adminui-main > .el-container .el-header .right-panel {
display: block; display: block;
margin-top: 1rem; margin-top: 1rem;
} }

View File

@ -247,6 +247,10 @@ tool.groupSeparator = function (num) {
}) })
.replace(/\.$/, '') .replace(/\.$/, '')
} }
// unicode解码
tool.unicodeDecode = function (str) {
return str.replace(/\\u([0-9a-fA-F]{4})/g, (match, grp) => String.fromCharCode(parseInt(grp, 16)))
}
// 属性排序 // 属性排序
tool.sortProperties = function (obj) { tool.sortProperties = function (obj) {
const sortedKeys = Object.keys(obj).sort() const sortedKeys = Object.keys(obj).sort()

View File

@ -1,6 +1,6 @@
<template> <template>
<el-container> <el-container>
<el-aside v-loading="loading" width="30rem"> <el-aside v-loading="loading" width="40rem">
<el-container> <el-container>
<el-header> <el-header>
<el-input v-model="filterText" :placeholder="$t('输入关键字进行过滤')" clearable></el-input> <el-input v-model="filterText" :placeholder="$t('输入关键字进行过滤')" clearable></el-input>
@ -23,6 +23,7 @@
<span>{{ data.name }} {{ data.code }}</span> <span>{{ data.name }} {{ data.code }}</span>
<span class="btn"> <span class="btn">
<el-button-group size="small"> <el-button-group size="small">
<el-button @click.stop="add(data.id, data.code)" icon="el-icon-plus"></el-button>
<el-button @click.stop="edit(data)" icon="el-icon-edit"></el-button> <el-button @click.stop="edit(data)" icon="el-icon-edit"></el-button>
<el-popconfirm :title="$t('确定删除 {item} 吗?', { item: data.name })" @confirm="del(data)" width="20rem"> <el-popconfirm :title="$t('确定删除 {item} 吗?', { item: data.name })" @confirm="del(data)" width="20rem">
<template #reference> <template #reference>
@ -36,7 +37,9 @@
</el-tree> </el-tree>
</el-main> </el-main>
<el-footer> <el-footer>
<el-button @click="add" icon="el-icon-plus" size="small" style="width: 100%" type="primary">{{ $t('字典分类') }}</el-button> <el-button @click="add(form.catalogId)" icon="el-icon-plus" size="small" style="width: 100%" type="primary">{{
$t('字典分类')
}}</el-button>
</el-footer> </el-footer>
</el-container> </el-container>
</el-aside> </el-aside>
@ -105,8 +108,8 @@ export default {
return targetText.indexOf(value) !== -1 return targetText.indexOf(value) !== -1
}, },
// //
async add() { async add(id, code) {
this.dialog.save = { mode: 'add' } this.dialog.save = { mode: 'add', data: { catalogId: id, code: code + '>' } }
}, },
// //
async edit(data) { async edit(data) {

View File

@ -16,7 +16,10 @@
ref="search" /> ref="search" />
</div> </div>
<div class="right-panel"> <div class="right-panel">
<el-button @click="this.dialog.save = { mode: 'add' }" icon="el-icon-plus" type="primary"></el-button> <el-button
@click="this.dialog.save = { mode: 'add', data: { catalogId: this.catalogId } }"
icon="el-icon-plus"
type="primary"></el-button>
<na-button-bulk-del :api="$API.sys_dic.bulkDeleteContent" :vue="this" /> <na-button-bulk-del :api="$API.sys_dic.bulkDeleteContent" :vue="this" />
</div> </div>
</el-header> </el-header>

View File

@ -5,7 +5,7 @@
<el-tab-pane :label="$t('基本信息')"> <el-tab-pane :label="$t('基本信息')">
<el-form :disabled="mode === 'view'" :model="form" :rules="rules" label-width="10rem" ref="dialogForm"> <el-form :disabled="mode === 'view'" :model="form" :rules="rules" label-width="10rem" ref="dialogForm">
<el-form-item :label="$t('所属字典')" prop="catalogId"> <el-form-item :label="$t('所属字典')" prop="catalogId">
<na-dic-catalog v-model="form.catalogId" /> <na-dic-catalog v-model="form.catalogId" class="w100p" />
</el-form-item> </el-form-item>
<el-form-item :label="$t('项名')" prop="key"> <el-form-item :label="$t('项名')" prop="key">
<el-input v-model="form.key" clearable></el-input> <el-input v-model="form.key" clearable></el-input>
@ -63,6 +63,7 @@ export default {
this.visible = true this.visible = true
this.loading = true this.loading = true
this.mode = data.mode this.mode = data.mode
this.form.catalogId = data.data?.catalogId
if (data.row?.id) { if (data.row?.id) {
const res = await this.$API.sys_dic.getContent.post({ id: data.row.id }) const res = await this.$API.sys_dic.getContent.post({ id: data.row.id })
Object.assign(this.form, res.data) Object.assign(this.form, res.data)

View File

@ -8,7 +8,7 @@
<el-input v-model="form.code" :placeholder="$t('字典编码')" clearable></el-input> <el-input v-model="form.code" :placeholder="$t('字典编码')" clearable></el-input>
</el-form-item> </el-form-item>
<el-form-item :label="$t('父路径')" prop="parentId"> <el-form-item :label="$t('父路径')" prop="parentId">
<na-dic-catalog v-model="form.parentId" /> <na-dic-catalog v-model="form.parentId" class="w100p" />
</el-form-item> </el-form-item>
</el-form> </el-form>
<template #footer> <template #footer>
@ -45,6 +45,8 @@ export default {
this.visible = true this.visible = true
this.loading = true this.loading = true
this.mode = data.mode this.mode = data.mode
this.form.parentId = data.data?.catalogId
this.form.code = data.data?.code
if (data.row?.id) { if (data.row?.id) {
const res = await this.$API.sys_dic.getCatalog.post({ id: data.row.id }) const res = await this.$API.sys_dic.getCatalog.post({ id: data.row.id })
Object.assign(this.form, res.data) Object.assign(this.form, res.data)

View File

@ -1,9 +1,15 @@
<template> <template>
<el-tabs v-model="tabId" class="w100p" style="background: var(--el-bg-color-overlay); padding-left: 1rem"> <el-container>
<el-header style="border: none">
<el-tabs v-model="tabId" class="w100p">
<el-tab-pane :label="$t('所有作业')" name="all"></el-tab-pane> <el-tab-pane :label="$t('所有作业')" name="all"></el-tab-pane>
<el-tab-pane :label="$t('异常作业')" name="fail"></el-tab-pane> <el-tab-pane :label="$t('异常作业')" name="fail"></el-tab-pane>
</el-tabs> </el-tabs>
</el-header>
<el-main class="nopadding">
<component :is="tabId" :status-codes="['300,399', '400,499', '500,599', '900,999']" /> <component :is="tabId" :status-codes="['300,399', '400,499', '500,599', '900,999']" />
</el-main>
</el-container>
</template> </template>
<script> <script>

View File

@ -159,10 +159,10 @@ export default {
async rowClick(row) { async rowClick(row) {
this.dialog.info = true this.dialog.info = true
await this.$nextTick() await this.$nextTick()
const res = await this.$API.sys_log.query.post({ const res = await this.$API.sys_log.get.post({
filter: { id: row.id }, id: row.id,
}) })
this.$refs.info.open(this.$TOOL.sortProperties(res.data[0]), this.$t('日志详情:{id}', { id: row.id })) this.$refs.info.open(this.$TOOL.sortProperties(res.data), this.$t('日志详情:{id}', { id: row.id }))
}, },
}, },
mounted() { mounted() {

View File

@ -205,7 +205,7 @@ export default {
}) })
} }
if (typeof form.dy.userId) { if (typeof form.dy.userId === 'number' && form.dy.userId !== 0) {
this.query.dynamicFilter.filters.push({ this.query.dynamicFilter.filters.push({
field: 'userId', field: 'userId',
operator: 'eq', operator: 'eq',
@ -234,10 +234,10 @@ export default {
async rowClick(row) { async rowClick(row) {
this.dialog.info = true this.dialog.info = true
await this.$nextTick() await this.$nextTick()
const res = await this.$API.sys_log.query.post({ const res = await this.$API.sys_log.get.post({
filter: { id: row.id }, id: row.id,
}) })
this.$refs.info.open(this.$TOOL.sortProperties(res.data[0]), this.$t('日志详情:{id}', { id: row.id })) this.$refs.info.open(this.$TOOL.sortProperties(res.data), this.$t('日志详情:{id}', { id: row.id }))
}, },
}, },
mounted() { mounted() {