lrf402788946 3 years ago
commit
946173f6c2
73 changed files with 4071 additions and 0 deletions
  1. 2 0
      .env
  2. 33 0
      .eslintrc.js
  3. 24 0
      .gitignore
  4. 0 0
      README.md
  5. 3 0
      babel.config.js
  6. 66 0
      package.json
  7. BIN
      public/favicon.ico
  8. 17 0
      public/index.html
  9. 41 0
      src/App.vue
  10. 28 0
      src/assets/css/color-dark.css
  11. 4 0
      src/assets/css/icon.css
  12. 177 0
      src/assets/css/main.css
  13. 29 0
      src/assets/css/theme-green/color-green.css
  14. BIN
      src/assets/css/theme-green/fonts/element-icons.ttf
  15. BIN
      src/assets/css/theme-green/fonts/element-icons.woff
  16. 1 0
      src/assets/css/theme-green/index.css
  17. BIN
      src/assets/img/login-bg.jpg
  18. BIN
      src/assets/logo.png
  19. 88 0
      src/components/HelloWorld.vue
  20. 198 0
      src/components/list/list-page.vue
  21. 37 0
      src/components/list/search.vue
  22. 65 0
      src/components/list/tabs.vue
  23. 28 0
      src/main.js
  24. 19 0
      src/plugins/axios.js
  25. 39 0
      src/plugins/check-res.js
  26. 12 0
      src/plugins/components.js
  27. 5 0
      src/plugins/element.js
  28. 6 0
      src/plugins/filters.js
  29. 27 0
      src/plugins/loading.js
  30. 4 0
      src/plugins/meta.js
  31. 33 0
      src/plugins/methods.js
  32. 21 0
      src/plugins/setting.js
  33. 65 0
      src/plugins/stomp.js
  34. 25 0
      src/plugins/var.js
  35. 136 0
      src/router/index.js
  36. 24 0
      src/store/index.js
  37. 117 0
      src/util/axios-wrapper.js
  38. 10 0
      src/util/filters.js
  39. 50 0
      src/util/methods-util.js
  40. 69 0
      src/util/user-util.js
  41. 93 0
      src/views/adminCenter/disclosure/detail.vue
  42. 70 0
      src/views/adminCenter/disclosure/evaluate.vue
  43. 179 0
      src/views/adminCenter/disclosure/index.vue
  44. 38 0
      src/views/adminCenter/disclosure/propose.vue
  45. 36 0
      src/views/adminCenter/homeIndex/index.vue
  46. 37 0
      src/views/adminCenter/index.vue
  47. 114 0
      src/views/adminCenter/mechanism/detail.vue
  48. 109 0
      src/views/adminCenter/mechanism/index.vue
  49. 41 0
      src/views/adminCenter/message/index.vue
  50. 45 0
      src/views/adminCenter/patent/index.vue
  51. 30 0
      src/views/adminCenter/statistics/index.vue
  52. 81 0
      src/views/adminCenter/test/detail.vue
  53. 82 0
      src/views/adminCenter/test/index.vue
  54. 43 0
      src/views/adminCenter/trade/index.vue
  55. 59 0
      src/views/adminCenter/trade/parts/list.vue
  56. 132 0
      src/views/adminCenter/user/detail.vue
  57. 65 0
      src/views/adminCenter/user/index.vue
  58. 79 0
      src/views/adminCenter/user/parts/expDetail.vue
  59. 115 0
      src/views/adminCenter/user/parts/expeUser.vue
  60. 198 0
      src/views/adminCenter/user/parts/mechUser.vue
  61. 69 0
      src/views/adminCenter/user/parts/orgDetail.vue
  62. 58 0
      src/views/adminCenter/user/parts/perDetail.vue
  63. 194 0
      src/views/adminCenter/user/parts/perUser.vue
  64. 93 0
      src/views/jg/disclosure/detail.vue
  65. 93 0
      src/views/jg/disclosure/index.vue
  66. 36 0
      src/views/jg/homeIndex/index.vue
  67. 41 0
      src/views/jg/message/index.vue
  68. 45 0
      src/views/jg/patent/index.vue
  69. 30 0
      src/views/jg/statistics/index.vue
  70. 43 0
      src/views/jg/trade/index.vue
  71. 59 0
      src/views/jg/trade/parts/list.vue
  72. 127 0
      src/views/login.vue
  73. 34 0
      vue.config.js

+ 2 - 0
.env

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

+ 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?

+ 0 - 0
README.md


+ 3 - 0
babel.config.js

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

+ 66 - 0
package.json

@@ -0,0 +1,66 @@
+{
+  "name": "patent-cms",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "@stomp/stompjs": "^6.1.0",
+    "axios": "^0.21.1",
+    "babel-polyfill": "^6.26.0",
+    "core-js": "^3.6.5",
+    "echarts": "^5.1.0",
+    "element-ui": "^2.15.1",
+    "html2canvas": "^1.0.0-rc.7",
+    "jsonwebtoken": "^8.5.1",
+    "jspdf": "^2.3.1",
+    "lodash": "^4.17.21",
+    "moment": "^2.29.1",
+    "naf-core": "^0.1.2",
+    "vue": "^2.6.11",
+    "vue-i18n": "^8.24.4",
+    "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.3.1",
+    "eslint-plugin-vue": "^6.2.2",
+    "less": "^3.0.4",
+    "less-loader": "^5.0.0",
+    "prettier": "^2.2.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>加载中...</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>

+ 41 - 0
src/App.vue

@@ -0,0 +1,41 @@
+<template>
+  <div id="App">
+    <router-view></router-view>
+  </div>
+</template>
+
+<script>
+export default {
+  metaInfo: { title: 'App' },
+  name: 'App',
+  props: {},
+  components: {},
+  data: function () {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {},
+};
+</script>
+
+<style>
+@import './assets/css/main.css';
+@import './assets/css/color-dark.css';
+body {
+  margin: 0;
+}
+.w_1200 {
+  width: 1200px;
+  margin: 0 auto;
+}
+p {
+  margin: 0;
+  padding: 0;
+}
+.textOver {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+</style>

+ 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;
+    }

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

@@ -0,0 +1,177 @@
+* {
+    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: auto;
+    height: 100%;
+    padding: 10px;
+    overflow-y: scroll;
+    box-sizing: border-box;
+}
+
+.content-collapse {
+    left: 65px;
+}
+
+.container {
+    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;
+}

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

@@ -0,0 +1,29 @@
+.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;
+    background-color: #00d1b2;
+}
+.collapse-btn:hover{
+    background: #00d1b2;
+}

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


BIN
src/assets/img/login-bg.jpg


BIN
src/assets/logo.png


+ 88 - 0
src/components/HelloWorld.vue

@@ -0,0 +1,88 @@
+<template>
+  <div class="hello">
+    <h1>{{ msg }}</h1>
+    <p>
+      For a guide and recipes on how to configure / customize this project,<br />
+      check out the
+      <a href="https://cli.vuejs.org" target="_blank" rel="noopener">vue-cli documentation</a>.
+    </p>
+    <h3>Installed CLI Plugins</h3>
+    <ul>
+      <li>
+        <a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-babel" target="_blank" rel="noopener">babel</a>
+      </li>
+      <li>
+        <a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-router" target="_blank" rel="noopener">router</a>
+      </li>
+      <li>
+        <a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-vuex" target="_blank" rel="noopener">vuex</a>
+      </li>
+      <li>
+        <a href="https://github.com/vuejs/vue-cli/tree/dev/packages/%40vue/cli-plugin-eslint" target="_blank" rel="noopener">eslint</a>
+      </li>
+    </ul>
+    <h3>Essential Links</h3>
+    <ul>
+      <li>
+        <a href="https://vuejs.org" target="_blank" rel="noopener">Core Docs</a>
+      </li>
+      <li>
+        <a href="https://forum.vuejs.org" target="_blank" rel="noopener">Forum</a>
+      </li>
+      <li>
+        <a href="https://chat.vuejs.org" target="_blank" rel="noopener">Community Chat</a>
+      </li>
+      <li>
+        <a href="https://twitter.com/vuejs" target="_blank" rel="noopener">Twitter</a>
+      </li>
+      <li>
+        <a href="https://news.vuejs.org" target="_blank" rel="noopener">News</a>
+      </li>
+    </ul>
+    <h3>Ecosystem</h3>
+    <ul>
+      <li>
+        <a href="https://router.vuejs.org" target="_blank" rel="noopener">vue-router</a>
+      </li>
+      <li>
+        <a href="https://vuex.vuejs.org" target="_blank" rel="noopener">vuex</a>
+      </li>
+      <li>
+        <a href="https://github.com/vuejs/vue-devtools#vue-devtools" target="_blank" rel="noopener">vue-devtools</a>
+      </li>
+      <li>
+        <a href="https://vue-loader.vuejs.org" target="_blank" rel="noopener">vue-loader</a>
+      </li>
+      <li>
+        <a href="https://github.com/vuejs/awesome-vue" target="_blank" rel="noopener">awesome-vue</a>
+      </li>
+    </ul>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'HelloWorld',
+  props: {
+    msg: String,
+  },
+};
+</script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped lang="less">
+h3 {
+  margin: 40px 0 0;
+}
+ul {
+  list-style-type: none;
+  padding: 0;
+}
+li {
+  display: inline-block;
+  margin: 0 10px;
+}
+a {
+  color: #42b983;
+}
+</style>

+ 198 - 0
src/components/list/list-page.vue

