feat: 游玩记录支持区分原故事和AI草稿,已下架草稿显示标签

This commit is contained in:
wangwuww111
2026-03-13 12:28:42 +08:00
parent 411110ce0c
commit 4a69bf2711
8 changed files with 449 additions and 99 deletions

View File

@@ -73,18 +73,21 @@ export default class ProfileScene extends BaseScene {
if (this.main.userManager.isLoggedIn) {
try {
const userId = this.main.userManager.userId;
this.myWorks = await this.main.userManager.getMyWorks?.() || [];
// 加载已发布到创作中心的作品(改写+续写)
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.play_count || 0), 0);
this.stats.totalLikes = this.myWorks.reduce((sum, w) => sum + (w.like_count || 0), 0);
this.stats.earnings = this.myWorks.reduce((sum, w) => sum + (w.earnings || 0), 0);
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);
}
@@ -333,25 +336,25 @@ export default class ProfileScene extends BaseScene {
ctx.fillStyle = 'rgba(255,255,255,0.35)';
ctx.font = '13px sans-serif';
ctx.textAlign = 'center';
const emptyTexts = ['还没有发布作品,去创作吧', '草稿箱空空如也', '还没有收藏的故事', '还没有游玩记录'];
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 - 50, btnY, 100, 36, 18);
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 - 50, y: btnY, width: 100, height: 36 };
ctx.fillText('前往草稿箱', this.screenWidth / 2, btnY + 23);
this.createBtnRect = { x: this.screenWidth / 2 - 55, y: btnY, width: 110, height: 36 };
}
ctx.restore();
@@ -431,7 +434,10 @@ export default class ProfileScene extends BaseScene {
// 渲染单条游玩记录版本卡片
renderRecordVersionCard(ctx, item, x, y, w, h, index) {
ctx.fillStyle = 'rgba(255,255,255,0.05)';
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();
@@ -441,34 +447,49 @@ export default class ProfileScene extends BaseScene {
const circleR = 18;
const colors = this.getGradientColors(index);
const circleGradient = ctx.createLinearGradient(circleX - circleR, circleY - circleR, circleX + circleR, circleY + circleR);
circleGradient.addColorStop(0, colors[0]);
circleGradient.addColorStop(1, colors[1]);
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 = '#ffffff';
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 = '#ffffff';
// 结局名称(只显示结局,不显示草稿标题)
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, w - 150), textX, y + 28);
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 = 'rgba(255,255,255,0.45)';
// 游玩时间(如果是草稿,显示草稿标题)
ctx.fillStyle = isUnpublished ? 'rgba(255,255,255,0.25)' : 'rgba(255,255,255,0.45)';
ctx.font = '11px sans-serif';
ctx.textAlign = 'left';
const timeText = item.createdAt ? this.formatDateTime(item.createdAt) : '';
ctx.fillText(timeText, textX, y + 52);
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)';
@@ -481,8 +502,8 @@ export default class ProfileScene extends BaseScene {
// 回放按钮
const btnGradient = ctx.createLinearGradient(x + w - 68, y + 28, x + w - 10, y + 28);
btnGradient.addColorStop(0, '#ff6b6b');
btnGradient.addColorStop(1, '#ffd700');
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();
@@ -526,10 +547,12 @@ export default class ProfileScene extends BaseScene {
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(item.category || '故事', x + 8 + coverW / 2, y + 8 + coverH / 2 + 3);
ctx.fillText(typeText, x + 8 + coverW / 2, y + 8 + coverH / 2 + 3);
const textX = x + 88;
const maxW = w - 100;
@@ -538,46 +561,55 @@ export default class ProfileScene extends BaseScene {
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'left';
ctx.fillText(this.truncateText(ctx, item.title || '未命名', maxW - 60), textX, y + 25);
const title = item.title || item.storyTitle || '未命名';
ctx.fillText(this.truncateText(ctx, title, maxW - 60), textX, y + 25);
// 审核状态标签
const statusMap = {
0: { text: '草稿', color: '#888888' },
1: { text: '审核中', color: '#f59e0b' },
2: { text: '已发布', color: '#22c55e' },
3: { text: '已下架', color: '#ef4444' },
4: { text: '被拒绝', color: '#ef4444' }
};
const status = statusMap[item.status] || statusMap[0];
const statusW = ctx.measureText(status.text).width + 12;
ctx.fillStyle = status.color + '33';
this.roundRect(ctx, textX + ctx.measureText(this.truncateText(ctx, item.title || '未命名', maxW - 60)).width + 8, y + 12, statusW, 18, 9);
// 已发布标签
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 = status.color;
ctx.fillStyle = '#22c55e';
ctx.font = 'bold 10px sans-serif';
ctx.fillText(status.text, textX + ctx.measureText(this.truncateText(ctx, item.title || '未命名', maxW - 60)).width + 8 + statusW / 2, y + 24);
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(` ${this.formatNumber(item.play_count || 0)}`, textX, y + 50);
ctx.fillText(`${this.formatNumber(item.like_count || 0)}`, textX + 55, y + 50);
ctx.fillText(`💰 ${(item.earnings || 0).toFixed(1)}`, textX + 105, y + 50);
ctx.fillText(`原故事: ${item.storyTitle || ''}`, textX, y + 48);
// 操作按钮
const btnY = y + 65;
const btns = ['编辑', '数据'];
btns.forEach((btn, i) => {
const btnX = textX + i * 55;
ctx.fillStyle = 'rgba(255,255,255,0.1)';
this.roundRect(ctx, btnX, btnY, 48, 24, 12);
ctx.fill();
ctx.fillStyle = 'rgba(255,255,255,0.7)';
ctx.font = '11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(btn, btnX + 24, btnY + 16);
});
// 创建时间
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) {
@@ -719,7 +751,7 @@ export default class ProfileScene extends BaseScene {
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 14px sans-serif';
ctx.textAlign = 'left';
// 记录Tab使用 storyTitle收藏Tab使用 story_title
// 记录Tab使用 storyTitle收藏Tab使用 storyTitle
const title = item.storyTitle || item.story_title || item.title || '未知';
ctx.fillText(this.truncateText(ctx, title, w - 150), textX, y + 28);
@@ -728,11 +760,14 @@ export default class ProfileScene extends BaseScene {
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/收藏Tab/作品Tab
const btnGradient = ctx.createLinearGradient(x + w - 58, y + 28, x + w - 10, y + 28);
btnGradient.addColorStop(0, '#ff6b6b');
btnGradient.addColorStop(1, '#ffd700');
@@ -742,7 +777,7 @@ export default class ProfileScene extends BaseScene {
ctx.fillStyle = '#ffffff';
ctx.font = 'bold 11px sans-serif';
ctx.textAlign = 'center';
ctx.fillText(this.currentTab === 3 ? '查看' : '继续', x + w - 34, y + 45);
ctx.fillText('查看', x + w - 34, y + 45);
}
getGradientColors(index) {
@@ -867,11 +902,14 @@ export default class ProfileScene extends BaseScene {
}
}
// 创作按钮
// 前往草稿箱按钮
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) {
this.main.sceneManager.switchScene('aiCreate');
// 切换到草稿箱 Tab
this.currentTab = 1;
this.scrollY = 0;
this.calculateMaxScroll();
return;
}
}
@@ -985,17 +1023,54 @@ export default class ProfileScene extends BaseScene {
}
if (this.currentTab === 2) {
// 收藏 - 跳转播放
this.main.sceneManager.switchScene('story', { storyId });
// 收藏 - 检查版本数量
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 });
}
// 作品Tab的按钮操作需要更精确判断暂略
}
}
// 显示故事的版本列表
async showStoryVersions(storyItem) {
const storyId = storyItem.story_id || storyItem.storyId || storyItem.id;
const storyTitle = storyItem.story_title || storyItem.title || '未知故事';
const storyTitle = storyItem.storyTitle || storyItem.story_title || storyItem.title || '未知故事';
try {
wx.showLoading({ title: '加载中...' });
@@ -1022,11 +1097,13 @@ export default class ProfileScene extends BaseScene {
async startRecordReplay(recordItem) {
const recordId = recordItem.id;
const storyId = this.selectedStoryInfo.id;
const draftId = recordItem.draftId; // AI草稿ID原故事为null
// 进入故事场景,传入 playRecordId 参数
// 进入故事场景,传入 playRecordId 参数draftId用于回放草稿内容
this.main.sceneManager.switchScene('story', {
storyId,
playRecordId: recordId
playRecordId: recordId,
draftId // 回放草稿内容
});
}
@@ -1069,8 +1146,18 @@ export default class ProfileScene extends BaseScene {
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' });
@@ -1080,6 +1167,70 @@ export default class ProfileScene extends BaseScene {
});
}
// 确认取消发布作品
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({