init:first commit of plus-ui
This commit is contained in:
		
							
								
								
									
										9
									
								
								src/views/monitor/admin/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/views/monitor/admin/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <i-frame v-model:src="url"></i-frame> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| const url = ref(import.meta.env.VITE_APP_MONITOR_ADMIN); | ||||
| </script> | ||||
							
								
								
									
										192
									
								
								src/views/monitor/cache/index.vue
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								src/views/monitor/cache/index.vue
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,192 @@ | ||||
| <template> | ||||
|   <div class="p-2"> | ||||
|     <el-row> | ||||
|       <el-col :span="24" class="card-box"> | ||||
|         <el-card shadow="hover"> | ||||
|           <template #header> | ||||
|             <Monitor style="width: 1em; height: 1em; vertical-align: middle" /> | ||||
|             <span style="vertical-align: middle">基本信息</span> | ||||
|           </template> | ||||
|  | ||||
|           <div class="el-table el-table--enable-row-hover el-table--medium"> | ||||
|             <table style="width: 100%"> | ||||
|               <tbody> | ||||
|                 <tr> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">Redis版本</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.redis_version }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">运行模式</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.redis_mode === 'standalone' ? '单机' : '集群' }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">端口</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.tcp_port }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">客户端数</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.connected_clients }}</div> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|                 <tr> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">运行时间(天)</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.uptime_in_days }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">使用内存</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.used_memory_human }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">使用CPU</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ parseFloat(cache.info.used_cpu_user_children).toFixed(2) }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">内存配置</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.maxmemory_human }}</div> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|                 <tr> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">AOF是否开启</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.aof_enabled === '0' ? '否' : '是' }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">RDB是否成功</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell">{{ cache.info.rdb_last_bgsave_status }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">Key数量</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.dbSize" class="cell">{{ cache.dbSize }}</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div class="cell">网络入口/出口</div> | ||||
|                   </td> | ||||
|                   <td class="el-table__cell is-leaf"> | ||||
|                     <div v-if="cache.info" class="cell"> | ||||
|                       {{ cache.info.instantaneous_input_kbps }}kps/{{ cache.info.instantaneous_output_kbps }}kps | ||||
|                     </div> | ||||
|                   </td> | ||||
|                 </tr> | ||||
|               </tbody> | ||||
|             </table> | ||||
|           </div> | ||||
|         </el-card> | ||||
|       </el-col> | ||||
|  | ||||
|       <el-col :span="12" class="card-box"> | ||||
|         <el-card shadow="hover"> | ||||
|           <template #header> | ||||
|             <PieChart style="width: 1em; height: 1em; vertical-align: middle" /> | ||||
|             <span style="vertical-align: middle">命令统计</span> | ||||
|           </template> | ||||
|           <div class="el-table el-table--enable-row-hover el-table--medium"> | ||||
|             <div ref="commandstats" style="height: 420px" /> | ||||
|           </div> | ||||
|         </el-card> | ||||
|       </el-col> | ||||
|  | ||||
|       <el-col :span="12" class="card-box"> | ||||
|         <el-card shadow="hover"> | ||||
|           <template #header> | ||||
|             <Odometer style="width: 1em; height: 1em; vertical-align: middle" /> <span style="vertical-align: middle">内存信息</span> | ||||
|           </template> | ||||
|           <div class="el-table el-table--enable-row-hover el-table--medium"> | ||||
|             <div ref="usedmemory" style="height: 420px" /> | ||||
|           </div> | ||||
|         </el-card> | ||||
|       </el-col> | ||||
|     </el-row> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Cache" lang="ts"> | ||||
| import { getCache } from '@/api/monitor/cache'; | ||||
| import * as echarts from 'echarts'; | ||||
| import { CacheVO } from '@/api/monitor/cache/types'; | ||||
|  | ||||
| const cache = ref<Partial<CacheVO>>({}); | ||||
| const commandstats = ref(); | ||||
| const usedmemory = ref(); | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
|  | ||||
| const getList = async () => { | ||||
|   proxy?.$modal.loading('正在加载缓存监控数据,请稍候!'); | ||||
|   const res = await getCache(); | ||||
|   proxy?.$modal.closeLoading(); | ||||
|   cache.value = res.data; | ||||
|   const commandstatsIntance = echarts.init(commandstats.value, 'macarons'); | ||||
|   commandstatsIntance.setOption({ | ||||
|     tooltip: { | ||||
|       trigger: 'item', | ||||
|       formatter: '{a} <br/>{b} : {c} ({d}%)' | ||||
|     }, | ||||
|     series: [ | ||||
|       { | ||||
|         name: '命令', | ||||
|         type: 'pie', | ||||
|         roseType: 'radius', | ||||
|         radius: [15, 95], | ||||
|         center: ['50%', '38%'], | ||||
|         data: res.data.commandStats, | ||||
|         animationEasing: 'cubicInOut', | ||||
|         animationDuration: 1000 | ||||
|       } | ||||
|     ] | ||||
|   }); | ||||
|   const usedmemoryInstance = echarts.init(usedmemory.value, 'macarons'); | ||||
|   usedmemoryInstance.setOption({ | ||||
|     tooltip: { | ||||
|       formatter: '{b} <br/>{a} : ' + cache.value.info.used_memory_human | ||||
|     }, | ||||
|     series: [ | ||||
|       { | ||||
|         name: '峰值', | ||||
|         type: 'gauge', | ||||
|         min: 0, | ||||
|         max: 1000, | ||||
|         detail: { | ||||
|           formatter: cache.value.info.used_memory_human | ||||
|         }, | ||||
|         data: [ | ||||
|           { | ||||
|             value: parseFloat(cache.value.info.used_memory_human), | ||||
|             name: '内存消耗' | ||||
|           } | ||||
|         ] | ||||
|       } | ||||
|     ] | ||||
|   }); | ||||
|   window.addEventListener('resize', () => { | ||||
|     commandstatsIntance.resize(); | ||||
|     usedmemoryInstance.resize(); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										208
									
								
								src/views/monitor/logininfor/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										208
									
								
								src/views/monitor/logininfor/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,208 @@ | ||||
| <template> | ||||
|   <div class="p-2"> | ||||
|     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> | ||||
|       <div v-show="showSearch" class="mb-[10px]"> | ||||
|         <el-card shadow="hover"> | ||||
|           <el-form ref="queryFormRef" :model="queryParams" :inline="true"> | ||||
|             <el-form-item label="登录地址" prop="ipaddr"> | ||||
|               <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="用户名称" prop="userName"> | ||||
|               <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="状态" prop="status"> | ||||
|               <el-select v-model="queryParams.status" placeholder="登录状态" clearable> | ||||
|                 <el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="登录时间" style="width: 308px"> | ||||
|               <el-date-picker | ||||
|                 v-model="dateRange" | ||||
|                 value-format="YYYY-MM-DD HH:mm:ss" | ||||
|                 type="daterange" | ||||
|                 range-separator="-" | ||||
|                 start-placeholder="开始日期" | ||||
|                 end-placeholder="结束日期" | ||||
|                 :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" | ||||
|               ></el-date-picker> | ||||
|             </el-form-item> | ||||
|             <el-form-item> | ||||
|               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> | ||||
|               <el-button icon="Refresh" @click="resetQuery">重置</el-button> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </el-card> | ||||
|       </div> | ||||
|     </transition> | ||||
|  | ||||
|     <el-card shadow="hover"> | ||||
|       <template #header> | ||||
|         <el-row :gutter="10" class="mb8"> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['monitor:logininfor:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"> | ||||
|               删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['monitor:logininfor:remove']" type="danger" plain icon="Delete" @click="handleClean">清空</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['monitor:logininfor:unlock']" type="primary" plain icon="Unlock" :disabled="single" @click="handleUnlock"> | ||||
|               解锁 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['monitor:logininfor:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table | ||||
|         ref="loginInfoTableRef" | ||||
|         v-loading="loading" | ||||
|         :data="loginInfoList" | ||||
|         :default-sort="defaultSort" | ||||
|         @selection-change="handleSelectionChange" | ||||
|         @sort-change="handleSortChange" | ||||
|       > | ||||
|         <el-table-column type="selection" width="55" align="center" /> | ||||
|         <el-table-column label="访问编号" align="center" prop="infoId" /> | ||||
|         <el-table-column | ||||
|           label="用户名称" | ||||
|           align="center" | ||||
|           prop="userName" | ||||
|           :show-overflow-tooltip="true" | ||||
|           sortable="custom" | ||||
|           :sort-orders="['descending', 'ascending']" | ||||
|         /> | ||||
|         <el-table-column label="客户端" align="center" prop="clientKey" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="设备类型" align="center"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="sys_device_type" :value="scope.row.deviceType" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="地址" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="操作系统" align="center" prop="os" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="浏览器" align="center" prop="browser" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="登录状态" align="center" prop="status"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="sys_common_status" :value="scope.row.status" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="描述" align="center" prop="msg" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="访问时间" align="center" prop="loginTime" sortable="custom" :sort-orders="['descending', 'ascending']" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ proxy.parseTime(scope.row.loginTime) }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table> | ||||
|  | ||||
|       <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" /> | ||||
|     </el-card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Logininfor" lang="ts"> | ||||
| import { list, delLoginInfo, cleanLoginInfo, unlockLoginInfo } from '@/api/monitor/loginInfo'; | ||||
| import { LoginInfoQuery, LoginInfoVO } from '@/api/monitor/loginInfo/types'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type')); | ||||
| const { sys_common_status } = toRefs<any>(proxy?.useDict('sys_common_status')); | ||||
|  | ||||
| const loginInfoList = ref<LoginInfoVO[]>([]); | ||||
| const loading = ref(true); | ||||
| const showSearch = ref(true); | ||||
| const ids = ref<Array<number | string>>([]); | ||||
| const single = ref(true); | ||||
| const multiple = ref(true); | ||||
| const selectName = ref<Array<string>>([]); | ||||
| const total = ref(0); | ||||
| const dateRange = ref<[DateModelType, DateModelType]>(['', '']); | ||||
| const defaultSort = ref<any>({ prop: 'loginTime', order: 'descending' }); | ||||
|  | ||||
| const queryFormRef = ref<ElFormInstance>(); | ||||
| const loginInfoTableRef = ref<ElTableInstance>(); | ||||
| // 查询参数 | ||||
| const queryParams = ref<LoginInfoQuery>({ | ||||
|   pageNum: 1, | ||||
|   pageSize: 10, | ||||
|   ipaddr: '', | ||||
|   userName: '', | ||||
|   status: '', | ||||
|   orderByColumn: defaultSort.value.prop, | ||||
|   isAsc: defaultSort.value.order | ||||
| }); | ||||
|  | ||||
| /** 查询登录日志列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value)); | ||||
|   loginInfoList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   dateRange.value = ['', '']; | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   queryParams.value.pageNum = 1; | ||||
|   loginInfoTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order); | ||||
| }; | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: LoginInfoVO[]) => { | ||||
|   ids.value = selection.map((item) => item.infoId); | ||||
|   multiple.value = !selection.length; | ||||
|   single.value = selection.length != 1; | ||||
|   selectName.value = selection.map((item) => item.userName); | ||||
| }; | ||||
| /** 排序触发事件 */ | ||||
| const handleSortChange = (column: any) => { | ||||
|   queryParams.value.orderByColumn = column.prop; | ||||
|   queryParams.value.isAsc = column.order; | ||||
|   getList(); | ||||
| }; | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: LoginInfoVO) => { | ||||
|   const infoIds = row?.infoId || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除访问编号为"' + infoIds + '"的数据项?'); | ||||
|   await delLoginInfo(infoIds); | ||||
|   await getList(); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
| }; | ||||
| /** 清空按钮操作 */ | ||||
| const handleClean = async () => { | ||||
|   await proxy?.$modal.confirm('是否确认清空所有登录日志数据项?'); | ||||
|   await cleanLoginInfo(); | ||||
|   await getList(); | ||||
|   proxy?.$modal.msgSuccess('清空成功'); | ||||
| }; | ||||
| /** 解锁按钮操作 */ | ||||
| const handleUnlock = async () => { | ||||
|   const username = selectName.value; | ||||
|   await proxy?.$modal.confirm('是否确认解锁用户"' + username + '"数据项?'); | ||||
|   await unlockLoginInfo(username); | ||||
|   proxy?.$modal.msgSuccess('用户' + username + '解锁成功'); | ||||
| }; | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'monitor/logininfor/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `logininfor_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										116
									
								
								src/views/monitor/online/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								src/views/monitor/online/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,116 @@ | ||||
| <template> | ||||
|   <div class="p-2"> | ||||
|     <div class="mb-[10px]"> | ||||
|       <el-card shadow="hover"> | ||||
|         <el-form ref="queryFormRef" :model="queryParams" :inline="true"> | ||||
|           <el-form-item label="登录地址" prop="ipaddr"> | ||||
|             <el-input v-model="queryParams.ipaddr" placeholder="请输入登录地址" clearable @keyup.enter="handleQuery" /> | ||||
|           </el-form-item> | ||||
|           <el-form-item label="用户名称" prop="userName"> | ||||
|             <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable @keyup.enter="handleQuery" /> | ||||
|           </el-form-item> | ||||
|           <el-form-item> | ||||
|             <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> | ||||
|             <el-button icon="Refresh" @click="resetQuery">重置</el-button> | ||||
|           </el-form-item> | ||||
|         </el-form> | ||||
|       </el-card> | ||||
|     </div> | ||||
|     <el-card shadow="hover"> | ||||
|       <el-table | ||||
|         v-loading="loading" | ||||
|         :data="onlineList.slice((queryParams.pageNum - 1) * queryParams.pageSize, queryParams.pageNum * queryParams.pageSize)" | ||||
|         style="width: 100%" | ||||
|       > | ||||
|         <el-table-column label="序号" width="50" type="index" align="center"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ (queryParams.pageNum - 1) * queryParams.pageSize + scope.$index + 1 }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="会话编号" align="center" prop="tokenId" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="登录名称" align="center" prop="userName" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="客户端" align="center" prop="clientKey" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="设备类型" align="center"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="sys_device_type" :value="scope.row.deviceType" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="所属部门" align="center" prop="deptName" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="主机" align="center" prop="ipaddr" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="登录地点" align="center" prop="loginLocation" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="操作系统" align="center" prop="os" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="浏览器" align="center" prop="browser" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="登录时间" align="center" prop="loginTime" width="180"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ proxy.parseTime(scope.row.loginTime) }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="操作" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-tooltip content="强退" placement="top"> | ||||
|               <el-button v-hasPermi="['monitor:online:forceLogout']" link type="primary" icon="Delete" @click="handleForceLogout(scope.row)"> | ||||
|               </el-button> | ||||
|             </el-tooltip> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table> | ||||
|  | ||||
|       <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" /> | ||||
|     </el-card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Online" lang="ts"> | ||||
| import { forceLogout, list as initData } from '@/api/monitor/online'; | ||||
| import { OnlineQuery, OnlineVO } from '@/api/monitor/online/types'; | ||||
| import api from '@/api/system/user'; | ||||
| import { to } from 'await-to-js'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { sys_device_type } = toRefs<any>(proxy?.useDict('sys_device_type')); | ||||
|  | ||||
| const onlineList = ref<OnlineVO[]>([]); | ||||
| const loading = ref(true); | ||||
| const total = ref(0); | ||||
|  | ||||
| const queryFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const queryParams = ref<OnlineQuery>({ | ||||
|   pageNum: 1, | ||||
|   pageSize: 10, | ||||
|   ipaddr: '', | ||||
|   userName: '' | ||||
| }); | ||||
|  | ||||
| /** 查询登录日志列表 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await initData(queryParams.value); | ||||
|   onlineList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   handleQuery(); | ||||
| }; | ||||
| /** 强退按钮操作 */ | ||||
| const handleForceLogout = async (row: OnlineVO) => { | ||||
|   const [err] = await to(proxy?.$modal.confirm('是否确认强退名称为"' + row.userName + '"的用户?') as any); | ||||
|   if (!err) { | ||||
|     await forceLogout(row.tokenId); | ||||
|     await getList(); | ||||
|     proxy?.$modal.msgSuccess('删除成功'); | ||||
|   } | ||||
| }; | ||||
|  | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										260
									
								
								src/views/monitor/operlog/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										260
									
								
								src/views/monitor/operlog/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,260 @@ | ||||
| <template> | ||||
|   <div class="p-2"> | ||||
|     <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave"> | ||||
|       <div v-show="showSearch" class="mb-[10px]"> | ||||
|         <el-card shadow="hover"> | ||||
|           <el-form ref="queryFormRef" :model="queryParams" :inline="true"> | ||||
|             <el-form-item label="操作地址" prop="operIp"> | ||||
|               <el-input v-model="queryParams.operIp" placeholder="请输入操作地址" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="系统模块" prop="title"> | ||||
|               <el-input v-model="queryParams.title" placeholder="请输入系统模块" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="操作人员" prop="operName"> | ||||
|               <el-input v-model="queryParams.operName" placeholder="请输入操作人员" clearable @keyup.enter="handleQuery" /> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="类型" prop="businessType"> | ||||
|               <el-select v-model="queryParams.businessType" placeholder="操作类型" clearable> | ||||
|                 <el-option v-for="dict in sys_oper_type" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="状态" prop="status"> | ||||
|               <el-select v-model="queryParams.status" placeholder="操作状态" clearable> | ||||
|                 <el-option v-for="dict in sys_common_status" :key="dict.value" :label="dict.label" :value="dict.value" /> | ||||
|               </el-select> | ||||
|             </el-form-item> | ||||
|             <el-form-item label="操作时间" style="width: 308px"> | ||||
|               <el-date-picker | ||||
|                 v-model="dateRange" | ||||
|                 value-format="YYYY-MM-DD HH:mm:ss" | ||||
|                 type="daterange" | ||||
|                 range-separator="-" | ||||
|                 start-placeholder="开始日期" | ||||
|                 end-placeholder="结束日期" | ||||
|                 :default-time="[new Date(2000, 1, 1, 0, 0, 0), new Date(2000, 1, 1, 23, 59, 59)]" | ||||
|               ></el-date-picker> | ||||
|             </el-form-item> | ||||
|             <el-form-item> | ||||
|               <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button> | ||||
|               <el-button icon="Refresh" @click="resetQuery">重置</el-button> | ||||
|             </el-form-item> | ||||
|           </el-form> | ||||
|         </el-card> | ||||
|       </div> | ||||
|     </transition> | ||||
|  | ||||
|     <el-card shadow="hover"> | ||||
|       <template #header> | ||||
|         <el-row :gutter="10" class="mb8"> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete()"> | ||||
|               删除 | ||||
|             </el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['monitor:operlog:remove']" type="danger" plain icon="WarnTriangleFilled" @click="handleClean">清空</el-button> | ||||
|           </el-col> | ||||
|           <el-col :span="1.5"> | ||||
|             <el-button v-hasPermi="['monitor:operlog:export']" type="warning" plain icon="Download" @click="handleExport">导出</el-button> | ||||
|           </el-col> | ||||
|           <right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar> | ||||
|         </el-row> | ||||
|       </template> | ||||
|  | ||||
|       <el-table | ||||
|         ref="operLogTableRef" | ||||
|         v-loading="loading" | ||||
|         :data="operlogList" | ||||
|         :default-sort="defaultSort" | ||||
|         @selection-change="handleSelectionChange" | ||||
|         @sort-change="handleSortChange" | ||||
|       > | ||||
|         <el-table-column type="selection" width="50" align="center" /> | ||||
|         <el-table-column label="日志编号" align="center" prop="operId" /> | ||||
|         <el-table-column label="系统模块" align="center" prop="title" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="操作类型" align="center" prop="businessType"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="sys_oper_type" :value="scope.row.businessType" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column | ||||
|           label="操作人员" | ||||
|           align="center" | ||||
|           width="110" | ||||
|           prop="operName" | ||||
|           :show-overflow-tooltip="true" | ||||
|           sortable="custom" | ||||
|           :sort-orders="['descending', 'ascending']" | ||||
|         /> | ||||
|         <el-table-column label="部门" align="center" prop="deptName" width="130" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="操作地址" align="center" prop="operIp" width="130" :show-overflow-tooltip="true" /> | ||||
|         <el-table-column label="操作状态" align="center" prop="status"> | ||||
|           <template #default="scope"> | ||||
|             <dict-tag :options="sys_common_status" :value="scope.row.status" /> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="操作日期" align="center" prop="operTime" width="180" sortable="custom" :sort-orders="['descending', 'ascending']"> | ||||
|           <template #default="scope"> | ||||
|             <span>{{ proxy.parseTime(scope.row.operTime) }}</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column | ||||
|           label="消耗时间" | ||||
|           align="center" | ||||
|           prop="costTime" | ||||
|           width="110" | ||||
|           :show-overflow-tooltip="true" | ||||
|           sortable="custom" | ||||
|           :sort-orders="['descending', 'ascending']" | ||||
|         > | ||||
|           <template #default="scope"> | ||||
|             <span>{{ scope.row.costTime }}毫秒</span> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|         <el-table-column label="操作" fixed="right" align="center" class-name="small-padding fixed-width"> | ||||
|           <template #default="scope"> | ||||
|             <el-tooltip content="详细" placement="top"> | ||||
|               <el-button v-hasPermi="['monitor:operlog:query']" link type="primary" icon="View" @click="handleView(scope.row)"> </el-button> | ||||
|             </el-tooltip> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table> | ||||
|  | ||||
|       <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" /> | ||||
|     </el-card> | ||||
|     <!-- 操作日志详细 --> | ||||
|     <OperInfoDialog ref="operInfoDialogRef" /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup name="Operlog" lang="ts"> | ||||
| import { list, delOperlog, cleanOperlog } from '@/api/monitor/operlog'; | ||||
| import { OperLogForm, OperLogQuery, OperLogVO } from '@/api/monitor/operlog/types'; | ||||
| import OperInfoDialog from './oper-info-dialog.vue'; | ||||
|  | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { sys_oper_type, sys_common_status } = toRefs<any>(proxy?.useDict('sys_oper_type', 'sys_common_status')); | ||||
|  | ||||
| const operlogList = ref<OperLogVO[]>([]); | ||||
| const loading = ref(true); | ||||
| const showSearch = ref(true); | ||||
| const ids = ref<Array<number | string>>([]); | ||||
| const multiple = ref(true); | ||||
| const total = ref(0); | ||||
| const dateRange = ref<[DateModelType, DateModelType]>(['', '']); | ||||
| const defaultSort = ref<any>({ prop: 'operTime', order: 'descending' }); | ||||
|  | ||||
| const operLogTableRef = ref<ElTableInstance>(); | ||||
| const queryFormRef = ref<ElFormInstance>(); | ||||
|  | ||||
| const data = reactive<PageData<OperLogForm, OperLogQuery>>({ | ||||
|   form: { | ||||
|     operId: undefined, | ||||
|     tenantId: undefined, | ||||
|     title: '', | ||||
|     businessType: 0, | ||||
|     businessTypes: undefined, | ||||
|     method: '', | ||||
|     requestMethod: '', | ||||
|     operatorType: 0, | ||||
|     operName: '', | ||||
|     deptName: '', | ||||
|     operUrl: '', | ||||
|     operIp: '', | ||||
|     operLocation: '', | ||||
|     operParam: '', | ||||
|     jsonResult: '', | ||||
|     status: 0, | ||||
|     errorMsg: '', | ||||
|     operTime: '', | ||||
|     costTime: 0 | ||||
|   }, | ||||
|   queryParams: { | ||||
|     pageNum: 1, | ||||
|     pageSize: 10, | ||||
|     operIp: '', | ||||
|     title: '', | ||||
|     operName: '', | ||||
|     businessType: '', | ||||
|     status: '', | ||||
|     orderByColumn: defaultSort.value.prop, | ||||
|     isAsc: defaultSort.value.order | ||||
|   }, | ||||
|   rules: {} | ||||
| }); | ||||
|  | ||||
| const { queryParams, form } = toRefs(data); | ||||
|  | ||||
| /** 查询登录日志 */ | ||||
| const getList = async () => { | ||||
|   loading.value = true; | ||||
|   const res = await list(proxy?.addDateRange(queryParams.value, dateRange.value)); | ||||
|   operlogList.value = res.rows; | ||||
|   total.value = res.total; | ||||
|   loading.value = false; | ||||
| }; | ||||
| /** 操作日志类型字典翻译 */ | ||||
| const typeFormat = (row: OperLogForm) => { | ||||
|   return proxy?.selectDictLabel(sys_oper_type.value, row.businessType); | ||||
| }; | ||||
| /** 搜索按钮操作 */ | ||||
| const handleQuery = () => { | ||||
|   queryParams.value.pageNum = 1; | ||||
|   getList(); | ||||
| }; | ||||
| /** 重置按钮操作 */ | ||||
| const resetQuery = () => { | ||||
|   dateRange.value = ['', '']; | ||||
|   queryFormRef.value?.resetFields(); | ||||
|   queryParams.value.pageNum = 1; | ||||
|   operLogTableRef.value?.sort(defaultSort.value.prop, defaultSort.value.order); | ||||
| }; | ||||
| /** 多选框选中数据 */ | ||||
| const handleSelectionChange = (selection: OperLogVO[]) => { | ||||
|   ids.value = selection.map((item) => item.operId); | ||||
|   multiple.value = !selection.length; | ||||
| }; | ||||
| /** 排序触发事件 */ | ||||
| const handleSortChange = (column: any) => { | ||||
|   queryParams.value.orderByColumn = column.prop; | ||||
|   queryParams.value.isAsc = column.order; | ||||
|   getList(); | ||||
| }; | ||||
|  | ||||
| const operInfoDialogRef = ref<InstanceType<typeof OperInfoDialog>>(); | ||||
| /** 详细按钮操作 */ | ||||
| const handleView = (row: OperLogVO) => { | ||||
|   operInfoDialogRef.value.openDialog(row); | ||||
| }; | ||||
|  | ||||
| /** 删除按钮操作 */ | ||||
| const handleDelete = async (row?: OperLogVO) => { | ||||
|   const operIds = row?.operId || ids.value; | ||||
|   await proxy?.$modal.confirm('是否确认删除日志编号为"' + operIds + '"的数据项?'); | ||||
|   await delOperlog(operIds); | ||||
|   await getList(); | ||||
|   proxy?.$modal.msgSuccess('删除成功'); | ||||
| }; | ||||
|  | ||||
| /** 清空按钮操作 */ | ||||
| const handleClean = async () => { | ||||
|   await proxy?.$modal.confirm('是否确认清空所有操作日志数据项?'); | ||||
|   await cleanOperlog(); | ||||
|   await getList(); | ||||
|   proxy?.$modal.msgSuccess('清空成功'); | ||||
| }; | ||||
|  | ||||
| /** 导出按钮操作 */ | ||||
| const handleExport = () => { | ||||
|   proxy?.download( | ||||
|     'monitor/operlog/export', | ||||
|     { | ||||
|       ...queryParams.value | ||||
|     }, | ||||
|     `config_${new Date().getTime()}.xlsx` | ||||
|   ); | ||||
| }; | ||||
| onMounted(() => { | ||||
|   getList(); | ||||
| }); | ||||
| </script> | ||||
							
								
								
									
										111
									
								
								src/views/monitor/operlog/oper-info-dialog.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										111
									
								
								src/views/monitor/operlog/oper-info-dialog.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,111 @@ | ||||
| <template> | ||||
|   <el-dialog v-model="open" title="操作日志详细" width="700px" append-to-body close-on-click-modal @closed="info = null"> | ||||
|     <el-descriptions v-if="info" :column="1" border> | ||||
|       <el-descriptions-item label="操作状态"> | ||||
|         <template #default> | ||||
|           <el-tag v-if="info.status === 0" type="success">正常</el-tag> | ||||
|           <el-tag v-else-if="info.status === 1" type="danger">失败</el-tag> | ||||
|         </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="登录信息"> | ||||
|         <template #default> {{ info.operName }} / {{ info.deptName }} / {{ info.operIp }} / {{ info.operLocation }} </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="请求信息"> | ||||
|         <template #default> {{ info.requestMethod }} {{ info.operUrl }} </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="操作模块"> | ||||
|         <template #default> {{ info.title }} / {{ typeFormat(info) }} </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="操作方法"> | ||||
|         <template #default> | ||||
|           {{ info.method }} | ||||
|         </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="请求参数"> | ||||
|         <template #default> | ||||
|           <div class="max-h-300px overflow-y-auto"> | ||||
|             <VueJsonPretty :data="formatToJsonObject(info.operParam)" /> | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="返回参数"> | ||||
|         <template #default> | ||||
|           <div class="max-h-300px overflow-y-auto"> | ||||
|             <VueJsonPretty :data="formatToJsonObject(info.jsonResult)" /> | ||||
|           </div> | ||||
|         </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="消耗时间"> | ||||
|         <template #default> | ||||
|           <span> {{ info.costTime }}ms </span> | ||||
|         </template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item label="操作时间"> | ||||
|         <template #default> {{ proxy.parseTime(info.operTime) }}</template> | ||||
|       </el-descriptions-item> | ||||
|       <el-descriptions-item v-if="info.status === 1" label="异常信息"> | ||||
|         <template #default> | ||||
|           <span class="text-danger"> {{ info.errorMsg }}</span> | ||||
|         </template> | ||||
|       </el-descriptions-item> | ||||
|     </el-descriptions> | ||||
|   </el-dialog> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import type { OperLogForm } from '@/api/monitor/operlog/types'; | ||||
| import VueJsonPretty from 'vue-json-pretty'; | ||||
| import 'vue-json-pretty/lib/styles.css'; | ||||
|  | ||||
| const open = ref(false); | ||||
| const info = ref<OperLogForm | null>(null); | ||||
| function openDialog(row: OperLogForm) { | ||||
|   info.value = row; | ||||
|   open.value = true; | ||||
| } | ||||
|  | ||||
| function closeDialog() { | ||||
|   open.value = false; | ||||
| } | ||||
|  | ||||
| defineExpose({ | ||||
|   openDialog, | ||||
|   closeDialog | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * json转为对象 | ||||
|  * @param data 原始数据 | ||||
|  */ | ||||
| function formatToJsonObject(data: string) { | ||||
|   try { | ||||
|     return JSON.parse(data); | ||||
|   } catch (error) { | ||||
|     return data; | ||||
|   } | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * 字典信息 | ||||
|  */ | ||||
| const { proxy } = getCurrentInstance() as ComponentInternalInstance; | ||||
| const { sys_oper_type } = toRefs<any>(proxy?.useDict('sys_oper_type')); | ||||
| const typeFormat = (row: OperLogForm) => { | ||||
|   return proxy?.selectDictLabel(sys_oper_type.value, row.businessType); | ||||
| }; | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| /** | ||||
| label宽度固定 | ||||
| */ | ||||
| :deep(.el-descriptions__label) { | ||||
|   min-width: 100px; | ||||
| } | ||||
| /** | ||||
| 文字超过 换行显示 | ||||
| */ | ||||
| :deep(.el-descriptions__content) { | ||||
|   max-width: 300px; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										9
									
								
								src/views/monitor/snailjob/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/views/monitor/snailjob/index.vue
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <i-frame v-model:src="url"></i-frame> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| const url = ref(import.meta.env.VITE_APP_SNAILJOB_ADMIN); | ||||
| </script> | ||||
		Reference in New Issue
	
	Block a user