lrf402788946 5 years ago
parent
commit
6cad29d98c

+ 4 - 0
.env

@@ -0,0 +1,4 @@
+VUE_APP_AXIOS_BASE_URL = ''
+VUE_APP_ROOT_URL=/admin/
+VUE_APP_MODULE='school'
+VUE_APP_LIMIT = 10

+ 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: 250,
+      },
+    ],
+    'no-unused-vars': 'off',
+    'no-console': 'off',
+    'prettier/prettier': [
+      'warn',
+      {
+        singleQuote: true,
+        trailingComma: 'es5',
+        bracketSpacing: true,
+        jsxBracketSameLine: true,
+        printWidth: 160,
+      },
+    ],
+  },
+  parserOptions: {
+    parser: 'babel-eslint',
+  },
+};

+ 1 - 0
.gitignore

@@ -1,6 +1,7 @@
 .DS_Store
 node_modules
 /dist
+package-lock.json
 
 # local env files
 .env.local

+ 1 - 1
babel.config.js

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

+ 130 - 25
package-lock.json

@@ -2031,6 +2031,14 @@
       "integrity": "sha1-3TeelPDbgxCwgpH51kwyCXZmF/0=",
       "dev": true
     },
+    "async-validator": {
+      "version": "1.8.5",
+      "resolved": "https://registry.npm.taobao.org/async-validator/download/async-validator-1.8.5.tgz?cache=0&sync_timestamp=1575620599372&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fasync-validator%2Fdownload%2Fasync-validator-1.8.5.tgz",
+      "integrity": "sha1-3D4I7B/Q3dtn5ghC8CwM0c7G1/A=",
+      "requires": {
+        "babel-runtime": "6.x"
+      }
+    },
     "asynckit": {
       "version": "0.4.0",
       "resolved": "https://registry.npm.taobao.org/asynckit/download/asynckit-0.4.0.tgz",
@@ -2078,6 +2086,37 @@
       "integrity": "sha1-fjPY99RJs/ZzzXLeuavcVS2+Uo4=",
       "dev": true
     },
+    "axios": {
+      "version": "0.19.2",
+      "resolved": "https://registry.npm.taobao.org/axios/download/axios-0.19.2.tgz",
+      "integrity": "sha1-PqNsXYgY0NX4qKl6bTa4bNwAyyc=",
+      "requires": {
+        "follow-redirects": "1.5.10"
+      },
+      "dependencies": {
+        "debug": {
+          "version": "3.1.0",
+          "resolved": "https://registry.npm.taobao.org/debug/download/debug-3.1.0.tgz",
+          "integrity": "sha1-W7WgZyYotkFJVmuhaBnmFRjGcmE=",
+          "requires": {
+            "ms": "2.0.0"
+          }
+        },
+        "follow-redirects": {
+          "version": "1.5.10",
+          "resolved": "https://registry.npm.taobao.org/follow-redirects/download/follow-redirects-1.5.10.tgz",
+          "integrity": "sha1-e3qfmuov3/NnhqlP9kPtB/T/Xio=",
+          "requires": {
+            "debug": "=3.1.0"
+          }
+        },
+        "ms": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npm.taobao.org/ms/download/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
+        }
+      }
+    },
     "babel-eslint": {
       "version": "10.0.3",
       "resolved": "https://registry.npm.taobao.org/babel-eslint/download/babel-eslint-10.0.3.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fbabel-eslint%2Fdownload%2Fbabel-eslint-10.0.3.tgz",
@@ -2092,6 +2131,11 @@
         "resolve": "^1.12.0"
       }
     },
+    "babel-helper-vue-jsx-merge-props": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npm.taobao.org/babel-helper-vue-jsx-merge-props/download/babel-helper-vue-jsx-merge-props-2.0.3.tgz",
+      "integrity": "sha1-Iq69OzOQIyjlEyk6jkmSs4T58bY="
+    },
     "babel-loader": {
       "version": "8.0.6",
       "resolved": "https://registry.npm.taobao.org/babel-loader/download/babel-loader-8.0.6.tgz",
@@ -2113,6 +2157,27 @@
         "object.assign": "^4.1.0"
       }
     },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npm.taobao.org/babel-runtime/download/babel-runtime-6.26.0.tgz",
