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

Merge commit 'refs/pull/309/head' of https://gitee.com/zuohuaijun/Admin.NET into next

zuohuaijun 4 лет назад
Родитель
Сommit
dbef785469

+ 150 - 0
Admin.NET/Admin.NET.Core/Admin.NET.Core.xml

@@ -511,6 +511,51 @@
             备注
             </summary>
         </member>
+        <member name="T:Admin.NET.Core.SysDataResource">
+             <summary>
+              数据资源表
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Pid">
+             <summary>
+             父节点Id 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Name">
+             <summary>
+             名称 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Value">
+             <summary>
+             值 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Code">
+             <summary>
+             节点编码 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Order">
+             <summary>
+             排序 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Remark">
+             <summary>
+             备注 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Status">
+             <summary>
+             状态:是否启用 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.SysDataResource.Children">
+            <summary>
+            数据资源子项
+            </summary>
+        </member>
         <member name="T:Admin.NET.Core.SysDictData">
             <summary>
             系统字典值表
@@ -2062,6 +2107,21 @@
             父菜单不存在
             </summary>
         </member>
+        <member name="F:Admin.NET.Core.ErrorCodeEnum.D1600">
+            <summary>
+            父资源不存在
+            </summary>
+        </member>
+        <member name="F:Admin.NET.Core.ErrorCodeEnum.D1601">
+            <summary>
+            当前资源Id不能与父资源Id相同
+            </summary>
+        </member>
+        <member name="F:Admin.NET.Core.ErrorCodeEnum.D1602">
+            <summary>
+            已有相同数据资源,编码或名称相同
+            </summary>
+        </member>
         <member name="F:Admin.NET.Core.ErrorCodeEnum.xg1000">
             <summary>
             已存在同名或同编码项目
@@ -2728,6 +2788,17 @@
             </summary>
             <returns></returns>
         </member>
+        <member name="T:Admin.NET.Core.SeedData.SysDataResourceSeedData">
+            <summary>
+            系统数据资源种子数据
+            </summary>
+        </member>
+        <member name="M:Admin.NET.Core.SeedData.SysDataResourceSeedData.HasData">
+            <summary>
+            种子数据
+            </summary>
+            <returns></returns>
+        </member>
         <member name="T:Admin.NET.Core.SeedData.SysDictDataSeedData">
             <summary>
             系统字典值表种子数据
@@ -3888,6 +3959,85 @@
              </summary>
              <example>Magic.Application</example>
         </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.DataResourceInput.Pid">
+            <summary>
+            父Id
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.DataResourceInput.Name">
+            <summary>
+            名称
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.DataResourceInput.Value">
+             <summary>
+             值 
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.DataResourceInput.Code">
+            <summary>
+            编码
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.DataResourceInput.Order">
+            <summary>
+            排序
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.DataResourceInput.Remark">
+            <summary>
+            备注
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.DataResourceInput.Status">
+            <summary>
+            状态
+            </summary>
+        </member>
+        <member name="P:Admin.NET.Core.Service.DataResources.Dto.AddDataResourceInput.Name">
+            <summary>
+            名称
+            </summary>
+        </member>
+        <member name="T:Admin.NET.Core.Service.DataResource.SysDataResourceService">
+            <summary>
+            系统数据资源服务
+            </summary>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DataResource.SysDataResourceService.GetDataResourceList(Admin.NET.Core.Service.DataResources.Dto.DataResourceInput)">
+            <summary>
+            获取数据资源列表
+            </summary>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DataResource.SysDataResourceService.AddDataResource(Admin.NET.Core.Service.DataResources.Dto.AddDataResourceInput)">
+            <summary>
+            增加数据资源
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DataResource.SysDataResourceService.UpdateDataResource(Admin.NET.Core.Service.DataResources.Dto.UpdateDataResourceInput)">
+            <summary>
+            更新数据资源
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DataResource.SysDataResourceService.DeleteDataResource(Admin.NET.Core.Service.DataResources.Dto.DeleteDataResourceInput)">
+            <summary>
+            删除数据资源
+            </summary>
+            <param name="input"></param>
+            <returns></returns>
+        </member>
+        <member name="M:Admin.NET.Core.Service.DataResource.SysDataResourceService.GetChildIdListWithSelfById(System.Int64)">
+            <summary>
+            根据节点Id获取子节点Id集合(包含自己)
+            </summary>
+            <param name="pid"></param>
+            <returns></returns>
+        </member>
         <member name="P:Admin.NET.Core.Service.GetDataDictDataInput.DictTypeId">
             <summary>
             字典类型Id

