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

Co-authored-by: tk <fiyne1a@dingtalk.com>
This commit is contained in:
nsnail 2024-06-15 21:41:03 +08:00 committed by GitHub
parent 366a26a5cd
commit ae2d1c4932
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 285 additions and 137 deletions

View File

@ -16,8 +16,8 @@
<None Include="$(SolutionDir)/assets/res/Nations.ln"> <None Include="$(SolutionDir)/assets/res/Nations.ln">
<Link>Languages/Nations.ln</Link> <Link>Languages/Nations.ln</Link>
</None> </None>
<None Include="$(SolutionDir)/assets/res/Enums.ln"> <None Include="$(SolutionDir)/assets/res/Fields.ln">
<Link>Languages/Enums.ln</Link> <Link>Languages/Fields.ln</Link>
</None> </None>
<EmbeddedResource Include="$(SolutionDir)/assets/res/Ln.resx"> <EmbeddedResource Include="$(SolutionDir)/assets/res/Ln.resx">
<Link>Languages/Ln.resx</Link> <Link>Languages/Ln.resx</Link>

View File

@ -0,0 +1,14 @@
namespace NetAdmin.Domain.Attributes;
/// <summary>
/// 标记一个枚举的状态指示
/// </summary>
/// <inheritdoc />
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Enum)]
public sealed class IndicatorAttribute(string indicate) : Attribute
{
/// <summary>
/// 状态指示
/// </summary>
public string Indicate { get; init; } = indicate;
}

View File

@ -1,3 +1,5 @@
using NetAdmin.Domain.Attributes;
namespace NetAdmin.Domain.Enums.Sys; namespace NetAdmin.Domain.Enums.Sys;
/// <summary> /// <summary>
@ -9,6 +11,7 @@ public enum JobStatues
/// <summary> /// <summary>
/// 空闲 /// 空闲
/// </summary> /// </summary>
[Indicator(nameof(Indicates.Success))]
[ResourceDescription<Ln>(nameof(Ln.空闲))] [ResourceDescription<Ln>(nameof(Ln.空闲))]
Idle = 1 Idle = 1
@ -17,6 +20,7 @@ public enum JobStatues
/// <summary> /// <summary>
/// 运行 /// 运行
/// </summary> /// </summary>
[Indicator(nameof(Indicates.Warning))]
[ResourceDescription<Ln>(nameof(Ln.运行))] [ResourceDescription<Ln>(nameof(Ln.运行))]
Running = 2 Running = 2
} }

View File