+      "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=",
+      "requires": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.11",
+          "resolved": "https://registry.npm.taobao.org/core-js/download/core-js-2.6.11.tgz?cache=0&sync_timestamp=1578957006406&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fcore-js%2Fdownload%2Fcore-js-2.6.11.tgz",
+          "integrity": "sha1-OIMUafmSK97Y7iHJ3EaYXgOZMIw="
+        },
+        "regenerator-runtime": {
+          "version": "0.11.1",
+          "resolved": "https://registry.npm.taobao.org/regenerator-runtime/download/regenerator-runtime-0.11.1.tgz",
+          "integrity": "sha1-vgWtf5v30i4Fb5cmzuUBf78Z4uk="
+        }
+      }
+    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.0.tgz",
@@ -3631,8 +3696,7 @@
     "deepmerge": {
       "version": "1.5.2",
       "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-1.5.2.tgz?cache=0&sync_timestamp=1572279812893&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdeepmerge%2Fdownload%2Fdeepmerge-1.5.2.tgz",
-      "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M=",
-      "dev": true
+      "integrity": "sha1-EEmdhohEza1P7ghC34x/bwyVp1M="
     },
     "default-gateway": {
       "version": "5.0.5",
@@ -4071,6 +4135,19 @@
       "integrity": "sha1-8Tl6Yzw15yZzDCS+EITNJcPugUg=",
       "dev": true
     },