+ 62 - 0
Admin.NET/Admin.NET.Core/Entity/SysDataResource.cs

@@ -0,0 +1,62 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using Admin.NET.Core;
+using SqlSugar;
+namespace Admin.NET.Core
+{
+    /// <summary>
+    ///  数据资源表
+    ///</summary>
+    [SugarTable("sys_data_resource", "数据资源表")]
+    [SqlSugarEntity]
+    public class SysDataResource : EntityBase
+    {
+        /// <summary>
+        /// 父节点Id 
+        ///</summary>
+        [SugarColumn(ColumnName = "pid", ColumnDescription = "父Id")]
+        public long Pid { get; set; }
+        /// <summary>
+        /// 名称 
+        ///</summary>
+        [SugarColumn(ColumnName = "name", ColumnDescription = "名称", Length = 200)]
+        [MaxLength(200)]
+        public string Name { get; set; }
+        /// <summary>
+        /// 值 
+        ///</summary>
+        [SugarColumn(ColumnName = "value", ColumnDescription = "值", Length = 200)]
+        [MaxLength(200)]
+        public string Value { get; set; }
+        /// <summary>
+        /// 节点编码 
+        ///</summary>
+        [SugarColumn(ColumnName = "code", ColumnDescription = "节点编码", Length = 100)]
+        [MaxLength(100)]
+        public string Code { get; set; }
+        /// <summary>
+        /// 排序 
+        ///</summary>
+        [SugarColumn(ColumnName = "order", ColumnDescription = "排序")]
+        public int Order { get; set; }
+        /// <summary>
+        /// 备注 
+        ///</summary>
+        [SugarColumn(ColumnName = "remark", ColumnDescription = "备注", Length = 200)]
+        [MaxLength(200)]
+        public string Remark { get; set; }
+        /// <summary>
+        /// 状态:是否启用 
+        ///</summary>
+        [SugarColumn(ColumnName = "status", ColumnDescription = "状态")]
+        public StatusEnum Status { get; set; } = StatusEnum.Enable;
+
+        /// <summary>
+        /// 数据资源子项
+        /// </summary>
+        [SugarColumn(IsIgnore = true)]
+        public List<SysDataResource> Children { get; set; }
+    }
+}

+ 18 - 0
Admin.NET/Admin.NET.Core/Enum/ErrorCodeEnum.cs

@@ -428,6 +428,24 @@ namespace Admin.NET.Core
         [ErrorCodeItemMetadata("父菜单不存在")]
         D1505,
 
+        /// <summary>
+        /// 父资源不存在
+        /// </summary>
+        [ErrorCodeItemMetadata("父资源不存在")]
+        D1600,
+
+        /// <summary>
+        /// 当前资源Id不能与父资源Id相同
+        /// </summary>
+        [ErrorCodeItemMetadata("当前资源Id不能与父资源Id相同")]
+        D1601,
+
+        /// <summary>
+        /// 已有相同数据资源,编码或名称相同
+        /// </summary>
+        [ErrorCodeItemMetadata("已有相同数据资源,编码或名称相同")]
+        D1602,
+
         /// <summary>
         /// 已存在同名或同编码项目
         /// </summary>

+ 30 - 0
Admin.NET/Admin.NET.Core/SeedData/SysDataResourceSeedData.cs

@@ -0,0 +1,30 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Admin.NET.Core.SeedData
+{
+    /// <summary>
+    /// 系统数据资源种子数据
+    /// </summary>
+    public class SysDataResourceSeedData : ISqlSugarEntitySeedData<SysDataResource>
+    {
+        /// <summary>
+        /// 种子数据
+        /// </summary>
+        /// <returns></returns>
+        public IEnumerable<SysDataResource> HasData()
+        {
+            return new[]
+            {
+                new SysDataResource{ Id=243848612100001, Pid=0, Name="行政区",Value="district", Code="1001", CreateTime=DateTime.Parse("2022-05-30 00:00:00"), Remark="行政区"},
+                new SysDataResource{ Id=243848612100002, Pid=243848612100001, Name="北京市",Value="110000", Code="1001001", CreateTime=DateTime.Parse("2022-05-30 00:00:00"), Remark="北京市"},
+                new SysDataResource{ Id=243848612100003, Pid=243848612100002, Name="东城区",Value="110101", Code="1001001001", CreateTime=DateTime.Parse("2022-05-30 00:00:00"), Remark="东城区"},
+
+            };
+        }
+
+    }
+}

