155 lines
4.9 KiB
Vue
155 lines
4.9 KiB
Vue
<script lang="ts" setup>
|
||
/**
|
||
* Api 访问历史日志详情弹窗
|
||
* 列表不带大 JSON,打开时按历史表 id 再拉详情;操作人只用 xk_admin / 患者,不兼容 oldadmin
|
||
*/
|
||
import { ref } from 'vue';
|
||
|
||
import { useVbenModal } from '@vben/common-ui';
|
||
|
||
import { Descriptions, Spin } from 'ant-design-vue';
|
||
|
||
import { Icon } from '#/components/icon';
|
||
import { getIcon } from '#/util/tool';
|
||
|
||
import {
|
||
getApiOpLogOperatorLabel,
|
||
getApiOpLogPlatformLabel,
|
||
} from '../../api-log/config/platform';
|
||
import { getApiOpLogHistoryDetail } from '../api';
|
||
|
||
const gridApi = ref();
|
||
const data = ref<Record<string, any>>({});
|
||
const loading = ref(false);
|
||
|
||
/** 列表行没有 param/result,打开时按 id 拉详情;解析失败则原样展示 */
|
||
function formatJson(raw: unknown) {
|
||
if (raw == null || raw === '') return '';
|
||
if (typeof raw === 'object') return raw;
|
||
try {
|
||
return JSON.parse(String(raw));
|
||
} catch {
|
||
return raw;
|
||
}
|
||
}
|
||
|
||
const [Modal, modalApi] = useVbenModal({
|
||
fullscreenButton: false,
|
||
draggable: true,
|
||
onCancel() {
|
||
modalApi.close();
|
||
},
|
||
onConfirm: async () => {
|
||
modalApi.close();
|
||
},
|
||
async onOpenChange(isOpen: boolean) {
|
||
gridApi.value = isOpen ? modalApi.getData()?.gridApi : null;
|
||
if (!isOpen) return;
|
||
const { values } = modalApi.getData<Record<string, any>>();
|
||
data.value = values || {};
|
||
const id = Number(values?.id || 0);
|
||
if (!id) return;
|
||
loading.value = true;
|
||
try {
|
||
data.value = (await getApiOpLogHistoryDetail(id)) || values || {};
|
||
} catch (error) {
|
||
console.error(error);
|
||
} finally {
|
||
loading.value = false;
|
||
}
|
||
},
|
||
});
|
||
</script>
|
||
|
||
<template>
|
||
<Modal class="w-[80%] h-[80%]" title="Api访问历史日志详情">
|
||
<Spin :spinning="loading">
|
||
<div class="flex flex-col gap-4">
|
||
<Descriptions
|
||
:column="{ xxl: 2, xl: 2, lg: 2, md: 2, sm: 2, xs: 2 }"
|
||
bordered
|
||
>
|
||
<Descriptions.Item label="ID">{{ data.id }}</Descriptions.Item>
|
||
<Descriptions.Item label="接口名称">
|
||
{{ data.url_title || '-' }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="接口说明">
|
||
{{ data.url_description || '-' }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="是否鉴权">
|
||
<span v-if="data.url_need_auth === 1">需要鉴权</span>
|
||
<span v-else-if="data.url_need_auth === 0">无需鉴权</span>
|
||
<span v-else>-</span>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="操作人员">
|
||
<span>{{ getApiOpLogOperatorLabel(data) }}</span>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="URL">{{ data.url }}</Descriptions.Item>
|
||
<Descriptions.Item label="请求方式">
|
||
<span v-if="data.method === 1">GET</span>
|
||
<span v-else-if="data.method === 2">POST</span>
|
||
<span v-else>未知</span>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="控制器">
|
||
{{ data.controller }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="IP">
|
||
IP地址:{{ data.ip }}
|
||
<p>归属地:{{ data.ip_address }}</p>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="类型">
|
||
{{ data.type === 0 ? '访问成功' : '访问失败' }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="返回结果代码">
|
||
{{ data.result_code }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="接口来源">
|
||
<span>{{ getApiOpLogPlatformLabel(data.platform_type) }}</span>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="设备">
|
||
<Icon :icon="getIcon(data.equipment)" :size="20" />
|
||
{{ data.equipment }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="浏览器">
|
||
<Icon :icon="getIcon(data.browser)" :size="20" />
|
||
{{ data.browser }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="所属平台">
|
||
{{ data.platform?.name || '未绑定' }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="访问时间">
|
||
{{ data.created_at }}
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="归档时间">
|
||
{{ data.archived_at }}
|
||
</Descriptions.Item>
|
||
</Descriptions>
|
||
<Descriptions
|
||
:column="{ xxl: 1, xl: 1, lg: 1, md: 1, sm: 1, xs: 1 }"
|
||
bordered
|
||
>
|
||
<Descriptions.Item label="参数">
|
||
<pre>{{ formatJson(data.param) }}</pre>
|
||
</Descriptions.Item>
|
||
<Descriptions.Item label="结果">
|
||
<pre>{{ formatJson(data.result) }}</pre>
|
||
</Descriptions.Item>
|
||
</Descriptions>
|
||
</div>
|
||
</Spin>
|
||
</Modal>
|
||
</template>
|
||
|
||
<style scoped>
|
||
.ant-descriptions-item-label {
|
||
font-weight: bold;
|
||
}
|
||
|
||
pre {
|
||
padding: 8px;
|
||
overflow-x: auto;
|
||
white-space: pre-wrap;
|
||
border-radius: 4px;
|
||
}
|
||
</style>
|