This commit is contained in:
itcsx committed 2026-09-19 20:37:31 +08:00
1 parent 04c69d4941
commit 4f57b65451
15 files changed
+2338 -24

No files matched your search

@@ -27,26 +27,62 @@ public SecurityHelpFunctionRequest(IServiceRequest serviceRequest)
this.serviceRequest = serviceRequest;
}
public SecurityHelpFunctionEntity FindByKey(string key)
public SecurityHelpFunctionData FindByKey(string key)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionEntity>("/SecurityHelpFunction/FindByKey?key=" + key.ToString())).GetAwaiter().GetResult(); }
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionData>("/SecurityHelpFunction/FindByKey?key=" + key.ToString())).GetAwaiter().GetResult(); }
public SecurityHelpFunctionData FindAll()
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionData> ("/SecurityHelpFunction/FindAll")).GetAwaiter().GetResult(); }
public SecurityHelpFunctionEntity Insert(SecurityHelpFunctionEntity data)
public SecurityHelpFunctionResultData FindBySearchCriteria(SecurityHelpFunctionSearchCriteria criteria)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionEntity,SecurityHelpFunctionEntity>("/SecurityHelpFunction/Insert" , data)).GetAwaiter().GetResult(); }
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionSearchCriteria,SecurityHelpFunctionResultData>("/SecurityHelpFunction/FindBySearchCriteria" , criteria)).GetAwaiter().GetResult(); }
public SecurityHelpFunctionEntity Update(SecurityHelpFunctionEntity data)
public SecurityHelpFunctionResultData FindFeatureBySearchCriteria(SecurityHelpFunctionSearchCriteria criteria)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionEntity,SecurityHelpFunctionEntity>("/SecurityHelpFunction/Update" , data)).GetAwaiter().GetResult(); }
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionSearchCriteria,SecurityHelpFunctionResultData>("/SecurityHelpFunction/FindFeatureBySearchCriteria" , criteria)).GetAwaiter().GetResult(); }
public SecurityHelpFunctionData Insert(SecurityHelpFunctionData data)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionData,SecurityHelpFunctionData>("/SecurityHelpFunction/Insert" , data)).GetAwaiter().GetResult(); }
public SecurityHelpFunctionData Update(SecurityHelpFunctionData data)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionData,SecurityHelpFunctionData>("/SecurityHelpFunction/Update" , data)).GetAwaiter().GetResult(); }
public void Delete(string key)
{
Task.Run(() => serviceRequest.PostAsync("/SecurityHelpFunction/Delete?key=" + key.ToString())).GetAwaiter().GetResult(); }
public SecurityHelpFunctionData FindByFunctionName(string functionName)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionData>("/SecurityHelpFunction/FindByFunctionName?functionName=" + functionName.ToString())).GetAwaiter().GetResult(); }
public SecurityHelpFunctionData SaveFunction(SecurityHelpFunctionData data)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFunctionData,SecurityHelpFunctionData>("/SecurityHelpFunction/SaveFunction" , data)).GetAwaiter().GetResult(); }
public void DeleteByFunctionName(string functionName)
{
Task.Run(() => serviceRequest.PostAsync("/SecurityHelpFunction/DeleteByFunctionName?functionName=" + functionName.ToString())).GetAwaiter().GetResult(); }
public SecurityHelpFeatureData FindByGroupCode(string groupCode)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFeatureData>("/SecurityHelpFunction/FindByGroupCode?groupCode=" + groupCode.ToString())).GetAwaiter().GetResult(); }
public SecurityHelpFeatureData SaveFeatureGroup(SecurityHelpFeatureData data)
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityHelpFeatureData,SecurityHelpFeatureData>("/SecurityHelpFunction/SaveFeatureGroup" , data)).GetAwaiter().GetResult(); }
public void DeleteByGroupCode(string groupCode)
{
Task.Run(() => serviceRequest.PostAsync("/SecurityHelpFunction/DeleteByGroupCode?groupCode=" + groupCode.ToString())).GetAwaiter().GetResult(); }
public SecurityFunctionDisplayData[] FindFunctionNameOptions()
{
return Task.Run(() => serviceRequest.PostAsJsonAsync<SecurityFunctionDisplayData[]> ("/SecurityHelpFunction/FindFunctionNameOptions")).GetAwaiter().GetResult(); }
}
}
@@ -1,11 +1,15 @@
using CloudBuilder.Core.DatabaseAccessor;
using CloudBuilder.Core.DatabaseAccessor.Entity;
using CloudBuilder.Core.Extensions;
using CloudBuilder.Core.Service;
using CloudBuilder.Security.Data;
using CloudBuilder.Security.Entity;
using CloudBuilder.Security.Policy;
using CloudBuilder.Security.SearchCriteria;
using CloudBuilder.Security.Utility;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection;
namespace CloudBuilder.Security.Service
{
public class SecurityHelpFunctionService : ISecurityHelpFunctionService
@@ -20,10 +24,12 @@ namespace CloudBuilder.Security.Service
}
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Find)]
public SecurityHelpFunctionEntity FindByKey(string key)
public SecurityHelpFunctionData FindByKey(string key)
{
return repository.DetachedEntities.Where(x => x.HelpFunctionId == key).FirstOrDefault()!;
return FindByFunctionName(key);
}
[AllowAnonymous]
public SecurityHelpFunctionData FindAll()
{
@@ -42,10 +48,103 @@ namespace CloudBuilder.Security.Service
return data;
}
//[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Find)]
public dynamic FindBySearchCriteria(SecurityHelpFunctionSearchCriteria criteria)
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Find)]
public SecurityHelpFunctionResultData FindBySearchCriteria(SecurityHelpFunctionSearchCriteria criteria)
{
throw new NotImplementedException();
SecurityHelpFunctionResultData data = new SecurityHelpFunctionResultData();
SecurityHelpFunctionEntity[] list = null;
Int64 count = 0;
string sqlWhere = null;
if (criteria.Filter != null && criteria.Filter.Length > 0)
{
sqlWhere = SearchFilter.ComposeWhereClause(criteria.Filter);
}
SecurityHelpFunctionEntity entity = SecurityHelpFunctionEntity.GetInstance(repository.GetDbContext().Database.ProviderName);
string sql = entity.GetSelectTopFrom(criteria.limit, criteria.page, criteria.orderby.StringToDbField(), criteria.orderbytag, sqlWhere);
string err = null;
try
{
list = repository.FromSql(sql).ToArray();
}
catch (System.Exception ex)
{
err = ex.Message;
}
if (list != null && list.Length > 0)
{
sql = entity.GetSelectCountFrom(sqlWhere);
count = Convert.ToInt64(repository.ExecuteScalar(sql));
data.Datas = list;
}
else
{
data.Datas = null;
}
data.Limit = criteria.limit;
data.Page = criteria.page;
data.Total = count;
data.ErrorMessage = err;
return data;
}
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Find)]
public SecurityHelpFunctionResultData FindFeatureBySearchCriteria(SecurityHelpFunctionSearchCriteria criteria)
{
SecurityHelpFunctionResultData data = new SecurityHelpFunctionResultData();
IRepository<SecurityHelpFeatureEntity> repositoryFeature = repository.GetRepository<IRepository<SecurityHelpFeatureEntity>>();
SecurityHelpFeatureEntity[] list = null;
Int64 count = 0;
string sqlWhere = null;
if (criteria.Filter != null && criteria.Filter.Length > 0)
{
sqlWhere = SearchFilter.ComposeWhereClause(criteria.Filter);
}
SecurityHelpFeatureEntity entity = SecurityHelpFeatureEntity.GetInstance(repositoryFeature.GetDbContext().Database.ProviderName);
string sql = entity.GetSelectTopFrom(criteria.limit, criteria.page, criteria.orderby.StringToDbField(), criteria.orderbytag, sqlWhere);
string err = null;
try
{
list = repositoryFeature.FromSql(sql).ToArray();
}
catch (System.Exception ex)
{
err = ex.Message;
}
if (list != null && list.Length > 0)
{
sql = entity.GetSelectCountFrom(sqlWhere);
count = Convert.ToInt64(repositoryFeature.ExecuteScalar(sql));
data.Features = list;
}
else
{
data.Features = null;
}
data.Limit = criteria.limit;
data.Page = criteria.page;
data.Total = count;
data.ErrorMessage = err;
return data;
}
//[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Print)]
@@ -56,14 +155,14 @@ namespace CloudBuilder.Security.Service
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Insert)]
[UnitOfWork]
public SecurityHelpFunctionEntity Insert(SecurityHelpFunctionEntity data)
public SecurityHelpFunctionData Insert(SecurityHelpFunctionData data)
{
throw new NotImplementedException();
}
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Update)]
[UnitOfWork]
public SecurityHelpFunctionEntity Update(SecurityHelpFunctionEntity data)
public SecurityHelpFunctionData Update(SecurityHelpFunctionData data)
{
throw new NotImplementedException();
}
@@ -74,7 +173,293 @@ namespace CloudBuilder.Security.Service
public void Delete(string key)
{
var ent = repository.DetachedEntities.Where(x => x.HelpFunctionId == key).FirstOrDefault()!;
if (ent != null) { repository.DeleteNow(ent); }
if (ent == null) { throw new ValidatedException("记录不存在."); }
repository.DeleteNow(ent);
// 一并清掉关联明细,避免留下孤儿记录
DeleteSteps(ent.HelpFunctionId);
DeleteFunctionFeatures(ent.FunctionName);
}
//==================== 帮助文档维护:主表 + 步骤 + 引用的组 ====================
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Find)]
public SecurityHelpFunctionData FindByFunctionName(string functionName)
{
SecurityHelpFunctionData data = new SecurityHelpFunctionData();
if (string.IsNullOrEmpty(functionName)) return data;
SecurityHelpFunctionEntity function = repository.DetachedEntities.Where(x => x.FunctionName == functionName).FirstOrDefault();
if (function == null) return data;
data.SecurityHelpFunction = function;
data.SecurityHelpSteps = repository.GetRepository<IRepository<SecurityHelpStepEntity>>()
.DetachedEntities.Where(x => x.HelpFunctionId == function.HelpFunctionId)
.OrderBy(x => x.StepNo).ToArray();
data.SecurityHelpFunctionFeatures = repository.GetRepository<IRepository<SecurityHelpFunctionFeatureEntity>>()
.DetachedEntities.Where(x => x.FunctionName == functionName)
.OrderBy(x => x.SortNo).ToArray();
return data;
}
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Update)]
[UnitOfWork]
public SecurityHelpFunctionData SaveFunction(SecurityHelpFunctionData data)
{
if (data == null || data.SecurityHelpFunction == null) throw new ValidatedException("没有要保存的内容.");
SecurityHelpFunctionEntity function = data.SecurityHelpFunction;
ValidateHelper.Validate(service, function, SecurityHelpFunctionEntity.DB_NAME_SECURITY_HELP_FUNCTION);
SecurityHelpFunctionEntity exist = repository.DetachedEntities.Where(x => x.FunctionName == function.FunctionName).FirstOrDefault();
if (exist == null)
{
if (string.IsNullOrEmpty(function.HelpFunctionId))
{
function.HelpFunctionId = NewId(SecurityPrefixPolicy.SECURITY_HELP_FUNCTION_ID);
}
repository.InsertNow(function);
}
else
{
ObjectCopyHelper.Copy(function, exist);
repository.UpdateNow(exist);
function = exist;
}
SaveSteps(function.HelpFunctionId, data.SecurityHelpSteps);
SaveFunctionFeatures(function.FunctionName, data.SecurityHelpFunctionFeatures);
return FindByFunctionName(function.FunctionName);
}
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Delete)]
[UnitOfWork]
public void DeleteByFunctionName(string functionName)
{
SecurityHelpFunctionEntity function = repository.DetachedEntities.Where(x => x.FunctionName == functionName).FirstOrDefault();
if (function == null) throw new ValidatedException("记录不存在.");
repository.DeleteNow(function);
DeleteSteps(function.HelpFunctionId);
DeleteFunctionFeatures(function.FunctionName);
}
//==================== 功能点组维护:组 + 组内功能点 ====================
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Find)]
public SecurityHelpFeatureData FindByGroupCode(string groupCode)
{
SecurityHelpFeatureData data = new SecurityHelpFeatureData();
if (string.IsNullOrEmpty(groupCode)) return data;
IRepository<SecurityHelpFeatureGroupEntity> repositoryGroup = repository.GetRepository<IRepository<SecurityHelpFeatureGroupEntity>>();
SecurityHelpFeatureGroupEntity group = repositoryGroup.DetachedEntities.Where(x => x.GroupCode == groupCode).FirstOrDefault();
if (group == null) return data;
data.SecurityHelpFeatureGroup = group;
data.SecurityHelpFeatures = repository.GetRepository<IRepository<SecurityHelpFeatureEntity>>()
.DetachedEntities.Where(x => x.HelpFeatureGroupId == group.HelpFeatureGroupId)
.OrderBy(x => x.SortNo).ToArray();
return data;
}
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Update)]
[UnitOfWork]
public SecurityHelpFeatureData SaveFeatureGroup(SecurityHelpFeatureData data)
{
if (data == null || data.SecurityHelpFeatureGroup == null) throw new ValidatedException("没有要保存的内容.");
IRepository<SecurityHelpFeatureGroupEntity> repositoryGroup = repository.GetRepository<IRepository<SecurityHelpFeatureGroupEntity>>();
SecurityHelpFeatureGroupEntity group = data.SecurityHelpFeatureGroup;
SecurityHelpFeatureGroupEntity exist = repositoryGroup.DetachedEntities.Where(x => x.GroupCode == group.GroupCode).FirstOrDefault();
if (exist == null)
{
if (string.IsNullOrEmpty(group.HelpFeatureGroupId))
{
group.HelpFeatureGroupId = NewId(SecurityPrefixPolicy.SECURITY_HELP_FEATURE_GROUP_ID);
}
repositoryGroup.InsertNow(group);
}
else
{
ObjectCopyHelper.Copy(group, exist);
repositoryGroup.UpdateNow(exist);
group = exist;
}
SaveFeatures(group.HelpFeatureGroupId, data.SecurityHelpFeatures);
return FindByGroupCode(group.GroupCode);
}
[PermissionOperate(name: SecurityPermissionPolicy.SecurityHelpFunction, operate: Operate.Delete)]
[UnitOfWork]
public void DeleteByGroupCode(string groupCode)
{
IRepository<SecurityHelpFeatureGroupEntity> repositoryGroup = repository.GetRepository<IRepository<SecurityHelpFeatureGroupEntity>>();
SecurityHelpFeatureGroupEntity group = repositoryGroup.DetachedEntities.Where(x => x.GroupCode == groupCode).FirstOrDefault();
if (group == null) throw new ValidatedException("记录不存在.");
repositoryGroup.DeleteNow(group);
DeleteFeatures(group.HelpFeatureGroupId);
}
//==================== 下拉选项 ====================
[AllowAnonymous]
public SecurityFunctionDisplayData[] FindFunctionNameOptions()
{
return service.ServiceProvider.GetService<ISecurityFunctionService>()!.FindMenus();
}
//==================== 私有辅助 ====================
/// <summary>
/// 生成主键,走统一的流水号服务
/// </summary>
private string NewId(string subsystemId)
{
ISecurityGeneratorNumberService generatorNumberService = service.ServiceProvider.GetService<ISecurityGeneratorNumberService>()!;
return generatorNumberService.GetNo(
SecurityPrefixPolicy.SECURITY_HELP,
subsystemId,
null,
YesNoPolicy.NO,
SecurityPrefixPolicy.SECURITY_HELP_FORMAT);
}
/// <summary>
/// 步骤整存:先清旧再插新,StepNo 按传入顺序重排
/// </summary>
private void SaveSteps(string helpFunctionId, SecurityHelpStepEntity[]? steps)
{
IRepository<SecurityHelpStepEntity> repositoryStep = repository.GetRepository<IRepository<SecurityHelpStepEntity>>();
SecurityHelpStepEntity[] olds = repositoryStep.Entities.Where(x => x.HelpFunctionId == helpFunctionId).ToArray();
if (olds != null && olds.Length > 0)
{
foreach (SecurityHelpStepEntity old in olds) repositoryStep.DeleteNow(old);
}
if (steps == null || steps.Length == 0) return;
int i = 1;
foreach (SecurityHelpStepEntity step in steps.OrderBy(x => x.StepNo).ToArray())
{
if (string.IsNullOrEmpty(step.HelpStepId))
{
step.HelpStepId = NewId(SecurityPrefixPolicy.SECURITY_HELP_STEP_ID);
}
step.HelpFunctionId = helpFunctionId;
step.StepNo = i++;
repositoryStep.InsertNow(step);
}
}
/// <summary>
/// 组合关系整存:先清旧再插新,SortNo 按传入顺序重排
/// </summary>
private void SaveFunctionFeatures(string functionName, SecurityHelpFunctionFeatureEntity[]? functionFeatures)
{
IRepository<SecurityHelpFunctionFeatureEntity> repositoryFunctionFeature = repository.GetRepository<IRepository<SecurityHelpFunctionFeatureEntity>>();
SecurityHelpFunctionFeatureEntity[] olds = repositoryFunctionFeature.Entities.Where(x => x.FunctionName == functionName).ToArray();
if (olds != null && olds.Length > 0)
{
foreach (SecurityHelpFunctionFeatureEntity old in olds) repositoryFunctionFeature.DeleteNow(old);
}
if (functionFeatures == null || functionFeatures.Length == 0) return;
int i = 1;
foreach (SecurityHelpFunctionFeatureEntity functionFeature in functionFeatures.OrderBy(x => x.SortNo).ToArray())
{
if (string.IsNullOrEmpty(functionFeature.HelpFunctionFeatureId))
{
functionFeature.HelpFunctionFeatureId = NewId(SecurityPrefixPolicy.SECURITY_HELP_FUNCTION_FEATURE_ID);
}
functionFeature.FunctionName = functionName;
functionFeature.SortNo = i++;
repositoryFunctionFeature.InsertNow(functionFeature);
}
}
/// <summary>
/// 组内功能点整存:先清旧再插新,SortNo 按传入顺序重排
/// </summary>
private void SaveFeatures(string helpFeatureGroupId, SecurityHelpFeatureEntity[]? features)
{
IRepository<SecurityHelpFeatureEntity> repositoryFeature = repository.GetRepository<IRepository<SecurityHelpFeatureEntity>>();
SecurityHelpFeatureEntity[] olds = repositoryFeature.Entities.Where(x => x.HelpFeatureGroupId == helpFeatureGroupId).ToArray();
if (olds != null && olds.Length > 0)
{
foreach (SecurityHelpFeatureEntity old in olds) repositoryFeature.DeleteNow(old);
}
if (features == null || features.Length == 0) return;
int i = 1;
foreach (SecurityHelpFeatureEntity feature in features.OrderBy(x => x.SortNo).ToArray())
{
if (string.IsNullOrEmpty(feature.HelpFeatureId))
{
feature.HelpFeatureId = NewId(SecurityPrefixPolicy.SECURITY_HELP_FEATURE_ID);
}
feature.HelpFeatureGroupId = helpFeatureGroupId;
feature.SortNo = i++;
repositoryFeature.InsertNow(feature);
}
}
private void DeleteSteps(string helpFunctionId)
{
IRepository<SecurityHelpStepEntity> repositoryStep = repository.GetRepository<IRepository<SecurityHelpStepEntity>>();
SecurityHelpStepEntity[] olds = repositoryStep.Entities.Where(x => x.HelpFunctionId == helpFunctionId).ToArray();
if (olds == null || olds.Length == 0) return;
foreach (SecurityHelpStepEntity old in olds) repositoryStep.DeleteNow(old);
}
private void DeleteFunctionFeatures(string functionName)
{
IRepository<SecurityHelpFunctionFeatureEntity> repositoryFunctionFeature = repository.GetRepository<IRepository<SecurityHelpFunctionFeatureEntity>>();
SecurityHelpFunctionFeatureEntity[] olds = repositoryFunctionFeature.Entities.Where(x => x.FunctionName == functionName).ToArray();
if (olds == null || olds.Length == 0) return;
foreach (SecurityHelpFunctionFeatureEntity old in olds) repositoryFunctionFeature.DeleteNow(old);
}
private void DeleteFeatures(string helpFeatureGroupId)
{
IRepository<SecurityHelpFeatureEntity> repositoryFeature = repository.GetRepository<IRepository<SecurityHelpFeatureEntity>>();
SecurityHelpFeatureEntity[] olds = repositoryFeature.Entities.Where(x => x.HelpFeatureGroupId == helpFeatureGroupId).ToArray();
if (olds == null || olds.Length == 0) return;
foreach (SecurityHelpFeatureEntity old in olds) repositoryFeature.DeleteNow(old);
}
}
}
@@ -0,0 +1,19 @@
using CloudBuilder.Core.Authorization;
using CloudBuilder.Security.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Security.Data
{
[TypeScript]
public class SecurityHelpFeatureData
{
public SecurityHelpFeatureGroupEntity? SecurityHelpFeatureGroup { get; set; }
public SecurityHelpFeatureEntity[]? SecurityHelpFeatures { get; set; }
}
}
@@ -12,6 +12,8 @@ namespace CloudBuilder.Security.Data
[TypeScript]
public class SecurityHelpFunctionData
{
public SecurityHelpFunctionEntity? SecurityHelpFunction { get; set; }
public SecurityHelpFeatureEntity[]? SecurityHelpFeatures { get; set; }
public SecurityHelpFeatureGroupEntity[]? SecurityHelpFeatureGroups { get; set; }
@@ -0,0 +1,19 @@
using CloudBuilder.Core.Authorization;
using CloudBuilder.Core.Service;
using CloudBuilder.Security.Entity;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CloudBuilder.Security.Data
{
[TypeScript]
public class SecurityHelpFunctionResultData : SearchCriteriaResultData
{
public SecurityHelpFunctionEntity[] Datas { get; set; } = null!;
public SecurityHelpFeatureEntity[] Features { get; set; } = null!;
}
}
@@ -12,5 +12,14 @@ namespace CloudBuilder.Security.Policy
public const string SECURITY_USER_USER_NAME = "USER_NAME";
public const string SECURITY_USER_FORMAT = "000000";
// 帮助文档相关:用于 SecurityGeneratorNumberService 生成各表主键
public const string SECURITY_HELP = "SECURITY_HELP";
public const string SECURITY_HELP_FUNCTION_ID = "HELP_FUNCTION_ID";
public const string SECURITY_HELP_STEP_ID = "HELP_STEP_ID";
public const string SECURITY_HELP_FEATURE_GROUP_ID = "HELP_FEATURE_GROUP_ID";
public const string SECURITY_HELP_FEATURE_ID = "HELP_FEATURE_ID";
public const string SECURITY_HELP_FUNCTION_FEATURE_ID = "HELP_FUNCTION_FEATURE_ID";
public const string SECURITY_HELP_FORMAT = "000000";
}
}
@@ -7,8 +7,24 @@ using CloudBuilder.Security.SearchCriteria;
namespace CloudBuilder.Security.Service
{
public interface ISecurityHelpFunctionService :IServiceBase<SecurityHelpFunctionEntity, string ,SecurityHelpFunctionSearchCriteria>
public interface ISecurityHelpFunctionService :IServiceBase<SecurityHelpFunctionData, string ,SecurityHelpFunctionSearchCriteria>
{
SecurityHelpFunctionData FindAll();
SecurityHelpFunctionResultData FindBySearchCriteria(SecurityHelpFunctionSearchCriteria tc);
SecurityHelpFunctionResultData FindFeatureBySearchCriteria(SecurityHelpFunctionSearchCriteria tc);
// 帮助文档维护:主表 + 步骤 + 引用的功能点组
SecurityHelpFunctionData FindByFunctionName(string functionName);
SecurityHelpFunctionData SaveFunction(SecurityHelpFunctionData data);
void DeleteByFunctionName(string functionName);
// 功能点组维护:组 + 组内功能点
SecurityHelpFeatureData FindByGroupCode(string groupCode);
SecurityHelpFeatureData SaveFeatureGroup(SecurityHelpFeatureData data);
void DeleteByGroupCode(string groupCode);
// 新增帮助文档时,下拉选择「给哪个功能写帮助」
SecurityFunctionDisplayData[] FindFunctionNameOptions();
}
}
@@ -71,12 +71,52 @@ this.securityHelpFunctionService= securityHelpFunctionService;
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Find")]
[HttpPost]
public dynamic FindBySearchCriteria(SecurityHelpFunctionSearchCriteria criteria)
{
try
{
var result = securityHelpFunctionService.FindBySearchCriteria(criteria);
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Find")]
[HttpPost]
public dynamic FindFeatureBySearchCriteria(SecurityHelpFunctionSearchCriteria criteria)
{
try
{
var result = securityHelpFunctionService.FindFeatureBySearchCriteria(criteria);
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Insert")]
[HttpPost]
[UnitOfWork]
public dynamic Insert(SecurityHelpFunctionEntity data)
public dynamic Insert(SecurityHelpFunctionData data)
{
try
{
@@ -97,7 +137,7 @@ this.securityHelpFunctionService= securityHelpFunctionService;
[PermissionOperate(name: "SecurityHelpFunction", operate: "Update")]
[HttpPost]
[UnitOfWork]
public dynamic Update(SecurityHelpFunctionEntity data)
public dynamic Update(SecurityHelpFunctionData data)
{
try
{
@@ -135,6 +175,148 @@ this.securityHelpFunctionService= securityHelpFunctionService;
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Find")]
[HttpPost]
public dynamic FindByFunctionName(string functionName)
{
try
{
var result = securityHelpFunctionService.FindByFunctionName(functionName);
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Update")]
[HttpPost]
[UnitOfWork]
public dynamic SaveFunction(SecurityHelpFunctionData data)
{
try
{
var result = securityHelpFunctionService.SaveFunction(data);
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Delete")]
[HttpPost]
[UnitOfWork]
public dynamic DeleteByFunctionName(string functionName){
try
{
securityHelpFunctionService.DeleteByFunctionName(functionName);
return string.Empty;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Find")]
[HttpPost]
public dynamic FindByGroupCode(string groupCode)
{
try
{
var result = securityHelpFunctionService.FindByGroupCode(groupCode);
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Update")]
[HttpPost]
[UnitOfWork]
public dynamic SaveFeatureGroup(SecurityHelpFeatureData data)
{
try
{
var result = securityHelpFunctionService.SaveFeatureGroup(data);
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[PermissionOperate(name: "SecurityHelpFunction", operate: "Delete")]
[HttpPost]
[UnitOfWork]
public dynamic DeleteByGroupCode(string groupCode){
try
{
securityHelpFunctionService.DeleteByGroupCode(groupCode);
return string.Empty;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
[AllowAnonymous]
[HttpPost]
public dynamic FindFunctionNameOptions()
{
try
{
var result = securityHelpFunctionService.FindFunctionNameOptions();
return result;
}
catch (ValidatedException vex)
{
return vex;
}
catch (Exception ex)
{
return new ServiceErrorException(ex);
}
}
}
}
@@ -14,6 +14,7 @@ export class OperationToEnquiryModelOption {
public isModalPhoto?: boolean = false;
public isModalVideo?: boolean = false;
public isModalAudio?: boolean = false;
public isModalHelpFunction?: boolean = false; //帮助功能查询
public initBindOptionsUser?: BindOption[] = []; //初始化查询条件的值
public initBindOptionsGroup?: BindOption[] = [];
public initBindOptionsActor?: BindOption[] = [];
@@ -23,6 +24,7 @@ export class OperationToEnquiryModelOption {
public initBindOptionsFunction?: BindOption[] = [];
public initBindOptionsTree?: BindOption[] = [];
public initBindOptionsPerson?: BindOption[] = [];
public initBindOptionsHelpFunction?: BindOption[] = [];
public selectionTypeGroup?: string = "radio"; //记录单选还是多选
public selectionTypeUser?: string = "radio";
public selectionTypeActor?: string = "radio";
@@ -32,6 +34,7 @@ export class OperationToEnquiryModelOption {
public selectionTypeFunction?: string = "radio";
public selectionTypeTree?: string = "radio";
public selectionTypePerson?: string = "radio";
public selectionTypeHelpFunction?: string = "radio";
}
//子组件调用父组件的方法==================
@@ -1,12 +1,15 @@
import { request } from "@/utils/request";
import type { SecurityHelpFunctionEntity } from "@/data/auto/securityHelpFunctionEntity";
import type { SecurityHelpFunctionData } from "@/data/auto/securityHelpFunctionData";
import type { SecurityHelpFunctionSearchCriteria } from "@/data/auto/securityHelpFunctionSearchCriteria";
import type { SecurityHelpFunctionResultData } from "@/data/auto/securityHelpFunctionResultData";
import type { SecurityHelpFeatureData } from "@/data/auto/securityHelpFeatureData";
import type { SecurityFunctionDisplayData } from "@/data/auto/securityFunctionDisplayData";
class SecurityHelpFunctionService{
async FindByKey(data: string) {
return request<SecurityHelpFunctionEntity>(
return request<SecurityHelpFunctionData>(
{
url: "/SecurityHelpFunction/FindByKey?key="+data,
method: "post",
@@ -24,8 +27,28 @@ class SecurityHelpFunctionService{
)
};
async Insert(data: SecurityHelpFunctionEntity) {
return request<SecurityHelpFunctionEntity>(
async FindBySearchCriteria(data: SecurityHelpFunctionSearchCriteria) {
return request<SecurityHelpFunctionResultData>(
{
url: "/SecurityHelpFunction/FindBySearchCriteria",
method: "post",
data,
}
)
};
async FindFeatureBySearchCriteria(data: SecurityHelpFunctionSearchCriteria) {
return request<SecurityHelpFunctionResultData>(
{
url: "/SecurityHelpFunction/FindFeatureBySearchCriteria",
method: "post",
data,
}
)
};
async Insert(data: SecurityHelpFunctionData) {
return request<SecurityHelpFunctionData>(
{
url: "/SecurityHelpFunction/Insert",
method: "post",
@@ -34,8 +57,8 @@ class SecurityHelpFunctionService{
)
};
async Update(data: SecurityHelpFunctionEntity) {
return request<SecurityHelpFunctionEntity>(
async Update(data: SecurityHelpFunctionData) {
return request<SecurityHelpFunctionData>(
{
url: "/SecurityHelpFunction/Update",
method: "post",
@@ -54,6 +77,75 @@ class SecurityHelpFunctionService{
)
};
async FindByFunctionName(data: string) {
return request<SecurityHelpFunctionData>(
{
url: "/SecurityHelpFunction/FindByFunctionName?functionName="+data,
method: "post",
data,
}
)
};
async SaveFunction(data: SecurityHelpFunctionData) {
return request<SecurityHelpFunctionData>(
{
url: "/SecurityHelpFunction/SaveFunction",
method: "post",
data,
}
)
};
async DeleteByFunctionName(data: string) {
return request<any>(
{
url: "/SecurityHelpFunction/DeleteByFunctionName?functionName="+data,
method: "post",
data,
}
)
};
async FindByGroupCode(data: string) {
return request<SecurityHelpFeatureData>(
{
url: "/SecurityHelpFunction/FindByGroupCode?groupCode="+data,
method: "post",
data,
}
)
};
async SaveFeatureGroup(data: SecurityHelpFeatureData) {
return request<SecurityHelpFeatureData>(
{
url: "/SecurityHelpFunction/SaveFeatureGroup",
method: "post",
data,
}
)
};
async DeleteByGroupCode(data: string) {
return request<any>(
{
url: "/SecurityHelpFunction/DeleteByGroupCode?groupCode="+data,
method: "post",
data,
}
)
};
async FindFunctionNameOptions() {
return request<SecurityFunctionDisplayData[]>(
{
url: "/SecurityHelpFunction/FindFunctionNameOptions",
method: "post",
}
)
};
}
const securityHelpFunctionService= new SecurityHelpFunctionService();
@@ -53,7 +53,7 @@
<div class="ft-field">
<div class="ft-label">功能名称<span class="ft-required">*</span></div>
<a-input size="large" required placeholder="请输入功能名称"
:readonly="!isEdit || !!data.SecurityFunction.FunctionName"
:readonly="!isEdit || !!data.SecurityFunction.CreatedBy"
v-model:value="data.SecurityFunction.FunctionName" />
</div>
<div class="ft-field">
@@ -0,0 +1,323 @@
<template>
<search-card @on-submit="onSubmit" @on-reset="onReset" @use-pagination="onPagination" :is-modal="isModal"
:loading="loading" :pagination="result" :rowSelectionType="selectionType"
:card-title-keys="[SecurityHelpFeatureEntity.NAME]" @on-row-dblclick="onRowDblclick"
@on-select-change="onSelectChange">
<template #control>
<a-button type="primary" v-if="canCreate && !isModal" :icon="h(FileAddOutlined)"
@click.prevent="onCreate">新增</a-button>
</template>
<template #bodyCell="{ column, record }">
<!-- 所属组存的是组主键,这里换成组编码显示,便于和维护页对应 -->
<template v-if="column.dataIndex === SecurityHelpFeatureEntity.HELP_FEATURE_GROUP_ID">
<span>{{ groupCodeOf(record.HelpFeatureGroupId) || "-" }}</span>
</template>
</template>
<template #footer>
<div v-if="!isModal" class="page-footer">
<a-row :gutter="24">
<a-col :lg="24" :sm="24">
<a-row :gutter="[16, 8]">
<a-space>
<a-button type="primary" v-if="canView" :icon="h(FileTextOutlined)"
:disabled="!(keys && keys.length == 1)" @click.prevent="onView">查看</a-button>
<a-button type="primary" v-if="canAmend" :icon="h(EditOutlined)"
:disabled="!(keys && keys.length == 1)" @click.prevent="onAmend">修改</a-button>
</a-space>
</a-row>
</a-col>
</a-row>
</div>
</template>
</search-card>
</template>
<script setup lang="ts">
import { reactive, inject, onMounted, ref, unref, provide, h, computed } from "vue";
import { BindOption } from "@/data/custom/bindOption";
import searchCard from "@/components/basic/SearchCard.vue";
import { EditOutlined, FileAddOutlined, FileTextOutlined } from "@ant-design/icons-vue";
import { SecurityHelpFunctionResultData } from "@/data/auto/securityHelpFunctionResultData";
import { SecurityHelpFeatureEntity } from "@/data/auto/securityHelpFeatureEntity";
import { SecurityHelpFeatureGroupEntity } from "@/data/auto/securityHelpFeatureGroupEntity";
import { SearchFilter } from "@/data/auto/searchFilter"
import securityHelpFunctionService from "@/service/security/securityHelpFunctionService";
import { SecurityHelpFunctionSearchCriteria } from "@/data/auto/securityHelpFunctionSearchCriteria";
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
import { PermissionContext, usePermissionContext } from "@/hooks/PermissionContext"
import { useRoute, useRouter } from "vue-router";
import { useBindOption } from "@/hooks/useBindOption";
import { SearchFilterOperatorPolicy } from "@/data/custom/searchFilterOperatorPolicy"
import { SearchFilterTypePolicy } from "@/data/custom/searchFilterTypePolicy"
import { OperationToEnquiryModelOption, operationToEnquiryMethodKey, enquiryMethodKey, searchCriteriaManagerKey } from "@/injections";
import { useOrderedChildren } from "@/hooks/use-ordered-children";
import { SearchCriteriaContext } from "@/injections";
//================inject================
const operationMethod = inject(operationToEnquiryMethodKey, undefined);
const modelOption = operationMethod?.getOperationToEnquiryModelOption() as OperationToEnquiryModelOption;
const isModal = ref(modelOption ? !!modelOption.isModalHelpFunction : false);
const selectionType = modelOption ? modelOption.selectionTypeHelpFunction ?? 'radio' : 'checkbox';
const initBindOptions = ref((modelOption ? modelOption.initBindOptionsHelpFunction : []) as BindOption[]);
//================inject================
const emit = defineEmits(['onRowDblclick']);
const loading = ref<boolean>(false);
const canView = ref<boolean>(false);
const canAmend = ref<boolean>(false);
const canDelete = ref<boolean>(false);
const canCreate = ref<boolean>(false);
const route = useRoute();
const router = useRouter();
const columns = ref([
{
title: '序号',
dataIndex: 'index',
width: '5%',
align: 'center',
fixed: 'left',
sequence: 0,
hidden: 'N',
format: ''
},
{
title: "功能点名称",
dataIndex: SecurityHelpFeatureEntity.NAME,
sorter: true,
width: '20%',
sequence: 1,
hidden: 'N'
},
{
title: "作用说明",
dataIndex: SecurityHelpFeatureEntity.DESCRIPTION,
sorter: true,
width: '35%',
sequence: 2,
hidden: 'N'
},
{
title: "所属组",
dataIndex: SecurityHelpFeatureEntity.HELP_FEATURE_GROUP_ID,
sorter: true,
width: '15%',
sequence: 3,
hidden: 'N'
},
{
title: "图标",
dataIndex: SecurityHelpFeatureEntity.ICON,
sorter: true,
width: '15%',
sequence: 4,
hidden: 'N'
},
]);
const viewOption = {
Value: 'default',
SelectOptions: [{ BindValue: 'default', DisplayName: "默认" }],
Columns: [{ BindValue: 'default', BindColumn: columns }]
};
const bindOptions = ref<BindOption[]>([]);
const datas = ref<SecurityHelpFeatureEntity[]>([]);
const pagingParams = ref<PagingParams>({ pageSize: 20, page: 1 });
const result = ref<SearchCriteriaResultData>(new SearchCriteriaResultData());
const data = ref<SecurityHelpFunctionResultData>(new SecurityHelpFunctionResultData());
const isSearched = ref<boolean>(false);
const currentSearchCriteria = ref<SecurityHelpFunctionSearchCriteria>(new SecurityHelpFunctionSearchCriteria());
const keys = ref<SecurityHelpFeatureEntity[]>([]);
/** 功能点组,用来把「组主键」翻译成「组编码」 */
const groups = ref<SecurityHelpFeatureGroupEntity[]>([]);
const groupCodeOf = ((helpFeatureGroupId?: string) => {
if (!helpFeatureGroupId) return "";
const g = groups.value.find(x => x.HelpFeatureGroupId == helpFeatureGroupId);
return g ? g.GroupCode : "";
})
const {
children: items,
addChild: addItem,
removeChild: removeItem,
} = useOrderedChildren<SearchCriteriaContext>();
//================provide================
provide(enquiryMethodKey, {
getLayoutSetting: () => { return { controlLeft: 16, controlRight: 8, space: true, soltLeft: 24, soltRight: 24 }; },
getBindOptions: () => { return unref(bindOptions); },
getViewOption: () => { return unref(viewOption); },
getDatas: () => { return datas; },
});
provide(
searchCriteriaManagerKey,
reactive({
items,
addItem,
removeItem,
})
);
//================provide================
defineExpose({
keys,
initBindOptions,
})
onMounted(() => {
let pContext: PermissionContext = usePermissionContext();
canView.value = pContext.canView(route.meta.permission as string);
canAmend.value = pContext.canAmend(route.meta.permission as string);
canDelete.value = pContext.canDelete(route.meta.permission as string);
canCreate.value = pContext.canCreate(route.meta.permission as string);
initBindOption();
loadGroups();
onSearch();
})
const onSelectChange = ((selected: SecurityHelpFeatureEntity[]) => {
keys.value = selected;
})
/** 点一条功能点,进它所属组的维护页 */
const gotoGroup = ((record: SecurityHelpFeatureEntity) => {
const groupCode = groupCodeOf(record.HelpFeatureGroupId);
router.push({ name: "SecurityHelpFeatureOperation", params: { id: groupCode, mode: 'view' } });
})
const onRowDblclick = ((e: any, record: SecurityHelpFeatureEntity) => {
if (isModal.value) {
emit('onRowDblclick', record);
}
else {
gotoGroup(record);
}
})
const onReset = (() => {
keys.value = [];
result.value.Total = 0;
})
const onView = (() => {
if (keys.value.length == 0) return;
gotoGroup(keys.value[0]);
})
const onCreate = (() => {
router.push({ name: "SecurityHelpFeatureOperation", params: { id: '', mode: 'create' } });
})
const onAmend = (() => {
if (keys.value.length == 0) return;
const groupCode = groupCodeOf(keys.value[0].HelpFeatureGroupId);
router.push({ name: "SecurityHelpFeatureOperation", params: { id: groupCode, mode: 'amend' } });
})
const setBindOptionValue = ((bindValue: string, value: string, value1: string, values: string[], disabled: boolean) => {
let bindOption: BindOption = bindOptions.value.filter(x => x.BindValue == bindValue)[0];
if (!bindOption) return;
bindOption.Value = value;
bindOption.Value1 = value1;
bindOption.Values = values;
bindOption.disabled = disabled;
})
//初始化查询条件
const initBindOption = (async () => {
let ubo = useBindOption();
let bindOption: BindOption = ubo.getBindOption(SecurityHelpFeatureEntity.DB_NAME, '功能点名称', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOptions.value.push(bindOption);
bindOption = ubo.getBindOption(SecurityHelpFeatureEntity.DB_DESCRIPTION, '作用说明', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOption.lg = 10;
bindOptions.value.push(bindOption);
bindOption = ubo.getBindOption(SecurityHelpFeatureEntity.DB_ICON, '图标', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOption.lg = 10;
bindOptions.value.push(bindOption);
initBindOptions.value.forEach(element => {
setBindOptionValue(element.BindValue, element.Value, element.Value1, element.Values, element.disabled);
});
})
/** 组是翻译「组主键 → 组编码」用的,数据来自整表查询 */
const loadGroups = (async () => {
try {
const all = await securityHelpFunctionService.FindAll();
groups.value = all.SecurityHelpFeatureGroups ?? [];
} catch (error) {
console.log(error);
}
})
//table from-----------------------------------------------------------------------------------------------------------------------------------
result.value.Page = pagingParams.value.page ?? 1;
result.value.Limit = pagingParams.value.pageSize;
const onSubmit = (async (filters: SearchFilter[]) => {
let c: SecurityHelpFunctionSearchCriteria = new SecurityHelpFunctionSearchCriteria();
isSearched.value = true;
c.Filter = filters;
c.limit = pagingParams.value.pageSize;
c.page = pagingParams.value.page ?? 1;
c.GridColumns = [];
currentSearchCriteria.value = c;
await findBySearchCriteria(c);
})
const onSearch = (async () => {
onSubmit(items.value[0].getFilter());
})
const findBySearchCriteria = (async (c: SecurityHelpFunctionSearchCriteria) => {
try {
data.value = await securityHelpFunctionService.FindFeatureBySearchCriteria(c);
result.value.Total = data.value.Total;
result.value.Page = data.value.Page;
result.value.Limit = data.value.Limit;
datas.value = data.value.Features ?? [];
} catch (error) {
console.log(error);
}
})
const onPagination = (async (params: PagingParams) => {
if (!isSearched.value) return;
let c: SecurityHelpFunctionSearchCriteria = new SecurityHelpFunctionSearchCriteria();
c.Filter = items.value[0].getFilter();
c.limit = params.pageSize;
c.page = params.page ?? 1;
if (params.sortField)
c.orderby = params.sortField!;
if (params.sortOrder)
c.orderbytag = params.sortOrder?.toString().substring(0, 4)!;
c.GridColumns = [];
currentSearchCriteria.value = c;
await findBySearchCriteria(c);
});
const onReload = (async () => {
await findBySearchCriteria(unref(currentSearchCriteria));
})
//table end-----------------------------------------------------------------------------------------------------------------------------------
</script>
@@ -0,0 +1,412 @@
<template>
<a-spin size="large" :spinning="loading">
<div class="page-container">
<div class="page-header">
<a-row :gutter="[16, 8]">
<a-col :lg="8" :sm="24">
<a-row :wrap="false">
<a-col flex="none">
<div style="padding: 0 "><a-input flex="none" readonly value="组编码"
class="site-input-split" style="width: 160px;border-radius:0; border-top-left-radius: 5pt 5pt;
border-bottom-left-radius: 5pt 5pt;
pointer-events: none" /></div>
</a-col>
<a-col flex="auto">
<a-input-search :disabled="isEdit" v-model:value="groupCode" flex="auto"
@search="onModalSearch" />
</a-col>
</a-row>
</a-col>
<a-col :lg="12" :sm="24">
<a-space>
<a-button type="primary" :disabled="isEdit" :icon="h(FileSearchOutlined)"
@click.prevent="onSearch">查询</a-button>
<a-button type="primary" :disabled="isEdit" :icon="h(ClearOutlined)"
@click.prevent="onReset">重置</a-button>
<a-button type="primary" :disabled="isEdit" :icon="h(FileAddOutlined)"
@click.prevent="onCreate">新增</a-button>
</a-space>
</a-col>
</a-row>
</div>
<div class="page-main ft-page" :class="{ 'ft-readonly': !isEdit }" :style="{ height: resultH }">
<!-- 顶部信息卡 -->
<div class="ft-hero">
<div class="ft-hero-icon">
<AppstoreOutlined />
</div>
<div class="ft-hero-main">
<div class="ft-hero-name">
{{ data.SecurityHelpFeatureGroup.GroupName || data.SecurityHelpFeatureGroup.GroupCode ||
"未填写功能点组" }}
</div>
<div class="ft-hero-meta">
<span class="ft-chip">组编码 {{ data.SecurityHelpFeatureGroup.GroupCode || "-" }}</span>
<span class="ft-chip">功能点 {{ (data.SecurityHelpFeatures || []).length }} 条</span>
</div>
</div>
</div>
<!-- 组信息 -->
<div class="ft-card">
<div class="ft-card-title">功能点组</div>
<div class="ft-grid">
<div class="ft-field">
<div class="ft-label">组编码<span class="ft-required">*</span></div>
<a-input size="large" placeholder="如 TOOLBAR、CRUD_HEAD"
:readonly="!isEdit || !!data.SecurityHelpFeatureGroup.CreatedBy"
v-model:value="data.SecurityHelpFeatureGroup.GroupCode" />
</div>
<div class="ft-field">
<div class="ft-label">组名称</div>
<a-input size="large" placeholder="便于后台维护时识别" :readonly="!isEdit"
v-model:value="data.SecurityHelpFeatureGroup.GroupName" />
</div>
</div>
</div>
<!-- 组内功能点 -->
<div class="ft-card">
<div class="ft-card-title">组内功能点</div>
<div class="ft-list">
<div v-for="(record, index) in data.SecurityHelpFeatures" :key="index" class="ft-item">
<div class="ft-item-head">
<span class="ft-item-index">{{ index + 1 }}</span>
<div class="ft-item-title">
<span class="ft-item-name">
{{ record.Name || "未填写功能点名称" }}
</span>
</div>
<div v-if="isEdit" class="ft-item-actions">
<UpSquareTwoTone class="ft-action" @click="upFeature(index)" />
<DownSquareTwoTone class="ft-action" @click="downFeature(index)" />
<DeleteOutlined class="ft-action ft-action-delete"
@click="deleteFeature(index)" />
</div>
</div>
<div class="ft-item-body">
<div class="ft-item-field">
<span class="ft-item-label">名称</span>
<a-input v-if="isEdit" size="large" v-model:value="record.Name"
placeholder="一般就是按钮上的字" />
<span v-else class="ft-item-text">{{ record.Name || "-" }}</span>
</div>
<div class="ft-item-field">
<span class="ft-item-label">说明</span>
<a-input v-if="isEdit" size="large" v-model:value="record.Description"
placeholder="这个按钮 / 图标的作用" />
<span v-else class="ft-item-text">{{ record.Description || "-" }}</span>
</div>
<div class="ft-item-field">
<span class="ft-item-label">图标</span>
<a-input v-if="isEdit" size="large" v-model:value="record.Icon"
placeholder="如 DeleteOutlined" />
<span v-else class="ft-item-text">{{ record.Icon || "-" }}</span>
</div>
<div class="ft-item-field">
<span class="ft-item-label">颜色</span>
<a-input v-if="isEdit" size="large" v-model:value="record.Color"
placeholder="页面上的真实颜色,如 #faad14 / red" />
<span v-else class="ft-item-text">{{ record.Color || "-" }}</span>
</div>
</div>
</div>
<div v-if="!data.SecurityHelpFeatures || !data.SecurityHelpFeatures.length" class="ft-empty">
暂无功能点
</div>
<a-button class="ft-add-btn" type="dashed" block v-if="isEdit" @click="addFeature">
<PlusSquareTwoTone /> 新增功能点
</a-button>
</div>
</div>
</div>
<div class="page-footer">
<div style="">
<a-row :gutter="24">
<a-col :lg="24" :sm="24">
<div>
<a-row v-if="isEdit" :gutter="[16, 8]">
<a-space>
<a-popconfirm v-if="data.SecurityHelpFeatureGroup.CreatedBy"
title="确定是否取消修改?" ok-text="是" cancel-text="否" @confirm="onCancel">
<a-button type="primary" ghost :icon="h(UndoOutlined)">取消</a-button>
</a-popconfirm>
<a-button v-if="!data.SecurityHelpFeatureGroup.CreatedBy" type="primary" ghost
:icon="h(UndoOutlined)" @click.prevent="onCancel">取消</a-button>
<a-button type="primary" ghost :icon="h(SaveOutlined)"
@click.prevent="onSave">保存</a-button>
</a-space>
</a-row>
<a-row v-else :gutter="[16, 8]">
<a-space>
<a-button type="primary" v-if="canAmend" :icon="h(EditOutlined)"
:disabled="!hasData" @click.prevent="onAmend">修改</a-button>
<a-popconfirm :disabled="!hasData" title="确定是否删除这组功能点?" ok-text="是"
cancel-text="否" @confirm="onDelete">
<a-button type="primary" danger :icon="h(DeleteOutlined)"
:disabled="!hasData" v-if="canDelete">删除</a-button>
</a-popconfirm>
</a-space>
</a-row>
</div>
</a-col>
</a-row>
</div>
</div>
</div>
<dragModal v-model:open="modelOption.isModalHelpFunction" title="功能点查询" @cancel="handleCance"
:fullscreen="width < 640" :mask-closable="false" @ok="handleSelect">
<SecurityHelpFeatureEnquiry ref="enquiry" @on-row-dblclick="handleSelect"></SecurityHelpFeatureEnquiry>
</dragModal>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, unref, onBeforeMount, inject, provide, h, onMounted } from "vue";
import { notification } from "ant-design-vue";
import { useRoute, useRouter } from 'vue-router'
import {
DeleteOutlined, EditOutlined, ClearOutlined, FileSearchOutlined, FileAddOutlined, UndoOutlined,
SaveOutlined, PlusSquareTwoTone, DownSquareTwoTone, UpSquareTwoTone, AppstoreOutlined
} from "@ant-design/icons-vue";
import { PermissionContext, usePermissionContext } from "@/hooks/PermissionContext"
import dragModal from "@/components/basic/DragModal.vue"
import SecurityHelpFeatureEnquiry from "./SecurityHelpFeatureEnquiry.vue";
import { SecurityHelpFeatureData } from "@/data/auto/securityHelpFeatureData";
import { SecurityHelpFeatureEntity } from "@/data/auto/securityHelpFeatureEntity";
import { SecurityHelpFeatureGroupEntity } from "@/data/auto/securityHelpFeatureGroupEntity";
import securityHelpFunctionService from "@/service/security/securityHelpFunctionService";
import { useWindowSize } from '@vueuse/core'
import { OperationToEnquiryModelOption, operationToEnquiryMethodKey } from "@/injections"
const loading = ref<boolean>(false);
const { width, height } = useWindowSize()
const route = useRoute();
const router = useRouter();
const groupCode = ref<string>(route.params.id as string)
const mode = ref<string>(route.params.mode as string)
const isEdit = ref<boolean>(false);
const canAmend = ref<boolean>(false);
const canDelete = ref<boolean>(false);
const canCreate = ref<boolean>(false);
const data = ref<SecurityHelpFeatureData>(new SecurityHelpFeatureData());
const hasData = ref<boolean>(false);
const resultH = ref('');
const oldGroupCode = ref<string>(groupCode.value);
/** 功能点组,弹窗选中的是功能点,需要它把组主键翻译成组编码 */
const groups = ref<SecurityHelpFeatureGroupEntity[]>([]);
interface HelpFeatureEnquiry {
keys: SecurityHelpFeatureEntity[],
setBindOptionValue(bindValue: string, value: string, value1: string, values: string[]): void,
}
const enquiry = ref<HelpFeatureEnquiry | null>(null)
//================provide================
const modelOption = ref<OperationToEnquiryModelOption>(new OperationToEnquiryModelOption());
modelOption.value.isModalHelpFunction = false;
const getOperationToEnquiryModelOption = () => {
return modelOption.value
}
provide(
operationToEnquiryMethodKey,
{
getOperationToEnquiryModelOption,
}
);
//================provide================
onBeforeMount(async () => {
})
onMounted(async () => {
let pContext: PermissionContext = usePermissionContext();
canAmend.value = pContext.canAmend(route.meta.permission as string);
canDelete.value = pContext.canDelete(route.meta.permission as string);
canCreate.value = pContext.canCreate(route.meta.permission as string);
computResultTableHeight();
try {
const all = await securityHelpFunctionService.FindAll();
groups.value = all.SecurityHelpFeatureGroups ?? [];
} catch (error) {
console.log(error);
}
if (groupCode.value && groupCode.value.trim().length > 0) {
await onSearch();
}
if (mode.value && mode.value == 'create') {
onCreate();
isEdit.value = true;
}
else if (mode.value && mode.value == 'amend') {
isEdit.value = true;
}
})
const computResultTableHeight = () => {
resultH.value = width.value > 640 ? 'calc(100vh - 220px)' : 'calc(100vh - 250px)';
}
const onModalSearch = (() => {
modelOption.value.isModalHelpFunction = true;
})
const onSearch = (async () => {
if (!groupCode.value || groupCode.value.trim().length == 0) {
notification.info({
message: '系统提示:',
description: '请输入组编码',
placement: "topRight",
});
hasData.value = false;
return;
}
data.value = await securityHelpFunctionService.FindByGroupCode(groupCode.value.trim());
if (!data.value.SecurityHelpFeatureGroup || !data.value.SecurityHelpFeatureGroup.GroupCode) {
notification.info({
message: '系统提示:',
description: '记录不存在',
placement: "topRight",
});
data.value = new SecurityHelpFeatureData();
hasData.value = false;
return;
}
if (!data.value.SecurityHelpFeatures) data.value.SecurityHelpFeatures = [];
hasData.value = true;
})
const onReset = (() => {
data.value = new SecurityHelpFeatureData();
data.value.SecurityHelpFeatures = [];
groupCode.value = '';
hasData.value = false;
})
const onCreate = (() => {
onReset();
isEdit.value = true;
})
const onAmend = (() => {
isEdit.value = true;
oldGroupCode.value = unref(data.value.SecurityHelpFeatureGroup.GroupCode);
})
const onSave = (async () => {
try {
loading.value = true;
if (!data.value.SecurityHelpFeatureGroup.GroupCode) {
notification.info({
message: '系统提示:',
description: '请输入组编码',
placement: "topRight",
});
loading.value = false;
return;
}
// 组内顺序按界面顺序重排
(data.value.SecurityHelpFeatures ?? []).forEach((f, i) => f.SortNo = i + 1);
(data.value.SecurityHelpFeatures ?? []).forEach((f) => {
f.HelpFeatureGroupId = data.value.SecurityHelpFeatureGroup.HelpFeatureGroupId;
});
data.value = await securityHelpFunctionService.SaveFeatureGroup(data.value);
groupCode.value = data.value.SecurityHelpFeatureGroup.GroupCode;
loading.value = false;
hasData.value = true;
isEdit.value = false;
} catch (error) {
loading.value = false;
console.log(error);
}
})
const onCancel = (() => {
onReset();
if (oldGroupCode.value && oldGroupCode.value.length > 0) {
groupCode.value = oldGroupCode.value;
onSearch();
}
isEdit.value = false;
})
const onDelete = (async () => {
try {
loading.value = true;
await securityHelpFunctionService.DeleteByGroupCode(data.value.SecurityHelpFeatureGroup.GroupCode);
onReset();
loading.value = false;
} catch (error) {
loading.value = false;
console.log(error);
}
})
//==================== 组内功能点操作 ====================
const addFeature = (() => {
if (!data.value.SecurityHelpFeatures) data.value.SecurityHelpFeatures = [];
let feature: SecurityHelpFeatureEntity = new SecurityHelpFeatureEntity();
feature.SortNo = data.value.SecurityHelpFeatures.length + 1;
data.value.SecurityHelpFeatures.push(feature);
})
const deleteFeature = ((index: number) => {
data.value.SecurityHelpFeatures.splice(index, 1);
})
const swap = ((arr: any[], index: number, offset: number) => {
const target = index + offset;
if (target < 0 || target >= arr.length) return;
const item = arr.splice(index, 1)[0];
arr.splice(target, 0, item);
})
const upFeature = ((index: number) => swap(data.value.SecurityHelpFeatures, index, -1))
const downFeature = ((index: number) => swap(data.value.SecurityHelpFeatures, index, 1))
const handleCance = (async () => {
})
/** 弹窗里选中的是功能点,要换算成它所属组的编码再查询 */
const handleSelect = (async () => {
if (enquiry.value) {
if (enquiry.value.keys.length > 0) {
const record = enquiry.value.keys[0];
const g = groups.value.find(x => x.HelpFeatureGroupId == record.HelpFeatureGroupId);
groupCode.value = g ? g.GroupCode : "";
await onSearch();
modelOption.value.isModalHelpFunction = false;
}
else {
notification.info({
message: '系统提示:',
description: '请选择一条记录',
placement: "topRight",
});
}
}
})
</script>
@@ -0,0 +1,301 @@
<template>
<search-card @on-submit="onSubmit" @on-reset="onReset" @use-pagination="onPagination" :is-modal="isModal"
:loading="loading" :pagination="result" :rowSelectionType="selectionType"
:card-title-keys="[SecurityHelpFunctionEntity.FUNCTION_NAME]" @on-row-dblclick="onRowDblclick"
@on-select-change="onSelectChange">
<template #control>
<a-button type="primary" v-if="canCreate && !isModal" :icon="h(FileAddOutlined)"
@click.prevent="onCreate">新增</a-button>
</template>
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === SecurityHelpFunctionEntity.ENABLED_INDC">
<span v-if="record.EnabledIndc == 'Y'">是</span>
<span v-else>否</span>
</template>
</template>
<template #footer>
<div v-if="!isModal" class="page-footer">
<a-row :gutter="24">
<a-col :lg="24" :sm="24">
<a-row :gutter="[16, 8]">
<a-space>
<a-button type="primary" v-if="canView" :icon="h(FileTextOutlined)"
:disabled="!(keys && keys.length == 1)" @click.prevent="onView">查看</a-button>
<a-button type="primary" v-if="canAmend" :icon="h(EditOutlined)"
:disabled="!(keys && keys.length == 1)" @click.prevent="onAmend">修改</a-button>
<a-popconfirm title="确定是否删除这条记录?" ok-text="是" cancel-text="否" @confirm="onDelete">
<a-button type="primary" danger :icon="h(DeleteOutlined)" v-if="canDelete"
:disabled="!(keys && keys.length > 0)">删除</a-button>
</a-popconfirm>
</a-space>
</a-row>
</a-col>
</a-row>
</div>
</template>
</search-card>
</template>
<script setup lang="ts">
import { reactive, inject, onMounted, ref, unref, provide, h } from "vue";
import { notification } from "ant-design-vue";
import { BindOption } from "@/data/custom/bindOption";
import searchCard from "@/components/basic/SearchCard.vue";
import { DeleteOutlined, EditOutlined, FileAddOutlined, FileTextOutlined } from "@ant-design/icons-vue";
import { SecurityHelpFunctionResultData } from "@/data/auto/securityHelpFunctionResultData";
import { SecurityHelpFunctionEntity } from "@/data/auto/securityHelpFunctionEntity";
import { SearchFilter } from "@/data/auto/searchFilter"
import securityHelpFunctionService from "@/service/security/securityHelpFunctionService";
import { SecurityHelpFunctionSearchCriteria } from "@/data/auto/securityHelpFunctionSearchCriteria";
import { SearchCriteriaResultData } from "@/data/auto/searchCriteriaResultData";
import { PermissionContext, usePermissionContext } from "@/hooks/PermissionContext"
import { useRoute, useRouter } from "vue-router";
import { useBindOption } from "@/hooks/useBindOption";
import { SearchFilterOperatorPolicy } from "@/data/custom/searchFilterOperatorPolicy"
import { SearchFilterTypePolicy } from "@/data/custom/searchFilterTypePolicy"
import { OperationToEnquiryModelOption, operationToEnquiryMethodKey, enquiryMethodKey, searchCriteriaManagerKey } from "@/injections";
import { useOrderedChildren } from "@/hooks/use-ordered-children";
import { SearchCriteriaContext } from "@/injections";
//================inject================
const operationMethod = inject(operationToEnquiryMethodKey, undefined);
const modelOption = operationMethod?.getOperationToEnquiryModelOption() as OperationToEnquiryModelOption;
const isModal = ref(modelOption ? !!modelOption.isModalHelpFunction : false);
const selectionType = modelOption ? modelOption.selectionTypeHelpFunction ?? 'radio' : 'checkbox';
const initBindOptions = ref((modelOption ? modelOption.initBindOptionsHelpFunction : []) as BindOption[]);
//================inject================
const emit = defineEmits(['onRowDblclick']);
const loading = ref<boolean>(false);
const canView = ref<boolean>(false);
const canAmend = ref<boolean>(false);
const canDelete = ref<boolean>(false);
const canCreate = ref<boolean>(false);
const route = useRoute();
const router = useRouter();
const columns = ref([
{
title: '序号',
dataIndex: 'index',
width: '5%',
align: 'center',
fixed: 'left',
sequence: 0,
hidden: 'N',
format: ''
},
{
title: "功能名称",
dataIndex: SecurityHelpFunctionEntity.FUNCTION_NAME,
sorter: true,
width: '20%',
sequence: 1,
hidden: 'N'
},
{
title: "功能说明",
dataIndex: SecurityHelpFunctionEntity.INTRO,
sorter: true,
width: '45%',
sequence: 2,
hidden: 'N'
},
{
title: "启用?",
dataIndex: SecurityHelpFunctionEntity.ENABLED_INDC,
sorter: true,
width: '10%',
sequence: 3,
hidden: 'N'
},
]);
const viewOption = {
Value: 'default',
SelectOptions: [{ BindValue: 'default', DisplayName: "默认" }],
Columns: [{ BindValue: 'default', BindColumn: columns }]
};
const bindOptions = ref<BindOption[]>([]);
const datas = ref<SecurityHelpFunctionEntity[]>([]);
const pagingParams = ref<PagingParams>({ pageSize: 20, page: 1 });
const result = ref<SearchCriteriaResultData>(new SearchCriteriaResultData());
const data = ref<SecurityHelpFunctionResultData>(new SecurityHelpFunctionResultData());
const isSearched = ref<boolean>(false);
const currentSearchCriteria = ref<SecurityHelpFunctionSearchCriteria>(new SecurityHelpFunctionSearchCriteria());
const keys = ref<SecurityHelpFunctionEntity[]>([]);
const {
children: items,
addChild: addItem,
removeChild: removeItem,
} = useOrderedChildren<SearchCriteriaContext>();
//================provide================
provide(enquiryMethodKey, {
getLayoutSetting: () => { return { controlLeft: 16, controlRight: 8, space: true, soltLeft: 24, soltRight: 24 }; },
getBindOptions: () => { return unref(bindOptions); },
getViewOption: () => { return unref(viewOption); },
getDatas: () => { return datas; },
});
provide(
searchCriteriaManagerKey,
reactive({
items,
addItem,
removeItem,
})
);
//================provide================
defineExpose({
keys,
initBindOptions,
})
onMounted(() => {
let pContext: PermissionContext = usePermissionContext();
canView.value = pContext.canView(route.meta.permission as string);
canAmend.value = pContext.canAmend(route.meta.permission as string);
canDelete.value = pContext.canDelete(route.meta.permission as string);
canCreate.value = pContext.canCreate(route.meta.permission as string);
initBindOption();
onSearch();
})
const onSelectChange = ((selected: SecurityHelpFunctionEntity[]) => {
keys.value = selected;
})
const onRowDblclick = ((e: any, record: SecurityHelpFunctionEntity) => {
if (isModal.value) {
emit('onRowDblclick', record);
}
else {
router.push({ name: "SecurityHelpFunctionOperation", params: { id: record.FunctionName, mode: 'view' } });
}
})
const onReset = (() => {
keys.value = [];
result.value.Total = 0;
})
const onView = (() => {
if (keys.value.length == 0) return;
router.push({ name: "SecurityHelpFunctionOperation", params: { id: keys.value[0].FunctionName, mode: 'view' } });
})
const onCreate = (() => {
router.push({ name: "SecurityHelpFunctionOperation", params: { id: '', mode: 'create' } });
})
const onAmend = (() => {
if (keys.value.length == 0) return;
router.push({ name: "SecurityHelpFunctionOperation", params: { id: keys.value[0].FunctionName, mode: 'amend' } });
})
const onDelete = (async () => {
for (let i = 0; i < keys.value.length; i++) {
await securityHelpFunctionService.DeleteByFunctionName(keys.value[i].FunctionName);
}
notification.info({
message: '系统提示:',
description: '记录删除成功',
placement: "topRight",
});
onReload();
})
const setBindOptionValue = ((bindValue: string, value: string, value1: string, values: string[], disabled: boolean) => {
let bindOption: BindOption = bindOptions.value.filter(x => x.BindValue == bindValue)[0];
if (!bindOption) return;
bindOption.Value = value;
bindOption.Value1 = value1;
bindOption.Values = values;
bindOption.disabled = disabled;
})
//初始化查询条件
const initBindOption = (async () => {
let ubo = useBindOption();
let bindOption: BindOption = ubo.getBindOption(SecurityHelpFunctionEntity.DB_FUNCTION_NAME, '功能名称', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOptions.value.push(bindOption);
bindOption = ubo.getBindOption(SecurityHelpFunctionEntity.DB_INTRO, '功能说明', SearchFilterTypePolicy.TEXT, SearchFilterOperatorPolicy.CONTAIN);
bindOption.lg = 10;
bindOptions.value.push(bindOption);
initBindOptions.value.forEach(element => {
setBindOptionValue(element.BindValue, element.Value, element.Value1, element.Values, element.disabled);
});
})
//table from-----------------------------------------------------------------------------------------------------------------------------------
result.value.Page = pagingParams.value.page ?? 1;
result.value.Limit = pagingParams.value.pageSize;
const onSubmit = (async (filters: SearchFilter[]) => {
let c: SecurityHelpFunctionSearchCriteria = new SecurityHelpFunctionSearchCriteria();
isSearched.value = true;
c.Filter = filters;
c.limit = pagingParams.value.pageSize;
c.page = pagingParams.value.page ?? 1;
c.GridColumns = [];
currentSearchCriteria.value = c;
await findBySearchCriteria(c);
})
const onSearch = (async () => {
onSubmit(items.value[0].getFilter());
})
const findBySearchCriteria = (async (c: SecurityHelpFunctionSearchCriteria) => {
try {
data.value = await securityHelpFunctionService.FindBySearchCriteria(c);
result.value.Total = data.value.Total;
result.value.Page = data.value.Page;
result.value.Limit = data.value.Limit;
datas.value = data.value.Datas;
} catch (error) {
console.log(error);
}
})
const onPagination = (async (params: PagingParams) => {
if (!isSearched.value) return;
let c: SecurityHelpFunctionSearchCriteria = new SecurityHelpFunctionSearchCriteria();
c.Filter = items.value[0].getFilter();
c.limit = params.pageSize;
c.page = params.page ?? 1;
if (params.sortField)
c.orderby = params.sortField!;
if (params.sortOrder)
c.orderbytag = params.sortOrder?.toString().substring(0, 4)!;
c.GridColumns = [];
currentSearchCriteria.value = c;
await findBySearchCriteria(c);
});
const onReload = (async () => {
await findBySearchCriteria(unref(currentSearchCriteria));
})
//table end-----------------------------------------------------------------------------------------------------------------------------------
</script>
@@ -0,0 +1,515 @@
<template>
<a-spin size="large" :spinning="loading">
<div class="page-container">
<div class="page-header">
<a-row :gutter="[16, 8]">
<a-col :lg="8" :sm="24">
<a-row :wrap="false">
<a-col flex="none">
<div style="padding: 0 "><a-input flex="none" readonly value="功能名称"
class="site-input-split" style="width: 160px;border-radius:0; border-top-left-radius: 5pt 5pt;
border-bottom-left-radius: 5pt 5pt;
pointer-events: none" /></div>
</a-col>
<a-col flex="auto">
<a-input-search :disabled="isEdit" v-model:value="functionName" flex="auto"
@search="onModalSearch" />
</a-col>
</a-row>
</a-col>
<a-col :lg="12" :sm="24">
<a-space>
<a-button type="primary" :disabled="isEdit" :icon="h(FileSearchOutlined)"
@click.prevent="onSearch">查询</a-button>
<a-button type="primary" :disabled="isEdit" :icon="h(ClearOutlined)"
@click.prevent="onReset">重置</a-button>
<a-button type="primary" :disabled="isEdit" :icon="h(FileAddOutlined)"
@click.prevent="onCreate">新增</a-button>
</a-space>
</a-col>
</a-row>
</div>
<div class="page-main ft-page" :class="{ 'ft-readonly': !isEdit }" :style="{ height: resultH }">
<!-- 顶部信息卡 -->
<div class="ft-hero">
<div class="ft-hero-icon">
<AppstoreOutlined />
</div>
<div class="ft-hero-main">
<div class="ft-hero-name">
{{ data.SecurityHelpFunction.FunctionName || "未选择功能" }}
</div>
<div class="ft-hero-meta">
<span class="ft-chip">编号 {{ data.SecurityHelpFunction.HelpFunctionId || "-" }}</span>
<span v-if="data.SecurityHelpFunction.EnabledIndc == 'Y'" class="ft-chip">启用</span>
</div>
</div>
</div>
<!-- 帮助信息 -->
<div class="ft-card">
<div class="ft-card-title">帮助信息</div>
<div class="ft-grid">
<div class="ft-field">
<div class="ft-label">功能名称<span class="ft-required">*</span></div>
<a-select v-if="isEdit && !data.SecurityHelpFunction.CreatedBy" size="large"
style="width: 100%" show-search placeholder="请选择要给哪个功能写帮助"
v-model:value="data.SecurityHelpFunction.FunctionName"
:options="functionNameOptions" :filter-option="filterOption" />
<a-input v-else size="large" readonly
v-model:value="data.SecurityHelpFunction.FunctionName" />
</div>
<div class="ft-field">
<div class="ft-label">启用?</div>
<a-select size="large" style="width: 100%" placeholder="请选择"
v-model:value="data.SecurityHelpFunction.EnabledIndc" :disabled="!isEdit">
<a-select-option v-for="o in yesNoPolicy" :value="o.BindValue">{{
o.DisplayName }}</a-select-option>
</a-select>
</div>
<div class="ft-field ft-field-full">
<div class="ft-label">功能说明</div>
<a-textarea :auto-size="{ minRows: 3, maxRows: 10 }" :maxlength="1000"
placeholder="一句话说明这个功能是什么" :readonly="!isEdit"
v-model:value="data.SecurityHelpFunction.Intro" />
</div>
</div>
</div>
<!-- 明细 -->
<div class="ft-card">
<a-collapse v-model:activeKey="activeKey" class="ft-collapse">
<!-- 操作步骤 -->
<a-collapse-panel key="1" header="操作步骤">
<div class="ft-list">
<div v-for="(record, index) in data.SecurityHelpSteps" :key="index" class="ft-item">
<div class="ft-item-head">
<span class="ft-item-index">{{ index + 1 }}</span>
<div class="ft-item-title">
<span class="ft-item-name">
{{ record.Title || "未填写步骤标题" }}
</span>
</div>
<div v-if="isEdit" class="ft-item-actions">
<UpSquareTwoTone class="ft-action" @click="upStep(index)" />
<DownSquareTwoTone class="ft-action" @click="downStep(index)" />
<DeleteOutlined class="ft-action ft-action-delete"
@click="deleteStep(index)" />
</div>
</div>
<div class="ft-item-body">
<div class="ft-item-field">
<span class="ft-item-label">标题</span>
<a-input v-if="isEdit" size="large" v-model:value="record.Title"
placeholder="这一步先做什么" />
<span v-else class="ft-item-text">{{ record.Title || "-" }}</span>
</div>
<div class="ft-item-field">
<span class="ft-item-label">说明</span>
<a-textarea v-if="isEdit" :auto-size="{ minRows: 2, maxRows: 6 }"
v-model:value="record.Description" placeholder="这一步具体怎么做" />
<span v-else class="ft-item-text">{{ record.Description || "-" }}</span>
</div>
<div class="ft-item-field">
<span class="ft-item-label">提示</span>
<a-input v-if="isEdit" size="large" v-model:value="record.Tips"
placeholder="补充说明,可留空" />
<span v-else class="ft-item-text">{{ record.Tips || "-" }}</span>
</div>
<div class="ft-item-field">
<span class="ft-item-label">图标</span>
<a-input v-if="isEdit" size="large" v-model:value="record.Icon"
placeholder="页面上的图标名,如 DeleteOutlined" />
<span v-else class="ft-item-text">{{ record.Icon || "-" }}</span>
</div>
</div>
</div>
<div v-if="!data.SecurityHelpSteps || !data.SecurityHelpSteps.length" class="ft-empty">
暂无操作步骤
</div>
<a-button class="ft-add-btn" type="dashed" block v-if="isEdit" @click="addStep">
<PlusSquareTwoTone /> 新增步骤
</a-button>
</div>
</a-collapse-panel>
<!-- 引用的功能点组 -->
<a-collapse-panel key="2" header="引用的功能点组">
<div class="ft-list">
<div v-for="(record, index) in data.SecurityHelpFunctionFeatures" :key="index"
class="ft-item">
<div class="ft-item-head">
<span class="ft-item-index">{{ index + 1 }}</span>
<div class="ft-item-title">
<span class="ft-item-name">
{{ groupNameOf(record.GroupCode) }}
</span>
</div>
<div v-if="isEdit" class="ft-item-actions">
<UpSquareTwoTone class="ft-action" @click="upFeature(index)" />
<DownSquareTwoTone class="ft-action" @click="downFeature(index)" />
<DeleteOutlined class="ft-action ft-action-delete"
@click="deleteFeature(index)" />
</div>
</div>
<div class="ft-item-body">
<div class="ft-item-field">
<span class="ft-item-label">功能点组</span>
<a-select v-if="isEdit" size="large" style="width: 100%" show-search
placeholder="请选择要引用哪一组功能点" v-model:value="record.GroupCode"
:options="groupOptions" :filter-option="filterOption" />
<span v-else class="ft-item-text">{{ record.GroupCode || "-" }}</span>
</div>
</div>
</div>
<div v-if="!data.SecurityHelpFunctionFeatures || !data.SecurityHelpFunctionFeatures.length"
class="ft-empty">
暂未引用功能点组
</div>
<a-button class="ft-add-btn" type="dashed" block v-if="isEdit" @click="addFeature">
<PlusSquareTwoTone /> 引用功能点组
</a-button>
</div>
</a-collapse-panel>
</a-collapse>
</div>
</div>
<div class="page-footer">
<div style="">
<a-row :gutter="24">
<a-col :lg="24" :sm="24">
<div>
<a-row v-if="isEdit" :gutter="[16, 8]">
<a-space>
<a-popconfirm v-if="data.SecurityHelpFunction.CreatedBy" title="确定是否取消修改?"
ok-text="是" cancel-text="否" @confirm="onCancel">
<a-button type="primary" ghost :icon="h(UndoOutlined)">取消</a-button>
</a-popconfirm>
<a-button v-if="!data.SecurityHelpFunction.CreatedBy" type="primary" ghost
:icon="h(UndoOutlined)" @click.prevent="onCancel">取消</a-button>
<a-button type="primary" ghost :icon="h(SaveOutlined)"
@click.prevent="onSave">保存</a-button>
</a-space>
</a-row>
<a-row v-else :gutter="[16, 8]">
<a-space>
<a-button type="primary" v-if="canAmend" :icon="h(EditOutlined)"
:disabled="!hasData" @click.prevent="onAmend">修改</a-button>
<a-popconfirm :disabled="!hasData" title="确定是否删除这条记录?" ok-text="是"
cancel-text="否" @confirm="onDelete">
<a-button type="primary" danger :icon="h(DeleteOutlined)"
:disabled="!hasData" v-if="canDelete">删除</a-button>
</a-popconfirm>
</a-space>
</a-row>
</div>
</a-col>
</a-row>
</div>
</div>
</div>
<dragModal v-model:open="modelOption.isModalHelpFunction" title="帮助功能查询" @cancel="handleCance"
:fullscreen="width < 640" :mask-closable="false" @ok="handleSelect">
<SecurityHelpFunctionEnquiry ref="enquiry" @on-row-dblclick="handleSelect"></SecurityHelpFunctionEnquiry>
</dragModal>
</a-spin>
</template>
<script lang="ts" setup>
import { ref, unref, onBeforeMount, inject, provide, h, onMounted, computed } from "vue";
import { notification } from "ant-design-vue";
import { useRoute, useRouter } from 'vue-router'
import {
DeleteOutlined, EditOutlined, ClearOutlined, FileSearchOutlined, FileAddOutlined, UndoOutlined,
SaveOutlined, PlusSquareTwoTone, DownSquareTwoTone, UpSquareTwoTone, AppstoreOutlined
} from "@ant-design/icons-vue";
import { PermissionContext, usePermissionContext } from "@/hooks/PermissionContext"
import dragModal from "@/components/basic/DragModal.vue"
import SecurityHelpFunctionEnquiry from "./SecurityHelpFunctionEnquiry.vue";
import { SecurityHelpFunctionData } from "@/data/auto/securityHelpFunctionData";
import { SecurityHelpStepEntity } from "@/data/auto/securityHelpStepEntity";
import { SecurityHelpFunctionFeatureEntity } from "@/data/auto/securityHelpFunctionFeatureEntity";
import { SecurityHelpFeatureGroupEntity } from "@/data/auto/securityHelpFeatureGroupEntity";
import { SecurityFunctionDisplayData } from "@/data/auto/securityFunctionDisplayData";
import securityHelpFunctionService from "@/service/security/securityHelpFunctionService";
import { useYesNoPolicy } from "@/hooks/useYesNoPolicy"
import { SelectOption } from "@/data/auto/selectOption";
import { useWindowSize } from '@vueuse/core'
import { OperationToEnquiryModelOption, operationToEnquiryMethodKey } from "@/injections"
const loading = ref<boolean>(false);
const { width, height } = useWindowSize()
const route = useRoute();
const router = useRouter();
const functionName = ref<string>(route.params.id as string)
const mode = ref<string>(route.params.mode as string)
const isEdit = ref<boolean>(false);
const canAmend = ref<boolean>(false);
const canDelete = ref<boolean>(false);
const canCreate = ref<boolean>(false);
const data = ref<SecurityHelpFunctionData>(new SecurityHelpFunctionData());
const hasData = ref<boolean>(false);
const yesNoPolicy = ref<SelectOption[]>([]);
const resultH = ref('');
const oldFunctionName = ref<string>(functionName.value);
const activeKey = ref(['1', '2']);
/** 可引用的功能点组(整块表取回来做下拉) */
const groups = ref<SecurityHelpFeatureGroupEntity[]>([]);
const groupOptions = computed(() => groups.value.map(g => ({
value: g.GroupCode,
label: `${g.GroupCode}${g.GroupName ? "(" + g.GroupName + ")" : ""}`
})));
/** 新增时供选择的「给哪个功能写帮助」 */
const functionOptions = ref<SecurityFunctionDisplayData[]>([]);
const functionNameOptions = computed(() => functionOptions.value.map(f => ({
value: f.FunctionName,
label: `${f.FunctionLocalName || f.FunctionName}(${f.FunctionName})`
})));
const groupNameOf = (groupCode?: string) => {
if (!groupCode) return "未选择功能点组";
const g = groups.value.find(x => x.GroupCode == groupCode);
return g ? `${g.GroupCode}${g.GroupName ? "(" + g.GroupName + ")" : ""}` : groupCode;
};
const filterOption = (input: string, option: any) =>
String(option.label ?? "").toLowerCase().indexOf(input.toLowerCase()) >= 0;
interface HelpFunctionEnquiry {
keys: SecurityHelpFunctionData[],
setBindOptionValue(bindValue: string, value: string, value1: string, values: string[]): void,
}
const enquiry = ref<HelpFunctionEnquiry | null>(null)
//================provide================
const modelOption = ref<OperationToEnquiryModelOption>(new OperationToEnquiryModelOption());
modelOption.value.isModalHelpFunction = false;
const getOperationToEnquiryModelOption = () => {
return modelOption.value
}
provide(
operationToEnquiryMethodKey,
{
getOperationToEnquiryModelOption,
}
);
//================provide================
onBeforeMount(async () => {
yesNoPolicy.value = await useYesNoPolicy().getSelectOption(false);
})
onMounted(async () => {
let pContext: PermissionContext = usePermissionContext();
canAmend.value = pContext.canAmend(route.meta.permission as string);
canDelete.value = pContext.canDelete(route.meta.permission as string);
canCreate.value = pContext.canCreate(route.meta.permission as string);
computResultTableHeight();
// 下拉数据:功能点组 + 可选功能名
try {
const all = await securityHelpFunctionService.FindAll();
groups.value = all.SecurityHelpFeatureGroups ?? [];
} catch (error) {
console.log(error);
}
try {
functionOptions.value = await securityHelpFunctionService.FindFunctionNameOptions();
} catch (error) {
console.log(error);
}
if (functionName.value && functionName.value.trim().length > 0) {
await onSearch();
}
if (mode.value && mode.value == 'create') {
onCreate();
isEdit.value = true;
}
else if (mode.value && mode.value == 'amend') {
isEdit.value = true;
}
})
const computResultTableHeight = () => {
resultH.value = width.value > 640 ? 'calc(100vh - 220px)' : 'calc(100vh - 250px)';
}
const onModalSearch = (() => {
modelOption.value.isModalHelpFunction = true;
})
const onSearch = (async () => {
if (!functionName.value || functionName.value.trim().length == 0) {
notification.info({
message: '系统提示:',
description: '请输入功能名称',
placement: "topRight",
});
hasData.value = false;
return;
}
data.value = await securityHelpFunctionService.FindByFunctionName(functionName.value.trim());
if (!data.value.SecurityHelpFunction || !data.value.SecurityHelpFunction.FunctionName) {
notification.info({
message: '系统提示:',
description: '记录不存在',
placement: "topRight",
});
data.value = new SecurityHelpFunctionData();
hasData.value = false;
return;
}
// 后端未返回时也要保证明细是数组,避免模板报错
if (!data.value.SecurityHelpSteps) data.value.SecurityHelpSteps = [];
if (!data.value.SecurityHelpFunctionFeatures) data.value.SecurityHelpFunctionFeatures = [];
hasData.value = true;
})
const onReset = (() => {
data.value = new SecurityHelpFunctionData();
data.value.SecurityHelpSteps = [];
data.value.SecurityHelpFunctionFeatures = [];
functionName.value = '';
hasData.value = false;
})
const onCreate = (() => {
onReset();
data.value.SecurityHelpFunction.EnabledIndc = 'Y';
isEdit.value = true;
})
const onAmend = (() => {
isEdit.value = true;
oldFunctionName.value = unref(data.value.SecurityHelpFunction.FunctionName);
})
const onSave = (async () => {
try {
loading.value = true;
if (!data.value.SecurityHelpFunction.FunctionName) {
notification.info({
message: '系统提示:',
description: '请选择功能名称',
placement: "topRight",
});
loading.value = false;
return;
}
// 明细序号按界面顺序重排,顺序即显示顺序
(data.value.SecurityHelpSteps ?? []).forEach((s, i) => s.StepNo = i + 1);
(data.value.SecurityHelpFunctionFeatures ?? []).forEach((f, i) => f.SortNo = i + 1);
data.value = await securityHelpFunctionService.SaveFunction(data.value);
functionName.value = data.value.SecurityHelpFunction.FunctionName;
loading.value = false;
hasData.value = true;
isEdit.value = false;
} catch (error) {
loading.value = false;
console.log(error);
}
})
const onCancel = (() => {
onReset();
if (oldFunctionName.value && oldFunctionName.value.length > 0) {
functionName.value = oldFunctionName.value;
onSearch();
}
isEdit.value = false;
})
const onDelete = (async () => {
try {
loading.value = true;
await securityHelpFunctionService.DeleteByFunctionName(data.value.SecurityHelpFunction.FunctionName);
onReset();
loading.value = false;
} catch (error) {
loading.value = false;
console.log(error);
}
})
//==================== 步骤明细操作 ====================
const addStep = (() => {
if (!data.value.SecurityHelpSteps) data.value.SecurityHelpSteps = [];
let step: SecurityHelpStepEntity = new SecurityHelpStepEntity();
step.StepNo = data.value.SecurityHelpSteps.length + 1;
data.value.SecurityHelpSteps.push(step);
})
const deleteStep = ((index: number) => {
data.value.SecurityHelpSteps.splice(index, 1);
})
const swap = ((arr: any[], index: number, offset: number) => {
const target = index + offset;
if (target < 0 || target >= arr.length) return;
const item = arr.splice(index, 1)[0];
arr.splice(target, 0, item);
})
const upStep = ((index: number) => swap(data.value.SecurityHelpSteps, index, -1))
const downStep = ((index: number) => swap(data.value.SecurityHelpSteps, index, 1))
//==================== 引用的功能点组操作 ====================
const addFeature = (() => {
if (!data.value.SecurityHelpFunctionFeatures) data.value.SecurityHelpFunctionFeatures = [];
let feature: SecurityHelpFunctionFeatureEntity = new SecurityHelpFunctionFeatureEntity();
feature.FunctionName = data.value.SecurityHelpFunction.FunctionName;
feature.SortNo = data.value.SecurityHelpFunctionFeatures.length + 1;
data.value.SecurityHelpFunctionFeatures.push(feature);
})
const deleteFeature = ((index: number) => {
data.value.SecurityHelpFunctionFeatures.splice(index, 1);
})
const upFeature = ((index: number) => swap(data.value.SecurityHelpFunctionFeatures, index, -1))
const downFeature = ((index: number) => swap(data.value.SecurityHelpFunctionFeatures, index, 1))
const handleCance = (async () => {
})
const handleSelect = (async () => {
if (enquiry.value) {
if (enquiry.value.keys.length > 0) {
functionName.value = enquiry.value.keys[0].SecurityHelpFunction.FunctionName;
await onSearch();
modelOption.value.isModalHelpFunction = false;
}
else {
notification.info({
message: '系统提示:',
description: '请选择一条记录',
placement: "topRight",
});
}
}
})
</script>