聊天记录功能-初步
This commit is contained in:
@@ -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": {}
|
||||
}
|
||||
@@ -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
|
||||
},
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user