222 lines
7.5 KiB
TypeScript
222 lines
7.5 KiB
TypeScript
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<void> {
|
||
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();
|
||
}
|
||
}
|