@ -254,7 +254,7 @@ public static class ServiceCollectionExtensions
#endif #endif
private static (string Date, string LogName, string LogFormat) ParseMessage(LogMessage message, bool showColor) private static (string Date, string LogName, string LogFormat) ParseMessage(LogMessage message, bool showColor)
{ {
var date = message.LogDateTime.ToString(Chars.TPL_DATE_HH_MM_SS_FFFFFF, CultureInfo.InvariantCulture); var date = $"{message.LogDateTime:HH:mm:ss.ffffff}";
var logName = message.LogName.PadRight(64, ' ')[^64..]; var logName = message.LogName.PadRight(64, ' ')[^64..];
var format = showColor var format = showColor
? $"[{nameof(ConsoleColor.Gray)}][[{{0}} {{1}} {{2,-{64}}} #{{3,4}}]][/] {{4}}" ? $"[{nameof(ConsoleColor.Gray)}][[{{0}} {{1}} {{2,-{64}}} #{{3,4}}]][/] {{4}}"

View File

@ -5,8 +5,7 @@ namespace NetAdmin.Infrastructure.Configuration.Options;
/// </summary> /// </summary>
public sealed record CaptchaOptions : OptionAbstraction public sealed record CaptchaOptions : OptionAbstraction
{ {
private static readonly double _seed private static readonly double _seed = BitConverter.ToInt32("yyyyMMdd"[..4].Select(x => (byte)x).ToArray());
= BitConverter.ToInt32(Chars.TPL_DATE_YYYYMMDD[..4].Select(x => (byte)x).ToArray());
#pragma warning disable S3963 #pragma warning disable S3963
static CaptchaOptions() static CaptchaOptions()

View File

@ -56,7 +56,9 @@ public static class Chars
public const string FLG_HTTP_HEADER_KEY_X_FORWARDED_FOR = "X-Forwarded-For"; public const string FLG_HTTP_HEADER_KEY_X_FORWARDED_FOR = "X-Forwarded-For";
public const string FLG_HTTP_HEADER_KEY_X_REAL_IP = "X-Real-IP"; public const string FLG_HTTP_HEADER_KEY_X_REAL_IP = "X-Real-IP";
public const string FLG_HTTP_HEADER_VALUE_APPLICATION_JSON = "application/json"; public const string FLG_HTTP_HEADER_VALUE_APPLICATION_JSON = "application/json";
public const string FLG_HTTP_HEADER_VALUE_APPLICATION_OCTET_STREAM = "application/octet-stream";
public const string FLG_HTTP_HEADER_VALUE_APPLICATION_URLENCODED = "application/x-www-form-urlencoded"; public const string FLG_HTTP_HEADER_VALUE_APPLICATION_URLENCODED = "application/x-www-form-urlencoded";
public const string FLG_HTTP_HEADER_VALUE_ATTACHMENT = "attachment";
public const string FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA = "Bearer"; public const string FLG_HTTP_HEADER_VALUE_AUTH_SCHEMA = "Bearer";
public const string FLG_HTTP_METHOD_CONNECT = "CONNECT"; public const string FLG_HTTP_METHOD_CONNECT = "CONNECT";
public const string FLG_HTTP_METHOD_DELETE = "DELETE"; public const string FLG_HTTP_METHOD_DELETE = "DELETE";
@ -105,10 +107,4 @@ public static class Chars
public const string RGXL_IP_V4 public const string RGXL_IP_V4
= @"^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$"; = @"^(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})(\.(25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})){3}$";
public const string TPL_DATE_HH_MM_SS_FFFFFF = "HH:mm:ss.ffffff";
public const string TPL_DATE_YYYY_MM_DD = "yyyy-MM-dd";
public const string TPL_DATE_YYYY_MM_DD_HH_MM_SS = "yyyy-MM-dd HH:mm:ss";
public const string TPL_DATE_YYYYMMDD = "yyyyMMdd";
public const string TPL_DATE_YYYYMMDDHHMMSSFFFZZZZ = "yyyyMMddHHmmssfffzzz";
} }

View File

@ -0,0 +1,41 @@
namespace NetAdmin.Infrastructure.Enums;
/// <summary>
/// 状态表示
/// </summary>
[Export]
public enum Indicates
{
/// <summary>
/// 信息
/// </summary>
Info = 1
,
/// <summary>
/// 主要
/// </summary>
Primary = 2
,
/// <summary>
/// 警告
/// </summary>
Warning = 3
,
/// <summary>
/// 成功
/// </summary>
Success = 4
,
/// <summary>
/// 危险
/// </summary>
Danger = 5
}

View File

@ -1,4 +1,6 @@
using Microsoft.OpenApi.Extensions;
using NetAdmin.Application.Services; using NetAdmin.Application.Services;
using NetAdmin.Domain.Attributes;
using NetAdmin.SysComponent.Application.Services.Sys.Dependency; using NetAdmin.SysComponent.Application.Services.Sys.Dependency;
namespace NetAdmin.SysComponent.Application.Services.Sys; namespace NetAdmin.SysComponent.Application.Services.Sys;
@ -20,26 +22,29 @@ public sealed class ConstantService : ServiceBase<IConstantService>, IConstantSe
{ {
var ret = App.EffectiveTypes.Where(x => x.IsEnum && x.GetCustomAttribute<ExportAttribute>(false) != null) var ret = App.EffectiveTypes.Where(x => x.IsEnum && x.GetCustomAttribute<ExportAttribute>(false) != null)
.ToDictionary(x => x.Name, x => // .ToDictionary(x => x.Name, x => //
x.GetEnumValues() x.GetEnumValues().Cast<Enum>().ToDictionary(y => y.ToString(), GetDicValue));
.Cast<Enum>()
.ToDictionary( //
y => y.ToString()
, y => new[] {
Convert.ToInt64(y, CultureInfo.InvariantCulture)
.ToString(CultureInfo.InvariantCulture)
, y.ResDesc<Ln>()
}));
ret.Add($"{nameof(HttpStatusCode)}s", Enum.GetNames<HttpStatusCode>() ret.Add( //
.ToDictionary( // $"{nameof(HttpStatusCode)}s" //
x => x, x => new[] { , Enum.GetNames<HttpStatusCode>()
Convert.ToInt64( // .ToDictionary(
Enum.Parse<HttpStatusCode>(x) x => x
, CultureInfo.InvariantCulture) , x => new[] {
Convert.ToInt64(Enum.Parse<HttpStatusCode>(x), CultureInfo.InvariantCulture)
.ToString(CultureInfo.InvariantCulture) .ToString(CultureInfo.InvariantCulture)
, x , x
})); }));
return ret; return ret;
static string[] GetDicValue(Enum y)
{
var ret = new[] {
Convert.ToInt64(y, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture)
, y.ResDesc<Ln>()
};
var indicate = y.GetAttributeOfType<IndicatorAttribute>()?.Indicate.ToLowerInvariant();
return indicate.NullOrEmpty() ? ret : [..ret, indicate];
}
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -1,23 +1,12 @@
<template> <template>
<el-table-column v-bind:="$attrs"> <el-table-column v-bind:="$attrs">
<template #default="{ row }"> <template #default="{ row }">
<template v-for="(item, i) in options" :key="i"> <na-indicator :data="row" :options="options" :prop="$attrs.prop" />
<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">&nbsp;{{ item.text }}</span>
<slot :row="row" :text="item.text"></slot>
</div>
</template>
<slot :row="row" name="info"></slot>
</template> </template>
</el-table-column> </el-table-column>
</template> </template>
<script> <script>
import tool from '@/utils/tool' import naIndicator from '@/components/naIndicator/index.vue'
export default { export default {
emits: [], emits: [],
props: { props: {
@ -28,12 +17,8 @@ export default {
}, },
mounted() {}, mounted() {},
created() {}, created() {},
components: {}, components: { naIndicator },
computed: { computed: {},
tool() {
return tool
},
},
methods: {}, methods: {},
} }
</script> </script>

View File

@ -0,0 +1,37 @@
<template>
<template v-for="(item, i) in options" :key="i">
<div v-if="tool.getNestedProperty(data, this.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">&nbsp;{{ item.text }}</span>
<slot :data="data" :text="item.text"></slot>
</div>
</template>
<slot :data="data" name="info"></slot>
</template>
<script>
import tool from '@/utils/tool'
export default {
emits: [],
props: {
options: { type: Array },
data: { type: Object },
prop: { type: String },
},
data() {
return {}
},
mounted() {},
created() {},
components: {},
computed: {
tool() {
return tool
},
},
methods: {},
}
</script>

View File

@ -116,22 +116,55 @@
:command="menu" :command="menu"
:key="index" :key="index"
:title="`${menu}`"> :title="`${menu}`">
<sc-contextmenu-item :command="`${menu}^|^Equal^|^${current.row[menu]}`" title="="></sc-contextmenu-item> <sc-contextmenu-item :command="`${menu}^|^Equal^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" title="="></sc-contextmenu-item>
<sc-contextmenu-item :command="`${menu}^|^NotEqual^|^${current.row[menu]}`" title="≠"></sc-contextmenu-item> <sc-contextmenu-item :command="`${menu}^|^NotEqual^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" title="≠"></sc-contextmenu-item>
<sc-contextmenu-item :command="`${menu}^|^GreaterThan^|^${current.row[menu]}`" divided title=""></sc-contextmenu-item> <sc-contextmenu-item
<sc-contextmenu-item :command="`${menu}^|^GreaterThanOrEqual^|^${current.row[menu]}`" title="≥"></sc-contextmenu-item> :command="`${menu}^|^GreaterThan^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
<sc-contextmenu-item :command="`${menu}^|^LessThan^|^${current.row[menu]}`" title=""></sc-contextmenu-item> divided
<sc-contextmenu-item :command="`${menu}^|^LessThanOrEqual^|^${current.row[menu]}`" title="≤"></sc-contextmenu-item> title=""></sc-contextmenu-item>
<sc-contextmenu-item :command="`${menu}^|^Contains^|^${current.row[menu]}`" divided title="包含"></sc-contextmenu-item> <sc-contextmenu-item
<sc-contextmenu-item :command="`${menu}^|^NotContains^|^${current.row[menu]}`" title="不含"></sc-contextmenu-item> :command="`${menu}^|^GreaterThanOrEqual^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
<sc-contextmenu-item :command="`${menu}^|^StartsWith^|^${current.row[menu]}`" divided title="以 x 开始"></sc-contextmenu-item> title="≥"></sc-contextmenu-item>
<sc-contextmenu-item :command="`${menu}^|^NotStartsWith^|^${current.row[menu]}`" title="非 x 开始"></sc-contextmenu-item> <sc-contextmenu-item
<sc-contextmenu-item :command="`${menu}^|^EndsWith^|^${current.row[menu]}`" title="以 x 结束"></sc-contextmenu-item> :command="`${menu}^|^LessThan^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
<sc-contextmenu-item :command="`${menu}^|^NotEndsWith^|^${current.row[menu]}`" title="非 x 结束"></sc-contextmenu-item> title=""></sc-contextmenu-item>
<sc-contextmenu-item :command="`${menu}^|^Range^|^${current.row[menu]}`" divided title="数值范围"></sc-contextmenu-item> <sc-contextmenu-item
<sc-contextmenu-item :command="`${menu}^|^DateRange^|^${current.row[menu]}`" title="日期范围"></sc-contextmenu-item> :command="`${menu}^|^LessThanOrEqual^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
<sc-contextmenu-item :command="`${menu}^|^Any^|^${current.row[menu]}`" divided title="为其一"></sc-contextmenu-item> title="≤"></sc-contextmenu-item>
<sc-contextmenu-item :command="`${menu}^|^NotAny^|^${current.row[menu]}`" title="非其一"></sc-contextmenu-item> <sc-contextmenu-item
:command="`${menu}^|^Contains^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
divided
title="包含"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^NotContains^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
title="不含"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^StartsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
divided
title="以 x 开始"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^NotStartsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
title="非 x 开始"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^EndsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
title="以 x 结束"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^NotEndsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
title="非 x 结束"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^Range^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
divided
title="数值范围"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^DateRange^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
title="日期范围"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^Any^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
divided
title="为其一"></sc-contextmenu-item>
<sc-contextmenu-item
:command="`${menu}^|^NotAny^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
title="非其一"></sc-contextmenu-item>
</sc-contextmenu-item> </sc-contextmenu-item>
<sc-contextmenu-item v-if="contextOpers.includes('view')" command="view" divided icon="el-icon-view" title="查看"></sc-contextmenu-item> <sc-contextmenu-item v-if="contextOpers.includes('view')" command="view" divided icon="el-icon-view" title="查看"></sc-contextmenu-item>
<sc-contextmenu-item v-if="contextOpers.includes('edit')" command="edit" icon="el-icon-edit" title="编辑"></sc-contextmenu-item> <sc-contextmenu-item v-if="contextOpers.includes('edit')" command="edit" icon="el-icon-edit" title="编辑"></sc-contextmenu-item>
@ -153,6 +186,7 @@ import columnSetting from './columnSetting'
import scContextmenuItem from '@/components/scContextmenu/item.vue' import scContextmenuItem from '@/components/scContextmenu/item.vue'
import scContextmenu from '@/components/scContextmenu/index.vue' import scContextmenu from '@/components/scContextmenu/index.vue'
import { h } from 'vue' import { h } from 'vue'
import tool from '@/utils/tool'
export default { export default {
name: 'scTable', name: 'scTable',
@ -222,6 +256,9 @@ export default {
}, },
}, },
computed: { computed: {
tool() {
return tool
},
_height() { _height() {
return Number(this.height) ? Number(this.height) + 'px' : this.height return Number(this.height) ? Number(this.height) + 'px' : this.height
}, },
@ -321,9 +358,9 @@ export default {
try { try {
value = await this.$prompt(`仅显示 ${kv[0]} ${kv[1]}`, '高级筛选', { value = await this.$prompt(`仅显示 ${kv[0]} ${kv[1]}`, '高级筛选', {
inputPlaceholder: '一行一个', inputPlaceholder: '一行一个',
inputPattern: /.+/, inputPattern: /.*/,
inputType: 'textarea', inputType: 'textarea',
inputValue: command.split('^|^')[2], inputValue: kv[2],
}) })
} catch { } catch {
return return

View File

@ -85,6 +85,35 @@ axios.interceptors.response.use(
if (response.headers['x-access-token']) { if (response.headers['x-access-token']) {
setCookie('X-ACCESS-TOKEN', response.headers['x-access-token']) setCookie('X-ACCESS-TOKEN', response.headers['x-access-token'])
} }
//此处判断是否下载请求
if (response.headers['content-type'] === 'application/octet-stream') {
//根据响应头获取文件名称
console.error(response.headers['content-disposition'])
let fileName = response.headers['content-disposition'].match(/filename\*=UTF-8''([^;]+)/)[1]
if (fileName) {
fileName = decodeURI(fileName)
} else {
//此处表示后台没有设置响应头 content-disposition,请根据业务场景自行处理
fileName = Date.now() + 'download.txt'
}
const blob = new Blob([response.data], { type: 'application/octet-stream' })
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(blob, fileName)
} else {
const blobURL = window.URL.createObjectURL(blob)
const tempLink = document.createElement('a')
tempLink.style.display = 'none'
tempLink.href = blobURL
tempLink.setAttribute('download', fileName)
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank')
}
document.body.appendChild(tempLink)
tempLink.click()
document.body.removeChild(tempLink)
window.URL.revokeObjectURL(blobURL)
}
}
return response return response
}, },
async (error) => { async (error) => {

View File

@ -93,10 +93,11 @@
<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" />
<na-col-indicator <na-col-indicator
:label="$t('作业状态')" :label="$t('作业状态')"
:options="[ :options="
{ text: '空闲', type: 'success', value: 'idle' }, Object.entries(this.$GLOBAL.enums.jobStatues).map((x) => {
{ text: '运行', type: 'warning', value: 'running' }, return { value: x[0], text: x[1][1], type: x[1][2] }
]" })
"
align="center" align="center"
prop="status" prop="status"
sortable="custom" sortable="custom"
@ -105,7 +106,7 @@
:label="$t('请求方式')" :label="$t('请求方式')"
:options=" :options="
Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => { Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => {
return { value: x[0], text: x[1][1] } return { value: x[0], text: x[1][1], type: x[1][2] }
}) })
" "
align="center" align="center"

View File

@ -68,7 +68,7 @@
:label="$t('请求方式')" :label="$t('请求方式')"
:options=" :options="
Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => { Object.entries(this.$GLOBAL.enums.httpMethods).map((x) => {
return { value: x[0], text: x[1][1] } return { value: x[0], text: x[1][1], type: x[1][2] }
}) })
" "
align="center" align="center"

View File

@ -64,7 +64,7 @@
:label="$t('消息类型')" :label="$t('消息类型')"
:options=" :options="
Object.entries(this.$GLOBAL.enums.siteMsgTypes).map((x) => { Object.entries(this.$GLOBAL.enums.siteMsgTypes).map((x) => {
return { value: x[0], text: x[1][1] } return { value: x[0], text: x[1][1], type: x[1][2] }
}) })
" "
align="center" align="center"

View File

@ -98,7 +98,7 @@
:label="$t('数据范围')" :label="$t('数据范围')"
:options=" :options="
Object.entries(this.$GLOBAL.enums.dataScopes).map((x) => { Object.entries(this.$GLOBAL.enums.dataScopes).map((x) => {
return { value: x[0], text: x[1][1] } return { value: x[0], text: x[1][1], type: x[1][2] }
}) })
" "
prop="dataScope" prop="dataScope"