Explorar o código

完成第一版

dusenyao %!s(int64=3) %!d(string=hai) anos
pai
achega
d302b3ca2e

+ 39 - 0
src/api/teacher.js

@@ -23,3 +23,42 @@ export function auditOrgTeacherUser(Parameter) {
     }
   });
 }
+
+/**
+ * @description 得到机构教师用户的权限列
+ * @param {Object} Parameter user_org_id 机构教师ID
+ */
+export function getPopedomList_OrgTeacherUse(Parameter) {
+  let params = getRequestParameter('popedom_manager-GetPopedomList_OrgTeacherUser', Parameter);
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_FILE_SERVE,
+    params
+  });
+}
+
+/**
+ * @description 为机构教师用户设置权限
+ * @param {Object} Parameter
+ */
+export function setPopedom_OrgTeacherUser(Parameter) {
+  let data = getRequestParameter(
+    'popedom_manager-SetPopedom_OrgTeacherUser',
+    JSON.stringify(Parameter)
+  );
+
+  // 解决传数组的问题
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_FILE_SERVE,
+    data,
+    transformRequest: [
+      function (data) {
+        return qs.stringify(data);
+      }
+    ],
+    headers: {
+      'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8'
+    }
+  });
+}

+ 1 - 1
src/api/user.js

