Browse Source

Merge branch 'dev'

zhangwenjian 4 years ago
parent
commit
3402babbe3
100 changed files with 2831 additions and 598 deletions
  1. 2 2
      package.json
  2. 0 8
      src/App.vue
  3. 1 1
      src/api/admin/dict/data.js
  4. 0 0
      src/api/admin/dict/type.js
  5. 46 0
      src/api/admin/sys-api.js
  6. 14 0
      src/api/admin/sys-config.js
  7. 0 0
      src/api/admin/sys-dept.js
  8. 0 0
      src/api/admin/sys-login-log.js
  9. 10 9
      src/api/admin/sys-menu.js
  10. 0 0
      src/api/admin/sys-opera-log.js
  11. 0 0
      src/api/admin/sys-post.js
  12. 12 7
      src/api/admin/sys-role.js
  13. 29 27
      src/api/admin/sys-user.js
  14. 0 0
      src/api/cms/sys-category.js
  15. 0 0
      src/api/cms/sys-content.js
  16. 9 9
      src/api/file.js
  17. 0 0
      src/api/job/sys-job.js
  18. 1 1
      src/api/monitor/server.js
  19. 11 7
      src/api/user.js
  20. 0 2
      src/components/FileManage/Left.vue
  21. 0 3
      src/components/FileManage/Right.vue
  22. 193 0
      src/components/FormGenParser/Parser.vue
  23. 122 0
      src/components/FormGenRender/render.js
  24. 5 0
      src/components/FormGenRender/slots/el-button.js
  25. 13 0
      src/components/FormGenRender/slots/el-checkbox-group.js
  26. 8 0
      src/components/FormGenRender/slots/el-input.js
  27. 13 0
      src/components/FormGenRender/slots/el-radio-group.js
  28. 9 0
      src/components/FormGenRender/slots/el-select.js
  29. 17 0
      src/components/FormGenRender/slots/el-upload.js
  30. 0 31
      src/components/MarkdownEditor/default-options.js
  31. 0 118
      src/components/MarkdownEditor/index.vue
  32. 0 1
      src/components/Tinymce/components/EditorImage.vue
  33. 0 1
      src/components/Tinymce/index.vue
  34. 144 0
      src/components/TopNav/index.vue
  35. 0 0
      src/icons/svg/access.svg
  36. 1 0
      src/icons/svg/add-db.svg
  37. 1 0
      src/icons/svg/add-doc.svg
  38. 0 0
      src/icons/svg/alarm-settings.svg
  39. 1 0
      src/icons/svg/api-ctl.svg
  40. 0 0
      src/icons/svg/api-doc.svg
  41. 0 0
      src/icons/svg/api-gateway.svg
  42. 1 0
      src/icons/svg/api-monitor.svg
  43. 0 0
      src/icons/svg/api-server.svg
  44. 1 0
      src/icons/svg/api-test.svg
  45. 1 0
      src/icons/svg/app-group-fill.svg
  46. 1 0
      src/icons/svg/archived.svg
  47. 1 0
      src/icons/svg/base-info.svg
  48. 0 0
      src/icons/svg/batch-update.svg
  49. 1 0
      src/icons/svg/db.svg
  50. 1 0
      src/icons/svg/del.svg
  51. 1 0
      src/icons/svg/dev-tools.svg
  52. 1 0
      src/icons/svg/doc.svg
  53. 0 0
      src/icons/svg/err-list.svg
  54. 1 0
      src/icons/svg/http-err.svg
  55. 0 0
      src/icons/svg/info-setting.svg
  56. 0 0
      src/icons/svg/intel.svg
  57. 1 0
      src/icons/svg/json-push.svg
  58. 0 0
      src/icons/svg/map.svg
  59. 0 0
      src/icons/svg/next-page.svg
  60. 1 0
      src/icons/svg/operators.svg
  61. 1 0
      src/icons/svg/project-group.svg
  62. 0 0
      src/icons/svg/project-manage.svg
  63. 1 0
      src/icons/svg/pwd.svg
  64. 1 0
      src/icons/svg/return-code-doc.svg
  65. 1 0
      src/icons/svg/return-code.svg
  66. 1 0
      src/icons/svg/root-addr.svg
  67. 0 0
      src/icons/svg/safety.svg
  68. 0 0
      src/icons/svg/scene.svg
  69. 1 0
      src/icons/svg/signature.svg
  70. 1 0
      src/icons/svg/statistics.svg
  71. 0 0
      src/icons/svg/system-tools.svg
  72. 1 0
      src/icons/svg/time-avg.svg
  73. 1 0
      src/icons/svg/user-info.svg
  74. 0 0
      src/icons/svg/webhock.svg
  75. 22 2
      src/layout/components/Navbar.vue
  76. 20 0
      src/layout/components/Settings/index.vue
  77. 8 2
      src/layout/components/Sidebar/index.vue
  78. 0 1
      src/layout/components/TagsView/ScrollPane.vue
  79. 7 2
      src/main.js
  80. 1 1
      src/router/index.js
  81. 5 0
      src/settings.js
  82. 3 0
      src/store/getters.js
  83. 26 5
      src/store/modules/permission.js
  84. 2 2
      src/store/modules/settings.js
  85. 0 1
      src/store/modules/system.js
  86. 180 84
      src/styles/admin.scss
  87. 31 25
      src/styles/sidebar.scss
  88. 3 0
      src/utils/costum.js
  89. 151 90
      src/utils/generator/html.js
  90. 55 11
      src/utils/generator/index.js
  91. 4 0
      src/utils/index.js
  92. 19 14
      src/views/admin/dict/data.vue
  93. 11 11
      src/views/admin/dict/index.vue
  94. 428 0
      src/views/admin/sys-api/index.vue
  95. 113 32
      src/views/admin/sys-config/index.vue
  96. 304 0
      src/views/admin/sys-config/set.vue
  97. 18 14
      src/views/admin/sys-dept/index.vue
  98. 13 9
      src/views/admin/sys-login-log/index.vue
  99. 628 0
      src/views/admin/sys-menu/index.vue
  100. 96 65
      src/views/admin/sys-oper-log/index.vue

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "go-admin",
-  "version": "1.3.3",
+  "version": "2.0.0",
   "description": "A magical vue admin. An out-of-box UI solution for enterprise applications. Newest development stack of vue. Lots of awesome features",
   "author": "https://github.com/wenjianzhang",
   "license": "MIT",
@@ -72,7 +72,6 @@
     "showdown": "^1.9.1",
     "solarlunar": "^2.0.7",
     "sortablejs": "^1.10.2",
-    "tui-editor": "1.4.10",
     "uuid": "^8.3.0",
     "viser-vue": "^2.4.8",
     "vue": "2.6.11",
@@ -108,6 +107,7 @@
     "connect": "3.7.0",
     "eslint": "7.6.0",
     "eslint-plugin-vue": "6.2.2",
+    "form-gen-parser": "^1.0.3",
     "html-webpack-plugin": "4.3.0",
     "husky": "4.2.5",
     "lint-staged": "10.2.11",

+ 0 - 8
src/App.vue

@@ -19,11 +19,3 @@ var _hmt = _hmt || [];
 })();
 </script>
 
-<style lang="scss">
-  // .el-dialog__wrapper{
-  //   display: flex;
-  //   justify-content: center;
-  //   align-items: center;
-  // }
-</style>
-

+ 1 - 1
src/api/system/dict/data.js → src/api/admin/dict/data.js

