mirror of
https://github.com/nsnail/NetAdmin.git
synced 2025-08-01 01:36:00 +08:00
1
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.5 AS base
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0.7 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 8080
|
||||
RUN apt update
|
||||
|
@ -23,7 +23,7 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.12.0.118525">
|
||||
<PackageReference Include="SonarAnalyzer.CSharp" Version="10.13.0.120203">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
|
@ -52,21 +52,25 @@ public record QueryReq<T> : DataAbstraction
|
||||
}
|
||||
|
||||
var expParameter = Expression.Parameter(typeof(TEntity), "a");
|
||||
var bindings = new List<MemberBinding>();
|
||||
var bindings = new List<(PropertyInfo, MemberInitExpression)>();
|
||||
|
||||
// ReSharper disable once LoopCanBeConvertedToQuery
|
||||
foreach (var field in RequiredFields) {
|
||||
var prop = typeof(TEntity).GetProperty(field);
|
||||
var prop = typeof(TEntity).GetRecursiveProperty(field);
|
||||
if (prop == null || prop.GetCustomAttribute<DangerFieldAttribute>() != null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var propExp = Expression.Property(expParameter, prop);
|
||||
var binding = Expression.Bind(prop, propExp);
|
||||
bindings.Add(binding);
|
||||
var parentPath = field[..field.LastIndexOf('.').Is(-1, field.Length)];
|
||||
var parentProperty = typeof(TEntity).GetRecursiveProperty(parentPath);
|
||||
var propExp = Expression.Property(Expression.Parameter(prop.DeclaringType!, parentPath), prop);
|
||||
|
||||
bindings.Add((parentProperty, Expression.MemberInit(Expression.New(prop.DeclaringType), Expression.Bind(prop, propExp))));
|
||||
}
|
||||
|
||||
var expBody = Expression.MemberInit(Expression.New(typeof(TEntity)), bindings);
|
||||
var expBody = Expression.MemberInit( //
|
||||
Expression.New(typeof(TEntity))
|
||||
, bindings.SelectMany(x => x.Item1.PropertyType == x.Item2.Type ? [Expression.Bind(x.Item1, x.Item2)] : x.Item2.Bindings.ToArray()));
|
||||
return Expression.Lambda<Func<TEntity, TEntity>>(expBody, expParameter);
|
||||
}
|
||||
}
|
@ -8,4 +8,9 @@ public sealed record QueryUserInviteReq : Sys_UserInvite
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Id { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否平面查询
|
||||
/// </summary>
|
||||
public bool IsPlainQuery { get; init; }
|
||||
}
|
@ -0,0 +1,32 @@
|
||||
namespace NetAdmin.Infrastructure.Enums;
|
||||
|
||||
/// <summary>
|
||||
/// 类型扩展方法
|
||||
/// </summary>
|
||||
public static class TypeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// 递归获取类型的属性
|
||||
/// </summary>
|
||||
/// <param name="type">要查找的类型</param>
|
||||
/// <param name="propertyPath">属性路径,如"a.b.c"</param>
|
||||
/// <returns>找到的属性信息,如果路径中任何属性不存在则返回null</returns>
|
||||
public static PropertyInfo GetRecursiveProperty(this Type type, string propertyPath)
|
||||
{
|
||||
var properties = propertyPath.Split('.');
|
||||
PropertyInfo propertyInfo = null;
|
||||
var currentType = type;
|
||||
|
||||
foreach (var propertyName in properties) {
|
||||
propertyInfo = currentType.GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
|
||||
|
||||
if (propertyInfo == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
currentType = propertyInfo.PropertyType;
|
||||
}
|
||||
|
||||
return propertyInfo;
|
||||
}
|
||||
}
|
@ -6,7 +6,7 @@
|
||||
<PackageReference Include="NetAdmin.FreeSql.DbContext" Version="1.1.7" Label="refs"/>
|
||||
<PackageReference Include="NetAdmin.FreeSql.Provider.Sqlite" Version="1.1.7" Label="refs"/>
|
||||
<PackageReference Include="Gurion" Version="1.2.16" Label="refs"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.6"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.0.7"/>
|
||||
<PackageReference Include="Minio" Version="6.0.5"/>
|
||||
<PackageReference Include="NSExt" Version="2.3.7"/>
|
||||
<PackageReference Include="SixLabors.ImageSharp.Drawing" Version="2.1.6"/>
|
||||
|
@ -40,8 +40,11 @@ public sealed class CodeTemplateService(BasicRepository<Sys_CodeTemplate, long>
|
||||
.ConfigureAwait(false);
|
||||
return ret.Select(x => new KeyValuePair<IImmutableDictionary<string, string>, int>(
|
||||
req.RequiredFields.ToImmutableDictionary(
|
||||
y => y, y => typeof(Sys_CodeTemplate).GetProperty(y)!.GetValue(x.Key)?.ToString()), x.Value))
|
||||
.Where(x => x.Key.Any(y => !y.Value.NullOrEmpty()))
|
||||
y => y
|
||||
, y => y.Contains('.')
|
||||
? typeof(Sys_CodeTemplate).GetRecursiveProperty(y)!.GetValue(
|
||||
x.Key.GetType().GetRecursiveProperty(y[..y.LastIndexOf('.')]).GetValue(x.Key))!.ToString()
|
||||
: typeof(Sys_CodeTemplate).GetProperty(y)!.GetValue(x.Key)!.ToString()), x.Value))
|
||||
.OrderByDescending(x => x.Value);
|
||||
}
|
||||
|
||||
|
@ -31,15 +31,13 @@ public sealed class ConstantService : ServiceBase<IConstantService>, IConstantSe
|
||||
static string[] GetDicValue(Enum y)
|
||||
{
|
||||
var ret = new[] { Convert.ToInt64(y, CultureInfo.InvariantCulture).ToString(CultureInfo.InvariantCulture), y.ResDesc<Ln>() };
|
||||
if (y is CountryCodes z) {
|
||||
return [..ret, z.GetCallingCode().ToInvString()];
|
||||
}
|
||||
|
||||
var decorationAttribute = y.Attr<EnumDecorationAttribute>() ?? new EnumDecorationAttribute();
|
||||
return [
|
||||
ret = [
|
||||
..ret, decorationAttribute.Indicate.ToLowerInvariant(), decorationAttribute.Pulse.ToString().ToLowerInvariant()
|
||||
, decorationAttribute.Sort.ToInvString()
|
||||
];
|
||||
return y is CountryCodes z ? [..ret, z.GetCallingCode().ToInvString()] : ret;
|
||||
}
|
||||
|
||||
static string[] GetHttpStatusCodeDicValue(string name)
|
||||
|
@ -302,78 +302,6 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
|
||||
await WriteCsCodeAsync(Path.Combine(dir, $"{req.EntityName}Cache.cs"), templateContent).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
private static Task GenerateDomainCreateReqFileAsync(GenerateCsCodeReq req, string project, string prefix)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"using {project}.DbMaps.{prefix};");
|
||||
if (!req.Interfaces.NullOrEmpty()) {
|
||||
_ = sb.AppendLine("using NetAdmin.Domain.DbMaps.Dependency.Fields;");
|
||||
}
|
||||
|
||||
_ = sb.AppendLine();
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"namespace {project}.Dto.{prefix}.{req.EntityName};");
|
||||
_ = sb.AppendLine();
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"""
|
||||
/// <summary>
|
||||
/// 请求:创建{req.Summary}
|
||||
/// </summary>
|
||||
""");
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"public record Create{req.EntityName}Req : {prefix}_{req.EntityName}");
|
||||
_ = sb.Append('{');
|
||||
if (req.Interfaces?.Contains(nameof(IFieldEnabled)) == true) {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="IFieldEnabled.Enabled" />
|
||||
public override bool Enabled { get; init; } = true;
|
||||
""");
|
||||
}
|
||||
|
||||
if (req.BaseClass != nameof(SimpleEntity) && req.FieldList.Single(x => x.IsPrimary).Type == "string") {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[Required]
|
||||
public override string Id { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
if (req.Interfaces?.Contains(nameof(IFieldSort)) == true) {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="IFieldSort.Sort" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Sort { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
if (req.Interfaces?.Contains(nameof(IFieldSummary)) == true) {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="IFieldSummary.Summary" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string Summary { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
foreach (var field in req.FieldList) {
|
||||
var condition = field.IsStruct ? "Never" : "WhenWritingNull";
|
||||
var nul = field.IsStruct && field.IsNullable ? "?" : string.Empty;
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $$"""
|
||||
|
||||
/// <inheritdoc cref="{{prefix}}_{{req.EntityName}}.{{field.Name}}" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.{{condition}})]
|
||||
public override {{field.Type}}{{nul}} {{field.Name}} { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
_ = sb.Append('}');
|
||||
|
||||
var outPath = Path.Combine(Path.GetDirectoryName(req.Project)!, "Dto", prefix, req.EntityName);
|
||||
_ = Directory.CreateDirectory(outPath);
|
||||
return WriteCsCodeAsync(Path.Combine(outPath, $"Create{req.EntityName}Req.cs"), sb.ToString());
|
||||
}
|
||||
|
||||
private static Task GenerateDomainEditReqFileAsync(GenerateCsCodeReq req, string project, string prefix)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
@ -1074,4 +1002,85 @@ public sealed class DevService(IApiService apiService) : ServiceBase<DevService>
|
||||
content = string.Join('\n', usings) + sb;
|
||||
return File.WriteAllTextAsync(path, usings.Count == 0 ? content.Trim() : content);
|
||||
}
|
||||
|
||||
private Task GenerateDomainCreateReqFileAsync(GenerateCsCodeReq req, string project, string prefix)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"using {project}.DbMaps.{prefix};");
|
||||
if (!req.Interfaces.NullOrEmpty()) {
|
||||
_ = sb.AppendLine("using NetAdmin.Domain.DbMaps.Dependency.Fields;");
|
||||
}
|
||||
|
||||
_ = sb.AppendLine();
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"namespace {project}.Dto.{prefix}.{req.EntityName};");
|
||||
_ = sb.AppendLine();
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"""
|
||||
/// <summary>
|
||||
/// 请求:创建{req.Summary}
|
||||
/// </summary>
|
||||
""");
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $"public record Create{req.EntityName}Req : {prefix}_{req.EntityName}");
|
||||
_ = sb.Append('{');
|
||||
if (req.Interfaces?.Contains(nameof(IFieldEnabled)) == true) {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="IFieldEnabled.Enabled" />
|
||||
public override bool Enabled { get; init; } = true;
|
||||
""");
|
||||
}
|
||||
|
||||
if (req.BaseClass != nameof(SimpleEntity) && req.FieldList.Single(x => x.IsPrimary).Type == "string") {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="EntityBase{T}.Id" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
[Required]
|
||||
public override string Id { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
if (req.Interfaces?.Contains(nameof(IFieldSort)) == true) {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="IFieldSort.Sort" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.Never)]
|
||||
public override long Sort { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
if (req.Interfaces?.Contains(nameof(IFieldSummary)) == true) {
|
||||
_ = sb.AppendLine("""
|
||||
|
||||
/// <inheritdoc cref="IFieldSummary.Summary" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||
public override string Summary { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
foreach (var field in req.FieldList) {
|
||||
var condition = field.IsStruct ? "Never" : "WhenWritingNull";
|
||||
var nul = field.IsStruct && field.IsNullable ? "?" : null;
|
||||
var isEnum = S<IConstantService>().GetEnums().FirstOrDefault(x => x.Key == field.Type);
|
||||
string enumConstraint = null;
|
||||
if (!isEnum.Key.NullOrEmpty()) {
|
||||
enumConstraint = $"""
|
||||
|
||||
[EnumDataType(typeof({field.Type}))]
|
||||
""";
|
||||
}
|
||||
|
||||
_ = sb.AppendLine(CultureInfo.InvariantCulture, $$"""
|
||||
|
||||
/// <inheritdoc cref="{{prefix}}_{{req.EntityName}}.{{field.Name}}" />
|
||||
[JsonIgnore(Condition = JsonIgnoreCondition.{{condition}})]{{enumConstraint}}
|
||||
public override {{field.Type}}{{nul}} {{field.Name}} { get; init; }
|
||||
""");
|
||||
}
|
||||
|
||||
_ = sb.Append('}');
|
||||
|
||||
var outPath = Path.Combine(Path.GetDirectoryName(req.Project)!, "Dto", prefix, req.EntityName);
|
||||
_ = Directory.CreateDirectory(outPath);
|
||||
return WriteCsCodeAsync(Path.Combine(outPath, $"Create{req.EntityName}Req.cs"), sb.ToString());
|
||||
}
|
||||
}
|
@ -104,7 +104,11 @@ public sealed class UserInviteService(BasicRepository<Sys_UserInvite, long> rpo)
|
||||
public async Task<IEnumerable<QueryUserInviteRsp>> QueryAsync(QueryReq<QueryUserInviteReq> req)
|
||||
{
|
||||
req.ThrowIfInvalid();
|
||||
var ret = await QueryInternal(req).Include(a => a.Owner).Include(a => a.User).WithNoLockNoWait().ToTreeListAsync().ConfigureAwait(false);
|
||||
var query = QueryInternal(req).Include(a => a.Owner).Include(a => a.User).WithNoLockNoWait();
|
||||
var ret = req.Filter?.IsPlainQuery == true
|
||||
? await query.ToListAsync().ConfigureAwait(false)
|
||||
: await query.ToTreeListAsync().ConfigureAwait(false);
|
||||
|
||||
return ret.Adapt<IEnumerable<QueryUserInviteRsp>>();
|
||||
}
|
||||
|
||||
@ -117,7 +121,9 @@ public sealed class UserInviteService(BasicRepository<Sys_UserInvite, long> rpo)
|
||||
|
||||
private ISelect<Sys_UserInvite> QueryInternal(QueryReq<QueryUserInviteReq> req)
|
||||
{
|
||||
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter).WhereDynamic(req.Filter);
|
||||
var ret = Rpo.Select.WhereDynamicFilter(req.DynamicFilter)
|
||||
.WhereIf( //
|
||||
req.Filter?.Id > 0, a => a.Id == req.Filter.Id);
|
||||
|
||||
// ReSharper disable once SwitchStatementMissingSomeEnumCasesNoDefault
|
||||
switch (req.Order) {
|
||||
|
@ -3,7 +3,7 @@
|
||||
<ProjectReference Include="../NetAdmin.Host/NetAdmin.Host.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.6"/>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="9.0.7"/>
|
||||
<PackageReference Include="xunit" Version="2.9.3"/>
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<svg class="icon" height="300" version="1.1" viewBox="0 0 1024 1024" width="300" xmlns="http://www.w3.org/2000/svg">
|
||||
<svg class="icon" height="256" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M398.222 398.222h341.334v56.89H398.222v-56.89z m0-170.666h341.334v56.888H398.222v-56.888zM341.333 56.889h455.111v56.889h-455.11v-56.89zM568.89 682.667V568.889h56.889v170.667H398.222V568.889h56.89v113.778h113.777zM341.333 568.889h56.89v56.889H113.777v-56.89h56.889V170.668h56.889v398.222h56.888V227.556h-56.888v-56.89h56.888V56.89h56.89v512z m455.111 0v-512h56.89v512h56.888v56.889H625.778v-56.89h170.666z m56.89 341.333V625.778h56.888V967.11H113.778V625.778h56.889v284.444h682.666z" />
|
||||
d="M770.47 292.2h-83.26v-37.16c0-39.72-32.31-72.03-72.03-72.03H408.82c-39.72 0-72.03 32.31-72.03 72.03v37.16h-83.26c-45.71 0-82.9 37.19-82.9 82.9v420.14c0 45.71 37.19 82.9 82.9 82.9h516.94c45.71 0 82.9-37.19 82.9-82.9V375.1c0-45.72-37.19-82.9-82.9-82.9z m-378.41-37.16c0-9.25 7.52-16.76 16.76-16.76h206.35c9.25 0 16.77 7.52 16.77 16.76v37.16H392.06v-37.16z m-138.53 92.42h516.94c15.24 0 27.63 12.4 27.63 27.63v75.35H225.9V375.1c0-15.24 12.4-27.64 27.63-27.64z m516.94 475.41H253.53c-15.24 0-27.63-12.4-27.63-27.63V505.72h572.2v289.52c0 15.23-12.4 27.63-27.63 27.63z"></path>
|
||||
</svg>
|
||||
</template>
|
@ -1 +1,6 @@
|
||||
<template><svg t="1751618283110" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5188" width="256" height="256"><path d="M533.333333 597.333333H106.666667c-12.8 0-21.333333-8.533333-21.333334-21.333333V106.666667c0-12.8 8.533333-21.333333 21.333334-21.333334h426.666666c12.8 0 21.333333 8.533333 21.333334 21.333334v469.333333c0 12.8-8.533333 21.333333-21.333334 21.333333zM128 554.666667h384V128H128v426.666667zM917.333333 938.666667H106.666667c-12.8 0-21.333333-8.533333-21.333334-21.333334V704c0-12.8 8.533333-21.333333 21.333334-21.333333h810.666666c12.8 0 21.333333 8.533333 21.333334 21.333333v213.333333c0 12.8-8.533333 21.333333-21.333334 21.333334zM128 896h768v-170.666667H128v170.666667zM917.333333 298.666667H661.333333c-12.8 0-21.333333-8.533333-21.333333-21.333334V106.666667c0-12.8 8.533333-21.333333 21.333333-21.333334h256c12.8 0 21.333333 8.533333 21.333334 21.333334v170.666666c0 12.8-8.533333 21.333333-21.333334 21.333334z m-234.666666-42.666667h213.333333V128H682.666667v128zM917.333333 597.333333H661.333333c-12.8 0-21.333333-8.533333-21.333333-21.333333v-170.666667c0-12.8 8.533333-21.333333 21.333333-21.333333h256c12.8 0 21.333333 8.533333 21.333334 21.333333v170.666667c0 12.8-8.533333 21.333333-21.333334 21.333333z m-234.666666-42.666666h213.333333v-128H682.666667v128z" fill="#333333" p-id="5189"></path></svg></template>
|
||||
<template>
|
||||
<svg class="icon" height="256" version="1.1" viewBox="0 0 1024 1024" width="256" xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M533.333333 597.333333H106.666667c-12.8 0-21.333333-8.533333-21.333334-21.333333V106.666667c0-12.8 8.533333-21.333333 21.333334-21.333334h426.666666c12.8 0 21.333333 8.533333 21.333334 21.333334v469.333333c0 12.8-8.533333 21.333333-21.333334 21.333333zM128 554.666667h384V128H128v426.666667zM917.333333 938.666667H106.666667c-12.8 0-21.333333-8.533333-21.333334-21.333334V704c0-12.8 8.533333-21.333333 21.333334-21.333333h810.666666c12.8 0 21.333333 8.533333 21.333334 21.333333v213.333333c0 12.8-8.533333 21.333333-21.333334 21.333334zM128 896h768v-170.666667H128v170.666667zM917.333333 298.666667H661.333333c-12.8 0-21.333333-8.533333-21.333333-21.333334V106.666667c0-12.8 8.533333-21.333333 21.333333-21.333334h256c12.8 0 21.333333 8.533333 21.333334 21.333334v170.666666c0 12.8-8.533333 21.333333-21.333334 21.333334z m-234.666666-42.666667h213.333333V128H682.666667v128zM917.333333 597.333333H661.333333c-12.8 0-21.333333-8.533333-21.333333-21.333333v-170.666667c0-12.8 8.533333-21.333333 21.333333-21.333333h256c12.8 0 21.333333 8.533333 21.333334 21.333333v170.666667c0 12.8-8.533333 21.333333-21.333334 21.333333z m-234.666666-42.666666h213.333333v-128H682.666667v128z"></path>
|
||||
</svg>
|
||||
</template>
|
@ -1,17 +1,19 @@
|
||||
<template>
|
||||
<el-table-column v-bind="$attrs">
|
||||
<template #default="{ row }">
|
||||
<div
|
||||
:style="{ display: $TOOL.getNestedProperty(row, $attrs.prop) ? 'flex' : 'none' }"
|
||||
<div :style="{ display: $TOOL.getNestedProperty(row, $attrs.prop) ? 'flex' : 'none' }" class="el-table-column-avatar">
|
||||
<el-avatar
|
||||
v-if="$TOOL.getNestedProperty(row, $attrs.nestProp)"
|
||||
:src="getAvatar(row, $attrs.nestProp)"
|
||||
@click="click($TOOL.getNestedProperty(row, $attrs.prop))"
|
||||
class="el-table-column-avatar">
|
||||
<el-avatar v-if="$TOOL.getNestedProperty(row, $attrs.nestProp)" :src="getAvatar(row, $attrs.nestProp)" size="small" />
|
||||
size="small"
|
||||
style="cursor: pointer" />
|
||||
<div>
|
||||
<p>{{ $TOOL.getNestedProperty(row, $attrs.nestProp) }}</p>
|
||||
<p v-if="$attrs.nestProp2">{{ $TOOL.getNestedProperty(row, $attrs.nestProp2) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<save-dialog v-if="dialog.save" @closed="dialog.save = false" ref="saveDialog" />
|
||||
<save-dialog v-if="dialog.save" @closed="dialog.save = null" @mounted="$refs.saveDialog.open(dialog.save)" ref="saveDialog" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
</template>
|
||||
@ -25,7 +27,7 @@ export default {
|
||||
created() {},
|
||||
data() {
|
||||
return {
|
||||
dialog: { save: false },
|
||||
dialog: {},
|
||||
}
|
||||
},
|
||||
emits: ['click'],
|
||||
@ -34,9 +36,7 @@ export default {
|
||||
if (!this.clickOpenDialog) {
|
||||
return
|
||||
}
|
||||
this.dialog.save = true
|
||||
await this.$nextTick()
|
||||
await this.$refs.saveDialog.open({ mode: 'view', row: { id: id } })
|
||||
this.dialog.save = { mode: 'view', row: { id: id } }
|
||||
},
|
||||
//获取头像
|
||||
getAvatar(row, prop) {
|
||||
|
@ -11,10 +11,13 @@
|
||||
<template v-for="(item, i) in columns" :key="i">
|
||||
<el-form-item v-if="item.show?.includes(mode)" :label="item.label" :prop="i">
|
||||
<el-date-picker v-if="i.endsWith(`Time`)" v-model="form[i]" :disabled="item.disabled?.includes(mode)" type="datetime" />
|
||||
<el-select v-else-if="item.enum" v-model="form[i]" :disabled="item.disabled?.includes(mode)">
|
||||
<el-select v-else-if="item.enum" v-model="form[i]" :disabled="item.disabled?.includes(mode)" clearable filterable>
|
||||
<el-option
|
||||
v-for="e in Object.entries(this.$GLOBAL.enums[item.enum]).map((x) => {
|
||||
return { value: x[0], text: x[1][1] }
|
||||
return {
|
||||
value: x[0],
|
||||
text: item.enumSelectText ? item.enumSelectText(x) : item.enumText ? item.enumText(x) : x[1][1],
|
||||
}
|
||||
})"
|
||||
:key="e.value"
|
||||
:label="e.text"
|
||||
@ -74,7 +77,11 @@ export default {
|
||||
x[0],
|
||||
[
|
||||
{ required: true, message: this.$t(`{field} 不能为空`, { field: x[1].label }) },
|
||||
{ validator: x[1].rule.validator, message: this.$t(`{field} 不正确`, { field: x[1].label }) },
|
||||
{
|
||||
type: x[1].rule.type ?? 'string',
|
||||
validator: x[1].rule.validator,
|
||||
message: this.$t(`{field} 不正确`, { field: x[1].label }),
|
||||
},
|
||||
],
|
||||
]
|
||||
}
|
||||
|
@ -30,8 +30,9 @@
|
||||
ref="search" />
|
||||
</div>
|
||||
<div class="right-panel">
|
||||
<el-button v-if="operations.includes('add')" @click="onAddClick" icon="el-icon-plus" type="primary" />
|
||||
<el-button v-if="operations.includes(`add`)" @click="onAddClick" icon="el-icon-plus" type="primary" />
|
||||
<el-button
|
||||
v-if="operations.includes(`del`)"
|
||||
:disabled="this.table.selection.length === 0 || this.loading"
|
||||
@click="onBulkDeleteClick"
|
||||
icon="el-icon-delete"
|
||||
@ -62,20 +63,33 @@
|
||||
<template v-for="(item, i) in columns" :key="i">
|
||||
<component
|
||||
v-bind="item"
|
||||
v-if="item.show.includes('list')"
|
||||
v-if="item.show.includes(`list`)"
|
||||
:formatter="item.thousands ? (row) => $TOOL.groupSeparator(row[i]) : undefined"
|
||||
:is="item.is ?? `el-table-column`"
|
||||
:options="
|
||||
item.options ??
|
||||
(item.enum
|
||||
? Object.entries(this.$GLOBAL.enums[item.enum]).map((x) => {
|
||||
return { value: x[0], text: x[1][1], type: x[1][2], pulse: x[1][3] === 'true' }
|
||||
return {
|
||||
value: x[0],
|
||||
text: item.enumText ? item.enumText(x) : x[1][1],
|
||||
type: x[1][2],
|
||||
pulse: x[1][3] === `true`,
|
||||
}
|
||||
})
|
||||
: null)
|
||||
"
|
||||
:prop="i"
|
||||
:prop="item.prop ?? i"
|
||||
:sortable="item.sortable ?? `custom`">
|
||||
<template v-if="item.isBoolean" #default="{ row }">
|
||||
<el-switch v-model="row[i]" @change="onSwitchChange(row, i === `enabled` ? `setEnabled` : item.onChange)"></el-switch>
|
||||
<el-switch
|
||||
v-model="row[i]"
|
||||
:disabled="
|
||||
!$GLOBAL.hasApiPermission(
|
||||
$API[entityName][i === `enabled` ? `setEnabled` : item.onChange].url.replace(`${config.API_URL}/`, ``),
|
||||
)
|
||||
"
|
||||
@change="onSwitchChange(row, i === `enabled` ? `setEnabled` : item.onChange)"></el-switch>
|
||||
</template>
|
||||
</component>
|
||||
</template>
|
||||
@ -83,7 +97,7 @@
|
||||
<el-table-column :label="$t(`操作`)" align="right" fixed="right" width="150">
|
||||
<template #default="{ row }">
|
||||
<el-button-group size="small">
|
||||
<el-button @click="onViewClick(row)" icon="el-icon-view" />
|
||||
<el-button v-if="operations.includes(`view`)" @click="onViewClick(row)" icon="el-icon-view" />
|
||||
<el-button v-if="operations.includes(`edit`)" @click="onEditClick(row)" icon="el-icon-edit" />
|
||||
<el-button v-if="operations.includes(`del`)" @click="onDeleteClick(row)" icon="el-icon-delete" type="danger" />
|
||||
</el-button-group>
|
||||
@ -107,14 +121,20 @@ import { h } from 'vue'
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
import tableConfig from '@/config/table'
|
||||
import naColOperation from '@/config/na-col-operation'
|
||||
import config from '@/config'
|
||||
const naColAvatar = defineAsyncComponent(() => import('@/components/na-col-avatar'))
|
||||
const detailDialog = defineAsyncComponent(() => import('./detail'))
|
||||
const naColUser = defineAsyncComponent(() => import('@/components/na-col-user'))
|
||||
export default {
|
||||
components: {
|
||||
detailDialog,
|
||||
naColAvatar,
|
||||
naColUser,
|
||||
},
|
||||
computed: {
|
||||
config() {
|
||||
return config
|
||||
},
|
||||
naColOperation() {
|
||||
return naColOperation
|
||||
},
|
||||
@ -124,6 +144,7 @@ export default {
|
||||
},
|
||||
created() {
|
||||
const searchFields = []
|
||||
this.searchControls = []
|
||||
for (const item in this.columns) {
|
||||
this.table.menu.extra[item] = this.columns[item].extra
|
||||
if (this.columns[item].searchable) {
|
||||
@ -132,14 +153,40 @@ export default {
|
||||
}
|
||||
if (searchFields.length > 0) {
|
||||
this.searchControls.push({
|
||||
type: 'select-input',
|
||||
field: ['dy', searchFields],
|
||||
placeholder: this.$t('匹配内容'),
|
||||
style: 'width:25rem',
|
||||
selectStyle: 'width:8rem',
|
||||
type: `select-input`,
|
||||
field: [`dy`, searchFields],
|
||||
placeholder: this.$t(`匹配内容`),
|
||||
style: `width:25rem`,
|
||||
selectStyle: `width:8rem`,
|
||||
})
|
||||
}
|
||||
|
||||
this.searchControls = this.customSearchControls.concat(this.searchControls)
|
||||
|
||||
for (const i in this.columns) {
|
||||
const col = this.columns[i]
|
||||
if (!col.countBy) {
|
||||
continue
|
||||
}
|
||||
|
||||
this.selectFilterData.push({
|
||||
title: col.label,
|
||||
key: i,
|
||||
options: [{ label: this.$t(`全部`), value: `` }].concat(
|
||||
col.isBoolean
|
||||
? [
|
||||
{ value: true, label: this.$t(`是`) },
|
||||
{ value: false, label: this.$t(`否`) },
|
||||
]
|
||||
: Object.entries(this.$GLOBAL.enums[col.enum]).map((x) => {
|
||||
return {
|
||||
value: x[0],
|
||||
label: x[1][1],
|
||||
}
|
||||
}),
|
||||
),
|
||||
})
|
||||
}
|
||||
for (const filter of this.selectFilters) {
|
||||
this.selectFilterData.push({
|
||||
title: filter.title,
|
||||
@ -221,11 +268,16 @@ export default {
|
||||
})
|
||||
}
|
||||
for (const item in this.columns) {
|
||||
const field = form.dy[item] || form.dy[item[0].toUpperCase() + item.substring(1)]
|
||||
if (field === undefined || field === '' || typeof field === 'object') continue
|
||||
let field = form.dy[item]
|
||||
if (field === undefined) field = form.dy[item[0].toUpperCase() + item.substring(1)]
|
||||
|
||||
if (field === undefined || field === `` || typeof field === `object`) {
|
||||
continue
|
||||
}
|
||||
|
||||
this.query.dynamicFilter.filters.push({
|
||||
field: item,
|
||||
operator: this.columns[item].searchable,
|
||||
operator: this.columns[item].operator ?? `eq`,
|
||||
value: field,
|
||||
})
|
||||
}
|
||||
@ -236,23 +288,23 @@ export default {
|
||||
|
||||
// ---------------------------- ↓ 表格事件 ----------------------------
|
||||
async onViewClick(row) {
|
||||
this.dialog.detail = { mode: 'view', row }
|
||||
this.dialog.detail = { mode: `view`, row }
|
||||
},
|
||||
async onEditClick(row) {
|
||||
this.dialog.detail = { mode: 'edit', row }
|
||||
this.dialog.detail = { mode: `edit`, row }
|
||||
},
|
||||
async onBulkDeleteClick() {
|
||||
let loading
|
||||
try {
|
||||
await this.$confirm(this.$t('确定删除选中的 {count} 项吗?', { count: this.table.selection.length }), this.$t('提示'), {
|
||||
type: 'warning',
|
||||
await this.$confirm(this.$t(`确定删除选中的 {count} 项吗?`, { count: this.table.selection.length }), this.$t(`提示`), {
|
||||
type: `warning`,
|
||||
})
|
||||
loading = this.$loading()
|
||||
const res = await this.$API[this.entityName].bulkDelete.post({
|
||||
items: this.table.selection,
|
||||
})
|
||||
this.$refs.table.refresh()
|
||||
this.$message.success(this.$t('删除 {count} 项', { count: res.data }))
|
||||
this.$message.success(this.$t(`删除 {count} 项`, { count: res.data }))
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
@ -260,8 +312,8 @@ export default {
|
||||
},
|
||||
async onDeleteClick(row) {
|
||||
try {
|
||||
await this.$confirm(h('div', [h('p', this.$t('是否确认删除?')), h('p', row.id)]), this.$t('提示'), {
|
||||
type: 'warning',
|
||||
await this.$confirm(h(`div`, [h(`p`, this.$t(`是否确认删除?`)), h(`p`, row.id)]), this.$t(`提示`), {
|
||||
type: `warning`,
|
||||
})
|
||||
} catch {
|
||||
return
|
||||
@ -277,12 +329,12 @@ export default {
|
||||
loading?.close()
|
||||
},
|
||||
async onAddClick() {
|
||||
this.dialog.detail = { mode: 'add' }
|
||||
this.dialog.detail = { mode: `add` }
|
||||
},
|
||||
async onSwitchChange(row, method) {
|
||||
try {
|
||||
await this.$API[this.entityName][method].post(row)
|
||||
this.$message.success(this.$t('操作成功'))
|
||||
this.$message.success(this.$t(`操作成功`))
|
||||
} catch {
|
||||
//
|
||||
}
|
||||
@ -293,25 +345,33 @@ export default {
|
||||
|
||||
const calls = []
|
||||
for (const col in this.columns) {
|
||||
if (this.columns[col].countBy)
|
||||
if (this.columns[col].countBy) {
|
||||
for (const item of this.selectFilterData.find((x) => x.key === col).options) {
|
||||
delete item.badge
|
||||
}
|
||||
|
||||
calls.push(
|
||||
this.$API[this.entityName].countBy.post({
|
||||
dynamicFilter: { filters: this.query.dynamicFilter.filters },
|
||||
requiredFields: [col[0].toUpperCase() + col.substring(1)],
|
||||
requiredFields: [col.replace(/(?:^|\.)[a-z]/g, (m) => m.toUpperCase())],
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
const res = await Promise.all(calls)
|
||||
Object.assign(this.statistics, { res })
|
||||
for (const item of res) {
|
||||
for (const item2 of item.data) {
|
||||
const key = Object.entries(item2.key)[0]
|
||||
const filter = this.selectFilterData
|
||||
.find((x) => x.key.toString().toLowerCase() === key[0].toLowerCase())
|
||||
.find((x) => x.key.toLowerCase() === key[0].toLowerCase())
|
||||
?.options.find((x) => x.value.toString().toLowerCase() === key[1].toLowerCase())
|
||||
if (filter) filter.badge = item2.value
|
||||
}
|
||||
}
|
||||
for (const item of this.selectFilterData) {
|
||||
item.options.sort((x, y) => (y.label === this.$t(`全部`) ? 999999999 : (y.badge ?? 0 - x.badge ?? 0)))
|
||||
}
|
||||
},
|
||||
onSelectionChange(data) {
|
||||
this.table.selection = data
|
||||
@ -332,9 +392,10 @@ export default {
|
||||
keywords: { type: String },
|
||||
entityName: { type: String },
|
||||
summary: { type: String },
|
||||
selectFilters: { type: Array },
|
||||
selectFilters: { type: Array, default: [] },
|
||||
customSearchControls: { type: Array, default: [] },
|
||||
columns: { type: Object },
|
||||
operations: { type: Array, default: ['add', 'edit', 'del'] },
|
||||
operations: { type: Array, default: [`view`, `add`, `edit`, `del`] },
|
||||
dialogFullScreen: { type: Boolean },
|
||||
},
|
||||
watch: {},
|
||||
|
@ -4,7 +4,7 @@
|
||||
<div v-for="item in data" :class="`sc-select-filter__item${item.w100p ? ' sc-select-filter__item-w100p' : ''}`" :key="item.key">
|
||||
<div :style="{ width: labelWidth + 'rem' }" @click="autoHeight" class="sc-select-filter__item-title">
|
||||
<label>
|
||||
<span>{{ item.title }}({{ item.options?.length - 1 }})</span>
|
||||
<span>{{ item.title }}({{ item.options?.filter((x) => x.badge).length }})</span>
|
||||
<el-icon style="display: none">
|
||||
<el-icon-arrow-up />
|
||||
</el-icon>
|
||||
@ -54,13 +54,6 @@ export default {
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// data(val) {
|
||||
// val.forEach((item) => {
|
||||
// this.selected[item.key] =
|
||||
// this.selectedValues[item.key] || (Array.isArray(item.options) && item.options.length) ? [item.options[0].value] : []
|
||||
// })
|
||||
// },
|
||||
|
||||
data: {
|
||||
immediate: true,
|
||||
handler() {
|
||||
|
@ -6,7 +6,11 @@
|
||||
</el-icon>
|
||||
</div>
|
||||
<el-select v-bind="$attrs" :allow-create="allowCreate" :loading="loading" @visible-change="visibleChange">
|
||||
<el-option v-for="item in options" :key="item[props.value]" :label="item[props.label]" :value="objValueType ? item : item[props.value]">
|
||||
<el-option
|
||||
v-for="item in options"
|
||||
:key="$TOOL.getNestedProperty(item, props.value)"
|
||||
:label="typeof props.label === `function` ? props.label(item) : $TOOL.getNestedProperty(item, props.label)"
|
||||
:value="objValueType ? item : $TOOL.getNestedProperty(item, props.value)">
|
||||
<slot :data="item" name="option" />
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
@ -7,7 +7,7 @@
|
||||
:data="tableData"
|
||||
:default-expand-all="config.defaultExpandAll"
|
||||
:default-sort="defaultSort"
|
||||
:height="height === 'auto' ? null : '100%'"
|
||||
:height="height === `auto` ? null : `100%`"
|
||||
:key="toggleIndex"
|
||||
:row-key="rowKey"
|
||||
:size="config.size"
|
||||
@ -64,18 +64,18 @@
|
||||
<div v-if="!hideDo" class="scTable-do">
|
||||
<el-button
|
||||
v-if="exportApi"
|
||||
:title="$t('导出文件')"
|
||||
:title="$t(`导出文件`)"
|
||||
@click="exportData"
|
||||
circle
|
||||
icon="el-icon-download"
|
||||
plain
|
||||
style="margin-left: 1rem"
|
||||
type="primary" />
|
||||
<el-button v-if="!hideRefresh" :title="$t('刷新')" @click="refresh" circle icon="el-icon-refresh" style="margin-left: 1rem" />
|
||||
<el-button v-if="!hideRefresh" :title="$t(`刷新`)" @click="refresh" circle icon="el-icon-refresh" style="margin-left: 1rem" />
|
||||
<el-popover
|
||||
v-if="column"
|
||||
:hide-after="0"
|
||||
:title="$t('列设置')"
|
||||
:title="$t(`列设置`)"
|
||||
:width="500"
|
||||
@after-leave="customColumnShow = false"
|
||||
@show="customColumnShow = true"
|
||||
@ -92,21 +92,21 @@
|
||||
@userChange="columnSettingChange"
|
||||
ref="columnSetting" />
|
||||
</el-popover>
|
||||
<el-popover v-if="!hideSetting" :hide-after="0" :title="$t('表格设置')" :width="400" placement="top" trigger="click">
|
||||
<el-popover v-if="!hideSetting" :hide-after="0" :title="$t(`表格设置`)" :width="400" placement="top" trigger="click">
|
||||
<template #reference>
|
||||
<el-button circle icon="el-icon-setting" style="margin-left: 1rem" />
|
||||
</template>
|
||||
<el-form label-position="left" label-width="10rem">
|
||||
<el-form-item :label="$t('表格尺寸')">
|
||||
<el-form-item :label="$t(`表格尺寸`)">
|
||||
<el-radio-group v-model="config.size" @change="configSizeChange" size="small">
|
||||
<el-radio-button value="large">{{ $t('大') }}</el-radio-button>
|
||||
<el-radio-button value="default">{{ $t('正常') }}</el-radio-button>
|
||||
<el-radio-button value="small">{{ $t('小') }}</el-radio-button>
|
||||
<el-radio-button value="large">{{ $t(`大`) }}</el-radio-button>
|
||||
<el-radio-button value="default">{{ $t(`正常`) }}</el-radio-button>
|
||||
<el-radio-button value="small">{{ $t(`小`) }}</el-radio-button>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item :label="$t('样式')">
|
||||
<el-checkbox v-model="config.border" :label="$t('纵向边框')" />
|
||||
<el-checkbox v-model="config.stripe" :label="$t('斑马纹')" />
|
||||
<el-form-item :label="$t(`样式`)">
|
||||
<el-checkbox v-model="config.border" :label="$t(`纵向边框`)" />
|
||||
<el-checkbox v-model="config.stripe" :label="$t(`斑马纹`)" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-popover>
|
||||
@ -127,44 +127,44 @@
|
||||
:command="menu"
|
||||
:key="index"
|
||||
:title="`${menu}`">
|
||||
<sc-contextmenu-item :command="`${menu}^|^Equal^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" title="=" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotEqual^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" title="≠" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^GreaterThan^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" divided title=">" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^GreaterThanOrEqual^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" title="≥" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^LessThan^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" title="<" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^LessThanOrEqual^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" title="≤" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^Contains^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('包含')" divided />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotContains^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('不含')" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^Equal^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" title="=" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotEqual^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" title="≠" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^GreaterThan^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" divided title=">" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^GreaterThanOrEqual^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" title="≥" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^LessThan^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" title="<" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^LessThanOrEqual^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" title="≤" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^Contains^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`包含`)" divided />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotContains^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`不含`)" />
|
||||
<sc-contextmenu-item
|
||||
:command="`${menu}^|^StartsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
|
||||
:title="$t('以 x 开始')"
|
||||
:command="`${menu}^|^StartsWith^|^${tool.getNestedProperty(current.row, menu) ?? ``}`"
|
||||
:title="$t(`以 x 开始`)"
|
||||
divided />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotStartsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('非 x 开始')" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^EndsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('以 x 结束')" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotEndsWith^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('非 x 结束')" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^Range^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('数值范围')" divided />
|
||||
<sc-contextmenu-item :command="`${menu}^|^DateRange^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('日期范围')" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^Any^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('为其一')" divided />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotAny^|^${tool.getNestedProperty(current.row, menu) ?? ''}`" :title="$t('非其一')" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotStartsWith^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`非 x 开始`)" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^EndsWith^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`以 x 结束`)" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotEndsWith^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`非 x 结束`)" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^Range^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`数值范围`)" divided />
|
||||
<sc-contextmenu-item :command="`${menu}^|^DateRange^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`日期范围`)" />
|
||||
<sc-contextmenu-item :command="`${menu}^|^Any^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`为其一`)" divided />
|
||||
<sc-contextmenu-item :command="`${menu}^|^NotAny^|^${tool.getNestedProperty(current.row, menu) ?? ``}`" :title="$t(`非其一`)" />
|
||||
<sc-contextmenu-item
|
||||
:command="`${menu}^|^order-ascending^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
|
||||
:title="$t('顺序排序')"
|
||||
:command="`${menu}^|^order-ascending^|^${tool.getNestedProperty(current.row, menu) ?? ``}`"
|
||||
:title="$t(`顺序排序`)"
|
||||
divided />
|
||||
<sc-contextmenu-item
|
||||
:command="`${menu}^|^order-descending^|^${tool.getNestedProperty(current.row, menu) ?? ''}`"
|
||||
:title="$t('倒序排序')" />
|
||||
:command="`${menu}^|^order-descending^|^${tool.getNestedProperty(current.row, menu) ?? ``}`"
|
||||
:title="$t(`倒序排序`)" />
|
||||
</sc-contextmenu-item>
|
||||
<sc-contextmenu-item v-if="showCopy" :title="$t('复制')" command="copy" divided icon="el-icon-copy-document" suffix="C" />
|
||||
<sc-contextmenu-item v-if="showCopy" :title="$t(`复制`)" command="copy" divided icon="el-icon-copy-document" suffix="C" />
|
||||
<sc-contextmenu-item
|
||||
v-if="contextOpers.includes('add')"
|
||||
v-if="contextOpers.includes(`add`)"
|
||||
:divided="showCopy"
|
||||
:title="$t('新建')"
|
||||
:title="$t(`新建`)"
|
||||
command="add"
|
||||
icon="el-icon-plus"
|
||||
suffix="A" />
|
||||
<sc-contextmenu-item :title="$t('查看')" command="view" icon="el-icon-view" suffix="V" />
|
||||
<sc-contextmenu-item v-if="contextOpers.includes('edit')" :title="$t('编辑')" command="edit" icon="el-icon-edit" suffix="E" />
|
||||
<sc-contextmenu-item v-if="contextOpers.includes('del')" :title="$t('删除')" command="del" icon="el-icon-delete" suffix="D" />
|
||||
<sc-contextmenu-item v-if="contextOpers.includes(`view`)" :title="$t(`查看`)" command="view" icon="el-icon-view" suffix="V" />
|
||||
<sc-contextmenu-item v-if="contextOpers.includes(`edit`)" :title="$t(`编辑`)" command="edit" icon="el-icon-edit" suffix="E" />
|
||||
<sc-contextmenu-item v-if="contextOpers.includes(`del`)" :title="$t(`删除`)" command="del" icon="el-icon-delete" suffix="D" />
|
||||
<sc-contextmenu-item
|
||||
v-for="(adv, index) in contextAdvs"
|
||||
:command="adv"
|
||||
@ -172,8 +172,8 @@
|
||||
:icon="adv.icon"
|
||||
:key="index"
|
||||
:title="adv.label" />
|
||||
<sc-contextmenu-item v-if="exportApi" :title="$t('导出文件')" command="export" divided icon="el-icon-download" />
|
||||
<sc-contextmenu-item :title="$t('重新加载')" command="refresh" divided icon="el-icon-refresh" suffix="R" />
|
||||
<sc-contextmenu-item v-if="exportApi" :title="$t(`导出文件`)" command="export" divided icon="el-icon-download" />
|
||||
<sc-contextmenu-item :title="$t(`重新加载`)" command="refresh" divided icon="el-icon-refresh" suffix="R" />
|
||||
</sc-contextmenu>
|
||||
<field-filter ref="fieldFilterDialog" />
|
||||
</template>
|
||||
@ -186,7 +186,6 @@ const scContextmenuItem = defineAsyncComponent(() => import('@/components/sc-con
|
||||
const scContextmenu = defineAsyncComponent(() => import('@/components/sc-context-menu'))
|
||||
const fieldFilter = defineAsyncComponent(() => import('./field-filter'))
|
||||
|
||||
import { h } from 'vue'
|
||||
import tool from '@/utils/tool'
|
||||
import iframe from '@/store/modules/iframe'
|
||||
|
||||
@ -205,7 +204,7 @@ export default {
|
||||
contextOpers: { type: Array, default: [] },
|
||||
contextAdvs: { type: Array, default: [] },
|
||||
contextExtra: { type: Object },
|
||||
tableName: { type: String, default: '' },
|
||||
tableName: { type: String, default: `` },
|
||||
beforePost: {
|
||||
type: Function,
|
||||
},
|
||||
@ -222,14 +221,14 @@ export default {
|
||||
type: Object,
|
||||
default: () => {},
|
||||
},
|
||||
height: { type: [String, Number], default: '100%' },
|
||||
size: { type: String, default: 'default' },
|
||||
height: { type: [String, Number], default: `100%` },
|
||||
size: { type: String, default: `default` },
|
||||
border: { type: Boolean, default: false },
|
||||
stripe: { type: Boolean, default: false },
|
||||
defaultExpandAll: { type: Boolean, default: false },
|
||||
pageSize: { type: Number, default: config.pageSize },
|
||||
pageSizes: { type: Array, default: config.pageSizes },
|
||||
rowKey: { type: String, default: '' },
|
||||
rowKey: { type: String, default: `` },
|
||||
summaryMethod: { type: Function, default: null },
|
||||
filterMethod: { type: Function, default: null },
|
||||
cellClickMethod: { type: Function, default: null },
|
||||
@ -273,10 +272,10 @@ export default {
|
||||
return tool
|
||||
},
|
||||
_height() {
|
||||
return Number(this.height) ? Number(this.height) + 'px' : this.height
|
||||
return Number(this.height) ? Number(this.height) + `px` : this.height
|
||||
},
|
||||
_table_height() {
|
||||
return this.hidePagination && this.hideDo ? '100%' : 'calc(100% - 4rem)'
|
||||
return this.hidePagination && this.hideDo ? `100%` : `calc(100% - 4rem)`
|
||||
},
|
||||
},
|
||||
data() {
|
||||
@ -289,7 +288,7 @@ export default {
|
||||
},
|
||||
scPageSize: this.pageSize,
|
||||
isActivate: true,
|
||||
emptyText: '暂无数据',
|
||||
emptyText: `暂无数据`,
|
||||
toggleIndex: 0,
|
||||
tableData: [],
|
||||
total: 0,
|
||||
@ -297,7 +296,7 @@ export default {
|
||||
prop: null,
|
||||
order: null,
|
||||
loading: false,
|
||||
tableHeight: '100%',
|
||||
tableHeight: `100%`,
|
||||
tableParams: this.params,
|
||||
userColumn: [],
|
||||
customColumnShow: false,
|
||||
@ -336,68 +335,68 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
dbClick(row) {
|
||||
if (this.dblClickDisable) {
|
||||
if (this.dblClickDisable || !this.contextOpers.includes(`view`)) {
|
||||
return
|
||||
}
|
||||
if (this.vue.dialog) {
|
||||
this.vue.dialog.detail = { mode: 'view', row: { id: row.id } }
|
||||
this.vue.dialog.detail = { mode: `view`, row: { id: row.id } }
|
||||
}
|
||||
},
|
||||
async contextMenuCommand(command) {
|
||||
if (typeof command === 'object') {
|
||||
if (typeof command === `object`) {
|
||||
return command.action(this.vue, this.current.row)
|
||||
}
|
||||
if (command === 'refresh') {
|
||||
if (command === `refresh`) {
|
||||
this.vue.reload()
|
||||
return
|
||||
}
|
||||
if (command === 'copy') {
|
||||
if (command === `copy`) {
|
||||
let data = tool.getNestedProperty(this.current.row, this.current.column?.property)
|
||||
if (!data) return
|
||||
|
||||
const textarea = document.createElement('textarea')
|
||||
const textarea = document.createElement(`textarea`)
|
||||
textarea.readOnly = true
|
||||
textarea.style.position = 'absolute'
|
||||
textarea.style.left = '-9999px'
|
||||
textarea.style.position = `absolute`
|
||||
textarea.style.left = `-9999px`
|
||||
textarea.value = data
|
||||
document.body.appendChild(textarea)
|
||||
textarea.select()
|
||||
textarea.setSelectionRange(0, textarea.value.length)
|
||||
const result = document.execCommand('Copy')
|
||||
const result = document.execCommand(`Copy`)
|
||||
if (result) {
|
||||
this.$message.success(this.$t('复制成功'))
|
||||
this.$message.success(this.$t(`复制成功`))
|
||||
}
|
||||
document.body.removeChild(textarea)
|
||||
return
|
||||
}
|
||||
if (command === 'view') {
|
||||
if (command === `view`) {
|
||||
await this.vue.onViewClick(this.current.row)
|
||||
return
|
||||
}
|
||||
if (command === 'export') {
|
||||
if (command === `export`) {
|
||||
await this.exportData()
|
||||
return
|
||||
}
|
||||
if (command === 'add') {
|
||||
if (command === `add`) {
|
||||
await this.vue.onAddClick()
|
||||
return
|
||||
}
|
||||
if (command === 'edit') {
|
||||
if (command === `edit`) {
|
||||
await this.vue.onEditClick(this.current.row)
|
||||
return
|
||||
}
|
||||
if (command === 'del') {
|
||||
if (command === `del`) {
|
||||
await this.vue.onDeleteClick(this.current.row)
|
||||
return
|
||||
}
|
||||
const kv = command.split('^|^')
|
||||
if (kv[1].indexOf('order-') === 0) {
|
||||
const kv = command.split(`^|^`)
|
||||
if (kv[1].indexOf(`order-`) === 0) {
|
||||
this.vue.query.prop = kv[0]
|
||||
this.vue.query.order = kv[1].substring(6)
|
||||
await this.upData()
|
||||
} else {
|
||||
this.$refs.fieldFilterDialog.open({ field: kv[0], operator: kv[1], value: kv[2] }, (data) => {
|
||||
const value = data.value?.split('\n') ?? ['']
|
||||
const value = data.value?.split(`\n`) ?? [``]
|
||||
this.vue.query.dynamicFilter.filters.push({
|
||||
field: data.field,
|
||||
operator: data.operator,
|
||||
@ -445,8 +444,8 @@ export default {
|
||||
if (ids.length > 0) {
|
||||
reqData.dynamicFilter = {
|
||||
filters: [reqData.dynamicFilter],
|
||||
field: 'id',
|
||||
operator: 'Any',
|
||||
field: `id`,
|
||||
operator: `Any`,
|
||||
value: ids,
|
||||
}
|
||||
}
|
||||
@ -473,7 +472,7 @@ export default {
|
||||
} catch (error) {
|
||||
this._clearData()
|
||||
this.loading = false
|
||||
this.emptyText = '数据格式错误'
|
||||
this.emptyText = `数据格式错误`
|
||||
return false
|
||||
}
|
||||
if (response.code !== config.successCode) {
|
||||
@ -481,7 +480,7 @@ export default {
|
||||
this.loading = false
|
||||
this.emptyText = response.msg
|
||||
} else {
|
||||
this.emptyText = '暂无数据'
|
||||
this.emptyText = `暂无数据`
|
||||
if (this.hidePagination) {
|
||||
this.tableData = response.data || []
|
||||
} else {
|
||||
@ -495,7 +494,7 @@ export default {
|
||||
return res
|
||||
})()
|
||||
|
||||
this.$emit('dataChange', ret, this.tableData)
|
||||
this.$emit(`dataChange`, ret, this.tableData)
|
||||
},
|
||||
//清空数据
|
||||
_clearData() {
|
||||
@ -519,8 +518,8 @@ export default {
|
||||
async exportData() {
|
||||
this.loading = true
|
||||
try {
|
||||
await this.exportApi.post(this.getQueryParams(), { responseType: 'blob' })
|
||||
this.$message.success(this.$t('数据已导出(上限 {n} 条)', { n: 50000 }))
|
||||
await this.exportApi.post(this.getQueryParams(), { responseType: `blob` })
|
||||
this.$message.success(this.$t(`数据已导出(上限 {n} 条)`, { n: 50000 }))
|
||||
} catch {}
|
||||
this.loading = false
|
||||
},
|
||||
@ -551,10 +550,10 @@ export default {
|
||||
try {
|
||||
await config.columnSettingSave(this.tableName, userColumn)
|
||||
} catch (error) {
|
||||
this.$message.error('保存失败')
|
||||
this.$message.error(`保存失败`)
|
||||
this.$refs.columnSetting.isSave = false
|
||||
}
|
||||
this.$message.success('保存成功')
|
||||
this.$message.success(`保存成功`)
|
||||
this.$refs.columnSetting.isSave = false
|
||||
},
|
||||
//自定义列重置
|
||||
@ -564,7 +563,7 @@ export default {
|
||||
this.userColumn = await config.columnSettingReset(this.tableName, this.column)
|
||||
this.$refs.columnSetting.userColumn = JSON.parse(JSON.stringify(this.userColumn || []))
|
||||
} catch (error) {
|
||||
this.$message.error('重置失败')
|
||||
this.$message.error(`重置失败`)
|
||||
this.$refs.columnSetting.isSave = false
|
||||
}
|
||||
this.$refs.columnSetting.isSave = false
|
||||
@ -597,7 +596,7 @@ export default {
|
||||
return this.filterMethod(filters)
|
||||
}
|
||||
Object.keys(filters).forEach((key) => {
|
||||
filters[key] = filters[key].join(',')
|
||||
filters[key] = filters[key].join(`,`)
|
||||
})
|
||||
this.upData(filters)
|
||||
},
|
||||
@ -607,14 +606,14 @@ export default {
|
||||
const sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if (index === 0) {
|
||||
sums[index] = '合计'
|
||||
sums[index] = `合计`
|
||||
return
|
||||
}
|
||||
const values = this.summary[column.property]
|
||||
if (values) {
|
||||
sums[index] = values
|
||||
} else {
|
||||
sums[index] = ''
|
||||
sums[index] = ``
|
||||
}
|
||||
})
|
||||
return sums
|
||||
|
@ -220,7 +220,8 @@
|
||||
import { defineAsyncComponent } from 'vue'
|
||||
const sideM = defineAsyncComponent(() => import('./components/side-m'))
|
||||
const topbar = defineAsyncComponent(() => import('./components/topbar'))
|
||||
const tags = defineAsyncComponent(() => import('./components/tags'))
|
||||
// 这里直接import避免闪烁
|
||||
import tags from '@/layout/components/tags.vue'
|
||||
const navMenu = defineAsyncComponent(() => import('./components/nav-menu'))
|
||||
const userBar = defineAsyncComponent(() => import('./components/user-bar'))
|
||||
const iframeView = defineAsyncComponent(() => import('./components/iframe-view'))
|
||||
|
@ -576,7 +576,6 @@ textarea {
|
||||
.el-table-column-avatar {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
|
||||
|
@ -91,18 +91,4 @@
|
||||
entity-name="sys_codetemplate" />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
computed: {},
|
||||
created() {},
|
||||
data() {},
|
||||
inject: [`reload`],
|
||||
methods: {},
|
||||
mounted() {},
|
||||
props: [`keywords`],
|
||||
watch: {},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped />
|
Reference in New Issue
Block a user