<template>
  <!-- 全局错误兜底:子树渲染/生命周期抛错时显示降级 UI,防止白屏。
       用法:用 <ErrorBoundary> 包裹可能出错的子组件树(slot 透传)。 -->
  <template v-if="error">
    <div class="error-boundary">
      <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>
    </div>
  </template>
  <slot v-else />
</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>
.error-boundary {
  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>