@@ -7,7 +7,7 @@ import { request } from '@/utils/request';
 export function login(Parameter) {
   return request({
     method: 'post',
-    url: process.env.VUE_APP_WEB_SI,
+    url: process.env.VUE_APP_FILE_SERVE,
     params: {
       MethodName: 'login_control-Login',
       Parameter: Parameter

+ 55 - 7
src/layouts/components/LayoutHeader.vue

@@ -4,15 +4,24 @@
       <el-row type="flex" justify="space-between">
         <el-col :span="4" class="header-top-logo">LOGO</el-col>
         <el-col class="header-top-tab" :span="16">
-          <span>HOME PAGE</span>
-          <router-link to="/org_manager">机构管理</router-link>
-          <router-link to="/account_manager">账户管理</router-link>
-          <router-link to="/teacher_manager">机构教师</router-link>
+          <span>主页</span>
+          <template v-for="item in routerList">
+            <router-link v-if="item.isHidden" :key="item.path" :to="item.path">
+              {{ item.name }}
+            </router-link>
+          </template>
         </el-col>
         <el-col class="header-top-user" :span="4">
           <el-avatar> user </el-avatar>
-          <span class="header-top-user-name">name</span>
-          <el-badge is-dot class="header-top-user-bell"><i class="el-icon-bell"></i></el-badge>
+          <el-dropdown class="header-top-user-name" placement="bottom">
+            <span>{{ realName }}</span>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item @click.native="signOut">退出</el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+          <el-badge :is-dot="isDot" class="header-top-user-bell">
+            <i class="el-icon-bell"></i>
+          </el-badge>
         </el-col>
       </el-row>
     </div>
@@ -32,9 +41,41 @@ export default {
   name: 'LayoutHeader',
   data() {
     return {
-      levelList: null
+      levelList: null,
+      isDot: false
     };
   },
+  computed: {
+    realName() {
+      let user = this.$store.state.user;
+      return user.user_real_name ? user.user_real_name : user.user_name;
+    },
+    routerList() {
+      let popedomList = this.$store.state.user.popedom_code_list;
+      if (popedomList === undefined) {
+        popedomList = [];
+      }
+      let list = [
+        {
+          path: '/org_manager',
+          name: '机构管理',
+          isHidden: popedomList.indexOf(1000001) !== -1
+        },
+        {
+          path: '/account_manager',
+          name: '账户管理',
+          isHidden: popedomList.indexOf(1000002) !== -1
+        },
+        {
+          path: '/teacher_manager',
+          name: '机构教师',
+          isHidden: popedomList.indexOf(1000003) !== -1
+        }
+      ];
+
+      return list;
+    }
+  },
   watch: {
     $route() {
       this.getBreadcrumb();
@@ -46,6 +87,10 @@ export default {
   methods: {
     getBreadcrumb() {
       this.levelList = this.$route.matched.filter(item => item.meta && item.meta.title);
+    },
+    async signOut() {
+      await this.$store.dispatch('user/signOut');
+      this.$router.push(`/login`);
     }
   }
 };
@@ -56,6 +101,7 @@ export default {
   width: 100%;
   min-width: 1200px;
 
+  // 顶部
   &-top {
     height: 74px;
     padding: 15px 24px;
@@ -77,6 +123,7 @@ export default {
       &-name {
         vertical-align: top;
         margin-left: 8px;
+        cursor: pointer;
       }
 
       & > &-bell {
@@ -87,6 +134,7 @@ export default {
     }
   }
 
+  // 面包屑导航
   &-crumbs {
     height: 56px;
     background-color: #ddeaf6;

+ 0 - 1
src/layouts/index.vue

@@ -28,7 +28,6 @@ export default {
   height: 100%;
 
   .app-main {
-    height: calc(100% - 130px);
     background-color: #f5f5f5;
   }
 }

+ 1 - 1
src/permission.js

@@ -25,7 +25,7 @@ router.beforeEach(async (to, from, next) => {
     next();
   } else {
     // 其他无权访问的页面将重定向到登录页面
-    next(`/login?redirect=${to.path}`);
+    next(`/login`);
     NProgress.done();
   }
 });

+ 55 - 5
src/router/index.js

@@ -2,32 +2,76 @@ import Vue from 'vue';
 import VueRouter from 'vue-router';
 import login from '@/views/login';
 import Layout from '@/layouts';
+import store from '@/store';
+import NProgress from 'nprogress';
+import { Message } from 'element-ui';
 
 Vue.use(VueRouter);
 
+function isHasPopedom(code) {
+  return store.state.user.popedom_code_list.indexOf(code) !== -1;
+}
+
+function jumpURL() {
+  if (isHasPopedom(1000001)) {
+    return '/org_manager';
+  }
+  if (isHasPopedom(1000002)) {
+    return '/account_manager/index';
+  }
+  if (isHasPopedom(1000003)) {
+    return '/teacher_manager/index';
+  }
+  return '/404';
+}
+
+// 路由独享的守卫的权限控制
+function beforeRouterPopedom(code, next) {
+  if (isHasPopedom(code)) {
+    next();
+  } else {
+    next(false);
+    Message({
+      type: 'warning',
+      message: '无权限'
+    });
+    NProgress.done();
+  }
+}
+
 const routes = [
   {
     path: '/login',
     component: login
   },
   {
+    path: '/jump',
+    redirect: () => jumpURL()
+  },
+  {
     path: '/404',
     component: () => import('@/views/404')
   },
   {
     path: '/',
     component: Layout,
-    redirect: '/org_manager',
+    redirect: { name: 'OrgManager' },
     meta: { title: '机构管理' },
     children: [
       {
         path: '/org_manager',
         name: 'OrgManager',
-        component: () => import('@/views/org_manager')
+        component: () => import('@/views/org_manager'),
+        beforeEnter: (to, form, next) => {
+          beforeRouterPopedom(1000001, next);
+        }
       },
       {
         path: '/add_org',
-        component: () => import('@/views/org_manager/AddOrg')
+        component: () => import('@/views/org_manager/AddOrg'),
+        beforeEnter: (to, form, next) => {
+          beforeRouterPopedom(1000001, next);
+        }
       }
     ]
   },
@@ -40,7 +84,10 @@ const routes = [
       {
         path: '/account_manager/index',
         name: 'AccountManager',
-        component: () => import('@/views/account_manager')
+        component: () => import('@/views/account_manager'),
+        beforeEnter: (to, form, next) => {
+          beforeRouterPopedom(1000002, next);
+        }
       }
     ]
   },
@@ -53,7 +100,10 @@ const routes = [
       {
         path: '/teacher_manager/index',
         name: 'TeacherManager',
-        component: () => import('@/views/teacher_manager')
+        component: () => import('@/views/teacher_manager'),
+        beforeEnter: (to, form, next) => {
+          beforeRouterPopedom(1000003, next);
+        }
       }
     ]
   },

+ 11 - 3
src/store/modules/user.js

@@ -4,13 +4,16 @@ import { resetRouter } from '@/router';
 import { login } from '@/api/user';
 
 const getDefaultSate = () => {
-  const { session_id, user_code, user_real_name, user_type } = getUserInfo();
+  const { session_id, user_code, user_real_name, user_type, user_name, popedom_code_list } =
+    getUserInfo();
 
   return {
     session_id: session_id,
     user_code: user_code,
     user_real_name: user_real_name,
-    user_type: user_type
+    user_type: user_type,
+    user_name: user_name,
+    popedom_code_list: popedom_code_list === undefined ? [] : JSON.parse(popedom_code_list)
   };
 };
 
@@ -21,11 +24,16 @@ const mutations = {
     Object.assign(state, getDefaultSate());
   },
 
-  [user.SET_USER_INFO]: (state, { user_code, user_real_name, user_type, session_id }) => {
+  [user.SET_USER_INFO]: (
+    state,
+    { user_code, user_real_name, user_type, session_id, popedom_code_list, user_name }
+  ) => {
     state.user_code = user_code;
     state.user_real_name = user_real_name;
     state.user_type = user_type;
     state.session_id = session_id;
+    state.user_name = user_name;
+    state.popedom_code_list = popedom_code_list;
   }
 };
 

+ 37 - 0
src/styles/mixin.scss

@@ -11,3 +11,40 @@
   border-radius: 20px;
   padding: 0 20px;
 }
+
+@mixin list {
+  width: 100%;
+  min-height: 594px;
+  border-radius: 8px;
+  margin-top: 24px;
+  background-color: #fff;
+
+  &-title {
+    height: 78px;
+    line-height: 78px;
+    padding: 0 30px;
+    border-bottom: 1px solid #d9d9d9;
+  }
+
+  .el-table {
+    width: 100%;
+
+    &__header th:first-child,
+    &__body .el-table__row > td:first-child {
+      padding-left: 20px;
+      font-weight: 700;
+    }
+  }
+}
+
+@mixin pagination {
+  .el-pagination {
+    margin-top: 24px;
+
+    &.is-background .btn-next,
+    &.is-background .btn-prev,
+    &.is-background .el-pager li {
+      background-color: #fff;
+    }
+  }
+}

+ 14 - 2
src/utils/auth.js

@@ -1,6 +1,14 @@
 import Cookies from 'js-cookie';
+// import qs from 'qs';
 
-const userInfoList = ['user_code', 'session_id', 'user_type', 'user_real_name'];
+const userInfoList = [
+  'user_code',
+  'session_id',
+  'user_type',
+  'user_real_name',
+  'user_name',
+  'popedom_code_list'
+];
 
 export function getUserInfo() {
   let userInfo = {};
@@ -18,8 +26,12 @@ export function getSessionID() {
 export function setUserInfo(data) {
   for (let i = 0; i < userInfoList.length; i++) {
     const key = userInfoList[i];
+    let info = data[key];
+    if (typeof info === 'object') {
+      info = JSON.stringify(info);
+    }
 
-    Cookies.set(key, data[key]);
+    Cookies.set(key, info);
   }
 }
 

+ 16 - 0
src/utils/request.js

@@ -1,6 +1,7 @@
 import axios from 'axios';
 import { Message } from 'element-ui';
 import { getUserInfo } from '@/utils/auth';
+import store from '@/store';
 
 axios.defaults.withCredentials = true; // 跨域请求时是否需要使用凭证
 axios.defaults.dataType = 'json';
@@ -40,6 +41,21 @@ service.interceptors.response.use(
       });
       return Promise.reject(new Error(res.error || 'Error'));
     }
+
+    // -1 连接会话无效
+    if (res.status === -1) {
+      Message({
+        message: res.error,
+        type: 'error',
+        duration: 3 * 1000
+      });
+      store.dispatch('user/resetSessionID').then(() => {
+        location.reload();
+      });
+
+      return Promise.reject(new Error(res.error || 'Error'));
+    }
+
     return res;
   },
   error => {

+ 9 - 32
src/views/account_manager/index.vue

@@ -31,7 +31,11 @@
           />
         </el-col>
         <el-col :span="2">
-          <el-button class="search-button" @click="queryAccountList">Search</el-button>
+          <el-button
+            class="search-button"
+            icon="el-icon-search"
+            @click="queryAccountList"
+          ></el-button>
         </el-col>
       </el-row>
     </div>
@@ -40,7 +44,7 @@
       <div class="account-manager-list-title">
         <div>账户管理</div>
       </div>
-      <el-table :data="user_list" height="500" max-height="500">
+      <el-table :data="user_list">
         <el-table-column prop="user_name" label="用户名" width="180" />
         <el-table-column prop="real_name" label="姓名" width="180" />
         <el-table-column prop="user_type_name" label="用户类型" width="120" />
@@ -147,6 +151,7 @@ export default {
 
 .account-manager {
   @include container;
+  @include pagination;
 
   padding: 24px 0 46px;
 
@@ -171,44 +176,16 @@ export default {
     }
 
     .search-button {
-      width: 100%;
+      float: right;
     }
   }
 
   &-list {
-    width: 100%;
-    height: 594px;
-    border-radius: 8px;
-    margin-top: 24px;
-    background-color: #fff;
+    @include list;
 
     &-title {
-      height: 78px;
-      line-height: 78px;
-      padding: 0 30px;
       font-size: 20px;
       font-weight: 400;
-      border-bottom: 1px solid #d9d9d9;
-    }
-
-    .el-table {
-      width: 100%;
-
-      &__header th:first-child,
-      &__body .el-table__row > td:first-child {
-        padding-left: 20px;
-        font-weight: 700;
-      }
-    }
-  }
-
-  .el-pagination {
-    margin-top: 24px;
-
-    &.is-background .btn-next,
-    &.is-background .btn-prev,
-    &.is-background .el-pager li {
-      background-color: #fff;
     }
   }
 }

+ 2 - 42
src/views/login/index.vue

@@ -1,15 +1,5 @@
 <template>
   <div class="login">
-    <div class="login-top">
-      <span
-        v-for="item in tabList"
-        :key="item.id"
-        :class="[item.id === curTab ? 'active' : '']"
-        @click="curTab = item.id"
-      >
-        {{ item.name }}
-      </span>
-    </div>
     <div class="login-container">
       <div class="login-container-logo"></div>
       <div class="login-container-title">欢迎使用全球汉语教学平台后台管理系统</div>
@@ -92,14 +82,9 @@ export default {
     };
 
     return {
-      curTab: 'home',
       isRemember: false,
       loading: false,
       isAgree: true,
-      tabList: [
-        { id: 'home', name: '主页' },
-        { id: 'system', name: '系统' }
-      ],
       loginForm: {
         user_name: 'admin',
         password: '123456',
@@ -111,14 +96,6 @@ export default {
       }
     };
   },
-  watch: {
-    $router: {
-      handler: function (router) {
-        this.redirect = router.query && router.query.redirect;
-      }
-    }
-  },
-  created() {},
   methods: {
     handleLogin() {
       this.$refs.loginForm.validate(valid => {
@@ -127,7 +104,7 @@ export default {
           this.$store
             .dispatch('user/login', { loginForm: this.loginForm })
             .then(() => {
-              this.$router.push({ path: this.redirect || '/' });
+              this.$router.push({ path: '/jump' });
               this.loading = false;
               this.$message.success('登录成功!');
             })
@@ -151,24 +128,7 @@ export default {
   background: linear-gradient(rgba(0, 0, 0, 0.45), rgba(0, 0, 0, 0.45)),
     url('../../assets/login.png');
   background-size: cover;
-  // 顶部
-  &-top {
-    height: 55px;
-    padding: 27px 24px 0;
-    color: #fff;
-
-    span {
-      display: inline-block;
-      text-align: center;
-      padding-bottom: 5px;
-      width: 82px;
-      cursor: pointer;
-
-      &.active {
-        border-bottom: 1px solid #f90;
-      }
-    }
-  }
+  padding-top: 55px;
 
   // 主容器
   &-container {

+ 7 - 41
src/views/org_manager/index.vue

@@ -6,7 +6,7 @@
       prefix-icon="el-icon-search"
       @keyup.enter.native="queryOrgList"
     >
-      <el-button slot="append" @click="queryOrgList">Search</el-button>
+      <el-button slot="append" icon="el-icon-search" @click="queryOrgList"></el-button>
     </el-input>
     <div class="org-manager-list">
       <div class="org-manager-list-title">
@@ -15,18 +15,11 @@
           <el-button icon="el-icon-plus" @click="$router.push('/add_org')">创建机构</el-button>
         </div>
       </div>
-      <el-table :data="org_list" height="500" max-height="500">
+      <el-table :data="org_list">
         <el-table-column prop="name" label="名称" width="240" />
-        <el-table-column prop="teacher_count" label="注册教师人数" width="180">
-          <template slot-scope="scope">
-            <span>{{ scope.row.teacher_count }}人</span>
-          </template>
-        </el-table-column>
-        <el-table-column prop="teacher_count_audited" label="审核通过的注册教师人数">
-          <template slot-scope="scope">
-            <span>{{ scope.row.teacher_count_audited }}人</span>
-          </template>
-        </el-table-column>
+        <el-table-column prop="teacher_count" label="注册教师人数" width="110" />
+        <el-table-column prop="teacher_count_audited" label="审核通过的注册教师人数" width="180" />
+        <el-table-column prop="admin_user_name" label="机构管理员" />
         <el-table-column fixed="right" width="180">
           <template slot-scope="scope">
             <el-row type="flex" justify="space-between">
@@ -112,6 +105,7 @@ export default {
 
 .org-manager {
   @include container;
+  @include pagination;
 
   padding: 24px 0 46px;
 
@@ -120,45 +114,17 @@ export default {
   }
 
   &-list {
-    width: 100%;
-    height: 594px;
-    border-radius: 8px;
-    margin-top: 24px;
-    background-color: #fff;
+    @include list;
 
     &-title {
       display: flex;
       justify-content: space-between;
-      height: 78px;
-      line-height: 78px;
-      padding: 0 30px;
-      border-bottom: 1px solid #d9d9d9;
 
       &:first-child {
         font-size: 24px;
         font-weight: 700;
       }
     }
-
-    .el-table {
-      width: 100%;
-
-      &__header th:first-child,
-      &__body .el-table__row > td:first-child {
-        padding-left: 20px;
-        font-weight: 700;
-      }
-    }
-  }
-
-  .el-pagination {
-    margin-top: 24px;
-
-    &.is-background .btn-next,
-    &.is-background .btn-prev,
-    &.is-background .el-pager li {
-      background-color: #fff;
-    }
   }
 }
 </style>

+ 99 - 30
src/views/teacher_manager/index.vue

@@ -20,7 +20,11 @@
           />
         </el-col>
         <el-col :span="2">
-          <el-button class="search-button" @click="queryOrgTeacherUserList">Search</el-button>
+          <el-button
+            class="search-button"
+            icon="el-icon-search"
+            @click="queryOrgTeacherUserList"
+          ></el-button>
         </el-col>
       </el-row>
     </div>
@@ -29,7 +33,7 @@
       <div class="teacher-manager-list-title">
         <div>教师列表</div>
       </div>
-      <el-table :data="org_teacher_user_list" height="500" max-height="500">
+      <el-table :data="org_teacher_user_list">
         <el-table-column prop="user_name" label="用户名" width="180" />
         <el-table-column prop="user_real_name" label="姓名" width="180" />
         <el-table-column prop="org_name" label="服务机构" width="180" />
@@ -52,6 +56,23 @@
                   审核{{ scope.row.is_audited === 'true' ? '拒绝' : '同意' }}
                 </el-link>
               </el-col>
+              <el-col>
+                <el-popover trigger="click" width="200" @show="getPopedomList(scope.row.id)">
+                  <div class="popedom">
+                    <span class="popedom-manager">权限管理</span>
+                    <div v-for="item in popedom_list" :key="item.popedom_code" class="popedom-list">
+                      <span>{{ item.popedom_name }}</span>
+                      <el-switch
+                        v-model="item.is_selected"
+                        active-color="#34CC83"
+                        inactive-color="#C4C4C4"
+                      />
+                    </div>
+                    <el-button type="primary" @click="setPopedom(scope.row.id)">保存</el-button>
+                  </div>
+                  <el-link slot="reference" :underline="false">权限</el-link>
+                </el-popover>
+              </el-col>
             </el-row>
           </template>
         </el-table-column>
@@ -75,7 +96,11 @@
 
 <script>
 import { pageQueryOrgTeacherUserList } from '@/api/list';
-import { auditOrgTeacherUser } from '@/api/teacher';
+import {
+  auditOrgTeacherUser,
+  getPopedomList_OrgTeacherUse,
+  setPopedom_OrgTeacherUser
+} from '@/api/teacher';
 
 export default {
   name: 'TeacherManager',
@@ -86,7 +111,8 @@ export default {
       org_teacher_user_list: [],
       page_capacity: 10,
       total_count: 0,
-      cur_page: 1
+      cur_page: 1,
+      popedom_list: []
     };
   },
   created() {
@@ -117,6 +143,7 @@ export default {
         }
       });
     },
+    // 审核教师
     auditOrgTeacher(row) {
       let data = {
         id_list: [row.id],
@@ -132,6 +159,52 @@ export default {
           });
         }
       });
+    },
+    // 得到权限列表
+    getPopedomList(user_org_id) {
+      getPopedomList_OrgTeacherUse({ user_org_id }).then(response => {
+        if (response.status) {
+          response.popedom_list.forEach((el, i, arr) => {
+            el.is_selected = el.is_selected === 'true';
+            console.log(arr);
+            arr[i] = el;
+          });
+          this.popedom_list = response.popedom_list;
+          console.log(this.popedom_list);
+        } else {
+          this.$message({
+            type: 'error',
+            message: response.error
+          });
+        }
+      });
+    },
+    // 设置教师权限
+    setPopedom(user_org_id) {
+      let popedom_code_list = this.popedom_list.filter(el => {
+        return el.is_selected;
+      });
+      popedom_code_list.forEach((el, i, arr) => {
+        arr[i] = el.popedom_code;
+      });
+
+      let data = {
+        user_org_id,
+        popedom_code_list
+      };
+      setPopedom_OrgTeacherUser(data).then(response => {
+        if (response.status) {
+          this.$message({
+            type: 'success',
+            message: '设置教师权限成功'
+          });
+        } else {
+          this.$message({
+            type: 'error',
+            message: response.error
+          });
+        }
+      });
     }
   }
 };
@@ -142,6 +215,7 @@ export default {
 
 .teacher-manager {
   @include container;
+  @include pagination;
 
   padding: 24px 0 46px;
 
@@ -156,45 +230,40 @@ export default {
     }
 
     .search-button {
-      width: 100%;
+      float: right;
     }
   }
 
   &-list {
-    width: 100%;
-    height: 594px;
-    border-radius: 8px;
-    margin-top: 24px;
-    background-color: #fff;
+    @include list;
 
     &-title {
-      height: 78px;
-      line-height: 78px;
-      padding: 0 30px;
       font-size: 20px;
       font-weight: 400;
-      border-bottom: 1px solid #d9d9d9;
     }
+  }
+}
 
-    .el-table {
-      width: 100%;
-
-      &__header th:first-child,
-      &__body .el-table__row > td:first-child {
-        padding-left: 20px;
-        font-weight: 700;
-      }
-    }
+// 权限管理
+.popedom {
+  &-manager {
+    display: flex;
+    font-weight: 700;
+    justify-content: space-between;
+    padding-bottom: 8px;
+    border-bottom: 1px solid #d9d9d9;
   }
 
-  .el-pagination {
-    margin-top: 24px;
+  &-list {
+    padding-top: 10px;
+    display: flex;
+    justify-content: space-between;
+  }
 
-    &.is-background .btn-next,
-    &.is-background .btn-prev,
-    &.is-background .el-pager li {
-      background-color: #fff;
-    }
+  .el-button {
+    width: 100%;
+    padding: 6px 20px;
+    margin-top: 16px;
   }
 }
 </style>