6 changed files with 301 additions and 0 deletions
@ -0,0 +1,21 @@ |
|||
import { ApiServiceEnum, defHttp } from '@/utils/http/axios'; |
|||
|
|||
enum Api { |
|||
listOnlineUser = 'auth/listOnlineUser', |
|||
offline = 'auth/offline', |
|||
} |
|||
|
|||
export const listOnlineUserApi = (params) => |
|||
defHttp.post({ |
|||
service: ApiServiceEnum.SMART_AUTH, |
|||
url: Api.listOnlineUser, |
|||
data: params, |
|||
}); |
|||
|
|||
export const offlineApi = (username, token) => { |
|||
return defHttp.post({ |
|||
service: ApiServiceEnum.SMART_AUTH, |
|||
url: Api.offline, |
|||
data: { username, token }, |
|||
}); |
|||
}; |
|||
@ -0,0 +1,99 @@ |
|||
import { |
|||
type SmartColumn, |
|||
type SmartSearchFormSchema, |
|||
tableBooleanColumnClass, |
|||
} from '@/components/SmartTable'; |
|||
|
|||
export const getTableColumns = (): SmartColumn[] => { |
|||
return [ |
|||
{ |
|||
title: '#', |
|||
type: 'expand', |
|||
fixed: 'left', |
|||
width: 80, |
|||
slots: { |
|||
content: 'table-expand', |
|||
}, |
|||
}, |
|||
{ |
|||
title: '{system.views.user.table.username}', |
|||
field: 'username', |
|||
width: 120, |
|||
fixed: 'left', |
|||
}, |
|||
{ |
|||
title: '{system.views.user.table.fullName}', |
|||
field: 'fullName', |
|||
width: 120, |
|||
fixed: 'left', |
|||
}, |
|||
{ |
|||
title: '{system.views.user.table.email}', |
|||
field: 'email', |
|||
minWidth: 160, |
|||
}, |
|||
{ |
|||
title: '{system.views.user.table.mobile}', |
|||
field: 'mobile', |
|||
minWidth: 140, |
|||
}, |
|||
{ |
|||
title: '{common.table.operation}', |
|||
field: 'operation', |
|||
width: 120, |
|||
fixed: 'right', |
|||
slots: { |
|||
default: 'table-operation', |
|||
}, |
|||
}, |
|||
]; |
|||
}; |
|||
|
|||
export const getTableExpandColumns = (): SmartColumn[] => { |
|||
return [ |
|||
{ |
|||
title: '{system.views.onlineUser.title.authType}', |
|||
field: 'authType', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '{system.views.onlineUser.title.loginType}', |
|||
field: 'loginType', |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '{system.views.onlineUser.title.loginTime}', |
|||
field: 'loginTime', |
|||
sortable: true, |
|||
width: 170, |
|||
}, |
|||
{ |
|||
title: '{system.views.onlineUser.title.loginIp}', |
|||
field: 'loginIp', |
|||
minWidth: 200, |
|||
}, |
|||
{ |
|||
...tableBooleanColumnClass('{system.views.onlineUser.title.bindIp}', 'bindIp'), |
|||
width: 120, |
|||
}, |
|||
{ |
|||
title: '{common.table.operation}', |
|||
field: 'operation', |
|||
width: 120, |
|||
fixed: 'right', |
|||
slots: { |
|||
default: 'expand-table-operation', |
|||
}, |
|||
}, |
|||
]; |
|||
}; |
|||
|
|||
export const getSearchFormSchemas = (t: Function): SmartSearchFormSchema[] => { |
|||
return [ |
|||
{ |
|||
label: t('system.views.user.table.username'), |
|||
field: 'username', |
|||
component: 'Input', |
|||
}, |
|||
]; |
|||
}; |
|||
@ -0,0 +1,113 @@ |
|||
<template> |
|||
<div class="full-height page-container"> |
|||
<SmartTable :size="getTableSize" @register="registerTable"> |
|||
<template #table-expand="{ row }"> |
|||
<SmartTable |
|||
:size="getTableSize" |
|||
class="expand-wrapper" |
|||
:data="row.userLoginDataList" |
|||
@register="registerExpandTable" |
|||
> |
|||
<template #expand-table-operation="data"> |
|||
<SmartVxeTableAction :actions="getTableActions(data.row, true)" /> |
|||
</template> |
|||
</SmartTable> |
|||
</template> |
|||
<template #table-operation="{ row }"> |
|||
<SmartVxeTableAction :actions="getTableActions(row, false)" /> |
|||
</template> |
|||
</SmartTable> |
|||
</div> |
|||
</template> |
|||
|
|||
<script lang="ts" setup> |
|||
import type { ActionItem } from '@/components/SmartTable'; |
|||
|
|||
import { useI18n } from '@/hooks/web/useI18n'; |
|||
import { useSmartTable, SmartTable, SmartVxeTableAction } from '@/components/SmartTable'; |
|||
import { useSizeSetting } from '@/hooks/setting/UseSizeSetting'; |
|||
import { message, Modal } from 'ant-design-vue'; |
|||
import { createVNode } from 'vue'; |
|||
import { ExclamationCircleOutlined } from '@ant-design/icons-vue'; |
|||
|
|||
import { |
|||
getTableColumns, |
|||
getTableExpandColumns, |
|||
getSearchFormSchemas, |
|||
} from './OnlineUserListView.config'; |
|||
import { listOnlineUserApi, offlineApi } from './OnlineUserListView.api'; |
|||
|
|||
const { t } = useI18n(); |
|||
const { getTableSize } = useSizeSetting(); |
|||
|
|||
const getTableActions = (row, isExpand: boolean): ActionItem[] => { |
|||
return [ |
|||
{ |
|||
label: t('system.views.onlineUser.button.offline'), |
|||
auth: 'sys:auth:offline', |
|||
danger: true, |
|||
onClick: () => { |
|||
handleOffline(isExpand ? null : row.username, isExpand ? row.token : null); |
|||
}, |
|||
}, |
|||
]; |
|||
}; |
|||
|
|||
/** |
|||
* 执行登出操作 |
|||
* @param username 用户名 |
|||
* @param token token |
|||
*/ |
|||
const handleOffline = (username?: string, token?: string) => { |
|||
Modal.confirm({ |
|||
title: t('common.notice.confirm'), |
|||
icon: createVNode(ExclamationCircleOutlined), |
|||
content: t('system.views.onlineUser.message.offlineConfirm'), |
|||
onOk: async () => { |
|||
await offlineApi(username, token); |
|||
message.success(t('system.views.onlineUser.message.offlineSuccess')); |
|||
reload(); |
|||
}, |
|||
}); |
|||
}; |
|||
|
|||
const [registerTable, { reload }] = useSmartTable({ |
|||
border: true, |
|||
height: 'auto', |
|||
highlightHoverRow: true, |
|||
columns: getTableColumns(), |
|||
useSearchForm: true, |
|||
searchFormConfig: { |
|||
layout: 'inline', |
|||
schemas: getSearchFormSchemas(t), |
|||
colon: true, |
|||
actionColOptions: { |
|||
span: undefined, |
|||
}, |
|||
compact: true, |
|||
}, |
|||
proxyConfig: { |
|||
ajax: { |
|||
query: ({ ajaxParameter }) => listOnlineUserApi(ajaxParameter), |
|||
}, |
|||
}, |
|||
columnConfig: { |
|||
resizable: true, |
|||
}, |
|||
toolbarConfig: { |
|||
refresh: true, |
|||
}, |
|||
}); |
|||
|
|||
const [registerExpandTable] = useSmartTable({ |
|||
columns: getTableExpandColumns(), |
|||
border: true, |
|||
stripe: true, |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped lang="less"> |
|||
.expand-wrapper { |
|||
padding: 15px; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,22 @@ |
|||
export default { |
|||
system: { |
|||
views: { |
|||
onlineUser: { |
|||
title: { |
|||
authType: 'Auth type', |
|||
loginType: 'Login type', |
|||
loginTime: 'Login time', |
|||
loginIp: 'Login IP', |
|||
bindIp: 'Bind IP', |
|||
}, |
|||
button: { |
|||
offline: 'Offline', |
|||
}, |
|||
message: { |
|||
offlineConfirm: 'Are you sure you want to go offline?', |
|||
offlineSuccess: 'Offline success', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
|||
@ -0,0 +1,22 @@ |
|||
export default { |
|||
system: { |
|||
views: { |
|||
onlineUser: { |
|||
title: { |
|||
authType: '认证类型', |
|||
loginType: '登录类型', |
|||
loginTime: '登录时间', |
|||
loginIp: '登录IP', |
|||
bindIp: '是否绑定IP', |
|||
}, |
|||
button: { |
|||
offline: '下线', |
|||
}, |
|||
message: { |
|||
offlineConfirm: '确定要下线吗?', |
|||
offlineSuccess: '下线成功', |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
}; |
|||
Loading…
Reference in new issue