feat: 登录日志添加按钮级权限管理
This commit is contained in:
@@ -42,6 +42,7 @@ buttons:More: More
|
||||
buttons:Deselect: Deselect
|
||||
buttons:DeleteInBatches: Delete In Batches
|
||||
buttons:ExportInBatches: Export In Batches
|
||||
buttons:ExitInBatches: Exit In Batches
|
||||
buttons:ExportAll: Export All
|
||||
buttons:UploadAvatar: Upload Avatar
|
||||
buttons:ResetPassword: Reset Password
|
||||
|
||||
@@ -48,6 +48,7 @@ buttons:ResetPassword: 重置密码
|
||||
buttons:RoleAllocation: 角色分配
|
||||
buttons:PermissionDetails: 权限详情
|
||||
buttons:ForceToExit: 强制退出
|
||||
buttons:ExitInBatches: 批量强退
|
||||
search:Total: 共
|
||||
search:History: 搜索历史
|
||||
search:Collect: 收藏
|
||||
|
||||
@@ -14,10 +14,27 @@ import { filterEmptyObject } from "./utils";
|
||||
* 用户获取登录日志
|
||||
*/
|
||||
|
||||
export const getUserLoginLogAPI = (params: {
|
||||
/**获取用户登录日志参数 */
|
||||
type GetUserLoginLogParams = {
|
||||
/**页码 */
|
||||
page: number;
|
||||
/**每页数量 */
|
||||
pageSize: number;
|
||||
}) => {
|
||||
/**用户账号 */
|
||||
username?: string;
|
||||
/**用户昵称 */
|
||||
nickname?: string;
|
||||
/**部门ID */
|
||||
department_id?: string;
|
||||
/**登录状态 */
|
||||
status?: number | string;
|
||||
/**开始时间 */
|
||||
startTime?: string | number;
|
||||
/**结束时间 */
|
||||
endTime?: string | number;
|
||||
};
|
||||
|
||||
export const getUserLoginLogAPI = (params: GetUserLoginLogParams) => {
|
||||
return http.request<QueryListResult<UserLoginLogInfo>>(
|
||||
"get",
|
||||
"/api/log/login",
|
||||
@@ -36,6 +53,20 @@ export const deleteUserOnlineAPI = (id: string) => {
|
||||
return http.request<null>("delete", `/api/log/logout/${id}`);
|
||||
};
|
||||
|
||||
/**用户批量强退*/
|
||||
export const deleteUserOnlineListAPI = (data: { ids: string[] }) => {
|
||||
return http.request<null>("delete", `/api/log/logoutList`, { data });
|
||||
};
|
||||
|
||||
/**删除登录日志 */
|
||||
export const deleteUserLoginLogAPI = (id: string) => {
|
||||
return http.request<null>("delete", `/api/log/delete/login/${id}`);
|
||||
};
|
||||
/**批量删除登录日志 */
|
||||
export const deleteUserLoginLogListAPI = (data: { ids: string[] }) => {
|
||||
return http.request<null>("delete", `/api/log/deleteList/login`, { data });
|
||||
};
|
||||
|
||||
// ------------------------操作日志相关----------------------------------------
|
||||
|
||||
/**
|
||||
|
||||
@@ -5,9 +5,10 @@ import { getPickerShortcuts } from "../utils";
|
||||
import { PureTableBar } from "@/components/RePureTableBar";
|
||||
import { useRenderIcon } from "@/components/ReIcon/src/hooks";
|
||||
import { useI18n } from "vue-i18n";
|
||||
import Delete from "@iconify-icons/ep/delete";
|
||||
import Plane from "@iconify-icons/ri/plane-line";
|
||||
import Refresh from "@iconify-icons/ep/refresh";
|
||||
|
||||
import { hasAuth } from "@/utils/auth";
|
||||
defineOptions({
|
||||
name: "MonitorLogin"
|
||||
});
|
||||
@@ -23,13 +24,16 @@ const {
|
||||
dataList,
|
||||
pagination,
|
||||
selectedNum,
|
||||
departments,
|
||||
onSearch,
|
||||
deleteUserHandle,
|
||||
resetForm,
|
||||
onbatchDel,
|
||||
onbatchForce,
|
||||
resetForm,
|
||||
deleteUserHandle,
|
||||
onSelectionCancel,
|
||||
handleCurrentChange,
|
||||
handleSizeChange,
|
||||
handleDelete,
|
||||
handleSelectionChange
|
||||
} = useLogin(tableRef);
|
||||
</script>
|
||||
@@ -42,12 +46,20 @@ const {
|
||||
:model="form"
|
||||
class="search-form bg-bg_color w-[99/100] pl-8 pt-[12px] overflow-auto"
|
||||
>
|
||||
<el-form-item label="用户名" prop="username">
|
||||
<el-form-item label="用户账号" prop="username">
|
||||
<el-input
|
||||
v-model="form.username"
|
||||
placeholder="请输入用户名"
|
||||
placeholder="请输入用户账号~"
|
||||
clearable
|
||||
class="!w-[150px]"
|
||||
class="!w-[200px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="用户名称" prop="nickname">
|
||||
<el-input
|
||||
v-model="form.nickname"
|
||||
placeholder="请输入用户名称~"
|
||||
clearable
|
||||
class="!w-[200px]"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="登录状态" prop="status">
|
||||
@@ -55,12 +67,33 @@ const {
|
||||
v-model="form.status"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
class="!w-[150px]"
|
||||
class="!w-[200px]"
|
||||
>
|
||||
<el-option label="成功" value="1" />
|
||||
<el-option label="失败" value="0" />
|
||||
<el-option label="成功" :value="1" />
|
||||
<el-option label="失败" :value="0" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="所属部门:" prop="department_id">
|
||||
<el-cascader
|
||||
v-model="form.department_id"
|
||||
class="!w-[200px]"
|
||||
:options="departments"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'name',
|
||||
emitPath: false,
|
||||
checkStrictly: true
|
||||
}"
|
||||
clearable
|
||||
filterable
|
||||
placeholder="请选择所属部门~"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span>{{ data.name }}</span>
|
||||
<span v-if="!node.isLeaf"> ({{ data.children.length }}) </span>
|
||||
</template>
|
||||
</el-cascader>
|
||||
</el-form-item>
|
||||
<el-form-item label="登录时间" prop="loginTime">
|
||||
<el-date-picker
|
||||
v-model="form.loginTime"
|
||||
@@ -69,6 +102,8 @@ const {
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期时间"
|
||||
end-placeholder="结束日期时间"
|
||||
value-format="x"
|
||||
unlink-panels
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
@@ -86,16 +121,7 @@ const {
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<PureTableBar title="登录日志" :columns="columns" @refresh="onSearch">
|
||||
<!-- <template #buttons>
|
||||
<el-popconfirm title="确定要删除所有日志数据吗?" @confirm="clearAll">
|
||||
<template #reference>
|
||||
<el-button type="danger" :icon="useRenderIcon(Delete)">
|
||||
清空日志
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template> -->
|
||||
<PureTableBar title="登录日志管理" :columns="columns" @refresh="onSearch">
|
||||
<template v-slot="{ size, dynamicColumns }">
|
||||
<div
|
||||
v-if="selectedNum > 0"
|
||||
@@ -113,9 +139,34 @@ const {
|
||||
{{ t("buttons:Deselect") }}
|
||||
</el-button>
|
||||
</div>
|
||||
<el-popconfirm title="是否确认删除?" @confirm="onbatchDel">
|
||||
<el-popconfirm
|
||||
v-if="hasAuth('login:btn:logout')"
|
||||
title="是否确认强制退出?"
|
||||
@confirm="onbatchForce"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="danger" text class="mr-1">
|
||||
<el-button
|
||||
type="warning"
|
||||
text
|
||||
class="mr-1"
|
||||
:disabled="selectedNum < 0 || !hasAuth('login:btn:logout')"
|
||||
>
|
||||
{{ t("buttons:ExitInBatches") }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-popconfirm
|
||||
v-if="hasAuth('login:btn:delete')"
|
||||
title="是否确认删除?"
|
||||
@confirm="onbatchDel"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
type="danger"
|
||||
text
|
||||
class="mr-1"
|
||||
:disabled="selectedNum < 0 || !hasAuth('login:btn:delete')"
|
||||
>
|
||||
{{ t("buttons:DeleteInBatches") }}
|
||||
</el-button>
|
||||
</template>
|
||||
@@ -142,6 +193,23 @@ const {
|
||||
@page-current-change="handleCurrentChange"
|
||||
>
|
||||
<template #operation="{ row }">
|
||||
<el-popconfirm
|
||||
:title="`是否删除这条登录记录?`"
|
||||
@confirm="handleDelete(row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button
|
||||
class="reset-margin"
|
||||
link
|
||||
type="danger"
|
||||
:disabled="!hasAuth('login:btn:delete')"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Delete)"
|
||||
>
|
||||
{{ t("buttons:Delete") }}
|
||||
</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
<el-popconfirm
|
||||
:title="`是否强制下线${row.username}`"
|
||||
@confirm="deleteUserHandle(row.session_id)"
|
||||
@@ -151,7 +219,7 @@ const {
|
||||
class="reset-margin"
|
||||
link
|
||||
type="primary"
|
||||
:disabled="!row.online"
|
||||
:disabled="!row.online || !hasAuth('login:btn:logout')"
|
||||
:size="size"
|
||||
:icon="useRenderIcon(Plane)"
|
||||
>
|
||||
|
||||
@@ -1,17 +1,27 @@
|
||||
import dayjs from "dayjs";
|
||||
import { message } from "@/utils/message";
|
||||
import { getKeyList } from "@pureadmin/utils";
|
||||
import { deleteUserOnlineAPI, getUserLoginLogAPI } from "@/api/monitor";
|
||||
import { getKeyList, handleTree } from "@pureadmin/utils";
|
||||
import {
|
||||
deleteUserOnlineAPI,
|
||||
getUserLoginLogAPI,
|
||||
deleteUserLoginLogAPI,
|
||||
deleteUserOnlineListAPI,
|
||||
deleteUserLoginLogListAPI
|
||||
} from "@/api/monitor";
|
||||
import { usePublicHooks } from "@/views/system/hooks";
|
||||
import type { PaginationProps } from "@pureadmin/table";
|
||||
import { type Ref, reactive, ref, onMounted } from "vue";
|
||||
import type { UserLoginLogInfo } from "types/monitor";
|
||||
import type { DepartmentInfo } from "types/system";
|
||||
import { getDepartmentListAPI } from "@/api/system";
|
||||
|
||||
export const useLogin = (tableRef: Ref) => {
|
||||
const form = reactive({
|
||||
username: "",
|
||||
nickname: "",
|
||||
status: "",
|
||||
loginTime: ""
|
||||
department_id: "",
|
||||
loginTime: []
|
||||
});
|
||||
/**
|
||||
* 表格数据
|
||||
@@ -93,6 +103,16 @@ export const useLogin = (tableRef: Ref) => {
|
||||
</el-tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: "在线状态",
|
||||
prop: "online",
|
||||
minWidth: 100,
|
||||
cellRenderer: ({ row, props }) => (
|
||||
<el-tag size={props.size} type={row.online ? "success" : "primary"}>
|
||||
{row.online ? "在线" : "离线"}
|
||||
</el-tag>
|
||||
)
|
||||
},
|
||||
{
|
||||
label: "登录时间",
|
||||
prop: "login_time",
|
||||
@@ -103,7 +123,8 @@ export const useLogin = (tableRef: Ref) => {
|
||||
{
|
||||
label: "操作",
|
||||
fixed: "right",
|
||||
slot: "operation"
|
||||
slot: "operation",
|
||||
width: 200
|
||||
}
|
||||
];
|
||||
|
||||
@@ -114,12 +135,19 @@ export const useLogin = (tableRef: Ref) => {
|
||||
const handleCurrentChange = async (val: number) => {
|
||||
const res = await getUserLoginLogAPI({
|
||||
page: val,
|
||||
pageSize: pagination.pageSize
|
||||
pageSize: pagination.pageSize,
|
||||
department_id: form.department_id,
|
||||
status: form.status,
|
||||
username: form.username,
|
||||
nickname: form.nickname,
|
||||
startTime: form.loginTime[0] ? form.loginTime[0] : null,
|
||||
endTime: form.loginTime[1] ? form.loginTime[1] : null
|
||||
});
|
||||
if (res.success) {
|
||||
dataList.value = res.data.result;
|
||||
pagination.total = res.data.total;
|
||||
pagination.currentPage = res.data.page;
|
||||
pagination.pageSize = res.data.pageSize;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -127,12 +155,19 @@ export const useLogin = (tableRef: Ref) => {
|
||||
const handleSizeChange = async (val: number) => {
|
||||
const res = await getUserLoginLogAPI({
|
||||
page: pagination.currentPage,
|
||||
pageSize: val
|
||||
pageSize: val,
|
||||
department_id: form.department_id,
|
||||
status: form.status,
|
||||
username: form.username,
|
||||
nickname: form.nickname,
|
||||
startTime: form.loginTime[0] ? form.loginTime[0] : null,
|
||||
endTime: form.loginTime[1] ? form.loginTime[1] : null
|
||||
});
|
||||
if (res.success) {
|
||||
dataList.value = res.data.result;
|
||||
pagination.total = res.data.total;
|
||||
pagination.currentPage = res.data.page;
|
||||
pagination.pageSize = res.data.pageSize;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -149,38 +184,77 @@ export const useLogin = (tableRef: Ref) => {
|
||||
// 用于多选表格,清空用户的选择
|
||||
tableRef.value.getTableRef().clearSelection();
|
||||
};
|
||||
|
||||
/**处理删除 */
|
||||
/**
|
||||
* 处理删除
|
||||
* @param row
|
||||
*/
|
||||
const handleDelete = async (row: UserLoginLogInfo) => {
|
||||
const res = await deleteUserLoginLogAPI(row.id);
|
||||
if (res.success) {
|
||||
onSearch();
|
||||
}
|
||||
message(res.msg, {
|
||||
type: res.success ? "success" : "error"
|
||||
});
|
||||
};
|
||||
/** 批量删除 */
|
||||
const onbatchDel = () => {
|
||||
const onbatchDel = async () => {
|
||||
// 返回当前选中的行
|
||||
const curSelected = tableRef.value.getTableRef().getSelectionRows();
|
||||
// 接下来根据实际业务,通过选中行的某项数据,比如下面的id,调用接口进行批量删除
|
||||
message(`已删除序号为 ${getKeyList(curSelected, "id")} 的数据`, {
|
||||
const res = await deleteUserLoginLogListAPI({
|
||||
ids: getKeyList(curSelected, "id")
|
||||
});
|
||||
if (res.success) {
|
||||
message(res.msg, {
|
||||
type: "success"
|
||||
});
|
||||
tableRef.value.getTableRef().clearSelection();
|
||||
onSearch();
|
||||
} else {
|
||||
message(res.msg, {
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/** 清空日志 */
|
||||
const clearAll = async () => {
|
||||
// 根据实际业务,调用接口删除所有日志数据
|
||||
message("已删除所有日志数据", {
|
||||
/**批量强退 */
|
||||
const onbatchForce = async () => {
|
||||
// 返回当前选中的行
|
||||
const curSelected = tableRef.value.getTableRef().getSelectionRows();
|
||||
const res = await deleteUserOnlineListAPI({
|
||||
ids: getKeyList(curSelected, "id")
|
||||
});
|
||||
if (res.success) {
|
||||
message(res.msg, {
|
||||
type: "success"
|
||||
});
|
||||
await onSearch();
|
||||
tableRef.value.getTableRef().clearSelection();
|
||||
onSearch();
|
||||
} else {
|
||||
message(res.msg, {
|
||||
type: "error"
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const onSearch = async () => {
|
||||
loading.value = true;
|
||||
const res = await getUserLoginLogAPI({
|
||||
page: pagination.currentPage,
|
||||
pageSize: pagination.pageSize
|
||||
pageSize: pagination.pageSize,
|
||||
department_id: form.department_id,
|
||||
status: form.status,
|
||||
username: form.username,
|
||||
nickname: form.nickname,
|
||||
startTime: form.loginTime[0] ? form.loginTime[0] : null,
|
||||
endTime: form.loginTime[1] ? form.loginTime[1] : null
|
||||
});
|
||||
if (res.success) {
|
||||
dataList.value = res.data.result;
|
||||
pagination.total = res.data.total;
|
||||
pagination.currentPage = res.data.page;
|
||||
pagination.pageSize = res.data.pageSize;
|
||||
}
|
||||
setTimeout(() => {
|
||||
loading.value = false;
|
||||
@@ -201,9 +275,34 @@ export const useLogin = (tableRef: Ref) => {
|
||||
onSearch();
|
||||
}
|
||||
};
|
||||
/**部门列表 */
|
||||
const departments = ref<DepartmentInfo[]>([]);
|
||||
/**获取部门列表 */
|
||||
const getDepartments = async () => {
|
||||
const res = await getDepartmentListAPI({ page: 1, pageSize: 9999 });
|
||||
if (res.success) {
|
||||
departments.value = formatHigherOptions(
|
||||
handleTree(res.data.result, "id", "parent_id")
|
||||
);
|
||||
} else {
|
||||
departments.value = [];
|
||||
}
|
||||
};
|
||||
const formatHigherOptions = (treeList: any) => {
|
||||
// 根据返回数据的status字段值判断追加是否禁用disabled字段,返回处理后的树结构,用于上级部门级联选择器的展示(实际开发中也是如此,不可能前端需要的每个字段后端都会返回,这时需要前端自行根据后端返回的某些字段做逻辑处理)
|
||||
if (!treeList || !treeList.length) return;
|
||||
const newTreeList = [];
|
||||
for (let i = 0; i < treeList.length; i++) {
|
||||
treeList[i].disabled = treeList[i].status === 0 ? true : false;
|
||||
formatHigherOptions(treeList[i].children);
|
||||
newTreeList.push(treeList[i]);
|
||||
}
|
||||
return newTreeList;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
onSearch();
|
||||
onMounted(async () => {
|
||||
await onSearch();
|
||||
await getDepartments();
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -213,14 +312,16 @@ export const useLogin = (tableRef: Ref) => {
|
||||
dataList,
|
||||
pagination,
|
||||
selectedNum,
|
||||
departments,
|
||||
onSearch,
|
||||
clearAll,
|
||||
resetForm,
|
||||
onbatchDel,
|
||||
onbatchForce,
|
||||
resetForm,
|
||||
deleteUserHandle,
|
||||
onSelectionCancel,
|
||||
handleCurrentChange,
|
||||
handleSizeChange,
|
||||
handleDelete,
|
||||
handleSelectionChange
|
||||
};
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user