import { _decorator, Node, Button, Label, ScrollView, instantiate, UITransform } from 'cc'; import { BaseView } from 'db://assets/scripts/core/BaseView'; import { ViewManager } from 'db://assets/scripts/core/ViewManager'; import { CompletedLevelsManager } from 'db://assets/scripts/utils/CompletedLevelsManager'; import { PreviewLevelItem } from './PreviewLevelItem'; const { ccclass, property } = _decorator; /** * 预览试卷页面 * 垂直滚动展示用户在 PageWriteLevels 中选中的 6 个关卡 * 每个关卡展示:封面图、线索1、线索2、线索3、答案 * * 节点结构(仅 ScrollView 侧需要固定): * PagePreviewLevels * ├── ScrollView / view / content ← listContent 容器 * └── ListTpl ← listTemplate 模板根节点 * (挂 PreviewLevelItem 组件,字段由编辑器拖拽绑定) * * item 内部节点层级/命名对本文件透明:所有引用都来自 PreviewLevelItem 的 @property。 */ /** 布局配置 — 垂直列表 */ const LAYOUT = { /** 关卡项高度(与 ListTpl UITransform 高度一致) */ ITEM_HEIGHT: 300, /** 关卡项之间的垂直间距 */ SPACING_Y: 30, /** 列表顶部内边距 */ PADDING_TOP: 20, }; @ccclass('PagePreviewLevels') export class PagePreviewLevels extends BaseView { @property({ type: Node, tooltip: '返回按钮(左上角)' }) backBtn: Node | null = null; @property({ type: Node, tooltip: 'ScrollView 节点' }) scrollView: Node | null = null; @property({ type: Node, tooltip: '列表 content 节点' }) listContent: Node | null = null; @property({ type: Node, tooltip: '关卡模板节点' }) listTemplate: Node | null = null; @property({ type: Node, tooltip: '底部返回按钮' }) backButton: Node | null = null; @property({ type: Node, tooltip: '标题Label节点' }) pkTitle: Node | null = null; /** 已创建的 item 节点列表 */ private _itemNodes: Node[] = []; // ─── 生命周期 ─────────────────────────────────────── onViewLoad(): void { console.log('[PagePreviewLevels] onViewLoad'); this._initButtons(); this._initScrollView(); } onViewShow(): void { console.log('[PagePreviewLevels] onViewShow'); this._buildList(); } onViewHide(): void { console.log('[PagePreviewLevels] onViewHide'); } onViewDestroy(): void { console.log('[PagePreviewLevels] onViewDestroy'); this._offButtons(); this._clearList(); } // ─── 初始化 ───────────────────────────────────────── private _initButtons(): void { if (this.backBtn) { this.backBtn.on(Button.EventType.CLICK, this._onBackClick, this); } if (this.backButton) { this.backButton.on(Button.EventType.CLICK, this._onBackClick, this); } } private _offButtons(): void { if (this.backBtn) { this.backBtn.off(Button.EventType.CLICK, this._onBackClick, this); } if (this.backButton) { this.backButton.off(Button.EventType.CLICK, this._onBackClick, this); } } private _initScrollView(): void { if (!this.listContent) return; const contentTransform = this.listContent.getComponent(UITransform); if (contentTransform) { contentTransform.setAnchorPoint(0.5, 1); } } // ─── 列表构建 ─────────────────────────────────────── private _buildList(): void { this._clearList(); const params = this.getParams(); if (!params || !params.selectedIndices || params.selectedIndices.length === 0) { console.warn('[PagePreviewLevels] 未传入选中关卡数据'); return; } // 显示用户输入的标题 if (this.pkTitle) { const label = this.pkTitle.getComponent(Label); if (label) { label.string = params.shareTitle || '挑战'; } } const indices: number[] = params.selectedIndices; console.log('[PagePreviewLevels] 选中关卡索引:', indices); // 更新 content 高度 this._updateContentSize(indices.length); // 创建每个关卡 item for (let i = 0; i < indices.length; i++) { const levelIndex = indices[i]; const itemNode = this._createItem(i); if (itemNode) { this.listContent!.addChild(itemNode); this._itemNodes.push(itemNode); this._loadLevelData(itemNode, levelIndex, i); } } // 滚动到顶部 const scrollComp = this.scrollView?.getComponent(ScrollView); if (scrollComp) { scrollComp.scrollToTop(0); } } private _clearList(): void { for (const node of this._itemNodes) { if (node && node.isValid) { node.destroy(); } } this._itemNodes = []; } private _updateContentSize(count: number): void { if (!this.listContent) return; const contentTransform = this.listContent.getComponent(UITransform); if (!contentTransform) return; const totalHeight = LAYOUT.PADDING_TOP + count * LAYOUT.ITEM_HEIGHT + (count > 0 ? (count - 1) * LAYOUT.SPACING_Y : 0) + LAYOUT.PADDING_TOP; contentTransform.setContentSize(contentTransform.contentSize.width, totalHeight); } /** * 创建单个关卡展示项 * content anchor=(0.5, 1),y 轴负向下 */ private _createItem(displayIndex: number): Node | null { if (!this.listTemplate) return null; const item = instantiate(this.listTemplate); item.active = true; item.name = `preview_item_${displayIndex}`; // 垂直居中排列:x=0(水平居中于 content),y 负向下 const y = -(LAYOUT.PADDING_TOP + displayIndex * (LAYOUT.ITEM_HEIGHT + LAYOUT.SPACING_Y) + LAYOUT.ITEM_HEIGHT / 2); item.setPosition(0, y, 0); return item; } /** * 异步加载关卡数据并填充到 item 节点 */ private async _loadLevelData(item: Node, levelIndex: number, displayIndex: number): Promise { const level = CompletedLevelsManager.instance.getByIndex(levelIndex); if (!level || !item.isValid) return; const view = item.getComponent(PreviewLevelItem); if (!view) { console.warn('[PagePreviewLevels] listTemplate 缺少 PreviewLevelItem 组件'); return; } view.setTexts({ answer: level.answer || '', hint1: level.hint1 || '', hint2: level.hint2 || '', hint3: level.hint3 || '', }); // 异步加载封面图(通常已由 WriteLevels 预热到缓存) const spriteFrame = await CompletedLevelsManager.instance.loadImage(level.image1Url); if (!spriteFrame || !item.isValid) return; view.setCover(spriteFrame); } // ─── 事件处理 ─────────────────────────────────────── private _onBackClick(): void { console.log('[PagePreviewLevels] 返回'); ViewManager.instance.back(); } }