diff --git a/docker/Dockerfile b/docker/Dockerfile index 349fcba..934eaea 100644 --- a/docker/Dockerfile +++ b/docker/Dockerfile @@ -1,5 +1,5 @@ # 使用官方的 Nginx 镜像作为基础镜像 -FROM nginx +FROM nginx:1.27.3 # 删除默认的 Nginx 配置文件 RUN rm /etc/nginx/conf.d/default.conf diff --git a/src/api/v1/log/requestLog.ts b/src/api/v1/log/requestLog.ts new file mode 100644 index 0000000..6fae23d --- /dev/null +++ b/src/api/v1/log/requestLog.ts @@ -0,0 +1,27 @@ +import { http } from '@/api/service/request'; +import type { BaseResult, ResultTable } from '@/api/service/types'; + +/** 用户请求日志---获取用户请求日志列表 */ +export const fetchRequestLogList = (data: any) => { + data = { + url: data.url, + userId: data.userId, + username: data.username, + token: data.token, + ipAddress: data.ipAddress, + ipRegion: data.ipRegion, + userAgent: data.userAgent, + arg: data.arg, + result: data.result, + executeTime: data.executeTime, + xRequestedWith: data.xRequestedWith, + currentPage: data.currentPage, + pageSize: data.pageSize, + }; + return http.request>('get', `userRequestLog/getRequestLogList/${data.currentPage}/${data.pageSize}`, { params: data }); +}; + +/** 用户请求日志---删除用户请求日志 */ +export const fetchDeletedRequestLogByIds = (data: any) => { + return http.request>('delete', 'userRequestLog/deletedRequestLogByIds', { data }); +}; diff --git a/src/store/monitor/requestLog.ts b/src/store/monitor/requestLog.ts new file mode 100644 index 0000000..3f935dc --- /dev/null +++ b/src/store/monitor/requestLog.ts @@ -0,0 +1,62 @@ +import { defineStore } from 'pinia'; +import { pageSizes } from '@/enums/baseConstant'; +import { storeMessage } from '@/utils/message'; +import { storePagination } from '@/store/useStorePagination'; +import { fetchDeletedRequestLogByIds, fetchRequestLogList } from '@/api/v1/log/requestLog'; + +/** + * 用户登录日志 Store + */ +export const useRequestLogStore = defineStore('requestLogStore', { + state() { + return { + // 用户登录日志列表 + datalist: [], + // 查询表单 + form: { + // 用户名 + username: undefined, + // 登录Ip + ipAddress: undefined, + // 登录Ip归属地 + ipRegion: undefined, + // 登录时代理 + userAgent: undefined, + // 操作类型 + type: undefined, + // 标识客户端是否是通过Ajax发送请求的 + xRequestedWith: undefined, + }, + // 分页查询结果 + pagination: { + currentPage: 1, + pageSize: 30, + total: 1, + pageSizes, + }, + // 加载 + loading: false, + }; + }, + getters: {}, + actions: { + /** 获取用户请求日志列表 */ + async getRequestLogList() { + // 整理请求参数 + const data = { ...this.pagination, ...this.form }; + + // 获取用户登录日志列表 + const result = await fetchRequestLogList(data); + + // 公共页面函数hook + const pagination = storePagination.bind(this); + return pagination(result); + }, + + /** 删除用户请求日志 */ + async deletedRequestLogByIds(data: any) { + const result = await fetchDeletedRequestLogByIds(data); + return storeMessage(result); + }, + }, +}); diff --git a/src/views/monitor/requestLog/index.vue b/src/views/monitor/requestLog/index.vue new file mode 100644 index 0000000..8c488c8 --- /dev/null +++ b/src/views/monitor/requestLog/index.vue @@ -0,0 +1,143 @@ + + + diff --git a/src/views/monitor/requestLog/user-login-log-dialog.vue b/src/views/monitor/requestLog/user-login-log-dialog.vue new file mode 100644 index 0000000..457b536 --- /dev/null +++ b/src/views/monitor/requestLog/user-login-log-dialog.vue @@ -0,0 +1,98 @@ + + + diff --git a/src/views/monitor/requestLog/utils/auth.ts b/src/views/monitor/requestLog/utils/auth.ts new file mode 100644 index 0000000..4738ad5 --- /dev/null +++ b/src/views/monitor/requestLog/utils/auth.ts @@ -0,0 +1,6 @@ +export const auth = { + // 分页查询 + search: ['userRequestLog::getRequestLogList'], + // 删除操作 + deleted: ['userRequestLog::deletedRequestLogByIds'], +}; diff --git a/src/views/monitor/requestLog/utils/columns.ts b/src/views/monitor/requestLog/utils/columns.ts new file mode 100644 index 0000000..6f394e4 --- /dev/null +++ b/src/views/monitor/requestLog/utils/columns.ts @@ -0,0 +1,27 @@ +import { $t } from '@/plugins/i18n'; + +// 表格列 +export const columns: TableColumnList = [ + { type: 'selection', align: 'left' }, + { type: 'index', index: (index: number) => index + 1, label: '序号', minWidth: 60 }, + // 请求URL + { label: $t('url'), prop: 'url', minWidth: 300 }, + // 用户名 + { label: $t('userLoginLog_username'), prop: 'username', minWidth: 180 }, + // 登录Ip + { label: $t('userLoginLog_ipAddress'), prop: 'ipAddress', minWidth: 130 }, + // 登录Ip归属地 + { label: $t('userLoginLog_ipRegion'), prop: 'ipRegion', minWidth: 160 }, + // 参数 + { label: $t('arg'), prop: 'arg', minWidth: 130 }, + // 结果 + { label: $t('result'), prop: 'result', minWidth: 130 }, + { label: $t('executeTime'), prop: 'executeTime', sortable: true, width: 160 }, + // 登录时代理 + { label: $t('userLoginLog_userAgent'), prop: 'userAgent', minWidth: 200 }, + // 标识客户端是否是通过Ajax发送请求的 + { label: $t('userLoginLog_xRequestedWith'), prop: 'xRequestedWith', minWidth: 150 }, + // 登录token + { label: $t('userLoginLog_token'), prop: 'token', width: 200 }, + { label: $t('table.operation'), fixed: 'right', width: 90, slot: 'operation' }, +]; diff --git a/src/views/monitor/requestLog/utils/hooks.ts b/src/views/monitor/requestLog/utils/hooks.ts new file mode 100644 index 0000000..b25e1d6 --- /dev/null +++ b/src/views/monitor/requestLog/utils/hooks.ts @@ -0,0 +1,83 @@ +import { addDialog } from '@/components/BaseDialog/index'; +import UserLoginLogDialog from '@/views/monitor/requestLog/user-login-log-dialog.vue'; +import { useRequestLogStore } from '@/store/monitor/requestLog'; +import { h, ref } from 'vue'; +import { message } from '@/utils/message'; +import { $t } from '@/plugins/i18n'; +import DeleteBatchDialog from '@/components/Table/DeleteBatchDialog.vue'; + +export const formRef = ref(); +// 删除ids +export const deleteIds = ref([]); +const requestLogStore = useRequestLogStore(); + +/** 搜索初始化用户登录日志 */ +export async function onSearch() { + requestLogStore.loading = true; + await requestLogStore.getRequestLogList(); + requestLogStore.loading = false; +} + +/** + * * 查看用户登录日志 + * @param row + */ +export function onView(row: any) { + addDialog({ + title: `${$t('view')}${$t('userLoginLog')}`, + width: '30%', + props: { + formInline: { + url: row.url, + userId: row.userId, + username: row.username, + token: row.token, + ipAddress: row.ipAddress, + ipRegion: row.ipRegion, + userAgent: row.userAgent, + arg: row.arg, + result: row.result, + executeTime: row.executeTime, + xRequestedWith: row.xRequestedWith, + }, + }, + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => h(UserLoginLogDialog, { ref: formRef }), + beforeSure: async done => { + done(); + await onSearch(); + }, + }); +} + +/** 批量删除 */ +export const onDeleteBatch = async () => { + const ids = deleteIds.value; + const formDeletedBatchRef = ref(); + + addDialog({ + title: $t('deleteBatchTip'), + width: '30%', + props: { formInline: { confirmText: '' } }, + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => h(DeleteBatchDialog, { ref: formDeletedBatchRef }), + beforeSure: (done, { options }) => { + formDeletedBatchRef.value.formDeletedBatchRef.validate(async (valid: any) => { + if (!valid) return; + + const text = options.props.formInline.confirmText.toLowerCase(); + if (text === 'yes' || text === 'y') { + // 删除数据 + await requestLogStore.deletedRequestLogByIds(ids); + await onSearch(); + + done(); + } else message($t('deleteBatchTip'), { type: 'warning' }); + }); + }, + }); +}; diff --git a/src/views/monitor/requestLog/utils/types.ts b/src/views/monitor/requestLog/utils/types.ts new file mode 100644 index 0000000..921caa0 --- /dev/null +++ b/src/views/monitor/requestLog/utils/types.ts @@ -0,0 +1,31 @@ +// 添加或者修改表单元素 + +export interface FormItemProps { + // 用户Id + userId: number; + // 用户名 + username: string; + // 登录token + token: string; + // 登录Ip + ipAddress: string; + // 登录Ip归属地 + ipRegion: string; + // 登录时代理 + userAgent: string; + // 请求URL + url: string; + // 参数 + arg: string; + // 结果 + result: string; + // 执行时间 + executeTime: string; + // 标识客户端是否是通过Ajax发送请求的 + xRequestedWith: string; +} + +// 添加或修改表单Props +export interface FormProps { + formInline: FormItemProps; +} diff --git a/src/views/monitor/userLoginLog/utils/hooks.ts b/src/views/monitor/userLoginLog/utils/hooks.ts index 2f937fa..20aade0 100644 --- a/src/views/monitor/userLoginLog/utils/hooks.ts +++ b/src/views/monitor/userLoginLog/utils/hooks.ts @@ -13,9 +13,9 @@ const userLoginLogStore = useUserLoginLogStore(); /** 搜索初始化用户登录日志 */ export async function onSearch() { - userLoginLogStore.loading = true; - await userLoginLogStore.getUserLoginLogList(); - userLoginLogStore.loading = false; + userLoginLogStore.loading = true; + await userLoginLogStore.getUserLoginLogList(); + userLoginLogStore.loading = false; } /** @@ -23,76 +23,76 @@ export async function onSearch() { * @param row */ export function onView(row: any) { - addDialog({ - title: `${$t('view')}${$t('userLoginLog')}`, - width: '30%', - props: { - formInline: { - userId: row.userId, - username: row.username, - token: row.token, - ipAddress: row.ipAddress, - ipRegion: row.ipRegion, - userAgent: row.userAgent, - type: row.type, - xRequestedWith: row.xRequestedWith, - }, - }, - draggable: true, - fullscreenIcon: true, - closeOnClickModal: false, - contentRenderer: () => h(UserLoginLogDialog, { ref: formRef }), - beforeSure: async done => { - done(); - await onSearch(); - }, - }); + addDialog({ + title: `${$t('view')}${$t('userLoginLog')}`, + width: '30%', + props: { + formInline: { + userId: row.userId, + username: row.username, + token: row.token, + ipAddress: row.ipAddress, + ipRegion: row.ipRegion, + userAgent: row.userAgent, + type: row.type, + xRequestedWith: row.xRequestedWith, + }, + }, + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => h(UserLoginLogDialog, { ref: formRef }), + beforeSure: async done => { + done(); + await onSearch(); + }, + }); } /** 删除用户登录日志 */ export const onDelete = async (row: any) => { - const id = row.id; + const id = row.id; - // 是否确认删除 - const result = await messageBox({ - title: $t('confirmDelete'), - showMessage: false, - confirmMessage: undefined, - cancelMessage: $t('confirmDelete'), - }); - if (!result) return; + // 是否确认删除 + const result = await messageBox({ + title: $t('confirmDelete'), + showMessage: false, + confirmMessage: undefined, + cancelMessage: $t('confirmDelete'), + }); + if (!result) return; - // 删除数据 - await userLoginLogStore.deleteUserLoginLog([id]); - await onSearch(); + // 删除数据 + await userLoginLogStore.deleteUserLoginLog([id]); + await onSearch(); }; /** 批量删除 */ export const onDeleteBatch = async () => { - const ids = deleteIds.value; - const formDeletedBatchRef = ref(); + const ids = deleteIds.value; + const formDeletedBatchRef = ref(); - addDialog({ - title: $t('deleteBatchTip'), - width: '30%', - props: { formInline: { confirmText: '' } }, - draggable: true, - fullscreenIcon: true, - closeOnClickModal: false, - contentRenderer: () => h(DeleteBatchDialog, { ref: formDeletedBatchRef }), - beforeSure: (done, { options }) => { - formDeletedBatchRef.value.formDeletedBatchRef.validate(async (valid: any) => { - if (!valid) return; + addDialog({ + title: $t('deleteBatchTip'), + width: '30%', + props: { formInline: { confirmText: '' } }, + draggable: true, + fullscreenIcon: true, + closeOnClickModal: false, + contentRenderer: () => h(DeleteBatchDialog, { ref: formDeletedBatchRef }), + beforeSure: (done, { options }) => { + formDeletedBatchRef.value.formDeletedBatchRef.validate(async (valid: any) => { + if (!valid) return; - const text = options.props.formInline.confirmText.toLowerCase(); - if (text === 'yes' || text === 'y') { - // 删除数据 - await userLoginLogStore.deleteUserLoginLog(ids); - await onSearch(); + const text = options.props.formInline.confirmText.toLowerCase(); + if (text === 'yes' || text === 'y') { + // 删除数据 + await userLoginLogStore.deleteUserLoginLog(ids); + await onSearch(); - done(); - } else message($t('deleteBatchTip'), { type: 'warning' }); - }); - }, - }); + done(); + } else message($t('deleteBatchTip'), { type: 'warning' }); + }); + }, + }); };