zhengnaiwen_citu преди 7 месеца
родител
ревизия
07a5d59a69

+ 26 - 9
src/components/AutoComponents/MForm/index.vue

@@ -58,15 +58,26 @@
           </el-select>
         </template>
         <template v-if="item.type === 'radioGroup'">
-          <el-radio-group v-model="query[item.prop]">
-            <el-radio
-              v-for="_item in item.options.items"
-              :key="_item.label"
-              v-model="query[item.prop]"
-              v-bind="_item"
-            >
-            {{ _item.text }}
-            </el-radio>
+          <el-radio-group v-model="query[item.prop]" v-bind="item.options">
+            <template v-if="item.button">
+              <el-radio-button
+                v-for="_item in item.options.items"
+                :key="_item.label"
+                v-bind="_item"
+              >
+              {{ _item.text }}
+              </el-radio-button>
+            </template>
+            <template v-else>
+              <el-radio
+                v-for="_item in item.options.items"
+                :key="_item.label"
+                v-model="query[item.prop]"
+                v-bind="_item"
+              >
+              {{ _item.text }}
+              </el-radio>
+            </template>
           </el-radio-group>
         </template>
       </el-form-item>
@@ -122,6 +133,12 @@ export default {
     resetFields () {
       this.$refs.formRef.resetFields()
     },
+    clearValidate () {
+      return this.$refs.formRef.clearValidate(...arguments)
+    },
+    validateField () {
+      return this.$refs.formRef.validateField(...arguments)
+    },
     validate (callback) {
       this.$refs.formRef.validate((valid) => {
         callback(valid)

+ 4 - 2
src/components/AutoComponents/MSearch/index.vue

@@ -45,8 +45,10 @@
         <!-- 其他类型可以根据需要扩展 -->
       </el-form-item>
       <el-form-item>
-        <m-button class="ml-3" icon="el-icon-search" @click="onSubmit">查询</m-button>
-        <m-button class="ml-3" icon="el-icon-refresh" @click="onReset">重置</m-button>
+        <template v-if="items.length">
+          <m-button class="ml-3" icon="el-icon-search" @click="onSubmit">查询</m-button>
+          <m-button class="ml-3" icon="el-icon-refresh" @click="onReset">重置</m-button>
+        </template>
         <slot name="button"></slot>
       </el-form-item>
     </el-form>

+ 1 - 1
src/layout/components/MenuSide.vue

@@ -46,7 +46,7 @@ export default {
         if (item.children && item.children.length) {
           item.children = this.filterMenu(item.children)
         }
-        return !item.hidden
+        return item.hidden
       })
     }
   }

+ 1 - 1
src/router/defaultRoute.js

@@ -17,7 +17,7 @@ const base = [
     name: '404',
     meta: { title: '404' },
     hidden: true,
-    component: () => import('@/views/systemManage/error/404')
+    component: () => import('@/views/system/error/404')
   }
 ]
 

+ 44 - 1
src/store/modules/menu.js

@@ -1,5 +1,5 @@
 // import { getPermission, setPermission } from '@/utils/auth'
