聊天记录功能-初步
This commit is contained in:
@@ -73,11 +73,23 @@ export class ChatPanel extends li_BaseView {
|
|||||||
this.onDialogUpdate
|
this.onDialogUpdate
|
||||||
);
|
);
|
||||||
|
|
||||||
Utils.addInnerEL(InnerMsgCode.LanguageChange, this, this.onLanguageChangeCallback);
|
Utils.addInnerEL(
|
||||||
|
InnerMsgCode.LanguageChange,
|
||||||
|
this,
|
||||||
|
this.onLanguageChangeCallback
|
||||||
|
);
|
||||||
}
|
}
|
||||||
onDestroy(): void {
|
onDestroy(): void {
|
||||||
Utils.removeInnerEL(InnerMsgCode.Chat_DialogRefresh, this, this.onDialogUpdate);
|
Utils.removeInnerEL(
|
||||||
Utils.removeInnerEL(InnerMsgCode.LanguageChange, this, this.onLanguageChangeCallback);
|
InnerMsgCode.Chat_DialogRefresh,
|
||||||
|
this,
|
||||||
|
this.onDialogUpdate
|
||||||
|
);
|
||||||
|
Utils.removeInnerEL(
|
||||||
|
InnerMsgCode.LanguageChange,
|
||||||
|
this,
|
||||||
|
this.onLanguageChangeCallback
|
||||||
|
);
|
||||||
}
|
}
|
||||||
refresh(id: number) {
|
refresh(id: number) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
@@ -146,4 +158,7 @@ export class ChatPanel extends li_BaseView {
|
|||||||
|
|
||||||
//ViewManager.I.openBundlesView("GirlListPanel");
|
//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,36 +8,38 @@
|
|||||||
* 为不支持 structuredClone 的环境提供兼容实现
|
* 为不支持 structuredClone 的环境提供兼容实现
|
||||||
*/
|
*/
|
||||||
export function initPolyfills(): void {
|
export function initPolyfills(): void {
|
||||||
// structuredClone polyfill
|
// structuredClone polyfill
|
||||||
if (typeof (globalThis as any).structuredClone === 'undefined') {
|
if (typeof (globalThis as any).structuredClone === "undefined") {
|
||||||
(globalThis as any).structuredClone = function(obj: any): any {
|
(globalThis as any).structuredClone = function (obj: any): any {
|
||||||
if (obj === null || typeof obj !== 'object') {
|
if (obj === null || typeof obj !== "object") {
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj instanceof Date) {
|
if (obj instanceof Date) {
|
||||||
return new Date(obj.getTime());
|
return new Date(obj.getTime());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Array.isArray(obj)) {
|
if (Array.isArray(obj)) {
|
||||||
return obj.map((item: any) => (globalThis as any).structuredClone(item));
|
return obj.map((item: any) =>
|
||||||
}
|
(globalThis as any).structuredClone(item)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof obj === 'object') {
|
if (typeof obj === "object") {
|
||||||
const cloned: any = {};
|
const cloned: any = {};
|
||||||
for (const key in obj) {
|
for (const key in obj) {
|
||||||
if (obj.hasOwnProperty(key)) {
|
if (obj.hasOwnProperty(key)) {
|
||||||
cloned[key] = (globalThis as any).structuredClone(obj[key]);
|
cloned[key] = (globalThis as any).structuredClone(obj[key]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cloned;
|
return cloned;
|
||||||
}
|
}
|
||||||
|
|
||||||
return obj;
|
return obj;
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('[Polyfill] structuredClone polyfill loaded');
|
//console.log('[Polyfill] structuredClone polyfill loaded');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 自动初始化
|
// 自动初始化
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -6609,7 +6609,9 @@
|
|||||||
"img": {
|
"img": {
|
||||||
"__id__": 233
|
"__id__": 233
|
||||||
},
|
},
|
||||||
"lock": null,
|
"lock": {
|
||||||
|
"__id__": 246
|
||||||
|
},
|
||||||
"question": {
|
"question": {
|
||||||
"__id__": 240
|
"__id__": 240
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -4835,7 +4835,9 @@
|
|||||||
"tags": {
|
"tags": {
|
||||||
"__id__": 84
|
"__id__": 84
|
||||||
},
|
},
|
||||||
"price": null,
|
"price": {
|
||||||
|
"__id__": 172
|
||||||
|
},
|
||||||
"freeNode": {
|
"freeNode": {
|
||||||
"__id__": 147
|
"__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