🔨 用户锁定列表.

This commit is contained in:
lijiahangmax
2025-11-07 10:01:16 +08:00
parent 6deebedc75
commit c3da882950
3 changed files with 249 additions and 2 deletions

View File

@@ -60,10 +60,31 @@ export interface UserQueryResponse extends TableData {
roles: Array<RoleQueryResponse>;
}
/**
* 锁定用户查询响应
*/
export interface LockedUserQueryResponse {
username: string;
address: string;
location: string;
userAgent: string;
expireTime: number;
loginTime: number;
}
/**
* 解锁用户请求
*/
export interface UserUnlockRequest {
username: string;
}
/**
* 用户会话查询响应
*/
export interface UserSessionQueryResponse {
id: number;
username: string;
visible: boolean;
current: boolean;
address: string;
@@ -183,10 +204,31 @@ export function batchDeleteUser(idList: Array<number>) {
}
/**
* 获取用户会话列表
* 获取锁定的用户列表
*/
export function getLockedUserList() {
return axios.get<Array<LockedUserQueryResponse>>('/infra/system-user/locked/list');
}
/**
* 解锁锁定的用户
*/
export function unlockLockedUser(request: UserUnlockRequest) {
return axios.put('/infra/system-user/locked/unlock', request);
}
/**
* 获取全部用户会话列表
*/
export function getUsersSessionList() {
return axios.get<Array<UserSessionQueryResponse>>('/infra/system-user/session/users/list');
}
/**
* 获取单个用户会话列表
*/
export function getUserSessionList(id: number) {
return axios.get<Array<UserSessionQueryResponse>>('/infra/system-user/session/list', { params: { id } });
return axios.get<Array<UserSessionQueryResponse>>('/infra/system-user/session/user/list', { params: { id } });
}
/**

View File

@@ -0,0 +1,137 @@
<template>
<div class="layout-container">
<!-- 表格 -->
<a-card class="general-card table-card">
<template #title>
<!-- 左侧操作 -->
<div class="table-left-bar-handle">
<!-- 标题 -->
<div class="table-title">
锁定用户列表
</div>
</div>
<!-- 右侧操作 -->
<div class="table-right-bar-handle">
<a-button type="primary" @click="fetchTableData">
查询
<template #icon>
<icon-search />
</template>
</a-button>
</div>
</template>
<!-- table -->
<a-table row-key="id"
ref="tableRef"
class="table-resize"
:loading="loading"
:columns="columns"
:data="tableRenderData"
:pagination="false"
:bordered="false"
:column-resizable="true">
<!-- 用户名 -->
<template #username="{ record }">
<div style="display: contents;">
<!-- 用户名 -->
<span class="text-copy" @click="copy(record.username, true)">
{{ record.username }}
</span>
<!-- 当前会话 -->
<a-tag v-if="record.current"
size="small"
class="ml8"
color="green">
当前
</a-tag>
</div>
</template>
<!-- 登录IP -->
<template #address="{ record }">
<div style="display: contents;">
<span class="text-copy mr4" @click="copy(record.address, true)">
{{ record.address }}
</span>
<span>({{ record.location }})</span>
</div>
</template>
<!-- 操作 -->
<template #handle="{ record }">
<div class="table-handle-wrapper">
<!-- 删除 -->
<a-popconfirm content="确定要解锁当前用户吗?"
position="left"
type="warning"
@ok="unlockUser(record)">
<a-button v-if="!record.current"
v-permission="['infra:system-user:management:unlock']"
type="text"
size="mini"
status="danger">
解锁
</a-button>
</a-popconfirm>
</div>
</template>
</a-table>
</a-card>
</div>
</template>
<script lang="ts">
export default {
name: 'userLockedUser'
};
</script>
<script lang="ts" setup>
import type { LockedUserQueryResponse } from '@/api/user/user';
import { ref, onMounted } from 'vue';
import { Message } from '@arco-design/web-vue';
import useLoading from '@/hooks/loading';
import columns from './types/table.columns';
import { copy } from '@/hooks/copy';
import { getLockedUserList, unlockLockedUser } from '@/api/user/user';
const { loading, setLoading } = useLoading();
const tableRenderData = ref<Array<LockedUserQueryResponse>>([]);
// 解锁用户
const unlockUser = async (record: LockedUserQueryResponse) => {
try {
setLoading(true);
// 解锁用户
await unlockLockedUser({
username: record.username,
});
Message.success('操作成功');
// 查询数据
await fetchTableData();
} catch (e) {
} finally {
setLoading(false);
}
};
// 查询数据
const fetchTableData = async () => {
try {
setLoading(true);
const { data } = await getLockedUserList();
tableRenderData.value = data;
} catch (e) {
} finally {
setLoading(false);
}
};
onMounted(async () => {
await fetchTableData();
});
</script>
<style lang="less" scoped>
</style>

View File

@@ -0,0 +1,68 @@
import type { TableColumnData } from '@arco-design/web-vue';
import { dateFormat } from '@/utils';
const columns = [
{
title: '序号',
dataIndex: 'seq',
slotName: 'seq',
width: 100,
align: 'left',
fixed: 'left',
default: true,
render: ({ rowIndex }) => {
return rowIndex + 1;
},
}, {
title: '用户名',
dataIndex: 'username',
slotName: 'username',
width: 188,
ellipsis: true,
tooltip: true,
default: true,
}, {
title: '登录IP',
dataIndex: 'address',
slotName: 'address',
minWidth: 228,
default: true,
}, {
title: 'userAgent',
dataIndex: 'userAgent',
slotName: 'userAgent',
minWidth: 328,
ellipsis: true,
tooltip: true,
default: true,
}, {
title: '登录时间',
dataIndex: 'loginTime',
slotName: 'loginTime',
align: 'center',
width: 180,
render: ({ record }) => {
return dateFormat(new Date(record.loginTime));
},
default: true,
}, {
title: '解锁时间',
dataIndex: 'expireTime',
slotName: 'expireTime',
align: 'center',
width: 180,
render: ({ record }) => {
return dateFormat(new Date(record.expireTime));
},
default: true,
}, {
title: '操作',
slotName: 'handle',
width: 108,
align: 'center',
fixed: 'right',
default: true,
},
] as TableColumnData[];
export default columns;