CzRger 2 weeks ago
parent
commit
b10802e045

+ 10 - 4
src/layout/top-left/head/index.vue

@@ -27,7 +27,13 @@
     <el-dropdown :disabled="AppStore.tenants.length < 2">
       <div class="__hover flex items-center gap-2 text-[#303133]">
         {{ AppStore.tenantInfo?.name }}
-        <SvgIcon name="czr_arrow" :rotate="90" size="10" color="#303133" />
+        <SvgIcon
+          name="czr_arrow"
+          :rotate="90"
+          size="10"
+          color="#303133"
+          v-if="AppStore.tenants.length > 1"
+        />
       </div>
       <template #dropdown>
         <el-dropdown-menu>
@@ -112,9 +118,9 @@ const state: any = reactive({
   activeNames: '',
 })
 const titleCpt = computed(() => import.meta.env.VITE_TITLE)
-const menusCpt: any = computed(
-  () => router.options.routes.filter((v) => v.name === 'root')[0],
-)
+const menusCpt: any = computed(() => {
+  return router.getRoutes().filter((v) => v.name === 'root')[0]
+})
 
 const copyDict = (label, value) => {
   try {

+ 4 - 6
src/router/index.ts

@@ -1,19 +1,17 @@
 import { createRouter, createWebHistory } from 'vue-router'
-import demoRouter from './modules/demo'
-import bigModelRouter from './modules/big-model'
 import Temp404 from '@/views/global/temp/404.vue'
 import RouterView from '@/layout/router-view.vue'
 // @ts-ignore
 import { useAppStore } from '@/stores'
 const routes = [
-  demoRouter,
   { path: '/:pathMatch(.*)*', name: 'NotFound', component: Temp404 },
   {
     name: 'root',
     path: '/',
     component: () => import('@/layout/index.vue'),
-    redirect: '/home',
-    children: [...bigModelRouter],
+    // redirect: '/home',
+    children: [],
+    // children: [...bigModelRouter],
   },
   {
     name: 'login',
@@ -62,7 +60,7 @@ router.beforeEach((to, from, next) => {
         if (localStorage.getItem((import.meta as any).env.VITE_TOKEN)) {
           AppStore.initUserInfo()
             .then(() => {
-              isLogin ? next({ name: 'root' }) : next()
+              isLogin ? next({ name: 'root' }) : next(to.path)
             })
             .catch((e) => {
               localStorage.removeItem((import.meta as any).env.VITE_TOKEN)

+ 2 - 4
src/router/modules/big-model.ts

@@ -1,4 +1,4 @@
-const BigModelRouter = [
+const getMenus = () => [
   {
     path: '/home',
     name: '4806d051-e037-4d9d-99a0-78aa2f2f362b',
@@ -119,7 +119,6 @@ const BigModelRouter = [
     meta: {
       title: '管理中心',
     },
-    redirect: '/center/user',
     children: [
       {
         path: 'tenant',
@@ -204,5 +203,4 @@ const BigModelRouter = [
     ],
   },
 ]
-
-export default BigModelRouter
+export default getMenus

+ 0 - 45
src/router/modules/demo.ts

@@ -1,45 +0,0 @@
-import demoLayout from '@/views/demo/index.vue'
-const demoRouter = {
-  path: '/demo',
-  name: 'demo',
-  redirect: '/demo/list',
-  component: demoLayout,
-  meta: {
-    title: '案例',
-  },
-  children: [
-    {
-      path: 'list',
-      name: '5e8d9d9e-b9bf-4cf4-8ab2-c606fb3c21e6',
-      component: () => import('@/views/demo/list/index.vue'),
-      meta: {
-        title: '列表查询',
-      },
-    },
-    {
-      path: 'form',
-      name: 'c51b8128-e1ef-478a-9791-1687666c801c',
-      component: () => import('@/views/demo/form/index.vue'),
-      meta: {
-        title: '表单',
-      },
-    },
-    {
-      path: 'table',
-      name: '80f7d7e8-131a-4a3e-ac1c-add9167f1a6c',
-      component: () => import('@/views/demo/table/index.vue'),
-      meta: {
-        title: '表格',
-      },
-    },
-    {
-      path: 'dialog',
-      name: 'a123970f-e73d-44c7-b7bb-a016c57a2425',
-      component: () => import('@/views/demo/dialog/index.vue'),
-      meta: {
-        title: '弹窗',
-      },
-    },
-  ],
-}
-export default demoRouter

+ 77 - 0
src/stores/modules/app.ts

@@ -1,11 +1,19 @@
 import { defineStore } from 'pinia'
 import { userInfo } from '@/api/modules/global/login'
+import bigModelRouter from '@/router/modules/big-model'
+import { useRouter } from 'vue-router'
+import router from '@/router'
 
 export const useAppStore = defineStore('app', {
   state: () => ({
     userInfo: null,
     tenantInfo: null,
     tenants: [],
+    permission: {
+      splitAuth: '___',
+      menusMap: new Map(),
+      authsMap: new Map(),
+    },
   }),
   getters: {},
   actions: {
@@ -16,6 +24,7 @@ export const useAppStore = defineStore('app', {
             this.userInfo = data
             this.tenantInfo = data?.tenant
             this.tenants = data?.tenants || []
+            this.initAuth()
             resolve(this.userInfo)
           })
           .catch((e) => {
@@ -35,5 +44,73 @@ export const useAppStore = defineStore('app', {
         l.style.display = 'none'
       }
     },
+    initAuth() {
+      const aMap = new Map()
+      const mMap = new Map()
+      const a = [
+        '3b046708-5a14-450f-9dcd-9d869e336ed7',
+        '7b05e9a1-37cc-4701-b10c-ad1e31121ab5',
+        'd8b2a8e5-d087-4722-b833-7b7e10df0ec0222',
+        'ef9f545d-803e-4fbe-97cf-c89e27bd02ce',
+        'd8b2a8e5-d087-4722-b833-dddd',
+        '7b05e9a1-37cc-4701-b10c-ad1e31121ab5___add',
+        '7b05e9a1-37cc-4701-b10c-ad1e31121ab5___edit',
+        'ef9f545d-803e-4fbe-97cf-c89e27bd02ce___auth',
+        'ef9f545d-803e-4fbe-97cf-c89e27bd02ce___relation',
+      ]
+      a.forEach((v) => {
+        if (v.includes(this.permission.splitAuth)) {
+          aMap.set(v, v)
+        } else {
+          mMap.set(v, v)
+        }
+      })
+      this.permission.menusMap = mMap
+      this.permission.authsMap = aMap
+      const deepMenu = (arr) => {
+        return arr.filter((v) => {
+          if (this.permission.menusMap.has(v.name)) {
+            if (v.children?.length > 0) {
+              v.children = deepMenu(v.children)
+            }
+            return true
+          }
+          return false
+        })
+      }
+      const authMenus = deepMenu(bigModelRouter())
+      const getOne = (obj, str = '') => {
+        if (obj.children?.length > 0) {
+          return getOne(obj.children[0], str + '/' + obj.path)
+        }
+        return str + '/' + obj.path
+      }
+      const deepRedirect = (obj, pRe = '') => {
+        if (obj.children?.length > 0) {
+          obj.redirect = getOne(obj.children[0], pRe)
+          obj.children.forEach((v) => {
+            deepRedirect(v, pRe + '/' + v.path)
+          })
+        }
+      }
+      authMenus.forEach((v) => {
+        deepRedirect(v, v.path)
+      })
+      authMenus.forEach((v) => {
+        router.addRoute('root', v)
+      })
+      const Root = router.getRoutes().filter((v) => v.name === 'root')[0]
+      Root.children = authMenus
+      Root.redirect = authMenus[0].redirect
+    },
+    hasPermission(name, key) {
+      if (key) {
+        return this.permission.authsMap.has(
+          name + this.permission.splitAuth + key,
+        )
+      } else {
+        return this.permission.menusMap.has(name)
+      }
+    },
   },
 })

+ 0 - 52
src/views/demo/dialog/dialog1.vue

@@ -1,52 +0,0 @@
-<template>
-  <CzrDialog
-    :show="show"
-    title="默认弹窗"
-    @onClose="$emit('update:show', false)"
-    width="1000px"
-    height="auto"
-    maxHeight="80%"
-    :loading="state.loading"
-    :show-submit="false"
-  >
-    <div class="__normal-form">
-      <el-button @click="() => state.length++">新增一行</el-button>
-      <el-button @click="() => state.length--">删除一行</el-button>
-      <template v-for="(item, index) in state.length">
-        <h1>{{ index }}</h1>
-      </template>
-    </div>
-  </CzrDialog>
-</template>
-
-<script setup lang="ts">
-import {
-  computed,
-  getCurrentInstance,
-  nextTick,
-  reactive,
-  ref,
-  watch,
-} from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-const emit = defineEmits(['update:show', 'refresh'])
-const { proxy } = getCurrentInstance()
-
-const props = defineProps({
-  show: { default: false },
-  transfer: {},
-})
-const state: any = reactive({
-  loading: false,
-  form: {},
-  length: 10,
-})
-const ref_form = ref()
-watch(
-  () => props.show,
-  (n) => {},
-)
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 52
src/views/demo/dialog/dialog2.vue

@@ -1,52 +0,0 @@
-<template>
-  <CzrDialog
-    :show="show"
-    title="默认弹窗"
-    @onClose="$emit('update:show', false)"
-    width="1000px"
-    height="auto"
-    maxHeight="80%"
-    :loading="state.loading"
-    :show-submit="false"
-  >
-    <div class="__normal-form">
-      <el-button @click="() => state.length++">新增一行</el-button>
-      <el-button @click="() => state.length--">删除一行</el-button>
-      <template v-for="(item, index) in state.length">
-        <h1>{{ index }}</h1>
-      </template>
-    </div>
-  </CzrDialog>
-</template>
-
-<script setup lang="ts">
-import {
-  computed,
-  getCurrentInstance,
-  nextTick,
-  reactive,
-  ref,
-  watch,
-} from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-const emit = defineEmits(['update:show', 'refresh'])
-const { proxy } = getCurrentInstance()
-
-const props = defineProps({
-  show: { default: false },
-  transfer: {},
-})
-const state: any = reactive({
-  loading: false,
-  form: {},
-  length: 10,
-})
-const ref_form = ref()
-watch(
-  () => props.show,
-  (n) => {},
-)
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 49
src/views/demo/dialog/index.vue

@@ -1,49 +0,0 @@
-<template>
-  <div class="main">
-    <el-button @click="() => (state.show1 = true)">默认弹窗</el-button>
-    <Dialog1 v-model:show="state.show1" />
-    <el-button @click="() => (state.show2 = true)">hiddenStyle</el-button>
-    <Dialog2 v-model:show="state.show2" :hiddenStyle="true" />
-  </div>
-</template>
-
-<script setup lang="ts">
-import {
-  computed,
-  getCurrentInstance,
-  onMounted,
-  reactive,
-  ref,
-  watch,
-} from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import Dialog1 from './dialog1.vue'
-import Dialog2 from './dialog2.vue'
-
-const { proxy } = getCurrentInstance()
-const state: any = reactive({
-  show1: false,
-  show2: false,
-})
-const ref_form = ref()
-
-onMounted(() => {})
-</script>
-
-<style lang="scss" scoped>
-.main {
-  width: 100%;
-  height: 100%;
-  padding: 20px;
-  overflow-y: auto;
-  .content {
-    display: flex;
-    gap: 20px;
-    > div {
-      flex: 1;
-      height: fit-content;
-      border: 4px solid #ccc;
-    }
-  }
-}
-</style>

+ 0 - 126
src/views/demo/form/index.vue

@@ -1,126 +0,0 @@
-<template>
-  <div class="main">
-    <el-collapse v-model="state.activeNames" accordion>
-      <el-collapse-item title="表单(CzrForm、CzrFormColumn)" name="form">
-        <h1>表单应用及校验</h1>
-        <el-row>
-          <CzrFormColumn
-            label="labelWidth"
-            link="number"
-            v-model:param="state.form.labelWidth"
-            :clearable="false"
-          />
-          <CzrFormColumn
-            label="formView"
-            link="radio"
-            v-model:param="state.form.formView"
-            :clearable="false"
-            :options="[
-              { label: '禁用', value: true },
-              { label: '可修改', value: false },
-            ]"
-          />
-          <el-button type="primary" @click="onSubmit"
-            >触发表单校验信息</el-button
-          >
-          <el-button type="warning" @click="() => ref_form.reset()"
-            >移除表单校验信息</el-button
-          >
-        </el-row>
-        <CzrForm
-          ref="ref_form"
-          :label-width="state.form.labelWidth + 'px'"
-          :form-view="state.form.formView"
-          @handleEnter="() => ElMessage.success('表单内触发了回车方法!')"
-        >
-          <CzrFormColumn label="非必填" v-model:param="state.form.input1" />
-          <CzrFormColumn
-            required
-            label="必填"
-            v-model:param="state.form.input2"
-          />
-          <CzrFormColumn
-            label="特殊校验(<10)"
-            link="number"
-            v-model:param="state.form.input3"
-            :rules="[
-              {
-                handle: (val: any) =>
-                  !isValue(val) || (isValue(val) && val < 10),
-                message: `输入值需<10`,
-              },
-            ]"
-          />
-          <el-col :span="24">
-            模拟表格
-            <div style="border: 1px solid #000000">
-              <el-row>
-                <el-col :span="6" style="text-align: center">p1</el-col>
-                <el-col :span="6" style="text-align: center">p2</el-col>
-              </el-row>
-              <template v-for="item in state.form.table">
-                <el-row>
-                  <CzrFormColumn v-model:param="item.p1" />
-                  <CzrFormColumn required v-model:param="item.p2" />
-                </el-row>
-              </template>
-            </div>
-          </el-col>
-        </CzrForm>
-      </el-collapse-item>
-    </el-collapse>
-  </div>
-</template>
-
-<script setup lang="ts">
-import {
-  computed,
-  getCurrentInstance,
-  onMounted,
-  reactive,
-  ref,
-  watch,
-} from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { isValue } from '@/utils/czr-util'
-
-const { proxy } = getCurrentInstance()
-const state: any = reactive({
-  activeNames: 'form',
-  form: {
-    labelWidth: 100,
-    formView: false,
-    input1: '',
-    input2: '',
-    input3: '',
-    table: [
-      { p1: '', p2: '' },
-      { p1: '', p2: '' },
-      { p1: '', p2: '' },
-    ],
-  },
-})
-const ref_form = ref()
-
-const onSubmit = () => {
-  ref_form.value
-    .submit()
-    .then(() => {})
-    .catch((e) => {
-      ElMessage({
-        message: e[0].message,
-        grouping: true,
-        type: 'warning',
-      })
-    })
-}
-onMounted(() => {})
-</script>
-
-<style lang="scss" scoped>
-.main {
-  width: 100%;
-  height: 100%;
-  padding: 20px;
-}
-</style>

+ 0 - 202
src/views/demo/index.vue

@@ -1,202 +0,0 @@
-<template>
-  <div class="layout">
-    <div class="layout-head">
-      智能问答系统<span>这是一次规范的定义,也是自我思维格局的提升</span>
-    </div>
-    <div class="layout-content">
-      <div class="layout-content-menu">
-        <div class="sub-menu-main">
-          <template
-            v-for="(item, index) in $router.options.routes.filter(
-              (v) => v.name === 'demo',
-            )[0].children"
-          >
-            <div class="smm-parent">
-              <div
-                class="p-item __hover"
-                :class="{ current: item.name === $route.name }"
-                @click="
-                  item.children?.length > 0
-                    ? (item.expend = !item.expend)
-                    : toSubMenu(item)
-                "
-              >
-                {{ item.meta.title }}
-                <SvgIcon
-                  class="menu-expend"
-                  name="czr_arrow"
-                  size="12"
-                  color="#ffffff"
-                  :class="{ active: item.expend }"
-                  v-if="item.children?.length > 0"
-                />
-              </div>
-              <div
-                class="smm-son"
-                v-if="item.children?.length > 0 && item.expend"
-              >
-                <template v-for="(subItem, subIndex) in item.children">
-                  <div
-                    class="s-item __hover"
-                    :class="{ current: subItem.name === $route.name }"
-                    @click="toSubMenu(subItem)"
-                  >
-                    {{ subItem.meta.title }}
-                  </div>
-                </template>
-              </div>
-            </div>
-          </template>
-        </div>
-      </div>
-      <div class="layout-content-main">
-        <router-view />
-      </div>
-    </div>
-  </div>
-</template>
-
-<script setup lang="ts">
-import { getCurrentInstance, reactive } from 'vue'
-import { useRoute, useRouter } from 'vue-router'
-const route = useRoute()
-const router = useRouter()
-const state: any = reactive({})
-const toSubMenu = (menu: any) => {
-  const deep = (r: any) => {
-    if (r.children?.length > 0) {
-      deep(r.children[0])
-    } else {
-      if (menu.name !== route.name) {
-        router.push({
-          name: r.name,
-        })
-      }
-    }
-  }
-  deep(menu)
-}
-</script>
-
-<style lang="scss" scoped>
-.layout {
-  width: 100%;
-  height: 100%;
-  display: flex;
-  flex-direction: column;
-  .layout-head {
-    //background-image: url('@/assets/images/demo-head-bg.png');
-    background-repeat: no-repeat;
-    background-position-x: right;
-    width: 100%;
-    height: 59px;
-    font-size: 30px;
-    font-family: Microsoft YaHei;
-    font-weight: bold;
-    color: #ffffff;
-    user-select: none;
-    padding-left: 20px;
-    display: flex;
-    align-items: center;
-    > span {
-      font-size: 16px;
-      margin-left: 40px;
-    }
-  }
-  .layout-content {
-    flex: 1;
-    display: flex;
-    overflow: hidden;
-    .layout-content-menu {
-      width: 204px;
-      height: 100%;
-      .current {
-        background-color: #0062e9;
-      }
-      .sub-menu-main {
-        width: 100%;
-        height: 100%;
-        overflow-y: auto;
-        background: linear-gradient(0deg, #021d78 0%, #0f44b6 100%);
-        padding-top: 13px;
-        .smm-parent {
-          .p-item {
-            display: flex;
-            align-items: center;
-            height: 44px;
-            line-height: 1;
-            user-select: none;
-            font-size: 14px;
-            font-family: Microsoft YaHei;
-            font-weight: 400;
-            color: #ffffff;
-            padding-left: 20px;
-            .menu-logo {
-              margin: 0 13px 0 15px;
-            }
-            .menu-expend {
-              margin: 0 12px 0 auto;
-              transition: 0.2s;
-              &.active {
-                transform: rotate(90deg) !important;
-              }
-            }
-          }
-          .smm-son {
-            .s-item {
-              height: 44px;
-              padding-left: 46px;
-              display: flex;
-              align-items: center;
-              line-height: 1.2;
-              user-select: none;
-              font-size: 14px;
-              font-family: Microsoft YaHei;
-              font-weight: 400;
-              color: #ffffff;
-              padding-left: 20px;
-            }
-          }
-        }
-        .smm-parent-not-expend {
-          height: 44px;
-          display: flex;
-          align-items: center;
-          .menu-logo {
-            margin: 0 13px 0 15px;
-          }
-        }
-        :deep(.smm-parent-son) {
-          background: #0a369f;
-          border: none;
-          padding: 0;
-          width: auto !important;
-          min-width: 0;
-          .el-popper__arrow::before {
-            background: #0a369f;
-            border-color: #0a369f;
-          }
-          .smm-son {
-            .s-item {
-              padding: 0 20px;
-              height: 44px;
-              display: flex;
-              align-items: center;
-              line-height: 1.2;
-              user-select: none;
-              font-size: 14px;
-              font-family: Microsoft YaHei;
-              font-weight: 400;
-              color: #ffffff;
-            }
-          }
-        }
-      }
-    }
-    .layout-content-main {
-      flex: 1;
-      overflow: hidden;
-    }
-  }
-}
-</style>

+ 0 - 122
src/views/demo/list/detail.vue

@@ -1,122 +0,0 @@
-<template>
-  <CzrDialog
-    :show="show"
-    :title="titleCpt"
-    @onClose="$emit('update:show', false)"
-    @onSubmit="onSubmit"
-    width="1000px"
-    height="auto"
-    maxHeight="80%"
-    :loading="state.loading"
-  >
-    <div class="__normal-form">
-      <CzrForm ref="ref_form" label-width="100px" :form-view="!noViewCpt">
-        <CzrFormColumn
-          required
-          :span="8"
-          label="参数"
-          v-model:param="state.form.p1"
-        />
-        <CzrFormColumn :span="8" label="参数" v-model:param="state.form.p1" />
-        <CzrFormColumn :span="8" label="参数" v-model:param="state.form.p1" />
-        <CzrFormColumn :span="8" label="参数" v-model:param="state.form.p1" />
-        <CzrFormColumn :span="8" label="参数" v-model:param="state.form.p1" />
-        <CzrFormColumn :span="8" label="参数" v-model:param="state.form.p1" />
-        <CzrFormColumn :span="8" label="参数" v-model:param="state.form.p1" />
-      </CzrForm>
-    </div>
-  </CzrDialog>
-</template>
-
-<script setup lang="ts">
-import {
-  computed,
-  getCurrentInstance,
-  nextTick,
-  reactive,
-  ref,
-  watch,
-} from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-
-const emit = defineEmits(['update:show', 'refresh'])
-const { proxy } = getCurrentInstance()
-
-const props = defineProps({
-  show: { default: false },
-  transfer: {},
-})
-const state: any = reactive({
-  loading: false,
-  form: {},
-})
-const ref_form = ref()
-const titleCpt = computed(() => {
-  let t = '表单'
-  switch (props.transfer.mode) {
-    case 'add':
-      t = '新增' + t
-      break
-    case 'edit':
-      t = '修改' + t
-      break
-    case 'view':
-      t = '查看' + t
-      break
-  }
-  return t
-})
-const noViewCpt = computed(() => props.transfer?.mode !== 'view')
-watch(
-  () => props.show,
-  (n) => {
-    if (n) {
-      initDictionary()
-      state.form = {}
-      if (props.transfer.mode !== 'add') {
-        initData()
-      }
-      nextTick(() => {
-        ref_form.value.reset()
-      })
-    }
-  },
-)
-const initData = () => {}
-const onSubmit = () => {
-  ref_form.value
-    .submit()
-    .then(() => {
-      ElMessageBox.confirm('是否提交?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      } as any)
-        .then(() => {
-          state.loading = true
-          if (props.transfer.mode == 'add') {
-            ElMessage.success('新增成功!')
-            emit('update:show', false)
-            emit('refresh')
-            state.loading = false
-          } else if (props.transfer.mode == 'edit') {
-            ElMessage.success('修改成功!')
-            emit('update:show', false)
-            emit('refresh')
-            state.loading = false
-          }
-        })
-        .catch(() => {})
-    })
-    .catch((e) => {
-      ElMessage({
-        message: e[0].message,
-        grouping: true,
-        type: 'warning',
-      })
-    })
-}
-const initDictionary = () => {}
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 328
src/views/demo/list/index.vue

@@ -1,328 +0,0 @@
-<template>
-  <div class="__normal-page">
-    <div class="__normal-content">
-      <CzrContent
-        v-model:head="state.query.tableHead"
-        @handleReset="onReset"
-        @handleSearch="onSearch"
-        v-model:full="state.query.isFull"
-      >
-        <template #fieldOut>
-          <CzrForm>
-            <CzrFormColumn
-              filterSpan="1"
-              label="参数"
-              v-model:param="state.query.form.p1"
-            />
-            <CzrFormColumn
-              filterSpan="1"
-              label="参数"
-              v-model:param="state.query.form.p1"
-            />
-            <CzrFormColumn
-              filterSpan="1"
-              label="参数"
-              v-model:param="state.query.form.p1"
-            />
-            <CzrFormColumn
-              filterSpan="1"
-              label="参数"
-              v-model:param="state.query.form.p1"
-            />
-            <CzrFormColumn
-              filterSpan="1"
-              label="参数"
-              v-model:param="state.query.form.p1"
-            />
-            <CzrFormColumn
-              filterSpan="1"
-              label="参数"
-              v-model:param="state.query.form.p1"
-            />
-            <CzrFormColumn
-              filterSpan="1"
-              label="参数"
-              v-model:param="state.query.form.p1"
-            />
-            <CzrFormColumn
-              filterSpan="2"
-              label="创建时间"
-              v-model:param="state.query.form.createTime"
-              link="datetime"
-              type="datetimerange"
-            />
-
-            <template v-if="state.query.expandValue">
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-              <CzrFormColumn
-                filterSpan="1"
-                label="参数"
-                v-model:param="state.query.form.p1"
-              />
-            </template>
-            <CzrSearchButtons
-              v-model:expandValue="state.query.expandValue"
-              @handleSearch="onSearch"
-              @handleReset="onReset"
-            />
-          </CzrForm>
-        </template>
-        <template #buttons>
-          <CzrButton type="add" @click="onAdd()" />
-          <CzrButton type="export" @click="onExport" />
-          <CzrButton type="del" @click="onDel()" />
-        </template>
-        <template #table>
-          <CzrTable
-            v-loading="state.query.loading"
-            ref="ref_czrTable"
-            :data="state.query.result.data"
-            :head="state.query.tableHead"
-            :total="state.query.result.total"
-            :page="state.query.page.pageNum"
-            :pageSize="state.query.page.pageSize"
-            @handlePage="onPage"
-            @handleSort="onSort"
-            v-model:selected="state.query.selected"
-            v-model:full="state.query.isFull"
-          >
-            <template #importAndExportType-column-value="{ scope }"> </template>
-            <template #caozuo-column-value="{ scope }">
-              <div class="__czr-table-operations">
-                <CzrButton
-                  type="table"
-                  title="修改"
-                  @click="onEdit(scope.row)"
-                />
-                <CzrButton type="table-del" @click="onDel(scope.row)" />
-              </div>
-            </template>
-          </CzrTable>
-        </template>
-      </CzrContent>
-    </div>
-    <detailCom
-      v-model:show="state.detail.show"
-      :transfer="state.detail.transfer"
-      @refresh="onSearch"
-    />
-  </div>
-</template>
-
-<script setup lang="ts">
-import { computed, getCurrentInstance, onMounted, reactive, watch } from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import detailCom from './detail.vue'
-
-const { proxy } = getCurrentInstance()
-const state: any = reactive({
-  query: {
-    loading: false,
-    expandValue: false,
-    page: {
-      pageNum: 1,
-      pageSize: 10,
-    },
-    tableHead: [
-      {
-        value: 'p1',
-        label: '参数1',
-        show: true,
-        width: 120,
-        fixed: 'left',
-        popover: false,
-      },
-      { value: 'p2', label: '排序', show: true, sort: true },
-      { value: 'p3', label: '参数3', show: true },
-      { value: 'p4', label: '参数4', show: true },
-      { value: 'p5', label: '参数5', show: true },
-      { value: 'p6', label: '参数6', show: true },
-      { value: 'p7', label: '参数7', show: true },
-      { value: 'p8', label: '参数8', show: true },
-      { value: 'p9', label: '参数9', show: true },
-      { value: 'p10', label: '参数11', show: true },
-      {
-        value: 'caozuo',
-        label: '操作',
-        width: 200,
-        show: true,
-        fixed: 'right',
-      },
-    ],
-    form: {},
-    formReal: {},
-    sort: {},
-    result: {
-      total: 0,
-      data: [
-        {
-          p1: '啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊',
-          p2: '啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊',
-        },
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-        {},
-      ],
-    },
-    selected: [],
-    isFull: false,
-  },
-  detail: {
-    show: false,
-    transfer: {},
-  },
-})
-
-const onSort = ({ key, value }) => {
-  state.query.sort[key] = value
-  onSearch()
-}
-const onPage = (pageNum, pageSize) => {
-  state.query.page = {
-    pageNum: pageNum,
-    pageSize: pageSize,
-  }
-  const params = {
-    pageNum: state.query.page.pageNum,
-    pageSize: state.query.page.pageSize,
-  }
-  //  添加表单参数
-  for (const [k, v] of Object.entries(state.query.formReal)) {
-    if (k === 'createTime') {
-      params['beginCreateTime'] = v[0]
-      params['endCreateTime'] = v[1]
-    } else if (proxy.$czrUtil.isValue(v)) {
-      params[k] = v
-    }
-  }
-  //  添加排序参数
-  for (const [k, v] of Object.entries(state.query.sort)) {
-  }
-  // state.query.loading = true
-  // api请求(params).then(res => {
-  //   if (res.code == 200) {
-  //     state.query.result.total = res.total
-  //     state.query.result.data = res.rows
-  //     state.query.loading = false
-  //   } else {
-  //     ElMessage.error(res.msg)
-  //   }
-  // }).catch(() => {
-  //   state.query.loading = false
-  // })
-}
-const onSearch = () => {
-  state.query.selected = []
-  state.query.formReal = JSON.parse(JSON.stringify(state.query.form))
-  onPage(1, state.query.page.pageSize)
-}
-const onReset = () => {
-  state.query.page = {
-    pageNum: 1,
-    pageSize: 10,
-  }
-  state.query.sort = {}
-  state.query.form = {}
-  onSearch()
-}
-const onAdd = () => {
-  state.detail.transfer = {
-    mode: 'add',
-  }
-  state.detail.show = true
-}
-const onEdit = (row) => {
-  state.detail.transfer = {
-    mode: 'edit',
-    id: row.id,
-  }
-  state.detail.show = true
-}
-const onDel = (row = null) => {
-  ElMessageBox.confirm(`请确认是否删除${row.name}?`, '提示', {
-    confirmButtonText: '确定',
-    cancelButtonText: '取消',
-    type: 'warning',
-  } as any)
-    .then(() => {
-      state.query.loading = true
-      if ('成功') {
-        ElMessage.success('删除成功!')
-        onSearch()
-      } else {
-        ElMessage.error('错误信息')
-      }
-    })
-    .catch(() => {
-      state.query.loading = false
-    })
-}
-const onExport = () => {}
-const initDictionary = () => {}
-onMounted(() => {
-  initDictionary()
-  onReset()
-})
-</script>
-
-<style lang="scss" scoped></style>

+ 0 - 143
src/views/demo/table/index.vue

@@ -1,143 +0,0 @@
-<template>
-  <div class="main">
-    <el-collapse v-model="state.activeNames" accordion>
-      <el-collapse-item title="高度(full)" name="height">
-        <el-button @click="() => state.table1.data.push({})"
-          >添加一行</el-button
-        >
-        <el-button type="danger" @click="() => state.table1.data.splice(0, 1)"
-          >删除一行</el-button
-        >
-        <div class="content">
-          <div style="height: 300px">
-            <CzrTable
-              :data="state.table1.data"
-              :head="[{ label: '外层高度固定', value: 'p', show: true }]"
-              :full="false"
-              no-foot
-            />
-          </div>
-          <div>
-            <CzrTable
-              :data="state.table1.data"
-              :head="[{ label: '动态最大高度', value: 'p', show: true }]"
-              :full="true"
-              no-foot
-              maxHeight="300px"
-            />
-          </div>
-          <div>
-            <CzrTable
-              :data="state.table1.data"
-              :head="[{ label: '不限制高度', value: 'p', show: true }]"
-              :full="true"
-              no-foot
-            />
-          </div>
-        </div>
-      </el-collapse-item>
-      <el-collapse-item title="多选(selected)" name="selected">
-        selected:{{ state.table2.selected }}
-        <el-button
-          @click="() => (state.table2.selected = [{ p1: 1 }, { p1: 3 }])"
-          >选择1、3</el-button
-        >
-        <div style="height: 300px">
-          <CzrTable
-            :data="state.table2.data"
-            :head="state.table2.head"
-            :full="false"
-            no-page
-            id-key="p1"
-            v-model:selected="state.table2.selected"
-          />
-        </div>
-      </el-collapse-item>
-      <el-collapse-item title="单选(singled)" name="singled">
-        selected:{{ state.table2.singled }}
-        <el-button @click="() => (state.table2.singled = { p1: 3 })"
-          >选择3</el-button
-        >
-        <div style="height: 300px">
-          <CzrTable
-            :data="state.table2.data"
-            :head="state.table2.head"
-            :full="false"
-            no-page
-            id-key="p1"
-            v-model:singled="state.table2.singled"
-            :singleable="(scope) => scope.$index < 3"
-          />
-        </div>
-      </el-collapse-item>
-      <el-collapse-item title="行拖拽(dragable)" name="dragable">
-        <div style="height: 300px">
-          <CzrTable
-            v-model:data="state.table2.data"
-            :head="state.table2.head"
-            id-key="p1"
-            no-page
-            :dragable="true"
-          />
-        </div>
-      </el-collapse-item>
-    </el-collapse>
-  </div>
-</template>
-
-<script setup lang="ts">
-import {
-  computed,
-  getCurrentInstance,
-  onMounted,
-  reactive,
-  ref,
-  watch,
-} from 'vue'
-import { ElMessage, ElMessageBox } from 'element-plus'
-import { isValue } from '@/utils/czr-util'
-
-const { proxy } = getCurrentInstance()
-const state: any = reactive({
-  activeNames: 'dragable',
-  table1: {
-    data: [],
-  },
-  table2: {
-    head: [{ label: '参数1', value: 'p1', show: true }],
-    data: [
-      { p1: 1 },
-      { p1: 2 },
-      { p1: 3 },
-      { p1: 4 },
-      { p1: 5 },
-      { p1: 6 },
-      { p1: 7 },
-      { p1: 8 },
-    ],
-    selected: [],
-    singled: {},
-  },
-})
-const ref_form = ref()
-
-onMounted(() => {})
-</script>
-
-<style lang="scss" scoped>
-.main {
-  width: 100%;
-  height: 100%;
-  padding: 20px;
-  overflow-y: auto;
-  .content {
-    display: flex;
-    gap: 20px;
-    > div {
-      flex: 1;
-      height: fit-content;
-      border: 4px solid #ccc;
-    }
-  }
-}
-</style>

+ 54 - 14
src/views/manage/center/role/auth.vue

@@ -20,7 +20,7 @@
             :prefix-icon="Search"
           />
         </div>
-        <div class="flex-1">
+        <div class="flex-1 overflow-y-auto">
           <el-tree
             ref="ref_tree"
             class="tree"
@@ -31,6 +31,7 @@
             node-key="value"
             :check-on-click-leaf="false"
             :expand-on-click-node="false"
+            :check-strictly="true"
             @node-click="onNodeClick"
           />
         </div>
@@ -44,9 +45,13 @@
             <div class="font-bold">{{ state.currentMenu.title }}</div>
             <div class="flex flex-1 flex-col gap-2 overflow-auto">
               <template v-if="state.currentMenu.auths?.length > 0">
-                <template v-for="item in state.currentMenu.auths">
-                  <div>{{ item }}</div>
-                </template>
+                <el-checkbox-group v-model="state.activeAuths">
+                  <template v-for="item in state.currentMenu.auths">
+                    <el-checkbox :value="item.value">
+                      {{ item.label }}
+                    </el-checkbox>
+                  </template>
+                </el-checkbox-group>
               </template>
               <template v-else> 该菜单暂无可配置权限 </template>
             </div>
@@ -71,9 +76,11 @@ import { useAppStore, useDialogStore, useDictionaryStore } from '@/stores'
 import { useRouter } from 'vue-router'
 import { Search } from '@element-plus/icons-vue'
 import CzrDialog from '@/components/czr-ui/CzrDialog.vue'
-import { rolesMenus } from '@/api/modules/center/role'
+import { rolesMenus, rolesMenusAdd } from '@/api/modules/center/role'
+import bigModelRouter from '@/router/modules/big-model'
 
 const DialogStore = useDialogStore()
+const AppStore = useAppStore()
 const router = useRouter()
 const emit = defineEmits(['update:show', 'refresh'])
 const { proxy } = getCurrentInstance()
@@ -84,10 +91,11 @@ const props = defineProps({
 const state: any = reactive({
   loading: false,
   text: '',
-  auths: [],
+  authsOptions: [],
+  activeAuths: [],
   currentMenu: {
     title: '',
-    auth: [],
+    auths: [],
   },
 })
 const ref_tree = ref()
@@ -107,9 +115,30 @@ watch(
 const initDictionary = () => {}
 const initData = () => {
   state.loading = true
+  state.activeAuths = []
+  ref_tree.value?.setCheckedKeys([], false)
   rolesMenus(props.transfer.roleId)
     .then(({ data }: any) => {
-      state.auths = data
+      data = [
+        '7b05e9a1-37cc-4701-b10c-ad1e31121ab5',
+        'ef9f545d-803e-4fbe-97cf-c89e27bd02ce',
+        '3b046708-5a14-450f-9dcd-9d869e336ed7',
+        '7b05e9a1-37cc-4701-b10c-ad1e31121ab5___add',
+        '7b05e9a1-37cc-4701-b10c-ad1e31121ab5___edit',
+        'ef9f545d-803e-4fbe-97cf-c89e27bd02ce___auth',
+        'ef9f545d-803e-4fbe-97cf-c89e27bd02ce___relation',
+      ]
+      const authArr: any = []
+      const menuArr: any = []
+      data.forEach((v) => {
+        if (v.includes(AppStore.permission.splitAuth)) {
+          authArr.push(v)
+        } else {
+          menuArr.push(v)
+        }
+      })
+      state.activeAuths = authArr
+      ref_tree.value?.setCheckedKeys(menuArr, true)
     })
     .catch(() => {})
     .finally(() => {
@@ -127,22 +156,27 @@ const filterNode = (value, data) => {
   return data.label.includes(value)
 }
 const treeDataCpt = computed(() => {
-  const routes: any = router.options.routes.filter((v) => v.name === 'root')[0]
   const subsMap = new Map()
   const menus: any = []
   const formatRou = (rou) => {
     const obj = {
-      meta: rou.meta,
       label: rou.meta?.title || rou.name,
       value: rou.name,
       children: [],
+      auths:
+        rou.meta?.auths?.map((v) => ({
+          label: v.label,
+          value: `${rou.name}${AppStore.permission.splitAuth}${v.value}`,
+        })) || [],
+      active: [],
     }
     if (rou.children?.length > 0) {
       obj.children = rou.children.map((v) => formatRou(v))
     }
     return obj
   }
-  routes.children.forEach((r) => {
+  console.log(bigModelRouter())
+  bigModelRouter().forEach((r) => {
     if (r.meta?.root) {
       if (subsMap.has(r.meta.root)) {
         subsMap.set(r.meta.root, [...subsMap.get(r.meta.root), formatRou(r)])
@@ -175,11 +209,17 @@ const onNodeClick = (data, node) => {
     return title
   }
   state.currentMenu.title = getTitle(node)
-  state.currentMenu.auths = data.meta?.auths || []
+  state.currentMenu.auths = data.auths
 }
 const onSubmit = () => {
-  console.log(ref_tree.value.getCheckedKeys())
-  console.log(ref_tree.value.getHalfCheckedKeys())
+  const arr = [...ref_tree.value.getCheckedKeys(), ...state.activeAuths]
+  state.loading = true
+  rolesMenusAdd(props.transfer.roleId, arr)
+    .then(({ data }: any) => {})
+    .finally(() => {
+      state.loading = false
+      initData()
+    })
 }
 </script>
 

+ 17 - 3
src/views/manage/center/role/index.vue

@@ -19,7 +19,12 @@
             placeholder="输入关键词以检索"
             :prefix-icon="Search"
           />
-          <CzrButton type="add" @click="onAdd" title="新增角色" />
+          <CzrButton
+            v-if="AppStore.hasPermission($route.name, 'add')"
+            type="add"
+            @click="onAdd"
+            title="新增角色"
+          />
         </CzrForm>
       </div>
     </template>
@@ -37,8 +42,17 @@
       >
         <template #caozuo-column-value="{ scope }">
           <div class="__czr-table-operations">
-            <CzrButton type="table" title="编辑" @click="onEdit(scope.row)" />
-            <CzrButton type="table-del" @click="onDel(scope.row)" />
+            <CzrButton
+              v-if="AppStore.hasPermission($route.name, 'edit')"
+              type="table"
+              title="编辑"
+              @click="onEdit(scope.row)"
+            />
+            <CzrButton
+              v-if="AppStore.hasPermission($route.name, 'del')"
+              type="table-del"
+              @click="onDel(scope.row)"
+            />
             <CzrButton
               type="table"
               title="关联用户"

+ 1 - 1
src/views/manage/center/role/relation.vue

@@ -207,7 +207,7 @@ const onDel = () => {
       content: `请确认是否移除${state.query.selected.length}位用户?`,
       onSubmit: () => {
         rolesDeleteUsers(
-          props.transfer.id,
+          props.transfer.roleId,
           state.query.selected.map((v) => v.id),
         )
           .then(() => {