Просмотр исходного кода

增加五级行政区域(村级)管理页面(与国家统计局进行同步)

zuohuaijun 3 лет назад
Родитель
Сommit
8d8d1b3db3
25 измененных файлов с 1935 добавлено и 50 удалено
  1. 3 27
      Admin.NET/Admin.NET.Application/Service/TestService.cs
  2. 5 4
      Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj
  3. 87 1
      Admin.NET/Admin.NET.Core/Admin.NET.Core.xml
  4. 8 2
      Admin.NET/Admin.NET.Core/Entity/SysRegion.cs
  5. 19 1
      Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs
  6. 9 11
      Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs
  7. 0 2
      Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs
  8. 43 0
      Admin.NET/Admin.NET.Core/Service/Region/Dto/RegionInput.cs
  9. 0 1
      Admin.NET/Admin.NET.Core/Service/Region/RegionUtil.cs
  10. 195 0
      Admin.NET/Admin.NET.Core/Service/Region/SysRegionService.cs
  11. 4 1
      Admin.NET/Admin.NET.Core/Service/Server/SysServerService.cs
  12. 1 0
      Web/plugins.d.ts
  13. 1 0
      Web/src/api-services/api.ts
  14. 567 0
      Web/src/api-services/apis/sys-region-api.ts
  15. 111 0
      Web/src/api-services/models/add-region-input.ts
  16. 57 0
      Web/src/api-services/models/admin-result-list-sys-region.ts
  17. 57 0
      Web/src/api-services/models/admin-result-sql-sugar-paged-list-sys-region.ts
  18. 26 0
      Web/src/api-services/models/delete-region-input.ts
  19. 7 0
      Web/src/api-services/models/index.ts
  20. 63 0
      Web/src/api-services/models/sql-sugar-paged-list-sys-region.ts
  21. 111 0
      Web/src/api-services/models/sys-region.ts
  22. 111 0
      Web/src/api-services/models/update-region-input.ts
  23. 117 0
      Web/src/views/system/region/component/editRegion.vue
  24. 140 0
      Web/src/views/system/region/component/regionTree.vue
  25. 193 0
      Web/src/views/system/region/index.vue

+ 3 - 27
Admin.NET/Admin.NET.Application/Service/TestService.cs

@@ -1,5 +1,6 @@
 using Admin.NET.Application.Const;
-using Admin.NET.Core.Service;
+using AngleSharp;
+using AngleSharp.Html.Dom;
 using Furion.DatabaseAccessor;
 using Furion.FriendlyException;
 using Furion.Localization;
@@ -17,16 +18,9 @@ public class TestService : IDynamicApiController, ITransient
 {
     private readonly SqlSugarRepository<Test> _testRep;
 
-    private readonly SqlSugarRepository<SysMenu> _sysMenuRep;
-    private readonly SysMenuService _sysMenuService;
-
-    public TestService(SqlSugarRepository<Test> testRep, 
-        SqlSugarRepository<SysMenu> sysMenuRep,
-        SysMenuService sysMenuService)
+    public TestService(SqlSugarRepository<Test> testRep)
     {
         _testRep = testRep;
-        _sysMenuRep = sysMenuRep;
-        _sysMenuService = sysMenuService;
     }
 
     /// <summary>
@@ -58,24 +52,6 @@ public class TestService : IDynamicApiController, ITransient
         throw Oops.Oh("异常").WithData("数据");
     }
 
-    /// <summary>
-    /// 事务测试
-    /// </summary>
-    [UnitOfWork]
-    public async Task TestTran()
-    {
-        //await _sysMenuRep.GetListAsync();
-
-        //var menuIdList = new List<long> { 0, 1, 2 };
-
-        //await _sysMenuRep.DeleteAsync(u => menuIdList.Contains(u.Id));
-
-        //await _sysMenuRep.DeleteAsync(u => u.Id == 1);
-
-
-        await _sysMenuService.DeleteMenu(new DeleteMenuInput { Id = 252885263002711 });
-    }
-
     /// <summary>
     /// 事务和工作单元测试
     /// </summary>

+ 5 - 4
Admin.NET/Admin.NET.Core/Admin.NET.Core.csproj

@@ -23,10 +23,11 @@
   </ItemGroup>
 
   <ItemGroup>
+    <PackageReference Include="AngleSharp" Version="0.17.1" />
     <PackageReference Include="AspNetCoreRateLimit" Version="4.0.2" />
-    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.7.0.1" />
-    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.7.0.1" />
-    <PackageReference Include="Furion.Pure" Version="4.7.0.1" />
+    <PackageReference Include="Furion.Extras.Authentication.JwtBearer" Version="4.7.1" />
+    <PackageReference Include="Furion.Extras.ObjectMapper.Mapster" Version="4.7.1" />
+    <PackageReference Include="Furion.Pure" Version="4.7.1" />
     <PackageReference Include="Lazy.Captcha.Core" Version="1.1.6" />
     <PackageReference Include="Magicodes.IE.Excel" Version="2.6.9" />
     <PackageReference Include="Magicodes.IE.Pdf" Version="2.6.9" />
@@ -38,7 +39,7 @@
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.Api" Version="2.19.0" />
     <PackageReference Include="SKIT.FlurlHttpClient.Wechat.TenpayV3" Version="2.13.1" />
     <PackageReference Include="SqlSugarCore" Version="5.1.3.29" />
-    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.20" />
+    <PackageReference Include="System.Linq.Dynamic.Core" Version="1.2.21" />
     <PackageReference Include="UAParser" Version="3.1.47" />
     <PackageReference Include="Yitter.IdGenerator" Version="1.0.14" />
     <PackageReference Include="Masuit.Tools.Core" Version="2.5.6.3" />

+ 87 - 1
Admin.NET/Admin.NET.Core/Admin.NET.Core.xml

@@ -1182,7 +1182,7 @@
             组合名
             </summary>
         </member>
-        <member name="P:Admin.NET.Core.SysRegion.AreaCode">
+        <member name="P:Admin.NET.Core.SysRegion.Code">
             <summary>
             行政代码
             </summary>
@@ -1217,6 +1217,11 @@
             维度
             </summary>
         </member>
+        <member name="P:Admin.NET.Core.SysRegion.Order">
+            <summary>
+            排序
+            </summary>
+        </member>
         <member name="P:Admin.NET.Core.SysRegion.Remark">
             <summary>
             备注
@@ -2518,6 +2523,21 @@
             数据表不存在
             </summary>
         </member>
+        <member name="F:Admin.NET.Core.ErrorCodeEnum.R2000">
+            <summary>
+            父节点不存在
+            </summary>
+        </member>
+        <member name="F:Admin.NET.Core.ErrorCodeEnum.R2001">
+            <summary>
+            当前节点Id不能与父节点Id相同
+            </summary>
+        </member>
+        <member name="F:Admin.NET.Core.ErrorCodeEnum.R2002">
+            <summary>
+            已有相同编码或名称
+            </summary>
+        </member>
         <member name="T:Admin.NET.Core.GenderEnum">
             <summary>
             性别枚举
@@ -5854,6 +5874,72 @@
             <param name="input"></param>
             <returns></returns>
         </member>
+        <member name="P:Admin.NET.Core.Service.PageRegionInput.Pid">
+            <summary>
+            父节点Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.PageRegionInput.Name">
+            <summary>
+            名称
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.PageRegionInput.Code">
+            <summary>
+            编码
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.AddRegionInput.Name">
+            <summary>
+            名称
+            </summary>
+        </member>
+        <member name="T:Admin.NET.Core.Service.SysRegionService">
+            <summary>
+            系统行政区域服务
+            </summary>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysRegionService.GetRegionPage(Admin.NET.Core.Service.PageRegionInput)">
+            <summary>
+            获取行政区域列表
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysRegionService.GetRegionList(Admin.NET.Core.Service.RegionInput)">
+            <summary>
+            获取行政区域列表
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysRegionService.AddRegion(Admin.NET.Core.Service.AddRegionInput)">
+            <summary>
+            增加行政区域
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysRegionService.UpdateRegion(Admin.NET.Core.Service.UpdateRegionInput)">
+            <summary>
+            更新行政区域
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysRegionService.DeleteRegion(Admin.NET.Core.Service.DeleteRegionInput)">
+            <summary>
+            删除行政区域
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.SysRegionService.SyncRegion">
+            <summary>
+            同步行政区域
+            </summary>
+            <returns></returns>
+        </member>
         <member name="P:Admin.NET.Core.Service.RoleInput.Status">
             <summary>
             状态

