Forráskód Böngészése

new file: .browserslistrc
new file: .editorconfig
new file: .env
new file: .eslintignore
new file: .eslintrc.js
new file: .gitignore
new file: README.md
new file: babel.config.js
new file: lib/axios.js
new file: lib/last.js
new file: lib/resChange.js
new file: lib/setChildrenSession.js
new file: lib/setParentsetSession.js
new file: lib/tree.js
new file: package-lock.json
new file: package.json
new file: public/favicon.ico
new file: public/index.html
new file: src/App.vue
new file: src/assets/cg.jpg
new file: src/assets/gg.png
new file: src/assets/icon1.png
new file: src/assets/icon2.png
new file: src/assets/icon3.png
new file: src/assets/icon4.png
new file: src/assets/icon5.png
new file: src/assets/icon6.png
new file: src/assets/index.scss
new file: src/assets/jh.png
new file: src/assets/jt.png
new file: src/assets/newbg.jpg
new file: src/assets/sign.png
new file: src/assets/sknj.jpg
new file: src/assets/sknj.png
new file: src/assets/szskl.jpg
new file: src/assets/top.jpg
new file: src/assets/xz.jpg
new file: src/assets/zc.jpg
new file: src/components/banner/banner.vue
new file: src/components/breadcrumb/breadcrumb-item.vue
new file: src/components/breadcrumb/index.vue
new file: src/components/foots/index.vue
new file: src/components/heads/index.vue
new file: src/components/heads/nav/index.vue
new file: src/components/heads/nav/nav-item.vue
new file: src/components/leftmenu/index.vue
new file: src/components/leftmenu/nav-item.vue
new file: src/components/link/index.vue
new file: src/components/list/index.vue
new file: src/components/list/threeList.vue
new file: src/components/pagination.vue
new file: src/components/upwindow.vue
new file: src/main.js
new file: src/router/index.js
new file: src/store/index.js
new file: src/views/Home.vue
new file: src/views/details.vue
new file: src/views/leader.vue
new file: src/views/list.vue
new file: src/views/pages.vue
new file: vue.config.js

asd123a20 2 éve
commit
c0e3c962d8
61 módosított fájl, 30496 hozzáadás és 0 törlés
  1. 3 0
      .browserslistrc
  2. 5 0
      .editorconfig
  3. 2 0
      .env
  4. 1 0
      .eslintignore
  5. 23 0
      .eslintrc.js
  6. 23 0
      .gitignore
  7. 24 0
      README.md
  8. 5 0
      babel.config.js
  9. 51 0
      lib/axios.js
  10. 17 0
      lib/last.js
  11. 17 0
      lib/resChange.js
  12. 16 0
      lib/setChildrenSession.js
  13. 18 0
      lib/setParentsetSession.js
  14. 26 0
      lib/tree.js
  15. 28050 0
      package-lock.json
  16. 37 0
      package.json
  17. BIN
      public/favicon.ico
  18. 17 0
      public/index.html
  19. 34 0
      src/App.vue
  20. BIN
      src/assets/cg.jpg
  21. BIN
      src/assets/gg.png
  22. BIN
      src/assets/icon1.png
  23. BIN
      src/assets/icon2.png
  24. BIN
      src/assets/icon3.png
  25. BIN
      src/assets/icon4.png
  26. BIN
      src/assets/icon5.png
  27. BIN
      src/assets/icon6.png
  28. 68 0
      src/assets/index.scss
  29. BIN
      src/assets/jh.png
  30. BIN
      src/assets/jt.png
  31. BIN
      src/assets/newbg.jpg
  32. BIN
      src/assets/sign.png
  33. BIN
      src/assets/sknj.jpg
  34. BIN
      src/assets/sknj.png
  35. BIN
      src/assets/szskl.jpg
  36. BIN
      src/assets/top.jpg
  37. BIN
      src/assets/xz.jpg
  38. BIN
      src/assets/zc.jpg
  39. 126 0
      src/components/banner/banner.vue
  40. 24 0
      src/components/breadcrumb/breadcrumb-item.vue
  41. 42 0
      src/components/breadcrumb/index.vue
  42. 97 0
      src/components/foots/index.vue
  43. 50 0
      src/components/heads/index.vue
  44. 111 0
      src/components/heads/nav/index.vue
  45. 43 0
      src/components/heads/nav/nav-item.vue
  46. 93 0
      src/components/leftmenu/index.vue
  47. 43 0
      src/components/leftmenu/nav-item.vue
  48. 107 0
      src/components/link/index.vue
  49. 202 0
      src/components/list/index.vue
  50. 152 0
      src/components/list/threeList.vue
  51. 47 0
      src/components/pagination.vue
  52. 135 0
      src/components/upwindow.vue
  53. 24 0
      src/main.js
  54. 42 0
      src/router/index.js
  55. 133 0
      src/store/index.js
  56. 199 0
      src/views/Home.vue
  57. 82 0
      src/views/details.vue
  58. 83 0
      src/views/leader.vue
  59. 140 0
      src/views/list.vue
  60. 59 0
      src/views/pages.vue
  61. 25 0
      vue.config.js

+ 3 - 0
.browserslistrc

@@ -0,0 +1,3 @@
+> 1%
+last 2 versions
+not dead

+ 5 - 0
.editorconfig

@@ -0,0 +1,5 @@
+[*.{js,jsx,ts,tsx,vue}]
+indent_style = space
+indent_size = 2
+trim_trailing_whitespace = true
+insert_final_newline = true

+ 2 - 0
.env

@@ -0,0 +1,2 @@
+# 窗口使用弹出还是抽屉 (dialog And drawer)
+VUE_APP_WINDOW=dialog

+ 1 - 0
.eslintignore

@@ -0,0 +1 @@
+public-path.js

+ 23 - 0
.eslintrc.js

@@ -0,0 +1,23 @@
+module.exports = {
+  root: true,
+  env: {
+    node: true
+  },
+  extends: [
+    'plugin:vue/essential',
+    '@vue/standard'
+  ],
+  parserOptions: {
+    parser: 'babel-eslint'
+  },
+  rules: {
+    'no-console': 0,
+    'no-debugger': 0,
+    'comma-dangle': [2, 'never'],
+    'no-extra-parens': 2,
+    'no-extra-semi': 2,
+    semi: [2, 'always'],
+    'space-before-function-paren': [0, 'always'],
+    eqeqeq: 0
+  }
+};

+ 23 - 0
.gitignore

@@ -0,0 +1,23 @@
+.DS_Store
+node_modules
+/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 @@
+# admin-code
+
+## 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/).

+ 5 - 0
babel.config.js

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

+ 51 - 0
lib/axios.js

@@ -0,0 +1,51 @@
+import axios from 'axios';
+import router from '../src/router/index';
+import { Message } from 'element-ui';
+// 添加请求拦截器
+axios.interceptors.request.use(function (config) {
+  const token = sessionStorage.getItem('token');
+  if (token) config.headers.authorization = token;
+  return config;
+}, function (error) {
+  // 对请求错误做些什么
+  return Promise.reject(error);
+});
+
+// 添加响应拦截器
+axios.interceptors.response.use(function (response) {
+  if (response.status == 401) {
+    // 返回登录处理
+    sessionStorage.removeItem('token');
+    router.replace('/frame/login');
+  }
+  if (response.status == 500) {
+    // 错误处理
+    response.data.data = { errcode: -1001, errmsg: '服务器错误' };
+  }
+  return response.data;
+}, function (error) {
+  Message.error('服务器错误');
+  // 对响应错误做点什么
+  return Promise.reject(error);
+});
+
+// 私有方法
+const request = async ({ url, method, params, data, headers }) => {
+  return await axios.request({ url, method, params, data, headers });
+};
+
+// 定义类
+class Point {
+  get(url, params, headers) {
+    return request({ url, params, headers });
+  }
+
+  post(url, data, params, headers) {
+    return request({ url, method: 'post', data, params, headers });
+  }
+
+  delete(url, params, headers) {
+    return request({ url, method: 'delete', params, headers });
+  }
+}
+export default new Point();

+ 17 - 0
lib/last.js

@@ -0,0 +1,17 @@
+// 树状结构构建插件
+const last = {
+  install (Vue) {
+    Vue.prototype.$last = function(options) {
+      const last = item => {
+        const isCode = options.menus.filter(j => j.code == item);
+        const children = this.menusall.filter(k => k.parentCode == isCode[0]?.code);
+        if (children.length > 0) return last(children[0]?.code);
+        return isCode[0];
+      };
+      const item = last(options.code);
+      sessionStorage.setItem('code', item.code);
+      return item;
+    };
+  }
+};
+export default last;

+ 17 - 0
lib/resChange.js

@@ -0,0 +1,17 @@
+const resChange = {
+  install (vue) {
+    vue.prototype.$resChange = function (data, msg) {
+      if (data.errcode !== 0) {
+        this.$message.error(data.errmsg);
+        return;
+      }
+      if (data.errcode == 0 || !data.errcode) {
+        this.$message({
+          message: msg,
+          type: 'success'
+        });
+      }
+    };
+  }
+};
+export default resChange;

+ 16 - 0
lib/setChildrenSession.js