+ 6 - 0
Admin.NET/Admin.NET.Core/SeedData/SysMenuSeedData.cs

@@ -99,6 +99,12 @@ namespace Admin.NET.Core
                 new SysMenu{ Id=252885263003881, Pid=252885263003880, Title="缓存查询", Permission="cache:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
                 new SysMenu{ Id=252885263003882, Pid=252885263003880, Title="缓存删除", Permission="cache:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
 
+                new SysMenu{ Id=252885263003890, Pid=252885263003780, Title="数据资源管理", Path="dataResource", Name="DataResourceManagement", Component="/sys/admin/dataResource/index", Icon="ant-design:funnel-plot-filled", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-05-30 00:00:00"), OrderNo=100 },
+                new SysMenu{ Id=252885263003891, Pid=252885263003890, Title="数据资源查询", Permission="sysDataResource:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-05-30 00:00:00"), OrderNo=100 },
+                new SysMenu{ Id=252885263003892, Pid=252885263003890, Title="数据资源编辑", Permission="sysDataResource:update", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-05-30 00:00:00"), OrderNo=100 },
+                new SysMenu{ Id=252885263003893, Pid=252885263003890, Title="数据资源增加", Permission="sysDataResource:add", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-05-30 00:00:00"), OrderNo=100 },
+                new SysMenu{ Id=252885263003894, Pid=252885263003890, Title="数据资源删除", Permission="sysDataResource:delete", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-05-30 00:00:00"), OrderNo=100 },
+
                 new SysMenu{ Id=252885263003900, Pid=0, Title="日志管理", Path="/log", Name="log", Component="LAYOUT", Redirect="", Icon="ant-design:carry-out-outlined", Type=MenuTypeEnum.Dir, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=300 },
                 new SysMenu{ Id=252885263003910, Pid=252885263003900, Title="访问日志", Path="vislog", Name="VislogManagement", Component="/sys/admin/log/vislog/index", Type=MenuTypeEnum.Menu, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },
                 new SysMenu{ Id=252885263003911, Pid=252885263003910, Title="日志查询", Permission="sysVislog:page", Type=MenuTypeEnum.Btn, CreateTime=DateTime.Parse("2022-02-10 00:00:00"), OrderNo=100 },

+ 64 - 0
Admin.NET/Admin.NET.Core/Service/DataResources/Dto/DataResourceInput.cs

@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel.DataAnnotations;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Admin.NET.Core.Service.DataResources.Dto
+{
+    public class DataResourceInput : BaseIdInput
+    {
+        /// <summary>
+        /// 父Id
+        /// </summary>
+        public virtual long Pid { get; set; }
+
+        /// <summary>
+        /// 名称
+        /// </summary>
+        public virtual string Name { get; set; }
+
+        /// <summary>
+        /// 值 
+        ///</summary>
+        public virtual string Value { get; set; }
+
+        /// <summary>
+        /// 编码
+        /// </summary>
+        public virtual string Code { get; set; }
+
+        /// <summary>
+        /// 排序
+        /// </summary>
+        public virtual int Order { get; set; }
+
+        /// <summary>
+        /// 备注
+        /// </summary>
+        public virtual string Remark { get; set; }
+
+        /// <summary>
+        /// 状态
+        /// </summary>
+        public virtual int Status { get; set; }
+    }
+
+    public class AddDataResourceInput : DataResourceInput
+    {
+        /// <summary>
+        /// 名称
+        /// </summary>
+        [Required(ErrorMessage = "资源名称不能为空")]
+        public override string Name { get; set; }
+    }
+
+    public class UpdateDataResourceInput : AddDataResourceInput
+    {
+    }
+
+    public class DeleteDataResourceInput : BaseIdInput
+    {
+    }
+}

+ 162 - 0
Admin.NET/Admin.NET.Core/Service/DataResources/SysDataResourceService.cs

@@ -0,0 +1,162 @@
+using Admin.NET.Core.Service.DataResources.Dto;
+using Furion.DependencyInjection;
+using Furion.DynamicApiController;
+using Furion.FriendlyException;
+using Mapster;
+using Microsoft.AspNetCore.Mvc;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+
+namespace Admin.NET.Core.Service.DataResource
+{
+    /// <summary>
+    /// 系统数据资源服务
+    /// </summary>
+    [ApiDescriptionSettings(Name = "系统数据资源", Order = 201)]
+    public class SysDataResourceService : IDynamicApiController, ITransient
+    {
+        private readonly SqlSugarRepository<SysDataResource> _sysDataResourceRep;
+        private readonly ISysCacheService _sysCacheService;
+
+        public SysDataResourceService(SqlSugarRepository<SysDataResource> sysDataResourceRep, ISysCacheService sysCacheService)
+        {
+            _sysDataResourceRep = sysDataResourceRep;
+            _sysCacheService = sysCacheService;
+        }
+
+        /// <summary>
+        /// 获取数据资源列表
+        /// </summary>
+        /// <returns></returns>
+        [HttpGet("/sysDataResource/list")]
+        public async Task<List<SysDataResource>> GetDataResourceList([FromQuery] DataResourceInput input)
+        {
+            var idList = new List<long>();
+
+            if (input.Id > 0)
+            {
+                idList = await GetChildIdListWithSelfById(input.Id);
+            }
+
+
+            var iSugarQueryable = _sysDataResourceRep.AsQueryable().OrderBy(u => u.Order)
+                .WhereIF(idList.Count > 0, u => idList.Contains(u.Id)); // 非超级管理员限制
+
+            if (!string.IsNullOrWhiteSpace(input.Name) || !string.IsNullOrWhiteSpace(input.Code) || !string.IsNullOrWhiteSpace(input.Value) || input.Id > 0)
+            {
+                return await iSugarQueryable
+                    .WhereIF(!string.IsNullOrWhiteSpace(input.Name), u => u.Name.Contains(input.Name))
+                    .WhereIF(!string.IsNullOrWhiteSpace(input.Value), u => u.Value.Contains(input.Value))
+                    .WhereIF(!string.IsNullOrWhiteSpace(input.Code), u => u.Code.Contains(input.Code))
+                    .ToListAsync();
+            }
+            return await iSugarQueryable.ToTreeAsync(u => u.Children, u => u.Pid, input.Id > 0 ? input.Id : 0);
+        }
+
+        /// <summary>
+        /// 增加数据资源
+        /// </summary>
+        /// <param name="input"></param>
+        /// <returns></returns>
+        [HttpPost("/sysDataResource/add")]
+        public async Task<long> AddDataResource(AddDataResourceInput input)
+        {
+            var isExist = await _sysDataResourceRep.IsAnyAsync(u => u.Code == input.Code && u.Name == input.Name);
+            if (isExist)
+                throw Oops.Oh(ErrorCodeEnum.D1602);
+
+            var newCode = "";
+            // 生成编码Code和排序(每级2位编码)
+            SysDataResource sysDataResource = await _sysDataResourceRep.AsQueryable().OrderByDescending(o => o.Code)
+                    .FirstAsync(u => u.Pid == input.Pid);
+
+            if (sysDataResource != null)
+            {
+                newCode = sysDataResource.Code[0..^3] + string.Format("{0:d3}", int.Parse(sysDataResource.Code[^3..]) + 1);
+            }
+            else
+            {
+                //如果没有根节点,默认为1001
+                if (input.Pid == 0)
+                {
+                    newCode = "1001";
+                }
+                else
+                {
+                    sysDataResource = await _sysDataResourceRep.GetFirstAsync(u => u.Id == input.Pid);
+                    newCode = sysDataResource.Code + "01";
+                }
+
+            }
+            var newDataResource = input.Adapt<SysDataResource>();
+            newDataResource.Code = newCode;
+            newDataResource.Order = int.Parse(newCode[^3..]);
+            newDataResource = await _sysDataResourceRep.AsInsertable(newDataResource).ExecuteReturnEntityAsync();
+
+            return newDataResource.Id;
+        }
+
+        /// <summary>
+        /// 更新数据资源
+        /// </summary>
+        /// <param name="input"></param>
+        /// <returns></returns>
+        [HttpPost("/sysDataResource/update")]
+        [SqlSugarUnitOfWork]
+        public async Task UpdateDataResource(UpdateDataResourceInput input)
+        {
+            if (input.Pid != 0)
+            {
+                var pDataResource = await _sysDataResourceRep.GetFirstAsync(u => u.Id == input.Pid);
+                _ = pDataResource ?? throw Oops.Oh(ErrorCodeEnum.D1600);
+            }
+            if (input.Id == input.Pid)
+                throw Oops.Oh(ErrorCodeEnum.D1601);
+
+            var sysDataResource = await _sysDataResourceRep.GetFirstAsync(u => u.Id == input.Id);
+            var isExist = await _sysDataResourceRep.IsAnyAsync(u => (u.Name == input.Name && u.Code == input.Code) && u.Id != sysDataResource.Id);
+            if (isExist)
+                throw Oops.Oh(ErrorCodeEnum.D1602);
+
+            // 父Id不能为自己的子节点
+            var childIdList = await GetChildIdListWithSelfById(input.Id);
+            if (childIdList.Contains(input.Pid))
+                throw Oops.Oh(ErrorCodeEnum.D1601);
+
+
+            var dataResource = input.Adapt<SysDataResource>();
+            await _sysDataResourceRep.AsUpdateable(dataResource).IgnoreColumns(true).ExecuteCommandAsync();
+        }
+
+        /// <summary>
+        /// 删除数据资源
+        /// </summary>
+        /// <param name="input"></param>
+        /// <returns></returns>
+        [HttpPost("/sysDataResource/delete")]
+        public async Task DeleteDataResource(DeleteDataResourceInput input)
+        {
+            var sysDataResource = await _sysDataResourceRep.GetFirstAsync(u => u.Id == input.Id);
+
+            // 获取本节点对应所有子节点id列表
+            var treeList = await _sysDataResourceRep.AsQueryable().ToChildListAsync(u => u.Pid, input.Id);
+            var idList = treeList.Select(u => u.Id).ToList();
+
+            // 级联删除数据资源子节点
+            await _sysDataResourceRep.DeleteAsync(u => idList.Contains(u.Id));
+        }
+
+        /// <summary>
+        /// 根据节点Id获取子节点Id集合(包含自己)
+        /// </summary>
+        /// <param name="pid"></param>
+        /// <returns></returns>
+        [NonAction]
+        public async Task<List<long>> GetChildIdListWithSelfById(long pid)
+        {
+            var treeList = await _sysDataResourceRep.AsQueryable().ToChildListAsync(u => u.Pid, pid);
+            return treeList.Select(u => u.Id).ToList();
+        }
+    }
+}

+ 19 - 0
Vben2/src/api/sys/admin.ts

@@ -124,6 +124,12 @@ enum Api {
   GrantTenantMenu = '/sysTenant/GrantMenu',
   TenantOwnMenuList = '/sysTenant/ownMenu',
   ResetTenantPwd = '/sysTenant/resetPwd',
+
+  // 数据资源接口
+  DataResourceList = '/sysDataResource/list',
+  AddDataResource = '/sysDataResource/add',
+  DeleteDataResource = '/sysDataResource/delete',
+  UpdateDataResource = '/sysDataResource/update',
 }
 
 ////////// 账号管理接口 //////////
@@ -427,3 +433,16 @@ export const tenantOwnMenuList = (id: number) =>
 export function resetTenantPwd(id: number) {
   return defHttp.post<any>({ url: Api.ResetTenantPwd, params: { id } });
 }
+
+////////// 数据资源管理接口 //////////
+// 获取数据资源列表
+export const getDataResourceList = (params?: any) =>
+  defHttp.get<any>({ url: Api.DataResourceList, params });
+// 增加数据资源
+export const addDataResource = (params: any) => defHttp.post({ url: Api.AddDataResource, params });
+// 删除数据资源
+export const deleteDataResource = (id: number) =>
+  defHttp.post({ url: Api.DeleteDataResource, params: { id } });
+// 更新数据资源
+export const updateDataResource = (params: any) =>
+  defHttp.post({ url: Api.UpdateDataResource, params });

+ 94 - 0
Vben2/src/views/sys/admin/dataResource/DataResourceModal.vue

@@ -0,0 +1,94 @@
+<!--
+ * @Author: kenny 362270511@qq.com
+ * @Date: 2022-05-30 11:36:19
+ * @LastEditors: kenny 362270511@qq.com
+ * @LastEditTime: 2022-05-30 16:00:49
+ * @FilePath: \frontend\src\views\sys\admin\dataResource\org\DataResourceModal.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
+<template>
+  <BasicModal v-bind="$attrs" @register="registerModal" :title="getTitle" @ok="handleSubmit">
+    <BasicForm @register="registerForm" />
+  </BasicModal>
+</template>
+<script lang="ts">
+  import { defineComponent, ref, computed, unref } from 'vue';
+  import { BasicModal, useModalInner } from '/@/components/Modal';
+  import { BasicForm, useForm } from '/@/components/Form/index';
+
+  import { formSchema } from './dataResource.data';
+  import { getDataResourceList, addDataResource, updateDataResource } from '/@/api/sys/admin';
+
+  export default defineComponent({
+    name: 'DataResourceModal',
+    components: { BasicModal, BasicForm },
+    emits: ['success', 'register'],
+    setup(_, { emit }) {
+      const isUpdate = ref(true);
+      let rowId: number;
+
+      const [registerForm, { resetFields, setFieldsValue, updateSchema, validate }] = useForm({
+        labelWidth: 100,
+        schemas: formSchema,
+        showActionButtonGroup: false,
+      });
+
+      const [registerModal, { setModalProps, closeModal }] = useModalInner(async (data) => {
+        resetFields();
+        setModalProps({ confirmLoading: false });
+        isUpdate.value = !!data?.isUpdate;
+
+        const treeData = await getDataResourceList({ id: data.parentId || 0 });
+        debugger;
+        if (!data.parentId) {
+          treeData.push({
+            id: 0,
+            name: '根节点',
+            pid: 0,
+            remark: '根节点',
+            children: [],
+          });
+        }
+        updateSchema({
+          field: 'pid',
+          componentProps: { treeData },
+        });
+
+        if (unref(isUpdate)) {
+          rowId = data.record.id;
+          setFieldsValue({
+            ...data.record,
+          });
+        } else {
+          setFieldsValue({ pid: data.parentId });
+        }
+      });
+
+      const getTitle = computed(() => (!unref(isUpdate) ? '新增数据资源' : '编辑数据资源'));
+
+      async function handleSubmit() {
+        try {
+          const values = await validate();
+          setModalProps({ confirmLoading: true });
+
+          if (unref(isUpdate)) {
+            values.id = rowId;
+            await updateDataResource(values);
+          } else {
+            rowId = await addDataResource(values);
+          }
+
+          closeModal();
+          emit('success', {
+            isUpdate: unref(isUpdate),
+            values: { ...values, id: rowId },
+          });
+        } finally {
+          setModalProps({ confirmLoading: false });
+        }
+      }
+
+      return { registerModal, registerForm, getTitle, handleSubmit };
+    },
+  });
+</script>

+ 82 - 0
Vben2/src/views/sys/admin/dataResource/DataResourceTree.vue

@@ -0,0 +1,82 @@
+<!--
+ * @Author: kenny 362270511@qq.com
+ * @Date: 2022-05-30 14:35:51
+ * @LastEditors: kenny 362270511@qq.com
+ * @LastEditTime: 2022-05-30 14:41:06
+ * @FilePath: \frontend\src\views\sys\admin\dataResource\DataResourceTree.vue
+ * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
+-->
+<template>
+  <div class="m-4 mr-0 overflow-hidden bg-white">
+    <BasicTree
+      title="数据资源列表"
+      toolbar
+      search
+      :clickRowToExpand="true"
+      :treeData="treeData"
+      :fieldNames="{ key: 'id', title: 'name' }"
+      @select="handleSelect"
+      ref="treeAction"
+    />
+  </div>
+</template>
+<script lang="ts">
+  import { defineComponent, onMounted, ref, unref, nextTick } from 'vue';
+  import { BasicTree, TreeActionType, TreeItem } from '/@/components/Tree/index';
+
+  import { getDataResourceList } from '/@/api/sys/admin';
+
+  export default defineComponent({
+    name: 'DataResourceTree',
+    components: { BasicTree },
+
+    emits: ['select'],
+    setup(_, { emit }) {
+      const treeData = ref<TreeItem[]>([]);
+      const treeAction = ref<Nullable<TreeActionType>>(null);
+
+      const appendNodeByKey = (parentKey: string, values) => {
+        unref(treeAction).insertNodeByKey({
+          parentKey: parentKey,
+          node: values,
+          // 往后插入
+          push: 'push',
+          // 往前插入
+          // push:'unshift'
+        });
+      };
+
+      const updateNodeByKey = (key: string, values) => {
+        unref(treeAction).updateNodeByKey(key, values);
+      };
+
+      const deleteNodeByKey = (key: string) => {
+        unref(treeAction).deleteNodeByKey(key);
+      };
+
+      async function fetch() {
+        treeData.value = (await getDataResourceList()) as unknown as TreeItem[];
+        nextTick(() => {
+          unref(treeAction)?.filterByLevel(2);
+        });
+      }
+
+      function handleSelect(keys, obj) {
+        emit('select', keys[0], obj.selectedNodes[0]);
+      }
+
+      onMounted(() => {
+        fetch();
+      });
+      return {
+        treeData,
+        handleSelect,
+        treeAction,
+        appendNodeByKey,
+        updateNodeByKey,
+        deleteNodeByKey,
+        fetch,
+      };
+    },
+  });
+</script>

+ 140 - 0
Vben2/src/views/sys/admin/dataResource/dataResource.data.ts

@@ -0,0 +1,140 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { h } from 'vue';
+import { Tag } from 'ant-design-vue';
+
+export const columns: BasicColumn[] = [
+  {
+    title: '资源名称',
+    dataIndex: 'name',
+    width: 200,
+    align: 'left',
+  },
+  {
+    title: '资源值',
+    dataIndex: 'value',
+    width: 200,
+    align: 'left',
+  },
+  {
+    title: '编码',
+    dataIndex: 'code',
+    width: 200,
+    align: 'left',
+  },
+  {
+    title: '状态',
+    dataIndex: 'status',
+    width: 80,
+    customRender: ({ record }) => {
+      const status = record.status;
+      const enable = ~~status === 1;
+      const color = enable ? 'green' : 'red';
+      const text = enable ? '启用' : '停用';
+      return h(Tag, { color: color }, () => text);
+    },
+  },
+  {
+    title: '创建时间',
+    dataIndex: 'createTime',
+    width: 180,
+  },
+  {
+    title: '排序',
+    dataIndex: 'order',
+    width: 50,
+  },
+  {
+    title: '备注',
+    dataIndex: 'remark',
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'name',
+    label: '资源名称',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'value',
+    label: '资源值',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+  {
+    field: 'code',
+    label: '资源编码',
+    component: 'Input',
+    colProps: { span: 8 },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'pid',
+    label: '上级资源',
+    component: 'TreeSelect',
+    defaultValue: 0,
+    componentProps: {
+      fieldNames: {
+        label: 'name',
+        key: 'id',
+        value: 'id',
+      },
+      getPopupContainer: () => document.body,
+    },
+    required: true,
+    colProps: { span: 24 },
+  },
+  {
+    field: 'name',
+    label: '资源名称',
+    component: 'Input',
+    required: true,
+    colProps: { span: 24 },
+  },
+  {
+    field: 'value',
+    label: '资源值',
+    component: 'Input',
+    required: true,
+    colProps: { span: 24 },
+  },
+  // {
+  //   field: 'code',
+  //   label: '编码',
+  //   component: 'Input',
+  //   colProps: { span: 24 },
+  // },
+  {
+    field: 'order',
+    label: '排序',
+    component: 'InputNumber',
+    defaultValue: 0,
+    required: true,
+    componentProps: { style: { width: '100%' } },
+    colProps: { span: 24 },
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'RadioButtonGroup',
+    defaultValue: 1,
+    componentProps: {
+      options: [
+        { label: '启用', value: 1 },
+        { label: '停用', value: 2 },
+      ],
+    },
+    required: true,
+    colProps: { span: 24 },
+  },
+  {
+    label: '备注',
+    field: 'remark',
+    component: 'InputTextArea',
+    colProps: { span: 24 },
+  },
+];

+ 189 - 0
Vben2/src/views/sys/admin/dataResource/index.vue

@@ -0,0 +1,189 @@
+<template>
+  <PageWrapper dense contentFullHeight fixedHeight contentClass="flex">
+    <DataResourceTree
+      class="w-1/4 xl:w-1/5"
+      style="overflow: auto"
+      @select="handleSelect"
+      ref="DataResourceTreeChild"
+    />
+    <BasicTable @register="registerTable" class="w-3/4 xl:w-4/5" :searchInfo="searchInfo">
+      <template #toolbar>
+        <a-button
+          type="primary"
+          @click="handleCreatebrother"
+          :disabled="!hasPermission('sysDataResource:add')"
+        >
+          添加同级资源
+        </a-button>
+        <a-button
+          type="primary"
+          @click="handleCreatechild()"
+          :disabled="!hasPermission('sysDataResource:add')"
+        >
+          添加下级资源
+        </a-button>
+      </template>
+      <template #action="{ record }">
+        <TableAction
+          :actions="[
+            {
+              icon: 'clarity:note-edit-line',
+              label: '编辑',
+              disabled: !hasPermission('sysDataResource:update'),
+              onClick: handleEdit.bind(null, record),
+            },
+            {
+              icon: 'ant-design:delete-outlined',
+              label: '删除',
+              color: 'error',
+              ifShow: hasPermission('sysDataResource:delete'),
+              popConfirm: {
+                title: '是否确认删除',
+                confirm: handleDelete.bind(null, record),
+              },
+            },
+          ]"
+        />
+      </template>
+    </BasicTable>
+    <DataResourceModal @register="registerModal" @success="handleSuccess" />
+  </PageWrapper>
+</template>
+<script lang="ts">
+  import { defineComponent, reactive, ref, unref } from 'vue';
+  import { BasicTable, useTable, TableAction } from '/@/components/Table';
+  import { useModal } from '/@/components/Modal';
+  import { usePermission } from '/@/hooks/web/usePermission';
+
+  import { PageWrapper } from '/@/components/Page';
+  import DataResourceTree from './DataResourceTree.vue';
+
+  import DataResourceModal from './DataResourceModal.vue';
+
+  import { columns, searchFormSchema } from './dataResource.data';
+  import { getDataResourceList, deleteDataResource } from '/@/api/sys/admin';
+
+  export default defineComponent({
+    name: 'DataResourceManagement',
+    components: { BasicTable, DataResourceModal, TableAction, PageWrapper, DataResourceTree },
+    setup() {
+      const { hasPermission } = usePermission();
+      const DataResourceTreeChild = ref(null);
+      const [registerModal, { openModal }] = useModal();
+      const searchInfo = reactive<Recordable>({});
+      const [registerTable, { reload, updateTableDataRecord }] = useTable({
+        title: '数据资源列表',
+        api: getDataResourceList,
+        columns,
+        formConfig: {
+          labelWidth: 120,
+          schemas: searchFormSchema,
+        },
+        rowKey: 'id',
+        pagination: false,
+        striped: false,
+        useSearchForm: true,
+        showTableSetting: true,
+        bordered: true,
+        showIndexColumn: false,
+        canResize: true,
+        actionColumn: {
+          width: 150,
+          title: '操作',
+          dataIndex: 'action',
+          slots: { customRender: 'action' },
+          fixed: undefined,
+        },
+      });
+
+      function getTree() {
+        const tree = unref(DataResourceTreeChild);
+        if (!tree) {
+          throw new Error('Tree is null!');
+        }
+        return tree;
+      }
+
+      function appendNodeByKey(parentKey, values) {
+        getTree().appendNodeByKey(parentKey, values);
+      }
+
+      function updateNodeByKey(key, values) {
+        getTree().updateNodeByKey(key, values); // 子组件里的方法
+      }
+
+      function deleteNodeByKey(key) {
+        getTree().deleteNodeByKey(key);
+      }
+
+      function handleCreate() {
+        openModal(true, {
+          isUpdate: false,
+        });
+      }
+
+      function handleCreatechild() {
+        openModal(true, {
+          isUpdate: false,
+          parentId: searchInfo.Id,
+        });
+      }
+
+      function handleCreatebrother() {
+        openModal(true, {
+          isUpdate: false,
+          parentId: searchInfo.pId,
+        });
+      }
+
+      function handleEdit(record: Recordable) {
+        openModal(true, {
+          record,
+          isUpdate: true,
+        });
+      }
+
+      async function handleDelete(record: Recordable) {
+        await deleteDataResource(record.id);
+        deleteNodeByKey(record.id);
+        searchInfo.Id = record.pid;
+        reload();
+      }
+
+      function handleSelect(orgId: number, obj) {
+        searchInfo.Id = orgId;
+        searchInfo.pId = obj.pid ? obj.pid : 0;
+        reload();
+      }
+
+      function handleSuccess({ isUpdate, values }) {
+        if (isUpdate) {
+          updateTableDataRecord(values.id, values);
+          updateNodeByKey(values.id, values);
+        } else {
+          reload();
+          appendNodeByKey(values.pid, values);
+        }
+        // getTree().fetch();
+      }
+
+      return {
+        registerTable,
+        registerModal,
+        searchInfo,
+        DataResourceTreeChild,
+        handleSelect,
+        updateNodeByKey,
+        appendNodeByKey,
+        deleteNodeByKey,
+        handleCreate,
+        handleCreatebrother,
+        handleCreatechild,
+        handleEdit,
+        handleDelete,
+        handleSuccess,
+        hasPermission,
+      };
+    },
+  });
+</script>