feat: 添加登陆日志页面

master
luoer 2023-09-12 17:30:45 +08:00
parent e7e6c93236
commit 4eab238712
5 changed files with 588 additions and 98 deletions

14
.env
View File

@ -2,13 +2,11 @@
# 应用配置 # 应用配置
# ===================================================================================== # =====================================================================================
# 网站标题 # 网站标题
VITE_TITLE = Appnify VITE_TITLE = 绝弹管理后台
# 网站副标题 # 网站副标题
VITE_SUBTITLE = 快速开发web应用的模板工具 VITE_SUBTITLE = 快速开发web应用的模板工具
# API接口前缀:参见 axios 的 baseURL # 接口前缀 说明:参见 axios 的 baseURL
VITE_API = http://127.0.0.1:3030/ VITE_API = http://127.0.0.1:3030/
# API文档地址需返回符合 OPENAPI 规范的json内容
VITE_OPENAPI = http://127.0.0.1:3030/openapi.json
# ===================================================================================== # =====================================================================================
# 开发设置 # 开发设置
@ -17,11 +15,13 @@ VITE_OPENAPI = http://127.0.0.1:3030/openapi.json
VITE_HOST = 0.0.0.0 VITE_HOST = 0.0.0.0
# 端口 # 端口
VITE_PORT = 3020 VITE_PORT = 3020
# API代理地址 # 代理地址
VITE_PROXY = /api VITE_PROXY = /api
# API文档 说明:需返回符合 OPENAPI 规范的json内容
VITE_OPENAPI = http://127.0.0.1:3030/openapi.json
# ===================================================================================== # =====================================================================================
# 构建设置 # 构建设置
# ===================================================================================== # =====================================================================================
# 构建时加载的文件后缀: 设为mp时会优先加载index.mp.vue文件否则回退至index.vue文件 # 文件后缀 说明设为dev时会优先加载index.dev.vue文件否则回退至index.vue文件
VITE_EXTENSION = todo VITE_EXTENSION = dev

View File