@@ -0,0 +1,16 @@
+const setChildrenSession = {
+  install (Vue) {
+    Vue.prototype.$setChildrenSession = function(options) {
+      const children = items => {
+        const childrenList = options.menus.filter(j => items.code == j.parentCode).map(e => children(e));
+        if (childrenList.length > 0) {
+          return { ...items, children: childrenList };
+        }
+        return { ...items };
+      };
+      const list = options.menus.filter(e => e.code == options.iscode).map(e => children(e));
+      return list[0];
+    };
+  }
+};
+export default setChildrenSession;

+ 18 - 0
lib/setParentsetSession.js

@@ -0,0 +1,18 @@
+// 树状结构构建插件
+const setParentsetSession = {
+  install (Vue) {
+    Vue.prototype.$setParentsetSession = function(options) {
+      // 点击制造当前菜单父级项
+      const tree = item => {
+        const parent = options.menus.filter(j => item.parentCode === j.code).map(e => tree({ ...e, children: item }));
+        if (parent.length > 0) {
+          return { ...parent[0] };
+        }
+        return { ...item };
+      };
+      const childrens = tree(options.iscode);
+      sessionStorage.setItem('childrens', JSON.stringify(childrens));
+    };
+  }
+};
+export default setParentsetSession;

+ 26 - 0
lib/tree.js

@@ -0,0 +1,26 @@
+// 树状结构构建插件
+const tree = {
+  install (Vue) {
+    Vue.prototype.$tree = function(options) {
+      // 子级菜单构建函数
+      const children = items => {
+        /*
+        * 根据items.code与所有菜单parentCode比较
+        * 如果相等所有菜单当前项就是子级菜单
+        * 同时调用当前函数创建多级菜单
+        */
+        const childrenList = options.filter(j => items.code == j.parentCode).map(e => children(e));
+        // 如果当前子菜单项存在 就添加children属性
+        if (childrenList.length > 0) {
+          return { ...items, children: childrenList };
+        }
+        // 不存在就不添加
+        return { ...items };
+      };
+      // 过滤一级菜单并添加子级菜单
+      const list = options.filter(e => e.parentCode == 'null').map(e => children(e));
+      return list;
+    };
+  }
+};
+export default tree;

A különbségek nem kerülnek megjelenítésre, a fájl túl nagy
+ 28050 - 0
package-lock.json


+ 37 - 0
package.json

@@ -0,0 +1,37 @@
+{
+  "name": "www",
+  "version": "0.1.0",
+  "private": true,
+  "scripts": {
+    "serve": "vue-cli-service serve",
+    "build": "vue-cli-service build",
+    "lint": "vue-cli-service lint"
+  },
+  "dependencies": {
+    "axios": "^0.27.2",
+    "core-js": "^3.6.5",
+    "element-ui": "^2.15.9",
+    "moment": "^2.29.3",
+    "vue": "^2.6.11",
+    "vue-router": "^3.2.0",
+    "vuex": "^3.4.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-standard": "^5.1.2",
+    "babel-eslint": "^10.1.0",
+    "eslint": "^6.7.2",
+    "eslint-plugin-import": "^2.20.2",
+    "eslint-plugin-node": "^11.1.0",
+    "eslint-plugin-promise": "^4.2.1",
+    "eslint-plugin-standard": "^4.0.0",
+    "eslint-plugin-vue": "^6.2.2",
+    "sass": "^1.45.1",
+    "sass-loader": "^10.0.0",
+    "vue-template-compiler": "^2.6.11"
+  }
+}

BIN
public/favicon.ico


+ 17 - 0
public/index.html

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

+ 34 - 0
src/App.vue

@@ -0,0 +1,34 @@
+<template>
+  <div id="app">
+    <heads></heads>
+    <router-view :key="$route.fullPath" />
+    <foot></foot>
+    <upwindow></upwindow>
+  </div>
+</template>
+
+<script>
+import heads from './components/heads/index.vue';
+import foot from './components/foots/index.vue';
+import upwindow from './components/upwindow.vue';
+export default {
+  components: {
+    heads,
+    foot,
+    upwindow
+  },
+  computed: {},
+  data() {
+    return {};
+  },
+  mounted() {},
+  methods: {}
+};
+</script>
+
+<style lang="scss" scoped>
+#app {
+  width: 1920px;
+  height: 1080px;
+}
+</style>

BIN
src/assets/cg.jpg


BIN
src/assets/gg.png


BIN
src/assets/icon1.png


BIN
src/assets/icon2.png


BIN
src/assets/icon3.png


BIN
src/assets/icon4.png


BIN
src/assets/icon5.png


BIN
src/assets/icon6.png


+ 68 - 0
src/assets/index.scss

@@ -0,0 +1,68 @@
+html, body, #app{
+  width: 100%;
+  height: 100%;
+  margin: 0;
+  padding: 0;
+  background: #fff;
+}
+*,
+*::before,
+*::after {
+  box-sizing: border-box;
+}
+[class*=" el-icon-naf"], [class^=el-icon-naf] {
+  font-family: naf-icons!important;
+}
+::-webkit-scrollbar {/*滚动条整体样式*/
+  width: 8px;     /*高宽分别对应横竖滚动条的尺寸*/
+  height: 8px;
+}
+::-webkit-scrollbar-thumb {/*滚动条里面小方块*/
+  box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
+  border-radius: 5px;
+  background: hsla(220,4%,58%,.3)
+}
+::-webkit-scrollbar-track {/*滚动条里面轨道*/
+  box-shadow: inset 0 0 5px rgba(0,0,0,0.2);
+  background: #EDEDED;
+}
+.el-menu-item [class^=naf-icon],.el-submenu [class^=naf-icon] {
+  vertical-align: middle;
+  margin-right: 5px;
+  width: 24px;
+  text-align: center;
+  font-size: 18px;
+}
+.flex.el-tabs {
+  display: flex;
+  flex-direction: column;
+  .el-tabs__content {
+    flex: 1;
+  }
+}
+.el-message {
+  z-index: 9999 !important;
+}
+.el-transfer.compact {
+  .el-transfer-panel {
+    width: 160px;
+  }
+  .el-transfer__buttons {
+    padding: 0 10px;
+  }
+  .el-transfer__buttons > .el-transfer__button {
+    padding: 9px 5px;
+  }
+  .el-transfer__button + .el-transfer__button {
+    margin-left: 5px;
+  }
+  .el-transfer-panel__item {
+    width: 100%;
+  }
+}
+.el-tooltip__popper.is-dark {
+  opacity: 0.8;
+}
+.large-icon {
+  font-size: 2em;
+}

BIN
src/assets/jh.png


BIN
src/assets/jt.png


BIN
src/assets/newbg.jpg


BIN
src/assets/sign.png


BIN
src/assets/sknj.jpg


BIN
src/assets/sknj.png


BIN
src/assets/szskl.jpg


BIN
src/assets/top.jpg


BIN
src/assets/xz.jpg


BIN
src/assets/zc.jpg


+ 126 - 0
src/components/banner/banner.vue

