This commit is contained in:
tk
2025-07-10 14:56:40 +08:00
committed by nsnail
parent 4a5a7b96fc
commit c15abdc5bb
23 changed files with 353 additions and 241 deletions

View File

@ -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

View File

@ -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>

View File

@ -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);
}
}

View File

@ -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; }
}

View File

@ -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;
}
}

View File

@ -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"/>

View File

@ -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);
}

View File

@ -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)

View File

@ -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());
}
}

View File

@ -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) {

View File

@ -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>

View File

@ -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>

View File

@ -76,4 +76,4 @@ export { default as 'device-log' } from './device-log'
export { default as 'nick-name' } from './nick-name'
export { default as telegram } from './telegram'
export { default as country } from './country'
export {default as template} from './template.vue'
export { default as template } from './template.vue'

View File

@ -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>

View File

@ -1,17 +1,19 @@
<template>
<el-table-column v-bind="$attrs">
<template #default="{ row }">
<div
:style="{ display: $TOOL.getNestedProperty(row, $attrs.prop) ? 'flex' : 'none' }"
@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" />
<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))"
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) {

View File

@ -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 }),
},
],
]
}

View File

@ -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,13 +345,18 @@ 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 })
@ -307,11 +364,14 @@ export default {
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: {},

View File

@ -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() {

View File

@ -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>

View File

@ -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

View File

@ -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'))

View File

@ -576,7 +576,6 @@ textarea {
.el-table-column-avatar {
justify-content: center;
align-items: center;
cursor: pointer;
display: flex;
gap: 0.5rem;

View File

@ -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 />