diff --git a/src/ElectionSystem.Api/Components/Pages/Admin/Regions.razor b/src/ElectionSystem.Api/Components/Pages/Admin/Regions.razor index 597e90d..511e025 100644 --- a/src/ElectionSystem.Api/Components/Pages/Admin/Regions.razor +++ b/src/ElectionSystem.Api/Components/Pages/Admin/Regions.razor @@ -8,10 +8,11 @@ 行政区划

基础资料 / 地域

行政区划目录

省、市、区县、镇街来自受控 AreaCity 数据版本;村/社区只可使用本地确认目录。

导入四级目录
@if (message is not null) {
@message
} -@if (systemAdmin) {

在线新增村/社区

} +@if (systemAdmin) {

在线新增村/社区

} @if (!loaded) {

正在加载目录…

} else if (roots.Count == 0) {

尚未导入行政区划目录。

} else {
@foreach (var node in roots) { }
}
-@code { [CascadingParameter] private Task AuthenticationStateTask { get; set; } = default!; private bool loaded, systemAdmin; private string? message; private VillageDraft draft = new(); private HashSet scope = []; private readonly List roots = []; private readonly List townships = []; - protected override async Task OnInitializedAsync() { var id = RegionScopeService.GetRequiredUserId((await AuthenticationStateTask).User); systemAdmin = await Db.UserRegionalRoleAssignments.AnyAsync(x => x.UserId == id && x.Role == RegionalRole.SystemAdministrator && x.RegionId == null); scope = await Scope.GetManagedRegionIdsAsync(id, CancellationToken.None); roots.Clear(); var rootIds = systemAdmin ? null : await Db.UserRegionalRoleAssignments.Where(x => x.UserId == id && x.RegionId.HasValue && x.Role != RegionalRole.CountingStationClerk && x.Role != RegionalRole.Supervisor).Select(x => x.RegionId!.Value).Distinct().ToListAsync(); var rootQuery = Db.AdministrativeRegions.Where(x => systemAdmin ? x.ParentId == null : rootIds!.Contains(x.Id)); roots.AddRange(await rootQuery.OrderBy(x => x.Code).Select(x => new RegionTreeNode(x.Id,x.Code,x.Name,x.Level,x.IsActive)).ToListAsync()); if (systemAdmin) { townships.Clear(); townships.AddRange(await Db.AdministrativeRegions.Where(x => x.IsActive && x.Level == AdministrativeRegionLevel.Township).OrderBy(x => x.Code).Select(x => new Township(x.Id, x.Code, x.Name)).ToListAsync()); } loaded = true; } +@code { [CascadingParameter] private Task AuthenticationStateTask { get; set; } = default!; private bool loaded, systemAdmin; private string? message; private VillageDraft draft = new(); private HashSet scope = []; private readonly List roots = []; private readonly List provinces = [], cities = [], districts = [], townships = []; + protected override async Task OnInitializedAsync() { var id = RegionScopeService.GetRequiredUserId((await AuthenticationStateTask).User); systemAdmin = await Db.UserRegionalRoleAssignments.AnyAsync(x => x.UserId == id && x.Role == RegionalRole.SystemAdministrator && x.RegionId == null); scope = await Scope.GetManagedRegionIdsAsync(id, CancellationToken.None); roots.Clear(); var rootIds = systemAdmin ? null : await Db.UserRegionalRoleAssignments.Where(x => x.UserId == id && x.RegionId.HasValue && x.Role != RegionalRole.CountingStationClerk && x.Role != RegionalRole.Supervisor).Select(x => x.RegionId!.Value).Distinct().ToListAsync(); var rootQuery = Db.AdministrativeRegions.Where(x => systemAdmin ? x.ParentId == null : rootIds!.Contains(x.Id)); roots.AddRange(await rootQuery.OrderBy(x => x.Code).Select(x => new RegionTreeNode(x.Id,x.Code,x.Name,x.Level,x.IsActive)).ToListAsync()); if (systemAdmin) await LoadOptionsAsync(provinces, null, AdministrativeRegionLevel.Province); loaded = true; } + private async Task SelectProvinceAsync(ChangeEventArgs e) { draft.ProvinceId=Parse(e); draft.CityId=draft.DistrictId=draft.ParentId=null; cities.Clear(); districts.Clear(); townships.Clear(); if(draft.ProvinceId.HasValue) await LoadOptionsAsync(cities,draft.ProvinceId,AdministrativeRegionLevel.City); } private async Task SelectCityAsync(ChangeEventArgs e) { draft.CityId=Parse(e); draft.DistrictId=draft.ParentId=null; districts.Clear(); townships.Clear(); if(draft.CityId.HasValue) await LoadOptionsAsync(districts,draft.CityId,AdministrativeRegionLevel.District); } private async Task SelectDistrictAsync(ChangeEventArgs e) { draft.DistrictId=Parse(e); draft.ParentId=null; townships.Clear(); if(draft.DistrictId.HasValue) await LoadOptionsAsync(townships,draft.DistrictId,AdministrativeRegionLevel.Township); } private async Task LoadOptionsAsync(List target,Guid? parent,AdministrativeRegionLevel level) { target.Clear(); target.AddRange(await Db.AdministrativeRegions.Where(x=>x.IsActive&&x.Level==level&&x.ParentId==parent).OrderBy(x=>x.Code).Select(x=>new Township(x.Id,x.Code,x.Name)).ToListAsync()); } private static Guid? Parse(ChangeEventArgs e)=>Guid.TryParse(e.Value?.ToString(),out var id)?id:null; private async Task ToggleAsync(RegionTreeNode node) { if (node.Children is null) node.Children = await Db.AdministrativeRegions.Where(x => x.ParentId == node.Id && scope.Contains(x.Id)).OrderBy(x => x.Code).Select(x => new RegionTreeNode(x.Id,x.Code,x.Name,x.Level,x.IsActive)).ToListAsync(); node.Expanded = !node.Expanded; } private async Task CreateVillageAsync() { if (!draft.ParentId.HasValue || string.IsNullOrWhiteSpace(draft.Code) || string.IsNullOrWhiteSpace(draft.Name) || string.IsNullOrWhiteSpace(draft.SourceVersion)) { message = "请完整填写村/社区信息。"; return; } var parent = await Db.AdministrativeRegions.SingleOrDefaultAsync(x => x.Id == draft.ParentId && x.IsActive && x.Level == AdministrativeRegionLevel.Township); if (parent is null) { message = "请选择有效的镇/街道。"; return; } if (await Db.AdministrativeRegions.AnyAsync(x => x.Code == draft.Code.Trim() && x.SourceVersion == draft.SourceVersion.Trim())) { message = "该代码与目录版本已存在。"; return; } Db.AdministrativeRegions.Add(new AdministrativeRegion { ParentId = parent.Id, Code = draft.Code.Trim(), Name = draft.Name.Trim(), Level = AdministrativeRegionLevel.Village, Source = "LocalApprovedVillageImport", SourceVersion = draft.SourceVersion.Trim() }); await Db.SaveChangesAsync(); message = "村/社区已保存。"; draft = new(); await OnInitializedAsync(); } - private static string Level(AdministrativeRegionLevel value) => value switch { AdministrativeRegionLevel.Province => "省", AdministrativeRegionLevel.City => "市", AdministrativeRegionLevel.District => "区/县", AdministrativeRegionLevel.Township => "镇/街道", _ => "村/社区" }; private sealed record Township(Guid Id, string Code, string Name); private sealed class VillageDraft { public Guid? ParentId { get; set; } public string Code { get; set; } = ""; public string Name { get; set; } = ""; public string SourceVersion { get; set; } = ""; } } + private static string Level(AdministrativeRegionLevel value) => value switch { AdministrativeRegionLevel.Province => "省", AdministrativeRegionLevel.City => "市", AdministrativeRegionLevel.District => "区/县", AdministrativeRegionLevel.Township => "镇/街道", _ => "村/社区" }; private sealed record Township(Guid Id, string Code, string Name); private sealed class VillageDraft { public Guid? ProvinceId { get; set; } public Guid? CityId { get; set; } public Guid? DistrictId { get; set; } public Guid? ParentId { get; set; } public string Code { get; set; } = ""; public string Name { get; set; } = ""; public string SourceVersion { get; set; } = ""; } }