dedsudiyu 1 ano atrás
pai
commit
709f43b412

+ 29 - 11
README.md

@@ -72,22 +72,40 @@
     
 ## 四.二维码相关
 
-### 1.二维码生成规则
+### 1.二维码生成规则 
 
->##### 二维码参数由三部分组成,各部分组成之间用下划线分割
->##### 项目标识 固定为lsmf 扫码成功后优先识别,项目标识不匹配时则return与提示msg
->##### 功能标识 用于识别二维码所属功能,用于后续跳转对应后续业务页面
->##### 参数 用于该功能所需参数
+>#### 二维码参数由三部分组成 
 
-    lsmf_chemicalsCabinet_17
-    项目标识_功能标识_参数
+##### 微信小程序二维码标识
+
+    需要在微信小程序后台开发配置中配置
+    同时需要在后台配置开发配置中配置 
+    两个配置需要完全一致
+    在用户登录后会存储在local storage中字段为'codeOnlineAdd'
+    
+##### 参数 code
+
+    用于该功能所需参数
+    
+##### 功能标识 type
+
+    用于识别二维码所属功能,用于后续跳转对应后续业务页面
+    
+>##### 示例
+    http://lab.zjznai.com/labAppTest?code=11&type=1
+    微信小程序二维码标识?参数&功能标识
     
 ### 2.功能标识表
 
-|  功能标识   | 功能名称  |
-|  ----  | ----  |
-| chemicalsCabinet  | 化学品柜 |
-| trainingCourse  | 培训课程 |
+| 功能标识 | 功能名称 | 备注 |
+|  ---- | ---- |  ---- | 
+| type == 8 | 化学品柜 | 前端生成 |
+| type == 7 | 培训课程 | 前端生成 |
+| type == 6 | 专项检查 | 前端生成 |
+| type == 5 | 实验室详情 | 后端生成 |
+| type == 3 | 危险源列表 | 后端生成 |
+| type == 2 | 安全制度 | 后端生成 |
+| type == 1 | MSDS说明书 | 后端生成 |
 
     
 ## 五.版本相关

+ 1 - 1
src/api/commonality/noPermission.js

