Переглянути джерело

标签管理,菜单添加

sujunling 2 роки тому
батько
коміт
aea28ac43d

+ 2 - 3
src/api/sys/user.ts

@@ -50,12 +50,11 @@ export function loginApi(params: LoginParams, mode: ErrorMessageMode = 'modal')
   var a = { ...params, ...client };
   Object.keys(a).forEach((key) => formData.append(key, a[key]))
   return new Promise<void>((resolve, reject) => {
-    defHttp.post<LoginResultModel>(
-      { url: Api.Login, params: formData },
-      { errorMessageMode: mode, joinPrefix: false, formatDate: true })
+    defHttp.post<LoginResultModel>({ url: Api.Login, params: formData })
       .then((r) => {
         r.refreshToken = r.refresh_token;
         r.token = r.access_token;
+        session.setItem('token', r.access_token);
         resolve(r);
       })
   })

+ 22 - 0
src/mock/menu.json

@@ -1126,6 +1126,28 @@
                             "hideMenu": false,
                             "status": "0"
                         }
+                    },
+                    {
+                        "id": "246ddwec-d414-4c38-85a0-86104dsdb172",
+                        "createTime": "2021-11-16 19:17:48",
+                        "updateTime": "2021-12-20 11:46:16",
+                        "name": "标签管理",
+                        "parentId": "a8ffa8c5-637e-471b-a9e6-b60cebe95713",
+                        "children": [],
+                        "path": "/system/zhiwei",
+                        "type": "SYSADMIN",
+                        "permission": "system:tag:view",
+                        "sort": 2,
+                        "component": "/systemAdmin/system/tag/index",
+                        "meta": {
+                            "icon": "ant-design:gold-outlined",
+                            "title": "标签管理",
+                            "isLink": false,
+                            "menuType": "1",
+                            "ignoreKeepAlive": false,
+                            "hideMenu": false,
+                            "status": "0"
+                        }
                     }
                 ],
                 "meta": {

+ 1 - 0
src/mock/menu_get_ids.json

@@ -11,6 +11,7 @@
     "4ce99298-d100-4479-ac61-3f6762ade2c4",
     "50843259-c2de-442a-9da2-9a17f161a970",
     "246ddwec-d414-4c38-85a0-86104d21b172",
+    "246ddwec-d414-4c38-85a0-86104dsdb172",
     "8a8ed060-c22b-425c-93dc-d2bca8249e92",
     "dccbd1ba-f506-4837-ada1-6675261471e8",
     "7cda452a-a8f9-43f3-8fb1-974f169244a7",

+ 1 - 0
src/mock/role_me_permissions.json

@@ -15,6 +15,7 @@
     "system:menu:view",
     "map:upload:view",
     "system:zhiwei:view",
+    "system:tag:view",
     "dataAdmin",
     "dataAdmin:dataAdmin:resourceCataloging",
     "dataAdmin:dataAdmin",

+ 1 - 1
src/utils/http/axios/index.ts

@@ -96,7 +96,7 @@ const transform: AxiosTransform = {
   requestInterceptors: (config, options) => {
     // 请求之前处理config
     const token = getJwtToken();
-    if (token && (config as Recordable)?.requestOptions?.withToken !== false) {
+    if (token && (config as Recordable)?.requestOptions?.withToken !== false &&config.url != '/agent/callProvider') {
       // jwt token
       // config.headers['X-Authorization'] = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token;
       config.headers['Authorization'] = options.authenticationScheme ? `${options.authenticationScheme} ${token}` : token;

+ 281 - 299
src/views/dataAdmin/dataAdmin/directoryManagement/index.vue

@@ -19,11 +19,7 @@
     <div class="mains">
       <div class="left">
         <a-card class="box-card" body-style="{height:'100%'}">
-          <a-input-search
-            :value="TreeSearchValue"
-            style="margin-bottom: 8px"
-            placeholder="搜索关键字"
-          />
+          <a-input-search :value="TreeSearchValue" style="margin-bottom: 8px" placeholder="搜索关键字" />
           <a-tree :tree-data="treeData" @select="select" />
         </a-card>
       </div>
@@ -31,8 +27,7 @@
       <div class="right">
         <div class="right-top">
           <a-card class="box-card" body-style="{height:'100%'}">
-            <div
-              style="
+            <div style="
                 border-bottom-style: solid;
                 border-bottom-width: 1px;
                 border-bottom-color: #ddd;
@@ -40,11 +35,9 @@
                 padding: 10px;
                 padding-top: 0px;
                 margin-top: 0px;
-              "
-            >
+              ">
               <a-row>
-                <a-col :span="10"
-                  ><span style="font-weight: 700; font-size: 16px">节点编辑</span>
+                <a-col :span="10"><span style="font-weight: 700; font-size: 16px">节点编辑</span>
                 </a-col>
                 <a-col :span="13">
                   <a-row type="flex" justify="end">
@@ -58,13 +51,8 @@
                 </a-col>
               </a-row>
             </div>
-            <a-form
-              :model="formState"
-              :labelCol="{ span: 5 }"
-              :wrapperCol="{ span: 15 }"
-              style="margin-top: 20px"
-              layout="horizontal"
-            >
+            <a-form :model="formState" :labelCol="{ span: 5 }" :wrapperCol="{ span: 15 }" style="margin-top: 20px"
+              layout="horizontal">
               <a-row :gutter="24">
                 <a-col :span="8">
                   <a-form-item label="名称">
@@ -92,9 +80,7 @@
             </a-form>
             <a-row type="flex" justify="center" style="margin-top: 60px">
               <a-col :span="4">
-                <a-button type="primary" @click="editNode" :disabled="editBtuState"
-                  >保存节点</a-button
-                >
+                <a-button type="primary" @click="editNode" :disabled="editBtuState">保存节点</a-button>
               </a-col>
             </a-row>
           </a-card>
@@ -102,19 +88,16 @@
 
         <div class="right-bottom">
           <a-card class="box-card" body-style="{height:'100%'}">
-            <div
-              style="
+            <div style="
                 border-bottom-style: solid;
                 border-bottom-width: 1px;
                 border-bottom-color: #ddd;
                 height: 40px;
                 padding: 10px;
                 padding-top: 0px;
-              "
-            >
+              ">
               <a-row>
-                <a-col :span="10"
-                  ><span style="font-weight: 700; font-size: 16px">操作记录</span>
+                <a-col :span="10"><span style="font-weight: 700; font-size: 16px">操作记录</span>
                 </a-col>
               </a-row>
             </div>
@@ -158,311 +141,310 @@
   </div>
 </template>
 <script>
-  import { defineComponent, ref } from 'vue';
-  import { getDirectoryTreeList, addTreeNode } from '/@/api/sys/gis';
-  import Guid from 'guid';
+import { defineComponent, ref } from 'vue';
+import { getDirectoryTreeList, addTreeNode } from '/@/api/sys/gis';
+import Guid from 'guid';
+import { session } from '/@/utils/Memory';
 
-  export default defineComponent({
-    setup() {
-      const visible = ref(false);
-      return {
-        visible,
-      };
-    },
+export default defineComponent({
+  setup() {
+    const visible = ref(false);
+    return {
+      visible,
+    };
+  },
 
-    data() {
-      return {
-        // 搜索框数据
-        searchCategory: [],
-        // 树的搜索值
-        treeSearchValue: '',
-        // 树形数据
-        treeData: [],
-        // 表单数据
-        formState: {
-          id: '',
-          pid: '',
-          pName: '',
-          name: '',
-          alaisename: '',
-          type: '',
-          remark: '',
-          sortindex: '',
-        },
-        // 头部搜索框的值
-        inputSearchValue: '',
-        // 控制新增弹窗显示
-        // eslint-disable-next-line vue/no-dupe-keys
-        visible: false,
-        // 新增弹窗表单
-        formState2: {
-          pid: '',
-          pName: '',
-          name: '',
-          alaisename: '',
-          type: 0,
-          remark: '',
-          sortindex: '',
-        },
-        // 节点和子节点新增区别
+  data() {
+    return {
+      // 搜索框数据
+      searchCategory: [],
+      // 树的搜索值
+      treeSearchValue: '',
+      // 树形数据
+      treeData: [],
+      // 表单数据
+      formState: {
+        id: '',
+        pid: '',
+        pName: '',
+        name: '',
+        alaisename: '',
         type: '',
-        // 父级id
+        remark: '',
+        sortindex: '',
+      },
+      // 头部搜索框的值
+      inputSearchValue: '',
+      // 控制新增弹窗显示
+      // eslint-disable-next-line vue/no-dupe-keys
+      visible: false,
+      // 新增弹窗表单
+      formState2: {
         pid: '',
-        // 编辑按钮使用状态
-        editBtuState: true,
-      };
+        pName: '',
+        name: '',
+        alaisename: '',
+        type: 0,
+        remark: '',
+        sortindex: '',
+      },
+      // 节点和子节点新增区别
+      type: '',
+      // 父级id
+      pid: '',
+      // 编辑按钮使用状态
+      editBtuState: true,
+    };
+  },
+
+  mounted() {
+    this.getTreeList();
+  },
+
+  methods: {
+    //获取树节点列表
+    getTreeList() {
+      debugger;
+      let f = new FormData();
+      f.append('interfaceName', 'antu.space.provider.sdatacatlog.SDataCatlogProvider');
+      f.append('methodName', 'findList');
+      f.append('args[]', session.getItem("token"));
+      f.append('args[]', '');
+      getDirectoryTreeList(f).then((res) => {
+        if (res.status == 0) {
+          const data = JSON.parse(res.result);
+          console.log('获取设备列表2:', data);
+          // 让每层带上自己的孩子节点、title、key
+          data.forEach((item) => {
+            item['title'] = item.name;
+            item['key'] = item.id;
+            data.forEach((item2) => {
+              if (item.id == item2.pid) {
+                if (item.children) {
+                  item.children.push(item2);
+                } else {
+                  item['children'] = [];
+                  item.children.push(item2);
+                }
+              }
+            });
+          });
+          console.log('11111111111:', data);
+          // 找出搜索框数据
+          data.forEach((item) => {
+            if (item.pid == '') {
+              this.searchCategory.push(item);
+            }
+          });
+          this.inputSearchValue = this.searchCategory[0].id;
+          // 找出树形数据
+          this.searchCategory.forEach((item1) => {
+            data.forEach((item2) => {
+              if (item1.id == item2.pid) {
+                this.treeData.push(item2);
+              }
+            });
+          });
+        }
+      });
     },
 
-    mounted() {
-      this.getTreeList();
+    // 点击树节点
+    select(value1, value2) {
+      console.log('树的数据' + JSON.stringify(value2.selectedNodes[0].props));
+      this.formState = { ...value2.selectedNodes[0].props };
+      // const pid = { ...value2.selectedNodes[0].props };
+      this.formState2.pid = value2.selectedNodes[0].props.id;
+      console.log('222' + JSON.stringify(value2.selectedNodes[0].props));
+      this.editBtuState = false;
     },
 
-    methods: {
-      //获取树节点列表
-      getTreeList() {
+    // 添加树节点
+    addTreeNode() {
+      this.visible = true;
+      this.type = 1;
+      if (this.inputSearchValue) {
+        this.searchCategory.forEach((item) => {
+          if (this.inputSearchValue == item.id) {
+            this.formState2.pName = item.name;
+            this.formState2.pid = item.id;
+          }
+        });
+      } else {
+        //没有选择头部分类
+      }
+    },
+
+    // 添加子节点
+    addTreeChildNode() {
+      // message.warning('请选择需要添加子节点的父节点!', 5);
+      this.visible = true;
+      this.type = 2;
+    },
+
+    // 新增弹窗提交新增数据
+    handleOk() {
+      if (this.type == 1) {
+        console.log('表单数据' + JSON.stringify(this.formState2));
         const gisToken = sessionStorage.getItem('gisToken');
+        this.formState2.id = Guid.raw();
+        this.formState2.sortindex = this.treeData.length;
         let directoryFormData = new FormData();
         directoryFormData.append(
           'interfaceName',
           'antu.space.provider.sdatacatlog.SDataCatlogProvider'
         );
-        directoryFormData.append('methodName', 'findList');
+        directoryFormData.append('methodName', 'insert');
         directoryFormData.append('args[]', gisToken);
-        directoryFormData.append('args[]', '');
-        getDirectoryTreeList(directoryFormData).then((res) => {
+        directoryFormData.append('args[]', JSON.stringify(this.formState2));
+        addTreeNode(directoryFormData).then((res) => {
           if (res.status == 0) {
-            const data = JSON.parse(res.result);
-            console.log('获取设备列表2:', data);
-            // 让每层带上自己的孩子节点、title、key
-            data.forEach((item) => {
-              item['title'] = item.name;
-              item['key'] = item.id;
-              data.forEach((item2) => {
-                if (item.id == item2.pid) {
-                  if (item.children) {
-                    item.children.push(item2);
-                  } else {
-                    item['children'] = [];
-                    item.children.push(item2);
-                  }
-                }
-              });
-            });
-            console.log('11111111111:', data);
-            // 找出搜索框数据
-            data.forEach((item) => {
-              if (item.pid == '') {
-                this.searchCategory.push(item);
-              }
-            });
-            this.inputSearchValue = this.searchCategory[0].id;
-            // 找出树形数据
-            this.searchCategory.forEach((item1) => {
-              data.forEach((item2) => {
-                if (item1.id == item2.pid) {
-                  this.treeData.push(item2);
-                }
-              });
-            });
+            console.log('新增' + res);
+            this.searchCategory = [];
+            this.treeData = [];
+            this.getTreeList();
+            this.visible = false;
           }
         });
-      },
-
-      // 点击树节点
-      select(value1, value2) {
-        console.log('树的数据' + JSON.stringify(value2.selectedNodes[0].props));
-        this.formState = { ...value2.selectedNodes[0].props };
-        // const pid = { ...value2.selectedNodes[0].props };
-        this.formState2.pid = value2.selectedNodes[0].props.id;
-        console.log('222' + JSON.stringify(value2.selectedNodes[0].props));
-        this.editBtuState = false;
-      },
-
-      // 添加树节点
-      addTreeNode() {
-        this.visible = true;
-        this.type = 1;
-        if (this.inputSearchValue) {
-          this.searchCategory.forEach((item) => {
-            if (this.inputSearchValue == item.id) {
-              this.formState2.pName = item.name;
-              this.formState2.pid = item.id;
-            }
-          });
-        } else {
-          //没有选择头部分类
-        }
-      },
-
-      // 添加子节点
-      addTreeChildNode() {
-        // message.warning('请选择需要添加子节点的父节点!', 5);
-        this.visible = true;
-        this.type = 2;
-      },
-
-      // 新增弹窗提交新增数据
-      handleOk() {
-        if (this.type == 1) {
-          console.log('表单数据' + JSON.stringify(this.formState2));
-          const gisToken = sessionStorage.getItem('gisToken');
-          this.formState2.id = Guid.raw();
-          this.formState2.sortindex = this.treeData.length;
-          let directoryFormData = new FormData();
-          directoryFormData.append(
-            'interfaceName',
-            'antu.space.provider.sdatacatlog.SDataCatlogProvider'
-          );
-          directoryFormData.append('methodName', 'insert');
-          directoryFormData.append('args[]', gisToken);
-          directoryFormData.append('args[]', JSON.stringify(this.formState2));
-          addTreeNode(directoryFormData).then((res) => {
-            if (res.status == 0) {
-              console.log('新增' + res);
-              this.searchCategory = [];
-              this.treeData = [];
-              this.getTreeList();
-              this.visible = false;
-            }
-          });
-          this.type = '';
-        } else if (this.type == 2) {
-          console.log('表单数据2' + JSON.stringify(this.formState2));
-          const gisToken = sessionStorage.getItem('gisToken');
-          this.formState2.id = Guid.raw();
-          // 排序
-          // this.formState2.sortindex = this.treeData.length
-          let directoryFormData = new FormData();
-          directoryFormData.append(
-            'interfaceName',
-            'antu.space.provider.sdatacatlog.SDataCatlogProvider'
-          );
-          directoryFormData.append('methodName', 'insert');
-          directoryFormData.append('args[]', gisToken);
-          directoryFormData.append('args[]', JSON.stringify(this.formState2));
-          addTreeNode(directoryFormData).then((res) => {
-            if (res.status == 0) {
-              console.log('新增' + res);
-              this.searchCategory = [];
-              this.treeData = [];
-              this.getTreeList();
-              this.visible = false;
-            }
-          });
-          this.visible = false;
-          this.type = '';
-        }
-      },
-      // 修改节点
-      editNode() {
-        if (!this.editBtuState) {
-          console.log('表单数据3' + JSON.stringify(this.formState));
-          const gisToken = sessionStorage.getItem('gisToken');
-          let editData = {
-            id: this.formState.id,
-            pid: this.formState.pid,
-            name: this.formState.name,
-            alaisename: this.formState.alaisename,
-            type: this.formState.type,
-            sortindex: this.formState.sortindex,
-            remark: this.formState.remark,
-          };
-          let directoryFormData = new FormData();
-          directoryFormData.append(
-            'interfaceName',
-            'antu.space.provider.sdatacatlog.SDataCatlogProvider'
-          );
-          directoryFormData.append('methodName', 'update');
-          directoryFormData.append('args[]', gisToken);
-          directoryFormData.append('args[]', JSON.stringify(editData));
-          addTreeNode(directoryFormData).then((res) => {
-            if (res.status == 0) {
-              console.log('编辑' + res);
-              this.searchCategory = [];
-              this.treeData = [];
-              this.getTreeList();
-              this.editBtuState = true;
-            }
-          });
-        }
-      },
+        this.type = '';
+      } else if (this.type == 2) {
+        console.log('表单数据2' + JSON.stringify(this.formState2));
+        const gisToken = sessionStorage.getItem('gisToken');
+        this.formState2.id = Guid.raw();
+        // 排序
+        // this.formState2.sortindex = this.treeData.length
+        let directoryFormData = new FormData();
+        directoryFormData.append(
+          'interfaceName',
+          'antu.space.provider.sdatacatlog.SDataCatlogProvider'
+        );
+        directoryFormData.append('methodName', 'insert');
+        directoryFormData.append('args[]', gisToken);
+        directoryFormData.append('args[]', JSON.stringify(this.formState2));
+        addTreeNode(directoryFormData).then((res) => {
+          if (res.status == 0) {
+            console.log('新增' + res);
+            this.searchCategory = [];
+            this.treeData = [];
+            this.getTreeList();
+            this.visible = false;
+          }
+        });
+        this.visible = false;
+        this.type = '';
+      }
+    },
+    // 修改节点
+    editNode() {
+      if (!this.editBtuState) {
+        console.log('表单数据3' + JSON.stringify(this.formState));
+        const gisToken = sessionStorage.getItem('gisToken');
+        let editData = {
+          id: this.formState.id,
+          pid: this.formState.pid,
+          name: this.formState.name,
+          alaisename: this.formState.alaisename,
+          type: this.formState.type,
+          sortindex: this.formState.sortindex,
+          remark: this.formState.remark,
+        };
+        let directoryFormData = new FormData();
+        directoryFormData.append(
+          'interfaceName',
+          'antu.space.provider.sdatacatlog.SDataCatlogProvider'
+        );
+        directoryFormData.append('methodName', 'update');
+        directoryFormData.append('args[]', gisToken);
+        directoryFormData.append('args[]', JSON.stringify(editData));
+        addTreeNode(directoryFormData).then((res) => {
+          if (res.status == 0) {
+            console.log('编辑' + res);
+            this.searchCategory = [];
+            this.treeData = [];
+            this.getTreeList();
+            this.editBtuState = true;
+          }
+        });
+      }
     },
-  });
+  },
+});
 </script>
 
 <style lang="scss" scoped>
-  .head {
-    padding: 10px;
-    height: 74px;
-    margin-right: 30px;
-    margin-left: 10px;
+.head {
+  padding: 10px;
+  height: 74px;
+  margin-right: 30px;
+  margin-left: 10px;
+}
+
+.mains {
+  .box-card {
+    height: calc(100% - 0px);
   }
-  .mains {
-    .box-card {
-      height: calc(100% - 0px);
-    }
 
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 20px;
+  height: calc(100% - 74px);
+
+  .left {
+    height: calc(100% - 0px);
+    width: 20%;
+  }
+
+  .right {
     display: flex;
-    justify-content: space-between;
-    align-items: center;
+    flex-direction: column; //设置主轴为y轴
+    justify-content: flex-start; //子元素排列方式,
+    align-items: center; //设置侧轴上子元素对齐方式
     padding: 20px;
-    height: calc(100% - 74px);
-
-    .left {
-      height: calc(100% - 0px);
-      width: 20%;
-    }
+    padding-top: 0;
+    padding-bottom: 0;
+    height: calc(100% - 0px);
+    width: 100%;
+    // height: 50%;
+    // width: 78%;
+    // margin-left: 1%;
+    // margin-right: 1%;
+    // border: 1px red solid;
 
-    .right {
-      display: flex;
-      flex-direction: column; //设置主轴为y轴
-      justify-content: flex-start; //子元素排列方式,
-      align-items: center; //设置侧轴上子元素对齐方式
-      padding: 20px;
-      padding-top: 0;
-      padding-bottom: 0;
-      height: calc(100% - 0px);
+    .right-top {
+      height: 50%;
       width: 100%;
-      // height: 50%;
-      // width: 78%;
-      // margin-left: 1%;
-      // margin-right: 1%;
-      // border: 1px red solid;
+      margin-left: 1%;
+      margin-right: 1%;
+      margin-bottom: 2%;
 
-      .right-top {
-        height: 50%;
-        width: 100%;
-        margin-left: 1%;
-        margin-right: 1%;
-        margin-bottom: 2%;
-
-        .right-top-item {
-          border-style: solid;
-          border-width: 1px;
-          border-color: #ddd;
-          box-shadow: 0 0 10px #ddd;
-          height: 250px;
-          margin-top: 10px;
-          padding: 10px;
-        }
+      .right-top-item {
+        border-style: solid;
+        border-width: 1px;
+        border-color: #ddd;
+        box-shadow: 0 0 10px #ddd;
+        height: 250px;
+        margin-top: 10px;
+        padding: 10px;
       }
+    }
 
-      .right-bottom {
-        height: 50%;
-        width: 100%;
-        margin-left: 1%;
-        margin-right: 1%;
+    .right-bottom {
+      height: 50%;
+      width: 100%;
+      margin-left: 1%;
+      margin-right: 1%;
 
-        .right-bottom-item {
-          border-style: solid;
-          border-width: 1px;
-          border-color: #ddd;
-          box-shadow: 0 0 10px #ddd;
-          height: 250px;
-          margin-top: 10px;
-          padding: 10px;
-        }
+      .right-bottom-item {
+        border-style: solid;
+        border-width: 1px;
+        border-color: #ddd;
+        box-shadow: 0 0 10px #ddd;
+        height: 250px;
+        margin-top: 10px;
+        padding: 10px;
       }
     }
   }
+}
 </style>

+ 225 - 0
src/views/systemAdmin/system/tag/RoleDrawer.vue

@@ -0,0 +1,225 @@
+<template>
+  <BasicDrawer v-bind="$attrs" @register="registerDrawer" showFooter :title="getTitle" width="500px" @ok="handleSubmit">
+    <BasicForm @register="registerForm">
+      <template #menu>
+        <Spin :spinning="spinning"></Spin>
+      </template>
+    </BasicForm>
+  </BasicDrawer>
+</template>
+<script lang="ts">
+import { defineComponent, ref, computed, unref, nextTick } from 'vue';
+import { BasicForm, useForm } from '/@/components/Form/index';
+import { formSchema, KeysTypeEnum, RoleMenuDictEnum } from './role.data';
+import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
+import { BasicTree, TreeItem } from '/@/components/Tree';
+import { useMessage } from '/@/hooks/web/useMessage';
+const { t } = useI18n(); //加载国际化
+// 加载菜单数据
+import { getMenuList } from '/@/api/sys/menu';
+import { useI18n } from '/@/hooks/web/useI18n';
+import { MenuRecord } from '/@/api/sys/model/menuModel';
+import { saveOrUpdateRoleInfoWithMenu } from '/@/api/system/system';
+import { findDictItemByCode } from '/@/api/system/dict';
+import { RoleEnum } from '/@/enums/roleEnum';
+import { Spin } from 'ant-design-vue';
+import { useUserStore } from '/@/store/modules/user';
+import { session } from '/@/utils/Memory';
+import { v4 as uuidv4 } from 'uuid';
+
+
+type TreeData = MenuRecord & TreeItem;
+
+export default defineComponent({
+  name: 'RoleDrawer',
+  components: { BasicDrawer, BasicForm, BasicTree, Spin },
+  emits: ['success', 'register'],
+  setup(_, { emit }) {
+    const isUpdate = ref<boolean>(true);
+    const treeData = ref<TreeData[]>([]);
+    const roleMenus = ref<string[]>([]);
+    const roleId = ref<string>('');
+    const checked = ref<string[]>([]); //需要选中的节点
+    const spinning = ref(false);
+    var nowRole = null;
+
+    const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
+      labelWidth: 100,
+      schemas: formSchema,
+      showActionButtonGroup: false,
+    });
+
+    const transformName = (data: TreeData[]) => {
+      return data.map((item) => {
+        item.name = t(item.name);
+        if (item.children && item.children.length) {
+          item.children = transformName(item.children as unknown as TreeData[]);
+        }
+        return item;
+      });
+    };
+
+    const userStore = useUserStore();
+    const [registerDrawer, { setDrawerProps, closeDrawer }] = useDrawerInner(async (data) => {
+      resetFields();
+      roleId.value = '';
+      // 在打开弹窗时清除所有选择的菜单
+      isUpdate.value = data.isUpdate;
+      const roleType = data?.record?.roleType || userStore.getRoleList.at(0);
+
+      try {
+        spinning.value = true;
+        // 需要在setFieldsValue之前先填充treeData,否则Tree组件可能会报key not exist警告
+
+        if (!unref(treeData).length) {
+          // 获取全部的菜单
+          const menuListModel = await getMenuList();
+          treeData.value = transformName(menuListModel as unknown as TreeData[]);
+        }
+
+        const keys = await getPermissionByRole(roleType);
+        const { keyType } = RoleMenuDictEnum[roleType];
+        treeData.value = getPermissionTreeData(
+          unref(treeData) as unknown as TreeData[],
+          keys,
+          keyType
+        );
+        // 更新
+        if (unref(isUpdate)) {
+          checked.value = [];
+          roleId.value = data.record.id;
+          nowRole = data.record;
+          setFieldsValue(data.record);
+        } else {
+        }
+      } catch (error) {
+        throw error;
+      } finally {
+        spinning.value = false;
+      }
+    });
+
+    const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色'));
+
+    async function handleSubmit() {
+      setDrawerProps({ confirmLoading: true });
+      const { createMessage } = useMessage();
+      try {
+        const values = await validate();
+        const req = {
+          groupName: values.groupName,
+          sort: values.sort
+        };
+        if (unref(isUpdate)) {
+          if (nowRole) {
+            req.updateuser = session.getItem("userInfo").EMPLOYEE.EMPLOYEE_ID;
+            req.groupid = nowRole.groupid;
+          }
+        } else {
+          req.groupid = uuidv4()
+        }
+        saveOrUpdateRoleInfoWithMenu(req).then(() => {
+          closeDrawer();
+          emit('success');
+          nowRole = null;
+          createMessage.success(`${unref(isUpdate) ? '编辑' : '新增'}成功`);
+        });
+      } finally {
+        setTimeout(() => {
+          setDrawerProps({ confirmLoading: false });
+        }, 300);
+      }
+    }
+
+    const getPermissionByRole = async (roleType: RoleEnum) => {
+      try {
+        const { key } = RoleMenuDictEnum[roleType];
+        const res = await findDictItemByCode({ dictCode: key });
+        return res.map((item) => item.itemValue);
+      } catch (error) { }
+      return [];
+    };
+
+    const getPermissionTreeData = (
+      data: MenuRecord[],
+      permissionKeys: string[],
+      keysType: KeysTypeEnum
+    ) => {
+      const setDisabled = (data: MenuRecord[], flag: boolean) => {
+        return data.map((item) => {
+          item.name = t(item.name);
+          if (item.children && item.children.length) {
+            item.children = setDisabled(item.children, flag);
+          }
+          return {
+            ...item,
+            disabled: flag,
+            icon: item.meta.icon,
+          } as TreeData;
+        });
+      };
+
+      const permissionCompare = (
+        data: MenuRecord[],
+        permissionKeys: string[],
+        keysType: KeysTypeEnum
+      ) => {
+        return data.map((item) => {
+          item.name = t(item.name);
+          const findFlag = permissionKeys.includes(item.permission);
+          if (findFlag) item.isDictCompareDisabled = true;
+          const disabledFlag = keysType === KeysTypeEnum.DISABLED ? findFlag : !findFlag;
+          item.disabled = disabledFlag;
+
+          if (item.isDictCompareDisabled && item.children && item.children.length) {
+            setDisabled(item.children, disabledFlag);
+          } else {
+            if (item.children && item.children.length) {
+              item.children = permissionCompare(item.children, permissionKeys, keysType);
+              item.disabled = item.children.every((temp) => temp.disabled);
+            }
+          }
+          return {
+            ...item,
+            icon: item.meta.icon,
+          } as TreeData;
+        });
+      };
+
+      const result = permissionCompare(data, permissionKeys, keysType).map((item) => {
+        if (item.children && item.children.length) {
+          const rootDisabledFlag = item.children.every((temp) => temp.disabled);
+          item.disabled = rootDisabledFlag;
+        }
+        return item;
+      });
+
+      return result;
+    };
+
+    return {
+      spinning,
+      registerDrawer,
+      registerForm,
+      getTitle,
+      handleSubmit,
+      treeData,
+      roleMenus,
+    };
+  },
+});
+</script>
+
+<style scoped lang="less">
+:deep(.vben-basic-tree) {
+  width: 100% !important;
+}
+
+:deep(.is-unflod) {
+  display: none !important;
+}
+
+:deep(.is-flod) {
+  display: none !important;
+}
+</style>

+ 147 - 0
src/views/systemAdmin/system/tag/index.vue

@@ -0,0 +1,147 @@
+<template>
+  <div>
+    <BasicTable :rowSelection="{ type: 'checkbox' }" @register="registerTable" :clickToRowSelect="false">
+      <template #toolbar>
+        <Authority>
+          <a-button type="primary" @click="handleCreate">新增角色</a-button>
+        </Authority>
+        <Authority>
+          <Popconfirm title="您确定要批量删除数据" ok-text="确定" cancel-text="取消" @confirm="handleDeleteOrBatchDelete(null)">
+            <a-button type="primary" color="error" :disabled="hasBatchDelete"> 批量删除 </a-button>
+          </Popconfirm>
+        </Authority>
+      </template>
+      <template #status="{ record }">
+        <Switch :checked="record.status === 1" :loading="record.pendingStatus" checkedChildren="启用" unCheckedChildren="禁用"
+          @change="(checked: boolean) => statusChange(checked, record)" />
+      </template>
+      <template #action="{ record }">
+        <TableAction :actions="[
+          {
+            label: '编辑',
+            icon: 'clarity:note-edit-line',
+            onClick: handleEdit.bind(null, record),
+          },
+          {
+            label: '删除',
+            icon: 'ant-design:delete-outlined',
+            color: 'error',
+            ifShow: record.roleType != RoleEnum.SYS_ADMIN,
+            popConfirm: {
+              title: '是否确认删除',
+              confirm: handleDeleteOrBatchDelete.bind(null, record),
+            },
+          },
+        ]" />
+      </template>
+    </BasicTable>
+    <RoleDrawer @register="registerDrawer" @success="handleSuccess" />
+  </div>
+</template>
+<script lang="ts">
+import { defineComponent, nextTick } from 'vue';
+import { BasicTable, useTable, TableAction } from '/@/components/Table';
+import { delRole, getRoleListByPage, setRoleStatus } from '/@/api/system/system';
+import { useDrawer } from '/@/components/Drawer';
+import RoleDrawer from './RoleDrawer.vue';
+import { columns, searchFormSchema } from './role.data';
+import { RoleEnum } from '/@/enums/roleEnum';
+import { Authority } from '/@/components/Authority';
+import { useBatchDelete } from '/@/hooks/web/useBatchDelete';
+import { useMessage } from '/@/hooks/web/useMessage';
+import { Switch, Popconfirm } from 'ant-design-vue';
+import { roleList } from '/@/api/sys/user';
+
+export default defineComponent({
+  name: 'RoleManagement',
+  components: { BasicTable, RoleDrawer, TableAction, Authority, Switch, Popconfirm },
+  setup() {
+    const [registerDrawer, { openDrawer }] = useDrawer();
+    function handleSuccess() {
+      reload();
+    }
+    const [registerTable, { setProps, reload, setSelectedRowKeys }] = useTable({
+      title: '角色列表',
+      api: roleList,
+      columns,
+      formConfig: {
+        labelWidth: 120,
+        schemas: searchFormSchema,
+      },
+      useSearchForm: true,
+      showTableSetting: true,
+      bordered: true,
+      showIndexColumn: false,
+      actionColumn: {
+        width: 200,
+        title: '操作',
+        dataIndex: 'action',
+        slots: { customRender: 'action' },
+        fixed: 'right',
+      },
+    });
+    const { hasBatchDelete, handleDeleteOrBatchDelete, selectionOptions, resetSelectedRowKeys } =
+      useBatchDelete(delRole, handleSuccess, setProps);
+    selectionOptions.rowSelection.getCheckboxProps = (record: Recordable) => {
+      // Demo:status为1的选择框禁用
+      if (record.status === 1) {
+        return { disabled: true };
+      } else {
+        return { disabled: false };
+      }
+    };
+    nextTick(() => {
+      setProps(selectionOptions);
+    });
+
+    function handleCreate() {
+      openDrawer(true, {
+        isUpdate: false,
+      });
+    }
+
+    function handleEdit(record: Recordable) {
+      openDrawer(true, {
+        record,
+        isUpdate: true,
+      });
+      console.log(record)
+    }
+
+    const statusChange = async (checked, record) => {
+      setProps({
+        loading: true,
+      });
+      setSelectedRowKeys([]);
+      resetSelectedRowKeys();
+      const newStatus = checked ? 1 : 0;
+      const { createMessage } = useMessage();
+      try {
+        await setRoleStatus(record.id, newStatus);
+        if (newStatus) {
+          createMessage.success(`启用成功`);
+        } else {
+          createMessage.success('禁用成功');
+        }
+      } finally {
+        setProps({
+          loading: false,
+        });
+        reload();
+      }
+    };
+
+    return {
+      registerTable,
+      registerDrawer,
+      handleCreate,
+      handleEdit,
+      handleSuccess,
+      RoleEnum,
+      hasBatchDelete,
+      handleDeleteOrBatchDelete,
+      statusChange,
+    };
+  },
+});
+</script>

+ 90 - 0
src/views/systemAdmin/system/tag/role.data.ts

@@ -0,0 +1,90 @@
+import { BasicColumn } from '/@/components/Table';
+import { FormSchema } from '/@/components/Table';
+import { RoleEnum } from '/@/enums/roleEnum';
+
+export enum KeysTypeEnum {
+  DISABLED = 'disabled',
+  ENABLED = 'enabled',
+}
+
+export const RoleMenuDictEnum: Recordable<{ key: string; keyType: KeysTypeEnum }> = {
+  [RoleEnum.PLATFORM_ADMIN]: { key: 'enabled_platform_admin_auth', keyType: KeysTypeEnum.ENABLED },
+  [RoleEnum.SYS_ADMIN]: { key: 'enabled_sysadmin_auth', keyType: KeysTypeEnum.ENABLED },
+  [RoleEnum.TENANT_ADMIN]: { key: 'disabled_tenant_auth', keyType: KeysTypeEnum.DISABLED },
+  [RoleEnum.CUSTOMER_USER]: { key: 'disabled_tenant_auth', keyType: KeysTypeEnum.DISABLED },
+};
+
+export const columns: BasicColumn[] = [
+  {
+    title: '角色名称',
+    dataIndex: 'groupName',
+    width: 200,
+  },
+  {
+    title: '角色Code',
+    dataIndex: 'groupid',
+    width: 200,
+  },
+  {
+    title: '类型',
+    dataIndex: 'groupType',
+    width: 120,
+  },
+
+  {
+    title: '备注',
+    dataIndex: 'memo',
+    width: 240,
+  },
+  {
+    title: '排序',
+    dataIndex: 'sort',
+    width: 180,
+  },
+];
+
+export const searchFormSchema: FormSchema[] = [
+  {
+    field: 'roleName',
+    label: '角色名称',
+    component: 'Input',
+    colProps: { span: 6 },
+    componentProps: {
+      maxLength: 255,
+    },
+  },
+  {
+    field: 'status',
+    label: '状态',
+    component: 'Select',
+    componentProps: {
+      options: [
+        { label: '启用', value: 1 },
+        { label: '停用', value: 0 },
+      ],
+    },
+    colProps: { span: 6 },
+  },
+];
+
+export const formSchema: FormSchema[] = [
+  {
+    field: 'groupName',
+    label: '角色名称',
+    required: true,
+    component: 'Input',
+    componentProps: {
+      maxLength: 255,
+      placeholder: '请输入角色名称',
+    },
+  },
+  {
+    label: '排序',
+    field: 'sort',
+    component: 'Input',
+    componentProps: {
+      maxLength: 255,
+      placeholder: '请输入排序',
+    },
+  }
+];