Bläddra i källkod

新增-权限管理模块

吴朋磊 1 månad sedan
förälder
incheckning
944253ed8b

+ 1 - 0
index.html

@@ -4,6 +4,7 @@
     <meta charset="UTF-8" />
     <link rel="icon" type="image/svg+xml" href="/favicon.ico" />
     <link rel="stylesheet" href="//at.alicdn.com/t/c/font_4939100_4d2bva7l2pq.css">
+    <link rel="stylesheet" href="//at.alicdn.com/t/c/font_4939100_xa8g84rmxb.css">
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
     <meta name="robots" content="noindex, nofollow" />
     <title>后台管理系统</title>

+ 5 - 0
src/api/school.ts

@@ -101,3 +101,8 @@ export const loginTeach = (data: any) => {
 export const resetSchoolPassWord = (data: any) => {
   return http.post('/adminApi/v1/resetSchoolInfo', data)
 }
+
+// 学校管理--教师列表
+export const getAllTeacher = (params: any) => {
+  return http.get('/adminApi/v1/teacher/teacher_list', params)
+}

BIN
src/assets/icon/back.png


+ 23 - 23
src/layout/components/SiderBar.vue

@@ -88,30 +88,30 @@ onMounted(() => {
 
     console.log('isOpsUser:', isOpsUser, 'hasAdminMenu:', hasAdminMenu)
 
-    // if (!isOpsUser && hasAdminMenu) {
-    //   // 非运维人员且有管理员菜单,添加日志监控和业务监控
-    //   const additionalMenuList = [
-    //     {
-    //       icon: "icon_rizhijiankong",
-    //       route: "/main/logMonitor",
-    //       title: "日志监控",
-    //       label:'',
-    //     },
-    //     {
-    //       icon: "icon_yewujiankong",
-    //       route: "/main/performanceMonitor",
-    //       title: "业务监控",
-    //       label:'',
-    //     },
-    //   ]
+    if (!isOpsUser && hasAdminMenu) {
+       // 非运维人员且有管理员菜单,添加日志监控和业务监控
+      const additionalMenuList = [
+         {
+           icon: "icon_rizhijiankong",
+           route: "/main/logMonitor",
+           title: "日志监控",
+           label:'',
+         },
+         {
+           icon: "icon_yewujiankong",
+           route: "/main/performanceMonitor",
+           title: "业务监控",
+           label:'',
+         },
+       ]
 
-    //   additionalMenuList.forEach(item => {
-    //     const exists = menuItems.value?.some(menu => menu.route === item.route)
-    //     if (!exists) {
-    //       menuItems.value?.push(item)
-    //     }
-    //   })
-    // }
+       additionalMenuList.forEach(item => {
+         const exists = menuItems.value?.some(menu => menu.route === item.route)
+         if (!exists) {
+           menuItems.value?.push(item)
+         }
+       })
+     }
   }
 
 });

+ 5 - 0
src/router/index.ts

@@ -24,6 +24,11 @@ const routes: Array<RouteRecordRaw> = [
         name: 'school',
         component: () => import('@/views/school/index.vue')
       },
+      {
+        path: 'teacherList',
+        name: 'teacherList',
+        component: () => import('@/views/school/teacherList.vue')
+      },
       {
         path: 'manager',
         name: 'manager',

+ 26 - 22
src/views/school/index.vue

@@ -178,9 +178,10 @@
                 {{ row.sysUserName ||  '-' }}
               </template>
             </el-table-column>
-            <el-table-column label="操作" width="280" fixed="right" align="center">
+            <el-table-column label="操作" width="360" fixed="right" align="center">
               <template #default="{ row }">
                 <div class="table_row_option">
+                  <span class="button_editor" :class="row.status == 0 || row.dateDiffDayS==0 ? 'disabled_text' : ''" @click="GoTeacherList(row)" v-if="permissions.includes('UserInfo') ">教师列表</span>
                   <span v-if="row.status == 0 && permissions.includes('schoolFreeze')" class="button_delete" @click="ChangeStatus(row, '解冻')">
                     解冻
                   </span>
@@ -221,9 +222,12 @@ import ResetPassword from '@/components/resetPassword.vue'
 import { Search } from '@element-plus/icons-vue'
 import { ElMessageBox, ElMessage } from 'element-plus'
 import { ref, reactive, provide, onMounted, nextTick, onUnmounted } from 'vue'
+import { useRouter } from 'vue-router'
 import { getSchoolNumber, getSchoolList, changeStatus ,getTicket, getSearchList} from '@/api/school'
 import { throttle } from 'lodash';
 
+const router = useRouter()
+
 interface UserNumber {
   allUser: number
   officialUser: number
@@ -601,6 +605,16 @@ const ChangeStatus = (row: any, text: String) => {
     })
   })
 }