@@ -0,0 +1,126 @@
+<template>
+  <div class="bannerBox">
+    <div class="imgbox">
+      <img :src="thumbnail" @click="btn">
+      <div class="el-icon-arrow-left left" @click="prev"></div>
+      <div class="el-icon-arrow-right right" @click="next"></div>
+      <div class="titlebox">
+        <span class="title">{{ title }}</span>
+        <ul>
+          <li v-for="(i, idx) in list && list.data" :key="idx" :class="{ item: i._id == liID }" @click="liClick(idx)"></li>
+        </ul>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import { mapState, mapActions } from 'vuex';
+export default {
+  components: {},
+  computed: {
+    ...mapState(['imgNewsList'])
+  },
+  data() {
+    return {
+      list: null,
+      liID: '',
+      index: 0,
+      thumbnail: '',
+      title: ''
+    };
+  },
+  async mounted() {
+    this.list = await this.contentsQuery({ bind: '021' });
+    this.liClick(0);
+  },
+  methods: {
+    ...mapActions(['contentsQuery']),
+    liClick(idx) {
+      this.index = idx;
+      this.liID = this.list?.data[idx]?._id;
+      this.thumbnail = this.list?.data[this.index]?.thumbnail;
+      this.title = this.list?.data[this.index]?.title;
+    },
+    prev() {
+      this.liClick(this.index - 1 <= 0 ? 0 : this.index - 1);
+    },
+    next() {
+      this.liClick(this.index + 1 >= this.list.data.length - 1 ? this.list.data.length - 1 : this.index + 1);
+    },
+    btn() {
+      // const router = this.$router.resolve(`/details/${this.liID}`);
+      // window.open(router.href, '_blank');
+      this.$router.push(`/details/${this.liID}`);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.bannerBox {
+  width: 30%;
+  // display: flex;
+  height: 300px;
+  overflow: hidden;
+  .imgbox {
+    width: 100%;
+    height: 100%;
+    position: relative;
+    img {
+      width: 100%;
+      height: 100%;
+      display: block;
+    }
+    .titlebox {
+      width: 100%;
+      height: 3em;
+      position: absolute;
+      bottom: 0;
+      left: 0;
+      background: #000;
+      opacity: 0.7;
+      display: flex;
+      .title {
+        color: #fff;
+        display: block;
+        width: 60%;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        line-height: 3em;
+        margin-left: 5%;
+      }
+      ul {
+        width: 35%;
+        display: flex;
+        margin-top: 5%;
+        li {
+          border-radius: 50%;
+          background: #fff;
+          width: 10px;
+          height: 10px;
+          margin-right: 10%;
+        }
+        .item {
+          background: #459aff !important;
+        }
+      }
+    }
+    .right, .left {
+      position: absolute;
+      top: 43%;
+      color: #fff;
+      font-size: 52px;
+      width: 10%;
+      background: #000;
+      opacity: 0.5;
+      text-align: center;
+    }
+    .right {
+      right: 0;
+    }
+    .left {
+      left: 0;
+    }
+  }
+}
+</style>

+ 24 - 0
src/components/breadcrumb/breadcrumb-item.vue

@@ -0,0 +1,24 @@
+<template>
+  <span class="breadcrumbbox" v-if="childrens && childrens !== null">
+    <el-breadcrumb-item>{{ childrens.name }}</el-breadcrumb-item>
+    <breadcrumb-item v-if="childrens.children" :childrens="childrens.children"></breadcrumb-item>
+  </span>
+</template>
+
+<script>
+export default {
+  name: 'breadcrumb-item',
+  components: {},
+  computed: {},
+  props: {
+    childrens: null
+  },
+  data() {
+    return {};
+  },
+  async mounted() {},
+  methods: {}
+};
+</script>
+
+<style lang="scss" scoped></style>

+ 42 - 0
src/components/breadcrumb/index.vue

@@ -0,0 +1,42 @@
+<template>
+  <div class="headsBreadcrumb">
+    <el-breadcrumb separator-class="el-icon-arrow-right">
+      <el-breadcrumb-item :to="{ path: '/' }">首页</el-breadcrumb-item>
+      <breadcrumbItem v-if="childrens !== null" :childrens="childrens"></breadcrumbItem>
+    </el-breadcrumb>
+  </div>
+</template>
+
+<script>
+import breadcrumbItem from './breadcrumb-item';
+export default {
+  components: {
+    breadcrumbItem
+  },
+  computed: {},
+  data() {
+    return {
+      childrens: null
+    };
+  },
+  mounted() {
+    this.getSession();
+  },
+  methods: {
+    getSession() {
+      this.childrens = JSON.parse(sessionStorage.getItem('childrens'));
+    },
+    reset() {
+      this.getSession();
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.headsBreadcrumb {
+  width: 65%;
+  margin: 0 auto;
+  margin-top: 2%;
+}
+</style>

+ 97 - 0
src/components/foots/index.vue

@@ -0,0 +1,97 @@
+<template>
+  <div class="foots">
+    <div class="main">
+      <el-image @click="signClick" class="sign" :src="url"></el-image>
+      <div class="info">
+        <p class="itemBox itemBox1">
+          <span class="item">{{ infos.title && infos.title.name }}:{{ infos.title && infos.title.value }}</span>
+          <span class="item">{{ infos.record && infos.record.name }}:{{ infos.record && infos.record.value }}</span>
+        </p>
+        <p class="itemBox itemBox2">
+          <span class="item">{{ infos.address && infos.address.name }}:{{ infos.address && infos.address.value }}</span>
+          <span class="item item2">{{ infos.postalCode && infos.postalCode.name }}:{{ infos.postalCode && infos.postalCode.value }}</span>
+          <span class="item">{{ infos.phone && infos.phone.name }}:{{ infos.phone && infos.phone.value }}</span>
+        </p>
+        <p class="itemBox itemBox3">
+          <span class="item">{{ infos.fax && infos.fax.name }}:{{ infos.fax && infos.fax.value }}</span>
+          <span class="item">{{ infos.email && infos.email.name }}:{{ infos.email && infos.email.value }}</span>
+        </p>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex';
+export default {
+  components: {},
+  computed: {
+    ...mapState(['websiteInfo']),
+    infos() {
+      const info = {};
+      this.websiteInfo.find(e => {
+        info[e.code] = e;
+      });
+      return info;
+    }
+  },
+  data() {
+    return {
+      url: require('../../assets/sign.png')
+    };
+  },
+  async mounted() {
+    await this.websiteQuery();
+  },
+  methods: {
+    ...mapActions(['websiteQuery']),
+    signClick() {
+      window.open('https://bszs.conac.cn/sitename?method=show&id=520E0E00E9F106D3E053022819AC70F2');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.foots {
+  width: 100%;
+  height: 300px;
+  background-color: #f5f5fb;
+  .main {
+    width: 70%;
+    display: flex;
+    margin: 0 auto;
+    padding-top: 5%;
+    .sign {
+      width: 10%;
+    }
+    .info {
+      width: 80%;
+      margin-left: 8%;
+      .itemBox {
+        display: flex;
+        .item {
+          margin: 0 10px;
+        }
+      }
+      .itemBox1 .item {
+        width: 50%;
+        text-align: center;
+      }
+      .itemBox2 {
+        .item {
+          width: 40%;
+          text-align: center;
+        }
+        .item2 {
+          width: 20%;
+        }
+      }
+      .itemBox3 .item {
+        width: 50%;
+        text-align: center;
+      }
+    }
+  }
+}
+</style>

+ 50 - 0
src/components/heads/index.vue

@@ -0,0 +1,50 @@
+<template>
+  <div class="heads">
+    <!-- <el-image class="top" :src="url"></el-image> -->
+    <el-carousel class="top" indicator-position="none" arrow="never">
+      <el-carousel-item class="imge" v-for="item in imgNewsList" :key="item._id">
+         <el-image class="imge" :src="item.img" :fit="'fit'"></el-image>
+      </el-carousel-item>
+    </el-carousel>
+    <navs></navs>
+  </div>
+</template>
+
+<script>
+import navs from './nav/index.vue';
+import { mapState, mapActions } from 'vuex';
+export default {
+  components: {
+    navs
+  },
+  computed: {
+    ...mapState(['imgNewsList'])
+  },
+  data() {
+    return {
+      url: require('../../assets/top.jpg')
+    };
+  },
+  async mounted() {
+    await this.imgNewsQuery();
+  },
+  methods: {
+    ...mapActions(['imgNewsQuery'])
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.top {
+  width: 100%;
+  height: 400px;
+  ::v-deep .el-carousel__container {
+    width: 100%;
+    height: 100%;
+  }
+  .imge {
+    width: 100%;
+    height: 100%;
+  }
+}
+</style>

+ 111 - 0
src/components/heads/nav/index.vue

@@ -0,0 +1,111 @@
+<template>
+  <div class="nav">
+    <el-menu unique-opened menu-trigger="hover" background-color="#007ce2" text-color="#fff" active-text-color="#ffd200" :default-active="activeIndex" mode="horizontal">
+      <div class="menuitem" v-for="(item, index) in menus" :key="index">
+        <nav-item :items="item" @itemClick="handleSelect"></nav-item>
+      </div>
+    </el-menu>
+  </div>
+</template>
+
+<script>
+import navItem from './nav-item';
+import { mapState, mapActions } from 'vuex';
+export default {
+  components: {
+    navItem
+  },
+  computed: {
+    ...mapState(['menusall']),
+    menus() {
+      const menus = this.$tree(this.menusall) ?? [];
+      return menus;
+    }
+  },
+  data() {
+    return {
+      activeIndex: '00'
+    };
+  },
+  async mounted() {
+    await this.menusQueryAll();
+  },
+  methods: {
+    ...mapActions(['menusQueryAll']),
+    async handleSelect(env) {
+      // 获取最后一级菜单
+      const last = await this.setactiveIndex(env);
+      // 赋值当前选选个
+      this.activeIndex = last?.code;
+      // 缓存写入当前一例菜单
+      this.$setParentsetSession({ menus: this.menusall, iscode: last });
+      // 写入当前菜单编码
+      sessionStorage.setItem('code', this.activeIndex);
+      // 如果编码 = 00 进入主页
+      if (env.code == '00') {
+        this.$router.push('/www');
+        return;
+      }
+      // 如果编码 = 012(领导)进入领导页
+      if (env.code == '012') {
+        this.$router.push('/leader');
+        return;
+      }
+      // 获取当前一例的顶级菜单编码
+      const parentCode = env.code.substring(0, 2);
+      // 类型为1(栏目)进入列表页
+      if (env.type == '1') this.$router.push(`/list/${this.activeIndex}?parentCode=${parentCode}`);
+      // 类型为2(单页)进入单页页面
+      if (env.type == '2') this.$router.push(`/pages/${env.code}`);
+    },
+    // 递归最后的菜单编码
+    async setactiveIndex (e) {
+      if (e.children) return await this.setactiveIndex(e.children[0]);
+      return e;
+    },
+    // 重置当前选项
+    setIndex() {
+      const code = sessionStorage.getItem('code');
+      this.activeIndex = code;
+    }
+  },
+  // 路由改变重置当前选项
+  watch: {
+    $route(to, from) {
+      this.setIndex();
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.nav {
+  width: 100%;
+  background: #007ce2;
+  padding-top: 5px;
+  margin-top: -5px;
+  .el-menu {
+    width: 70%;
+    margin: 0 auto;
+    display: flex;
+    border: none;
+    .menuitem {
+      // width: 100%;
+      .menubox {
+        .el-submenu .title {
+          text-align: center;
+          font-size: 16px;
+          font-weight: 700;
+          display: block;
+          width: 100%;
+        }
+      }
+    }
+    .el-menu-item {
+      font-size: 16px;
+      font-weight: 700;
+      text-align: center;
+    }
+  }
+}
+</style>

+ 43 - 0
src/components/heads/nav/nav-item.vue

@@ -0,0 +1,43 @@
+<template>
+  <div class="menubox">
+    <el-menu-item v-if="!items.children" :index="`${items.code}`" @click="btn(items)">{{ items.name }}</el-menu-item>
+    <el-submenu v-else :index="items.code">
+      <template slot="title">
+        <span @click="btn(items)" class="title">{{ items.name }}</span>
+      </template>
+      <nav-item v-for="(i, idx) in items.children" :key="idx" :items="i" @itemClick="btn"></nav-item>
+    </el-submenu>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'nav-item',
+  props: {
+    items: { type: Object, default: () => {} }
+  },
+  components: {},
+  computed: {},
+  data() {
+    return {};
+  },
+  async mounted() {},
+  methods: {
+    btn(item) {
+      this.$emit('itemClick', item);
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.title {
+  width: 100%;
+  font-size: 16px;
+  font-weight: 700;
+}
+.el-menu-item {
+  font-size: 16px;
+  font-weight: 700;
+}
+</style>

+ 93 - 0
src/components/leftmenu/index.vue

@@ -0,0 +1,93 @@
+<template>
+  <div class="nav">
+    <div class="titlename">{{ menuTree && menuTree.name }}</div>
+    <el-menu unique-opened menu-trigger="hover" background-color="#b9c1d7" text-color="#000" active-text-color="#ffd200" :default-active="activeIndex" mode="vertical">
+      <div class="menuitem" v-for="(item, index) in menuTree.children" :key="index">
+        <nav-item :items="item" @itemClick="handleSelect"></nav-item>
+      </div>
+    </el-menu>
+  </div>
+</template>
+
+<script>
+import navItem from './nav-item';
+import { mapState } from 'vuex';
+export default {
+  props: {
+    menuTree: { type: Object, default: () => {} }
+  },
+  components: {
+    navItem
+  },
+  computed: {
+    ...mapState(['menusall'])
+  },
+  data() {
+    return {
+      activeIndex: '00'
+    };
+  },
+  async mounted() {},
+  methods: {
+    handleSelect(item) {
+      // 获取当前一例的顶级菜单编码
+      const parentCode = item.code.substring(0, 2);
+      // 类型为1(栏目)进入列表页
+      if (item.type == '1') this.$router.push(`/list/${item.code}?parentCode=${parentCode}`);
+      // 类型为2(单页)进入单页页面
+      if (item.type == '2') this.$router.push(`/pages/${item.code}`);
+      // 缓存写入当前一例菜单
+      this.$setParentsetSession({ menus: this.menusall, iscode: item });
+      // 缓存写入当前菜单编码
+      sessionStorage.setItem('code', item.code);
+    },
+    setIndex() {
+      this.activeIndex = sessionStorage.getItem('code');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.nav {
+  width: 100%;
+  background: #fff;
+  padding-top: 5px;
+  margin-top: -5px;
+  .titlename {
+    font-size: 36px;
+    font-weight: 700;
+    text-align: center;
+    // background: #007ce2;
+    background: url('../../assets/newbg.jpg');
+    background-size: 100% 100%;
+    color: #fff;
+    width: 70%;
+    margin: 0 auto;
+    line-height: 3em;
+  }
+  .el-menu {
+    width: 70%;
+    margin: 0 auto;
+    // display: flex;
+    border: none;
+    .menuitem {
+      // width: 100%;
+      .menubox {
+        .el-submenu .title {
+          text-align: center;
+          font-size: 16px;
+          font-weight: 700;
+          display: block;
+          width: 100%;
+        }
+      }
+    }
+    .el-menu-item {
+      font-size: 16px;
+      font-weight: 700;
+      text-align: center;
+    }
+  }
+}
+</style>

+ 43 - 0
src/components/leftmenu/nav-item.vue

@@ -0,0 +1,43 @@
+<template>
+  <div class="menubox">
+    <el-menu-item v-if="!items.children" :index="`${items.code}`" @click="btn(items)">{{ items && items.name }}</el-menu-item>
+    <el-submenu v-else :index="items.code">
+      <template slot="title">
+        <span class="title">{{ items && items.name }}</span>
+      </template>
+      <nav-item v-for="(i, idx) in items.children" :key="idx" :items="i" @itemClick="btn"></nav-item>
+    </el-submenu>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'nav-item',
+  props: {
+    items: { type: Object, default: () => {} }
+  },
+  components: {},
+  computed: {},
+  data() {
+    return {};
+  },
+  async mounted() {},
+  methods: {
+    btn(item) {
+      this.$emit('itemClick', item);
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.title {
+  width: 100%;
+  font-size: 16px;
+  font-weight: 700;
+}
+.el-menu-item {
+  font-size: 16px;
+  font-weight: 700;
+}
+</style>

+ 107 - 0
src/components/link/index.vue

@@ -0,0 +1,107 @@
+<template>
+  <div class="links">
+    <div class="title">
+      <el-image class="titleImg" :src="url"></el-image>
+      <h2 class="titleText">友情链接</h2>
+    </div>
+    <div class="linkbox">
+      <div class="iinkItem" :class="{ item: itemId == item._id }" v-for="(item, index) in linkTypeList" :key="index" @click="itemClick(item)">
+        {{ item.name }}
+        <div class="sj"></div>
+      </div>
+    </div>
+    <div class="itemBox">
+      <a v-for="(item, index) in linkList" :key="index" :href="item.url">{{ item.title }}</a>
+    </div>
+  </div>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex';
+export default {
+  components: {},
+  computed: {
+    ...mapState(['linkTypeList', 'linkList'])
+  },
+  data() {
+    return {
+      itemId: null,
+      url: require('../../assets/icon6.png')
+    };
+  },
+  async mounted() {
+    const res = await this.linkTypeQuery();
+    if (res.errcode == 0) this.itemClick(this.linkTypeList[0]);
+  },
+  methods: {
+    ...mapActions(['linkTypeQuery', 'linkQuery']),
+    async itemClick(e) {
+      this.itemId = e._id;
+      await this.linkQuery({ code: e.code });
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.links {
+  width: 70%;
+  margin: 0 auto;
+  .title {
+    display: flex;
+    margin: 15px 0;
+    border-bottom: 5px solid #007ce2;
+
+    .titleImg {
+      width: 2%;
+      height: 5%;
+      margin-top: 1%;
+    }
+
+    .titleText {
+      margin: 0 15px;
+      color: #007ce2;
+      width: 78%;
+      line-height: 3em;
+    }
+  }
+  .linkbox {
+    display: flex;
+    width: 100%;
+    .iinkItem {
+      width: 20%;
+      text-align: center;
+      line-height: 3em;
+      cursor: pointer;
+    }
+    .item {
+      position: relative;
+      border-radius: 8px;
+      color: #fff;
+      background-color: #007ce2;
+      .sj {
+        width: 0;
+        height: 0;
+        border-width: 15px;
+        border-style: solid;
+        border-color: #007ce2 transparent transparent transparent;
+        position: absolute;
+        left: 45%;
+        bottom: -60%;
+      }
+    }
+  }
+  .itemBox {
+    width: 100%;
+    min-height: 200px;
+    border: 1px solid #999;
+    margin: 2% 0;
+    a {
+      color: #999;
+      line-height: 2em;
+      margin-left: 10px;
+      text-decoration:none
+    }
+  }
+}
+</style>

+ 202 - 0
src/components/list/index.vue

@@ -0,0 +1,202 @@
+<template>
+  <div class="lists">
+    <div class="title">
+      <el-image class="titleImg" :src="imgUrl"></el-image>
+      <h2 class="titleText">{{ title }}</h2>
+      <div class="more" @click="moreClick">
+        <span class="el-icon-plus plus"></span>
+        更多
+      </div>
+    </div>
+    <el-tabs v-model="activeName" :type="tabsType" @tab-click="handleClick" :class="{ borderCard: tabsType == 'border-card' }">
+      <el-tab-pane v-for="(item, index) in tabsList" :key="index" :label="item.title" :name="item.code">
+        <div class="isimg" v-if="isImg" @click="itemClick(item)">
+          <el-image class="isimg-img" :src="items.item && items.item.thumbnail"></el-image>
+          <div class="isimg-text">
+            <h4 class="isimg-title">{{ items.item && items.item.title }}</h4>
+            <div class="isimg-describe">{{ items.item && items.item.describe }}</div>
+          </div>
+        </div>
+        <div class="isList" :class="{ borderList: tabsType == 'border-card' }" v-for="(i, index) in items.list" :key="index"  @click="itemClick(i)">
+          <el-image class="isList-img" :src="icon"></el-image>
+          <h4 class="isList-title">{{ i.title }}</h4>
+          <div class="isList-date">{{ i.date | dates }}</div>
+        </div>
+      </el-tab-pane>
+    </el-tabs>
+  </div>
+</template>
+
+<script>
+import moment from 'moment';
+import { mapState } from 'vuex';
+export default {
+  props: {
+    // 标题
+    title: { type: String, default: '社团建设' },
+    // 标题图片
+    imgUrl: { type: String, default: '' },
+    // tabs样式类型
+    tabsType: { type: String, default: 'border-card' },
+    // tabs标签标题
+    tabsList: { type: Array, default: () => [] },
+    // 内容数据
+    data: { type: Array, default: () => [] },
+    // 是否需要图片
+    isImg: { type: Boolean, default: false },
+    parentCode: { type: String, default: null }
+  },
+  components: {},
+  computed: {
+    ...mapState(['menusall']),
+    // 如果使用图片 默认加载第一条的
+    items() {
+      if (this.isImg) {
+        const item = this.data[0];
+        const list = this.data.slice(1, this.data.length);
+        return { item, list };
+      }
+      return { list: this.data };
+    }
+  },
+  data() {
+    return {
+      // 当前选项名
+      activeName: null,
+      icon: require('../../assets/jh.png'),
+      code: null
+    };
+  },
+  mounted() {
+    this.activeName = this.tabsList[0]?.code;
+    // this.handleClick(this.tabsList[0]);
+  },
+  methods: {
+    handleClick(e) {
+      // 菜单编码
+      this.code = e.name || e.code;
+      this.$emit('tabClick', { name: this.code });
+    },
+    itemClick(e) {
+      this.$router.push(`/details/${e._id}`);
+    },
+    async moreClick() {
+      // 获取最后一级菜单
+      const item = this.$last({ menus: this.menusall, code: this.code });
+      // 缓存写入当前一例菜单
+      this.$setParentsetSession({ menus: this.menusall, iscode: item });
+      if (this.parentCode !== null) this.$router.push(`/list/${this.code}?parentCode=${this.parentCode}`);
+      if (this.parentCode == null) this.$router.push(`/list/${this.code}`);
+    }
+  },
+  filters: {
+    dates(e) {
+      return moment(e).format('MM-DD');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.borderCard {
+  border-top: 5px solid #007ce2;
+}
+.borderList {
+  border-bottom: 1px dashed #666;
+}
+.lists {
+  width: 30%;
+  .title {
+    display: flex;
+    margin: 5px 0;
+    .titleImg {
+      width: 5%;
+      height: 5%;
+      margin-top: 1%;
+    }
+    .titleText {
+      width: 60%;
+      margin: 0 15px;
+      color: #007ce2;
+    }
+    .more {
+      width: 35%;
+      text-align: right;
+      color: #999;
+      cursor: pointer;
+      margin-right: 3%;
+      .plus {
+        width: 15px;
+        height: 15px;
+        border-radius: 50%;
+        text-align: center;
+        background: #999;
+        color: #fff;
+      }
+    }
+  }
+  .isimg {
+    display: flex;
+    width: 100%;
+    margin-top: 13px;
+    cursor: pointer;
+    .isimg-img {
+      display: block;
+      width: 30%;
+      margin-right: 5%;
+      height: 100px;
+    }
+    .isimg-text {
+      width: 65%;
+      .isimg-title {
+        margin: 0;
+        margin-bottom: 5px;
+        width: 100%;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        white-space: nowrap;
+        line-height: 2em;
+        font-size: 1.2em;
+      }
+      .isimg-describe {
+        width: 100%;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        display: -webkit-box;
+        -webkit-line-clamp: 2;
+        -webkit-box-orient: vertical;
+        color: #999;
+        font-size: 14px;
+      }
+    }
+  }
+  .isList {
+    display: flex;
+    margin: 10px 0;
+    .isList-img {
+      width: 10px;
+      height: 10px;
+      display: block;
+      margin-top: 3%;
+    }
+    .isList-title {
+      width: 70%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      margin: 0;
+      margin-right: 5%;
+      margin-left: 3%;
+      cursor: pointer;
+      line-height: 3em;
+      font-size: 1.2em;
+    }
+    .isList-date {
+      color: #999;
+      width: 20%;
+      text-align: right;
+      line-height: 2em;
+    }
+  }
+}
+</style>

+ 152 - 0
src/components/list/threeList.vue

@@ -0,0 +1,152 @@
+<template>
+  <div class="lists" :style="{ backgroundColor: isImg ? '#f4f4f4' : '', border: isImg ? '1px solid #dfdfdf' : 'none' }">
+    <el-image class="titleImg" :src="imgUrl" v-if="isImg"></el-image>
+    <div class="title" v-else>
+      <el-image class="titleImg" :src="imgUrl"></el-image>
+      <h2 class="titleText">{{ title }}</h2>
+      <div class="more" @click="moreClick">
+        <span class="el-icon-plus plus"></span>
+        更多
+      </div>
+    </div>
+    <div class="isList" v-for="(i, index) in data" :key="index"  @click="itemClick(i)">
+      <el-image class="isList-img" :src="icon"></el-image>
+      <h4 class="isList-title">{{ i.title }}</h4>
+      <span class="isList-date">{{ i.date | dates }}</span>
+    </div>
+    <div class="more more2" @click="moreClick" v-if="isImg">
+      <span class="el-icon-plus plus"></span>
+      更多
+    </div>
+  </div>
+</template>
+
+<script>
+import moment from 'moment';
+import { mapState } from 'vuex';
+export default {
+  props: {
+    // 标题
+    title: { type: String, default: '通知公告' },
+    // 标题图片
+    imgUrl: { type: String, default: '' },
+    // 内容数据
+    data: { type: Array, default: () => [] },
+    isImg: { type: Boolean, default: true },
+    code: { type: String, default: '' },
+    parentCode: { type: String, default: null }
+  },
+  components: {},
+  computed: {
+    ...mapState(['menusall'])
+  },
+  data() {
+    return {
+      icon: require('../../assets/jt.png')
+    };
+  },
+  mounted() {},
+  methods: {
+    itemClick(e) {
+      this.$router.push(`/details/${e._id}`);
+    },
+    async moreClick() {
+      // 获取最后一级菜单
+      const item = this.$last({ menus: this.menusall, code: this.code });
+      // 缓存写入当前一例菜单
+      this.$setParentsetSession({ menus: this.menusall, iscode: item });
+      if (this.parentCode !== null) this.$router.push(`/list/${this.code}?parentCode=${this.parentCode}`);
+      if (this.parentCode == null) this.$router.push(`/list/${this.code}`);
+    },
+    // 递归第一级最后的菜单编码
+    async setactiveIndex (code) {
+      const isCode = this.menusall.filter(j => j.code == code);
+      const children = this.menusall.filter(k => k.parentCode == isCode[0]?.code);
+      if (children.length > 0) return this.setactiveIndex(children[0]?.code);
+      return isCode[0];
+    }
+  },
+  filters: {
+    dates(e) {
+      return moment(e).format('MM-DD');
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.lists {
+  // margin-left: 5%;
+  width: 30%;
+  .titleImg {
+    display: block;
+    width: 100%;
+    height: 20%;
+  }
+  .title {
+    display: flex;
+    // margin: 15px 0;
+    .titleImg {
+      width: 8%;
+      height: 5%;
+      margin-top: 1%;
+    }
+
+    .titleText {
+      margin: 0 15px;
+      color: #007ce2;
+      width: 78%;
+    }
+  }
+  .more {
+    width: 35%;
+    text-align: right;
+    color: #999;
+    cursor: pointer;
+    margin-right: 3%;
+    .plus {
+      width: 15px;
+      height: 15px;
+      border-radius: 50%;
+      text-align: center;
+      background: #999;
+      color: #fff;
+    }
+  }
+  .more2 {
+    width: 18%;
+    margin-bottom: 2%;
+  }
+  .isList {
+    width: 90%;
+    display: flex;
+    margin: 10px auto;
+    border-bottom: 1px dashed #999;
+    .isList-img {
+      width: 10px;
+      height: 10px;
+      display: block;
+      margin-top: 6%;
+    }
+    .isList-title {
+      width: 80%;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      white-space: nowrap;
+      margin: 0;
+      margin-right: 5%;
+      margin-left: 3%;
+      cursor: pointer;
+      line-height: 3em;
+      font-size: 1.2em;
+    }
+    .isList-date {
+      font-size: 1em;
+      color: #999;
+      line-height: 3.5em;
+      text-align: right;
+      width: 20%;
+    }
+  }
+}
+</style>

+ 47 - 0
src/components/pagination.vue

@@ -0,0 +1,47 @@
+<template>
+  <div class="pagination-box">
+    <el-pagination
+      hide-on-single-page
+      @current-change="handleCurrentChange"
+      :current-page="currentPage"
+      :page-size="pageSize"
+      layout="total, prev, pager, next"
+      :total="total">
+    </el-pagination>
+  </div>
+</template>
+<script>
+export default {
+  components: {},
+  props: {
+    total: { type: Number, default: 0 },
+    pageSize: { type: Number, default: 10 }
+  },
+  data() {
+    return {
+      currentPage: 1
+    };
+  },
+  computed: {},
+  mounted() {},
+  methods: {
+    handleCurrentChange(e) {
+      this.currentPage = e > 0 ? e - 1 : 0;
+      this.$emit('query', { paging: { page: this.currentPage, size: this.pageSize } });
+    },
+    resetPage(e) {
+      if (e == -1) {
+        this.currentPage = 0;
+      } else {
+        this.currentPage = Math.ceil(this.total / this.pageSize);
+      }
+      this.$emit('query', { page: this.currentPage, size: this.pageSize });
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.pagination-box {
+  margin-top: 10px;
+}
+</style>

+ 135 - 0
src/components/upwindow.vue

@@ -0,0 +1,135 @@
+<template>
+  <div
+    v-if="show"
+    @mouseover="mouseover"
+    @mouseout="mouseout"
+    class="box"
+    :style="{ top: top + 'px', left: left + 'px', backgroundImage: `${img}`}"
+  >
+    <span class="close el-icon-close" @click="close"></span>
+    <div class="text">{{ upwindow && upwindow[0] && upwindow[0].title }}</div>
+  </div>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex';
+export default {
+  name: 'MoveWindow',
+  data() {
+    return {
+      show: true, // 是否展现飘窗
+      stepX: 1, // 水平方向的步长
+      stepY: 1, // 垂直方向的步长
+      timer: null, // 定时器
+      maxTop: 0, // 最大的 top 值
+      maxLeft: 0, // 最大的 left 值
+      top: 0,
+      left: 0,
+      img: ''
+    };
+  },
+  computed: {
+    ...mapState(['upwindow'])
+  },
+  async mounted() {
+    this.init();
+    const res = await this.upWindewQuery();
+    this.img = `url(${res.data[0]?.img})`;
+    if (res.data.length < 1) this.show = false;
+  },
+  beforeDestroy() {
+    // dom 销毁前清除定时器
+    clearInterval(this.timer);
+  },
+  methods: {
+    ...mapActions(['upWindewQuery']),
+    // 初始化飘窗规则
+    init() {
+      // 设置最大的top和left值:根元素可视区域宽高 - 飘窗的宽高 - 边距
+      this.maxTop = document.documentElement.clientHeight - 150 - 20;
+      this.maxLeft = document.documentElement.clientWidth - 200 - 20;
+
+      // 设置 top 和 left 的初始值
+      this.top = 0;
+      this.left = 0;
+
+      // 创建定时器前清除定时器,避免类似在 onresize 中调用 init() 时,产生多个定时器
+      clearInterval(this.timer);
+      this.timer = setInterval(() => {
+        this.move();
+      }, 20);
+
+      this.onresize();
+    },
+    // 移动函数
+    move() {
+      if (this.top >= this.maxTop || this.top < 0) {
+        this.stepY = -this.stepY;
+      }
+      if (this.left >= this.maxLeft || this.left < 0) {
+        this.stepX = -this.stepX;
+      }
+
+      this.top += this.stepY;
+      this.left += this.stepX;
+    },
+    // 鼠标悬浮在飘窗时停止移动
+    mouseover() {
+      clearInterval(this.timer);
+    },
+    // 鼠标离开飘窗时恢复移动
+    mouseout() {
+      clearInterval(this.timer);
+      this.timer = setInterval(() => {
+        this.move();
+      }, 20);
+    },
+    // 关闭飘窗
+    close() {
+      clearInterval(this.timer);
+      this.show = false;
+    },
+    // 窗口大小调整时重置飘窗规则
+    onresize() {
+      const that = this;
+      window.onresize = function () {
+        that.init();
+      };
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.box {
+  background-size: 100% 100%;
+  width: 200px;
+  height: 200px;
+  border-radius: 5px;
+  position: fixed;
+  text-align: left;
+  padding: 10px;
+  color: #ffffff;
+  top: 0;
+  left: 0;
+  z-index: 999;
+  .close {
+    text-align: right;
+    position: absolute;
+    right: 10px;
+    top: 10px;
+    color: #fff;
+    cursor: pointer;
+    border-radius: 50%;
+    border: 1px solid #fff;
+  }
+
+  .text {
+    width: 80%;
+    margin: 0 auto;
+    margin-top: 50px;
+    font-size: 18px;
+    text-align: center;
+  }
+}
+</style>

+ 24 - 0
src/main.js

@@ -0,0 +1,24 @@
+import Vue from 'vue';
+import App from './App.vue';
+import router from './router';
+import store from './store';
+import tree from '@lib/tree.js';
+import setParentsetSession from '@lib/setParentsetSession.js';
+import setChildrenSession from '@lib/setChildrenSession.js';
+import last from '@lib/last.js';
+import resChange from '@lib/resChange.js';
+import ElementUI from 'element-ui';
+import 'element-ui/lib/theme-chalk/index.css';
+import './assets/index.scss';
+Vue.config.productionTip = false;
+Vue.use(ElementUI);
+Vue.use(tree);
+Vue.use(resChange);
+Vue.use(setParentsetSession);
+Vue.use(setChildrenSession);
+Vue.use(last);
+new Vue({
+  router,
+  store,
+  render: h => h(App)
+}).$mount('#app');

+ 42 - 0
src/router/index.js

@@ -0,0 +1,42 @@
+import Vue from 'vue';
+import VueRouter from 'vue-router';
+
+Vue.use(VueRouter);
+
+const routes = [
+  {
+    path: '/www',
+    name: 'home',
+    component: () => import('../views/Home.vue')
+  },
+  {
+    path: '/leader',
+    name: 'leader',
+    component: () => import('../views/leader.vue')
+  },
+  {
+    path: '/details/:id',
+    name: 'details',
+    component: () => import('../views/details.vue')
+  },
+  {
+    path: '/pages/:code',
+    name: 'page',
+    component: () => import('../views/pages.vue')
+  },
+  {
+    path: '/list/:code',
+    name: 'list',
+    component: () => import('../views/list.vue')
+  }
+];
+
+const router = new VueRouter({
+  mode: 'history',
+  routes
+});
+const originalPush = VueRouter.prototype.push;
+VueRouter.prototype.push = function push (location) {
+  return originalPush.call(this, location).catch(err => err);
+};
+export default router;

+ 133 - 0
src/store/index.js

@@ -0,0 +1,133 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import $axios from '@lib/axios.js';
+Vue.use(Vuex);
+const api = {
+  menusQuery: '/api/cms/menus/query',
+  websiteQuery: '/api/cms/toconfig/query',
+  imgNewsQuery: '/api/cms/imgnews/query',
+  contentsQuery: '/api/cms/contents/query',
+  linkTypeQuery: '/api/cms/imgtype/query',
+  contentsFetch: '/api/cms/contents/fetch',
+  pagesFetch: '/api/cms/pages/fetch',
+  imgNewsFetch: '/api/cms/imgnews/fetch'
+};
+
+const state = () => ({
+  menusall: [],
+  websiteInfo: [],
+  imgNewsList: [],
+  linkTypeList: [],
+  linkList: [],
+  contentList: [],
+  listTotal: 0,
+  contentsItem: null,
+  pagesItem: null,
+  upwindow: []
+});
+
+const actions = {
+  async menusQueryNoShow ({ commit }) {
+    const res = await $axios.get(api.menusQuery);
+    return res.data;
+  },
+  async menusQueryAll ({ commit }) {
+    const res = await $axios.get(api.menusQuery, { isshow: true });
+    commit('menusQueryAll', res);
+    return res;
+  },
+  async websiteQuery ({ commit }) {
+    const res = await $axios.get(api.websiteQuery);
+    commit('websiteQuery', res);
+    return res;
+  },
+  async imgNewsQuery ({ commit }) {
+    const res = await $axios.get(api.imgNewsQuery);
+    commit('imgNewsQuery', res);
+    return res;
+  },
+  async contentsQuery ({ commit }, { parentCode, bind, limit }) {
+    const res = await $axios.get(api.contentsQuery, { parentCode, bind, skip: 0, limit: limit || 5 });
+    return res;
+  },
+  async linkTypeQuery ({ commit }) {
+    const res = await $axios.get(api.linkTypeQuery);
+    commit('linkTypeQuery', res);
+    return res;
+  },
+  async linkQuery ({ commit }, { code }) {
+    const res = await $axios.get(api.imgNewsQuery, { bind: code });
+    commit('linkQuery', res);
+    return res;
+  },
+  async contentsList ({ commit }, { filter, paging } = {}) {
+    const res = await $axios.get(api.contentsQuery, { ...filter, skip: paging.page, limit: paging.size });
+    commit('contentsList', res);
+    return res;
+  },
+  async contentsFetch ({ commit }, payload) {
+    const res = await $axios.get(api.contentsFetch, payload);
+    commit('contentsFetch', res);
+    return res;
+  },
+  async imgNewsFetch ({ commit }, payload) {
+    const res = await $axios.get(api.imgNewsFetch, payload);
+    commit('imgNewsFetch', res);
+    return res;
+  },
+  async pagesFetch ({ commit }, payload) {
+    const res = await $axios.get(api.pagesFetch, payload);
+    commit('pagesFetch', res);
+    return res;
+  },
+  async upWindewQuery ({ commit }) {
+    const res = await $axios.get(api.imgNewsQuery, { isshow: true, bind: 'window' });
+    commit('upWindewQuery', res);
+    return res;
+  },
+  async leaderList ({ commit }, payload) {
+    const res = await $axios.get(api.contentsQuery, payload);
+    return res.data;
+  }
+};
+
+const mutations = {
+  menusQueryAll(state, payload) {
+    state.menusall = payload.data;
+  },
+  websiteQuery(state, payload) {
+    state.websiteInfo = payload.data;
+  },
+  imgNewsQuery(state, payload) {
+    state.imgNewsList = payload.data.filter(e => e.bind == 'banner');
+  },
+  upWindewQuery(state, payload) {
+    state.upwindow = payload.data;
+  },
+  linkTypeQuery(state, payload) {
+    state.linkTypeList = payload.data.filter(e => e.code.indexOf('link') !== -1);
+  },
+  linkQuery(state, payload) {
+    state.linkList = payload.data;
+  },
+  contentsList(state, payload) {
+    state.contentList = payload.data;
+    state.listTotal = payload.total;
+  },
+  contentsFetch(state, payload) {
+    state.contentsItem = payload.data;
+  },
+  imgNewsFetch(state, payload) {
+    state.contentsItem = payload.data;
+  },
+  pagesFetch(state, payload) {
+    state.pagesItem = payload.data;
+  }
+};
+
+export default new Vuex.Store({
+  state,
+  mutations,
+  actions,
+  modules: {}
+});

+ 199 - 0
src/views/Home.vue

@@ -0,0 +1,199 @@
+<template>
+  <div class="home">
+    <div class="listBox">
+      <banner class="lists"></banner>
+      <threeList parentCode="02" class="listItem" :imgUrl="newsImgUrl" :data="news" :isImg="false" title="社科要闻" code="021"></threeList>
+      <threeList parentCode="10" :imgUrl="threeImggg" :data="threeDatagg" :isImg="false" title="通知公告" code="101"></threeList>
+    </div>
+    <div class="listBox">
+      <lists parentCode="03" title="社会组织建设" code="031" :imgUrl="corporationimgUrl" :tabsList="corporationList" :tabsType="null" :data="corporation" isImg @tabClick="btnClick({ ...$event, type: 'corporation' })"></lists>
+      <lists parentCode="04" title="社会科学普及" class="listItem" :imgUrl="prizeImgUrl" :tabsList="prizeList" :tabsType="null" :data="prize" isImg @tabClick="btnClick({ ...$event, type: 'prize' })"></lists>
+      <threeList parentCode="07" :imgUrl="threeImg" :data="threeData" code="07"></threeList>
+    </div>
+    <div class="listBox">
+      <lists parentCode="05" title="智库基金项目" :imgUrl="buildsImgUrl" :tabsList="buildsList" tabsType="border-card" :data="builds" @tabClick="btnClick({ ...$event, type: 'builds', limit: 6 })"></lists>
+      <lists parentCode="06" title="社会科学评奖" class="listItem" :imgUrl="universalImgUrl" :tabsList="universalList" tabsType="border-card" :data="universal" @tabClick="btnClick({ ...$event, type: 'universal', limit: 6 })"></lists>
+      <div class="xzbox">
+        <threeList parentCode="09" class="three" :imgUrl="threeImgzc" :data="threeDatazc"></threeList>
+        <el-image :src="xzUrl" class="xz" @click="imgClick('11', '11')"></el-image>
+      </div>
+    </div>
+    <div class="listBox">
+      <el-image :src="sknj" class="sknj"></el-image>
+      <el-image :src="szsklUrl" class="szskl" @click="imgClick('08', '08')"></el-image>
+    </div>
+    <links></links>
+  </div>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex';
+import banner from '../components/banner/banner.vue';
+import lists from '../components/list/index';
+import threeList from '../components/list/threeList';
+import links from '../components/link/index';
+export default {
+  name: 'Home',
+  components: {
+    banner,
+    lists,
+    threeList,
+    links
+  },
+  computed: {
+    ...mapState(['menusall'])
+  },
+  data() {
+    return {
+      sknj: require('../assets/sknj.jpg'),
+      xzUrl: require('../assets/xz.jpg'),
+      szsklUrl: require('../assets/szskl.jpg'),
+      // 社团
+      corporation: [],
+      corporationimgUrl: require('../assets/icon1.png'),
+      corporationList: [
+        { title: '社会组织党建', code: '031' },
+        { title: '社会组织活动', code: '032' }
+        // { title: '社会组织简介', code: '033' }
+      ],
+      // 社科普及
+      prize: [],
+      prizeImgUrl: require('../assets/icon3.png'),
+      prizeList: [
+        { title: '科普讲座', code: '041' },
+        { title: '科普活动', code: '042' },
+        { title: '科普周', code: '043' }
+      ],
+      // 社科奖项
+      universal: [],
+      universalImgUrl: require('../assets/icon3.png'),
+      universalList: [
+        { title: '评奖信息', code: '061' },
+        { title: '获奖成果', code: '062' },
+        { title: '网上申报', code: '064' }
+      ],
+      // 智库建设
+      builds: [],
+      buildsImgUrl: require('../assets/icon4.png'),
+      buildsList: [
+        { title: '申报评审', code: '051' },
+        { title: '立项管理', code: '052' },
+        { title: '服务平台', code: '053' }
+      ],
+      // 社科要闻
+      news: [],
+      newsImgUrl: require('../assets/icon5.png'),
+      // 成果发布
+      threeImg: require('../assets/cg.jpg'),
+      threeData: [],
+      // 政策法规
+      threeImgzc: require('../assets/zc.jpg'),
+      threeDatazc: [],
+      // 通知公告
+      threeImggg: require('../assets/gg.png'),
+      threeDatagg: []
+    };
+  },
+  async mounted() {
+    // 社会组织建设
+    await this.btnClick({ name: '031', type: 'corporation' });
+    // 社科普及
+    await this.btnClick({ name: '041', type: 'prize', parentCode: true });
+    // 科学评奖
+    await this.btnClick({ name: '061', type: 'universal', limit: 6 });
+    // 智库基金
+    await this.btnClick({ name: '051', type: 'builds', limit: 6 });
+    // 社科要闻
+    await this.btnClick({ name: '02', type: 'news', limit: 4, parentCode: true });
+    // 成果发布
+    await this.btnClick({ name: '07', type: 'threeData', limit: 5, parentCode: true });
+    // 政策法规
+    await this.btnClick({ name: '09', type: 'threeDatazc', limit: 3, parentCode: true });
+    // 通知公告
+    await this.btnClick({ name: '10', type: 'threeDatagg', limit: 4, parentCode: true });
+  },
+  methods: {
+    ...mapActions(['contentsQuery']),
+    async btnClick(e) {
+      const filter = { bind: e?.name };
+      if (e?.parentCode) filter.parentCode = e?.name;
+      if (e?.limit) filter.limit = e?.limit;
+      const res = await this.contentsQuery({ ...filter });
+      if (res.errcode == 0) {
+        switch (e?.type) {
+          case 'prize':
+            this.prize = res.data;
+            break;
+          case 'corporation':
+            this.corporation = res.data;
+            break;
+          case 'universal':
+            this.universal = res.data;
+            break;
+          case 'builds':
+            this.builds = res.data;
+            break;
+          case 'news':
+            this.news = res.data;
+            break;
+          case 'threeData':
+            this.threeData = res.data;
+            break;
+          case 'threeDatazc':
+            this.threeDatazc = res.data;
+            break;
+          case 'threeDatagg':
+            this.threeDatagg = res.data;
+            break;
+        }
+      }
+    },
+    // 图片点击
+    imgClick(code, parentCode) {
+      const item = this.$last({ menus: this.menusall, code: code });
+      this.$setParentsetSession({ menus: this.menusall, iscode: item });
+      this.$router.push(`/list/${code}?parentCode=${parentCode}`);
+    }
+  }
+};
+</script>
+<style lang="scss" scoped>
+.home {
+  width: 100%;
+  .top {
+    width: 100%;
+  }
+  .listBox {
+    width: 70%;
+    margin: 3% auto;
+    display: flex;
+    .listItem {
+      margin: 0 5%;
+    }
+  }
+  .sknj {
+    width: 65%;
+    display: block;
+    margin-right: 5%;
+  }
+  .szskl {
+    width: 30%;
+    display: block;
+  }
+  // .twoList {
+  //   width: 71%;
+  // }
+  .xzbox {
+    width: 30%;
+    position: relative;
+    .three {
+      width: 100%;
+    }
+    .xz {
+      width: 100%;
+      position: absolute;
+      bottom: 0;
+    }
+  }
+}
+</style>

+ 82 - 0
src/views/details.vue

@@ -0,0 +1,82 @@
+<template>
+  <div class="detailsHome" v-if="contentsItem">
+    <img class="thumbnail" v-if="isshow" :src="contentsItem.thumbnail">
+    <h1 class="title">{{ contentsItem.title }}</h1>
+    <span class="describe">{{ contentsItem.describe }}</span>
+    <span class="date">发表时间: {{ contentsItem.updateAt | dates }}<span class="visit">访问量: {{ contentsItem.visit }}</span></span>
+    <div class="content" v-html="contentsItem.content"></div>
+  </div>
+  <el-divider class="divider" v-else>暂无数据</el-divider>
+</template>
+
+<script>
+import moment from 'moment';
+import { mapState, mapActions } from 'vuex';
+export default {
+  name: 'detailsHome',
+  components: {},
+  computed: {
+    ...mapState(['contentsItem']),
+    isshow() {
+      if (this.contentsItem.bind.includes('012')) return true;
+      return false;
+    }
+  },
+  data() {
+    return {
+      id: ''
+    };
+  },
+  async mounted() {
+    this.id = this.$route.params.id;
+    const type = this.$route.query.type;
+    if (type) {
+      await this.imgNewsFetch({ id: this.id });
+      return;
+    }
+    await this.contentsFetch({ id: this.id });
+  },
+  methods: {
+    ...mapActions(['contentsFetch', 'imgNewsFetch'])
+  },
+  filters: {
+    dates(e) {
+      return moment(e).format('YYYY-MM-DD');
+    }
+  }
+};
+</script>
+<style lang="scss">
+.detailsHome {
+  width: 70%;
+  margin: 0 auto;
+  .thumbnail {
+    width: 20%;
+    display: block;
+    margin: 5% auto;
+  }
+  .title, .describe, .content, .date {
+    width: 100%;
+    text-align: center;
+  }
+  .describe, .date {
+    display: block;
+    color: #999;
+    line-height: 2em;
+    .visit {
+      margin-left: 30px;
+    }
+  }
+  .content {
+    text-align: left;
+    white-space: pre-wrap;
+    word-wrap: break-word;
+    iframe {
+      width: 70%;
+      height: 500px;
+      display: block;
+      margin: 0 auto;
+    }
+  }
+}
+</style>

+ 83 - 0
src/views/leader.vue

@@ -0,0 +1,83 @@
+<template>
+  <div class="detailsHome" v-if="menus.length > 0">
+    <div class="leader" v-for="(i, index) in menus" :key="index">
+      <h2 class="title">{{ i.name }}</h2>
+      <div class="item" v-if="i.datas">
+        <div v-for="item in i.datas" :key="item._id" @click="btn(item)">{{ item.describe }}: {{ item.title }}</div>
+      </div>
+    </div>
+  </div>
+  <el-divider class="divider" v-else>暂无数据</el-divider>
+</template>
+
+<script>
+import { mapState, mapActions } from 'vuex';
+export default {
+  name: 'leaderHome',
+  components: {},
+  computed: {
+    ...mapState(['menusall'])
+  },
+  data() {
+    return {
+      leader: [],
+      member: [],
+      menus: []
+    };
+  },
+  async mounted() {
+    this.leader = await this.leaderList({ bind: '0121' });
+    this.member = await this.leaderList({ bind: '0122' });
+    const res = await this.menusQueryNoShow();
+    if (res.length > 0) {
+      const menu = res.filter(j => j.parentCode == '012');
+      for (let i = 0; i < menu.length; i++) {
+        const datas = await this.leaderList({ bind: menu[i].code });
+        menu[i].datas = datas;
+      }
+      this.menus = menu;
+    }
+  },
+  methods: {
+    ...mapActions(['leaderList', 'menusQueryNoShow']),
+    btn(e) {
+      console.log(e);
+      this.$router.push(`/details/${e._id}`);
+    }
+  }
+};
+</script>
+<style lang="scss">
+.detailsHome {
+  width: 70%;
+  margin: 5% auto;
+  .title {
+   text-align: center;
+  }
+  .leader, .member {
+    width: 50%;
+    margin: 0 auto;
+    text-align: left;
+    font-weight: 700;
+    font-size: 1.2em;
+    line-height: 2em;
+    .item {
+      width: 100%;
+      text-align: left;
+      cursor: pointer;
+      // display: flex;
+      // .isdescribe {
+      //   width: 40%;
+      // }
+    }
+  }
+  .describe, .date {
+    display: block;
+    color: #999;
+    line-height: 2em;
+    .visit {
+      margin-left: 30px;
+    }
+  }
+}
+</style>

+ 140 - 0
src/views/list.vue

@@ -0,0 +1,140 @@
+<template>
+  <div>
+    <breadcrumb ref="breadcrumb"></breadcrumb>
+    <div class="listHome">
+      <div class="listBoxLeft">
+        <letnav ref="letnav" :menuTree="menu"></letnav>
+      </div>
+      <div class="listBoxRight"  v-if="listTotal > 0">
+        <el-card class="listBox" v-for="(item, index) in contentList" :key="index">
+          <el-image :src="item.thumbnail" class="listimg" @click="newClick(item)"></el-image>
+          <div class="text">
+            <h2 class="title" @click="newClick(item)">{{ item.title }}</h2>
+            <span class="describe">{{ item.describe }}</span>
+            <span class="date">{{ item.date | dates }}</span>
+          </div>
+        </el-card>
+        <pagination ref="pagination" class="pagination" :total="listTotal" @query="filterQuery"></pagination>
+      </div>
+      <el-divider class="divider" v-else>暂无数据</el-divider>
+    </div>
+  </div>
+</template>
+
+<script>
+import moment from 'moment';
+import letnav from '../components/leftmenu/index.vue';
+import pagination from '../components/pagination';
+import breadcrumb from '../components/breadcrumb/index.vue';
+import { mapState, mapActions } from 'vuex';
+export default {
+  name: 'listHome',
+  components: {
+    pagination,
+    letnav,
+    breadcrumb
+  },
+  computed: {
+    ...mapState(['contentList', 'listTotal', 'menusall'])
+  },
+  data() {
+    return {
+      code: null,
+      pageSize: 10,
+      parentCode: null,
+      menu: {}
+    };
+  },
+  async mounted() {
+    // 当前菜单参数
+    this.code = this.$route.params.code;
+    // 顶级菜单参数
+    this.parentCode = this.$route.query.parentCode;
+    await this.filterQuery();
+    // 获取一例菜单
+    this.menu = this.$setChildrenSession({ menus: this.menusall, iscode: this.parentCode });
+    // 控制左侧菜单当前选项
+    this.$refs.letnav.setIndex();
+  },
+  methods: {
+    ...mapActions(['contentsList']),
+    // 查询函数
+    async filterQuery ({ filter = {}, paging = { page: 0, size: 10 } } = {}) {
+      filter.bind = this.code;
+      if (this.code.length == 2) filter.parentCode = this.code;
+      await this.contentsList({ filter, paging });
+    },
+    // 列表点击
+    newClick(e) {
+      this.$router.push(`/details/${e._id}`);
+    }
+  },
+  filters: {
+    dates(e) {
+      return moment(e).format('YYYY-MM-DD');
+    }
+  }
+};
+</script>
+<style lang="scss">
+.divider {
+  width: 70%;
+  margin: 5% auto;
+}
+.listHome {
+  width: 70%;
+  margin: 0 auto;
+  display: flex;
+  .listBoxLeft {
+    width: 25%;
+    margin-top: 5%;
+    margin-right: 5%;
+  }
+  .listBoxRight {
+    width: 70%;
+    margin-top: 5%;
+    .listBox {
+      width: 100%;
+      margin: 0 auto;
+      margin-bottom: 5%;
+      .el-card__body {
+        display: flex;
+      }
+      .listimg {
+        display: block;
+        width: 15%;
+        height: 150px;
+        cursor: pointer;
+      }
+      .text {
+        width: 70%;
+        margin-left: 5%;
+        .title {
+          cursor: pointer;
+          width: 100%;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          white-space: nowrap;
+        }
+        .describe {
+          width: 100%;
+          overflow: hidden;
+          text-overflow: ellipsis;
+          display: -webkit-box;
+          -webkit-line-clamp: 2;
+          -webkit-box-orient: vertical;
+          color: #999;
+        }
+        .date {
+          width: 100%;
+          color: #999;
+          line-height: 3em;
+        }
+      }
+    }
+  }
+  .pagination {
+    margin-bottom: 5%;
+  }
+}
+</style>

+ 59 - 0
src/views/pages.vue

@@ -0,0 +1,59 @@
+<template>
+  <div class="detailsHome" v-if="pagesItem">
+    <h1 class="title">{{ pagesItem.title }}</h1>
+    <span class="describe">{{ pagesItem.describe }}</span>
+    <span class="date">发表时间: {{ pagesItem.updateAt | dates }}<span class="visit">访问量: {{ pagesItem.visit }}</span></span>
+    <pre class="content" v-html="pagesItem.content"></pre>
+  </div>
+  <el-divider class="divider" v-else>暂无数据</el-divider>
+</template>
+
+<script>
+import moment from 'moment';
+import { mapState, mapActions } from 'vuex';
+export default {
+  name: 'pagesHome',
+  components: {},
+  computed: {
+    ...mapState(['pagesItem'])
+  },
+  data() {
+    return {
+      id: ''
+    };
+  },
+  async mounted() {
+    this.code = this.$route.params.code;
+    await this.pagesFetch({ bind: this.code });
+  },
+  methods: {
+    ...mapActions(['pagesFetch'])
+  },
+  filters: {
+    dates(e) {
+      return moment(e).format('YYYY-MM-DD');
+    }
+  }
+};
+</script>
+<style lang="scss">
+.detailsHome {
+  width: 70%;
+  margin: 0 auto;
+  .thumbnail {
+    width: 100%;
+  }
+  .title, .describe, .content, .date {
+    width: 100%;
+    text-align: center;
+  }
+  .describe, .date {
+    display: block;
+    color: #999;
+    line-height: 2em;
+    .visit {
+      margin-left: 30px;
+    }
+  }
+}
+</style>

+ 25 - 0
vue.config.js

@@ -0,0 +1,25 @@
+module.exports = {
+  publicPath: '/www',
+  productionSourceMap: false,
+  devServer: {
+    port: 18080,
+    proxy: {
+      '/api/': {
+        // target: 'http://192.168.0.45:18090'
+        target: 'http://127.0.0.1:18090'
+      },
+      '/upload/': {
+        target: 'http://127.0.0.1:9002'
+        // target: 'http://192.168.0.45:18090'
+      }
+    }
+  },
+  configureWebpack: {
+    resolve: {
+      alias: {
+        '@components': '/src/components',
+        '@lib': '/lib'
+      }
+    }
+  }
+};