聊天记录功能-初步

This commit is contained in:
2025-08-13 16:53:27 +08:00
parent a859fe51d1
commit ef02eedac6
9 changed files with 6223 additions and 497 deletions
+19 -4
View File
@@ -48,7 +48,7 @@ export class ChatPanel extends li_BaseView {
private _nodeTab: any = {};
nameKey: string;
private onLanguageChangeCallback = () => {
if (!this.girlName) return;
this.girlName.string = LanguageUtils.getText(this.nameKey);
@@ -73,11 +73,23 @@ export class ChatPanel extends li_BaseView {
this.onDialogUpdate
);
Utils.addInnerEL(InnerMsgCode.LanguageChange, this, this.onLanguageChangeCallback);
Utils.addInnerEL(
InnerMsgCode.LanguageChange,
this,
this.onLanguageChangeCallback
);
}
onDestroy(): void {
Utils.removeInnerEL(InnerMsgCode.Chat_DialogRefresh, this, this.onDialogUpdate);
Utils.removeInnerEL(InnerMsgCode.LanguageChange, this, this.onLanguageChangeCallback);
Utils.removeInnerEL(
InnerMsgCode.Chat_DialogRefresh,
this,
this.onDialogUpdate
);
Utils.removeInnerEL(
InnerMsgCode.LanguageChange,
this,
this.onLanguageChangeCallback
);
}
refresh(id: number) {
this.id = id;
@@ -146,4 +158,7 @@ export class ChatPanel extends li_BaseView {
//ViewManager.I.openBundlesView("GirlListPanel");
}
onClickRecord() {
ViewManager.I.openBundlesView("RecordPanel", this.id);
}
}
@@ -0,0 +1,299 @@
import {
_decorator,
Component,
Node,
Label,
Sprite,
UITransform,
ScrollView,
Button,
} from "cc";
import {
ChatHistoryManager,
ChatMessage,
} from "../../manager/ChatHistoryManager";
import li_BaseView from "db://assets/Scripts/Main/Common/li_BaseView";
import Utils from "db://assets/Scripts/Main/Common/Utils";
import { ChatContentsLayout } from "../components/ChatContentsLayout";
import { ViewManager } from "db://assets/Scripts/Main/Manager/ViewManager";
import GameRootUI from "db://assets/Scripts/Main/Common/GameRootUI";
import ResManager from "db://assets/Scripts/Main/Manager/ResManager";
import ConfigManager from "../../manager/ConfigManager";
import LanguageUtils from "../../../Main/Common/LanguageUtils";
import { Dialog } from "../../data/DialogData";
import { InnerMsgCode } from "../../../Main/Config/InnerMsgCode";
const { ccclass, property } = _decorator;
@ccclass("RecordPanel")
export class RecordPanel extends li_BaseView {
@property(Label)
girlName: Label = null;
@property(Sprite)
girlImg: Sprite = null;
@property(ChatContentsLayout)
layout: ChatContentsLayout = null;
@property(ScrollView)
scrollView: ScrollView = null;
@property(Button)
loadMoreBtn: Button = null;
@property(Label)
loadMoreLabel: Label = null;
id: number;
private _nodeTab: any = {};
nameKey: string;
// 分页相关
private currentPage: number = 0;
private pageSize: number = 20;
private allMessages: ChatMessage[] = [];
private displayedMessages: Dialog[] = [];
private onLanguageChangeCallback = () => {
if (!this.girlName) return;
this.girlName.string = LanguageUtils.getText(this.nameKey);
};
openUIDataCT(data) {
this.id = data;
}
onLoadCT() {
Utils.parseNode(this.node, this._nodeTab);
this.register();
this.refresh(this.id);
this.initLoadMoreBtn();
this.loadChatHistory();
}
register() {
// 注册语言变化监听
Utils.addInnerEL(
InnerMsgCode.LanguageChange,
this,
this.onLanguageChangeCallback
);
}
onDestroy(): void {
Utils.removeInnerEL(
InnerMsgCode.LanguageChange,
this,
this.onLanguageChangeCallback
);
}
/**
* 初始化加载更多按钮
*/
private initLoadMoreBtn() {
if (this.loadMoreBtn) {
this.loadMoreBtn.node.on(Button.EventType.CLICK, this.onLoadMore, this);
}
this.updateLoadMoreBtn();
}
refresh(id: number) {
this.id = id;
const data = ConfigManager.tables.TbGirls.get(this.id);
const dataDetail = ConfigManager.tables.TbGirlsDetail.get(this.id);
if (!data) return;
this.nameKey = data.nameKey;
this.girlName.string = LanguageUtils.getText(data.nameKey);
// 设置角色头像
ResManager.I.changeBundleSpriteFrame(
this.girlImg,
data.avatarPath,
"Chat18x",
() => {
let sizeTran = this.girlImg.node.parent.getComponent(UITransform);
Utils.adjustBgPixelRatioToSize(
sizeTran.contentSize,
this.girlImg.node,
2
);
}
);
}
/**
* 加载聊天历史记录并显示
*/
private loadChatHistory() {
// 从ChatHistoryManager获取历史记录
this.allMessages = ChatHistoryManager.Instance.loadHistory(this.id);
if (this.allMessages.length === 0) {
console.log(`No chat history found for role ${this.id}`);
this.updateLoadMoreBtn();
return;
}
// 倒序排列消息(最新的在前)
this.allMessages.reverse();
// 重置分页
this.currentPage = 0;
this.displayedMessages = [];
// 加载第一页
this.loadMoreMessages();
}
/**
* 加载更多消息
*/
private loadMoreMessages() {
const startIndex = this.currentPage * this.pageSize;
const endIndex = Math.min(
startIndex + this.pageSize,
this.allMessages.length
);
// 获取当前页的消息
const pageMessages = this.allMessages.slice(startIndex, endIndex);
// 将ChatMessage转换为Dialog格式并添加到显示列表
const newDialogs: Dialog[] = pageMessages.map((msg: ChatMessage) => ({
isPlayer: msg.role === "user",
content: msg.parts[0]?.text || "",
}));
// 添加到已显示的消息列表
this.displayedMessages.push(...newDialogs);
console.log(
`Loaded page ${this.currentPage + 1}, showing ${
this.displayedMessages.length
}/${this.allMessages.length} messages`
);
// 更新聊天内容布局
if (this.layout) {
this.layout.UpdateDialog(this.displayedMessages);
}
// 更新页数
this.currentPage++;
// 更新加载更多按钮状态
this.updateLoadMoreBtn();
// 如果是第一次加载,滚动到顶部
if (this.currentPage === 1 && this.scrollView) {
this.scheduleOnce(() => {
this.scrollView.scrollToTop(0.5);
}, 0.1);
}
}
/**
* 加载更多按钮点击事件
*/
private onLoadMore() {
if (this.hasMoreMessages()) {
this.loadMoreMessages();
}
}
/**
* 判断是否还有更多消息
*/
private hasMoreMessages(): boolean {
return this.currentPage * this.pageSize < this.allMessages.length;
}
/**
* 更新加载更多按钮状态
*/
private updateLoadMoreBtn() {
if (!this.loadMoreBtn) return;
const hasMore = this.hasMoreMessages();
this.loadMoreBtn.node.active = hasMore;
if (this.loadMoreLabel) {
const remainingCount =
this.allMessages.length - this.currentPage * this.pageSize;
this.loadMoreLabel.string = hasMore
? `加载更多 (还有${remainingCount}条)`
: "没有更多消息了";
}
}
/**
* 清除当前角色的聊天记录
*/
public clearHistory() {
ChatHistoryManager.Instance.clearHistory(this.id);
console.log(`Cleared chat history for role ${this.id}`);
// 重置数据
this.allMessages = [];
this.displayedMessages = [];
this.currentPage = 0;
// 重新加载(显示空记录)
this.loadChatHistory();
}
/**
* 刷新聊天记录显示
*/
public refreshHistory() {
this.loadChatHistory();
}
/**
* 滚动到最新记录(顶部,因为是倒序)
*/
public scrollToLatest() {
if (this.scrollView) {
this.scrollView.scrollToTop(0.5);
}
}
/**
* 滚动到最早记录(底部,因为是倒序)
*/
public scrollToEarliest() {
if (this.scrollView) {
this.scrollView.scrollToBottom(0.5);
}
}
/**
* 获取消息统计信息
*/
public getMessageStats(): {
total: number;
displayed: number;
remaining: number;
} {
return {
total: this.allMessages.length,
displayed: this.displayedMessages.length,
remaining: this.allMessages.length - this.displayedMessages.length,
};
}
protected onEnable(): void {
GameRootUI.I.hideDefaultView();
}
/**
* 返回按钮点击事件
*/
returnBtn() {
this.onClose();
ViewManager.I.closeView(this.node);
}
}
@@ -0,0 +1,9 @@
{
"ver": "4.0.24",
"importer": "typescript",
"imported": true,
"uuid": "ad34b4b3-7e82-4a1d-b8f4-c4d2f1e0a5c7",
"files": [],
"subMetas": {},
"userData": {}
}
+33 -31
View File
@@ -8,37 +8,39 @@
* 为不支持 structuredClone 的环境提供兼容实现
*/
export function initPolyfills(): void {
// structuredClone polyfill
if (typeof (globalThis as any).structuredClone === 'undefined') {
(globalThis as any).structuredClone = function(obj: any): any {
if (obj === null || typeof obj !== 'object') {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (Array.isArray(obj)) {
return obj.map((item: any) => (globalThis as any).structuredClone(item));
}
if (typeof obj === 'object') {
const cloned: any = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = (globalThis as any).structuredClone(obj[key]);
}
}
return cloned;
}
return obj;
};
console.log('[Polyfill] structuredClone polyfill loaded');
}
// structuredClone polyfill
if (typeof (globalThis as any).structuredClone === "undefined") {
(globalThis as any).structuredClone = function (obj: any): any {
if (obj === null || typeof obj !== "object") {
return obj;
}
if (obj instanceof Date) {
return new Date(obj.getTime());
}
if (Array.isArray(obj)) {
return obj.map((item: any) =>
(globalThis as any).structuredClone(item)
);
}
if (typeof obj === "object") {
const cloned: any = {};
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
cloned[key] = (globalThis as any).structuredClone(obj[key]);
}
}
return cloned;
}
return obj;
};
//console.log('[Polyfill] structuredClone polyfill loaded');
}
}
// 自动初始化
initPolyfills();
initPolyfills();
File diff suppressed because it is too large Load Diff
@@ -6609,7 +6609,9 @@
"img": {
"__id__": 233
},
"lock": null,
"lock": {
"__id__": 246
},
"question": {
"__id__": 240
},
+3 -1
View File
@@ -4835,7 +4835,9 @@
"tags": {
"__id__": 84
},
"price": null,
"price": {
"__id__": 172
},
"freeNode": {
"__id__": 147
},
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,13 @@
{
"ver": "1.1.50",
"importer": "prefab",
"imported": true,
"uuid": "a62646a7-32f3-4560-9f53-f3e476752922",
"files": [
".json"
],
"subMetas": {},
"userData": {
"syncNodeName": "RecordPanel"
}
}