Files
ai_game/client/js/scenes/ProfileScene.js

1461 lines
49 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* 个人中心场景 - 支持创作者功能
*/
import BaseScene from './BaseScene';
export default class ProfileScene extends BaseScene {
constructor(main, params) {
super(main, params);
// Tab: 0我的作品 1AI草稿 2收藏 3游玩记录
this.currentTab = params.tab || 0; // 支持传入初始tab
this.tabs = ['作品', 'AI草稿', '收藏', '记录'];
// 数据
this.myWorks = [];
this.drafts = [];
this.collections = [];
this.progress = [];
// 记录版本列表相关状态
this.recordViewMode = 'list'; // 'list' 故事列表 | 'versions' 版本列表
this.selectedStoryRecords = []; // 选中故事的记录列表
this.selectedStoryInfo = {}; // 选中故事的信息
// 头像相关
this.avatarImage = null;
this.avatarImageLoaded = false;
// 统计
this.stats = {
works: 0,
totalPlays: 0,
totalLikes: 0,
earnings: 0
};
// 滚动
this.scrollY = 0;
this.maxScrollY = 0;
this.isDragging = false;
this.lastTouchY = 0;
this.scrollVelocity = 0;
this.hasMoved = false;
}
async init() {
await this.loadData();
this.loadAvatarImage();
}
// 加载头像图片
loadAvatarImage() {
let avatarUrl = this.main.userManager.avatarUrl;
if (!avatarUrl) return;
// 如果是相对路径,拼接完整 URL
if (avatarUrl.startsWith('/uploads')) {
avatarUrl = 'http://172.20.10.8:8000' + avatarUrl;
}
if (avatarUrl.startsWith('http')) {
this.avatarImage = wx.createImage();
this.avatarImage.onload = () => {
this.avatarImageLoaded = true;
};
this.avatarImage.onerror = () => {
this.avatarImageLoaded = false;
};
this.avatarImage.src = avatarUrl;
}
}
async loadData() {
if (this.main.userManager.isLoggedIn) {
try {
const userId = this.main.userManager.userId;
// 加载已发布到创作中心的作品(改写+续写)
const publishedRewrites = await this.main.userManager.getPublishedDrafts('rewrite') || [];
const publishedContinues = await this.main.userManager.getPublishedDrafts('continue') || [];
this.myWorks = [...publishedRewrites, ...publishedContinues];
// 加载 AI 改写草稿
this.drafts = await this.main.storyManager.getDrafts(userId) || [];
this.collections = await this.main.userManager.getCollections() || [];
// 加载游玩记录(故事列表)
this.progress = await this.main.userManager.getPlayRecords() || [];
// 计算统计(作品数=已发布作品数)
this.stats.works = this.myWorks.length;
this.stats.totalPlays = this.myWorks.reduce((sum, w) => sum + (w.playCount || 0), 0);
this.stats.totalLikes = this.myWorks.reduce((sum, w) => sum + (w.likeCount || 0), 0);
this.stats.earnings = 0; // 暂无收益功能
} catch (e) {
console.error('加载数据失败:', e);
}
}
this.calculateMaxScroll();
}
// 刷新草稿列表
async refreshDrafts() {
if (this.main.userManager.isLoggedIn) {
try {
const userId = this.main.userManager.userId;
this.drafts = await this.main.storyManager.getDrafts(userId) || [];
this.calculateMaxScroll();
} catch (e) {
console.error('刷新草稿失败:', e);
}
}
}
getCurrentList() {
switch (this.currentTab) {
case 0: return this.myWorks;
case 1: return this.drafts;
case 2: return this.collections;
case 3:
// 记录 Tab根据视图模式返回不同列表
return this.recordViewMode === 'versions' ? this.selectedStoryRecords : this.progress;
default: return [];
}
}
calculateMaxScroll() {
const list = this.getCurrentList();
const cardHeight = this.currentTab <= 1 ? 100 : 85;
const gap = 10;
const headerHeight = 260;
const contentHeight = list.length * (cardHeight + gap) + headerHeight;
this.maxScrollY = Math.max(0, contentHeight - this.screenHeight + 20);
}
update() {
if (!this.isDragging && Math.abs(this.scrollVelocity) > 0.5) {
this.scrollY += this.scrollVelocity;
this.scrollVelocity *= 0.95;
this.scrollY = Math.max(0, Math.min(this.scrollY, this.maxScrollY));
}
}
render(ctx) {
this.renderBackground(ctx);
this.renderHeader(ctx);
this.renderUserCard(ctx);
this.renderTabs(ctx);
this.renderList(ctx);
this.renderLogoutButton(ctx);
}
renderBackground(ctx) {
const gradient = ctx.createLinearGradient(0, 0, 0, this.screenHeight);
gradient.addColorStop(0, '#0f0c29');
gradient.addColorStop(0.5, '#302b63');
gradient.addColorStop(1, '#24243e');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, this.screenWidth, this.screenHeight);
}
renderHeader(ctx) {
ctx.fillStyle = '#ffffff';
ctx.font = '16px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(' 返回', 15, 35);
ctx.textAlign = 'center';
ctx.font = 'bold 17px sans-serif';
ctx.fillText('个人中心', this.screenWidth / 2, 35);
}
renderUserCard(ctx) {
const cardY = 55;
const cardH = 145;
const user = this.main.userManager;
// 卡片背景
ctx.fillStyle = 'rgba(255,255,255,0.06)';
this.roundRect(ctx, 12, cardY, this.screenWidth - 24, cardH, 14);
ctx.fill();
// 头像
const avatarSize = 50;
const avatarX = 30;
const avatarY = cardY + 18;
// 保存头像区域用于点击检测
this.avatarRect = { x: avatarX, y: avatarY, width: avatarSize, height: avatarSize };
// 如果有头像图片则绘制图片,否则绘制默认渐变头像
if (this.avatarImage && this.avatarImageLoaded) {
ctx.save();
ctx.beginPath();
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
ctx.clip();
ctx.drawImage(this.avatarImage, avatarX, avatarY, avatarSize, avatarSize);
ctx.restore();
} else {
const avatarGradient = ctx.createLinearGradient(avatarX, avatarY, avatarX + avatarSize, avatarY + avatarSize);
avatarGradient.addColorStop(0, '#ff6b6b');
avatarGradient.addColorStop(1, '#ffd700');
ctx.fillStyle = avatarGradient;
ctx.beginPath();
ctx.arc(avatarX + avatarSize / 2, avatarY + avatarSize / 2, avatarSize / 2, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 20px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(user.nickname ? user.nickname[0] : '游', avatarX + avatarSize / 2, avatarY + avatarSize / 2 + 7);
}
// 编辑图标(头像右下角)
ctx.fillStyle = 'rgba(0,0,0,0.5)';
ctx.beginPath();
ctx.arc(avatarX + avatarSize - 8, avatarY + avatarSize - 8, 10, 0, Math.PI * 2);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('✎', avatarX + avatarSize - 8, avatarY + avatarSize - 5);
// 昵称和ID
ctx.textAlign = 'left';
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 16px sans-serif';
const nickname = user.nickname || '游客用户';
ctx.fillText(nickname, avatarX + avatarSize + 15, avatarY + 22);
// 昵称旁边的编辑按钮
const nicknameWidth = ctx.measureText(nickname).width;
const editNicknameBtnX = avatarX + avatarSize + 15 + nicknameWidth + 8;
ctx.fillStyle = 'rgba(255,255,255,0.15)';
this.roundRect(ctx, editNicknameBtnX, avatarY + 8, 24, 20, 10);
ctx.fill();
ctx.fillStyle = 'rgba(255,255,255,0.6)';
ctx.font = '11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('✎', editNicknameBtnX + 12, avatarY + 22);
this.editNicknameRect = { x: editNicknameBtnX, y: avatarY + 8, width: 24, height: 20 };
ctx.fillStyle = 'rgba(255,255,255,0.4)';
ctx.font = '11px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(`ID: ${user.userId || '未登录'}`, avatarX + avatarSize + 15, avatarY + 42);
// 创作者标签
if (this.myWorks.length > 0) {
const tagX = avatarX + avatarSize + 15 + ctx.measureText(`ID: ${user.userId || '未登录'}`).width + 10;
ctx.fillStyle = '#a855f7';
this.roundRect(ctx, tagX, avatarY + 30, 45, 18, 9);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 9px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('创作者', tagX + 22, avatarY + 42);
}
// 分割线
const lineY = cardY + 80;
ctx.strokeStyle = 'rgba(255,255,255,0.08)';
ctx.lineWidth = 1;
ctx.beginPath();
ctx.moveTo(25, lineY);
ctx.lineTo(this.screenWidth - 25, lineY);
ctx.stroke();
// 统计数据
const statsY = lineY + 30;
const statW = (this.screenWidth - 24) / 4;
const statsData = [
{ num: this.stats.works, label: '作品' },
{ num: this.stats.totalPlays, label: '播放' },
{ num: this.stats.totalLikes, label: '获赞' },
{ num: this.stats.earnings.toFixed(1), label: '收益' }
];
statsData.forEach((stat, i) => {
const x = 12 + statW * i + statW / 2;
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 16px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(this.formatNumber(stat.num), x, statsY);
ctx.fillStyle = 'rgba(255,255,255,0.45)';
ctx.font = '10px sans-serif';
ctx.fillText(stat.label, x, statsY + 15);
});
}
renderTabs(ctx) {
const tabY = 210;
const tabW = (this.screenWidth - 24) / this.tabs.length;
this.tabRects = [];
this.tabs.forEach((tab, index) => {
const x = 12 + index * tabW;
const isActive = index === this.currentTab;
const centerX = x + tabW / 2;
ctx.fillStyle = isActive ? '#ffffff' : 'rgba(255,255,255,0.45)';
ctx.font = isActive ? 'bold 13px sans-serif' : '13px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(tab, centerX, tabY + 12);
// 下划线
if (isActive) {
const lineGradient = ctx.createLinearGradient(centerX - 14, tabY + 20, centerX + 14, tabY + 20);
lineGradient.addColorStop(0, '#ff6b6b');
lineGradient.addColorStop(1, '#ffd700');
ctx.fillStyle = lineGradient;
this.roundRect(ctx, centerX - 14, tabY + 18, 28, 3, 1.5);
ctx.fill();
}
this.tabRects.push({ x, y: tabY - 5, width: tabW, height: 30, index });
});
}
renderList(ctx) {
const list = this.getCurrentList();
const startY = 250;
const cardH = this.currentTab <= 1 ? 100 : 85;
const gap = 10;
const padding = 12;
ctx.save();
ctx.beginPath();
ctx.rect(0, startY - 5, this.screenWidth, this.screenHeight - startY + 5);
ctx.clip();
// 记录 Tab 版本列表模式:显示返回按钮和标题
if (this.currentTab === 3 && this.recordViewMode === 'versions') {
this.renderVersionListHeader(ctx, startY);
}
const listStartY = (this.currentTab === 3 && this.recordViewMode === 'versions') ? startY + 45 : startY;
if (list.length === 0) {
ctx.fillStyle = 'rgba(255,255,255,0.35)';
ctx.font = '13px sans-serif';
ctx.textAlign = 'center';
const emptyTexts = ['还没有发布作品,去草稿箱发布吧', '草稿箱空空如也', '还没有收藏的故事', '还没有游玩记录'];
const emptyText = (this.currentTab === 3 && this.recordViewMode === 'versions')
? '该故事还没有游玩记录'
: emptyTexts[this.currentTab];
ctx.fillText(emptyText, this.screenWidth / 2, listStartY + 50);
// 作品Tab引导按钮 - 跳转到草稿箱
if (this.currentTab === 0) {
const btnY = listStartY + 80;
const btnGradient = ctx.createLinearGradient(this.screenWidth / 2 - 50, btnY, this.screenWidth / 2 + 50, btnY);
btnGradient.addColorStop(0, '#a855f7');
btnGradient.addColorStop(1, '#ec4899');
ctx.fillStyle = btnGradient;
this.roundRect(ctx, this.screenWidth / 2 - 55, btnY, 110, 36, 18);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 13px sans-serif';
ctx.fillText('前往草稿箱', this.screenWidth / 2, btnY + 23);
this.createBtnRect = { x: this.screenWidth / 2 - 55, y: btnY, width: 110, height: 36 };
}
ctx.restore();
return;
}
list.forEach((item, index) => {
const y = listStartY + index * (cardH + gap) - this.scrollY;
if (y > listStartY - cardH && y < this.screenHeight) {
if (this.currentTab === 0) {
this.renderWorkCard(ctx, item, padding, y, this.screenWidth - padding * 2, cardH, index);
} else if (this.currentTab === 1) {
this.renderDraftCard(ctx, item, padding, y, this.screenWidth - padding * 2, cardH, index);
} else if (this.currentTab === 3 && this.recordViewMode === 'versions') {
this.renderRecordVersionCard(ctx, item, padding, y, this.screenWidth - padding * 2, cardH, index);
} else {
this.renderSimpleCard(ctx, item, padding, y, this.screenWidth - padding * 2, cardH, index);
}
}
});
ctx.restore();
}
// 渲染底部退出登录按钮
renderLogoutButton(ctx) {
const btnW = 120;
const btnH = 40;
const btnX = (this.screenWidth - btnW) / 2;
const btnY = this.screenHeight - 70;
// 按钮背景
ctx.fillStyle = 'rgba(239, 68, 68, 0.15)';
this.roundRect(ctx, btnX, btnY, btnW, btnH, 20);
ctx.fill();
// 按钮边框
ctx.strokeStyle = 'rgba(239, 68, 68, 0.4)';
ctx.lineWidth = 1;
this.roundRect(ctx, btnX, btnY, btnW, btnH, 20);
ctx.stroke();
// 按钮文字
ctx.fillStyle = '#ef4444';
ctx.font = '14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('退出登录', this.screenWidth / 2, btnY + 26);
// 保存按钮区域
this.logoutBtnRect = { x: btnX, y: btnY, width: btnW, height: btnH };
}
// 渲染版本列表头部(返回按钮+故事标题)
renderVersionListHeader(ctx, startY) {
const headerY = startY - 5;
// 返回按钮
ctx.fillStyle = 'rgba(255,255,255,0.8)';
ctx.font = '14px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(' 返回', 15, headerY + 20);
this.versionBackBtnRect = { x: 5, y: headerY, width: 70, height: 35 };
// 故事标题
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'center';
const title = this.selectedStoryInfo.title || '游玩记录';
ctx.fillText(this.truncateText(ctx, title, this.screenWidth - 120), this.screenWidth / 2, headerY + 20);
// 记录数量
ctx.fillStyle = 'rgba(255,255,255,0.5)';
ctx.font = '12px sans-serif';
ctx.textAlign = 'right';
ctx.fillText(`${this.selectedStoryRecords.length} 条记录`, this.screenWidth - 15, headerY + 20);
}
// 渲染单条游玩记录版本卡片
renderRecordVersionCard(ctx, item, x, y, w, h, index) {
const isUnpublished = item.draftId && item.isPublished === false;
// 已下架的卡片背景更暗
ctx.fillStyle = isUnpublished ? 'rgba(255,255,255,0.02)' : 'rgba(255,255,255,0.05)';
this.roundRect(ctx, x, y, w, h, 12);
ctx.fill();
// 左侧序号圆圈
const circleX = x + 30;
const circleY = y + h / 2;
const circleR = 18;
const colors = this.getGradientColors(index);
const circleGradient = ctx.createLinearGradient(circleX - circleR, circleY - circleR, circleX + circleR, circleY + circleR);
circleGradient.addColorStop(0, isUnpublished ? 'rgba(128,128,128,0.5)' : colors[0]);
circleGradient.addColorStop(1, isUnpublished ? 'rgba(96,96,96,0.5)' : colors[1]);
ctx.fillStyle = circleGradient;
ctx.beginPath();
ctx.arc(circleX, circleY, circleR, 0, Math.PI * 2);
ctx.fill();
// 序号
ctx.fillStyle = isUnpublished ? 'rgba(255,255,255,0.5)' : '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(`${index + 1}`, circleX, circleY + 5);
const textX = x + 65;
const maxTextWidth = w - 200; // 留出按钮空间
// 结局名称(只显示结局,不显示草稿标题)
ctx.fillStyle = isUnpublished ? 'rgba(255,255,255,0.5)' : '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'left';
const endingLabel = `结局:${item.endingName || '未知结局'}`;
ctx.fillText(this.truncateText(ctx, endingLabel, maxTextWidth - 60), textX, y + 28);
// 已下架标签(固定在结局名称右边)
if (isUnpublished) {
ctx.fillStyle = 'rgba(239, 68, 68, 0.3)';
this.roundRect(ctx, x + w - 185, y + 15, 44, 18, 9);
ctx.fill();
ctx.fillStyle = '#ef4444';
ctx.font = '10px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('已下架', x + w - 163, y + 27);
}
// 游玩时间(如果是草稿,显示草稿标题)
ctx.fillStyle = isUnpublished ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.45)';
ctx.font = '11px sans-serif';
ctx.textAlign = 'left';
let subText = item.createdAt ? this.formatDateTime(item.createdAt) : '';
if (item.draftTitle) {
subText = this.truncateText(ctx, item.draftTitle, 100) + ' · ' + subText;
}
ctx.fillText(subText, textX, y + 52);
// 删除按钮
ctx.fillStyle = 'rgba(239, 68, 68, 0.2)';
this.roundRect(ctx, x + w - 125, y + 28, 48, 26, 13);
ctx.fill();
ctx.fillStyle = '#ef4444';
ctx.font = 'bold 11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('删除', x + w - 101, y + 45);
// 回放按钮
const btnGradient = ctx.createLinearGradient(x + w - 68, y + 28, x + w - 10, y + 28);
btnGradient.addColorStop(0, isUnpublished ? '#888888' : '#ff6b6b');
btnGradient.addColorStop(1, isUnpublished ? '#666666' : '#ffd700');
ctx.fillStyle = btnGradient;
this.roundRect(ctx, x + w - 68, y + 28, 58, 26, 13);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('回放', x + w - 39, y + 45);
}
// 格式化日期时间
formatDateTime(dateStr) {
if (!dateStr) return '';
try {
// iOS 兼容:将 "2026-03-10 11:51" 转换为 "2026-03-10T11:51:00"
const isoStr = dateStr.replace(' ', 'T');
const date = new Date(isoStr);
if (isNaN(date.getTime())) return dateStr;
const month = date.getMonth() + 1;
const day = date.getDate();
const hour = date.getHours().toString().padStart(2, '0');
const minute = date.getMinutes().toString().padStart(2, '0');
return `${month}${day}${hour}:${minute}`;
} catch (e) {
return dateStr;
}
}
renderWorkCard(ctx, item, x, y, w, h, index) {
// 卡片背景
ctx.fillStyle = 'rgba(255,255,255,0.05)';
this.roundRect(ctx, x, y, w, h, 12);
ctx.fill();
// 封面
const coverW = 70, coverH = h - 16;
const colors = this.getGradientColors(index);
const coverGradient = ctx.createLinearGradient(x + 8, y + 8, x + 8 + coverW, y + 8 + coverH);
coverGradient.addColorStop(0, colors[0]);
coverGradient.addColorStop(1, colors[1]);
ctx.fillStyle = coverGradient;
this.roundRect(ctx, x + 8, y + 8, coverW, coverH, 10);
ctx.fill();
// 类型标签
const typeText = item.draftType === 'continue' ? '续写' : '改写';
ctx.fillStyle = 'rgba(255,255,255,0.8)';
ctx.font = 'bold 9px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(typeText, x + 8 + coverW / 2, y + 8 + coverH / 2 + 3);
const textX = x + 88;
const maxW = w - 100;
// 标题
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'left';
const title = item.title || item.storyTitle || '未命名';
ctx.fillText(this.truncateText(ctx, title, maxW - 60), textX, y + 25);
// 已发布标签
const statusText = '已发布';
const statusW = ctx.measureText(statusText).width + 12;
ctx.fillStyle = 'rgba(34, 197, 94, 0.2)';
const titleWidth = ctx.measureText(this.truncateText(ctx, title, maxW - 60)).width;
this.roundRect(ctx, textX + titleWidth + 8, y + 12, statusW, 18, 9);
ctx.fill();
ctx.fillStyle = '#22c55e';
ctx.font = 'bold 10px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(statusText, textX + titleWidth + 8 + statusW / 2, y + 24);
// 原故事标题
ctx.fillStyle = 'rgba(255,255,255,0.45)';
ctx.font = '11px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(`原故事: ${item.storyTitle || ''}`, textX, y + 48);
// 创建时间
ctx.fillStyle = 'rgba(255,255,255,0.35)';
ctx.font = '10px sans-serif';
ctx.fillText(item.createdAt || '', textX, y + 68);
// 按钮区域
const btnY = y + 55;
// 取消发布按钮
ctx.fillStyle = 'rgba(239, 68, 68, 0.2)';
this.roundRect(ctx, x + w - 125, btnY, 60, 26, 13);
ctx.fill();
ctx.fillStyle = '#ef4444';
ctx.font = '11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('取消发布', x + w - 95, btnY + 17);
// 播放按钮
const btnGradient = ctx.createLinearGradient(x + w - 58, btnY, x + w - 10, btnY);
btnGradient.addColorStop(0, '#a855f7');
btnGradient.addColorStop(1, '#ec4899');
ctx.fillStyle = btnGradient;
this.roundRect(ctx, x + w - 58, btnY, 48, 26, 13);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('播放', x + w - 34, btnY + 17);
}
renderDraftCard(ctx, item, x, y, w, h, index) {
ctx.fillStyle = 'rgba(255,255,255,0.05)';
this.roundRect(ctx, x, y, w, h, 12);
ctx.fill();
const coverW = 70, coverH = h - 16;
const colors = this.getGradientColors(index);
const coverGradient = ctx.createLinearGradient(x + 8, y + 8, x + 8 + coverW, y + 8 + coverH);
coverGradient.addColorStop(0, colors[0]);
coverGradient.addColorStop(1, colors[1]);
ctx.fillStyle = coverGradient;
this.roundRect(ctx, x + 8, y + 8, coverW, coverH, 10);
ctx.fill();
// AI标签
ctx.fillStyle = '#a855f7';
this.roundRect(ctx, x + 8, y + 8, 28, 16, 8);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 9px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('AI', x + 22, y + 19);
const textX = x + 88;
// 标题(故事标题-改写)
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(this.truncateText(ctx, item.title || item.storyTitle || 'AI改写', w - 180), textX, y + 25);
// 状态标签
const statusMap = {
'pending': { text: '等待中', color: '#888888' },
'processing': { text: '生成中', color: '#f59e0b' },
'completed': { text: '已完成', color: '#22c55e' },
'failed': { text: '失败', color: '#ef4444' }
};
const status = statusMap[item.status] || statusMap['pending'];
const titleWidth = ctx.measureText(this.truncateText(ctx, item.title || item.storyTitle || 'AI改写', w - 180)).width;
const statusW = ctx.measureText(status.text).width + 12;
ctx.fillStyle = status.color + '33';
this.roundRect(ctx, textX + titleWidth + 8, y + 12, statusW, 18, 9);
ctx.fill();
ctx.fillStyle = status.color;
ctx.font = 'bold 10px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(status.text, textX + titleWidth + 8 + statusW / 2, y + 24);
// 改写指令
ctx.fillStyle = 'rgba(255,255,255,0.5)';
ctx.font = '11px sans-serif';
ctx.textAlign = 'left';
const promptText = item.userPrompt ? `"「${item.userPrompt}」"` : '';
ctx.fillText(this.truncateText(ctx, promptText, w - 100), textX, y + 48);
// 时间(放在左下角)
ctx.fillStyle = 'rgba(255,255,255,0.35)';
ctx.font = '10px sans-serif';
ctx.fillText(item.createdAt || '', textX, y + 72);
// 未读标记
if (!item.isRead && item.status === 'completed') {
ctx.fillStyle = '#ef4444';
ctx.beginPath();
ctx.arc(x + w - 20, y + 20, 5, 0, Math.PI * 2);
ctx.fill();
}
// 按钮行(放在右下角)
const btnY = y + 60;
const btnStartX = x + w - 170; // 从右边开始排列按钮
// 播放按钮(仅已完成状态)
if (item.status === 'completed') {
const btnGradient = ctx.createLinearGradient(btnStartX, btnY, btnStartX + 50, btnY);
btnGradient.addColorStop(0, '#a855f7');
btnGradient.addColorStop(1, '#ec4899');
ctx.fillStyle = btnGradient;
this.roundRect(ctx, btnStartX, btnY, 50, 26, 13);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('播放', btnStartX + 25, btnY + 17);
// 发布按钮(仅已完成且未发布)
if (!item.publishedToCenter) {
ctx.fillStyle = 'rgba(34, 197, 94, 0.2)';
this.roundRect(ctx, btnStartX + 58, btnY, 50, 26, 13);
ctx.fill();
ctx.fillStyle = '#22c55e';
ctx.font = '11px sans-serif';
ctx.fillText('发布', btnStartX + 83, btnY + 17);
} else {
// 已发布标识
ctx.fillStyle = 'rgba(34, 197, 94, 0.15)';
this.roundRect(ctx, btnStartX + 58, btnY, 55, 26, 13);
ctx.fill();
ctx.fillStyle = '#22c55e';
ctx.font = '10px sans-serif';
ctx.fillText('已发布', btnStartX + 85, btnY + 17);
}
}
// 删除按钮(所有状态都显示,最右边)
ctx.fillStyle = 'rgba(239, 68, 68, 0.2)';
this.roundRect(ctx, x + w - 55, btnY, 45, 26, 13);
ctx.fill();
ctx.fillStyle = '#ef4444';
ctx.font = '11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('删除', x + w - 32, btnY + 17);
}
renderSimpleCard(ctx, item, x, y, w, h, index) {
ctx.fillStyle = 'rgba(255,255,255,0.05)';
this.roundRect(ctx, x, y, w, h, 12);
ctx.fill();
const coverW = 60, coverH = h - 16;
const colors = this.getGradientColors(index);
const coverGradient = ctx.createLinearGradient(x + 8, y + 8, x + 8 + coverW, y + 8 + coverH);
coverGradient.addColorStop(0, colors[0]);
coverGradient.addColorStop(1, colors[1]);
ctx.fillStyle = coverGradient;
this.roundRect(ctx, x + 8, y + 8, coverW, coverH, 8);
ctx.fill();
ctx.fillStyle = 'rgba(255,255,255,0.85)';
ctx.font = 'bold 9px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(item.category || '故事', x + 8 + coverW / 2, y + 8 + coverH / 2 + 3);
const textX = x + 78;
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'left';
// 记录Tab使用 storyTitle收藏Tab使用 storyTitle
const title = item.storyTitle || item.story_title || item.title || '未知';
ctx.fillText(this.truncateText(ctx, title, w - 150), textX, y + 28);
ctx.fillStyle = 'rgba(255,255,255,0.45)';
ctx.font = '11px sans-serif';
if (this.currentTab === 3) {
// 记录Tab只显示记录数量
ctx.fillText(`${item.recordCount || 0} 条记录`, textX, y + 50);
} else if (this.currentTab === 2 && item.versionCount > 1) {
// 收藏Tab显示版本数量
ctx.fillText(`${item.versionCount} 个版本`, textX, y + 50);
} else {
ctx.fillText(item.category || '', textX, y + 50);
}
// 查看按钮记录Tab/收藏Tab/作品Tab
const btnGradient = ctx.createLinearGradient(x + w - 58, y + 28, x + w - 10, y + 28);
btnGradient.addColorStop(0, '#ff6b6b');
btnGradient.addColorStop(1, '#ffd700');
ctx.fillStyle = btnGradient;
this.roundRect(ctx, x + w - 58, y + 28, 48, 26, 13);
ctx.fill();
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText('查看', x + w - 34, y + 45);
}
getGradientColors(index) {
const colors = [
['#ff758c', '#ff7eb3'],
['#667eea', '#764ba2'],
['#4facfe', '#00f2fe'],
['#43e97b', '#38f9d7'],
['#fa709a', '#fee140'],
['#a855f7', '#ec4899']
];
return colors[index % colors.length];
}
formatNumber(num) {
if (!num) return '0';
if (num >= 10000) return (num / 10000).toFixed(1) + 'w';
if (num >= 1000) return (num / 1000).toFixed(1) + 'k';
return num.toString();
}
truncateText(ctx, text, maxW) {
if (!text) return '';
if (ctx.measureText(text).width <= maxW) return text;
let t = text;
while (t.length > 0 && ctx.measureText(t + '...').width > maxW) t = t.slice(0, -1);
return t + '...';
}
roundRect(ctx, x, y, w, h, r) {
ctx.beginPath();
ctx.moveTo(x + r, y);
ctx.lineTo(x + w - r, y);
ctx.quadraticCurveTo(x + w, y, x + w, y + r);
ctx.lineTo(x + w, y + h - r);
ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
ctx.lineTo(x + r, y + h);
ctx.quadraticCurveTo(x, y + h, x, y + h - r);
ctx.lineTo(x, y + r);
ctx.quadraticCurveTo(x, y, x + r, y);
ctx.closePath();
}
onTouchStart(e) {
const touch = e.touches[0];
this.lastTouchY = touch.clientY;
this.touchStartY = touch.clientY;
this.isDragging = true;
this.scrollVelocity = 0;
this.hasMoved = false;
}
onTouchMove(e) {
if (!this.isDragging) return;
const touch = e.touches[0];
const deltaY = this.lastTouchY - touch.clientY;
if (Math.abs(touch.clientY - this.touchStartY) > 5) this.hasMoved = true;
this.scrollVelocity = deltaY;
this.scrollY += deltaY;
this.scrollY = Math.max(0, Math.min(this.scrollY, this.maxScrollY));
this.lastTouchY = touch.clientY;
}
onTouchEnd(e) {
this.isDragging = false;
if (this.hasMoved) return;
const touch = e.changedTouches[0];
const x = touch.clientX;
const y = touch.clientY;
// 返回
if (y < 50 && x < 80) {
this.main.sceneManager.switchScene('home');
return;
}
// 退出登录按钮
if (this.logoutBtnRect) {
const btn = this.logoutBtnRect;
if (x >= btn.x && x <= btn.x + btn.width && y >= btn.y && y <= btn.y + btn.height) {
this.confirmLogout();
return;
}
}
// 头像点击(修改头像)
if (this.avatarRect) {
const rect = this.avatarRect;
if (x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height) {
this.chooseAndUploadAvatar();
return;
}
}
// 昵称编辑按钮点击
if (this.editNicknameRect) {
const rect = this.editNicknameRect;
if (x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height) {
this.showEditNicknameDialog();
return;
}
}
// Tab切换
if (this.tabRects) {
for (const rect of this.tabRects) {
if (x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height) {
if (this.currentTab !== rect.index) {
this.currentTab = rect.index;
this.scrollY = 0;
this.recordViewMode = 'list'; // 切换 Tab 时重置记录视图模式
this.calculateMaxScroll();
// 切换到 AI 草稿 tab 时刷新数据
if (rect.index === 1) {
this.refreshDrafts();
}
}
return;
}
}
}
// 前往草稿箱按钮
if (this.createBtnRect && this.currentTab === 0) {
const btn = this.createBtnRect;
if (x >= btn.x && x <= btn.x + btn.width && y >= btn.y && y <= btn.y + btn.height) {
// 切换到草稿箱 Tab
this.currentTab = 1;
this.scrollY = 0;
this.calculateMaxScroll();
return;
}
}
// 卡片点击
this.handleCardClick(x, y);
}
handleCardClick(x, y) {
const list = this.getCurrentList();
const startY = 250;
const cardH = this.currentTab <= 1 ? 100 : 85;
const gap = 10;
const padding = 12;
const cardW = this.screenWidth - padding * 2;
// 记录 Tab 版本列表模式下,检测返回按钮
if (this.currentTab === 3 && this.recordViewMode === 'versions') {
if (this.versionBackBtnRect) {
const btn = this.versionBackBtnRect;
if (x >= btn.x && x <= btn.x + btn.width && y >= btn.y && y <= btn.y + btn.height) {
this.recordViewMode = 'list';
this.scrollY = 0;
this.calculateMaxScroll();
return;
}
}
}
const listStartY = (this.currentTab === 3 && this.recordViewMode === 'versions') ? startY + 45 : startY;
const adjustedY = y + this.scrollY;
const index = Math.floor((adjustedY - listStartY) / (cardH + gap));
if (index >= 0 && index < list.length) {
const item = list[index];
const storyId = item.story_id || item.storyId || item.id;
// 计算卡片内的相对位置
const cardY = listStartY + index * (cardH + gap) - this.scrollY;
const relativeY = y - cardY;
// AI草稿 Tab 的按钮检测
if (this.currentTab === 1) {
const btnY = 60;
const btnH = 26;
const btnStartX = padding + cardW - 170;
// 检测删除按钮点击(最右侧)
const deleteBtnX = padding + cardW - 55;
if (x >= deleteBtnX && x <= deleteBtnX + 45 && relativeY >= btnY && relativeY <= btnY + btnH) {
this.confirmDeleteDraft(item, index);
return;
}
// 检测播放按钮点击(仅已完成状态)
if (item.status === 'completed') {
if (x >= btnStartX && x <= btnStartX + 50 && relativeY >= btnY && relativeY <= btnY + btnH) {
this.main.sceneManager.switchScene('story', { storyId: item.storyId, draftId: item.id });
return;
}
// 检测发布按钮点击(仅未发布状态)
if (!item.publishedToCenter) {
const publishBtnX = btnStartX + 58;
if (x >= publishBtnX && x <= publishBtnX + 50 && relativeY >= btnY && relativeY <= btnY + btnH) {
this.confirmPublishDraft(item, index);
return;
}
}
}
// 点击卡片其他区域
if (item.status === 'completed') {
this.main.sceneManager.switchScene('story', { storyId: item.storyId, draftId: item.id });
} else if (item.status === 'failed') {
wx.showToast({ title: 'AI改写失败', icon: 'none' });
} else {
wx.showToast({ title: '正在生成中,请稍后', icon: 'none' });
}
return;
}
// 记录 Tab 处理
if (this.currentTab === 3) {
if (this.recordViewMode === 'list') {
// 故事列表模式:点击进入版本列表
this.showStoryVersions(item);
} else {
// 版本列表模式
const btnY = 28;
const btnH = 26;
// 检测删除按钮点击
const deleteBtnX = padding + cardW - 125;
if (x >= deleteBtnX && x <= deleteBtnX + 48 && relativeY >= btnY && relativeY <= btnY + btnH) {
this.confirmDeleteRecord(item, index);
return;
}
// 检测回放按钮点击
const replayBtnX = padding + cardW - 68;
if (x >= replayBtnX && x <= replayBtnX + 58 && relativeY >= btnY && relativeY <= btnY + btnH) {
this.startRecordReplay(item);
return;
}
// 点击卡片其他区域也进入回放
this.startRecordReplay(item);
}
return;
}
if (this.currentTab === 2) {
// 收藏 - 检查版本数量
if (item.versionCount > 1) {
// 多版本:弹出选择框
this.showVersionSelector(item);
} else if (item.versions && item.versions.length > 0) {
// 单版本:直接播放
const version = item.versions[0];
if (version.draftId) {
this.main.sceneManager.switchScene('story', { storyId: item.storyId, draftId: version.draftId });
} else {
this.main.sceneManager.switchScene('story', { storyId: item.storyId });
}
} else {
// 兼容旧数据
this.main.sceneManager.switchScene('story', { storyId });
}
return;
}
// 作品Tab - 点击按钮进入草稿播放或取消发布
if (this.currentTab === 0) {
const btnY = 55;
const btnH = 26;
// 检测取消发布按钮点击
const unpublishBtnX = padding + cardW - 125;
if (x >= unpublishBtnX && x <= unpublishBtnX + 60 && relativeY >= btnY && relativeY <= btnY + btnH) {
this.confirmUnpublishWork(item, index);
return;
}
// 检测播放按钮点击
const playBtnX = padding + cardW - 58;
if (x >= playBtnX && x <= playBtnX + 48 && relativeY >= btnY && relativeY <= btnY + btnH) {
this.main.sceneManager.switchScene('story', { storyId: item.storyId, draftId: item.id });
return;
}
// 点击卡片其他区域也进入播放
this.main.sceneManager.switchScene('story', { storyId: item.storyId, draftId: item.id });
}
}
}
// 显示故事的版本列表
async showStoryVersions(storyItem) {
const storyId = storyItem.story_id || storyItem.storyId || storyItem.id;
const storyTitle = storyItem.storyTitle || storyItem.story_title || storyItem.title || '未知故事';
try {
wx.showLoading({ title: '加载中...' });
const records = await this.main.userManager.getPlayRecords(storyId);
wx.hideLoading();
if (records && records.length > 0) {
this.selectedStoryInfo = { id: storyId, title: storyTitle };
this.selectedStoryRecords = records;
this.recordViewMode = 'versions';
this.scrollY = 0;
this.calculateMaxScroll();
} else {
wx.showToast({ title: '暂无游玩记录', icon: 'none' });
}
} catch (e) {
wx.hideLoading();
console.error('加载版本列表失败:', e);
wx.showToast({ title: '加载失败', icon: 'none' });
}
}
// 开始回放记录
async startRecordReplay(recordItem) {
const recordId = recordItem.id;
const storyId = this.selectedStoryInfo.id;
const draftId = recordItem.draftId; // AI草稿ID原故事为null
// 进入故事场景,传入 playRecordId 参数draftId用于回放草稿内容
this.main.sceneManager.switchScene('story', {
storyId,
playRecordId: recordId,
draftId // 回放草稿内容
});
}
// 确认删除草稿
confirmDeleteDraft(item, index) {
wx.showModal({
title: '删除草稿',
content: `确定要删除「${item.title || 'AI改写'}」吗?`,
confirmText: '删除',
confirmColor: '#ef4444',
cancelText: '取消',
success: async (res) => {
if (res.confirm) {
const userId = this.main.userManager.userId;
const success = await this.main.storyManager.deleteDraft(item.id, userId);
if (success) {
// 从列表中移除
this.drafts.splice(index, 1);
this.calculateMaxScroll();
wx.showToast({ title: '删除成功', icon: 'success' });
} else {
wx.showToast({ title: '删除失败', icon: 'none' });
}
}
}
});
}
// 确认发布草稿到创作中心
confirmPublishDraft(item, index) {
wx.showModal({
title: '发布到创作中心',
content: `确定要将「${item.title || 'AI改写'}」发布到创作中心吗?`,
confirmText: '发布',
confirmColor: '#22c55e',
cancelText: '取消',
success: async (res) => {
if (res.confirm) {
wx.showLoading({ title: '发布中...' });
const success = await this.main.userManager.publishDraft(item.id);
wx.hideLoading();
if (success) {
// 更新草稿箱状态
this.drafts[index].publishedToCenter = true;
// 同步添加到作品列表
this.myWorks.push({
id: item.id,
storyId: item.storyId,
storyTitle: item.storyTitle,
title: item.title,
draftType: item.draftType || 'rewrite',
createdAt: item.createdAt
});
this.stats.works = this.myWorks.length;
wx.showToast({ title: '发布成功', icon: 'success' });
} else {
wx.showToast({ title: '发布失败', icon: 'none' });
}
}
}
});
}
// 确认取消发布作品
confirmUnpublishWork(item, index) {
wx.showModal({
title: '取消发布',
content: `确定要将「${item.title || 'AI改写'}」从创作中心移除吗?草稿仍会保留在草稿箱中。`,
confirmText: '取消发布',
confirmColor: '#ef4444',
cancelText: '返回',
success: async (res) => {
if (res.confirm) {
wx.showLoading({ title: '处理中...' });
const success = await this.main.userManager.unpublishDraft(item.id);
wx.hideLoading();
if (success) {
// 从作品列表中移除
this.myWorks.splice(index, 1);
this.stats.works = this.myWorks.length;
this.calculateMaxScroll();
// 同步更新草稿箱状态
const draftIndex = this.drafts.findIndex(d => d.id === item.id);
if (draftIndex !== -1) {
this.drafts[draftIndex].publishedToCenter = false;
}
wx.showToast({ title: '已取消发布', icon: 'success' });
} else {
wx.showToast({ title: '操作失败', icon: 'none' });
}
}
}
});
}
// 显示版本选择弹窗收藏Tab用
showVersionSelector(item) {
const versions = item.versions || [];
const itemList = versions.map(v => {
if (v.type === 'original') {
return '原版故事';
} else if (v.type === 'rewrite') {
return `AI改写: ${v.title}`;
} else if (v.type === 'continue') {
return `AI续写: ${v.title}`;
}
return v.title;
});
wx.showActionSheet({
itemList,
success: (res) => {
const selectedVersion = versions[res.tapIndex];
if (selectedVersion) {
if (selectedVersion.draftId) {
this.main.sceneManager.switchScene('story', {
storyId: item.storyId,
draftId: selectedVersion.draftId
});
} else {
this.main.sceneManager.switchScene('story', { storyId: item.storyId });
}
}
}
});
}
// 确认删除游玩记录
confirmDeleteRecord(item, index) {
wx.showModal({
title: '删除记录',
content: `确定要删除这条「${item.endingName || '未知结局'}」的记录吗?`,
confirmText: '删除',
confirmColor: '#ef4444',
cancelText: '取消',
success: async (res) => {
if (res.confirm) {
const success = await this.main.userManager.deletePlayRecord(item.id);
if (success) {
// 从版本列表中移除
this.selectedStoryRecords.splice(index, 1);
this.calculateMaxScroll();
wx.showToast({ title: '删除成功', icon: 'success' });
// 如果删光了,返回故事列表
if (this.selectedStoryRecords.length === 0) {
this.recordViewMode = 'list';
// 从 progress 列表中也移除该故事
const storyId = this.selectedStoryInfo.id;
const idx = this.progress.findIndex(p => (p.story_id || p.storyId) === storyId);
if (idx >= 0) {
this.progress.splice(idx, 1);
}
}
} else {
wx.showToast({ title: '删除失败', icon: 'none' });
}
}
}
});
}
// 显示设置菜单
showSettingsMenu() {
wx.showActionSheet({
itemList: ['修改头像', '修改昵称', '退出登录'],
success: (res) => {
switch (res.tapIndex) {
case 0:
this.chooseAndUploadAvatar();
break;
case 1:
this.showEditNicknameDialog();
break;
case 2:
this.confirmLogout();
break;
}
}
});
}
// 显示头像选项
showAvatarOptions() {
wx.showActionSheet({
itemList: ['从相册选择', '取消'],
success: (res) => {
if (res.tapIndex === 0) {
this.chooseAndUploadAvatar();
}
}
});
}
// 选择并上传头像
chooseAndUploadAvatar() {
console.log('[ProfileScene] 开始选择头像');
wx.chooseMedia({
count: 1,
mediaType: ['image'],
sourceType: ['album', 'camera'],
sizeType: ['compressed'],
success: async (res) => {
console.log('[ProfileScene] 选择图片成功:', res);
const tempFilePath = res.tempFiles[0].tempFilePath;
wx.showLoading({ title: '上传中...' });
try {
// 上传图片到服务器
const uploadRes = await this.uploadAvatar(tempFilePath);
console.log('[ProfileScene] 上传结果:', uploadRes);
if (uploadRes && uploadRes.url) {
// 更新用户头像
const success = await this.main.userManager.updateProfile(
this.main.userManager.nickname,
uploadRes.url
);
wx.hideLoading();
if (success) {
// 重新加载头像
this.loadAvatarImage();
wx.showToast({ title: '头像更新成功', icon: 'success' });
} else {
wx.showToast({ title: '更新失败', icon: 'none' });
}
} else {
wx.hideLoading();
wx.showToast({ title: '上传失败', icon: 'none' });
}
} catch (error) {
wx.hideLoading();
console.error('[ProfileScene] 上传头像失败:', error);
wx.showToast({ title: '上传失败', icon: 'none' });
}
},
fail: (err) => {
console.error('[ProfileScene] 选择图片失败:', err);
if (err.errMsg && err.errMsg.indexOf('cancel') === -1) {
wx.showToast({ title: '选择图片失败', icon: 'none' });
}
}
});
}
// 上传头像到服务器
uploadAvatar(filePath) {
return new Promise((resolve, reject) => {
const token = this.main.userManager.token || '';
const baseUrl = 'http://172.20.10.8:8000'; // 与 http.js 保持一致
wx.uploadFile({
url: `${baseUrl}/api/upload/avatar`,
filePath: filePath,
name: 'file',
header: {
'Authorization': token ? `Bearer ${token}` : ''
},
success: (res) => {
try {
const data = JSON.parse(res.data);
if (data.code === 0) {
resolve(data.data);
} else {
reject(new Error(data.message || '上传失败'));
}
} catch (e) {
reject(e);
}
},
fail: reject
});
});
}
// 修改昵称弹窗
showEditNicknameDialog() {
console.log('[ProfileScene] 显示修改昵称弹窗');
const currentNickname = this.main.userManager.nickname || '';
// 微信小游戏使用 wx.showModal 的 editable 参数
wx.showModal({
title: '修改昵称',
editable: true,
placeholderText: '请输入新昵称',
success: async (res) => {
console.log('[ProfileScene] showModal 回调:', res);
if (res.confirm) {
const newNickname = (res.content || '').trim();
console.log('[ProfileScene] 新昵称:', newNickname);
if (!newNickname) {
wx.showToast({ title: '昵称不能为空', icon: 'none' });
return;
}
if (newNickname === currentNickname) {
wx.showToast({ title: '昵称未变更', icon: 'none' });
return;
}
wx.showLoading({ title: '保存中...' });
try {
const success = await this.main.userManager.updateProfile(
newNickname,
this.main.userManager.avatarUrl || ''
);
wx.hideLoading();
if (success) {
wx.showToast({ title: '修改成功', icon: 'success' });
} else {
wx.showToast({ title: '修改失败', icon: 'none' });
}
} catch (e) {
wx.hideLoading();
console.error('[ProfileScene] 修改昵称失败:', e);
wx.showToast({ title: '修改失败', icon: 'none' });
}
}
},
fail: (err) => {
console.error('[ProfileScene] showModal 失败:', err);
}
});
}
// 确认退出登录
confirmLogout() {
wx.showModal({
title: '确认退出',
content: '退出登录后需要重新授权登录,确定退出吗?',
confirmText: '退出',
confirmColor: '#e74c3c',
success: (res) => {
if (res.confirm) {
// 执行退出登录
this.main.userManager.logout();
// 停止草稿检查
this.main.stopDraftChecker();
// 跳转到登录页
this.main.sceneManager.switchScene('login');
wx.showToast({ title: '已退出登录', icon: 'success' });
}
}
});
}
}