소스 검색

add userAdmin

Starink 6 년 전
부모
커밋
a123edc769

+ 6 - 2
assets/css/person.scss

@@ -100,8 +100,12 @@
     *::-webkit-scrollbar-thumb {
       background-color: #ddd;
       border-radius: 3px;
+    }
   }
 
-}
-
+  &__chart{
+    width: 600px;
+    height: 400px;
+    margin: 20px 20px;
+  }
 }

+ 7 - 3
layouts/admin.vue

@@ -14,7 +14,11 @@
           <i class="el-icon-data-analysis"></i>
           <span slot="title">平台统计</span>
         </el-menu-item>
-        <el-menu-item index="3" disabled>
+        <el-menu-item index="3">
+          <i class="el-icon-user"></i>
+          <span slot="title">用户管理</span>
+        </el-menu-item>
+        <el-menu-item index="4" disabled>
           <i class="el-icon-menu"></i>
           <span slot="title">教务管理</span>
         </el-menu-item>
@@ -40,12 +44,12 @@
     },
     methods: {
       handleSelect(index){
-        const routes=['/admin/checkApplications', '/admin/statistics', '/admin/educationAdmin'];
+        const routes=['/admin/checkApplications', '/admin/statistics', '/admin/userAdmin', '/admin/educationAdmin'];
         this.$router.push(routes[index-1]);
       }
     },
     mounted(){
-      const routes=['/admin/checkApplications', '/admin/statistics', '/admin/educationAdmin'];
+      const routes=['/admin/checkApplications', '/admin/statistics', '/admin/userAdmin', '/admin/educationAdmin'];
       this.activeIndex = (1 + routes.indexOf(this.$router.app._route.path)) + "";
     }
   }

+ 6 - 6
layouts/person.vue

@@ -14,19 +14,19 @@
           <i class="el-icon-document"></i>
           <span slot="title">身份认证</span>
         </el-menu-item>
-        <el-menu-item index="3" disabled>
+        <el-menu-item index="3">
           <i class="el-icon-data-line"></i>
           <span slot="title">学习数据</span>
         </el-menu-item>
-        <el-menu-item index="4" disabled>
+        <el-menu-item index="4">
           <i class="el-icon-reading"></i>
           <span slot="title">我的课程</span>
         </el-menu-item>
-        <el-menu-item index="5" disabled>
+        <el-menu-item index="5">
           <i class="el-icon-document"></i>
           <span slot="title">当前任务</span>
         </el-menu-item>
-        <el-menu-item index="6" disabled>
+        <el-menu-item index="6">
           <i class="el-icon-s-order"></i>
           <span slot="title">我的订单</span>
         </el-menu-item>
@@ -52,12 +52,12 @@
     },
     methods: {
       handleSelect(index){
-        const routes=['/person/info', '/person/identification', '/person/analysis', '/person/orders'];
+        const routes=['/person/info', '/person/identification', '/person/statistics', '/person/orders'];
         this.$router.push(routes[index-1]);
       }
     },
     mounted(){
-      const routes=['/person/info', '/person/identification', '/person/analysis', '/person/orders'];
+      const routes=['/person/info', '/person/identification', '/person/statistics', '/person/orders'];
       this.activeIndex = (1 + routes.indexOf(this.$router.app._route.path)) + "";
     }
   }

+ 4 - 2
nuxt.config.js

