|
|
@ -1,50 +1,73 @@ |
|
|
|
<template> |
|
|
|
<teleport to="body"> |
|
|
|
<div v-if="showErrorStack" class="error-stack-overlay"> |
|
|
|
<div class="error-stack-container" :class="typeClass"> |
|
|
|
<div class="error-stack-container" > |
|
|
|
<div class="error-stack-header"> |
|
|
|
<!-- <div class="icon-wrapper"> |
|
|
|
<img :src="iconMap[type]" alt="icon" /> |
|
|
|
</div> --> |
|
|
|
<span class="error-stack-title">{{ typeText }}</span> |
|
|
|
<span class="error-stack-title" :class="type">{{ title }}</span> |
|
|
|
</div> |
|
|
|
<div class="error-stack-content"> |
|
|
|
<p class="error-stack-message">{{ info || "未知错误" }}</p> |
|
|
|
<p class="error-stack-message">{{ info || '--' }}</p> |
|
|
|
<div class="error-stack-details-wrapper"> |
|
|
|
<div class="details-header" @click="toggleDetails"> |
|
|
|
<span class="details-title">详细信息</span> |
|
|
|
<span class="details-arrow" :class="{ 'is-expanded': isShow }"> |
|
|
|
<svg viewBox="0 0 24 24" width="24" height="24"> |
|
|
|
<path d="M7 10l5 5 5-5" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" /> |
|
|
|
<path |
|
|
|
d="M7 10l5 5 5-5" |
|
|
|
fill="none" |
|
|
|
stroke="currentColor" |
|
|
|
stroke-width="2" |
|
|
|
stroke-linecap="round" |
|
|
|
/> |
|
|
|
</svg> |
|
|
|
</span> |
|
|
|
</div> |
|
|
|
<div class="error-stack-details" v-show="isShow"> |
|
|
|
<template v-if="detailInfos.length"> |
|
|
|
<div v-for="(detail, index) in detailInfos" :key="index" class="detail-item"> |
|
|
|
<span class="detail-label">{{ detail.name }}</span> |
|
|
|
<span class="detail-value">{{ detail.description }}</span> |
|
|
|
<template v-if="detailInfo && detailInfo.length > 0"> |
|
|
|
<div class="detail-item"> |
|
|
|
<!-- <span class="detail-label">{{ detailInfo }}</span> --> |
|
|
|
<span class="detail-value">{{ detailInfo }}</span> |
|
|
|
</div> |
|
|
|
</template> |
|
|
|
<div v-else class="empty-details"> |
|
|
|
<svg class="empty-icon" viewBox="0 0 24 24" width="48" height="48"> |
|
|
|
<svg |
|
|
|
class="empty-icon" |
|
|
|
viewBox="0 0 24 24" |
|
|
|
width="48" |
|
|
|
height="48" |
|
|
|
> |
|
|
|
<path |
|
|
|
d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" |
|
|
|
fill="currentColor" /> |
|
|
|
fill="currentColor" |
|
|
|
/> |
|
|
|
</svg> |
|
|
|
<span>暂无详细信息</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<div v-if="stackInfo" class="stack-info-trigger" @click="showStackInfo"> |
|
|
|
<div |
|
|
|
v-if="stackInfo" |
|
|
|
class="stack-info-trigger" |
|
|
|
@click="showStackInfo" |
|
|
|
> |
|
|
|
<svg viewBox="0 0 24 24" width="20" height="20"> |
|
|
|
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" fill="none" stroke="currentColor" |
|
|
|
stroke-width="2" stroke-linecap="round" /> |
|
|
|
<path |
|
|
|
d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" |
|
|
|
fill="none" |
|
|
|
stroke="currentColor" |
|
|
|
stroke-width="2" |
|
|
|
stroke-linecap="round" |
|
|
|
/> |
|
|
|
</svg> |
|
|
|
<span>查看栈错误信息</span> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
<button :class="['error-stack-close', buttonClass]" @click="closeErrorStack">关闭</button> |
|
|
|
<button |
|
|
|
:class="['error-stack-close']" |
|
|
|
@click="closeErrorStack" |
|
|
|
> |
|
|
|
关闭 |
|
|
|
</button> |
|
|
|
</div> |
|
|
|
</div> |
|
|
|
</teleport> |
|
|
@ -115,6 +138,15 @@ |
|
|
|
font-size: 32px; |
|
|
|
font-weight: 600; |
|
|
|
color: var(--theme-color); |
|
|
|
&.Info { |
|
|
|
color: #333; |
|
|
|
} |
|
|
|
&.Warn { |
|
|
|
color: #fa9d3b; |
|
|
|
} |
|
|
|
&.Error { |
|
|
|
color: #d81212; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
@ -176,11 +208,11 @@ |
|
|
|
border-bottom: none; |
|
|
|
} |
|
|
|
|
|
|
|
.detail-label { |
|
|
|
flex: 0 0 120px; |
|
|
|
font-size: 26px; |
|
|
|
color: #6b7280; |
|
|
|
} |
|
|
|
// .detail-label { |
|
|
|
// flex: 0 0 120px; |
|
|
|
// font-size: 26px; |
|
|
|
// color: #6b7280; |
|
|
|
// } |
|
|
|
|
|
|
|
.detail-value { |
|
|
|
flex: 1; |
|
|
@ -263,60 +295,18 @@ |
|
|
|
|
|
|
|
<script setup lang="ts"> |
|
|
|
import { eventBus } from '../../eventBus' |
|
|
|
import { onMounted, onUnmounted, ref, computed } from 'vue' |
|
|
|
import type { ErrorModalData, ErrorDetail } from '../../eventBus' |
|
|
|
import { onMounted, onUnmounted, ref } from 'vue' |
|
|
|
import type { ErrorModalData } from '../../eventBus' |
|
|
|
import { MsgLevel } from '@/websocket/socket' |
|
|
|
|
|
|
|
// let ErrorIcon = new URL('@/assets/Warn.svg', import.meta.url).href |
|
|
|
// let WarnIcon = new URL('@/assets/update-pin-icon.svg', import.meta.url).href |
|
|
|
// let NotifyIcon = new URL('@/assets/notify.svg', import.meta.url).href |
|
|
|
// let FatalIcon = new URL('@/assets/fatal.svg', import.meta.url).href |
|
|
|
const showErrorStack = ref(false) |
|
|
|
const title = ref('') |
|
|
|
const info = ref('') |
|
|
|
const type = ref<'Fatal' | 'Error' | 'Warn' | 'Notify'>('Notify') |
|
|
|
const detailInfos = ref<ErrorDetail[]>([]) |
|
|
|
const type = ref<MsgLevel>('Info') |
|
|
|
const detailInfo = ref<string>('') |
|
|
|
const isShow = ref(true) |
|
|
|
const stackInfo = ref('') |
|
|
|
|
|
|
|
// 类型映射 |
|
|
|
const TYPE_MAP = { |
|
|
|
Fatal: '严重错误', |
|
|
|
Error: '错误', |
|
|
|
Warn: '警告', |
|
|
|
Notify: '提示', |
|
|
|
} as const |
|
|
|
|
|
|
|
// 计算属性:类型文本 |
|
|
|
const typeText = computed(() => { |
|
|
|
return TYPE_MAP[type.value as keyof typeof TYPE_MAP] || '未知类型' |
|
|
|
}) |
|
|
|
|
|
|
|
// 计算属性:类型样式 |
|
|
|
const typeClass = computed(() => { |
|
|
|
return { |
|
|
|
'error-fatal': type.value === 'Fatal', |
|
|
|
'error-error': type.value === 'Error', |
|
|
|
'error-warn': type.value === 'Warn', |
|
|
|
'error-notify': type.value === 'Notify' |
|
|
|
} |
|
|
|
}) |
|
|
|
//计算属性 按钮样式 |
|
|
|
const buttonClass = computed(() => { |
|
|
|
return { |
|
|
|
'error-fatal-button': type.value === 'Fatal', |
|
|
|
'error-error-button': type.value === 'Error', |
|
|
|
'error-warn-button': type.value === 'Warn', |
|
|
|
'error-notify-button': type.value === 'Notify' |
|
|
|
} |
|
|
|
}) |
|
|
|
// 计算属性:图标映射 |
|
|
|
// const iconMap = computed(() => { |
|
|
|
// return { |
|
|
|
// 'Fatal': FatalIcon, |
|
|
|
// 'Error': ErrorIcon, |
|
|
|
// 'Warn': WarnIcon, |
|
|
|
// 'Notify': NotifyIcon |
|
|
|
// } |
|
|
|
// }) |
|
|
|
|
|
|
|
const toggleDetails = () => { |
|
|
|
isShow.value = !isShow.value |
|
|
@ -330,9 +320,10 @@ onUnmounted(() => { |
|
|
|
}) |
|
|
|
const handleErrorModal = (data: ErrorModalData) => { |
|
|
|
console.log('data---', data) |
|
|
|
title.value = data.title |
|
|
|
info.value = data.info |
|
|
|
type.value = data.type |
|
|
|
detailInfos.value = data.detailInfos || [] |
|
|
|
type.value = data.messageLevel |
|
|
|
detailInfo.value = data.detailInfo || '' |
|
|
|
stackInfo.value = data.stackInfo || '' |
|
|
|
showErrorStack.value = true |
|
|
|
} |
|
|
@ -340,7 +331,9 @@ const closeErrorStack = () => { |
|
|
|
showErrorStack.value = false |
|
|
|
} |
|
|
|
const showStackInfo = () => { |
|
|
|
eventBus.emit('show-stack-modal', stackInfo.value as unknown as null | undefined) |
|
|
|
|
|
|
|
eventBus.emit( |
|
|
|
'show-stack-modal', |
|
|
|
stackInfo.value as unknown as null | undefined, |
|
|
|
) |
|
|
|
} |
|
|
|
</script> |