zs 1 year ago
commit
03b84252b2
100 changed files with 8134 additions and 0 deletions
  1. 2 0
      .env
  2. 33 0
      .eslintrc.js
  3. 24 0
      .gitignore
  4. 24 0
      README.md
  5. 3 0
      babel.config.js
  6. 63 0
      package.json
  7. BIN
      public/favicon.ico
  8. 17 0
      public/index.html
  9. 7 0
      src/App.vue
  10. BIN
      src/assets/banner.png
  11. 28 0
      src/assets/css/color-dark.css
  12. 4 0
      src/assets/css/icon.css
  13. 178 0
      src/assets/css/main.css
  14. 40 0
      src/assets/css/theme-green/color-green.css
  15. BIN
      src/assets/css/theme-green/fonts/element-icons.ttf
  16. BIN
      src/assets/css/theme-green/fonts/element-icons.woff
  17. 1 0
      src/assets/css/theme-green/index.css
  18. 83 0
      src/assets/icon/iconfont.css
  19. 1 0
      src/assets/icon/iconfont.js
  20. 128 0
      src/assets/icon/iconfont.json
  21. BIN
      src/assets/icon/iconfont.ttf
  22. BIN
      src/assets/icon/iconfont.woff
  23. BIN
      src/assets/icon/iconfont.woff2
  24. BIN
      src/assets/img/img.jpg
  25. BIN
      src/assets/logo.png
  26. BIN
      src/assets/square_big.png
  27. 122 0
      src/components/admin-frame/Header.vue
  28. 177 0
      src/components/admin-frame/Home.vue
  29. 214 0
      src/components/admin-frame/Sidebar.vue
  30. 199 0
      src/components/admin-frame/Tags.vue
  31. 33 0
      src/components/admin-frame/breadcrumb.vue
  32. 6 0
      src/components/admin-frame/bus.js
  33. 79 0
      src/components/admin-frame/directives.js
  34. 30 0
      src/components/admin-frame/i18n.js
  35. 73 0
      src/components/admin-frame/parts/password-1.vue
  36. 171 0
      src/components/admin-frame/备份/Header copy.vue
  37. 66 0
      src/components/admin-frame/备份/Home copy.vue
  38. 121 0
      src/components/admin-frame/备份/Sidebar copy.vue
  39. 181 0
      src/components/admin-frame/备份/Tags copy.vue
  40. 66 0
      src/components/frame/e-dateRange.vue
  41. 96 0
      src/components/frame/e-upload.vue
  42. 52 0
      src/components/frame/filter-page-table.md
  43. 414 0
      src/components/frame/filter-page-table.vue
  44. 83 0
      src/components/frame/form.md
  45. 285 0
      src/components/frame/form.vue
  46. 11 0
      src/components/frame/pagination.md
  47. 54 0
      src/components/frame/pagination.vue
  48. 131 0
      src/components/frame/s-upload.vue
  49. 172 0
      src/components/frame/swiper-frame.md
  50. 64 0
      src/components/frame/swiper-frame.vue
  51. 264 0
      src/components/frame/vform.vue
  52. 89 0
      src/components/frame/wang-editor.vue
  53. 38 0
      src/components/mobile-frame/foot.vue
  54. 111 0
      src/components/mobile-frame/mobile-main.vue
  55. 43 0
      src/components/mobile-frame/page.vue
  56. 9 0
      src/components/mobile-frame/top.md
  57. 123 0
      src/components/mobile-frame/top.vue
  58. 66 0
      src/components/web-frame/banner.vue
  59. 57 0
      src/components/web-frame/foot.vue
  60. 59 0
      src/components/web-frame/index.vue
  61. 105 0
      src/components/web-frame/menus.vue
  62. 87 0
      src/components/web-frame/top.vue
  63. 1056 0
      src/layout/deploy/dict.js
  64. 986 0
      src/layout/deploy/menu.js
  65. 61 0
      src/layout/deploy/site.js
  66. 54 0
      src/layout/unitdict.js
  67. 12 0
      src/main.js
  68. 17 0
      src/router/index.js
  69. 42 0
      src/store/assistance_investigation.js
  70. 42 0
      src/store/assistance_investigation_reply.js
  71. 42 0
      src/store/baoan_news.js
  72. 101 0
      src/store/baoan_statis.js
  73. 42 0
      src/store/baoan_work_warning.js
  74. 42 0
      src/store/certificates_base.js
  75. 42 0
      src/store/certificates_grant.js
  76. 42 0
      src/store/certificates_print_again.js
  77. 42 0
      src/store/certificates_print_log.js
  78. 42 0
      src/store/company_accept.js
  79. 42 0
      src/store/company_applicant.js
  80. 42 0
      src/store/company_baoan_dispatch.js
  81. 42 0
      src/store/company_baoan_punch.js
  82. 42 0
      src/store/company_baoan_work.js
  83. 42 0
      src/store/company_base.js
  84. 42 0
      src/store/company_car.js
  85. 42 0
      src/store/company_dress.js
  86. 42 0
      src/store/company_equipment.js
  87. 42 0
      src/store/company_examine.js
  88. 42 0
      src/store/company_gun.js
  89. 42 0
      src/store/company_information.js
  90. 42 0
      src/store/company_keep.js
  91. 42 0
      src/store/company_keep_grant.js
  92. 42 0
      src/store/company_legal_change.js
  93. 42 0
      src/store/company_legal_person.js
  94. 42 0
      src/store/company_licence.js
  95. 42 0
      src/store/company_licence_grant.js
  96. 42 0
      src/store/company_managers.js
  97. 42 0
      src/store/company_person_charge.js
  98. 42 0
      src/store/company_recruit_work.js
  99. 42 0
      src/store/company_service_area.js
  100. 0 0
      src/store/company_service_object.js

+ 2 - 0
.env

@@ -0,0 +1,2 @@
+VUE_APP_AXIOS_BASE_URL = ''
+VUE_APP_ROUTER="/baoancommon"

+ 33 - 0
.eslintrc.js

@@ -0,0 +1,33 @@
+// https://eslint.org/docs/user-guide/configuring
+
+module.exports = {
+  root: true,
+  env: {
+    node: true,
+  },
+  extends: ["plugin:vue/essential", "@vue/prettier"],
+  plugins: ["vue"],
+  rules: {
+    "max-len": [
+      "warn",
+      {
+        code: 10000,
+      },
+    ],
+    "no-unused-vars": "off",
+    "no-console": "off",
+    "prettier/prettier": [
+      "warn",
+      {
+        singleQuote: true,
+        trailingComma: "es5",
+        bracketSpacing: true,
+        jsxBracketSameLine: true,
+        printWidth: 160,
+      },
+    ],
+  },
+  parserOptions: {
+    parser: "babel-eslint",
+  },
+};

+ 24 - 0
.gitignore

@@ -0,0 +1,24 @@
+.DS_Store
+node_modules
+package-lock.json
+/dist
+
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+pnpm-debug.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?

+ 24 - 0
README.md

@@ -0,0 +1,24 @@
+# common
+
+## Project setup
+```
+npm install
+```
+
+### Compiles and hot-reloads for development
+```
+npm run serve
+```
+
+### Compiles and minifies for production
+```
+npm run build
+```
+
+### Lints and fixes files
+```
+npm run lint
+```
+
+### Customize configuration
+See [Configuration Reference](https://cli.vuejs.org/config/).

+ 3 - 0
babel.config.js

@@ -0,0 +1,3 @@
+module.exports = {
+  presets: ["@vue/cli-plugin-babel/preset"]
+};

+ 63 - 0
package.json

@@ -0,0 +1,63 @@
+{
+  "name": "baoan-common",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "axios": "^0.21.1",
+    "core-js": "^3.6.5",
+    "echarts": "^5.0.1",
+    "element-ui": "^2.15.0",
+    "jsonwebtoken": "^8.5.1",
+    "lodash": "^4.17.20",
+    "moment": "^2.29.1",
+    "naf-core": "^0.1.2",
+    "swiper": "^5.3.6",
+    "vue": "^2.6.11",
+    "vue-awesome-swiper": "^4.1.1",
+    "vue-meta": "^2.4.0",
+    "vue-router": "^3.2.0",
+    "vuex": "^3.4.0",
+    "wangeditor": "^3.1.1"
+  },
+  "devDependencies": {
+    "@vue/cli-plugin-babel": "~4.5.0",
+    "@vue/cli-plugin-eslint": "~4.5.0",
+    "@vue/cli-plugin-router": "~4.5.0",
+    "@vue/cli-plugin-vuex": "~4.5.0",
+    "@vue/cli-service": "~4.5.0",
+    "@vue/eslint-config-prettier": "^6.0.0",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-prettier": "^3.1.3",
+    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "prettier": "^1.19.1",
+    "vue-template-compiler": "^2.6.11"
+  },
+  "eslintConfig": {
+    "root": true,
+    "env": {
+      "node": true
+    },
+    "extends": [
+      "plugin:vue/essential",
+      "eslint:recommended",
+      "@vue/prettier"
+    ],
+    "parserOptions": {
+      "parser": "babel-eslint"
+    },
+    "rules": {}
+  },
+  "browserslist": [
+    "> 1%",
+    "last 2 versions",
+    "not dead"
+  ]
+}

BIN
public/favicon.ico


+ 17 - 0
public/index.html

@@ -0,0 +1,17 @@
+<!DOCTYPE html>
+<html lang="">
+  <head>
+    <meta charset="utf-8">
+    <meta http-equiv="X-UA-Compatible" content="IE=edge">
+    <meta name="viewport" content="width=device-width,initial-scale=1.0">
+    <link rel="icon" href="<%= BASE_URL %>favicon.ico">
+    <title><%= htmlWebpackPlugin.options.title %></title>
+  </head>
+  <body>
+    <noscript>
+      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
+    </noscript>
+    <div id="app"></div>
+    <!-- built files will be auto injected -->
+  </body>
+</html>

+ 7 - 0
src/App.vue

@@ -0,0 +1,7 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+
+<style lang="less"></style>

BIN
src/assets/banner.png


+ 28 - 0
src/assets/css/color-dark.css

@@ -0,0 +1,28 @@
+.header{
+    background-color: #242f42;
+}
+.login-wrap{
+    background: #324157;
+}
+.plugins-tips{
+    background: #eef1f6;
+}
+.plugins-tips a{
+    color: #20a0ff;
+}
+.el-upload--text em {
+    color: #20a0ff;
+}
+.pure-button{
+    background: #20a0ff;
+}
+.tags-li.active {
+    border: 1px solid #409EFF;
+    background-color: #409EFF;
+}
+.message-title{
+    color: #20a0ff;
+}
+.collapse-btn:hover{
+    background: rgb(40,52,70);
+}

+ 4 - 0
src/assets/css/icon.css

@@ -0,0 +1,4 @@
+
+    [class*=" el-icon-lx"], [class^=el-icon-lx] {
+        font-family: lx-iconfont!important;
+    }

+ 178 - 0
src/assets/css/main.css

@@ -0,0 +1,178 @@
+* {
+    margin: 0;
+    padding: 0;
+}
+
+html,
+body,
+#app,
+.wrapper {
+    width: 100%;
+    height: 100%;
+    overflow: hidden;
+}
+
+body {
+    font-family: 'PingFang SC', "Helvetica Neue", Helvetica, "microsoft yahei", arial, STHeiTi, sans-serif;
+}
+
+a {
+    text-decoration: none
+}
+
+
+.content-box {
+    position: absolute;
+    left: 200px;
+    right: 0;
+    top: 60px;
+    bottom: 0;
+    padding-bottom: 30px;
+    -webkit-transition: left .3s ease-in-out;
+    transition: left .3s ease-in-out;
+    background: #f0f0f0;
+}
+
+.content {
+    width: 100%;
+    height: 100%;
+    padding: 10px;
+    overflow-y: scroll;
+    box-sizing: border-box;
+}
+
+.content-collapse {
+    left: 65px;
+}
+
+.container {
+    width: 100%;
+    padding: 30px;
+    background: #fff;
+    border: 1px solid #ddd;
+    border-radius: 5px;
+}
+
+.crumbs {
+    margin: 10px 0;
+}
+
+.el-table th {
+    background-color: #f5f7fa !important;
+}
+
+.pagination {
+    margin: 20px 0;
+    text-align: right;
+}
+
+.plugins-tips {
+    padding: 20px 10px;
+    margin-bottom: 20px;
+}
+
+.el-button+.el-tooltip {
+    margin-left: 10px;
+}
+
+.el-table tr:hover {
+    background: #f6faff;
+}
+
+.mgb20 {
+    margin-bottom: 20px;
+}
+
+.move-enter-active,
+.move-leave-active {
+    transition: opacity .5s;
+}
+
+.move-enter,
+.move-leave {
+    opacity: 0;
+}
+
+/*BaseForm*/
+
+.form-box {
+    width: 600px;
+}
+
+.form-box .line {
+    text-align: center;
+}
+
+.el-time-panel__content::after,
+.el-time-panel__content::before {
+    margin-top: -7px;
+}
+
+.el-time-spinner__wrapper .el-scrollbar__wrap:not(.el-scrollbar__wrap--hidden-default) {
+    padding-bottom: 0;
+}
+
+/*Upload*/
+
+.pure-button {
+    width: 150px;
+    height: 40px;
+    line-height: 40px;
+    text-align: center;
+    color: #fff;
+    border-radius: 3px;
+}
+
+.g-core-image-corp-container .info-aside {
+    height: 45px;
+}
+
+.el-upload--text {
+    background-color: #fff;
+    border: 1px dashed #d9d9d9;
+    border-radius: 6px;
+    box-sizing: border-box;
+    width: 360px;
+    height: 180px;
+    text-align: center;
+    cursor: pointer;
+    position: relative;
+    overflow: hidden;
+}
+
+.el-upload--text .el-icon-upload {
+    font-size: 67px;
+    color: #97a8be;
+    margin: 40px 0 16px;
+    line-height: 50px;
+}
+
+.el-upload--text {
+    color: #97a8be;
+    font-size: 14px;
+    text-align: center;
+}
+
+.el-upload--text em {
+    font-style: normal;
+}
+
+/*VueEditor*/
+
+.ql-container {
+    min-height: 400px;
+}
+
+.ql-snow .ql-tooltip {
+    transform: translateX(117.5px) translateY(10px) !important;
+}
+
+.editor-btn {
+    margin-top: 20px;
+}
+
+/*markdown*/
+
+.v-note-wrapper .v-note-panel {
+    min-height: 500px;
+}

+ 40 - 0
src/assets/css/theme-green/color-green.css

@@ -0,0 +1,40 @@
+.header{
+    background-color: #07c4a8;
+}
+.login-wrap{
+    background: rgba(56, 157, 170, 0.82);;
+}
+.plugins-tips{
+    background: #f2f2f2;
+}
+.plugins-tips a{
+    color: #00d1b2;
+}
+.el-upload--text em {
+    color: #00d1b2;
+}
+.pure-button{
+    background: #00d1b2;
+}
+.pagination > .active > a, .pagination > .active > a:hover, .pagination > .active > a:focus, .pagination > .active > span, .pagination > .active > span:hover, .pagination > .active > span:focus {
+    background-color: #00d1b2 !important;
+    border-color: #00d1b2 !important;
+}
+.tags-li.active {
+    border: 1px solid #00d1b2 !important;
+    background-color: #00d1b2 !important;
+}
+/* .collapse-btn:hover{
+    background: #00d1b2;
+} */
+.el-submenu__title i{
+  color: #000000 !important;
+  font-size: 16px !important;
+  font-weight: bold !important;
+}
+.sidebar{
+   background-color: #00d1b2 !important;
+}
+.el-menu-item.is-active{
+  background-color: #00A78E !important;
+}

BIN
src/assets/css/theme-green/fonts/element-icons.ttf


BIN
src/assets/css/theme-green/fonts/element-icons.woff


File diff suppressed because it is too large
+ 1 - 0
src/assets/css/theme-green/index.css


+ 83 - 0
src/assets/icon/iconfont.css