@@ -0,0 +1,198 @@
+<template>
+  <div id="list-page">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="top">
+          <el-col :span="3" class="left">
+            <span>|</span> <span>{{ title }}</span>
+          </el-col>
+          <el-col :span="21" class="right">
+            <el-row type="flex" justify="end">
+              <el-col :span="12" class="tabs" v-if="useTab">
+                <tabs :displayList="displayList" :dropList="dropList" @toSearch="change"></tabs>
+              </el-col>
+              <el-col :span="12" class="search" v-if="useSearch">
+                <search @toSearch="toSearch"></search>
+              </el-col>
+            </el-row>
+          </el-col>
+        </el-col>
+        <el-col :span="24" class="down">
+          <slot></slot>
+          <!-- <el-col :span="24" class="list" v-for="(item, index) in list" :key="index">
+            <el-col :span="21" class="name" @click.native="clickDetail(item.id)">
+              {{ item.p1 }}
+            </el-col>
+            <el-col :span="3" class="date">
+              {{ getDate(item.p2) }}
+            </el-col>
+            <el-col :span="24" class="brief"> 成果简介:{{ item.p3 || '暂无' }} </el-col>
+          </el-col> -->
+        </el-col>
+        <el-col :span="24" class="page" v-if="usePage">
+          <el-pagination @current-change="search" :current-page="currentPage" layout="total, prev, pager, next, jumper" :total="total" :page-size="pageSize">
+          </el-pagination>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import tabs from './tabs.vue';
+import search from './search.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'list-page',
+  props: {
+    useTab: { type: Boolean, default: false },
+    useSearch: { type: Boolean, default: true },
+    displayList: { type: Array, default: () => [] },
+    dropList: { type: Array, default: () => [] },
+    list: { type: Array, default: () => [] },
+    searchModel: { type: String, default: 'name' },
+    title: { type: String },
+    total: { type: Number, default: 0 },
+    pageSize: { type: Number, default: 5 },
+    usePage: { type: Boolean, default: true },
+  },
+  components: { tabs, search },
+  data: function () {
+    return {
+      searchInfo: undefined,
+      currentPage: 1,
+      condition: {},
+    };
+  },
+  created() {},
+  methods: {
+    search(page = 1) {
+      this.currentPage = page;
+      const skip = (this.currentPage - 1) * this.pageSize;
+      let condition = { skip, limit: this.pageSize, ...this.condition };
+      if (this.searchInfo && this.searchInfo !== '') condition[this.searchModel] = this.searchInfo;
+      this.$emit('toSearch', condition);
+    },
+    toSearch(condition) {
+      if (condition) {
+        this.$set(this, `searchInfo`, condition);
+      } else {
+        this.$set(this, `searchInfo`, undefined);
+      }
+      this.currentPage = 1;
+      this.search();
+    },
+    change(condition) {
+      this.$set(this, `condition`, condition);
+      this.currentPage = 1;
+      this.search();
+      // this.$emit('toChangeTab', condition);
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .top {
+    height: 49px;
+    border-bottom: 1px solid #ccc;
+    padding: 5px 0 0 0;
+    .left {
+      text-align: left;
+      span:first-child {
+        color: #22529a;
+        font-weight: bold;
+        font-size: 25px;
+      }
+      span:last-child {
+        color: #22529a;
+        font-size: 20px;
+        font-weight: bold;
+      }
+    }
+    .right {
+      .tabs {
+        span {
+          font-size: 16px;
+          padding: 8px 10px;
+          display: inline-block;
+          font-weight: bold;
+        }
+        span:hover {
+          cursor: pointer;
+          color: #409eff;
+        }
+        .btn {
+          padding: 0px 0 0 0;
+          position: relative;
+          top: 1px;
+          i {
+            font-size: 20px;
+            font-weight: bold;
+          }
+        }
+      }
+      .search {
+        /deep/.el-input__inner {
+          height: 35px;
+          line-height: 35px;
+        }
+      }
+    }
+  }
+  .down {
+    height: 500px;
+    overflow: hidden;
+    .list {
+      padding: 10px 0;
+      .name {
+        font-size: 18px;
+        font-weight: bold;
+      }
+      .date {
+        font-size: 16px;
+        text-align: center;
+      }
+      .brief {
+        font-size: 16px;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        -webkit-line-clamp: 2;
+        word-break: break-all;
+        display: -webkit-box;
+        -webkit-box-orient: vertical;
+        margin: 10px 0 0 0;
+        max-height: 42px;
+      }
+    }
+    .list:hover {
+      cursor: pointer;
+      .name {
+        -webkit-transform: translateY(-3px);
+        -ms-transform: translateY(-3px);
+        transform: translateY(-3px);
+        -webkit-box-shadow: 0 0 6px #999;
+        box-shadow: 0 0 6px #999;
+        -webkit-transition: all 0.5s ease-out;
+        transition: all 0.5s ease-out;
+        color: #0085d2;
+      }
+    }
+  }
+  .page {
+    text-align: center;
+    height: 30px;
+    overflow: hidden;
+  }
+}
+</style>

+ 37 - 0
src/components/list/search.vue

@@ -0,0 +1,37 @@
+<template>
+  <div id="search">
+    <el-input placeholder="请输入名称" v-model="input" class="input-with-select" clearable>
+      <el-button slot="append" icon="el-icon-search" @click="searchData()"></el-button>
+    </el-input>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'search',
+  components: {},
+  data: function () {
+    return {
+      input: undefined,
+    };
+  },
+  created() {},
+  methods: {
+    searchData() {
+      this.$emit('toSearch', !this.input ? undefined : this.input);
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 65 - 0
src/components/list/tabs.vue

@@ -0,0 +1,65 @@
+<template>
+  <div id="tabs">
+    <!-- 主要成果单位 -->
+    <span v-for="(item, index) in displayList" :key="index" @click="change(item.name)">{{ item.name }}</span>
+    <!-- 其他 -->
+    <el-dropdown trigger="click" @command="change">
+      <span class="el-dropdown-link btn"><i class="el-icon-d-arrow-right"></i> </span>
+      <el-dropdown-menu slot="dropdown">
+        <el-dropdown-item v-for="(item, index) in dropList" :key="index" :command="item.name">{{ item.name }}</el-dropdown-item>
+      </el-dropdown-menu>
+    </el-dropdown>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'tabs',
+  props: {
+    displayList: { type: Array, default: () => [] },
+    dropList: { type: Array, default: () => [] },
+  },
+  components: {},
+  data: function () {
+    return {};
+  },
+  created() {},
+  methods: {
+    change(data) {
+      this.$emit('toSearch', { company: data });
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+span {
+  font-size: 16px;
+  padding: 8px 10px;
+  display: inline-block;
+  font-weight: bold;
+}
+span:hover {
+  cursor: pointer;
+  color: #409eff;
+}
+.btn {
+  padding: 0px 0 0 0;
+  position: relative;
+  top: 1px;
+  i {
+    font-size: 20px;
+    font-weight: bold;
+  }
+}
+</style>

+ 28 - 0
src/main.js

@@ -0,0 +1,28 @@
+import Vue from 'vue';
+import App from './App.vue';
+import router from './router';
+import store from './store';
+import '@/plugins/element.js';
+import '@/plugins/axios';
+import '@/plugins/check-res';
+import '@/plugins/meta';
+import '@/plugins/filters';
+import '@/plugins/loading';
+import '@/plugins/var';
+import '@/plugins/methods';
+import '@/plugins/setting';
+import '@/plugins/components';
+import '@frame/src/assets/icon/iconfont.css';
+// 管理中心页面效果布局所需插件
+import ElementUI from 'element-ui';
+import VueI18n from 'vue-i18n';
+import 'babel-polyfill';
+import { messages } from '@frame/src/components/admin-frame/i18n';
+import '@frame/src/components/admin-frame/directives';
+import 'element-ui/lib/theme-chalk/index.css';
+Vue.config.productionTip = false;
+Vue.use(VueI18n);
+Vue.use(ElementUI, { size: 'small' });
+const i18n = new VueI18n({ locale: 'zh', messages });
+new Vue({ router, store, i18n, render: (h) => h(App) }).$mount('#app');
+window.vm = new Vue({ router });

+ 19 - 0
src/plugins/axios.js

@@ -0,0 +1,19 @@
+import Vue from 'vue';
+import AxiosWrapper from '@/util/axios-wrapper';
+
+const Plugin = {
+  install(vue, options) {
+    // 3. 注入组件
+    vue.mixin({
+      created() {
+        if (this.$store && !this.$store.$axios) {
+          this.$store.$axios = this.$axios;
+        }
+      },
+    });
+    // 4. 添加实例方法
+    vue.prototype.$axios = new AxiosWrapper(options);
+  },
+};
+
+Vue.use(Plugin, { baseUrl: process.env.VUE_APP_AXIOS_BASE_URL });

+ 39 - 0
src/plugins/check-res.js

@@ -0,0 +1,39 @@
+/* eslint-disable no-underscore-dangle */
+/* eslint-disable no-param-reassign */
+/* eslint-disable no-unused-vars */
+/* eslint-disable no-shadow */
+import Vue from 'vue';
+import _ from 'lodash';
+import { Message } from 'element-ui';
+
+const vm = new Vue({});
+const Plugin = {
+  install(Vue, options) {
+    // 4. 添加实例方法
+    Vue.prototype.$checkRes = (res, okText, errText) => {
+      let _okText = okText;
+      let _errText = errText;
+      if (!_.isFunction(okText) && _.isObject(okText) && okText != null) {
+        ({ okText: _okText, errText: _errText } = okText);
+      }
+      const { errcode = 0, errmsg } = res || {};
+      if (errcode === 0) {
+        if (_.isFunction(_okText)) {
+          return _okText();
+        }
+        if (_okText) {
+          Message.success(_okText);
+        }
+        return true;
+      }
+      if (_.isFunction(_errText)) {
+        return _errText();
+      }
+      Message.error(_errText || errmsg);
+      // Message({ message: _errText || errmsg, duration: 60000 });
+      return false;
+    };
+  },
+};
+
+Vue.use(Plugin);

+ 12 - 0
src/plugins/components.js

@@ -0,0 +1,12 @@
+import Vue from 'vue';
+import dataTable from '@frame/src/components/frame/filter-page-table.vue';
+import dataForm from '@frame/src/components/frame/form.vue';
+import eUpload from '@frame/src/components/frame/e-upload.vue';
+const Plugin = (vue) => {
+  vue.prototype.$dev_mode = process.env.NODE_ENV === 'development';
+  vue.component('data-table', dataTable);
+  vue.component('data-form', dataForm);
+  vue.component('eUpload', eUpload);
+};
+
+Vue.use(Plugin);

+ 5 - 0
src/plugins/element.js

@@ -0,0 +1,5 @@
+import Vue from 'vue';
+import Element from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+
+Vue.use(Element);

+ 6 - 0
src/plugins/filters.js

@@ -0,0 +1,6 @@
+import Vue from 'vue';
+import filters from '@/util/filters';
+
+for (const method in filters) {
+  Vue.filter(method, filters[method]);
+}

+ 27 - 0
src/plugins/loading.js

@@ -0,0 +1,27 @@
+/* eslint-disable no-console */
+/* eslint-disable no-param-reassign */
+
+import Vue from 'vue';
+
+const Plugin = {
+  // eslint-disable-next-line no-unused-vars
+  install(vue, options) {
+    // 3. 注入组件
+    vue.mixin({
+      created() {
+        // eslint-disable-next-line no-underscore-dangle
+        const isRoot = this.constructor === Vue;
+        // console.log(`rootId:${rootVue_uid}; thisId:${this._uid}`);
+        // if (rootVue_uid !== 3) {
+        //   console.log(this);
+        // }
+        if (isRoot) {
+          const el = document.getElementById('loading');
+          if (el) el.style.display = 'none';
+        }
+      },
+    });
+  },
+};
+
+Vue.use(Plugin, { baseUrl: process.env.VUE_APP_AXIOS_BASE_URL });

+ 4 - 0
src/plugins/meta.js

@@ -0,0 +1,4 @@
+import Vue from 'vue';
+import Meta from 'vue-meta';
+
+Vue.use(Meta);

+ 33 - 0
src/plugins/methods.js

@@ -0,0 +1,33 @@
+import Vue from 'vue';
+import _ from 'lodash';
+const Plugin = {
+  install(Vue, options) {
+    // 3. 注入组件
+    Vue.mixin({
+      created() {
+        if (this.$store && !this.$store.$toUndefined) {
+          this.$store.$toUndefined = this.$toUndefined;
+        }
+      },
+    });
+    // 4. 添加实例方法
+    Vue.prototype.$toUndefined = (object) => {
+      let keys = Object.keys(object);
+      keys.map((item) => {
+        object[item] = object[item] === '' ? (object[item] = undefined) : object[item];
+      });
+      return object;
+    };
+    Vue.prototype.$turnTo = (item) => {
+      if (item.info_type == 1) {
+        window.open(item.url);
+      } else {
+        let router = window.vm.$router;
+        let route = window.vm.$route.path;
+        router.push({ path: `/info/detail?id=${item.id}` });
+      }
+    };
+  },
+};
+
+Vue.use(Plugin);

+ 21 - 0
src/plugins/setting.js

@@ -0,0 +1,21 @@
+import Vue from 'vue';
+
+Vue.config.weixin = {
+  // baseUrl: process.env.BASE_URL + 'weixin',
+  baseUrl: `http://${location.host}`,
+};
+
+Vue.config.stomp = {
+  // brokerURL: 'ws://192.168.1.118/ws',
+  brokerURL: '/ws', // ws://${location.host}/ws
+  // brokerURL: 'ws://127.0.0.1:8000/ws',
+  connectHeaders: {
+    host: 'platform',
+    login: 'visit', //visit
+    passcode: 'visit', //visit123
+  },
+  // debug: true,
+  reconnectDelay: 5000,
+  heartbeatIncoming: 4000,
+  heartbeatOutgoing: 4000,
+};

+ 65 - 0
src/plugins/stomp.js

@@ -0,0 +1,65 @@
+/**
+ * 基于WebStomp的消息处理插件
+ */
+
+import Vue from 'vue';
+import _ from 'lodash';
+import assert from 'assert';
+import { Client } from '@stomp/stompjs/esm5/client';
+
+const Plugin = {
+  install(Vue, options) {
+    assert(_.isObject(options));
+    if (options.debug && !_.isFunction(options.debug)) {
+      options.debug = (str) => {
+        console.log(str);
+      };
+    }
+    assert(_.isString(options.brokerURL));
+    if (!options.brokerURL.startsWith('ws://')) {
+      options.brokerURL = `ws://${location.host}${options.brokerURL}`;
+    }
+
+    // 3. 注入组件
+    Vue.mixin({
+      beforeDestroy: function () {
+        if (this.$stompClient) {
+          this.$stompClient.deactivate();
+          delete this.$stompClient;
+        }
+      },
+    });
+
+    // 4. 添加实例方法
+    Vue.prototype.$stomp = function (subscribes = {}) {
+      // connect to mq
+      const client = new Client(options);
+      client.onConnect = (frame) => {
+        // Do something, all subscribes must be done is this callback
+        // This is needed because this will be executed after a (re)connect
+        console.log('[stomp] connected');
+        Object.keys(subscribes)
+          .filter((p) => _.isFunction(subscribes[p]))
+          .forEach((key) => {
+            client.subscribe(key, subscribes[key]);
+          });
+      };
+
+      client.onStompError = (frame) => {
+        // Will be invoked in case of error encountered at Broker
+        // Bad login/passcode typically will cause an error
+        // Complaint brokers will set `message` header with a brief message. Body may contain details.
+        // Compliant brokers will terminate the connection after any error
+        console.log('Broker reported error: ' + frame.headers['message']);
+        console.log('Additional details: ' + frame.body);
+      };
+
+      client.activate();
+
+      this.$stompClient = client;
+    };
+  },
+};
+export default () => {
+  Vue.use(Plugin, Vue.config.stomp);
+};

+ 25 - 0
src/plugins/var.js

@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import _ from 'lodash';
+
+const getSiteId = () => {
+  let host = `${window.location.hostname}`; //`999991.smart.jilinjobswx.cn ${window.location.hostname}`
+  let schId;
+  host = host.replace('http://', '');
+  let arr = host.split('.');
+  if (arr.length > 0) {
+    schId = arr[0];
+    if (schId === 'smart') schId = 'master';
+    else `${schId}`.includes('localhost') || `${schId}`.includes('127.0.0.1') ? (schId = '99991') : '';
+    sessionStorage.setItem('schId', `${schId}`.includes('localhost') || `${schId}`.includes('127.0.0.1') ? '99991' : schId);
+  }
+  return schId;
+};
+const Plugin = {
+  install(vue, options) {
+    // 4. 添加实例方法
+    vue.prototype.$limit = 10;
+    vue.prototype.$site = getSiteId();
+  },
+};
+
+Vue.use(Plugin);

+ 136 - 0
src/router/index.js

@@ -0,0 +1,136 @@
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+import store from '@/store/index';
+const jwt = require('jsonwebtoken');
+const originalPush = VueRouter.prototype.push;
+VueRouter.prototype.push = function push(location) {
+  return originalPush.call(this, location).catch((err) => err);
+};
+Vue.use(VueRouter);
+const web = [
+  // 管理登录
+  {
+    path: '/login',
+    name: 'login',
+    meta: { title: '管理登录' },
+    component: () => import('../views/login.vue'),
+  },
+  // 管理中心
+  {
+    path: '/adminCenter/homeIndex',
+    name: 'adminCenter',
+    component: () => import('@frame/src/components/admin-frame/Home.vue'),
+    children: [
+      {
+        path: '/adminCenter/homeIndex',
+        name: 'admin_homeIndex',
+        meta: { title: '首页' },
+        component: () => import('../views/adminCenter/homeIndex/index.vue'),
+      },
+      {
+        path: '/adminCenter/test/index',
+        meta: { title: '测试菜单' },
+        component: () => import('../views/adminCenter/test/index.vue'),
+      },
+      {
+        path: '/adminCenter/test/detail',
+        meta: { title: '测试菜单-信息管理' },
+        component: () => import('../views/adminCenter/test/detail.vue'),
+      },
+      {
+        path: '/adminCenter/mechanism/index',
+        meta: { title: '机构用户管理' },
+        component: () => import('../views/adminCenter/mechanism/index.vue'),
+      },
+      {
+        path: '/adminCenter/mechanism/detail',
+        meta: { title: '机构用户详情' },
+        component: () => import('../views/adminCenter/mechanism/detail.vue'),
+      },
+      {
+        path: '/adminCenter/users/index',
+        meta: { title: '平台用户管理' },
+        component: () => import('../views/adminCenter/user/index.vue'),
+      },
+      {
+        path: '/adminCenter/users/detail',
+        meta: { title: '平台用户详情' },
+        component: () => import('../views/adminCenter/user/detail.vue'),
+      },
+      {
+        path: '/adminCenter/disclosure/index',
+        meta: { title: '申请管理' },
+        component: () => import('../views/adminCenter/disclosure/index.vue'),
+      },
+      {
+        path: '/adminCenter/disclosure/evaluate',
+        meta: { title: '预评估' },
+        component: () => import('../views/adminCenter/disclosure/evaluate.vue'),
+      },
+    ],
+  },
+  {
+    path: '/jg/homeIndex',
+    name: 'jgCenter',
+    component: () => import('@frame/src/components/admin-frame/Home.vue'),
+    children: [
+      {
+        path: '/jg/homeIndex',
+        name: 'jgIndex',
+        meta: { title: '首页' },
+        component: () => import('../views/jg/homeIndex/index.vue'),
+      },
+      {
+        path: '/jg/disclosure/index',
+        meta: { title: '申请管理' },
+        component: () => import('../views/jg/disclosure/index.vue'),
+      },
+      {
+        path: '/jg/patent/index',
+        meta: { title: '专利管理' },
+        component: () => import('../views/jg/patent/index.vue'),
+      },
+      {
+        path: '/jg/message/index',
+        meta: { title: '我的消息' },
+        component: () => import('../views/jg/message/index.vue'),
+      },
+      {
+        path: '/jg/trade/index',
+        meta: { title: '交易管理' },
+        component: () => import('../views/jg/trade/index.vue'),
+      },
+      {
+        path: '/jg/statistics/index',
+        meta: { title: '统计管理' },
+        component: () => import('../views/jg/statistics/index.vue'),
+      },
+    ],
+  },
+];
+const routes = [...web];
+const router = new VueRouter({
+  mode: 'history',
+  base: process.env.VUE_APP_ROUTER,
+  routes,
+});
+router.beforeEach((to, from, next) => {
+  document.title = `${to.meta.title} `;
+  const token = localStorage.getItem('token');
+  if (to.path == '/adminCenter/homeIndex') {
+    if (!token) {
+      // next('/login');
+      next();
+    } else {
+      let user = jwt.decode(token);
+      store.commit('setUser', user, { root: true });
+      next();
+    }
+  } else {
+    let user = jwt.decode(token);
+    store.commit('setUser', user, { root: true });
+    next();
+  }
+});
+
+export default router;

+ 24 - 0
src/store/index.js

@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import * as ustate from '@common/src/store/user/state';
+import * as umutations from '@common/src/store/user/mutations';
+import disclosure from '@common/src/store/patent/disclosure';
+import report from '@common/src/store/patent/report';
+// 管理员
+import adminLogin from '@common/src/store/adminLogin';
+// 个人
+import personal from '@common/src/store/personal';
+// 邀请码
+import inviteCode from '@common/src/store/inviteCode';
+// 机构
+import organization from '@common/src/store/organization';
+// 专家
+import expert from '@common/src/store/expert';
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+  state: { ...ustate },
+  mutations: { ...umutations },
+  actions: {},
+  modules: { disclosure, adminLogin, personal, inviteCode, organization, expert, report },
+});

+ 117 - 0
src/util/axios-wrapper.js

@@ -0,0 +1,117 @@
+/* eslint-disable no-console */
+/* eslint-disable no-param-reassign */
+
+import _ from 'lodash';
+import Axios from 'axios';
+import { Util, Error } from 'naf-core';
+// import { Indicator } from 'mint-ui';
+import util from './user-util';
+
+const { trimData, isNullOrUndefined } = Util;
+const { ErrorCode } = Error;
+
+let currentRequests = 0;
+
+export default class AxiosWrapper {
+  constructor({ baseUrl = '', unwrap = true } = {}) {
+    this.baseUrl = baseUrl;
+    this.unwrap = unwrap;
+  }
+
+  // 替换uri中的参数变量
+  static merge(uri, query = {}) {
+    if (!uri.includes(':')) {
+      return uri;
+    }
+    const keys = [];
+    const regexp = /\/:([a-z0-9_]+)/gi;
+    let res;
+    // eslint-disable-next-line no-cond-assign
+    while ((res = regexp.exec(uri)) != null) {
+      keys.push(res[1]);
+    }
+    keys.forEach((key) => {
+      if (!isNullOrUndefined(query[key])) {
+        uri = uri.replace(`:${key}`, query[key]);
+      }
+    });
+    return uri;
+  }
+
+  $get(uri, query, options) {
+    return this.$request(uri, null, query, options);
+  }
+
+  $post(uri, data = {}, query, options) {
+    return this.$request(uri, data, query, options);
+  }
+  $delete(uri, data = {}, router, query, options = {}) {
+    options = { ...options, method: 'delete' };
+    return this.$request(uri, data, query, options, router);
+  }
+  async $request(uri, data, query, options) {
+    // TODO: 合并query和options
+    if (_.isObject(query) && _.isObject(options)) {
+      options = { ...options, params: query, method: 'get' };
+    } else if (_.isObject(query) && !query.params) {
+      options = { params: query };
+    } else if (_.isObject(query) && query.params) {
+      options = query;
+    }
+    if (!options) options = {};
+    if (options.params) options.params = trimData(options.params);
+    const url = AxiosWrapper.merge(uri, options.params);
+    currentRequests += 1;
+    // Indicator.open({
+    //   spinnerType: 'fading-circle',
+    // });
+
+    try {
+      const axios = Axios.create({
+        baseURL: this.baseUrl,
+      });
+      axios.defaults.headers.common.Authorization = util.token;
+      let res = await axios.request({
+        method: isNullOrUndefined(data) ? 'get' : 'post',
+        url,
+        data,
+        responseType: 'json',
+        ...options,
+      });
+      res = res.data;
+      const { errcode, errmsg, details } = res;
+      if (errcode) {
+        console.warn(`[${uri}] fail: ${errcode}-${errmsg} ${details}`);
+        return res;
+      }
+      // unwrap data
+      if (this.unwrap) {
+        res = _.omit(res, ['errmsg', 'details']);
+        const keys = Object.keys(res);
+        if (keys.length === 1 && keys.includes('data')) {
+          res = res.data;
+        }
+      }
+      return res;
+    } catch (err) {
+      let errmsg = '接口请求失败,请稍后重试';
+      if (err.response) {
+        const { status } = err.response;
+        if (status === 401) errmsg = '用户认证失败,请重新登录';
+        if (status === 403) errmsg = '当前用户不允许执行该操作';
+      }
+      console.error(
+        `[AxiosWrapper] 接口请求失败: ${err.config && err.config.url} - 
+        ${err.message}`
+      );
+      return { errcode: ErrorCode.SERVICE_FAULT, errmsg, details: err.message };
+    } finally {
+      /* eslint-disable */
+      currentRequests -= 1;
+      if (currentRequests <= 0) {
+        currentRequests = 0;
+        // Indicator.close();
+      }
+    }
+  }
+}

+ 10 - 0
src/util/filters.js

@@ -0,0 +1,10 @@
+import _ from 'lodash';
+
+const filters = {
+  getName(object) {
+    const { data, searchItem } = object;
+    return _.get(data, searchItem) === undefined ? '' : _.get(data, searchItem);
+  },
+};
+
+export default filters;

+ 50 - 0
src/util/methods-util.js

@@ -0,0 +1,50 @@
+import { Util } from 'naf-core';
+
+const { isNullOrUndefined } = Util;
+
+export default {
+  //判断信息是否过期
+  isDateOff(dataDate) {
+    const now = new Date(new Date().getTime() - 24 * 60 * 60 * 1000);
+    dataDate = new Date(dataDate);
+    return now.getTime() <= dataDate.getTime();
+  },
+  //判断企业是否可以执行此动作/显示
+  checkCorp(data) {
+    const { role, unit, selfUnit, status, displayType, userid } = data;
+    if (!isNullOrUndefined(selfUnit) && !isNullOrUndefined(status)) {
+      return role === 'corp' && selfUnit === unit && status === '0';
+    } else if (!isNullOrUndefined(displayType)) {
+      if (role === 'corp') {
+        return role === displayType;
+      } else {
+        return role === displayType && !isNullOrUndefined(userid);
+      }
+    }
+  },
+  //获取url的参数params
+  getParams() {
+    let str = location.href;
+    let num = str.indexOf('?');
+    const param = {};
+    str = str.substr(num + 1);
+    let num2 = str.indexOf('#');
+    let str2 = '';
+    if (num2 > 0) {
+      str2 = str.substr(0, num2);
+    } else {
+      num2 = str.indexOf('/');
+      str2 = str.substr(0, num2);
+    }
+    const arr = str2.split('&');
+    for (let i = 0; i < arr.length; i++) {
+      num = arr[i].indexOf('=');
+      if (num > 0) {
+        const name = arr[i].substring(0, num);
+        const value = arr[i].substr(num + 1);
+        param[name] = decodeURI(value);
+      }
+    }
+    return param;
+  },
+};

+ 69 - 0
src/util/user-util.js

@@ -0,0 +1,69 @@
+/* eslint-disable no-console */
+export default {
+  get user() {
+    const val = sessionStorage.getItem('user');
+    try {
+      if (val) return JSON.parse(val);
+    } catch (err) {
+      console.error(err);
+    }
+    return null;
+  },
+  set user(userinfo) {
+    sessionStorage.setItem('user', JSON.stringify(userinfo));
+  },
+  get token() {
+    return sessionStorage.getItem('token');
+  },
+  set token(token) {
+    sessionStorage.setItem('token', token);
+  },
+  get openid() {
+    return sessionStorage.getItem('openid');
+  },
+  set openid(openid) {
+    sessionStorage.setItem('openid', openid);
+  },
+  get isGuest() {
+    return !this.user || this.user.role === 'guest';
+  },
+  save({ userinfo, token }) {
+    sessionStorage.setItem('user', JSON.stringify(userinfo));
+    sessionStorage.setItem('token', token);
+  },
+
+  get corpInfo() {
+    const val = sessionStorage.getItem('corpInfo');
+    if (val) return JSON.parse(val);
+    return null;
+  },
+  set corpInfo(corpInfo) {
+    sessionStorage.setItem('corpInfo', JSON.stringify(corpInfo));
+  },
+  saveCorpInfo(corpInfo) {
+    sessionStorage.setItem('corpInfo', JSON.stringify(corpInfo));
+  },
+
+  get unit() {
+    const val = sessionStorage.getItem('unit');
+    if (val) return JSON.parse(val);
+    return null;
+  },
+  set unit(unitList) {
+    sessionStorage.setItem('unit', JSON.stringify(unitList));
+  },
+  saveUnit(unitList) {
+    sessionStorage.setItem('unit', JSON.stringify(unitList));
+  },
+  get userInfo() {
+    const val = sessionStorage.getItem('userInfo');
+    if (val) return JSON.parse(val);
+    return null;
+  },
+  set userInfo(userInfo) {
+    sessionStorage.setItem('userInfo', JSON.stringify(userInfo));
+  },
+  saveUserInfo(userInfo) {
+    sessionStorage.setItem('userInfo', JSON.stringify(userInfo));
+  },
+};

+ 93 - 0
src/views/adminCenter/disclosure/detail.vue

@@ -0,0 +1,93 @@
+<template>
+  <div id="detail">
+    <data-form v-model="form" @save="toSave" :fields="fields" submitText="审核" :needSave="false" labelWidth="180px">
+      <template #status>
+        <el-radio v-for="(i, index) in statusList" :key="`status-${index}`" :label="i.value"> {{ i.label }}</el-radio>
+      </template>
+    </data-form>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import quests from '@common/dict/patent/quests';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'detail',
+  props: {
+    form: { type: Object, default: () => {} },
+  },
+  model: {
+    prop: 'form',
+    event: 'change',
+  },
+  components: {},
+  data: function () {
+    return {
+      fields: [
+        { label: '发明名称', model: 'name', type: 'text', required: true },
+        { label: '申请人', model: 'apply_name', required: true, type: 'text' },
+        { label: '申请类型', model: 'type', type: 'text', required: true },
+        { label: '发明人', model: 'inventer', type: 'text', required: true },
+        { label: '技术联系人', model: 'contact', type: 'text', required: true },
+        { label: '联系人电话', model: 'phone', type: 'text', required: true },
+        { label: '联系人邮箱', model: 'email', type: 'text', required: true },
+        { label: '特殊情况说明', model: 'desc', type: 'text', required: true },
+      ],
+      typeList: [{ label: '国家发明' }, { label: '国防专利' }, { label: '实用新型' }, { label: '外观' }],
+      statusList: [
+        { label: '通过', value: '1' },
+        { label: '拒绝', value: '-1' },
+      ],
+    };
+  },
+  created() {
+    this.setQuest();
+  },
+  methods: {
+    async toSave({ data }) {
+      this.$emit('submit', data);
+    },
+    setQuest() {
+      let fields = _.cloneDeep(this.fields);
+      let qs = _.cloneDeep(quests);
+      qs = qs.map((i) => {
+        i.type = 'text';
+        return i;
+      });
+      fields.push(...qs);
+      fields = fields.map((i) => {
+        delete i.required;
+        return i;
+      });
+      // 添加审核
+      // const checkField = [
+      //   {
+      //     label: '预估结果',
+      //     model: 'status',
+      //     type: 'radio',
+      //     required: true,
+      //   },
+      //   {
+      //     label: '意见&建议',
+      //     model: 'remark',
+      //     type: 'textarea',
+      //   },
+      // ];
+      // fields.push(...checkField);
+      this.$set(this, `fields`, fields);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 70 - 0
src/views/adminCenter/disclosure/evaluate.vue

@@ -0,0 +1,70 @@
+<template>
+  <div id="evaluate">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @check="toCheck"> </data-table>
+    <el-dialog title="评估" width="80vw" center :visible.sync="dialog" :close-on-click-modal="false">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <info v-model="form"></info>
+        </el-col>
+        <el-col :span="12"> <propose></propose></el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import info from './detail.vue';
+import propose from './propose.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: disclosure } = createNamespacedHelpers('disclosure');
+export default {
+  name: 'evaluate',
+  props: {},
+  components: { info, propose },
+  data: function () {
+    return {
+      list: [],
+      total: 0,
+      dialog: false,
+      form: {},
+      fields: [
+        { label: '发明名称', model: 'name', filter: true },
+        { label: '申请人', model: 'apply_name' },
+        { label: '申请类型', model: 'type' },
+        { label: '技术联系人', model: 'contact' },
+        { label: '联系人电话', model: 'phone' },
+        { label: '联系人邮箱', model: 'email' },
+      ],
+      opera: [{ label: '评估报告', method: 'check' }],
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    ...disclosure(['query', 'check']),
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      const res = await this.query({ skip, limit, ...info });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    async toCheck({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = true;
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 179 - 0
src/views/adminCenter/disclosure/index.vue

@@ -0,0 +1,179 @@
+<template>
+  <div id="index">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @view="toView" @check="toCheck" @up="toUp"> </data-table>
+    <el-dialog :title="dtitle" width="80vw" center :visible.sync="dialog" :close-on-click-modal="dspan === 24" :destroy-on-close="true">
+      <el-row :gutter="20">
+        <el-col :span="dspan">
+          <info v-model="form"></info>
+        </el-col>
+        <el-col :span="12" v-if="dspan === 12">
+          <el-input v-model="form.remark" placeholder="请输入您的意见" type="textarea" :autosize="{ minRows: 5, maxRows: 10 }"></el-input>
+          <div style="width: 100%; margin: 16px; text-align: center">
+            <el-button type="success" style="width: 25%" @click="toReport('3')">通过</el-button>
+            <el-button type="danger" style="width: 25%" @click="toReport('-2')">拒绝</el-button>
+            <el-button style="width: 25%" @click="dialog = false">返回</el-button>
+          </div>
+        </el-col>
+      </el-row>
+    </el-dialog>
+    <el-dialog title="评估报告" center :visible.sync="d2" :close-on-click-modal="true" :destroy-on-close="true">
+      <el-input v-model="report.content" placeholder="请输入评估报告内容" type="textarea" :autosize="{ minRows: 5, maxRows: 10 }"></el-input>
+      <div style="width: 100%; margin: 16px; text-align: center">
+        <el-button type="success" style="width: 80%" @click="makeReport()">评估</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import info from './detail.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: disclosure } = createNamespacedHelpers('disclosure');
+const { mapActions: report } = createNamespacedHelpers('report');
+export default {
+  name: 'index',
+  props: {},
+  components: { info },
+  data: function () {
+    return {
+      list: [{ status: '2' }],
+      total: 0,
+      dialog: false,
+      d2: false,
+      dtitle: '查看',
+      dspan: 24,
+      form: {},
+      report: {},
+      fields: [
+        { label: '发明名称', model: 'name', filter: true },
+        { label: '申请人', model: 'apply_name' },
+        { label: '申请类型', model: 'type' },
+        { label: '技术联系人', model: 'contact' },
+        { label: '联系人电话', model: 'phone' },
+        { label: '联系人邮箱', model: 'email' },
+        { label: '申请进度', model: 'status', format: (i) => this.statusWord(i) },
+      ],
+      opera: [
+        { label: '查看', method: 'view' },
+        { label: '评估', method: 'check', display: (i) => i.status === '2' },
+        { label: '上传', method: 'up', display: (i) => i.status === '3' },
+      ],
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    ...disclosure(['query', 'check']),
+    ...report(['create']),
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      const res = await this.query({ skip, limit, ...info, admin_id: this.user._id });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    //查看
+    async toView({ data }) {
+      this.$set(this, `form`, data);
+      this.dtitle = '查看';
+      this.dspan = 24;
+      this.dialog = true;
+    },
+    //去评估
+    async toCheck({ data }) {
+      this.$set(this, `form`, data);
+      this.dtitle = '评估';
+      this.dspan = 12;
+      this.dialog = true;
+    },
+    //去上传
+    async toUp({ data }) {
+      this.$confirm(`${data.name} 已经上传到国家专利库中?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.$set(this, `form`, data);
+          this.toStatus('4');
+        })
+        .catch(() => {});
+    },
+    // 评估
+    async toReport(status) {
+      const { _id: disclosure_id } = _.cloneDeep(this.form);
+      this.report.disclosure_id = disclosure_id;
+      await this.toStatus(status);
+      this.$confirm(`是否生成评估报告`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning',
+      })
+        .then(() => {
+          this.d2 = true;
+        })
+        .catch(() => {});
+    },
+    // 申请状态修改
+    async toStatus(status) {
+      const { id, remark } = this.form;
+      const obj = { id, status, remark };
+      const res = await this.check(obj);
+      if (this.$checkRes(res, '操作成功', res.errmsg || '操作失败')) {
+        this.dialog = false;
+        this.search();
+      }
+    },
+    async makeReport() {
+      const res = await this.create(this.report);
+      if (this.$checkRes(res, '评估报告已生成', res.errmsg || '评估报告生成失败')) {
+        this.d2 = false;
+        this.report = {};
+      }
+    },
+    // 状态词
+    statusWord(status) {
+      let word = '';
+      switch (status) {
+        case '0':
+          word = '已申请';
+          break;
+        case '1':
+          word = '机构审核';
+          break;
+        case '-1':
+          word = '机构审核未通过';
+          break;
+        case '2':
+          word = '管理员评估';
+          break;
+        case '-2':
+          word = '管理员评估未通过';
+          break;
+        case '3':
+          word = '管理员评估通过,等待上传至国家库';
+          break;
+        case '4':
+          word = '上传完成';
+          break;
+        default:
+          break;
+      }
+      return word;
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 38 - 0
src/views/adminCenter/disclosure/propose.vue

@@ -0,0 +1,38 @@
+<template>
+  <div id="propose">
+    <data-form v-model="form" :fields="fields" @save="toSave"></data-form>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'propose',
+  props: {
+    form: { type: Object, default: () => {} },
+  },
+  components: {},
+  data: function () {
+    return {
+      fields: [{ label: '整体意见', model: 'remark', type: 'textarea' }],
+    };
+  },
+  created() {},
+  methods: {
+    async toSave({ data }) {
+      console.log(data);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 36 - 0
src/views/adminCenter/homeIndex/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main"> test </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  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>

+ 37 - 0
src/views/adminCenter/index.vue

@@ -0,0 +1,37 @@
+<template>
+  <div id="index">
+    <admin-frame></admin-frame>
+  </div>
+</template>
+
+<script>
+import adminFrame from '@frame/src/components/adminCommon/frame.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {
+    adminFrame,
+  },
+  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>

+ 114 - 0
src/views/adminCenter/mechanism/detail.vue

@@ -0,0 +1,114 @@
+<template>
+  <div id="detail">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="back">
+          <el-button type="primary" size="mini" @click="back">返回</el-button>
+        </el-col>
+        <el-col :span="24" class="detail">
+          <data-form v-model="form" :fields="formFields" :rules="rules" @save="toSave" labelWidth="135px">
+            <template #pid>
+              <el-option v-for="(i, index) in pidList" :key="index" :label="i.name" :value="i.id"></el-option>
+            </template>
+          </data-form>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: adminLogin } = createNamespacedHelpers('adminLogin');
+export default {
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  name: 'detail',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      formFields: [
+        { label: '所属管理员', model: 'pid', type: 'select' },
+        { label: '机构代码或邀请码', model: 'code' },
+        { label: '机构名称', model: 'deptname' },
+        { label: '姓名', model: 'name' },
+        { label: '手机号', model: 'phone', options: { maxLength: 11, minLength: 11, type: 'number' } },
+        { label: '密码', model: 'passwd', type: 'password' },
+      ],
+      form: {},
+      rules: {
+        pid: [{ required: true, message: '请选择所属管理员' }],
+        code: [{ required: true, message: '请输入机构代码或邀请码' }],
+        name: [{ required: true, message: '请输入姓名' }],
+        phone: [{ required: true, message: '请输入手机号' }],
+        passwd: [{ required: true, message: '请输入密码' }],
+      },
+      // 管理员列表
+      pidList: [],
+    };
+  },
+  async created() {
+    await this.search();
+  },
+  methods: {
+    ...adminLogin(['query', 'fetch', 'create', 'update']),
+    async search() {
+      let res = await this.query({ role: '1' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `pidList`, res.data);
+      }
+      if (this.id) {
+        let res = await this.fetch(this.id);
+        if (this.$checkRes(res)) {
+          this.$set(this, `form`, res.data);
+        }
+      }
+    },
+    // 提交
+    async toSave({ data }) {
+      data.role = '2';
+      if (data.id) {
+        let res = await this.update(data);
+        if (this.$checkRes(res)) {
+          this.$message({
+            message: '信息修改成功',
+            type: 'success',
+          });
+          this.back();
+        }
+      } else {
+        let res = await this.create(data);
+        if (this.$checkRes(res)) {
+          this.$message({
+            message: '信息创建成功',
+            type: 'success',
+          });
+          this.back();
+        }
+      }
+    },
+    // 返回
+    back() {
+      this.$router.push({ path: '/adminCenter/mechanism/index' });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    id() {
+      return this.$route.query.id;
+    },
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .back {
+    text-align: right;
+    margin: 0 0 10px 0;
+  }
+}
+</style>

+ 109 - 0
src/views/adminCenter/mechanism/index.vue

@@ -0,0 +1,109 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="add">
+          <el-button type="primary" size="mini" @click="add">添加</el-button>
+        </el-col>
+        <el-col :span="24" class="list">
+          <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @edit="toEdit" @delete="toDelete"></data-table>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: adminLogin } = createNamespacedHelpers('adminLogin');
+export default {
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      opera: [
+        {
+          label: '编辑',
+          method: 'edit',
+        },
+        {
+          label: '删除',
+          method: 'delete',
+        },
+      ],
+      fields: [
+        { label: '机构代码或邀请码', prop: 'code', model: 'code' },
+        { label: '姓名', prop: 'name', model: 'name' },
+        { label: '手机号', prop: 'phone', model: 'phone', options: { maxLength: 11, minLength: 11, type: 'number' } },
+        { label: '机构名称', prop: 'deptname', model: 'deptname' },
+      ],
+      list: [],
+      total: 0,
+      // 分配权限
+      dialog: false,
+      formfields: [
+        { label: '用户名', model: 'name' },
+        { label: '机构名称', model: 'deptname' },
+        { label: '权限', model: 'menus', custom: true },
+      ],
+      form: {
+        menus: [],
+      },
+      rules: {},
+      // 菜单列表
+      menuList: [],
+    };
+  },
+  async created() {
+    await this.search();
+  },
+  methods: {
+    ...adminLogin(['query', 'fetch', 'update', 'delete']),
+    // 查询列表
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      if (this.user.role == '1' || this.user.role == '2') info.pid = this.user.id;
+      let res = await this.query({ skip, limit, role: '2', ...info });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    // 修改
+    toEdit({ data }) {
+      this.$router.push({ path: '/adminCenter/mechanism/detail', query: { id: data.id } });
+    },
+    // 删除
+    async toDelete({ data }) {
+      let res = await this.delete(data.id);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '信息修改成功',
+          type: 'success',
+        });
+        this.search();
+      }
+    },
+    // 添加数据
+    add() {
+      this.$router.push({ path: '/adminCenter/mechanism/detail' });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .add {
+    text-align: right;
+    margin: 0 0 10px 0;
+  }
+}
+</style>

+ 41 - 0
src/views/adminCenter/message/index.vue

@@ -0,0 +1,41 @@
+<template>
+  <div id="index">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @check="toCheck"> </data-table>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      list: [
+        { contact: '消息1', time: '2021-07-01 10:00:21', status: '0' },
+        { contact: '消息2', time: '2021-07-02 12:00:30', status: '1' },
+      ],
+      total: 2,
+      fields: [
+        { label: '消息内容', model: 'contact' },
+        { label: '时间', model: 'time' },
+      ],
+      opera: [{ label: '取消提醒', method: 'off', display: (i) => i.status === '0' }],
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 45 - 0
src/views/adminCenter/patent/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <div id="index">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @check="toCheck"> </data-table>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      list: [
+        { name: '专利1', apply_name: '申请人', type: '申请类型', contact: '联系人', phone: '11111111111', email: 'test@qq.com' },
+        { name: '专利2', apply_name: '申请人2', type: '申请类型2', contact: '联系人2', phone: '11111111112', email: 'test2@qq.com' },
+      ],
+      total: 0,
+      fields: [
+        { label: '专利名称', model: 'name', filter: true },
+        { label: '申请人', model: 'apply_name' },
+        { label: '申请类型', model: 'type' },
+        { label: '技术联系人', model: 'contact' },
+        { label: '联系人电话', model: 'phone' },
+        { label: '联系人邮箱', model: 'email' },
+      ],
+      opera: [{ label: '查看', method: 'check' }],
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 30 - 0
src/views/adminCenter/statistics/index.vue

@@ -0,0 +1,30 @@
+<template>
+  <div id="index">
+    <p>statistics</p>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 81 - 0
src/views/adminCenter/test/detail.vue

@@ -0,0 +1,81 @@
+<template>
+  <div id="detail">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <data-form :fields="fields" :data="form" @save="toSave" returns="/adminCenter/test/index">
+            <template #custom="{ item, form }">
+              <template v-if="item.model == 'img_url'">
+                <e-upload :limit="1" url="/files/study/test_imgurl/upload" type="img_url" v-model="form[item.model]"></e-upload>
+              </template>
+            </template>
+          </data-form>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: maptest } = createNamespacedHelpers('test');
+export default {
+  name: 'detail',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      form: {
+        img_url: [],
+      },
+      fields: [
+        { label: '标题', model: 'title' },
+        { label: '图片文件', model: 'img_url', custom: true },
+        { label: '内容', model: 'content', type: 'editor', url: '/files/study/news_editor/upload' },
+      ],
+    };
+  },
+  created() {
+    if (this.id) this.search();
+  },
+  methods: {
+    ...maptest(['fetch', 'create', 'update']),
+    async search() {
+      let res = await this.fetch(this.id);
+      if (this.$checkRes(res)) {
+        this.$set(this, `form`, res.data);
+      }
+    },
+    async toSave({ data }) {
+      let dup = _.cloneDeep(data);
+      let res;
+      if (_.get(dup, 'id')) {
+        res = await this.update(dup);
+      } else {
+        res = await this.create(dup);
+      }
+      if (this.$checkRes(res, '保存成功', '保存失败')) {
+        if (this.$dev_mode) this.$router.push('/adminCenter/test/index');
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    id() {
+      return this.$route.query.id;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 82 - 0
src/views/adminCenter/test/index.vue

@@ -0,0 +1,82 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one">
+          <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @edit="toEdit" @delete="toDelete">
+            <template #selfbtn>
+              <el-button type="primary" size="mini" @click="toAdd">添加</el-button>
+            </template>
+          </data-table>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: maptest } = createNamespacedHelpers('test');
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      list: [],
+      total: 0,
+      opera: [
+        { label: '修改', method: 'edit' },
+        { label: '删除', method: 'delete', type: 'danger' },
+      ],
+      fields: [
+        { label: '标题', prop: 'title', filter: 'input' },
+        { label: '信息', prop: 'content' },
+      ],
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    ...maptest(['query', 'delete']),
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let res = await this.query({ skip, limit, ...info });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    // 添加
+    toAdd() {
+      this.$router.push({ path: '/adminCenter/test/detail' });
+    },
+    // 修改
+    toEdit({ data }) {
+      this.$router.push({ path: '/adminCenter/test/detail', query: { id: data.id } });
+    },
+    // 删除
+    async toDelete({ data }) {
+      const res = await this.delete(data._id);
+      if (this.$checkRes(res, '删除成功', '删除失败')) {
+        this.search();
+      }
+    },
+  },
+  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/views/adminCenter/trade/index.vue

@@ -0,0 +1,43 @@
+<template>
+  <div id="index">
+    <el-tabs v-model="active" type="card">
+      <el-tab-pane label="许可" name="first">
+        <list type="0"></list>
+      </el-tab-pane>
+      <el-tab-pane label="转让" name="second">
+        <list type="1"></list>
+      </el-tab-pane>
+      <el-tab-pane label="质押" name="third">
+        <list type="2"></list>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import list from './parts/list.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: { list },
+  data: function () {
+    return {
+      active: 'first',
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 59 - 0
src/views/adminCenter/trade/parts/list.vue

@@ -0,0 +1,59 @@
+<template>
+  <div id="list">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search"></data-table>
+  </div>
+</template>
+
+<script>
+const moment = require('moment');
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'list',
+  props: {
+    type: { type: String, default: '0' },
+  },
+  components: {},
+  data: function () {
+    return {
+      list: [],
+      total: 0,
+      fields: [
+        { label: '专利名称', model: 'name' },
+        { label: '交易时间', model: 'time' },
+        { label: '交易人', model: 'trade_person' },
+        { label: '交易人电话', model: 'trade_phone' },
+        { label: '交易人邮箱', model: 'trade_email' },
+      ],
+      opera: [{ label: '提醒到期' }],
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    async search() {
+      // 根据type去找各类型的数据
+      for (let i = 1; i <= 5; i++) {
+        this.list.push({
+          name: `专利${i}`,
+          time: moment().add(i, 'd').format('YYYY-MM-DD HH:mm:ss'),
+          trade_person: 'test',
+          trade_phone: '12345678900',
+          trade_email: 'test@qq.com',
+        });
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 132 - 0
src/views/adminCenter/user/detail.vue

@@ -0,0 +1,132 @@
+<template>
+  <div id="detail">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="back">
+          <el-button type="primary" size="mini" @click="back">返回</el-button>
+        </el-col>
+        <el-col :span="24" class="info">
+          <span v-if="type == '4'">
+            <perDetail :form="form" @toSave="perSave"></perDetail>
+          </span>
+          <span v-else-if="type == '5'">
+            <orgDetail :form="form" @toSave="orgSave"></orgDetail>
+          </span>
+          <span v-else-if="type == '6'">
+            <expDetail :form="form" @toSave="expSave"></expDetail>
+          </span>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import perDetail from './parts/perDetail.vue';
+import orgDetail from './parts/orgDetail.vue';
+import expDetail from './parts/expDetail.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: personal } = createNamespacedHelpers('personal');
+const { mapActions: organization } = createNamespacedHelpers('organization');
+const { mapActions: expert } = createNamespacedHelpers('expert');
+export default {
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  name: 'detail',
+  props: {},
+  components: {
+    perDetail,
+    orgDetail,
+    expDetail,
+  },
+  data: function() {
+    return {
+      form: {},
+    };
+  },
+  async created() {
+    await this.search();
+  },
+  methods: {
+    ...personal({ personalFetch: 'fetch', personalUpdate: 'update' }),
+    ...organization({ organizationFetch: 'fetch', organizationUpdate: 'update' }),
+    ...expert({ expertFetch: 'fetch', expertUpdate: 'update' }),
+    async search() {
+      let type = this.type;
+      if (type == '4') {
+        let res = await this.personalFetch(this.id);
+        if (this.$checkRes(res)) {
+          this.$set(this, `form`, res.data);
+        }
+      } else if (type == '5') {
+        let res = await this.organizationFetch(this.id);
+        if (this.$checkRes(res)) {
+          this.$set(this, `form`, res.data);
+        }
+      } else if (type == '6') {
+        let res = await this.expertFetch(this.id);
+        if (this.$checkRes(res)) {
+          this.$set(this, `form`, res.data);
+        }
+      }
+    },
+    // 提交审核-个人用户
+    async perSave(data) {
+      let res = await this.personalUpdate(data);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '信息审核成功',
+          type: 'success',
+        });
+        this.back();
+      }
+    },
+    // 提交审核-机构用户
+    async orgSave(data) {
+      let res = await this.organizationUpdate(data);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '信息审核成功',
+          type: 'success',
+        });
+        this.back();
+      }
+    },
+    // 提交审核-专家用户
+    async expSave(data) {
+      let res = await this.expertUpdate(data);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '信息审核成功',
+          type: 'success',
+        });
+        this.back();
+      }
+    },
+    // 返回列表
+    back() {
+      this.$router.push({ path: '/user' });
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    id() {
+      return this.$route.query.id;
+    },
+    type() {
+      return this.$route.query.type;
+    },
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .back {
+    text-align: right;
+    margin: 0 0 15px 0s;
+  }
+}
+</style>

+ 65 - 0
src/views/adminCenter/user/index.vue

@@ -0,0 +1,65 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="type">
+          <el-radio-group v-model="userType">
+            <el-radio label="1" border>个人用户</el-radio>
+            <el-radio label="2" border>机构用户</el-radio>
+            <el-radio label="3" border>专家用户</el-radio>
+          </el-radio-group>
+        </el-col>
+        <el-col :span="24" class="list">
+          <span v-if="userType == '1'">
+            <perUser></perUser>
+          </span>
+          <span v-else-if="userType == '2'">
+            <mechUser></mechUser>
+          </span>
+          <span v-else-if="userType == '3'">
+            <expeUser></expeUser>
+          </span>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import perUser from './parts/perUser.vue';
+import mechUser from './parts/mechUser.vue';
+import expeUser from './parts/expeUser.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  name: 'index',
+  props: {},
+  components: {
+    perUser,
+    mechUser,
+    expeUser,
+  },
+  data: function () {
+    return {
+      // 用户类别
+      userType: '1',
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .type {
+    margin: 0 0 20px 0;
+  }
+}
+</style>

+ 79 - 0
src/views/adminCenter/user/parts/expDetail.vue

@@ -0,0 +1,79 @@
+<template>
+  <div id="expDetail">
+    <el-row>
+      <el-col :span="24">
+        <data-form :data="form" :fields="formFields" :rules="{}" @save="toSave">
+          <template #radios="{ item }">
+            <template v-if="item.model === 'status'">
+              <el-radio label="0">待审核</el-radio>
+              <el-radio label="1">审核通过</el-radio>
+              <el-radio label="2">审核拒绝</el-radio>
+            </template>
+          </template>
+          <template #custom="{ item, form }">
+            <template v-if="item.model === 'img_path'">
+              <el-image :src="form.img_path" class="img_path">
+                <div slot="error" class="image-slot">
+                  <i class="el-icon-picture-outline"></i></div
+              ></el-image>
+            </template>
+          </template>
+        </data-form>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import dataForm from '@common/src/components/frame/form.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'expDetail',
+  props: { form: { type: Object } },
+  components: {
+    dataForm,
+  },
+  data: function () {
+    return {
+      formFields: [
+        { label: '姓名', model: 'name' },
+        { label: '手机号', model: 'phone' },
+        { label: '机构代码', model: 'code' },
+        { label: '头像', model: 'img_path', custom: true },
+        { label: '最高学历', model: 'education' },
+        { label: '毕业院校', model: 'school' },
+        { label: '出生日期', model: 'birthDate', type: 'date' },
+        { label: 'QQ/微信', model: 'qqwx' },
+        { label: '电子邮箱', model: 'email' },
+        { label: '工作单位', model: 'company' },
+        { label: '职务职称', model: 'zwzc' },
+        { label: '擅长领域', model: 'expertise' },
+        { label: '工作经历', model: 'workexperience', type: 'textarea' },
+        { label: '科研综述', model: 'scientific', type: 'textarea' },
+        { label: '承担项目', model: 'undertakingproject', type: 'textarea' },
+        { label: '科技奖励', model: 'scienceaward', type: 'textarea' },
+        { label: '社会任职', model: 'social', type: 'textarea' },
+        { label: '状态', model: 'status', type: 'radio' },
+      ],
+    };
+  },
+  created() {},
+  methods: {
+    toSave({ data }) {
+      this.$emit('toSave', data);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped>
+.img_path {
+  width: 150px;
+  height: 150px;
+  border: 1px solid#ccc;
+}
+</style>

+ 115 - 0
src/views/adminCenter/user/parts/expeUser.vue

@@ -0,0 +1,115 @@
+<template>
+  <div id="expeUser">
+    <el-row>
+      <el-col :span="24">
+        <el-tabs v-model="active" type="card">
+          <el-tab-pane label="待审核" name="first">
+            <data-table :fields="fields" :opera="opera" :data="oneList" :total="oneTotal" @query="search" @edit="toEdit" @delete="toDelete"></data-table>
+          </el-tab-pane>
+          <el-tab-pane label="审核通过" name="second">
+            <data-table :fields="fields" :opera="opera" :data="twoList" :total="twoTotal" @query="search" @edit="toEdit" @delete="toDelete"></data-table>
+          </el-tab-pane>
+          <el-tab-pane label="审核失败" name="third">
+            <data-table :fields="fields" :opera="opera" :data="thrList" :total="thrTotal" @query="search" @edit="toEdit" @delete="toDelete"></data-table>
+          </el-tab-pane>
+        </el-tabs>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import dataTable from '@common/src/components/frame/filter-page-table.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: expert } = createNamespacedHelpers('expert');
+export default {
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  name: 'expeUser',
+  props: {},
+  components: {
+    dataTable,
+  },
+  data: function () {
+    return {
+      active: 'first',
+      opera: [
+        {
+          label: '审核&查看',
+          method: 'edit',
+        },
+        {
+          label: '删除',
+          method: 'delete',
+        },
+      ],
+      fields: [
+        { label: '机构代码或邀请码', prop: 'code' },
+        { label: '用户名', prop: 'name' },
+        { label: '用户ID', prop: 'phone' },
+        { label: '电子邮箱', prop: 'email' },
+        {
+          label: '状态',
+          prop: 'status',
+          format: (item) => {
+            return item === '0' ? '审核中' : item === '1' ? '审核通过' : '审核拒绝';
+          },
+        },
+      ],
+      // 待审核
+      oneList: [],
+      oneTotal: 0,
+      // 审核通过
+      twoList: [],
+      twoTotal: 0,
+      // 审核失败
+      thrList: [],
+      thrTotal: 0,
+    };
+  },
+  async created() {
+    await this.search();
+  },
+  methods: {
+    ...expert(['query', 'delete']),
+    // 查询列表
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let user = this.user;
+      if (user.role != '0') info.code = user.code;
+      let one = await this.query({ skip, limit, status: '0', ...info });
+      let two = await this.query({ skip, limit, status: '1', ...info });
+      let thr = await this.query({ skip, limit, status: '2', ...info });
+      if (this.$checkRes(one) || this.$checkRes(two) || this.$checkRes(thr)) {
+        this.$set(this, `oneList`, one.data);
+        this.$set(this, `oneTotal`, one.total);
+        this.$set(this, `twoList`, two.data);
+        this.$set(this, `twoTotal`, two.total);
+        this.$set(this, `thrList`, thr.data);
+        this.$set(this, `thrTotal`, thr.total);
+      }
+    },
+    // 修改
+    toEdit({ data }) {
+      this.$router.push({ path: '/user/detail', query: { id: data.user_id, type: '6' } });
+    },
+    // 删除
+    async toDelete({ data }) {
+      let res = await this.delete(data.id);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '信息修改成功',
+          type: 'success',
+        });
+        this.search();
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped></style>

+ 198 - 0
src/views/adminCenter/user/parts/mechUser.vue

@@ -0,0 +1,198 @@
+<template>
+  <div id="mechUser">
+    <el-row>
+      <el-col :span="24">
+        <el-tabs v-model="active" type="card">
+          <el-tab-pane label="待审核" name="first">
+            <data-table
+              :fields="fields"
+              :opera="opera"
+              :data="oneList"
+              :total="oneTotal"
+              @query="search"
+              @change="toChange"
+              @edit="toEdit"
+              @delete="toDelete"
+            ></data-table>
+          </el-tab-pane>
+          <el-tab-pane label="审核通过" name="second">
+            <data-table
+              :fields="fields"
+              :opera="opera"
+              :data="twoList"
+              :total="twoTotal"
+              @query="search"
+              @change="toChange"
+              @edit="toEdit"
+              @delete="toDelete"
+            ></data-table>
+          </el-tab-pane>
+          <el-tab-pane label="审核失败" name="third">
+            <data-table
+              :fields="fields"
+              :opera="opera"
+              :data="thrList"
+              :total="thrTotal"
+              @query="search"
+              @change="toChange"
+              @edit="toEdit"
+              @delete="toDelete"
+            ></data-table>
+          </el-tab-pane>
+        </el-tabs>
+      </el-col>
+    </el-row>
+    <el-dialog title="更换管理员" width="40%" :visible.sync="dialog" @closed="handleClose" :destroy-on-close="true">
+      <data-form :data="form" :fields="formfields" :rules="{}" @save="toSave">
+        <template #options="{ item }">
+          <template v-if="item.model === 'code'">
+            <el-option v-for="(i, index) in codeList" :key="index" :label="i.user_name" :value="i.code"></el-option>
+          </template>
+        </template>
+      </data-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import dataTable from '@common/src/components/frame/filter-page-table.vue';
+import dataForm from '@common/src/components/frame/form.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: organization } = createNamespacedHelpers('organization');
+const { mapActions: inviteCode } = createNamespacedHelpers('inviteCode');
+export default {
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  name: 'mechUser',
+  props: {},
+  components: {
+    dataTable,
+    dataForm,
+  },
+  data: function () {
+    return {
+      active: 'first',
+      opera: [
+        {
+          label: '更换管理员',
+          method: 'change',
+        },
+        {
+          label: '审核&查看',
+          method: 'edit',
+        },
+        {
+          label: '删除',
+          method: 'delete',
+        },
+      ],
+      fields: [
+        { label: '机构代码或邀请码', prop: 'code' },
+        { label: '用户名', prop: 'name' },
+        { label: '用户ID', prop: 'phone' },
+        { label: '社会统一信用代码', prop: 'institution_code' },
+        { label: '电子邮箱', prop: 'email' },
+        { label: '所属辖区', prop: 'juris' },
+        {
+          label: '状态',
+          prop: 'status',
+          format: (item) => {
+            return item === '0' ? '审核中' : item === '1' ? '审核通过' : '审核拒绝';
+          },
+        },
+      ],
+      // 待审核
+      oneList: [],
+      oneTotal: 0,
+      // 审核通过
+      twoList: [],
+      twoTotal: 0,
+      // 审核失败
+      thrList: [],
+      thrTotal: 0,
+      // 更换管理员
+      dialog: false,
+      codeList: [],
+      formfields: [
+        { label: '姓名', model: 'name', options: { readonly: true } },
+        { label: '所属机构', model: 'code', type: 'select' },
+      ],
+      form: {},
+    };
+  },
+  async created() {
+    await this.searchOther();
+    await this.search();
+  },
+  methods: {
+    ...inviteCode({ codeQuery: 'query' }),
+    ...organization(['query', 'delete', 'update']),
+    // 查询列表
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let user = this.user;
+      if (user.role != '0') info.code = user.code;
+      let one = await this.query({ skip, limit, status: '0', ...info });
+      let two = await this.query({ skip, limit, status: '1', ...info });
+      let thr = await this.query({ skip, limit, status: '2', ...info });
+      if (this.$checkRes(one) || this.$checkRes(two) || this.$checkRes(thr)) {
+        this.$set(this, `oneList`, one.data);
+        this.$set(this, `oneTotal`, one.total);
+        this.$set(this, `twoList`, two.data);
+        this.$set(this, `twoTotal`, two.total);
+        this.$set(this, `thrList`, thr.data);
+        this.$set(this, `thrTotal`, thr.total);
+      }
+    },
+    // 修改
+    toEdit({ data }) {
+      this.$router.push({ path: '/user/detail', query: { id: data.id, type: '5' } });
+    },
+    // 删除
+    async toDelete({ data }) {
+      let res = await this.delete(data.id);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '信息修改成功',
+          type: 'success',
+        });
+        this.search();
+      }
+    },
+    // 更换管理员
+    toChange({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = true;
+    },
+    async toSave({ data }) {
+      let res = await this.update(data);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '更换管理员成功',
+          type: 'success',
+        });
+        this.handleClose();
+      }
+    },
+    // 取消更换
+    handleClose() {
+      this.form = {};
+      this.dialog = false;
+      this.search();
+    },
+    // 查询机构代码
+    async searchOther() {
+      let res = await this.codeQuery();
+      if (this.$checkRes(res)) {
+        this.$set(this, `codeList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped></style>

+ 69 - 0
src/views/adminCenter/user/parts/orgDetail.vue

@@ -0,0 +1,69 @@
+<template>
+  <div id="orgDetail">
+    <el-row>
+      <el-col :span="24">
+        <data-form :data="form" :fields="formFields" :rules="{}" @save="toSave" labelWidth="130px">
+          <template #radios="{item}">
+            <template v-if="item.model === 'status'">
+              <el-radio label="0">待审核</el-radio>
+              <el-radio label="1">审核通过</el-radio>
+              <el-radio label="2">审核拒绝</el-radio>
+            </template>
+          </template>
+        </data-form>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import dataForm from '@common/src/components/frame/form.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'orgDetail',
+  props: { form: { type: Object } },
+  components: {
+    dataForm,
+  },
+  data: function() {
+    return {
+      formFields: [
+        { label: '姓名', model: 'name', options: { readonly: true } },
+        { label: '手机号', model: 'phone', options: { readonly: true } },
+        { label: '机构代码', model: 'code', options: { readonly: true } },
+        { label: '电子邮箱', model: 'email', options: { readonly: true } },
+        { label: '联系地址', model: 'addr', options: { readonly: true } },
+        { label: '办公电话', model: 'office_phone', options: { readonly: true } },
+        { label: '所属行业', model: 'profession', options: { readonly: true } },
+        { label: '所属辖区', model: 'juris', options: { readonly: true } },
+        { label: '机构代码', model: 'institution_code', options: { readonly: true } },
+        { label: '统一社会信用代码', model: 'code', options: { readonly: true } },
+        { label: '注册类型', model: 'companytype', options: { readonly: true } },
+        { label: '注册时间', model: 'companydate', options: { readonly: true } },
+        { label: '注册资金', model: 'companycapital', options: { readonly: true } },
+        { label: '企业法人', model: 'companyperson', options: { readonly: true } },
+        { label: '上年度企业总收入', model: 'sndqyzsr', options: { readonly: true } },
+        { label: '上年度研发费用', model: 'sndyffy', options: { readonly: true } },
+        { label: '企业总人数', model: 'companytotal', options: { readonly: true } },
+        { label: '专&兼职研发人数', model: 'zjzyfrs', options: { readonly: true } },
+        { label: '企业简介', model: 'companybrief', type: 'textarea', options: { readonly: true } },
+        { label: '主要产品', model: 'mainproduct', type: 'textarea', options: { readonly: true } },
+        { label: '企业资质&荣誉', model: 'qualifications', type: 'textarea', options: { readonly: true } },
+        { label: '状态', model: 'status', type: 'radio' },
+      ],
+    };
+  },
+  created() {},
+  methods: {
+    toSave({ data }) {
+      this.$emit('toSave', data);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped></style>

+ 58 - 0
src/views/adminCenter/user/parts/perDetail.vue

@@ -0,0 +1,58 @@
+<template>
+  <div id="perDetail">
+    <el-row>
+      <el-col :span="24">
+        <data-form :data="form" :fields="formFields" :rules="{}" @save="toSave">
+          <template #radios="{item}">
+            <template v-if="item.model === 'status'">
+              <el-radio label="0">待审核</el-radio>
+              <el-radio label="1">审核通过</el-radio>
+              <el-radio label="2">审核拒绝</el-radio>
+            </template>
+          </template>
+        </data-form>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import dataForm from '@common/src/components/frame/form.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'perDetail',
+  props: {
+    form: { type: Object },
+  },
+  components: {
+    dataForm,
+  },
+  data: function() {
+    return {
+      formFields: [
+        { label: '姓名', model: 'name', options: { readonly: true } },
+        { label: '手机号', model: 'phone', options: { readonly: true } },
+        { label: '机构代码', model: 'code', options: { readonly: true } },
+        { label: '电子邮箱', model: 'email', options: { readonly: true } },
+        { label: '地址', model: 'addr', options: { readonly: true } },
+        { label: '办公电话', model: 'office_phone', options: { readonly: true } },
+        { label: '所属行业', model: 'profession', options: { readonly: true } },
+        { label: '所属辖区', model: 'juris', options: { readonly: true } },
+        { label: '状态', model: 'status', type: 'radio' },
+      ],
+    };
+  },
+  created() {},
+  methods: {
+    toSave({ data }) {
+      this.$emit('toSave', data);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped></style>

+ 194 - 0
src/views/adminCenter/user/parts/perUser.vue

@@ -0,0 +1,194 @@
+<template>
+  <div id="perUser">
+    <el-row>
+      <el-col :span="24">
+        <el-tabs v-model="active" type="card">
+          <el-tab-pane label="待审核" name="first">
+            <data-table
+              :fields="fields"
+              :opera="opera"
+              :data="oneList"
+              :total="oneTotal"
+              @query="search"
+              @change="toChange"
+              @edit="toEdit"
+              @delete="toDelete"
+            ></data-table>
+          </el-tab-pane>
+          <el-tab-pane label="审核通过" name="second">
+            <data-table
+              :fields="fields"
+              :opera="opera"
+              :data="twoList"
+              :total="twoTotal"
+              @query="search"
+              @change="toChange"
+              @edit="toEdit"
+              @delete="toDelete"
+            ></data-table>
+          </el-tab-pane>
+          <el-tab-pane label="审核失败" name="third">
+            <data-table
+              :fields="fields"
+              :opera="opera"
+              :data="thrList"
+              :total="thrTotal"
+              @query="search"
+              @change="toChange"
+              @edit="toEdit"
+              @delete="toDelete"
+            ></data-table>
+          </el-tab-pane>
+        </el-tabs>
+      </el-col>
+    </el-row>
+    <el-dialog title="更换管理员" width="40%" :visible.sync="dialog" @closed="handleClose" :destroy-on-close="true">
+      <data-form :data="form" :fields="formfields" :rules="{}" @save="toSave">
+        <template #options="{ item }">
+          <template v-if="item.model === 'code'">
+            <el-option v-for="(i, index) in codeList" :key="index" :label="i.user_name" :value="i.code"></el-option>
+          </template>
+        </template>
+      </data-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import dataTable from '@common/src/components/frame/filter-page-table.vue';
+import dataForm from '@common/src/components/frame/form.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: personal } = createNamespacedHelpers('personal');
+const { mapActions: inviteCode } = createNamespacedHelpers('inviteCode');
+export default {
+  name: 'perUser',
+  props: {},
+  components: {
+    dataTable,
+    dataForm,
+  },
+  data: function () {
+    return {
+      active: 'first',
+      opera: [
+        {
+          label: '更换管理员',
+          method: 'change',
+        },
+        {
+          label: '审核&查看',
+          method: 'edit',
+        },
+        {
+          label: '删除',
+          method: 'delete',
+        },
+      ],
+      fields: [
+        { label: '机构代码或邀请码', prop: 'code' },
+        { label: '用户名', prop: 'name' },
+        { label: '用户ID', prop: 'phone' },
+        { label: '电子邮箱', prop: 'email' },
+        { label: '所属辖区', prop: 'juris' },
+        {
+          label: '状态',
+          prop: 'status',
+          format: (item) => {
+            return item === '0' ? '审核中' : item === '1' ? '审核通过' : '审核拒绝';
+          },
+        },
+      ],
+      // 待审核
+      oneList: [],
+      oneTotal: 0,
+      // 审核通过
+      twoList: [],
+      twoTotal: 0,
+      // 审核失败
+      thrList: [],
+      thrTotal: 0,
+      // 更换管理员
+      dialog: false,
+      codeList: [],
+      formfields: [
+        { label: '姓名', model: 'name', options: { readonly: true } },
+        { label: '所属机构', model: 'code', type: 'select' },
+      ],
+      form: {},
+    };
+  },
+  async created() {
+    await this.searchOther();
+    await this.search();
+  },
+  methods: {
+    ...inviteCode({ codeQuery: 'query' }),
+    ...personal(['query', 'delete', 'update']),
+    // 查询列表
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      let user = this.user;
+      if (user.role != '0') info.code = user.code;
+      let one = await this.query({ skip, limit, status: '0', ...info });
+      let two = await this.query({ skip, limit, status: '1', ...info });
+      let thr = await this.query({ skip, limit, status: '2', ...info });
+      if (this.$checkRes(one) || this.$checkRes(two) || this.$checkRes(thr)) {
+        this.$set(this, `oneList`, one.data);
+        this.$set(this, `oneTotal`, one.total);
+        this.$set(this, `twoList`, two.data);
+        this.$set(this, `twoTotal`, two.total);
+        this.$set(this, `thrList`, thr.data);
+        this.$set(this, `thrTotal`, thr.total);
+      }
+    },
+    // 修改
+    toEdit({ data }) {
+      this.$router.push({ path: '/user/detail', query: { id: data.id, type: '4' } });
+    },
+    // 删除
+    async toDelete({ data }) {
+      let res = await this.delete(data.id);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '信息修改成功',
+          type: 'success',
+        });
+        this.search();
+      }
+    },
+    // 更换管理员
+    toChange({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = true;
+    },
+    async toSave({ data }) {
+      let res = await this.update(data);
+      if (this.$checkRes(res)) {
+        this.$message({
+          message: '更换管理员成功',
+          type: 'success',
+        });
+        this.handleClose();
+      }
+    },
+    // 取消更换
+    handleClose() {
+      this.form = {};
+      this.dialog = false;
+      this.search();
+    },
+    // 查询机构代码
+    async searchOther() {
+      let res = await this.codeQuery();
+      if (this.$checkRes(res)) {
+        this.$set(this, `codeList`, res.data);
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  watch: {},
+};
+</script>
+
+<style lang="less" scoped></style>

+ 93 - 0
src/views/jg/disclosure/detail.vue

@@ -0,0 +1,93 @@
+<template>
+  <div id="detail">
+    <data-form v-model="form" @save="toSave" :fields="fields" submitText="审核" :needSave="false">
+      <template #status>
+        <el-radio v-for="(i, index) in statusList" :key="`status-${index}`" :label="i.value"> {{ i.label }}</el-radio>
+      </template>
+    </data-form>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import quests from '@common/dict/patent/quests';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'detail',
+  props: {
+    form: { type: Object, default: () => {} },
+  },
+  model: {
+    prop: 'form',
+    event: 'change',
+  },
+  components: {},
+  data: function () {
+    return {
+      fields: [
+        { label: '发明名称', model: 'name', type: 'text', required: true },
+        { label: '申请人', model: 'apply_name', required: true, type: 'text' },
+        { label: '申请类型', model: 'type', type: 'text', required: true },
+        { label: '发明人', model: 'inventer', type: 'text', required: true },
+        { label: '技术联系人', model: 'contact', type: 'text', required: true },
+        { label: '联系人电话', model: 'phone', type: 'text', required: true },
+        { label: '联系人邮箱', model: 'email', type: 'text', required: true },
+        { label: '特殊情况说明', model: 'desc', type: 'text', required: true },
+      ],
+      typeList: [{ label: '国家发明' }, { label: '国防专利' }, { label: '实用新型' }, { label: '外观' }],
+      statusList: [
+        { label: '通过', value: '1' },
+        { label: '拒绝', value: '-1' },
+      ],
+    };
+  },
+  created() {
+    this.setQuest();
+  },
+  methods: {
+    async toSave({ data }) {
+      this.$emit('submit', data);
+    },
+    setQuest() {
+      let fields = _.cloneDeep(this.fields);
+      let qs = _.cloneDeep(quests);
+      qs = qs.map((i) => {
+        i.type = 'text';
+        return i;
+      });
+      fields.push(...qs);
+      fields = fields.map((i) => {
+        delete i.required;
+        return i;
+      });
+      // 添加审核
+      // const checkField = [
+      //   {
+      //     label: '预估结果',
+      //     model: 'status',
+      //     type: 'radio',
+      //     required: true,
+      //   },
+      //   {
+      //     label: '意见&建议',
+      //     model: 'remark',
+      //     type: 'textarea',
+      //   },
+      // ];
+      // fields.push(...checkField);
+      this.$set(this, `fields`, fields);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 93 - 0
src/views/jg/disclosure/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <div id="index">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @check="toCheck"> </data-table>
+    <el-dialog title="审核" width="80vw" center :visible.sync="dialog" :destroy-on-close="true">
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <info v-model="form"></info>
+        </el-col>
+        <el-col :span="12">
+          <el-input v-model="form.remark" placeholder="请输入您的意见" type="textarea" :autosize="{ minRows: 5, maxRows: 10 }"></el-input>
+          <div style="width: 100%; margin: 16px; text-align: center">
+            <el-button type="success" style="width: 25%" @click="toStatus('2')">通过</el-button>
+            <el-button type="danger" style="width: 25%" @click="toStatus('-1')">拒绝</el-button>
+            <el-button style="width: 25%" @click="dialog = false">返回</el-button>
+          </div>
+        </el-col>
+      </el-row>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import info from './detail.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: disclosure } = createNamespacedHelpers('disclosure');
+export default {
+  name: 'index',
+  props: {},
+  components: { info },
+  data: function () {
+    return {
+      list: [],
+      total: 0,
+      dialog: false,
+      form: {},
+      fields: [
+        { label: '发明名称', model: 'name', filter: true },
+        { label: '申请人', model: 'apply_name' },
+        { label: '申请类型', model: 'type' },
+        { label: '技术联系人', model: 'contact' },
+        { label: '联系人电话', model: 'phone' },
+        { label: '联系人邮箱', model: 'email' },
+      ],
+      opera: [{ label: '审核', method: 'check' }],
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    ...disclosure(['query', 'check']),
+    async search({ skip = 0, limit = 10, ...info } = {}) {
+      const res = await this.query({ skip, limit, ...info, mechanism_id: this.user._id, status: '1' });
+      if (this.$checkRes(res)) {
+        this.$set(this, `list`, res.data);
+        this.$set(this, `total`, res.total);
+      }
+    },
+    async toCheck({ data }) {
+      this.$set(this, `form`, data);
+      this.dialog = true;
+    },
+    // 申请状态修改
+    async toStatus(status) {
+      const { id, remark } = this.form;
+      const obj = { id, status, remark };
+      const res = await this.check(obj);
+      if (this.$checkRes(res, '操作成功', res.errmsg || '操作失败')) {
+        this.dialog = false;
+        this.search();
+      }
+    },
+    async statusCheck(data) {
+      const res = await this.check(data);
+      if (this.$checkRes(res, '审核成功', res.errmsg || '审核失败')) {
+        this.search();
+        this.dialog = false;
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 36 - 0
src/views/jg/homeIndex/index.vue

@@ -0,0 +1,36 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main"> test </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  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>

+ 41 - 0
src/views/jg/message/index.vue

@@ -0,0 +1,41 @@
+<template>
+  <div id="index">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @check="toCheck"> </data-table>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      list: [
+        { contact: '消息1', time: '2021-07-01 10:00:21', status: '0' },
+        { contact: '消息2', time: '2021-07-02 12:00:30', status: '1' },
+      ],
+      total: 2,
+      fields: [
+        { label: '消息内容', model: 'contact' },
+        { label: '时间', model: 'time' },
+      ],
+      opera: [{ label: '取消提醒', method: 'off', display: (i) => i.status === '0' }],
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 45 - 0
src/views/jg/patent/index.vue

@@ -0,0 +1,45 @@
+<template>
+  <div id="index">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search" @check="toCheck"> </data-table>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      list: [
+        { name: '专利1', apply_name: '申请人', type: '申请类型', contact: '联系人', phone: '11111111111', email: 'test@qq.com' },
+        { name: '专利2', apply_name: '申请人2', type: '申请类型2', contact: '联系人2', phone: '11111111112', email: 'test2@qq.com' },
+      ],
+      total: 2,
+      fields: [
+        { label: '专利名称', model: 'name', filter: true },
+        { label: '申请人', model: 'apply_name' },
+        { label: '申请类型', model: 'type' },
+        { label: '技术联系人', model: 'contact' },
+        { label: '联系人电话', model: 'phone' },
+        { label: '联系人邮箱', model: 'email' },
+      ],
+      opera: [{ label: '查看', method: 'check' }],
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 30 - 0
src/views/jg/statistics/index.vue

@@ -0,0 +1,30 @@
+<template>
+  <div id="index">
+    <p>statistics</p>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {},
+  data: function () {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 43 - 0
src/views/jg/trade/index.vue

@@ -0,0 +1,43 @@
+<template>
+  <div id="index">
+    <el-tabs v-model="active" type="card">
+      <el-tab-pane label="许可" name="first">
+        <list type="0"></list>
+      </el-tab-pane>
+      <el-tab-pane label="转让" name="second">
+        <list type="1"></list>
+      </el-tab-pane>
+      <el-tab-pane label="质押" name="third">
+        <list type="2"></list>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import list from './parts/list.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: { list },
+  data: function () {
+    return {
+      active: 'first',
+    };
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 59 - 0
src/views/jg/trade/parts/list.vue

@@ -0,0 +1,59 @@
+<template>
+  <div id="list">
+    <data-table :fields="fields" :opera="opera" :data="list" :total="total" @query="search"></data-table>
+  </div>
+</template>
+
+<script>
+const moment = require('moment');
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'list',
+  props: {
+    type: { type: String, default: '0' },
+  },
+  components: {},
+  data: function () {
+    return {
+      list: [],
+      total: 0,
+      fields: [
+        { label: '专利名称', model: 'name' },
+        { label: '交易时间', model: 'time' },
+        { label: '交易人', model: 'trade_person' },
+        { label: '交易人电话', model: 'trade_phone' },
+        { label: '交易人邮箱', model: 'trade_email' },
+      ],
+      opera: [{ label: '提醒到期' }],
+    };
+  },
+  created() {
+    this.search();
+  },
+  methods: {
+    async search() {
+      // 根据type去找各类型的数据
+      for (let i = 1; i <= 5; i++) {
+        this.list.push({
+          name: `专利${i}`,
+          time: moment().add(i, 'd').format('YYYY-MM-DD HH:mm:ss'),
+          trade_person: 'test',
+          trade_phone: '12345678900',
+          trade_email: 'test@qq.com',
+        });
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 127 - 0
src/views/login.vue

@@ -0,0 +1,127 @@
+<template>
+  <div id="login">
+    <el-row>
+      <el-col :span="24">
+        <el-row>
+          <el-col :span="24" class="main">
+            <div class="w_1200">
+              <el-col :span="24" class="login">
+                <div class="ms-title">后台管理系统</div>
+                <el-form :model="form" :rules="rules" ref="login" label-width="0px" class="ms-content">
+                  <el-form-item prop="username">
+                    <el-input v-model="form.code_phone" placeholder="请输入手机号">
+                      <el-button slot="prepend" icon="el-icon-user"></el-button>
+                    </el-input>
+                  </el-form-item>
+                  <el-form-item prop="password">
+                    <el-input type="password" placeholder="请输入密码" v-model="form.passwd">
+                      <el-button slot="prepend" icon="el-icon-lock"></el-button>
+                    </el-input>
+                  </el-form-item>
+                  <div class="login-btn">
+                    <el-button type="primary" @click="submitForm()">登录</el-button>
+                  </div>
+                </el-form>
+              </el-col>
+            </div>
+          </el-col>
+        </el-row>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: adminLogin } = createNamespacedHelpers('adminLogin');
+export default {
+  metaInfo: { title: '登录' },
+  name: 'login',
+  props: {},
+  components: {},
+  data: function () {
+    return {
+      form: {},
+      rules: {
+        tel: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
+        pwd: [{ required: true, message: '请输入密码', trigger: 'blur' }],
+      },
+    };
+  },
+  created() {},
+  methods: {
+    ...adminLogin(['login']),
+    submitForm() {
+      this.$refs.login.validate(async (valid) => {
+        if (valid) {
+          let res = await this.login({ user: this.form });
+          if (this.$checkRes(res)) {
+            const { user } = res;
+            this.$message.success('登录成功');
+            if (user.role === '1') this.$router.push('/adminCenter/homeIndex');
+            else this.$router.push('/jg/homeIndex');
+          }
+        } else {
+          this.$message.error('请输入账号和密码');
+          console.log('error submit!!');
+          return false;
+        }
+      });
+    },
+  },
+  computed: {},
+};
+</script>
+
+<style lang="less" scoped>
+.w_1200 {
+  width: 1200px;
+  margin: 0 auto;
+}
+.main {
+  background-image: url(../assets/img/login-bg.jpg);
+  height: 100vh;
+  background-repeat: no-repeat;
+  background-size: cover;
+  .login {
+    position: absolute;
+    left: 50%;
+    top: 50%;
+    width: 350px;
+    margin: -190px 0 0 -175px;
+    border-radius: 5px;
+    background: hsla(0, 0%, 100%, 0.3);
+    overflow: hidden;
+    .ms-title {
+      width: 100%;
+      line-height: 50px;
+      text-align: center;
+      font-size: 20px;
+      color: #fff;
+      border-bottom: 1px solid #ddd;
+    }
+    .ms-content {
+      padding: 30px 30px;
+    }
+    .login-btn {
+      text-align: center;
+    }
+    .login-btn button {
+      width: 100%;
+      height: 36px;
+      margin-bottom: 10px;
+    }
+    .login-tips {
+      font-size: 12px;
+      line-height: 30px;
+      color: #fff;
+    }
+    /deep/.js .el-form-item__content {
+      padding: 0 25px;
+      .el-radio {
+        color: #fff;
+      }
+    }
+  }
+}
+</style>

+ 34 - 0
vue.config.js

@@ -0,0 +1,34 @@
+const path = require('path');
+const common = path.resolve(__dirname, '../common');
+const frame = path.resolve(__dirname, '../frame');
+module.exports = {
+  publicPath: `/${process.env.VUE_APP_ROUTER}`,
+  outputDir: 'studycms',
+  productionSourceMap: false,
+  configureWebpack: (config) => {
+    Object.assign(config, {
+      resolve: {
+        alias: {
+          '@': path.resolve(__dirname, './src'),
+          '@c': path.resolve(__dirname, './src/components'),
+          '@a': path.resolve(__dirname, './src/assets'),
+          '@common': common,
+          '@frame': frame,
+        },
+      },
+    });
+  },
+  devServer: {
+    port: '8001',
+    proxy: {
+      '/files': {
+        target: 'http://broadcast.waityou24.cn',
+      },
+      '/api': {
+        target: 'http://broadcast.waityou24.cn',
+        changeOrigin: true,
+        ws: false,
+      },
+    },
+  },
+};