mirror of
				https://github.com/continew-org/continew-admin.git
				synced 2025-10-31 10:57:13 +08:00 
			
		
		
		
	新增:新增系统监控/在线用户功能,并优化部分注释规范
This commit is contained in:
		
							
								
								
									
										35
									
								
								continew-admin-ui/src/api/monitor/online.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								continew-admin-ui/src/api/monitor/online.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| import axios from 'axios'; | ||||
| import qs from 'query-string'; | ||||
|  | ||||
| export interface OnlineUserRecord { | ||||
|   token: string; | ||||
|   username: string; | ||||
|   nickname: string; | ||||
|   clientIp: string; | ||||
|   location: string; | ||||
|   browser: string; | ||||
|   loginTime: string; | ||||
| } | ||||
|  | ||||
| export interface OnlineUserParams extends Partial<OnlineUserRecord> { | ||||
|   page: number; | ||||
|   size: number; | ||||
|   sort: Array<string>; | ||||
| } | ||||
| export interface OnlineUserListRes { | ||||
|   list: OnlineUserRecord[]; | ||||
|   total: number; | ||||
| } | ||||
|  | ||||
| export function queryOnlineUserList(params: OnlineUserParams) { | ||||
|   return axios.get<OnlineUserListRes>('/monitor/online/user', { | ||||
|     params, | ||||
|     paramsSerializer: (obj) => { | ||||
|       return qs.stringify(obj); | ||||
|     }, | ||||
|   }); | ||||
| } | ||||
|  | ||||
| export function kickout(token: string) { | ||||
|   return axios.delete(`/monitor/online/user/${token}`); | ||||
| } | ||||
| @@ -8,6 +8,7 @@ import localeMonitor from '@/views/dashboard/monitor/locale/en-US'; | ||||
| import localeDataAnalysis from '@/views/visualization/data-analysis/locale/en-US'; | ||||
| import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/en-US'; | ||||
|  | ||||
| import localeOnlineUser from '@/views/monitor/online/locale/en-US'; | ||||
| import localeLoginLog from '@/views/monitor/log/login/locale/en-US'; | ||||
| import localeOperationLog from '@/views/monitor/log/operation/locale/en-US'; | ||||
| import localeSystemLog from '@/views/monitor/log/system/locale/en-US'; | ||||
| @@ -57,6 +58,7 @@ export default { | ||||
|   ...localeDataAnalysis, | ||||
|   ...localeMultiDAnalysis, | ||||
|  | ||||
|   ...localeOnlineUser, | ||||
|   ...localeLoginLog, | ||||
|   ...localeOperationLog, | ||||
|   ...localeSystemLog, | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import localeMonitor from '@/views/dashboard/monitor/locale/zh-CN'; | ||||
| import localeDataAnalysis from '@/views/visualization/data-analysis/locale/zh-CN'; | ||||
| import localeMultiDAnalysis from '@/views/visualization/multi-dimension-data-analysis/locale/zh-CN'; | ||||
|  | ||||
| import localeOnlineUser from '@/views/monitor/online/locale/zh-CN'; | ||||
| import localeLoginLog from '@/views/monitor/log/login/locale/zh-CN'; | ||||
| import localeOperationLog from '@/views/monitor/log/operation/locale/zh-CN'; | ||||
| import localeSystemLog from '@/views/monitor/log/system/locale/zh-CN'; | ||||
| @@ -57,6 +58,7 @@ export default { | ||||
|   ...localeDataAnalysis, | ||||
|   ...localeMultiDAnalysis, | ||||
|  | ||||
|   ...localeOnlineUser, | ||||
|   ...localeLoginLog, | ||||
|   ...localeOperationLog, | ||||
|   ...localeSystemLog, | ||||
|   | ||||
| @@ -12,6 +12,16 @@ const Monitor: AppRouteRecordRaw = { | ||||
|     order: 2, | ||||
|   }, | ||||
|   children: [ | ||||
|     { | ||||
|       path: '/online', | ||||
|       name: 'OnlineUser', | ||||
|       component: () => import('@/views/monitor/online/index.vue'), | ||||
|       meta: { | ||||
|         locale: 'menu.online.user.list', | ||||
|         requiresAuth: true, | ||||
|         roles: ['*'], | ||||
|       }, | ||||
|     }, | ||||
|     { | ||||
|       path: 'log/login', | ||||
|       name: 'LoginLog', | ||||
|   | ||||
| @@ -4,15 +4,8 @@ | ||||
|     <a-card class="general-card" :title="$t('menu.log.login.list')"> | ||||
|       <a-row style="margin-bottom: 15px"> | ||||
|         <a-col :span="24"> | ||||
|           <a-form | ||||
|             ref="queryFormRef" | ||||
|             :model="queryFormData" | ||||
|             layout="inline" | ||||
|           > | ||||
|             <a-form-item | ||||
|               field="status" | ||||
|               hide-label | ||||
|             > | ||||
|           <a-form ref="queryFormRef" :model="queryFormData" layout="inline"> | ||||
|             <a-form-item field="status" hide-label> | ||||
|               <a-select | ||||
|                 v-model="queryFormData.status" | ||||
|                 :options="statusOptions" | ||||
| @@ -21,10 +14,7 @@ | ||||
|                 style="width: 150px;" | ||||
|               /> | ||||
|             </a-form-item> | ||||
|             <a-form-item | ||||
|               field="createTime" | ||||
|               hide-label | ||||
|             > | ||||
|             <a-form-item field="createTime" hide-label> | ||||
|               <date-range-picker v-model="queryFormData.createTime" /> | ||||
|             </a-form-item> | ||||
|             <a-button type="primary" @click="toQuery"> | ||||
|   | ||||
| @@ -4,15 +4,8 @@ | ||||
|     <a-card class="general-card" :title="$t('menu.log.operation.list')"> | ||||
|       <a-row style="margin-bottom: 15px"> | ||||
|         <a-col :span="24"> | ||||
|           <a-form | ||||
|             ref="queryFormRef" | ||||
|             :model="queryFormData" | ||||
|             layout="inline" | ||||
|           > | ||||
|             <a-form-item | ||||
|               field="description" | ||||
|               hide-label | ||||
|             > | ||||
|           <a-form ref="queryFormRef" :model="queryFormData" layout="inline"> | ||||
|             <a-form-item field="description" hide-label> | ||||
|               <a-input | ||||
|                 v-model="queryFormData.description" | ||||
|                 placeholder="输入操作内容搜索" | ||||
| @@ -21,10 +14,7 @@ | ||||
|                 @press-enter="toQuery" | ||||
|               /> | ||||
|             </a-form-item> | ||||
|             <a-form-item | ||||
|               field="status" | ||||
|               hide-label | ||||
|             > | ||||
|             <a-form-item field="status" hide-label> | ||||
|               <a-select | ||||
|                 v-model="queryFormData.status" | ||||
|                 :options="statusOptions" | ||||
| @@ -33,10 +23,7 @@ | ||||
|                 style="width: 150px;" | ||||
|               /> | ||||
|             </a-form-item> | ||||
|             <a-form-item | ||||
|               field="createTime" | ||||
|               hide-label | ||||
|             > | ||||
|             <a-form-item field="createTime" hide-label> | ||||
|               <date-range-picker v-model="queryFormData.createTime" /> | ||||
|             </a-form-item> | ||||
|             <a-button type="primary" @click="toQuery"> | ||||
|   | ||||
							
								
								
									
										202
									
								
								continew-admin-ui/src/views/monitor/online/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										202
									
								
								continew-admin-ui/src/views/monitor/online/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,202 @@ | ||||
| <template> | ||||
|   <div class="container"> | ||||
|     <Breadcrumb :items="['menu.monitor', 'menu.online.user.list']" /> | ||||
|     <a-card class="general-card" :title="$t('menu.online.user.list')"> | ||||
|       <a-row style="margin-bottom: 15px"> | ||||
|         <a-col :span="24"> | ||||
|           <a-form ref="queryFormRef" :model="queryFormData" layout="inline"> | ||||
|             <a-form-item field="nickname" hide-label> | ||||
|               <a-input | ||||
|                 v-model="queryFormData.nickname" | ||||
|                 placeholder="输入用户昵称搜索" | ||||
|                 allow-clear | ||||
|                 style="width: 150px;" | ||||
|                 @press-enter="toQuery" | ||||
|               /> | ||||
|             </a-form-item> | ||||
|             <a-form-item field="loginTime" hide-label> | ||||
|               <date-range-picker v-model="queryFormData.loginTime" /> | ||||
|             </a-form-item> | ||||
|             <a-button type="primary" @click="toQuery"> | ||||
|               <template #icon> | ||||
|                 <icon-search /> | ||||
|               </template> | ||||
|               查询 | ||||
|             </a-button> | ||||
|             <a-button @click="resetQuery"> | ||||
|               <template #icon> | ||||
|                 <icon-refresh /> | ||||
|               </template> | ||||
|               重置 | ||||
|             </a-button> | ||||
|           </a-form> | ||||
|         </a-col> | ||||
|       </a-row> | ||||
|       <a-table | ||||
|         :columns="columns" | ||||
|         :data="renderData" | ||||
|         :pagination="paginationProps" | ||||
|         row-key="logId" | ||||
|         :bordered="false" | ||||
|         :stripe="true" | ||||
|         :loading="loading" | ||||
|         size="large" | ||||
|         @page-change="handlePageChange" | ||||
|         @page-size-change="handlePageSizeChange" | ||||
|       > | ||||
|         <template #index="{ rowIndex }"> | ||||
|           {{ rowIndex + 1 + (pagination.current - 1) * pagination.pageSize }} | ||||
|         </template> | ||||
|         <template #nickname="{ record }"> | ||||
|           {{ record.nickname }}({{record.username}}) | ||||
|         </template> | ||||
|         <template #operations="{ record }"> | ||||
|           <a-button | ||||
|             v-permission="['admin']" | ||||
|             type="text" | ||||
|             size="small" | ||||
|             :title="currentToken === record.token ? '不能强退当前登录' : ''" | ||||
|             :disabled="currentToken === record.token" | ||||
|             @click="handleClick(record.token)" | ||||
|           > | ||||
|             强退 | ||||
|           </a-button> | ||||
|         </template> | ||||
|       </a-table> | ||||
|     </a-card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts" setup> | ||||
|   import { computed, ref, reactive } from 'vue'; | ||||
|   import useLoading from '@/hooks/loading'; | ||||
|   import { Message } from '@arco-design/web-vue'; | ||||
|   import { queryOnlineUserList, OnlineUserRecord, OnlineUserParams, kickout } from '@/api/monitor/online'; | ||||
|   import { Pagination } from '@/types/global'; | ||||
|   import { PaginationProps } from '@arco-design/web-vue'; | ||||
|   import type { TableColumnData } from '@arco-design/web-vue/es/table/interface'; | ||||
|   import { FormInstance } from '@arco-design/web-vue/es/form'; | ||||
|   import { getToken } from '@/utils/auth'; | ||||
|  | ||||
|   const { loading, setLoading } = useLoading(true); | ||||
|   const currentToken = computed(() => getToken()); | ||||
|   const queryFormRef = ref<FormInstance>(); | ||||
|   const queryFormData = ref({ | ||||
|     nickname: '', | ||||
|     status: undefined, | ||||
|     loginTime: [], | ||||
|   }); | ||||
|  | ||||
|   // 查询 | ||||
|   const toQuery = () => { | ||||
|     fetchData({ | ||||
|       page: pagination.current, | ||||
|       size: pagination.pageSize, | ||||
|       sort: ['createTime,desc'], | ||||
|       ...queryFormData.value, | ||||
|     } as unknown as OnlineUserParams); | ||||
|   }; | ||||
|  | ||||
|   // 重置 | ||||
|   const resetQuery = async () => { | ||||
|     await queryFormRef.value?.resetFields(); | ||||
|     await fetchData(); | ||||
|   }; | ||||
|  | ||||
|   const renderData = ref<OnlineUserRecord[]>([]); | ||||
|   const basePagination: Pagination = { | ||||
|     current: 1, | ||||
|     pageSize: 10, | ||||
|   }; | ||||
|   const pagination = reactive({ | ||||
|     ...basePagination, | ||||
|   }); | ||||
|   const paginationProps = computed((): PaginationProps => { | ||||
|     return { | ||||
|       showTotal: true, | ||||
|       showPageSize: true, | ||||
|       total: pagination.total, | ||||
|       current: pagination.current, | ||||
|     } | ||||
|   }); | ||||
|   const columns = computed<TableColumnData[]>(() => [ | ||||
|     { | ||||
|       title: '序号', | ||||
|       dataIndex: 'index', | ||||
|       slotName: 'index', | ||||
|     }, | ||||
|     { | ||||
|       title: '用户昵称', | ||||
|       dataIndex: 'nickname', | ||||
|       slotName: 'nickname', | ||||
|     }, | ||||
|     { | ||||
|       title: '登录 IP', | ||||
|       dataIndex: 'clientIp', | ||||
|     }, | ||||
|     { | ||||
|       title: '登录地点', | ||||
|       dataIndex: 'location', | ||||
|     }, | ||||
|     { | ||||
|       title: '浏览器', | ||||
|       dataIndex: 'browser', | ||||
|     }, | ||||
|     { | ||||
|       title: '登录时间', | ||||
|       dataIndex: 'loginTime', | ||||
|     }, | ||||
|     { | ||||
|       title: '操作', | ||||
|       slotName: 'operations', | ||||
|       align: 'center', | ||||
|     }, | ||||
|   ]); | ||||
|  | ||||
|   // 分页查询列表 | ||||
|   const fetchData = async ( | ||||
|     params: OnlineUserParams = { page: 1, size: 10, sort: ['createTime,desc'] } | ||||
|   ) => { | ||||
|     setLoading(true); | ||||
|     try { | ||||
|       const { data } = await queryOnlineUserList(params); | ||||
|       renderData.value = data.list; | ||||
|       pagination.current = params.page; | ||||
|       pagination.total = data.total; | ||||
|     } finally { | ||||
|       setLoading(false); | ||||
|     } | ||||
|   }; | ||||
|   const handlePageChange = (current: number) => { | ||||
|     fetchData({ page: current, size: pagination.pageSize, sort: ['createTime,desc'] }); | ||||
|   }; | ||||
|   const handlePageSizeChange = (pageSize: number) => { | ||||
|     fetchData({ page: pagination.current, size: pageSize, sort: ['createTime,desc'] }); | ||||
|   }; | ||||
|   fetchData(); | ||||
|  | ||||
|   // 强退 | ||||
|   const handleClick = async (token: string) => { | ||||
|     const res = await kickout(token); | ||||
|     if (res.success) Message.success(res.msg); | ||||
|   }; | ||||
| </script> | ||||
|  | ||||
| <script lang="ts"> | ||||
|   export default { | ||||
|     name: 'OnlineUser', | ||||
|   }; | ||||
| </script> | ||||
|  | ||||
| <style scoped lang="less"> | ||||
|   .container { | ||||
|     padding: 0 20px 20px 20px; | ||||
|   } | ||||
|   :deep(.arco-table-th) { | ||||
|     &:last-child { | ||||
|       .arco-table-th-item-title { | ||||
|         margin-left: 16px; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| </style> | ||||
| @@ -0,0 +1,3 @@ | ||||
| export default { | ||||
|   'menu.online.user.list': 'Online user', | ||||
| }; | ||||
| @@ -0,0 +1,3 @@ | ||||
| export default { | ||||
|   'menu.online.user.list': '在线用户', | ||||
| }; | ||||
		Reference in New Issue
	
	Block a user