guhongwei пре 4 година
комит
aee5c00fe2

+ 3 - 0
.env

@@ -0,0 +1,3 @@
+VUE_APP_AXIOS_BASE_URL = ''
+VUE_APP_ROUTER="jlstcompany"
+VUE_APP_HOST="http://broadcast.waityou24.cn"

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

+ 24 - 0
.gitignore

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

+ 24 - 0
README.md

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

+ 3 - 0
babel.config.js

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

+ 63 - 0
package.json

@@ -0,0 +1,63 @@
+{
+  "name": "jlst-company",
+  "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",
+    "core-js": "^3.6.5",
+    "element": "^0.1.4",
+    "element-ui": "^2.15.1",
+    "jsonwebtoken": "^8.5.1",
+    "lodash": "^4.17.21",
+    "moment": "^2.29.1",
+    "naf-core": "^0.1.2",
+    "vant": "^2.12.15",
+    "vue": "^2.6.11",
+    "vue-meta": "^2.4.0",
+    "vue-router": "^3.2.0",
+    "vuex": "^3.4.0",
+    "weixin-js-sdk": "^1.6.0"
+  },
+  "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>

+ 17 - 0
src/App.vue

@@ -0,0 +1,17 @@
+<template>
+  <div id="app">
+    <router-view />
+  </div>
+</template>
+
+<style lang="less">
+p {
+  padding: 0;
+  margin: 0;
+}
+.textOver {
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+</style>

BIN
src/assets/logo.png


+ 130 - 0
src/components/HelloWorld.vue

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

+ 47 - 0
src/layout/common/page.vue

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

+ 7 - 0
src/layout/common/top.md

@@ -0,0 +1,7 @@
+<!-- 头部类型 -->
+# topType
+## topType=="1" 带有查询,查询方法:search
+
+## topType=="2" 带有标题和返回,返回方法:upback
+
+## topType=="3" 带有返回和查询,返回方法:upback,查询方法:search

+ 130 - 0
src/layout/common/top.vue

@@ -0,0 +1,130 @@
+<template>
+  <div id="top">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="one" v-if="topType == '1'">
+          <van-search v-model="searchName" placeholder="请输入信息标题" @search="search" />
+        </el-col>
+        <el-col :span="24" class="two" v-else-if="topType == '2'">
+          <van-nav-bar :title="this.$route.meta.title" :left-arrow="leftArrow" @click-left="upBack">
+            <template #left>
+              <span v-if="leftArrow"><van-icon name="arrow-left" /><span style="color: #409eff">返回</span> </span></template
+            >
+          </van-nav-bar>
+        </el-col>
+        <el-col :span="24" class="thr" v-else-if="topType == '3'">
+          <el-col :span="4" class="back" @click.native="upBack"> <van-icon name="arrow-left" />返回 </el-col>
+          <el-col :span="20" class="search">
+            <van-search v-model="searchName" placeholder="请输入赠与人名称" @search="search" />
+          </el-col>
+        </el-col>
+        <el-col :span="24" class="four" v-else-if="topType == '4'">
+          <el-col :span="4" class="back" @click.native="upBack"> <van-icon name="arrow-left" />返回 </el-col>
+          <el-col :span="16" class="title">
+            {{ $route.meta.title }}
+          </el-col>
+          <el-col :span="4" class="add">
+            <van-button icon="plus" size="small" type="info" round @click="add" />
+          </el-col>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'top',
+  props: {
+    topType: { type: String, default: () => '1' },
+    // 只有类型为2时,有用
+    leftArrow: { type: Boolean, default: () => true },
+  },
+  components: {},
+  data: function () {
+    return {
+      searchName: '',
+    };
+  },
+  created() {},
+  methods: {
+    // 查询
+    search() {
+      this.$emit('search', { searchName: this.searchName });
+    },
+    // 返回
+    upBack() {
+      this.$emit('back');
+    },
+    // 添加
+    add() {
+      this.$emit('add');
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .one {
+    /deep/.van-search {
+      padding: 3px 5px;
+    }
+  }
+  .two {
+    /deep/.van-nav-bar__content {
+      height: 40px;
+    }
+    /deep/.van-icon {
+      top: 2px;
+    }
+  }
+  .thr {
+    .back {
+      color: #409eff;
+      padding: 8px 0;
+      text-align: center;
+      .van-icon {
+        top: 3px;
+      }
+    }
+    .search {
+      /deep/.van-search {
+        padding: 2px 5px 2px 0px;
+      }
+    }
+  }
+  .four {
+    .back {
+      color: #409eff;
+      padding: 8px 0;
+      text-align: center;
+      .van-icon {
+        top: 3px;
+      }
+    }
+    .title {
+      text-align: center;
+      padding: 9px 0;
+    }
+    .add {
+      text-align: center;
+      padding: 4px 0;
+    }
+  }
+}
+</style>

+ 27 - 0
src/main.js

@@ -0,0 +1,27 @@
+import Vue from 'vue';
+import App from './App.vue';
+import '@/plugins/weixin';
+import router from './router';
+import store from './store';
+import '@/plugins/element.js';
+import '@/plugins/vant';
+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 InitStomp from '@/plugins/stomp';
+Vue.config.productionTip = false;
+
+new Vue({
+  router,
+  store,
+  render: (h) => h(App),
+}).$mount('#app');
+InitStomp();
+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 });

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