+// 跳转到教师列表
+const GoTeacherList = (row: any) => {
+  router.push({
+    path: '/main/teacherList',
+    query: {
+      schoolId: row.id,
+      schoolName: row.tenantName
+    }
+  })
+}
 // 进入学校
 const GoTeach = (row: any) => {
   //剩余天数为0的禁止进入学校
@@ -614,33 +628,23 @@ const GoTeach = (row: any) => {
     return
   }
 
-  console.log("打印row",row)
-   let params = {
+  const params = {
     id: row.id,
   }
   
   getTicket(params).then((res: any) => {
-    console.log("打印获取票据接口返回res",res)
-    if(res.code == 200 && res.data) 
-    {
-      let ticket = res.data;
-      let url=`https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//正式环境
+    const ticket = res.data;
+    let url = `https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;
 
-      if(import.meta.env.VITE_API_BASE_URL=='https://admin.k12100.com/admin/')
-      {
-        url=`https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//正式环境
-      }
-      else
-      {
-        url=`https://index.k12100.net/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//测试环境
-      }
-      // let url=`https://dev3.k12100.net/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;//测试环境
-      console.log("打印获取票据接口返回ticket",ticket)
-      console.log("打印第三方登录地址",url);
-      // window.open(`https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`)
-      //  window.open(url)
+    if(import.meta.env.VITE_API_BASE_URL == 'https://admin.k12100.com/admin/')
+    {
+      url = `https://www.k12100.com/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;
     }
-    
+    else
+    {
+      url = `https://index.k12100.net/#/otherLogin?schoolId=${row.id}&ticket=${ticket}`;
+    }
+    window.open(url);
   }).catch(() => {
    
   })

+ 405 - 0
src/views/school/teacherList.vue

@@ -0,0 +1,405 @@
+<template>
+  <div class="teacher_list_container">
+    <!-- 顶部导航 -->
+    <div class="header">
+       <el-button class="back_button" @click="GoBack">
+        <i class="iconfont icon_xiangzuo back_icon" ></i>
+        返回
+      </el-button>
+      <div class="school_name">{{ schoolName }}</div>
+    </div>
+
+    <!-- 筛选区域 -->
+    <div class="filter_area">
+      <TabSearch.default
+        :tabList="tabList" 
+        tabType="box" 
+        :defaultTabKey="currentTab" 
+        @tabChange="ChangeTab"
+      />
+      <div class="search_area">
+        <el-input 
+          v-model="searchKeyword" 
+          placeholder="请输入姓名/账号"
+          @input="HandleSearch"
+        >
+          <template #append>
+            <el-button :icon="Search" @click="HandleSearch" />
+          </template>
+        </el-input>
+      </div>
+    </div>
+
+    <!-- 表格区域 -->
+    <div class="page_table">
+      <el-table 
+        :data="tableData" 
+        stripe 
+        v-loading="tableLoading" 
+        element-loading-text="拼命加载中……"
+        :height="tableHeight"
+      >
+        <el-table-column type="index" label="序号" width="60" align="center" />
+        <el-table-column prop="userAccount" label="账号" min-width="100" align="center" show-overflow-tooltip />
+        <el-table-column prop="userName" label="姓名" min-width="100" align="center">
+          <template #default="{ row }">
+            <span class="name_text" @click="GoEnter(row)">{{ row.userName }}</span>
+          </template>
+        </el-table-column>
+        <el-table-column prop="wxName" label="微信名" min-width="100" align="center">
+          <template #default="{ row }">
+            {{ row.wxName || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="phone" label="手机号" min-width="120" align="center">
+          <template #default="{ row }">
+            {{ row.phone || '-' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="binXKW" label="是否绑定学科网" width="160" align="center">
+          <template #default="{ row }">
+            {{ row.binXKW === 1 ? '是' : '否' }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="status" label="状态" width="80" align="center">
+          <template #default="{ row }">
+            <div class="status_container">
+              <span 
+                class="status_dot" 
+                :class="row.status === 1 ? 'active' : 'disabled'"
+              ></span>
+              <span :class="row.status === 1 ? 'status_active' : 'status_disabled'">
+                {{ row.status === 1 ? '启用' : '禁用' }}
+              </span>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" width="80" align="center" fixed="right">
+          <template #default="{ row }">
+            <span class="enter_button" @click="GoEnter(row)">进入</span>
+          </template>
+        </el-table-column>
+      </el-table>
+    </div>
+
+    <!-- 分页器 -->
+    <div class="page_pagination">
+      <el-pagination 
+        v-model:current-page="pageNum" 
+        v-model:page-size="pageSize" 
+        :page-sizes="[20, 50, 100]" 
+        background 
+        layout="sizes, prev, pager, next" 
+        :total="total" 
+        @current-change="HandleCurrentChange" 
+        @size-change="HandleSizeChange" 
+      />
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { ref, onMounted, onUnmounted, nextTick } from 'vue'
+import { useRoute, useRouter } from 'vue-router'
+import { Search } from '@element-plus/icons-vue'
+import { ElMessage } from 'element-plus'
+import { throttle } from 'lodash'
+import * as TabSearch from '@/baseComponents/TabSearch.vue'
+import { getAllTeacher, getTicket } from '@/api/school'
+
+const route = useRoute()
+const router = useRouter()
+
+// 学校信息
+const schoolName = ref('')
+const schoolId = ref('')
+
+// Tab列表
+interface TabItem {
+  label: string
+  key: string
+  prefix?: string
+  prefixLight?: string
+  suffix?: string | number
+}
+const tabList = ref<TabItem[]>([
+  { label: '全部', key: 'all' },
+  { label: '已绑定学科网', key: 'bound' },
+  { label: '未绑定学科网', key: 'unbound' },
+])
+
+// Tab切换
+const currentTab = ref('all')
+
+// 搜索关键字
+const searchKeyword = ref('')
+
+// 表格数据
+interface Teacher {
+  userId: number | string
+  userAccount: string
+  userName: string
+  wxName: string
+  phone: string
+  status: number
+  binXKW: number
+  [key: string]: any
+}
+
+const tableData = ref<Teacher[]>([])
+const allData = ref<Teacher[]>([])
+const tableLoading = ref(false)
+const pageNum = ref(1)
+const pageSize = ref(20)
+const total = ref(0)
+const tableHeight = ref(0)
+
+// 前端筛选 + 分页
+const FilterAndPaginate = () => {
+  let filteredData = [...allData.value]
+
+  // tab筛选
+  if (currentTab.value === 'bound') {
+    filteredData = filteredData.filter(item => item.binXKW === 1)
+  } else if (currentTab.value === 'unbound') {
+    filteredData = filteredData.filter(item => item.binXKW === 0)
+  }
+
+  // 搜索筛选
+  if (searchKeyword.value) {
+    const keyword = searchKeyword.value.toLowerCase()
+    filteredData = filteredData.filter(item =>
+      (item.userName || '').toLowerCase().includes(keyword) ||
+      (item.userAccount || '').toLowerCase().includes(keyword)
+    )
+  }
+
+  total.value = filteredData.length
+  const start = (pageNum.value - 1) * pageSize.value
+  const end = start + pageSize.value
+  tableData.value = filteredData.slice(start, end)
+}
+
+// 获取教师列表(仅初始化时调用)
+const GetTeacherList = async () => {
+  tableLoading.value = true
+  try {
+    const params: Record<string, any> = {
+      schoolId: schoolId.value,
+    }
+    const res = await getAllTeacher(params)
+    allData.value = res.data || []
+    FilterAndPaginate()
+  } catch (error) {
+    console.error('获取教师列表失败', error)
+  } finally {
+    tableLoading.value = false
+  }
+}
+
+// 切换Tab(前端筛选)
+const ChangeTab = (tab: string) => {
+  currentTab.value = tab
+  pageNum.value = 1
+  FilterAndPaginate()
+}
+
+// 搜索(前端筛选)
+const HandleSearch = () => {
+  pageNum.value = 1
+  FilterAndPaginate()
+}
+
+// 分页操作(前端分页)
+const HandleCurrentChange = (val: number) => {
+  pageNum.value = val
+  FilterAndPaginate()
+}
+
+const HandleSizeChange = (val: number) => {
+  pageSize.value = val
+  pageNum.value = 1
+  FilterAndPaginate()
+}
+
+// 返回
+const GoBack = () => {
+  router.back()
+}
+
+// 进入
+const GoEnter = (row: Teacher) => {
+  // 禁用状态禁止进入
+  if (row.status === 0) {
+    ElMessage.error('该教师已禁用,无法进入')
+    return
+  }
+  const params = {
+    id: schoolId.value,
+  }
+
+  getTicket(params).then((res: any) => {
+    const ticket = res.data
+    let url = `https://www.k12100.com/#/otherLogin?schoolId=${schoolId.value}&ticket=${ticket}`
+
+    if (import.meta.env.VITE_API_BASE_URL === 'https://admin.k12100.com/admin/') {
+      url = `https://www.k12100.com/#/otherLogin?schoolId=${schoolId.value}&ticket=${ticket}`
+    } else {
+      url = `https://index.k12100.net/#/otherLogin?schoolId=${schoolId.value}&ticket=${ticket}`
+    }
+    window.open(url)
+  }).catch(() => {})
+}
+
+// 计算表格高度
+const SetTableHeight = () => {
+  const containerHeight = document.documentElement.clientHeight
+  const headerHeight = 60 // 顶部导航
+  const filterHeight = 100 // 筛选区域
+  const paginationHeight = 100 // 分页器
+  const marginHeight = 40 // 边距
+  tableHeight.value = containerHeight - headerHeight - filterHeight - paginationHeight - marginHeight
+}
+
+// 监听窗口大小变化
+const HandleResize = throttle(() => {
+  SetTableHeight()
+}, 500)
+
+onMounted(() => {
+  // 获取路由参数
+  schoolName.value = route.query.schoolName as string || ''
+  schoolId.value = route.query.schoolId as string || ''
+  
+  nextTick(() => {
+    SetTableHeight()
+    GetTeacherList()
+  })
+  
+  window.addEventListener('resize', HandleResize)
+})
+
+onUnmounted(() => {
+  window.removeEventListener('resize', HandleResize)
+})
+</script>
+
+<style lang="scss" scoped>
+.teacher_list_container {
+  margin: 20px;
+  box-sizing: border-box;
+  background:#fff;
+  border-radius: 10px;
+  padding:20px;
+  width: 100%;
+  overflow: hidden;
+}
+
+// 顶部导航
+.header {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  margin-bottom: 20px;
+  
+  .back_button {
+    width: 82px;
+    height: 36px;
+    background: #FFFFFF !important;
+    border-radius: 4px;
+    border: 1px solid #DCDFE6 !important;
+    color: #606266;
+    font-size: 14px;
+    padding: 0;
+    .back_icon {
+      width: 14px;
+      height: 14px;
+      font-size: 14px;
+      vertical-align: middle;
+      color: #333;
+      margin-right: 5px;
+    }
+  }
+  
+  .school_name {
+    font-size: 16px;
+    font-weight: 550;
+    color: #333;
+  }
+}
+
+// 筛选区域
+.filter_area {
+  display: flex;
+  align-items: center;
+  gap: 20px;
+  border-radius: 8px;
+  margin-bottom: 16px;
+  
+  .search_area {
+    flex: 1;
+    max-width: 217px;
+  }
+  
+  // TabSearch 组件样式穿透
+  :deep(.box_tab) {
+    .un_selected {
+      color: #BBBBBB !important;
+    }
+  }
+}
+
+.page_table {
+  width: 100%;
+  min-width: 0;
+  overflow: hidden;
+}
+// 表格内自定义样式
+.name_text {
+  color: #2E64FA;
+  cursor: pointer;
+  
+  &:hover {
+    text-decoration: underline;
+  }
+}
+
+// 状态样式
+.status_container {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 4px;
+}
+
+.status_dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  
+  &.active {
+    background: #2BC644;
+  }
+  
+  &.disabled {
+    background: #F56C6C;
+  }
+}
+
+.status_active {
+  color: #2BC644;
+}
+
+.status_disabled {
+  color: #F56C6C;
+}
+
+.enter_button {
+  color: #2E64FA;
+  cursor: pointer;
+  
+  &:hover {
+    text-decoration: underline;
+  }
+}
+</style>

+ 1 - 1
tsconfig.app.json

@@ -15,5 +15,5 @@
     "noFallthroughCasesInSwitch": true,
     "noUncheckedSideEffectImports": true
   },
-  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"]
+  "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue", "src/**/*.d.ts"]
 }

+ 1 - 0
tsconfig.json

@@ -5,6 +5,7 @@
     { "path": "./tsconfig.node.json" }
   ],
   "compilerOptions": {
+    "module": "ESNext",
     "moduleResolution": "node",
     "types": ["vuex"],
     "baseUrl": "./",