houyaf пре 3 година
родитељ
комит
733b8822bd

+ 68 - 0
src/api/apps/appPermitApi.ts

@@ -0,0 +1,68 @@
+import apiRequest from '@/network/http/apiRequest'
+
+/**
+ * 获取App Permit列表
+ * @param data
+ * @returns
+ */
+export function getAppPermitByList(data: any) {
+  return apiRequest({
+    url: '/app/permit',
+    method: 'GET',
+    params: data,
+  })
+}
+
+/**
+ * 根据Id获取App Permit
+ * @param appId
+ * @param roleId
+ * @param permitId
+ * @returns
+ */
+export function getAppPermitById(appId: number, roleId: number, permitId: number) {
+  return apiRequest({
+    url: `/app/permit/${appId}/${roleId}/${permitId}`,
+    method: 'GET',
+  })
+}
+
+/**
+ * 添加App Permit
+ * @param data
+ * @returns
+ */
+export function createAppPermit(data: any) {
+  return apiRequest({
+    url: '/app/permit/add',
+    method: 'POST',
+    data,
+  })
+}
+
+/**
+ * 更新App Permit
+ * @param data
+ * @returns
+ */
+export function updateAppPermit(data: any) {
+  return apiRequest({
+    url: '/app/permit/update',
+    method: 'PUT',
+    data,
+  })
+}
+
+/**
+ * 删除App Permit
+ * @returns
+ * @param appId
+ * @param roleId
+ * @param permitId
+ */
+export function deleteAppPermitById(appId: number, roleId: number, permitId: number) {
+  return apiRequest({
+    url: `/app/permit/${appId}/${roleId}/${permitId}`,
+    method: 'DELETE',
+  })
+}

+ 13 - 1
src/router/modules/app.center.ts

