소스 검색

feat: 管理员审核、封禁、解禁功能完成

ChenYangfan 6 년 전
부모
커밋
3ba4f4203c
5개의 변경된 파일252개의 추가작업 그리고 45개의 파일을 삭제
  1. 1 6
      src/components/Basic/SearchBar.vue
  2. 75 39
      src/views/admin/Admin.vue
  3. 68 0
      src/views/admin/CheckingUsersTable.vue
  4. 82 0
      src/views/admin/UsersTable.vue
  5. 26 0
      src/views/admin/table-common.scss

+ 1 - 6
src/components/Basic/SearchBar.vue

@@ -9,7 +9,7 @@
         @input="$emit('input', $event.target.value)"
       />
     </label>
-    <c-icon bold :size="24" @click="handleIconClick">{{this.value ? 'close' : 'search'}}</c-icon>
+    <c-icon bold :size="24">search</c-icon>
   </div>
 </template>
 
@@ -30,11 +30,6 @@ export default {
     return {
       iconMap: iconMap
     }
-  },
-  methods: {
-    handleIconClick () {
-      this.value ? this.value = '' : this.$refs.input.focus()
-    }
   }
 }
 </script>

+ 75 - 39
src/views/admin/Admin.vue

@@ -6,25 +6,29 @@
       <div class="spacer"></div>
       <c-button class="exit" text @click="handleLogout" icon="exit_to_app">注销</c-button>
     </div>
+
     <div class="sub-title">教师用户审核</div>
-    <table class="checking-table">
-      <tr>
-        <td>邮箱</td>
-        <td>姓名</td>
-        <td>手机号</td>
-        <td>申请时间</td>
-        <td class="table-center">操作</td>
-      </tr>
-      <tr v-for="user of checkingUsers" :key="user.id">
-        <td>{{user.email}}</td>
-        <td>{{user.name}}</td>
-        <td>{{user.phone}}</td>
-        <td>{{momentFromNow(user.createAt)}}</td>
-        <td class="table-center">
-          <c-button text icon="check">审核通过</c-button>
-        </td>
-      </tr>
-    </table>
+    <checking-users-table :checking-users="checkingUsers" @update="updateData"></checking-users-table>
+
+    <div class="sub-title mt-32">用户管理</div>
+    <search-bar class="mb-16" v-model="searchKey" @input="debounceSearch"></search-bar>
+    <div class="vertical-center mb-16">
+      <c-button icon="face" :theme="searchType === userType.student ? 'green' : 'normal'" class="mr-8"
+                @click="searchType = userType.student">查看学生
+      </c-button>
+      <c-button icon="supervised_user_circle" class="mr-8" :theme="searchType === userType.teacher ? 'blue' : 'normal'"
+                @click="searchType = userType.teacher">查看教师
+      </c-button>
+      <div class="spacer"></div>
+      <c-button :icon="searchState === userState.normal ? 'lock' : 'lock_open'"
+                @click="searchState === userState.normal ? searchState = userState.locked : searchState = userState.normal"
+                :theme="searchState === userState.normal ? 'red' : 'green'">
+        查看{{searchState === userState.normal ? '封禁' : '正常'}}账号
+      </c-button>
+    </div>
+    <users-table :users="users" @update-users="updateUsers({})"></users-table>
+    <pagination @change="handlePageChange" v-show="users && users.length > 0" :page="page" :size="size"
+                :total-elements="totalElements"></pagination>
   </div>
 </template>
 
@@ -36,33 +40,82 @@ import { getUsers } from '@/server/admin'
 import { userState } from '@/server/enums/user-state'
 import userType from '@/server/enums/user-type'
 import momentFromNow from '@/utils/moment-from-now'
