Bläddra i källkod

Merge branch 'master' of http://git.cc-lotus.info/article-system/article-mobile

guhongwei 4 år sedan
förälder
incheckning
f315765288

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
     "@stomp/stompjs": "^5.4.4",
     "axios": "^0.19.2",
     "core-js": "^3.6.4",
+    "echarts": "^5.0.2",
     "element-ui": "^2.13.2",
     "flv.js": "^1.5.0",
     "jsonwebtoken": "^8.5.1",

+ 1 - 3
src/plugins/check-res.js

@@ -23,7 +23,6 @@ const Plugin = {
           return _okText();
         }
         if (_okText) {
-          Message.success(_okText);
           Notify({ type: 'success', message: _okText });
         }
         return true;
@@ -31,8 +30,7 @@ const Plugin = {
       if (_.isFunction(_errText)) {
         return _errText();
       }
-      Message.error(_errText || errmsg);
-      Notify({ type: 'danger', message: _okText });
+      Notify({ type: 'danger', message: errText });
       // Message({ message: _errText || errmsg, duration: 60000 });
       return false;
     };

+ 14 - 1
src/router/index.js

@@ -4,13 +4,24 @@ import store from '@/store/index';
 const jwt = require('jsonwebtoken');
 
 Vue.use(VueRouter);
