更新
This commit is contained in:
@@ -60,11 +60,10 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, h, nextTick, defineAsyncComponent } from 'vue';
|
import { reactive, ref, nextTick, defineAsyncComponent } from 'vue';
|
||||||
import { EyeOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
import { EyeOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
import { schemas } from './loginLog/querySchemas';
|
import { schemas } from './loginLog/querySchemas';
|
||||||
import { useForm } from '@/components/Form/index';
|
import { useForm } from '@/components/Form/index';
|
||||||
import { TableAction } from '@/components/Table';
|
|
||||||
import { getLoginLogList, loginLogDelete, loginLogBatchDelete } from '@/api/system/loginLog';
|
import { getLoginLogList, loginLogDelete, loginLogBatchDelete } from '@/api/system/loginLog';
|
||||||
import { columns } from './loginLog/columns';
|
import { columns } from './loginLog/columns';
|
||||||
import { Modal, message } from 'ant-design-vue';
|
import { Modal, message } from 'ant-design-vue';
|
||||||
@@ -87,7 +86,7 @@
|
|||||||
*/
|
*/
|
||||||
const formParams = reactive({
|
const formParams = reactive({
|
||||||
username: '',
|
username: '',
|
||||||
type: '',
|
logType: '',
|
||||||
status: '',
|
status: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,21 @@
|
|||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
import { Tag } from 'ant-design-vue';
|
import { Tag, Avatar } from 'ant-design-vue';
|
||||||
|
|
||||||
|
// 灰色副文本统一风格
|
||||||
|
const subText: Record<string, string> = {
|
||||||
|
fontSize: '14px',
|
||||||
|
color: '#666',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 请求方式标签颜色
|
||||||
|
function methodTagColor(method?: string) {
|
||||||
|
const m = (method || '').toUpperCase();
|
||||||
|
if (m === 'GET') return 'success';
|
||||||
|
if (m === 'POST') return 'processing';
|
||||||
|
if (m === 'PUT' || m === 'PATCH') return 'warning';
|
||||||
|
if (m === 'DELETE') return 'error';
|
||||||
|
return 'default';
|
||||||
|
}
|
||||||
|
|
||||||
export const columns = [
|
export const columns = [
|
||||||
{
|
{
|
||||||
@@ -11,99 +27,166 @@ export const columns = [
|
|||||||
{
|
{
|
||||||
title: '访客',
|
title: '访客',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 200,
|
minWidth: 220,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
return h('div', [
|
const textBlock = h('div', { style: { minWidth: '0', flex: '1' } }, [
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '4px', fontWeight: 'bold' },
|
||||||
},
|
},
|
||||||
record.username,
|
record.username || '-',
|
||||||
),
|
),
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '4px' },
|
||||||
},
|
},
|
||||||
record.ip,
|
[
|
||||||
|
h(
|
||||||
|
Tag,
|
||||||
|
{
|
||||||
|
color: 'default',
|
||||||
|
style: { fontSize: '12px', lineHeight: '20px' },
|
||||||
|
},
|
||||||
|
{ default: () => record.ip || '-' },
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: { marginBottom: '4px' },
|
||||||
|
},
|
||||||
|
[
|
||||||
h(
|
h(
|
||||||
Tag,
|
Tag,
|
||||||
{
|
{
|
||||||
color: 'processing',
|
color: 'processing',
|
||||||
|
style: { fontSize: '12px', lineHeight: '20px' },
|
||||||
},
|
},
|
||||||
{
|
{ default: () => record.location || '-' },
|
||||||
default: () => record.location,
|
|
||||||
},
|
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
return h('div', { style: { display: 'flex', alignItems: 'center' } }, [
|
||||||
|
h(
|
||||||
|
Avatar,
|
||||||
|
{
|
||||||
|
src: record.avatar,
|
||||||
|
size: 48,
|
||||||
|
shape: 'square',
|
||||||
|
style: {
|
||||||
|
marginRight: '12px',
|
||||||
|
flexShrink: 0,
|
||||||
|
background: '#1890ff',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => (record.username || '?').slice(0, 1) },
|
||||||
|
),
|
||||||
|
textBlock,
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '请求接口',
|
title: '请求接口',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 200,
|
minWidth: 220,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
return h('div', [
|
return h('div', [
|
||||||
h(
|
h(
|
||||||
Tag,
|
Tag,
|
||||||
{
|
{
|
||||||
color: 'default',
|
color: methodTagColor(record.requestMethod),
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '5px', fontSize: '12px', lineHeight: '20px' },
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => record.requestMethod,
|
|
||||||
},
|
},
|
||||||
|
{ default: () => record.requestMethod || '-' },
|
||||||
),
|
),
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
style: { marginBottom: '5px' },
|
style: { ...subText, wordBreak: 'break-all', marginBottom: '4px' },
|
||||||
|
title: record.url,
|
||||||
},
|
},
|
||||||
'接口:' + record.url,
|
'接口:' + (record.url || '-'),
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: { fontWeight: 'bold' },
|
||||||
|
},
|
||||||
|
'名称:' + (record.title || '-'),
|
||||||
),
|
),
|
||||||
h('div', '名称:' + record.title),
|
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '接口响应',
|
title: '接口响应',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 250,
|
minWidth: 250,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
|
const abnormal = record.status != 0;
|
||||||
return h('div', [
|
return h('div', [
|
||||||
h('span', '状态:'),
|
h('span', { style: subText }, '状态:'),
|
||||||
h(
|
h(
|
||||||
Tag,
|
Tag,
|
||||||
{
|
{
|
||||||
color: record.status == 0 ? 'success' : 'error',
|
color: abnormal ? 'error' : 'success',
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '5px', fontSize: '12px', lineHeight: '20px' },
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => (record.status == 0 ? '正常' : '异常'),
|
|
||||||
},
|
},
|
||||||
|
{ default: () => (abnormal ? '异常' : '正常') },
|
||||||
),
|
),
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
style: { marginBottom: '5px' },
|
style: {
|
||||||
|
...subText,
|
||||||
|
marginBottom: '4px',
|
||||||
|
color: abnormal ? '#f5222d' : '#666',
|
||||||
|
fontWeight: abnormal ? 'bold' : 'normal',
|
||||||
},
|
},
|
||||||
'请求耗时:' + record.consumeTime + 'ms',
|
},
|
||||||
|
'请求耗时:' + (record.consumeTime ?? '-') + 'ms',
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: subText,
|
||||||
|
},
|
||||||
|
'响应时间:' + (record.updateTime || '-'),
|
||||||
),
|
),
|
||||||
h('div', '响应时间:' + record.updateTime),
|
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作来源',
|
title: '操作来源',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 200,
|
minWidth: 200,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
return h('div', [
|
return h('div', [
|
||||||
h('div', '系统:' + record.os),
|
h(
|
||||||
h('div', '类型:' + record.logTypeText),
|
'div',
|
||||||
h('div', '来源:' + record.sourceText),
|
{
|
||||||
|
style: { marginBottom: '4px' },
|
||||||
|
},
|
||||||
|
'系统:' + (record.os || '-'),
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: { marginBottom: '4px' },
|
||||||
|
},
|
||||||
|
'类型:' + (record.logTypeText || '-'),
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: subText,
|
||||||
|
},
|
||||||
|
'来源:' + (record.sourceText || '-'),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,40 +1,87 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal v-model:visible="props.visible" title="登录日志详情" width="800px" @cancel="dialogClose">
|
<a-modal
|
||||||
<a-descriptions class="margin-top" :column="2" bordered :labelStyle="{ width: '120px' }">
|
v-model:visible="props.visible"
|
||||||
<a-descriptions-item label="操作用户">
|
title="登录日志详情"
|
||||||
{{ formData.createUser }}
|
width="860px"
|
||||||
</a-descriptions-item>
|
:mask-closable="false"
|
||||||
<a-descriptions-item label="IP地址">
|
@cancel="dialogClose"
|
||||||
{{ formData.ip }}
|
>
|
||||||
</a-descriptions-item>
|
<!-- 概览条:头像 + 操作用户 + 状态标签 + 摘要 -->
|
||||||
<a-descriptions-item label="操作系统">
|
<div class="overview">
|
||||||
{{ formData.os }}
|
<a-avatar :src="formData.avatar" :size="56" shape="square" class="overview-avatar">
|
||||||
</a-descriptions-item>
|
{{ (formData.createUser || '?').slice(0, 1) }}
|
||||||
<a-descriptions-item label="操作浏览器">
|
</a-avatar>
|
||||||
{{ formData.browser }}
|
<div class="overview-main">
|
||||||
</a-descriptions-item>
|
<div class="overview-name-row">
|
||||||
<a-descriptions-item label="请求耗时"> {{ formData.consumeTime }}s </a-descriptions-item>
|
<span class="overview-name">{{ formData.createUser || '-' }}</span>
|
||||||
<a-descriptions-item label="请求地区">
|
<a-tag :color="formData.status ? 'error' : 'success'" class="ml-3">
|
||||||
{{ formData.location }}
|
{{ formData.status ? '异常' : '正常' }}
|
||||||
</a-descriptions-item>
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
<div class="overview-sub" :title="formData.ip">
|
||||||
|
{{ formData.ip || '-' }} · {{ formData.location || '-' }}
|
||||||
|
</div>
|
||||||
|
<div class="overview-sub-light" :title="formData.os">
|
||||||
|
{{ formData.os || '-' }} · {{ formData.browser || '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overview-time">
|
||||||
|
<div>请求耗时</div>
|
||||||
|
<div class="overview-time-value">{{ formData.consumeTime ?? '-' }}ms</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 请求信息 -->
|
||||||
|
<a-descriptions class="section-gap" title="请求信息" :column="2" bordered>
|
||||||
<a-descriptions-item label="请求方式">
|
<a-descriptions-item label="请求方式">
|
||||||
{{ formData.requestMethod }}
|
<a-tag>{{ formData.requestMethod || '-' }}</a-tag>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="请求状态">
|
<a-descriptions-item label="请求状态">
|
||||||
<a-tag :color="formData.status ? 'danger' : 'success'">{{
|
<a-tag :color="formData.status ? 'error' : 'success'">
|
||||||
formData.status ? '异常' : '正常'
|
{{ formData.status ? '异常' : '正常' }}
|
||||||
}}</a-tag>
|
</a-tag>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="调用方法" :span="3">
|
<a-descriptions-item label="请求地区">{{ formData.location || '-' }}</a-descriptions-item>
|
||||||
{{ formData.method }}
|
<a-descriptions-item label="请求耗时"
|
||||||
</a-descriptions-item>
|
>{{ formData.consumeTime ?? '-' }}ms</a-descriptions-item
|
||||||
<a-descriptions-item label="请求参数" :span="3">
|
>
|
||||||
{{ formData.param }}
|
<a-descriptions-item label="调用方法" :span="2">
|
||||||
</a-descriptions-item>
|
<span class="field-break">{{ formData.method || '-' }}</span>
|
||||||
<a-descriptions-item label="返回结果" :span="3">
|
|
||||||
{{ formData.result }}
|
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
|
|
||||||
|
<!-- 登录环境 -->
|
||||||
|
<a-descriptions class="section-gap" title="登录环境" :column="2" bordered>
|
||||||
|
<a-descriptions-item label="操作用户">{{ formData.createUser || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="IP地址">
|
||||||
|
<a-tag color="default">{{ formData.ip || '-' }}</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作系统">{{ formData.os || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="浏览器">{{ formData.browser || '-' }}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
|
||||||
|
<!-- 请求参数 -->
|
||||||
|
<div class="code-block section-gap">
|
||||||
|
<div class="code-head">
|
||||||
|
<span>请求参数</span>
|
||||||
|
<a-button type="link" size="small" class="code-copy" @click="copyText(formData.param)"
|
||||||
|
>复制</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<pre class="code-body">{{ formData.param || '-' }}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 返回结果 -->
|
||||||
|
<div class="code-block mt-3">
|
||||||
|
<div class="code-head">
|
||||||
|
<span>返回结果</span>
|
||||||
|
<a-button type="link" size="small" class="code-copy" @click="copyText(formData.result)"
|
||||||
|
>复制</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<pre class="code-body">{{ formData.result || '-' }}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<a-button @click="dialogClose">取消</a-button>
|
<a-button @click="dialogClose">取消</a-button>
|
||||||
@@ -45,10 +92,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getLoginLogDetail } from '@/api/system/loginLog';
|
import { getLoginLogDetail } from '@/api/system/loginLog';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
/**
|
|
||||||
* 定义参数变量
|
|
||||||
*/
|
|
||||||
const emit = defineEmits(['success', 'update:visible']);
|
const emit = defineEmits(['success', 'update:visible']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -56,6 +101,7 @@
|
|||||||
*/
|
*/
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
createUser: '',
|
createUser: '',
|
||||||
|
avatar: '',
|
||||||
ip: '',
|
ip: '',
|
||||||
os: '',
|
os: '',
|
||||||
browser: '',
|
browser: '',
|
||||||
@@ -91,6 +137,22 @@
|
|||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制文本
|
||||||
|
*/
|
||||||
|
const copyText = async (text?: string) => {
|
||||||
|
if (!text) {
|
||||||
|
message.warn('无内容可复制');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
message.success('复制成功');
|
||||||
|
} catch {
|
||||||
|
message.error('复制失败,请手动选择复制');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置表单数据
|
* 设置表单数据
|
||||||
*/
|
*/
|
||||||
@@ -103,12 +165,127 @@
|
|||||||
* 钩子函数
|
* 钩子函数
|
||||||
*/
|
*/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 组件挂载后执行的代码
|
||||||
if (props.loginlogId) {
|
if (props.loginlogId) {
|
||||||
setFormData();
|
setFormData();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
.overview {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: linear-gradient(120deg, #f5f7ff 0%, #f8fafc 100%);
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.overview-avatar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #1890ff;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-main {
|
||||||
|
margin-left: 16px;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-name-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-sub {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-sub-light {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-time {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
.overview-time-value {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-3 {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-gap {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-3 {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-break {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-block {
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.code-copy {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #303133;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
max-height: 160px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
:deep(.ant-descriptions__body .ant-descriptions__table .ant-descriptions__cell) {
|
:deep(.ant-descriptions__body .ant-descriptions__table .ant-descriptions__cell) {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,20 +9,20 @@ export const schemas: FormSchema[] = [
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'type',
|
name: 'logType',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
label: '请求类型',
|
label: '登录类型',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
placeholder: '请选择请求类型',
|
placeholder: '请选择登录类型',
|
||||||
clearable: true,
|
clearable: true,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: '登录',
|
label: '登录',
|
||||||
value: '0',
|
value: '1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '退出',
|
label: '退出',
|
||||||
value: '1',
|
value: '2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
@@ -30,17 +30,17 @@ export const schemas: FormSchema[] = [
|
|||||||
{
|
{
|
||||||
name: 'status',
|
name: 'status',
|
||||||
component: 'Select',
|
component: 'Select',
|
||||||
label: '请求状态',
|
label: '登录状态',
|
||||||
componentProps: {
|
componentProps: {
|
||||||
placeholder: '请选择状态',
|
placeholder: '请选择状态',
|
||||||
clearable: true,
|
clearable: true,
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: '正常',
|
label: '成功',
|
||||||
value: '0',
|
value: '0',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '异常',
|
label: '失败',
|
||||||
value: '1',
|
value: '1',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { reactive, ref, h, nextTick, defineAsyncComponent } from 'vue';
|
import { reactive, ref, nextTick, defineAsyncComponent } from 'vue';
|
||||||
import { EyeOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
import { EyeOutlined, DeleteOutlined } from '@ant-design/icons-vue';
|
||||||
import { schemas } from './operLog/querySchemas';
|
import { schemas } from './operLog/querySchemas';
|
||||||
import { useForm } from '@/components/Form/index';
|
import { useForm } from '@/components/Form/index';
|
||||||
|
|||||||
@@ -1,5 +1,28 @@
|
|||||||
import { h } from 'vue';
|
import { h } from 'vue';
|
||||||
import { Tag } from 'ant-design-vue';
|
import { Tag, Avatar } from 'ant-design-vue';
|
||||||
|
|
||||||
|
// 灰色副文本统一风格
|
||||||
|
const subText: Record<string, string> = {
|
||||||
|
fontSize: '14px',
|
||||||
|
color: '#666',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 请求方式标签颜色
|
||||||
|
function methodTagColor(method?: string) {
|
||||||
|
const m = (method || '').toUpperCase();
|
||||||
|
if (m === 'GET') return 'success';
|
||||||
|
if (m === 'POST') return 'processing';
|
||||||
|
if (m === 'PUT' || m === 'PATCH') return 'warning';
|
||||||
|
if (m === 'DELETE') return 'error';
|
||||||
|
return 'default';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 操作类型标签颜色:删除/异常类用 error,修改用 warning,其余 processing
|
||||||
|
function typeTagColor(type?: number) {
|
||||||
|
if (type === 3) return 'error'; // 删除
|
||||||
|
if (type === 2) return 'warning'; // 修改
|
||||||
|
return 'processing';
|
||||||
|
}
|
||||||
|
|
||||||
export const columns = [
|
export const columns = [
|
||||||
{
|
{
|
||||||
@@ -11,93 +34,168 @@ export const columns = [
|
|||||||
{
|
{
|
||||||
title: '访客',
|
title: '访客',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 200,
|
minWidth: 220,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
return h('div', [
|
const textBlock = h('div', { style: { minWidth: '0', flex: '1' } }, [
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '4px', fontWeight: 'bold' },
|
||||||
},
|
},
|
||||||
record.createUser,
|
[
|
||||||
|
record.createUser || '-',
|
||||||
|
record.typeText
|
||||||
|
? h(
|
||||||
|
Tag,
|
||||||
|
{
|
||||||
|
color: typeTagColor(record.type),
|
||||||
|
style: { marginLeft: '8px', fontSize: '12px', lineHeight: '20px' },
|
||||||
|
},
|
||||||
|
{ default: () => record.typeText },
|
||||||
|
)
|
||||||
|
: null,
|
||||||
|
],
|
||||||
),
|
),
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '4px' },
|
||||||
},
|
},
|
||||||
record.ip,
|
[
|
||||||
),
|
|
||||||
h(
|
h(
|
||||||
Tag,
|
Tag,
|
||||||
{
|
{
|
||||||
color: 'processing',
|
color: 'default',
|
||||||
},
|
style: { fontSize: '12px', lineHeight: '20px' },
|
||||||
{
|
|
||||||
default: () => record.location,
|
|
||||||
},
|
},
|
||||||
|
{ default: () => record.ip || '-' },
|
||||||
),
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: subText,
|
||||||
|
},
|
||||||
|
'地点:' + (record.location || '-'),
|
||||||
|
),
|
||||||
|
]);
|
||||||
|
return h('div', { style: { display: 'flex', alignItems: 'center' } }, [
|
||||||
|
h(
|
||||||
|
Avatar,
|
||||||
|
{
|
||||||
|
src: record.avatar,
|
||||||
|
size: 48,
|
||||||
|
shape: 'square',
|
||||||
|
style: {
|
||||||
|
marginRight: '12px',
|
||||||
|
flexShrink: 0,
|
||||||
|
background: '#1890ff',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ default: () => (record.createUser || '?').slice(0, 1) },
|
||||||
|
),
|
||||||
|
textBlock,
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '请求接口',
|
title: '请求接口',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 300,
|
minWidth: 220,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
return h('div', [
|
return h('div', [
|
||||||
h(
|
h(
|
||||||
Tag,
|
Tag,
|
||||||
{
|
{
|
||||||
color: 'default',
|
color: methodTagColor(record.requestMethod),
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '5px', fontSize: '12px', lineHeight: '20px' },
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => record.requestMethod,
|
|
||||||
},
|
},
|
||||||
|
{ default: () => record.requestMethod || '-' },
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: { ...subText, wordBreak: 'break-all', marginBottom: '4px' },
|
||||||
|
title: record.url,
|
||||||
|
},
|
||||||
|
'接口:' + (record.url || '-'),
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: { fontWeight: 'bold' },
|
||||||
|
},
|
||||||
|
'名称:' + (record.title || '-'),
|
||||||
),
|
),
|
||||||
h('div', '接口:' + record.url),
|
|
||||||
h('div', '名称:' + record.title),
|
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '接口响应',
|
title: '接口响应',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 250,
|
minWidth: 200,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
|
const abnormal = record.status != 0;
|
||||||
return h('div', [
|
return h('div', [
|
||||||
h('span', '状态:'),
|
|
||||||
h(
|
h(
|
||||||
Tag,
|
Tag,
|
||||||
{
|
{
|
||||||
color: record.status == 0 ? 'success' : 'error',
|
color: abnormal ? 'error' : 'success',
|
||||||
style: { marginBottom: '5px' },
|
style: { marginBottom: '5px', fontSize: '12px', lineHeight: '20px' },
|
||||||
},
|
|
||||||
{
|
|
||||||
default: () => (record.status == 0 ? '正常' : '异常'),
|
|
||||||
},
|
},
|
||||||
|
{ default: () => (abnormal ? '异常' : '正常') },
|
||||||
),
|
),
|
||||||
h(
|
h(
|
||||||
'div',
|
'div',
|
||||||
{
|
{
|
||||||
style: { marginBottom: '5px' },
|
style: {
|
||||||
|
...subText,
|
||||||
|
marginBottom: '4px',
|
||||||
|
color: abnormal ? '#f5222d' : '#666',
|
||||||
|
fontWeight: abnormal ? 'bold' : 'normal',
|
||||||
},
|
},
|
||||||
'请求耗时:' + record.consumeTime + 'ms',
|
},
|
||||||
|
'请求耗时:' + (record.consumeTime ?? '-') + 'ms',
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: subText,
|
||||||
|
},
|
||||||
|
'响应时间:' + (record.updateTime || '-'),
|
||||||
),
|
),
|
||||||
h('div', '响应时间:' + record.updateTime),
|
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作来源',
|
title: '操作来源',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
width: 200,
|
minWidth: 180,
|
||||||
customRender({ record }) {
|
customRender({ record }) {
|
||||||
return h('div', [
|
return h('div', [
|
||||||
h('div', '系统:' + record.os),
|
h(
|
||||||
h('div', '类型:' + record.typeText),
|
'div',
|
||||||
h('div', '来源:' + record.sourceText),
|
{
|
||||||
|
style: { marginBottom: '4px' },
|
||||||
|
},
|
||||||
|
[h('span', { style: subText }, '系统:'), h('span', record.os || '-')],
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: { marginBottom: '4px' },
|
||||||
|
},
|
||||||
|
[h('span', { style: subText }, '来源:'), h('span', record.sourceText || '-')],
|
||||||
|
),
|
||||||
|
h(
|
||||||
|
'div',
|
||||||
|
{
|
||||||
|
style: subText,
|
||||||
|
},
|
||||||
|
'浏览器:' + (record.browser || '-'),
|
||||||
|
),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,43 +1,109 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal v-model:visible="props.visible" title="操作日志详情" width="800px" @cancel="dialogClose">
|
<a-modal
|
||||||
<a-descriptions class="margin-top" :column="2" bordered :labelStyle="{ width: '120px' }">
|
v-model:visible="props.visible"
|
||||||
<a-descriptions-item label="日志标题">
|
title="操作日志详情"
|
||||||
{{ formData.title }}
|
width="860px"
|
||||||
</a-descriptions-item>
|
:mask-closable="false"
|
||||||
<a-descriptions-item label="操作用户">
|
@cancel="dialogClose"
|
||||||
{{ formData.createUser }}
|
>
|
||||||
</a-descriptions-item>
|
<!-- 概览条:头像 + 操作人 + 状态/类型标签 + 摘要 -->
|
||||||
<a-descriptions-item label="IP地址">
|
<div class="overview">
|
||||||
{{ formData.ip }}
|
<a-avatar :src="formData.avatar" :size="56" shape="square" class="overview-avatar">
|
||||||
</a-descriptions-item>
|
{{ (formData.createUser || '?').slice(0, 1) }}
|
||||||
<a-descriptions-item label="操作系统">
|
</a-avatar>
|
||||||
{{ formData.os }}
|
<div class="overview-main">
|
||||||
</a-descriptions-item>
|
<div class="overview-name-row">
|
||||||
<a-descriptions-item label="操作浏览器">
|
<span class="overview-name">{{ formData.createUser || '-' }}</span>
|
||||||
{{ formData.browser }}
|
<a-tag :color="formData.status ? 'error' : 'success'" class="ml-3">
|
||||||
</a-descriptions-item>
|
{{ formData.status ? '异常' : '正常' }}
|
||||||
<a-descriptions-item label="请求耗时"> {{ formData.consumeTime }}s </a-descriptions-item>
|
</a-tag>
|
||||||
|
<a-tag v-if="formData.typeText" color="processing" class="ml-2">
|
||||||
|
{{ formData.typeText }}
|
||||||
|
</a-tag>
|
||||||
|
<a-tag v-if="formData.sourceText" color="default" class="ml-2">
|
||||||
|
{{ formData.sourceText }}
|
||||||
|
</a-tag>
|
||||||
|
</div>
|
||||||
|
<div class="overview-sub" :title="formData.title">
|
||||||
|
{{ formData.title || '-' }}
|
||||||
|
</div>
|
||||||
|
<div class="overview-sub-light" :title="formData.url">
|
||||||
|
{{ formData.url || '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="overview-time">
|
||||||
|
<div>响应时间</div>
|
||||||
|
<div class="overview-time-value">{{ formData.updateTime || '-' }}</div>
|
||||||
|
<div class="overview-time-label">创建时间</div>
|
||||||
|
<div class="overview-time-value">{{ formData.createTime || '-' }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 请求信息 -->
|
||||||
|
<a-descriptions class="section-gap" title="请求信息" :column="2" bordered>
|
||||||
<a-descriptions-item label="请求方式">
|
<a-descriptions-item label="请求方式">
|
||||||
{{ formData.requestMethod }}
|
<a-tag>{{ formData.requestMethod || '-' }}</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求耗时">
|
||||||
|
<span :class="{ 'consume-abnormal': formData.status }">
|
||||||
|
{{ formData.consumeTime ?? '-' }}ms
|
||||||
|
</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求URL" :span="2">
|
||||||
|
<span class="field-break">{{ formData.url || '-' }}</span>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="调用方法" :span="2">
|
||||||
|
<span class="field-break">{{ formData.method || '-' }}</span>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="请求状态">
|
<a-descriptions-item label="请求状态">
|
||||||
<a-tag :color="formData.status ? 'danger' : 'success'">{{
|
<a-tag :color="formData.status ? 'error' : 'success'">
|
||||||
formData.status ? '异常' : '正常'
|
{{ formData.status ? '异常' : '正常' }}
|
||||||
}}</a-tag>
|
</a-tag>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="调用方法" :span="3">
|
<a-descriptions-item label="错误信息">
|
||||||
{{ formData.method }}
|
<span class="field-break">{{ formData.error || '-' }}</span>
|
||||||
</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="请求参数" :span="3">
|
|
||||||
{{ formData.param }}
|
|
||||||
</a-descriptions-item>
|
|
||||||
<a-descriptions-item label="返回结果" :span="3">
|
|
||||||
{{ formData.result }}
|
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
|
|
||||||
|
<!-- 操作环境 -->
|
||||||
|
<a-descriptions class="section-gap" title="操作环境" :column="2" bordered>
|
||||||
|
<a-descriptions-item label="操作人">{{ formData.createUser || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作类型">{{ formData.typeText || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作来源">{{ formData.sourceText || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求IP">
|
||||||
|
<a-tag color="default">{{ formData.ip || '-' }}</a-tag>
|
||||||
|
</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="请求地区">{{ formData.location || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="操作系统">{{ formData.os || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="浏览器">{{ formData.browser || '-' }}</a-descriptions-item>
|
||||||
|
<a-descriptions-item label="日志标题">{{ formData.title || '-' }}</a-descriptions-item>
|
||||||
|
</a-descriptions>
|
||||||
|
|
||||||
|
<!-- 请求参数 -->
|
||||||
|
<div class="code-block section-gap">
|
||||||
|
<div class="code-head">
|
||||||
|
<span>请求参数</span>
|
||||||
|
<a-button type="link" size="small" class="code-copy" @click="copyText(formData.param)"
|
||||||
|
>复制</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<pre class="code-body">{{ formData.param || '-' }}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 返回结果 -->
|
||||||
|
<div class="code-block mt-3">
|
||||||
|
<div class="code-head">
|
||||||
|
<span>返回结果</span>
|
||||||
|
<a-button type="link" size="small" class="code-copy" @click="copyText(formData.result)"
|
||||||
|
>复制</a-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<pre class="code-body">{{ formData.result || '-' }}</pre>
|
||||||
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<span class="dialog-footer">
|
<span class="dialog-footer">
|
||||||
<a-button @click="dialogClose">取消</a-button>
|
<a-button @click="dialogClose">关 闭</a-button>
|
||||||
</span>
|
</span>
|
||||||
</template>
|
</template>
|
||||||
</a-modal>
|
</a-modal>
|
||||||
@@ -45,10 +111,8 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { getOperLogDetail } from '@/api/system/operLog';
|
import { getOperLogDetail } from '@/api/system/operLog';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
/**
|
|
||||||
* 定义参数变量
|
|
||||||
*/
|
|
||||||
const emit = defineEmits(['success', 'update:visible']);
|
const emit = defineEmits(['success', 'update:visible']);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -57,7 +121,9 @@
|
|||||||
const formData = ref({
|
const formData = ref({
|
||||||
title: '',
|
title: '',
|
||||||
createUser: '',
|
createUser: '',
|
||||||
|
avatar: '',
|
||||||
ip: '',
|
ip: '',
|
||||||
|
location: '',
|
||||||
os: '',
|
os: '',
|
||||||
browser: '',
|
browser: '',
|
||||||
consumeTime: '',
|
consumeTime: '',
|
||||||
@@ -66,6 +132,12 @@
|
|||||||
method: '',
|
method: '',
|
||||||
param: '',
|
param: '',
|
||||||
result: '',
|
result: '',
|
||||||
|
error: '',
|
||||||
|
url: '',
|
||||||
|
typeText: '',
|
||||||
|
sourceText: '',
|
||||||
|
updateTime: '',
|
||||||
|
createTime: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -91,6 +163,22 @@
|
|||||||
emit('update:visible', false);
|
emit('update:visible', false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制文本
|
||||||
|
*/
|
||||||
|
const copyText = async (text?: string) => {
|
||||||
|
if (!text) {
|
||||||
|
message.warn('无内容可复制');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await navigator.clipboard.writeText(text);
|
||||||
|
message.success('复制成功');
|
||||||
|
} catch {
|
||||||
|
message.error('复制失败,请手动选择复制');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置表单数据
|
* 设置表单数据
|
||||||
*/
|
*/
|
||||||
@@ -103,8 +191,141 @@
|
|||||||
* 钩子函数
|
* 钩子函数
|
||||||
*/
|
*/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 组件挂载后执行的代码
|
||||||
if (props.operlogId) {
|
if (props.operlogId) {
|
||||||
setFormData();
|
setFormData();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.overview {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 16px 20px;
|
||||||
|
background: linear-gradient(120deg, #f5f7ff 0%, #f8fafc 100%);
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.overview-avatar {
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: #1890ff;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-main {
|
||||||
|
margin-left: 16px;
|
||||||
|
min-width: 0;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-name-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-name {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-sub {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-sub-light {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-time {
|
||||||
|
flex-shrink: 0;
|
||||||
|
margin-left: 16px;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
|
||||||
|
.overview-time-label {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.overview-time-value {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-2 {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.ml-3 {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-gap {
|
||||||
|
margin-top: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-3 {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.field-break {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.consume-abnormal {
|
||||||
|
color: #f5222d;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-block {
|
||||||
|
border: 1px solid #ebeef5;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-head {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 14px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border-bottom: 1px solid #ebeef5;
|
||||||
|
font-size: 13px;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
.code-copy {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.code-body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 12px 14px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #303133;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
max-height: 160px;
|
||||||
|
overflow-y: auto;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.ant-descriptions__body .ant-descriptions__table .ant-descriptions__cell) {
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user