+import CheckingUsersTable from '@/views/admin/CheckingUsersTable'
+import UsersTable from '@/views/admin/UsersTable'
+import SearchBar from '@/components/Basic/SearchBar'
+import debounce from '@/utils/debounce'
+import Pagination from '@/components/Pagination'
 
 export default {
-  components: { CButton, CIcon },
+  components: { Pagination, SearchBar, UsersTable, CheckingUsersTable, CButton, CIcon },
   data () {
     return {
       checkingUsers: null,
 
+      page: 1,
+      totalElements: 10,
+      size: 12,
+      users: null,
+      searchKey: '',
+      searchType: userType.student,
+      searchState: userState.normal,
+
       userType,
-      userState
+      userState,
+
+      debounceSearch: debounce(() => {
+        this.page = 1
+        this.updateUsers({})
+      }, 300)
     }
   },
   mounted () {
     this.updateData()
   },
+  watch: {
+    searchState () {
+      this.page = 1
+      this.updateUsers({})
+    },
+    searchType () {
+      this.page = 1
+      this.updateUsers({})
+    }
+  },
   methods: {
     updateData () {
+      this.updateCheckingUsers()
+      this.updateUsers({})
+    },
+    updateCheckingUsers () {
       // 获取状态为正在审核中的用户
       getUsers({ state: userState.checking, type: userType.teacher, size: 200 })
         .then(res => { this.checkingUsers = res.data })
         .catch(err => { this.$setErrorMessage(err.response.data.msg) })
     },
+    updateUsers ({ page = this.page }) {
+      // 按条件获取所有用户
+      getUsers({ page, key: this.searchKey, state: this.searchState, type: this.searchType, size: this.size })
+        .then(res => {
+          console.log(res)
+          this.users = res.data
+          this.totalElements = res.page.totalElements
+          if (this.page > res.page.totalPages) {
+            this.page = res.page.totalPages
+            this.updateUsers({})
+          }
+        })
+        .catch(err => { this.$setErrorMessage(err.response.data.msg) })
+    },
     handleLogout () {
       logout()
       this.$setSuccessMessage('您已退出登录')
       this.$router.push({ name: 'login' })
     },
-
+    handlePageChange ({ page }) {
+      this.page = page
+      this.updateUsers({ page })
+    },
     momentFromNow
   }
 }
