feat: AI创作流程优化,添加userId参数和轮询状态
This commit is contained in:
@@ -345,7 +345,12 @@ export default class StoryManager {
|
|||||||
*/
|
*/
|
||||||
async createStory(params) {
|
async createStory(params) {
|
||||||
try {
|
try {
|
||||||
|
if (!params.userId) {
|
||||||
|
console.error('AI创作失败: 缺少userId');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
const result = await post('/stories/ai-create', {
|
const result = await post('/stories/ai-create', {
|
||||||
|
userId: params.userId,
|
||||||
genre: params.genre,
|
genre: params.genre,
|
||||||
keywords: params.keywords,
|
keywords: params.keywords,
|
||||||
protagonist: params.protagonist,
|
protagonist: params.protagonist,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
* AI创作中心场景
|
* AI创作中心场景
|
||||||
*/
|
*/
|
||||||
import BaseScene from './BaseScene';
|
import BaseScene from './BaseScene';
|
||||||
|
import { get, post } from '../utils/http';
|
||||||
|
|
||||||
export default class AICreateScene extends BaseScene {
|
export default class AICreateScene extends BaseScene {
|
||||||
constructor(main, params) {
|
constructor(main, params) {
|
||||||
@@ -31,6 +32,10 @@ export default class AICreateScene extends BaseScene {
|
|||||||
|
|
||||||
// 快捷标签
|
// 快捷标签
|
||||||
this.genreTags = ['都市言情', '古风宫廷', '悬疑推理', '校园青春', '修仙玄幻', '职场商战'];
|
this.genreTags = ['都市言情', '古风宫廷', '悬疑推理', '校园青春', '修仙玄幻', '职场商战'];
|
||||||
|
|
||||||
|
// 创作确认面板
|
||||||
|
this.showCreatePanel = false;
|
||||||
|
this.createPanelBtns = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
async init() {
|
async init() {
|
||||||
@@ -66,7 +71,10 @@ export default class AICreateScene extends BaseScene {
|
|||||||
} else if (this.currentTab === 1) {
|
} else if (this.currentTab === 1) {
|
||||||
contentHeight = 300 + this.publishedContinues.length * 90;
|
contentHeight = 300 + this.publishedContinues.length * 90;
|
||||||
} else {
|
} else {
|
||||||
contentHeight = 600;
|
// AI创作Tab:表单高度 + 已创作列表高度
|
||||||
|
const formHeight = 500;
|
||||||
|
const listHeight = this.createdStories ? this.createdStories.length * 90 : 0;
|
||||||
|
contentHeight = formHeight + listHeight + 100;
|
||||||
}
|
}
|
||||||
this.maxScrollY = Math.max(0, contentHeight - this.screenHeight + 200);
|
this.maxScrollY = Math.max(0, contentHeight - this.screenHeight + 200);
|
||||||
}
|
}
|
||||||
@@ -79,6 +87,11 @@ export default class AICreateScene extends BaseScene {
|
|||||||
this.renderQuotaBar(ctx);
|
this.renderQuotaBar(ctx);
|
||||||
this.renderTabs(ctx);
|
this.renderTabs(ctx);
|
||||||
this.renderContent(ctx);
|
this.renderContent(ctx);
|
||||||
|
|
||||||
|
// 创作确认面板(最上层)
|
||||||
|
if (this.showCreatePanel) {
|
||||||
|
this.renderCreatePanel(ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
renderBackground(ctx) {
|
renderBackground(ctx) {
|
||||||
@@ -286,6 +299,120 @@ export default class AICreateScene extends BaseScene {
|
|||||||
ctx.fillText('✨ 开始AI创作', this.screenWidth / 2, btnY + 32);
|
ctx.fillText('✨ 开始AI创作', this.screenWidth / 2, btnY + 32);
|
||||||
|
|
||||||
this.createBtnRect = { x: padding, y: btnY + this.scrollY, width: inputWidth, height: 50 };
|
this.createBtnRect = { x: padding, y: btnY + this.scrollY, width: inputWidth, height: 50 };
|
||||||
|
|
||||||
|
// 提示文字
|
||||||
|
const tipY = btnY + 75;
|
||||||
|
ctx.fillStyle = 'rgba(255,255,255,0.5)';
|
||||||
|
ctx.font = '12px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('创作完成后可在「个人中心 > 草稿箱」查看', this.screenWidth / 2, tipY);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderCreatedList(ctx, startY, list) {
|
||||||
|
const padding = 15;
|
||||||
|
const cardWidth = this.screenWidth - padding * 2;
|
||||||
|
const cardHeight = 80;
|
||||||
|
const cardGap = 10;
|
||||||
|
|
||||||
|
this.createdItemRects = [];
|
||||||
|
|
||||||
|
list.forEach((item, index) => {
|
||||||
|
const y = startY + index * (cardHeight + cardGap);
|
||||||
|
|
||||||
|
// 卡片背景
|
||||||
|
ctx.fillStyle = 'rgba(255,255,255,0.08)';
|
||||||
|
this.roundRect(ctx, padding, y, cardWidth, cardHeight, 12);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = 'bold 14px sans-serif';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
const title = item.title || '未命名故事';
|
||||||
|
ctx.fillText(title.length > 15 ? title.substring(0, 15) + '...' : title, padding + 15, y + 25);
|
||||||
|
|
||||||
|
// 状态标签
|
||||||
|
const isPending = item.status === 'pending';
|
||||||
|
const isFailed = item.status === 'failed';
|
||||||
|
const isCompleted = item.status === 'completed';
|
||||||
|
const isPublished = item.published_to_center;
|
||||||
|
|
||||||
|
let statusText = '草稿';
|
||||||
|
let statusColor = '#fbbf24';
|
||||||
|
if (isPublished) {
|
||||||
|
statusText = '已发布';
|
||||||
|
statusColor = '#10b981';
|
||||||
|
} else if (isCompleted) {
|
||||||
|
statusText = '已完成';
|
||||||
|
statusColor = '#60a5fa';
|
||||||
|
} else if (isPending) {
|
||||||
|
statusText = '创作中...';
|
||||||
|
statusColor = '#a855f7';
|
||||||
|
} else if (isFailed) {
|
||||||
|
statusText = '失败';
|
||||||
|
statusColor = '#ef4444';
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.fillStyle = statusColor;
|
||||||
|
ctx.font = '12px sans-serif';
|
||||||
|
ctx.fillText(statusText, padding + 15, y + 50);
|
||||||
|
|
||||||
|
// 按钮(只有完成状态才能操作)
|
||||||
|
if (isCompleted) {
|
||||||
|
const btnWidth = 50;
|
||||||
|
const btnHeight = 28;
|
||||||
|
const btnGap = 8;
|
||||||
|
let btnX = this.screenWidth - padding - btnWidth - 10;
|
||||||
|
const btnY = y + (cardHeight - btnHeight) / 2;
|
||||||
|
|
||||||
|
// 阅读按钮
|
||||||
|
const readGradient = ctx.createLinearGradient(btnX, btnY, btnX + btnWidth, btnY);
|
||||||
|
readGradient.addColorStop(0, '#a855f7');
|
||||||
|
readGradient.addColorStop(1, '#ec4899');
|
||||||
|
ctx.fillStyle = readGradient;
|
||||||
|
this.roundRect(ctx, btnX, btnY, btnWidth, btnHeight, 14);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = '11px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('阅读', btnX + btnWidth / 2, btnY + 18);
|
||||||
|
|
||||||
|
this.createdItemRects.push({
|
||||||
|
x: btnX,
|
||||||
|
y: btnY + this.scrollY,
|
||||||
|
width: btnWidth,
|
||||||
|
height: btnHeight,
|
||||||
|
action: 'preview',
|
||||||
|
item: item
|
||||||
|
});
|
||||||
|
|
||||||
|
// 发布按钮(未发布时显示)
|
||||||
|
if (!isPublished) {
|
||||||
|
btnX = btnX - btnWidth - btnGap;
|
||||||
|
const pubGradient = ctx.createLinearGradient(btnX, btnY, btnX + btnWidth, btnY);
|
||||||
|
pubGradient.addColorStop(0, '#10b981');
|
||||||
|
pubGradient.addColorStop(1, '#059669');
|
||||||
|
ctx.fillStyle = pubGradient;
|
||||||
|
this.roundRect(ctx, btnX, btnY, btnWidth, btnHeight, 14);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = '11px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('发布', btnX + btnWidth / 2, btnY + 18);
|
||||||
|
|
||||||
|
this.createdItemRects.push({
|
||||||
|
x: btnX,
|
||||||
|
y: btnY + this.scrollY,
|
||||||
|
width: btnWidth,
|
||||||
|
height: btnHeight,
|
||||||
|
action: 'publish',
|
||||||
|
item: item
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
renderTags(ctx, tags, startX, startY, type) {
|
renderTags(ctx, tags, startX, startY, type) {
|
||||||
@@ -550,6 +677,134 @@ export default class AICreateScene extends BaseScene {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderCreatePanel(ctx) {
|
||||||
|
const padding = 20;
|
||||||
|
const panelWidth = this.screenWidth - padding * 2;
|
||||||
|
const panelHeight = 380;
|
||||||
|
const panelX = padding;
|
||||||
|
const panelY = (this.screenHeight - panelHeight) / 2;
|
||||||
|
|
||||||
|
// 遮罩层
|
||||||
|
ctx.fillStyle = 'rgba(0, 0, 0, 0.85)';
|
||||||
|
ctx.fillRect(0, 0, this.screenWidth, this.screenHeight);
|
||||||
|
|
||||||
|
// 面板背景渐变
|
||||||
|
const panelGradient = ctx.createLinearGradient(panelX, panelY, panelX, panelY + panelHeight);
|
||||||
|
panelGradient.addColorStop(0, '#1a1a3e');
|
||||||
|
panelGradient.addColorStop(1, '#0d0d1a');
|
||||||
|
ctx.fillStyle = panelGradient;
|
||||||
|
this.roundRect(ctx, panelX, panelY, panelWidth, panelHeight, 20);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
// 面板边框渐变
|
||||||
|
const borderGradient = ctx.createLinearGradient(panelX, panelY, panelX + panelWidth, panelY);
|
||||||
|
borderGradient.addColorStop(0, '#a855f7');
|
||||||
|
borderGradient.addColorStop(1, '#ec4899');
|
||||||
|
ctx.strokeStyle = borderGradient;
|
||||||
|
ctx.lineWidth = 2;
|
||||||
|
this.roundRect(ctx, panelX, panelY, panelWidth, panelHeight, 20);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 标题栏
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = 'bold 18px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('✨ 确认创作', this.screenWidth / 2, panelY + 35);
|
||||||
|
|
||||||
|
// 配额提示
|
||||||
|
const remaining = this.quota.daily - this.quota.used + this.quota.purchased;
|
||||||
|
ctx.fillStyle = remaining > 0 ? 'rgba(255,255,255,0.6)' : 'rgba(255,100,100,0.8)';
|
||||||
|
ctx.font = '11px sans-serif';
|
||||||
|
ctx.textAlign = 'right';
|
||||||
|
ctx.fillText(`剩余次数:${remaining}`, panelX + panelWidth - 15, panelY + 35);
|
||||||
|
|
||||||
|
// 分隔线
|
||||||
|
const lineGradient = ctx.createLinearGradient(panelX + 20, panelY + 55, panelX + panelWidth - 20, panelY + 55);
|
||||||
|
lineGradient.addColorStop(0, 'transparent');
|
||||||
|
lineGradient.addColorStop(0.5, 'rgba(168,85,247,0.5)');
|
||||||
|
lineGradient.addColorStop(1, 'transparent');
|
||||||
|
ctx.strokeStyle = lineGradient;
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
ctx.beginPath();
|
||||||
|
ctx.moveTo(panelX + 20, panelY + 55);
|
||||||
|
ctx.lineTo(panelX + panelWidth - 20, panelY + 55);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
// 创作信息展示
|
||||||
|
let infoY = panelY + 85;
|
||||||
|
const lineHeight = 45;
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{ label: '题材', value: this.createForm.genre || '未选择' },
|
||||||
|
{ label: '关键词', value: this.createForm.keywords || '未填写' },
|
||||||
|
{ label: '主角设定', value: this.createForm.protagonist || '未填写' },
|
||||||
|
{ label: '核心冲突', value: this.createForm.conflict || '未填写' }
|
||||||
|
];
|
||||||
|
|
||||||
|
items.forEach((item, index) => {
|
||||||
|
const y = infoY + index * lineHeight;
|
||||||
|
|
||||||
|
// 标签
|
||||||
|
ctx.fillStyle = 'rgba(255,255,255,0.6)';
|
||||||
|
ctx.font = '13px sans-serif';
|
||||||
|
ctx.textAlign = 'left';
|
||||||
|
ctx.fillText(item.label + ':', panelX + 20, y);
|
||||||
|
|
||||||
|
// 值
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = '14px sans-serif';
|
||||||
|
let displayValue = item.value;
|
||||||
|
if (displayValue.length > 18) {
|
||||||
|
displayValue = displayValue.substring(0, 18) + '...';
|
||||||
|
}
|
||||||
|
ctx.fillText(displayValue, panelX + 85, y);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 消耗提示
|
||||||
|
ctx.fillStyle = 'rgba(255,200,100,0.8)';
|
||||||
|
ctx.font = '12px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('将消耗 1 次 AI 次数', this.screenWidth / 2, panelY + panelHeight - 85);
|
||||||
|
|
||||||
|
// 按钮区域
|
||||||
|
const btnWidth = (panelWidth - 50) / 2;
|
||||||
|
const btnHeight = 42;
|
||||||
|
const btnY = panelY + panelHeight - 60;
|
||||||
|
|
||||||
|
// 取消按钮
|
||||||
|
const cancelX = panelX + 15;
|
||||||
|
ctx.fillStyle = 'rgba(255,255,255,0.1)';
|
||||||
|
this.roundRect(ctx, cancelX, btnY, btnWidth, btnHeight, 21);
|
||||||
|
ctx.fill();
|
||||||
|
ctx.strokeStyle = 'rgba(255,255,255,0.3)';
|
||||||
|
ctx.lineWidth = 1;
|
||||||
|
this.roundRect(ctx, cancelX, btnY, btnWidth, btnHeight, 21);
|
||||||
|
ctx.stroke();
|
||||||
|
|
||||||
|
ctx.fillStyle = 'rgba(255,255,255,0.8)';
|
||||||
|
ctx.font = '14px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('取消', cancelX + btnWidth / 2, btnY + 27);
|
||||||
|
|
||||||
|
this.createPanelBtns.cancel = { x: cancelX, y: btnY, width: btnWidth, height: btnHeight };
|
||||||
|
|
||||||
|
// 确认按钮
|
||||||
|
const confirmX = panelX + panelWidth - btnWidth - 15;
|
||||||
|
const confirmGradient = ctx.createLinearGradient(confirmX, btnY, confirmX + btnWidth, btnY);
|
||||||
|
confirmGradient.addColorStop(0, '#a855f7');
|
||||||
|
confirmGradient.addColorStop(1, '#ec4899');
|
||||||
|
ctx.fillStyle = confirmGradient;
|
||||||
|
this.roundRect(ctx, confirmX, btnY, btnWidth, btnHeight, 21);
|
||||||
|
ctx.fill();
|
||||||
|
|
||||||
|
ctx.fillStyle = '#ffffff';
|
||||||
|
ctx.font = 'bold 14px sans-serif';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText('开始创作', confirmX + btnWidth / 2, btnY + 27);
|
||||||
|
|
||||||
|
this.createPanelBtns.confirm = { x: confirmX, y: btnY, width: btnWidth, height: btnHeight };
|
||||||
|
}
|
||||||
|
|
||||||
roundRect(ctx, x, y, width, height, radius) {
|
roundRect(ctx, x, y, width, height, radius) {
|
||||||
ctx.beginPath();
|
ctx.beginPath();
|
||||||
ctx.moveTo(x + radius, y);
|
ctx.moveTo(x + radius, y);
|
||||||
@@ -594,6 +849,12 @@ export default class AICreateScene extends BaseScene {
|
|||||||
const x = touch.clientX;
|
const x = touch.clientX;
|
||||||
const y = touch.clientY;
|
const y = touch.clientY;
|
||||||
|
|
||||||
|
// 创作确认面板优先处理
|
||||||
|
if (this.showCreatePanel) {
|
||||||
|
this.handleCreatePanelTouch(x, y);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 返回按钮
|
// 返回按钮
|
||||||
if (y < 60 && x < 80) {
|
if (y < 60 && x < 80) {
|
||||||
this.main.sceneManager.switchScene('home');
|
this.main.sceneManager.switchScene('home');
|
||||||
@@ -684,6 +945,43 @@ export default class AICreateScene extends BaseScene {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handlePreviewCreated(item) {
|
||||||
|
// 跳转到故事场景,播放AI创作的故事(使用 draftId)
|
||||||
|
this.main.sceneManager.switchScene('story', {
|
||||||
|
storyId: item.story_id,
|
||||||
|
draftId: item.id,
|
||||||
|
fromDrafts: true,
|
||||||
|
draftType: 'create' // 标记为AI创作类型
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async handlePublishCreated(item) {
|
||||||
|
wx.showModal({
|
||||||
|
title: '确认发布',
|
||||||
|
content: `确定要发布《${item.title || '未命名故事'}》吗?\n发布后可在"我的作品"中查看`,
|
||||||
|
success: async (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
wx.showLoading({ title: '发布中...', mask: true });
|
||||||
|
try {
|
||||||
|
const result = await post(`/stories/ai-create/${item.id}/publish`);
|
||||||
|
wx.hideLoading();
|
||||||
|
if (result && result.code === 0) {
|
||||||
|
wx.showToast({ title: '发布成功!', icon: 'success' });
|
||||||
|
// 刷新列表
|
||||||
|
this.loadData();
|
||||||
|
this.render();
|
||||||
|
} else {
|
||||||
|
wx.showToast({ title: result?.data?.message || '发布失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
wx.hideLoading();
|
||||||
|
wx.showToast({ title: '发布失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
isInRect(x, y, rect) {
|
isInRect(x, y, rect) {
|
||||||
return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height;
|
return x >= rect.x && x <= rect.x + rect.width && y >= rect.y && y <= rect.y + rect.height;
|
||||||
}
|
}
|
||||||
@@ -829,10 +1127,10 @@ export default class AICreateScene extends BaseScene {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const remaining = this.quota.daily - this.quota.used + this.quota.purchased;
|
const remaining = this.quota.daily - this.quota.used + this.quota.purchased;
|
||||||
if (remaining < 5) {
|
if (remaining < 1) {
|
||||||
wx.showModal({
|
wx.showModal({
|
||||||
title: '次数不足',
|
title: '次数不足',
|
||||||
content: 'AI创作需要5次配额,当前剩余' + remaining + '次',
|
content: 'AI创作需要1次配额,当前剩余' + remaining + '次',
|
||||||
confirmText: '获取更多',
|
confirmText: '获取更多',
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
if (res.confirm) this.showQuotaModal();
|
if (res.confirm) this.showQuotaModal();
|
||||||
@@ -841,30 +1139,122 @@ export default class AICreateScene extends BaseScene {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
wx.showModal({
|
// 显示创作确认面板
|
||||||
title: '确认创作',
|
this.showCreatePanel = true;
|
||||||
content: `题材:${this.createForm.genre}\n关键词:${this.createForm.keywords}\n\n将消耗5次AI次数`,
|
}
|
||||||
success: async (res) => {
|
|
||||||
if (res.confirm) {
|
handleCreatePanelTouch(x, y) {
|
||||||
wx.showLoading({ title: 'AI创作中...', mask: true });
|
// 点击取消
|
||||||
|
if (this.createPanelBtns.cancel && this.isInRect(x, y, this.createPanelBtns.cancel)) {
|
||||||
|
this.showCreatePanel = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 点击确认
|
||||||
|
if (this.createPanelBtns.confirm && this.isInRect(x, y, this.createPanelBtns.confirm)) {
|
||||||
|
this.showCreatePanel = false;
|
||||||
|
this.confirmCreate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async confirmCreate() {
|
||||||
|
wx.showLoading({ title: '提交中...', mask: true });
|
||||||
try {
|
try {
|
||||||
// TODO: 实现完整创作API
|
const userId = this.main?.userManager?.userId;
|
||||||
const result = await this.main.storyManager.createStory(this.createForm);
|
if (!userId) {
|
||||||
wx.hideLoading();
|
wx.hideLoading();
|
||||||
if (result) {
|
wx.showToast({ title: '请先登录', icon: 'none' });
|
||||||
this.quota.used += 5;
|
return;
|
||||||
wx.showToast({ title: '创作成功!', icon: 'success' });
|
}
|
||||||
// 跳转到新故事
|
const result = await this.main.storyManager.createStory({
|
||||||
setTimeout(() => {
|
...this.createForm,
|
||||||
this.main.sceneManager.switchScene('story', { storyId: result.storyId });
|
userId: userId
|
||||||
}, 1500);
|
});
|
||||||
|
|
||||||
|
wx.hideLoading();
|
||||||
|
|
||||||
|
const draftId = result?.data?.draftId || result?.draftId;
|
||||||
|
if (draftId) {
|
||||||
|
this.quota.used += 1;
|
||||||
|
// 显示提示框,用户可以选择等待或返回
|
||||||
|
wx.showModal({
|
||||||
|
title: '创作已提交',
|
||||||
|
content: 'AI正在创作故事,预计需要1-2分钟,完成后可在草稿箱查看',
|
||||||
|
confirmText: '等待结果',
|
||||||
|
cancelText: '返回',
|
||||||
|
success: (modalRes) => {
|
||||||
|
if (modalRes.confirm) {
|
||||||
|
// 用户选择等待,显示loading并轮询
|
||||||
|
wx.showLoading({ title: 'AI创作中...', mask: true });
|
||||||
|
this.pollCreateStatus(draftId);
|
||||||
|
}
|
||||||
|
// 用户选择返回,后台继续创作,稍后可在草稿箱查看
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
wx.showToast({ title: '创作失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
wx.hideLoading();
|
wx.hideLoading();
|
||||||
wx.showToast({ title: '创作失败', icon: 'none' });
|
wx.showToast({ title: '创作失败', icon: 'none' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮询AI创作状态
|
||||||
|
*/
|
||||||
|
async pollCreateStatus(draftId, retries = 0) {
|
||||||
|
const maxRetries = 60; // 最多等待5分钟(每5秒检查一次)
|
||||||
|
|
||||||
|
if (retries >= maxRetries) {
|
||||||
|
wx.hideLoading();
|
||||||
|
wx.showModal({
|
||||||
|
title: '创作超时',
|
||||||
|
content: '故事创作时间较长,请稍后在"AI创作"中查看',
|
||||||
|
showCancel: false
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await get(`/stories/ai-create/${draftId}/status`);
|
||||||
|
const status = res?.data || res; // 兼容两种格式
|
||||||
|
|
||||||
|
if (status && status.isCompleted) {
|
||||||
|
wx.hideLoading();
|
||||||
|
wx.showModal({
|
||||||
|
title: '创作成功!',
|
||||||
|
content: `故事《${status.title}》已保存到草稿箱`,
|
||||||
|
confirmText: '去查看',
|
||||||
|
cancelText: '继续创作',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
// 刷新当前页面数据
|
||||||
|
this.loadData();
|
||||||
|
this.render();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
} else if (status && (status.status === -1 || status.isFailed)) {
|
||||||
|
wx.hideLoading();
|
||||||
|
wx.showModal({
|
||||||
|
title: '创作失败',
|
||||||
|
content: status.errorMessage || '故事创作失败,请检查输入后重试',
|
||||||
|
showCancel: false
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 继续轮询
|
||||||
|
setTimeout(() => {
|
||||||
|
this.pollCreateStatus(draftId, retries + 1);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.error('轮询状态失败:', e);
|
||||||
|
// 请求失败,继续重试
|
||||||
|
setTimeout(() => {
|
||||||
|
this.pollCreateStatus(draftId, retries + 1);
|
||||||
|
}, 5000);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user