Jelajahi Sumber

修改ts规则、左侧导航条、日志接口调用

吴朋磊 2 minggu lalu
induk
melakukan
fe72d1c066

+ 6 - 22
src/api/examination.ts

@@ -1,33 +1,17 @@
-import request from '../utils/request.ts'
+import { http } from '../utils/request'
 // 联考学校列表
 export const getJoinExamList = (params: any) => {
-  return request({
-    url: `/adminApi/v1/listSchoolICh`,
-    method: 'get',
-    params
-  })
+  return http.get('/adminApi/v1/listSchoolICh', params)
 }
 // 单考学校列表
 export const getSingleExamList = (params: any) => {
-  return request({
-    url: `/adminApi/v1/schoolOneAll`,
-    method: 'get',
-    params
-  })
+  return http.get('/adminApi/v1/schoolOneAll', params)
 }
 // 新增学校
 export const addSingleSchool = (data: any) => {
-  return request({
-    url: `/adminApi/v1/admin-child-schools`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/admin-child-schools', data)
 }
 // 删除学校
 export const deleteSchool = (data: any) => {
-  return request({
-    url: `/adminApi/v1/admin-child-schools`,
-    method: 'delete',
-    data
-  })
-}
+  return http.delete('/adminApi/v1/admin-child-schools', data)
+}

+ 16 - 0
src/api/logMonitor.ts

@@ -0,0 +1,16 @@
+import { http } from '../utils/request'
+
+// 获取日志头
+export const getLogHeadApi = () => {
+  return http.get('/adminApi/v1/sys/logs/head')
+}
+
+// 获取日志表格
+export const getLogPageApi = (params:any) => {
+  return http.post('/adminApi/v1/sys/logs/page', params)
+}
+
+// 日志详情
+export const getLogDetailApi = (params:any) => {
+  return http.post('/adminApi/v1/sys/logs/details', params)
+}

+ 5 - 20
src/api/login.ts

@@ -1,34 +1,19 @@
 // src/api/login.ts
-import request from '../utils/request.ts'
+import { http } from '../utils/request'
 // 登录接口
 export const login = (data:any) => {
-  return request({
-    url: '/adminApi/v1/auth/login',
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/auth/login', data)
 }
 
 // 获取用户信息
 export const getUserInfo = () => {
-  return request({
-    url: '/adminApi/v1/sys/users/info',
-    method: 'get'
-  })
+  return http.get('/adminApi/v1/sys/users/info')
 }
 // 退出登录
 export const loginOut = (userId: any) => {
-  return request({
-    url: `/adminApi/v1/sys/users/${userId}/kickOut`,
-    method: 'post'
-  })
+  return http.post(`/adminApi/v1/sys/users/${userId}/kickOut`)
 }
 // 修改密码
 export const changePassWord = (data: any) => {
-  return request({
-    url: `/adminApi/v1/sys/users/updatePassword`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/sys/users/updatePassword', data)
 }
-

+ 7 - 26
src/api/manager.ts

@@ -1,41 +1,22 @@
-import request from '../utils/request.ts'
+import { http } from '../utils/request'
 
 // 查询运维人员详情
 export const managerDetails = (id: any) => {
-  return request({
-    url: `/adminApi/v1/sys/users/${id}`,
-    method: 'get'
-  })
+  return http.get(`/adminApi/v1/sys/users/${id}`)
 }
 // 新增运维人员
 export const addManager = (data: any) => {
-  return request({
-    url: `/adminApi/v1/sys/users`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/sys/users', data)
 }
 // 编辑运维人员
 export const editManager = (data: any): Promise<any> => {
-  return request({
-    url: `/adminApi/v1/sys/usersPut`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/sys/usersPut', data)
 }
 // 删除运维人员
 export const deleteManager = (data: any) => {
-  return request({
-    url: `/adminApi/v1/sys/users`,
-    method: 'delete',
-    data
-  })
+  return http.delete('/adminApi/v1/sys/users', data)
 }
 // 重置密码
 export const resetPassWord = (id: any, data: any) => {
-  return request({
-    url: `/adminApi/v1/sys/users/${id}/password`,
-    method: 'put',
-    data
-  })
-}
+  return http.put(`/adminApi/v1/sys/users/${id}/password`, data)
+}

+ 25 - 104
src/api/school.ts

@@ -1,182 +1,103 @@
-import request from '../utils/request.ts'
+import { http } from '../utils/request'
 
 // 获取学校数量
 export const getSchoolNumber = () => {
-  return request({
-    url: '/adminApi/v1/adminSchoolNumber',
-    method: 'get'
-  })
+  return http.get(`/adminApi/v1/adminSchoolNumber`)
 }
 // 获取学校管理列表
 export const getSchoolList = (data: any) => {
-  return request({
-    url: '/adminApi/v1/adminSchoolInfosAll',
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/adminSchoolInfosAll', data)
 }
 // 获取学校管理筛选列表
 export const getSearchList = () => {
-  return request({
-    url: '/adminApi/v1/provinceCityTree',
-    method: 'get'
-  })
+  return http.get('/adminApi/v1/provinceCityTree')
 }
 // 学校管理--新增用户
 export const addUser = (data: any) => {
-  return request({
-    url: '/adminApi/v1/adminSchoolInfos',
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/adminSchoolInfos', data)
 }
 // 学校管理--运维人员列表
 export const getUserList = (params: any) => {
-  return request({
-    url: '/adminApi/v1/sys/userList',
-    method: 'get',
-    params
-  })
+  return http.get('/adminApi/v1/sys/userList', params)
 }
 // 学校管理--学校logo上传
 export const uploadFile = (data: any) => {
-  return request({
-    url: '/adminApi/v1/oss/oss/upload_filesSele',
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/oss/oss/upload_filesSele', data)
 }
 // 学校管理--省市区树列表
 export const provinceTree = () => {
-  return request({
-    url: '/adminApi/v1/provinceTree',
-    method: 'get'
-  })
+  return http.get('/adminApi/v1/provinceTree')
 }
 // 学校管理--省市区树列表
 export const provinceTreeByContain = () => {
-  return request({
-    url: '/adminApi/v1/provinceTreeByContain',
-    method: 'get'
-  })
+  return http.get('/adminApi/v1/provinceTreeByContain')
 }
 // 学校管理--查看详情
 export const getSchoolInfo = (id: any) => {
-  return request({
-    url: `/adminApi/v1/admin-school-infos/${id}`,
-    method: 'get'
-  })
+  return http.get(`/adminApi/v1/admin-school-infos/${id}`)
 }
 // 学校管理--编辑
 export const editSchoolInfo = (data: any) => {
-  return request({
-    url: `/adminApi/v1/update_school_info`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/update_school_info', data)
 }
 // 学校管理--改变状态(冻结/解冻)
 export const changeStatus = (data: any) => {
-  return request({
-    url: `/adminApi/v1/adminSchoolInfos/status`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/adminSchoolInfos/status', data)
 }
 
 
 // 学校管理--查询所有年级
 export const getAllGrade = (id: any) => {
-  return request({
-    url: `/adminApi/v1/admin/school/grade/getAllList/${id}`,
-    method: 'get'
-  })
+  return http.get(`/adminApi/v1/admin/school/grade/getAllList/${id}`)
 }
 // 学校管理--添加年级
 export const addGrade = (data: any) => {
-  return request({
-    url: `/adminApi/v1/admin/school/grade/addSchoolGrade`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/admin/school/grade/addSchoolGrade', data)
 }
 
 // 学校管理 —— 查询所有系统
 export const getAllSystemApi = (schoolId:any)=>{
-  return request({
-    url: `/adminApi/v1/admin/school/module/allSystemList/${schoolId}`,
-    method: 'get',
-  })
+  return http.get(`/adminApi/v1/admin/school/module/allSystemList/${schoolId}`)
 }
 
 
 // 学校管理 —— 查询所有模块
 export const getAllModuleApi = (schoolId:any)=>{
-  return request({
-    url: `/adminApi/v1/admin/school/module/allSystemModuleList/${schoolId}`,
-    method: 'get',
-  })
+  return http.get(`/adminApi/v1/admin/school/module/allSystemModuleList/${schoolId}`)
 }
 
 
 // 学校管理-添加删除系统和模块
 export const deleteOrAddSystemModuleApi = (data:any)=>{
-  return request({
-    url: `/adminApi/v1/admin/school/module/addSchoolSystemModule`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/admin/school/module/addSchoolSystemModule', data)
 }
 
 
 
 
+
 // 学校管理--查询所有科目
 export const getAllSubject = (params: any) => {
-  return request({
-    url: `/adminApi/v1/admin/school/course/getAllList`,
-    method: 'get',
-    params
-  })
+  return http.get('/adminApi/v1/admin/school/course/getAllList', params)
 }
 // 学校管理--新增科目
 export const addSubject = (data: any) => {
-  return request({
-    url: `/adminApi/v1/admin/school/course/courses`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/admin/school/course/courses', data)
 }
 // 学校管理--删除科目
 export const deleteSubject = (data: any) => {
-  return request({
-    url: `/adminApi/v1/admin/school/course/deleteByIds`,
-    method: 'delete',
-    data
-  })
+  return http.delete('/adminApi/v1/admin/school/course/deleteByIds', data)
 }
 
 //学校管理 -- 获取票据接口  
 export const getTicket = (params: any) => {
-  return request({
-    url: `/adminApi/v1/obtain_ticket`,
-    method: 'get',
-    params
-  })
+  return http.get('/adminApi/v1/obtain_ticket', params)
 }
 // 免密登录
 export const loginTeach = (data: any) => {
-  return request({
-    url: `/adminApi/v1/admin_login_teach`,
-    method: 'post',
-    data
-  })
+  return http.post('/adminApi/v1/admin_login_teach', data)
 }
 // 重置密码
 export const resetSchoolPassWord = (data: any) => {
-  return request({
-    url: `/adminApi/v1/resetSchoolInfo`,
-    method: 'post',
-    data
-  })
-}
+  return http.post('/adminApi/v1/resetSchoolInfo', data)
+}

+ 4 - 8
src/api/userSearch.ts

@@ -1,10 +1,6 @@
-import request from '../utils/request.ts'
+import { http } from '../utils/request'
 
 // 获取用户信息
-export const getUserInfoApi = (params:any) => {
-  return request({
-    url: '/adminApi/v1/teacher/teacher_page',
-    method: 'get',
-    params
-  })
-}
+export const getUserInfoApi = () => {
+  return http.get('/adminApi/v1/teacher/teacher_page')
+}

+ 91 - 109
src/baseComponents/FormSearchGroup.vue

@@ -1,171 +1,153 @@
-
 <!-- 需要配合 page_search   search_content    content_left  一起使用 -->
- 
+
 <template>
     <div class="simple_filter">
         <template v-for="item in props.searchList" :key="item.key">
-            <el-select 
-                v-if="item.type == 'select' && !item.hidden" 
-                v-model="searchData[item.key]" 
-                @change="(val)=>HandleSearchItemChange(item.key)" 
-                :placeholder="item.placeholder"  
-                :disabled="formDisabled || item.disabled"
-            >
-                <el-option 
-                    v-for="optItm in item.options" 
-                    :key="optItm.value" :value="optItm.value" 
-                    :label="optItm.label" 
-                    :disabled="optItm.disabled ? true : false">
+            <div class="filter_item">
+                <div v-if="item.label" class="filter_label">{{ item.label }}:</div>
+                <el-select v-if="item.type == 'select' && !item.hidden" v-model="searchData[item.key]"
+                @change="() => HandleSearchItemChange(item.key)" :placeholder="item.placeholder"
+                :disabled="formDisabled || item.disabled" :filterable="item.filterable === true"
+                :clearable="item.clearable === true">
+                <el-option v-for="optItm in item.options" :key="optItm.value" :value="optItm.value"
+                    :label="optItm.label" :disabled="optItm.disabled ? true : false">
                 </el-option>
             </el-select>
 
+            <el-date-picker @change="() => HandleSearchItemChange(item.key)" v-else-if="item.type == 'datePicker'"
+                v-model="searchData[item.key]" :disabled="formDisabled || item.disabled" type="daterange"
+                range-separator="-" start-placeholder="开始时间" end-placeholder="结束时间" />
 
-
-            <!-- :size="size" -->
-            <el-date-picker
-                @change="(val)=>HandleSearchItemChange(item.key)" 
-                v-else-if="item.type == 'datePicker'"  
-                v-model="searchData[item.key]"
-                :disabeld="formDisabled || item.disabled"
-                type="daterange"
-                range-separator="-"
-                start-placeholder="开始时间"
-                end-placeholder="结束时间"
-            />
-
-
-
-            <!-- 这个输入框的搜索要动态查询 ————  -->
-            <el-input 
-                v-else-if="item.type == 'input'"  
-                v-model="searchData[item.key]" 
-
-                @input="(val,e)=>HandleInputChange(val,e,item.key)"
-
-                @keyup.enter="(event)=>HandleSearchItemChange(item.key)" 
-                :placeholder="item.placeholder"  
-                :disabled="formDisabled || item.disabled"
-            >
+            <el-input v-else-if="item.type == 'input'" v-model="searchData[item.key]"
+                @input="(val: any) => HandleInputChange(val, item.key)" @keyup.enter="() => HandleSearchItemChange(item.key)"
+                :placeholder="item.placeholder" :disabled="formDisabled || item.disabled">
                 <template #append>
                     <el-button @click="HandleSearchItemChange(item.key)" :icon="Search" />
-                </template> 
+                </template>
             </el-input>
 
-
-            <el-radio-group
-                v-else-if="item.type == 'radio'" 
-                v-model="searchData[item.key]" 
-                @change="()=>HandleSearchItemChange(item.key)"
-                :disabled="formDisabled || item.disabled"
-            >
-                <el-radio v-for="radioItem in item.options" :key="radioItem.value" :value="radioItem.value">{{ radioItem.label }}</el-radio>
+            
+            <el-radio-group v-else-if="item.type == 'radio'" v-model="searchData[item.key]"
+                @change="() => HandleSearchItemChange(item.key)" :disabled="formDisabled || item.disabled">
+                <el-radio v-for="radioItem in item.options" :key="radioItem.value" :value="radioItem.value">{{
+                    radioItem.label
+                    }}</el-radio>
             </el-radio-group>
 
+            </div>
         </template>
     </div>
 </template>
 
 <script setup lang="ts">
 import { Search } from '@element-plus/icons-vue'
+import type { PropType } from 'vue'
 
 import { ref, watch } from 'vue'
 
 interface SearchItem {
-    key:String,
-    type:'input'|'select'|'datePicker'|'radio',
-    placeholder?:String,
-    hidden:boolean,
-    defaultValue?:any,
-    disabled?:Boolean
-    options?:{
-        label:String,
-        value:String,
-        disabled?:Boolean
-    }[] 
+    key: string,
+    type: 'input' | 'select' | 'datePicker' | 'radio',
+    label?: string,
+    placeholder?: string,
+    hidden: boolean,
+    defaultValue?: any,
+    disabled?: boolean,
+    filterable?: boolean,
+    clearable?: boolean,
+    options?: {
+        label: string,
+        value: string | number,
+        disabled?: boolean
+    }[]
 }
 
 const props = defineProps({
-    searchList:{
-        type:Array<SearchItem>,
-        required:true
+    searchList: {
+        type: Array as PropType<SearchItem[]>,
+        required: true
     },
-    formDisabled:{
-        type:Boolean,
-        required:false
+    formDisabled: {
+        type: Boolean,
+        required: false
     }
 })
 
-const emit = defineEmits(['searchChange'])
+const emit = defineEmits<{
+    (e: 'searchChange', data: Record<string, any>, key: string): void
+}>()
 
-const searchData = ref({})
+const searchData = ref<Record<string, any>>({})
 
 
-// 筛选条件变更触发新的查询 
-const HandleSearchItemChange = async (key)=>{
-    //   触发父级页面的查询
-    emit('searchChange',searchData.value,key)
+const HandleSearchItemChange = async (key: string) => {
+    emit('searchChange', searchData.value, key)
 }
 
-const inputFlag = ref() 
+const inputFlag = ref<ReturnType<typeof setTimeout> | null>(null)
 
-const HandleInputChange = (val,e,key)=>{
-    if(inputFlag.value){
-        // 还在定时器内,延迟触发
+const HandleInputChange = (val: any, key: string) => {
+    if (inputFlag.value) {
         clearTimeout(inputFlag.value)
-        inputFlag.value = ''
     }
-    // 要防抖
-    inputFlag.value = setTimeout(()=>{
-        console.log('input__输入的值',val)
-        //   触发父级页面的查询
-        emit('searchChange',searchData.value,key)
-    },400)
+    inputFlag.value = setTimeout(() => {
+        emit('searchChange', searchData.value, key)
+    }, 400)
 }
 
 
-// 
-watch(()=>props.searchList, (newVal)=>{
+watch(() => props.searchList, (newVal: SearchItem[]) => {
     searchData.value = {}
-    // console.log('查询项变更了===表单',newVal)
-    newVal.forEach(item=>{
-        if(item.defaultValue){
+    newVal.forEach(item => {
+        if (item.defaultValue !== undefined && item.defaultValue !== null) {
             searchData.value[item.key] = item.defaultValue
         }
     })
-    // emit('searchChange',searchData.value)
-},{immediate:true,deep:true})
+}, { immediate: true, deep: true })
 
 </script>
 
 
 <style lang="scss" scoped>
 .simple_filter {
-  display: flex;
-  flex-wrap: wrap;
-  gap: 10px;
-
-  :deep(.el-date-editor){
-    max-width: 240px;
-  }
-  
-  :deep(.el-input){
-    width: 260px;
-  }
-
-  :deep(.el-radio-group){
-    .el-radio{
-        margin-right: 20px;
+    display: flex;
+    // flex-wrap: wrap;
+    gap: 10px;
+
+    .filter_item {
+        display: flex;
+        gap: 5px;
+        align-items: center;
+    }
+
+    .filter_label {
+        font-size: 14px;
+        color: #606266;
+        line-height: 32px;
+        white-space: nowrap;
+    }
+
+    :deep(.el-date-editor) {
+        max-width: 240px;
+    }
+
+    :deep(.el-input) {
+        width: 260px;
+    }
+
+    :deep(.el-radio-group) {
+        .el-radio {
+            margin-right: 20px;
+        }
     }
-  }
 
 }
 
 
-.page_search .search_content .content_right{
+.page_search .search_content .content_right {
     .el-button {
         margin-left: -20px !important;
         min-width: auto !important;
     }
 }
-    
-</style>
+</style>

+ 10 - 10
src/baseComponents/HonorTimeLine.vue

@@ -56,16 +56,16 @@
 
 <script setup lang="ts">
 interface HonorItem {
-    id:String,
-    awardDatetime: String,
-    certPicUrl:String,
-    honorTypeName: String,
-    honorName:String,
-    teacherName:String,
-    radioData:Array<String>,
-    inputData:Array<Object>    
-    createdAt:String,
-    reviewStatus:String,
+    id: string,
+    awardDatetime: string,
+    certPicUrl: string,
+    honorTypeName: string,
+    honorName: string,
+    teacherName: string,
+    radioData: string[],
+    inputData: object[]
+    createdAt: string,
+    reviewStatus: string,
 }
 const props = defineProps({
     honorList:{

+ 14 - 8
src/baseComponents/Table.vue

@@ -7,7 +7,7 @@
           element-loading-spinner="el-icon-loading" element-loading-background="#ffffff"
           element-loading-custom-class="loading_icon"
           :row-class-name="props.tableRowClassName"
-          @row-click="(row,column)=> emit('rowClick',row)"
+          @row-click="(row:any,column:any)=> emit('rowClick',row)"
           :span-method="props.spanMethod">
             <!-- show-overflow-tooltip -->
             <template 
@@ -45,24 +45,30 @@
             :page-sizes="[20, 50, 100]" 
             background layout="prev, pager, next" 
             :total="Number(props.totalNum)" 
-            @current-change="(val)=>emit('paginationChange',{type:'currentPage',val})" 
-            @size-change="(val)=>emit('paginationChange',{type:'pageSize',val})" />
+            @current-change="(val:number)=>emit('paginationChange',{type:'currentPage',val})" 
+            @size-change="(val:number)=>emit('paginationChange',{type:'pageSize',val})" />
         </div>
     </div>
 </template>
 
 <script setup lang="ts">
+import type { PropType } from 'vue';
+
 interface ColumnProperty  {
   name:String,
   label:String,
-  width?:String,
+  width?:Number | String,
   fixed?:String,
   align?:String,
-  minWidth?:String,
-  custom:String,
+  minWidth?:Number | String,
+  custom?:Boolean,
   customHeader?:Boolean,
-  hidden:Boolean,
+  hidden?:Boolean,
 }
+
+defineSlots<{
+  [key: string]: (props: { row?: any; currentIndex?: number }) => any
+}>()
 const props = defineProps({
   tableData:{
     required:true,
@@ -70,7 +76,7 @@ const props = defineProps({
   },
   tableColumns:{
     required:true,
-    type:Array<ColumnProperty>
+    type:Array as PropType<ColumnProperty[]>
   },
   currentPage:{
     type:Number,

+ 22 - 9
src/layout/components/SiderBar.vue

@@ -4,8 +4,8 @@
       <img v-if="isCollapse" src="@/assets/icon/open.svg" alt="">
       <img v-else src="@/assets/icon/close.svg" alt="">
     </div>
-    <el-menu :collapse="isCollapse" router>
-      <el-menu-item v-for="item in menuItems" :key="item.route" :index="item.route" :class="activeRoute == item.route ? 'is-active' : ''" >
+    <el-menu :collapse="isCollapse" router :default-active="activeMenuRoute">
+      <el-menu-item v-for="item in menuItems" :key="item.route" :index="item.route">
         <template #default>
           <i class="iconfont" :class="item.icon"></i>
           <span v-if="!isCollapse">{{ item.title }}</span>
@@ -23,7 +23,7 @@ export default {
 }
 </script>
 <script lang="ts" setup> 
-import { ref, watch, onMounted } from 'vue';
+import { ref, watch, onMounted, computed } from 'vue';
 import { useRoute } from 'vue-router'
 import router from '@/router';
 
@@ -33,9 +33,19 @@ watch(() => route.path, (newVal) => {
   activeRoute.value = newVal
 })
 
+// 当前应选中的菜单项:精确匹配优先,否则匹配路由前缀(处理详情页等子页面高亮父级菜单)
+const activeMenuRoute = computed(() => {
+  if (!menuItems.value) return activeRoute.value
+  const exact = menuItems.value.find(item => item.route === activeRoute.value)
+  if (exact) return exact.route
+  const prefix = menuItems.value.find(item => item.route && activeRoute.value.startsWith(item.route))
+  return prefix?.route || activeRoute.value
+})
+
 const isCollapse = ref(false)
 interface menuItem {
   label: string;
+  title: string;
   icon: string;
   route: string;
 }
@@ -43,7 +53,7 @@ const menuItems = ref<menuItem[]>()
 
 onMounted(() => {
   const menuListStr = localStorage.getItem('menuList')
-  console.log(menuListStr)
+
   if (menuListStr) {
     menuItems.value = JSON.parse(menuListStr)
 
@@ -62,12 +72,15 @@ onMounted(() => {
       },
     ]
 
-    menuItems.value?.push(...additionalMenuList)
-
-
-    // router.push(menuItems.value[0].route)
+    // 只追加 menuList 中不存在的菜单项,避免重复
+    additionalMenuList.forEach(item => {
+      const exists = menuItems.value?.some(menu => menu.route === item.route)
+      if (!exists) {
+        menuItems.value?.push(item)
+      }
+    })
   }
-  console.log(menuItems.value)
+
 });
 const toggleCollapse = () => {
   isCollapse.value = !isCollapse.value

+ 2 - 1
src/styles/common.scss

@@ -627,7 +627,8 @@ button:focus, button:focus-visible {
       font-size: 14px !important;
     }
     .el-select {
-      width: 120px;
+      min-width: 150px;
+      max-width: 160px;
     }
     .el-input {
       width: 200px;

+ 2 - 0
src/utils/global.d.ts

@@ -0,0 +1,2 @@
+declare const _default: { [key: string]: (...args: any[]) => any };
+export default _default;

+ 16 - 0
src/utils/request.ts

@@ -61,4 +61,20 @@ service.interceptors.response.use(
   }
 )
 
+// 常用请求方法封装
+export const http = {
+  get<T = any>(url: string, params?: any): Promise<T> {
+    return service.get(url, { params })
+  },
+  post<T = any>(url: string, data?: any): Promise<T> {
+    return service.post(url, data)
+  },
+  put<T = any>(url: string, data?: any): Promise<T> {
+    return service.put(url, data)
+  },
+  delete<T = any>(url: string, data?: any): Promise<T> {
+    return service.delete(url, { data })
+  }
+}
+
 export default service

+ 119 - 38
src/views/logMonitor/LogMonitorDetail.vue

@@ -9,21 +9,21 @@
             <div class="people_info">
                 <div>
                     <span class="label">学校:</span>
-                    <span class="value">xxx</span>
+                    <span class="value">{{ detailParams.schoolName }}</span>
                 </div>
                 <div>
                     <span class="label">账户:</span>
-                    <span class="value">xxx</span>
+                    <span class="value">{{ detailParams.userAccount }}</span>
                 </div>
                 <div><span class="label">姓名:</span>
-                    <span class="value">xxx</span>
+                    <span class="value">{{ detailParams.userName }}</span>
                 </div>
             </div>
 
             <div class="page_search" >
                 <div class="search_content">
                     <div class="content_left">
-                        <FormSearchGroup :searchList="peopleLogSearchList"></FormSearchGroup>
+                        <FormSearchGroup :searchList="peopleLogSearchList" @searchChange="handleSearchChange"></FormSearchGroup>
                     </div>
                     <div class="content_right">
                         <div class="desc">
@@ -44,41 +44,125 @@
 </template>  
 
 <script setup lang="ts">
-import { ref } from 'vue';
-import { useRouter } from 'vue-router';
+import { ref, reactive, onMounted } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { ArrowLeftBold } from '@element-plus/icons-vue';
+import { ElMessage } from 'element-plus';
 import FormSearchGroup from '@/baseComponents/FormSearchGroup.vue';
-import {peopleLogSearchList} from './formSearchGroup'
+import { peopleLogSearchList } from './formSearchGroup';
 import HonorTimeLine from '@/baseComponents/HonorTimeLine.vue';
+import { getLogDetailApi } from '@/api/logMonitor';
 
+const route = useRoute()
 const router = useRouter()
 
-const peopleLogList = ref([
-    {
-        id:'1',
-        awardDatetime: '26-07-14',
-        certPicUrl:'',
-        honorTypeName: '日志名称',
-        honorName:'日志名称',
-        teacherName:'192.168.1.101',
-        radioData:[''],
-        inputData:[],    
-        createdAt:'',
-        reviewStatus:'String',
-    },
-    {
-        id:'1',
-        awardDatetime: '26-07-14',
-        certPicUrl:'',
-        honorTypeName: '日志名称',
-        honorName:'日志名称',
-        teacherName:'192.168.1.101',
-        radioData:[''],
-        inputData:[],    
-        createdAt:'',
-        reviewStatus:'String',
+const detailParams = reactive({
+    schoolId: 0,
+    userId: 0,
+    accountTypeCode: 0,
+    moduleCode: 0,
+    schoolName: '',
+    userAccount: '',
+    userName: '',
+})
+
+// 详情搜索参数
+const detailSearchParams = reactive({
+    startTime: '',
+    endTime: '',
+})
+
+const peopleLogList = ref<any[]>([])
+
+// 获取日志详情
+const getLogDetailData = async () => {
+    try {
+        const params = {
+            schoolId: detailParams.schoolId,
+            userId: detailParams.userId,
+            accountTypeCode: detailParams.accountTypeCode,
+            moduleCode: detailParams.moduleCode,
+            startTime: detailSearchParams.startTime,
+            endTime: detailSearchParams.endTime,
+        }
+        const res = await getLogDetailApi(params)
+        const data = res.data || res
+        
+        peopleLogList.value = (data || []).map((item: any, index: number) => ({
+            id: String(index),
+            awardDatetime: item.createTime || '',
+            certPicUrl: '',
+            honorTypeName: '',
+            honorName: item.operation || '',
+            teacherName: item.ip || '',
+            radioData: [],
+            inputData: [],
+            createdAt: item.createTime || '',
+            reviewStatus: '',
+        }))
+    } catch (err) {
+        console.error('getLogDetailApi 请求失败:', err)
+    }
+}
+
+// 处理搜索变化
+const handleSearchChange = (data: Record<string, any>, key: string) => {
+    if (key === 'dateRange') {
+        const range = data.dateRange
+        if (Array.isArray(range) && range.length === 2 && range[0] && range[1]) {
+            const start = new Date(range[0])
+            const end = new Date(range[1])
+            const diffDays = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24))
+            if (diffDays > 30) {
+                ElMessage.warning('日志选择最大范围30天')
+                return
+            }
+            detailSearchParams.startTime = formatDateTime(start, true)
+            detailSearchParams.endTime = formatDateTime(end, false)
+        } else {
+            detailSearchParams.startTime = ''
+            detailSearchParams.endTime = ''
+        }
     }
+    getLogDetailData()
+}
+
+// 格式化日期时间,isStart为true时返回 00:00:00,为false时返回 23:59:59
+const formatDateTime = (date: Date, isStart: boolean) => {
+    if (!date) return ''
+    const year = date.getFullYear()
+    const month = String(date.getMonth() + 1).padStart(2, '0')
+    const day = String(date.getDate()).padStart(2, '0')
+    const time = isStart ? '00:00:00' : '23:59:59'
+    return `${year}-${month}-${day} ${time}`
+}
 
-])
+// 获取默认日期范围(最近3天)
+const getDefaultDateRange = () => {
+    const end = new Date()
+    const start = new Date()
+    start.setDate(start.getDate() - 2)
+    return [start, end]
+}
+
+// 初始化
+onMounted(() => {
+    detailParams.schoolId = Number(route.query.schoolId) || 0
+    detailParams.userId = Number(route.query.userId) || 0
+    detailParams.accountTypeCode = Number(route.query.accountTypeCode) || 0
+    detailParams.moduleCode = Number(route.query.moduleCode) || 0
+    detailParams.schoolName = (route.query.schoolName as string) || ''
+    detailParams.userAccount = (route.query.userAccount as string) || ''
+    detailParams.userName = (route.query.userName as string) || ''
+
+    // 默认最近3天
+    const defaultRange = getDefaultDateRange()
+    peopleLogSearchList.value[0].defaultValue = defaultRange
+    detailSearchParams.startTime = formatDateTime(defaultRange[0], true)
+    detailSearchParams.endTime = formatDateTime(defaultRange[1], false)
+
+    getLogDetailData()
+})
 </script>
 
 
@@ -108,18 +192,15 @@ const peopleLogList = ref([
     }
 
     .page_search{
-        display: flex;
-        justify-content: flex-end;
         .search_content{
-            flex: 0 0;
+            justify-content: flex-end;
+            gap:20px;
             .content_right{
                 .desc{
-                    margin-left: 5px;
                     font-size: 14px;
-                    width: 200px;  //必须加
                 }
             }
         }
     }
 }
-</style>
+</style>

+ 52 - 26
src/views/logMonitor/formSearchGroup.ts

@@ -1,41 +1,67 @@
 import { ref } from "vue";
 
+interface SearchItem {
+    key: string;
+    type: 'input' | 'select' | 'datePicker' | 'radio';
+    label?: string;
+    placeholder?: string;
+    hidden: boolean;
+    defaultValue?: any;
+    disabled?: boolean;
+    filterable?: boolean;
+    clearable?: boolean;
+    options?: { label: string; value: string | number; disabled?: boolean }[];
+}
 
 // 不同类别的日志筛选项
-export const differentTypeLogSearchList = ref([
+export const differentTypeLogSearchList = ref<SearchItem[]>([
     {
-        key:'school',
-        type:'select',
-        placeholder:'请选择学校',
-        hidden:false,
-        defaultValue:'',
-        options:[],
+        key: 'accountType',
+        type: 'select',
+        label: '账号类型',
+        placeholder: '请选择账号类型',
+        hidden: false,
+        defaultValue: 0,
+        options: [],
     },
     {
-        key:'peopleType',
-        type:'select',
-        placeholder:'请选择人员类型',
-        hidden:false,
-        defaultValue:'',
-        options:[],
+        key: 'moduleType',
+        type: 'select',
+        label: '功能模块',
+        placeholder: '请选择功能模块',
+        hidden: false,
+        defaultValue: 0,
+        options: [],
     },
     {
-        key:'account',
-        type:'input',
-        placeholder:'请输入账号',
-        hidden:false,
-        defaultValue:'',
+        key: 'operationType',
+        type: 'select',
+        label: '操作类型',
+        placeholder: '请选择操作类型',
+        hidden: false,
+        defaultValue: 0,
+        options: [],
+    },
+    {
+        key: 'account',
+        type: 'select',
+        label: '用户账号',
+        placeholder: '请选择用户账号',
+        hidden: false,
+        defaultValue: 0,
+        filterable: true,
+        options: [],
     },
 ])
 
-export const peopleLogSearchList = ref([
+export const peopleLogSearchList = ref<SearchItem[]>([
     {
-        key:'school',
-        type:'datePicker',
-        placeholder:'请选择学校',
-        hidden:false,
-        defaultValue:'',
-        options:[],
+        key: 'dateRange',
+        type: 'datePicker',
+        placeholder: '请选择时间范围',
+        hidden: false,
+        defaultValue: '',
+        options: [],
     },
 
-])
+])

+ 113 - 52
src/views/logMonitor/index.vue

@@ -14,23 +14,19 @@
             <div class="page_search">
                 <div class="search_content">
                     <div class="content_left">
-                        <FormSearchGroup :searchList="differentTypeLogSearchList"></FormSearchGroup>
+                        <FormSearchGroup :searchList="differentTypeLogSearchList" @searchChange="handleSearchChange"></FormSearchGroup>
                     </div>
                 </div>
             </div>
             <div class="page_jg_20"></div>
-            <Table 
-                :currentPage="currentPage"
-                :pageSize="pageSize"
-                :totalNum="totalNum"
-                :tableColumns="tableColumns" 
-                :tableData="logTableData">
-
-                <template #num="{row,currentIndex}">
-                    {{ currentIndex + pageSize * (currentPage - 1) }}
+            <Table :currentPage="currentPage" :pageSize="pageSize" :totalNum="totalNum" :tableColumns="tableColumns"
+                :tableData="logTableData" @paginationChange="handlePaginationChange">
+
+                <template #num="{ row, currentIndex }">
+                    {{ (currentIndex ?? 0) + pageSize * (currentPage - 1) }}
                 </template>
-                
-                <template #operation="{row}">
+
+                <template #action="{ row }">
                     <div class="table_row_option">
                         <span class="option_button_editor" @click="GoToLogMonitorDetail(row)">查看详情</span>
                     </div>
@@ -38,62 +34,129 @@
             </Table>
         </div>
     </div>
-</template>  
+</template>
 
 <script setup lang="ts">
-import { ref } from 'vue';
+import { ref, onMounted } from 'vue';
 import { useRouter } from 'vue-router';
 
 import FormSearchGroup from '@/baseComponents/FormSearchGroup.vue';
 import Table from '@/baseComponents/Table.vue';
 
-import {differentTypeLogSearchList} from './formSearchGroup'
+import { differentTypeLogSearchList } from './formSearchGroup'
+
+import { tableColumns } from './table'
 
-import {tableColumns} from './table'
+import { getLogHeadApi, getLogPageApi } from '@/api/logMonitor'
 
 const router = useRouter()
 const currentPage = ref(1)
 const totalNum = ref(0)
 const pageSize = ref(20)
 
+// 日志表格数据
+const logTableData = ref<any[]>([])
+
+// 搜索参数
+const searchParams = ref({
+    schoolId: 0,
+    accountTypeCode: 0,
+    moduleCode: 0,
+    operationTypeCode: 0,
+    userAccount: 0,
+})
+
+// 跳转日志详情页
+const GoToLogMonitorDetail = (row: any) => {
+    router.push({
+        name: 'logMonitorDetail',
+        query: {
+            schoolId: row.schoolId,
+            userId: row.userId,
+            accountTypeCode: row.accountTypeCode,
+            moduleCode: row.moduleCode,
+            schoolName: row.schoolName,
+            userAccount: row.userAccount,
+            userName: row.userName,
+        }
+    })
+}
 
-const logTableData = ref([
-    {
-        id:'1',
-        school: '学校1',
-        account: 'teacher01',
-        name: '白语忱',
-        type: '教师',
-        operationDetail: '系统登录',
-        ip: '192.168.1.101',
-        operationTime: '2026-07-14 08:22:15',
-    },
-    {
-        id:'2',
-        school: '学校1',
-        account: 'stu_2026001',
-        name: '蔡宜萱',
-        type: '学生',
-        operationDetail: '查询成绩',
-        ip: '192.168.1.205',
-        operationTime: '2026-07-14 09:10:33',
-    },
-    {
-        id:'3',
-        school: '学校1',
-        account: 'teacher05',
-        name: '曹延熠',
-        type: '教师',
-        operationDetail: '提交纠错',
-        ip: '192.168.1.108',
-        operationTime: '2026-07-14 10:05:12',
+// 处理搜索变化
+const handleSearchChange = (data: Record<string, any>, key: string) => {
+    const fieldMap: Record<string, string> = {
+        accountType: 'accountTypeCode',
+        moduleType: 'moduleCode',
+        operationType: 'operationTypeCode',
+        account: 'userAccount',
+    }
+    
+    if (fieldMap[key]) {
+        const val = data[key]
+        searchParams.value[fieldMap[key] as keyof typeof searchParams.value] = (val !== undefined && val !== null && val !== '') ? val : 0
     }
-])
+    
+    currentPage.value = 1
+    getLogTableData()
+}
 
+// 获取日志表格数据
+const getLogTableData = async () => {
+    try {
+        const params = {
+            ...searchParams.value,
+            pageParam: {
+                pageNum: currentPage.value,
+                pageSize: pageSize.value,
+            },
+        }
+        const res = await getLogPageApi(params)
+        const data = res.data || res
+        
+        logTableData.value = data.records || []
+        totalNum.value = data.total || 0
+    } catch (err) {
+        console.error('getLogPageApi 请求失败:', err)
+    }
+}
 
-const GoToLogMonitorDetail = (row)=>{
-    router.push(`logMonitorDetail?id=${row.id}`)
+// 处理分页变化
+const handlePaginationChange = (data: { type: string; val: number }) => {
+    if (data.type === 'currentPage') {
+        currentPage.value = data.val
+    } else if (data.type === 'pageSize') {
+        pageSize.value = data.val
+        currentPage.value = 1
+    }
+    getLogTableData()
 }
+// 获取日志头
+const getLogHead = async () => {
+    try {
+        const res = await getLogHeadApi()
+        const data = res.data || res
+        const optionMap: Record<string, { label: string; value: any }[]> = {
+            accountType: (data.accountTypeList || []).map((item: any) => ({ label: item.name, value: item.code })),
+            moduleType: (data.moduleTypeList || []).map((item: any) => ({ label: item.name, value: item.code })),
+            operationType: (data.operationTypeList || []).map((item: any) => ({ label: item.name, value: item.code })),
+            account: (data.schoolList || []).map((item: any) => ({ label: item.schoolName, value: item.schoolId })),
+        }
+
+        differentTypeLogSearchList.value = differentTypeLogSearchList.value.map(item => {
+            if (optionMap[item.key]) {
+                item.options = optionMap[item.key]
+            }
+            return { ...item }
+        })
+    } catch (err) {
+        console.error('getLogHeadApi 请求失败:', err)
+    }
+}
+// 获取日志表格数据
+onMounted(async () => {
+    await getLogHead()
+    await getLogTableData()
+})
 
 
 </script>
@@ -101,6 +164,4 @@ const GoToLogMonitorDetail = (row)=>{
 
 
 
-<style lang="scss" scoped>
-
-</style>
+<style lang="scss" scoped></style>

+ 7 - 7
src/views/logMonitor/table.ts

@@ -12,7 +12,7 @@ export const tableColumns = ref([
         custom: true,
     },
     {
-        name: 'school',
+        name: 'schoolName',
         label: '学校',
         width: 150,
         fixed: '',
@@ -21,7 +21,7 @@ export const tableColumns = ref([
         custom: false,
     },
     {
-        name: 'account',
+        name: 'userAccount',
         label: '账号',
         width: 120,
         fixed: '',
@@ -30,7 +30,7 @@ export const tableColumns = ref([
         custom: false,
     },
     {
-        name: 'name',
+        name: 'userName',
         label: '姓名',
         width: 100,
         fixed: '',
@@ -39,7 +39,7 @@ export const tableColumns = ref([
         custom: false,
     },
     {
-        name: 'type', 
+        name: 'accountTypeName', 
         label: '类型',
         width: 100,
         fixed: '',
@@ -48,7 +48,7 @@ export const tableColumns = ref([
         custom: false,
     },
     {
-        name: 'operationDetail',
+        name: 'operation',
         label: '操作详情',
         width: 150,
         fixed: '',
@@ -66,7 +66,7 @@ export const tableColumns = ref([
         custom: false,
     },
     {
-        name: 'operationTime', 
+        name: 'createTime', 
         label: '操作时间',
         width: 180,
         fixed: '',
@@ -75,7 +75,7 @@ export const tableColumns = ref([
         custom: false,
     },
     {
-        name: 'operation', 
+        name: 'action', 
         label: '操作',
         width: 120,
         fixed: 'right', 

+ 4 - 0
src/vite-env.d.ts

@@ -1 +1,5 @@
 /// <reference types="vite/client" />
+
+interface Window {
+  $global: { [key: string]: (...args: any[]) => any };
+}

+ 4 - 0
tsconfig.app.json

@@ -3,6 +3,10 @@
   "compilerOptions": {
     "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
     "outDir": "./dist",
+    "target": "ES2020",
+    "useDefineForClassFields": true,
+    "module": "ESNext",
+    "lib": ["ES2020", "DOM", "DOM.Iterable"],
     /* Linting */
     "strict": true,
     "noUnusedLocals": true,