@ -6,9 +6,11 @@ import { fileURLToPath } from "url";
const __dirname = path.join(fileURLToPath(new URL(import.meta.url)), ".."); const __dirname = path.join(fileURLToPath(new URL(import.meta.url)), "..");
const env = loadEnv("development", process.cwd()); const env = loadEnv("development", process.cwd());
console.log(env.VITE_OPENAPI);
const run = async () => { const run = async () => {
const output = await generateApi({ const output = await generateApi({
url: env.VITE_API_OPENAPI, url: env.VITE_OPENAPI,
templates: path.resolve(__dirname, "./template"), templates: path.resolve(__dirname, "./template"),
output: path.resolve(process.cwd(), "src/api/service"), output: path.resolve(process.cwd(), "src/api/service"),
name: "Api.ts", name: "Api.ts",

View File

@ -9,27 +9,32 @@
* --------------------------------------------------------------- * ---------------------------------------------------------------
*/ */
export interface Permission { export interface CreateUserDto {
/** /**
* *
* @example "文章列表" * @example "juetan"
*/ */
name: string; username: string;
/** /**
* *
* @example "post:list" * @example "绝弹"
*/ */
slug: string; nickname: string;
/** /**
* *
* @example "文章列表" * @example "password"
*/ */
description: string; password?: string;
/** /**
* * ID
* @example "文章列表" * @example 1
*/ */
roles: Role[]; avatarId?: number;
/**
* ID
* @example [1,2,3]
*/
roleIds?: number[];
} }
export interface User { export interface User {
@ -67,88 +72,32 @@ export interface User {
roleIds: number[]; roleIds: number[];
} }
export interface Role {
/**
*
* @example "管理员"
*/
name: string;
/**
*
* @example "admin"
*/
slug: string;
/**
*
* @example "拥有所有权限"
*/
description: string;
/**
*
* @example [1,2,3]
*/
permissions: Permission[];
/**
*
* @example [1,2,3]
*/
user: User;
}
export interface CreateUserDto {
/**
*
* @example "juetan"
*/
username: string;
/**
*
* @example "password"
*/
password: string;
/**
*
* @example "绝弹"
*/
nickname: string;
/**
*
* @example "./assets/222421415123.png "
*/
avatar: string;
/**
*
* @example [1,2,3]
*/
roles: Role[];
}
export interface UpdateUserDto { export interface UpdateUserDto {
/** /**
* *
* @example "juetan" * @example "juetan"
*/ */
username?: string; username?: string;
/**
*
* @example "password"
*/
password?: string;
/** /**
* *
* @example "绝弹" * @example "绝弹"
*/ */
nickname?: string; nickname?: string;
/** /**
* *
* @example "./assets/222421415123.png " * @example "password"
*/ */
avatar?: string; password?: string;
/** /**
* * ID
* @example 1
*/
avatarId?: number;
/**
* ID
* @example [1,2,3] * @example [1,2,3]
*/ */
roles?: Role[]; roleIds?: number[];
} }
export interface AuthUserDto { export interface AuthUserDto {
@ -201,6 +150,111 @@ export interface LoginedUserVo {
roleIds: number[]; roleIds: number[];
} }
export interface CreateLogDto {
/**
* (Swagger)
* @example "demo"
*/
demo: string;
}
export interface LoginLog {
/**
*
* @example "绝弹"
*/
nickname: string;
/**
*
* @example "1"
*/
description: string;
/**
* IP
* @example "127.0.0.1"
*/
ip: string;
/**
*
* @example "广东省深圳市"
*/
addr: string;
/**
*
* @example "chrome"
*/
browser: string;
/**
*
* @example "windows 10"
*/
os: string;
}
export interface UpdateLogDto {
/**
* (Swagger)
* @example "demo"
*/
demo?: string;
}
export interface Role {
/**
*
* @example "管理员"
*/
name: string;
/**
*
* @example "admin"
*/
slug: string;
/**
*
* @example "拥有所有权限"
*/
description: string;
/**
*
* @example [1,2,3]
*/
permissions: Permission[];
/**
* ID
* @example [1,2,3]
*/
permissionIds: number[];
/**
*
* @example [1,2,3]
*/
user: User;
}
export interface Permission {
/**
*
* @example "文章列表"
*/
name: string;
/**
*
* @example "post:list"
*/
slug: string;
/**
*
* @example "文章列表"
*/
description: string;
/**
*
* @example "文章列表"
*/
roles: Role[];
}
export interface CreateRoleDto { export interface CreateRoleDto {
name: string; name: string;
slug: string; slug: string;
@ -209,6 +263,7 @@ export interface CreateRoleDto {
} }
export interface UpdateRoleDto { export interface UpdateRoleDto {
permissionIds?: number[];
name?: string; name?: string;
slug?: string; slug?: string;
description?: string; description?: string;
@ -344,6 +399,67 @@ export interface GetUsersParams {
* @example "绝弹" * @example "绝弹"
*/ */
nickname?: string; nickname?: string;
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/**
*
* @min 1
* @example 1
*/
page?: number;
/**
*
* @min 0
* @example 10
*/
size?: number;
}
export interface GetLogsParams {
/**
*
* @example "绝弹"
*/
nickname?: string;
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/**
*
* @min 1
* @example 1
*/
page?: number;
/**
*
* @min 0
* @example 10
*/
size?: number;
}
export interface GetLoginLogsParams {
/**
*
* @example "绝弹"
*/
nickname?: string;
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/** /**
* *
* @min 1 * @min 1
@ -359,6 +475,13 @@ export interface GetUsersParams {
} }
export interface GetPostsParams { export interface GetPostsParams {
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/** /**
* *
* @min 1 * @min 1
@ -403,6 +526,13 @@ export namespace User {
* @example "绝弹" * @example "绝弹"
*/ */
nickname?: string; nickname?: string;
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/** /**
* *
* @min 1 * @min 1
@ -489,6 +619,151 @@ export namespace Auth {
} }
} }
export namespace Log {
/**
* @description
* @tags log
* @name AddLog
* @request POST:/api/v1/logs
*/
export namespace AddLog {
export type RequestParams = {};
export type RequestQuery = {};
export type RequestBody = CreateLogDto;
export type RequestHeaders = {};
export type ResponseBody = Response & {
data: number;
};
}
/**
* @description /
* @tags log
* @name GetLogs
* @request GET:/api/v1/logs
*/
export namespace GetLogs {
export type RequestParams = {};
export type RequestQuery = {
/**
*
* @example "绝弹"
*/
nickname?: string;
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/**
*
* @min 1
* @example 1
*/
page?: number;
/**
*
* @min 0
* @example 10
*/
size?: number;
};
export type RequestBody = never;
export type RequestHeaders = {};
export type ResponseBody = Response & {
data: LoginLog[];
};
}
/**
* No description
* @tags log
* @name GetLoginLogs
* @request GET:/api/v1/logs/login
*/
export namespace GetLoginLogs {
export type RequestParams = {};
export type RequestQuery = {
/**
*
* @example "绝弹"
*/
nickname?: string;
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/**
*
* @min 1
* @example 1
*/
page?: number;
/**
*
* @min 0
* @example 10
*/
size?: number;
};
export type RequestBody = never;
export type RequestHeaders = {};
export type ResponseBody = Response & {
data: LoginLog[];
};
}
/**
* @description ID
* @tags log
* @name GetLog
* @request GET:/api/v1/logs/{id}
*/
export namespace GetLog {
export type RequestParams = {
id: number;
};
export type RequestQuery = {};
export type RequestBody = never;
export type RequestHeaders = {};
export type ResponseBody = Response & {
data: LoginLog;
};
}
/**
* @description ID
* @tags log
* @name UpdateLog
* @request PATCH:/api/v1/logs/{id}
*/
export namespace UpdateLog {
export type RequestParams = {
id: number;
};
export type RequestQuery = {};
export type RequestBody = UpdateLogDto;
export type RequestHeaders = {};
export type ResponseBody = Response;
}
/**
* @description ID
* @tags log
* @name DelLog
* @request DELETE:/api/v1/logs/{id}
*/
export namespace DelLog {
export type RequestParams = {
id: number;
};
export type RequestQuery = {};
export type RequestBody = never;
export type RequestHeaders = {};
export type ResponseBody = Response;
}
}
export namespace Role { export namespace Role {
/** /**
* @description * @description
@ -526,7 +801,7 @@ export namespace Role {
*/ */
export namespace GetRole { export namespace GetRole {
export type RequestParams = { export type RequestParams = {
id: string; id: number;
}; };
export type RequestQuery = {}; export type RequestQuery = {};
export type RequestBody = never; export type RequestBody = never;
@ -543,7 +818,7 @@ export namespace Role {
*/ */
export namespace UpdateRole { export namespace UpdateRole {
export type RequestParams = { export type RequestParams = {
id: string; id: number;
}; };
export type RequestQuery = {}; export type RequestQuery = {};
export type RequestBody = UpdateRoleDto; export type RequestBody = UpdateRoleDto;
@ -558,7 +833,7 @@ export namespace Role {
*/ */
export namespace DelRole { export namespace DelRole {
export type RequestParams = { export type RequestParams = {
id: string; id: number;
}; };
export type RequestQuery = {}; export type RequestQuery = {};
export type RequestBody = never; export type RequestBody = never;
@ -686,7 +961,7 @@ export namespace Upload {
*/ */
export namespace GetFile { export namespace GetFile {
export type RequestParams = { export type RequestParams = {
id: string; id: number;
}; };
export type RequestQuery = {}; export type RequestQuery = {};
export type RequestBody = never; export type RequestBody = never;
@ -720,7 +995,7 @@ export namespace Upload {
*/ */
export namespace DelFile { export namespace DelFile {
export type RequestParams = { export type RequestParams = {
id: string; id: number;
}; };
export type RequestQuery = {}; export type RequestQuery = {};
export type RequestBody = never; export type RequestBody = never;
@ -754,6 +1029,13 @@ export namespace Post {
export namespace GetPosts { export namespace GetPosts {
export type RequestParams = {}; export type RequestParams = {};
export type RequestQuery = { export type RequestQuery = {
/**
*
* @default "id:desc"
* @pattern /^(\w+:\w+,)*\w+:\w+$/
* @example "id:desc"
*/
sort?: string;
/** /**
* *
* @min 1 * @min 1
@ -1097,6 +1379,129 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
}); });
}, },
}; };
log = {
/**
*
*
* @tags log
* @name AddLog
* @request POST:/api/v1/logs
*/
addLog: (data: CreateLogDto, params: RequestParams = {}) => {
return this.request<
Response & {
data: number;
},
any
>({
path: `/api/v1/logs`,
method: "POST",
body: data,
type: ContentType.Json,
format: "json",
...params,
});
},
/**
* /
*
* @tags log
* @name GetLogs
* @request GET:/api/v1/logs
*/
getLogs: (query: GetLogsParams, params: RequestParams = {}) => {
return this.request<
Response & {
data: LoginLog[];
},
any
>({
path: `/api/v1/logs`,
method: "GET",
query: query,
format: "json",
...params,
});
},
/**
* No description
*
* @tags log
* @name GetLoginLogs
* @request GET:/api/v1/logs/login
*/
getLoginLogs: (query: GetLoginLogsParams, params: RequestParams = {}) => {
return this.request<
Response & {
data: LoginLog[];
},
any
>({
path: `/api/v1/logs/login`,
method: "GET",
query: query,
format: "json",
...params,
});
},
/**
* ID
*
* @tags log
* @name GetLog
* @request GET:/api/v1/logs/{id}
*/
getLog: (id: number, params: RequestParams = {}) => {
return this.request<
Response & {
data: LoginLog;
},
any
>({
path: `/api/v1/logs/${id}`,
method: "GET",
format: "json",
...params,
});
},
/**
* ID
*
* @tags log
* @name UpdateLog
* @request PATCH:/api/v1/logs/{id}
*/
updateLog: (id: number, data: UpdateLogDto, params: RequestParams = {}) => {
return this.request<Response, any>({
path: `/api/v1/logs/${id}`,
method: "PATCH",
body: data,
type: ContentType.Json,
format: "json",
...params,
});
},
/**
* ID
*
* @tags log
* @name DelLog
* @request DELETE:/api/v1/logs/{id}
*/
delLog: (id: number, params: RequestParams = {}) => {
return this.request<Response, any>({
path: `/api/v1/logs/${id}`,
method: "DELETE",
format: "json",
...params,
});
},
};
role = { role = {
/** /**
* *
@ -1144,7 +1549,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
* @name GetRole * @name GetRole
* @request GET:/api/v1/roles/{id} * @request GET:/api/v1/roles/{id}
*/ */
getRole: (id: string, params: RequestParams = {}) => { getRole: (id: number, params: RequestParams = {}) => {
return this.request< return this.request<
Response & { Response & {
data: string; data: string;
@ -1165,7 +1570,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
* @name UpdateRole * @name UpdateRole
* @request PATCH:/api/v1/roles/{id} * @request PATCH:/api/v1/roles/{id}
*/ */
updateRole: (id: string, data: UpdateRoleDto, params: RequestParams = {}) => { updateRole: (id: number, data: UpdateRoleDto, params: RequestParams = {}) => {
return this.request<Response, any>({ return this.request<Response, any>({
path: `/api/v1/roles/${id}`, path: `/api/v1/roles/${id}`,
method: "PATCH", method: "PATCH",
@ -1183,7 +1588,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
* @name DelRole * @name DelRole
* @request DELETE:/api/v1/roles/{id} * @request DELETE:/api/v1/roles/{id}
*/ */
delRole: (id: string, params: RequestParams = {}) => { delRole: (id: number, params: RequestParams = {}) => {
return this.request< return this.request<
Response & { Response & {
data: string; data: string;
@ -1344,7 +1749,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
* @name GetFile * @name GetFile
* @request GET:/api/v1/upload/{id} * @request GET:/api/v1/upload/{id}
*/ */
getFile: (id: string, params: RequestParams = {}) => { getFile: (id: number, params: RequestParams = {}) => {
return this.request< return this.request<
Response & { Response & {
data: Upload; data: Upload;
@ -1386,7 +1791,7 @@ export class Api<SecurityDataType extends unknown> extends HttpClient<SecurityDa
* @name DelFile * @name DelFile
* @request DELETE:/api/v1/upload/{id} * @request DELETE:/api/v1/upload/{id}
*/ */
delFile: (id: string, params: RequestParams = {}) => { delFile: (id: number, params: RequestParams = {}) => {
return this.request<Response, any>({ return this.request<Response, any>({
path: `/api/v1/upload/${id}`, path: `/api/v1/upload/${id}`,
method: "DELETE", method: "DELETE",

View File

@ -20,7 +20,6 @@ export const config = {
const tableRef = inject<any>("ref:table"); const tableRef = inject<any>("ref:table");
return ( return (
<div class="w-full flex gap-x-2 justify-end"> <div class="w-full flex gap-x-2 justify-end">
{/* <Link>收起 <i class="icon-park-outline-up"></i> </Link> */}
{(tableRef.search?.items?.length || 0) > config.searchInlineCount && ( {(tableRef.search?.items?.length || 0) > config.searchInlineCount && (
<Button disabled={tableRef?.loading.value} onClick={() => tableRef?.reloadData()}> <Button disabled={tableRef?.loading.value} onClick={() => tableRef?.reloadData()}>
{{ icon: () => <IconRefresh></IconRefresh>, default: () => "重置" }} {{ icon: () => <IconRefresh></IconRefresh>, default: () => "重置" }}

View File

@ -0,0 +1,84 @@
<template>
<BreadPage>
<Table v-bind="table"></Table>
</BreadPage>
</template>
<script setup lang="tsx">
import { api } from "@/api";
import { Table, useTable } from "@/components";
import { dayjs } from "@/libs/dayjs";
import { Tag } from "@arco-design/web-vue";
const table = useTable({
data: async (model, paging) => {
return api.log.getLoginLogs({ ...model, ...paging });
},
columns: [
{
title: "登陆账号",
dataIndex: "nickname",
width: 200,
},
{
title: "操作描述",
dataIndex: "description",
render: ({ record: { status, description } }) => {
return (
<span>
<Tag color={status === null || status ? "green" : "red"} class="mr-2">
{ status === null || status ? "成功" : "失败" }
</Tag>
{description}
</span>
);
},
},
{
title: "登陆地址",
dataIndex: "ip",
render: ({ record }) => `${record.addr || "未知"}(${record.ip})`,
},
{
title: "操作系统",
dataIndex: "os",
},
{
title: "浏览器",
dataIndex: "browser",
},
{
title: "登陆时间",
dataIndex: "createdAt",
width: 200,
render: ({ record }) => dayjs(record.createdAt).fromNow(),
},
],
search: {
items: [
{
field: "nickname",
label: "登陆账号",
type: "input",
required: false,
nodeProps: {
placeholder: '请输入登陆账号',
hideLabel: true,
}
},
],
},
});
</script>
<style scoped></style>
<route lang="json">
{
"meta": {
"sort": 10303,
"title": "登陆日志",
"icon": "icon-park-outline-log"
}
}
</route>