@@ -9,7 +9,8 @@ module.exports = {
     meta: [
       { charset: 'utf-8' },
       { name: 'viewport', content: 'width=device-width, initial-scale=1' },
-      { hid: 'description', name: 'description', content: process.env.npm_package_description || '' }
+      { hid: 'description', name: 'description', content: process.env.npm_package_description ||
+        'SEEC软件工程教育云平台,一个旨在为在校学生或行业内人士,提供系统性自主学习软件工程领域知识服务的平台' }
     ],
     link: [
       { rel: 'icon', type: 'image/x-icon', href: '/seec.ico' }
@@ -31,7 +32,8 @@ module.exports = {
   */
   plugins: [
     {src: '@/plugins/element-ui', ssr: true},
-    '~/plugins/api'
+    '~/plugins/api',
+     '~plugins/echarts'
   ],
   /*
   ** Nuxt.js dev-modules

+ 1 - 0
package.json

@@ -23,6 +23,7 @@
     "cookie-universal-nuxt": "^2.1.3",
     "cross-env": "^5.2.0",
     "debug": "^4.1.1",
+    "echarts": "^4.7.0",
     "element-ui": "^2.4.11",
     "jsonwebtoken": "^8.5.1",
     "koa": "^2.6.2",

+ 78 - 4
pages/admin/statistics.vue

@@ -1,14 +1,88 @@
 <template>
-  <div></div>
+  <div class="main-card__body" v-loading="pageLoading">
+    <h1 class="main-card__title">
+      平台统计
+    </h1>
+    <div class="main-card__chart" id="distributionChart"></div>
+    <div class="main-card__chart" id="onlineChart"></div>
+  </div>
+
 </template>
 
 <script>
   export default {
     name: "statistics",
-    layout: 'admin'
+    layout: 'admin',
+    data(){
+      return{
+        pageLoading: false,
+        distribution: [{
+          name: '0分',
+          value: 4
+        }, {
+          name: '1-59分',
+          value: 19
+        }, {
+          name: '60-69分',
+          value: 34
+        }, {
+          name: '70-84分',
+          value: 70
+        }, {
+          name: '85-100分',
+          value: 65
+        }],
+        online: [87, 23, 36, 167, 171, 90, 45],
+        tests: [
+          {}
+        ]
+      }
+    },
+    mounted(){
+      this.initEcharts();
+    },
+    methods:{
+      initEcharts(){
+        let distributionChart = this.$echarts.init(document.getElementById('distributionChart'), 'light');
+        distributionChart.setOption({
+          title: {text: '本次测验得分分布情况', left: 'center'},
+          tooltip: {
+            trigger: 'item',
+          },
+          series: [{
+            data: this.distribution,
+            type: 'pie',
+            radius: '65%',
+            center: ['50%', '50%'],
+            selectedMode: 'single',
+            // areaStyle: {}
+          }],
+
+        });
+
+        let onlineChart = this.$echarts.init(document.getElementById('onlineChart'), 'light');
+        onlineChart.setOption({
+          title: {text: '上周每日活跃人数图表', left: 'center'},
+          tooltip: {
+            trigger: 'item',
+          },
+          xAxis: {
+            type: 'category',
+            data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
+          },
+          yAxis: {
+            type: 'value'
+          },
+          series: [{
+            data: this.online,
+            type: 'bar'
+          }]
+        });
+      }
+    }
   };
 </script>
 
-<style scoped>
-
+<style lang="scss" scoped>
+  @import '~/assets/css/person.scss';
 </style>

+ 141 - 0
pages/admin/userAdmin.vue

@@ -0,0 +1,141 @@
+<template>
+  <div class="main-card__body" v-loading="pageLoading">
+    <h1 class="main-card__title">
+      账户管理
+    </h1>
+    <el-table
+      :data="users"
+      stripe
+      height="500"
+      class="main-card__table"
+    >
+      <el-table-column
+        type="index"
+        width="50">
+      </el-table-column>
+      <el-table-column
+        prop="name"
+        label="名称"
+        width="120">
+      </el-table-column>
+      <el-table-column
+        prop="phone"
+        label="联系方式">
+      </el-table-column>
+      <el-table-column
+        prop="role"
+        label="角色"
+        width="100">
+      </el-table-column>
+      <el-table-column
+        prop="state"
+        label="状态"
+        width="100">
+      </el-table-column>
+      <el-table-column
+        fixed="right"
+        label="操作"
+        width="100">
+        <template slot-scope="scope">
+          <el-button @click="block(scope.row)" type="text" size="small" v-if="scope.row.isValid">封禁</el-button>
+          <el-button @click="unblock(scope.row)" type="text" size="small" v-else>解封</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: "userAdmin",
+    layout: 'admin',
+    data(){
+      return{
+        users: [],
+        pageLoading: true
+      }
+    },
+    mounted(){
+      this.getUsers();
+      this.pageLoading = false;
+    },
+    methods: {
+      getUsers(){
+        this.$api.$get('/admin/users').then((res) =>
+          {
+            console.log('users',res.data);
+            if(res.code === 0){
+              for(let item of res.data){
+                if(item.isValid){
+                  item.state = '正常';
+                }else{
+                  item.state = '封禁中';
+                }
+              }
+              this.users = res.data;
+            }else{
+              this.$message({
+                showClose: true,
+                message: res.message,
+                type: 'error'
+              });
+            }
+          }
+        ).catch((e)=>{
+          console.log(e);
+        });
+      },
+      block(row){
+        this.$api.$put('/admin/user', {id: row.id, isValid: false}).then((res) =>
+          {
+            if(res.code === 0){
+              this.$message({
+                showClose: true,
+                message: "已封禁用户" + row.name,
+                type: 'success'
+              });
+              row.isValid = false;
+              row.state = "封禁中"
+            }else{
+              this.$message({
+                showClose: true,
+                message: res.message,
+                type: 'error'
+              });
+            }
+          }
+        ).catch((e)=>{
+          console.log(e);
+        });
+      },
+      unblock(row){
+        this.$api.$put('/admin/user', {id: row.id, isValid: true}).then((res) =>
+          {
+            if(res.code === 0){
+              this.$message({
+                showClose: true,
+                message: "已解封用户" + row.name,
+                type: 'success'
+              });
+              row.isValid = true;
+              row.state = "正常"
+            }else{
+              this.$message({
+                showClose: true,
+                message: res.message,
+                type: 'error'
+              });
+            }
+          }
+        ).catch((e)=>{
+          console.log(e);
+        });
+      }
+    }
+  };
+</script>
+
+
+<style lang="scss" scoped>
+  @import '~/assets/css/person.scss';
+</style>

+ 56 - 0
pages/person/statistics.vue

@@ -0,0 +1,56 @@
+<template>
+  <div class="main-card__body" v-loading="pageLoading">
+    <h1 class="main-card__title">
+      学习数据
+    </h1>
+    <div class="main-card__chart" id="scoreChart"></div>
+  </div>
+
+</template>
+
+<script>
+  export default {
+    name: "statistics",
+    layout: 'person',
+    data(){
+      return{
+        pageLoading: false,
+        scores: [84, 76, 82, 90, 85],
+      }
+    },
+    mounted(){
+      this.initEcharts();
+    },
+    methods:{
+      initEcharts(){
+        let scoreChart = this.$echarts.init(document.getElementById('scoreChart'), 'light');
+        scoreChart.setOption({
+          title: {text: '最近五次测验得分情况', left: 'center'},
+          tooltip: {
+            trigger: 'item',
+          },
+          xAxis: {
+            type: 'category',
+            data: ['第一次', '第二次', '第三次', '第四次', '第五次'],
+            boundaryGap: false
+          },
+          yAxis: {
+            type: 'value',
+            name: '得分'
+          },
+          series: [{
+            data: this.scores,
+            type: 'line',
+            // areaStyle: {}
+          }],
+
+        });
+
+      }
+    }
+  };
+</script>
+
+<style lang="scss" scoped>
+  @import '~/assets/css/person.scss';
+</style>

+ 4 - 0
plugins/echarts.js

@@ -0,0 +1,4 @@
+import Vue from 'vue'
+import echarts from 'echarts' // 引入echarts
+
+Vue.prototype.$echarts = echarts; // 引入组件(将echarts注册为全局)

+ 30 - 0
server/controller/admin.js

@@ -21,6 +21,36 @@ class AdminController {
 
   }
 
+  static async getUsers(ctx){
+    let theUser = await getTokenUser(ctx);
+    if(theUser.role !== "ADMIN"){
+      ctx.status = 401;
+    }else{
+      let result = await userModel.getUsers();
+      if(result){
+        ctx.body = Response.success(result);
+      }else{
+        ctx.body = Response.failed("获取失败");
+      }
+    }
+
+  }
+
+  static async updateUser(ctx){
+    let theUser = await getTokenUser(ctx);
+    if(theUser.role !== "ADMIN"){
+      ctx.status = 401;
+    }else{
+      let result = await userModel.updateIsValid(ctx.request.body.id, ctx.request.body.isValid);
+      if(result){
+        ctx.body = Response.success(result);
+      }else{
+        ctx.body = Response.failed("获取失败");
+      }
+    }
+
+  }
+
   static async passApplication(ctx){
     let theUser = await getTokenUser(ctx);
     if(theUser.role !== "ADMIN"){

+ 3 - 1
server/controller/user.js

@@ -19,7 +19,9 @@ class UserController {
         if (user) {
             const confirmRes = bcrypt.compareSync(data.password, user.password);
             if(!confirmRes){
-                ctx.body = Response.failed('密码错误,请重试')
+              ctx.body = Response.failed('密码错误,请重试')
+            }else if(!user.isValid){
+              ctx.body = Response.failed('账号封禁中')
             }else{
                 // console.log('ctx',ctx);
               // console.log('ctx.cookies.redirect_uri',ctx.cookies.get('redirect_uri'));

+ 13 - 0
server/model/user.js

@@ -1,6 +1,8 @@
 const db = require('../config/db');
 const getNowFormatTime = require('../util/time-processer');
 const User = db.import('../schema/user.js');
+const seq = require('sequelize');
+const Op = seq.Op;
 
 User.sync();
 
@@ -78,6 +80,17 @@ class UserModel {
     let result = await User.update({password: newPassword}, {where: {phone: phone}});
     return result[0]> 0;
   }
+
+  static async getUsers(){
+    return User.findAll({
+      where: {role: {[Op.ne]: 'ADMIN'}}
+    });
+  }
+
+  static async updateIsValid(id, isValid){
+    await User.update({isValid: isValid}, {where: {id: id}});
+    return true;
+  }
 }
 
 module.exports = UserModel;

+ 2 - 0
server/routes/index.js

@@ -24,6 +24,8 @@ module.exports = () => {
   router.get('/admin/getApplications', AdminController.getApplications);
   router.post('/admin/passApplication', AdminController.passApplication);
   router.post('/admin/rejectApplication', AdminController.rejectApplication);
+  router.get('/admin/users', AdminController.getUsers);
+  router.put('/admin/user', AdminController.updateUser);
 
     router.get('/course/getMyCourses',  CourseController.getMyCourses);
     router.get('/course/getHotCourses',  CourseController.getHotCourses);

+ 6 - 1
server/schema/user.js

@@ -51,7 +51,12 @@ module.exports = (sequelize, DataTypes) =>
                 return moment(this.getDataValue('createdTime')).format('YYYY-MM-DD HH:mm:ss');
             }
 
-        }
+        },
+      isValid: {
+        type: DataTypes.BOOLEAN,
+        allowNull: false,
+        defaultValue: true
+      },
     }, {
         tableName: 'user'
     });

+ 12 - 0
yarn.lock

@@ -4055,6 +4055,13 @@ ecdsa-sig-formatter@1.0.11:
   dependencies:
     safe-buffer "^5.0.1"
 
+echarts@^4.7.0:
+  version "4.7.0"
+  resolved "https://registry.yarnpkg.com/echarts/-/echarts-4.7.0.tgz#5b3875a4c2f91e3929425fabab9eace7e4098b3f"
+  integrity sha512-NlOTdUcAsIyCCG+N4uh0ZEvXtrPW2jvcuqf03RyqYeCKzyPbiOQ4I3MdKXMhxG3lBdqQNdNXVT71SB4KTQjN0A==
+  dependencies:
+    zrender "4.3.0"
+
 editorconfig@^0.15.3:
   version "0.15.3"
   resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5"
@@ -12011,3 +12018,8 @@ ylru@^1.2.0:
   version "1.2.1"
   resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.2.1.tgz#f576b63341547989c1de7ba288760923b27fe84f"
   integrity sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==
+
+zrender@4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/zrender/-/zrender-4.3.0.tgz#9f056121b20bbae44414d287bf6a119ff7042661"
+  integrity sha512-Dii6j2bDsPkxQayuVf2DXJeruIB/mKVxxcGRZQ9GExiBd4c3w7+oBuvo1O/JGHeFeA1nCmSDVDs/S7yKZG1nrA==