-
+const admin = [
+  {
+    path: '/admin/index',
+    meta: { title: '管理首页', isleftarrow: false },
+    component: () => import('../views/adminCenter/index.vue'),
+  },
+];
 const routes = [
   {
     path: '/',
     meta: { title: '系统首页', isleftarrow: false },
     component: () => import('../views/index.vue'),
   },
+  {
+    path: '/login',
+    meta: { title: '管理登陆', isleftarrow: false },
+    component: () => import('../views/login.vue'),
+  },
   // 文章辟谣
   {
     path: '/refute/index',
@@ -39,6 +50,8 @@ const routes = [
     meta: { title: 'service_index', isleftarrow: false },
     component: () => import('../views/service/index.vue'),
   },
+  // 引用管理员
+  ...admin,
 ];
 
 const router = new VueRouter({

+ 14 - 2
src/store/index.js

@@ -1,14 +1,26 @@
 import Vue from 'vue';
 import Vuex from 'vuex';
 import refute from './refute';
+import login from './login';
 
 Vue.use(Vuex);
 
 export default new Vuex.Store({
-  state: {},
-  mutations: {},
+  state: {
+    user: {},
+  },
+  mutations: {
+    setUser(state, payload) {
+      sessionStorage.setItem('user', JSON.stringify(payload));
+      state.user = payload;
+    },
+    getUser(state, payload) {
+      state.user = jwt.decode(JSON.parse(sessionStorage.getItem('user')));
+    },
+  },
   actions: {},
   modules: {
     refute,
+    login,
   },
 });

+ 25 - 0
src/store/login.js

@@ -0,0 +1,25 @@
+import Vue from 'vue';
+import Vuex from 'vuex';
+import _ from 'lodash';
+Vue.use(Vuex);
+const api = {
+  interface: `/api/article/login`,
+};
+const state = () => ({});
+const mutations = {};
+
+const actions = {
+  async login({ commit }, payload) {
+    const res = await this.$axios.$post(api.interface, payload);
+    if (res.errcode === 0) {
+      commit('setUser', res.data, { root: true });
+    }
+    return res;
+  },
+};
+export default {
+  namespaced: true,
+  state,
+  mutations,
+  actions,
+};

+ 138 - 0
src/views/adminCenter/chart/gauge.vue

@@ -0,0 +1,138 @@
+<template>
+  <div id="gauge">
+    <div :id="id" style="width: 99%;height:400px;"></div>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import * as echarts from 'echarts';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'gauge',
+  props: {
+    data: {
+      type: Array,
+      default: () => [
+        { value: 1048, name: 'first' },
+        { value: 735, name: 'second' },
+        { value: 580, name: 'third' },
+      ],
+    },
+    title: { type: String, default: '仪表盘' },
+  },
+  components: {},
+  data: function() {
+    return {
+      id: `gauge${_.random(100000, 999999)}`,
+      // 位移
+      tags: [
+        // 第一个数据的标题与详情位置位移
+        {
+          title: { offsetCenter: ['0%', '-50%'] },
+          detail: { offsetCenter: ['0%', '-35%'] },
+        },
+        // 第二个数据的标题与详情位置位移
+        {
+          title: { offsetCenter: ['0%', '-15%'] },
+          detail: { offsetCenter: ['0%', '0%'] },
+        },
+        // 第三个数据的标题与详情位置位移
+        {
+          title: { offsetCenter: ['0%', '20%'] },
+          detail: { offsetCenter: ['0%', '35%'] },
+        },
+      ],
+    };
+  },
+  created() {},
+  mounted() {
+    this.init();
+  },
+  methods: {
+    init() {
+      // 处理数据
+      let dup = _.cloneDeep(this.data);
+      for (let i = 0; i < dup.length; i++) {
+        const e = dup[i];
+        e.title = this.tags[i].title;
+        e.detail = this.tags[i].detail;
+      }
+      const max = dup.reduce((p, n) => p + n.value * 1, 0);
+      let chart = echarts.init(document.getElementById(this.id));
+      chart.setOption({
+        title: {
+          text: this.title,
+          left: 'center',
+        },
+        series: [
+          {
+            type: 'gauge',
+            startAngle: 90,
+            endAngle: -270,
+            pointer: {
+              show: false,
+            },
+            progress: {
+              show: true,
+              overlap: false,
+              roundCap: true,
+              clip: true,
+              itemStyle: {
+                borderWidth: 1,
+                borderColor: '#464646',
+              },
+            },
+            axisTick: {
+              show: false,
+            },
+            axisLine: {
+              lineStyle: {
+                width: 40,
+              },
+            },
+            splitLine: {
+              show: false,
+              distance: 0,
+              length: 10,
+            },
+            axisLabel: {
+              show: false,
+              distance: 50,
+            },
+            // max需要通过data进行计算
+            max: max,
+            min: 0,
+            data: dup,
+            detail: {
+              width: 50,
+              height: 14,
+              lineHeight: 50,
+              fontSize: 14,
+              color: 'auto',
+              borderColor: 'auto',
+              borderRadius: 20,
+              borderWidth: 1,
+              // 需要计算
+              formatter: value => {
+                return `${_.multiply(_.round(_.divide(value, max), 2), 100)}%`;
+              },
+            },
+          },
+        ],
+      });
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 63 - 0
src/views/adminCenter/chart/lineBar.vue

@@ -0,0 +1,63 @@
+<template>
+  <div id="lineBar">
+    <div :id="id" style="width: 99%;height:400px;"></div>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import * as echarts from 'echarts';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'lineBar',
+  props: {
+    x: { type: Array, default: () => ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子'] },
+    data: { type: Array, default: () => [5, 20, 36, 10, 10, 20] },
+    type: { type: String, default: 'line' }, //bar
+    title: { type: String, default: '折线/柱状图' },
+  },
+  components: {},
+  data: function() {
+    return {
+      id: `lineBar${_.random(100000, 999999)}`,
+    };
+  },
+  created() {},
+  mounted() {
+    this.init();
+  },
+  methods: {
+    init() {
+      let chart = echarts.init(document.getElementById(this.id));
+      chart.setOption({
+        title: {
+          text: this.title,
+          left: 'center',
+        },
+        tooltip: {},
+        xAxis: {
+          data: this.x,
+        },
+        yAxis: {},
+        series: [
+          {
+            type: this.type,
+            data: this.data,
+          },
+        ],
+      });
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 82 - 0
src/views/adminCenter/chart/pie.vue

@@ -0,0 +1,82 @@
+<template>
+  <div id="pie">
+    <div :id="id" style="width: 99%;height:400px;"></div>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import * as echarts from 'echarts';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'pie',
+  props: {
+    data: {
+      type: Array,
+      default: () => [
+        { value: 1048, name: '搜索引擎' },
+        { value: 735, name: '直接访问' },
+        { value: 580, name: '邮件营销' },
+        { value: 484, name: '联盟广告' },
+        { value: 300, name: '视频广告' },
+      ],
+    },
+    title: { type: String, default: '饼图' },
+  },
+  components: {},
+  data: function() {
+    return {
+      id: `pie${_.random(100000, 999999)}`,
+    };
+  },
+  created() {},
+  mounted() {
+    this.init();
+  },
+  methods: {
+    init() {
+      let chart = echarts.init(document.getElementById(this.id));
+      chart.setOption({
+        title: {
+          text: this.title,
+          left: 'center',
+        },
+        legend: {
+          show: true,
+          top: 30,
+        },
+        series: [
+          {
+            label: {
+              show: true,
+              position: 'inside',
+              formatter: '{d}%',
+            },
+            type: 'pie',
+            radius: '50%',
+            data: this.data,
+            emphasis: {
+              itemStyle: {
+                shadowBlur: 10,
+                shadowOffsetX: 0,
+                shadowColor: 'rgba(0, 0, 0, 0.5)',
+              },
+            },
+          },
+        ],
+      });
+    },
+  },
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

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

@@ -0,0 +1,37 @@
+<template>
+  <div id="index">
+    <top topType="2"></top>
+    <lineBar title="发表数"></lineBar>
+    <gauge title="阅读数"></gauge>
+    <pie title="点赞数"></pie>
+  </div>
+</template>
+
+<script>
+import top from '@/layout/common/top.vue';
+import lineBar from './chart/lineBar.vue';
+import pie from './chart/pie.vue';
+import gauge from './chart/gauge.vue';
+import { mapState, createNamespacedHelpers } from 'vuex';
+export default {
+  name: 'index',
+  props: {},
+  components: { lineBar, pie, gauge, top },
+  data: function() {
+    return {};
+  },
+  created() {},
+  methods: {},
+  computed: {
+    ...mapState(['user', 'menuParams']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>

+ 50 - 0
src/views/login.vue

@@ -0,0 +1,50 @@
+<template>
+  <div id="login">
+    <van-form @submit="onSubmit" style="margin-top:20vh">
+      <van-field v-model="form.login_id" name="用户名" label="用户名" placeholder="用户名" :rules="[{ required: true, message: '请填写用户名' }]" />
+      <van-field v-model="form.password" type="password" name="密码" label="密码" placeholder="密码" :rules="[{ required: true, message: '请填写密码' }]" />
+      <div style="margin: 16px;">
+        <van-button round block type="info" native-type="submit">提交</van-button>
+      </div>
+    </van-form>
+  </div>
+</template>
+
+<script>
+const _ = require('lodash');
+import { mapState, createNamespacedHelpers } from 'vuex';
+const { mapActions: mapLogin } = createNamespacedHelpers('login');
+export default {
+  name: 'login',
+  props: {},
+  components: {},
+  data: function() {
+    return {
+      form: {},
+    };
+  },
+  created() {},
+  methods: {
+    ...mapLogin(['login']),
+    async onSubmit() {
+      let dup = _.cloneDeep(this.form);
+      const res = await this.login(dup);
+      if (this.$checkRes(res, '登录成功', res.errmsg || '登陆失败')) {
+        // 跳转login
+        this.$router.push('/admin/index');
+      }
+    },
+  },
+  computed: {
+    ...mapState(['user']),
+    pageTitle() {
+      return `${this.$route.meta.title}`;
+    },
+  },
+  metaInfo() {
+    return { title: this.$route.meta.title };
+  },
+};
+</script>
+
+<style lang="less" scoped></style>