+    "element-ui": {
+      "version": "2.13.0",
+      "resolved": "https://registry.npm.taobao.org/element-ui/download/element-ui-2.13.0.tgz",
+      "integrity": "sha1-9rsE5bCnbqX2JGYES3dEB7pOvS0=",
+      "requires": {
+        "async-validator": "~1.8.1",
+        "babel-helper-vue-jsx-merge-props": "^2.0.0",
+        "deepmerge": "^1.2.0",
+        "normalize-wheel": "^1.0.1",
+        "resize-observer-polyfill": "^1.5.0",
+        "throttle-debounce": "^1.0.1"
+      }
+    },
     "elliptic": {
       "version": "6.5.2",
       "resolved": "https://registry.npm.taobao.org/elliptic/download/elliptic-6.5.2.tgz",
@@ -5039,14 +5116,12 @@
         "balanced-match": {
           "version": "1.0.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "brace-expansion": {
           "version": "1.1.11",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "balanced-match": "^1.0.0",
             "concat-map": "0.0.1"
@@ -5061,20 +5136,17 @@
         "code-point-at": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "concat-map": {
           "version": "0.0.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "console-control-strings": {
           "version": "1.1.0",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "core-util-is": {
           "version": "1.0.2",
@@ -5191,8 +5263,7 @@
         "inherits": {
           "version": "2.0.4",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "ini": {
           "version": "1.3.5",
@@ -5204,7 +5275,6 @@
           "version": "1.0.0",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "number-is-nan": "^1.0.0"
           }
@@ -5219,7 +5289,6 @@
           "version": "3.0.4",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "brace-expansion": "^1.1.7"
           }
@@ -5227,14 +5296,12 @@
         "minimist": {
           "version": "0.0.8",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "minipass": {
           "version": "2.9.0",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "safe-buffer": "^5.1.2",
             "yallist": "^3.0.0"
@@ -5253,7 +5320,6 @@
           "version": "0.5.1",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "minimist": "0.0.8"
           }
@@ -5343,8 +5409,7 @@
         "number-is-nan": {
           "version": "1.0.1",
           "bundled": true,
-          "dev": true,
-          "optional": true
+          "dev": true
         },
         "object-assign": {
           "version": "4.1.1",
@@ -5356,7 +5421,6 @@
           "version": "1.4.0",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "wrappy": "1"
           }
@@ -5478,7 +5542,6 @@
           "version": "1.0.2",
           "bundled": true,
           "dev": true,
-          "optional": true,
           "requires": {
             "code-point-at": "^1.0.0",
             "is-fullwidth-code-point": "^1.0.0",
@@ -6953,8 +7016,7 @@
     "lodash": {
       "version": "4.17.15",
       "resolved": "https://registry.npm.taobao.org/lodash/download/lodash-4.17.15.tgz?cache=0&sync_timestamp=1563508077056&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Flodash%2Fdownload%2Flodash-4.17.15.tgz",
-      "integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg=",
-      "dev": true
+      "integrity": "sha1-tEf2ZwoEVbv+7dETku/zMOoJdUg="
     },
     "lodash.defaultsdeep": {
       "version": "4.6.1",
@@ -7439,6 +7501,14 @@
         "thenify-all": "^1.0.0"
       }
     },
+    "naf-core": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npm.taobao.org/naf-core/download/naf-core-0.1.2.tgz",
+      "integrity": "sha1-0UetT3+BTsnSvYGPWCOVHgWAsJU=",
+      "requires": {
+        "lodash": "^4.17.11"
+      }
+    },
     "nan": {
       "version": "2.14.0",
       "resolved": "https://registry.npm.taobao.org/nan/download/nan-2.14.0.tgz",
@@ -7601,6 +7671,11 @@
       "integrity": "sha1-suHE3E98bVd0PfczpPWXjRhlBVk=",
       "dev": true
     },
+    "normalize-wheel": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npm.taobao.org/normalize-wheel/download/normalize-wheel-1.0.1.tgz",
+      "integrity": "sha1-rsiGr/2wRQcNhWRH32Ls+GFG7EU="
+    },
     "npm-run-path": {
       "version": "2.0.2",
       "resolved": "https://registry.npm.taobao.org/npm-run-path/download/npm-run-path-2.0.2.tgz",
@@ -9201,6 +9276,11 @@
       "integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
       "dev": true
     },
+    "resize-observer-polyfill": {
+      "version": "1.5.1",
+      "resolved": "https://registry.npm.taobao.org/resize-observer-polyfill/download/resize-observer-polyfill-1.5.1.tgz",
+      "integrity": "sha1-DpAg3T0hAkRY1OvSfiPkAmmBBGQ="
+    },
     "resolve": {
       "version": "1.15.0",
       "resolved": "https://registry.npm.taobao.org/resolve/download/resolve-1.15.0.tgz?cache=0&sync_timestamp=1579709925738&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fresolve%2Fdownload%2Fresolve-1.15.0.tgz",
@@ -10327,6 +10407,11 @@
         "neo-async": "^2.6.0"
       }
     },
+    "throttle-debounce": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npm.taobao.org/throttle-debounce/download/throttle-debounce-1.1.0.tgz",
+      "integrity": "sha1-UYU9o3vmihVctugns1FKPEIuic0="
+    },
     "through": {
       "version": "2.3.8",
       "resolved": "https://registry.npm.taobao.org/through/download/through-2.3.8.tgz",
@@ -10888,6 +10973,21 @@
         "vue-style-loader": "^4.1.0"
       }
     },
+    "vue-meta": {
+      "version": "2.3.2",
+      "resolved": "https://registry.npm.taobao.org/vue-meta/download/vue-meta-2.3.2.tgz",
+      "integrity": "sha1-2HpFZYLaBA/3vJMEcBzKR6w27h4=",
+      "requires": {
+        "deepmerge": "^4.2.2"
+      },
+      "dependencies": {
+        "deepmerge": {
+          "version": "4.2.2",
+          "resolved": "https://registry.npm.taobao.org/deepmerge/download/deepmerge-4.2.2.tgz?cache=0&sync_timestamp=1572279812893&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fdeepmerge%2Fdownload%2Fdeepmerge-4.2.2.tgz",
+          "integrity": "sha1-RNLqNnm49NT/ujPwPYZfwee/SVU="
+        }
+      }
+    },
     "vue-router": {
       "version": "3.1.5",
       "resolved": "https://registry.npm.taobao.org/vue-router/download/vue-router-3.1.5.tgz",
@@ -10924,6 +11024,11 @@
       "resolved": "https://registry.npm.taobao.org/vuex/download/vuex-3.1.2.tgz",
       "integrity": "sha1-ooY/QAWqc/JYflXD+t8/AfacfU0="
     },