+ 8 - 2
Admin.NET/Admin.NET.Core/Entity/SysRegion.cs

@@ -17,7 +17,7 @@ public class SysRegion : EntityBaseId
     /// </summary>
     [SugarColumn(ColumnDescription = "名称", Length = 64)]
     [Required, MaxLength(64)]
-    public string Name { get; set; }
+    public virtual string Name { get; set; }
 
     /// <summary>
     /// 简称
@@ -38,7 +38,7 @@ public class SysRegion : EntityBaseId
     /// </summary>
     [SugarColumn(ColumnDescription = "行政代码", Length = 32)]
     [MaxLength(32)]
-    public string AreaCode { get; set; }
+    public string Code { get; set; }
 
     /// <summary>
     /// 邮政编码
@@ -79,6 +79,12 @@ public class SysRegion : EntityBaseId
     [SugarColumn(ColumnDescription = "维度")]
     public float Lat { get; set; }
 
+    /// <summary>
+    /// 排序
+    /// </summary>
+    [SugarColumn(ColumnDescription = "排序")]
+    public int Order { get; set; } = 100;
+
     /// <summary>
     /// 备注
     /// </summary>

+ 19 - 1
Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs

@@ -484,5 +484,23 @@ public enum ErrorCodeEnum
     /// 数据表不存在
     /// </summary>
     [ErrorCodeItemMetadata("数据表不存在")]
-    db1001
+    db1001,
+
+    /// <summary>
+    /// 父节点不存在
+    /// </summary>
+    [ErrorCodeItemMetadata("父节点不存在")]
+    R2000,
+
+    /// <summary>
+    /// 当前节点Id不能与父节点Id相同
+    /// </summary>
+    [ErrorCodeItemMetadata("当前节点Id不能与父节点Id相同")]
+    R2001,
+
+    /// <summary>
+    /// 已有相同编码或名称
+    /// </summary>
+    [ErrorCodeItemMetadata("已有相同编码或名称")]
+    R2002,
 }

+ 9 - 11
Admin.NET/Admin.NET.Core/SeedData/SysRoleMenuSeedData.cs

@@ -16,7 +16,7 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             // 数据面板【admin/252885263003721】
             new SysRoleMenu{ Id=252885263003000, RoleId=252885263003721, MenuId=252885263002100 },
             new SysRoleMenu{ Id=252885263003001, RoleId=252885263003721, MenuId=252885263002110 },
-            new SysRoleMenu{ Id=252885263003002, RoleId=252885263003721, MenuId=252885263002111 },
+            //new SysRoleMenu{ Id=252885263003002, RoleId=252885263003721, MenuId=252885263002111 },
 
             // 系统管理
             new SysRoleMenu{ Id=252885263003100, RoleId=252885263003721, MenuId=252885263002200 },
@@ -76,11 +76,11 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             new SysRoleMenu{ Id=252885263003213, RoleId=252885263003721, MenuId=252885263002332 },
             new SysRoleMenu{ Id=252885263003214, RoleId=252885263003721, MenuId=252885263002333 },
             new SysRoleMenu{ Id=252885263003215, RoleId=252885263003721, MenuId=252885263002334 },
-            // 在线用户
-            new SysRoleMenu{ Id=252885263003221, RoleId=252885263003721, MenuId=252885263002350 },
-            new SysRoleMenu{ Id=252885263003222, RoleId=252885263003721, MenuId=252885263002351 },
-            new SysRoleMenu{ Id=252885263003223, RoleId=252885263003721, MenuId=252885263002352 },
-            new SysRoleMenu{ Id=252885263003224, RoleId=252885263003721, MenuId=252885263002353 },
+            //// 在线用户
+            //new SysRoleMenu{ Id=252885263003221, RoleId=252885263003721, MenuId=252885263002350 },
+            //new SysRoleMenu{ Id=252885263003222, RoleId=252885263003721, MenuId=252885263002351 },
+            //new SysRoleMenu{ Id=252885263003223, RoleId=252885263003721, MenuId=252885263002352 },
+            //new SysRoleMenu{ Id=252885263003224, RoleId=252885263003721, MenuId=252885263002353 },
             // 系统监控
             new SysRoleMenu{ Id=252885263003231, RoleId=252885263003721, MenuId=252885263002360 },
             // 缓存管理
@@ -119,7 +119,7 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             // 数据面板【user1/252885263003722】
             new SysRoleMenu{ Id=252885263004000, RoleId=252885263003722, MenuId=252885263002100 },
             new SysRoleMenu{ Id=252885263004001, RoleId=252885263003722, MenuId=252885263002110 },
-            new SysRoleMenu{ Id=252885263004002, RoleId=252885263003722, MenuId=252885263002111 },
+            //new SysRoleMenu{ Id=252885263004002, RoleId=252885263003722, MenuId=252885263002111 },
             // 系统管理
             new SysRoleMenu{ Id=252885263004100, RoleId=252885263003722, MenuId=252885263002200 },
             // 密码修改
@@ -128,19 +128,17 @@ public class SysRoleMenuSeedData : ISqlSugarEntitySeedData<SysRoleMenu>
             // 数据面板【user3/252885263003724】
             new SysRoleMenu{ Id=252885263005000, RoleId=252885263003724, MenuId=252885263002100 },
             new SysRoleMenu{ Id=252885263005001, RoleId=252885263003724, MenuId=252885263002110 },
-            new SysRoleMenu{ Id=252885263005002, RoleId=252885263003724, MenuId=252885263002111 },
+            //new SysRoleMenu{ Id=252885263005002, RoleId=252885263003724, MenuId=252885263002111 },
             // 系统管理
             new SysRoleMenu{ Id=252885263005100, RoleId=252885263003724, MenuId=252885263002200 },
             // 密码修改
             new SysRoleMenu{ Id=252885263005151, RoleId=252885263003724, MenuId=252885263002260},
 
-
-
             // 其他租户
             // 数据面板【admin1/252885263004721】
             new SysRoleMenu{ Id=252885263101000, RoleId=252885263004721, MenuId=252885263002100 },
             new SysRoleMenu{ Id=252885263101001, RoleId=252885263004721, MenuId=252885263002110 },
-            new SysRoleMenu{ Id=252885263101002, RoleId=252885263004721, MenuId=252885263002111 },
+            //new SysRoleMenu{ Id=252885263101002, RoleId=252885263004721, MenuId=252885263002111 },
             // 系统管理
             new SysRoleMenu{ Id=252885263101100, RoleId=252885263004721, MenuId=252885263002200 },
             // 账号管理

+ 0 - 2
Admin.NET/Admin.NET.Core/Service/File/SysFileService.cs

@@ -209,8 +209,6 @@ public class SysFileService : IDynamicApiController, ITransient
                 Directory.CreateDirectory(filePath);
 
             var realFile = Path.Combine(filePath, finalName);
