|
@@ -9,9 +9,9 @@ import {
|
|
|
IonIcon
|
|
IonIcon
|
|
|
} from '@ionic/angular/standalone';
|
|
} from '@ionic/angular/standalone';
|
|
|
import { addIcons } from 'ionicons';
|
|
import { addIcons } from 'ionicons';
|
|
|
-import {
|
|
|
|
|
- arrowBackOutline,
|
|
|
|
|
- helpCircleOutline,
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ arrowBackOutline,
|
|
|
|
|
+ helpCircleOutline,
|
|
|
checkmark,
|
|
checkmark,
|
|
|
boatOutline,
|
|
boatOutline,
|
|
|
informationCircleOutline,
|
|
informationCircleOutline,
|
|
@@ -19,7 +19,8 @@ import {
|
|
|
chevronBackOutline,
|
|
chevronBackOutline,
|
|
|
chevronForwardOutline,
|
|
chevronForwardOutline,
|
|
|
createOutline,
|
|
createOutline,
|
|
|
- saveOutline
|
|
|
|
|
|
|
+ saveOutline,
|
|
|
|
|
+ addOutline
|
|
|
} from 'ionicons/icons';
|
|
} from 'ionicons/icons';
|
|
|
|
|
|
|
|
// 单词数据接口
|
|
// 单词数据接口
|
|
@@ -140,6 +141,16 @@ listSwipeDirection: 'left' | 'right' | null = null;
|
|
|
// 退出确认弹窗状态
|
|
// 退出确认弹窗状态
|
|
|
showExitModal: boolean = false;
|
|
showExitModal: boolean = false;
|
|
|
|
|
|
|
|
|
|
+ // 预设选项数据
|
|
|
|
|
+ styleOptions: string[] = ['童话', '青春', '悬疑', '励志', '科幻', '日常', '历史', '浪漫'];
|
|
|
|
|
+ difficultyOptions: string[] = ['小学', '初中', '高中', '大学', '雅思', '托福', '四级', '六级'];
|
|
|
|
|
+ genreOptions: string[] = ['记叙', '说明', '议论', '书信'];
|
|
|
|
|
+
|
|
|
|
|
+ // "其它"输入框显示状态
|
|
|
|
|
+ showOtherStyleInput: boolean = false;
|
|
|
|
|
+ showOtherDifficultyInput: boolean = false;
|
|
|
|
|
+ showOtherGenreInput: boolean = false;
|
|
|
|
|
+
|
|
|
// 生成状态
|
|
// 生成状态
|
|
|
isGenerating: boolean = false;
|
|
isGenerating: boolean = false;
|
|
|
|
|
|
|
@@ -164,15 +175,20 @@ listSwipeDirection: 'left' | 'right' | null = null;
|
|
|
currentWizardStep: number = 0;
|
|
currentWizardStep: number = 0;
|
|
|
|
|
|
|
|
wizardData: {
|
|
wizardData: {
|
|
|
- style: string;
|
|
|
|
|
|
|
+ styles: string[]; // 多选风格,最多3个
|
|
|
|
|
+ otherStyle: string; // "其它"输入的自定义风格
|
|
|
difficulty: string;
|
|
difficulty: string;
|
|
|
genre: string;
|
|
genre: string;
|
|
|
} = {
|
|
} = {
|
|
|
- style: '',
|
|
|
|
|
|
|
+ styles: [],
|
|
|
|
|
+ otherStyle: '',
|
|
|
difficulty: '',
|
|
difficulty: '',
|
|
|
genre: ''
|
|
genre: ''
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
+ // 风格多选的最大数量
|
|
|
|
|
+ readonly maxStyleCount = 3;
|
|
|
|
|
+
|
|
|
constructor(private router: Router, private wordStoryService: WordStoryService) {
|
|
constructor(private router: Router, private wordStoryService: WordStoryService) {
|
|
|
addIcons({
|
|
addIcons({
|
|
|
arrowBackOutline,
|
|
arrowBackOutline,
|
|
@@ -184,7 +200,8 @@ listSwipeDirection: 'left' | 'right' | null = null;
|
|
|
chevronBackOutline,
|
|
chevronBackOutline,
|
|
|
chevronForwardOutline,
|
|
chevronForwardOutline,
|
|
|
createOutline,
|
|
createOutline,
|
|
|
- saveOutline
|
|
|
|
|
|
|
+ saveOutline,
|
|
|
|
|
+ addOutline
|
|
|
});
|
|
});
|
|
|
// 从其他页通过 state 进入做题页时恢复故事
|
|
// 从其他页通过 state 进入做题页时恢复故事
|
|
|
const nav = this.router.getCurrentNavigation();
|
|
const nav = this.router.getCurrentNavigation();
|
|
@@ -763,11 +780,132 @@ listSwipeDirection: 'left' | 'right' | null = null;
|
|
|
|
|
|
|
|
/** ========== 向导相关方法 ========== */
|
|
/** ========== 向导相关方法 ========== */
|
|
|
|
|
|
|
|
|
|
+ // 判断风格是否已选中
|
|
|
|
|
+ isStyleSelected(style: string): boolean {
|
|
|
|
|
+ return this.wizardData.styles.includes(style);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 选择风格(多选,最多3个)
|
|
|
|
|
+ selectStyle(style: string) {
|
|
|
|
|
+ const index = this.wizardData.styles.indexOf(style);
|
|
|
|
|
+ if (index > -1) {
|
|
|
|
|
+ // 已选中,取消选择
|
|
|
|
|
+ this.wizardData.styles.splice(index, 1);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // 未选中,检查是否可以添加
|
|
|
|
|
+ if (this.wizardData.styles.length < this.maxStyleCount) {
|
|
|
|
|
+ this.wizardData.styles.push(style);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 关闭"其它"输入框并清空内容
|
|
|
|
|
+ this.showOtherStyleInput = false;
|
|
|
|
|
+ this.wizardData.otherStyle = '';
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 判断风格选项是否可选(用于禁用未选中的选项)
|
|
|
|
|
+ isStyleSelectable(style: string): boolean {
|
|
|
|
|
+ // 如果已选中,总是可以点击(取消选择)
|
|
|
|
|
+ if (this.isStyleSelected(style)) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ // 计算当前有效的选择数量:已确认的选项 + "其它"输入框有内容算1个
|
|
|
|
|
+ const effectiveCount = this.wizardData.styles.length +
|
|
|
|
|
+ (this.showOtherStyleInput && this.wizardData.otherStyle.trim() ? 1 : 0);
|
|
|
|
|
+ // 如果未选中,只有未达到上限时才可选
|
|
|
|
|
+ return effectiveCount < this.maxStyleCount;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 选择难度
|
|
|
|
|
+ selectDifficulty(difficulty: string) {
|
|
|
|
|
+ this.wizardData.difficulty = difficulty;
|
|
|
|
|
+ this.showOtherDifficultyInput = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 选择体裁
|
|
|
|
|
+ selectGenre(genre: string) {
|
|
|
|
|
+ this.wizardData.genre = genre;
|
|
|
|
|
+ this.showOtherGenreInput = false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 显示"其它"输入框
|
|
|
|
|
+ showOtherInput(type: 'style' | 'difficulty' | 'genre') {
|
|
|
|
|
+ if (type === 'style') {
|
|
|
|
|
+ // 计算当前有效的选择数量:已确认的选项 + "其它"输入框有内容算1个
|
|
|
|
|
+ const effectiveCount = this.wizardData.styles.length +
|
|
|
|
|
+ (this.showOtherStyleInput && this.wizardData.otherStyle.trim() ? 1 : 0);
|
|
|
|
|
+ // 检查是否还有名额添加"其它"
|
|
|
|
|
+ if (effectiveCount >= this.maxStyleCount) {
|
|
|
|
|
+ return; // 已满,不允许添加
|
|
|
|
|
+ }
|
|
|
|
|
+ this.showOtherStyleInput = true;
|
|
|
|
|
+ this.wizardData.otherStyle = '';
|
|
|
|
|
+ } else if (type === 'difficulty') {
|
|
|
|
|
+ this.showOtherDifficultyInput = true;
|
|
|
|
|
+ this.wizardData.difficulty = '';
|
|
|
|
|
+ } else if (type === 'genre') {
|
|
|
|
|
+ this.showOtherGenreInput = true;
|
|
|
|
|
+ this.wizardData.genre = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 确认"其它"风格输入
|
|
|
|
|
+ confirmOtherStyle() {
|
|
|
|
|
+ const trimmed = this.wizardData.otherStyle.trim();
|
|
|
|
|
+ if (trimmed) {
|
|
|
|
|
+ // 检查是否已存在相同风格
|
|
|
|
|
+ if (!this.wizardData.styles.includes(trimmed)) {
|
|
|
|
|
+ // 如果还没满,可以添加
|
|
|
|
|
+ if (this.wizardData.styles.length < this.maxStyleCount) {
|
|
|
|
|
+ this.wizardData.styles.push(trimmed);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ this.showOtherStyleInput = false;
|
|
|
|
|
+ this.wizardData.otherStyle = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 点击弹窗背景时,隐藏"其它"输入框,回到最初状态
|
|
|
|
|
+ onModalOverlayClick() {
|
|
|
|
|
+ // 如果任何一个"其它"输入框显示且有内容,不关闭
|
|
|
|
|
+ if (this.showOtherStyleInput && this.wizardData.otherStyle.trim()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.showOtherDifficultyInput && this.wizardData.difficulty.trim()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.showOtherGenreInput && this.wizardData.genre.trim()) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 隐藏并清除"其它"输入框
|
|
|
|
|
+ if (this.showOtherStyleInput) {
|
|
|
|
|
+ this.showOtherStyleInput = false;
|
|
|
|
|
+ this.wizardData.otherStyle = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.showOtherDifficultyInput) {
|
|
|
|
|
+ this.showOtherDifficultyInput = false;
|
|
|
|
|
+ this.wizardData.difficulty = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.showOtherGenreInput) {
|
|
|
|
|
+ this.showOtherGenreInput = false;
|
|
|
|
|
+ this.wizardData.genre = '';
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
// 检查当前步骤是否可以继续
|
|
// 检查当前步骤是否可以继续
|
|
|
get canProceedWizard(): boolean {
|
|
get canProceedWizard(): boolean {
|
|
|
const currentKey = this.wizardSteps[this.currentWizardStep].key;
|
|
const currentKey = this.wizardSteps[this.currentWizardStep].key;
|
|
|
|
|
+
|
|
|
|
|
+ // 风格步骤:至少选择1个,最多3个
|
|
|
|
|
+ if (currentKey === 'style') {
|
|
|
|
|
+ // 已选风格 >= 1,或者 "其它"输入框有内容
|
|
|
|
|
+ return this.wizardData.styles.length >= 1 ||
|
|
|
|
|
+ (this.showOtherStyleInput && this.wizardData.otherStyle.trim() !== '');
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 其他步骤:非空字符串
|
|
|
const value = this.wizardData[currentKey as keyof typeof this.wizardData];
|
|
const value = this.wizardData[currentKey as keyof typeof this.wizardData];
|
|
|
- return value !== undefined && value !== null && value.trim() !== '';
|
|
|
|
|
|
|
+ return typeof value === 'string' && value.trim() !== '';
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 下一步
|
|
// 下一步
|
|
@@ -797,7 +935,7 @@ listSwipeDirection: 'left' | 'right' | null = null;
|
|
|
|
|
|
|
|
// 重置向导状态
|
|
// 重置向导状态
|
|
|
this.currentWizardStep = 0;
|
|
this.currentWizardStep = 0;
|
|
|
- this.wizardData = { style: '', difficulty: '', genre: '' };
|
|
|
|
|
|
|
+ this.wizardData = { styles: [], otherStyle: '', difficulty: '', genre: '' };
|
|
|
this.showStyleModal = true;
|
|
this.showStyleModal = true;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -805,6 +943,9 @@ listSwipeDirection: 'left' | 'right' | null = null;
|
|
|
closeStyleModal() {
|
|
closeStyleModal() {
|
|
|
this.showStyleModal = false;
|
|
this.showStyleModal = false;
|
|
|
this.currentWizardStep = 0;
|
|
this.currentWizardStep = 0;
|
|
|
|
|
+ this.showOtherStyleInput = false;
|
|
|
|
|
+ this.showOtherDifficultyInput = false;
|
|
|
|
|
+ this.showOtherGenreInput = false;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 确认风格并生成故事
|
|
// 确认风格并生成故事
|
|
@@ -822,11 +963,15 @@ listSwipeDirection: 'left' | 'right' | null = null;
|
|
|
this.isGenerating = true;
|
|
this.isGenerating = true;
|
|
|
this.storyError = null;
|
|
this.storyError = null;
|
|
|
|
|
|
|
|
|
|
+ // 将风格数组转为逗号分隔的字符串
|
|
|
|
|
+ const styleString = this.wizardData.styles.join('、');
|
|
|
|
|
+
|
|
|
// 调用AI生成故事
|
|
// 调用AI生成故事
|
|
|
this.wordStoryService.generateStory(
|
|
this.wordStoryService.generateStory(
|
|
|
selectedWords,
|
|
selectedWords,
|
|
|
this.wizardData.difficulty || '高中水平',
|
|
this.wizardData.difficulty || '高中水平',
|
|
|
- this.wizardData.genre || '记叙文'
|
|
|
|
|
|
|
+ this.wizardData.genre || '记叙文',
|
|
|
|
|
+ styleString || undefined
|
|
|
).subscribe({
|
|
).subscribe({
|
|
|
next: (response) => {
|
|
next: (response) => {
|
|
|
this.storyResponse = response;
|
|
this.storyResponse = response;
|