-import { getMenu2 } from '@/api/menu'
+import { getMenu2, saveMenu, saveMenuAll, deleteMenu } from '@/api/menu'
 
 const state = {
   refresh: true, // 更新路由
@@ -22,6 +22,49 @@ const mutations = {
 }
 
 const actions = {
+  deleteMenu ({ commit, dispatch }, params) {
+    return new Promise((resolve, reject) => {
+      deleteMenu(params).then(res => {
+        // 更新模块
+        dispatch('getMenu2').then((route) => {
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })
+      }).catch(error => {
+        reject(error)
+      })
+    })
+  },
+  saveMenu ({ commit, dispatch }, params) {
+    return new Promise((resolve, reject) => {
+      saveMenu(params).then(res => {
+        // 更新模块
+        dispatch('getMenu2').then((route) => {
+          resolve()
+        }).catch(error => {
+          reject(error)
+        })
+      }).catch(error => {
+        reject(error)
+      })
+    })
+  },
+  saveMenuAll ({ commit, dispatch }, params) {
+    return new Promise((resolve, reject) => {
+      saveMenuAll(params)
+        .then(res => {
+          // 更新home模块
+          dispatch('getMenu2').then((route) => {
+            // console.log(route)
+            resolve()
+          }).catch(error => {
+            reject(error)
+          })
+        })
+        .catch(error => { reject(error) })
+    })
+  },
   getMenu2 ({ commit, dispatch }) {
     return new Promise((resolve, reject) => {
       getMenu2({}).then(response => {

+ 0 - 0
src/views/systemManage/error/404.vue → src/views/system/error/404.vue


+ 215 - 0
src/views/system/menu/index.vue

@@ -0,0 +1,215 @@
+<template>
+  <div class="pa-3 white">
+    <m-search class="mb-3">
+      <template #button>
+        <m-button type="primary" icon="el-icon-plus" @click="onAdd">
+          新增
+        </m-button>
+      </template>
+    </m-search>
+    <m-table
+      v-loading="loading"
+      row-key="id"
+      :items="items"
+      :headers="headers"
+      :page-size="total"
+      :page-current="1"
+      :total="total"
+      :default-sort="{ prop: 'sort', order: 'ascending' }"
+    >
+      <template v-slot:hidden="{ row }">
+        <el-tag
+          size="small"
+          :type="row.type !== 3 ? !row.hidden ? 'danger' : 'success' : row.active === '1' ? 'success' : 'danger'"
+          text-color="white"
+        >
+          {{ row.type !== 3 ? !row.hidden ? '已关闭' : '已开启' : row.active === '1' ? '已开启' : '已关闭'}}
+        </el-tag>
+      </template>
+      <template #icon="scope">
+        <i class="mdi" :class="scope.row.icon"></i>
+      </template>
+      <template #actions="scope">
+        <m-button type="primary" class="pa-0" text @click="onEdit(scope.row)">编辑</m-button>
+        <m-button type="primary" class="pa-0" text @click="onCopy(scope.row)">复制</m-button>
+        <m-button type="danger" class="pa-0" text @click="onDelete(scope.row)">删除</m-button>
+      </template>
+    </m-table>
+    <menuEdit ref="menuEdit" :title="title" @refresh="initPage"></menuEdit>
+  </div>
+</template>
+
+<script>
+import menuEdit from './menuEdit.vue'
+import { getRouters, getRoutersDetail } from '@/api/menu'
+export default {
+  name: 'route-manage',
+  components: { menuEdit },
+  data () {
+    return {
+      headers: [
+        { label: '菜单名称', prop: 'label', width: 300 },
+        { label: '图标', prop: 'icon', width: 50 },
+        { label: '排序', prop: 'sort', width: 50 },
+        { label: '状态', prop: 'hidden', width: 100, align: 'center' },
+        { label: '组件路径', prop: 'component', width: 300 },
+        { label: '权限标识', prop: 'code', width: 300 },
+        { label: '备注', prop: 'remark' },
+        { label: '操作', prop: 'actions', width: 200, fixed: 'right' }
+      ],
+      items: [],
+      showParents: false,
+      show: false,
+      title: '',
+      header: [],
+      total: 0,
+      orders: [],
+      loading: true,
+      itemData: {},
+      uploadLoading: false,
+      downloadLoading: false
+    }
+  },
+
+  created () {
+    this.initPage()
+  },
+  methods: {
+    async initPage () {
+      this.loading = true
+      try {
+        const { data } = await getRouters({})
+        this.items = data
+        this.total = data.length
+        this.loading = false
+      } catch (error) {
+        this.$message.error(error)
+      }
+    },
+    onAdd () {
+      this.title = '添加菜单'
+      this.itemData = {}
+      this.$refs.menuEdit.open({
+        parentId: 0,
+        label: null,
+        type: 0,
+        path: null,
+        component: null,
+        name: null,
+        code: null,
+        sort: 0,
+        hidden: 1,
+        keepAlive: true
+      }, this.items, null)
+    },
+    async onEdit ({ id }) {
+      try {
+        const { data } = await getRoutersDetail({ id })
+        this.title = '编辑菜单'
+        this.itemData = data
+        const meta = JSON.parse(data.metastr)
+        this.$refs.menuEdit.open({
+          parentId: data.parentId,
+          label: data.label,
+          type: data.type,
+          path: data.path,
+          component: data.component,
+          name: data.name,
+          code: data.code,
+          sort: data.sort,
+          hidden: data.hidden,
+          keepAlive: meta?.keepAlive
+        }, this.items, data.id)
+      } catch (error) {
+        this.$message.error(error)
+      }
+    },
+    async onCopy ({ id }) {
+      try {
+        const { data } = await getRoutersDetail({ id })
+        this.title = '添加菜单'
+        this.itemData = data
+        const meta = JSON.parse(data.metastr)
+        this.$refs.menuEdit.open({
+          parentId: data.parentId,
+          label: data.label,
+          type: data.type,
+          path: data.path,
+          component: data.component,
+          name: data.name,
+          code: data.code,
+          sort: data.sort,
+          hidden: data.hidden,
+          keepAlive: meta?.keepAlive
+        }, this.items, null)
+      } catch (error) {
+        this.$message.error(error)
+      }
+    },
+    onDelete (item) {
+      this.$confirm(`是否确定删除该选项 ${item.label} 包括所以子项`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      })
+        .then(async () => {
+          try {
+            await this.$store.dispatch('menu/deleteMenu', { id: item.id })
+            this.$message.success('删除成功')
+            this.initPage()
+          } catch (error) {
+            this.$message.error(error)
+          }
+        })
+        .catch(_ => {})
+    },
+    async handleCopy ({ id }) {
+      try {
+        const { data } = await getRoutersDetail({ id })
+        const { id: _id, ...obj } = data
+        this.title = '复制'
+        this.show = true
+        this.itemData = obj
+      } catch (error) {
+        this.$message.error(error)
+      }
+    }
+    // 导出菜单 数据有问题
+    // async exportMenu () {
+    //   const date = new Date()
+    //   const time = date.toLocaleString()
+    //   try {
+    //     this.downloadLoading = true
+    //     const { data } = await exportMenu()
+    //     const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' })
+    //     FileSaver.saveAs(blob, `菜单${time}`)
+    //   } catch (error) {
+    //     this.$message.error(error)
+    //   } finally {
+    //     this.downloadLoading = false
+    //   }
+    // },
+    // // 导入菜单
+    // async importMenu (file) {
+    //   // console.log(file)
+    //   const formData = new FormData()
+    //   formData.append('file', file)
+    //   this.uploadLoading = true
+    //   try {
+    //     await importMenu(formData)
+    //     this.$message.success('导入成功')
+    //   } catch (error) {
+    //     this.$message.error(error)
+    //   } finally {
+    //     this.uploadLoading = false
+    //   }
+    // }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.mdi {
+  font-size: 1.5em;
+}
+</style>

+ 217 - 0
src/views/system/menu/menuEdit.vue

@@ -0,0 +1,217 @@
+<template>
+  <MDialog ref="editDialog" v-bind="$attrs" @sure="handleSaveEdit">
+    <MForm ref="editForm" :items="editForm" v-model="editValues">
+      <template #parentId>
+        <el-popover
+          placement="bottom"
+          width="600"
+          trigger="click">
+          <div class="popover-tree">
+            <el-tree
+              :data="itemsParents"
+              node-key="id"
+              ref="tree"
+              highlight-current
+              auto-expand-parent
+              :expand-on-click-node="false"
+              @node-click="nodeClick"
+              :props="{
+                children: 'children',
+                label: 'label'
+              }">
+            </el-tree>
+          </div>
+
+          <el-input
+            slot="reference"
+            v-model="currentNodeName"
+            placeholder="请选择根菜单"
+            readonly
+          ></el-input>
+        </el-popover>
+      </template>
+    </MForm>
+  </MDialog>
+</template>
+
+<script>
+export default {
+  name: 'menu-edit',
+  data () {
+    return {
+      itemsParents: [],
+      editValues: {
+        parentId: null,
+        label: null,
+        type: null,
+        icon: null,
+        path: null,
+        component: null,
+        name: null,
+        code: null,
+        sort: null,
+        hidden: null,
+        keepAlive: null
+      },
+      currentNodeName: null,
+      isEdit: null
+    }
+  },
+  computed: {
+    editForm () {
+      const type = this.editValues.type
+      return [
+        {
+          label: '上级菜单',
+          prop: 'parentId',
+          rules: [{ required: true, message: '请选择上级菜单', trigger: 'change' }]
+        },
+        {
+          label: '菜单名称',
+          prop: 'label',
+          type: 'input',
+          options: { placeholder: '请输入菜单名称' },
+          rules: [{ required: true, message: '请输入菜单名称', trigger: 'change' }]
+        },
+        {
+          label: '菜单类型',
+          prop: 'type',
+          type: 'radioGroup',
+          button: true,
+          options: {
+            size: 'small',
+            items: [
+              { text: '目录', label: 0 },
+              { text: '菜单', label: 1 },
+              { text: '按钮', label: 2 },
+              { text: '组件', label: 3 }
+            ]
+          }
+        },
+        {
+          label: '菜单图标',
+          prop: 'icon',
+          hidden: [2].includes(type),
+          type: 'input',
+          options: { placeholder: '请输入菜单图标' }
+        },
+        {
+          label: '路由地址',
+          prop: 'path',
+          hidden: [2, 3].includes(type),
+          type: 'input',
+          options: { placeholder: '请输入路由地址' },
+          rules: [{ required: true, message: '请输入路由地址', trigger: 'change' }]
+        },
+        {
+          label: '组件地址',
+          prop: 'component',
+          hidden: [2].includes(type),
+          type: 'input',
+          options: { placeholder: '请输入组件地址' },
+          rules: [{ required: true, message: '请输入组件地址', trigger: 'change' }]
+        },
+        {
+          label: '组件名称',
+          prop: 'name',
+          hidden: [2].includes(type),
+          type: 'input',
+          options: { placeholder: '请输入组件名称' },
+          rules: [{ required: true, message: '请输入组件名称', trigger: 'change' }]
+        },
+        {
+          label: '权限标识',
+          prop: 'code',
+          hidden: [0, 1, 3].includes(type),
+          type: 'input',
+          options: { placeholder: '请输入权限标识' },
+          rules: [{ required: true, message: '请输入权限标识', trigger: 'change' }]
+        },
+        {
+          label: '显示排序',
+          prop: 'sort',
+          type: 'number',
+          options: { placeholder: '显示排序' },
+          rules: [{ required: true, message: '请输入排序序号', trigger: 'change' }]
+        },
+        {
+          label: '显示状态',
+          prop: 'hidden',
+          type: 'radioGroup',
+          options: {
+            items: [
+              { text: '显示', label: 1 },
+              { text: '隐藏', label: 0 }
+            ]
+          }
+        },
+        {
+          label: '缓存状态',
+          prop: 'keepAlive',
+          type: 'radioGroup',
+          hidden: [0, 2, 3].includes(type),
+          options: {
+            items: [
+              { text: '缓存', label: true },
+              { text: '不缓存', label: false }
+            ]
+          }
+        }
+      ]
+    }
+  },
+  methods: {
+    open (val, items, isEdit) {
+      this.isEdit = isEdit
+      this.editValues = val
+      this.itemsParents = [{ label: '根菜单', id: 0, children: items }]
+      this.$refs.editDialog.open()
+      this.$nextTick(() => {
+        this.$refs.tree.setChecked(val.parentId, true)
+        const data = this.$refs.tree.getNode(val.parentId)
+        this.currentNodeName = data.label
+        this.$refs.editForm.clearValidate()
+      })
+    },
+    handleSaveEdit () {
+      this.$refs.editForm.validate(async (valid) => {
+        if (!valid) {
+          return
+        }
+        const metaProp = ['keepAlive']
+        const obj = this.editForm.reduce((res, item) => {
+          if (item.hidden) {
+            return res
+          }
+          if (metaProp.includes(item.prop)) {
+            res.meta = res.meta || {}
+            res.meta[item.prop] = this.editValues[item.prop]
+            return res
+          }
+          res[item.prop] = this.editValues[item.prop]
+          return res
+        }, { id: this.isEdit ? this.isEdit : undefined })
+        try {
+          await this.$store.dispatch('menu/saveMenu', obj)
+          this.$refs.editDialog.close()
+          this.$emit('refresh')
+        } catch (error) {
+          this.$message.error(error.message)
+        }
+      })
+    },
+    nodeClick (v) {
+      this.currentNodeName = v.label
+      this.editValues.parentId = v.id
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.popover-tree {
+  max-height: 500px;
+  overflow: auto;
+}
+
+</style>

+ 4 - 4
src/views/systemManage/roleManage/index.vue → src/views/system/role/index.vue

@@ -20,10 +20,10 @@
         {{ dateFormat(scope.row.createTime) }}
       </template>
       <template #actions="scope">
-        <m-button type="primary" text @click="onEdit(scope.row)">编辑</m-button>
-        <m-button type="primary" text @click="onMenu(scope.row)">菜单权限</m-button>
-        <m-button type="primary" text @click="onPermission(scope.row)">数据权限</m-button>
-        <m-button type="danger" text @click="onDelete(scope.row)">删除</m-button>
+        <m-button class="pa-0" v-permission="['systemManage:roleManage:edit']" type="primary" text @click="onEdit(scope.row)">编辑</m-button>
+        <m-button class="pa-0" type="primary" text @click="onMenu(scope.row)">菜单权限</m-button>
+        <m-button class="pa-0" v-permission="['systemManage:roleManage:data']" type="primary" text @click="onPermission(scope.row)">数据权限</m-button>
+        <m-button class="pa-0" v-permission="['systemManage:roleManage:delete']" type="danger" text @click="onDelete(scope.row)">删除</m-button>
       </template>
     </MTable>
     <MDialog ref="dialog" :title="itemData.id ? '编辑角色' : '新增角色'" @sure="handleSave">

+ 0 - 0
src/views/systemManage/safety/index.vue → src/views/system/safety/index.vue


+ 2 - 2
src/views/systemManage/user/index.vue → src/views/system/user/index.vue

@@ -5,12 +5,12 @@
         <m-button type="primary" icon="el-icon-plus" @click="onAdd">
           新增
         </m-button>
-        <m-button type="primary" icon="el-icon-upload2">
+        <!-- <m-button type="primary" icon="el-icon-upload2">
           批量导入
         </m-button>
         <m-button type="primary" icon="el-icon-download">
           模板下载
-        </m-button>
+        </m-button> -->
       </template>
     </m-search>
     <MTable