-            using var stream = File.Create(realFile);
-
             IDetector detector;
             using (var stream = File.Create(realFile)) 
             {

+ 43 - 0
Admin.NET/Admin.NET.Core/Service/Region/Dto/RegionInput.cs

@@ -0,0 +1,43 @@
+namespace Admin.NET.Core.Service;
+
+public class PageRegionInput : BasePageInput
+{
+    /// <summary>
+    /// 父节点Id
+    /// </summary>
+    public long Pid { get; set; }
+
+    /// <summary>
+    /// 名称
+    /// </summary>
+    public string Name { get; set; }
+
+    /// <summary>
+    /// 编码
+    /// </summary>
+    public string Code { get; set; }
+}
+
+public class RegionInput : BaseIdInput
+{
+
+}
+
+[NotTable]
+public class AddRegionInput : SysRegion
+{
+    /// <summary>
+    /// 名称
+    /// </summary>
+    [Required(ErrorMessage = "名称不能为空")]
+    public override string Name { get; set; }
+}
+
+[NotTable]
+public class UpdateRegionInput : AddRegionInput
+{
+}
+
+public class DeleteRegionInput : BaseIdInput
+{
+}

+ 0 - 1
Admin.NET/Admin.NET.Core/Service/Region/RegionUtil.cs

@@ -1 +0,0 @@
-

+ 195 - 0
Admin.NET/Admin.NET.Core/Service/Region/SysRegionService.cs

@@ -0,0 +1,195 @@
+using AngleSharp;
+using AngleSharp.Html.Dom;
+
+namespace Admin.NET.Core.Service;
+
+/// <summary>
+/// 系统行政区域服务
+/// </summary>
+[ApiDescriptionSettings(Order = 100)]
+public class SysRegionService : IDynamicApiController, ITransient
+{
+    private readonly SqlSugarRepository<SysRegion> _sysRegionRep;
+
+    public SysRegionService(SqlSugarRepository<SysRegion> sysRegionRep)
+    {
+        _sysRegionRep = sysRegionRep;
+    }
+
+    /// <summary>
+    /// 获取行政区域列表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpGet("/sysRegion/page")]
+    public async Task<SqlSugarPagedList<SysRegion>> GetRegionPage([FromQuery] PageRegionInput input)
+    {
+        return await _sysRegionRep.AsQueryable()
+            .WhereIF(input.Pid > 0, u => u.Pid == input.Pid || u.Id == input.Pid)
+            .WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name))
+            .WhereIF(!string.IsNullOrWhiteSpace(input.Code), u => u.Code.Contains(input.Code))
+            .ToPagedListAsync(input.Page, input.PageSize);
+    }
+
+    /// <summary>
+    /// 获取行政区域列表
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpGet("/sysRegion/list")]
+    public async Task<List<SysRegion>> GetRegionList([FromQuery] RegionInput input)
+    {
+        return await _sysRegionRep.GetListAsync(u => u.Pid == input.Id);
+    }
+
+    /// <summary>
+    /// 增加行政区域
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost("/sysRegion/add")]
+    public async Task<long> AddRegion(AddRegionInput input)
+    {
+        var isExist = await _sysRegionRep.IsAnyAsync(u => u.Code == input.Code && u.Name == input.Name);
+        if (isExist)
+            throw Oops.Oh(ErrorCodeEnum.R2002);
+
+        var sysRegion = input.Adapt<SysRegion>();
+        var newRegion = await _sysRegionRep.AsInsertable(sysRegion).ExecuteReturnEntityAsync();
+        return newRegion.Id;
+    }
+
+    /// <summary>
+    /// 更新行政区域
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost("/sysRegion/update")]
+    public async Task UpdateRegion(UpdateRegionInput input)
+    {
+        if (input.Pid != 0)
+        {
+            var pRegion = await _sysRegionRep.GetFirstAsync(u => u.Id == input.Pid);
+            _ = pRegion ?? throw Oops.Oh(ErrorCodeEnum.D2000);
+        }
+        if (input.Id == input.Pid)
+            throw Oops.Oh(ErrorCodeEnum.R2001);
+
+        var sysRegion = await _sysRegionRep.GetFirstAsync(u => u.Id == input.Id);
+        var isExist = await _sysRegionRep.IsAnyAsync(u => (u.Name == input.Name && u.Code == input.Code) && u.Id != sysRegion.Id);
+        if (isExist)
+            throw Oops.Oh(ErrorCodeEnum.R2002);
+
+        //// 父Id不能为自己的子节点
+        //var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
+        //var childIdList = regionTreeList.Select(u => u.Id).ToList();
+        //if (childIdList.Contains(input.Pid))
+        //    throw Oops.Oh(ErrorCodeEnum.R2001);
+
+        await _sysRegionRep.AsUpdateable(input.Adapt<SysRegion>()).IgnoreColumns(true).ExecuteCommandAsync();
+    }
+
+    /// <summary>
+    /// 删除行政区域
+    /// </summary>
+    /// <param name="input"></param>
+    /// <returns></returns>
+    [HttpPost("/sysRegion/delete")]
+    public async Task DeleteRegion(DeleteRegionInput input)
+    {
+        var regionTreeList = await _sysRegionRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id, true);
+        var regionIdList = regionTreeList.Select(u => u.Id).ToList();
+        await _sysRegionRep.DeleteAsync(u => regionIdList.Contains(u.Id));
+    }
+
+    /// <summary>
+    /// 同步行政区域
+    /// </summary>
+    /// <returns></returns>
+    [HttpPost("/sysRegion/sync")]
+    public async Task SyncRegion()
+    {
+        await _sysRegionRep.DeleteAsync(u => u.Id > 0);
+
+        // 国家统计局行政区域2021年
+        var url = "http://www.stats.gov.cn/tjsj/tjbz/tjyqhdmhcxhfdm/2021/index.html";
+        var context = BrowsingContext.New(Configuration.Default.WithDefaultLoader());
+        var dom = await context.OpenAsync(url);
+
+        // 省级
+        var itemList = dom.QuerySelectorAll("table.provincetable tr.provincetr td a");
+        foreach (IHtmlAnchorElement item in itemList)
+        {
+            var region = await _sysRegionRep.InsertReturnEntityAsync(new SysRegion
+            {
+                Pid = 0,
+                Name = item.TextContent,
+                Remark = item.Href,
+                Level = 1,
+            });
+
+            // 市级
+            var dom1 = await context.OpenAsync(item.Href);
+            var itemList1 = dom1.QuerySelectorAll("table.citytable tr.citytr td a");
+            for (var i1 = 0; i1 < itemList1.Length; i1 += 2)
+            {
+                var item1 = (IHtmlAnchorElement)itemList1[i1 + 1];
+                var region1 = await _sysRegionRep.InsertReturnEntityAsync(new SysRegion
+                {
+                    Pid = region.Id,
+                    Name = item1.TextContent,
+                    Code = itemList1[i1].TextContent,
+                    Remark = item1.Href,
+                    Level = 2,
+                });
+
+                // 区县级
+                var dom2 = await context.OpenAsync(item1.Href);
+                var itemList2 = dom2.QuerySelectorAll("table.countytable tr.countytr td a");
+                for (var i2 = 0; i2 < itemList2.Length; i2 += 2)
+                {
+                    var item2 = (IHtmlAnchorElement)itemList2[i2 + 1];
+                    var region2 = await _sysRegionRep.InsertReturnEntityAsync(new SysRegion
+                    {
+                        Pid = region1.Id,
+                        Name = item2.TextContent,
+                        Code = itemList2[i2].TextContent,
+                        Remark = item2.Href,
+                        Level = 3,
+                    });
+
+                    // 街道级
+                    var dom3 = await context.OpenAsync(item2.Href);
+                    var itemList3 = dom3.QuerySelectorAll("table.towntable tr.towntr td a");
+                    for (var i3 = 0; i3 < itemList3.Length; i3 += 2)
+                    {
+                        var item3 = (IHtmlAnchorElement)itemList3[i3 + 1];
+                        var region3 = await _sysRegionRep.InsertReturnEntityAsync(new SysRegion
+                        {
+                            Pid = region2.Id,
+                            Name = item3.TextContent,
+                            Code = itemList3[i3].TextContent,
+                            Remark = item3.Href,
+                            Level = 4,
+                        });
+
+                        // 村级
+                        var dom4 = await context.OpenAsync(item3.Href);
+                        var itemList4 = dom4.QuerySelectorAll("table.villagetable tr.villagetr td");
+                        for (var i4 = 0; i4 < itemList4.Length; i4 += 3)
+                        {
+                            await _sysRegionRep.InsertAsync(new SysRegion
+                            {
+                                Pid = region3.Id,
+                                Name = itemList4[i4 + 2].TextContent,
+                                Code = itemList4[i4].TextContent,
+                                CityCode = itemList4[i4 + 1].TextContent,
+                                Level = 5,
+                            });
+                        }
+                    }
+                }
+            }
+        }
+    }
+}

+ 4 - 1
Admin.NET/Admin.NET.Core/Service/Server/SysServerService.cs

@@ -1,4 +1,5 @@
-using AspNetCoreRateLimit;
+using AngleSharp.Html.Parser;
+using AspNetCoreRateLimit;
 using Lazy.Captcha.Core;
 using Magicodes.ExporterAndImporter.Excel;
 using Magicodes.ExporterAndImporter.Pdf;
@@ -94,6 +95,7 @@ public class SysServerService : IDynamicApiController, ITransient
         var parserAssembly = typeof(Parser).Assembly.GetName();
         var nestAssembly = typeof(IElasticClient).Assembly.GetName();
         var limitAssembly = typeof(IpRateLimitMiddleware).Assembly.GetName();
