feat: 接入我创建的挑战列表接口

This commit is contained in:
richarjiang
2026-04-13 09:51:38 +08:00
parent 2f74f260b7
commit ddf51919b0
5 changed files with 99 additions and 4 deletions

16
AGENTS.md Normal file
View File

@@ -0,0 +1,16 @@
# Repository Guidelines
## 项目结构与模块组织
本仓库是 `Cocos Creator 3.8.8` 小游戏项目。主入口在 `assets/main.ts`,主场景是 `assets/main.scene`。页面与弹窗组件集中在 `assets/prefabs/`,命名通常为 `PageXxx.ts``PassModal.ts`。公共逻辑位于 `assets/scripts/``core/` 放页面基类与视图管理,`utils/` 放 SDK、鉴权、存储、网络等工具`config/` 放接口配置,`types/` 放类型定义。静态资源在 `assets/resources/`,编辑器配置在 `settings/v2/packages/`,设计说明在 `docs/` 与根目录分析文档中。
## 构建、调试与开发命令
先运行 `npm install`,同步 `minigame-api-typings` 依赖。日常开发主要通过 Cocos Creator 编辑器完成:使用 3.8.8 打开仓库,点击 `Play` 预览,使用 `Project > Build``Cmd+B` 构建小游戏包。若编辑器已生成 `temp/tsconfig.cocos.json`,可执行 `npx tsc --noEmit` 做一次 TypeScript 静态检查。
## 代码风格与命名约定
项目使用 TypeScript当前代码统一为 4 空格缩进。组件类、页面类、管理器类使用 `PascalCase`,实例属性与私有方法使用 `camelCase` / `_camelCase`,管理器统一使用 `XxxManager` 后缀。新增页面、预制体、脚本请保持同名,例如 `PageLevel.prefab` 对应 `PageLevel.ts`。优先把复用逻辑放入 `assets/scripts/utils/``assets/scripts/core/`不要把业务代码散落到场景脚本中。Cocos 资源的 `.meta` 文件必须一并提交。
## 测试与验证
仓库当前未配置 Jest、Vitest 一类自动化测试。提交前至少完成三项验证1. 编辑器预览主流程可进入页面2. 目标平台构建成功3. 涉及微信能力时,在真机或开发者工具验证登录、分享、隐私授权等流程。若修改接口或体力/关卡逻辑,补充手动验证步骤到 PR 描述。
## 提交与 Pull Request 规范
Git 历史采用 Conventional Commits且摘要多为中文例如 `feat: 支持分享关卡通关上报``fix: 修复关卡排序``docs: 添加设计文档`。请继续使用 `feat:``fix:``perf:``docs:` 前缀首行聚焦单一变更。PR 需说明改动范围、影响页面或模块、验证方式;涉及 UI 请附截图或录屏,涉及微信环境差异请写明复现条件与平台。

View File

