收藏功能&排行榜功能
This commit is contained in:
@@ -76,6 +76,7 @@ export class PreloadUI extends Component {
|
||||
private progressNode: Node;
|
||||
|
||||
private loginLabel: Label;
|
||||
private guestLoginLabel: Label;
|
||||
private desc: Label;
|
||||
|
||||
// 视频播放状态跟踪
|
||||
@@ -93,12 +94,14 @@ export class PreloadUI extends Component {
|
||||
this.loginBtn.active = false;
|
||||
|
||||
this.guestLoginBtn = this._nodeTab.guestBtnLogin;
|
||||
this.loginBtn.active = false;
|
||||
this.guestLoginBtn.active = false;
|
||||
|
||||
this.appVersion.string = `app version: ${prodConfig.appVersion}`;
|
||||
this.VideoPlayer = this._nodeTab.VideoPlayer.getComponent(VideoPlayer);
|
||||
|
||||
this.loginLabel = this._nodeTab.loginLabel.getComponent(Label);
|
||||
this.guestLoginLabel = this._nodeTab.guestloginLabel.getComponent(Label);
|
||||
|
||||
this.desc = this._nodeTab.Desc.getComponent(Label);
|
||||
|
||||
// 设置视频为静音,提高移动端自动播放成功率
|
||||
@@ -140,14 +143,19 @@ export class PreloadUI extends Component {
|
||||
switch (LanguageUtils.CurrentLanguage) {
|
||||
case LanguageType.CN:
|
||||
this.loginLabel.string = "登录";
|
||||
this.guestLoginLabel.string = "游客登陆";
|
||||
this.desc.string = "释放你的所有欲望";
|
||||
break;
|
||||
case LanguageType.EN:
|
||||
this.loginLabel.string = "LOG IN";
|
||||
this.guestLoginLabel.string = "GUEST LOG IN";
|
||||
|
||||
this.desc.string = "Unleash All Your Desires";
|
||||
break;
|
||||
case LanguageType.HI:
|
||||
this.loginLabel.string = "लॉग इन";
|
||||
this.guestLoginLabel.string = "ज़ियारती लॉगिन";
|
||||
|
||||
this.desc.string = "अपनी सारी इच्छाओं को मुक्त करें";
|
||||
break;
|
||||
default:
|
||||
@@ -310,7 +318,7 @@ export class PreloadUI extends Component {
|
||||
this.preloadFinish = true;
|
||||
|
||||
//TODO:获取当前有无账号信息
|
||||
this.loginBtn.active = true;
|
||||
this.loginBtn.active = false;
|
||||
this.guestLoginBtn.active = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ export class MainController {
|
||||
private isDailyRecommendPreloaded: boolean = false;
|
||||
private isRecommendListPreloaded: boolean = false;
|
||||
private isShopListPreloaded: boolean = false;
|
||||
private isBookmarkListPreloaded: boolean = false;
|
||||
|
||||
/**
|
||||
* 初始化并预加载数据
|
||||
@@ -41,6 +42,7 @@ export class MainController {
|
||||
//this.preloadDailyRecommend(),
|
||||
this.preloadRecommendList(true, 1, 10),
|
||||
this.preloadShopList(),
|
||||
this.preloadBookmarkList(),
|
||||
]);
|
||||
|
||||
logger.log("[MainController] 数据预加载完成");
|
||||
@@ -180,6 +182,37 @@ export class MainController {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 预加载收藏列表
|
||||
*/
|
||||
async preloadBookmarkList(): Promise<boolean> {
|
||||
if (this.isBookmarkListPreloaded) {
|
||||
logger.log("[MainController] 收藏列表数据已预加载");
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
logger.log("[MainController] 开始预加载收藏列表数据");
|
||||
const reqData = {};
|
||||
const res = await GirlService.I.reqBookmarkGirlList(reqData);
|
||||
|
||||
if (res && res.code === proto.cs.EnmRetCode.SUCCESS) {
|
||||
// 保存数据到DataManager
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
girlData.setBookmarkGirlList(res.data);
|
||||
this.isBookmarkListPreloaded = true;
|
||||
logger.log("[MainController] 收藏列表数据预加载成功");
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("[MainController] 收藏列表数据预加载失败:", res);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("[MainController] 收藏列表数据预加载异常:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查数据是否已预加载
|
||||
*/
|
||||
@@ -188,7 +221,8 @@ export class MainController {
|
||||
this.isThemeDataPreloaded &&
|
||||
this.isDailyRecommendPreloaded &&
|
||||
this.isRecommendListPreloaded &&
|
||||
this.isShopListPreloaded
|
||||
this.isShopListPreloaded &&
|
||||
this.isBookmarkListPreloaded
|
||||
);
|
||||
}
|
||||
|
||||
@@ -201,6 +235,7 @@ export class MainController {
|
||||
dailyRecommend: this.isDailyRecommendPreloaded,
|
||||
recommendList: this.isRecommendListPreloaded,
|
||||
shopList: this.isShopListPreloaded,
|
||||
bookmarkList: this.isBookmarkListPreloaded,
|
||||
allComplete: this.isDataPreloaded(),
|
||||
};
|
||||
}
|
||||
@@ -213,6 +248,7 @@ export class MainController {
|
||||
this.isDailyRecommendPreloaded = false;
|
||||
this.isRecommendListPreloaded = false;
|
||||
this.isShopListPreloaded = false;
|
||||
this.isBookmarkListPreloaded = false;
|
||||
logger.log("[MainController] 预加载状态已重置");
|
||||
}
|
||||
|
||||
@@ -296,4 +332,30 @@ export class MainController {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 强制刷新收藏列表数据
|
||||
*/
|
||||
async refreshBookmarkList(): Promise<boolean> {
|
||||
logger.log("[MainController] 开始强制刷新收藏列表数据");
|
||||
try {
|
||||
const reqData = {};
|
||||
const res = await GirlService.I.reqBookmarkGirlList(reqData);
|
||||
|
||||
if (res && res.code === proto.cs.EnmRetCode.SUCCESS) {
|
||||
// 保存数据到DataManager
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
girlData.setBookmarkGirlList(res.data);
|
||||
this.isBookmarkListPreloaded = true;
|
||||
logger.log("[MainController] 收藏列表数据刷新成功");
|
||||
return true;
|
||||
} else {
|
||||
logger.warn("[MainController] 收藏列表数据刷新失败:", res);
|
||||
return false;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("[MainController] 收藏列表数据刷新异常:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1533,7 +1533,35 @@ export class GirlData extends BaseData {
|
||||
}
|
||||
// 返回结果
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/** 获取按收藏时间排序的收藏技师ID(最新的在前) */
|
||||
public getAllBookmarkGirlIdsSortedByTime(): number[] {
|
||||
const result: Array<{
|
||||
girlId: number;
|
||||
bookmarkTime: number;
|
||||
category: string;
|
||||
}> = [];
|
||||
const seen = new Set<number>(); // 用来去重
|
||||
const cats = this.ensureCategories();
|
||||
|
||||
// 收集所有收藏的女孩ID及其收藏时间
|
||||
for (const [categoryId, bucket] of cats.entries()) {
|
||||
for (const girlId of bucket.girlIds) {
|
||||
if (this.isBookmarkGirl(categoryId, girlId) && !seen.has(girlId)) {
|
||||
seen.add(girlId); // 标记已加入
|
||||
const bookmarkTime = this.getBookmarkTime(categoryId, girlId);
|
||||
result.push({ girlId, bookmarkTime, category: categoryId });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 按收藏时间降序排序(最新的在前)
|
||||
result.sort((a, b) => b.bookmarkTime - a.bookmarkTime);
|
||||
|
||||
// 返回排序后的ID数组
|
||||
return result.map((item) => item.girlId);
|
||||
}
|
||||
|
||||
/** --------------------------------------------------------- 技师的其他数据 --------------------------------------------------------- */
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import {
|
||||
tween,
|
||||
view,
|
||||
screen,
|
||||
Button,
|
||||
} from "cc";
|
||||
import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView";
|
||||
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
|
||||
@@ -30,6 +31,7 @@ import { GirlService } from "db://assets/Scripts/chat18x/network/services/GirlSe
|
||||
import proto from "db://assets/Scripts/proto/proto.pb.js";
|
||||
import { SceneBgVideoLayer } from "../../../Sub/UI/SceneBgVideoLayer";
|
||||
import { logger } from "db://assets/Scripts/Main/Common/Logger";
|
||||
import { GButton } from "../../../Main/Common/GButton";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -87,10 +89,15 @@ export class GirlDetailPanel extends li_BaseView {
|
||||
ImgVidSelect: Node;
|
||||
ImgVidFakePos: Node;
|
||||
|
||||
AddFavoriteBtn: Node;
|
||||
|
||||
// 跟随控制相关属性
|
||||
private isFollowing: boolean = true; // 当前是否处于跟随状态
|
||||
private lastFakePosPosition: Vec3 = new Vec3(); // 记录上次的位置
|
||||
|
||||
// 收藏请求状态标记,防止重复点击
|
||||
private isRequestingBookmark: boolean = false;
|
||||
|
||||
protected onEnable(): void {
|
||||
this.refresh();
|
||||
}
|
||||
@@ -101,6 +108,7 @@ export class GirlDetailPanel extends li_BaseView {
|
||||
|
||||
this.ImgVidSelect = this._nodeTab.ImgVidSelect;
|
||||
this.ImgVidFakePos = this._nodeTab.ImgVidFakePos;
|
||||
this.AddFavoriteBtn = this._nodeTab.AddFavoriteBtn;
|
||||
|
||||
// 初始化位置跟随
|
||||
if (this.ImgVidFakePos) {
|
||||
@@ -111,18 +119,15 @@ export class GirlDetailPanel extends li_BaseView {
|
||||
|
||||
Utils.addInnerEL(InnerMsgCode.LanguageChange, this, () => {
|
||||
this.girlName.string = LanguageUtils.getText(this.nameKey);
|
||||
let desc = "";
|
||||
const tags = LanguageUtils.getText(this.tagKey);
|
||||
// for (let i = 0; i < tags.length; i++) {
|
||||
// if (i != 0) desc += "\n";
|
||||
// desc += tags[i];
|
||||
// }
|
||||
this.tags.string = tags;
|
||||
this.desc.string = LanguageUtils.getText(this.descKey);
|
||||
});
|
||||
|
||||
this.imgToggle.callback = this.bindImgToggle.bind(this);
|
||||
this.videoToggle.callback = this.bindVideoToggle.bind(this);
|
||||
|
||||
GButton.BandClick(this.AddFavoriteBtn, this.OnClickAddFavoriteBtn, this);
|
||||
}
|
||||
|
||||
setVideEnable(enable: boolean) {
|
||||
@@ -263,9 +268,44 @@ export class GirlDetailPanel extends li_BaseView {
|
||||
this.id,
|
||||
resIds
|
||||
);
|
||||
|
||||
// 刷新收藏按钮状态
|
||||
this.refreshAddFavoriteBtn();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新收藏按钮状态
|
||||
*/
|
||||
refreshAddFavoriteBtn() {
|
||||
if (!this.AddFavoriteBtn) {
|
||||
return;
|
||||
}
|
||||
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
const isBookmarked = girlData.isBookmarkGirl(
|
||||
this.category.toString(),
|
||||
this.id
|
||||
);
|
||||
|
||||
// 查找收藏按钮的状态节点(通常命名为 "selected" 或 "checked")
|
||||
// 如果有这样的子节点,根据收藏状态显示/隐藏
|
||||
const selectedNode = this.AddFavoriteBtn.getChildByName("selected");
|
||||
if (selectedNode) {
|
||||
selectedNode.active = isBookmarked;
|
||||
}
|
||||
|
||||
// 如果有未选中状态的节点
|
||||
const unselectedNode = this.AddFavoriteBtn.getChildByName("unselected");
|
||||
if (unselectedNode) {
|
||||
unselectedNode.active = !isBookmarked;
|
||||
}
|
||||
|
||||
logger.log(
|
||||
`[GirlDetailPanel] 收藏按钮状态已刷新: girlId=${this.id}, isBookmarked=${isBookmarked}`
|
||||
);
|
||||
}
|
||||
|
||||
bindImgToggle() {
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
let resIds = girlData.getAllDisplayGrilPhotoId(
|
||||
@@ -348,6 +388,78 @@ export class GirlDetailPanel extends li_BaseView {
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击收藏按钮
|
||||
*/
|
||||
async OnClickAddFavoriteBtn() {
|
||||
// 防止重复点击
|
||||
if (this.isRequestingBookmark) {
|
||||
logger.warn("[GirlDetailPanel] 收藏请求进行中,请稍候");
|
||||
return;
|
||||
}
|
||||
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
const isBookmarked = girlData.isBookmarkGirl(
|
||||
this.category.toString(),
|
||||
this.id
|
||||
);
|
||||
|
||||
logger.log(
|
||||
`[GirlDetailPanel] 点击收藏按钮: girlId=${this.id}, 当前状态=${
|
||||
isBookmarked ? "已收藏" : "未收藏"
|
||||
}`
|
||||
);
|
||||
|
||||
try {
|
||||
this.isRequestingBookmark = true;
|
||||
|
||||
// 调用收藏接口
|
||||
const reqData = {
|
||||
girlId: this.id,
|
||||
cancel: isBookmarked, // true=取消收藏, false=添加收藏
|
||||
};
|
||||
const res = await GirlService.I.reqBookmarkGirl(reqData);
|
||||
|
||||
if (res && res.code === proto.cs.EnmRetCode.SUCCESS) {
|
||||
// 更新本地收藏时间
|
||||
if (isBookmarked) {
|
||||
// 取消收藏:设置为0
|
||||
girlData.setBookmarkTime(this.category.toString(), this.id, 0);
|
||||
logger.log(`[GirlDetailPanel] 取消收藏成功: girlId=${this.id}`);
|
||||
} else {
|
||||
// 添加收藏:设置为当前时间戳(秒)
|
||||
const currentTimestamp = Math.floor(Date.now() / 1000);
|
||||
girlData.setBookmarkTime(
|
||||
this.category.toString(),
|
||||
this.id,
|
||||
currentTimestamp
|
||||
);
|
||||
logger.log(
|
||||
`[GirlDetailPanel] 添加收藏成功: girlId=${this.id}, timestamp=${currentTimestamp}`
|
||||
);
|
||||
}
|
||||
|
||||
// 刷新按钮状态
|
||||
this.refreshAddFavoriteBtn();
|
||||
|
||||
// TODO: 可以在这里添加UI提示,比如显示Toast
|
||||
// Utils.showToast(isBookmarked ? "已取消收藏" : "收藏成功");
|
||||
} else {
|
||||
logger.warn(
|
||||
`[GirlDetailPanel] 收藏操作失败: girlId=${this.id}, code=${res?.code}`
|
||||
);
|
||||
// TODO: 显示错误提示
|
||||
// Utils.showToast("操作失败,请稍后重试");
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error(`[GirlDetailPanel] 收藏操作异常: girlId=${this.id}`, error);
|
||||
// TODO: 显示错误提示
|
||||
// Utils.showToast("网络错误,请稍后重试");
|
||||
} finally {
|
||||
this.isRequestingBookmark = false;
|
||||
}
|
||||
}
|
||||
|
||||
returnBtn() {
|
||||
// GirlDetailPanel现在是subpanel,关闭自己即可
|
||||
NavigationManager.Instance.closeSubPanel(this);
|
||||
|
||||
@@ -162,6 +162,14 @@ export class PersonalPanel extends li_BaseView {
|
||||
}
|
||||
|
||||
private openFavorites() {
|
||||
TipsPanel.show(LanguageUtils.getText("rankpanel.comingsoon"));
|
||||
NavigationManager.Instance.openSubPanel(
|
||||
"GirlCollectionPanel",
|
||||
this.node,
|
||||
null,
|
||||
{
|
||||
openAnimation: PageTransitionType.SLIDE_LEFT,
|
||||
closeAnimation: PageTransitionType.SLIDE_RIGHT,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,374 @@
|
||||
import { _decorator, Component, Node } from "cc";
|
||||
import { _decorator, Component, Node, ScrollView } from "cc";
|
||||
import li_BaseView from "../../../Main/Common/li_BaseView";
|
||||
import { GirlService } from "../../network/services/GirlService";
|
||||
import proto from "db://assets/Scripts/proto/proto.pb.js";
|
||||
import { DataManager, DataId } from "../../data/DataManager";
|
||||
import { GirlData } from "../../data/GirlData";
|
||||
import Utils from "../../../Main/Common/Utils";
|
||||
import { GButton } from "../../../Main/Common/GButton";
|
||||
import HXZ_ScrollViewList from "../../../Main/Common/ScrollViewList";
|
||||
import { RankItem } from "../../uiitems/RankItem";
|
||||
import { logger } from "../../../Main/Common/Logger";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass("RankPanel")
|
||||
export class RankPanel extends li_BaseView {}
|
||||
export class RankPanel extends li_BaseView {
|
||||
private _nodeTab: any = {};
|
||||
|
||||
// 虚拟列表组件
|
||||
private scrollViewList: HXZ_ScrollViewList = null;
|
||||
|
||||
// 排行榜项模板
|
||||
private rankItemInst: RankItem;
|
||||
|
||||
// 当前选中的榜单类型
|
||||
private currentRankType: proto.cs.EnmRankType = proto.cs.EnmRankType.ERT_Day;
|
||||
|
||||
// 分页状态
|
||||
private currentPage: number = 1;
|
||||
private pageLimit: number = 20;
|
||||
private isLoadingMore: boolean = false;
|
||||
private hasMoreData: boolean = true;
|
||||
|
||||
// 记录上次触发加载的 ID,避免重复触发
|
||||
private lastLoadTriggerId: number = -1;
|
||||
|
||||
// 榜单切换按钮
|
||||
private dayRankBtn: Node;
|
||||
private weekRankBtn: Node;
|
||||
private monthRankBtn: Node;
|
||||
|
||||
// 滚动视图
|
||||
private rankScroll: Node;
|
||||
private rankScrollView: ScrollView = null;
|
||||
|
||||
onLoadCT() {
|
||||
Utils.parseNode(this.node, this._nodeTab);
|
||||
|
||||
// 获取榜单切换按钮
|
||||
this.dayRankBtn = this._nodeTab.dayRankBtn;
|
||||
this.weekRankBtn = this._nodeTab.weekRankBtn;
|
||||
this.monthRankBtn = this._nodeTab.monthRankBtn;
|
||||
|
||||
// 获取滚动视图
|
||||
this.rankScroll = this._nodeTab.rankScroll;
|
||||
|
||||
// 获取推荐列表的 ScrollView 组件
|
||||
if (this.rankScroll) {
|
||||
this.rankScrollView = this.rankScroll.getComponent(ScrollView);
|
||||
|
||||
// 获取虚拟列表组件(需要在场景中手动添加 HXZ_ScrollViewList 组件)
|
||||
this.scrollViewList = this.rankScroll.getComponent(HXZ_ScrollViewList);
|
||||
if (!this.scrollViewList) {
|
||||
logger.warn(
|
||||
"[RankPanel] rankScroll 节点缺少 HXZ_ScrollViewList 组件,请在编辑器中添加"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// 获取排行榜项模板
|
||||
this.rankItemInst = this._nodeTab.RankItem?.getComponent(RankItem);
|
||||
if (this.rankItemInst) {
|
||||
this.rankItemInst.node.active = false;
|
||||
}
|
||||
|
||||
this.registerListener();
|
||||
}
|
||||
|
||||
openUIDataCT(data: any): void {
|
||||
// 默认打开日榜
|
||||
this.switchRankType(proto.cs.EnmRankType.ERT_Day);
|
||||
}
|
||||
|
||||
/**
|
||||
* 注册事件监听
|
||||
*/
|
||||
private registerListener() {
|
||||
// 榜单切换按钮
|
||||
if (this.dayRankBtn) {
|
||||
GButton.BandClick(this.dayRankBtn, this.openDayRank, this);
|
||||
}
|
||||
if (this.weekRankBtn) {
|
||||
GButton.BandClick(this.weekRankBtn, this.openWeekRank, this);
|
||||
}
|
||||
if (this.monthRankBtn) {
|
||||
GButton.BandClick(this.monthRankBtn, this.openMonthRank, this);
|
||||
}
|
||||
|
||||
// 注册虚拟列表滚动事件(延迟到下一帧,确保虚拟列表已初始化)
|
||||
this.scheduleOnce(() => {
|
||||
if (this.scrollViewList) {
|
||||
// 监听 scrolling 事件,通过滚动偏移量检测是否接近底部
|
||||
this.scrollViewList.node.on("scrolling", this.onRankScrolling, this);
|
||||
logger.log("[RankPanel] 虚拟列表滚动事件已绑定");
|
||||
} else {
|
||||
logger.warn("[RankPanel] 无法绑定滚动事件,scrollViewList 未初始化");
|
||||
}
|
||||
}, 0.1);
|
||||
}
|
||||
|
||||
/**
|
||||
* 虚拟列表渲染回调
|
||||
* 该方法需要在 Cocos Creator 编辑器中配置到 HXZ_ScrollViewList 的 renderEvent
|
||||
* @param itemNode 虚拟列表传入的节点
|
||||
* @param index 数据索引
|
||||
*/
|
||||
onRenderRankItem(itemNode: Node, index: number): void {
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
const girlIDs = girlData.getRankIdsByType(this.currentRankType);
|
||||
|
||||
if (index < girlIDs.length) {
|
||||
const girlId = girlIDs[index];
|
||||
const item = itemNode.getComponent(RankItem);
|
||||
|
||||
if (!item) {
|
||||
logger.error(`[RankPanel] 节点缺少 RankItem 组件,index: ${index}`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 确保节点已初始化
|
||||
if (!item._initialized) {
|
||||
item.init();
|
||||
}
|
||||
|
||||
// 刷新数据(index + 1 作为排名序号)
|
||||
item.refresh(girlId, index + 1);
|
||||
itemNode.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开日榜
|
||||
*/
|
||||
openDayRank() {
|
||||
this.switchRankType(proto.cs.EnmRankType.ERT_Day);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开周榜
|
||||
*/
|
||||
openWeekRank() {
|
||||
this.switchRankType(proto.cs.EnmRankType.ERT_Week);
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开月榜
|
||||
*/
|
||||
openMonthRank() {
|
||||
this.switchRankType(proto.cs.EnmRankType.ERT_Month);
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换榜单类型
|
||||
* @param rankType 榜单类型
|
||||
*/
|
||||
private async switchRankType(rankType: proto.cs.EnmRankType) {
|
||||
// 如果是同一个榜单,不重复请求
|
||||
if (
|
||||
this.currentRankType === rankType &&
|
||||
this.scrollViewList?.numItems > 0
|
||||
) {
|
||||
logger.log(`[RankPanel] 当前已是该榜单类型: ${rankType}`);
|
||||
this.updateTabState(rankType);
|
||||
return;
|
||||
}
|
||||
|
||||
this.currentRankType = rankType;
|
||||
|
||||
// 重置分页状态
|
||||
this.currentPage = 1;
|
||||
this.hasMoreData = true;
|
||||
this.isLoadingMore = false;
|
||||
this.lastLoadTriggerId = -1;
|
||||
|
||||
// 请求榜单数据
|
||||
await this.reqGirlRankData(rankType);
|
||||
|
||||
// 更新虚拟列表
|
||||
if (this.scrollViewList) {
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
const girlIDs = girlData.getRankIdsByType(rankType);
|
||||
|
||||
this.scrollViewList.numItems = girlIDs.length;
|
||||
logger.log(
|
||||
`[RankPanel] 切换到榜单类型 ${rankType},共 ${girlIDs.length} 项`
|
||||
);
|
||||
} else {
|
||||
logger.warn("[RankPanel] scrollViewList 未初始化");
|
||||
}
|
||||
|
||||
// 更新标签页状态
|
||||
this.updateTabState(rankType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新标签页状态
|
||||
* @param rankType 当前榜单类型
|
||||
*/
|
||||
private updateTabState(rankType: proto.cs.EnmRankType) {
|
||||
if (this.dayRankBtn && this.dayRankBtn.children.length >= 2) {
|
||||
this.dayRankBtn.children[0].active =
|
||||
rankType !== proto.cs.EnmRankType.ERT_Day;
|
||||
this.dayRankBtn.children[1].active =
|
||||
rankType === proto.cs.EnmRankType.ERT_Day;
|
||||
}
|
||||
|
||||
if (this.weekRankBtn && this.weekRankBtn.children.length >= 2) {
|
||||
this.weekRankBtn.children[0].active =
|
||||
rankType !== proto.cs.EnmRankType.ERT_Week;
|
||||
this.weekRankBtn.children[1].active =
|
||||
rankType === proto.cs.EnmRankType.ERT_Week;
|
||||
}
|
||||
|
||||
if (this.monthRankBtn && this.monthRankBtn.children.length >= 2) {
|
||||
this.monthRankBtn.children[0].active =
|
||||
rankType !== proto.cs.EnmRankType.ERT_Month;
|
||||
this.monthRankBtn.children[1].active =
|
||||
rankType === proto.cs.EnmRankType.ERT_Month;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 滚动中回调,基于虚拟列表 displayData 检测并触发分页加载
|
||||
* 当显示到接近已加载数据末尾时(倒数第3个),自动加载下一页
|
||||
*/
|
||||
private onRankScrolling(): void {
|
||||
if (!this.scrollViewList) return;
|
||||
|
||||
// 获取虚拟列表的 displayData 和总数
|
||||
const displayData = this.scrollViewList.displayData;
|
||||
const numItems = this.scrollViewList.numItems;
|
||||
|
||||
// 检查数据有效性
|
||||
if (!displayData || displayData.length === 0 || numItems === 0) return;
|
||||
|
||||
// 获取当前显示的最后一个 item 的 ID
|
||||
const lastDisplayedId = displayData[displayData.length - 1].id;
|
||||
|
||||
// 触发条件:显示到倒数第 3 个 item 时开始加载下一页
|
||||
const threshold = numItems - 3;
|
||||
|
||||
if (
|
||||
lastDisplayedId >= threshold &&
|
||||
lastDisplayedId > this.lastLoadTriggerId
|
||||
) {
|
||||
logger.log(
|
||||
`[RankPanel] 接近末尾 (${lastDisplayedId}/${numItems}),触发加载第 ${
|
||||
this.currentPage + 1
|
||||
} 页`
|
||||
);
|
||||
|
||||
// 记录本次触发的 ID,避免重复触发
|
||||
this.lastLoadTriggerId = lastDisplayedId;
|
||||
|
||||
// 触发分页加载
|
||||
this.loadMoreRankData();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载下一页排行榜数据
|
||||
*/
|
||||
private async loadMoreRankData(): Promise<void> {
|
||||
// 防止重复加载
|
||||
if (this.isLoadingMore) {
|
||||
logger.log("[RankPanel] 正在加载中,跳过");
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否还有更多数据
|
||||
if (!this.hasMoreData) {
|
||||
logger.log("[RankPanel] 没有更多数据了");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
this.isLoadingMore = true;
|
||||
this.currentPage++;
|
||||
|
||||
logger.log(`[RankPanel] 开始加载第 ${this.currentPage} 页排行榜数据`);
|
||||
|
||||
const reqData = {
|
||||
rankType: this.currentRankType,
|
||||
page: this.currentPage,
|
||||
limit: this.pageLimit,
|
||||
};
|
||||
|
||||
const res = await GirlService.I.reqGirlRankData(reqData);
|
||||
|
||||
if (res && res.code === proto.cs.EnmRetCode.SUCCESS) {
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
|
||||
// 追加数据到 GirlData
|
||||
girlData.addRankData(this.currentRankType, res.data);
|
||||
|
||||
// 检查是否还有更多数据
|
||||
const girls = res.data.girls || [];
|
||||
if (girls.length < this.pageLimit) {
|
||||
this.hasMoreData = false;
|
||||
logger.log("[RankPanel] 已加载所有排行榜数据");
|
||||
}
|
||||
|
||||
// 使用虚拟列表:更新总数量,自动触发渲染
|
||||
if (this.scrollViewList) {
|
||||
const allGirlIDs = girlData.getRankIdsByType(this.currentRankType);
|
||||
this.scrollViewList.numItems = allGirlIDs.length;
|
||||
logger.log(
|
||||
`[RankPanel] 第 ${this.currentPage} 页加载完成,新增 ${girls.length} 项,总计 ${allGirlIDs.length} 项`
|
||||
);
|
||||
} else {
|
||||
logger.warn("[RankPanel] scrollViewList 未初始化,无法更新列表");
|
||||
}
|
||||
} else {
|
||||
logger.warn("[RankPanel] 加载下一页排行榜数据失败:", res);
|
||||
// 加载失败,回退页码
|
||||
this.currentPage--;
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error("[RankPanel] 加载下一页排行榜数据异常:", error);
|
||||
// 加载失败,回退页码
|
||||
this.currentPage--;
|
||||
} finally {
|
||||
this.isLoadingMore = false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取排行榜数据
|
||||
* @param type 榜单类型
|
||||
*/
|
||||
private async reqGirlRankData(type: proto.cs.EnmRankType) {
|
||||
const reqData = {
|
||||
rankType: type,
|
||||
page: 1,
|
||||
limit: this.pageLimit,
|
||||
};
|
||||
let res = await GirlService.I.reqGirlRankData(reqData);
|
||||
if (res && res.code === proto.cs.EnmRetCode.SUCCESS) {
|
||||
const resData = res.data;
|
||||
const girlData = DataManager.I.getDataById<GirlData>(DataId.Girl);
|
||||
girlData.setGirlRank(type, resData);
|
||||
|
||||
// 检查是否还有更多数据
|
||||
const girls = resData.girls || [];
|
||||
if (girls.length < this.pageLimit) {
|
||||
this.hasMoreData = false;
|
||||
logger.log("[RankPanel] 首次加载数据不足一页,无需分页");
|
||||
}
|
||||
|
||||
logger.log(
|
||||
`[RankPanel] 榜单类型 ${type} 首次加载完成,共 ${girls.length} 项`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
onDestroy(): void {
|
||||
logger.log("[RankPanel] onDestroy called");
|
||||
|
||||
// 移除虚拟列表滚动事件监听
|
||||
if (this.scrollViewList) {
|
||||
this.scrollViewList.node.off("scrolling", this.onRankScrolling, this);
|
||||
logger.log("[RankPanel] 虚拟列表滚动事件已移除");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import { EnvData } from "../data/EnvData";
|
||||
import { logger } from "db://assets/Scripts/Main/Common/Logger";
|
||||
import { GButton } from "../../Main/Common/GButton";
|
||||
import { GirlListPanel } from "../ui/panels/GirlListPanel";
|
||||
import { GirlCollectionPanel } from "../ui/panels/GirlCollectionPanel";
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@@ -84,8 +85,12 @@ export class GirlListItem extends Component {
|
||||
);
|
||||
}
|
||||
|
||||
baseNode: GirlListPanel;
|
||||
refreshData(category: number, girlId: number, baseNode: GirlListPanel) {
|
||||
baseNode: GirlListPanel | GirlCollectionPanel;
|
||||
refreshData(
|
||||
category: number,
|
||||
girlId: number,
|
||||
baseNode: GirlListPanel | GirlCollectionPanel
|
||||
) {
|
||||
if (!this.stars) {
|
||||
this.stars = this.starParent.getComponentsInChildren(Sprite);
|
||||
}
|
||||
@@ -208,11 +213,16 @@ export class GirlListItem extends Component {
|
||||
const currentPanel = NavigationManager.Instance.getCurrentActivePanelNode();
|
||||
if (currentPanel) {
|
||||
const chatData = { girlId: this.id, base: currentPanel };
|
||||
NavigationManager.Instance.openSubPanel("ChatPanel", currentPanel, chatData, {
|
||||
openAnimation: PageTransitionType.NONE,
|
||||
closeAnimation: PageTransitionType.NONE,
|
||||
hideBasePanel: true,
|
||||
});
|
||||
NavigationManager.Instance.openSubPanel(
|
||||
"ChatPanel",
|
||||
currentPanel,
|
||||
chatData,
|
||||
{
|
||||
openAnimation: PageTransitionType.NONE,
|
||||
closeAnimation: PageTransitionType.NONE,
|
||||
hideBasePanel: true,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
logger.warn("[GirlListItem] 无法获取当前激活的主页面");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user