- 推送/操作/开放接口日志列表各加"大小"列(payload_size/response_size,Utils.getFileSize 格式化)
- 详情弹窗:下载按钮常驻;Utils.canPreview(≤50KB) 才显示"在线预览",点击 fetch 复用各自 /Download/{id} 取原文渲染
- oapilog 详情从无条件内联改为同一套门槛逻辑
- utils 新增 PREVIEW_MAX_BYTES 与 canPreview
This commit is contained in:
@@ -67,6 +67,20 @@ export default class Utils {
|
|||||||
} else return "";
|
} else return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 在线预览大小上限(字节):日志大字段(请求体/响应)超过此值时隐藏"在线预览"、只保留"下载"。
|
||||||
|
* 浏览器 JSON 美化 + <pre> 渲染在数百 KB 起会卡顿,50KB 为体验无损的保守阈值。
|
||||||
|
*/
|
||||||
|
public static readonly PREVIEW_MAX_BYTES = 50 * 1024;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否允许在线预览:有内容(size > 0)且字节数不超过阈值。
|
||||||
|
* size 为 null/undefined/0(未记录请求体、saveResponse=false、响应为空)→ 不可预览,仅下载。
|
||||||
|
*/
|
||||||
|
public static canPreview(size?: number | null): boolean {
|
||||||
|
return size != null && size > 0 && size <= Utils.PREVIEW_MAX_BYTES;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除指定键
|
* 删除指定键
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -20,7 +20,13 @@
|
|||||||
<pre class="json-pre">{{ formatJson(row?.requestParams) }}</pre>
|
<pre class="json-pre">{{ formatJson(row?.requestParams) }}</pre>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="响应结果:">
|
<FormItem label="响应结果:">
|
||||||
<pre class="json-pre">{{ formatJson(row?.responseData) }}</pre>
|
<template v-if="row?.id">
|
||||||
|
<a href="javascript:;" class="download-link" @click.prevent="handleDownload">下载</a>
|
||||||
|
<a v-if="canPreview" href="javascript:;" class="download-link" style="margin-left: 12px"
|
||||||
|
@click.prevent="handlePreview">在线预览</a>
|
||||||
|
<span v-if="sizeText" class="sub-text">({{ sizeText }})</span>
|
||||||
|
</template>
|
||||||
|
<span v-else>-</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="创建时间:">
|
<FormItem label="创建时间:">
|
||||||
<span>{{ formatTime(row?.createTime) }}</span>
|
<span>{{ formatTime(row?.createTime) }}</span>
|
||||||
@@ -31,10 +37,18 @@
|
|||||||
<Button type="primary" @click="show = false">确定</Button>
|
<Button type="primary" @click="show = false">确定</Button>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal v-model="previewVisible" title="响应结果预览" width="760" :mask-closable="true">
|
||||||
|
<pre class="json-pre">{{ previewText }}</pre>
|
||||||
|
<template #footer>
|
||||||
|
<Button @click="previewVisible = false">关闭</Button>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
|
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
||||||
import Utils from '@/utils';
|
import Utils from '@/utils';
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
@@ -81,6 +95,32 @@ function formatTime(val: any): string {
|
|||||||
if (!val) return '-';
|
if (!val) return '-';
|
||||||
return Utils.dateFormat(val, 'yyyy-MM-dd HH:mm:ss');
|
return Utils.dateFormat(val, 'yyyy-MM-dd HH:mm:ss');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { $conf } = useCurrentInstance();
|
||||||
|
|
||||||
|
const previewVisible = ref(false);
|
||||||
|
const previewText = ref('');
|
||||||
|
const canPreview = computed(() => Utils.canPreview(props.row?.responseSize));
|
||||||
|
const sizeText = computed(() =>
|
||||||
|
props.row?.responseSize != null && props.row.responseSize > 0 ? Utils.getFileSize(props.row.responseSize) : ''
|
||||||
|
);
|
||||||
|
|
||||||
|
function handleDownload() {
|
||||||
|
if (!props.row?.id) return;
|
||||||
|
location.href = `${$conf.serviceHost}/api/oapilog/Download/${props.row.id}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handlePreview() {
|
||||||
|
if (!props.row?.id) return;
|
||||||
|
previewText.value = '加载中…';
|
||||||
|
previewVisible.value = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${$conf.serviceHost}/api/oapilog/Download/${props.row.id}`, { credentials: 'include' });
|
||||||
|
previewText.value = formatJson(await res.text());
|
||||||
|
} catch (e: any) {
|
||||||
|
previewText.value = '预览加载失败:' + (e?.message || e);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -116,4 +156,32 @@ function formatTime(val: any): string {
|
|||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.download-link {
|
||||||
|
color: #577afb;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #3d5afe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sub-text {
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.json-pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border: 1px solid #e8eaec;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 420px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -151,6 +151,13 @@ const columns = [
|
|||||||
align: 'right',
|
align: 'right',
|
||||||
render: (h: any, { row }: any) => h('span', row.costTime != null ? `${row.costTime} ms` : '-'),
|
render: (h: any, { row }: any) => h('span', row.costTime != null ? `${row.costTime} ms` : '-'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '响应',
|
||||||
|
key: 'responseSize',
|
||||||
|
width: 90,
|
||||||
|
align: 'right',
|
||||||
|
render: (h: any, { row }: any) => h('span', row.responseSize != null && row.responseSize > 0 ? Utils.getFileSize(row.responseSize) : '-'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '调用时间',
|
title: '调用时间',
|
||||||
key: 'createTime',
|
key: 'createTime',
|
||||||
|
|||||||
@@ -20,7 +20,12 @@
|
|||||||
<pre class="json-pre">{{ formatJson(row?.requestParams) }}</pre>
|
<pre class="json-pre">{{ formatJson(row?.requestParams) }}</pre>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="数据:">
|
<FormItem label="数据:">
|
||||||
<a v-if="row?.id" href="javascript:;" class="download-link" @click.prevent="handleDownload">下载</a>
|
<template v-if="row?.id">
|
||||||
|
<a href="javascript:;" class="download-link" @click.prevent="handleDownload">下载</a>
|
||||||
|
<a v-if="canPreview" href="javascript:;" class="download-link" style="margin-left: 12px"
|
||||||
|
@click.prevent="handlePreview">在线预览</a>
|
||||||
|
<span v-if="sizeText" class="sub-text">({{ sizeText }})</span>
|
||||||
|
</template>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="创建时间:">
|
<FormItem label="创建时间:">
|
||||||
@@ -32,10 +37,17 @@
|
|||||||
<Button type="primary" @click="show = false">确定</Button>
|
<Button type="primary" @click="show = false">确定</Button>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal v-model="previewVisible" title="响应结果预览" width="760" :mask-closable="true">
|
||||||
|
<pre class="json-pre">{{ previewText }}</pre>
|
||||||
|
<template #footer>
|
||||||
|
<Button @click="previewVisible = false">关闭</Button>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
||||||
import Utils from '@/utils';
|
import Utils from '@/utils';
|
||||||
|
|
||||||
@@ -83,6 +95,25 @@ function handleDownload() {
|
|||||||
if (!props.row?.id) return;
|
if (!props.row?.id) return;
|
||||||
location.href = `${$conf.serviceHost}/api/oplog/Download/${props.row.id}`;
|
location.href = `${$conf.serviceHost}/api/oplog/Download/${props.row.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const previewVisible = ref(false);
|
||||||
|
const previewText = ref('');
|
||||||
|
const canPreview = computed(() => Utils.canPreview(props.row?.responseSize));
|
||||||
|
const sizeText = computed(() =>
|
||||||
|
props.row?.responseSize != null && props.row.responseSize > 0 ? Utils.getFileSize(props.row.responseSize) : ''
|
||||||
|
);
|
||||||
|
|
||||||
|
async function handlePreview() {
|
||||||
|
if (!props.row?.id) return;
|
||||||
|
previewText.value = '加载中…';
|
||||||
|
previewVisible.value = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${$conf.serviceHost}/api/oplog/Download/${props.row.id}`, { credentials: 'include' });
|
||||||
|
previewText.value = formatJson(await res.text());
|
||||||
|
} catch (e: any) {
|
||||||
|
previewText.value = '预览加载失败:' + (e?.message || e);
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@@ -126,4 +157,18 @@ function handleDownload() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.json-pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border: 1px solid #e8eaec;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 420px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -152,6 +152,13 @@ const columns = [
|
|||||||
align: 'right',
|
align: 'right',
|
||||||
render: (h: any, { row }: any) => h('span', row.costTime != null ? `${row.costTime} ms` : '-'),
|
render: (h: any, { row }: any) => h('span', row.costTime != null ? `${row.costTime} ms` : '-'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '响应',
|
||||||
|
key: 'responseSize',
|
||||||
|
width: 90,
|
||||||
|
align: 'right',
|
||||||
|
render: (h: any, { row }: any) => h('span', row.responseSize != null && row.responseSize > 0 ? Utils.getFileSize(row.responseSize) : '-'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作时间ed',
|
title: '操作时间ed',
|
||||||
key: 'createTime',
|
key: 'createTime',
|
||||||
|
|||||||
@@ -156,6 +156,13 @@ const columns = [
|
|||||||
align: 'right',
|
align: 'right',
|
||||||
render: (h: any, { row }: any) => h('span', row.costMs != null ? `${row.costMs}` : '-'),
|
render: (h: any, { row }: any) => h('span', row.costMs != null ? `${row.costMs}` : '-'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '请求体',
|
||||||
|
key: 'payloadSize',
|
||||||
|
width: 100,
|
||||||
|
align: 'right',
|
||||||
|
render: (h: any, { row }: any) => h('span', row.payloadSize != null && row.payloadSize > 0 ? Utils.getFileSize(row.payloadSize) : '-'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
key: 'action',
|
key: 'action',
|
||||||
|
|||||||
@@ -43,7 +43,12 @@
|
|||||||
<span>{{ formatTime(row?.createdAt) }}</span>
|
<span>{{ formatTime(row?.createdAt) }}</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem label="请求体:">
|
<FormItem label="请求体:">
|
||||||
<a v-if="row?.id" href="javascript:;" class="download-link" @click.prevent="handleDownload">下载请求体</a>
|
<template v-if="row?.id">
|
||||||
|
<a href="javascript:;" class="download-link" @click.prevent="handleDownload">下载请求体</a>
|
||||||
|
<a v-if="canPreview" href="javascript:;" class="download-link" style="margin-left: 12px"
|
||||||
|
@click.prevent="handlePreview">在线预览</a>
|
||||||
|
<span v-if="sizeText" class="sub-text">({{ sizeText }})</span>
|
||||||
|
</template>
|
||||||
<span v-else>-</span>
|
<span v-else>-</span>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
</Form>
|
</Form>
|
||||||
@@ -51,10 +56,17 @@
|
|||||||
<Button type="primary" @click="show = false">确定</Button>
|
<Button type="primary" @click="show = false">确定</Button>
|
||||||
</template>
|
</template>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
<Modal v-model="previewVisible" title="请求体预览" width="760" :mask-closable="true">
|
||||||
|
<pre class="json-pre">{{ previewText }}</pre>
|
||||||
|
<template #footer>
|
||||||
|
<Button @click="previewVisible = false">关闭</Button>
|
||||||
|
</template>
|
||||||
|
</Modal>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { computed } from 'vue';
|
import { computed, ref } from 'vue';
|
||||||
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
import useCurrentInstance from '@/utils/hook/useCurrentInstance';
|
||||||
import Utils from '@/utils';
|
import Utils from '@/utils';
|
||||||
|
|
||||||
@@ -87,11 +99,40 @@ function onVisibleChange(val: boolean) {
|
|||||||
|
|
||||||
const { $conf } = useCurrentInstance();
|
const { $conf } = useCurrentInstance();
|
||||||
|
|
||||||
|
const previewVisible = ref(false);
|
||||||
|
const previewText = ref('');
|
||||||
|
|
||||||
|
const canPreview = computed(() => Utils.canPreview(props.row?.payloadSize));
|
||||||
|
const sizeText = computed(() =>
|
||||||
|
props.row?.payloadSize != null && props.row.payloadSize > 0 ? Utils.getFileSize(props.row.payloadSize) : ''
|
||||||
|
);
|
||||||
|
|
||||||
function handleDownload() {
|
function handleDownload() {
|
||||||
if (!props.row?.id) return;
|
if (!props.row?.id) return;
|
||||||
location.href = `${$conf.serviceHost}/api/push/Download/${props.row.id}`;
|
location.href = `${$conf.serviceHost}/api/push/Download/${props.row.id}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function handlePreview() {
|
||||||
|
if (!props.row?.id) return;
|
||||||
|
previewText.value = '加载中…';
|
||||||
|
previewVisible.value = true;
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${$conf.serviceHost}/api/push/Download/${props.row.id}`, { credentials: 'include' });
|
||||||
|
previewText.value = formatJson(await res.text());
|
||||||
|
} catch (e: any) {
|
||||||
|
previewText.value = '预览加载失败:' + (e?.message || e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatJson(val: string): string {
|
||||||
|
if (!val) return '-';
|
||||||
|
try {
|
||||||
|
return JSON.stringify(JSON.parse(val), null, 2);
|
||||||
|
} catch {
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function formatTime(val: any): string {
|
function formatTime(val: any): string {
|
||||||
if (!val) return '-';
|
if (!val) return '-';
|
||||||
return Utils.dateFormat(val, 'yyyy-MM-dd HH:mm:ss');
|
return Utils.dateFormat(val, 'yyyy-MM-dd HH:mm:ss');
|
||||||
@@ -135,4 +176,18 @@ function formatTime(val: any): string {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.json-pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border: 1px solid #e8eaec;
|
||||||
|
border-radius: 4px;
|
||||||
|
max-height: 420px;
|
||||||
|
overflow: auto;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-break: break-all;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user