@@ -70,6 +123,7 @@ export default {
 
 <style lang="scss" scoped>
 @import "~@/style/theme.scss";
+
 .admin {
   margin: 64px auto;
   max-width: 800px;
@@ -80,22 +134,4 @@ export default {
   width: 1px;
   flex: 1;
 }
-
-.checking-table {
-  width: 100%;
-  margin: 8px -8px;
-
-  .table-center {
-    text-align: center;
-  }
-
-  tr:first-child {
-    color: $theme-blue;
-    background-color: $card-background;
-  }
-
-  td {
-    padding: 4px 8px;
-  }
-}
 </style>

+ 68 - 0
src/views/admin/CheckingUsersTable.vue

@@ -0,0 +1,68 @@
+<template>
+  <div class="checking-users-table">
+    <div v-if="this.checkingUsers === null" class="no-content-warning">
+      加载中...
+    </div>
+    <div v-else-if="!this.checkingUsers.length" class="no-content-warning">
+      暂无待审核的新用户
+    </div>
+    <table v-else class="common-table">
+      <tr>
+        <td>邮箱</td>
+        <td>姓名</td>
+        <td>手机号</td>
+        <td>申请时间</td>
+        <td class="table-center">操作</td>
+      </tr>
+      <tr v-for="user of checkingUsers" :key="user.id">
+        <td>{{user.email}}</td>
+        <td>{{user.name}}</td>
+        <td>{{user.phone}}</td>
+        <td>{{momentFromNow(user.createAt)}}</td>
+        <td class="table-center">
+          <c-button text icon="check" theme="green" @click="handleCheckButtonClick(user)">确认通过</c-button>
+        </td>
+      </tr>
+    </table>
+  </div>
+</template>
+
+<script>
+import CButton from '@/components/Basic/CButton'
+import { passUser } from '@/server/admin'
+import { userState } from '@/server/enums/user-state'
+import userType from '@/server/enums/user-type'
+import momentFromNow from '@/utils/moment-from-now'
+
+export default {
+  components: { CButton },
+  props: {
+    checkingUsers: Array
+  },
+  data () {
+    return {
+      userType,
+      userState
+    }
+  },
+  methods: {
+    handleCheckButtonClick (user) {
+      const msg = `本操作将使以下账号可用:\n\n${user.email} - ${user.name}`
+      if (confirm(msg)) {
+        passUser({ userId: user.id })
+          .then(() => {
+            this.$setSuccessMessage('审核通过,该账号目前已可用')
+            this.$emit('update')
+          })
+          .catch(err => this.$setErrorMessage(err.response.data.msg))
+      }
+    },
+
+    momentFromNow
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "./table-common";
+</style>

+ 82 - 0
src/views/admin/UsersTable.vue

@@ -0,0 +1,82 @@
+<template>
+  <div class="users-table">
+    <div v-if="this.users === null" class="no-content-warning">
+      加载中...
+    </div>
+    <div v-else-if="!this.users.length" class="no-content-warning">
+      无符合条件的用户
+    </div>
+    <table v-else class="common-table">
+      <tr>
+        <td>邮箱</td>
+        <td>姓名</td>
+        <td>手机号</td>
+        <td>申请时间</td>
+        <td class="table-center">操作</td>
+      </tr>
+      <tr v-for="user of users" :key="user.id">
+        <td>{{user.email}}</td>
+        <td>{{user.name}}</td>
+        <td>{{user.phone}}</td>
+        <td>{{momentFromNow(user.createAt)}}</td>
+        <td class="table-center">
+          <c-button v-if="user.state === userState.normal" text icon="lock" theme="red" @click="handleLock(user)">封禁
+          </c-button>
+          <c-button v-if="user.state === userState.locked" text icon="lock_open" theme="green"
+                    @click="handleUnlock(user)">解禁
+          </c-button>
+        </td>
+      </tr>
+    </table>
+  </div>
+</template>
+
+<script>
+import CButton from '@/components/Basic/CButton'
+import { userState } from '@/server/enums/user-state'
+import userType from '@/server/enums/user-type'
+import momentFromNow from '@/utils/moment-from-now'
+import { lockUser, unlockUser } from '@/server/admin'
+
+export default {
+  components: { CButton },
+  props: {
+    users: Array
+  },
+  data () {
+    return {
+      userType,
+      userState
+    }
+  },
+  methods: {
+    handleLock (user) {
+      const msg = `您确定要封禁以下账号吗?\n\n- ${user.email} - ${user.name}`
+      if (confirm(msg)) {
+        lockUser({ userId: user.id })
+          .then(() => {
+            this.$setSuccessMessage('已将本账号封禁处理')
+            this.$emit('update-users')
+          })
+          .catch(err => this.$setErrorMessage(err.response.data.msg))
+      }
+    },
+    handleUnlock (user) {
+      const msg = `您确定要解禁以下账号吗?\n\n- ${user.email} - ${user.name}`
+      if (confirm(msg)) {
+        unlockUser({ userId: user.id })
+          .then(() => {
+            this.$setSuccessMessage('已将本账号解封')
+            this.$emit('update-users')
+          })
+          .catch(err => this.$setErrorMessage(err.response.data.msg))
+      }
+    },
+    momentFromNow
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import "./table-common";
+</style>

+ 26 - 0
src/views/admin/table-common.scss

@@ -0,0 +1,26 @@
+@import "~@/style/theme.scss";
+
+.common-table {
+  width: 100%;
+  margin: 8px 0;
+
+  .table-center {
+    text-align: center;
+  }
+
+  tr:first-child {
+    color: $theme-blue;
+    background-color: $theme-blue-transparent;
+
+    td {
+      color: #4a5a6a;
+      padding: 8px;
+    }
+  }
+
+  td {
+    padding: 6px 8px;
+    color: #7a8a9a;
+    font-size: 14px;
+  }
+}