2026-03-03 16:57:49 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 故事数据管理器
|
|
|
|
|
|
*/
|
|
|
|
|
|
import { get, post } from '../utils/http';
|
|
|
|
|
|
|
|
|
|
|
|
export default class StoryManager {
|
|
|
|
|
|
constructor() {
|
|
|
|
|
|
this.storyList = [];
|
|
|
|
|
|
this.currentStory = null;
|
|
|
|
|
|
this.currentNodeKey = 'start';
|
|
|
|
|
|
this.categories = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载故事列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadStoryList(options = {}) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.storyList = await get('/stories', options);
|
|
|
|
|
|
return this.storyList;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载故事列表失败:', error);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载热门故事
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadHotStories(limit = 10) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
return await get('/stories/hot', { limit });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载热门故事失败:', error);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载分类列表
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadCategories() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.categories = await get('/stories/categories');
|
|
|
|
|
|
return this.categories;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载分类失败:', error);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载故事详情
|
|
|
|
|
|
*/
|
|
|
|
|
|
async loadStoryDetail(storyId) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
this.currentStory = await get(`/stories/${storyId}`);
|
|
|
|
|
|
this.currentNodeKey = 'start';
|
|
|
|
|
|
|
|
|
|
|
|
// 记录游玩次数
|
|
|
|
|
|
await post(`/stories/${storyId}/play`);
|
|
|
|
|
|
|
|
|
|
|
|
return this.currentStory;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('加载故事详情失败:', error);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取当前节点
|
|
|
|
|
|
*/
|
|
|
|
|
|
getCurrentNode() {
|
|
|
|
|
|
if (!this.currentStory || !this.currentStory.nodes) return null;
|
|
|
|
|
|
return this.currentStory.nodes[this.currentNodeKey];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 选择选项,前进到下一个节点
|
|
|
|
|
|
*/
|
|
|
|
|
|
selectChoice(choiceIndex) {
|
|
|
|
|
|
const currentNode = this.getCurrentNode();
|
|
|
|
|
|
if (!currentNode || !currentNode.choices || !currentNode.choices[choiceIndex]) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const choice = currentNode.choices[choiceIndex];
|
|
|
|
|
|
this.currentNodeKey = choice.nextNodeKey;
|
|
|
|
|
|
|
|
|
|
|
|
return this.getCurrentNode();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 检查当前节点是否为结局
|
|
|
|
|
|
*/
|
|
|
|
|
|
isEnding() {
|
|
|
|
|
|
const currentNode = this.getCurrentNode();
|
|
|
|
|
|
return currentNode && currentNode.is_ending;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取结局信息
|
|
|
|
|
|
*/
|
|
|
|
|
|
getEndingInfo() {
|
|
|
|
|
|
const currentNode = this.getCurrentNode();
|
|
|
|
|
|
if (!currentNode || !currentNode.is_ending) return null;
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
name: currentNode.ending_name,
|
|
|
|
|
|
score: currentNode.ending_score,
|
|
|
|
|
|
type: currentNode.ending_type,
|
|
|
|
|
|
content: currentNode.content
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 重置故事进度
|
|
|
|
|
|
*/
|
|
|
|
|
|
resetStory() {
|
|
|
|
|
|
this.currentNodeKey = 'start';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 点赞故事
|
|
|
|
|
|
*/
|
|
|
|
|
|
async likeStory(like = true) {
|
|
|
|
|
|
if (!this.currentStory) return;
|
|
|
|
|
|
await post(`/stories/${this.currentStory.id}/like`, { like });
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* AI改写结局
|
|
|
|
|
|
*/
|
|
|
|
|
|
async rewriteEnding(storyId, ending, prompt) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await post(`/stories/${storyId}/rewrite`, {
|
|
|
|
|
|
ending_name: ending?.name,
|
|
|
|
|
|
ending_content: ending?.content,
|
|
|
|
|
|
prompt: prompt
|
2026-03-05 15:57:51 +08:00
|
|
|
|
}, { timeout: 60000 });
|
2026-03-03 16:57:49 +08:00
|
|
|
|
return result;
|
|
|
|
|
|
} catch (error) {
|
2026-03-05 15:57:51 +08:00
|
|
|
|
console.error('AI改写失败:', error?.errMsg || error?.message || JSON.stringify(error));
|
2026-03-03 16:57:49 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-03 17:06:08 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* AI续写故事
|
|
|
|
|
|
*/
|
|
|
|
|
|
async continueStory(storyId, prompt) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await post(`/stories/${storyId}/continue`, {
|
|
|
|
|
|
current_node_key: this.currentNodeKey,
|
|
|
|
|
|
prompt: prompt
|
|
|
|
|
|
});
|
|
|
|
|
|
return result;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('AI续写失败:', error);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* AI创作新故事
|
|
|
|
|
|
*/
|
|
|
|
|
|
async createStory(params) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const result = await post('/stories/ai-create', {
|
|
|
|
|
|
genre: params.genre,
|
|
|
|
|
|
keywords: params.keywords,
|
|
|
|
|
|
protagonist: params.protagonist,
|
|
|
|
|
|
conflict: params.conflict
|
|
|
|
|
|
});
|
|
|
|
|
|
return result;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('AI创作失败:', error);
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-03-03 16:57:49 +08:00
|
|
|
|
}
|