@@ -0,0 +1,83 @@
+@font-face {
+  font-family: "iconfont"; /* Project id 2769491 */
+  src: url('iconfont.woff2?t=1631847015897') format('woff2'),
+       url('iconfont.woff?t=1631847015897') format('woff'),
+       url('iconfont.ttf?t=1631847015897') format('truetype');
+}
+
+.iconfont {
+  font-family: "iconfont" !important;
+  font-size: 16px;
+  font-style: normal;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.icon-farenshenpi:before {
+  content: "\e666";
+}
+
+.icon-zhengjianguanli-15:before {
+  content: "\e634";
+}
+
+.icon-qiyeshenfenrz:before {
+  content: "\e65b";
+}
+
+.icon-monikaoshi:before {
+  content: "\e78c";
+}
+
+.icon-fuwuquyu:before {
+  content: "\e62a";
+}
+
+.icon-daohang_fuwuduixiangguanli:before {
+  content: "\e641";
+}
+
+.icon-danweixinxi:before {
+  content: "\e6af";
+}
+
+.icon-bumentongbao:before {
+  content: "\e657";
+}
+
+.icon-jiangli:before {
+  content: "\e79d";
+}
+
+.icon-tousu:before {
+  content: "\e715";
+}
+
+.icon-xukezheng:before {
+  content: "\e620";
+}
+
+.icon-hangzheng:before {
+  content: "\e648";
+}
+
+.icon-richang:before {
+  content: "\e61f";
+}
+
+.icon-xinxixiecha:before {
+  content: "\e642";
+}
+
+.icon-index-copy:before {
+  content: "\e622";
+}
+
+.icon-baoanyuanzheng:before {
+  content: "\e68d";
+}
+
+.icon-ico_zonghexinxi_xuanzhong:before {
+  content: "\e62b";
+}
+

File diff suppressed because it is too large
+ 1 - 0
src/assets/icon/iconfont.js


+ 128 - 0
src/assets/icon/iconfont.json

@@ -0,0 +1,128 @@
+{
+  "id": "2769491",
+  "name": "保安",
+  "font_family": "iconfont",
+  "css_prefix_text": "icon-",
+  "description": "",
+  "glyphs": [
+    {
+      "icon_id": "5191243",
+      "name": "法人审批",
+      "font_class": "farenshenpi",
+      "unicode": "e666",
+      "unicode_decimal": 58982
+    },
+    {
+      "icon_id": "20431513",
+      "name": "证件管理-15",
+      "font_class": "zhengjianguanli-15",
+      "unicode": "e634",
+      "unicode_decimal": 58932
+    },
+    {
+      "icon_id": "749854",
+      "name": "企业名称认证",
+      "font_class": "qiyeshenfenrz",
+      "unicode": "e65b",
+      "unicode_decimal": 58971
+    },
+    {
+      "icon_id": "10232776",
+      "name": "模拟考试",
+      "font_class": "monikaoshi",
+      "unicode": "e78c",
+      "unicode_decimal": 59276
+    },
+    {
+      "icon_id": "1235212",
+      "name": "服务区域",
+      "font_class": "fuwuquyu",
+      "unicode": "e62a",
+      "unicode_decimal": 58922
+    },
+    {
+      "icon_id": "22378757",
+      "name": "导航_服务对象管理",
+      "font_class": "daohang_fuwuduixiangguanli",
+      "unicode": "e641",
+      "unicode_decimal": 58945
+    },
+    {
+      "icon_id": "16206729",
+      "name": "单位信息",
+      "font_class": "danweixinxi",
+      "unicode": "e6af",
+      "unicode_decimal": 59055
+    },
+    {
+      "icon_id": "8651700",
+      "name": "部门通报",
+      "font_class": "bumentongbao",
+      "unicode": "e657",
+      "unicode_decimal": 58967
+    },
+    {
+      "icon_id": "4799929",
+      "name": "奖励",
+      "font_class": "jiangli",
+      "unicode": "e79d",
+      "unicode_decimal": 59293
+    },
+    {
+      "icon_id": "7090072",
+      "name": "投诉",
+      "font_class": "tousu",
+      "unicode": "e715",
+      "unicode_decimal": 59157
+    },
+    {
+      "icon_id": "7261518",
+      "name": "许可证",
+      "font_class": "xukezheng",
+      "unicode": "e620",
+      "unicode_decimal": 58912
+    },
+    {
+      "icon_id": "9735395",
+      "name": "行政",
+      "font_class": "hangzheng",
+      "unicode": "e648",
+      "unicode_decimal": 58952
+    },
+    {
+      "icon_id": "16709096",
+      "name": "日常",
+      "font_class": "richang",
+      "unicode": "e61f",
+      "unicode_decimal": 58911
+    },
+    {
+      "icon_id": "17806901",
+      "name": "信息协查",
+      "font_class": "xinxixiecha",
+      "unicode": "e642",
+      "unicode_decimal": 58946
+    },
+    {
+      "icon_id": "19985212",
+      "name": "首页-选中",
+      "font_class": "index-copy",
+      "unicode": "e622",
+      "unicode_decimal": 58914
+    },
+    {
+      "icon_id": "21786502",
+      "name": "保安员证",
+      "font_class": "baoanyuanzheng",
+      "unicode": "e68d",
+      "unicode_decimal": 59021
+    },
+    {
+      "icon_id": "22550332",
+      "name": "综合信息",
+      "font_class": "ico_zonghexinxi_xuanzhong",
+      "unicode": "e62b",
+      "unicode_decimal": 58923
+    }
+  ]
+}

BIN
src/assets/icon/iconfont.ttf


BIN
src/assets/icon/iconfont.woff


BIN
src/assets/icon/iconfont.woff2


BIN
src/assets/img/img.jpg


BIN
src/assets/logo.png


BIN
src/assets/square_big.png


+ 122 - 0
src/components/admin-frame/Header.vue

@@ -0,0 +1,122 @@
+<template>
+  <div id="Header">
+    <el-row>
+      <el-col :span="24" class="main header">
+        <el-col :span="24" class="one">
+          <el-col :span="8" class="left">
+            <span @click="collapseChage">
+              <i v-if="!collapse" class="el-icon-s-fold"></i>
+              <i v-else class="el-icon-s-unfold"></i>
+            </span>
+            <span
+              >{{ siteInfo.zhTitle }}-{{
+                type == 'baoanpolice' ? '公安端' : type == 'baoancompany' ? '企业端' : type == 'baoanexam' ? '考试端' : type == 'baoansociety' ? '协会端' : ''
+              }}</span
+            >
+          </el-col>
+          <el-col :span="16" class="right">
+            <el-button type="primary" size="mini" @click="headerBtn('统计')" v-if="type == 'baoanpolice'">统计</el-button>
+            <el-button type="primary" size="mini" @click="headerBtn('统计2')" v-if="type == 'baoanpolice'">统计-模板2</el-button>
+            <el-button type="primary" size="mini" @click="headerBtn('统计3')" v-if="type == 'baoanpolice'">统计-模板3</el-button>
+            <el-button type="primary" size="mini" @click="headerBtn('统计4')" v-if="type == 'baoanpolice'">统计-模板4</el-button>
+            <el-button type="primary" size="mini" @click="headerBtn('修改密码')">修改密码</el-button>
+            <el-button size="mini" class="name"><i class="el-icon-user-solid"></i>{{ user.name || user.company || '游客' }}</el-button>
+            <el-button type="danger" size="mini" @click="logout">退出登录</el-button>
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+const { siteInfo } = require('../../layout/deploy/site');
+import { mapState, mapMutations } from 'vuex';
+import bus from './bus';
+
+export default {
+  name: 'Header',
+  props: {},
+  components: {},
+  data: function() {
+    return { collapse: false, siteInfo: siteInfo, type: '' };
+  },
+  created() {
+    this.initTitle();
+  },
+  methods: {
+    ...mapMutations(['deleteUser']),
+    // 侧边栏折叠
+    collapseChage() {
+      this.collapse = !this.collapse;
+      bus.$emit('collapse', this.collapse);
+    },
+    // 退出登录
+    logout() {
+      this.deleteUser();
+      this.$router.push('/login');
+    },
+    initTitle() {
+      // 不同用户类别,显示不同的名称
+      const { options } = this.$router;
+      if (!options) {
+        console.warn('获取菜单:解析base错误');
+        return;
+      }
+      const { base } = options;
+      this.$set(this, `type`, base);
+    },
+    headerBtn(type) {
+      this.$emit('headerBtn', type);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  mounted() {
+    if (document.body.clientWidth < 1500) {
+      this.collapseChage();
+    }
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    height: 60px;
+    border-bottom: 1px solid #f1f1f1;
+    padding: 0 10px;
+    .left {
+      line-height: 60px;
+      span {
+        display: inline-block;
+        margin: 0 10px;
+        font-size: 22px;
+        color: #ffffff;
+        font-weight: bold;
+        font-family: cursive;
+      }
+    }
+    .right {
+      text-align: right;
+      padding: 15px 0;
+      .name {
+        background-color: transparent;
+        border: none;
+        color: #fff;
+      }
+    }
+  }
+}
+</style>

+ 177 - 0
src/components/admin-frame/Home.vue

@@ -0,0 +1,177 @@
+<template>
+  <div id="Home">
+    <el-container>
+      <el-header style="padding:0"><v-head @headerBtn="headerBtn"></v-head></el-header>
+      <el-container>
+        <el-aside width="200px">
+          <v-sidebar v-if="user && user._id"></v-sidebar>
+          <!-- 做个未加载完成的loading -->
+          <div style="height:300px" v-loading="true" v-else></div>
+        </el-aside>
+        <el-main>
+          <div class="content-box" :class="{ 'content-collapse': collapse }">
+            <v-tags></v-tags>
+            <el-col :span="24" class="content">
+              <transition name="move" mode="out-in">
+                <el-row>
+                  <breadcrumb :breadcrumbTitle="this.$route.meta.title"></breadcrumb>
+                  <el-col :span="24" class="container" :style="{ height: clientHeight - 150 + 'px' }"><router-view></router-view></el-col>
+                </el-row>
+              </transition>
+              <el-backtop target=".content"></el-backtop>
+            </el-col>
+          </div>
+        </el-main>
+      </el-container>
+    </el-container>
+    <el-dialog class="oneDialog" title="修改密码" :visible.sync="oneDialog" width="30%" :before-close="passClose" :close-on-click-modal="false">
+      <password-1 :form="passwordForm" @onSubmit="onSubmit"></password-1>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import bus from './bus';
+import { mapState, mapMutations, createNamespacedHelpers } from 'vuex';
+import jwt from 'jsonwebtoken';
+const { mapActions: company_baseAction } = createNamespacedHelpers('company_base');
+const { mapActions: system_accountAction } = createNamespacedHelpers('system_account');
+const { mapActions: society_accountAction } = createNamespacedHelpers('society_account');
+const { mapActions: exam_accountAction } = createNamespacedHelpers('exam_account');
+const { mapActions } = createNamespacedHelpers('login');
+export default {
+  name: 'Home',
+  props: {},
+  // 想改变生命周期.但是失败了,就这么写了.有问题再换回去
+  components: {
+    vSidebar: () => import('./Sidebar.vue'),
+    vTags: () => import('./Tags.vue'),
+    breadcrumb: () => import('./breadcrumb.vue'),
+    vHead: () => import('./Header.vue'),
+    password1: () => import('./parts/password-1.vue'),
+  },
+  data: function() {
+    return {
+      collapse: false,
+      tagsList: [],
+      clientHeight: document.documentElement.clientHeight,
+      // 修改密码
+      oneDialog: false,
+      passwordForm: {},
+    };
+  },
+  created() {
+    this.initUserInfo();
+    bus.$on('collapse-content', msg => {
+      this.collapse = msg;
+    });
+    // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。
+    bus.$on('tags', (msg) => {
+      let arr = [];
+      for (let i = 0, len = msg.length; i < len; i++) {
+        msg[i].name && arr.push(msg[i].name);
+      }
+      this.tagsList = arr;
+    });
+  },
+  methods: {
+    ...mapMutations(['setUser', 'deleteUser']),
+    ...company_baseAction({ company_baseInfo: 'fetch', company_baseUpdate: 'update' }),
+    ...system_accountAction({ system_accountInfo: 'fetch', system_accountUpdate: 'update' }),
+    ...society_accountAction({ society_accountInfo: 'fetch', society_accountUpdate: 'update' }),
+    ...exam_accountAction({ exam_accountInfo: 'fetch', exam_accountUpdate: 'update' }),
+    ...mapActions(['login']),
+    headerBtn(type) {
+      // window.open('/baoanpolice/header/statis/index');
+      if (type == '统计') {
+        this.$router.push('/header/statis/index');
+      } else if (type == '统计2') {
+        this.$router.push('/header/statis-two/index');
+      } else if (type == '统计3') {
+        this.$router.push('/header/statis-thr/index');
+      } else if (type == '统计4') {
+        this.$router.push('/header/statis-four/index');
+      } else if (type == '修改密码') {
+        this.oneDialog = true;
+      }
+    },
+    async initUserInfo() {
+      const token = localStorage.getItem('token');
+      if (!token) return;
+      const arr = jwt.decode(token);
+      await this.setUser(arr);
+    },
+    // 修改密码
+    // 提交修改
+    async onSubmit({ data }) {
+      // 查询用户列表
+      const { options } = this.$router;
+      if (!options) {
+        console.warn('获取菜单:解析base错误');
+        return;
+      }
+      let { base } = options;
+      if (base == 'baoanpolice') {
+        let p1 = await this.login({ table: 'system_account', phone: this.user.phone, password: data.oldPassword });
+        if (this.$checkRes(p1)) {
+          let p2 = await this.system_accountUpdate({ id: this.user.id, password: data.newPassword });
+          if (this.$checkRes(p2)) {
+            this.$message({ type: `success`, message: `修改密码成功` });
+            this.deleteUser();
+            this.$router.push('/login');
+          }
+        }
+      } else if (base == 'baoancompany') {
+        let p1 = await this.login({ table: 'company_base', phone: this.user.phone, password: data.oldPassword });
+        let p2 = await this.company_baseUpdate({ id: this.user.id, password: data.newPassword });
+        if (this.$checkRes(p2)) {
+          this.$message({ type: `success`, message: `修改密码成功` });
+          this.deleteUser();
+          this.$router.push('/login');
+        }
+      }
+    },
+    // 关闭修改密码
+    passClose() {
+      this.oneDialog = false;
+    },
+  },
+  mounted() {
+    const that = this;
+    window.onresize = () => {
+      return (() => {
+        window.fullHeight = document.documentElement.clientHeight;
+        that.clientHeight = window.fullHeight;
+      })();
+    };
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    fullHeight(val) {
+      if (!this.timer) {
+        this.fullHeight = val;
+        this.timer = true;
+        let that = this;
+        setTimeout(function() {
+          that.timer = false;
+        }, 400);
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.container {
+  padding: 15px;
+  overflow: auto;
+}
+/deep/.el-dialog__body {
+  padding: 10px;
+}
+</style>

+ 214 - 0
src/components/admin-frame/Sidebar.vue

@@ -0,0 +1,214 @@
+<template>
+  <div id="Sidebar">
+    <el-row class="sidebar">
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <el-menu
+            class="sidebar-el-menu"
+            :default-active="onRoutes"
+            :collapse="collapse"
+            :background-color="adminMenus.backColor"
+            :text-color="adminMenus.textColor"
+            :active-text-color="adminMenus.activeColor"
+            unique-opened
+            router
+          >
+            <template v-for="item in items">
+              <template v-if="item.subs">
+                <el-submenu :index="item.index" :key="item.index">
+                  <template slot="title">
+                    <i :class="['iconfont', item.icon]"></i>
+                    <span slot="title">{{ item.title }}</span>
+                  </template>
+                  <template v-for="subItem in item.subs">
+                    <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
+                      <template slot="title">{{ subItem.title }}</template>
+                      <el-menu-item v-for="(threeItem, i) in subItem.subs" :key="i" :index="threeItem.index">{{ threeItem.title }}</el-menu-item>
+                    </el-submenu>
+                    <el-menu-item v-else :index="subItem.index" :key="subItem.index">{{ subItem.title }}</el-menu-item>
+                  </template>
+                </el-submenu>
+              </template>
+              <template v-else>
+                <el-menu-item class="first" :index="item.index" :key="item.index">
+                  <i :class="['iconfont', item.icon]"></i>
+                  <span slot="title">{{ item.title }}</span>
+                </el-menu-item>
+              </template>
+            </template>
+          </el-menu>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+const { system, adminMenu, companyMenu, examMenu, societyMenu } = require('../../layout/deploy/menu');
+const { adminMenus } = require('../../layout/deploy/site');
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: systen_role } = createNamespacedHelpers('systen_role');
+import _ from 'lodash';
+import bus from './bus';
+export default {
+  name: 'Sidebar',
+  props: {},
+  components: {},
+  data: function() {
+    return {
+      adminMenus: adminMenus,
+      collapse: false,
+      items: examMenu,
+    };
+  },
+  created() {
+    bus.$on('collapse', msg => {
+      this.collapse = msg;
+      bus.$emit('collapse-content', msg);
+    });
+  },
+  methods: {
+    ...systen_role(['fetch']),
+    async getMenu() {
+      // 不登录,没菜单
+      if (!_.get(this.user, 'id')) return;
+      const { options } = this.$router;
+      if (!options) {
+        console.warn('获取菜单:解析base错误');
+        return;
+      }
+      const { base } = options;
+      const { role_id, purpose } = this.user;
+      let menus = [];
+      if (base.includes('company')) {
+        // 添加角色的请求.为之后菜单筛选用
+        if (purpose) {
+          const res = await this.fetch({ name: purpose });
+          if (this.$checkRes(res)) {
+            const { menu } = res.data;
+            if (menu.length > 0) menus = JSON.parse(menu);
+          }
+        }
+        this.toDealCompanyMenu(menus);
+      } else if (base.includes('exam')) {
+        let list = _.cloneDeep(this.items);
+        let examMenus = _.cloneDeep(examMenu);
+        list.push(...examMenus);
+        this.$set(this, `items`, _.uniqBy(list, 'index'));
+      } else if (base.includes('society')) {
+        let list = _.cloneDeep(this.items);
+        let societyMenus = _.cloneDeep(societyMenu);
+        list.push(...societyMenus);
+        this.$set(this, `items`, _.uniqBy(list, 'index'));
+      } else {
+        if (role_id) {
+          if (role_id !== '-1') {
+            const res = await this.fetch({ id: role_id });
+            if (this.$checkRes(res)) {
+              const { menu } = res.data;
+              if (menu.length > 0) menus = JSON.parse(menu);
+            }
+          } else {
+            // -1:管理员,不需要menu,全都要
+            menus = undefined;
+          }
+        }
+        this.toDealAdminMenu(menus);
+      }
+    },
+    toDealAdminMenu(menu) {
+      // 系统首页
+      let list = _.cloneDeep(this.items);
+      // 总菜单,如果没有用户角色,使用全部菜单权限
+      let adminMenus = _.cloneDeep(adminMenu);
+      if (!menu) list.push(...adminMenus);
+      const nmenu = this.getMenuOptions(adminMenus, menu);
+      list.push(...nmenu);
+      this.$set(this, `items`, _.uniqBy(list, 'index'));
+    },
+    toDealCompanyMenu(menu) {
+      let list = _.cloneDeep(this.items);
+      const { status } = this.user;
+      if (status === '2' || status === '5') {
+        if (!menu) list.push(...companyMenu);
+        else {
+          const nmenu = this.getMenuOptions(companyMenu, menu);
+          list.push(...nmenu);
+        }
+      } else {
+        let dup = _.cloneDeep(companyMenu);
+        dup = dup.filter(f => f.pass);
+        list.push(...dup);
+      }
+      this.$set(this, `items`, _.uniqBy(list, 'index'));
+    },
+
+    getMenuOptions(allMenu, menus) {
+      //  全部菜单,角色菜单
+      menus = _.uniq(_.flattenDeep(menus));
+      return this.getOptions(allMenu, menus) || [];
+    },
+
+    getOptions(allMenu, menus) {
+      let res = allMenu.map(l => {
+        const r = menus.find(f => f == l.num);
+        if (r) {
+          if (l.subs) l.subs = this.getOptions(l.subs, menus);
+          return l;
+        }
+      });
+      res = _.compact(res);
+      return res;
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    onRoutes() {
+      return this.$route.path;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    user: {
+      deep: true,
+      immediate: true,
+      handler(val) {
+        // this.getMenu();
+      },
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.sidebar {
+  display: block;
+  position: absolute;
+  left: 0;
+  top: 60px;
+  bottom: 0;
+  overflow-y: scroll;
+}
+.sidebar::-webkit-scrollbar {
+  width: 0;
+}
+.sidebar-el-menu:not(.el-menu--collapse) {
+  width: 200px;
+}
+.sidebar > ul {
+  height: 100%;
+}
+.main {
+  .one {
+    .iconfont {
+      font-size: 18px;
+      margin: 0 5px 0 0;
+    }
+  }
+}
+/deep/.el-submenu .el-menu-item {
+  padding: 0 45px !important;
+}
+</style>

+ 199 - 0
src/components/admin-frame/Tags.vue

@@ -0,0 +1,199 @@
+<template>
+  <div id="Tags">
+    <el-row v-if="showTags">
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <el-col :span="23" class="left">
+            <ul>
+              <li class="tags-li" v-for="(item, index) in tagsList" :class="{ active: isActive(item.path) }" :key="index">
+                <router-link :to="item.path" class="tags-li-title">
+                  {{ item.title }}
+                </router-link>
+                <span class="tags-li-icon" @click="closeTags(index)"><i class="el-icon-close"></i></span>
+              </li>
+            </ul>
+          </el-col>
+          <el-col :span="1" class="right">
+            <el-dropdown @command="handleTags">
+              <el-button size="mini" type="primary"> 标签选项<i class="el-icon-arrow-down el-icon--right"></i> </el-button>
+              <el-dropdown-menu size="small" slot="dropdown">
+                <el-dropdown-item command="other">关闭其他</el-dropdown-item>
+                <el-dropdown-item command="all">关闭所有</el-dropdown-item>
+              </el-dropdown-menu>
+            </el-dropdown>
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import bus from './bus';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'Tags',
+  props: {},
+  components: {},
+  data: function() {
+    return {
+      tagsList: [],
+    };
+  },
+  created() {
+    this.setTags(this.$route);
+    // 监听关闭当前页面的标签页
+    bus.$on('close_current_tags', () => {
+      for (let i = 0, len = this.tagsList.length; i < len; i++) {
+        const item = this.tagsList[i];
+        if (item.path === this.$route.fullPath) {
+          if (i < len - 1) {
+            this.$router.push(this.tagsList[i + 1].path);
+          } else if (i > 0) {
+            this.$router.push(this.tagsList[i - 1].path);
+          } else {
+            this.$router.push('/');
+          }
+          this.tagsList.splice(i, 1);
+          break;
+        }
+      }
+    });
+  },
+  methods: {
+    isActive(path) {
+      return path === this.$route.fullPath;
+    },
+    // 打开标签
+    handleTags(command) {
+      command === 'other' ? this.closeOther() : this.closeAll();
+    },
+    // 关闭单个标签
+    closeTags(index) {
+      const delItem = this.tagsList.splice(index, 1)[0];
+      const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1];
+      if (item) {
+        delItem.path === this.$route.fullPath && this.$router.push(item.path);
+      } else {
+        this.$router.push('/adminCenter/homeIndex');
+      }
+    },
+    // 关闭全部标签
+    closeAll() {
+      this.tagsList = [];
+      this.$router.push('/adminCenter/homeIndex');
+    },
+    // 关闭其他标签
+    closeOther() {
+      const curItem = this.tagsList.filter(item => {
+        return item.path === this.$route.fullPath;
+      });
+      this.tagsList = curItem;
+    },
+    // 设置标签
+    setTags(route) {
+      const isExist = this.tagsList.some(item => {
+        return item.path === route.fullPath;
+      });
+      if (!isExist) {
+        if (this.tagsList.length >= 8) {
+          this.tagsList.shift();
+        }
+        this.tagsList.push({
+          title: route.meta.title,
+          path: route.fullPath,
+          name: route.matched[1].components.default.name,
+        });
+      }
+      bus.$emit('tags', this.tagsList);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    showTags() {
+      return this.tagsList.length > 0;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    $route(newValue, oldValue) {
+      this.setTags(newValue);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    position: relative;
+    height: 30px;
+    overflow: hidden;
+    background: #fff;
+    padding-right: 120px;
+    box-shadow: 0 5px 10px #ddd;
+    .left {
+      ul {
+        box-sizing: border-box;
+        width: 100%;
+        height: 100%;
+      }
+      .tags-li {
+        float: left;
+        margin: 3px 5px 2px 3px;
+        border-radius: 3px;
+        font-size: 12px;
+        overflow: hidden;
+        cursor: pointer;
+        height: 23px;
+        line-height: 23px;
+        border: 1px solid #e9eaec;
+        background: #fff;
+        padding: 0 5px 0 12px;
+        vertical-align: middle;
+        color: #666;
+        -webkit-transition: all 0.3s ease-in;
+        -moz-transition: all 0.3s ease-in;
+        transition: all 0.3s ease-in;
+      }
+      .tags-li:not(.active):hover {
+        background: #f8f8f8;
+      }
+      .tags-li.active {
+        color: #fff;
+      }
+      .tags-li-title {
+        float: left;
+        max-width: 80px;
+        overflow: hidden;
+        white-space: nowrap;
+        text-overflow: ellipsis;
+        margin-right: 5px;
+        color: #666;
+      }
+      .tags-li.active .tags-li-title {
+        color: #fff;
+      }
+      // .tags-li.active {
+      //   // border: 1px solid #409eff;
+      //   // background-color: #409eff;
+      // }
+    }
+    .right {
+      position: absolute;
+      right: 0;
+      top: 0;
+      box-sizing: border-box;
+      padding-top: 1px;
+      text-align: center;
+      width: 110px;
+      height: 30px;
+      background: #fff;
+      box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
+      z-index: 10;
+    }
+  }
+}
+</style>

+ 33 - 0
src/components/admin-frame/breadcrumb.vue

@@ -0,0 +1,33 @@
+<template>
+  <div id="breadcrumb">
+    <el-row>
+      <el-col :span="24" class="crumbs">
+        <el-breadcrumb separator="/">
+          <el-breadcrumb-item> <i class="el-icon-s-grid"></i> {{ breadcrumbTitle }} </el-breadcrumb-item>
+        </el-breadcrumb>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  metaInfo: {},
+  name: 'breadcrumb',
+  props: {
+    breadcrumbTitle: { type: String },
+  },
+  components: {},
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 6 - 0
src/components/admin-frame/bus.js

@@ -0,0 +1,6 @@
+import Vue from 'vue';
+
+// 使用 Event Bus
+const bus = new Vue();
+
+export default bus;

+ 79 - 0
src/components/admin-frame/directives.js

@@ -0,0 +1,79 @@
+import Vue from 'vue';
+
+// v-dialogDrag: 弹窗拖拽属性
+Vue.directive('dialogDrag', {
+  bind(el, binding, vnode, oldVnode) {
+    const dialogHeaderEl = el.querySelector('.el-dialog__header');
+    const dragDom = el.querySelector('.el-dialog');
+
+    dialogHeaderEl.style.cssText += ';cursor:move;';
+    dragDom.style.cssText += ';top:0px;';
+
+    // 获取原有属性 ie dom元素.currentStyle 火狐谷歌 window.getComputedStyle(dom元素, null);
+    const sty = (() => {
+      if (window.document.currentStyle) {
+        return (dom, attr) => dom.currentStyle[attr];
+      } else {
+        return (dom, attr) => getComputedStyle(dom, false)[attr];
+      }
+    })();
+
+    dialogHeaderEl.onmousedown = (e) => {
+      // 鼠标按下,计算当前元素距离可视区的距离
+      const disX = e.clientX - dialogHeaderEl.offsetLeft;
+      const disY = e.clientY - dialogHeaderEl.offsetTop;
+
+      const screenWidth = document.body.clientWidth; // body当前宽度
+      const screenHeight = document.documentElement.clientHeight; // 可见区域高度(应为body高度,可某些环境下无法获取)
+
+      const dragDomWidth = dragDom.offsetWidth; // 对话框宽度
+      const dragDomheight = dragDom.offsetHeight; // 对话框高度
+
+      const minDragDomLeft = dragDom.offsetLeft;
+      const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
+
+      const minDragDomTop = dragDom.offsetTop;
+      const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
+
+      // 获取到的值带px 正则匹配替换
+      let styL = sty(dragDom, 'left');
+      let styT = sty(dragDom, 'top');
+
+      // 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
+      if (styL.includes('%')) {
+        styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
+        styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
+      } else {
+        styL = +styL.replace(/\px/g, '');
+        styT = +styT.replace(/\px/g, '');
+      }
+
+      document.onmousemove = function (e) {
+        // 通过事件委托,计算移动的距离
+        let left = e.clientX - disX;
+        let top = e.clientY - disY;
+
+        // 边界处理
+        if (-left > minDragDomLeft) {
+          left = -minDragDomLeft;
+        } else if (left > maxDragDomLeft) {
+          left = maxDragDomLeft;
+        }
+
+        if (-top > minDragDomTop) {
+          top = -minDragDomTop;
+        } else if (top > maxDragDomTop) {
+          top = maxDragDomTop;
+        }
+
+        // 移动当前元素
+        dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
+      };
+
+      document.onmouseup = function (e) {
+        document.onmousemove = null;
+        document.onmouseup = null;
+      };
+    };
+  },
+});

+ 30 - 0
src/components/admin-frame/i18n.js

@@ -0,0 +1,30 @@
+export const messages = {
+  zh: {
+    i18n: {
+      breadcrumb: '国际化产品',
+      tips: '通过切换语言按钮,来改变当前内容的语言。',
+      btn: '切换英文',
+      title1: '常用用法',
+      p1: '要是你把你的秘密告诉了风,那就别怪风把它带给树。',
+      p2: '没有什么比信念更能支撑我们度过艰难的时光了。',
+      p3: '只要能把自己的事做好,并让自己快乐,你就领先于大多数人了。',
+      title2: '组件插值',
+      info: 'Element组件需要国际化,请参考 {action}。',
+      value: '文档',
+    },
+  },
+  en: {
+    i18n: {
+      breadcrumb: 'International Products',
+      tips: 'Click on the button to change the current language. ',
+      btn: 'Switch Chinese',
+      title1: 'Common usage',
+      p1: 'If you reveal your secrets to the wind you should not blame the wind for  revealing them to the trees.',
+      p2: 'Nothing can help us endure dark times better than our faith. ',
+      p3: "If you can do what you do best and be happy, you're further along in life  than most people.",
+      title2: 'Component interpolation',
+      info: 'The default language of Element is Chinese. If you wish to use another language, please refer to the {action}.',
+      value: 'documentation',
+    },
+  },
+};

+ 73 - 0
src/components/admin-frame/parts/password-1.vue

@@ -0,0 +1,73 @@
+<template>
+  <div id="password-1">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <el-form :model="form" :rules="rules" ref="form" label-width="100px">
+            <el-form-item label="旧密码" prop="oldPassword">
+              <el-input v-model="form.oldPassword" type="password" placeholder="请输入旧密码"></el-input>
+            </el-form-item>
+            <el-form-item label="新密码" prop="newPassword">
+              <el-input v-model="form.newPassword" type="password" placeholder="请输入新密码"></el-input>
+            </el-form-item>
+            <el-form-item>
+              <el-button type="danger" size="small" @click="onReset('form')">重置信息</el-button>
+              <el-button type="success" size="small" @click="onSubmit('form')">提交修改</el-button>
+            </el-form-item>
+          </el-form>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'password-1',
+  props: {
+    form: { type: Object },
+  },
+  components: {},
+  data: function() {
+    return {
+      rules: {
+        oldPassword: [{ required: true, message: '请输入旧密码', trigger: 'blur' }],
+        newPassword: [{ required: true, message: '请输入新密码', trigger: 'blur' }],
+      },
+    };
+  },
+  created() {},
+  methods: {
+    onReset(formName) {
+      this.$refs[formName].resetFields();
+    },
+
+    onSubmit(formName) {
+      this.$refs[formName].validate(valid => {
+        if (valid) {
+          this.$emit('onSubmit', { data: this.form });
+        } else {
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 171 - 0
src/components/admin-frame/备份/Header copy.vue

@@ -0,0 +1,171 @@
+<template>
+  <div class="header">
+    <!-- 折叠按钮 -->
+    <div class="collapse-btn" @click="collapseChage">
+      <i v-if="!collapse" class="el-icon-s-fold"></i>
+      <i v-else class="el-icon-s-unfold"></i>
+    </div>
+    <div class="logo">管理中心</div>
+    <div class="header-right">
+      <div class="header-user-con">
+        <!-- 全屏显示 -->
+        <div class="btn-fullscreen" @click="handleFullScreen">
+          <el-tooltip effect="dark" :content="fullscreen ? `取消全屏` : `全屏`" placement="bottom">
+            <i class="el-icon-rank"></i>
+          </el-tooltip>
+        </div>
+        <!-- 用户头像 -->
+        <div class="user-avator">
+          <img src="../../assets/img/img.jpg" alt="" />
+        </div>
+        <!-- 用户名下拉菜单 -->
+        <el-dropdown class="user-name" trigger="click" @command="handleCommand">
+          <span class="el-dropdown-link">
+            {{ user.name || '游客' }}
+            <i class="el-icon-caret-bottom"></i>
+          </span>
+          <el-dropdown-menu slot="dropdown">
+            <el-dropdown-item divided command="loginout">退出登录</el-dropdown-item>
+          </el-dropdown-menu>
+        </el-dropdown>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import bus from './bus';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  data() {
+    return {
+      collapse: false,
+      fullscreen: false,
+    };
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  methods: {
+    // 用户名下拉菜单选择事件
+    handleCommand(command) {
+      if (command == 'loginout') {
+        localStorage.removeItem('token');
+        this.$router.push('/login');
+      }
+    },
+    // 侧边栏折叠
+    collapseChage() {
+      this.collapse = !this.collapse;
+      bus.$emit('collapse', this.collapse);
+    },
+    // 全屏事件
+    handleFullScreen() {
+      let element = document.documentElement;
+      if (this.fullscreen) {
+        if (document.exitFullscreen) {
+          document.exitFullscreen();
+        } else if (document.webkitCancelFullScreen) {
+          document.webkitCancelFullScreen();
+        } else if (document.mozCancelFullScreen) {
+          document.mozCancelFullScreen();
+        } else if (document.msExitFullscreen) {
+          document.msExitFullscreen();
+        }
+      } else {
+        if (element.requestFullscreen) {
+          element.requestFullscreen();
+        } else if (element.webkitRequestFullScreen) {
+          element.webkitRequestFullScreen();
+        } else if (element.mozRequestFullScreen) {
+          element.mozRequestFullScreen();
+        } else if (element.msRequestFullscreen) {
+          // IE11
+          element.msRequestFullscreen();
+        }
+      }
+      this.fullscreen = !this.fullscreen;
+    },
+  },
+  mounted() {
+    if (document.body.clientWidth < 1500) {
+      this.collapseChage();
+    }
+  },
+};
+</script>
+<style scoped>
+.header {
+  position: relative;
+  box-sizing: border-box;
+  width: 100%;
+  height: 60px;
+  font-size: 22px;
+  color: #fff;
+}
+.collapse-btn {
+  float: left;
+  padding: 0 21px;
+  cursor: pointer;
+  line-height: 60px;
+}
+.header .logo {
+  float: left;
+  width: 25%;
+  line-height: 60px;
+}
+.header-right {
+  float: right;
+  padding-right: 50px;
+}
+.header-user-con {
+  display: flex;
+  height: 60px;
+  align-items: center;
+}
+.btn-fullscreen {
+  transform: rotate(45deg);
+  margin-right: 5px;
+  font-size: 24px;
+}
+.btn-bell,
+.btn-fullscreen {
+  position: relative;
+  width: 30px;
+  height: 30px;
+  text-align: center;
+  border-radius: 15px;
+  cursor: pointer;
+}
+.btn-bell-badge {
+  position: absolute;
+  right: 0;
+  top: -2px;
+  width: 8px;
+  height: 8px;
+  border-radius: 4px;
+  background: #f56c6c;
+  color: #fff;
+}
+.btn-bell .el-icon-bell {
+  color: #fff;
+}
+.user-name {
+  margin-left: 10px;
+}
+.user-avator {
+  margin-left: 20px;
+}
+.user-avator img {
+  display: block;
+  width: 40px;
+  height: 40px;
+  border-radius: 50%;
+}
+.el-dropdown-link {
+  color: #fff;
+  cursor: pointer;
+}
+.el-dropdown-menu__item {
+  text-align: center;
+}
+</style>

+ 66 - 0
src/components/admin-frame/备份/Home copy.vue

@@ -0,0 +1,66 @@
+<template>
+  <el-container>
+    <el-header style="padding: 0">
+      <v-head></v-head>
+    </el-header>
+    <el-container>
+      <el-aside width="200px">
+        <v-sidebar></v-sidebar>
+      </el-aside>
+      <el-main>
+        <div class="content-box" :class="{ 'content-collapse': collapse }">
+          <v-tags></v-tags>
+          <div class="content">
+            <transition name="move" mode="out-in">
+              <el-row>
+                <el-col :span="24">
+                  <breadcrumb :breadcrumbTitle="this.$route.meta.title"></breadcrumb>
+                  <el-col :span="24" class="container">
+                    <router-view></router-view>
+                  </el-col>
+                </el-col>
+              </el-row>
+            </transition>
+            <el-backtop target=".content"></el-backtop>
+          </div>
+        </div>
+      </el-main>
+    </el-container>
+  </el-container>
+</template>
+
+<script>
+import breadcrumb from './breadcrumb.vue';
+import vHead from './Header.vue';
+import vSidebar from './Sidebar.vue';
+import vTags from './Tags.vue';
+import bus from './bus';
+export default {
+  data() {
+    return {
+      tagsList: [],
+      collapse: false,
+    };
+  },
+  components: {
+    vHead,
+    vSidebar,
+    vTags,
+    breadcrumb,
+  },
+  created() {
+    bus.$on('collapse-content', msg => {
+      this.collapse = msg;
+    });
+
+    // 只有在标签页列表里的页面才使用keep-alive,即关闭标签之后就不保存到内存中了。
+    bus.$on('tags', msg => {
+      let arr = [];
+      for (let i = 0, len = msg.length; i < len; i++) {
+        msg[i].name && arr.push(msg[i].name);
+      }
+      this.tagsList = arr;
+    });
+  },
+};
+</script>

+ 121 - 0
src/components/admin-frame/备份/Sidebar copy.vue

@@ -0,0 +1,121 @@
+<template>
+  <div class="sidebar">
+    <el-menu
+      class="sidebar-el-menu"
+      :default-active="onRoutes"
+      :collapse="collapse"
+      background-color="#324157"
+      text-color="#bfcbd9"
+      active-text-color="#20a0ff"
+      unique-opened
+      router
+    >
+      <template v-for="item in items">
+        <template v-if="item.subs">
+          <el-submenu class="second" :index="item.index" :key="item.index">
+            <template slot="title">
+              <i :class="item.icon"></i>
+              <span slot="title">{{ item.title }}</span>
+            </template>
+            <template v-for="subItem in item.subs">
+              <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
+                <template slot="title">{{ subItem.title }}</template>
+                <el-menu-item v-for="(threeItem, i) in subItem.subs" :key="i" :index="threeItem.index">{{ threeItem.title }}</el-menu-item>
+              </el-submenu>
+              <el-menu-item v-else :index="subItem.index" :key="subItem.index">{{ subItem.title }}</el-menu-item>
+            </template>
+          </el-submenu>
+        </template>
+        <template v-else>
+          <el-menu-item class="first" :index="item.index" :key="item.index">
+            <i :class="item.icon"></i>
+            <span slot="title">{{ item.title }}</span>
+          </el-menu-item>
+        </template>
+      </template>
+    </el-menu>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+import { mapState, createNamespacedHelpers } from 'vuex';
+import bus from './bus';
+export default {
+  data() {
+    return {
+      collapse: false,
+      items: [
+        {
+          icon: 'el-icon-s-home',
+          index: '/adminCenter/homeIndex',
+          title: '系统首页',
+        },
+      ],
+    };
+  },
+  computed: {
+    ...mapState(['user']),
+    onRoutes() {
+      return this.$route.path.replace('/', '');
+    },
+  },
+  created() {
+    // 通过 Event Bus 进行组件间通信,来折叠侧边栏
+    bus.$on('collapse', msg => {
+      this.collapse = msg;
+      bus.$emit('collapse-content', msg);
+    });
+  },
+  methods: {
+    // 分配用户彩带权限
+    async getMenu() {
+      let list = _.cloneDeep(this.items);
+      let data = [
+        {
+          icon: 'el-icon-s-home',
+          index: '/adminCenter/test/index',
+          title: '测试页面',
+        },
+      ];
+      list.push(...data);
+      this.$set(this, `items`, _.uniqBy(list, 'index'));
+    },
+  },
+  watch: {
+    user: {
+      deep: true,
+      immediate: true,
+      handler(val) {
+        this.getMenu();
+      },
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.sidebar {
+  display: block;
+  position: absolute;
+  left: 0;
+  top: 60px;
+  bottom: 0;
+  overflow-y: scroll;
+}
+.sidebar::-webkit-scrollbar {
+  width: 0;
+}
+.sidebar-el-menu:not(.el-menu--collapse) {
+  width: 200px;
+}
+.sidebar > ul {
+  height: 100%;
+}
+// /deep/.first {
+//   padding-left: 0 !important;
+// }
+// /deep/.second .el-submenu__title {
+//   padding-left: 0 !important;
+// }
+</style>

+ 181 - 0
src/components/admin-frame/备份/Tags copy.vue

@@ -0,0 +1,181 @@
+<template>
+  <div class="tags" v-if="showTags">
+    <ul>
+      <li class="tags-li" v-for="(item, index) in tagsList" :class="{ active: isActive(item.path) }" :key="index">
+        <router-link :to="item.path" class="tags-li-title">
+          {{ item.title }}
+        </router-link>
+        <span class="tags-li-icon" @click="closeTags(index)"><i class="el-icon-close"></i></span>
+      </li>
+    </ul>
+    <div class="tags-close-box">
+      <el-dropdown @command="handleTags">
+        <el-button size="mini" type="primary"> 标签选项<i class="el-icon-arrow-down el-icon--right"></i> </el-button>
+        <el-dropdown-menu size="small" slot="dropdown">
+          <el-dropdown-item command="other">关闭其他</el-dropdown-item>
+          <el-dropdown-item command="all">关闭所有</el-dropdown-item>
+        </el-dropdown-menu>
+      </el-dropdown>
+    </div>
+  </div>
+</template>
+
+<script>
+import bus from './bus';
+export default {
+  data() {
+    return {
+      tagsList: [],
+    };
+  },
+  methods: {
+    isActive(path) {
+      return path === this.$route.fullPath;
+    },
+    // 关闭单个标签
+    closeTags(index) {
+      const delItem = this.tagsList.splice(index, 1)[0];
+      const item = this.tagsList[index] ? this.tagsList[index] : this.tagsList[index - 1];
+      if (item) {
+        delItem.path === this.$route.fullPath && this.$router.push(item.path);
+      } else {
+        this.$router.push('/');
+      }
+    },
+    // 关闭全部标签
+    closeAll() {
+      this.tagsList = [];
+      this.$router.push('/');
+    },
+    // 关闭其他标签
+    closeOther() {
+      const curItem = this.tagsList.filter(item => {
+        return item.path === this.$route.fullPath;
+      });
+      this.tagsList = curItem;
+    },
+    // 设置标签
+    setTags(route) {
+      const isExist = this.tagsList.some(item => {
+        return item.path === route.fullPath;
+      });
+      if (!isExist) {
+        if (this.tagsList.length >= 8) {
+          this.tagsList.shift();
+        }
+        this.tagsList.push({
+          title: route.meta.title,
+          path: route.fullPath,
+          name: route.matched[1].components.default.name,
+        });
+      }
+      bus.$emit('tags', this.tagsList);
+    },
+    handleTags(command) {
+      command === 'other' ? this.closeOther() : this.closeAll();
+    },
+  },
+  computed: {
+    showTags() {
+      return this.tagsList.length > 0;
+    },
+  },
+  watch: {
+    $route(newValue, oldValue) {
+      this.setTags(newValue);
+    },
+  },
+  created() {
+    this.setTags(this.$route);
+    // 监听关闭当前页面的标签页
+    bus.$on('close_current_tags', () => {
+      for (let i = 0, len = this.tagsList.length; i < len; i++) {
+        const item = this.tagsList[i];
+        if (item.path === this.$route.fullPath) {
+          if (i < len - 1) {
+            this.$router.push(this.tagsList[i + 1].path);
+          } else if (i > 0) {
+            this.$router.push(this.tagsList[i - 1].path);
+          } else {
+            this.$router.push('/');
+          }
+          this.tagsList.splice(i, 1);
+          break;
+        }
+      }
+    });
+  },
+};
+</script>
+
+<style>
+.tags {
+  position: relative;
+  height: 30px;
+  overflow: hidden;
+  background: #fff;
+  padding-right: 120px;
+  box-shadow: 0 5px 10px #ddd;
+}
+
+.tags ul {
+  box-sizing: border-box;
+  width: 100%;
+  height: 100%;
+}
+
+.tags-li {
+  float: left;
+  margin: 3px 5px 2px 3px;
+  border-radius: 3px;
+  font-size: 12px;
+  overflow: hidden;
+  cursor: pointer;
+  height: 23px;
+  line-height: 23px;
+  border: 1px solid #e9eaec;
+  background: #fff;
+  padding: 0 5px 0 12px;
+  vertical-align: middle;
+  color: #666;
+  -webkit-transition: all 0.3s ease-in;
+  -moz-transition: all 0.3s ease-in;
+  transition: all 0.3s ease-in;
+}
+
+.tags-li:not(.active):hover {
+  background: #f8f8f8;
+}
+
+.tags-li.active {
+  color: #fff;
+}
+
+.tags-li-title {
+  float: left;
+  max-width: 80px;
+  overflow: hidden;
+  white-space: nowrap;
+  text-overflow: ellipsis;
+  margin-right: 5px;
+  color: #666;
+}
+
+.tags-li.active .tags-li-title {
+  color: #fff;
+}
+
+.tags-close-box {
+  position: absolute;
+  right: 0;
+  top: 0;
+  box-sizing: border-box;
+  padding-top: 1px;
+  text-align: center;
+  width: 110px;
+  height: 30px;
+  background: #fff;
+  box-shadow: -3px 0 15px 3px rgba(0, 0, 0, 0.1);
+  z-index: 10;
+}
+</style>

+ 66 - 0
src/components/frame/e-dateRange.vue

@@ -0,0 +1,66 @@
+<template>
+  <div id="e-timeRange">
+    <el-date-picker
+      :value="range"
+      type="daterange"
+      range-separator="至"
+      start-placeholder="开始日期"
+      end-placeholder="结束日期"
+      :picker-options="pickerOptions"
+      clearable
+    >
+    </el-date-picker>
+  </div>
+</template>
+
+<script>
+/**
+ * 注意:使用前,需要axios-wrapper.js中有处理范围的函数checkRangeQuery(搜索函数名应该有2处)
+ * 2个参数
+ * format:数据格式化,以moment.format()方式进行格式化
+ * value:v-model;Object类型,内含start,end两个参数,要不都有,要不都没有;只有一个不会处理
+ */
+const moment = require('moment');
+export default {
+  name: 'e-timeRange',
+  props: {
+    format: { type: String, default: 'YYYY-MM-DD' },
+    value: { type: Object, default: () => {} },
+  },
+  model: {
+    prop: 'value',
+    event: 'change',
+  },
+  components: {},
+  data: function() {
+    return {
+      pickerOptions: {
+        onPick: this.toSelect, //选择后进行处理的函数
+      },
+    };
+  },
+  created() {},
+  methods: {
+    // 选择时间的处理函数
+    toSelect({ minDate, maxDate }) {
+      if (!minDate || !maxDate) return;
+      var start = moment(minDate).format(this.format);
+      var end = moment(maxDate).format(this.format);
+      const obj = { start, end };
+      this.$emit('change', obj);
+    },
+  },
+  computed: {
+    // range是根据v-model而来,这样就兼顾了初始 有/无 值的情况
+    range() {
+      let arr = [];
+      if (this.value && this.value.start && this.value.end) {
+        arr.push(this.value.start, this.value.end);
+      }
+      return arr;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 96 - 0
src/components/frame/e-upload.vue

@@ -0,0 +1,96 @@
+<template>
+  <div id="e-upload">
+    <el-upload
+      :action="url"
+      :http-request="upload"
+      :list-type="type"
+      :fileList="fileList"
+      :on-remove="onRemove"
+      :on-error="onError"
+      :on-preview="onPreview"
+      :limit="limit"
+      :on-exceed="onExceed"
+      :before-remove="beforeRemove"
+    >
+      <el-button slot="trigger" size="small" type="primary">选取文件</el-button>
+    </el-upload>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import axios from 'axios';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'e-upload',
+  props: {
+    url: { type: String, required: true },
+    type: { type: String, default: 'picture-card' },
+    fileList: { type: Array },
+    limit: { type: Number },
+    remove: { type: Function },
+    extra: { type: Object, default: () => {} },
+  },
+  model: {
+    prop: 'fileList',
+    event: 'change',
+  },
+  components: {},
+  created() {},
+  methods: {
+    async upload({ file }) {
+      let formdata = new FormData();
+      formdata.append('file', file, file.name);
+      const res = await axios.post(this.url, formdata, {
+        headers: { 'Content-Type': 'multipart/form-data' },
+      });
+      if (res.status === 200 && res.data.errcode == 0) {
+        const { id, name, uri } = res.data;
+        const obj = { url: uri, name: file.name, ...this.extra, uid: id };
+        this.fileList.push(obj);
+      }
+    },
+    beforeRemove(file) {
+      if (this.remove && _.isFunction(this.remove)) {
+        const { result, msg } = this.remove(file);
+        if (!result) {
+          this.$message.error(msg);
+          return false;
+        }
+      }
+      return true;
+    },
+    onRemove(file) {
+      const index = this.fileList.findIndex(f => f.url === file.url);
+      this.fileList.splice(index, 1);
+    },
+    onError(err) {
+      this.$message.error(err);
+    },
+    // 点击事件
+    onPreview(file) {
+      const { url } = file;
+      window.open(url);
+    },
+    // 超出限制
+    onExceed() {
+      this.$message.error(`只能上传${this.limit}个文件`);
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+/deep/.el-upload-list__item.is-ready {
+  display: none;
+}
+</style>

+ 52 - 0
src/components/frame/filter-page-table.md

@@ -0,0 +1,52 @@
+## filter-page-table.vue
+#### prop
+|参数|类型|默认值|是否必填|说明|
+|:-:|:-:|:-:|:-:|:-:|
+|fields|Array|`-`|是|字段列表(下文会说明如何使用)|
+|data|Array|`-`|是|数据列表|
+|opera|Array|[ ]|否|操作列的列表(下文会说明如何使用)|
+|toFormat|Function|`-`|否|如果fields中的format不是function类型,则会走toFormat的方法,需要自己写过滤规则,多个的情况需要区分|
+|select|Boolean|false|否|需要选择就变成true|
+|total|NUmber|0|否|分页的总数据|
+|usePage|Boolean|true|否|是否使用分页|
+|options|Object|null|否|加些属性,不知道能加啥,反正我把合计加上好使了|
+|useSum|Boolean|false|否|使用合计|
+|sumcol|Array|`[]`|否|计算哪一列,就把哪一列的prop写进去|
+|sumres|String|`total`|否|处理每列结果的要求,默认计算总和(total),平均值(avg),最大值(max),最小值(min)|
+|filter|Array|`[]`|否|额外查询|
+|operaWidth|Number|200|否|操作栏宽度|
+
+>fields
+>>
+|参数|类型|默认值|是否必填|说明|
+|:-:|:-:|:-:|:-:|:-:|
+|label|String|`-`|是|列名称|
+|prop|String|`-`|是|字段名称|
+|format|Function/String|`-`|否|Function类型:数据需要过滤则将过滤方法写在这;String类型:走toFormat方法,参数位(model=>字段名,value=>值)|
+|custom|Boolean|false|否|自定义输出|
+|options|Object|`-`|否|添加额外属性,比如说样式之类的|
+|filter|String|`-`|否|如果填写,则这个字段会查询,这里只填写类型,input/select,select的选项在options插槽中使用|
+|selected|Array|`-`|false|多选选项的数据|
+|showTip|Boolean|false|否|是否使用tooltip显示过长内容|
+|filterReturn|Boolean|`-`|否|针对这个选项需要在选择后就做些逻辑处理时,改成true,然后再使用filterReturn方法处理,(例如二级联动的情况)|
+|notable|Boolean|false/undefined|否|不需要在表格中显示|
+|selected|Array|`-`|false|多选选项的数据|
+
+>opera
+>>
+|参数|类型|默认值|是否必填|说明|
+|:-:|:-:|:-:|:-:|:-:|
+|label|String|`-`|是|操作按钮提示文字|
+|icon|String|`-`|否|图标|
+|method|String|`-`|是|此按钮连接的父级方法($emit)|
+|confirm|Boolean|`-`|否|是否需要确认提示|
+|methodZh|String/Function|label|否|确认提示的操作文字,1,Function参数为这条数据,自己随意组合;2,String为纯自定义字符串,需要自己写整个提示语;3,默认,使用label字段提示|
+|display|Function|`-`|否|控制按钮是否显示(目前为简单版,只是根据此条数据中的内容判断,以后要是有需求会修改成toFormat的形式)|
+
+>methods
+>>
+|方法名|参数|说明|
+|:-:|:-:|:-:|
+|handleSelect|Array[object]|返回选择的内容|
+|query|{skip,limit,...info}|分页查询,及条件查询|
+|filterReturn|{data,prop}|查询条件栏过滤条件中filterReturn字段为true的回调方法|

+ 414 - 0
src/components/frame/filter-page-table.vue

@@ -0,0 +1,414 @@
+<template>
+  <div id="data-table">
+    <el-row type="flex" justify="end">
+      <el-col v-if="isMobile" :span="20">
+        <el-form :model="searchInfo" :inline="true" style="padding: 0.9rem 1.875rem" size="mini" v-if="useFilter">
+          <el-form-item v-for="(item, index) in filterList" :key="index">
+            <template v-if="item.filter === 'select'">
+              <el-select
+                v-model="searchInfo[item.model]"
+                size="mini"
+                clearable
+                filterable
+                :placeholder="`请选择${item.label}`"
+                @clear="toClear(item.model)"
+                @change="data => filterReturn(data, item)"
+              >
+                <slot name="options" v-bind="{ item }"></slot>
+              </el-select>
+            </template>
+            <template v-else-if="item.filter === 'date'">
+              <el-date-picker
+                v-model="searchInfo[item.model]"
+                value-format="yyyy-MM-dd"
+                format="yyyy-MM-dd"
+                type="daterange"
+                range-separator="-"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                clearable
+              >
+              </el-date-picker>
+            </template>
+            <template v-else>
+              <el-input v-model="searchInfo[item.model]" clearable size="mini" :placeholder="`请输入${item.label}`" @clear="toClear(item.model)"></el-input>
+            </template>
+          </el-form-item>
+          <el-form-item>
+            <el-button type="primary" size="mini" @click="filterSearch">查询</el-button>
+          </el-form-item>
+        </el-form>
+      </el-col>
+      <el-col :span="4" style="text-align: right; padding: 0.9rem 0">
+        <slot name="btn"></slot>
+      </el-col>
+    </el-row>
+
+    <el-table
+      ref="table"
+      row-key="id"
+      :data="data"
+      border
+      stripe
+      size="mini"
+      :max-height="height !== null ? height : ''"
+      @select="handleSelectionChange"
+      @select-all="handleSelectAll"
+      v-bind="options"
+      :show-summary="useSum"
+      @row-click="rowClick"
+      :summary-method="computedSum"
+    >
+      <el-table-column type="selection" width="55" v-if="select" prop="id" :reserve-selection="true"> </el-table-column>
+      <template v-for="(item, index) in fields">
+        <template v-if="!item.notable">
+          <template v-if="item.custom">
+            <el-table-column
+              :key="index"
+              align="center"
+              :label="item.label"
+              v-bind="item.options"
+              :show-overflow-tooltip="item.showTip || true"
+              :width="item.width"
+            >
+              <template v-slot="{ row }">
+                <slot name="custom" v-bind="{ item, row }"></slot>
+              </template>
+            </el-table-column>
+          </template>
+          <template v-else>
+            <el-table-column
+              :key="index"
+              align="center"
+              :label="item.label"
+              :prop="item.model"
+              :width="item.width"
+              :formatter="toFormatter"
+              :sortable="isMobile"
+              v-bind="item.options"
+              :show-overflow-tooltip="item.showTip || true"
+            >
+            </el-table-column>
+          </template>
+        </template>
+      </template>
+      <template v-if="opera.length > 0">
+        <el-table-column label="操作" align="center" :width="operaWidth">
+          <template v-slot="{ row, $index }">
+            <template v-for="(item, index) in opera">
+              <template v-if="display(item, row)">
+                <el-tooltip v-if="item.icon" :key="index" effect="dark" :content="item.label" placement="bottom">
+                  <el-button
+                    :key="index"
+                    type="text"
+                    :icon="item.icon || ''"
+                    size="mini"
+                    @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)"
+                  ></el-button>
+                </el-tooltip>
+                <!-- <el-button v-else :key="index" type="text" size="mini" @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)">
+                  {{ item.label }}
+                </el-button> -->
+                <el-link
+                  v-else
+                  :key="`${item.model}-column-${index}`"
+                  :type="item.type || 'primary'"
+                  :icon="item.icon || ''"
+                  size="mini"
+                  style="padding-right: 10px"
+                  :underline="false"
+                  @click="handleOpera(row, item.method, item.confirm, item.methodZh, item.label, $index)"
+                >
+                  {{ item.label }}
+                </el-link>
+              </template>
+            </template>
+          </template>
+        </el-table-column>
+      </template>
+    </el-table>
+    <el-row type="flex" align="middle" justify="end" style="padding-top: 1rem" v-if="usePage">
+      <el-col :span="24" style="text-align: right">
+        <el-pagination
+          background
+          layout="sizes,total, prev, pager, next"
+          :total="total"
+          :page-size="limit"
+          :current-page.sync="currentPage"
+          @current-change="changePage"
+          :page-sizes="[5, 10, 15, 20, 50, 100]"
+          @size-change="sizeChange"
+        >
+        </el-pagination>
+        <!-- sizes -->
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'data-table',
+  props: {
+    fields: { type: Array, required: true },
+    data: { type: Array, required: true },
+    opera: { type: Array, default: () => [] },
+    toFormat: null,
+    height: null,
+    select: { type: Boolean, default: false },
+    selected: { type: Array, default: () => [] },
+    usePage: { type: Boolean, default: true },
+    total: { type: Number, default: 0 },
+    options: null,
+    useSum: { type: Boolean, default: false },
+    sumcol: { type: Array, default: () => [] },
+    sumres: { type: String, default: 'total' },
+    filter: { type: Array, default: () => [] },
+    operaWidth: { type: String, default: '200' },
+    limit: { type: Number, default: _.get(this, `$limit`, undefined) !== undefined ? this.$limit : process.env.VUE_APP_LIMIT * 1 || 10 },
+    pageMode: { type: String, default: 'page' }, // 使用page方式计算分页; skip:使用skip方式计算分页
+  },
+  components: {},
+  data: () => ({
+    pageSelected: [],
+    currentPage: 1,
+    // limit: _.get(this, `$limit`, undefined) !== undefined ? this.$limit : process.env.VUE_APP_LIMIT * 1 || 10,
+    searchInfo: {},
+    useFilter: true,
+    filterList: [],
+  }),
+  created() {},
+  computed: {},
+  methods: {
+    toFormatter(row, column, cellValue, index) {
+      let this_fields = this.fields.filter(fil => fil.model === column.property);
+      if (this_fields.length > 0) {
+        let format = _.get(this_fields[0], `format`, false);
+        if (format) {
+          let res;
+          if (_.isFunction(format)) {
+            res = format(cellValue);
+          } else {
+            res = this.toFormat({
+              model: this_fields[0].model,
+              value: cellValue,
+            });
+          }
+          return res;
+        } else return cellValue;
+      }
+    },
+    handleOpera(data, method, confirm = false, methodZh, label, index) {
+      let self = true;
+      if (_.isFunction(methodZh)) {
+        methodZh = methodZh(data);
+      } else if (!_.isString(methodZh)) {
+        methodZh = label;
+        self = false;
+      }
+      if (confirm) {
+        this.$confirm(self ? methodZh : `您确认${methodZh}该数据?`, '提示', {
+          confirmButtonText: '确定',
+          cancelButtonText: '取消',
+          type: 'warning',
+        })
+          .then(() => {
+            this.$emit(method, { data, index });
+          })
+          .catch(() => {});
+      } else {
+        this.$emit(method, { data, index });
+      }
+    },
+    handleSelectionChange(selection, row) {
+      // console.log(selection);
+      // console.log(row);
+      //根据row是否再pageSelected中,判断是添加还是删除
+      let res = [];
+      if (this.pageSelected.find(i => i.id === row.id)) {
+        res = this.pageSelected.filter(f => f.id !== row.id);
+      } else {
+        this.pageSelected.push(row);
+        res = this.pageSelected;
+      }
+      this.$set(this, `pageSelected`, res);
+      this.$emit(`handleSelect`, _.uniqBy(res, 'id'));
+    },
+    handleSelectAll(selection) {
+      //处于没全选状态,选择之后一定是全选,只有处于全选状态时,才会反选(全取消)
+      // console.log(selection);
+      let res = [];
+      if (selection.length > 0) {
+        //全选
+        res = _.uniqBy(this.pageSelected.concat(selection), 'id');
+      } else {
+        //全取消
+        res = _.differenceBy(this.pageSelected, this.data, 'id');
+      }
+      this.$set(this, `pageSelected`, res);
+      this.$emit(`handleSelect`, res);
+    },
+    initSelection() {
+      this.$nextTick(() => {
+        this.$refs.table.clearSelection();
+        this.selected.forEach(info => {
+          let d = this.data.filter(p => p.id === info.id);
+          if (d.length > 0) this.$refs.table.toggleRowSelection(d[0]);
+        });
+      });
+    },
+    selectReset() {
+      this.$refs.table.clearSelection();
+    },
+    display(item, row) {
+      let display = _.get(item, `display`, true);
+      if (display === true) return true;
+      else {
+        let res = display(row);
+        return res;
+      }
+    },
+    //
+    changePage(page = this.currentPage) {
+      if (this.pageMode === 'page') this.$emit('query', { skip: page, limit: this.limit, ...this.searchInfo });
+      else this.$emit('query', { skip: (page - 1) * this.limit, limit: this.limit, ...this.searchInfo });
+    },
+    sizeChange(limit) {
+      this.limit = limit;
+      this.currentPage = 1;
+      if (this.pageMode === 'page') this.$emit('query', { skip: 1, limit: this.limit, ...this.searchInfo });
+      else this.$emit('query', { skip: 0, limit: this.limit, ...this.searchInfo });
+    },
+    getFilterList() {
+      let res = this.fields.filter(f => _.get(f, 'filter', false));
+      this.$set(this, `useFilter`, res.length > 0);
+      res.map(i => {
+        if (i.filter === 'date' && this.searchInfo[i.porp] === undefined) this.$set(this.searchInfo, i.model, []);
+      });
+      res = [...res, ...this.filter];
+      this.$set(this, `filterList`, res);
+    },
+    filterSearch() {
+      this.currentPage = 1;
+      const query = _.cloneDeep(this.searchInfo);
+      const dateType = this.fields.filter(f => f.filter === 'date');
+      if (dateType.length > 0) {
+        for (const i of dateType) {
+          const { model } = i;
+          const data = query[model];
+          if (data && _.isArray(data) && data.length == 2) {
+            query[`${model}@start`] = data[0];
+            query[`${model}@end`] = data[1];
+          }
+          delete query[model];
+        }
+      }
+      this.$emit('query', { skip: 0, limit: this.limit, ...query });
+    },
+    rowClick(row, column, event) {
+      this.$emit(`rowClick`, row);
+    },
+    toClear(model) {
+      delete this.searchInfo[model];
+    },
+    filterReturn(data, item) {
+      let { model, filterReturn } = item;
+      if (filterReturn) this.$emit('filterReturn', { data, model });
+    },
+    // 计算合计
+    computedSum({ columns, data }) {
+      if (columns.length <= 0 || data.length <= 0) return '';
+      const result = [];
+      const reg = new RegExp(/^\d+$/);
+      for (const column of columns) {
+        // 判断有没有prop属性
+        const model = _.get(column, 'property');
+        if (!model) {
+          result.push('');
+          continue;
+        }
+        // 判断是否需要计算
+        const inlist = this.sumcol.find(f => f == model);
+        if (!inlist) {
+          result.push('');
+          continue;
+        }
+        let res = 0;
+        // 整理出要计算的属性(只取出数字或者可以为数字的值)
+        const resetList = data.map(i => {
+          const d = _.get(i, model);
+          const res = reg.test(d);
+          if (res) return d * 1;
+          else return 0;
+        });
+        if (this.sumres === 'total') {
+          res = this.totalComputed(resetList);
+        } else if (this.sumres === 'avg') {
+          res = this.avgComputed(resetList);
+        } else if (this.sumres === 'max') {
+          res = this.maxComputed(resetList);
+        } else if (this.sumres === 'min') {
+          res = this.minComputed(resetList);
+        }
+        result.push(res);
+      }
+      result[0] = '合计';
+      return result;
+    },
+    // 合计计算
+    totalComputed(data) {
+      const total = data.reduce((p, n) => p + n, 0);
+      return total;
+    },
+    // 平均值计算
+    avgComputed(data) {
+      const total = this.totalComputed(data);
+      return _.round(_.divide(total, data.length), 2);
+    },
+    // 最大值计算
+    maxComputed(data) {
+      return _.max(data);
+    },
+    // 最小值计算
+    minComputed(data) {
+      return _.min(data);
+    },
+  },
+  watch: {
+    selected: {
+      handler(val) {
+        if (val.length > 0) {
+          this.pageSelected = val;
+          this.initSelection();
+        }
+      },
+      immediate: true,
+    },
+    data: {
+      handler(val, oval) {
+        if (this.select) {
+          this.initSelection();
+        }
+      },
+    },
+    fields: {
+      handler(val, oval) {
+        if (val) this.getFilterList();
+      },
+      immediate: true,
+    },
+  },
+  computed: {
+    isMobile() {
+      // 手机不排序
+      let flag = navigator.userAgent.match(
+        /(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
+      );
+      return !Boolean(flag);
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 83 - 0
src/components/frame/form.md

@@ -0,0 +1,83 @@
+# 组件说明文档
+### form.vue
+### props
+
+|参数|类型|默认值|是否必填|说明|
+|:-:|:-:|:-:|:-:|:-:|
+|fields|Array|`-`|是|字段相关都在这里,用来自动输出,详情见下面|
+|submitText|String|`保存`|否|默认保存按钮的文字|
+|rules|Object|`-`|否|校验规则,不会找el-form的例子,不过使用的async-validator这个依赖为基础,会写这个也可以~~(那就厉害了,反正我是不行)~~|
+|isNew|Boolean|`-`|是|修改还是添加的提示|
+|data|Object|`-`|否|修改传来的数据|
+|needSave|Boolean|false|否|是否禁用保存按钮|
+|useEnter|Boolean|true|否|使用回车提交|
+|reset|Boolean|true|否|提交后是否重置表单|
+|filterReturn|Function|`-`|否|fields中,filterReturn为true的情况从这个函数走,参数:({data,model})|
+
+### fields
+>Array类型 必填
+>>
+|参数|类型|默认值|是否必填|说明|
+|:-:|:-:|:-:|:-:|:-:|
+|label|String|`-`|是|显示的字段中文|
+|type|String|input|否|这个字段要用什么类型来输出 input的基本类型可选值:date,datetime,radio,checkbox,select,text(只显示值),editor(富文本编辑器),password|
+|required|Boolean|`-`|否|是否必须输入|
+|model|String|`-`|是|字段名|
+|placeholder|String|`-`|否|占位,正常用,只是个透传|
+|options|object|`-`|否|标签的属性设置,例如:textarea 需要显示剩余字数,或者input限制长度,都往这里写,key-value形式(键值对,json的基本了解,不知道百度,具体属性看你具体用那个组件,那个组件有什么属性,瞎写不一定好使)|
+|custom|Boolean|`-`|否|是否使用自定义插槽|
+|tip|String|`-`|否|提示语,例如:请输入11位电话号码|
+|labelWidth|String|`120px`|否|表单label宽度,element的,默认120px|
+|format|Function|`-`|否|当type = text 时需要将该字段内容转换,可以使用format|
+|filterReturn|Boolean|`-`|否|是否返回这个字段,返回到filterReturn方法中|
+
+
+
+
+
+
+
+### slot
+>
+|插槽名|说明|
+|:-:|:-:|
+|options|fields中type为select的,选项都写在这个插槽中,多个select则需要区分options所属问题|
+|radios|fields中type为radio的,选项都写在这个插槽中,多个radio则需要区分radios所属问题|
+|checkbox|fields中type为checkbox的,选项都写在这个插槽中,多个checkbox则需要区分checkboxs所属问题|
+|custom|自定义插槽,完全自己去写|
+|submit|提交按钮部分,当needSave为false时才可以使用|
+>>关于自定义的用法:
+>>在fields中,custom:true的情况即需要自定义,写法如下
+
+>>`<template #custom="{ item, form, fieldChange }"> ... </template>`
+>>
+|参数名|说明|
+|:-:|:-:|
+|item|fields循环出来的每一项|
+|form|组件内部的表单|
+|fieldChange|组件内部的修改方法,此方法不一定必须使用,看情况来;参数:{model:xxx,value:XXX}(model:字段名,value:值)|
+>>在使用时,此插槽内的v-model可以写成form[item.model],也可以写成form.字段名
+
+>>例如`<el-input v-model="form[item.model]">`或者`<el-input v-model="form.xxx">`
+
+>> **如果有多处需要自定义,请区分开去写**
+
+
+***
+### upload
+|参数|类型|默认值|是否必填|说明|
+|:-:|:-:|:-:|:-:|:-:|
+|url|String|`-`|是|上传地址|
+|limit|Number|`-`|是|限制上传数量|
+|data|any|`-`|否|上传数据|
+|model|String|`-`|否|上传返回的字段|
+|isBtn|Boolean|false|否|是否只显示按钮|
+|showList|Boolean|true|否|是否显示上传列表|
+|accept|String|`-`|否|可以上传的文件类型,不写就没限制|
+|tip|String|`-`|否|提示信息|
+|listType|String|picture-card|否|上传文件列表显示类型|
+
+>### method
+>|方法名|返回参数|说明|
+|:-:|:-:|:-:|
+|upload|{model,data}|上传成功返回

+ 285 - 0
src/components/frame/form.vue

@@ -0,0 +1,285 @@
+<template>
+  <div id="add">
+    <el-form
+      ref="form"
+      :model="form"
+      :rules="rules"
+      :label-width="labelWidth"
+      class="form"
+      size="small"
+      @submit.native.prevent
+      :style="styles"
+      :inline="inline"
+    >
+      <el-form-item v-if="back">
+        <el-row>
+          <el-col :span="24" style="text-align: right">
+            <el-button type="primary" size="mini" @click="toReturn">返回</el-button>
+          </el-col>
+        </el-row>
+      </el-form-item>
+      <template v-for="(item, index) in fields">
+        <template v-if="!item.noform">
+          <template v-if="!loading">
+            <el-form-item
+              v-if="display(item)"
+              :key="'form-field-' + index"
+              :label="getField('label', item)"
+              :prop="item.model"
+              :required="item.required"
+              :rules="getRules(item)"
+            >
+              <template v-if="!item.custom">
+                <template v-if="item.type !== 'text'">
+                  <template v-if="item.type === `date` || item.type === `datetime`">
+                    <el-date-picker
+                      v-model="form[item.model]"
+                      :type="item.type"
+                      placeholder="选择择"
+                      format="yyyy-MM-dd"
+                      value-format="yyyy-MM-dd"
+                      v-bind="item.options"
+                      style="width:100%"
+                    >
+                    </el-date-picker>
+                  </template>
+                  <template v-else-if="item.type === `year` || item.type === `week` || item.type === `day`">
+                    <el-date-picker
+                      v-model="form[item.model]"
+                      :type="item.type"
+                      placeholder="选择择"
+                      :format="`${item.type === 'year' ? 'yyyy' : item.type === 'week' ? 'MM' : 'dd'}`"
+                      :value-format="`${item.type === 'year' ? 'yyyy' : item.type === 'week' ? 'MM' : 'dd'}`"
+                      v-bind="item.options"
+                      style="width:100%"
+                    >
+                    </el-date-picker>
+                  </template>
+                  <template v-else-if="item.type === 'time'">
+                    <el-time-picker v-model="form[item.model]" placeholder="请选择时间" format="HH:mm" value-format="HH:mm" style="width:100%"></el-time-picker>
+                  </template>
+                  <template v-else-if="item.type === 'radio'">
+                    <el-radio-group v-model="form[item.model]" size="mini" v-bind="item.options">
+                      <slot :name="item.model" v-bind="{ item, form, fieldChange }"></slot>
+                    </el-radio-group>
+                  </template>
+                  <template v-else-if="item.type === 'checkbox'">
+                    <el-checkbox-group v-model="form[item.model]" v-bind="item.options">
+                      <slot :name="item.model" v-bind="{ item, form, fieldChange }"></slot>
+                    </el-checkbox-group>
+                  </template>
+                  <template v-else-if="item.type === 'select'">
+                    <el-select
+                      v-model="form[item.model]"
+                      v-bind="item.options"
+                      filterable
+                      clearable
+                      @change="data => filterReturn(data, item)"
+                      style="width:100%"
+                    >
+                      <slot :name="item.model" v-bind="{ item, form, fieldChange }"></slot>
+                    </el-select>
+                  </template>
+                  <template v-else-if="item.type === 'textarea'">
+                    <!-- v-model="form[item.model]" -->
+                    <el-input
+                      clearable
+                      :value="getText(item.model)"
+                      @input="v => inputActive(item.model, v)"
+                      type="textarea"
+                      :autosize="{ minRows: 3, maxRows: 5 }"
+                      v-bind="item.options"
+                    ></el-input>
+                  </template>
+                  <template v-else-if="item.type === 'editor'">
+                    <wang-editor v-model="form[item.model]"></wang-editor>
+                  </template>
+                  <template v-else>
+                    <el-input
+                      clearable
+                      v-model="form[item.model]"
+                      :type="getField('type', item)"
+                      :placeholder="getField('placeholder', item)"
+                      :show-password="getField('type', item) === 'password'"
+                      v-bind="item.options"
+                    ></el-input>
+                  </template>
+                </template>
+                <template v-else>
+                  <template v-if="item.format">
+                    {{ item.format(form[item.model]) }}
+                  </template>
+                  <template v-else>
+                    {{ getText(item.model) }}
+                  </template>
+                </template>
+              </template>
+              <template v-else>
+                <slot :name="item.model" v-bind="{ item, form, fieldChange }"></slot>
+              </template>
+            </el-form-item>
+          </template>
+        </template>
+      </template>
+      <div style="padding-top: 10px">
+        <template v-if="needSave" class="btn">
+          <el-row type="flex" align="middle" justify="space-around">
+            <el-col :span="8">
+              <el-button type="primary" @click="save" style="width: 100%">{{ submitText }}</el-button>
+            </el-col>
+          </el-row>
+        </template>
+        <template v-else>
+          <slot name="submit"></slot>
+        </template>
+      </div>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+import wangEditor from './wang-editor.vue';
+export default {
+  name: 'add',
+  props: {
+    fields: { type: Array, default: () => [] },
+    rules: { type: Object, default: () => {} },
+    isNew: { type: Boolean, default: true },
+    form: { type: Object, default: () => {} },
+    styles: { type: Object, default: () => {} },
+    needSave: { type: Boolean, default: true },
+    labelWidth: { type: String, default: '120px' },
+    useEnter: { type: Boolean, default: true },
+    submitText: { type: String, default: '保存' },
+    inline: { type: Boolean, default: false },
+    reset: { type: Boolean, default: true },
+    back: null,
+  },
+  components: {
+    wangEditor,
+  },
+  model: {
+    prop: 'form',
+    event: 'change',
+  },
+  data: () => ({
+    show: false,
+    dateShow: false,
+    loading: false,
+  }),
+  created() {
+    if (this.useEnter) {
+      document.onkeydown = () => {
+        let key = window.event.keyCode;
+        if (key == 13) {
+          this.save();
+        }
+      };
+    }
+  },
+  computed: {},
+  mounted() {},
+  watch: {
+    // fields: {
+    //   handler(val) {
+    //     this.checkType();
+    //   },
+    //   immediate: true,
+    // },
+  },
+  methods: {
+    getField(item, data) {
+      let res = _.get(data, item, null);
+      if (item === 'type') res = res === null ? `text` : res;
+      if (item === 'placeholder') res = res === null ? `请输入${data.label}` : res;
+      if (item === 'required') res = res === null ? false : res;
+      if (item === `error`) res = res === null ? `${data.label}错误` : res;
+      return res;
+    },
+    save() {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          this.$emit(`save`, {
+            isNew: this.isNew,
+            data: JSON.parse(JSON.stringify(this.form)),
+          });
+          if (this.reset) this.$refs.form.resetFields();
+        } else {
+          console.warn('form validate error!!!');
+        }
+      });
+    },
+    fieldChange({ model, value }) {
+      this.$set(this.form, model, value);
+    },
+    checkType() {
+      let arr = this.fields.filter(fil => fil.type === 'checkbox');
+      if (arr.length > 0 && this.isNew) {
+        for (const item of arr) {
+          this.$set(this.form, `${item.model}`, []);
+        }
+      }
+    },
+    display(field) {
+      let noForm = _.get(field, `noForm`);
+      if (_.isBoolean(noForm) && noForm) return false;
+      let dis = _.get(field, `display`);
+      if (!_.isFunction(dis)) return true;
+      else return dis(field, this.form);
+    },
+    filterReturn(data, item) {
+      let { model, filterReturn } = item;
+      if (filterReturn) this.$emit('filterReturn', { data, model });
+    },
+    toReturn() {
+      if (_.isFunction(this.back)) eval(this.back)();
+      else this.$router.push(this.back);
+    },
+    getText(model) {
+      return _.get(this.form, model);
+    },
+    inputActive(model, value) {
+      this.$set(this.form, model, value);
+    },
+    getRules(data) {
+      let rules = [];
+      const { model, required, label } = data;
+      if (this.rules && _.get(this.rules, model)) {
+        const r = _.get(this.rules, model);
+        let has_required = false;
+        if (_.isArray(r)) {
+          for (const rule of r) {
+            if (_.get(rule, 'required')) {
+              has_required = true;
+              break;
+            }
+          }
+          rules = r;
+        } else {
+          if (_.get(rule, 'required')) has_required = true;
+          rules.push(r);
+        }
+        if (!has_required) rules.push({ required: true, message: `请填写${label}`, trigger: 'blur' });
+      } else {
+        if (required) {
+          let obj = { required: true, message: `请填写${label}`, trigger: 'blur' };
+          rules.push(obj);
+        }
+      }
+      return rules;
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.form {
+  padding: 2rem 1rem;
+  background: #fff;
+  border-radius: 20px;
+}
+// /deep/.btn .el-form-item__content {
+//   margin-left: 0 !important;
+// }
+</style>

+ 11 - 0
src/components/frame/pagination.md

@@ -0,0 +1,11 @@
+# pagination.vue 分页组件
+#### props
+|参数|类型|默认值|是否必填|说明|
+|:-:|:-:|:-:|:-:|:-:|
+|position|String|'right'|否|组件的布局位置,默认为靠右侧|
+|total|Number|0|是|分页的总数据数,用来计算页码|
+|limit|Number|10|否|每页的数量|  
+#### methods
+|方法名|参数|说明|
+|:-:|:-:|:-:|
+|query|{skip,limit,...info}|分页查询|

+ 54 - 0
src/components/frame/pagination.vue

@@ -0,0 +1,54 @@
+<template>
+  <div id="pagination">
+    <el-row type="flex" align="middle" style="padding-top:1rem">
+      <el-col :span="24" :style="`text-align:${position};`">
+        <el-pagination
+          background
+          layout=" total, prev, pager, next"
+          :total="total"
+          :page-size="limit"
+          :current-page.sync="currentPage"
+          @current-change="changePage"
+          :page-sizes="[5, 10, 15, 20, 50, 100]"
+          @size-change="sizeChange"
+        >
+        </el-pagination>
+        <!-- 
+          :page-sizes="[5, 10, 15, 20, 50, 100]"
+          @size-change="sizeChange"
+         -->
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'pagination',
+  props: {
+    position: { type: String, default: 'right' },
+    total: { type: Number, default: 0 },
+    limit: { type: Number, default: 10 },
+  },
+  components: {},
+  data: () => {
+    return {
+      currentPage: 1,
+    };
+  },
+  created() {},
+  methods: {
+    changePage(page) {
+      // this.$emit('query', { skip: (page - 1) * this.limit, limit: this.limit });
+      this.$emit('query', { skip: page, limit: this.limit });
+    },
+    sizeChange(limit) {
+      this.limit = limit;
+      this.$emit('query', { skip: 1, limit: this.limit });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 131 - 0
src/components/frame/s-upload.vue

@@ -0,0 +1,131 @@
+<template>
+  <div id="upload">
+    <el-upload
+      v-if="url"
+      ref="upload"
+      :action="url"
+      :list-type="listType"
+      :file-list="fileList"
+      :limit="limit"
+      :on-exceed="outLimit"
+      :on-preview="handlePictureCardPreview"
+      :before-remove="handleRemove"
+      :on-success="onSuccess"
+      :before-upload="beforeUpload"
+      :show-file-list="showList"
+      :accept="accept"
+    >
+      <el-button size="small" type="primary" v-if="isBtn">点击上传</el-button>
+      <template v-else-if="uploadBtn">
+        <el-button type="danger">选择文件</el-button>
+      </template>
+      <template v-else>
+        <el-button type="primary" size="mini">选择文件</el-button>
+      </template>
+      <template #tip v-if="tip">
+        {{ tip }}
+      </template>
+    </el-upload>
+    <el-dialog :visible.sync="dialogVisible" append-to-body>
+      <img width="100%" :src="dialogImageUrl" alt="" />
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+export default {
+  name: 'upload',
+  props: {
+    url: { type: null },
+    limit: { type: Number },
+    data: { type: null },
+    type: { type: String },
+    isBtn: { type: Boolean, default: false },
+    uploadBtn: { type: Boolean, default: false },
+    showList: { type: Boolean, default: true },
+    accept: { type: String, default: '' },
+    tip: { type: String, default: undefined },
+    listType: { type: String, default: 'picture-card' },
+    file_type: { type: String, default: 'jpeg' },
+  },
+  components: {},
+  data: () => ({
+    dialogVisible: false,
+    dialogImageUrl: '',
+    fileList: [],
+  }),
+  created() {
+    if (this.data) {
+      this.defalutProcess(this.data);
+    }
+  },
+  watch: {
+    data: {
+      handler(val) {
+        this.defalutProcess(val);
+      },
+    },
+  },
+  computed: {},
+  methods: {
+    handlePictureCardPreview(file) {
+      this.dialogImageUrl = file.url;
+      this.dialogVisible = true;
+    },
+    handleRemove(file, fileList) {
+      this.$set(this, `fileList`, []);
+      // let index = fileList.findIndex(f => _.isEqual(f, file));
+      this.$emit('delete', { file, type: this.type });
+      return false;
+    },
+    outLimit() {
+      this.$message.error(`只允许上传${this.limit}个文件`);
+    },
+    onSuccess(response, file, fileList) {
+      //将文件整理好传回父组件
+      this.$emit('upload', { type: this.type, data: { ...response, name: file.name } });
+    },
+    beforeUpload(file) {
+      let setting_type = this.file_type.split('|');
+      let file_type = file.type.split('/')[1];
+      let isType = false;
+      for (const val of setting_type) {
+        if (val == 'xlsx') isType = true;
+        else if (val == file_type) isType = true;
+      }
+      // 文件大小改为10Mb
+      const isLt20M = file.size / 1024 / 1024 < 10;
+      if (!isType) {
+        this.$message.error('图片格式不正确');
+      }
+      if (!isLt20M) {
+        this.$message.error('文件超出10M');
+      }
+      return isType && isLt20M;
+    },
+    defalutProcess(val) {
+      if (_.isArray(val)) {
+        let newArr = val.map(item => {
+          let object = {};
+          object.name = item.name;
+          object.url = item.url;
+          return object;
+        });
+        this.$set(this, `fileList`, newArr);
+      } else if (_.isObject(val)) {
+        let object = {};
+        if (_.get(val, `url`)) {
+          object.name = val.name;
+          object.url = val.url;
+          this.$set(this, `fileList`, [object]);
+        }
+      } else if (typeof val === 'string') {
+        this.$set(this, `fileList`, [{ name: '附件', url: val }]);
+      }
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 172 - 0
src/components/frame/swiper-frame.md

@@ -0,0 +1,172 @@
+# 组件文档说明
+
+## 下载依赖
+### npm install vue-awesome-swiper  --save
+### npm install swiper@5.3.6  --save
+
+## options设置
+### 1
+切换效果 fade cube coverflow flip
+effect: 'flip',
+样式设定
+fadeEffect: {
+淡出fade
+crossFade: true,
+方块切换 cube
+slideShadows: true,
+shadow: true,
+shadowOffset: 100,
+shadowScale: 0.6,
+coverflow
+slidesPerView: 3,
+centeredSlides: true,
+coverflowEffect: {
+  rotate: 30,
+  stretch: 10,
+  depth: 60,
+  modifier: 2,
+  slideShadows: true,
+},
+flip
+slideShadows: true,
+limitRotation: true,
+},
+### 2
+不可拖动文字
+noSwiping: true,
+### 3
+当你的Swiper在过渡时将无法滑动
+preventInteractionOnTransition : true,
+短切换,长切花
+shortSwipes: false,
+自由滑动,不知道滑过几个
+freeMode: true,
+### 4
+设置预览值 px
+slidesOffsetBefore: 100,
+显示多行
+slidesPerView: 3, //一行显示3个
+slidesPerColumn: 2, //显示2行
+### 5
+一页显示三个,一组为三个
+slidesPerView: 3,
+slidesPerGroup: 3,
+### 6
+slidesPerView: 2,
+//slidesPerView : 'auto',    根据slide的宽度自动调整展示数量。此时需要设置slide的宽度,如下style所示
+//slidesPerView : 3.7,
+### 7
+默认居中,并一页显示三个,第一条在左侧
+slidesPerView: 3,
+centeredSlides: true,
+centeredSlidesBounds: true,
+### 8
+默认居中,并一页显示三个,第一个在中间
+slidesPerView: 3,
+centeredSlides: true,
+### 9
+如果有一个数据时,所有分页,按钮,分页点全部失效
+watchOverflow: true,
+### 10
+滑过触发回调
+runCallbacksOnInit: true,
+on: {
+  slideChangeTransitionStart: function() {
+    选中值
+    console.log(this.activeIndex);
+  },
+},
+### 11
+自适应高度 随silde变化而变化
+autoHeight: true,
+### 12
+默认一页显示数
+slidesPerView: 4,
+数据间隔 px
+spaceBetween: 30,
+不同屏幕配置
+breakpoints: {
+  320: {
+    //当屏幕宽度大于等于320
+    slidesPerView: 2,
+    spaceBetween: 10,
+  },
+  768: {
+    //当屏幕宽度大于等于768
+    slidesPerView: 3,
+    spaceBetween: 20,
+  },
+  1280: {
+    //当屏幕宽度大于等于1280
+    slidesPerView: 4,
+    spaceBetween: 30,
+  },
+},
+### 13
+鼠标滑过显示小手
+grabCursor: true,
+### 14
+自动滑过贴合时间
+speed: 300,
+autoplay: {
+  默认3秒切换一次
+  delay: 3000,
+到最后一个自动停止
+stopOnLastSlide: true,
+触碰当前页自动停止
+disableOnInteraction: true,
+反向自动轮播
+reverseDirection: true,
+},
+### 15
+自动滑过
+autoplay: true,
+//鼠标覆盖停止自动切换
+### 16
+默认索引值,0:开始
+initialSlide: 0,
+### 17
+垂直切换选项
+direction: 'vertical',
+### 18
+循环轮播
+loop: true,
+### 19
+分页
+pagination: {
+el: '.swiper-pagination',
+clickable为true时,点击小点控制轮播
+clickable: true,
+分页类型
+type: 'bullets',//点
+type: 'fraction',//数字
+type: 'progressbar',//上条
+type: 'custom', //自定义
+progressbarOpposite: true,//progressbar-左条
+bulletElement: 'li',//指定标签
+dynamicBullets: true,//bullets-点数过多,隐藏
+dynamicMainBullets: 4,//显示点数量
+hideOnClick: true,//点击数据时,隐藏分页
+自定义点样式
+renderBullet: function(index, className) {
+  return '<span class="' + className + '">' + (index + 1) + '</span>';
+},
+设置分式
+type: 'fraction',
+renderFraction: function(currentClass, totalClass) {
+  return '<span class="' + currentClass + '"></span>' + ' of ' + '<span class="' + totalClass + '"></span>';
+},
+自定义点样式
+type: 'custom',
+renderCustom: function(swiper, current, total) {
+  return current + ' of ' + total;
+},
+bulletClass: 'my-bullet', //需设置.my-bullet样式
+bulletActiveClass: 'my-bullet-active', //需设置my-bullet-active样式
+},
+### 20
+按钮导航
+navigation: {
+  nextEl: '.swiper-button-next',
+  prevEl: '.swiper-button-prev',
+},

+ 64 - 0
src/components/frame/swiper-frame.vue

@@ -0,0 +1,64 @@
+<template>
+  <div id="swiper-frame">
+    <div class="container">
+      <div class="swiper-box">
+        <swiper ref="mySwiper" :options="options">
+          <swiper-slide v-for="(item, index) in list" :key="index">
+            <slot v-bind="{ index, item }"></slot>
+          </swiper-slide>
+          <!-- 分页 -->
+          <div class="swiper-pagination" slot="pagination"></div>
+          <!-- 按钮导航 -->
+          <div class="swiper-button-prev" slot="button-prev"></div>
+          <div class="swiper-button-next" slot="button-next"></div>
+        </swiper>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { Swiper, SwiperSlide } from 'vue-awesome-swiper';
+import 'swiper/css/swiper.css';
+export default {
+  name: 'swiper-frame',
+  props: {
+    // 循环列表
+    list: { type: Array, default: () => [] },
+    // 配置文件
+    options: { type: Object, default: () => {} },
+  },
+  components: { Swiper, SwiperSlide },
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {},
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped>
+.swiper-box {
+  .swiper-container {
+    height: 45px;
+  }
+}
+// 选中背景颜色,字颜色
+// .swiper-slide-active {
+//   background: #ff6600;
+//   color: #fff;
+// }
+// 设定固定值
+// .swiper-slide {
+//   width: 300px; /*设为固定值*/
+//   width: auto; /*根据内容调整宽度*/
+// }
+// 自定义按钮样式
+// .swiper-container {
+//   --swiper-theme-color: #ff6600; /* 设置Swiper风格 */
+//   --swiper-navigation-color: #00ff33; /* 单独设置按钮颜色 */
+//   --swiper-navigation-size: 30px; /* 设置按钮大小 */
+// }
+</style>

+ 264 - 0
src/components/frame/vform.vue

@@ -0,0 +1,264 @@
+<template>
+  <div id="add">
+    <el-form ref="form" :model="form" :label-width="labelWidth" class="form" size="small" @submit.native.prevent :style="styles" :inline="inline">
+      <template v-for="(item, index) in fields">
+        <el-form-item
+          v-if="display(item)"
+          :key="'form-field-' + index"
+          :label="getField('label', item)"
+          :prop="item.model"
+          :required="item.required"
+          :rules="getRules(item)"
+        >
+          <template v-if="!item.custom">
+            <template v-if="item.type !== 'text'">
+              <el-tooltip class="item" effect="dark" :content="item.remark" placement="top-start" :disabled="!item.remark">
+                <template v-if="item.type === `date` || item.type === `datetime`">
+                  <el-date-picker
+                    v-model="form[item.model]"
+                    :type="item.type"
+                    placeholder="选择择"
+                    :format="`yyyy-MM-dd${item.type == 'datetime' ? ' HH:mm:ss' : ''}`"
+                    :value-format="`yyyy-MM-dd${item.type == 'datetime' ? ' HH:mm:ss' : ''}`"
+                    v-bind="item.options"
+                  >
+                  </el-date-picker>
+                </template>
+                <template v-else-if="item.type === `year` || item.type === `week` || item.type === `day`">
+                  <el-date-picker
+                    v-model="form[item.model]"
+                    :type="item.type"
+                    placeholder="选择择"
+                    :format="`${item.type === 'year' ? 'yyyy' : item.type === 'week' ? 'MM' : 'dd'}`"
+                    :value-format="`${item.type === 'year' ? 'yyyy' : item.type === 'week' ? 'MM' : 'dd'}`"
+                    v-bind="item.options"
+                  >
+                  </el-date-picker>
+                </template>
+                <template v-else-if="item.type === 'number'">
+                  <el-input-number v-model="form[item.model]" placeholder="" controls-position="right" style="width:200px"></el-input-number>
+                </template>
+                <template v-else-if="item.type === 'time'">
+                  <el-time-picker v-model="form[item.model]" placeholder="请选择时间" format="HH:mm" value-format="HH:mm"></el-time-picker>
+                </template>
+                <template v-else-if="item.type === 'radio'">
+                  <el-radio-group v-model="form[item.model]" size="mini" v-bind="item.options">
+                    <template v-if="item.list">
+                      <el-radio v-for="(i, iIndex) in item.list" :key="`radio-${iIndex}`" :label="i.value">{{ i.label }}</el-radio>
+                    </template>
+                    <template v-else>
+                      <slot name="radios" v-bind="{ item, form, fieldChange }"></slot>
+                    </template>
+                  </el-radio-group>
+                </template>
+                <template v-else-if="item.type === 'checkbox'">
+                  <el-checkbox-group v-model="form[item.model]" v-bind="item.options">
+                    <template v-if="item.list">
+                      <el-checkbox v-for="(i, iIndex) in item.list" :key="`checkbox-${iIndex}`" :label="i.value">{{ i.label }}</el-checkbox>
+                    </template>
+                    <template v-else>
+                      <slot name="checkboxs" v-bind="{ item, form, fieldChange }"></slot>
+                    </template>
+                  </el-checkbox-group>
+                </template>
+                <template v-else-if="item.type === 'select'">
+                  <el-select
+                    v-model="form[item.model]"
+                    v-bind="item.options"
+                    filterable
+                    clearable
+                    @change="data => filterReturn(data, item)"
+                    default-first-option
+                  >
+                    <template v-if="item.list">
+                      <el-option v-for="(i, iIndex) in item.list" :key="`checkbox-${iIndex}`" :label="i.label" :value="i.value"></el-option>
+                    </template>
+                    <template v-else>
+                      <slot name="options" v-bind="{ item, form, fieldChange }"> </slot>
+                    </template>
+                  </el-select>
+                </template>
+                <template v-else-if="item.type === 'textarea'">
+                  <el-input clearable v-model="form[item.model]" type="textarea" :autosize="{ minRows: 3, maxRows: 5 }"></el-input>
+                </template>
+                <template v-else-if="item.type === 'editor'">
+                  <wang-editor v-model="form[item.model]"></wang-editor>
+                </template>
+                <template v-else>
+                  <el-input
+                    clearable
+                    v-model="form[item.model]"
+                    :type="getField('type', item)"
+                    :placeholder="getField('placeholder', item)"
+                    :show-password="getField('type', item) === 'password'"
+                    v-bind="item.options"
+                  ></el-input>
+                </template>
+              </el-tooltip>
+            </template>
+            <template v-else>
+              <template v-if="item.format">
+                {{ item.format(form[item.model]) }}
+              </template>
+              <template v-else>
+                {{ form[item.model] }}
+              </template>
+            </template>
+          </template>
+          <template v-else>
+            <slot name="custom" v-bind="{ item, form, fieldChange }"></slot>
+          </template>
+        </el-form-item>
+      </template>
+      <el-form-item label="" v-if="needSave" class="btn">
+        <el-row type="flex" align="middle" justify="start">
+          <el-col :span="6">
+            <el-button type="primary" @click="save">{{ submitText }}</el-button>
+          </el-col>
+        </el-row>
+      </el-form-item>
+      <el-form-item v-else>
+        <slot name="submit"></slot>
+      </el-form-item>
+    </el-form>
+  </div>
+</template>
+
+<script>
+import _ from 'lodash';
+import wangEditor from '@common/src/components/frame/wang-editor.vue';
+export default {
+  name: 'vform',
+  model: {
+    prop: 'form',
+    event: 'change',
+  },
+  props: {
+    fields: { type: Array, default: () => [] },
+    rules: { type: Object, default: () => ({}) },
+    isNew: { type: Boolean, default: true },
+    form: { type: Object, default: () => {} },
+    styles: { type: Object, default: () => {} },
+    needSave: { type: Boolean, default: true },
+    labelWidth: { type: String, default: '120px' },
+    useEnter: { type: Boolean, default: true },
+    submitText: { type: String, default: '保存' },
+    inline: { type: Boolean, default: false },
+    reset: { type: Boolean, default: true },
+  },
+  components: {
+    wangEditor,
+  },
+  data: () => {
+    // form: {},
+    const _this = this;
+    return {
+      show: false,
+      dateShow: false,
+    };
+  },
+  created() {
+    if (this.useEnter) {
+      document.onkeydown = () => {
+        let key = window.event.keyCode;
+        if (key == 13) {
+          this.save();
+        }
+      };
+    }
+  },
+  computed: {},
+  mounted() {},
+  watch: {
+    fields: {
+      handler(val) {
+        this.checkType();
+      },
+      immediate: true,
+      deep: true,
+    },
+  },
+  methods: {
+    getField(item, data) {
+      let res = _.get(data, item, null);
+      if (item === 'type') res = res === null ? `text` : res;
+      if (item === 'placeholder') res = res === null ? `请输入${data.label}` : res;
+      if (item === 'required') res = res === null ? false : res;
+      if (item === `error`) res = res === null ? `${data.label}错误` : res;
+      return res;
+    },
+    save() {
+      this.$refs['form'].validate(valid => {
+        if (valid) {
+          this.$emit(`save`, { isNew: this.isNew, data: JSON.parse(JSON.stringify(this.form)) });
+          if (this.reset) this.$refs.form.resetFields();
+        } else {
+          console.warn('form validate error!!!');
+        }
+      });
+    },
+    fieldChange({ model, value }) {
+      this.$set(this.form, model, value);
+    },
+    //
+    checkType() {
+      let arr = this.fields.filter(fil => fil.type === 'checkbox');
+      if (arr.length > 0 && this.isNew) {
+        for (const item of arr) {
+          this.$set(this.form, `${item.model}`, []);
+        }
+      }
+    },
+    getRules(data) {
+      let rules = [];
+      const { model, required, label } = data;
+      if (this.rules && _.get(this.rules, model)) {
+        const r = _.get(this.rules, model);
+        let has_required = false;
+        if (_.isArray(r)) {
+          for (const rule of r) {
+            if (_.get(rule, 'required')) {
+              has_required = true;
+              break;
+            }
+          }
+          rules = r;
+        } else {
+          if (_.get(rule, 'required')) has_required = true;
+          rules.push(r);
+        }
+        if (!has_required) rules.push({ required: true, message: `请填写${label}`, trigger: 'blur' });
+      } else {
+        if (required) {
+          let obj = { required: true, message: `请填写${label}`, trigger: 'blur' };
+          rules.push(obj);
+        }
+      }
+      return rules;
+    },
+    display(field) {
+      // 添加noform属性,与列表fields共用
+      let noform = _.get(field, `noform`);
+      if (noform) return false;
+      let dis = _.get(field, `display`);
+      if (!_.isFunction(dis)) return true;
+      else return dis(field, this.form);
+    },
+    filterReturn(data, item) {
+      let { model, filterReturn } = item;
+      if (filterReturn) this.$emit('filterReturn', { data, model });
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.form {
+  padding: 1.5rem 1rem;
+  background: #fff;
+  border-radius: 20px;
+}
+// /deep/.btn .el-form-item__content {
+//   margin-left: 0 !important;
+// }
+</style>

+ 89 - 0
src/components/frame/wang-editor.vue

@@ -0,0 +1,89 @@
+<template>
+  <div>
+    <div ref="editor" style="text-align:left"></div>
+  </div>
+</template>
+<script>
+import E from 'wangeditor';
+
+const menus = [
+  'head', // 标题
+  'bold', // 粗体
+  'fontSize', // 字号
+  'fontName', // 字体
+  'italic', // 斜体
+  'underline', // 下划线
+  'strikeThrough', // 删除线
+  'foreColor', // 文字颜色
+  'backColor', // 背景颜色
+  'link', // 插入链接
+  'list', // 列表
+  'justify', // 对齐方式
+  'quote', // 引用
+  // 'emoticon', // 表情
+  'table', // 表格
+  // 'video', // 插入视频
+  // 'code', // 插入代码
+  'undo', // 撤销
+  'redo', // 重复
+];
+
+export default {
+  name: 'wang-editor',
+  model: {
+    prop: 'value',
+    event: 'change', // 默认为input时间,此处改为change
+  },
+  props: {
+    value: { type: String, required: false, default: '' },
+    url: { type: String },
+  },
+  data() {
+    return {
+      editorContent: this.value,
+      editor: {},
+    };
+  },
+  mounted() {
+    var editor = new E(this.$refs.editor);
+    editor.customConfig.onchange = html => {
+      this.editorContent = html;
+      this.$emit('change', html);
+    };
+    // 自定义菜单配置
+    editor.customConfig.menus = menus;
+    editor.customConfig.zIndex = 0;
+    editor.customConfig.uploadImgServer = this.url;
+    editor.customConfig.uploadImgMaxLength = 1;
+    editor.customConfig.uploadImgHooks = {
+      // 如果服务器端返回的不是 {errno:0, data: [...]} 这种格式,可使用该配置
+      // (但是,服务器端返回的必须是一个 JSON 格式字符串!!!否则会报错)
+      customInsert: function(insertImg, result, editor) {
+        // 图片上传并返回结果,自定义插入图片的事件(而不是编辑器自动插入图片!!!)
+        // insertImg 是插入图片的函数,editor 是编辑器对象,result 是服务器端返回的结果
+
+        // 举例:假如上传图片成功后,服务器端返回的是 {url:'....'} 这种格式,即可这样插入图片:
+        var url = result.uri;
+        insertImg(url);
+
+        // result 必须是一个 JSON 格式字符串!!!否则报错
+      },
+    };
+    editor.create();
+    editor.txt.html(this.value);
+    this.$set(this, 'editor', editor);
+  },
+  methods: {
+    getContent: function() {
+      return this.editorContent;
+    },
+  },
+  watch: {
+    value: {
+      handler(val) {
+        this.editor.txt.html(val);
+      },
+    },
+  },
+};
+</script>

+ 38 - 0
src/components/mobile-frame/foot.vue

@@ -0,0 +1,38 @@
+<template>
+  <div id="foot">
+    <van-tabbar route>
+      <van-tabbar-item v-for="(i, index) in menuList" :key="index" :to="i.index" :icon="i.icon">{{ i.name }}</van-tabbar-item>
+    </van-tabbar>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'foot',
+  props: {
+    menuList: { type: Array },
+  },
+  components: {},
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 111 - 0
src/components/mobile-frame/mobile-main.vue

@@ -0,0 +1,111 @@
+<template>
+  <div id="mobile-main">
+    <van-row>
+      <van-col span="24" class="main" :style="{ height: client.height + 'px' }">
+        <van-col span="24" class="top" v-if="useTop">
+          <top :topType="topType" @search="topSearch" :leftArrow="leftArrow" @back="back" :rightArrow="rightArrow" @add="add">
+            <template v-slot:top>
+              <slot name="slotTop"></slot>
+            </template>
+          </top>
+        </van-col>
+        <van-col span="24" class="info" :style="{ height: getHeight() }">
+          <slot name="info"></slot>
+        </van-col>
+        <van-col span="24" class="page" v-if="usePage">
+          <page :limit="limit" :total="total" @search="search"></page>
+        </van-col>
+        <van-col span="24" class="foot" v-if="useNav"><foot :menuList="menuList"></foot></van-col>
+      </van-col>
+    </van-row>
+  </div>
+</template>
+
+<script>
+import top from './top.vue';
+import page from './page.vue';
+import foot from './foot.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'mobile-main',
+  props: {
+    // 头部
+    useTop: { type: Boolean, default: () => true },
+    topType: { type: String, default: () => '1' },
+    leftArrow: { type: Boolean, default: () => true },
+    rightArrow: { type: Boolean, default: () => true },
+    // 分页
+    usePage: { type: Boolean, default: () => true },
+    limit: { type: Number, default: () => 10 },
+    total: { type: Number, default: () => 0 },
+    // 底部菜单
+    useNav: { type: Boolean, default: () => true },
+  },
+  components: {
+    top,
+    page,
+    foot,
+  },
+  data: function() {
+    return {
+      client: {},
+      menuList: [
+        { name: '首页1', index: '/', icon: 'after-sale' },
+        { name: '首页2', index: '/index', icon: 'cart-o' },
+      ],
+    };
+  },
+  created() {},
+  methods: {
+    // 头部名称查询
+    topSearch(searchName) {
+      this.$emit('search', searchName);
+    },
+    // 分页查询
+    search(skip) {
+      this.$emit('search', skip);
+    },
+    // 返回
+    back() {
+      this.$emit('back');
+    },
+    // 添加
+    add() {
+      this.$emit('add');
+    },
+    // 计算中间高度
+    getHeight() {
+      let windowH = this.client.height;
+      if (this.useTop) windowH = windowH - 45;
+      else windowH = windowH - 0;
+      if (this.usePage) windowH = windowH - 40;
+      else windowH = windowH - 0;
+      if (this.useNav) windowH = windowH - 50;
+      else windowH = windowH - 0;
+      return windowH + 'px';
+    },
+  },
+  mounted() {
+    let client = {
+      height: document.documentElement.clientHeight || document.body.clientHeight,
+      width: document.documentElement.clientWidth || document.body.clientWidth,
+    };
+    this.$set(this, `client`, client);
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 43 - 0
src/components/mobile-frame/page.vue

@@ -0,0 +1,43 @@
+<template>
+  <div id="page">
+    <van-pagination v-model="currentPage" @change="changePage" :total-items="total" :items-per-page="limit" :show-page-size="5" force-ellipses />
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'page',
+  props: {
+    total: { type: Number },
+    limit: { type: Number, default: () => 6 },
+  },
+  components: {},
+  data: function() {
+    return {
+      currentPage: 1,
+    };
+  },
+  created() {},
+  methods: {
+    changePage(page) {
+      this.$emit('search', { skip: (page - 1) * this.limit });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 9 - 0
src/components/mobile-frame/top.md

@@ -0,0 +1,9 @@
+<!-- 头部类型 -->
+# topType
+## topType=="1" 只有查询 search
+
+## topType=="2" 带有返回,标题,添加,返回方法:back,添加方法:add
+
+## topType=="3" 带有返回和查询,返回方法:back,查询方法:search
+
+## topType=="4" 自定义 slotTop

+ 123 - 0
src/components/mobile-frame/top.vue

@@ -0,0 +1,123 @@
+<template>
+  <div id="top">
+    <van-row>
+      <van-col span="24" class="main">
+        <van-col span="24" class="common type_1" v-if="topType == '1'">
+          <van-search v-model="searchName" placeholder="请输入信息标题" @search="search" />
+        </van-col>
+        <van-col span="24" class="common type_2" v-else-if="topType == '2'">
+          <van-nav-bar :title="this.$route.meta.title">
+            <template #left>
+              <van-col span="24" v-if="leftArrow" @click="back" class="leftArrow">
+                <van-icon name="arrow-left" />
+                <span>返回</span>
+              </van-col>
+            </template>
+            <template #right>
+              <van-col span="24" v-if="rightArrow" @click="add"><van-icon name="plus"/></van-col>
+            </template>
+          </van-nav-bar>
+        </van-col>
+        <van-col span="24" class="common type_3" v-else-if="topType == '3'">
+          <van-col span="4" class="left" @click.native="back">
+            <van-icon name="arrow-left" />
+            <span>返回</span>
+          </van-col>
+          <van-col span="20" class="right">
+            <van-search v-model="searchName" placeholder="请输入信息标题" @search="search" />
+          </van-col>
+        </van-col>
+        <van-col span="24" class="common type_4" v-else-if="topType == '4'">
+          <slot name="top"></slot>
+        </van-col>
+      </van-col>
+    </van-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'top',
+  props: {
+    topType: { typ: String },
+    leftArrow: { typ: Boolean },
+    rightArrow: { typ: Boolean },
+  },
+  components: {},
+  data: function() {
+    return {
+      searchName: '',
+    };
+  },
+  created() {},
+  methods: {
+    // 搜索
+    search() {
+      this.$emit('search', { searchName: this.searchName });
+    },
+    // 返回
+    back() {
+      this.$emit('back');
+    },
+    // 添加
+    add() {
+      this.$emit('add');
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .common {
+    height: 45px;
+    overflow: hidden;
+    border-bottom: 1px solid #f1f1f1;
+  }
+  .type_1 {
+    padding: 5px;
+    /deep/.van-search {
+      padding: 0;
+    }
+  }
+  .type_2 {
+    .leftArrow {
+      span {
+        color: #409eff;
+        position: relative;
+        top: -2px;
+      }
+    }
+  }
+  .type_3 {
+    padding: 5px;
+    /deep/.van-search {
+      padding: 0;
+    }
+    .left {
+      color: #409eff;
+      font-size: 15px;
+      text-align: center;
+      padding: 8px 0;
+      span {
+        position: relative;
+        top: -2px;
+      }
+    }
+  }
+}
+</style>

+ 66 - 0
src/components/web-frame/banner.vue

@@ -0,0 +1,66 @@
+<template>
+  <div id="banner">
+    <el-row v-if="bannerInfo.info.display">
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <el-carousel
+            :height="bannerInfo.info.height"
+            :trigger="bannerInfo.info.trigger"
+            :autoplay="bannerInfo.info.autoplay"
+            :interval="bannerInfo.info.interval"
+            :indicator-position="bannerInfo.info.indicatorpos"
+            :arrow="bannerInfo.info.arrow"
+            :type="bannerInfo.info.type"
+            :loop="bannerInfo.info.loop"
+            :direction="bannerInfo.info.direction"
+          >
+            <el-carousel-item v-for="(item, index) in bannerInfo.list" :key="index">
+              <el-image :src="item.url"></el-image>
+            </el-carousel-item>
+          </el-carousel>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'banner',
+  props: {
+    bannerInfo: { type: Object, default: () => {} },
+  },
+  components: {},
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    .el-image {
+      width: 100%;
+      height: 400px;
+      overflow: hidden;
+    }
+  }
+}
+</style>

+ 57 - 0
src/components/web-frame/foot.vue

@@ -0,0 +1,57 @@
+<template>
+  <div id="foot">
+    <el-row v-show="footInfo.display">
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <div class="w_1200">
+            <p>{{ footInfo.p2 }}</p>
+            <p>{{ footInfo.p3 }}</p>
+          </div>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'foot',
+  props: {
+    footInfo: { type: Object, default: () => {} },
+  },
+  components: {},
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    min-height: 30px;
+    background-color: #3a3637;
+    color: #999999;
+    padding: 10px 0;
+    p {
+      margin: 0 0 5px 0;
+    }
+  }
+}
+</style>

+ 59 - 0
src/components/web-frame/index.vue

@@ -0,0 +1,59 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main">
+        <top :siteInfo="siteInfo"><template v-slot:deRight></template></top>
+        <menus :menuInfo="menuInfo"></menus>
+        <banner :bannerInfo="bannerInfo"></banner>
+        <router-view></router-view>
+        <foot :footInfo="footInfo"></foot>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import top from './top.vue';
+import menus from './menus.vue';
+import banner from './banner.vue';
+import foot from './foot.vue';
+const { siteInfo, menuInfo, bannerInfo, footInfo } = require('../../layout/deploy/site');
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: { top, menus, banner, foot },
+  data: function() {
+    return {
+      siteInfo: siteInfo,
+      menuInfo: menuInfo,
+      bannerInfo: {
+        info: bannerInfo.info,
+        list: [
+          { name: '轮播一', url: require('../../assets/banner.png') },
+          { name: '轮播二', url: require('../../assets/banner.png') },
+          { name: '轮播二', url: require('../../assets/banner.png') },
+        ],
+      },
+      footInfo: footInfo,
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 105 - 0
src/components/web-frame/menus.vue

@@ -0,0 +1,105 @@
+<template>
+  <div id="menus">
+    <el-row v-if="menuInfo.info.display">
+      <el-col :span="24" class="main" :style="{ background: menuInfo.info.backColor }">
+        <div class="w_1200">
+          <el-col :span="24" class="one">
+            <el-menu
+              :default-active="$route.title"
+              :mode="menuInfo.info.mode"
+              :background-color="menuInfo.info.backColor"
+              :text-color="menuInfo.info.textColor"
+              :active-text-color="menuInfo.info.actColor"
+              router
+            >
+              <template v-for="item in menuInfo.menuList">
+                <!-- 二级菜单,三级菜单 -->
+                <template v-if="item.subs">
+                  <el-submenu :index="item.index" :key="item.index">
+                    <!-- 二级菜单-名称 -->
+                    <template slot="title">{{ item.title }}</template>
+                    <template v-for="subItem in item.subs">
+                      <!-- 三级菜单 -->
+                      <el-submenu v-if="subItem.subs" :index="subItem.index" :key="subItem.index">
+                        <!-- 三级菜单-名称 -->
+                        <template slot="title">{{ subItem.title }}</template>
+                        <!-- 三级菜单-菜单列表 -->
+                        <el-menu-item v-for="(threeItem, i) in subItem.subs" :key="i" :index="threeItem.index">
+                          <template slot="title">{{ threeItem.title }}</template>
+                        </el-menu-item>
+                      </el-submenu>
+                      <!-- 二级菜单-菜单列表 -->
+                      <el-menu-item v-else :index="subItem.index" :key="subItem.index" :route="{ path: item.index }">
+                        <template slot="title">{{ item.title }}</template>
+                      </el-menu-item>
+                    </template>
+                  </el-submenu>
+                </template>
+                <!-- 一级菜单 -->
+                <template v-else>
+                  <el-menu-item :index="item.title" :key="item.index" :route="{ path: item.index }">
+                    <template slot="title">{{ item.title }}</template>
+                  </el-menu-item>
+                </template>
+              </template>
+            </el-menu>
+          </el-col>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'menus',
+  props: {
+    menuInfo: { type: Object, default: () => {} },
+  },
+  components: {},
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  height: 51px;
+  border-bottom: 1px solid #f1f1f1;
+  .one {
+    height: 50px;
+    overflow: hidden;
+    .el-menu--horizontal > .el-menu-item {
+      height: 50px;
+      line-height: 50px;
+      border-bottom: none;
+      font-size: 16px;
+    }
+    /deep/.el-menu--horizontal > .el-menu-item.is-active {
+      background-color: #fe950e !important;
+    }
+    /deep/.el-menu--horizontal > .el-submenu .el-submenu__title {
+      height: 50px;
+      line-height: 50px;
+      border-bottom: none;
+    }
+  }
+}
+</style>

+ 87 - 0
src/components/web-frame/top.vue

@@ -0,0 +1,87 @@
+<template>
+  <div id="top">
+    <el-row v-show="siteInfo.display">
+      <el-col :span="24" class="main">
+        <div class="w_1200">
+          <el-col :span="24" class="one">
+            <el-col :span="20" class="left" @click.native="$router.push('/')">
+              <el-col :span="3" class="image">
+                <el-image :src="siteInfo.logo_url"></el-image>
+              </el-col>
+              <el-col :span="21" class="other">
+                <el-col :span="24" class="zhTitle textOver">
+                  {{ siteInfo.zhTitle }}
+                </el-col>
+                <el-col :span="24" class="enTitle textOver">
+                  {{ siteInfo.enTitle }}
+                </el-col>
+              </el-col>
+            </el-col>
+            <el-col :span="4" class="right">
+              <slot name="deRight"></slot>
+            </el-col>
+          </el-col>
+        </div>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'top',
+  props: {
+    siteInfo: { type: Object, default: () => {} },
+  },
+  components: {},
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    min-height: 130px;
+    .left {
+      .image {
+        padding: 16px 0;
+        text-align: center;
+        .el-image {
+          width: 90px;
+          height: 90px;
+        }
+      }
+      .other {
+        padding: 30px 0;
+        .zhTitle {
+          font-size: 35px;
+          font-family: cursive;
+          font-weight: 700;
+        }
+        .enTitle {
+          font-size: 15px;
+          text-transform: capitalize;
+        }
+      }
+    }
+  }
+}
+</style>

File diff suppressed because it is too large
+ 1056 - 0
src/layout/deploy/dict.js


+ 986 - 0
src/layout/deploy/menu.js

@@ -0,0 +1,986 @@
+export const system = [
+  {
+    icon: 'icon-index-copy',
+    index: '/adminCenter/homeIndex',
+    title: '系统首页',
+  },
+];
+// 公安端
+export const adminMenu = [
+  {
+    icon: 'icon-baoanyuanzheng',
+    index: '1',
+    title: '保安员管理',
+    num: '1',
+    subs: [
+      {
+        index: '1-1',
+        title: '报名考试管理',
+        num: '1-1',
+        subs: [
+          {
+            index: '/adminPolice/security/baybm',
+            title: '保安员报名',
+            num: '1-1-1',
+          },
+          {
+            index: '/adminPolice/security/bayzs',
+            title: '保安员资料政审',
+            num: '1-1-2',
+          },
+          {
+            index: '/adminPolice/security/zwzpcj',
+            title: '指纹与照片采集',
+            num: '1-1-3',
+          },
+          {
+            index: '/adminPolice/security/baybk',
+            title: '保安员补考',
+            num: '1-1-4',
+          },
+        ],
+      },
+      {
+        index: '/adminPolice/security/b_info',
+        title: '生物信息补采',
+        num: '1-2',
+      },
+      {
+        index: '1-3',
+        title: '保安员资格审核',
+        num: '1-3',
+        subs: [
+          {
+            index: '/adminPolice/security/bmzgsh',
+            title: '报名资格审核',
+            num: '1-3-1',
+          },
+          {
+            index: '/adminPolice/security/zgsh',
+            title: '资格审批',
+            num: '1-3-2',
+          },
+        ],
+      },
+      {
+        index: '1-4',
+        title: '准考证管理',
+        num: '1-4',
+        subs: [
+          {
+            index: '/adminPolice/security/bmjf',
+            title: '报名缴费',
+            num: '1-4-1',
+          },
+          {
+            index: '/adminPolice/security/kcbp',
+            title: '考场编排',
+            num: '1-4-2',
+          },
+          {
+            index: '/adminPolice/security/zkzdy',
+            title: '准考证打印',
+            num: '1-4-3',
+          },
+          // {
+          //   index: "/adminPolice/security/zkzfmdy",
+          //   title: "准考证封面打印",
+          //   num: "1-4-4",
+          // },
+          {
+            index: '/adminPolice/security/zkzcx',
+            title: '准考证查询',
+            num: '1-4-5',
+          },
+          {
+            index: '/adminPolice/security/ksxxdc',
+            title: '考生信息导出',
+            num: '1-4-6',
+          },
+          {
+            index: '/adminPolice/security/ksqkdj',
+            title: '考试情况登记',
+            num: '1-4-7',
+          },
+          {
+            index: '/adminPolice/security/kscjcx',
+            title: '考试成绩查询',
+            num: '1-4-8',
+          },
+          // {
+          //   index: '/adminPolice/security/zkzdyrz',
+          //   title: '准考证打印日志',
+          //   num: '1-4-9',
+          // },
+        ],
+      },
+      {
+        icon: 'icon-shouye',
+        index: '1-5',
+        title: '保安员证管理',
+        num: '1-5',
+        subs: [
+          // {
+          //   index: '/adminPolice/security/bayxxcx',
+          //   title: '保安员信息查询',
+          //   num: '1-5-1',
+          // },
+          // {
+          //   index: '/adminPolice/security/bayzzz',
+          //   title: '保安员证制作',
+          //   num: '1-5-2',
+          // },
+          // {
+          //   index: '/adminPolice/security/bayffdj',
+          //   title: '保安员证发放登记',
+          //   num: '1-5-3',
+          // },
+          {
+            index: '/adminPolice/security/workCard',
+            title: '保安员工牌管理',
+            num: '1-5-4',
+          },
+          {
+            index: '/adminPolice/security/ysbdsq',
+            title: '遗失补打申请',
+            num: '1-5-5',
+          },
+          {
+            index: '/adminPolice/security/ysbdsh',
+            title: '遗失补打审核',
+            num: '1-5-6',
+          },
+          {
+            index: '/adminPolice/security/ysbd',
+            title: '遗失补打',
+            num: '1-5-7',
+          },
+          {
+            index: '/adminPolice/security/dyrz',
+            title: '打印日志',
+            num: '1-5-8',
+          },
+          {
+            index: '/adminPolice/security/qbbayxxcx',
+            title: '全部保安员信息查询',
+            num: '1-5-9',
+          },
+          {
+            index: '/adminPolice/security/baoanbash',
+            title: '保安员证备案(绑定)审核',
+            num: '1-5-10',
+          },
+        ],
+      },
+      {
+        icon: 'icon-shouye',
+        index: '1-6',
+        title: '统计分析',
+        num: '1-6',
+        subs: [
+          // {
+          //   index: "/adminPolice/security/hjpcshctj",
+          //   title: "户籍派出所核查统计",
+          //   num: "1-6-1",
+          // },
+          {
+            index: '/adminPolice/security/bmzstj',
+            title: '报名政审统计',
+            num: '1-6-2',
+          },
+          {
+            index: '/adminPolice/security/bmqktj',
+            title: '报名情况统计',
+            num: '1-6-3',
+          },
+          {
+            index: '/adminPolice/security/ksqktj',
+            title: '考试情况统计',
+            num: '1-6-4',
+          },
+        ],
+      },
+      {
+        icon: 'icon-shouye',
+        index: '1-7',
+        title: '保安员信息导入',
+        num: '1-7',
+        subs: [
+          {
+            index: '/adminPolice/security/baybmxxpldr',
+            title: '保安员报名信息批量导入',
+            num: '1-7-1',
+          },
+          {
+            index: '/adminPolice/security/bayxxpldr',
+            title: '保安员信息批量导入',
+            num: '1-7-2',
+          },
+        ],
+      },
+    ],
+  },
+  {
+    icon: 'icon-hangzheng',
+    index: '2',
+    title: '保安公司管理',
+    num: '2',
+    subs: [
+      {
+        index: '2-1',
+        title: '保安服务公司',
+        num: '2-1',
+        subs: [
+          {
+            index: '/adminPolice/companyone/one',
+            title: '派出所审核',
+            num: '2-1-1',
+          },
+          {
+            index: '/adminPolice/companyone/two',
+            title: '分局审核',
+            num: '2-1-2',
+          },
+          {
+            index: '/adminPolice/companyone/thr',
+            title: '市局审核',
+            num: '2-1-3',
+          },
+          {
+            index: '/adminPolice/companyone/four',
+            title: '服务许可证管理',
+            num: '2-1-4',
+          },
+          {
+            index: '/adminPolice/companyone/five',
+            title: '服务许可证制作',
+            num: '2-1-5',
+          },
+          {
+            index: '/adminPolice/companyone/six',
+            title: '服务许可证发放登记',
+            num: '2-1-6',
+          },
+          {
+            index: '/adminPolice/companyone/seven',
+            title: '法人变更市级审核',
+            num: '2-1-7',
+          },
+        ],
+      },
+      {
+        index: '2-2',
+        title: '自行招用保安员单位',
+        num: '2-2',
+        subs: [
+          {
+            index: '/adminPolice/companytwo/one',
+            title: '派出所审核',
+            num: '2-2-1',
+          },
+          {
+            index: '/adminPolice/companytwo/two',
+            title: '分局审核',
+            num: '2-2-2',
+          },
+          {
+            index: '/adminPolice/companytwo/thr',
+            title: '市局审核',
+            num: '2-2-3',
+          },
+          {
+            index: '/adminPolice/companytwo/four',
+            title: '备案证明管理',
+            num: '2-2-4',
+          },
+          {
+            index: '/adminPolice/companytwo/five',
+            title: '备案证明制作',
+            num: '2-2-5',
+          },
+          {
+            index: '/adminPolice/companytwo/six',
+            title: '备案证明发放登记',
+            num: '2-2-6',
+          },
+        ],
+      },
+      {
+        index: '2-3',
+        title: '跨区域保安服务公司',
+        num: '2-3',
+        subs: [
+          {
+            index: '/adminPolice/companythr/one',
+            title: '派出所审核',
+            num: '2-3-1',
+          },
+          {
+            index: '/adminPolice/companythr/two',
+            title: '分局审核',
+            num: '2-3-2',
+          },
+          {
+            index: '/adminPolice/companythr/thr',
+            title: '市局审核',
+            num: '2-3-3',
+          },
+        ],
+      },
+      {
+        index: '2-4',
+        title: '保安服务分公司',
+        num: '2-4',
+        subs: [
+          {
+            index: '/adminPolice/companyfour/one',
+            title: '派出所审核',
+            num: '2-4-1',
+          },
+          {
+            index: '/adminPolice/companyfour/two',
+            title: '分局审核',
+            num: '2-4-2',
+          },
+          {
+            index: '/adminPolice/companyfour/thr',
+            title: '市局审核',
+            num: '2-4-3',
+          },
+        ],
+      },
+      {
+        index: '2-5',
+        title: '保安培训单位',
+        num: '2-5',
+        subs: [
+          {
+            index: '/adminPolice/companyfive/one',
+            title: '派出所审核',
+            num: '2-5-1',
+          },
+          {
+            index: '/adminPolice/companyfive/two',
+            title: '分局审核',
+            num: '2-5-2',
+          },
+          {
+            index: '/adminPolice/companyfive/thr',
+            title: '市局审核',
+            num: '2-5-3',
+          },
+        ],
+      },
+    ],
+  },
+  {
+    icon: 'icon-ico_zonghexinxi_xuanzhong',
+    index: '3',
+    title: '综合信息管理',
+    num: '3',
+    subs: [
+      {
+        index: '3-1',
+        title: '综合信息维护',
+        num: '3-1',
+        subs: [
+          {
+            index: '/adminPolice/information/bayxxwh',
+            title: '保安员信息维护',
+            num: '3-1-1',
+          },
+          {
+            index: '/adminPolice/information/fwgsxxwh',
+            title: '服务公司信息维护',
+            num: '3-1-2',
+          },
+          {
+            index: '/adminPolice/information/zxzydwxxwh',
+            title: '自行招用单位信息维护',
+            num: '3-1-3',
+          },
+          {
+            index: '/adminPolice/information/kqyfwgsxxwh',
+            title: '跨区域服务公司信息维护',
+            num: '3-1-4',
+          },
+          {
+            index: '/adminPolice/information/bafwgsxxwh',
+            title: '保安服务分公司信息维护',
+            num: '3-1-5',
+          },
+        ],
+      },
+      {
+        index: '3-2',
+        title: '综合信息查询',
+        num: '3-2',
+        subs: [
+          {
+            index: '/adminPolice/information/zbqccc',
+            title: '装备器材查询',
+            num: '3-2-1',
+          },
+          {
+            index: '/adminPolice/information/clxxcx',
+            title: '车辆信息查询',
+            num: '3-2-2',
+          },
+          // {
+          //   index: "/adminPolice/information/qzxxcx",
+          //   title: "枪证信息查询",
+          //   num: "3-2-3",
+          // },
+          {
+            index: '/adminPolice/information/qxxcx',
+            title: '枪支信息查询',
+            num: '3-2-4',
+          },
+          {
+            index: '/adminPolice/information/bapxrycx',
+            title: '保安培训人员查询',
+            num: '3-2-5',
+          },
+          {
+            index: '/adminPolice/information/babafwqycx',
+            title: '备案保安服务区域查询',
+            num: '3-2-6',
+          },
+          {
+            index: '/adminPolice/information/bayzhcx',
+            title: '保安员综合查询',
+            num: '3-2-7',
+          },
+          {
+            index: '/adminPolice/information/qyzhxxcx',
+            title: '企业综合信息查询',
+            num: '3-2-8',
+          },
+          {
+            index: '/adminPolice/information/baoannews',
+            title: '保安资讯信息查询',
+            num: '3-2-9',
+          },
+          // {
+          //   index: "/adminPolice/information/zdbdwtrycx",
+          //   title: "自动比对问题人员查询",
+          //   num: "3-2-10",
+          // },
+        ],
+      },
+    ],
+  },
+  {
+    icon: 'icon-tousu',
+    index: '4',
+    title: '位置监控',
+    num: '4',
+    subs: [
+      {
+        index: '/adminPolice/location/locamonitor',
+        title: '保安员实时位置监控',
+        num: '4-1',
+      },
+      {
+        index: '/adminPolice/location/trajectory',
+        title: '保安员行动轨迹查询',
+        num: '4-2',
+      },
+    ],
+  },
+  {
+    icon: 'icon-richang',
+    index: '5',
+    title: '协会业务',
+    num: '5',
+    subs: [
+      {
+        index: '/adminPolice/complaint/tsxxcx',
+        title: '投诉信息查询',
+        num: '5-1',
+      },
+      {
+        index: '/adminPolice/complaint/tsxxclcx',
+        title: '投诉信息处理查询',
+        num: '5-2',
+      },
+      {
+        index: '/adminPolice/notification/xctbgl',
+        title: '协查通报管理',
+        num: '5-3',
+      },
+      // {
+      //   index: '/adminPolice/notification/xctbcx',
+      //   title: '协查通报查询',
+      //   num: '5-4',
+      // },
+      {
+        index: '/adminPolice/notification/xctbhfqkck',
+        title: '协查通报回复情况查看',
+        num: '5-5',
+      },
+      // {
+      //   index: '/adminPolice/notification/xctbcxgl',
+      //   title: '协查通报撤销管理',
+      //   num: '5-6',
+      // },
+      {
+        index: '/adminPolice/reward/qyjlgl',
+        title: '企业奖励管理',
+        num: '5-7',
+      },
+      // {
+      //   index: '/adminPolice/reward/qyjlcx',
+      //   title: '企业奖励查询',
+      //   num: '5-8',
+      // },
+      {
+        index: '/adminPolice/daily/rcjcgl',
+        title: '日常检查管理',
+        num: '5-9',
+      },
+      // {
+      //   index: '/adminPolice/daily/rcjccx',
+      //   title: '日常检查查询',
+      //   num: '5-10',
+      // },
+    ],
+  },
+  {
+    icon: 'icon-richang',
+    index: '6',
+    title: '系统管理',
+    num: '6',
+    subs: [
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/securitydepart',
+        title: '公安端部门管理',
+        num: '6-1',
+      },
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/securityrole',
+        title: '公安端角色管理',
+        num: '6-2',
+      },
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/securityaccount',
+        title: '公安端账号管理',
+        num: '6-3',
+      },
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/companyaccount',
+        title: '企业账号管理',
+        num: '6-4',
+      },
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/baoanaccount',
+        title: '保安员账号管理',
+        num: '6-5',
+      },
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/societyaccount',
+        title: '协会端账号管理',
+        num: '6-6',
+      },
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/examaccount',
+        title: '考试端账号管理',
+        num: '6-7',
+      },
+      {
+        icon: 'icon-shouye',
+        index: '6-8',
+        title: '企业类型管理',
+        num: '6-8',
+        subs: [
+          // {
+          //   index: '/adminPolice/system/comapnytype',
+          //   title: '类型管理',
+          //   num: '6-8-1',
+          // },
+          {
+            index: '/adminPolice/system/companypower',
+            title: '权限分配',
+            num: '6-8-2',
+          },
+        ],
+      },
+      {
+        icon: 'icon-shouye',
+        index: '/adminPolice/system/reserve',
+        title: '信息采集预约安排',
+        num: '6-9',
+      },
+    ],
+  },
+  {
+    icon: 'icon-richang',
+    index: '/adminPolice/video',
+    title: '视频管理接入',
+    num: '7',
+  },
+];
+// 企业端
+// 增加pass字段:如果有该字段且为true,没有通过审核也可以使用
+export const companyMenu = [
+  {
+    icon: 'icon-qiyeshenfenrz',
+    index: '/admincompany/baseInfo',
+    title: '申报企业信息',
+    num: '1',
+    pass: true,
+  },
+  {
+    icon: 'icon-monikaoshi',
+    index: '2',
+    title: '保安员考试管理',
+    num: '2',
+    subs: [
+      {
+        index: '/admincompany/examination/sign',
+        title: '考试报名',
+        num: '2-1',
+      },
+      // {
+      //   index: "/admincompany/examination/messagecollect",
+      //   title: "信息采集(有设备)",
+      //   num: "2-2",
+      // },
+      // {
+      //   index: "/admincompany/examination/makemessagecollect",
+      //   title: "预约信息采集(企业)",
+      //   num: "2-3",
+      // },
+      {
+        index: '/admincompany/examination/examroom',
+        title: '考场信息',
+        num: '2-4',
+      },
+      // {
+      //   index: "/admincompany/examination/ticketprint",
+      //   title: "准考证打印",
+      //   num: "2-5",
+      // },
+    ],
+  },
+  {
+    icon: 'icon-zhengjianguanli-15',
+    index: '3',
+    title: '保安员管理',
+    num: '3',
+    subs: [
+      {
+        index: '/admincompany/security/checkinfo',
+        title: '保安员信息核查',
+        num: '3-1',
+      },
+      {
+        index: '/admincompany/security/workinfo',
+        title: '保安员从业信息',
+        num: '3-2',
+      },
+      {
+        index: '/admincompany/security/servicedispatch',
+        title: '保安员服务派遣',
+        num: '3-3',
+      },
+      {
+        index: '/admincompany/security/serviceadmin',
+        title: '服务派遣管理',
+        num: '3-4',
+      },
+      {
+        index: '/admincompany/security/cert',
+        title: '保安员证件信息查询',
+        num: '3-7',
+      },
+      {
+        index: '/admincompany/security/info',
+        title: '保安员信息查询',
+        num: '3-8',
+      },
+      {
+        index: '3-5',
+        title: '考勤与监管',
+        num: '3-5',
+        subs: [
+          {
+            index: '/admincompany/security/clock',
+            title: '保安员打卡情况查询',
+            num: '3-5-1',
+          },
+          {
+            index: '/admincompany/security/location',
+            title: '保安员实时位置监控',
+            num: '3-5-2',
+          },
+          {
+            index: '/admincompany/security/act',
+            title: '保安员行动轨迹查询',
+            num: '3-5-3',
+          },
+        ],
+      },
+      {
+        index: '3-6',
+        title: '招聘管理',
+        num: '3-6',
+        subs: [
+          {
+            index: '/admincompany/security/recruitwork',
+            title: '岗位信息',
+            num: '3-6-1',
+          },
+          {
+            index: '/admincompany/security/personnel',
+            title: '人才库',
+            num: '3-6-2',
+          },
+        ],
+      },
+    ],
+  },
+  {
+    icon: 'icon-daohang_fuwuduixiangguanli',
+    index: '4',
+    title: '服务对象管理',
+    num: '4',
+    subs: [
+      {
+        index: '/admincompany/service/target',
+        title: '服务对象查询',
+        num: '4-1',
+      },
+    ],
+  },
+  {
+    icon: 'icon-ico_zonghexinxi_xuanzhong',
+    index: '5',
+    title: '装备管理',
+    num: '5',
+    subs: [
+      {
+        index: '/admincompany/equipment/zbqccc',
+        title: '装备器材管理',
+        num: '5-1',
+      },
+      {
+        index: '/admincompany/equipment/qxxcx',
+        title: '枪支管理',
+        num: '5-2',
+      },
+      {
+        index: '/admincompany/equipment/clxxcx',
+        title: '车辆管理',
+        num: '5-3',
+      },
+      {
+        index: '/admincompany/equipment/clothing',
+        title: '保安企业服装购买情况',
+        num: '5-4',
+      },
+    ],
+  },
+  {
+    icon: 'icon-fuwuquyu',
+    index: '6',
+    title: '服务区域管理',
+    num: '6',
+    subs: [
+      {
+        index: '/admincompany/serviceregion/fwqycx',
+        title: '服务区域查询',
+        num: '6-1',
+      },
+    ],
+  },
+  {
+    icon: 'icon-danweixinxi',
+    index: '/admincompany/information',
+    title: '单位基本信息浏览',
+    num: '7',
+  },
+  {
+    icon: 'icon-farenshenpi',
+    index: '/admincompany/legalchange',
+    title: '法人变更申请',
+    num: '8',
+  },
+  {
+    icon: 'icon-richang',
+    index: '/admincompany/daily',
+    title: '日常检查查询',
+    num: '9',
+  },
+  {
+    icon: 'icon-jiangli',
+    index: '/admincompany/reward',
+    title: '企业奖励',
+    num: '10',
+  },
+  {
+    icon: 'icon-xinxixiecha',
+    index: '/admincompany/notification/xctbcx',
+    title: '协查通报查询',
+    num: '11',
+  },
+  // {
+  //   icon: "icon-xinxixiecha",
+  //   index: "10",
+  //   title: "协查通报",
+  //   num: "12",
+  //   subs: [
+  //     {
+  //       index: "/admincompany/notification/xctbcx",
+  //       title: "协查通报查询",
+  //       num: "12-1",
+  //     },
+  //     {
+  //       index: "/admincompany/notification/xctbhf",
+  //       title: "协查通报回复",
+  //       num: "12-2",
+  //     },
+  //     {
+  //       index: "/admincompany/notification/xctbxq",
+  //       title: "协查通报详情",
+  //       num: "12-3",
+  //     },
+  //   ],
+  // },
+];
+
+// 考试端
+export const examMenu = [
+  {
+    index: '/baoanexam/security/kcbp',
+    title: '考场编排',
+    num: '1-3-2',
+  },
+  {
+    index: '/baoanexam/security/zkzdy',
+    title: '准考证打印',
+    num: '1-3-3',
+  },
+  // {
+  //   index: "/baoanexam/security/zkzfmdy",
+  //   title: "准考证封面打印",
+  //   num: "1-3-4",
+  // },
+  {
+    index: '/baoanexam/security/zkzcx',
+    title: '准考证查询',
+    num: '1-3-5',
+  },
+  {
+    index: '/baoanexam/security/ksxxdc',
+    title: '考生信息导出',
+    num: '1-3-6',
+  },
+  {
+    index: '/baoanexam/security/ksqkdj',
+    title: '考试情况登记',
+    num: '1-3-7',
+  },
+  {
+    index: '/baoanexam/security/kscjcx',
+    title: '考试成绩查询',
+    num: '1-3-8',
+  },
+  // {
+  //   index: '/baoanexam/security/zkzdyrz',
+  //   title: '准考证打印日志',
+  //   num: '1-3-9',
+  // },
+  {
+    index: 'exam',
+    title: '考试管理',
+    num: '1-3-10',
+    subs: [
+      {
+        index: '/exam/questionType',
+        title: '考试题型设置',
+        num: '1-4-1',
+      },
+      {
+        index: '/exam/config',
+        title: '考试设置',
+        num: '1-4-2',
+      },
+      {
+        index: '/exam/question',
+        title: '考试题库',
+        num: '1-4-3',
+      },
+      // {
+      //   index: '/exam/exam',
+      //   title: '试卷管理',
+      //   num: '1-4-4',
+      // },
+      {
+        index: '/exam/answer',
+        title: '考试结果',
+        num: '1-4-5',
+      },
+    ],
+  },
+];
+// 协会端
+export const societyMenu = [
+  {
+    index: '/baoansociety/complaint/tsxxcx',
+    title: '投诉信息查询',
+    num: '6-1',
+  },
+  {
+    index: '/baoansociety/complaint/tsxxclcx',
+    title: '投诉信息处理查询',
+    num: '6-2',
+  },
+  {
+    index: '/baoansociety/notification/xctbgl',
+    title: '协查通报管理',
+    num: '7-1',
+  },
+  // {
+  //   index: '/baoansociety/notification/xctbcx',
+  //   title: '协查通报查询',
+  //   num: '7-2',
+  // },
+  {
+    index: '/baoansociety/notification/xctbhfqkck',
+    title: '协查通报回复情况查看',
+    num: '7-3',
+  },
+  // {
+  //   index: '/baoansociety/notification/xctbcxgl',
+  //   title: '协查通报撤销管理',
+  //   num: '7-4',
+  // },
+  {
+    index: '/baoansociety/reward/qyjlgl',
+    title: '企业奖励管理',
+    num: '8-1',
+  },
+  // {
+  //   index: '/baoansociety/reward/qyjlcx',
+  //   title: '企业奖励查询',
+  //   num: '8-2',
+  // },
+  {
+    index: '/baoansociety/daily/rcjcgl',
+    title: '日常检查管理',
+    num: '9-1',
+  },
+  // {
+  //   index: '/baoansociety/daily/rcjccx',
+  //   title: '日常检查查询',
+  //   num: '9-2',
+  // },
+];

+ 61 - 0
src/layout/deploy/site.js

@@ -0,0 +1,61 @@
+// 网站基本设置
+export const siteInfo = {
+  display: true,
+  zhTitle: '长春市保安服务监管系统',
+  enTitle: 'Galactic water drop',
+  logo_url: require('../../assets/logo.png'),
+};
+// 菜单设置
+export const menuInfo = {
+  info: {
+    display: true,
+    mode: 'horizontal',
+    backColor: '#0457c7',
+    textColor: '#ffffff',
+    // actColor: '#fe950e',
+    actColor: '#ffffff',
+  },
+  menuList: [
+    // { icon: '', index: '/test1/index', title: '测试菜单一' },
+  ],
+};
+// 轮播图设置
+export const bannerInfo = {
+  info: {
+    display: true,
+    // 轮播高度
+    height: '400px',
+    // 指示器触发方式-默认值:hover,click:点击
+    trigger: '',
+    // 是否自动切换-默认值:true
+    autoplay: true,
+    // 自动切换秒数
+    interval: 3000,
+    // 指示器位置显示-默认值:显示,outside:外部,none:不显示,
+    indicatorpos: '',
+    // 切换箭头-默认值:鼠标滑过时显示,always:一直显示,never:一直隐藏
+    arrow: '',
+    // 轮播类型-card:卡片化
+    type: '',
+    // 是否循环显示:默认值:true
+    loop: true,
+    // 轮播垂直方向显示-默认值:横向,vertical:垂直
+    direction: 'horizontal',
+  },
+  list: [],
+};
+// 网站底部信息
+export const footInfo = {
+  display: true,
+  p1: '技术运营:吉林省科技大市场专利分市场',
+  p2: '技术支持:长春福瑞科技有限公司',
+  p3: '地址:吉林省长春市朝阳区前进大街1244号电话:17319450324微信:guhongwei324邮箱:guhongwei0324@163.com',
+  p4: '吉ICP备2020007658号-1 Copyright 2019 版权所有 中科院长春分院 吉林省计算中心 All Rights Reserved',
+};
+// 网站左侧菜单颜色设置
+// 菜单背景颜色,文件颜色,选中颜色
+export const adminMenus = {
+  backColor: '#00d1b2',
+  textColor: '#ffffff',
+  activeColor: '#000000',
+};

+ 54 - 0
src/layout/unitdict.js

@@ -0,0 +1,54 @@
+export const steps = [
+  {
+    title: '基本信息',
+    value: 'one|two|thr|four|five|six',
+  },
+  {
+    title: '电子资料',
+    value: 'one|two|thr|four|five|six',
+  },
+  {
+    title: '申请人信息',
+    value: 'one|six',
+  },
+  {
+    title: '股东及出资额信息',
+    value: 'one|six',
+  },
+  {
+    title: '管理人信息',
+    value: 'one|two|six',
+  },
+  {
+    title: '申请单位信息',
+    value: 'two',
+  },
+  {
+    title: '培训机构师资信息',
+    value: 'two',
+  },
+  {
+    title: '单位法人信息',
+    value: 'thr|four|five',
+  },
+  {
+    title: '单位经营负责人',
+    value: 'thr',
+  },
+  {
+    title: '保安服务对象',
+    value: 'thr',
+  },
+  {
+    title: '单位分管负责人信息',
+    value: 'four',
+  },
+  {
+    title: '单位服务区域',
+    value: 'four',
+  },
+  {
+    title: '负责人信息',
+    value: 'five',
+  },
+];

+ 12 - 0
src/main.js

@@ -0,0 +1,12 @@
+import Vue from 'vue';
+import App from './App.vue';
+import router from './router';
+import store from './store';
+
+Vue.config.productionTip = false;
+
+new Vue({
+  router,
+  store,
+  render: h => h(App),
+}).$mount('#app');

+ 17 - 0
src/router/index.js

@@ -0,0 +1,17 @@
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+
+Vue.use(VueRouter);
+
+const routes = [
+  {
+    path: '/',
+    component: () => import('../views/index.vue'),
+  },
+];
+
+const router = new VueRouter({
+  routes,
+});
+
+export default router;

+ 42 - 0
src/store/assistance_investigation.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/assistance_investigation`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/assistance_investigation_reply.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/assistance_investigation_reply`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/baoan_news.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/baoan_news`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 101 - 0
src/store/baoan_statis.js

@@ -0,0 +1,101 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async oneQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/examination/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async twoQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/examine/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async thrQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/signup/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async fourQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/securitylevel/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async fiveQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/serviceobject/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async sixQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/securityarea/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async servenQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/headerstatis/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async eightQuery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/headerstatis3/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  // 新增2021-12-20
+  // 一级
+  async newOnequery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/newheaderstatis1/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async newTwoquery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/newheaderstatis2/statis`, {
+      ...info,
+    });
+    return res;
+  },
+
+  // 二级
+  async newThrquery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/newheaderstatis3/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  // 三级
+  async newFivequery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/newheaderstatis5/statis`, {
+      ...info,
+    });
+    return res;
+  },
+  async newSixquery({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/newheaderstatis6/statis`, {
+      ...info,
+    });
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/baoan_work_warning.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/baoan_work_warning`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/certificates_base.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/certificates_base`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/certificates_grant.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/certificates_grant`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/certificates_print_again.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/certificates_print_again`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/certificates_print_log.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/certificates_print_log`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_accept.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_accept`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_applicant.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_applicant`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_baoan_dispatch.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_baoan_dispatch`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_baoan_punch.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_baoan_punch`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_baoan_work.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_baoan_work`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_base.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_base`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_car.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_car`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_dress.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_dress`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_equipment.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_equipment`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_examine.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_examine`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_gun.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_gun`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_information.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_information`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_keep.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_keep`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_keep_grant.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_keep_grant`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_legal_change.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_legal_change`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_legal_person.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_legal_person`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_licence.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_licence`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_licence_grant.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_licence_grant`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_managers.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_managers`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_person_charge.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_person_charge`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_recruit_work.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_recruit_work`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 42 - 0
src/store/company_service_area.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  test: `/api/usual/company_service_area`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async query({ commit }, { skip = 0, limit, ...info } = {}) {
+    const res = await this.$axios.$get(`${api.test}/query`, {
+      skip,
+      limit,
+      ...info,
+    });
+    return res;
+  },
+  async fetch({ commit }, payload) {
+    const res = await this.$axios.$get(`${api.test}/fetch`, payload);
+    return res;
+  },
+  async create({ commit }, payload) {
+    const res = await this.$axios.$post(`${api.test}`, payload);
+    return res;
+  },
+  async update({ commit }, { id, ...info } = {}) {
+    const res = await this.$axios.$post(`${api.test}/update`, { ...info }, { id });
+    return res;
+  },
+  async delete({ commit }, payload) {
+    const res = await this.$axios.$delete(`${api.test}/${payload}`);
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 0 - 0
src/store/company_service_object.js


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