增加登录日志功能

This commit is contained in:
2026-08-26 15:01:35 +08:00
parent a4f7a97763
commit e60cd00395
9 changed files with 1008 additions and 1 deletions
@@ -0,0 +1,161 @@
<template>
<el-drawer v-model="visible" title="登录日志详情" size="50%">
<div class="log-detail" v-if="log">
<el-descriptions :column="2" border>
<el-descriptions-item label="日志ID" label-width="150px">
{{ log.id }}
</el-descriptions-item>
<el-descriptions-item label="登录状态" label-width="150px">
<el-tag :type="log.status === 1 ? 'success' : 'danger'" size="small">
{{ log.status === 1 ? "成功" : "失败" }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="用户ID" label-width="150px">
{{ log.user_id }}
</el-descriptions-item>
<el-descriptions-item label="用户姓名" label-width="150px">
{{ log.user_name || "-" }}
</el-descriptions-item>
<el-descriptions-item label="登录账号" label-width="150px">
{{ log.account }}
</el-descriptions-item>
<el-descriptions-item label="租户名称" label-width="150px">
{{ log.tenant_name || "-" }}
</el-descriptions-item>
<el-descriptions-item label="登录方式" label-width="150px">
<el-tag type="primary" size="small">
{{ getLoginTypeLabel(log.login_type) }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="登录时间" label-width="150px">
{{ log.create_time }}
</el-descriptions-item>
<el-descriptions-item label="IP地址" label-width="150px">
{{ log.ip || "-" }}
</el-descriptions-item>
<el-descriptions-item label="结果信息" label-width="150px">
{{ log.message || "-" }}
</el-descriptions-item>
<el-descriptions-item label="用户代理" :span="2" label-width="150px">
<div class="user-agent-text">{{ log.user_agent || "无" }}</div>
</el-descriptions-item>
</el-descriptions>
</div>
<div v-else class="loading-container">
<el-empty description="加载中..." />
</div>
</el-drawer>
</template>
<script setup lang="ts">
import { ref, watch } from "vue";
import { getLoginLogDetail } from "@/api/loginLog";
interface LoginLog {
id: number;
tid: number | null;
user_id: number;
account: string;
user_name: string;
tenant_name: string;
login_type: string;
status: number;
message: string;
ip: string;
user_agent: string;
create_time: string;
}
const props = defineProps({
modelValue: {
type: Boolean,
default: false,
},
logId: {
type: Number,
default: undefined,
},
});
const emit = defineEmits(["update:modelValue"]);
const visible = ref(false);
const log = ref<LoginLog | null>(null);
// 登录方式标签
const getLoginTypeLabel = (type: string): string => {
const typeMap: Record<string, string> = {
password: "密码登录",
sms: "短信验证码",
email: "邮箱验证码",
};
return typeMap[type] || type;
};
// 获取日志详情
const fetchLogDetail = async (logId?: number) => {
const targetId = logId || props.logId;
if (!targetId) {
return;
}
log.value = null; // 清空之前的数据
try {
const res = await getLoginLogDetail(targetId);
if (res.code === 200) {
log.value = res.data;
} else {
console.error("获取登录日志详情失败:", res?.msg || "未知错误");
log.value = null;
}
} catch (error: any) {
console.error("获取登录日志详情失败", error);
log.value = null;
}
};
// 监听 modelValue 变化
watch(
() => props.modelValue,
(newVal) => {
visible.value = newVal;
if (newVal && props.logId) {
fetchLogDetail();
}
}
);
// 监听 visible 变化
watch(visible, (newVal) => {
if (!newVal) {
emit("update:modelValue", false);
log.value = null;
}
});
// 暴露方法给父组件
defineExpose({
open: (logId: number) => {
if (logId) {
fetchLogDetail(logId);
}
},
});
</script>
<style lang="less" scoped>
.log-detail {
padding: 20px;
}
.user-agent-text {
word-break: break-all;
color: #606266;
font-size: 12px;
}
.loading-container {
padding: 40px;
text-align: center;
}
</style>