@@ -15,7 +15,7 @@ const routes: RouteRecordRaw[] = [
     children: [
       {
         path: 'index',
-        name: 'app_center_release',
+        name: 'appCenterAppList',
         component: () => import('@/views/app_center/apps/index.vue'),
         meta: {
           title: '应用中心',
@@ -24,6 +24,18 @@ const routes: RouteRecordRaw[] = [
           activeMenu: '/app-center',
         },
       },
+      {
+        path: 'app/permit/:id',
+        name: 'appPermit',
+        component: () => import('@/views/app_center/permit/index.vue'),
+        meta: {
+          title: '配置应用权限',
+          sidebar: false,
+          activeMenu: '/app-center/index',
+          cache: true,
+          noCache: 'appPermit',
+        },
+      },
     ],
   },
   {

+ 7 - 1
src/views/app_center/apps/index.vue

@@ -6,6 +6,7 @@ meta:
 <script lang="ts" setup>
 import App from './components/App.vue'
 import { getAppByList } from '@/api/apps/appApi'
+const tabbar = useTabbar()
 
 const state = reactive({
   title: '',
@@ -34,7 +35,12 @@ const onAppEdit = (data: any) => {
 }
 
 const onAppPermit = (data: any) => {
-
+  tabbar.open({
+    name: 'appPermit',
+    params: {
+      id: data.appId,
+    },
+  })
 }
 </script>
 

+ 124 - 404
src/views/app_center/permit/index.vue

@@ -4,428 +4,177 @@ meta:
 </route>
 
 <script lang="ts" setup name="AppRolePermit">
-import { ElMessage, ElMessageBox } from 'element-plus'
-import type Node from 'element-plus/es/components/tree/src/model/node'
-import dictionaryDialog from './components/dictionaryDialog/index.vue'
-import dictionaryItemDialog from './components/dictionaryItemDialog/index.vue'
-import api from '@/api'
+import { getAppPermitByList } from '@/api/apps/appPermitApi'
+import { getRoleByList } from '@/api/system/roleApi'
+import useSettingsStore from '@/store/modules/settings'
 
-interface Dict {
-  id: string | number
+const settingsStore = useSettingsStore()
+
+const route = useRoute()
+const router = useRouter()
+const tabbar = useTabbar()
+
+interface TreeNode {
   label: string
-  code: string
-  children?: Dict[]
+  roleId: number
+  isLeaf: boolean
+  children?: TreeNode[]
 }
-const dictionaryRef = ref()
-const dictionary = ref({
-  search: '',
-  tree: [] as Dict[],
-  currentNode: undefined as Node | undefined,
-  currentData: undefined as Dict | undefined,
-  dialog: {
-    visible: false,
-    parentId: '' as Dict['id'],
-    id: '' as Dict['id'],
-  },
-})
 
-const { pagination, getParams, onSizeChange, onCurrentChange, onSortChange } = usePagination()
-pagination.value.size = 20
-pagination.value.sizes = [20, 50, 100]
-const dictionaryItemRef = ref()
-const dictionaryItem = ref({
+const state = reactive({
   loading: false,
-  // 搜索
+  curRoleId: 0,
+  roles: [] as TreeNode[],
+  permitList: [],
   search: {
-    dictionaryId: '' as Dict['id'],
-    title: '',
-  },
-  // 列表数据
-  dataList: [],
-  selectionDataList: [],
-  dialog: {
-    visible: false,
-    id: '' as string | number,
+    appId: 0,
+    roleId: 0,
+    keywords: '',
   },
 })
 
-const getDictionaryList = () => {
-  dictionaryItem.value.search.dictionaryId = ''
-  api.get('pages_example/dictionary/list', {
-    baseURL: '/mock/',
-  }).then((res) => {
-    dictionary.value.tree = res.data
-  })
-}
-onMounted(() => {
-  getDictionaryList()
-})
-watch(() => dictionary.value.search, (val) => {
-  dictionaryRef.value!.filter(val)
-})
-const dictionaryFilter = (value: string, data: Dict) => {
-  if (!value) {
-    return true
+const roleTreeRef = ref()
+
+// 查询Permit列表
+const getPermitData = async () => {
+  const curRoleId = state.curRoleId
+  if (curRoleId > 0) {
+    state.search.roleId = curRoleId
+    state.loading = true
+    const { data } = await getAppPermitByList(state.search)
+    state.loading = false
+    state.permitList = data
   }
-  return data.label.includes(value)
 }
-const dictionaryAdd = (data?: Dict) => {
-  dictionary.value.currentData = data
-  dictionary.value.dialog.parentId = data?.id ?? ''
-  dictionary.value.dialog.id = ''
-  dictionary.value.dialog.visible = true
-}
-const dictionaryEdit = (node: Node, data: Dict) => {
-  dictionary.value.currentNode = node
-  dictionary.value.currentData = data
-  dictionary.value.dialog.parentId = node.parent.data.id ?? ''
-  dictionary.value.dialog.id = data.id
-  dictionary.value.dialog.visible = true
-}
-const dictionaryDelete = (node: Node, data: Dict) => {
-  ElMessageBox.confirm(`确认删除「${data.label}」吗?`, '确认信息').then(() => {
-    api.post('pages_example/dictionary/delete', {
-      id: data.id,
-    }, {
-      baseURL: '/mock/',
-    }).then(() => {
-      ElMessage.success({
-        message: '模拟删除成功',
-        center: true,
+
+// 查询Role数据
+const getRoleData = async () => {
+  state.loading = true
+  const { data } = await getRoleByList({})
+  if (data && data.length > 0) {
+    state.curRoleId = data[0].roleId
+    await getPermitData()
+
+    const children: TreeNode[] = []
+    data.forEach((item: any) => {
+      return children.push({
+        label: item.roleName,
+        roleId: item.roleId,
+        isLeaf: true,
       })
-      const parent = node.parent
-      const children: Dict[] = parent.data.children || parent.data
-      const index = children.findIndex(d => d.id === data.id)
-      children.splice(index, 1)
-      dictionary.value.tree = [...dictionary.value.tree]
     })
-  })
-}
-// 新增成功后更新树
-const dictionaryAddNode = (data: Dict) => {
-  if (dictionary.value.currentData) {
-    if (!dictionary.value.currentData.children) {
-      dictionary.value.currentData.children = []
-    }
-    dictionary.value.currentData.children.push({
-      id: data.id,
-      label: data.label,
-      code: data.code,
-    })
-  }
-  else {
-    dictionary.value.tree.push({
-      id: data.id,
-      label: data.label,
-      code: data.code,
-    })
-  }
-}
-// 编辑成功后更新树
-const dictionaryEditNode = (data: Dict, parentId: string | number) => {
-  if (dictionary.value.currentNode && dictionary.value.currentData) {
-    if ((dictionary.value.currentNode.parent.data.id ?? '') === parentId) {
-      // 如果 parentId 一致说明节点位置没有变化,直接更新
-      dictionary.value.currentData.label = data.label
-      dictionary.value.currentData.code = data.code
-    }
-    else {
-      // 先更新原有节点信息
-      const parent = dictionary.value.currentNode.parent
-      const children: Dict[] = parent.data.children || parent.data
-      const index = children.findIndex(item => item.id === data.id)
-      children[index].label = data.label
-      children[index].code = data.code
-      // 然后找到需要移动到的父节点位置,并将原有节点移动过去
-      if (parentId) {
-        const findDictionary: any = (list: Dict[], parentId: number) => {
-          for (const i in list) {
-            if (list[i].id === parentId) {
-              return list[i]
-            }
-            else if (list[i].children) {
-              const temp = findDictionary(list[i].children, parentId)
-              if (temp) {
-                return temp
-              }
-            }
-          }
-        }
-        const targetNode = findDictionary(dictionary.value.tree, parentId)
-        if (!targetNode.children) {
-          targetNode.children = []
-        }
-        targetNode.children.push(children[index])
-      }
-      else {
-        dictionary.value.tree.push(children[index])
-      }
-      // 最后删除原节点
-      children.splice(index, 1)
-    }
+    state.roles = [
+      {
+        label: '系统角色',
+        isLeaf: false,
+        roleId: -1,
+        children,
+      },
+    ]
   }
 }
-const dictionaryClick = (data: Dict) => {
-  pagination.value.page = 1
-  dictionaryItem.value.search.dictionaryId = data.id
-}
 
-watch(() => dictionaryItem.value.search.dictionaryId, () => {
-  getDictionaryItemList()
+onMounted(() => {
+  getRoleData()
 })
 
-function getDictionaryItemList() {
-  dictionaryItem.value.loading = true
-  const params = getParams()
-  dictionaryItem.value.search.dictionaryId && (params.dictionaryId = dictionaryItem.value.search.dictionaryId)
-  dictionaryItem.value.search.title && (params.title = dictionaryItem.value.search.title)
-  api.get('pages_example/dictionary/item/list', {
-    baseURL: '/mock/',
-    params,
-  }).then((res: any) => {
-    dictionaryItem.value.loading = false
-    dictionaryItem.value.dataList = res.data.list
-    dictionaryItem.value.dataList.forEach((item: any) => {
-      item.enableLoading = false
-    })
-    pagination.value.total = res.data.total
-  })
-}
-
-function onChangeEnable(row: any) {
-  return new Promise<boolean>((resolve) => {
-    ElMessageBox.confirm(`确认${!row.enable ? '启用' : '禁用'}「${row.name}」吗?`, '确认信息').then(() => {
-      row.enableLoading = true
-      api.post('pages_example/dictionary/item/change/enable', {
-        id: row.id,
-        enable: !row.enable,
-      }, {
-        baseURL: '/mock/',
-      }).then(() => {
-        row.enableLoading = false
-        ElMessage.success({
-          message: `模拟${!row.enable ? '启用' : '禁用'}成功`,
-          center: true,
-        })
-        return resolve(true)
-      }).catch(() => {
-        row.enableLoading = false
-        return resolve(false)
-      })
-    }).catch(() => {
-      return resolve(false)
-    })
-  })
-}
-
-// 每页数量切换
-function sizeChange(size: number) {
-  onSizeChange(size).then(() => getDictionaryItemList())
-}
-
-// 当前页码切换(翻页)
-function currentChange(page = 1) {
-  onCurrentChange(page).then(() => getDictionaryItemList())
-}
-
-// 字段排序
-function sortChange({ prop, order }: { prop: string; order: string }) {
-  onSortChange(prop, order).then(() => getDictionaryItemList())
-}
-
-function onCreate() {
-  dictionaryItem.value.dialog.id = ''
-  dictionaryItem.value.dialog.visible = true
-}
-
-function onEdit(row: any) {
-  dictionaryItem.value.dialog.id = row.id
-  dictionaryItem.value.dialog.visible = true
+const onTreeNodeClick = (node: TreeNode) => {
+  if (node.isLeaf) {
+    state.curRoleId = node.roleId
+    getRoleData()
+  }
 }
+const permitTableRef = ref()
+const onPermitAdd = () => { }
 
-function onDelete(row: any) {
-  ElMessageBox.confirm(`确认删除「${row.name}」吗?`, '确认信息').then(() => {
-    api.post('pages_example/dictionary/item/delete', {
-      id: row.id,
-    }, {
-      baseURL: '/mock/',
-    }).then(() => {
-      getDictionaryItemList()
-      ElMessage.success({
-        message: '模拟删除成功',
-        center: true,
-      })
-    })
-  }).catch(() => {})
+function onCancel() {
+  goBack()
 }
 
-function onDeleteMulti(rows: any[]) {
-  ElMessageBox.confirm(`确认删除选中的 ${rows.length} 条数据吗?`, '确认信息').then(() => {
-    api.post('pages_example/dictionary/item/delete/multi', {
-      ids: rows.map(item => item.id),
-    }, {
-      baseURL: '/mock/',
-    }).then(() => {
-      getDictionaryItemList()
-      ElMessage.success({
-        message: '模拟删除成功',
-        center: true,
-      })
-    })
-  }).catch(() => {})
+// 返回列表页
+function goBack() {
+  if (settingsStore.settings.tabbar.enable && settingsStore.settings.tabbar.mergeTabsBy !== 'activeMenu') {
+    tabbar.close({ name: 'appCenterAppList' })
+  }
+  else {
+    router.push({ name: 'appCenterAppList' })
+  }
 }
 </script>
 
 <template>
   <div class="absolute-container">
-    <page-header title="字典管理" content="页面数据为 Mock 示例数据,非真实数据。" />
+    <page-header title="应用权限管理" content="为每个应用配置基于角色的权限。">
+      <el-button size="default" round @click="goBack">
+        <template #icon>
+          <el-icon>
+            <svg-icon name="i-ep:arrow-left" />
+          </el-icon>
+        </template>
+        返回
+      </el-button>
+    </page-header>
+
     <div class="page-main">
       <LayoutContainer hide-left-side-toggle>
         <template #leftSide>
-          <el-button-group class="btns">
-            <el-button type="primary" class="add" @click="dictionaryAdd()">
-              新增字典
-            </el-button>
-            <el-button @click="getDictionaryList">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:refresh" />
-                </el-icon>
-              </template>
-            </el-button>
-          </el-button-group>
-          <el-input v-model="dictionary.search" placeholder="请输入关键词筛选字典" clearable class="search" />
           <el-scrollbar class="tree">
-            <ElTree ref="dictionaryRef" :data="dictionary.tree" :filter-node-method="dictionaryFilter as any" default-expand-all @node-click="dictionaryClick">
+            <ElTree ref="roleTreeRef" :data="state.roles" default-expand-all @node-click="onTreeNodeClick">
               <template #default="{ node, data }">
                 <div class="custom-tree-node">
                   <div class="label" :title="node.label">
-                    {{ node.label }}
-                  </div>
-                  <div class="code">
-                    {{ data.code }}
-                  </div>
-                  <div class="actions">
-                    <el-button-group>
-                      <el-button type="primary" plain size="default" @click.stop="dictionaryAdd(data)">
-                        <template #icon>
-                          <el-icon>
-                            <svg-icon name="i-ep:plus" />
-                          </el-icon>
-                        </template>
-                      </el-button>
-                      <el-button type="info" plain size="default" @click.stop="dictionaryEdit(node, data)">
-                        <template #icon>
-                          <el-icon>
-                            <svg-icon name="i-ep:edit" />
-                          </el-icon>
-                        </template>
-                      </el-button>
-                      <el-button type="danger" plain size="default" @click.stop="dictionaryDelete(node, data)">
-                        <template #icon>
-                          <el-icon>
-                            <svg-icon name="i-ep:delete" />
-                          </el-icon>
-                        </template>
-                      </el-button>
-                    </el-button-group>
+                    <el-icon v-if="data.isLeaf" size="16px" style="margin-right: 5px;">
+                      <svg-icon name="uim:box" />
+                    </el-icon>
+                    <div class="text">
+                      {{ data.label }}
+                    </div>
                   </div>
                 </div>
               </template>
             </ElTree>
           </el-scrollbar>
         </template>
-        <div v-show="dictionaryItem.search.dictionaryId" class="container">
-          <el-space wrap>
-            <el-button type="primary" @click="onCreate">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:plus" />
-                </el-icon>
-              </template>
-            </el-button>
-            <el-button type="danger" :disabled="!dictionaryItem.selectionDataList.length" @click="onDeleteMulti(dictionaryItem.selectionDataList)">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:delete" />
-                </el-icon>
-              </template>
-            </el-button>
-            <el-input v-model="dictionaryItem.search.title" placeholder="请输入关键词筛选字典项" clearable style="width: 200px;" />
-            <el-button @click="getDictionaryItemList">
-              <template #icon>
-                <el-icon>
-                  <svg-icon name="i-ep:search" />
-                </el-icon>
-              </template>
-            </el-button>
-          </el-space>
-          <el-table ref="dictionaryItemRef" v-loading="dictionaryItem.loading" :data="dictionaryItem.dataList" border stripe highlight-current-row height="100%" @sort-change="sortChange" @selection-change="dictionaryItem.selectionDataList = $event">
+
+        <div class="container">
+          <tool-bar>
+            <template #right>
+              <el-input v-model="state.search.keywords" placeholder="请输入关键词筛选字典项" clearable style="width: 200px;" />
+              <el-button @click="getPermitData">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:search" />
+                  </el-icon>
+                </template>
+              </el-button>
+
+              <el-button type="primary" @click="onPermitAdd">
+                <template #icon>
+                  <el-icon>
+                    <svg-icon name="i-ep:plus" />
+                  </el-icon>
+                </template>
+              </el-button>
+            </template>
+          </tool-bar>
+
+          <el-table ref="permitTableRef" v-loading="state.loading" :data="state.permitList" border stripe highlight-current-row height="100%">
             <el-table-column type="selection" align="center" fixed />
-            <el-table-column prop="name" label="名称" />
-            <el-table-column label="键值" align="center" width="150">
-              <template #default="scope">
-                <el-tag type="info">
-                  {{ scope.row.value }}
-                </el-tag>
-              </template>
-            </el-table-column>
-            <el-table-column label="状态" width="100" align="center">
-              <template #default="scope">
-                <el-switch v-model="scope.row.enable" :loading="scope.row.enableLoading" inline-prompt active-text="启用" inactive-text="禁用" :before-change="() => onChangeEnable(scope.row)" />
-              </template>
-            </el-table-column>
-            <el-table-column label="操作" width="200" align="center">
-              <template #default="scope">
-                <el-button type="primary" size="small" plain @click="onEdit(scope.row)">
-                  编辑
-                </el-button>
-                <el-button type="danger" size="small" plain @click="onDelete(scope.row)">
-                  删除
-                </el-button>
-              </template>
-            </el-table-column>
           </el-table>
-          <el-pagination :current-page="pagination.page" :total="pagination.total" :page-size="pagination.size" :page-sizes="pagination.sizes" :layout="pagination.layout" :hide-on-single-page="false" class="pagination" background @size-change="sizeChange" @current-change="currentChange" />
-        </div>
-        <div v-show="!dictionaryItem.search.dictionaryId" class="container">
-          <div class="empty">
-            请在左侧新增或选择一个字典
-          </div>
         </div>
       </LayoutContainer>
-      <dictionaryDialog v-if="dictionary.dialog.visible" :id="dictionary.dialog.id" v-model="dictionary.dialog.visible" :parent-id="dictionary.dialog.parentId" :tree="dictionary.tree" @add-node="dictionaryAddNode" @edit-node="dictionaryEditNode" />
-      <dictionaryItemDialog v-if="dictionaryItem.dialog.visible" :id="dictionaryItem.dialog.id" v-model="dictionaryItem.dialog.visible" :dictionary-id="dictionaryItem.search.dictionaryId" :tree="dictionary.tree" @success="getDictionaryItemList" />
     </div>
   </div>
 </template>
 
 <style lang="scss" scoped>
-.absolute-container {
-  position: absolute;
-  width: 100%;
+.container {
   height: 100%;
   display: flex;
   flex-direction: column;
-
-  .page-header {
-    margin-bottom: 0;
-  }
-
-  .page-main {
-    // 让 page-main 的高度自适应
-    flex: 1;
-    overflow: auto;
-    display: flex;
-    flex-direction: column;
-
-    .flex-container {
-      position: static;
-    }
-  }
 }
 
-.flex-container {
+.absolute-container {
   :deep(.left-side) {
     display: flex;
     flex-direction: column;
@@ -434,23 +183,20 @@ function onDeleteMulti(rows: any[]) {
     .btns {
       display: inline-flex;
       width: 100%;
+      margin-bottom: var(--container-padding);
 
       .add {
         width: 100%;
       }
     }
 
-    .search {
-      margin: 15px 0;
-    }
-
     .tree {
       flex: 1;
       overflow-y: auto;
 
       .el-tree {
         .el-tree-node__content {
-          height: 60px;
+          height: 40px;
         }
 
         .is-current > .el-tree-node__content {
@@ -467,17 +213,14 @@ function onDeleteMulti(rows: any[]) {
           justify-content: center;
 
           .label {
+            display: inline-flex;
+            align-items: center;
             width: calc(100% - 10px);
             color: var(--el-text-color-primary);
 
-            @include text-overflow;
-          }
-
-          .code {
-            width: calc(100% - 10px);
-            color: var(--el-text-color-placeholder);
-
-            @include text-overflow;
+            .text {
+              @include text-overflow;
+            }
           }
 
           &:hover {
@@ -501,28 +244,5 @@ function onDeleteMulti(rows: any[]) {
       }
     }
   }
-
-  :deep(.main) {
-    display: flex;
-    justify-content: center;
-  }
-
-  .container {
-    display: flex;
-    flex-direction: column;
-    justify-content: center;
-    width: 100%;
-    height: 100%;
-
-    .empty {
-      text-align: center;
-      font-size: 32px;
-      color: var(--el-text-color-placeholder);
-    }
-
-    .el-table {
-      margin: 15px 0;
-    }
-  }
 }
 </style>

+ 12 - 13
src/views/daas/model/index.vue

@@ -42,6 +42,16 @@ const modelRef = ref()
 const entityTableRef = ref()
 const entityRef = ref()
 
+const getEntityList = async () => {
+  const curModelId = state.curModelId
+  if (curModelId > 0) {
+    state.loading = true
+    const { data } = await getEntityByList({ modelId: curModelId })
+    state.entityList = data
+    state.loading = false
+  }
+}
+
 const getModelList = async () => {
   const { data } = await getModelByList('')
   const children: TreeNode[] = []
@@ -63,19 +73,7 @@ const getModelList = async () => {
 
   if (data && data.length > 0) {
     state.curModelId = data[0].modelId
-    window.console.log(`curModelId:${data[0].modelId}`)
-    const res = await getEntityByList({ modelId: state.curModelId })
-    state.entityList = res.data
-  }
-}
-
-const getEntityList = async () => {
-  const curModelId = state.curModelId
-  if (curModelId > 0) {
-    state.loading = true
-    const { data } = await getEntityByList({ modelId: curModelId })
-    state.entityList = data
-    state.loading = false
+    await getEntityList()
   }
 }
 
@@ -191,6 +189,7 @@ const onEntityDelete = (row: any) => {
             </ElTree>
           </el-scrollbar>
         </template>
+
         <div class="container">
           <tool-bar>
             <template #right>