+        var htmlParserAssembly = typeof(HtmlParser).Assembly.GetName();
 
         return new[]
         {
@@ -111,6 +113,7 @@ public class SysServerService : IDynamicApiController, ITransient
             new { parserAssembly.Name, parserAssembly.Version },
             new { nestAssembly.Name, nestAssembly.Version },
             new { limitAssembly.Name, limitAssembly.Version },
+            new { htmlParserAssembly.Name, htmlParserAssembly.Version },
         };
     }
 }

+ 1 - 0
Web/plugins.d.ts

@@ -2,3 +2,4 @@ declare module 'vue-grid-layout';
 declare module 'qrcodejs2-fixes';
 declare module 'splitpanes';
 declare module 'js-cookie';
+declare module 'vform3-builds'

+ 1 - 0
Web/src/api-services/api.ts

@@ -30,6 +30,7 @@ export * from './apis/sys-notice-api';
 export * from './apis/sys-online-user-api';
 export * from './apis/sys-org-api';
 export * from './apis/sys-pos-api';
+export * from './apis/sys-region-api';
 export * from './apis/sys-role-api';
 export * from './apis/sys-server-api';
 export * from './apis/sys-tenant-api';

+ 567 - 0
Web/src/api-services/apis/sys-region-api.ts

@@ -0,0 +1,567 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+import globalAxios, { AxiosResponse, AxiosInstance, AxiosRequestConfig } from 'axios';
+import { Configuration } from '../configuration';
+// Some imports not used depending on template conditions
+// @ts-ignore
+import { BASE_PATH, COLLECTION_FORMATS, RequestArgs, BaseAPI, RequiredError } from '../base';
+import { AddRegionInput } from '../models';
+import { AdminResultInt64 } from '../models';
+import { AdminResultListSysRegion } from '../models';
+import { AdminResultSqlSugarPagedListSysRegion } from '../models';
+import { DeleteRegionInput } from '../models';
+import { UpdateRegionInput } from '../models';
+/**
+ * SysRegionApi - axios parameter creator
+ * @export
+ */
+export const SysRegionApiAxiosParamCreator = function (configuration?: Configuration) {
+    return {
+        /**
+         * 
+         * @summary 增加行政区域
+         * @param {AddRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysRegionAddPost: async (body?: AddRegionInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysRegion/add`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 删除行政区域
+         * @param {DeleteRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysRegionDeletePost: async (body?: DeleteRegionInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysRegion/delete`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取行政区域列表
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysRegionListGet: async (id: number, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            // verify required parameter 'id' is not null or undefined
+            if (id === null || id === undefined) {
+                throw new RequiredError('id','Required parameter id was null or undefined when calling sysRegionListGet.');
+            }
+            const localVarPath = `/sysRegion/list`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            if (id !== undefined) {
+                localVarQueryParameter['Id'] = id;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 获取行政区域列表
+         * @param {number} [pid] 父节点Id
+         * @param {string} [name] 名称
+         * @param {string} [code] 编码
+         * @param {number} [page] 当前页码
+         * @param {number} [pageSize] 页码容量
+         * @param {string} [field] 排序字段
+         * @param {string} [order] 排序方向
+         * @param {string} [descStr] 降序排序
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysRegionPageGet: async (pid?: number, name?: string, code?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysRegion/page`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'GET', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            if (pid !== undefined) {
+                localVarQueryParameter['Pid'] = pid;
+            }
+
+            if (name !== undefined) {
+                localVarQueryParameter['Name'] = name;
+            }
+
+            if (code !== undefined) {
+                localVarQueryParameter['Code'] = code;
+            }
+
+            if (page !== undefined) {
+                localVarQueryParameter['Page'] = page;
+            }
+
+            if (pageSize !== undefined) {
+                localVarQueryParameter['PageSize'] = pageSize;
+            }
+
+            if (field !== undefined) {
+                localVarQueryParameter['Field'] = field;
+            }
+
+            if (order !== undefined) {
+                localVarQueryParameter['Order'] = order;
+            }
+
+            if (descStr !== undefined) {
+                localVarQueryParameter['DescStr'] = descStr;
+            }
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 同步行政区域
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysRegionSyncPost: async (options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysRegion/sync`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+        /**
+         * 
+         * @summary 更新行政区域
+         * @param {UpdateRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        sysRegionUpdatePost: async (body?: UpdateRegionInput, options: AxiosRequestConfig = {}): Promise<RequestArgs> => {
+            const localVarPath = `/sysRegion/update`;
+            // use dummy base URL string because the URL constructor only accepts absolute URLs.
+            const localVarUrlObj = new URL(localVarPath, 'https://example.com');
+            let baseOptions;
+            if (configuration) {
+                baseOptions = configuration.baseOptions;
+            }
+            const localVarRequestOptions :AxiosRequestConfig = { method: 'POST', ...baseOptions, ...options};
+            const localVarHeaderParameter = {} as any;
+            const localVarQueryParameter = {} as any;
+
+            // authentication Bearer required
+
+            localVarHeaderParameter['Content-Type'] = 'application/json-patch+json';
+
+            const query = new URLSearchParams(localVarUrlObj.search);
+            for (const key in localVarQueryParameter) {
+                query.set(key, localVarQueryParameter[key]);
+            }
+            for (const key in options.params) {
+                query.set(key, options.params[key]);
+            }
+            localVarUrlObj.search = (new URLSearchParams(query)).toString();
+            let headersFromBaseOptions = baseOptions && baseOptions.headers ? baseOptions.headers : {};
+            localVarRequestOptions.headers = {...localVarHeaderParameter, ...headersFromBaseOptions, ...options.headers};
+            const needsSerialization = (typeof body !== "string") || localVarRequestOptions.headers['Content-Type'] === 'application/json';
+            localVarRequestOptions.data =  needsSerialization ? JSON.stringify(body !== undefined ? body : {}) : (body || "");
+
+            return {
+                url: localVarUrlObj.pathname + localVarUrlObj.search + localVarUrlObj.hash,
+                options: localVarRequestOptions,
+            };
+        },
+    }
+};
+
+/**
+ * SysRegionApi - functional programming interface
+ * @export
+ */
+export const SysRegionApiFp = function(configuration?: Configuration) {
+    return {
+        /**
+         * 
+         * @summary 增加行政区域
+         * @param {AddRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionAddPost(body?: AddRegionInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultInt64>>> {
+            const localVarAxiosArgs = await SysRegionApiAxiosParamCreator(configuration).sysRegionAddPost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 删除行政区域
+         * @param {DeleteRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionDeletePost(body?: DeleteRegionInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysRegionApiAxiosParamCreator(configuration).sysRegionDeletePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取行政区域列表
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionListGet(id: number, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultListSysRegion>>> {
+            const localVarAxiosArgs = await SysRegionApiAxiosParamCreator(configuration).sysRegionListGet(id, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 获取行政区域列表
+         * @param {number} [pid] 父节点Id
+         * @param {string} [name] 名称
+         * @param {string} [code] 编码
+         * @param {number} [page] 当前页码
+         * @param {number} [pageSize] 页码容量
+         * @param {string} [field] 排序字段
+         * @param {string} [order] 排序方向
+         * @param {string} [descStr] 降序排序
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionPageGet(pid?: number, name?: string, code?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<AdminResultSqlSugarPagedListSysRegion>>> {
+            const localVarAxiosArgs = await SysRegionApiAxiosParamCreator(configuration).sysRegionPageGet(pid, name, code, page, pageSize, field, order, descStr, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 同步行政区域
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionSyncPost(options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysRegionApiAxiosParamCreator(configuration).sysRegionSyncPost(options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+        /**
+         * 
+         * @summary 更新行政区域
+         * @param {UpdateRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionUpdatePost(body?: UpdateRegionInput, options?: AxiosRequestConfig): Promise<(axios?: AxiosInstance, basePath?: string) => Promise<AxiosResponse<void>>> {
+            const localVarAxiosArgs = await SysRegionApiAxiosParamCreator(configuration).sysRegionUpdatePost(body, options);
+            return (axios: AxiosInstance = globalAxios, basePath: string = BASE_PATH) => {
+                const axiosRequestArgs :AxiosRequestConfig = {...localVarAxiosArgs.options, url: basePath + localVarAxiosArgs.url};
+                return axios.request(axiosRequestArgs);
+            };
+        },
+    }
+};
+
+/**
+ * SysRegionApi - factory interface
+ * @export
+ */
+export const SysRegionApiFactory = function (configuration?: Configuration, basePath?: string, axios?: AxiosInstance) {
+    return {
+        /**
+         * 
+         * @summary 增加行政区域
+         * @param {AddRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionAddPost(body?: AddRegionInput, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultInt64>> {
+            return SysRegionApiFp(configuration).sysRegionAddPost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 删除行政区域
+         * @param {DeleteRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionDeletePost(body?: DeleteRegionInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysRegionApiFp(configuration).sysRegionDeletePost(body, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取行政区域列表
+         * @param {number} id 主键Id
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionListGet(id: number, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultListSysRegion>> {
+            return SysRegionApiFp(configuration).sysRegionListGet(id, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 获取行政区域列表
+         * @param {number} [pid] 父节点Id
+         * @param {string} [name] 名称
+         * @param {string} [code] 编码
+         * @param {number} [page] 当前页码
+         * @param {number} [pageSize] 页码容量
+         * @param {string} [field] 排序字段
+         * @param {string} [order] 排序方向
+         * @param {string} [descStr] 降序排序
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionPageGet(pid?: number, name?: string, code?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig): Promise<AxiosResponse<AdminResultSqlSugarPagedListSysRegion>> {
+            return SysRegionApiFp(configuration).sysRegionPageGet(pid, name, code, page, pageSize, field, order, descStr, options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 同步行政区域
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionSyncPost(options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysRegionApiFp(configuration).sysRegionSyncPost(options).then((request) => request(axios, basePath));
+        },
+        /**
+         * 
+         * @summary 更新行政区域
+         * @param {UpdateRegionInput} [body] 
+         * @param {*} [options] Override http request option.
+         * @throws {RequiredError}
+         */
+        async sysRegionUpdatePost(body?: UpdateRegionInput, options?: AxiosRequestConfig): Promise<AxiosResponse<void>> {
+            return SysRegionApiFp(configuration).sysRegionUpdatePost(body, options).then((request) => request(axios, basePath));
+        },
+    };
+};
+
+/**
+ * SysRegionApi - object-oriented interface
+ * @export
+ * @class SysRegionApi
+ * @extends {BaseAPI}
+ */
+export class SysRegionApi extends BaseAPI {
+    /**
+     * 
+     * @summary 增加行政区域
+     * @param {AddRegionInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysRegionApi
+     */
+    public async sysRegionAddPost(body?: AddRegionInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultInt64>> {
+        return SysRegionApiFp(this.configuration).sysRegionAddPost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 删除行政区域
+     * @param {DeleteRegionInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysRegionApi
+     */
+    public async sysRegionDeletePost(body?: DeleteRegionInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysRegionApiFp(this.configuration).sysRegionDeletePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取行政区域列表
+     * @param {number} id 主键Id
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysRegionApi
+     */
+    public async sysRegionListGet(id: number, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultListSysRegion>> {
+        return SysRegionApiFp(this.configuration).sysRegionListGet(id, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 获取行政区域列表
+     * @param {number} [pid] 父节点Id
+     * @param {string} [name] 名称
+     * @param {string} [code] 编码
+     * @param {number} [page] 当前页码
+     * @param {number} [pageSize] 页码容量
+     * @param {string} [field] 排序字段
+     * @param {string} [order] 排序方向
+     * @param {string} [descStr] 降序排序
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysRegionApi
+     */
+    public async sysRegionPageGet(pid?: number, name?: string, code?: string, page?: number, pageSize?: number, field?: string, order?: string, descStr?: string, options?: AxiosRequestConfig) : Promise<AxiosResponse<AdminResultSqlSugarPagedListSysRegion>> {
+        return SysRegionApiFp(this.configuration).sysRegionPageGet(pid, name, code, page, pageSize, field, order, descStr, options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 同步行政区域
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysRegionApi
+     */
+    public async sysRegionSyncPost(options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysRegionApiFp(this.configuration).sysRegionSyncPost(options).then((request) => request(this.axios, this.basePath));
+    }
+    /**
+     * 
+     * @summary 更新行政区域
+     * @param {UpdateRegionInput} [body] 
+     * @param {*} [options] Override http request option.
+     * @throws {RequiredError}
+     * @memberof SysRegionApi
+     */
+    public async sysRegionUpdatePost(body?: UpdateRegionInput, options?: AxiosRequestConfig) : Promise<AxiosResponse<void>> {
+        return SysRegionApiFp(this.configuration).sysRegionUpdatePost(body, options).then((request) => request(this.axios, this.basePath));
+    }
+}

+ 111 - 0
Web/src/api-services/models/add-region-input.ts

@@ -0,0 +1,111 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+import { SysRegion } from './sys-region';
+/**
+ * 
+ * @export
+ * @interface AddRegionInput
+ */
+export interface AddRegionInput {
+    /**
+     * 雪花Id
+     * @type {number}
+     * @memberof AddRegionInput
+     */
+    id?: number;
+    /**
+     * 父Id
+     * @type {number}
+     * @memberof AddRegionInput
+     */
+    pid?: number;
+    /**
+     * 简称
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    shortName?: string | null;
+    /**
+     * 组合名
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    mergerName?: string | null;
+    /**
+     * 行政代码
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    code?: string | null;
+    /**
+     * 邮政编码
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    zipCode?: string | null;
+    /**
+     * 区号
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    cityCode?: string | null;
+    /**
+     * 层级
+     * @type {number}
+     * @memberof AddRegionInput
+     */
+    level?: number;
+    /**
+     * 拼音
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    pinYin?: string | null;
+    /**
+     * 经度
+     * @type {number}
+     * @memberof AddRegionInput
+     */
+    lng?: number;
+    /**
+     * 维度
+     * @type {number}
+     * @memberof AddRegionInput
+     */
+    lat?: number;
+    /**
+     * 排序
+     * @type {number}
+     * @memberof AddRegionInput
+     */
+    order?: number;
+    /**
+     * 备注
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    remark?: string | null;
+    /**
+     * 机构子项
+     * @type {Array<SysRegion>}
+     * @memberof AddRegionInput
+     */
+    children?: Array<SysRegion> | null;
+    /**
+     * 名称
+     * @type {string}
+     * @memberof AddRegionInput
+     */
+    name: string;
+}

+ 57 - 0
Web/src/api-services/models/admin-result-list-sys-region.ts

@@ -0,0 +1,57 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+import { SysRegion } from './sys-region';
+/**
+ * 全局返回结果
+ * @export
+ * @interface AdminResultListSysRegion
+ */
+export interface AdminResultListSysRegion {
+    /**
+     * 状态码
+     * @type {number}
+     * @memberof AdminResultListSysRegion
+     */
+    code?: number;
+    /**
+     * 类型success、warning、error
+     * @type {string}
+     * @memberof AdminResultListSysRegion
+     */
+    type?: string | null;
+    /**
+     * 错误信息
+     * @type {string}
+     * @memberof AdminResultListSysRegion
+     */
+    message?: string | null;
+    /**
+     * 数据
+     * @type {Array<SysRegion>}
+     * @memberof AdminResultListSysRegion
+     */
+    result?: Array<SysRegion> | null;
+    /**
+     * 附加数据
+     * @type {any}
+     * @memberof AdminResultListSysRegion
+     */
+    extras?: any | null;
+    /**
+     * 时间
+     * @type {Date}
+     * @memberof AdminResultListSysRegion
+     */
+    time?: Date;
+}

+ 57 - 0
Web/src/api-services/models/admin-result-sql-sugar-paged-list-sys-region.ts

@@ -0,0 +1,57 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+import { SqlSugarPagedListSysRegion } from './sql-sugar-paged-list-sys-region';
+/**
+ * 全局返回结果
+ * @export
+ * @interface AdminResultSqlSugarPagedListSysRegion
+ */
+export interface AdminResultSqlSugarPagedListSysRegion {
+    /**
+     * 状态码
+     * @type {number}
+     * @memberof AdminResultSqlSugarPagedListSysRegion
+     */
+    code?: number;
+    /**
+     * 类型success、warning、error
+     * @type {string}
+     * @memberof AdminResultSqlSugarPagedListSysRegion
+     */
+    type?: string | null;
+    /**
+     * 错误信息
+     * @type {string}
+     * @memberof AdminResultSqlSugarPagedListSysRegion
+     */
+    message?: string | null;
+    /**
+     * 
+     * @type {SqlSugarPagedListSysRegion}
+     * @memberof AdminResultSqlSugarPagedListSysRegion
+     */
+    result?: SqlSugarPagedListSysRegion;
+    /**
+     * 附加数据
+     * @type {any}
+     * @memberof AdminResultSqlSugarPagedListSysRegion
+     */
+    extras?: any | null;
+    /**
+     * 时间
+     * @type {Date}
+     * @memberof AdminResultSqlSugarPagedListSysRegion
+     */
+    time?: Date;
+}

+ 26 - 0
Web/src/api-services/models/delete-region-input.ts

@@ -0,0 +1,26 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+/**
+ * 
+ * @export
+ * @interface DeleteRegionInput
+ */
+export interface DeleteRegionInput {
+    /**
+     * 主键Id
+     * @type {number}
+     * @memberof DeleteRegionInput
+     */
+    id: number;
+}

+ 7 - 0
Web/src/api-services/models/index.ts

@@ -7,6 +7,7 @@ export * from './add-menu-input';
 export * from './add-notice-input';
 export * from './add-org-input';
 export * from './add-pos-input';
+export * from './add-region-input';
 export * from './add-role-input';
 export * from './add-tenant-input';
 export * from './add-timer-input';
@@ -33,6 +34,7 @@ export * from './admin-result-list-sys-dict-type';
 export * from './admin-result-list-sys-menu';
 export * from './admin-result-list-sys-org';
 export * from './admin-result-list-sys-pos';
+export * from './admin-result-list-sys-region';
 export * from './admin-result-list-sys-user-ext-org';
 export * from './admin-result-list-table-column-ouput';
 export * from './admin-result-list-table-output';
@@ -50,6 +52,7 @@ export * from './admin-result-sql-sugar-paged-list-sys-log-ex';
 export * from './admin-result-sql-sugar-paged-list-sys-log-op';
 export * from './admin-result-sql-sugar-paged-list-sys-log-vis';
 export * from './admin-result-sql-sugar-paged-list-sys-online-user';
+export * from './admin-result-sql-sugar-paged-list-sys-region';
 export * from './admin-result-sql-sugar-paged-list-sys-role';
 export * from './admin-result-sql-sugar-paged-list-sys-tenant';
 export * from './admin-result-sql-sugar-paged-list-sys-user';
@@ -88,6 +91,7 @@ export * from './delete-menu-input';
 export * from './delete-notice-input';
 export * from './delete-org-input';
 export * from './delete-pos-input';
+export * from './delete-region-input';
 export * from './delete-role-input';
 export * from './delete-tenant-input';
 export * from './delete-timer-input';
@@ -134,6 +138,7 @@ export * from './sql-sugar-paged-list-sys-log-ex';
 export * from './sql-sugar-paged-list-sys-log-op';
 export * from './sql-sugar-paged-list-sys-log-vis';
 export * from './sql-sugar-paged-list-sys-online-user';
+export * from './sql-sugar-paged-list-sys-region';
 export * from './sql-sugar-paged-list-sys-role';
 export * from './sql-sugar-paged-list-sys-tenant';
 export * from './sql-sugar-paged-list-sys-user';
@@ -157,6 +162,7 @@ export * from './sys-menu-meta';
 export * from './sys-online-user';
 export * from './sys-org';
 export * from './sys-pos';
+export * from './sys-region';
 export * from './sys-role';
 export * from './sys-tenant';
 export * from './sys-user';
@@ -177,6 +183,7 @@ export * from './update-menu-input';
 export * from './update-notice-input';
 export * from './update-org-input';
 export * from './update-pos-input';
+export * from './update-region-input';
 export * from './update-role-input';
 export * from './update-tenant-input';
 export * from './update-timer-input';

+ 63 - 0
Web/src/api-services/models/sql-sugar-paged-list-sys-region.ts

@@ -0,0 +1,63 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+import { SysRegion } from './sys-region';
+/**
+ * 分页泛型集合
+ * @export
+ * @interface SqlSugarPagedListSysRegion
+ */
+export interface SqlSugarPagedListSysRegion {
+    /**
+     * 页码
+     * @type {number}
+     * @memberof SqlSugarPagedListSysRegion
+     */
+    page?: number;
+    /**
+     * 页容量
+     * @type {number}
+     * @memberof SqlSugarPagedListSysRegion
+     */
+    pageSize?: number;
+    /**
+     * 总条数
+     * @type {number}
+     * @memberof SqlSugarPagedListSysRegion
+     */
+    total?: number;
+    /**
+     * 总页数
+     * @type {number}
+     * @memberof SqlSugarPagedListSysRegion
+     */
+    totalPages?: number;
+    /**
+     * 当前页集合
+     * @type {Array<SysRegion>}
+     * @memberof SqlSugarPagedListSysRegion
+     */
+    items?: Array<SysRegion> | null;
+    /**
+     * 是否有上一页
+     * @type {boolean}
+     * @memberof SqlSugarPagedListSysRegion
+     */
+    hasPrevPage?: boolean;
+    /**
+     * 是否有下一页
+     * @type {boolean}
+     * @memberof SqlSugarPagedListSysRegion
+     */
+    hasNextPage?: boolean;
+}

+ 111 - 0
Web/src/api-services/models/sys-region.ts

@@ -0,0 +1,111 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+import { SysRegion } from './sys-region';
+/**
+ * 系统行政地区表
+ * @export
+ * @interface SysRegion
+ */
+export interface SysRegion {
+    /**
+     * 雪花Id
+     * @type {number}
+     * @memberof SysRegion
+     */
+    id?: number;
+    /**
+     * 父Id
+     * @type {number}
+     * @memberof SysRegion
+     */
+    pid?: number;
+    /**
+     * 名称
+     * @type {string}
+     * @memberof SysRegion
+     */
+    name: string;
+    /**
+     * 简称
+     * @type {string}
+     * @memberof SysRegion
+     */
+    shortName?: string | null;
+    /**
+     * 组合名
+     * @type {string}
+     * @memberof SysRegion
+     */
+    mergerName?: string | null;
+    /**
+     * 行政代码
+     * @type {string}
+     * @memberof SysRegion
+     */
+    code?: string | null;
+    /**
+     * 邮政编码
+     * @type {string}
+     * @memberof SysRegion
+     */
+    zipCode?: string | null;
+    /**
+     * 区号
+     * @type {string}
+     * @memberof SysRegion
+     */
+    cityCode?: string | null;
+    /**
+     * 层级
+     * @type {number}
+     * @memberof SysRegion
+     */
+    level?: number;
+    /**
+     * 拼音
+     * @type {string}
+     * @memberof SysRegion
+     */
+    pinYin?: string | null;
+    /**
+     * 经度
+     * @type {number}
+     * @memberof SysRegion
+     */
+    lng?: number;
+    /**
+     * 维度
+     * @type {number}
+     * @memberof SysRegion
+     */
+    lat?: number;
+    /**
+     * 排序
+     * @type {number}
+     * @memberof SysRegion
+     */
+    order?: number;
+    /**
+     * 备注
+     * @type {string}
+     * @memberof SysRegion
+     */
+    remark?: string | null;
+    /**
+     * 机构子项
+     * @type {Array<SysRegion>}
+     * @memberof SysRegion
+     */
+    children?: Array<SysRegion> | null;
+}

+ 111 - 0
Web/src/api-services/models/update-region-input.ts

@@ -0,0 +1,111 @@
+/* tslint:disable */
+/* eslint-disable */
+/**
+ * Admin.NET
+ * 让 .NET 开发更简单、更通用、更流行。前后端分离架构(.NET6/Vue3),开箱即用紧随前沿技术。<br/><a href='https://gitee.com/zuohuaijun/Admin.NET/'>https://gitee.com/zuohuaijun/Admin.NET</a>
+ *
+ * OpenAPI spec version: 1.0.0
+ * Contact: 515096995@qq.com
+ *
+ * NOTE: This class is auto generated by the swagger code generator program.
+ * https://github.com/swagger-api/swagger-codegen.git
+ * Do not edit the class manually.
+ */
+import { SysRegion } from './sys-region';
+/**
+ * 
+ * @export
+ * @interface UpdateRegionInput
+ */
+export interface UpdateRegionInput {
+    /**
+     * 雪花Id
+     * @type {number}
+     * @memberof UpdateRegionInput
+     */
+    id?: number;
+    /**
+     * 父Id
+     * @type {number}
+     * @memberof UpdateRegionInput
+     */
+    pid?: number;
+    /**
+     * 简称
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    shortName?: string | null;
+    /**
+     * 组合名
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    mergerName?: string | null;
+    /**
+     * 行政代码
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    code?: string | null;
+    /**
+     * 邮政编码
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    zipCode?: string | null;
+    /**
+     * 区号
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    cityCode?: string | null;
+    /**
+     * 层级
+     * @type {number}
+     * @memberof UpdateRegionInput
+     */
+    level?: number;
+    /**
+     * 拼音
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    pinYin?: string | null;
+    /**
+     * 经度
+     * @type {number}
+     * @memberof UpdateRegionInput
+     */
+    lng?: number;
+    /**
+     * 维度
+     * @type {number}
+     * @memberof UpdateRegionInput
+     */
+    lat?: number;
+    /**
+     * 排序
+     * @type {number}
+     * @memberof UpdateRegionInput
+     */
+    order?: number;
+    /**
+     * 备注
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    remark?: string | null;
+    /**
+     * 机构子项
+     * @type {Array<SysRegion>}
+     * @memberof UpdateRegionInput
+     */
+    children?: Array<SysRegion> | null;
+    /**
+     * 名称
+     * @type {string}
+     * @memberof UpdateRegionInput
+     */
+    name: string;
+}

+ 117 - 0
Web/src/views/system/region/component/editRegion.vue

@@ -0,0 +1,117 @@
+<template>
+	<div class="sys-region-container">
+		<el-dialog v-model="isShowDialog" width="600px">
+			<template #header>
+				<div style="font-size: large" v-drag="['.el-dialog', '.el-dialog__header']">{{ title }}</div>
+			</template>
+			<el-form :model="ruleForm" ref="ruleFormRef" size="default" label-width="80px">
+				<el-row :gutter="35">
+					<!-- <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="上级名称">
+							<el-cascader
+								:options="regionData"
+								:props="{ checkStrictly: true, emitPath: false, value: 'id', label: 'name' }"
+								placeholder="请选择上级名称"
+								clearable
+								class="w100"
+								v-model="ruleForm.pid"
+							>
+								<template #default="{ node, data }">
+									<span>{{ data.name }}</span>
+									<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
+								</template>
+							</el-cascader>
+						</el-form-item>
+					</el-col> -->
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="行政名称" prop="name" :rules="[{ required: true, message: '行政名称不能为空', trigger: 'blur' }]">
+							<el-input v-model="ruleForm.name" placeholder="行政名称" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="行政代码" prop="code" :rules="[{ required: true, message: '行政代码不能为空', trigger: 'blur' }]">
+							<el-input v-model="ruleForm.code" placeholder="行政代码" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="区号" prop="cityCode">
+							<el-input v-model="ruleForm.cityCode" placeholder="区号" clearable />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb20">
+						<el-form-item label="排序">
+							<el-input-number v-model="ruleForm.order" placeholder="排序" class="w100" />
+						</el-form-item>
+					</el-col>
+					<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24" class="mb20">
+						<el-form-item label="备注">
+							<el-input v-model="ruleForm.remark" placeholder="请输入备注内容" clearable type="textarea" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</el-form>
+			<template #footer>
+				<span class="dialog-footer">
+					<el-button @click="cancel" size="default">取 消</el-button>
+					<el-button type="primary" @click="submit" size="default">确 定</el-button>
+				</span>
+			</template>
+		</el-dialog>
+	</div>
+</template>
+
+<script lang="ts">
+import { reactive, toRefs, defineComponent, getCurrentInstance, ref } from 'vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysRegionApi } from '/@/api-services/api';
+import { UpdateRegionInput } from '/@/api-services/models';
+
+export default defineComponent({
+	name: 'sysEditRegion',
+	components: {},
+	props: {
+		title: {
+			type: String,
+			default: '',
+		},
+	},
+	setup() {
+		const { proxy } = getCurrentInstance() as any;
+		const ruleFormRef = ref();
+		const state = reactive({
+			isShowDialog: false,
+			ruleForm: {} as UpdateRegionInput,
+		});
+		// 打开弹窗
+		const openDialog = (row: any) => {
+			state.ruleForm = row;
+			state.isShowDialog = true;
+		};
+		// 取消
+		const cancel = () => {
+			state.isShowDialog = false;
+		};
+		// 提交
+		const submit = () => {
+			ruleFormRef.value.validate(async (valid: boolean) => {
+				if (!valid) return;
+				if (state.ruleForm.id != undefined && state.ruleForm.id > 0) {
+					await getAPI(SysRegionApi).sysRegionUpdatePost(state.ruleForm);
+				} else {
+					await getAPI(SysRegionApi).sysRegionAddPost(state.ruleForm);
+				}
+				proxy.mittBus.emit('submitRefresh');
+				state.isShowDialog = false;
+			});
+		};
+		return {
+			ruleFormRef,
+			openDialog,
+			cancel,
+			submit,
+			...toRefs(state),
+		};
+	},
+});
+</script>

+ 140 - 0
Web/src/views/system/region/component/regionTree.vue

@@ -0,0 +1,140 @@
+<template>
+	<el-card class="box-card" shadow="hover" style="height: 100%" body-style="height:100%; overflow:auto">
+		<template #header>
+			<div class="card-header">
+				<div class="tree-h-flex">
+					<div class="tree-h-left">
+						<el-input :prefix-icon="Search" v-model="filterText" placeholder="行政区域名称" />
+					</div>
+					<div class="tree-h-right">
+						<el-dropdown @command="handleCommand">
+							<el-button style="margin-left: 8px; width: 34px">
+								<el-icon class="el-icon--center">
+									<more-filled />
+								</el-icon>
+							</el-button>
+							<template #dropdown>
+								<el-dropdown-menu>
+									<el-dropdown-item command="expandAll">全部展开</el-dropdown-item>
+									<el-dropdown-item command="collapseAll">全部折叠</el-dropdown-item>
+									<el-dropdown-item command="rootNode">根节点</el-dropdown-item>
+									<el-dropdown-item command="refresh">刷新</el-dropdown-item>
+								</el-dropdown-menu>
+							</template>
+						</el-dropdown>
+					</div>
+				</div>
+			</div>
+		</template>
+		<div style="margin-bottom: 45px" v-loading="state.loading">
+			<el-tree
+				ref="treeRef"
+				class="filter-tree"
+				:data="state.regionData"
+				node-key="id"
+				:props="{ children: 'children', label: 'name' }"
+				:filter-node-method="filterNode"
+				@node-click="nodeClick"
+				highlight-current
+				check-strictly
+				accordion
+				lazy
+				:load="loadNode"
+			/>
+		</div>
+	</el-card>
+</template>
+
+<script lang="ts" setup>
+import { onMounted, reactive, ref, watch } from 'vue';
+import type { ElTree } from 'element-plus';
+import { Search, MoreFilled } from '@element-plus/icons-vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysRegionApi } from '/@/api-services/api';
+import { SysRegion } from '/@/api-services/models';
+
+const filterText = ref('');
+const treeRef = ref<InstanceType<typeof ElTree>>();
+
+const state = reactive({
+	loading: false,
+	regionData: [] as Array<SysRegion>,
+});
+
+onMounted(() => {
+	initTreeData();
+});
+
+watch(filterText, (val) => {
+	treeRef.value!.filter(val);
+});
+
+const initTreeData = async () => {
+	state.loading = true;
+	var res = await getAPI(SysRegionApi).sysRegionListGet(0);
+	state.regionData = res.data.result ?? [];
+	state.loading = false;
+};
+
+const loadNode = async (node: any, resolve: any) => {
+	if (node.data == undefined || Array.isArray(node.data)) return;
+
+	state.loading = true;
+	var res = await getAPI(SysRegionApi).sysRegionListGet(node.data.id);
+	var data = res.data.result ?? [];
+	state.loading = false;
+	resolve(data);
+};
+
+// 获取已经选择
+const getCheckedKeys = () => {
+	return treeRef.value!.getCheckedKeys();
+};
+
+const filterNode = (value: string, data: any) => {
+	if (!value) return true;
+	return data.name.includes(value);
+};
+
+const handleCommand = async (command: string | number | object) => {
+	if ('expandAll' == command) {
+		for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
+			treeRef.value!.store._getAllNodes()[i].expanded = true;
+		}
+	} else if ('collapseAll' == command) {
+		for (let i = 0; i < treeRef.value!.store._getAllNodes().length; i++) {
+			treeRef.value!.store._getAllNodes()[i].expanded = false;
+		}
+	} else if ('refresh' == command) {
+		initTreeData();
+	} else if ('rootNode' == command) {
+		emits('node-click', { id: 0, name: '' });
+	}
+};
+
+// 与父组件的交互逻辑
+const emits = defineEmits(['node-click']);
+const nodeClick = (node: any) => {
+	emits('node-click', { id: node.id, name: node.name });
+};
+
+// 导出
+defineExpose({ initTreeData, getCheckedKeys });
+</script>
+
+<style scoped>
+.tree-h-flex {
+	display: flex;
+}
+
+.tree-h-left {
+	flex: 1;
+	width: 100%;
+}
+
+.tree-h-right {
+	width: 42px;
+	min-width: 42px;
+}
+</style>

+ 193 - 0
Web/src/views/system/region/index.vue

@@ -0,0 +1,193 @@
+<template>
+	<div class="sys-region-container">
+		<el-row :gutter="8" style="width: 100%">
+			<el-col :span="6" :xs="24">
+				<RegionTree ref="regionTreeRef" @node-click="nodeClick" />
+			</el-col>
+
+			<el-col :span="18" :xs="24">
+				<el-card shadow="hover" :body-style="{ paddingBottom: '0' }">
+					<el-form :model="queryParams" ref="queryForm" :inline="true">
+						<el-form-item label="行政名称" prop="name">
+							<el-input placeholder="行政名称" clearable @keyup.enter="handleQuery" v-model="queryParams.name" />
+						</el-form-item>
+						<el-form-item label="行政代码" prop="code">
+							<el-input placeholder="行政代码" clearable @keyup.enter="handleQuery" v-model="queryParams.code" />
+						</el-form-item>
+						<el-form-item>
+							<el-button icon="ele-Refresh" @click="resetQuery"> 重置 </el-button>
+							<el-button type="primary" icon="ele-Search" @click="handleQuery" v-auth="'sysRegion:page'"> 查询 </el-button>
+							<el-button icon="ele-Plus" @click="openAddRegion" v-auth="'sysRegion:add'"> 新增 </el-button>
+							<el-button type="danger" icon="ele-Lightning" @click="handlSync" v-auth="'sysRegion:sync'"> 同步国家统计局 </el-button>
+						</el-form-item>
+					</el-form>
+				</el-card>
+
+				<el-card shadow="hover" style="margin-top: 8px">
+					<el-table :data="regionData" style="width: 100%" v-loading="loading" row-key="id" default-expand-all :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" border>
+						<el-table-column prop="name" label="行政名称" show-overflow-tooltip />
+						<el-table-column prop="code" label="行政代码" show-overflow-tooltip />
+						<el-table-column prop="cityCode" label="区号" show-overflow-tooltip />
+						<el-table-column prop="order" label="排序" width="70" align="center" show-overflow-tooltip />
+						<el-table-column prop="remark" label="备注" show-overflow-tooltip />
+						<el-table-column label="操作" width="140" fixed="right" align="center" show-overflow-tooltip>
+							<template #default="scope">
+								<el-button icon="ele-Edit" size="small" text type="primary" @click="openEditRegion(scope.row)" v-auth="'sysRegion:update'"> 编辑 </el-button>
+								<el-button icon="ele-Delete" size="small" text type="danger" @click="delRegion(scope.row)" v-auth="'sysRegion:delete'"> 删除 </el-button>
+							</template>
+						</el-table-column>
+					</el-table>
+					<el-pagination
+						v-model:currentPage="tableParams.page"
+						v-model:page-size="tableParams.pageSize"
+						:total="tableParams.total"
+						:page-sizes="[10, 20, 50, 100]"
+						small
+						background
+						@size-change="handleSizeChange"
+						@current-change="handleCurrentChange"
+						layout="total, sizes, prev, pager, next, jumper"
+					/>
+				</el-card>
+			</el-col>
+		</el-row>
+		<EditRegion ref="editRegionRef" :title="editRegionTitle" />
+	</div>
+</template>
+
+<script lang="ts">
+import { ref, toRefs, reactive, onMounted, defineComponent, getCurrentInstance, onUnmounted } from 'vue';
+import { ElMessageBox, ElMessage, ElNotification } from 'element-plus';
+import RegionTree from '/@/views/system/region/component/regionTree.vue';
+import EditRegion from '/@/views/system/region/component/editRegion.vue';
+
+import { getAPI } from '/@/utils/axios-utils';
+import { SysRegionApi } from '/@/api-services/api';
+import { SysRegion } from '/@/api-services/models';
+
+export default defineComponent({
+	name: 'sysRegion',
+	components: { RegionTree, EditRegion },
+	setup() {
+		const { proxy } = getCurrentInstance() as any;
+		const editRegionRef = ref();
+		const regionTreeRef = ref();
+		const state = reactive({
+			loading: false,
+			regionData: [] as Array<SysRegion>, // 列表数据
+			queryParams: {
+				id: -1,
+				name: undefined,
+				code: undefined,
+			},
+			tableParams: {
+				page: 1,
+				pageSize: 10,
+				total: 0 as any,
+			},
+			editRegionTitle: '',
+		});
+		onMounted(() => {
+			handleQuery();
+
+			proxy.mittBus.on('submitRefresh', async () => {
+				handleQuery();
+
+				// 编辑删除后更新机构数据
+				regionTreeRef.value.initTreeData();
+			});
+		});
+		onUnmounted(() => {
+			proxy.mittBus.off('submitRefresh');
+		});
+		// 查询操作
+		const handleQuery = async () => {
+			state.loading = true;
+			var res = await getAPI(SysRegionApi).sysRegionPageGet(state.queryParams.id, state.queryParams.name, state.queryParams.code, state.tableParams.page, state.tableParams.pageSize);
+			state.regionData = res.data.result?.items ?? [];
+			state.tableParams.total = res.data.result?.total;
+			state.loading = false;
+		};
+		// 重置操作
+		const resetQuery = () => {
+			state.queryParams.id = -1;
+			state.queryParams.name = undefined;
+			state.queryParams.code = undefined;
+			handleQuery();
+		};
+		// 打开新增页面
+		const openAddRegion = () => {
+			state.editRegionTitle = '添加行政区域';
+			editRegionRef.value.openDialog({});
+		};
+		// 打开编辑页面
+		const openEditRegion = (row: any) => {
+			state.editRegionTitle = '编辑行政区域';
+			editRegionRef.value.openDialog(row);
+		};
+		// 删除
+		const delRegion = (row: any) => {
+			ElMessageBox.confirm(`确定删除行政区域:【${row.name}】?`, '提示', {
+				confirmButtonText: '确定',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(async () => {
+					await getAPI(SysRegionApi).sysRegionDeletePost({ id: row.id });
+					ElMessage.success('删除成功');
+					proxy.mittBus.emit('submitRefresh');
+				})
+				.catch(() => {});
+		};
+		// 树组件点击
+		const nodeClick = async (node: any) => {
+			state.queryParams.id = node.id;
+			state.queryParams.name = undefined;
+			state.queryParams.code = undefined;
+			handleQuery();
+		};
+		// 同步国家统计局操作
+		const handlSync = async () => {
+			ElMessageBox.confirm('确认同步国家统计局行政区域数据?', '提示', {
+				confirmButtonText: '确定',
+				cancelButtonText: '取消',
+				type: 'warning',
+			})
+				.then(async () => {
+					ElNotification({
+						title: '提示',
+						message: '后台努力同步中...',
+						type: 'success',
+						position: 'bottom-right',
+					});
+					await getAPI(SysRegionApi).sysRegionSyncPost({ timeout: 1000 * 60 * 30 });
+				})
+				.catch(() => {});
+		};
+		// 改变页面容量
+		const handleSizeChange = (val: number) => {
+			state.tableParams.pageSize = val;
+			handleQuery();
+		};
+		// 改变页码序号
+		const handleCurrentChange = (val: number) => {
+			state.tableParams.page = val;
+			handleQuery();
+		};
+		return {
+			handleQuery,
+			resetQuery,
+			editRegionRef,
+			regionTreeRef,
+			openAddRegion,
+			openEditRegion,
+			delRegion,
+			nodeClick,
+			handlSync,
+			handleSizeChange,
+			handleCurrentChange,
+			...toRefs(state),
+		};
+	},
+});
+</script>