@@ -0,0 +1,37 @@
+/* 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 { Toast } from 'vant';
+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) {
+          Toast({ type: 'success', message: _okText });
+        }
+        return true;
+      }
+      if (_.isFunction(_errText)) {
+        return _errText();
+      }
+      Toast({ type: 'danger', message: errText });
+      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';
+
+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);

+ 20 - 0
src/plugins/setting.js

@@ -0,0 +1,20 @@
+import Vue from 'vue';
+
+Vue.config.weixin = {
+  // baseUrl: process.env.BASE_URL + 'weixin',
+  // baseUrl: 'http://10.16.8.209:9005',
+};
+
+Vue.config.stomp = {
+  // brokerURL: 'ws://http://free.liaoningdoupo.com/ws',
+  brokerURL: '/ws', // ws://${location.host}/ws
+  connectHeaders: {
+    host: 'visit',
+    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);
+};

+ 5 - 0
src/plugins/vant.js

@@ -0,0 +1,5 @@
+import Vue from 'vue';
+import Vant from 'vant';
+import 'vant/lib/index.css';
+
+Vue.use(Vant);

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

+ 3 - 0
src/plugins/weixin.js

@@ -0,0 +1,3 @@
+import Vue from 'vue';
+const wx = require('weixin-js-sdk');
+Vue.prototype.$wx = wx;

+ 48 - 0
src/router/index.js

@@ -0,0 +1,48 @@
+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: '/',
+    name: 'index',
+    meta: { title: '网站首页' },
+    component: () => import('../views/index.vue'),
+  },
+  {
+    path: '/login',
+    name: 'login',
+    meta: { title: '登录页面' },
+    component: () => import('../views/login.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 == '/policy' || to.path == '/ticket' || to.path == '/userCenter') {
+//     if (!token) {
+//       next(`/login?path=${to.path}`);
+//     } 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;

+ 18 - 0
src/store/index.js

@@ -0,0 +1,18 @@
+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 upload from './upload';
+import login from '@common/src/store/login';
+
+Vue.use(Vuex);
+
+export default new Vuex.Store({
+  state: { ...ustate },
+  mutations: { ...umutations },
+  actions: {},
+  modules: {
+    upload,
+    login,
+  },
+});

+ 25 - 0
src/store/upload.js

@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+import axios from 'axios';
+Vue.use(Vuex);
+const api = {
+  upload: (dir) => `/files/article/${dir}/upload`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async upload({ commit }, { file, dir }) {
+    var formdata = new FormData();
+    formdata.append('file', file, file.name);
+    const res = await axios.post(api.upload(dir), formdata, { headers: { 'Content-Type': 'multipart/form-data' } });
+    return res.data;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

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

+ 72 - 0
src/views/index.vue

@@ -0,0 +1,72 @@
+<template>
+  <div id="index">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="top">
+          <top topType="2" :leftArrow="false"></top>
+        </el-col>
+        <el-col :span="24" class="info" :style="{ height: clientHeight + 'px' }">
+          <van-button @click="$router.push('policy')">高企政策服务</van-button>
+          <van-button @click="$router.push('ticket')">高企申报服务</van-button>
+          <van-button @click="$router.push('userCenter')">个人中心</van-button>
+        </el-col>
+        <el-col :span="24" class="foot"> 底部 </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import top from '@/layout/common/top.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: {
+    top,
+  },
+  data: function () {
+    return {
+      clientHeight: '',
+    };
+  },
+  async created() {},
+  mounted() {
+    let clientHeight = (document.documentElement.clientHeight || document.body.clientHeight) - 90;
+    this.$set(this, `clientHeight`, clientHeight);
+  },
+  methods: {},
+  computed: {
+    ...mapState(['user']),
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .top {
+    height: 40px;
+    overflow: hidden;
+    border-bottom: 1px solid #f1f1f1;
+  }
+  .info {
+    overflow-x: hidden;
+    overflow-y: auto;
+  }
+  .foot {
+    height: 50px;
+    overflow: hidden;
+    border-top: 1px solid #f1f1f1;
+  }
+}
+</style>

+ 156 - 0
src/views/login.vue

@@ -0,0 +1,156 @@
+<template>
+  <div id="login">
+    <el-row>
+      <el-col :span="24" class="main">
+        <el-col :span="24" class="top">
+          <top topType="2" :leftArrow="false"></top>
+        </el-col>
+        <el-col :span="24" class="info" :style="{ height: clientHeight + 'px' }">
+          <van-tabs v-model="active">
+            <van-tab title="管理登录">
+              <van-form>
+                <van-field v-model="form.phone" label="用户名" placeholder="用户名" :rules="[{ required: true, message: '请填写用户名' }]" />
+                <van-field v-model="form.passwd" label="密码" placeholder="密码" type="password" :rules="[{ required: true, message: '请填写密码' }]" />
+                <van-col span="24" class="btn">
+                  <van-button type="danger" round>取消登录</van-button>
+                  <van-button type="info" round @click="oneSubmit">确认登录</van-button>
+                </van-col>
+              </van-form>
+            </van-tab>
+            <van-tab title="企业登录">
+              <van-form>
+                <van-field v-model="form.phone" label="用户名" placeholder="用户名" :rules="[{ required: true, message: '请填写用户名' }]" />
+                <van-field v-model="form.passwd" label="密码" placeholder="密码" type="password" :rules="[{ required: true, message: '请填写密码' }]" />
+                <van-col span="24" class="btn">
+                  <van-button type="danger" round>取消登录</van-button>
+                  <van-button type="info" round @click="twoSubmit">确认登录</van-button>
+                </van-col>
+              </van-form>
+            </van-tab>
+            <van-tab title="机构登录">
+              <van-form>
+                <van-field v-model="form.phone" label="用户名" placeholder="用户名" :rules="[{ required: true, message: '请填写用户名' }]" />
+                <van-field v-model="form.passwd" label="密码" placeholder="密码" type="password" :rules="[{ required: true, message: '请填写密码' }]" />
+                <van-col span="24" class="btn">
+                  <van-button type="danger" round>取消登录</van-button>
+                  <van-button type="info" round @click="thrSubmit">确认登录</van-button>
+                </van-col>
+              </van-form>
+            </van-tab>
+            <van-tab title="专家登录">
+              <van-form>
+                <van-field v-model="form.phone" label="用户名" placeholder="用户名" :rules="[{ required: true, message: '请填写用户名' }]" />
+                <van-field v-model="form.passwd" label="密码" placeholder="密码" type="password" :rules="[{ required: true, message: '请填写密码' }]" />
+                <van-col span="24" class="btn">
+                  <van-button type="danger" round>取消登录</van-button>
+                  <van-button type="info" round @click="fourSubmit">确认登录</van-button>
+                </van-col>
+              </van-form>
+            </van-tab>
+          </van-tabs>
+        </el-col>
+      </el-col>
+    </el-row>
+  </div>
+</template>
+
+<script>
+import top from '@/layout/common/top.vue';
+const _ = require('lodash');
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: adminLogin } = createNamespacedHelpers('login');
+const { mapActions: organization } = createNamespacedHelpers('organization');
+const { mapActions: mechanism } = createNamespacedHelpers('mechanism');
+const { mapActions: expertLogin } = createNamespacedHelpers('expert');
+export default {
+  name: 'login',
+  props: {},
+  components: {
+    top,
+  },
+  data: function () {
+    return {
+      clientHeight: '',
+      active: '0',
+      form: {},
+    };
+  },
+  created() {},
+  mounted() {
+    let clientHeight = (document.documentElement.clientHeight || document.body.clientHeight) - 40;
+    this.$set(this, `clientHeight`, clientHeight);
+  },
+  methods: {
+    // 管理登录
+    ...adminLogin({ adminLogin: 'login' }),
+    // 企业
+    ...organization(['login']),
+    // 机构
+    ...mechanism({ interLogin: 'login' }),
+    // 专家
+    ...expertLogin({ expertLogin: 'login' }),
+    // 管理员
+    oneSubmit() {
+      let data = _.get(this, 'form');
+      data.code_phone = data.phone;
+      console.log(data);
+    },
+    // 企业
+    async twoSubmit() {
+      let data = _.get(this, 'form');
+      data.institution_code = data.phone;
+      const res = await this.login(data);
+      if (this.$checkRes(res)) {
+        this.$router.push({ path: this.path });
+      }
+    },
+    // 机构
+    thrSubmit() {
+      let data = this.form;
+      console.log(data);
+    },
+    // 专家
+    fourSubmit() {
+      let data = this.form;
+      console.log(data);
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    path() {
+      return this.$route.query.path;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+  watch: {
+    test: {
+      deep: true,
+      immediate: true,
+      handler(val) {},
+    },
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.main {
+  .top {
+    height: 40px;
+    overflow: hidden;
+    border-bottom: 1px solid #f1f1f1;
+  }
+  .info {
+    overflow-x: hidden;
+    overflow-y: auto;
+    .btn {
+      text-align: center;
+      padding: 15px 0;
+      .van-button {
+        margin: 0 10px;
+      }
+    }
+  }
+}
+</style>

+ 32 - 0
vue.config.js

@@ -0,0 +1,32 @@
+const path = require('path');
+const common = path.resolve(__dirname, '../common');
+module.exports = {
+  publicPath: `/${process.env.VUE_APP_ROUTER}`,
+  outputDir: 'jlstcompany',
+  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,
+        },
+      },
+    });
+  },
+  devServer: {
+    port: '8001',
+    proxy: {
+      '/files': {
+        target: 'http://broadcast.waityou24.cn',
+      },
+      '/api': {
+        target: 'http://192.168.1.19:9101', //http://192.168.1.19:9200
+        changeOrigin: true,
+        ws: false,
+      },
+    },
+  },
+};