+    "wangeditor": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npm.taobao.org/wangeditor/download/wangeditor-3.1.1.tgz",
+      "integrity": "sha1-+9PB1JdpI8nt67hbKdMLNVEq0Dk="
+    },
     "watchpack": {
       "version": "1.6.0",
       "resolved": "https://registry.npm.taobao.org/watchpack/download/watchpack-1.6.0.tgz?cache=0&sync_timestamp=1579095944058&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fwatchpack%2Fdownload%2Fwatchpack-1.6.0.tgz",

+ 7 - 1
package.json

@@ -8,10 +8,16 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
+    "axios": "^0.19.2",
     "core-js": "^3.4.4",
+    "element-ui": "^2.13.0",
+    "lodash": "^4.17.15",
+    "naf-core": "^0.1.2",
     "vue": "^2.6.10",
+    "vue-meta": "^2.3.2",
     "vue-router": "^3.1.3",
-    "vuex": "^3.1.2"
+    "vuex": "^3.1.2",
+    "wangeditor": "^3.1.1"
   },
   "devDependencies": {
     "@vue/cli-plugin-babel": "^4.1.0",

+ 17 - 22
src/App.vue

@@ -1,32 +1,27 @@
 <template>
   <div id="app">
-    <div id="nav">
-      <router-link to="/">Home</router-link> |
-      <router-link to="/about">About</router-link>
-    </div>
     <router-view />
   </div>
 </template>
+<script>
+export default {
+  name: 'App',
+  components: {},
+};
+</script>
 
 <style lang="less">
-#app {
-  font-family: "Avenir", Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-  color: #2c3e50;
-}
-
-#nav {
-  padding: 30px;
-
-  a {
-    font-weight: bold;
-    color: #2c3e50;
-
-    &.router-link-exact-active {
-      color: #42b983;
-    }
+html {
+  overflow: hidden;
+  body {
+    margin: 0;
+    padding: 0;
+  }
+  margin: 0;
+  padding: 0;
+  p {
+    margin: 0;
+    padding: 0;
   }
 }
 </style>

+ 0 - 130
src/components/HelloWorld.vue

@@ -1,130 +0,0 @@
-<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>

+ 10 - 6
src/main.js

@@ -1,12 +1,16 @@
-import Vue from "vue";
-import App from "./App.vue";
-import router from "./router";
-import store from "./store";
+import Vue from 'vue';
+import App from './App.vue';
+import router from './router';
+import store from './store';
+import '@/plugins/meta';
+import '@/plugins/axios';
+import '@/plugins/check-res';
+import '@/plugins/element';
 
 Vue.config.productionTip = false;
 
 new Vue({
   router,
   store,
-  render: h => h(App)
-}).$mount("#app");
+  render: h => h(App),
+}).$mount('#app');

+ 1 - 0
src/plugins/README.md

@@ -0,0 +1 @@
+### 框架使用的 vue plugin

+ 24 - 0
src/plugins/axios.js

@@ -0,0 +1,24 @@
+/* eslint-disable no-console */
+/* eslint-disable no-param-reassign */
+
+import Vue from 'vue';
+import AxiosWrapper from '@frame/utils/axios-wrapper';
+
+const Plugin = {
+  install(vue, options) {
+    // 3. 注入组件
+    vue.mixin({
+      created() {
+        if (this.$store && !this.$store.$axios) {
+          this.$store.$axios = this.$axios;
+          this.$store.$limit = process.env.VUE_APP_LIMIT;
+        }
+      },
+    });
+    // 4. 添加实例方法
+    vue.prototype.$axios = new AxiosWrapper(options);
+    vue.prototype.$limit = process.env.VUE_APP_LIMIT;
+  },
+};
+
+Vue.use(Plugin, { baseUrl: process.env.VUE_APP_AXIOS_BASE_URL, unwrap: true });

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

