<template>
  <!-- 全局错误兜底:子树渲染/生命周期抛错时显示降级 UI,防止白屏。
       用法:用 <ErrorBoundary> 包裹可能出错的子组件树(slot 透传)。
       根节点固定为单个 <div>(display:contents 布局透明),避免被 <Transition> 包裹时
       渲染非元素根节点(slot 透传)导致过渡动画警告/卡死(App.vue <transition name="page">)。 -->
  <div class="error-boundary-root" :class="{ 'is-error': !!error }">
    <template v-if="error">
      <div class="error-boundary-icon">⚠</div>
      <div class="error-boundary-title">{{ $t('error.title') }}</div>
      <div class="error-boundary-desc">{{ $t('error.desc') }}</div>
      <button class="error-boundary-retry" @click="reset">{{ $t('error.retry') }}</button>
    </template>
    <slot v-else />
  </div>
</template>

<script setup lang="ts">
import { ref, onErrorCaptured } from 'vue'

// errorCaptured 捕获子组件树抛出的错误(渲染/生命周期),返回 false 阻止继续向上冒泡
const error = ref<unknown>(null)

onErrorCaptured((err) => {
  error.value = err
  console.error('[ErrorBoundary] 子树渲染错误:', err)
  return false
})

// 重试:重置 errorState,slot 重新挂载子树
function reset() {
  error.value = null
}
</script>

<style scoped>
/* 根节点:正常态 display:contents 布局透明(slot 内容直接参与父级布局,不破坏页面 flex);
   error 态切回 flex 容器承载降级 UI。固定为元素根节点,避免 <Transition> 非元素根警告。 */
.error-boundary-root {
  display: contents;
}
.error-boundary-root.is-error {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 32px 16px;
  color: var(--df-text-secondary, #888);
  text-align: center;
}
.error-boundary-icon {
  font-size: 28px;
  color: var(--df-danger, #e5484d);
  line-height: 1;
}
.error-boundary-title {
  font-size: 14px;
  font-weight: 500;
  color: var(--df-text, #333);
}
.error-boundary-desc {
  font-size: 12px;
  line-height: 1.5;
  max-width: 360px;
}
.error-boundary-retry {
  margin-top: 8px;
  padding: 5px 16px;
  font-size: 12px;
  font-weight: 500;
  font-family: var(--df-font-sans);
  background: var(--df-accent, #7b6ff0);
  color: #fff;
  border: none;
  border-radius: var(--df-radius-sm, 4px);
  cursor: pointer;
  transition: filter 0.15s;
}
.error-boundary-retry:hover {
  filter: brightness(1.1);
}
</style>
