import { _decorator, Node, Button, Sprite, Label, ScrollView, instantiate, UITransform, Vec2 } from 'cc'; import { BaseView } from 'db://assets/scripts/core/BaseView'; import { ViewManager } from 'db://assets/scripts/core/ViewManager'; import { LevelDataManager } from 'db://assets/scripts/utils/LevelDataManager'; const { ccclass, property } = _decorator; /** * 预览试卷页面 * 垂直滚动展示用户在 PageWriteLevels 中选中的 6 个关卡 * 每个关卡展示:封面图、提示1、提示2、答案 * * prefab 节点结构(已在编辑器中搭建): * PagePreviewLevels * ├── Bg * ├── IconBack ← backBtn (返回按钮) * ├── PKTitle ← 标题 "挑战" * ├── ScrollView ← scrollView * │ ├── scrollBar * │ └── view * │ └── content ← listContent * ├── ListTpl ← listTemplate (关卡模板) * │ ├── LevelCover ← Sprite 封面图 * │ ├── Tips1 ← Label 提示1 * │ ├── Tips2 ← Label 提示2 * │ └── Answer ← Label 答案 * └── BackButton ← backButton (底部返回按钮) */ /** 布局配置 — 垂直列表 */ 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 config = await LevelDataManager.instance.ensureLevelReady(levelIndex); if (!config || !item.isValid) return; // 填充封面图 const levelCover = item.getChildByName('LevelCover'); if (levelCover) { const sprite = levelCover.getComponent(Sprite); if (sprite && config.spriteFrame) { sprite.spriteFrame = config.spriteFrame; } } // 填充提示1 const tips1 = item.getChildByName('Tips1'); if (tips1) { const label = tips1.getComponent(Label); if (label) { label.string = `线索一:${config.clue1 || ''}`; } } // 填充提示2 const tips2 = item.getChildByName('Tips2'); if (tips2) { const label = tips2.getComponent(Label); if (label) { label.string = `线索二:${config.clue2 || ''}`; } } // 填充答案 const answer = item.getChildByName('Answer'); if (answer) { const label = answer.getComponent(Label); if (label) { label.string = `答案:${config.answer || ''}`; } } } // ─── 事件处理 ─────────────────────────────────────── private _onBackClick(): void { console.log('[PagePreviewLevels] 返回'); ViewManager.instance.back(); } }