@@ -0,0 +1,40 @@
+/* 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);
+          // Message({ message: _okText, type: 'success', duration: 60000 });
+        }
+        return true;
+      }
+      if (_.isFunction(_errText)) {
+        return _errText();
+      }
+      Message.error(_errText || errmsg);
+      // Message({ message: _errText || errmsg, duration: 60000 });
+      return false;
+    };
+  },
+};
+
+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';
+// 注册element-ui组件
+Vue.use(Element);

+ 4 - 0
src/plugins/meta.js

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

+ 26 - 0
src/plugins/naf-dict.js

@@ -0,0 +1,26 @@
+/**
+ * 字典数据处理插件
+ */
+
+import Vue from 'vue';
+import _ from 'lodash';
+import assert from 'assert';
+
+const Plugin = {
+  install(vue, options) {
+    // 4. 添加实例方法
+    vue.prototype.$dict = function(codeType, code) {
+      assert(_.isString(codeType));
+      const state = this.$store.state.naf.dict;
+      if (!state) {
+        throw new Error("can't find store for naf dict");
+      }
+      if (_.isString(code)) {
+        return (state.codes[codeType] && state.codes[codeType][code]) || code;
+      } else {
+        return state.items[codeType];
+      }
+    };
+  },
+};
+Vue.use(Plugin);

+ 6 - 0
src/plugins/nut-ui.js

@@ -0,0 +1,6 @@
+import Vue from 'vue';
+import { Toast } from '@nutui/nutui';
+// import '@nutui/nutui/dist/nutui.css';
+
+// 注册nut-ui组件
+Toast.install(Vue); // 按需加载

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

+ 34 - 18
src/router/index.js

@@ -1,30 +1,46 @@
-import Vue from "vue";
-import VueRouter from "vue-router";
-import Home from "../views/Home.vue";
+import Vue from 'vue';
+import VueRouter from 'vue-router';
 
 Vue.use(VueRouter);
 
 const routes = [
   {
-    path: "/",
-    name: "home",
-    component: Home
+    path: '/',
+    name: 'frame',
+    component: () => import('@/views/index.vue'),
+    children: [
+      {
+        path: '/plan/list',
+        name: 'plan_list',
+        meta: { title: '计划', sub: '管理' },
+        component: () => import('@/views/plan/index.vue'),
+      },
+      {
+        path: '/teacher/list',
+        name: 'teacher_list',
+        meta: { title: '教师', sub: '管理' },
+        component: () => import('@/views/teacher/index.vue'),
+      },
+      {
+        path: '/leave/list',
+        name: 'leave_list',
+        meta: { title: '请假', sub: '管理' },
+        component: () => import('@/views/leave/index.vue'),
+      },
+      {
+        path: '/stud/detail',
+        name: 'stud_detail',
+        meta: { title: '学生', sub: '信息' },
+        component: () => import('@/views/leave/stud.vue'),
+      },
+    ],
   },
-  {
-    path: "/about",
-    name: "about",
-    // route level code-splitting
-    // this generates a separate chunk (about.[hash].js) for this route
-    // which is lazy-loaded when the route is visited.
-    component: () =>
-      import(/* webpackChunkName: "about" */ "../views/About.vue")
-  }
 ];
 
 const router = new VueRouter({
-  mode: "history",
-  base: process.env.BASE_URL,
-  routes
+  mode: 'history',
+  base: process.env.NODE_ENV === 'development' ? '' : process.env.VUE_APP_ROOT_URL + 'school',
+  routes,
 });
 
 export default router;

+ 4 - 4
src/store/index.js

@@ -1,11 +1,11 @@
-import Vue from "vue";
-import Vuex from "vuex";
-
+import Vue from 'vue';
+import Vuex from 'vuex';
+import student from '@center/src/store/student';
 Vue.use(Vuex);
 
 export default new Vuex.Store({
   state: {},
   mutations: {},
   actions: {},
-  modules: {}
+  modules: { student },
 });

+ 0 - 5
src/views/About.vue

@@ -1,5 +0,0 @@
-<template>
-  <div class="about">
-    <h1>This is an about page</h1>
-  </div>
-</template>

+ 0 - 18
src/views/Home.vue

@@ -1,18 +0,0 @@
-<template>
-  <div class="home">
-    <img alt="Vue logo" src="../assets/logo.png" />
-    <HelloWorld msg="Welcome to Your Vue.js App" />
-  </div>
-</template>
-
-<script>
-// @ is an alias to /src
-import HelloWorld from "@/components/HelloWorld.vue";
-
-export default {
-  name: "home",
-  components: {
-    HelloWorld
-  }
-};
-</script>

