feat: add active domains serialization and UI integration

- Implemented `serialize_active_domains` function to gather and format active hidden domains from the game world.
- Updated `game_loop` to include active domains in the broadcast state.
- Enhanced `StatusBar` component to display active domains with a new `StatusWidget`, including dynamic labels and colors based on the number of active domains.
- Added localization support for hidden domain messages in both English and Chinese.
- Updated the world store to manage active domains state and synchronize with the backend.
This commit is contained in:
bridge
2026-02-01 12:20:27 +08:00
parent bc3ebc006c
commit d53d5885c0
8 changed files with 213 additions and 51 deletions

View File

@@ -0,0 +1,106 @@
<script setup lang="ts">
import { NPopover, NList, NListItem, NTag, NEmpty } from 'naive-ui'
import type { HiddenDomainInfo } from '../../types/core'
interface Props {
// 触发器显示
label: string
color?: string
// 弹窗内容
title?: string
items?: HiddenDomainInfo[] // 通用列表数据 (这里暂时专用于秘境,如果未来需要其他类型再泛型化)
emptyText?: string
// 模式: 'single' (天地灵机) 或 'list' (秘境)
mode?: 'single' | 'list'
}
const props = withDefaults(defineProps<Props>(), {
color: '#ccc',
items: () => [],
mode: 'list',
emptyText: '暂无数据'
})
// 发射点击事件(用于天地灵机的"更易天象"
const emit = defineEmits(['trigger-click'])
</script>
<template>
<div class="status-widget">
<span class="divider">|</span>
<n-popover trigger="click" placement="bottom" style="max-width: 350px;">
<template #trigger>
<span
class="widget-trigger"
:style="{ color: props.color }"
@click="emit('trigger-click')"
>
{{ props.label }}
</span>
</template>
<!-- 弹窗内容区 -->
<div class="widget-content">
<!-- 模式A: 单个详情 (复用天地灵机样式) -->
<slot name="single" v-if="mode === 'single'"></slot>
<!-- 模式B: 列表展示 (用于秘境) -->
<div v-else-if="mode === 'list'" class="list-container">
<div class="list-header" v-if="title">{{ title }}</div>
<n-list v-if="items.length > 0" hoverable clickable>
<n-list-item v-for="item in items" :key="item.id">
<div class="domain-item">
<div class="d-header">
<span class="d-name">{{ item.name }}</span>
<n-tag size="small" :bordered="false" type="warning" class="d-tag">
{{ item.max_realm }}
</n-tag>
</div>
<div class="d-desc">{{ item.desc }}</div>
<div class="d-stats">
<span>💀 {{ (item.danger_prob * 100).toFixed(0) }}%</span>
<span>🎁 {{ (item.drop_prob * 100).toFixed(0) }}%</span>
</div>
</div>
</n-list-item>
</n-list>
<n-empty v-else :description="emptyText" class="empty-state" />
</div>
</div>
</n-popover>
</div>
</template>
<style scoped>
.widget-trigger {
cursor: pointer;
font-weight: bold;
transition: opacity 0.2s;
}
.widget-trigger:hover { opacity: 0.8; }
.divider { color: #444; margin-right: 10px; }
.list-header {
font-weight: bold;
padding: 8px 12px;
border-bottom: 1px solid #333;
margin-bottom: 4px;
font-size: 14px;
}
.domain-item { padding: 4px 0; }
.d-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 6px; }
.d-name { font-weight: bold; color: #fadb14; font-size: 14px; }
.d-tag { font-size: 10px; height: 18px; line-height: 18px; }
.d-desc { font-size: 12px; color: #aaa; margin-bottom: 8px; line-height: 1.4; }
.d-stats { display: flex; gap: 12px; font-size: 12px; color: #888; }
.empty-state { padding: 20px; }
/* Naive UI List Override */
:deep(.n-list-item) {
padding: 8px 12px !important;
}
</style>