@@ -29,7 +29,7 @@ export function getConfigKey(configKey) {
 // 根据字典类型查询字典数据信息
 export function getDicts(dictType) {
   return request({
-    url: '/system/dict/data/type/' + dictType,
+    url: '/system/dict/item/option?dictCode=' + dictType,
     method: 'get'
   })
 }

+ 161 - 0
src/api/systemManagement/index.js

@@ -0,0 +1,161 @@
+import request from '@/utils/request'
+
+// 查询菜单列表
+export function listMenu(data) {
+  return request({
+    url: '/system/menu/list',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询菜单详细
+export function getMenu(query) {
+  return request({
+    url: '/system/menu/detail',
+    method: 'get',
+    params: query
+  })
+}
+
+// 新增菜单
+export function addMenu(data) {
+  return request({
+    url: '/system/menu/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改菜单
+export function updateMenu(data) {
+  return request({
+    url: '/system/menu/update',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除菜单
+export function delMenu(query) {
+  return request({
+    url: '/system/menu/delete',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询字典数据列表
+export function listData(data) {
+  return request({
+    url: '/system/dict/item/list',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询字典数据详细
+export function getData(query) {
+  return request({
+    url: '/system/dict/item/detail',
+    method: 'get',
+    params: query
+  })
+}
+
+// 根据字典类型查询字典数据信息
+export function getDicts(query) {
+  return request({
+    url: '/system/dict/item/option',
+    method: 'get',
+    params: query
+  })
+}
+
+// 新增字典数据
+export function addData(data) {
+  return request({
+    url: '/system/dict/item/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改字典数据
+export function updateData(data) {
+  return request({
+    url: '/system/dict/item/update',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除字典数据
+export function delData(data) {
+  return request({
+    url: '/system/dict/item/delete',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询字典类型列表
+export function listType(data) {
+  return request({
+    url: '/system/dict/list',
+    method: 'post',
+    data: data
+  })
+}
+
+// 查询字典类型详细
+export function getType(query) {
+  return request({
+    url: '/system/dict/detail',
+    method: 'get',
+    params: query
+  })
+}
+
+// 新增字典类型
+export function addType(data) {
+  return request({
+    url: '/system/dict/add',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改字典类型
+export function updateType(data) {
+  return request({
+    url: '/system/dict/update',
+    method: 'post',
+    data: data
+  })
+}
+
+// 删除字典类型
+export function delType(data) {
+  return request({
+    url: '/system/dict/delete',
+    method: 'post',
+    data: data
+  })
+}
+
+// 刷新字典缓存
+export function refreshCache() {
+  return request({
+    url: '/system/dict/type/refreshCache',
+    method: 'delete'
+  })
+}
+
+// 获取字典选择框列表
+export function optionselect() {
+  return request({
+    url: '/system/dict/type/optionselect',
+    method: 'get'
+  })
+}

+ 68 - 0
src/components/IconSelect/index.vue

@@ -0,0 +1,68 @@
+<!-- @author zhengjie -->
+<template>
+  <div class="icon-body">
+    <el-input v-model="name" style="position: relative;" clearable placeholder="请输入图标名称" @clear="filterIcons" @input.native="filterIcons">
+      <i slot="suffix" class="el-icon-search el-input__icon" />
+    </el-input>
+    <div class="icon-list">
+      <div v-for="(item, index) in iconList" :key="index" @click="selectedIcon(item)">
+        <svg-icon :icon-class="item" style="height: 30px;width: 16px;" />
+        <span>{{ item }}</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import icons from './requireIcons'
+export default {
+  name: 'IconSelect',
+  data() {
+    return {
+      name: '',
+      iconList: icons
+    }
+  },
+  methods: {
+    filterIcons() {
+      this.iconList = icons
+      if (this.name) {
+        this.iconList = this.iconList.filter(item => item.includes(this.name))
+      }
+    },
+    selectedIcon(name) {
+      this.$emit('selected', name)
+      document.body.click()
+    },
+    reset() {
+      this.name = ''
+      this.iconList = icons
+    }
+  }
+}
+</script>
+
+<style rel="stylesheet/scss" lang="scss" scoped>
+  .icon-body {
+    width: 100%;
+    padding: 10px;
+    .icon-list {
+      height: 200px;
+      overflow-y: scroll;
+      div {
+        height: 30px;
+        line-height: 30px;
+        margin-bottom: -5px;
+        cursor: pointer;
+        width: 33%;
+        float: left;
+      }
+      span {
+        display: inline-block;
+        vertical-align: -0.15em;
+        fill: currentColor;
+        overflow: hidden;
+      }
+    }
+  }
+</style>

+ 11 - 0
src/components/IconSelect/requireIcons.js

@@ -0,0 +1,11 @@
+
+const req = require.context('../../assets/icons/svg', false, /\.svg$/)
+const requireAll = requireContext => requireContext.keys()
+
+const re = /\.\/(.*)\.svg/
+
+const icons = requireAll(req).map(i => {
+  return i.match(re)[1]
+})
+
+export default icons

+ 18 - 19
src/layout/components/TopNav/index.vue

@@ -7,8 +7,7 @@
   >
     <template v-for="(item, index) in topMenus">
       <el-menu-item :style="{'--theme': theme}" :index="item.path" :key="index" v-if="index < visibleNumber">
-        <!--<svg-icon :icon-class="item.meta.icon" />-->
-        {{ item.meta.title }}
+        {{ item.menuName }}
       </el-menu-item>
     </template>
 
@@ -21,8 +20,7 @@
           :index="item.path"
           :key="index"
           v-if="index >= visibleNumber">
-          <!--<svg-icon :icon-class="item.meta.icon" />-->
-          {{ item.meta.title }}
+          {{ item.menuName }}
         </el-menu-item>
       </template>
     </el-submenu>
@@ -52,11 +50,12 @@ export default {
     // 顶部显示菜单
     topMenus() {
       let topMenus = [];
+      console.log('router',this.routers)
       this.routers.map((menu) => {
         if (menu.hidden !== true) {
           // 兼容顶部栏一级菜单内部跳转
           if (menu.path === "/") {
-              topMenus.push(menu.children[0]);
+              topMenus.push(menu.child[0]);
           } else {
               topMenus.push(menu);
           }
@@ -72,20 +71,21 @@ export default {
     childrenMenus() {
       var childrenMenus = [];
       this.routers.map((router) => {
-        for (var item in router.children) {
-          if (router.children[item].parentPath === undefined) {
+        for (var item in router.child) {
+          if (router.child[item].parentPath === undefined) {
             if(router.path === "/") {
-              router.children[item].path = "/redirect/" + router.children[item].path;
+              router.child[item].path = "/redirect/" + router.child[item].path;
             } else {
-              if(!this.ishttp(router.children[item].path)) {
-                router.children[item].path = router.path + "/" + router.children[item].path;
+              if(!this.ishttp(router.child[item].path)) {
+                router.child[item].path = router.path + "/" + router.child[item].path;
               }
             }
-            router.children[item].parentPath = router.path;
+            router.child[item].parentPath = router.path;
           }
-          childrenMenus.push(router.children[item]);
+          childrenMenus.push(router.child[item]);
         }
       });
+      console.log('childrenMenus',childrenMenus)
       return constantRoutes.concat(childrenMenus);
     },
     // 默认激活的菜单
@@ -140,13 +140,12 @@ export default {
           return true;
         }
       });
-      let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
+      let matched = this.$route.matched.filter(item => item.menuName)
       for(let i=0;i<self.topMenus.length;i++){
         if(matched[0]){
           if(matched[0].path == self.topMenus[i].path){
-            if(matched[0].meta.title != '数据可视化'){
-              // console.log('matched[0].meta.title',matched[0].meta.title);
-              store.dispatch('settings/setPageName', matched[0].meta.title)
+            if(matched[0].menuName != '数据可视化'){
+              store.dispatch('settings/setPageName', matched[0].menuName)
             }
           }
         }
@@ -158,8 +157,8 @@ export default {
       let self = this;
       for(let i=0;i<self.topMenus.length;i++){
         if(key == self.topMenus[i].path){
-          if(self.topMenus[i].meta.title != '数据可视化'){
-            store.dispatch('settings/setPageName', self.topMenus[i].meta.title)
+          if(self.topMenus[i].menuName != '数据可视化'){
+            store.dispatch('settings/setPageName', self.topMenus[i].menuName)
           }
         }
       }
@@ -207,7 +206,7 @@ export default {
       if(routes.length > 0) {
         for(let i=0;i<self.topMenus.length;i++){
           if(key == self.topMenus[i].path){
-            localStorage.setItem('leftRoutesName',self.topMenus[i].meta.title);
+            localStorage.setItem('leftRoutesName',self.topMenus[i].menuName);
           }
         }
         localStorage.setItem('leftRoutesData',JSON.stringify(routes));

+ 11 - 7
src/router/index.js

@@ -27,15 +27,9 @@ import Layout from '@/layout'
 
 // 公共路由
 export const constantRoutes = [
-  //  前端开发路由
-  // {
-  //   path: '/login',
-  //   component: (resolve) => require(['@/views/serviceCenter/merchantManagement'], resolve),
-  //   name: 'login',
-  // },
   {
     path: '/',
-    component: (resolve) => require(['@/views/basicsModules/home'], resolve),//home loginOne
+    component: (resolve) => require(['@/views/basicsModules/home'], resolve),
     hidden: true
   },
   {
@@ -70,6 +64,16 @@ export const constantRoutes = [
         component: (resolve) => require(['@/views/basicsModules/403'], resolve),
         name: '403',
       },
+      {
+        path: '/demo',
+        component: (resolve) => require(['@/views/systemManagement/menu/index'], resolve),
+        name: 'demo',
+      },
+      {
+        path: '/demo1',
+        component: (resolve) => require(['@/views/systemManagement/dict/index'], resolve),
+        name: 'demo1',
+      },
     ]
   },
   {

+ 2 - 0
src/store/modules/permission.js

@@ -140,6 +140,8 @@ export const loadView = (view) => { // 路由懒加载
     return (resolve) => require([`@/views/safetyEducationExam${pathUrl}`], resolve)
   }else if(pathName === 'serviceCenter'){
     return (resolve) => require([`@/views/serviceCenter${pathUrl}`], resolve)
+  }else if(pathName === 'systemManagement'){
+    return (resolve) => require([`@/views/systemManagement${pathUrl}`], resolve)
   }
 }
 

+ 2 - 2
src/store/modules/user.js

@@ -91,14 +91,14 @@ const user = {
       return new Promise((resolve, reject) => {
         getInfo().then(res => {
           if(res){
-            let user = res.user
+            // let user = res.user
             // let avatar = user.avatar == "" ? require("@/assets/ZDimages/basicsModules/tx_cion.png") : user.avatar;
             commit('SET_ROLES', ['placeholder'])
             // commit('SET_PERMISSIONS', res.permissions)
             commit('SET_PERMISSIONS', res.data)
             // commit('SET_NAME', user.userName)
             // commit('SET_AVATAR', avatar)
-            localStorage.setItem('user',JSON.stringify(user));
+            // localStorage.setItem('user',JSON.stringify(user));
             resolve(res)
           }else{
             store.dispatch('LogOut').then(() => {

+ 4 - 0
src/utils/ruoyi.js

@@ -66,6 +66,10 @@ export function parseTime(time, pattern) {
 	if (arguments.length === 0 || !time) {
 		return null
 	}
+	if(time.indexOf('T')!== -1){
+    let newTime = time.split('T')
+    time = newTime[0] + ' ' + newTime[1]
+  }
 	const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
 	let date
 	if (typeof time === 'object') {

+ 2 - 2
src/views/basicsModules/login.vue

@@ -147,8 +147,8 @@ export default {
       cookiePassword: "",
       type:1,
       loginForm: {
-        username: "",
-        password: "",
+        username: "superadmin",
+        password: "zd123456",
         rememberMe: false,
         code: "",
         uuid: "",

+ 1 - 0
src/views/serviceCenter/merchantManagement.vue

@@ -1,3 +1,4 @@
+<!--商户管理-->
 <template>
     <div class="app-container merchantManagement">
       <el-form :model="queryParams" ref="queryForm" :inline="true" class="form-box">

+ 400 - 0
src/views/systemManagement/dict/data.vue

@@ -0,0 +1,400 @@
+<!--配置字典-->
+<template>
+  <div class="dict-data">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px" class="form-box">
+      <el-form-item style="margin:0 20px;">
+        <p class="reset-button-one" @click="backPage"><i class="el-icon-arrow-left"></i>返回</p>
+      </el-form-item>
+      <el-col :span="1.5">
+        <p class="add-button-one-120"
+           @click="handleAdd"
+           v-hasPermi="['system:dict:add']"
+        ><i class="el-icon-plus"></i>新增字典</p>
+      </el-col>
+      <!--<el-form-item label="字典名称" prop="dictCode">-->
+        <!--<el-select v-model="queryParams.dictCode" size="small">-->
+          <!--<el-option-->
+            <!--v-for="item in typeOptions"-->
+            <!--:key="item.dictId"-->
+            <!--:label="item.dictName"-->
+            <!--:value="item.dictCode"-->
+          <!--/>-->
+        <!--</el-select>-->
+      <!--</el-form-item>-->
+      <el-form-item label="标签名称" prop="label">
+        <el-input
+          v-model="queryParams.label"
+          placeholder="请输入字典标签"
+          clearable
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="state">
+        <el-select v-model="queryParams.state" placeholder="数据状态" clearable size="small">
+          <el-option
+            v-for="dict in statusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <p class="inquire-button-one" @click="handleQuery">查询</p>
+        <p class="reset-button-one" @click="resetQuery">重置</p>
+      </el-form-item>
+    </el-form>
+
+    <el-table v-loading="loading" border :data="dataList" @selection-change="handleSelectionChange" class="table-box">
+      <el-table-column label="字典名称" align="left" prop="label">
+        <!--<template slot-scope="scope">-->
+          <!--<span v-if="scope.row.listClass == '' || scope.row.listClass == 'default'">{{scope.row.label}}</span>-->
+          <!--<el-tag v-else :type="scope.row.listClass == 'primary' ? '' : scope.row.listClass">{{scope.row.label}}</el-tag>-->
+        <!--</template>-->
+      </el-table-column>
+      <el-table-column label="字典数值" align="left" prop="value" />
+      <el-table-column label="显示顺序" align="left" prop="sort" />
+      <el-table-column label="状态" align="left" prop="state">
+        <template slot-scope="scope">
+          <el-switch
+            class="switch"
+            @change="switchChange(scope.row)"
+            v-model="scope.row.state"
+            :active-value="true"
+            :inactive-value="false"
+            active-text="开"
+            inactive-text="关"
+          >
+          </el-switch>
+          <!--<dict-tag :options="statusOptions" :value="scope.row.state"/>-->
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="left" prop="remark" :show-overflow-tooltip="true" />
+      <el-table-column label="创建时间" align="left" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime,"{y}-{m}-{d} {h}:{i}:{s}") }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="left" class-name="small-padding fixed-width" width="160" v-if="tableButtonType">
+        <template slot-scope="scope">
+          <div class="table-button-box">
+            <p class="table-button-null"></p>
+            <p class="table-button-p"
+               @click="handleUpdate(scope.row)"
+               v-hasPermi="['system:dict:edit']"
+            >编辑</p>
+            <p class="table-button-p"
+               @click="handleDelete(scope.row)"
+               v-hasPermi="['system:dict:remove']"
+            >删除</p>
+            <p class="table-button-null"></p>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination :page-sizes="[20, 30, 40, 50]"
+      v-show="total>0"
+      :total="total"
+      layout="total, prev, pager, next, sizes, jumper"
+      :page.sync="queryParams.page"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改参数配置对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body :close-on-click-modal="false">
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="数据标签" prop="label">
+          <el-input v-model="form.label" placeholder="请输入数据标签" />
+        </el-form-item>
+        <el-form-item label="数据键值" prop="value">
+          <el-input v-model="form.value" placeholder="请输入数据键值" />
+        </el-form-item>
+        <el-form-item label="显示排序" prop="sort">
+          <el-input-number v-model="form.sort" controls-position="right" :min="0" />
+        </el-form-item>
+        <el-form-item label="状态" prop="state">
+          <el-radio-group v-model="form.state">
+            <el-radio
+              v-for="dict in statusOptions"
+              :key="dict.dictValue"
+              :label="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listData, getData, delData, addData, updateData,listType, getType } from "@/api/systemManagement/index";
+
+export default {
+  props: {
+    dictId: {},
+  },
+  name: "Data",
+  data() {
+    return {
+      tableButtonType:this.hasPermiDom(['system:dict:edit','system:dict:remove']),
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 字典表格数据
+      dataList: [],
+      // 默认字典类型
+      defaultDictType: "",
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 数据标签回显样式
+      listClassOptions: [
+        {
+          value: "default",
+          label: "默认"
+        },
+        {
+          value: "primary",
+          label: "主要"
+        },
+        {
+          value: "success",
+          label: "成功"
+        },
+        {
+          value: "info",
+          label: "信息"
+        },
+        {
+          value: "warning",
+          label: "警告"
+        },
+        {
+          value: "danger",
+          label: "危险"
+        }
+      ],
+      // 状态数据字典
+      statusOptions: [
+        { dictValue:true,dictLabel:"开" },
+        { dictValue:false,dictLabel:"关" },
+      ],
+      // 类型数据字典
+      typeOptions: [],
+      // 日期范围
+      dateRange: [],
+      // 查询参数
+      queryParams: {
+        page: 1,
+        pageSize:20,
+        dictName: undefined,
+        dictId: undefined,
+        state: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        label: [
+          { required: true, message: "数据标签不能为空", trigger: "blur" },
+          { required: true, message: "数据标签不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ],
+        value: [
+          { required: true, message: "数据键值不能为空", trigger: "blur" },
+          { required: true, message: "数据键值不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ],
+        sort: [
+          { required: true, message: "数据顺序不能为空", trigger: "blur" },
+          { required: true, message: "数据顺序不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    console.log("dictId",this.dictId)
+    // const dictId = this.$route.params && this.$route.params.dictId;
+    const dictId = this.dictId;
+    this.getType(dictId);
+    // this.getTypeList();
+  },
+  methods: {
+    //switch开关切换
+    switchChange(row){
+      console.log(row);
+      let obj = JSON.parse(JSON.stringify(row))
+      obj.dictId = this.dictId;
+      obj.state = obj.state?obj.state:false
+      updateData(obj).then(response => {
+        this.msgSuccess("操作成功");
+        this.getList();
+      });
+    },
+    //返回按钮
+    backPage(){
+      this.$parent.clickPage(1);
+    },
+    /** 查询字典类型详细 */
+    getType(dictId) {
+      getType({dictId:dictId}).then(response => {
+        this.defaultDictType = response.data.dictCode;
+        this.getList();
+      });
+    },
+    /** 查询字典类型列表 */
+    getTypeList() {
+      listType().then(response => {
+        this.typeOptions = response.data.records;
+      });
+    },
+    /** 查询字典数据列表 */
+    getList() {
+      this.loading = true;
+      let obj = JSON.parse(JSON.stringify(this.queryParams))
+      obj.dictId = this.dictId
+      listData(obj).then(response => {
+        this.dataList = response.data.records;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        label: null,
+        value: null,
+        sort: 0,
+        state: true,
+        remark: undefined
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.page = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.$set(this,'queryParams',{
+        page: 1,
+        pageSize:20,
+        label: null,
+        state: null
+      })
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加字典数据";
+      this.form.dictCode = this.queryParams.dictCode;
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.dictCode)
+      this.single = selection.length!=1
+      this.multiple = !selection.length
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      console.log(row)
+      this.$set(this,'form',{
+        itemId:row.itemId,
+        label:row.label,
+        value:row.value,
+        sort:row.sort,
+        state:row.state,
+        remark:row.remark,
+      });
+        this.open = true;
+      // this.reset();
+      // getData({itemId:row.dictId}).then(response => {
+      //   this.form = response.data;
+      //   this.open = true;
+      //   this.title = "修改字典数据";
+      // });
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          let obj = JSON.parse(JSON.stringify(this.form))
+          obj.dictId = this.dictId;
+          if (this.form.itemId) {
+            updateData(obj).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addData(obj).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      this.$confirm('是否确认删除该标签?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delData({dictId:row.dictId,itemId:row.itemId});
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/dict/data/export', {
+        ...this.queryParams
+      }, `data_${new Date().getTime()}.xlsx`)
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+  .dict-data {
+    flex:1;
+    display: flex!important;
+    flex-direction: column;
+    margin-top:9px;
+    .form-box{
+    }
+    .button-box{
+      width:200px;
+      display: flex;
+    }
+  }
+</style>

+ 378 - 0
src/views/systemManagement/dict/index.vue

@@ -0,0 +1,378 @@
+<!--数据字典-->
+<template>
+  <div class="app-container dict">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" class="form-box" v-if="pageType == 1">
+
+      <el-form-item label="关键字" prop="dictName" label-width="68px">
+        <el-input
+          v-model="queryParams.searchValue"
+          placeholder="字典名称/字典类型"
+          clearable
+          size="small"
+          style="width: 240px"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="state">
+        <el-select v-model="queryParams.state" placeholder="请选择字典状态" clearable size="small">
+          <el-option v-for="dict in statusOptions" :key="dict.dictValue" :label="dict.dictLabel" :value="dict.dictValue"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item label="创建时间">
+        <el-date-picker
+          v-model="dateRange"
+          size="small"
+          style="width: 240px"
+          clearable
+          value-format="yyyy-MM-dd"
+          type="daterange"
+          range-separator="-"
+          start-placeholder="开始日期"
+          end-placeholder="结束日期"
+        ></el-date-picker>
+      </el-form-item>
+      <el-form-item style="float: right;">
+        <el-col :span="1.5" style="margin-right:20px">
+          <p class="add-button-one-120"
+             @click="handleAdd"
+             v-hasPermi="['system:dict:add']"
+          ><i class="el-icon-plus"></i>新增字典</p>
+        </el-col>
+      </el-form-item>
+      <el-form-item style="float: right;">
+        <p class="inquire-button-one" @click="handleQuery">查询</p>
+        <p class="reset-button-one" @click="resetQuery">重置</p>
+      </el-form-item>
+    </el-form>
+
+    <el-table v-loading="loading" border :data="typeList" @selection-change="handleSelectionChange" class="table-box" v-if="pageType == 1">
+      <el-table-column label="字典名称" align="left" prop="dictName"/>
+      <el-table-column label="字典类型" align="left" prop="dictCode"/>
+      <el-table-column label="状态" align="left" prop="state">
+        <template slot-scope="scope">
+          <el-switch
+            class="switch"
+            @change="switchChange(scope.row)"
+            v-model="scope.row.state"
+            :active-value="true"
+            :inactive-value="false"
+            active-text="开"
+            inactive-text="关"
+          >
+          </el-switch>
+        </template>
+      </el-table-column>
+      <el-table-column label="备注" align="left" prop="remark"/>
+      <el-table-column label="创建时间" align="left" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime,"{y}-{m}-{d} {h}:{i}:{s}") }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="left" class-name="small-padding fixed-width" width="240" v-if="tableButtonType">
+        <template slot-scope="scope">
+          <div class="table-button-box">
+            <p class="table-button-null"></p>
+            <p class="table-button-p"
+               @click="clickPage(2,scope.row.dictId)"
+               v-hasPermi="['system:dict:edit']"
+            >配置字典</p>
+            <p class="table-button-p"
+               @click="handleUpdate(scope.row)"
+               v-hasPermi="['system:dict:edit']"
+            >编辑</p>
+            <p class="table-button-p"
+               @click="handleDelete(scope.row)"
+               v-hasPermi="['system:dict:remove']"
+            >删除</p>
+            <p class="table-button-null"></p>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination :page-sizes="[20, 30, 40, 50]"
+      v-if="pageType == 1"
+      v-show="total>0"
+      :total="total"
+      layout="total, prev, pager, next, sizes, jumper"
+      :page.sync="queryParams.page"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改参数配置对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body :close-on-click-modal="false">
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="字典名称" prop="dictName">
+          <el-input v-model="form.dictName" placeholder="请输入字典名称" />
+        </el-form-item>
+        <el-form-item label="字典类型" prop="dictCode">
+          <el-input v-model="form.dictCode" placeholder="请输入字典类型" />
+        </el-form-item>
+        <el-form-item label="状态" prop="state">
+          <el-radio-group v-model="form.state">
+            <el-radio
+              v-for="dict in statusOptions"
+              :key="dict.dictValue"
+              :label="dict.dictValue"
+            >{{dict.dictLabel}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" type="textarea" placeholder="请输入内容"></el-input>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+      </div>
+    </el-dialog>
+    <!--字典配置-->
+    <data-page v-if="pageType==2" :dictId="dictId"></data-page>
+  </div>
+</template>
+
+<script>
+import { listType, getType, delType, addType, updateType, refreshCache } from "@/api/systemManagement/index";
+import dataPage from "./data.vue";
+
+export default {
+  components: {
+    dataPage,
+  },
+  name: "Dict",
+  data() {
+    return {
+      tableButtonType:this.hasPermiDom(['system:dict:edit','system:dict:edit','system:dict:remove']),
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 字典表格数据
+      typeList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 状态数据字典
+      statusOptions: [
+        { dictValue:true,dictLabel:"开" },
+        { dictValue:false,dictLabel:"关" },
+      ],
+      // 日期范围
+      dateRange: [],
+      // 查询参数
+      queryParams: {
+        page: 1,
+        pageSize:20,
+        searchValue: null,
+        dictName: undefined,
+        dictCode: undefined,
+        state: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        dictName: [
+          { required: true, message: "字典名称不能为空", trigger: "blur" },
+          { required: true, message: "字典名称不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ],
+        dictCode: [
+          { required: true, message: "字典类型不能为空", trigger: "blur" },
+          { required: true, message: "字典类型不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ]
+      },
+      //页面状态
+      pageType:1,
+      // 选中id
+      dictId:"",
+    };
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    //页面切换
+    clickPage(type,id){
+      if(this.pageType!=type){
+        if(type==1){
+          this.dictId="";
+          this.pageType=1;
+        }else if(type==2){
+          this.dictId=id;
+          this.pageType=2;
+        }
+      }
+    },
+    //switch开关切换
+    switchChange(row){
+      let obj = {
+        dictId:row.dictId,
+        dictName:row.dictName,
+        dictCode:row.dictCode,
+        state:row.state?row.state:false,
+        remark:row.remark?row.remark:'',
+      }
+      updateType(obj).then(response => {
+        this.msgSuccess("操作成功");
+      });
+    },
+    /** 查询字典类型列表 */
+    getList() {
+      this.loading = true;
+      let obj = JSON.parse(JSON.stringify(this.queryParams))
+      if(this.dateRange[0]){
+        obj.startTime = this.dateRange[0]+'T00:00:00'
+        obj.endTime = this.dateRange[1]+'T23:59:59'
+      }else{
+        obj.startTime = "";
+        obj.endTime = "";
+      }
+      listType(this.addDateRange(obj)).then(response => {
+          this.typeList = response.data.records;
+          this.total = response.data.total;
+          this.loading = false;
+        }
+      );
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        dictId: undefined,
+        dictName: undefined,
+        dictCode: undefined,
+        state: true,
+        remark: undefined
+      };
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = [];
+      this.$set(this,'queryParams',{
+        page: 1,
+        pageSize:20,
+        searchValue: null,
+        dictName: undefined,
+        dictCode: undefined,
+        state: undefined
+      })
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加字典类型";
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.dictId)
+      this.single = selection.length!=1
+      this.multiple = !selection.length
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const dictId = row.dictId || this.ids
+      getType({dictId:dictId}).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改字典类型";
+      });
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.dictId != undefined) {
+            let obj = {
+              dictId:this.form.dictId,
+              dictName:this.form.dictName,
+              dictCode:this.form.dictCode,
+              state:this.form.state,
+              remark:this.form.remark?this.form.remark:'',
+            }
+            updateType(obj).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            let obj = {
+              dictName:this.form.dictName,
+              dictCode:this.form.dictCode,
+              state:this.form.state,
+              remark:this.form.remark?this.form.remark:'',
+            }
+            addType(obj).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const dictIds = row.dictId || this.ids;
+      this.$confirm('是否确认删除该字典?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delType({dictId:dictIds});
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('system/dict/type/export', {
+        ...this.queryParams
+      }, `type_${new Date().getTime()}.xlsx`)
+    },
+    /** 刷新缓存按钮操作 */
+    handleRefreshCache() {
+      refreshCache().then(() => {
+        this.msgSuccess("刷新成功");
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+  .dict {
+    display: flex!important;
+    flex-direction: column;
+    box-shadow: 0 0 8px 2px rgba(0, 0, 0, 0.1);
+    padding:11px 20px 20px!important;
+    .form-box{
+      margin-top:12px;
+    }
+    .button-box{
+      width:340px;
+      display: flex;
+    }
+  }
+</style>

+ 486 - 0
src/views/systemManagement/menu/index.vue

@@ -0,0 +1,486 @@
+<!--菜单管理(超管)-->
+<template>
+  <div class="app-container menu">
+    <el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch">
+      <el-form-item label="菜单名称" prop="menuName">
+        <el-input
+          v-model="queryParams.menuName"
+          placeholder="请输入菜单名称"
+          clearable
+          size="small"
+        />
+      </el-form-item>
+      <el-form-item label="状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="菜单状态" clearable size="small">
+          <el-option
+            v-for="dict in statusOptions"
+            :key="dict.dictValue"
+            :label="dict.dictLabel"
+            :value="dict.dictValue"
+          />
+        </el-select>
+      </el-form-item>
+      <el-form-item style="float: right;">
+        <el-col :span="1.5">
+          <el-button
+            type="primary"
+            plain
+            icon="el-icon-plus"
+            style="margin-right:20px;"
+            size="mini"
+            @click="handleAdd"
+            v-hasPermi="['system:menu:add']"
+          >新增</el-button>
+        </el-col>
+      </el-form-item>
+      <el-form-item>
+        <p class="inquire-button-one" @click="handleQuery">查询</p>
+        <p class="reset-button-one" @click="resetQuery">重置</p>
+      </el-form-item>
+    </el-form>
+
+    <el-table border
+      v-loading="loading"
+      :data="menuList"
+      row-key="menuId"
+      :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+    >
+      <el-table-column prop="menuName" label="菜单名称" :show-overflow-tooltip="true" width="160"></el-table-column>
+      <el-table-column prop="icon" label="图标" align="center" width="100">
+        <template slot-scope="scope">
+          <svg-icon :icon-class="scope.row.icon" />
+        </template>
+      </el-table-column>
+      <el-table-column prop="orderNum" label="排序" width="60"></el-table-column>
+      <el-table-column prop="perms" label="权限标识" :show-overflow-tooltip="true"></el-table-column>
+      <el-table-column prop="path" label="路由地址" :show-overflow-tooltip="true"></el-table-column>
+      <el-table-column prop="component" label="组件路径" :show-overflow-tooltip="true"></el-table-column>
+      <el-table-column prop="menuType" label="类型" width="60">
+        <template slot-scope="scope">
+          <span>{{scope.row.menuType==0?'目录':(scope.row.menuType==1?'菜单':(scope.row.menuType==2?'按钮':''))}}</span>
+        </template>
+      </el-table-column>
+      <el-table-column prop="status" label="状态" width="120">
+        <template slot-scope="scope">
+          <span>
+            {{scope.row.visible?'显示':(!scope.row.visible?'隐藏':'')}}
+            |
+            {{scope.row.status?'启用':(!scope.row.status?'停用':'')}}
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180" v-if="tableButtonType">
+        <template slot-scope="scope">
+          <div class="table-button-box">
+            <p class="table-button-null"></p>
+            <p class="table-button-p"
+               @click="handleAdd(scope.row)"
+               v-hasPermi="['system:menu:add']"
+            >新增</p>
+            <p class="table-button-p"
+               @click="handleUpdate(scope.row)"
+               v-hasPermi="['system:menu:edit']"
+            >修改</p>
+            <p class="table-button-p"
+               @click="handleDelete(scope.row)"
+               v-hasPermi="['system:menu:remove']"
+            >删除</p>
+            <p class="table-button-null"></p>
+          </div>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改菜单对话框 -->
+    <el-dialog :title="title" :visible.sync="open" width="680px" append-to-body :close-on-click-modal="false">
+      <el-form ref="form" :model="form" :rules="rules" label-width="100px">
+        <el-row>
+          <el-col :span="24">
+            <el-form-item label="上级菜单">
+              <treeselect
+                v-model="form.parentId"
+                :options="menuOptions"
+                :normalizer="normalizer"
+                :show-count="true"
+                placeholder="选择上级菜单"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item label="菜单类型" prop="menuType">
+              <el-radio-group v-model="form.menuType">
+                <el-radio :label="0">目录</el-radio>
+                <el-radio :label="1">菜单</el-radio>
+                <el-radio :label="2">按钮</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
+            <el-form-item v-if="form.menuType != '2'" label="菜单图标">
+              <el-popover
+                placement="bottom-start"
+                width="460"
+                trigger="click"
+                @show="$refs['iconSelect'].reset()"
+              >
+                <IconSelect ref="iconSelect" @selected="selected" />
+                <el-input slot="reference" v-model="form.icon" placeholder="点击选择图标" readonly>
+                  <svg-icon
+                    v-if="form.icon"
+                    slot="prefix"
+                    :icon-class="form.icon"
+                    class="el-input__icon"
+                    style="height: 32px;width: 16px;"
+                  />
+                  <i v-else slot="prefix" class="el-icon-search el-input__icon" />
+                </el-input>
+              </el-popover>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="菜单名称" prop="menuName">
+              <el-input v-model="form.menuName" placeholder="请输入菜单名称" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="显示排序" prop="orderNum">
+              <el-input-number v-model="form.orderNum" controls-position="right" :min="0" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item v-if="form.menuType != '2'">
+              <span slot="label">
+                <el-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                是否外链
+              </span>
+              <el-radio-group v-model="form.isFrame">
+                <el-radio :label="true">是</el-radio>
+                <el-radio :label="false">否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item v-if="form.menuType != '2'" prop="path">
+              <span slot="label">
+                <el-tooltip content="访问的路由地址,如:`user`,如外网地址需内链访问则以`http(s)://`开头" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                路由地址
+              </span>
+              <el-input v-model="form.path" placeholder="请输入路由地址" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12" v-if="form.menuType == '1'">
+            <el-form-item prop="component">
+              <span slot="label">
+                <el-tooltip content="访问的组件路径,如:`system/user/index`,默认在`views`目录下" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                组件路径
+              </span>
+              <el-input v-model="form.component" placeholder="请输入组件路径" />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item v-if="form.menuType != '0'">
+              <el-input v-model="form.perms" placeholder="请输入权限标识" maxlength="100" />
+              <span slot="label">
+                <el-tooltip content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermi('system:user:list')`)" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                权限字符
+              </span>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item v-if="form.menuType != '2'">
+              <span slot="label">
+                <el-tooltip content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                显示状态
+              </span>
+              <el-radio-group v-model="form.visible">
+                <el-radio
+                  v-for="dict in visibleOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictValue"
+                >{{dict.dictLabel}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item v-if="form.menuType != '2'">
+              <span slot="label">
+                <el-tooltip content="选择停用则路由将不会出现在侧边栏,也不能被访问" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                菜单状态
+              </span>
+              <el-radio-group v-model="form.status">
+                <el-radio
+                  v-for="dict in statusOptions"
+                  :key="dict.dictValue"
+                  :label="dict.dictValue"
+                >{{dict.dictLabel}}</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item v-if="form.menuType == '1'">
+              <span slot="label">
+                <el-tooltip content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                是否缓存
+              </span>
+              <el-radio-group v-model="form.isCache">
+                <el-radio :label="true">缓存</el-radio>
+                <el-radio :label="false">不缓存</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item>
+              <span slot="label">
+                <el-tooltip content="选择是后在权限配置时此项为选中并且无法操作" placement="top">
+                <i class="el-icon-question"></i>
+                </el-tooltip>
+                是否必选
+              </span>
+              <el-radio-group v-model="form.isRequired">
+                <el-radio :label="true">是</el-radio>
+                <el-radio :label="false">否</el-radio>
+              </el-radio-group>
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="cancel">取 消</el-button>
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listMenu, getMenu, delMenu, addMenu, updateMenu } from "@/api/systemManagement/index";
+import Treeselect from "@riophae/vue-treeselect";
+import "@riophae/vue-treeselect/dist/vue-treeselect.css";
+import IconSelect from "@/components/IconSelect";
+
+export default {
+  name: "Menu",
+  components: { Treeselect, IconSelect },
+  data() {
+    return {
+      tableButtonType:this.hasPermiDom(['system:menu:add','system:menu:edit','system:menu:remove']),
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 菜单表格树数据
+      menuList: [],
+      // 菜单树选项
+      menuOptions: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 显示状态数据字典
+      visibleOptions: [
+        {dictValue:true,dictLabel:"显示"},
+        {dictValue:false,dictLabel:"隐藏"},
+      ],
+      // 菜单状态数据字典
+      statusOptions: [
+        {dictValue:true,dictLabel:"启用"},
+        {dictValue:false,dictLabel:"停用"},
+      ],
+      // 查询参数
+      queryParams: {
+        menuName: undefined,
+        visible: undefined
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        menuName: [
+          { required: true, message: "菜单名称不能为空", trigger: "blur" },
+          { required: true, message: "菜单名称不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ],
+        orderNum: [
+          { required: true, message: "菜单顺序不能为空", trigger: "blur" },
+          { required: true, message: "菜单顺序不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ],
+        path: [
+          { required: true, message: "路由地址不能为空", trigger: "blur" },
+          { required: true, message: "路由地址不能为空", validator: this.spaceJudgment, trigger: "blur" }
+        ]
+      }
+    };
+  },
+  created() {
+    this.getList();
+    // this.getDicts("sys_show_hide").then(response => {
+    //   this.visibleOptions = response.data;
+    // });
+    // this.getDicts("sys_normal_disable").then(response => {
+    //   this.statusOptions = response.data;
+    // });
+  },
+  methods: {
+    // 选择图标
+    selected(name) {
+      this.form.icon = name;
+    },
+    /** 查询菜单列表 */
+    getList() {
+      this.loading = true;
+      listMenu(this.queryParams).then(response => {
+        this.menuList = this.handleTree(response.data, "menuId");
+        this.loading = false;
+      });
+    },
+    /** 转换菜单数据结构 */
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children;
+      }
+      return {
+        id: node.menuId,
+        label: node.menuName,
+        children: node.children
+      };
+    },
+    /** 查询菜单下拉树结构 */
+    getTreeselect() {
+      listMenu({}).then(response => {
+        this.menuOptions = [];
+        const menu = { menuId: 0, menuName: '主类目', children: [] };
+        menu.children = this.handleTree(response.data, "menuId");
+        this.menuOptions.push(menu);
+      });
+    },
+    // 显示状态字典翻译
+    visibleFormat(row, column) {
+      if (row.menuType == "2") {
+        return "";
+      }
+      return this.selectDictLabel(this.visibleOptions, row.visible);
+    },
+    // 菜单状态字典翻译
+    statusFormat(row, column) {
+      if (row.menuType == "2") {
+        return "";
+      }
+      return this.selectDictLabel(this.statusOptions, row.status);
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        menuId: undefined,
+        parentId: 0,
+        menuName: undefined,
+        icon: undefined,
+        menuType: 0,
+        orderNum: undefined,
+        isFrame: true,
+        isCache: true,
+        visible: true,
+        status: true,
+        isRequired: false
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd(row) {
+      this.reset();
+      this.getTreeselect();
+      if (row != null && row.menuId) {
+        this.form.parentId = row.menuId;
+      } else {
+        this.form.parentId = 0;
+      }
+      this.open = true;
+      this.title = "添加菜单";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      this.getTreeselect();
+      getMenu({menuId:row.menuId}).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改菜单";
+      });
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          let obj = JSON.parse(JSON.stringify(this.form))
+          obj.component = obj.menuType != 0 ? obj.component:'Layout';
+          if (obj.menuId != undefined) {
+            updateMenu(obj).then(response => {
+              this.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addMenu(obj).then(response => {
+              this.msgSuccess("新增成功");
+              this.open = false;
+              this.getList();
+            });
+          }
+        }
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      this.$confirm('是否确认删除名称为"' + row.menuName + '"的数据项?', "警告", {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning"
+        }).then(function() {
+          return delMenu({menuId:row.menuId});
+        }).then(() => {
+          this.getList();
+          this.msgSuccess("删除成功");
+        }).catch(() => {});
+    }
+  }
+};
+</script>
+<style scoped lang="scss">
+  .menu {
+    display: flex!important;
+    flex-direction: column;
+    box-shadow: 0 0 8px 2px rgba(0, 0, 0, 0.1);
+    padding:11px 20px 20px!important;
+  }
+</style>

+ 10 - 0
vue.config.js

@@ -365,6 +365,16 @@ module.exports = {
           enforce:true,
           reuseExistingChunk: true
         },
+        //系统管理
+        systemManagement: {
+          name: 'chunk-systemManagement',
+          test: resolve('src/views/systemManagement'),
+          priority: 0,
+          minSize: 0,
+          minChunks: 1,
+          enforce:true,
+          reuseExistingChunk: true
+        },
       }
     })
     config.optimization.runtimeChunk('single'),{