+ 60 - 0
src/views/index.vue

@@ -0,0 +1,60 @@
+<template>
+  <div id="admin-index">
+    <div class="app-wrapper">
+      <admin-menu class="sidebar-container"></admin-menu>
+      <div class="main-container">
+        <admin-bar></admin-bar>
+        <fw-admin class="display"></fw-admin>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import adminMenu from '@frame/layout/admin/admin-menu.vue';
+import adminBar from '@frame/layout/admin/navBar.vue';
+import fwAdmin from '@frame/layout/admin/fw-admin.vue';
+import { devMenu } from '@frame/config/menu-config';
+export default {
+  name: 'admin-index',
+  metaInfo: { title: ' 双困生培训系统' },
+  props: {},
+  components: {
+    fwAdmin,
+    adminMenu,
+    adminBar,
+    // breadcrumb,
+  },
+  data: () => ({
+    devMenu,
+  }),
+  created() {},
+  computed: {},
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped>
+.display {
+  height: ~'calc(100% - 30px)';
+  padding: 0.3rem;
+}
+
+.app-wrapper {
+  position: relative;
+  height: 100%;
+  width: 100%;
+  &:after {
+    content: '';
+    display: table;
+    clear: both;
+  }
+}
+
+.main-container {
+  min-height: 100vh;
+  transition: margin-left 0.28s;
+  margin-left: 12rem;
+  background-color: #f0f2f5;
+}
+</style>

+ 102 - 0
src/views/leave/index.vue

@@ -0,0 +1,102 @@
+<template>
+  <div id="index">
+    <list-frame :title="mainTitle" @query="search" :total="total" :needFilter="false" @add="$router.push({ path: '/dept/detail' })">
+      <data-table :fields="fields" :data="list" :opera="opera" @edit="toEdit" @check="toCheck"></data-table>
+    </list-frame>
+    <el-dialog title="请假审批" :visible.sync="dialog" width="30%" center @close="toClose">
+      <el-form label-width="120px">
+        <el-form-item label="姓名">{{ operaObject.name }}</el-form-item>
+        <el-form-item label="原因">{{ operaObject.reason }}</el-form-item>
+        <el-form-item label="时间">{{ operaObject.time }}</el-form-item>
+        <el-form-item>
+          <el-row type="flex" justify="middle" align="center">
+            <el-col :span="7">
+              <el-button type="primary" size="mini" @click="setCheck('0')">通过</el-button>
+            </el-col>
+            <el-col :span="7">
+              <el-button type="danger" size="mini" @click="setCheck('1')">拒绝</el-button>
+            </el-col>
+          </el-row>
+        </el-form-item>
+      </el-form>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import listFrame from '@frame/layout/admin/list-frame';
+import dataTable from '@frame/components/data-table';
+import { createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('dept');
+export default {
+  metaInfo: { title: '请假管理' },
+  name: 'index',
+  props: {},
+  components: { listFrame, dataTable },
+  data: () => ({
+    opera: [
+      {
+        label: '查看学生详情',
+        icon: 'el-icon-document',
+        method: 'edit',
+      },
+      {
+        label: '请假审核',
+        icon: 'el-icon-s-check',
+        method: 'check',
+      },
+    ],
+    fields: [
+      { label: '学生姓名', prop: 'name' },
+      { label: '时间', prop: 'time' },
+      { label: '理由', prop: 'reason' },
+    ],
+    list: [{ name: '测试学生1', time: '2020-05-01 - 2020-05-01', reason: '发烧' }],
+    total: 0,
+    dialog: false,
+    operaObject: {},
+  }),
+  created() {},
+  computed: {
+    mainTitle() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      let sub = meta.sub || '';
+      return `${main}${sub}`;
+    },
+    keyWord() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      return main;
+    },
+  },
+  methods: {
+    ...mapActions(['query', 'delete']),
+    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);
+      }
+    },
+    toEdit({ data }) {
+      this.$router.push({ path: '/stud/detail', query: { id: data.id } });
+    },
+
+    toCheck({ data }) {
+      this.$set(this, `operaObject`, data);
+      this.dialog = true;
+    },
+    async setCheck(status) {
+      //TODO 请假审核
+      this.toClose();
+    },
+    toClose() {
+      this.dialog = false;
+      this.operaObject = {};
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 99 - 0
src/views/leave/stud.vue

@@ -0,0 +1,99 @@
+<template>
+  <div id="stud">
+    <detail-frame :title="mainTitle" returns="/leave/index">
+      <data-form :data="info" :fields="fields" :needSave="false" :isNew="false">
+        <template #custom="{ item, form }">
+          <template v-if="item.model === 'gender'">{{ form[item.model] === '0' ? '女' : '男' }}</template>
+        </template>
+        <template #submit>
+          <el-row type="flex" justify="middle" align="center">
+            <el-col :span="7">
+              <el-button type="primary" size="mini" @click="setCheck('0')">通过</el-button>
+            </el-col>
+            <el-col :span="7">
+              <el-button type="danger" size="mini" @click="setCheck('1')">拒绝</el-button>
+            </el-col>
+          </el-row>
+        </template>
+      </data-form>
+    </detail-frame>
+  </div>
+</template>
+
+<script>
+import detailFrame from '@frame/layout/admin/detail-frame';
+import dataForm from '@frame/components/form';
+import { createNamespacedHelpers } from 'vuex';
+const { mapActions: mapStudent } = createNamespacedHelpers('student');
+export default {
+  metaInfo: { title: '学生信息' },
+  name: 'stud',
+  props: {},
+  components: { detailFrame, dataForm },
+  data: () => ({
+    info: { name: '测试学生1', gender: 0, reason: '发烧', time: '2020-05-01 - 2020-05-01' },
+    fields: [
+      { label: '姓名', model: 'name', type: 'text' },
+      { label: '性别', model: 'gender', custom: true },
+      // { label: '民族', model: 'nation', type: 'select' },
+      { label: '身份证号', model: 'id_number', options: { maxlength: 18 }, type: 'text' },
+      // { label: '学校', model: 'school_name', type: 'select' },
+      // { label: '院系', model: 'yard', type: 'select' },
+      // { label: '专业', model: 'major', type: 'select' },
+      { label: '入学年份', model: 'entry_year', type: 'text' },
+      { label: '毕业年份', model: 'finish_year', type: 'text' },
+      { label: '在校曾担任何种职务', model: 'school_job', type: 'text' },
+      { label: '手机号', model: 'phone', options: { maxlength: 11, minlength: 11 }, type: 'text' },
+      { label: 'QQ号', model: 'qq', type: 'text' },
+      { label: '邮箱', model: 'email', type: 'text' },
+      { label: '家庭所在地', model: 'family_place', type: 'text' },
+      { label: '家庭是否困难', model: 'family_is_hard', type: 'text' },
+      { label: '是否获得过助学金', model: 'have_grant', type: 'text' },
+      { label: '职务', model: 'job', type: 'text' },
+      { label: '期', model: 'term', type: 'text' },
+      { label: '批次', model: 'batch', type: 'text' },
+      { label: '班级', model: 'class', type: 'text' },
+      { label: '是否优秀', model: 'is_fine', type: 'text' },
+      //将请假原因,时间也放进info中显示,不会有样式违和
+      { label: '原因', model: 'reason', type: 'text' },
+      { label: '请假时间', model: 'time', type: 'text' },
+    ],
+    loading: true,
+  }),
+  created() {},
+  computed: {
+    id() {
+      return this.$route.query.id;
+    },
+    mainTitle() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      let sub = meta.sub || '';
+      return `${main}${sub}`;
+    },
+    keyWord() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      return main;
+    },
+  },
+  watch: {
+    id: {
+      immediate: true,
+      handler(val) {
+        if (val) this.loading = false;
+        else this.search();
+      },
+    },
+  },
+  methods: {
+    ...mapStudent(['fetch']),
+    async search() {},
+    async setCheck(status) {
+      //TODO 请假审核
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 77 - 0
src/views/plan/index.vue

@@ -0,0 +1,77 @@
+<template>
+  <div id="index">
+    <list-frame :title="mainTitle" @query="search" :total="total" :needFilter="false" @add="$router.push({ path: '/dept/detail' })">
+      <data-table :fields="fields" :data="list" :opera="opera" @edit="toEdit" @update="toUpdate"></data-table>
+    </list-frame>
+  </div>
+</template>
+
+<script>
+import listFrame from '@frame/layout/admin/list-frame';
+import dataTable from '@frame/components/data-table';
+import { createNamespacedHelpers } from 'vuex';
+const { mapActions } = createNamespacedHelpers('dept');
+export default {
+  metaInfo: { title: '计划管理' },
+  name: 'index',
+  props: {},
+  components: { listFrame, dataTable },
+  data: () => ({
+    opera: [
+      {
+        label: '查看全年计划',
+        icon: 'el-icon-date',
+        method: 'edit',
+      },
+      {
+        label: '上报时间',
+        icon: 'el-icon-document',
+        method: 'update',
+      },
+    ],
+    fields: [
+      { label: '计划标题', prop: 'name' },
+      { label: '时间', prop: 'time' },
+    ],
+    list: [{ name: '2020年计划', time: '2020-2021' }],
+    total: 0,
+  }),
+  created() {},
+  computed: {
+    mainTitle() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      let sub = meta.sub || '';
+      let title = main + sub;
+      return title;
+    },
+    keyWord() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      return main;
+    },
+  },
+  methods: {
+    ...mapActions(['query', 'delete']),
+    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);
+      }
+    },
+    toEdit({ data }) {
+      //TODO 该把详情做成什么样的比较好,是和大日历在一起选择还是其他形式
+      // this.$router.push({ path: '/plan/detail', query: { id: data.id } });
+    },
+
+    async toUpdate({ data }) {
+      // const res = await this.delete(data.id);
+      // this.$checkRes(res, '删除成功', '删除失败');
+      // this.search();
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 32 - 0
src/views/teacher/index.vue

@@ -0,0 +1,32 @@
+<template>
+  <div id="index">
+    <p>index</p>
+  </div>
+</template>
+
+<script>
+export default {
+  metaInfo: { title: '教师管理' },
+  name: 'index',
+  props: {},
+  components: {},
+  data: () => ({}),
+  created() {},
+  computed: {
+    mainTitle() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      let sub = meta.sub || '';
+      return `${main}${sub}`;
+    },
+    keyWord() {
+      let meta = this.$route.meta;
+      let main = meta.title || '';
+      return main;
+    },
+  },
+  methods: {},
+};
+</script>
+
+<style lang="less" scoped></style>