@@ -20,7 +20,7 @@ export function getData(dictCode) {
 // 根据字典类型查询字典数据信息
 export function getDicts(dictType) {
   return request({
-    url: '/api/v1/dict/data-all?dictType=' + dictType,
+    url: '/api/v1/dict-data/option-select?dictType=' + dictType,
     method: 'get'
   })
 }

+ 0 - 0
src/api/system/dict/type.js → src/api/admin/dict/type.js


+ 46 - 0
src/api/admin/sys-api.js

@@ -0,0 +1,46 @@
+import request from '@/utils/request'
+
+// 查询SysApi列表
+export function listSysApi(query) {
+  return request({
+    url: '/api/v1/sys-api',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询SysApi详细
+export function getSysApi(id) {
+  return request({
+    url: '/api/v1/sys-api/' + id,
+    method: 'get'
+  })
+}
+
+// 新增SysApi
+export function addSysApi(data) {
+  return request({
+    url: '/api/v1/sys-api',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改SysApi
+export function updateSysApi(data) {
+  return request({
+    url: '/api/v1/sys-api/' + data.id,
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除SysApi
+export function delSysApi(data) {
+  return request({
+    url: '/api/v1/sys-api',
+    method: 'delete',
+    data: data
+  })
+}
+

+ 14 - 0
src/api/system/config.js → src/api/admin/sys-config.js

@@ -52,3 +52,17 @@ export function delConfig(data) {
   })
 }
 
+export function getSetConfig(query) {
+  return request({
+    url: '/api/v1/set-config',
+    method: 'get'
+  })
+}
+
+export function updateSetConfig(data) {
+  return request({
+    url: '/api/v1/set-config',
+    method: 'put',
+    data: data
+  })
+}

+ 0 - 0
src/api/system/dept.js → src/api/admin/sys-dept.js


+ 0 - 0
src/api/system/sys-login-log.js → src/api/admin/sys-login-log.js


+ 10 - 9
src/api/system/menu.js → src/api/admin/sys-menu.js

@@ -18,12 +18,12 @@ export function getMenu(menuId) {
 }
 
 // 查询菜单下拉树结构
-export function treeselect() {
-  return request({
-    url: '/api/v1/menuTreeselect',
-    method: 'get'
-  })
-}
+// export function treeselect() {
+//   return request({
+//     url: '/api/v1/menuTreeselect',
+//     method: 'get'
+//   })
+// }
 
 // 根据角色ID查询菜单下拉树结构
 export function roleMenuTreeselect(roleId) {
@@ -52,9 +52,10 @@ export function updateMenu(data, id) {
 }
 
 // 删除菜单
-export function delMenu(menuId) {
+export function delMenu(data) {
   return request({
-    url: '/api/v1/menu/' + menuId,
-    method: 'delete'
+    url: '/api/v1/menu',
+    method: 'delete',
+    data: data
   })
 }

+ 0 - 0
src/api/system/sys-opera-log.js → src/api/admin/sys-opera-log.js


+ 0 - 0
src/api/system/post.js → src/api/admin/sys-post.js


+ 12 - 7
src/api/system/role.js → src/api/admin/sys-role.js

@@ -47,9 +47,14 @@ export function dataScope(data) {
 // 角色状态修改
 export function changeRoleStatus(roleId, status) {
   const data = {
+    roleId,
     status
   }
-  return updateRole(data, roleId)
+  return request({
+    url: '/api/v1/role-status',
+    method: 'put',
+    data: data
+  })
 }
 
 // 删除角色
@@ -75,9 +80,9 @@ export function getRoutes() {
   })
 }
 
-export function getMenuNames() {
-  return request({
-    url: '/api/v1/menuids',
-    method: 'get'
-  })
-}
+// export function getMenuNames() {
+//   return request({
+//     url: '/api/v1/menuids',
+//     method: 'get'
+//   })
+// }

+ 29 - 27
src/api/system/sysuser.js → src/api/admin/sys-user.js

@@ -3,7 +3,7 @@ import request from '@/utils/request'
 // 查询用户列表
 export function listUser(query) {
   return request({
-    url: '/api/v1/sysUser',
+    url: '/api/v1/sys-user',
     method: 'get',
     params: query
   })
@@ -12,14 +12,14 @@ export function listUser(query) {
 // 查询用户详细
 export function getUser(userId) {
   return request({
-    url: '/api/v1/sysUser/' + userId,
+    url: '/api/v1/sys-user/' + userId,
     method: 'get'
   })
 }
 
 export function getUserInit() {
   return request({
-    url: '/api/v1/sysUser/',
+    url: '/api/v1/sys-user/',
     method: 'get'
   })
 }
@@ -27,7 +27,7 @@ export function getUserInit() {
 // 新增用户
 export function addUser(data) {
   return request({
-    url: '/api/v1/sysUser',
+    url: '/api/v1/sys-user',
     method: 'post',
     data: data
   })
@@ -36,24 +36,25 @@ export function addUser(data) {
 // 修改用户
 export function updateUser(data) {
   return request({
-    url: '/api/v1/sysUser',
+    url: '/api/v1/sys-user',
     method: 'put',
     data: data
   })
 }
 
 // 删除用户
-export function delUser(userId) {
+export function delUser(data) {
   return request({
-    url: '/api/v1/sysUser/' + userId,
-    method: 'delete'
+    url: '/api/v1/sys-user',
+    method: 'delete',
+    data: data
   })
 }
 
 // 导出用户
 export function exportUser(query) {
   return request({
-    url: '/api/v1/sysUser/export',
+    url: '/api/v1/sys-user/export',
     method: 'get',
     params: query
   })
@@ -66,42 +67,42 @@ export function resetUserPwd(userId, password) {
     password
   }
   return request({
-    url: '/api/v1/sysUser',
+    url: '/api/v1/user/pwd/reset',
     method: 'put',
     data: data
   })
 }
 
 // 用户状态修改
-export function changeUserStatus(userId, status) {
+export function changeUserStatus(e) {
   const data = {
-    userId,
-    status
+    userId: e.userId,
+    status: e.status
   }
   return request({
-    url: '/api/v1/sysUser',
+    url: '/api/v1/user/status',
     method: 'put',
     data: data
   })
 }
 
-// 查询用户个人信息
-export function getUserProfile() {
-  return request({
-    url: '/api/v1/user/profile',
-    method: 'get'
-  })
-}
-
 // 修改用户个人信息
 export function updateUserProfile(data) {
   return request({
-    url: '/api/v1/sysUser/profile',
+    url: '/api/v1/sys-user/profile',
     method: 'put',
     data: data
   })
 }
 
+// 下载用户导入模板
+export function importTemplate() {
+  return request({
+    url: '/api/v1/sys-user/importTemplate',
+    method: 'get'
+  })
+}
+
 // 用户密码重置
 export function updateUserPwd(oldPassword, newPassword) {
   const data = {
@@ -109,7 +110,7 @@ export function updateUserPwd(oldPassword, newPassword) {
     newPassword
   }
   return request({
-    url: '/api/v1/user/pwd',
+    url: '/api/v1/user/pwd/set',
     method: 'put',
     data: data
   })
@@ -124,10 +125,11 @@ export function uploadAvatar(data) {
   })
 }
 
-// 下载用户导入模板
-export function importTemplate() {
+// 查询用户个人信息
+export function getUserProfile() {
   return request({
-    url: '/api/v1/sysUser/importTemplate',
+    url: '/api/v1/user/profile',
     method: 'get'
   })
 }
+

+ 0 - 0
src/api/syscategory.js → src/api/cms/sys-category.js


+ 0 - 0
src/api/syscontent.js → src/api/cms/sys-content.js


+ 9 - 9
src/api/file.js

@@ -2,53 +2,53 @@ import request from '@/utils/request'
 import { data } from 'autoprefixer'
 
 export const sysfiledirList = data => request({
-  url: '/api/v1/sysfiledir',
+  url: '/api/v1/file-dir',
   method: 'GET',
   data
 })
 
 export const sysfiledirAcionAdd = data => request({
-  url: '/api/v1/sysfiledir',
+  url: '/api/v1/file-dir',
   method: 'POST',
   data
 })
 
 export const sysfiledirAcionEdit = data => request({
-  url: '/api/v1/sysfiledir/' + data.id,
+  url: '/api/v1/file-dir/' + data.id,
   method: 'PUT',
   data
 })
 
 export const sysfiledirAcionDel = data => request({
-  url: '/api/v1/sysfiledir/' + data,
+  url: '/api/v1/file-dir/' + data,
   method: 'DELETE'
 })
 
 export const sysfileinfoList = data => request({
-  url: `/api/v1/sysfileinfo?pId=${data.pId}&pageSize=${data.pageSize}&pageIndex=${data.pageIndex}`,
+  url: `/api/v1/file-info?pId=${data.pId}&pageSize=${data.pageSize}&pageIndex=${data.pageIndex}`,
   method: 'GET',
   data
 })
 
 export const sysfileinfo = id => request({
-  url: '/api/v1/sysfileinfo/' + id,
+  url: '/api/v1/file-info/' + id,
   method: 'GET'
 })
 
 export const sysfileinfoAdd = data => request({
-  url: '/api/v1/sysfileinfo',
+  url: '/api/v1/file-info',
   method: 'POST',
   data
 })
 
 export const sysfileinfoEdit = data => request({
-  url: '/api/v1/sysfileinfo/' + data.id,
+  url: '/api/v1/file-info/' + data.id,
   method: 'put',
   data
 })
 
 export const sysfileinfoDelete = id => request({
-  url: '/api/v1/sysfileinfo/' + id,
+  url: '/api/v1/file-info/' + id,
   method: 'DELETE',
   data
 })

+ 0 - 0
src/api/sysjob.js → src/api/job/sys-job.js


+ 1 - 1
src/api/monitor/server.js

@@ -3,7 +3,7 @@ import request from '@/utils/request'
 // 查询服务器详细
 export function getServer() {
   return request({
-    url: '/api/v1/setting/serverInfo',
+    url: '/api/v1/server-monitor',
     method: 'get'
   })
 }

+ 11 - 7
src/api/user.js

@@ -1,5 +1,6 @@
 import request from '@/utils/request'
 
+// login 登陆
 export function login(data) {
   return request({
     url: '/api/v1/login',
@@ -8,6 +9,15 @@ export function login(data) {
   })
 }
 
+// logout 退出
+export function logout() {
+  return request({
+    url: '/api/v1/logout',
+    method: 'post'
+  })
+}
+
+// refreshtoken 刷新token
 export function refreshtoken(data) {
   return request({
     url: '/refreshtoken',
@@ -16,6 +26,7 @@ export function refreshtoken(data) {
   })
 }
 
+// getInfo 获取用户基本信息
 export function getInfo() {
   return request({
     url: '/api/v1/getinfo',
@@ -23,10 +34,3 @@ export function getInfo() {
   })
 }
 
-export function logout() {
-  return request({
-    url: '/api/v1/logout',
-    method: 'post'
-  })
-}
-

+ 0 - 2
src/components/FileManage/Left.vue

@@ -127,7 +127,6 @@ export default {
               status: true,
               node: this.rightData.currentData
             }
-            console.log(this.rename)
           }
         },
         {
@@ -216,7 +215,6 @@ export default {
         node: ''
       }
       d.label = this.$refs.nodeInput.value
-      console.log(d)
       sysfiledirAcionEdit({
         id: d.id,
         label: d.label,

+ 0 - 3
src/components/FileManage/Right.vue

@@ -383,8 +383,6 @@ export default {
       this.uploadShow = false
     },
     handleTableAction(a, b) {
-      console.log(typeof b)
-      console.log(a)
       this.rightData.currentData = a
       this.handleAction(b)
     },
@@ -444,7 +442,6 @@ export default {
       }
     },
     handleBlur(a, b) {
-      console.log(a, b)
       if (this.tableData.length > 0) {
         this.tableData.forEach(item => {
           item.open = false

+ 193 - 0
src/components/FormGenParser/Parser.vue

@@ -0,0 +1,193 @@
+<script>
+import { deepClone } from '@/utils/generator/index'
+import render from '@/components/FormGenRender/render.js'
+
+const ruleTrigger = {
+  'el-input': 'blur',
+  'el-input-number': 'blur',
+  'el-select': 'change',
+  'el-radio-group': 'change',
+  'el-checkbox-group': 'change',
+  'el-cascader': 'change',
+  'el-time-picker': 'change',
+  'el-date-picker': 'change',
+  'el-rate': 'change'
+}
+
+const layouts = {
+  colFormItem(h, scheme) {
+    const config = scheme.__config__
+    const listeners = buildListeners.call(this, scheme)
+
+    let labelWidth = config.labelWidth ? `${config.labelWidth}px` : null
+    if (config.showLabel === false) labelWidth = '0'
+    return (
+      <el-col span={config.span}>
+        <el-form-item label-width={labelWidth} prop={scheme.__vModel__}
+          label={config.showLabel ? config.label : ''}>
+          <render conf={scheme} on={listeners} />
+        </el-form-item>
+      </el-col>
+    )
+  },
+  rowFormItem(h, scheme) {
+    let child = renderChildren.apply(this, arguments)
+    if (scheme.type === 'flex') {
+      child = <el-row type={scheme.type} justify={scheme.justify} align={scheme.align}>
+        {child}
+      </el-row>
+    }
+    return (
+      <el-col span={scheme.span}>
+        <el-row gutter={scheme.gutter}>
+          {child}
+        </el-row>
+      </el-col>
+    )
+  }
+}
+
+function renderFrom(h) {
+  const { formConfCopy } = this
+
+  return (
+    <el-row gutter={formConfCopy.gutter}>
+      <el-form
+        size={formConfCopy.size}
+        label-position={formConfCopy.labelPosition}
+        disabled={formConfCopy.disabled}
+        label-width={`${formConfCopy.labelWidth}px`}
+        ref={formConfCopy.formRef}
+        // model不能直接赋值 https://github.com/vuejs/jsx/issues/49#issuecomment-472013664
+        props={{ model: this[formConfCopy.formModel] }}
+        rules={this[formConfCopy.formRules]}
+      >
+        {renderFormItem.call(this, h, formConfCopy.fields)}
+        {formConfCopy.formBtns && formBtns.call(this, h)}
+      </el-form>
+    </el-row>
+  )
+}
+
+function formBtns(h) {
+  return <el-col>
+    <el-form-item size='large'>
+      <el-button type='primary' onClick={this.submitForm}>提交</el-button>
+      <el-button onClick={this.resetForm}>重置</el-button>
+    </el-form-item>
+  </el-col>
+}
+
+function renderFormItem(h, elementList) {
+  return elementList.map(scheme => {
+    const config = scheme.__config__
+    const layout = layouts[config.layout]
+
+    if (layout) {
+      return layout.call(this, h, scheme)
+    }
+    throw new Error(`没有与${config.layout}匹配的layout`)
+  })
+}
+
+function renderChildren(h, scheme) {
+  const config = scheme.__config__
+  if (!Array.isArray(config.children)) return null
+  return renderFormItem.call(this, h, config.children)
+}
+
+function setValue(event, config, scheme) {
+  this.$set(config, 'defaultValue', event)
+  this.$set(this[this.formConf.formModel], scheme.__vModel__, event)
+}
+
+function buildListeners(scheme) {
+  const config = scheme.__config__
+  const methods = this.formConf.__methods__ || {}
+  const listeners = {}
+
+  // 给__methods__中的方法绑定this和event
+  Object.keys(methods).forEach(key => {
+    listeners[key] = event => methods[key].call(this, event)
+  })
+  // 响应 render.js 中的 vModel $emit('input', val)
+  listeners.input = event => setValue.call(this, event, config, scheme)
+
+  return listeners
+}
+
+export default {
+  components: {
+    render
+  },
+  props: {
+    formConf: {
+      type: Object,
+      required: true
+    }
+    // fileList: {
+    //   type: Array,
+    //   required: false
+    // }
+  },
+  data() {
+    const data = {
+      formConfCopy: deepClone(this.formConf),
+      [this.formConf.formModel]: {},
+      [this.formConf.formRules]: {}
+      // fileList: this.fileList
+    }
+    this.initFormData(data.formConfCopy.fields, data[this.formConf.formModel])
+    this.buildRules(data.formConfCopy.fields, data[this.formConf.formRules])
+    return data
+  },
+  methods: {
+    initFormData(componentList, formData) {
+      componentList.forEach(cur => {
+        const config = cur.__config__
+        if (cur.__vModel__) formData[cur.__vModel__] = config.defaultValue
+
+        if (config.children) this.initFormData(config.children, formData)
+      })
+    },
+    buildRules(componentList, rules) {
+      componentList.forEach(cur => {
+        const config = cur.__config__
+        if (Array.isArray(config.regList)) {
+          if (config.required) {
+            const required = { required: config.required, message: cur.placeholder }
+            if (Array.isArray(config.defaultValue)) {
+              required.type = 'array'
+              required.message = `请至少选择一个${config.label}`
+            }
+            required.message === undefined && (required.message = `${config.label}不能为空`)
+            config.regList.push(required)
+          }
+          rules[cur.__vModel__] = config.regList.map(item => {
+            // eslint-disable-next-line no-eval
+            item.pattern && (item.pattern = eval(item.pattern))
+            item.trigger = ruleTrigger && ruleTrigger[config.tag]
+            return item
+          })
+        }
+        if (config.children) this.buildRules(config.children, rules)
+      })
+    },
+    resetForm() {
+      this.formConfCopy = deepClone(this.formConf)
+      this.$refs[this.formConf.formRef].resetFields()
+    },
+    submitForm() {
+      this.$refs[this.formConf.formRef].validate(valid => {
+        if (!valid) return false
+        // 触发sumit事件
+        this.$emit('submit', this[this.formConf.formModel])
+        return true
+      })
+    }
+  },
+  render(h) {
+    return renderFrom.call(this, h)
+  }
+}
+</script>

+ 122 - 0
src/components/FormGenRender/render.js

@@ -0,0 +1,122 @@
+import { deepClone } from '@/utils/generator/index'
+
+const componentChild = {}
+/**
+ * 将./slots中的文件挂载到对象componentChild上
+ * 文件名为key,对应JSON配置中的__config__.tag
+ * 文件内容为value,解析JSON配置中的__slot__
+ */
+const slotsFiles = require.context('./slots', false, /\.js$/)
+const keys = slotsFiles.keys() || []
+keys.forEach(key => {
+  const tag = key.replace(/^\.\/(.*)\.\w+$/, '$1')
+  const value = slotsFiles(key).default
+  componentChild[tag] = value
+})
+
+function vModel(dataObject, defaultValue) {
+  dataObject.props.value = defaultValue
+
+  dataObject.on.input = val => {
+    this.$emit('input', val)
+  }
+}
+
+function mountSlotFiles(h, confClone, children) {
+  const childObjs = componentChild[confClone.__config__.tag]
+  if (childObjs) {
+    Object.keys(childObjs).forEach(key => {
+      const childFunc = childObjs[key]
+      if (confClone.__slot__ && confClone.__slot__[key]) {
+        children.push(childFunc(h, confClone, key))
+      }
+    })
+  }
+}
+
+function emitEvents(confClone) {
+  ['on', 'nativeOn'].forEach(attr => {
+    const eventKeyList = Object.keys(confClone[attr] || {})
+    eventKeyList.forEach(key => {
+      const val = confClone[attr][key]
+      if (typeof val === 'string') {
+        confClone[attr][key] = event => this.$emit(val, event)
+      }
+    })
+  })
+}
+
+function buildDataObject(confClone, dataObject) {
+  Object.keys(confClone).forEach(key => {
+    const val = confClone[key]
+    if (key === '__vModel__') {
+      vModel.call(this, dataObject, confClone.__config__.defaultValue)
+    } else if (dataObject[key] !== undefined) {
+      if (dataObject[key] === null ||
+        dataObject[key] instanceof RegExp ||
+        ['boolean', 'string', 'number', 'function', 'Array'].includes(typeof dataObject[key])) {
+        dataObject[key] = val
+      } else if (Array.isArray(dataObject[key])) {
+        dataObject[key] = [...dataObject[key], ...val]
+      } else {
+        dataObject[key] = { ...dataObject[key], ...val }
+      }
+    } else {
+      dataObject.attrs[key] = val
+    }
+  })
+
+  // 清理属性
+  clearAttrs(dataObject)
+}
+
+function clearAttrs(dataObject) {
+  delete dataObject.attrs.__config__
+  delete dataObject.attrs.__slot__
+  delete dataObject.attrs.__methods__
+}
+
+function makeDataObject() {
+  // 深入数据对象:
+  // https://cn.vuejs.org/v2/guide/render-function.html#%E6%B7%B1%E5%85%A5%E6%95%B0%E6%8D%AE%E5%AF%B9%E8%B1%A1
+  return {
+    class: {},
+    attrs: {},
+    props: {},
+    domProps: {},
+    nativeOn: {},
+    on: {},
+    style: {},
+    directives: [],
+    scopedSlots: {},
+    slot: null,
+    key: null,
+    ref: null,
+    refInFor: true
+  }
+}
+
+export default {
+  props: {
+    conf: {
+      type: Object,
+      required: true
+    }
+  },
+  render(h) {
+    const dataObject = makeDataObject()
+    const confClone = deepClone(this.conf)
+    const children = this.$slots.default || []
+
+    // 如果slots文件夹存在与当前tag同名的文件,则执行文件中的代码
+    mountSlotFiles.call(this, h, confClone, children)
+
+    // 将字符串类型的事件,发送为消息
+    emitEvents.call(this, confClone)
+
+    // 将json表单配置转化为vue render可以识别的 “数据对象(dataObject)”
+    buildDataObject.call(this, confClone, dataObject)
+
+    return h(this.conf.__config__.tag, dataObject, children)
+  }
+}

+ 5 - 0
src/components/FormGenRender/slots/el-button.js

@@ -0,0 +1,5 @@
+export default {
+  default(h, conf, key) {
+    return conf.__slot__[key]
+  }
+}

+ 13 - 0
src/components/FormGenRender/slots/el-checkbox-group.js

@@ -0,0 +1,13 @@
+export default {
+  options(h, conf, key) {
+    const list = []
+    conf.__slot__.options.forEach(item => {
+      if (conf.__config__.optionType === 'button') {
+        list.push(<el-checkbox-button label={item.value}>{item.label}</el-checkbox-button>)
+      } else {
+        list.push(<el-checkbox label={item.value} border={conf.border}>{item.label}</el-checkbox>)
+      }
+    })
+    return list
+  }
+}

+ 8 - 0
src/components/FormGenRender/slots/el-input.js

@@ -0,0 +1,8 @@
+export default {
+  prepend(h, conf, key) {
+    return <template slot='prepend'>{conf.__slot__[key]}</template>
+  },
+  append(h, conf, key) {
+    return <template slot='append'>{conf.__slot__[key]}</template>
+  }
+}

+ 13 - 0
src/components/FormGenRender/slots/el-radio-group.js

@@ -0,0 +1,13 @@
+export default {
+  options(h, conf, key) {
+    const list = []
+    conf.__slot__.options.forEach(item => {
+      if (conf.__config__.optionType === 'button') {
+        list.push(<el-radio-button label={item.value}>{item.label}</el-radio-button>)
+      } else {
+        list.push(<el-radio label={item.value} border={conf.border}>{item.label}</el-radio>)
+      }
+    })
+    return list
+  }
+}

+ 9 - 0
src/components/FormGenRender/slots/el-select.js

@@ -0,0 +1,9 @@
+export default {
+  options(h, conf, key) {
+    const list = []
+    conf.__slot__.options.forEach(item => {
+      list.push(<el-option label={item.label} value={item.value} disabled={item.disabled}></el-option>)
+    })
+    return list
+  }
+}

+ 17 - 0
src/components/FormGenRender/slots/el-upload.js

@@ -0,0 +1,17 @@
+export default {
+  'list-type': (h, conf, key) => {
+    const list = []
+    const config = conf.__config__
+    if (conf['list-type'] === 'picture-card') {
+      list.push(<i class='el-icon-plus'></i>)
+    } else {
+      list.push(<el-button size='small' type='primary' icon='el-icon-upload'>{config.buttonText}</el-button>)
+    }
+    if (config.showTip) {
+      list.push(
+        <div slot='tip' class='el-upload__tip'>只能上传不超过 {config.fileSize}{config.sizeUnit} 的{conf.accept}文件</div>
+      )
+    }
+    return list
+  }
+}

+ 0 - 31
src/components/MarkdownEditor/default-options.js

@@ -1,31 +0,0 @@
-// doc: https://nhnent.github.io/tui.editor/api/latest/ToastUIEditor.html#ToastUIEditor
-export default {
-  minHeight: '200px',
-  previewStyle: 'vertical',
-  useCommandShortcut: true,
-  useDefaultHTMLSanitizer: true,
-  usageStatistics: false,
-  hideModeSwitch: false,
-  toolbarItems: [
-    'heading',
-    'bold',
-    'italic',
-    'strike',
-    'divider',
-    'hr',
-    'quote',
-    'divider',
-    'ul',
-    'ol',
-    'task',
-    'indent',
-    'outdent',
-    'divider',
-    'table',
-    'image',
-    'link',
-    'divider',
-    'code',
-    'codeblock'
-  ]
-}

+ 0 - 118
src/components/MarkdownEditor/index.vue

@@ -1,118 +0,0 @@
-<template>
-  <div :id="id" />
-</template>
-
-<script>
-// deps for editor
-import 'codemirror/lib/codemirror.css' // codemirror
-import 'tui-editor/dist/tui-editor.css' // editor ui
-import 'tui-editor/dist/tui-editor-contents.css' // editor content
-
-import Editor from 'tui-editor'
-import defaultOptions from './default-options'
-
-export default {
-  name: 'MarkdownEditor',
-  props: {
-    value: {
-      type: String,
-      default: ''
-    },
-    id: {
-      type: String,
-      required: false,
-      default() {
-        return 'markdown-editor-' + +new Date() + ((Math.random() * 1000).toFixed(0) + '')
-      }
-    },
-    options: {
-      type: Object,
-      default() {
-        return defaultOptions
-      }
-    },
-    mode: {
-      type: String,
-      default: 'markdown'
-    },
-    height: {
-      type: String,
-      required: false,
-      default: '300px'
-    },
-    language: {
-      type: String,
-      required: false,
-      default: 'en_US' // https://github.com/nhnent/tui.editor/tree/master/src/js/langs
-    }
-  },
-  data() {
-    return {
-      editor: null
-    }
-  },
-  computed: {
-    editorOptions() {
-      const options = Object.assign({}, defaultOptions, this.options)
-      options.initialEditType = this.mode
-      options.height = this.height
-      options.language = this.language
-      return options
-    }
-  },
-  watch: {
-    value(newValue, preValue) {
-      if (newValue !== preValue && newValue !== this.editor.getValue()) {
-        this.editor.setValue(newValue)
-      }
-    },
-    language(val) {
-      this.destroyEditor()
-      this.initEditor()
-    },
-    height(newValue) {
-      this.editor.height(newValue)
-    },
-    mode(newValue) {
-      this.editor.changeMode(newValue)
-    }
-  },
-  mounted() {
-    this.initEditor()
-  },
-  destroyed() {
-    this.destroyEditor()
-  },
-  methods: {
-    initEditor() {
-      this.editor = new Editor({
-        el: document.getElementById(this.id),
-        ...this.editorOptions
-      })
-      if (this.value) {
-        this.editor.setValue(this.value)
-      }
-      this.editor.on('change', () => {
-        this.$emit('input', this.editor.getValue())
-      })
-    },
-    destroyEditor() {
-      if (!this.editor) return
-      this.editor.off('change')
-      this.editor.remove()
-    },
-    setValue(value) {
-      this.editor.setValue(value)
-    },
-    getValue() {
-      return this.editor.getValue()
-    },
-    setHtml(value) {
-      this.editor.setHtml(value)
-    },
-    getHtml() {
-      return this.editor.getHtml()
-    }
-  }
-}
-</script>

+ 0 - 1
src/components/Tinymce/components/EditorImage.vue

@@ -41,7 +41,6 @@ export default {
     },
     handleFileConfirm(e) {
       this.dialogVisible = false
-      console.log(e)
       this.$emit('successCBK', e)
     }
   }

+ 0 - 1
src/components/Tinymce/index.vue

@@ -203,7 +203,6 @@ export default {
     imageSuccessCBK(arr) {
       const _this = this
       arr.forEach(v => {
-        console.log(v)
         window.tinymce.get(_this.tinymceId).insertContent(`<img class="wscnph" width="100%" src="${v.fullUrl}" >`)
       })
     }

+ 144 - 0
src/components/TopNav/index.vue

@@ -0,0 +1,144 @@
+<template>
+  <el-menu
+    :default-active="activeMenu"
+    mode="horizontal"
+    @select="handleSelect"
+  >
+    <template v-for="(item, index) in topMenus">
+      <el-menu-item
+        v-if="index < visibleNumber"
+        :key="index"
+        :index="item.path"
+      ><svg-icon :icon-class="item.meta.icon" />
+        {{ item.meta.title }}</el-menu-item>
+    </template>
+
+    <!-- 顶部菜单超出数量折叠 -->
+    <el-submenu v-if="topMenus.length > visibleNumber" index="more">
+      <template slot="title">更多菜单</template>
+      <template v-for="(item, index) in topMenus">
+        <el-menu-item
+          v-if="index >= visibleNumber"
+          :key="index"
+          :index="item.path"
+        ><svg-icon :icon-class="item.meta.icon" />
+          {{ item.meta.title }}</el-menu-item>
+      </template>
+    </el-submenu>
+  </el-menu>
+</template>
+
+<script>
+import { constantRoutes } from '@/router'
+
+export default {
+  data() {
+    return {
+      // 顶部栏初始数
+      visibleNumber: 5,
+      // 是否为首次加载
+      isFrist: false
+    }
+  },
+  computed: {
+    // 顶部显示菜单
+    topMenus() {
+      return this.routers.map((menu) => ({
+        ...menu,
+        children: undefined
+      }))
+    },
+    // 所有的路由信息
+    routers() {
+      return this.$store.state.permission.topbarRouters
+    },
+    // 设置子路由
+    childrenMenus() {
+      var childrenMenus = []
+      this.routers.map((router) => {
+        for (var item in router.children) {
+          if (router.children[item].parentPath === undefined) {
+            router.children[item].parentPath = router.path
+          }
+          childrenMenus.push(router.children[item])
+        }
+      })
+      return constantRoutes.concat(childrenMenus)
+    },
+    // 默认激活的菜单
+    activeMenu() {
+      const path = this.$route.path
+      let activePath = this.routers[0].path
+      if (path.lastIndexOf('/') > 0) {
+        const tmpPath = path.substring(1, path.length)
+        activePath = '/' + tmpPath.substring(0, tmpPath.indexOf('/'))
+      } else if (path === '/index' || path === '') {
+        if (!this.isFrist) {
+          // eslint-disable-next-line vue/no-side-effects-in-computed-properties
+          this.isFrist = true
+        } else {
+          activePath = 'index'
+        }
+      }
+      this.activeRoutes(activePath)
+      return activePath
+    }
+  },
+  mounted() {
+    this.setVisibleNumber()
+  },
+  methods: {
+    // 根据宽度计算设置显示栏数
+    setVisibleNumber() {
+      const width = document.body.getBoundingClientRect().width - 200
+      const elWidth = this.$el.getBoundingClientRect().width
+      const menuItemNodes = this.$el.children
+      const menuWidth = Array.from(menuItemNodes).map(
+        (i) => i.getBoundingClientRect().width
+      )
+      this.visibleNumber = (
+        parseInt(width - elWidth) / parseInt(menuWidth)
+      ).toFixed(0)
+    },
+    // 菜单选择事件
+    handleSelect(key, keyPath) {
+      if (key.indexOf('http://') !== -1 || key.indexOf('https://') !== -1) {
+        // http(s):// 路径新窗口打开
+        window.open(key, '_blank')
+      } else {
+        this.activeRoutes(key)
+      }
+    },
+    // 当前激活的路由
+    activeRoutes(key) {
+      var routes = []
+      if (this.childrenMenus && this.childrenMenus.length > 0) {
+        this.childrenMenus.map((item) => {
+          if (key === item.parentPath || (key === 'index' && item.path === '')) {
+            routes.push(item)
+          }
+        })
+      }
+      this.$store.commit('permission/SET_SIDEBAR_ROUTERS', routes)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.el-menu--horizontal > .el-menu-item {
+  float: left;
+  height: 50px;
+  line-height: 50px;
+  margin: 0;
+  border-bottom: 3px solid transparent;
+  color: #999093;
+  padding: 0 5px;
+  margin: 0 10px;
+}
+
+.el-menu--horizontal > .el-menu-item.is-active {
+  border-bottom: 3px solid #409eff;
+  color: #303133;
+}
+</style>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/access.svg


+ 1 - 0
src/icons/svg/add-db.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564297343" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1725" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 0C229.23008 0 0 229.23008 0 512s229.23008 512 512 512 512-229.23008 512-512S794.76992 0 512 0z m0 998.4C243.36896 998.4 25.6 780.63104 25.6 512 25.6 243.3664 243.36896 25.6 512 25.6s486.4 217.7664 486.4 486.4c0 268.63104-217.76896 486.4-486.4 486.4z" fill="" p-id="1726"></path><path d="M666.60096 485.21984c-70.68928 0-128 57.31072-128 128s57.31072 128 128 128 128-57.31072 128-128-57.31072-128-128-128z m61.58336 142.464l-47.08608 0.02048 0.02048 47.09376a14.4896 14.4896 0 1 1-28.98432 0.01024l-0.02048-47.08864-47.09376 0.02048a14.48704 14.48704 0 0 1-14.49472-14.48448 14.48192 14.48192 0 0 1 14.48448-14.49472l47.0912-0.02304-0.02048-47.09632a14.4896 14.4896 0 1 1 28.9792-0.00768l0.0256 47.0912 47.0912-0.02304a14.49472 14.49472 0 1 1 0.00768 28.98176zM449.84576 591.5648c20.46464 0 40.256-0.4736 59.0592-1.33632 5.04064-37.07904 23.28576-69.94944 49.87392-93.70624-32.1408 3.03616-69.2992 4.77696-108.93568 4.77696-121.75104 0-220.44672-16.36352-220.44672-36.53888v90.26048c0.00256 20.18048 98.70336 36.544 220.44928 36.544zM449.84576 446.14656c121.76384 0 220.46464-16.36352 220.46464-36.55168v-90.26304c-0.01024-20.18048-98.71616-36.55168-220.46464-36.55168-121.7408 0-220.43136 16.37376-220.44672 36.55168v90.26304c0 20.19072 98.7008 36.55168 220.44672 36.55168zM508.2496 626.80832c-18.60608 0.84736-38.17472 1.30816-58.40384 1.30816-121.75104 0-220.44672-16.36864-220.44672-36.54912v90.26304c0 20.19072 98.7008 36.55168 220.44672 36.55168 35.10528 0 68.27008-1.36704 97.73056-3.78624-21.60128-23.67232-35.90144-54.12096-39.32672-87.78752z" fill="" p-id="1727"></path></svg>

+ 1 - 0
src/icons/svg/add-doc.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564259962" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1056" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M722.0992 279.1936h146.7392l-184.5248-192v154.2144c-0.0256 20.8384 16.9472 37.7856 37.7856 37.7856z" p-id="1057"></path><path d="M722.0992 321.024c-43.904 0-79.62112-35.712-79.62112-79.616V46.976H192.7168c-40.32 0-73.1136 32.7936-73.1136 73.1136V898.4064c0 40.32 32.7936 73.088 73.1136 73.088h640.1536c40.32 0 73.1136-32.7936 73.1136-73.088V321.024h-183.8848zM691.2 620.8h-153.6v153.6c0 14.14144-11.45856 25.6-25.6 25.6s-25.6-11.45856-25.6-25.6v-153.6H332.8c-14.14144 0-25.6-11.45856-25.6-25.6s11.45856-25.6 25.6-25.6h153.6v-153.6c0-14.14144 11.45856-25.6 25.6-25.6s25.6 11.45856 25.6 25.6v153.6h153.6c14.14144 0 25.6 11.45856 25.6 25.6s-11.45856 25.6-25.6 25.6z" p-id="1058"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/alarm-settings.svg


+ 1 - 0
src/icons/svg/api-ctl.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564424924" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6360" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M64 223.995345h168.001164v47.997673c0 26.428509 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.569164 41.984-47.997673v-47.997673h504.003491a32.004655 32.004655 0 0 0 0-64.009309H455.996509V111.988364c0-26.428509-18.878836-47.997673-41.984-47.997673H273.985164c-23.095855 0-41.984 21.569164-41.984 47.997673v47.997672H64a32.004655 32.004655 0 0 0 0 64.009309zM288.004655 128h111.997672V256H288.004655V128zM960 479.995345H791.998836v-47.997672c0-26.372655-18.878836-47.997673-41.984-47.997673H609.978182c-23.095855 0-41.984 21.634327-41.984 47.997673v47.997672H64a32.004655 32.004655 0 0 0 0 64.00931h504.003491v47.997672c0 26.363345 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.634327 41.984-47.997673v-47.997672h168.001164a32.004655 32.004655 0 1 0-0.009309-64.00931zM735.995345 576H623.997673v-128h111.997672v128zM960 800.293236v-0.288581H455.996509v-47.997673c0-26.363345-18.878836-47.997673-41.984-47.997673H274.050327c-23.105164 0-41.984 21.634327-41.984 47.997673v47.997673H64v0.288581a32.004655 32.004655 0 0 0 0 64.009309c0.986764 0 1.917673-0.195491 2.885818-0.288581h165.115346v47.997672c0 26.363345 18.878836 47.997673 41.984 47.997673h140.036654c23.095855 0 41.984-21.634327 41.984-47.997673v-47.997672h501.108364c0.968145 0.093091 1.899055 0.288582 2.895127 0.288581a32.004655 32.004655 0 1 0-0.009309-64.009309zM400.002327 896H288.004655V768h111.997672v128z" fill="" p-id="6361"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/api-doc.svg


File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/api-gateway.svg


+ 1 - 0
src/icons/svg/api-monitor.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564390735" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4903" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M138.24 163.84a25.6 25.6 0 0 0-25.6 25.6v650.24c0 11.31008 11.72992 20.48 23.04 20.48h10.24c11.31008 0 17.92-9.16992 17.92-20.48V189.44a25.6 25.6 0 0 0-25.6-25.6z" fill="" p-id="4904"></path><path d="M888.32 808.96h-752.64c-11.31008 0-23.04 9.16992-23.04 20.48v10.24c0 11.31008 11.72992 20.48 23.04 20.48h752.64c11.31008 0 17.92-9.16992 17.92-20.48v-10.24c0-11.31008-6.60992-20.48-17.92-20.48zM305.39264 603.66336a20.47488 20.47488 0 1 1-34.72384-21.72416l74.17856-115.52256a20.48 20.48 0 0 1 28.22656-6.49728 20.47488 20.47488 0 0 1 6.49728 28.22144l-74.17856 115.52256zM713.26208 587.26912a20.48512 20.48512 0 0 1-39.26528 11.66848l-97.01376-310.68672a20.48 20.48 0 0 1 39.26528-11.66336l97.01376 310.6816z" fill="" p-id="4905"></path><path d="M480.48128 699.97056a20.48 20.48 0 0 1-38.56896-13.78304l132.33152-413.35296a20.48 20.48 0 0 1 38.56896 13.78816l-132.33152 413.34784z" fill="" p-id="4906"></path><path d="M479.59552 682.368a20.48 20.48 0 0 1-10.14272 27.12576 20.49024 20.49024 0 0 1-27.13088-10.14784L344.72448 484.992a20.48 20.48 0 0 1 37.2736-16.9728l97.59744 214.3488zM302.08 593.92a20.48 20.48 0 0 1-20.48 20.48h-46.08a20.48 20.48 0 0 1 0-40.96h46.08a20.48 20.48 0 0 1 20.48 20.48zM860.16 593.92a20.48 20.48 0 0 1-20.48 20.48h-148.48a20.48 20.48 0 0 1 0-40.96h148.48a20.48 20.48 0 0 1 20.48 20.48z" fill="" p-id="4907"></path><path d="M839.68 591.36m-46.08 0a46.08 46.08 0 1 0 92.16 0 46.08 46.08 0 1 0-92.16 0Z" fill="" p-id="4908"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/api-server.svg


+ 1 - 0
src/icons/svg/api-test.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564393403" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5039" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M827.72992 153.6H196.27008C158.5664 153.6 128 184.1664 128 221.87008v580.25984C128 839.82848 158.5664 870.4 196.27008 870.4h631.45984c37.7088 0 68.27008-30.57152 68.27008-68.27008V221.87008C896 184.1664 865.43872 153.6 827.72992 153.6z m-42.66496 85.32992a25.61024 25.61024 0 0 1 0 51.20512 25.61024 25.61024 0 0 1-25.60512-25.6 25.62048 25.62048 0 0 1 25.60512-25.60512z m-102.40512 0a25.61024 25.61024 0 0 1 0 51.20512 25.60512 25.60512 0 0 1 0-51.20512z m-102.38976 0a25.60512 25.60512 0 0 1 0 51.20512 25.6 25.6 0 1 1 0-51.20512zM844.80512 768c0 37.68832-13.50144 51.18976-51.21024 51.18976H230.4c-37.69856 0-51.2-13.50144-51.2-51.18976V341.33504h665.6V768zM275.7888 542.208l96.38912 25.83552-90.91584 52.48512a17.07008 17.07008 0 0 0-6.2464 23.31136l8.52992 14.79168a17.03936 17.03936 0 0 0 23.31648 6.23104l135.4496-78.19776a17.00864 17.00864 0 0 0 19.23584-12.34944l4.41856-16.48128a17.08032 17.08032 0 0 0-12.07296-20.89984l-9.50784-2.54976a17.05984 17.05984 0 0 0-4.864-1.30048l-150.4768-40.32a17.05984 17.05984 0 0 0-20.90496 12.0576l-4.41344 16.49152a17.05472 17.05472 0 0 0 12.06272 20.89472z m295.95136 174.592H742.4a17.05472 17.05472 0 0 0 17.05984-17.07008v-17.05984a17.06496 17.06496 0 0 0-17.05984-17.08032h-170.65984a17.07008 17.07008 0 0 0-17.0752 17.08032v17.05984a17.05984 17.05984 0 0 0 17.0752 17.07008z" fill="" p-id="5040"></path></svg>

+ 1 - 0
src/icons/svg/app-group-fill.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621563615955" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3442" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M342.4 397.44s136.32-81.28 136.32-83.52c5.76 5.12-140.16-85.12-140.16-85.12L199.36 314.24l143.04 83.2zM351.36 427.2v167.04l144.32-83.84v-167.68c-0.32 0-144.32 82.24-144.32 84.48zM321.6 427.2c0-1.92-144.32-84.16-144.32-84.16v167.68l144.32 83.84v-167.36zM321.6 627.52c0-1.92-144.32-84.16-144.32-84.16v167.68l144.32 83.84v-167.36zM495.36 729.6c0-1.92-144.32-84.16-144.32-84.16v167.68l144.32 83.84V729.6zM684.48 397.76l143.04-83.52-139.52-85.12s-145.92 90.24-140.16 85.12c0 1.92 136.64 83.2 136.64 83.52zM675.52 427.2c0-1.92-144.32-84.16-144.32-84.16v167.68l144.32 83.84v-167.36zM704.96 427.2v167.04l144.32-83.84v-167.68s-144.32 82.24-144.32 84.48zM704.96 627.52v167.04l144.32-83.84v-167.68c0 0.32-144.32 82.56-144.32 84.48zM531.2 729.6v167.04l144.32-83.84v-167.68c0 0.32-144.32 82.24-144.32 84.48zM512.32 128l140.16 84.16-139.2 85.12-140.16-85.12zM512.32 532.8l140.16 84.48-139.2 84.8-140.16-85.12z" p-id="3443"></path></svg>

+ 1 - 0
src/icons/svg/archived.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564359898" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3710" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M105.472 347.875556l-39.082667 46.904888 39.082667-62.549333L480.711111 566.784c16.696889 9.756444 45.824 9.756444 62.549333 0l375.239112-234.552889 39.082666 62.549333-39.082666-46.904888 39.082666-46.904889-39.082666 62.549333-375.239112-234.524444c-16.696889-9.784889-45.852444-9.756444-62.549333 0L105.472 363.52l-39.082667-62.549333 39.082667 46.904889z m-39.082667 46.904888c-31.601778-27.192889-31.658667-66.56 0-93.809777l375.239111-242.346667c40.049778-21.020444 100.579556-21.077333 140.714667 0l375.239111 242.346667c31.601778 27.221333 31.658667 66.56 0 93.809777l-375.239111 242.346667c-40.049778 21.020444-100.579556 21.077333-140.714667 0l-375.239111-242.346667z m-7.822222 70.371556c-23.352889 29.582222-20.906667 65.450667 7.822222 93.809778l375.239111 242.346666c40.163556 20.935111 100.636444 20.878222 140.714667 0l375.239111-242.346666c28.757333-28.416 31.118222-64.256 7.822222-93.809778-3.072 2.673778-5.461333 5.290667-7.822222 7.822222l-375.239111 242.346667c-40.078222 20.963556-100.551111 20.992-140.714667 0l-375.239111-242.346667c-2.360889-2.56-4.750222-5.176889-7.822222-7.822222z m0 164.181333c-23.352889 29.582222-20.906667 65.450667 7.822222 93.809778l375.239111 242.346667c40.163556 20.935111 100.636444 20.878222 140.714667 0l375.239111-242.346667c28.757333-28.387556 31.118222-64.256 7.822222-93.809778-3.072 2.645333-5.461333 5.262222-7.822222 7.793778L582.343111 879.502222c-40.078222 20.935111-100.551111 20.963556-140.714667 0L66.389333 637.127111c-2.360889-2.531556-4.750222-5.148444-7.822222-7.793778z" fill="" p-id="3711"></path></svg>

+ 1 - 0
src/icons/svg/base-info.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564331563" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2785" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M701.639111 56.888889H231.338667c-75.093333 0-136.533333 61.44-136.533334 136.533333v637.155556c0 75.093333 61.44 136.533333 136.533334 136.533333h561.294222a136.931556 136.931556 0 0 0 136.561778-136.533333V284.444444l-227.555556-227.555555zM853.333333 830.577778a60.871111 60.871111 0 0 1-60.700444 60.672H231.338667A60.842667 60.842667 0 0 1 170.666667 830.577778V193.422222a60.871111 60.871111 0 0 1 60.672-60.672h364.117333V329.955556a60.871111 60.871111 0 0 0 60.672 60.672H853.333333V830.577778z m-166.855111-515.783111a15.189333 15.189333 0 0 1-15.160889-15.189334V132.750222l182.016 182.044445h-166.855111z" fill="" p-id="2786"></path><path d="M580.238222 671.288889h-257.877333c-21.219556 0-37.916444 16.696889-37.916445 37.916444s16.696889 37.916444 37.916445 37.916445h257.877333c21.248 0 37.944889-16.696889 37.944889-37.916445s-16.696889-37.916444-37.944889-37.916444zM284.444444 542.350222a37.546667 37.546667 0 0 0 37.916445 37.916445h379.278222c21.219556 0 37.916444-16.696889 37.916445-37.916445s-16.696889-37.916444-37.916445-37.916444H322.360889A37.546667 37.546667 0 0 0 284.444444 542.350222z" fill="" p-id="2787"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/batch-update.svg


+ 1 - 0
src/icons/svg/db.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564290409" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1592" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M512 0C229.23008 0 0 229.23008 0 512s229.23008 512 512 512 512-229.23008 512-512S794.76992 0 512 0z m0 998.4C243.36896 998.4 25.6 780.63104 25.6 512 25.6 243.3664 243.36896 25.6 512 25.6s486.4 217.7664 486.4 486.4c0 268.63104-217.76896 486.4-486.4 486.4z" fill="" p-id="1593"></path><path d="M291.5456 602.97984v0.00512-0.00512z m440.9088 0.00512v-0.00512 0.00512z m-220.46464 36.54912c-121.75104 0-220.44416-16.36864-220.44416-36.54912V693.248c0 20.19072 98.7008 36.55168 220.44416 36.55168 121.75872 0 220.46464-16.36352 220.46464-36.55168v-90.26304c0 20.18304-98.69824 36.54912-220.46464 36.54912z m-220.44416-163.35872z m220.44416 36.544c-121.75104 0-220.44416-16.36352-220.44416-36.53888v90.26048c0 20.18304 98.7008 36.544 220.44416 36.544 121.76384 0 220.46464-16.36352 220.46464-36.544v-90.2656c0 20.18304-98.69824 36.544-220.46464 36.544z m220.46464-36.544z m0-145.42336c-0.01024-20.18048-98.71616-36.55168-220.46464-36.55168-121.7408 0-220.4288 16.37376-220.44416 36.55168v90.26304c0 20.19072 98.7008 36.55168 220.44416 36.55168 121.76384 0 220.46464-16.36352 220.46464-36.55168V330.752z" fill="" p-id="1594"></path></svg>

+ 1 - 0
src/icons/svg/del.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564355834" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3578" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M360.305778 474.112a37.944889 37.944889 0 0 1 75.861333-0.028444V701.610667a37.944889 37.944889 0 0 1-75.861333 0.028444V474.112zM56.888889 280.661333c0-23.04 18.688-41.728 41.784889-41.728h826.652444a41.756444 41.756444 0 0 1 0 83.456H98.673778A41.813333 41.813333 0 0 1 56.888889 280.661333z m530.972444 193.450667a37.944889 37.944889 0 0 1 75.861334-0.028444V701.610667a37.944889 37.944889 0 0 1-75.861334 0.028444V474.112zM663.694222 238.933333V147.968a15.189333 15.189333 0 0 0-15.132444-15.217778h-273.152a15.217778 15.217778 0 0 0-15.160889 15.217778V238.933333H284.444444V117.504A60.586667 60.586667 0 0 1 345.031111 56.888889h333.824C712.391111 56.888889 739.555556 84.053333 739.555556 117.504V238.933333h-75.861334zM208.611556 967.111111a60.672 60.672 0 0 1-60.672-60.643555V322.360889h75.861333v553.671111a15.189333 15.189333 0 0 0 15.132444 15.217778h546.190223c8.362667 0 15.132444-6.826667 15.132444-15.189334V322.360889h75.861333v584.078222a60.672 60.672 0 0 1-60.672 60.643556H208.611556z" fill="" p-id="3579"></path></svg>

+ 1 - 0
src/icons/svg/dev-tools.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621825103391" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3559" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M224 85.333333A138.666667 138.666667 0 0 0 85.333333 224v490.666667A138.666667 138.666667 0 0 0 224 853.333333h309.930667c2.176-10.752 6.144-21.333333 11.946666-31.402666l18.773334-32.597334H224a74.666667 74.666667 0 0 1-74.666667-74.666666V298.666667h640v47.189333a192.341333 192.341333 0 0 1 64-4.138667V224A138.666667 138.666667 0 0 0 714.666667 85.333333H224z" p-id="3560"></path><path d="M819.712 384a149.162667 149.162667 0 0 1 40.96 1.877333l-70.442667 121.941334a49.792 49.792 0 1 0 86.186667 49.792l70.442667-121.984a149.376 149.376 0 0 1-126.933334 245.845333l-129.322666 224a62.208 62.208 0 0 1-107.818667-62.208l129.109333-223.573333A149.333333 149.333333 0 0 1 819.712 384z" p-id="3561"></path><path d="M647.04 579.2a191.829333 191.829333 0 0 1-0.810667-89.813333l-97.621333-97.792a21.333333 21.333333 0 1 0-30.208 30.165333l112.768 112.938667-112.768 112.896a21.333333 21.333333 0 0 0 30.208 30.165333l98.432-98.56zM420.565333 391.594667a21.333333 21.333333 0 0 1 0 30.165333l-113.066666 112.938667 113.066666 112.896a21.333333 21.333333 0 1 1-30.165333 30.165333l-128.128-128a21.333333 21.333333 0 0 1 0-30.165333l128.128-128a21.333333 21.333333 0 0 1 30.165333 0z" p-id="3562"></path></svg>

+ 1 - 0
src/icons/svg/doc.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564285504" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1458" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M616.6528 396.9792h73.3696l-92.2624-96v77.10464c-0.0128 10.42176 8.4736 18.89536 18.8928 18.89536z" fill="" p-id="1459"></path><path d="M616.6528 417.8944c-21.952 0-39.81056-17.856-39.81056-39.81056v-97.216h-224.88064c-20.16 0-36.5568 16.3968-36.5568 36.5568V706.5856c0 20.16 16.3968 36.544 36.5568 36.544h320.07424c20.16 0 36.5568-16.3968 36.5568-36.544V417.8944H616.6528z m-15.4496 149.888h-76.8v76.8c0 7.07072-5.72928 12.8-12.8 12.8s-12.8-5.72928-12.8-12.8v-76.8h-76.8c-7.07072 0-12.8-5.72928-12.8-12.8s5.72928-12.8 12.8-12.8h76.8v-76.8c0-7.07328 5.72928-12.8 12.8-12.8s12.8 5.72672 12.8 12.8v76.8h76.8a12.8 12.8 0 1 1 0 25.6z" fill="" p-id="1460"></path><path d="M512 0C229.23008 0 0 229.23008 0 512s229.23008 512 512 512 512-229.23008 512-512S794.76992 0 512 0z m0 998.4C243.36896 998.4 25.6 780.63104 25.6 512 25.6 243.3664 243.36896 25.6 512 25.6s486.4 217.7664 486.4 486.4c0 268.63104-217.76896 486.4-486.4 486.4z" fill="" p-id="1461"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/err-list.svg


+ 1 - 0
src/icons/svg/http-err.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564407644" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5700" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M905.59488 843.93472h-2.37056V257.55136l-160.24064 202.32192-122.48576-132.55168-201.472 132.04992-208.42496-125.69088L144.64 438.7072v-284.672c0-14.31552-11.80672-26.03008-26.24-26.03008S92.16 139.71456 92.16 154.03008V896h813.44c14.43328 0 26.24-11.70944 26.24-26.0352 0-14.31552-11.81184-26.03008-26.24512-26.03008zM148.14208 509.06112l75.4688-120.15616 196.3008 118.3744 194.19136-127.26272 131.2256 142.01856 117.62688-148.51072v466.7904H148.1472l-0.00512-331.25376z" fill="" p-id="5701"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/info-setting.svg


File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/intel.svg


+ 1 - 0
src/icons/svg/json-push.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564317834" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2389" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M993.735111 148.906667c0.483556-2.986667 1.820444-5.688889 1.820445-8.846223 0-33.536-27.960889-60.728889-62.378667-60.728888-14.165333 0-26.567111 5.376-37.063111 13.169777l-0.597334-1.109333-832.625777 417.28C42.951111 515.584 28.444444 533.788444 28.444444 555.548444c0 22.670222 15.616 41.102222 36.750223 47.388445l252.728889 84.508444 0.455111-1.052444c4.010667 1.336889 8.078222 2.446222 12.572444 2.446222 22.328889 0 40.448-17.664 40.448-39.338667 0-15.217778-9.159111-28.046222-22.129778-34.673777l0.312889-0.711111-192.796444-63.886223 661.048889-331.264-386.104889 422.257778c-11.776 9.728-20.309333 25.457778-20.309334 41.671111 0 4.323556 1.109333 42.609778 1.109334 42.609778v179.655111c0 21.845333 18.090667 39.509333 40.476444 39.509333 22.328889 0 40.419556-17.664 40.419556-39.452444V746.097778l287.175111 96.056889c7.281778 3.896889 15.075556 6.656 23.950222 6.656 27.562667 0 49.891556-21.731556 49.891556-48.668445 0-0.113778-0.312889-0.284444-0.312889-0.398222L995.555556 149.333333l-1.820445-0.426666z m-213.333333 609.763555l-266.780445-89.998222L889.002667 258.872889l-108.600889 499.797333z" fill="" p-id="2390"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/map.svg


File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/next-page.svg


+ 1 - 0
src/icons/svg/operators.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564381213" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4505" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M924.444444 135.111111h-824.888888a56.888889 56.888889 0 0 0-56.888889 56.888889v640a56.888889 56.888889 0 0 0 56.888889 56.888889h824.888888a56.888889 56.888889 0 0 0 56.888889-56.888889V192a56.888889 56.888889 0 0 0-56.888889-56.888889z m-28.444444 625.777778c0 23.552-20.081778 42.666667-44.828444 42.666667H172.828444c-24.775111 0-44.828444-19.114667-44.828444-42.666667V263.111111c0-23.552 20.081778-42.666667 44.828444-42.666667h678.343112c24.775111 0 44.828444 19.114667 44.828444 42.666667v497.777778z" fill="" p-id="4506"></path><path d="M827.562667 330.183111c-12.145778-12.117333-29.411556-14.506667-38.627556-5.290667L485.632 628.224l-118.926222-118.954667c-3.754667-3.726222-8.931556-5.091556-14.421334-5.176889-0.768-0.113778-1.479111-0.056889-2.247111-0.113777-0.739556 0.056889-1.479111 0-2.247111 0.113777-5.518222 0.056889-10.695111 1.422222-14.449778 5.176889l-142.136888 142.136889c-9.216 9.216-6.855111 26.510222 5.262222 38.627556 12.117333 12.117333 29.411556 14.449778 38.627555 5.262222l114.944-114.972444 114.944 114.972444c0.597333 0.597333 1.507556 0.739556 2.190223 1.251556 11.776 9.472 26.766222 11.150222 35.185777 2.759111L832.853333 368.810667c9.187556-9.216 6.826667-26.510222-5.290666-38.627556z" fill="" p-id="4507"></path></svg>

+ 1 - 0
src/icons/svg/project-group.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564340648" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3050" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M837.603556 277.134222a2.275556 2.275556 0 0 0-0.056889-0.483555c-0.199111-0.654222 14.848 44.856889-0.597334-1.934223a88.462222 88.462222 0 0 0-0.995555-2.929777c-14.961778-42.496-39.224889-58.965333-57.002667-62.321778-22.840889-4.352-47.587556 5.774222-47.587555 35.498667 0 4.437333 0.853333 9.272889 2.417777 14.250666l-0.142222-0.028444c0.085333 0.256 0.227556 0.512 0.369778 0.768 1.621333 5.063111 3.953778 10.268444 6.769778 15.445333 13.767111 34.019556 21.333333 71.224889 21.333333 110.165333 0 76.117333-28.956444 145.521778-76.458667 197.802667 102.030222 60.273778 177.208889 184.149333 203.064889 303.672889H981.333333c-21.162667-119.978667-82.915556-248.632889-170.524444-325.632a385.194667 385.194667 0 0 0 42.154667-175.843556 386.275556 386.275556 0 0 0-15.36-108.430222zM429.056 637.013333a250.026667 250.026667 0 1 0-0.028444-500.081777 250.026667 250.026667 0 0 0 0.028444 500.081777z m158.151111-253.354666a161.479111 161.479111 0 1 1-322.958222 0 161.479111 161.479111 0 0 1 322.958222 0z m182.784 503.381333c-30.663111-180.878222-180.707556-340.935111-352.483555-340.935111-191.744 0-343.950222 160.056889-374.840889 340.935111h91.306666c29.354667-130.304 141.624889-250.026667 283.534223-250.026667 122.737778 0 232.248889 119.694222 261.205333 250.026667h91.278222z" fill="" p-id="3051"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/project-manage.svg


+ 1 - 0
src/icons/svg/pwd.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564336846" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2918" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M668.444444 280.462222c0-88.405333-70.428444-159.573333-156.444444-159.573333s-156.444444 71.168-156.444444 159.573333v106.382222h312.888888V280.462222z m172.088889 184.604445H183.466667v438.044444h657.066666v-438.044444zM277.333333 280.462222c0-131.413333 104.817778-237.795556 234.666667-237.795555s234.666667 106.382222 234.666667 237.795555v106.382222h109.511111c34.417778 0 62.577778 28.16 62.577778 62.577778v469.333334c0 34.417778-28.16 62.577778-62.577778 62.577777H167.822222c-34.446222 0-62.577778-28.16-62.577778-62.577777V449.422222c0-34.417778 28.131556-62.577778 62.577778-62.577778h109.511111V280.462222z" fill="" p-id="2919"></path></svg>

+ 1 - 0
src/icons/svg/return-code-doc.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564271986" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1191" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M722.0992 279.1936h146.7392l-184.5248-192v154.2144c-0.0256 20.8384 16.9472 37.7856 37.7856 37.7856z" fill="" p-id="1192"></path><path d="M722.0992 321.024c-43.904 0-79.62112-35.712-79.62112-79.616V46.976H192.7168c-40.32 0-73.1136 32.7936-73.1136 73.1136V898.4064c0 40.32 32.7936 73.088 73.1136 73.088h640.1536c40.32 0 73.1136-32.7936 73.1136-73.088V321.024h-183.8848zM368.128 728.61696a28.9792 28.9792 0 0 1-40.98048 40.98048L242.38592 684.8512a36.47488 36.47488 0 0 1 0-51.56864l84.74624-84.7616a28.98944 28.98944 0 0 1 41.00608 40.98048l-69.56544 69.56032L368.128 728.61696z m197.83168-183.26528l-51.22048 240.95232a28.9792 28.9792 0 0 1-34.37568 22.31808 28.9792 28.9792 0 0 1-22.3232-34.3808l51.2256-240.95232a28.98432 28.98432 0 0 1 56.69376 12.06272z m215.63904 143.33952l-84.74112 84.74624a28.98944 28.98944 0 0 1-40.99072 0.01024 28.97408 28.97408 0 0 1-0.02048-40.99072l69.56544-69.5552-69.53984-69.56032a28.9792 28.9792 0 0 1 40.98048-40.98048l84.75136 84.74624 0.01024 0.00512a36.47488 36.47488 0 0 1-0.01536 51.57888z" fill="" p-id="1193"></path></svg>

+ 1 - 0
src/icons/svg/return-code.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564278647" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1324" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M616.6528 396.9792h73.3696l-92.2624-96v77.10464c-0.0128 10.42176 8.4736 18.89536 18.8928 18.89536z" fill="" p-id="1325"></path><path d="M616.6528 417.8944c-21.952 0-39.81056-17.856-39.81056-39.81056v-97.216h-224.88064c-20.16 0-36.5568 16.3968-36.5568 36.5568V706.5856c0 20.16 16.3968 36.544 36.5568 36.544h320.07424c20.16 0 36.5568-16.3968 36.5568-36.544V417.8944H616.6528z m-176.9856 203.79648a14.4896 14.4896 0 0 1-20.49024 20.49024L376.79872 599.808a18.23744 18.23744 0 0 1 0-25.78432l42.37312-42.3808a14.49472 14.49472 0 0 1 20.50304 20.49024l-34.78528 34.78016 34.7776 34.7776z m98.91584-91.63264l-25.61024 120.47616a14.48704 14.48704 0 0 1-17.18784 11.15904 14.4896 14.4896 0 0 1-11.1616-17.1904l25.6128-120.47616a14.4896 14.4896 0 1 1 28.34688 6.03136z m107.81952 71.66976l-42.37056 42.37312a14.49472 14.49472 0 0 1-20.49536 0.00512 14.48704 14.48704 0 0 1-0.01024-20.49536l34.78272-34.7776-34.77248-34.78016a14.4896 14.4896 0 0 1 20.49024-20.49024l42.37568 42.37312 0.00512 0.00256a18.23488 18.23488 0 0 1-0.00512 25.78944z" fill="" p-id="1326"></path><path d="M512 0C229.23008 0 0 229.23008 0 512s229.23008 512 512 512 512-229.23008 512-512S794.76992 0 512 0z m0 998.4C243.36896 998.4 25.6 780.63104 25.6 512 25.6 243.3664 243.36896 25.6 512 25.6s486.4 217.7664 486.4 486.4c0 268.63104-217.76896 486.4-486.4 486.4z" fill="" p-id="1327"></path></svg>

+ 1 - 0
src/icons/svg/root-addr.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564363678" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3842" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M510.236444 74.666667h-398.222222a56.888889 56.888889 0 0 0-56.888889 56.888889v398.222222a56.888889 56.888889 0 0 0 56.888889 56.888889h398.222222a56.888889 56.888889 0 0 0 56.888889-56.888889v-398.222222a56.888889 56.888889 0 0 0-56.888889-56.888889z m-28.444444 384a42.666667 42.666667 0 0 1-42.666667 42.666666h-256a42.666667 42.666667 0 0 1-42.666666-42.666666v-256a42.666667 42.666667 0 0 1 42.666666-42.666667h256a42.666667 42.666667 0 0 1 42.666667 42.666667v256z" fill="" p-id="3843"></path><path d="M911.985778 295.111111h-540.444445a56.888889 56.888889 0 0 0-56.888889 56.888889v540.444444a56.888889 56.888889 0 0 0 56.888889 56.888889h540.444445a56.888889 56.888889 0 0 0 56.888889-56.888889v-540.444444a56.888889 56.888889 0 0 0-56.888889-56.888889z m-28.444445 526.222222a42.666667 42.666667 0 0 1-42.666666 42.666667h-398.222223a42.666667 42.666667 0 0 1-42.666666-42.666667v-398.222222a42.666667 42.666667 0 0 1 42.666666-42.666667h398.222223a42.666667 42.666667 0 0 1 42.666666 42.666667v398.222222z" fill="" p-id="3844"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/safety.svg


File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/scene.svg


+ 1 - 0
src/icons/svg/signature.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564313494" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2257" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M339.797333 298.695111L277.105778 246.812444 56.888889 512.028444l220.216889 265.386667 62.691555-51.882667-177.095111-213.504 177.095111-213.333333z m-31.345777 254.008889h81.436444V471.324444h-81.436444v81.379556z m407.096888-81.408h-81.436444v81.379556h81.436444v-81.379556z m-244.280888 81.408h81.436444V471.324444h-81.436444v81.379556z m275.626666-306.119111l-62.691555 51.911111 177.095111 213.532444-177.095111 213.276445 62.691555 51.882667L967.111111 512.028444l-220.216889-265.443555z" fill="" p-id="2258"></path></svg>

+ 1 - 0
src/icons/svg/statistics.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564377984" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4372" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M824.860444 516.579556c0.284444 5.717333 0.853333 11.377778 0.853334 17.180444 0 185.6-150.528 336.128-336.156445 336.128S153.429333 719.388444 153.429333 533.788444c0-185.628444 150.499556-336.156444 336.128-336.156444 7.424 0 14.648889 0.625778 21.930667 1.109333V108.231111c-7.310222-0.398222-14.506667-1.109333-21.930667-1.109333-235.605333 0-426.666667 191.032889-426.666666 426.666666 0 235.605333 191.061333 426.666667 426.666666 426.666667 235.633778 0 426.666667-191.061333 426.666667-426.666667 0-5.802667-0.625778-11.434667-0.853333-17.180444h-90.510223z" fill="" p-id="4373"></path><path d="M491.776 63.544889a58.624 58.624 0 0 0-58.652444 58.652444v410.680889c0 32.455111 26.282667 58.652444 58.652444 58.652445h410.680889a58.652444 58.652444 0 0 0 58.652444-58.652445c0-259.185778-210.147556-469.333333-469.333333-469.333333z m31.715556 437.276444V154.311111c177.464889 29.895111 317.297778 169.159111 347.249777 346.538667H523.491556z" fill="" p-id="4374"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/system-tools.svg


+ 1 - 0
src/icons/svg/time-avg.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564405031" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5567" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M900.20352 351.27296c-42.15808-102.19008-124.87168-185.12896-227.1232-227.1232-102.89152-42.26048-219.53024-42.41408-322.36032 0-101.4016 41.84064-184.69376 124.29824-227.1232 227.1232-41.8304 101.4016-41.99424 219.20768 0 321.44896 42.26048 102.90176 125.7216 185.28768 227.1232 227.12832 102.83008 42.4192 219.47392 42.26048 322.36032 0 102.25664-41.99936 185.12896-124.88192 227.1232-227.12832 42.2656-102.89152 42.09664-219.392 0-321.44896z m-42.73152 304.22016c-37.73952 90.3936-112.19968 164.85888-202.59328 202.60864-90.7264 37.88288-194.53952 36.82304-287.55456 0-185.21088-73.33888-278.49728-304.35328-202.5984-489.22112 76.32896-185.9072 306.33472-282.90048 490.14784-202.60352 89.76896 39.2192 164.52608 110.46912 202.59328 202.60352 37.5552 90.86464 37.888 195.89632 0.00512 286.61248z" fill="" p-id="5568"></path><path d="M693.22752 540.84096h-174.0032V281.67168c0-33.88928-53.11488-33.88928-53.11488 0v288.49152c0 10.2912 4.11648 17.83808 12.3392 22.65088a29.70624 29.70624 0 0 0 18.80064 6.65088h195.97824c15.56992 0 29.30688-11.90912 29.30688-29.30176 0-15.58528-13.73184-29.32224-29.30688-29.32224z" fill="" p-id="5569"></path></svg>

+ 1 - 0
src/icons/svg/user-info.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1621564344677" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3182" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M903.111111 938.296889a391.651556 391.651556 0 0 0-233.102222-346.510222 296.448 296.448 0 0 0 139.264-251.875556C809.244444 175.644444 676.266667 42.666667 512 42.666667S214.755556 175.644444 214.755556 339.911111c0 106.382222 55.523556 199.480889 140.003555 251.875556-134.513778 59.448889-229.176889 191.630222-233.870222 346.510222v3.925333c0 21.902222 17.208889 39.111111 39.111111 39.111111S199.111111 964.124444 199.111111 942.222222c3.896889-168.96 142.364444-305.066667 312.888889-305.066666s308.963556 136.106667 312.888889 305.066666c0 21.902222 17.208889 39.111111 39.111111 39.111111s39.111111-17.208889 39.111111-39.111111v-3.925333zM512 558.933333a218.652444 218.652444 0 0 1-219.022222-219.022222c0-121.258667 97.792-219.022222 219.022222-219.022222s218.993778 97.792 218.993778 219.022222c0 121.230222-97.735111 219.022222-218.993778 219.022222z" fill="" p-id="3183"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
src/icons/svg/webhock.svg


+ 22 - 2
src/layout/components/Navbar.vue

@@ -2,7 +2,8 @@
   <div class="navbar">
     <hamburger id="hamburger-container" :is-active="sidebar.opened" class="hamburger-container" @toggleClick="toggleSideBar" />
 
-    <breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
+    <breadcrumb v-if="!topNav" id="breadcrumb-container" class="breadcrumb-container" />
+    <top-nav v-if="topNav" id="topmenu-container" class="breadcrumb-container" />
 
     <div class="right-menu">
       <template v-if="device!=='mobile'">
@@ -33,6 +34,7 @@
 <script>
 import { mapGetters } from 'vuex'
 import Breadcrumb from '@/components/Breadcrumb'
+import TopNav from '@/components/TopNav'
 import Hamburger from '@/components/Hamburger'
 import Screenfull from '@/components/Screenfull'
 import Search from '@/components/HeaderSearch'
@@ -40,6 +42,7 @@ import Search from '@/components/HeaderSearch'
 export default {
   components: {
     Breadcrumb,
+    TopNav,
     Hamburger,
     Screenfull,
     Search
@@ -49,7 +52,24 @@ export default {
       'sidebar',
       'avatar',
       'device'
-    ])
+    ]),
+    setting: {
+      get() {
+        return this.$store.state.settings.showSettings
+      },
+      set(val) {
+        this.$store.dispatch('settings/changeSetting', {
+          key: 'showSettings',
+          value: val
+        })
+      }
+    },
+    topNav: {
+      get() {
+        return this.$store.state.settings.topNav
+      }
+    }
+
   },
   methods: {
     toggleSideBar() {

+ 20 - 0
src/layout/components/Settings/index.vue

@@ -41,6 +41,12 @@
         <div class="setting-drawer-title">
           布局设置
         </div>
+
+        <div class="drawer-item">
+          <span>开启 TopNav</span>
+          <el-switch v-model="topNav" class="drawer-switch" />
+        </div>
+
         <div class="drawer-item">
           <span>开启任务栏</span>
           <el-switch v-model="tagsView" :active-color="activeColor" class="drawer-switch" />
@@ -88,6 +94,20 @@ export default {
         })
       }
     },
+    topNav: {
+      get() {
+        return this.$store.state.settings.topNav
+      },
+      set(val) {
+        this.$store.dispatch('settings/changeSetting', {
+          key: 'topNav',
+          value: val
+        })
+        if (!val) {
+          this.$store.commit('permission/SET_SIDEBAR_ROUTERS', this.$store.state.permission.defaultRoutes)
+        }
+      }
+    },
     tagsView: {
       get() {
         return this.$store.state.settings.tagsView

+ 8 - 2
src/layout/components/Sidebar/index.vue

@@ -12,7 +12,13 @@
         :collapse-transition="true"
         mode="vertical"
       >
-        <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" />
+        <sidebar-item
+          v-for="(route) in sidebarRouters"
+          :key="route.path"
+          :item="route"
+          :base-path="route.path"
+        />
+
       </el-menu>
     </el-scrollbar>
   </div>
@@ -28,7 +34,7 @@ export default {
   components: { SidebarItem, Logo },
   computed: {
     ...mapGetters([
-      'permission_routes',
+      'sidebarRouters',
       'sidebar'
     ]),
     activeMenu() {

+ 0 - 1
src/layout/components/TagsView/ScrollPane.vue

@@ -26,7 +26,6 @@ export default {
       $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
     },
     moveToTarget(currentTag) {
-      console.log(currentTag)
       const $container = this.$refs.scrollContainer.$el
       const $containerWidth = $container.offsetWidth
       const $scrollWrapper = this.scrollWrapper

+ 7 - 2
src/main.js

@@ -15,9 +15,9 @@ import store from './store'
 import router from './router'
 import permission from './directive/permission'
 
-import { getDicts } from '@/api/system/dict/data'
+import { getDicts } from '@/api/admin/dict/data'
 import { getItems, setItems } from '@/api/table'
-import { getConfigKey } from '@/api/system/config'
+import { getConfigKey } from '@/api/admin/sys-config'
 import { parseTime, resetForm, addDateRange, selectDictLabel, download, selectItemsLabel } from '@/utils/costum'
 
 import './icons' // icon
@@ -76,6 +76,11 @@ Vue.use(VueDND)
 
 import 'remixicon/fonts/remixicon.css'
 
+console.info(`欢迎使用go-admin,谢谢您对我们的支持,在使用过程中如果有什么问题,
+请访问https://github.com/go-admin-team/go-admin 或者
+ https://github.com/go-admin-team/go-admin-ui 向我们反馈,
+ 谢谢!`)
+
 // register global utility filters
 Object.keys(filters).forEach(key => {
   Vue.filter(key, filters[key])

+ 1 - 1
src/router/index.js

@@ -108,7 +108,7 @@ export const asyncRoutes = [
 ]
 
 const createRouter = () => new Router({
-  // mode: 'history', // require service support
+  mode: 'history', // require service support
   scrollBehavior: () => ({ y: 0 }),
   routes: constantRoutes
 })

+ 5 - 0
src/settings.js

@@ -7,6 +7,11 @@ module.exports = {
    */
   showSettings: true,
 
+  /**
+   * 是否显示顶部导航
+   */
+  topNav: true,
+
   /**
    * @type {boolean} true | false
    * @description Whether need tagsView

+ 3 - 0
src/store/getters.js

@@ -11,6 +11,9 @@ const getters = {
   roles: state => state.user.roles,
   permisaction: state => state.user.permisaction,
   permission_routes: state => state.permission.routes,
+  topbarRouters: state => state.permission.topbarRouters,
+  defaultRoutes: state => state.permission.defaultRoutes,
+  sidebarRouters: state => state.permission.sidebarRouters,
   errorLogs: state => state.errorLog.logs,
   appInfo: state => state.system.info
 }

+ 26 - 5
src/store/modules/permission.js

@@ -1,5 +1,5 @@
 import { asyncRoutes, constantRoutes } from '@/router'
-import { getRoutes } from '@/api/system/role'
+import { getRoutes } from '@/api/admin/sys-role'
 import Layout from '@/layout'
 // import sysuserindex from '@/views/sysuser/index'
 
@@ -44,7 +44,7 @@ export function generaMenu(routes, data) {
       meta: {
         title: item.title,
         icon: item.icon,
-        noCache: true
+        noCache: item.noCache
       }
     }
     if (item.children) {
@@ -55,8 +55,7 @@ export function generaMenu(routes, data) {
 }
 
 export const loadView = (view) => { // 路由懒加载
-  return (resolve) => require(['@/views' + view], resolve)
-  // return () => import(`@/views${view}`)
+  return (resolve) => require([`@/views${view}`], resolve)
 }
 
 /**
@@ -103,13 +102,30 @@ export function filterAsyncPathRoutes(routes, paths) {
 
 const state = {
   routes: [],
-  addRoutes: []
+  addRoutes: [],
+  defaultRoutes: [],
+  topbarRouters: [],
+  sidebarRouters: []
 }
 
 const mutations = {
   SET_ROUTES: (state, routes) => {
     state.addRoutes = routes
     state.routes = constantRoutes.concat(routes)
+  },
+  SET_DEFAULT_ROUTES: (state, routes) => {
+    state.defaultRoutes = constantRoutes.concat(routes)
+  },
+  SET_TOPBAR_ROUTES: (state, routes) => {
+    // 顶部导航菜单默认添加统计报表栏指向首页
+    // const index = [{
+    //   path: 'dashboard',
+    //   meta: { title: '统计报表', icon: 'dashboard' }
+    // }]
+    state.topbarRouters = routes // .concat(index)
+  },
+  SET_SIDEBAR_ROUTERS: (state, routes) => {
+    state.sidebarRouters = routes
   }
 }
 
@@ -133,6 +149,11 @@ const actions = {
           generaMenu(asyncRoutes, loadMenuData)
           asyncRoutes.push({ path: '*', redirect: '/', hidden: true })
           commit('SET_ROUTES', asyncRoutes)
+          const sidebarRoutes = []
+          generaMenu(sidebarRoutes, loadMenuData)
+          commit('SET_SIDEBAR_ROUTERS', constantRoutes.concat(sidebarRoutes))
+          commit('SET_DEFAULT_ROUTES', sidebarRoutes)
+          commit('SET_TOPBAR_ROUTES', sidebarRoutes)
           resolve(asyncRoutes)
         }
       }).catch(error => {

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

@@ -1,11 +1,12 @@
 import variables from '@/styles/element-variables.scss'
 import defaultSettings from '@/settings'
 
-const { showSettings, tagsView, fixedHeader, sidebarLogo, themeStyle } = defaultSettings
+const { showSettings, topNav, tagsView, fixedHeader, sidebarLogo, themeStyle } = defaultSettings
 
 const state = {
   theme: variables.theme,
   showSettings: showSettings,
+  topNav: topNav,
   tagsView: tagsView,
   fixedHeader: fixedHeader,
   sidebarLogo: sidebarLogo,
@@ -23,7 +24,6 @@ const mutations = {
 
 const actions = {
   changeSetting({ commit }, data) {
-    console.log(data)
     commit('CHANGE_SETTING', data)
   }
 }

+ 0 - 1
src/store/modules/system.js

@@ -1,5 +1,4 @@
 import { getSetting, updateSetting } from '@/api/login'
-// import { logout } from '@/api/user'
 import storage from '@/utils/storage'
 const state = {
   info: storage.get('app_info')

+ 180 - 84
src/styles/admin.scss

@@ -4,193 +4,289 @@
  */
 
  /** 基础通用 **/
-.pt5 {
-	padding-top: 5px;
+ .pt5 {
+  padding-top: 5px;
 }
+
 .pr5 {
-	padding-right: 5px;
+  padding-right: 5px;
 }
+
 .pb5 {
-	padding-bottom: 5px;
+  padding-bottom: 5px;
 }
+
 .mt5 {
-	margin-top: 5px;
+  margin-top: 5px;
 }
+
 .mr5 {
-	margin-right: 5px;
+  margin-right: 5px;
 }
+
 .mb5 {
-	margin-bottom: 5px;
+  margin-bottom: 5px;
 }
+
 .mb8 {
-	margin-bottom: 8px;
+  margin-bottom: 8px;
 }
+
 .ml5 {
-	margin-left: 5px;
+  margin-left: 5px;
 }
+
 .mt10 {
-	margin-top: 10px;
+  margin-top: 10px;
 }
+
 .mr10 {
-	margin-right: 10px;
+  margin-right: 10px;
 }
+
 .mb10 {
-	margin-bottom: 10px;
+  margin-bottom: 10px;
 }
+
 .ml0 {
-	margin-left: 10px;
+  margin-left: 10px;
 }
+
 .mt20 {
-	margin-top: 20px;
+  margin-top: 20px;
 }
+
 .mr20 {
-	margin-right: 20px;
+  margin-right: 20px;
 }
+
 .mb20 {
-	margin-bottom: 20px;
+  margin-bottom: 20px;
 }
+
 .m20 {
-	margin-left: 20px;
+  margin-left: 20px;
 }
 
-.el-dialog {
-	margin-top: 6vh !important;
+.el-dialog:not(.is-fullscreen) {
+  margin-top: 6vh !important;
 }
 
-.el-table .el-table__header-wrapper th {
-	word-break: break-word;
-	background-color: #f8f8f9;
-	color: #515a6e;
-	height: 40px;
-	font-size: 13px;
+.el-table {
+
+  .el-table__header-wrapper,
+  .el-table__fixed-header-wrapper {
+    th {
+      word-break: break-word;
+      background-color: #f8f8f9;
+      color: #515a6e;
+      height: 40px;
+      font-size: 13px;
+    }
+  }
+
+  .el-table__body-wrapper {
+    .el-button [class*="el-icon-"]+span {
+      margin-left: 1px;
+    }
+  }
 }
 
 /** 表单布局 **/
 .form-header {
-    font-size:15px;
-	color:#6379bb;
-	border-bottom:1px solid #ddd;
-	margin:8px 10px 25px 10px;
-	padding-bottom:5px
+  font-size: 15px;
+  color: #6379bb;
+  border-bottom: 1px solid #ddd;
+  margin: 8px 10px 25px 10px;
+  padding-bottom: 5px
 }
 
 /** 表格布局 **/
 .pagination-container {
-	position: relative;
-	height: 25px;
-	margin-bottom: 10px;
-	margin-top: 15px;
-	padding: 10px 20px !important;
+  position: relative;
+  height: 25px;
+  margin-bottom: 10px;
+  margin-top: 15px;
+  padding: 10px 20px !important;
+}
+
+/* tree border */
+.tree-border {
+  margin-top: 5px;
+  border: 1px solid #e5e6e7;
+  background: #FFFFFF none;
+  border-radius: 4px;
 }
 
 .pagination-container .el-pagination {
-	right: 0;
-	position: absolute;
+  right: 0;
+  position: absolute;
 }
 
 .el-table .fixed-width .el-button--mini {
-	color: #409EFF;
-	padding-left: 0;
-	padding-right: 0;
-	width: inherit;
+  padding-left: 0;
+  padding-right: 0;
+  width: inherit;
 }
 
-.el-tree-node__content > .el-checkbox {
-	margin-right: 8px;
+.el-tree-node__content>.el-checkbox {
+  margin-right: 8px;
 }
 
-.list-group-striped > .list-group-item {
-	border-left: 0;
-	border-right: 0;
-	border-radius: 0;
-	padding-left: 0;
-	padding-right: 0;
+.list-group-striped>.list-group-item {
+  border-left: 0;
+  border-right: 0;
+  border-radius: 0;
+  padding-left: 0;
+  padding-right: 0;
 }
 
 .list-group {
-	padding-left: 0px;
-	list-style: none;
+  padding-left: 0px;
+  list-style: none;
 }
 
 .list-group-item {
-	border-bottom: 1px solid #e7eaec;
-	border-top: 1px solid #e7eaec;
-	margin-bottom: -1px;
-	padding: 11px 0px;
-	font-size: 13px;
+  border-bottom: 1px solid #e7eaec;
+  border-top: 1px solid #e7eaec;
+  margin-bottom: -1px;
+  padding: 11px 0px;
+  font-size: 13px;
 }
 
 .pull-right {
-	float: right !important;
+  float: right !important;
 }
 
 .el-card__header {
-	padding: 14px 15px 7px;
-	min-height: 40px;
+  padding: 14px 15px 7px;
+  min-height: 40px;
 }
 
 .el-card__body {
-	padding: 15px 20px 20px 20px;
+  padding: 15px 20px 20px 20px;
 }
 
 .card-box {
-	padding-right: 15px;
-	padding-left: 15px;
-	margin-bottom: 10px;
+  padding-right: 15px;
+  padding-left: 15px;
+  margin-bottom: 10px;
 }
-  
+
+/* button color */
+.el-button--cyan.is-active,
+.el-button--cyan:active {
+  background: #20B2AA;
+  border-color: #20B2AA;
+  color: #FFFFFF;
+}
+
+.el-button--cyan:focus,
+.el-button--cyan:hover {
+  background: #48D1CC;
+  border-color: #48D1CC;
+  color: #FFFFFF;
+}
+
+.el-button--cyan {
+  background-color: #20B2AA;
+  border-color: #20B2AA;
+  color: #FFFFFF;
+}
+
+/* submenu item */
+.el-menu--horizontal>.el-submenu .el-submenu__title {
+  height: 50px !important;
+  line-height: 50px !important;
+}
+
 /* text color */
 .text-navy {
-	color: #1ab394;
+  color: #1ab394;
 }
 
 .text-primary {
-	color: inherit;
+  color: inherit;
 }
 
 .text-success {
-	color: #1c84c6;
+  color: #1c84c6;
 }
 
 .text-info {
-	color: #23c6c8;
+  color: #23c6c8;
 }
 
 .text-warning {
-	color: #f8ac59;
+  color: #f8ac59;
 }
 
 .text-danger {
-	color: #ed5565;
+  color: #ed5565;
 }
 
 .text-muted {
-	color: #888888;
+  color: #888888;
 }
 
 /* image */
 .img-circle {
-	border-radius: 50%;
+  border-radius: 50%;
 }
 
 .img-lg {
-	width: 120px;
-	height: 120px;
+  width: 120px;
+  height: 120px;
 }
 
 .avatar-upload-preview {
-	position: absolute;
-	top: 50%;
-	transform: translate(50%, -50%);
-	width: 180px;
-	height: 180px;
-	border-radius: 50%;
-	box-shadow: 0 0 4px #ccc;
-	overflow: hidden;
+  position: absolute;
+  top: 50%;
+  transform: translate(50%, -50%);
+  width: 200px;
+  height: 200px;
+  border-radius: 50%;
+  box-shadow: 0 0 4px #ccc;
+  overflow: hidden;
 }
 
-.el-tabs__item{
-	font-size: 13px;
+/* 拖拽列样式 */
+.sortable-ghost {
+  opacity: .8;
+  color: #fff !important;
+  background: #42b983 !important;
 }
 
+.top-right-btn {
+  position: relative;
+  float: right;
+}
 
+
+.demo-drawer__content {
+  display: flex;
+  flex-direction: column;
+  height: 100%
+}
+
+.demo-drawer__content form {
+  flex: 1
+}
+
+.demo-drawer__footer {
+  display: flex
+}
+
+.demo-drawer__footer button {
+  flex: 1
+}
+
+.el-drawer__body {
+  padding: 20px
+}
+
+// Drawer 抽屉增加滚动条
+.el-drawer {
+  overflow-y: scroll;
+  
+}

+ 31 - 25
src/styles/sidebar.scss

@@ -8,6 +8,7 @@
   }
 
   .sidebar-container {
+    -webkit-transition: width .28s;
     transition: width 0.28s;
     width: $sideBarWidth !important;
     background-color: $menuBg;
@@ -19,7 +20,8 @@
     left: 0;
     z-index: 1001;
     overflow: hidden;
-    box-shadow: 2px 0 6px rgba(0,21,41,.35);
+    -webkit-box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
+    box-shadow: 2px 0 6px rgba(0, 21, 41, .35);
 
     // reset element-ui css
     .horizontal-collapse-transition {
@@ -64,43 +66,42 @@
       width: 100% !important;
     }
 
+    .el-menu-item,
+    .el-submenu__title {
+      overflow: hidden !important;
+      text-overflow: ellipsis !important;
+      white-space: nowrap !important;
+    }
+
     // menu hover
     .submenu-title-noDropdown,
     .el-submenu__title {
       &:hover {
-        background-color: rgba(0,0,0,0.06)!important;
-        // background-color: transparent!important;
+        background-color: rgba(0, 0, 0, 0.06) !important;
       }
     }
 
-    .is-active>.el-submenu__title {
-      // color: $subMenuActiveText !important;
+    & .theme-dark .is-active>.el-submenu__title {
+      color: $subMenuActiveText !important;
     }
 
     & .nest-menu .el-submenu>.el-submenu__title,
     & .el-submenu .el-menu-item {
       min-width: $sideBarWidth !important;
-      // background-color: $subMenuBg !important;
-      position: relative;
 
-     &:hover {
-        background-color: rgba(0,0,0,0.06)!important;
-        // &::before{
-        //   content: "";
-        //   position: absolute;
-        //   top: 0;
-        //   bottom: 0;
-        //   width: 2px;
-        //   background-color: $menuActiveText;
-        //   height: 100%;
-        //   right: 0;
-        // }
+      &:hover {
+        background-color: rgba(0, 0, 0, 0.06) !important;
       }
     }
-  }
 
-  .el-submenu{
-    background-color: transparent!important;
+    & .theme-dark .nest-menu .el-submenu>.el-submenu__title,
+    & .theme-dark .el-submenu .el-menu-item {
+      background-color: $subMenuBg !important;
+
+      &:hover {
+        background-color: $subMenuHover !important;
+      }
+    }
   }
 
   .hideSidebar {
@@ -135,9 +136,6 @@
           margin-left: 20px;
         }
 
-        .el-submenu__icon-arrow {
-          display: none;
-        }
       }
     }
 
@@ -197,6 +195,14 @@
     }
   }
 
+  .nest-menu .el-submenu>.el-submenu__title,
+  .el-menu-item {
+    &:hover {
+      // you can use $subMenuHover
+      background-color: rgba(0, 0, 0, 0.06) !important;
+    }
+  }
+
   // the scroll bar appears when the subMenu is too long
   >.el-menu--popup {
     max-height: 100vh;

+ 3 - 0
src/utils/costum.js

@@ -4,6 +4,9 @@ export function parseTime(time, pattern) {
   if (arguments.length === 0 || !time) {
     return null
   }
+  if (time.indexOf('01-01-01') > -1) {
+    return '-'
+  }
   const format = pattern || '{y}-{m}-{d} {h}:{i}:{s}'
   let date
   if (typeof time === 'object') {

+ 151 - 90
src/utils/generator/html.js

@@ -1,5 +1,5 @@
 /* eslint-disable max-len */
-import { trigger } from './config'
+import ruleTrigger from './ruleTrigger'
 
 let confGlobal
 let someSpanIsNot24
@@ -34,27 +34,27 @@ export function cssStyle(cssStr) {
   </style>`
 }
 
-function buildFormTemplate(conf, child, type) {
+function buildFormTemplate(scheme, child, type) {
   let labelPosition = ''
-  if (conf.labelPosition !== 'right') {
-    labelPosition = `label-position="${conf.labelPosition}"`
+  if (scheme.labelPosition !== 'right') {
+    labelPosition = `label-position="${scheme.labelPosition}"`
   }
-  const disabled = conf.disabled ? `:disabled="${conf.disabled}"` : ''
-  let str = `<el-form ref="${conf.formRef}" :model="${conf.formModel}" :rules="${conf.formRules}" size="${conf.size}" ${disabled} label-width="${conf.labelWidth}px" ${labelPosition}>
+  const disabled = scheme.disabled ? `:disabled="${scheme.disabled}"` : ''
+  let str = `<el-form ref="${scheme.formRef}" :model="${scheme.formModel}" :rules="${scheme.formRules}" size="${scheme.size}" ${disabled} label-width="${scheme.labelWidth}px" ${labelPosition}>
       ${child}
-      ${buildFromBtns(conf, type)}
+      ${buildFromBtns(scheme, type)}
     </el-form>`
   if (someSpanIsNot24) {
-    str = `<el-row :gutter="${conf.gutter}">
+    str = `<el-row :gutter="${scheme.gutter}">
         ${str}
       </el-row>`
   }
   return str
 }
 
-function buildFromBtns(conf, type) {
+function buildFromBtns(scheme, type) {
   let str = ''
-  if (conf.formBtns && type === 'file') {
+  if (scheme.formBtns && type === 'file') {
     str = `<el-form-item size="large">
           <el-button type="primary" @click="submitForm">提交</el-button>
           <el-button @click="resetForm">重置</el-button>
@@ -69,9 +69,9 @@ function buildFromBtns(conf, type) {
 }
 
 // span不为24的用el-col包裹
-function colWrapper(element, str) {
-  if (someSpanIsNot24 || element.span !== 24) {
-    return `<el-col :span="${element.span}">
+function colWrapper(scheme, str) {
+  if (someSpanIsNot24 || scheme.__config__.span !== 24) {
+    return `<el-col :span="${scheme.__config__.span}">
       ${str}
     </el-col>`
   }
@@ -79,37 +79,59 @@ function colWrapper(element, str) {
 }
 
 const layouts = {
-  colFormItem(element) {
+  colFormItem(scheme) {
+    const config = scheme.__config__
     let labelWidth = ''
-    if (element.labelWidth && element.labelWidth !== confGlobal.labelWidth) {
-      labelWidth = `label-width="${element.labelWidth}px"`
+    let label = `label="${config.label}"`
+    if (config.labelWidth && config.labelWidth !== confGlobal.labelWidth) {
+      labelWidth = `label-width="${config.labelWidth}px"`
     }
-    const required = !trigger[element.tag] && element.required ? 'required' : ''
-    const tagDom = tags[element.tag] ? tags[element.tag](element) : null
-    let str = `<el-form-item ${labelWidth} label="${element.label}" prop="${element.vModel}" ${required}>
+    if (config.showLabel === false) {
+      labelWidth = 'label-width="0"'
+      label = ''
+    }
+    const required = !ruleTrigger[config.tag] && config.required ? 'required' : ''
+    const tagDom = tags[config.tag] ? tags[config.tag](scheme) : null
+    let str = `<el-form-item ${labelWidth} ${label} prop="${scheme.__vModel__}" ${required}>
         ${tagDom}
       </el-form-item>`
-    str = colWrapper(element, str)
+    str = colWrapper(scheme, str)
     return str
   },
-  rowFormItem(element) {
-    const type = element.type === 'default' ? '' : `type="${element.type}"`
-    const justify = element.type === 'default' ? '' : `justify="${element.justify}"`
-    const align = element.type === 'default' ? '' : `align="${element.align}"`
-    const gutter = element.gutter ? `gutter="${element.gutter}"` : ''
-    const children = element.children.map(el => layouts[el.layout](el))
+  rowFormItem(scheme) {
+    const config = scheme.__config__
+    const type = scheme.type === 'default' ? '' : `type="${scheme.type}"`
+    const justify = scheme.type === 'default' ? '' : `justify="${scheme.justify}"`
+    const align = scheme.type === 'default' ? '' : `align="${scheme.align}"`
+    const gutter = scheme.gutter ? `:gutter="${scheme.gutter}"` : ''
+    const children = config.children.map(el => layouts[el.__config__.layout](el))
     let str = `<el-row ${type} ${justify} ${align} ${gutter}>
       ${children.join('\n')}
     </el-row>`
-    str = colWrapper(element, str)
+    str = colWrapper(scheme, str)
     return str
   }
 }
 
 const tags = {
+  'el-button': el => {
+    const {
+      tag, disabled
+    } = attrBuilder(el)
+    const type = el.type ? `type="${el.type}"` : ''
+    const icon = el.icon ? `icon="${el.icon}"` : ''
+    const round = el.round ? 'round' : ''
+    const size = el.size ? `size="${el.size}"` : ''
+    const plain = el.plain ? 'plain' : ''
+    const circle = el.circle ? 'circle' : ''
+    let child = buildElButtonChild(el)
+
+    if (child) child = `\n${child}\n` // 换行
+    return `<${tag} ${type} ${icon} ${round} ${size} ${plain} ${disabled} ${circle}>${child}</${tag}>`
+  },
   'el-input': el => {
     const {
-      disabled, vModel, clearable, placeholder, width
+      tag, disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
     const maxlength = el.maxlength ? `:maxlength="${el.maxlength}"` : ''
     const showWordLimit = el['show-word-limit'] ? 'show-word-limit' : ''
@@ -124,10 +146,12 @@ const tags = {
     let child = buildElInputChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${el.tag}>`
+    return `<${tag} ${vModel} ${type} ${placeholder} ${maxlength} ${showWordLimit} ${readonly} ${disabled} ${clearable} ${prefixIcon} ${suffixIcon} ${showPassword} ${autosize} ${width}>${child}</${tag}>`
   },
   'el-input-number': el => {
-    const { disabled, vModel, placeholder } = attrBuilder(el)
+    const {
+      tag, disabled, vModel, placeholder
+    } = attrBuilder(el)
     const controlsPosition = el['controls-position'] ? `controls-position=${el['controls-position']}` : ''
     const min = el.min ? `:min='${el.min}'` : ''
     const max = el.max ? `:max='${el.max}'` : ''
@@ -135,39 +159,39 @@ const tags = {
     const stepStrictly = el['step-strictly'] ? 'step-strictly' : ''
     const precision = el.precision ? `:precision='${el.precision}'` : ''
 
-    return `<${el.tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${el.tag}>`
+    return `<${tag} ${vModel} ${placeholder} ${step} ${stepStrictly} ${precision} ${controlsPosition} ${min} ${max} ${disabled}></${tag}>`
   },
   'el-select': el => {
     const {
-      disabled, vModel, clearable, placeholder, width
+      tag, disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
     const filterable = el.filterable ? 'filterable' : ''
     const multiple = el.multiple ? 'multiple' : ''
     let child = buildElSelectChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${el.tag}>`
+    return `<${tag} ${vModel} ${placeholder} ${disabled} ${multiple} ${filterable} ${clearable} ${width}>${child}</${tag}>`
   },
   'el-radio-group': el => {
-    const { disabled, vModel } = attrBuilder(el)
+    const { tag, disabled, vModel } = attrBuilder(el)
     const size = `size="${el.size}"`
     let child = buildElRadioGroupChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${size} ${disabled}>${child}</${el.tag}>`
+    return `<${tag} ${vModel} ${size} ${disabled}>${child}</${tag}>`
   },
   'el-checkbox-group': el => {
-    const { disabled, vModel } = attrBuilder(el)
+    const { tag, disabled, vModel } = attrBuilder(el)
     const size = `size="${el.size}"`
     const min = el.min ? `:min="${el.min}"` : ''
     const max = el.max ? `:max="${el.max}"` : ''
     let child = buildElCheckboxGroupChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${el.tag}>`
+    return `<${tag} ${vModel} ${min} ${max} ${size} ${disabled}>${child}</${tag}>`
   },
   'el-switch': el => {
-    const { disabled, vModel } = attrBuilder(el)
+    const { tag, disabled, vModel } = attrBuilder(el)
     const activeText = el['active-text'] ? `active-text="${el['active-text']}"` : ''
     const inactiveText = el['inactive-text'] ? `inactive-text="${el['inactive-text']}"` : ''
     const activeColor = el['active-color'] ? `active-color="${el['active-color']}"` : ''
@@ -175,33 +199,33 @@ const tags = {
     const activeValue = el['active-value'] !== true ? `:active-value='${JSON.stringify(el['active-value'])}'` : ''
     const inactiveValue = el['inactive-value'] !== false ? `:inactive-value='${JSON.stringify(el['inactive-value'])}'` : ''
 
-    return `<${el.tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${el.tag}>`
+    return `<${tag} ${vModel} ${activeText} ${inactiveText} ${activeColor} ${inactiveColor} ${activeValue} ${inactiveValue} ${disabled}></${tag}>`
   },
   'el-cascader': el => {
     const {
-      disabled, vModel, clearable, placeholder, width
+      tag, disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
-    const options = el.options ? `:options="${el.vModel}Options"` : ''
-    const props = el.props ? `:props="${el.vModel}Props"` : ''
+    const options = el.options ? `:options="${el.__vModel__}Options"` : ''
+    const props = el.props ? `:props="${el.__vModel__}Props"` : ''
     const showAllLevels = el['show-all-levels'] ? '' : ':show-all-levels="false"'
     const filterable = el.filterable ? 'filterable' : ''
     const separator = el.separator === '/' ? '' : `separator="${el.separator}"`
 
-    return `<${el.tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${el.tag}>`
+    return `<${tag} ${vModel} ${options} ${props} ${width} ${showAllLevels} ${placeholder} ${separator} ${filterable} ${clearable} ${disabled}></${tag}>`
   },
   'el-slider': el => {
-    const { disabled, vModel } = attrBuilder(el)
+    const { tag, disabled, vModel } = attrBuilder(el)
     const min = el.min ? `:min='${el.min}'` : ''
     const max = el.max ? `:max='${el.max}'` : ''
     const step = el.step ? `:step='${el.step}'` : ''
     const range = el.range ? 'range' : ''
     const showStops = el['show-stops'] ? `:show-stops="${el['show-stops']}"` : ''
 
-    return `<${el.tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${el.tag}>`
+    return `<${tag} ${min} ${max} ${step} ${vModel} ${range} ${showStops} ${disabled}></${tag}>`
   },
   'el-time-picker': el => {
     const {
-      disabled, vModel, clearable, placeholder, width
+      tag, disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
     const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
     const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
@@ -211,11 +235,11 @@ const tags = {
     const valueFormat = el['value-format'] ? `value-format="${el['value-format']}"` : ''
     const pickerOptions = el['picker-options'] ? `:picker-options='${JSON.stringify(el['picker-options'])}'` : ''
 
-    return `<${el.tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${el.tag}>`
+    return `<${tag} ${vModel} ${isRange} ${format} ${valueFormat} ${pickerOptions} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${disabled}></${tag}>`
   },
   'el-date-picker': el => {
     const {
-      disabled, vModel, clearable, placeholder, width
+      tag, disabled, vModel, clearable, placeholder, width
     } = attrBuilder(el)
     const startPlaceholder = el['start-placeholder'] ? `start-placeholder="${el['start-placeholder']}"` : ''
     const endPlaceholder = el['end-placeholder'] ? `end-placeholder="${el['end-placeholder']}"` : ''
@@ -225,47 +249,55 @@ const tags = {
     const type = el.type === 'date' ? '' : `type="${el.type}"`
     const readonly = el.readonly ? 'readonly' : ''
 
-    return `<${el.tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${el.tag}>`
+    return `<${tag} ${type} ${vModel} ${format} ${valueFormat} ${width} ${placeholder} ${startPlaceholder} ${endPlaceholder} ${rangeSeparator} ${clearable} ${readonly} ${disabled}></${tag}>`
   },
   'el-rate': el => {
-    const { disabled, vModel } = attrBuilder(el)
-    // eslint-disable-next-line no-unused-vars
+    const { tag, disabled, vModel } = attrBuilder(el)
     const max = el.max ? `:max='${el.max}'` : ''
     const allowHalf = el['allow-half'] ? 'allow-half' : ''
     const showText = el['show-text'] ? 'show-text' : ''
     const showScore = el['show-score'] ? 'show-score' : ''
 
-    return `<${el.tag} ${vModel} ${allowHalf} ${showText} ${showScore} ${disabled}></${el.tag}>`
+    return `<${tag} ${vModel} ${max} ${allowHalf} ${showText} ${showScore} ${disabled}></${tag}>`
   },
   'el-color-picker': el => {
-    const { disabled, vModel } = attrBuilder(el)
+    const { tag, disabled, vModel } = attrBuilder(el)
     const size = `size="${el.size}"`
     const showAlpha = el['show-alpha'] ? 'show-alpha' : ''
     const colorFormat = el['color-format'] ? `color-format="${el['color-format']}"` : ''
 
-    return `<${el.tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${el.tag}>`
+    return `<${tag} ${vModel} ${size} ${showAlpha} ${colorFormat} ${disabled}></${tag}>`
   },
   'el-upload': el => {
+    const { tag } = el.__config__
     const disabled = el.disabled ? ':disabled=\'true\'' : ''
-    const action = el.action ? `:action="${el.vModel}Action"` : ''
+    const action = el.action ? `:action="${el.__vModel__}Action"` : ''
     const multiple = el.multiple ? 'multiple' : ''
     const listType = el['list-type'] !== 'text' ? `list-type="${el['list-type']}"` : ''
     const accept = el.accept ? `accept="${el.accept}"` : ''
     const name = el.name !== 'file' ? `name="${el.name}"` : ''
     const autoUpload = el['auto-upload'] === false ? ':auto-upload="false"' : ''
-    const beforeUpload = `:before-upload="${el.vModel}BeforeUpload"`
-    const fileList = `:file-list="${el.vModel}fileList"`
-    const ref = `ref="${el.vModel}"`
+    const beforeUpload = `:before-upload="${el.__vModel__}BeforeUpload"`
+    const onSuccess = `:on-success="${el.__vModel__}OnSuccess"`
+    const fileList = `:file-list="${el.__vModel__}fileList"`
+    const ref = `ref="${el.__vModel__}"`
     let child = buildElUploadChild(el)
 
     if (child) child = `\n${child}\n` // 换行
-    return `<${el.tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${listType} ${accept} ${name} ${disabled}>${child}</${el.tag}>`
+    return `<${tag} ${ref} ${fileList} ${action} ${autoUpload} ${multiple} ${beforeUpload} ${onSuccess} ${listType} ${accept} ${name} ${disabled}>${child}</${tag}>`
+  },
+  tinymce: el => {
+    const { tag, vModel, placeholder } = attrBuilder(el)
+    const height = el.height ? `:height="${el.height}"` : ''
+    const branding = el.branding ? `:branding="${el.branding}"` : ''
+    return `<${tag} ${vModel} ${placeholder} ${height} ${branding}></${tag}>`
   }
 }
 
 function attrBuilder(el) {
   return {
-    vModel: `v-model="${confGlobal.formModel}.${el.vModel}"`,
+    tag: el.__config__.tag,
+    vModel: `v-model="${confGlobal.formModel}.${el.__vModel__}"`,
     clearable: el.clearable ? 'clearable' : '',
     placeholder: el.placeholder ? `placeholder="${el.placeholder}"` : '',
     width: el.style && el.style.width ? ':style="{width: \'100%\'}"' : '',
@@ -273,64 +305,93 @@ function attrBuilder(el) {
   }
 }
 
-// el-input innerHTML
-function buildElInputChild(conf) {
+// el-buttin 子级
+function buildElButtonChild(scheme) {
+  const children = []
+  const slot = scheme.__slot__ || {}
+  if (slot.default) {
+    children.push(slot.default)
+  }
+  return children.join('\n')
+}
+
+// el-input 子级
+function buildElInputChild(scheme) {
   const children = []
-  if (conf.prepend) {
-    children.push(`<template slot="prepend">${conf.prepend}</template>`)
+  const slot = scheme.__slot__
+  if (slot && slot.prepend) {
+    children.push(`<template slot="prepend">${slot.prepend}</template>`)
   }
-  if (conf.append) {
-    children.push(`<template slot="append">${conf.append}</template>`)
+  if (slot && slot.append) {
+    children.push(`<template slot="append">${slot.append}</template>`)
   }
   return children.join('\n')
 }
 
-function buildElSelectChild(conf) {
+// el-select 子级
+function buildElSelectChild(scheme) {
   const children = []
-  if (conf.options && conf.options.length) {
-    children.push(`<el-option v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
+  const slot = scheme.__slot__
+  if (slot && slot.options && slot.options.length) {
+    children.push(`<el-option v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.label" :value="item.value" :disabled="item.disabled"></el-option>`)
   }
   return children.join('\n')
 }
 
-function buildElRadioGroupChild(conf) {
+// el-radio-group 子级
+function buildElRadioGroupChild(scheme) {
   const children = []
-  if (conf.options && conf.options.length) {
-    const tag = conf.optionType === 'button' ? 'el-radio-button' : 'el-radio'
-    const border = conf.border ? 'border' : ''
-    children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
+  const slot = scheme.__slot__
+  const config = scheme.__config__
+  if (slot && slot.options && slot.options.length) {
+    const tag = config.optionType === 'button' ? 'el-radio-button' : 'el-radio'
+    const border = config.border ? 'border' : ''
+    children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
   }
   return children.join('\n')
 }
 
-function buildElCheckboxGroupChild(conf) {
+// el-checkbox-group 子级
+function buildElCheckboxGroupChild(scheme) {
   const children = []
-  if (conf.options && conf.options.length) {
-    const tag = conf.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
-    const border = conf.border ? 'border' : ''
-    children.push(`<${tag} v-for="(item, index) in ${conf.vModel}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
+  const slot = scheme.__slot__
+  const config = scheme.__config__
+  if (slot && slot.options && slot.options.length) {
+    const tag = config.optionType === 'button' ? 'el-checkbox-button' : 'el-checkbox'
+    const border = config.border ? 'border' : ''
+    children.push(`<${tag} v-for="(item, index) in ${scheme.__vModel__}Options" :key="index" :label="item.value" :disabled="item.disabled" ${border}>{{item.label}}</${tag}>`)
   }
   return children.join('\n')
 }
 
-function buildElUploadChild(conf) {
+// el-upload 子级
+function buildElUploadChild(scheme) {
   const list = []
-  if (conf['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
-  else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${conf.buttonText}</el-button>`)
-  if (conf.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${conf.fileSize}${conf.sizeUnit} 的${conf.accept}文件</div>`)
+  const config = scheme.__config__
+  if (scheme['list-type'] === 'picture-card') list.push('<i class="el-icon-plus"></i>')
+  else list.push(`<el-button size="small" type="primary" icon="el-icon-upload">${config.buttonText}</el-button>`)
+  if (config.showTip) list.push(`<div slot="tip" class="el-upload__tip">只能上传不超过 ${config.fileSize}${config.sizeUnit} 的${scheme.accept}文件</div>`)
   return list.join('\n')
 }
 
-export function makeUpHtml(conf, type) {
+/**
+ * 组装html代码。【入口函数】
+ * @param {Object} formConfig 整个表单配置
+ * @param {String} type 生成类型,文件或弹窗等
+ */
+export function makeUpHtml(formConfig, type) {
   const htmlList = []
-  confGlobal = conf
-  someSpanIsNot24 = conf.fields.some(item => item.span !== 24)
-  conf.fields.forEach(el => {
-    htmlList.push(layouts[el.layout](el))
+  confGlobal = formConfig
+  // 判断布局是否都沾满了24个栅格,以备后续简化代码结构
+  someSpanIsNot24 = formConfig.fields.some(item => item.__config__.span !== 24)
+  // 遍历渲染每个组件成html
+  formConfig.fields.forEach(el => {
+    htmlList.push(layouts[el.__config__.layout](el))
   })
   const htmlStr = htmlList.join('\n')
-
-  let temp = buildFormTemplate(conf, htmlStr, type)
+  // 将组件代码放进form标签
+  let temp = buildFormTemplate(formConfig, htmlStr, type)
+  // dialog标签包裹代码
   if (type === 'dialog') {
     temp = dialogWrapper(temp)
   }

+ 55 - 11
src/utils/generator/index.js

@@ -1,14 +1,6 @@
-export function makeMap(str, expectsLowerCase) {
-  const map = Object.create(null)
-  const list = str.split(',')
-  for (let i = 0; i < list.length; i++) {
-    map[list[i]] = true
-  }
-  return expectsLowerCase
-    ? val => map[val.toLowerCase()]
-    : val => map[val]
-}
-
+/* eslint-disable no-nested-ternary */
+/* eslint-disable no-restricted-syntax */
+/* eslint-disable guard-for-in */
 /**
  * num 小于0,左缩进num*2个空格; 大于0,右缩进num*2个空格。
  * @param {string} str 代码
@@ -112,3 +104,55 @@ function parse(str) {
 export function jsonClone(obj) {
   return parse(stringify(obj))
 }
+
+// 深拷贝对象
+export function deepClone(obj) {
+  const _toString = Object.prototype.toString
+
+  // null, undefined, non-object, function
+  if (!obj || typeof obj !== 'object') {
+    return obj
+  }
+
+  // DOM Node
+  if (obj.nodeType && 'cloneNode' in obj) {
+    return obj.cloneNode(true)
+  }
+
+  // Date
+  if (_toString.call(obj) === '[object Date]') {
+    return new Date(obj.getTime())
+  }
+
+  // RegExp
+  if (_toString.call(obj) === '[object RegExp]') {
+    const flags = []
+    if (obj.global) { flags.push('g') }
+    if (obj.multiline) { flags.push('m') }
+    if (obj.ignoreCase) { flags.push('i') }
+
+    return new RegExp(obj.source, flags.join(''))
+  }
+
+  const result = Array.isArray(obj) ? [] : obj.constructor ? new obj.constructor() : {}
+
+  for (const key in obj) {
+    result[key] = deepClone(obj[key])
+  }
+
+  return result
+}
+
+const toStr = Function.prototype.call.bind(Object.prototype.toString)
+export function isObjectObject(t) {
+  return toStr(t) === '[object Object]'
+}
+export function isObjectArray(t) {
+  return toStr(t) === '[object Array]'
+}
+export function isObjectNull(t) {
+  return toStr(t) === '[object Null]'
+}
+export function isObjectUnde(t) {
+  return toStr(t) === '[object Undefined]'
+}

+ 4 - 0
src/utils/index.js

@@ -12,6 +12,9 @@ export function parseTime(time, cFormat) {
   if (arguments.length === 0) {
     return null
   }
+  if (time_str.indexOf('01-01-01') > -1) {
+    return '-'
+  }
   const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}'
   let date
   if (typeof time === 'object') {
@@ -40,6 +43,7 @@ export function parseTime(time, cFormat) {
     if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] }
     return value.toString().padStart(2, '0')
   })
+
   return time_str
 }
 

+ 19 - 14
src/views/dict/data.vue → src/views/admin/dict/data.vue

@@ -41,7 +41,7 @@
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysdictdata:add']"
+              v-permisaction="['admin:sysDictData:add']"
               type="primary"
               icon="el-icon-plus"
               size="mini"
@@ -50,7 +50,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysdictdata:edit']"
+              v-permisaction="['admin:sysDictData:edit']"
               type="success"
               icon="el-icon-edit"
               size="mini"
@@ -60,7 +60,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysdictdata:remove']"
+              v-permisaction="['admin:sysDictData:remove']"
               type="danger"
               icon="el-icon-delete"
               size="mini"
@@ -70,7 +70,7 @@
           </el-col>
         </el-row>
 
-        <el-table v-loading="loading" :data="dataList" @selection-change="handleSelectionChange">
+        <el-table v-loading="loading" :data="dataList" border @selection-change="handleSelectionChange">
           <el-table-column type="selection" width="55" align="center" />
           <el-table-column label="字典编码" width="80" align="center" prop="dictCode" />
           <el-table-column label="字典标签" align="center" prop="dictLabel" />
@@ -86,14 +86,14 @@
           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
             <template slot-scope="scope">
               <el-button
-                v-permisaction="['system:sysdictdata:edit']"
+                v-permisaction="['admin:sysDictData:edit']"
                 size="mini"
                 type="text"
                 icon="el-icon-edit"
                 @click="handleUpdate(scope.row)"
               >修改</el-button>
               <el-button
-                v-permisaction="['system:sysdictdata:remove']"
+                v-permisaction="['admin:sysDictData:remove']"
                 size="mini"
                 type="text"
                 icon="el-icon-delete"
@@ -150,11 +150,11 @@
 </template>
 
 <script>
-import { listData, getData, delData, addData, updateData, exportData } from '@/api/system/dict/data'
-import { listType, getType } from '@/api/system/dict/type'
+import { listData, getData, delData, addData, updateData, exportData } from '@/api/admin/dict/data'
+import { listType, getType } from '@/api/admin/dict/type'
 
 export default {
-  name: 'Data',
+  name: 'SysDictDataManage',
   data() {
     return {
       // 遮罩层
@@ -300,7 +300,7 @@ export default {
           if (this.form.dictCode !== undefined) {
             updateData(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('修改成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {
@@ -310,7 +310,7 @@ export default {
           } else {
             addData(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('新增成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {
@@ -330,9 +330,14 @@ export default {
         type: 'warning'
       }).then(function() {
         return delData(dictCodes)
-      }).then(() => {
-        this.getList()
-        this.msgSuccess('删除成功')
+      }).then((response) => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
       }).catch(function() {})
     },
     /** 导出按钮操作 */

+ 11 - 11
src/views/dict/index.vue → src/views/admin/dict/index.vue

@@ -49,7 +49,7 @@
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysdicttype:add']"
+              v-permisaction="['admin:sysDictType:add']"
               type="primary"
               icon="el-icon-plus"
               size="mini"
@@ -58,7 +58,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysdicttype:edit']"
+              v-permisaction="['admin:sysDictType:edit']"
               type="success"
               icon="el-icon-edit"
               size="mini"
@@ -68,7 +68,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysdicttype:remove']"
+              v-permisaction="['admin:sysDictType:remove']"
               type="danger"
               icon="el-icon-delete"
               size="mini"
@@ -78,7 +78,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysdicttype:export']"
+              v-permisaction="['admin:sysDictType:export']"
               type="warning"
               icon="el-icon-download"
               size="mini"
@@ -87,7 +87,7 @@
           </el-col>
         </el-row>
 
-        <el-table v-loading="loading" :data="typeList" @selection-change="handleSelectionChange">
+        <el-table v-loading="loading" :data="typeList" border @selection-change="handleSelectionChange">
           <el-table-column type="selection" width="55" align="center" />
           <el-table-column label="字典编号" width="80" align="center" prop="id" />
           <el-table-column label="字典名称" align="center" prop="dictName" :show-overflow-tooltip="true" />
@@ -108,14 +108,14 @@
           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
             <template slot-scope="scope">
               <el-button
-                v-permisaction="['system:sysdicttype:edit']"
+                v-permisaction="['admin:sysDictType:edit']"
                 size="mini"
                 type="text"
                 icon="el-icon-edit"
                 @click="handleUpdate(scope.row)"
               >修改</el-button>
               <el-button
-                v-permisaction="['system:sysdicttype:remove']"
+                v-permisaction="['admin:sysDictType:remove']"
                 size="mini"
                 type="text"
                 icon="el-icon-delete"
@@ -166,11 +166,11 @@
 </template>
 
 <script>
-import { listType, getType, delType, addType, updateType } from '@/api/system/dict/type'
+import { listType, getType, delType, addType, updateType } from '@/api/admin/dict/type'
 import { formatJson } from '@/utils'
 
 export default {
-  name: 'Dict',
+  name: 'SysDictTypeManage',
   data() {
     return {
       // 遮罩层
@@ -293,7 +293,7 @@ export default {
           if (this.form.id !== undefined) {
             updateType(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('修改成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {
@@ -303,7 +303,7 @@ export default {
           } else {
             addType(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('新增成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {

+ 428 - 0
src/views/admin/sys-api/index.vue

@@ -0,0 +1,428 @@
+
+<template>
+  <BasicLayout>
+    <template #wrapper>
+      <el-card class="box-card">
+        <el-form ref="queryForm" :model="queryParams" :inline="true" label-width="48px">
+          <el-form-item label="标题" prop="title">
+            <el-input
+              v-model="queryParams.title"
+              placeholder="请输入标题"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="地址" prop="path">
+            <el-input
+              v-model="queryParams.path"
+              placeholder="请输入地址"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="类型" prop="action">
+            <el-select
+              v-model="queryParams.action"
+              placeholder="请选择类型"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            >
+              <el-option value="GET">GET</el-option>
+              <el-option value="POST">POST</el-option>
+              <el-option value="PUT">PUT</el-option>
+              <el-option value="DELETE">DELETE</el-option>
+            </el-select>
+          </el-form-item>
+
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+            <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
+          </el-form-item>
+        </el-form>
+
+        <el-table
+          v-loading="loading"
+          :data="sysapiList"
+          border
+          @selection-change="handleSelectionChange"
+          @sort-change="handleSortChang"
+        >
+          <el-table-column
+            label="标题"
+            header-align="center"
+            align="left"
+            prop="title"
+            fixed="left"
+            sortable="custom"
+            width="260px"
+            :show-overflow-tooltip="true"
+          >
+            <template slot-scope="scope">
+              <span v-if="scope.row.type=='SYS' && scope.row.title!=''"><el-tag type="success">{{ '['+scope.row.type +'] '+ scope.row.title }}</el-tag></span>
+              <span v-if="scope.row.type!='SYS' && scope.row.title!=''"><el-tag type="">{{ '['+scope.row.type +'] '+scope.row.title }}</el-tag></span>
+              <span v-if="scope.row.title==''"><el-tag type="danger">暂无</el-tag></span>
+
+            </template>
+          </el-table-column>
+
+          <el-table-column
+            label="Request Info"
+            header-align="center"
+            align="left"
+            prop="path"
+            sortable="custom"
+            :show-overflow-tooltip="true"
+          >
+            <!-- <template slot-scope="scope">
+              <span>{{ "["+scope.row.action +"] "+ scope.row.path }}</span>
+            </template> -->
+            <template slot-scope="scope">
+              <el-popover trigger="hover" placement="top">
+                <p><span v-if="scope.row.type=='SYS' && scope.row.title!=''"><el-tag type="success">{{ '['+scope.row.type +'] '+ scope.row.title }}</el-tag></span>
+                  <span v-if="scope.row.type!='SYS' && scope.row.title!=''"><el-tag type="">{{ '['+scope.row.type +'] '+scope.row.title }}</el-tag></span>
+                  <span v-if="scope.row.title==''"><el-tag type="danger">暂无</el-tag></span>
+                </p>
+                <p>Handle: {{ scope.row.handle }}</p>
+                <p>Method:
+                  <el-tag v-if="scope.row.action=='GET'">{{ scope.row.action }}</el-tag>
+                  <el-tag v-if="scope.row.action=='POST'" type="success">{{ scope.row.action }}</el-tag>
+                  <el-tag v-if="scope.row.action=='PUT'" type="warning">{{ scope.row.action }}</el-tag>
+                  <el-tag v-if="scope.row.action=='DELETE'" type="danger">{{ scope.row.action }}</el-tag>
+                </p>
+                <p>接口类型: {{ scope.row.type }}</p>
+                <div slot="reference" class="name-wrapper">
+                  <el-tag v-if="scope.row.action=='GET'">{{ scope.row.action }}</el-tag>
+                  <el-tag v-if="scope.row.action=='POST'" type="success">{{ scope.row.action }}</el-tag>
+                  <el-tag v-if="scope.row.action=='PUT'" type="warning">{{ scope.row.action }}</el-tag>
+                  <el-tag v-if="scope.row.action=='DELETE'" type="danger">{{ scope.row.action }}</el-tag>
+                  {{ scope.row.path }}
+                </div>
+              </el-popover>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="创建时间"
+            align="center"
+            prop="createdAt"
+            width="155px"
+            sortable="custom"
+          >
+            <template slot-scope="scope">
+              <span>{{ parseTime(scope.row.createdAt) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="操作"
+            align="center"
+            width="80px"
+            class-name="small-padding fixed-width"
+          >
+            <template slot-scope="scope">
+              <el-button
+                v-permisaction="['admin:sysApi:edit']"
+                size="mini"
+                type="text"
+                icon="el-icon-edit"
+                @click="handleUpdate(scope.row)"
+              >修改
+              </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <pagination
+          v-show="total>0"
+          :total="total"
+          :page.sync="queryParams.pageIndex"
+          :limit.sync="queryParams.pageSize"
+          @pagination="getList"
+        />
+
+        <!-- 添加或修改对话框 -->
+        <el-drawer
+          ref="drawer"
+          :title="title"
+          :before-close="handleClose"
+          :visible.sync="open"
+          direction="rtl"
+          custom-class="demo-drawer"
+        >
+          <div class="demo-drawer__content">
+            <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+
+              <el-form-item label="Handle" prop="handle">
+                <el-input
+                  v-model="form.handle"
+                  placeholder="handle"
+                />
+              </el-form-item>
+              <el-form-item label="标题" prop="title">
+                <el-input
+                  v-model="form.title"
+                  placeholder="标题"
+                />
+              </el-form-item>
+              <el-form-item label="类型" prop="type">
+                <el-select
+                  v-model="form.type"
+                  placeholder="请选择类型"
+                  clearable
+                  size="small"
+                  @keyup.enter.native="handleQuery"
+                >
+                  <el-option value="SYS">SYS</el-option>
+                  <el-option value="BUS">BUS</el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item label="Method" prop="action">
+                <el-select
+                  v-model="form.action"
+                  placeholder="请选择方式"
+                  clearable
+                  size="small"
+                  @keyup.enter.native="handleQuery"
+                >
+                  <el-option value="GET">GET</el-option>
+                  <el-option value="POST">POST</el-option>
+                  <el-option value="PUT">PUT</el-option>
+                  <el-option value="DELETE">DELETE</el-option>
+                </el-select>
+              </el-form-item>
+
+              <el-form-item label="地址" prop="path">
+                <el-input
+                  v-model="form.path"
+                  :disabled="isEdit"
+                  placeholder="地址"
+                />
+              </el-form-item>
+
+            </el-form>
+            <div class="demo-drawer__footer">
+              <el-button type="primary" @click="submitForm">确 定</el-button>
+              <el-button @click="cancel">取 消</el-button>
+            </div>
+          </div>
+
+        </el-drawer>
+
+      </el-card>
+    </template>
+  </BasicLayout>
+</template>
+
+<script>
+import { addSysApi, delSysApi, getSysApi, listSysApi, updateSysApi } from '@/api/admin/sys-api'
+
+export default {
+  name: 'SysApiManage',
+  components: {
+  },
+  data() {
+    return {
+      dialog: false,
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 总条数
+      total: 0,
+      // 弹出层标题
+      title: '',
+      // 是否显示弹出层
+      open: false,
+      isEdit: false,
+      // 类型数据字典
+      typeOptions: [],
+      sysapiList: [],
+      dateRange: [],
+
+      // 查询参数
+      queryParams: {
+        pageIndex: 1,
+        pageSize: 10,
+        name: undefined,
+        title: undefined,
+        path: undefined,
+        action: undefined,
+        parentId: undefined
+
+      },
+      // 表单参数
+      form: {
+      },
+      // 表单校验
+      rules: {
+        title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
+        path: [{ required: true, message: '地址不能为空', trigger: 'blur' }],
+        action: [{ required: true, message: '类型不能为空', trigger: 'blur' }],
+        parentId: [{ required: true, message: '按钮id不能为空', trigger: 'blur' }]
+      }
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    handleClose(done) {
+      // if (this.loading) {
+      //   return
+      // }
+      // this.$confirm('需要提交表单吗?')
+      //   .then(_ => {
+      //     this.loading = true
+      //     this.timer = setTimeout(() => {
+      //       done()
+      //       // 动画关闭需要一定的时间
+      //       setTimeout(() => {
+      //         this.loading = false
+      //       }, 400)
+      //     }, 2000)
+      //   })
+      //   .catch(_ => {})
+    },
+    /** 查询参数列表 */
+    getList() {
+      this.loading = true
+      listSysApi(this.queryParams).then(response => {
+        this.sysapiList = response.data.list
+        this.total = response.data.count
+        this.loading = false
+      }
+      )
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        id: undefined,
+        name: undefined,
+        title: undefined,
+        path: undefined,
+        paths: undefined,
+        action: undefined,
+        parentId: undefined,
+        sort: undefined
+      }
+      this.resetForm('form')
+    },
+    parentIdFormat(row) {
+      return this.selectItemsLabel(this.parentIdOptions, row.parentId)
+    },
+    // 文件
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageIndex = 1
+      this.getList()
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.dateRange = []
+      this.resetForm('queryForm')
+      this.handleQuery()
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset()
+      this.open = true
+      this.title = '添加接口管理'
+      this.isEdit = false
+    },
+    /** 排序回调函数 */
+    handleSortChang(column, prop, order) {
+      prop = column.prop
+      order = column.order
+      if (this.order !== '' && this.order !== prop + 'Order') {
+        this.queryParams[this.order] = undefined
+      }
+      if (order === 'descending') {
+        this.queryParams[prop + 'Order'] = 'desc'
+        this.order = prop + 'Order'
+      } else if (order === 'ascending') {
+        this.queryParams[prop + 'Order'] = 'asc'
+        this.order = prop + 'Order'
+      } else {
+        this.queryParams[prop + 'Order'] = undefined
+      }
+      this.getList()
+    },
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset()
+      const id =
+                row.id || this.ids
+      getSysApi(id).then(response => {
+        this.form = response.data
+        this.open = true
+        this.title = '修改接口管理'
+        this.isEdit = true
+      })
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          if (this.form.id !== undefined) {
+            updateSysApi(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess(response.msg)
+                this.open = false
+                this.getList()
+              } else {
+                this.msgError(response.msg)
+              }
+            })
+          } else {
+            addSysApi(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess(response.msg)
+                this.open = false
+                this.getList()
+              } else {
+                this.msgError(response.msg)
+              }
+            })
+          }
+        }
+      })
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      var Ids = (row.id && [row.id]) || this.ids
+
+      this.$confirm('是否确认删除编号为"' + Ids + '"的数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        return delSysApi({ 'ids': Ids })
+      }).then((response) => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
+      }).catch(function() {})
+    }
+  }
+}
+</script>

+ 113 - 32
src/views/config/index.vue → src/views/admin/sys-config/index.vue

@@ -2,28 +2,28 @@
   <BasicLayout>
     <template #wrapper>
       <el-card class="box-card">
-        <el-form ref="queryForm" :model="queryParams" :inline="true" label-width="68px">
-          <el-form-item label="参数名称" prop="configName">
+        <el-form ref="queryForm" :model="queryParams" :inline="true" label-width="48px">
+          <el-form-item label="名称" prop="configName">
             <el-input
               v-model="queryParams.configName"
               placeholder="请输入参数名称"
               clearable
               size="small"
-              style="width: 240px"
+              style="width: 160px"
               @keyup.enter.native="handleQuery"
             />
           </el-form-item>
-          <el-form-item label="参数键名" prop="configKey">
+          <el-form-item label="键名" prop="configKey">
             <el-input
               v-model="queryParams.configKey"
               placeholder="请输入参数键名"
               clearable
               size="small"
-              style="width: 240px"
+              style="width: 160px"
               @keyup.enter.native="handleQuery"
             />
           </el-form-item>
-          <el-form-item label="系统内置" prop="configType">
+          <el-form-item label="内置" prop="configType">
             <el-select v-model="queryParams.configType" placeholder="系统内置" clearable size="small">
               <el-option
                 v-for="dict in typeOptions"
@@ -42,7 +42,7 @@
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysconfig:add']"
+              v-permisaction="['admin:sysConfig:add']"
               type="primary"
               icon="el-icon-plus"
               size="mini"
@@ -51,7 +51,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysconfig:edit']"
+              v-permisaction="['admin:sysConfig:edit']"
               type="success"
               icon="el-icon-edit"
               size="mini"
@@ -61,7 +61,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysconfig:remove']"
+              v-permisaction="['admin:sysConfig:remove']"
               type="danger"
               icon="el-icon-delete"
               size="mini"
@@ -71,7 +71,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysconfig:export']"
+              v-permisaction="['admin:sysConfig:export']"
               type="warning"
               icon="el-icon-download"
               size="mini"
@@ -80,30 +80,85 @@
           </el-col>
         </el-row>
 
-        <el-table v-loading="loading" :data="configList" @selection-change="handleSelectionChange">
+        <el-table
+          v-loading="loading"
+          :data="configList"
+          border
+          @selection-change="handleSelectionChange"
+          @sort-change="handleSortChang"
+        >
           <el-table-column type="selection" width="55" align="center" />
-          <el-table-column label="参数主键" width="80" align="center" prop="id" />
-          <el-table-column label="参数名称" align="center" prop="configName" :show-overflow-tooltip="true" />
-          <el-table-column label="参数键名" align="center" prop="configKey" :show-overflow-tooltip="true" />
-          <el-table-column label="参数键值" align="center" prop="configValue" />
-          <el-table-column label="系统内置" align="center" prop="configType" :formatter="typeFormat" />
-          <el-table-column label="备注" align="center" prop="remark" :show-overflow-tooltip="true" />
-          <el-table-column label="创建时间" align="center" prop="createdAt" width="180">
+          <el-table-column
+            label="编码"
+            sortable="custom"
+            width="75"
+            prop="id"
+          />
+          <el-table-column
+            label="名称"
+            sortable="custom"
+            prop="configName"
+            :show-overflow-tooltip="true"
+          />
+          <el-table-column
+            label="键名"
+            sortable="custom"
+            prop="configKey"
+          >
+            <template slot-scope="scope">
+              <el-popover trigger="hover" placement="top">
+                <p>键值: {{ scope.row.configValue }}</p>
+                <p>UI:  <el-tag v-if="scope.row.isFrontend=='0'">{{ scope.row.isFrontend }}</el-tag>
+                  <el-tag v-if="scope.row.isFrontend=='1'" type="success">{{ scope.row.isFrontend }}</el-tag>
+                  {{ scope.row.isFrontend }}</p>
+                <div slot="reference" class="name-wrapper">
+                  {{ scope.row.configKey }}
+                </div>
+              </el-popover>
+            </template>
+          </el-table-column>
+          <!-- <el-table-column
+            label="参数键值"
+            align="center"
+            prop="configValue"
+          /> -->
+          <el-table-column
+            label="内置"
+            sortable="custom"
+            prop="configType"
+            :formatter="typeFormat"
+            width="80"
+          />
+          <el-table-column
+            label="备注"
+            prop="remark"
+            :show-overflow-tooltip="true"
+          />
+          <el-table-column
+            label="创建时间"
+            sortable="custom"
+            prop="createdAt"
+            width="160"
+          >
             <template slot-scope="scope">
               <span>{{ parseTime(scope.row.createdAt) }}</span>
             </template>
           </el-table-column>
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <el-table-column
+            label="操作"
+            class-name="small-padding fixed-width"
+            width="120"
+          >
             <template slot-scope="scope">
               <el-button
-                v-permisaction="['system:sysconfig:edit']"
+                v-permisaction="['admin:sysConfig:edit']"
                 size="mini"
                 type="text"
                 icon="el-icon-edit"
                 @click="handleUpdate(scope.row)"
               >修改</el-button>
               <el-button
-                v-permisaction="['system:sysconfig:remove']"
+                v-permisaction="['admin:sysConfig:remove']"
                 size="mini"
                 type="text"
                 icon="el-icon-delete"
@@ -163,11 +218,11 @@
 </template>
 
 <script>
-import { listConfig, getConfig, delConfig, addConfig, updateConfig } from '@/api/system/config'
+import { listConfig, getConfig, delConfig, addConfig, updateConfig } from '@/api/admin/sys-config'
 import { formatJson } from '@/utils'
 
 export default {
-  name: 'Config',
+  name: 'SysConfigManage',
   data() {
     return {
       // 遮罩层
@@ -182,6 +237,8 @@ export default {
       total: 0,
       // 参数表格数据
       configList: [],
+      // 排序字段
+      order: 'createdAtOrder',
       // 弹出层标题
       title: '',
       isEdit: false,
@@ -197,7 +254,8 @@ export default {
         pageSize: 10,
         configName: undefined,
         configKey: undefined,
-        configType: undefined
+        configType: undefined,
+        createdAtOrder: 'desc'
       },
       // 表单参数
       form: {},
@@ -258,6 +316,7 @@ export default {
     resetQuery() {
       this.dateRange = []
       this.resetForm('queryForm')
+      this.queryParams['createdAtOrderOrder'] = 'desc'
       this.handleQuery()
     },
     /** 新增按钮操作 */
@@ -267,6 +326,23 @@ export default {
       this.title = '添加参数'
       this.isEdit = false
     },
+    handleSortChang(column, prop, order) {
+      prop = column.prop
+      order = column.order
+      if (this.order !== '' && this.order !== prop + 'Order') {
+        this.queryParams[this.order] = undefined
+      }
+      if (order === 'descending') {
+        this.queryParams[prop + 'Order'] = 'desc'
+        this.order = prop + 'Order'
+      } else if (order === 'ascending') {
+        this.queryParams[prop + 'Order'] = 'asc'
+        this.order = prop + 'Order'
+      } else {
+        this.queryParams[prop + 'Order'] = undefined
+      }
+      this.getList()
+    },
     // 多选框选中数据
     handleSelectionChange(selection) {
       this.ids = selection.map(item => item.id)
@@ -293,7 +369,7 @@ export default {
           if (this.form.id !== undefined) {
             updateConfig(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('修改成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {
@@ -303,7 +379,7 @@ export default {
           } else {
             addConfig(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('新增成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {
@@ -316,16 +392,21 @@ export default {
     },
     /** 删除按钮操作 */
     handleDelete(row) {
-      const data = (row.id && [row.id]) || this.ids
-      this.$confirm('是否确认删除参数编号为"' + data + '"的数据项?', '警告', {
+      const Ids = (row.id && [row.id]) || this.ids
+      this.$confirm('是否确认删除参数编号为"' + Ids + '"的数据项?', '警告', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
         type: 'warning'
       }).then(function() {
-        return delConfig({ 'ids': data })
-      }).then(() => {
-        this.getList()
-        this.msgSuccess('删除成功')
+        return delConfig({ 'ids': Ids })
+      }).then((response) => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
       }).catch(function() {})
     },
     /** 导出按钮操作 */

+ 304 - 0
src/views/admin/sys-config/set.vue

@@ -0,0 +1,304 @@
+<template>
+  <BasicLayout>
+    <template #wrapper>
+      <el-card class="box-card">
+        <el-tabs tab-position="left" style="height: 100%;">
+          <el-tab-pane label="系统内置">
+            <el-form label-width="80px">
+              <div class="test-form">
+                <parser :key="key2" :form-conf="formConf" @submit="sumbitForm2" @bind="bind" />
+              </div>
+            </el-form>
+          </el-tab-pane>
+          <el-tab-pane label="其他">其他</el-tab-pane>
+        </el-tabs>
+
+      </el-card>
+    </template>
+  </BasicLayout>
+</template>
+
+<script>
+import { getSetConfig, updateSetConfig } from '@/api/admin/sys-config'
+import Parser from '@/components/FormGenParser/Parser'
+
+export default {
+  name: 'SysConfigSet',
+  components: {
+    Parser
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 参数表格数据
+      configList: [],
+      key2: +new Date(),
+      formConf: {}
+    }
+  },
+  created() {
+    this.formConf = {
+      'fields': [{
+        '__config__': {
+          'label': '系统名称',
+          'labelWidth': null,
+          'showLabel': true,
+          'changeTag': true,
+          'tag': 'el-input',
+          'tagIcon': 'input',
+          'required': true,
+          'layout': 'colFormItem',
+          'span': 24,
+          'document': 'https://element.eleme.cn/#/zh-CN/component/input',
+          'regList': [],
+          'formId': 103,
+          'renderKey': 1621935615221
+        },
+        '__slot__': {
+          'prepend': '',
+          'append': ''
+        },
+        'placeholder': '请输入系统名称',
+        'style': {
+          'width': '100%'
+        },
+        'clearable': true,
+        'prefix-icon': '',
+        'suffix-icon': '',
+        'maxlength': null,
+        'show-word-limit': false,
+        'readonly': false,
+        'disabled': false,
+        '__vModel__': 'sys_app_name'
+      }, {
+        '__config__': {
+          'label': '系统logo',
+          'tag': 'el-upload',
+          'tagIcon': 'upload',
+          'layout': 'colFormItem',
+          'defaultValue': null,
+          'showLabel': true,
+          'labelWidth': null,
+          'required': false,
+          'span': 24,
+          'showTip': false,
+          'buttonText': '点击上传',
+          'regList': [],
+          'changeTag': true,
+          'fileSize': 2,
+          'sizeUnit': 'MB',
+          'document': 'https://element.eleme.cn/#/zh-CN/component/upload',
+          'formId': 102,
+          'renderKey': 1621935611177
+        },
+        '__slot__': {
+          'list-type': true
+        },
+        'action': 'http://localhost:8000/api/v1/public/uploadFile',
+        'disabled': false,
+        'accept': 'image/*',
+        'name': 'file',
+        'v-if': 'fileList',
+        'auto-upload': true,
+        'on-success': function(response, file, fileList) {
+          this.fileList[0] = { 'url': response.data.full_path }
+          this.$parent.$parent.$parent.$parent.$parent.$parent.$emit('bind', 'sys_app_logo', response.data.full_path)
+          return true
+        },
+        'props': {
+          'file-list': []
+        },
+        'list-type': 'picture-card',
+
+        'multiple': false,
+        '__vModel__': 'sys_app_logo'
+      }, {
+        '__config__': {
+          'label': '初始密码',
+          'labelWidth': null,
+          'showLabel': true,
+          'changeTag': true,
+          'tag': 'el-input',
+          'tagIcon': 'input',
+          'required': true,
+          'layout': 'colFormItem',
+          'span': 24,
+          'document': 'https://element.eleme.cn/#/zh-CN/component/input',
+          'regList': [],
+          'formId': 101,
+          'renderKey': 1621935520984
+        },
+        '__slot__': {
+          'prepend': '',
+          'append': ''
+        },
+        'placeholder': '请输入初始密码',
+        'style': {
+          'width': '100%'
+        },
+        'clearable': true,
+        'prefix-icon': 'el-icon-key',
+        'suffix-icon': '',
+        'maxlength': null,
+        'show-word-limit': false,
+        'readonly': false,
+        'disabled': false,
+        '__vModel__': 'sys_user_initPassword'
+      }, {
+        '__config__': {
+          'label': '皮肤样式',
+          'showLabel': true,
+          'labelWidth': null,
+          'tag': 'el-select',
+          'tagIcon': 'select',
+          'layout': 'colFormItem',
+          'span': 24,
+          'required': true,
+          'regList': [],
+          'changeTag': true,
+          'document': 'https://element.eleme.cn/#/zh-CN/component/select',
+          'formId': 104,
+          'renderKey': 1621935674152
+        },
+        '__slot__': {
+          'options': [{
+            'label': '蓝色',
+            'value': 'skin-blue'
+          }, {
+            'label': '绿色',
+            'value': 'skin-green'
+          }, {
+            'label': '紫色',
+            'value': 'skin-purple'
+          }, {
+            'label': '红色',
+            'value': 'skin-red'
+          }, {
+            'label': '黄色',
+            'value': 'skin-yellow'
+          }]
+        },
+        'placeholder': '请选择皮肤样式',
+        'style': {
+          'width': '100%'
+        },
+        'clearable': true,
+        'disabled': false,
+        'filterable': false,
+        'multiple': false,
+        '__vModel__': 'sys_index_skinName'
+      }, {
+        '__config__': {
+          'label': '侧栏主题',
+          'showLabel': true,
+          'labelWidth': null,
+          'tag': 'el-select',
+          'tagIcon': 'select',
+          'layout': 'colFormItem',
+          'span': 24,
+          'required': true,
+          'regList': [],
+          'changeTag': true,
+          'document': 'https://element.eleme.cn/#/zh-CN/component/select',
+          'formId': 106,
+          'renderKey': 1621935704111
+        },
+        '__slot__': {
+          'options': [{
+            'label': '深色主题',
+            'value': 'theme-dark'
+          }, {
+            'label': '浅色主题',
+            'value': 'theme-light'
+          }]
+        },
+        'placeholder': '请选择侧栏主题',
+        'style': {
+          'width': '100%'
+        },
+        'clearable': true,
+        'disabled': false,
+        'filterable': false,
+        'multiple': false,
+        '__vModel__': 'sys_index_sideTheme'
+      }
+      ],
+      'formRef': 'elForm',
+      'formModel': 'formData',
+      'size': 'medium',
+      'labelPosition': 'right',
+      'labelWidth': 100,
+      'formRules': 'rules',
+      'gutter': 15,
+      'disabled': false,
+      'span': 24,
+      'formBtns': true
+    }
+
+    this.getList()
+  },
+  methods: {
+    /** 查询参数列表 */
+    getList() {
+      this.loading = true
+      getSetConfig().then(response => {
+        this.configList = response.data
+        this.loading = false
+        this.fillFormData(this.formConf, this.configList)
+        // 更新表单
+        this.key2 = +new Date()
+      }
+      )
+    },
+    setUrl(url) {
+      const data = {
+        sys_app_logo: ''
+      }
+      data.sys_app_logo = url
+      // 回填数据
+      this.fillFormData(this.formConf, data)
+      // 更新表单
+      this.key2 = +new Date()
+    },
+    // 参数系统内置字典翻译
+    typeFormat(row, column) {
+      return this.selectDictLabel(this.typeOptions, row.configType)
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageIndex = 1
+      this.getList()
+    },
+    fillFormData(form, data) {
+      form.fields.forEach(item => {
+        const val = data[item.__vModel__]
+        if (val) {
+          item.__config__.defaultValue = val
+        }
+      })
+    },
+    bind(key, data) {
+      this.setUrl(data)
+    },
+    sumbitForm2(data) {
+      var list = []
+      var i = 0
+      for (var key in data) {
+        list[i] = { 'configKey': key, 'configValue': data[key] }
+        i++
+      }
+      updateSetConfig(list).then(response => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
+      }
+      )
+    }
+  }
+}
+</script>

+ 18 - 14
src/views/dept/index.vue → src/views/admin/sys-dept/index.vue

@@ -31,7 +31,7 @@
               @click="handleQuery"
             >搜索</el-button>
             <el-button
-              v-permisaction="['system:sysdept:add']"
+              v-permisaction="['admin:sysDept:add']"
               class="filter-item"
               type="primary"
               icon="el-icon-plus"
@@ -46,6 +46,7 @@
           :data="deptList"
           row-key="deptId"
           default-expand-all
+          border
           :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
         >
           <el-table-column prop="deptName" label="部门名称" />
@@ -66,14 +67,14 @@
           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
             <template slot-scope="scope">
               <el-button
-                v-permisaction="['system:sysdept:edit']"
+                v-permisaction="['admin:sysDept:edit']"
                 size="mini"
                 type="text"
                 icon="el-icon-edit"
                 @click="handleUpdate(scope.row)"
               >修改</el-button>
               <el-button
-                v-permisaction="['system:sysdept:add']"
+                v-permisaction="['admin:sysDept:add']"
                 size="mini"
                 type="text"
                 icon="el-icon-plus"
@@ -81,7 +82,7 @@
               >新增</el-button>
               <el-button
                 v-if="scope.row.p_id != 0"
-                v-permisaction="['system:sysdept:remove']"
+                v-permisaction="['admin:sysDept:remove']"
                 size="mini"
                 type="text"
                 icon="el-icon-delete"
@@ -156,12 +157,12 @@
 </template>
 
 <script>
-import { getDeptList, getDept, delDept, addDept, updateDept } from '@/api/system/dept'
+import { getDeptList, getDept, delDept, addDept, updateDept } from '@/api/admin/sys-dept'
 import Treeselect from '@riophae/vue-treeselect'
 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
 
 export default {
-  name: 'Dept',
+  name: 'SysDeptManage',
   components: { Treeselect },
   data() {
     return {
@@ -311,7 +312,7 @@ export default {
           if (this.form.deptId !== undefined) {
             updateDept(this.form, this.form.deptId).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('修改成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {
@@ -321,7 +322,7 @@ export default {
           } else {
             addDept(this.form).then(response => {
               if (response.code === 200) {
-                this.msgSuccess('新增成功')
+                this.msgSuccess(response.msg)
                 this.open = false
                 this.getList()
               } else {
@@ -345,12 +346,15 @@ export default {
       )
         .then(function() {
           return delDept(row.deptId)
-        })
-        .then(() => {
-          this.getList()
-          this.msgSuccess('删除成功')
-        })
-        .catch(function() {})
+        }).then((response) => {
+          if (response.code === 200) {
+            this.msgSuccess(response.msg)
+            this.open = false
+            this.getList()
+          } else {
+            this.msgError(response.msg)
+          }
+        }).catch(function() {})
     }
   }
 }

+ 13 - 9
src/views/loginlog/index.vue → src/views/admin/sys-login-log/index.vue

@@ -44,7 +44,7 @@
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['admin:sysloginlog:remove']"
+              v-permisaction="['admin:sysLoginLog:remove']"
               type="danger"
               icon="el-icon-delete"
               size="mini"
@@ -123,7 +123,7 @@
           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
             <template slot-scope="scope">
               <el-button
-                v-permisaction="['admin:sysloginlog:remove']"
+                v-permisaction="['admin:sysLoginLog:remove']"
                 size="mini"
                 type="text"
                 icon="el-icon-delete"
@@ -147,10 +147,10 @@
 </template>
 
 <script>
-import { delSysLoginlog, getSysLoginlog, listSysLoginlog } from '@/api/system/sys-login-log'
+import { delSysLoginlog, getSysLoginlog, listSysLoginlog } from '@/api/admin/sys-login-log'
 
 export default {
-  name: 'SysLoginlog',
+  name: 'SysLoginLogManage',
   components: {
   },
   data() {
@@ -291,11 +291,15 @@ export default {
         type: 'warning'
       }).then(function() {
         return delSysLoginlog(Ids)
-      }).then(() => {
-        this.getList()
-        this.msgSuccess('删除成功')
-      }).catch(function() {
-      })
+      }).then((response) => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
+      }).catch(function() {})
     }
   }
 }

+ 628 - 0
src/views/admin/sys-menu/index.vue

@@ -0,0 +1,628 @@
+<template>
+  <BasicLayout>
+    <template #wrapper>
+      <el-card class="box-card">
+        <el-form :inline="true">
+          <el-form-item label="菜单名称">
+            <el-input
+              v-model="queryParams.title"
+              placeholder="请输入菜单名称"
+              clearable
+              size="small"
+              @keyup.enter.native="handleQuery"
+            />
+          </el-form-item>
+          <el-form-item label="状态">
+            <el-select v-model="queryParams.visible" placeholder="菜单状态" clearable size="small">
+              <el-option
+                v-for="dict in visibleOptions"
+                :key="dict.dictValue"
+                :label="dict.dictLabel"
+                :value="dict.dictValue"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
+            <el-button
+              v-permisaction="['admin:sysMenu:add']"
+              type="primary"
+              icon="el-icon-plus"
+              size="mini"
+              @click="handleAdd"
+            >新增</el-button>
+          </el-form-item>
+        </el-form>
+
+        <el-table
+          v-loading="loading"
+          :data="menuList"
+          border
+          row-key="menuId"
+          :tree-props="{children: 'children', hasChildren: 'hasChildren'}"
+        >
+          <el-table-column prop="title" label="菜单名称" :show-overflow-tooltip="true" width="180px" />
+          <el-table-column prop="icon" label="图标" align="center" width="100px">
+            <template slot-scope="scope">
+              <svg-icon :icon-class="scope.row.icon" />
+            </template>
+          </el-table-column>
+          <el-table-column prop="sort" label="排序" width="60px" />
+          <el-table-column prop="permission" label="权限标识" :show-overflow-tooltip="true">
+            <!-- <template slot-scope="scope">
+              <span v-if="scope.row.permission==''">-</span>
+              <span v-else>{{ scope.row.permission }}</span>
+           > -->
+            <template slot-scope="scope">
+              <el-popover v-if="scope.row.sysApi.length>0" trigger="hover" placement="top">
+                <el-table
+                  :data="scope.row.sysApi"
+                  border
+                  style="width: 100%"
+                >
+                  <el-table-column
+                    prop="title"
+                    label="title"
+                    width="260px"
+                  >
+                    <template slot-scope="scope">
+                      <span v-if="scope.row.type=='SYS' && scope.row.title!=''"><el-tag type="success">{{ '['+scope.row.type +'] '+ scope.row.title }}</el-tag></span>
+                      <span v-if="scope.row.type!='SYS' && scope.row.title!=''"><el-tag type="">{{ '['+scope.row.type +'] '+scope.row.title }}</el-tag></span>
+                      <span v-if="scope.row.title==''"><el-tag type="danger">暂无</el-tag></span>
+
+                    </template>
+                  </el-table-column>
+                  <el-table-column
+                    prop="path"
+                    label="path"
+                    width="270px"
+                  >
+                    <template slot-scope="scope">
+                      <el-tag v-if="scope.row.action=='GET'">{{ scope.row.action }}</el-tag>
+                      <el-tag v-if="scope.row.action=='POST'" type="success">{{ scope.row.action }}</el-tag>
+                      <el-tag v-if="scope.row.action=='PUT'" type="warning">{{ scope.row.action }}</el-tag>
+                      <el-tag v-if="scope.row.action=='DELETE'" type="danger">{{ scope.row.action }}</el-tag>
+                      {{ scope.row.path }}
+                    </template>
+                  </el-table-column>
+
+                </el-table>
+                <div slot="reference" class="name-wrapper">
+                  <span v-if="scope.row.permission==''">-</span>
+                  <span v-else>{{ scope.row.permission }}</span>
+                </div>
+              </el-popover>
+              <span v-else>
+                <span v-if="scope.row.permission==''">-</span>
+                <span v-else>{{ scope.row.permission }}</span>
+              </span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="path" label="组建路径" :show-overflow-tooltip="true">
+            <template slot-scope="scope">
+              <span v-if="scope.row.menuType=='A'">{{ scope.row.path }}</span>
+              <span v-else>{{ scope.row.component }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="visible" label="可见" :formatter="visibleFormat" width="80">
+            <template slot-scope="scope">
+              <el-tag
+                :type="scope.row.visible === '1' ? 'danger' : 'success'"
+                disable-transitions
+              >{{ visibleFormat(scope.row) }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="创建时间" align="center" prop="createdAt" width="180">
+            <template slot-scope="scope">
+              <span>{{ parseTime(scope.row.createdAt) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180">
+            <template slot-scope="scope">
+              <el-button
+                v-permisaction="['admin:sysMenu:edit']"
+                size="mini"
+                type="text"
+                icon="el-icon-edit"
+                @click="handleUpdate(scope.row)"
+              >修改</el-button>
+              <el-button
+                v-permisaction="['admin:sysMenu:add']"
+                size="mini"
+                type="text"
+                icon="el-icon-plus"
+                @click="handleAdd(scope.row)"
+              >新增</el-button>
+              <el-button
+                v-permisaction="['admin:sysMenu:remove']"
+                size="mini"
+                type="text"
+                icon="el-icon-delete"
+                @click="handleDelete(scope.row)"
+              >删除</el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 添加或修改菜单对话框 -->
+        <el-drawer
+          ref="drawer"
+          :title="title"
+          :before-close="handleClose"
+          :visible.sync="open"
+          direction="rtl"
+          custom-class="demo-drawer"
+          size="830px"
+        >
+          <div class="demo-drawer__content">
+            <el-form ref="form" :model="form" :rules="rules" label-position="top" label-width="106px">
+              <el-row>
+                <el-col :span="24">
+                  <el-form-item prop="parentId">
+                    <span slot="label">
+                      上级菜单
+                      <el-tooltip content="指当前菜单停靠的菜单归属" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <treeselect
+                      v-model="form.parentId"
+                      :options="menuOptions"
+                      :normalizer="normalizer"
+                      :show-count="true"
+                      placeholder="选择上级菜单"
+                    />
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item prop="title">
+                    <span slot="label">
+                      菜单标题
+                      <el-tooltip content="菜单位置显示的说明信息" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-input v-model="form.title" placeholder="请输入菜单标题" />
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item prop="sort">
+                    <span slot="label">
+                      显示排序
+                      <el-tooltip content="根据序号升序排列" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-input-number v-model="form.sort" controls-position="right" :min="0" />
+                  </el-form-item>
+                </el-col>
+
+                <el-col :span="24">
+                  <el-form-item prop="menuType">
+                    <span slot="label">
+                      菜单类型
+                      <el-tooltip content="包含目录:以及菜单或者菜单组,菜单:具体对应某一个页面,按钮:功能才做按钮;" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-radio-group v-model="form.menuType">
+                      <el-radio label="M">目录</el-radio>
+                      <el-radio label="C">菜单</el-radio>
+                      <el-radio label="F">按钮</el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                </el-col>
+                <el-col :span="24">
+                  <el-form-item 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 v-if="form.menuType == 'M' || form.menuType == 'C'" prop="menuName">
+                    <span slot="label">
+                      路由名称
+                      <el-tooltip content="需要和页面name保持一致,对应页面即可选择缓存" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-input v-model="form.menuName" placeholder="请输入路由名称" />
+                  </el-form-item>
+                </el-col>
+
+                <el-col v-if="form.menuType == 'M' || form.menuType == 'C'" :span="12">
+                  <el-form-item prop="component">
+                    <span slot="label">
+                      组件路径
+                      <el-tooltip content="菜单对应的具体vue页面文件路径views的下级路径/admin/sys-api/index;目录类型:填写Layout,如何有二级目录请参照日志目录填写;" placement="top">
+                        <i class="el-icon-question" />
+                      </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 == 'M' || form.menuType == 'C'">
+                    <span slot="label">
+                      是否外链
+                      <el-tooltip content="可以通过iframe打开指定地址" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-radio-group v-model="form.isFrame">
+                      <el-radio label="0">是</el-radio>
+                      <el-radio label="1">否</el-radio>
+                    </el-radio-group>
+                  </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                  <el-form-item v-if="form.menuType != 'F'" prop="path">
+                    <span slot="label">
+                      路由地址
+                      <el-tooltip content="访问此页面自定义的url地址,建议/开头书写,例如 /app-name/menu-name" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-input v-model="form.path" placeholder="请输入路由地址" />
+                  </el-form-item>
+                </el-col>
+
+                <el-col :span="12">
+                  <el-form-item v-if="form.menuType == 'F' || form.menuType == 'C'">
+                    <span slot="label">
+                      权限标识
+                      <el-tooltip content="前端权限控制按钮是否显示" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-input v-model="form.permission" placeholder="请权限标识" maxlength="50" />
+                  </el-form-item>
+                </el-col>
+                <el-col :span="12">
+                  <el-form-item v-if="form.menuType != 'F'">
+                    <span slot="label">
+                      菜单状态
+                      <el-tooltip content="需要显示在菜单列表的菜单设置为显示,否则设置为隐藏" placement="top">
+                        <i class="el-icon-question" />
+                      </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="24">
+                  <el-form-item v-if="form.menuType == 'F' || form.menuType == 'C'">
+                    <span slot="label">
+                      api权限
+                      <el-tooltip content="配置在这个才做上需要使用到的接口,否则在设置用户角色时,接口将无权访问。" placement="top">
+                        <i class="el-icon-question" />
+                      </el-tooltip>
+                    </span>
+                    <el-transfer
+                      v-model="form.apis"
+                      style="text-align: left; display: inline-block"
+                      filterable
+                      :props="{
+                        key: 'id',
+                        label: 'title'
+                      }"
+                      :titles="['未授权', '已授权']"
+                      :button-texts="['收回', '授权 ']"
+                      :format="{
+                        noChecked: '${total}',
+                        hasChecked: '${checked}/${total}'
+                      }"
+                      class="panel"
+                      :data="sysapiList"
+                      @change="handleChange"
+                    >
+                      <span slot-scope="{ option }">{{ option.title }}</span>
+                    </el-transfer>
+                  </el-form-item>
+                </el-col>
+              </el-row>
+            </el-form>
+            <div class="demo-drawer__footer">
+              <el-button type="primary" @click="submitForm">确 定</el-button>
+              <el-button @click="cancel">取 消</el-button>
+            </div>
+          </div>
+
+        </el-drawer>
+      </el-card>
+    </template>
+  </BasicLayout>
+</template>
+
+<script>
+import { listMenu, getMenu, delMenu, addMenu, updateMenu } from '@/api/admin/sys-menu'
+import { listSysApi } from '@/api/admin/sys-api'
+
+import Treeselect from '@riophae/vue-treeselect'
+import '@riophae/vue-treeselect/dist/vue-treeselect.css'
+import IconSelect from '@/components/IconSelect'
+
+export default {
+  name: 'SysMenuManage',
+  components: { Treeselect, IconSelect },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 菜单表格树数据
+      menuList: [],
+      sysapiList: [],
+      // 菜单树选项
+      menuOptions: [],
+      // 弹出层标题
+      title: '',
+      // 是否显示弹出层
+      open: false,
+      // 菜单状态数据字典
+      visibleOptions: [],
+      // 查询参数
+      queryParams: {
+        title: undefined,
+        visible: undefined
+      },
+      // 表单参数
+      form: {
+        apis: [],
+        sysApi: []
+      },
+      // 表单校验
+      rules: {
+        title: [{ required: true, message: '菜单标题不能为空', trigger: 'blur' }],
+        sort: [{ required: true, message: '菜单顺序不能为空', trigger: 'blur' }]
+      }
+    }
+  },
+  created() {
+    this.getList()
+
+    this.getApiList()
+    this.getDicts('sys_show_hide').then(response => {
+      this.visibleOptions = response.data
+    })
+  },
+  methods: {
+    handleChange(value, direction, movedKeys) {
+      console.log(value, direction, movedKeys)
+      const list = this.form.sysApi
+      this.form.apis = value
+      if (direction === 'right') {
+        for (let x = 0; x < movedKeys.length; x++) {
+          for (let index = 0; index < this.sysapiList.length; index++) {
+            const element = this.sysapiList[index]
+            if (element.id === movedKeys[x]) {
+              list.push(element)
+              break
+            }
+          }
+        }
+        this.form.sysApi = list
+      } else if (direction === 'left') {
+        const l = []
+        for (let index = 0; index < movedKeys.length; index++) {
+          const element = movedKeys[index]
+          for (let x = 0; x < list.length; x++) {
+            const e = list[x]
+            if (element !== e.id) {
+              l.push()
+              break
+            }
+          }
+        }
+        this.form.sysApi = l
+      }
+      // this.setApis(this.form.SysApi)
+      console.log(this.form.sysApi)
+    },
+    getApiList() {
+      this.loading = true
+      listSysApi({ 'pageSize': 10000, 'type': 'BUS' }).then(response => {
+        this.sysapiList = response.data.list
+        this.loading = false
+      }
+      )
+    },
+    handleClose(done) {
+      // if (this.loading) {
+      //   return
+      // }
+      // this.$confirm('需要提交表单吗?')
+      //   .then(_ => {
+      //     this.loading = true
+      //     this.timer = setTimeout(() => {
+      //       done()
+      //       // 动画关闭需要一定的时间
+      //       setTimeout(() => {
+      //         this.loading = false
+      //       }, 400)
+      //     }, 1000)
+      //   })
+      //   .catch(_ => {})
+    },
+    // 选择图标
+    selected(name) {
+      this.form.icon = name
+    },
+    /** 查询菜单列表 */
+    getList() {
+      this.loading = true
+      listMenu(this.queryParams).then(response => {
+        this.menuList = response.data
+        this.loading = false
+      })
+    },
+    /** 转换菜单数据结构 */
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children
+      }
+      return {
+        id: node.menuId,
+        label: node.title,
+        children: node.children
+      }
+    },
+    /** 查询菜单下拉树结构 */
+    getTreeselect() {
+      listMenu().then(response => {
+        this.menuOptions = []
+        const menu = { menuId: 0, title: '主类目', children: [] }
+        menu.children = response.data
+        this.menuOptions.push(menu)
+      })
+    },
+    // 菜单显示状态字典翻译
+    visibleFormat(row) {
+      if (row.menuType === 'F') {
+        return '-- --'
+      }
+      return this.selectDictLabel(this.visibleOptions, row.visible)
+    },
+    // 取消按钮
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+    // 表单重置
+    reset() {
+      this.form = {
+        menuId: undefined,
+        parentId: 0,
+        menuName: undefined,
+        icon: undefined,
+        menuType: 'M',
+        apis: [],
+        sort: 0,
+        action: this.form.menuType === 'A' ? this.form.action : '',
+        isFrame: '1',
+        visible: '0'
+      }
+      this.resetForm('form')
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.getList()
+    },
+    /** 新增按钮操作 */
+    handleAdd(row) {
+      this.reset()
+      this.getTreeselect()
+      if (row != null) {
+        this.form.parentId = row.menuId
+      }
+      this.open = true
+      this.title = '添加菜单'
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset()
+      this.getTreeselect()
+      getMenu(row.menuId).then(response => {
+        this.form = response.data
+        this.open = true
+        this.title = '修改菜单'
+      })
+    },
+    setApis(apiArray) {
+      var l = []
+      for (var index = 0; index < apiArray.length; index++) {
+        const element = apiArray[index]
+        l.push(element.id)
+      }
+      this.form.apis = l
+    },
+    /** 提交按钮 */
+    submitForm: function() {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          if (this.form.menuId !== undefined) {
+            updateMenu(this.form, this.form.menuId).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess(response.msg)
+                this.open = false
+                this.getList()
+              } else {
+                this.msgError(response.msg)
+              }
+            })
+          } else {
+            addMenu(this.form).then(response => {
+              if (response.code === 200) {
+                this.msgSuccess(response.msg)
+                this.open = false
+                this.getList()
+              } else {
+                this.msgError(response.msg)
+              }
+            })
+          }
+        }
+      })
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      this.$confirm('是否确认删除名称为"' + row.title + '"的数据项?', '警告', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(function() {
+        var Ids = (row.menuId && [row.menuId]) || this.ids
+        return delMenu({ 'ids': Ids })
+      }).then((response) => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
+      }).catch(function() {})
+    }
+  }
+}
+</script>
+<style lang="css">
+.panel .el-transfer__buttons{
+  width: 150px;
+}
+.panel .el-transfer__buttons .el-button + .el-button{
+  margin-left:0;
+}
+.panel .el-transfer-panel{
+  width: 310px;
+}
+
+.el-col {
+padding: 0 5px;
+}
+.el-drawer__header{
+margin-bottom: 0;
+}
+</style>

+ 96 - 65
src/views/operlog/index.vue → src/views/admin/sys-oper-log/index.vue

@@ -3,39 +3,13 @@
     <template #wrapper>
       <el-card class="box-card">
         <el-form ref="queryForm" :model="queryParams" :inline="true" label-width="68px">
-          <el-form-item label="系统模块" prop="title">
-            <el-input
-              v-model="queryParams.title"
-              placeholder="请输入系统模块"
-              clearable
-              style="width: 240px;"
-              size="small"
-              @keyup.enter.native="handleQuery"
-            />
-          </el-form-item>
-          <el-form-item label="类型" prop="businessType">
-            <el-select
-              v-model="queryParams.businessType"
-              placeholder="操作类型"
-              clearable
-              size="small"
-              style="width: 240px"
-            >
-              <el-option
-                v-for="dict in typeOptions"
-                :key="dict.dictValue"
-                :label="dict.dictLabel"
-                :value="dict.dictValue"
-              />
-            </el-select>
-          </el-form-item>
           <el-form-item label="状态" prop="status">
             <el-select
               v-model="queryParams.status"
               placeholder="操作状态"
               clearable
               size="small"
-              style="width: 240px"
+              style="width: 160px"
             >
               <el-option
                 v-for="dict in statusOptions"
@@ -45,6 +19,19 @@
               />
             </el-select>
           </el-form-item>
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="dateRange"
+              size="small"
+              type="datetimerange"
+              :picker-options="pickerOptions"
+              range-separator="至"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              align="right"
+              value-format="yyyy-MM-dd HH:mm:ss"
+            />
+          </el-form-item>
           <el-form-item>
             <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
             <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
@@ -54,7 +41,7 @@
         <el-row :gutter="10" class="mb8">
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysoperlog:remove']"
+              v-permisaction="['admin:sysOperLog:remove']"
               type="danger"
               icon="el-icon-delete"
               size="mini"
@@ -64,7 +51,7 @@
           </el-col>
           <el-col :span="1.5">
             <el-button
-              v-permisaction="['system:sysoperlog:export']"
+              v-permisaction="['admin:sysOperLog:export']"
               type="warning"
               icon="el-icon-download"
               size="mini"
@@ -73,24 +60,67 @@
           </el-col>
         </el-row>
 
-        <el-table v-loading="loading" :data="list" @selection-change="handleSelectionChange">
+        <el-table v-loading="loading" :data="list" border @selection-change="handleSelectionChange">
           <el-table-column type="selection" width="55" align="center" />
-          <el-table-column label="日志编号" width="80" align="center" prop="id" />
-          <el-table-column label="请求方式" width="80" align="center" prop="requestMethod" :show-overflow-tooltip="true" />
-          <el-table-column label="请求地址" align="center" prop="operUrl" :show-overflow-tooltip="true" />
-          <el-table-column label="操作人员" align="center" prop="operName" :show-overflow-tooltip="true" />
-          <el-table-column label="主机" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" />
-          <el-table-column label="操作地点" align="center" prop="operLocation" :show-overflow-tooltip="true" />
-          <el-table-column label="操作状态" align="center" prop="status" :formatter="statusFormat" :show-overflow-tooltip="true" />
-          <el-table-column label="操作日期" align="center" prop="operTime" width="180">
+          <el-table-column label="编号" width="70" prop="id" />
+          <el-table-column
+            label="Request info"
+            prop="operUrl"
+            :show-overflow-tooltip="true"
+          >
+            <template slot-scope="scope">
+              <el-popover trigger="hover" placement="top">
+
+                <p>Request:
+                  <el-tag v-if="scope.row.requestMethod=='GET'">{{ scope.row.requestMethod }}</el-tag>
+                  <el-tag v-if="scope.row.requestMethod=='POST'" type="success">{{ scope.row.requestMethod }}</el-tag>
+                  <el-tag v-if="scope.row.requestMethod=='PUT'" type="warning">{{ scope.row.requestMethod }}</el-tag>
+                  <el-tag v-if="scope.row.requestMethod=='DELETE'" type="danger">{{ scope.row.requestMethod }}</el-tag>
+                  {{ scope.row.operUrl }}
+                </p>
+                <p>Host: {{ scope.row.operIp }}</p>
+                <p>Location: {{ scope.row.operLocation }}</p>
+                <p>耗时: {{ scope.row.latencyTime }}</p>
+                <div slot="reference" class="name-wrapper">
+                  <el-tag v-if="scope.row.requestMethod=='GET'">{{ scope.row.requestMethod }}</el-tag>
+                  <el-tag v-if="scope.row.requestMethod=='POST'" type="success">{{ scope.row.requestMethod }}</el-tag>
+                  <el-tag v-if="scope.row.requestMethod=='PUT'" type="warning">{{ scope.row.requestMethod }}</el-tag>
+                  <el-tag v-if="scope.row.requestMethod=='DELETE'" type="danger">{{ scope.row.requestMethod }}</el-tag>
+                  {{ scope.row.operUrl }}
+                </div>
+              </el-popover>
+            </template>
+          </el-table-column>
+          <el-table-column
+            label="操作人员"
+            prop="operName"
+            width="160"
+            :show-overflow-tooltip="true"
+          />
+          <el-table-column
+            label="状态"
+            prop="status"
+            width="80"
+            :show-overflow-tooltip="true"
+          >
+            <template slot-scope="scope">
+              <el-tag v-if="scope.row.status=='2'" type="success">{{ statusFormat(scope.row,scope.row.status) }}</el-tag>
+              <el-tag v-if="scope.row.status=='1'" type="danger">{{ statusFormat(scope.row,scope.row.status) }}</el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column label="操作日期" prop="operTime" width="160">
             <template slot-scope="scope">
               <span>{{ parseTime(scope.row.operTime) }}</span>
             </template>
           </el-table-column>
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <el-table-column
+            label="操作"
+            width="80"
+            class-name="small-padding fixed-width"
+          >
             <template slot-scope="scope">
               <el-button
-                v-permisaction="['system:sysoperlog:query']"
+                v-permisaction="['admin:sysOperLog:query']"
                 size="mini"
                 type="text"
                 icon="el-icon-view"
@@ -112,22 +142,21 @@
         <el-dialog title="操作日志详细" :visible.sync="open" width="700px">
           <el-form ref="form" :model="form" label-width="100px" size="mini">
             <el-row>
+              <el-col :span="24">
+                <el-form-item label="请求地址:">{{ form.operUrl }}</el-form-item>
+              </el-col>
               <el-col :span="12">
-                <el-form-item label="操作模块:">{{ form.title }} / {{ typeFormat(form) }}</el-form-item>
                 <el-form-item
                   label="登录信息:"
                 >{{ form.operName }} / {{ form.operIp }} / {{ form.operLocation }}</el-form-item>
               </el-col>
+
               <el-col :span="12">
-                <el-form-item label="请求地址:">{{ form.operUrl }}</el-form-item>
                 <el-form-item label="请求方式:">{{ form.requestMethod }}</el-form-item>
               </el-col>
               <el-col :span="12">
                 <el-form-item label="耗时:">{{ form.latencyTime }}</el-form-item>
               </el-col>
-              <el-col :span="24">
-                <el-form-item label="操作方法:">{{ form.method }}</el-form-item>
-              </el-col>
               <el-col :span="24">
                 <el-form-item label="请求参数:">{{ form.operParam }}</el-form-item>
               </el-col>
@@ -137,7 +166,7 @@
               <el-col :span="12">
                 <el-form-item label="操作状态:">
                   <div v-if="form.status === '2'">正常</div>
-                  <div v-else-if="form.status === '1'">失败</div>
+                  <div v-else-if="form.status === '1'">关闭</div>
                 </el-form-item>
               </el-col>
               <el-col :span="12">
@@ -158,11 +187,11 @@
 </template>
 
 <script>
-import { listSysOperlog, delSysOperlog, cleanOperlog } from '@/api/system/sys-opera-log'
+import { listSysOperlog, delSysOperlog, cleanOperlog } from '@/api/admin/sys-opera-log'
 import { formatJson } from '@/utils'
 
 export default {
-  name: 'Operlog',
+  name: 'SysOperLogManage',
   data() {
     return {
       // 遮罩层
@@ -178,8 +207,6 @@ export default {
       // 是否显示弹出层
       open: false,
       // 类型数据字典
-      typeOptions: [],
-      // 类型数据字典
       statusOptions: [],
       // 日期范围
       dateRange: [],
@@ -198,9 +225,7 @@ export default {
   },
   created() {
     this.getList()
-    this.getDicts('sys_oper_type').then(response => {
-      this.typeOptions = response.data
-    })
+
     this.getDicts('sys_common_status').then(response => {
       this.statusOptions = response.data
     })
@@ -220,10 +245,6 @@ export default {
     statusFormat(row, column) {
       return this.selectDictLabel(this.statusOptions, row.status)
     },
-    // 操作日志类型字典翻译
-    typeFormat(row, column) {
-      return this.selectDictLabel(this.typeOptions, row.businessType)
-    },
     /** 搜索按钮操作 */
     handleQuery() {
       this.queryParams.pageIndex = 1
@@ -247,16 +268,21 @@ export default {
     },
     /** 删除按钮操作 */
     handleDelete(row) {
-      const operIds = row.id || this.ids
+      const operIds = (row.id && [row.id]) || this.ids
       this.$confirm('是否确认删除日志编号为"' + operIds + '"的数据项?', '警告', {
         confirmButtonText: '确定',
         cancelButtonText: '取消',
         type: 'warning'
       }).then(function() {
-        return delSysOperlog(operIds)
-      }).then(() => {
-        this.getList()
-        this.msgSuccess('删除成功')
+        return delSysOperlog({ 'ids': operIds })
+      }).then((response) => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
       }).catch(function() {})
     },
     /** 清空按钮操作 */
@@ -267,9 +293,14 @@ export default {
         type: 'warning'
       }).then(function() {
         return cleanOperlog()
-      }).then(() => {
-        this.getList()
-        this.msgSuccess('清空成功')
+      }).then((response) => {
+        if (response.code === 200) {
+          this.msgSuccess(response.msg)
+          this.open = false
+          this.getList()
+        } else {
+          this.msgError(response.msg)
+        }
       }).catch(function() {})
     },
     /** 导出按钮操作 */

Some files were not shown because too many files changed in this diff