From 131d1e8ada4a4e3927a4b30897addffd7678f5c9 Mon Sep 17 00:00:00 2001 From: xiaoming <1923740402@qq.com> Date: Mon, 4 Mar 2024 16:44:30 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=B3=BB=E7=BB=9F?= =?UTF-8?q?=E7=9B=91=E6=8E=A7-=E5=9C=A8=E7=BA=BF=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E3=80=81=E7=99=BB=E5=BD=95=E6=97=A5=E5=BF=97=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E7=A4=BA=E4=BE=8B=20(#951)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 添加系统监控页面示例 * chore: 完成系统监控-在线用户 * chore: 完成登录日志 --- locales/en.yaml | 5 + locales/zh-CN.yaml | 5 + mock/asyncRoutes.ts | 65 ++++++- mock/system.ts | 214 +++++++++++++++++++++-- package.json | 4 +- pnpm-lock.yaml | 16 +- src/api/system.ts | 26 ++- src/components/ReIcon/src/offlineIcon.ts | 10 ++ src/router/enums.ts | 20 ++- src/views/monitor/logs/login/hook.tsx | 168 ++++++++++++++++++ src/views/monitor/logs/login/index.vue | 154 ++++++++++++++++ src/views/monitor/logs/operation.vue | 9 + src/views/monitor/logs/system.vue | 9 + src/views/monitor/online/hook.tsx | 117 +++++++++++++ src/views/monitor/online/index.vue | 125 +++++++++++++ src/views/monitor/utils.ts | 129 ++++++++++++++ src/views/system/user/utils/hook.tsx | 1 + 17 files changed, 1036 insertions(+), 41 deletions(-) create mode 100644 src/views/monitor/logs/login/hook.tsx create mode 100644 src/views/monitor/logs/login/index.vue create mode 100644 src/views/monitor/logs/operation.vue create mode 100644 src/views/monitor/logs/system.vue create mode 100644 src/views/monitor/online/hook.tsx create mode 100644 src/views/monitor/online/index.vue create mode 100644 src/views/monitor/utils.ts diff --git a/locales/en.yaml b/locales/en.yaml index 7ff33270d..b9b1b4187 100644 --- a/locales/en.yaml +++ b/locales/en.yaml @@ -31,6 +31,11 @@ menus: hsRole: Role Manage hsSystemMenu: Menu Manage hsDept: Dept Manage + hssysMonitor: System Monitor + hsOnlineUser: Online User + hsLoginLog: Login Log + hsOperationLog: Operation Log + hsSystemLog: System Log hseditor: Editor hsabnormal: Abnormal Page hsfourZeroFour: "404" diff --git a/locales/zh-CN.yaml b/locales/zh-CN.yaml index cc2ed2f61..800a8bbe2 100644 --- a/locales/zh-CN.yaml +++ b/locales/zh-CN.yaml @@ -31,6 +31,11 @@ menus: hsRole: 角色管理 hsSystemMenu: 菜单管理 hsDept: 部门管理 + hssysMonitor: 系统监控 + hsOnlineUser: 在线用户 + hsLoginLog: 登录日志 + hsOperationLog: 操作日志 + hsSystemLog: 系统日志 hseditor: 编辑器 hsabnormal: 异常页面 hsfourZeroFour: "404" diff --git a/mock/asyncRoutes.ts b/mock/asyncRoutes.ts index 043886777..080d4e521 100644 --- a/mock/asyncRoutes.ts +++ b/mock/asyncRoutes.ts @@ -1,6 +1,6 @@ // 模拟后端动态生成路由 import { defineFakeRoute } from "vite-plugin-fake-server/client"; -import { system, permission, frame, tabs } from "@/router/enums"; +import { system, monitor, permission, frame, tabs } from "@/router/enums"; /** * roles:页面级别权限,这里模拟二种 "admin"、"common" @@ -8,7 +8,7 @@ import { system, permission, frame, tabs } from "@/router/enums"; * common:普通角色 */ -const systemRouter = { +const systemManagementRouter = { path: "/system", meta: { icon: "ri:settings-3-line", @@ -55,6 +55,57 @@ const systemRouter = { ] }; +const systemMonitorRouter = { + path: "/monitor", + meta: { + icon: "ep:monitor", + title: "menus.hssysMonitor", + rank: monitor + }, + children: [ + { + path: "/monitor/online-user", + component: "monitor/online/index", + name: "OnlineUser", + meta: { + icon: "ri:user-voice-line", + title: "menus.hsOnlineUser", + roles: ["admin"] + } + }, + { + path: "/monitor/login-logs", + component: "monitor/logs/login/index", + name: "LoginLog", + meta: { + icon: "ri:window-line", + title: "menus.hsLoginLog", + roles: ["admin"] + } + }, + { + path: "/monitor/operation-logs", + component: "monitor/logs/operation", + name: "OperationLog", + meta: { + icon: "ri:history-fill", + title: "menus.hsOperationLog", + roles: ["admin"] + } + }, + { + path: "/monitor/system-logs", + component: "monitor/logs/system", + name: "SystemLog", + meta: { + icon: "ri:file-search-line", + title: "menus.hsSystemLog", + roles: ["admin"] + } + } + ] +}; + const permissionRouter = { path: "/permission", meta: { @@ -90,7 +141,7 @@ const permissionRouter = { const frameRouter = { path: "/iframe", meta: { - icon: "ep:monitor", + icon: "ri:links-fill", title: "menus.hsExternalPage", rank: frame }, @@ -239,7 +290,13 @@ export default defineFakeRoute([ response: () => { return { success: true, - data: [systemRouter, permissionRouter, frameRouter, tabsRouter] + data: [ + systemManagementRouter, + systemMonitorRouter, + permissionRouter, + frameRouter, + tabsRouter + ] }; } } diff --git a/mock/system.ts b/mock/system.ts index 30d84d1b5..4893c8cc9 100644 --- a/mock/system.ts +++ b/mock/system.ts @@ -159,7 +159,7 @@ export default defineFakeRoute([ component: "", rank: 7, redirect: "", - icon: "ep:monitor", + icon: "ri:links-fill", extraIcon: "", enterTransition: "", leaveTransition: "", @@ -657,18 +657,18 @@ export default defineFakeRoute([ showLink: true, showParent: false }, - // 标签页操作 + // 系统监控 { parentId: 0, id: 400, menuType: 0, - title: "menus.hstabs", - name: "PureTabs", - path: "/tabs", + title: "menus.hssysMonitor", + name: "PureMonitor", + path: "/monitor", component: "", rank: 11, redirect: "", - icon: "ri:bookmark-2-line", + icon: "ep:monitor", extraIcon: "", enterTransition: "", leaveTransition: "", @@ -685,6 +685,122 @@ export default defineFakeRoute([ parentId: 400, id: 401, menuType: 0, + title: "menus.hsOnlineUser", + name: "OnlineUser", + path: "/monitor/online-user", + component: "monitor/online/index", + rank: null, + redirect: "", + icon: "ri:user-voice-line", + extraIcon: "", + enterTransition: "", + leaveTransition: "", + activePath: "", + auths: "", + frameSrc: "", + frameLoading: true, + keepAlive: false, + hiddenTag: false, + showLink: true, + showParent: false + }, + { + parentId: 400, + id: 402, + menuType: 0, + title: "menus.hsLoginLog", + name: "LoginLog", + path: "/monitor/login-logs", + component: "monitor/logs/login/index", + rank: null, + redirect: "", + icon: "ri:window-line", + extraIcon: "", + enterTransition: "", + leaveTransition: "", + activePath: "", + auths: "", + frameSrc: "", + frameLoading: true, + keepAlive: false, + hiddenTag: false, + showLink: true, + showParent: false + }, + { + parentId: 400, + id: 403, + menuType: 0, + title: "menus.hsOperationLog", + name: "OperationLog", + path: "/monitor/operation-logs", + component: "monitor/logs/operation", + rank: null, + redirect: "", + icon: "ri:history-fill", + extraIcon: "", + enterTransition: "", + leaveTransition: "", + activePath: "", + auths: "", + frameSrc: "", + frameLoading: true, + keepAlive: false, + hiddenTag: false, + showLink: true, + showParent: false + }, + { + parentId: 400, + id: 404, + menuType: 0, + title: "menus.hsSystemLog", + name: "SystemLog", + path: "/monitor/system-logs", + component: "monitor/logs/system", + rank: null, + redirect: "", + icon: "ri:file-search-line", + extraIcon: "", + enterTransition: "", + leaveTransition: "", + activePath: "", + auths: "", + frameSrc: "", + frameLoading: true, + keepAlive: false, + hiddenTag: false, + showLink: true, + showParent: false + }, + // 标签页操作 + { + parentId: 0, + id: 500, + menuType: 0, + title: "menus.hstabs", + name: "PureTabs", + path: "/tabs", + component: "", + rank: 12, + redirect: "", + icon: "ri:bookmark-2-line", + extraIcon: "", + enterTransition: "", + leaveTransition: "", + activePath: "", + auths: "", + frameSrc: "", + frameLoading: true, + keepAlive: false, + hiddenTag: false, + showLink: true, + showParent: false + }, + { + parentId: 500, + id: 501, + menuType: 0, title: "menus.hstabs", name: "Tabs", path: "/tabs/index", @@ -705,8 +821,8 @@ export default defineFakeRoute([ showParent: false }, { - parentId: 400, - id: 402, + parentId: 500, + id: 502, menuType: 0, title: "query传参模式", name: "TabQueryDetail", @@ -728,8 +844,8 @@ export default defineFakeRoute([ showParent: false }, { - parentId: 400, - id: 403, + parentId: 500, + id: 503, menuType: 0, title: "params传参模式", name: "TabParamsDetail", @@ -895,5 +1011,83 @@ export default defineFakeRoute([ ] }; } + }, + // 在线用户 + { + url: "/online-logs", + method: "post", + response: ({ body }) => { + let list = [ + { + id: 1, + username: "admin", + ip: faker.internet.ipv4(), + address: "中国河南省信阳市", + system: "macOS", + browser: "Chrome", + loginTime: new Date() + }, + { + id: 2, + username: "common", + ip: faker.internet.ipv4(), + address: "中国广东省深圳市", + system: "Windows", + browser: "Firefox", + loginTime: new Date() + } + ]; + list = list.filter(item => item.username.includes(body?.username)); + return { + success: true, + data: { + list, + total: list.length, // 总条目数 + pageSize: 10, // 每页显示条目个数 + currentPage: 1 // 当前页数 + } + }; + } + }, + // 登录日志 + { + url: "/login-logs", + method: "post", + response: ({ body }) => { + let list = [ + { + id: 1, + username: "admin", + ip: faker.internet.ipv4(), + address: "中国河南省信阳市", + system: "macOS", + browser: "Chrome", + status: 1, // 登录状态 1 成功 0 失败 + behavior: "账号登录", + loginTime: new Date() + }, + { + id: 2, + username: "common", + ip: faker.internet.ipv4(), + address: "中国广东省深圳市", + system: "Windows", + browser: "Firefox", + status: 0, + behavior: "第三方登录", + loginTime: new Date() + } + ]; + list = list.filter(item => item.username.includes(body?.username)); + return { + success: true, + data: { + list, + total: list.length, // 总条目数 + pageSize: 10, // 每页显示条目个数 + currentPage: 1 // 当前页数 + } + }; + } } ]); diff --git a/package.json b/package.json index 4900cc9a3..1ca2a5194 100644 --- a/package.json +++ b/package.json @@ -53,8 +53,8 @@ "@logicflow/core": "^1.2.22", "@logicflow/extension": "^1.2.22", "@pureadmin/descriptions": "^1.2.0", - "@pureadmin/table": "^3.1.0", - "@pureadmin/utils": "^2.4.4", + "@pureadmin/table": "^3.1.2", + "@pureadmin/utils": "^2.4.5", "@vueuse/core": "^10.9.0", "@vueuse/motion": "^2.1.0", "@wangeditor/editor": "^5.1.23", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 340c1b17a..8da22420c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -21,11 +21,11 @@ dependencies: specifier: ^1.2.0 version: 1.2.0(element-plus@2.6.0)(typescript@5.3.3) '@pureadmin/table': - specifier: ^3.1.0 - version: 3.1.0(element-plus@2.6.0)(typescript@5.3.3) + specifier: ^3.1.2 + version: 3.1.2(element-plus@2.6.0)(typescript@5.3.3) '@pureadmin/utils': - specifier: ^2.4.4 - version: 2.4.4(echarts@5.5.0)(vue@3.4.21) + specifier: ^2.4.5 + version: 2.4.5(echarts@5.5.0)(vue@3.4.21) '@vueuse/core': specifier: ^10.9.0 version: 10.9.0(vue@3.4.21) @@ -1748,8 +1748,8 @@ packages: - typescript dev: false - /@pureadmin/table@3.1.0(element-plus@2.6.0)(typescript@5.3.3): - resolution: {integrity: sha512-0K/6nXMlq0GdMxWc44Z5BaJVHYhZFD1PHFsG5CSg864//gcA4TppQqM/2KO+0Fcl+VHlGij5AxRYLhAPB3aVBA==} + /@pureadmin/table@3.1.2(element-plus@2.6.0)(typescript@5.3.3): + resolution: {integrity: sha512-6GrZCjBDFn/kKjn/HGkx0BH9RiArg5QktPN2u5PNpzHBhZZXWMoFcKCkysWLfDdWfpCowQWgnOpr0KjTPEgT0A==} peerDependencies: element-plus: ^2.0.0 dependencies: @@ -1767,8 +1767,8 @@ packages: string-hash: 1.1.3 dev: true - /@pureadmin/utils@2.4.4(echarts@5.5.0)(vue@3.4.21): - resolution: {integrity: sha512-dH1ml+/U50Te7KlZX8pkA08/o+XKYx8aFyds9aTBC34JDyn0GQSyhe0zFIfGwnFztWMToWn/cyitpXmDEcq3NA==} + /@pureadmin/utils@2.4.5(echarts@5.5.0)(vue@3.4.21): + resolution: {integrity: sha512-0JAUv2YzdzkO2VVwE8g2aw7cOpDSaDtt3bCl11Uwve7TKHGwE7o6fK22p6u48Jfz5LZwVNB5ugI7qrmXYC4TDw==} peerDependencies: echarts: '*' vue: '*' diff --git a/src/api/system.ts b/src/api/system.ts index 1518ae060..c820c3735 100644 --- a/src/api/system.ts +++ b/src/api/system.ts @@ -19,32 +19,42 @@ type ResultTable = { }; }; -/** 获取用户管理列表 */ +/** 获取系统管理-用户管理列表 */ export const getUserList = (data?: object) => { return http.request("post", "/user", { data }); }; -/** 用户管理-获取所有角色列表 */ +/** 系统管理-用户管理-获取所有角色列表 */ export const getAllRoleList = () => { return http.request("get", "/list-all-role"); }; -/** 用户管理-根据userId,获取对应角色id列表(userId:用户id) */ +/** 系统管理-用户管理-根据userId,获取对应角色id列表(userId:用户id) */ export const getRoleIds = (data?: object) => { return http.request("post", "/list-role-ids", { data }); }; -/** 获取角色管理列表 */ +/** 获取系统管理-角色管理列表 */ export const getRoleList = (data?: object) => { return http.request("post", "/role", { data }); }; -/** 获取部门管理列表 */ +/** 获取系统管理-菜单管理列表 */ +export const getMenuList = (data?: object) => { + return http.request("post", "/menu", { data }); +}; + +/** 获取系统管理-部门管理列表 */ export const getDeptList = (data?: object) => { return http.request("post", "/dept", { data }); }; -/** 获取菜单管理列表 */ -export const getMenuList = (data?: object) => { - return http.request("post", "/menu", { data }); +/** 获取系统监控-在线用户列表 */ +export const getOnlineLogsList = (data?: object) => { + return http.request("post", "/online-logs", { data }); +}; + +/** 获取系统监控-登录日志列表 */ +export const getLoginLogsList = (data?: object) => { + return http.request("post", "/login-logs", { data }); }; diff --git a/src/components/ReIcon/src/offlineIcon.ts b/src/components/ReIcon/src/offlineIcon.ts index ceca7220b..faeacc37d 100644 --- a/src/components/ReIcon/src/offlineIcon.ts +++ b/src/components/ReIcon/src/offlineIcon.ts @@ -27,12 +27,17 @@ import Role from "@iconify-icons/ri/admin-fill"; import Info from "@iconify-icons/ri/file-info-line"; import Dept from "@iconify-icons/ri/git-branch-line"; import Table from "@iconify-icons/ri/table-line"; +import Links from "@iconify-icons/ri/links-fill"; import Search from "@iconify-icons/ri/search-line"; import FlUser from "@iconify-icons/ri/admin-line"; import Setting from "@iconify-icons/ri/settings-3-line"; +import LoginLog from "@iconify-icons/ri/window-line"; import Artboard from "@iconify-icons/ri/artboard-line"; +import SystemLog from "@iconify-icons/ri/file-search-line"; import ListCheck from "@iconify-icons/ri/list-check"; import UbuntuFill from "@iconify-icons/ri/ubuntu-fill"; +import OnlineUser from "@iconify-icons/ri/user-voice-line"; +import OperationLog from "@iconify-icons/ri/history-fill"; import InformationLine from "@iconify-icons/ri/information-line"; import TerminalWindowLine from "@iconify-icons/ri/terminal-window-line"; import CheckboxCircleLine from "@iconify-icons/ri/checkbox-circle-line"; @@ -42,13 +47,18 @@ addIcon("ri:bank-card-line", Card); addIcon("ri:admin-fill", Role); addIcon("ri:file-info-line", Info); addIcon("ri:git-branch-line", Dept); +addIcon("ri:links-fill", Links); addIcon("ri:table-line", Table); addIcon("ri:search-line", Search); addIcon("ri:admin-line", FlUser); addIcon("ri:settings-3-line", Setting); +addIcon("ri:window-line", LoginLog); +addIcon("ri:file-search-line", SystemLog); addIcon("ri:artboard-line", Artboard); addIcon("ri:list-check", ListCheck); addIcon("ri:ubuntu-fill", UbuntuFill); +addIcon("ri:user-voice-line", OnlineUser); +addIcon("ri:history-fill", OperationLog); addIcon("ri:information-line", InformationLine); addIcon("ri:terminal-window-line", TerminalWindowLine); addIcon("ri:checkbox-circle-line", CheckboxCircleLine); diff --git a/src/router/enums.ts b/src/router/enums.ts index 5ee47213d..9670bffd8 100644 --- a/src/router/enums.ts +++ b/src/router/enums.ts @@ -11,15 +11,16 @@ const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以 nested = 8, permission = 9, system = 10, - tabs = 11, - about = 12, - editor = 13, - flowchart = 14, - formdesign = 15, - board = 16, - ppt = 17, - guide = 18, - menuoverflow = 19; + monitor = 11, + tabs = 12, + about = 13, + editor = 14, + flowchart = 15, + formdesign = 16, + board = 17, + ppt = 18, + guide = 19, + menuoverflow = 20; export { home, @@ -33,6 +34,7 @@ export { nested, permission, system, + monitor, tabs, about, editor, diff --git a/src/views/monitor/logs/login/hook.tsx b/src/views/monitor/logs/login/hook.tsx new file mode 100644 index 000000000..093f262a5 --- /dev/null +++ b/src/views/monitor/logs/login/hook.tsx @@ -0,0 +1,168 @@ +import dayjs from "dayjs"; +import { message } from "@/utils/message"; +import { getKeyList } from "@pureadmin/utils"; +import { getLoginLogsList } from "@/api/system"; +import { usePublicHooks } from "@/views/system/hooks"; +import type { PaginationProps } from "@pureadmin/table"; +import { type Ref, reactive, ref, onMounted, toRaw } from "vue"; + +export function useRole(tableRef: Ref) { + const form = reactive({ + username: "", + loginTime: "" + }); + const dataList = ref([]); + const loading = ref(true); + const selectedNum = ref(0); + const { tagStyle } = usePublicHooks(); + + const pagination = reactive({ + total: 0, + pageSize: 10, + currentPage: 1, + background: true + }); + const columns: TableColumnList = [ + { + label: "勾选列", // 如果需要表格多选,此处label必须设置 + type: "selection", + fixed: "left", + reserveSelection: true // 数据刷新后保留选项 + }, + { + label: "序号", + prop: "id", + minWidth: 90 + }, + { + label: "用户名", + prop: "username", + minWidth: 100 + }, + { + label: "登录 IP", + prop: "ip", + minWidth: 140 + }, + { + label: "登录地点", + prop: "address", + minWidth: 140 + }, + { + label: "操作系统", + prop: "system", + minWidth: 100 + }, + { + label: "浏览器类型", + prop: "browser", + minWidth: 100 + }, + { + label: "登录状态", + prop: "status", + minWidth: 100, + cellRenderer: ({ row, props }) => ( + + {row.status === 1 ? "成功" : "失败"} + + ) + }, + { + label: "登录行为", + prop: "behavior", + minWidth: 100 + }, + { + label: "登录时间", + prop: "loginTime", + minWidth: 180, + formatter: ({ loginTime }) => + dayjs(loginTime).format("YYYY-MM-DD HH:mm:ss") + } + ]; + + function handleSizeChange(val: number) { + console.log(`${val} items per page`); + } + + function handleCurrentChange(val: number) { + console.log(`current page: ${val}`); + } + + /** 当CheckBox选择项发生变化时会触发该事件 */ + function handleSelectionChange(val) { + selectedNum.value = val.length; + // 重置表格高度 + tableRef.value.setAdaptive(); + } + + /** 取消选择 */ + function onSelectionCancel() { + selectedNum.value = 0; + // 用于多选表格,清空用户的选择 + tableRef.value.getTableRef().clearSelection(); + } + + /** 批量删除 */ + function onbatchDel() { + // 返回当前选中的行 + const curSelected = tableRef.value.getTableRef().getSelectionRows(); + // 接下来根据实际业务,通过选中行的某项数据,比如下面的id,调用接口进行批量删除 + message(`已删除序号为 ${getKeyList(curSelected, "id")} 的数据`, { + type: "success" + }); + tableRef.value.getTableRef().clearSelection(); + onSearch(); + } + + /** 清空日志 */ + function clearAll() { + // 根据实际业务,调用接口删除所有日志数据 + message("已删除所有日志数据", { + type: "success" + }); + onSearch(); + } + + async function onSearch() { + loading.value = true; + const { data } = await getLoginLogsList(toRaw(form)); + dataList.value = data.list; + pagination.total = data.total; + pagination.pageSize = data.pageSize; + pagination.currentPage = data.currentPage; + + setTimeout(() => { + loading.value = false; + }, 500); + } + + const resetForm = formEl => { + if (!formEl) return; + formEl.resetFields(); + onSearch(); + }; + + onMounted(() => { + onSearch(); + }); + + return { + form, + loading, + columns, + dataList, + pagination, + selectedNum, + onSearch, + clearAll, + resetForm, + onbatchDel, + handleSizeChange, + onSelectionCancel, + handleCurrentChange, + handleSelectionChange + }; +} diff --git a/src/views/monitor/logs/login/index.vue b/src/views/monitor/logs/login/index.vue new file mode 100644 index 000000000..777600e04 --- /dev/null +++ b/src/views/monitor/logs/login/index.vue @@ -0,0 +1,154 @@ + + + + + diff --git a/src/views/monitor/logs/operation.vue b/src/views/monitor/logs/operation.vue new file mode 100644 index 000000000..58030ff47 --- /dev/null +++ b/src/views/monitor/logs/operation.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/views/monitor/logs/system.vue b/src/views/monitor/logs/system.vue new file mode 100644 index 000000000..7f7a93fd0 --- /dev/null +++ b/src/views/monitor/logs/system.vue @@ -0,0 +1,9 @@ + + + diff --git a/src/views/monitor/online/hook.tsx b/src/views/monitor/online/hook.tsx new file mode 100644 index 000000000..65b5936e1 --- /dev/null +++ b/src/views/monitor/online/hook.tsx @@ -0,0 +1,117 @@ +import dayjs from "dayjs"; +import { message } from "@/utils/message"; +import { getOnlineLogsList } from "@/api/system"; +import { reactive, ref, onMounted, toRaw } from "vue"; +import type { PaginationProps } from "@pureadmin/table"; + +export function useRole() { + const form = reactive({ + username: "" + }); + const dataList = ref([]); + const loading = ref(true); + const pagination = reactive({ + total: 0, + pageSize: 10, + currentPage: 1, + background: true + }); + const columns: TableColumnList = [ + { + label: "序号", + prop: "id", + minWidth: 60 + }, + { + label: "用户名", + prop: "username", + minWidth: 100 + }, + { + label: "登录 IP", + prop: "ip", + minWidth: 140 + }, + { + label: "登录地点", + prop: "address", + minWidth: 140 + }, + { + label: "操作系统", + prop: "system", + minWidth: 100 + }, + { + label: "浏览器类型", + prop: "browser", + minWidth: 100 + }, + { + label: "登录时间", + prop: "loginTime", + minWidth: 180, + formatter: ({ loginTime }) => + dayjs(loginTime).format("YYYY-MM-DD HH:mm:ss") + }, + { + label: "操作", + fixed: "right", + slot: "operation" + } + ]; + + function handleSizeChange(val: number) { + console.log(`${val} items per page`); + } + + function handleCurrentChange(val: number) { + console.log(`current page: ${val}`); + } + + function handleSelectionChange(val) { + console.log("handleSelectionChange", val); + } + + function handleOffline(row) { + message(`${row.username}已被强制下线`, { type: "success" }); + onSearch(); + } + + async function onSearch() { + loading.value = true; + const { data } = await getOnlineLogsList(toRaw(form)); + dataList.value = data.list; + pagination.total = data.total; + pagination.pageSize = data.pageSize; + pagination.currentPage = data.currentPage; + + setTimeout(() => { + loading.value = false; + }, 500); + } + + const resetForm = formEl => { + if (!formEl) return; + formEl.resetFields(); + onSearch(); + }; + + onMounted(() => { + onSearch(); + }); + + return { + form, + loading, + columns, + dataList, + pagination, + onSearch, + resetForm, + handleOffline, + handleSizeChange, + handleCurrentChange, + handleSelectionChange + }; +} diff --git a/src/views/monitor/online/index.vue b/src/views/monitor/online/index.vue new file mode 100644 index 000000000..4b927155c --- /dev/null +++ b/src/views/monitor/online/index.vue @@ -0,0 +1,125 @@ + + + + + diff --git a/src/views/monitor/utils.ts b/src/views/monitor/utils.ts new file mode 100644 index 000000000..1350606cd --- /dev/null +++ b/src/views/monitor/utils.ts @@ -0,0 +1,129 @@ +/** 日期、时间选择器快捷选项,常搭配 [DatePicker](https://element-plus.org/zh-CN/component/date-picker.html) 和 [DateTimePicker](https://element-plus.org/zh-CN/component/datetime-picker.html) 的`shortcuts`属性使用 */ +export const getPickerShortcuts = (): Array<{ + text: string; + value: Date | Function; +}> => { + return [ + { + text: "今天", + value: () => { + const today = new Date(); + today.setHours(0, 0, 0, 0); + const todayEnd = new Date(); + todayEnd.setHours(23, 59, 59, 999); + return [today, todayEnd]; + } + }, + { + text: "昨天", + value: () => { + const yesterday = new Date(); + yesterday.setDate(yesterday.getDate() - 1); + yesterday.setHours(0, 0, 0, 0); + const yesterdayEnd = new Date(); + yesterdayEnd.setDate(yesterdayEnd.getDate() - 1); + yesterdayEnd.setHours(23, 59, 59, 999); + return [yesterday, yesterdayEnd]; + } + }, + { + text: "前天", + value: () => { + const beforeYesterday = new Date(); + beforeYesterday.setDate(beforeYesterday.getDate() - 2); + beforeYesterday.setHours(0, 0, 0, 0); + const beforeYesterdayEnd = new Date(); + beforeYesterdayEnd.setDate(beforeYesterdayEnd.getDate() - 2); + beforeYesterdayEnd.setHours(23, 59, 59, 999); + return [beforeYesterday, beforeYesterdayEnd]; + } + }, + { + text: "本周", + value: () => { + const today = new Date(); + const startOfWeek = new Date( + today.getFullYear(), + today.getMonth(), + today.getDate() - today.getDay() + (today.getDay() === 0 ? -6 : 1) + ); + startOfWeek.setHours(0, 0, 0, 0); + const endOfWeek = new Date( + startOfWeek.getTime() + + 6 * 24 * 60 * 60 * 1000 + + 23 * 60 * 60 * 1000 + + 59 * 60 * 1000 + + 59 * 1000 + + 999 + ); + return [startOfWeek, endOfWeek]; + } + }, + { + text: "上周", + value: () => { + const today = new Date(); + const startOfLastWeek = new Date( + today.getFullYear(), + today.getMonth(), + today.getDate() - today.getDay() - 7 + (today.getDay() === 0 ? -6 : 1) + ); + startOfLastWeek.setHours(0, 0, 0, 0); + const endOfLastWeek = new Date( + startOfLastWeek.getTime() + + 6 * 24 * 60 * 60 * 1000 + + 23 * 60 * 60 * 1000 + + 59 * 60 * 1000 + + 59 * 1000 + + 999 + ); + return [startOfLastWeek, endOfLastWeek]; + } + }, + { + text: "本月", + value: () => { + const today = new Date(); + const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); + startOfMonth.setHours(0, 0, 0, 0); + const endOfMonth = new Date( + today.getFullYear(), + today.getMonth() + 1, + 0 + ); + endOfMonth.setHours(23, 59, 59, 999); + return [startOfMonth, endOfMonth]; + } + }, + { + text: "上个月", + value: () => { + const today = new Date(); + const startOfLastMonth = new Date( + today.getFullYear(), + today.getMonth() - 1, + 1 + ); + startOfLastMonth.setHours(0, 0, 0, 0); + const endOfLastMonth = new Date( + today.getFullYear(), + today.getMonth(), + 0 + ); + endOfLastMonth.setHours(23, 59, 59, 999); + return [startOfLastMonth, endOfLastMonth]; + } + }, + { + text: "本年", + value: () => { + const today = new Date(); + const startOfYear = new Date(today.getFullYear(), 0, 1); + startOfYear.setHours(0, 0, 0, 0); + const endOfYear = new Date(today.getFullYear(), 11, 31); + endOfYear.setHours(23, 59, 59, 999); + return [startOfYear, endOfYear]; + } + } + ]; +}; diff --git a/src/views/system/user/utils/hook.tsx b/src/views/system/user/utils/hook.tsx index ed2e38dae..4bc98c5c3 100644 --- a/src/views/system/user/utils/hook.tsx +++ b/src/views/system/user/utils/hook.tsx @@ -261,6 +261,7 @@ export function useUser(tableRef: Ref, treeRef: Ref) { type: "success" }); tableRef.value.getTableRef().clearSelection(); + onSearch(); } async function onSearch() {