+ 60 - 1
vue.config.js

@@ -1,3 +1,62 @@
+const path = require('path');
+const frame = path.resolve(__dirname, '../frame');
+const center = path.resolve(__dirname, '../train-center');
 module.exports = {
-  lintOnSave: false
+  publicPath: process.env.NODE_ENV === 'development' ? '/' : process.env.VUE_APP_ROOT_URL + 'center',
+  configureWebpack: {
+    // externals: {
+    //   'element-ui': 'Element',
+    //   vue: 'Vue',
+    // },
+    // 开发生产共同配置
+    resolve: {
+      alias: {
+        '@': path.resolve(__dirname, './src'),
+        '@c': path.resolve(__dirname, './src/components'),
+        '@a': path.resolve(__dirname, './src/assets'),
+        '@frame': frame,
+        '@center': center,
+      },
+    },
+  },
+  devServer: {
+    port: '8002',
+    //api地址前缀
+    proxy: {
+      '/api': {
+        target: 'http://10.16.9.108:8001',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/files': {
+        target: 'http://smart.cc-lotus.info',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/ws': {
+        target: 'http://smart.cc-lotus.info',
+        ws: true,
+      },
+      '/weixin': {
+        target: 'http://smart.cc-lotus.info',
+        changeOrigin: true,
+        ws: true,
+      },
+      '/admin/center': {
+        target: 'http://localhost:8001',
+      },
+      '/admin/director': {
+        target: 'http://localhost:8002',
+      },
+      '/admin/teacher': {
+        target: 'http://localhost:8003',
+      },
+      '/admin/student': {
+        target: 'http://localhost:8004',
+      },
+      '/admin/school': {
+        target: 'http://localhost:8005',
+      },
+    },
+  },
 };