From ddf51919b02f8ec2349e6d8177c94068e3689dea Mon Sep 17 00:00:00 2001 From: richarjiang Date: Mon, 13 Apr 2026 09:51:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8E=A5=E5=85=A5=E6=88=91=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E7=9A=84=E6=8C=91=E6=88=98=E5=88=97=E8=A1=A8=E6=8E=A5?= =?UTF-8?q?=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- AGENTS.md | 16 ++++++++++++ assets/prefabs/PagePKData.ts | 27 ++++++++++++++++++++ assets/scripts/config/ApiConfig.ts | 1 + assets/scripts/types/ApiTypes.ts | 22 ++++++++++++++--- assets/scripts/utils/ShareManager.ts | 37 +++++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 4 deletions(-) create mode 100644 AGENTS.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..9cc3c83 --- /dev/null +++ b/AGENTS.md @@ -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 请附截图或录屏,涉及微信环境差异请写明复现条件与平台。 diff --git a/assets/prefabs/PagePKData.ts b/assets/prefabs/PagePKData.ts index 809f960..2daeb68 100644 --- a/assets/prefabs/PagePKData.ts +++ b/assets/prefabs/PagePKData.ts @@ -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 { + 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); diff --git a/assets/scripts/config/ApiConfig.ts b/assets/scripts/config/ApiConfig.ts index 5d9f9a2..96bc9b8 100644 --- a/assets/scripts/config/ApiConfig.ts +++ b/assets/scripts/config/ApiConfig.ts @@ -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`, diff --git a/assets/scripts/types/ApiTypes.ts b/assets/scripts/types/ApiTypes.ts index f1cedb9..a06e658 100644 --- a/assets/scripts/types/ApiTypes.ts +++ b/assets/scripts/types/ApiTypes.ts @@ -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[]; +} diff --git a/assets/scripts/utils/ShareManager.ts b/assets/scripts/utils/ShareManager.ts index 60586dd..5a7ed41 100644 --- a/assets/scripts/utils/ShareManager.ts +++ b/assets/scripts/utils/ShareManager.ts @@ -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 = 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 { try { const response = await HttpUtil.post>( @@ -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 { + try { + const response = await HttpUtil.get>( + 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 { if (!this._shareLevels || index < 0 || index >= this._shareLevels.length) { return null;