@@ -1,6 +1,9 @@
import { _decorator, Node, Button } from 'cc';
import { BaseView } from 'db://assets/scripts/core/BaseView';
import { ViewManager } from 'db://assets/scripts/core/ViewManager';
import { CreatedShareItem } from 'db://assets/scripts/types/ApiTypes';
import { ShareManager } from 'db://assets/scripts/utils/ShareManager';
import { ToastManager } from 'db://assets/scripts/utils/ToastManager';
const { ccclass, property } = _decorator;
@ccclass('PagePKData')
@@ -8,6 +11,9 @@ export class PagePKData extends BaseView {
@property({ type: Node, tooltip: '返回按钮' })
backBtn: Node | null = null;
private _createdShares: CreatedShareItem[] = [];
private _isLoading: boolean = false;
onViewLoad(): void {
if (this.backBtn) {
this.backBtn.on(Button.EventType.CLICK, this._onBackClick, this);
@@ -15,6 +21,7 @@ export class PagePKData extends BaseView {
}
onViewShow(): void {
void this._loadCreatedShares();
}
onViewHide(): void {
@@ -24,6 +31,26 @@ export class PagePKData extends BaseView {
ViewManager.instance.back();
}
private async _loadCreatedShares(): Promise<void> {
if (this._isLoading) {
return;
}
this._isLoading = true;
try {
const items = await ShareManager.instance.fetchCreatedShares();
if (!items) {
ToastManager.instance.show('获取挑战列表失败,请稍后重试');
return;
}
this._createdShares = items;
console.log('[PagePKData] 我创建的挑战列表:', this._createdShares);
} finally {
this._isLoading = false;
}
}
onViewDestroy(): void {
if (this.backBtn) {
this.backBtn.off(Button.EventType.CLICK, this._onBackClick, this);

View File

@@ -20,6 +20,7 @@ export const API_ENDPOINTS = {
GAME_CONFIGS: `${API_BASE}/game-configs`,
/** 分享相关 */
SHARE_CREATE: `${API_BASE}/share`,
SHARE_CREATED: `${API_BASE}/share/created`,
SHARE_PROGRESS: `${API_BASE}/share/progress`,
/** 用户信息 */
USER_INFO: `${API_BASE}/user/info`,

View File

@@ -97,9 +97,9 @@ export interface ShareLevelData {
level: number;
imageUrl: string;
answer: string;
hint1: string;
hint2: string;
hint3: string;
hint1: string | null;
hint2: string | null;
hint3: string | null;
sortOrder: number;
}
@@ -116,3 +116,19 @@ export interface ReportProgressData {
timeLimit: number | null;
withinTimeLimit: boolean;
}
/** 我创建的分享挑战条目 */
export interface CreatedShareItem {
id: string;
shareCode: string;
title: string;
levelCount: number;
participantCount: number;
userRank: number | null;
createdAt: string;
}
/** 我创建的分享挑战列表响应 */
export interface CreatedShareListData {
items: CreatedShareItem[];
}

View File

@@ -2,7 +2,15 @@ import { SpriteFrame, Texture2D, ImageAsset, assetManager } from 'cc';
import { HttpUtil } from './HttpUtil';
import { WxSDK } from './WxSDK';
import { API_ENDPOINTS, getShareJoinUrl, API_TIMEOUT } from '../config/ApiConfig';
import { ApiEnvelope, CreateShareData, JoinShareData, ReportProgressData, ShareLevelData } from '../types/ApiTypes';
import {
ApiEnvelope,
CreateShareData,
JoinShareData,
ReportProgressData,
ShareLevelData,
CreatedShareItem,
CreatedShareListData,
} from '../types/ApiTypes';
import { RuntimeLevelConfig } from '../types/LevelTypes';
/**
@@ -20,6 +28,7 @@ export class ShareManager {
private _shareTitle: string = '';
private _shareCode: string | null = null;
private _createdShares: CreatedShareItem[] = [];
/** 图片缓存URL -> SpriteFrame */
private _imageCache: Map<string, SpriteFrame> = new Map();
@@ -37,6 +46,10 @@ export class ShareManager {
return this._shareLevels !== null && this._shareLevels.length > 0;
}
get createdShares(): CreatedShareItem[] {
return [...this._createdShares];
}
async createShare(title: string, levelIds: string[]): Promise<string | null> {
try {
const response = await HttpUtil.post<ApiEnvelope<CreateShareData>>(
@@ -83,6 +96,7 @@ export class ShareManager {
clue2: level.hint2,
clue3: level.hint3,
answer: level.answer,
completed: false,
}));
// 预加载首关图片
@@ -102,6 +116,27 @@ export class ShareManager {
}
}
async fetchCreatedShares(): Promise<CreatedShareItem[] | null> {
try {
const response = await HttpUtil.get<ApiEnvelope<CreatedShareListData>>(
API_ENDPOINTS.SHARE_CREATED,
API_TIMEOUT.DEFAULT,
);
if (!response.success || !response.data) {
console.error('[ShareManager] 获取我创建的挑战列表失败:', response.message);
return null;
}
this._createdShares = response.data.items ?? [];
console.log(`[ShareManager] 获取我创建的挑战列表成功: ${this._createdShares.length}`);
return this.createdShares;
} catch (err) {
console.error('[ShareManager] 获取我创建的挑战列表异常:', err);
return null;
}
}
async ensureShareLevelReady(index: number): Promise<RuntimeLevelConfig | null> {
if (!this._shareLevels || index < 0 || index >= this._shareLevels.length) {
return null;