15270821319 5 months ago
parent
commit
a035296d74
39 changed files with 1961 additions and 32 deletions
  1. 6 0
      AiStudy-app/src/agent/README.md
  2. 18 0
      AiStudy-app/src/agent/agent-user-input/agent-user-input.component.html
  3. 0 0
      AiStudy-app/src/agent/agent-user-input/agent-user-input.component.scss
  4. 22 0
      AiStudy-app/src/agent/agent-user-input/agent-user-input.component.spec.ts
  5. 38 0
      AiStudy-app/src/agent/agent-user-input/agent-user-input.component.ts
  6. 28 0
      AiStudy-app/src/agent/agent.input.ts
  7. 17 0
      AiStudy-app/src/agent/agent.json.ts
  8. 19 0
      AiStudy-app/src/agent/agent.start.ts
  9. 15 0
      AiStudy-app/src/agent/agent.task.ts
  10. 64 0
      AiStudy-app/src/agent/tasks/inquiry/1.inquiry-user-story.ts
  11. 79 0
      AiStudy-app/src/agent/tasks/inquiry/2.inquiry-doctor-question.ts
  12. 141 0
      AiStudy-app/src/agent/tasks/inquiry/3.inquiry-user-answer.ts
  13. 43 0
      AiStudy-app/src/agent/tasks/learning-plan/1.collect-basic-info.ts
  14. 62 0
      AiStudy-app/src/agent/tasks/learning-plan/2.analyze-requirements.ts
  15. 72 0
      AiStudy-app/src/agent/tasks/learning-plan/3.generate-plan.ts
  16. 55 0
      AiStudy-app/src/agent/tasks/poem/poem-desc.ts
  17. 43 0
      AiStudy-app/src/agent/tasks/poem/poem-picture.ts
  18. 22 0
      AiStudy-app/src/app/app.routes.ts
  19. 46 0
      AiStudy-app/src/app/pages/custom-teacher/custom-teacher-form/custom-teacher-form.page.html
  20. 24 0
      AiStudy-app/src/app/pages/custom-teacher/custom-teacher-form/custom-teacher-form.page.scss
  21. 140 0
      AiStudy-app/src/app/pages/custom-teacher/custom-teacher-form/custom-teacher-form.page.ts
  22. 31 0
      AiStudy-app/src/app/pages/custom-teacher/custom-teacher.page.html
  23. 32 0
      AiStudy-app/src/app/pages/custom-teacher/custom-teacher.page.scss
  24. 121 0
      AiStudy-app/src/app/pages/custom-teacher/custom-teacher.page.ts
  25. 94 0
      AiStudy-app/src/app/pages/learning-design/learning-design.page.html
  26. 98 0
      AiStudy-app/src/app/pages/learning-design/learning-design.page.scss
  27. 132 0
      AiStudy-app/src/app/pages/learning-design/learning-design.page.ts
  28. 23 0
      AiStudy-app/src/app/pages/learning-history/learning-history.page.html
  29. 28 0
      AiStudy-app/src/app/pages/learning-history/learning-history.page.scss
  30. 84 0
      AiStudy-app/src/app/pages/learning-history/learning-history.page.ts
  31. 120 0
      AiStudy-app/src/app/services/database.service.ts
  32. 9 2
      AiStudy-app/src/app/tab1/tab1.page.html
  33. 15 0
      AiStudy-app/src/app/tab1/tab1.page.scss
  34. 193 21
      AiStudy-app/src/app/tab1/tab1.page.ts
  35. 2 2
      AiStudy-app/src/app/tab2/tab2.page.html
  36. 10 3
      AiStudy-app/src/app/tab2/tab2.page.ts
  37. 3 3
      AiStudy-app/src/main.ts
  38. 5 1
      AiStudy-app/tsconfig.json
  39. 7 0
      AiStudy-server/server.js

+ 6 - 0
AiStudy-app/src/agent/README.md

@@ -0,0 +1,6 @@
+
+
+# 技术设计
+- AgentTaskStep类 智能体任务的某一个步骤(简单任务)
+- TaskExecutor类 任务执行器
+- AgentTaskComponent类 展示任务进行进度的面板

+ 18 - 0
AiStudy-app/src/agent/agent-user-input/agent-user-input.component.html

@@ -0,0 +1,18 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-button color="medium" (click)="cancel()">取消</ion-button>
+    </ion-buttons>
+    <ion-title>用户输入</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="confirm()" [strong]="true">确认</ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+<ion-content class="ion-padding">
+  @for(field of fieldsArray;track field.name){
+    <ion-item>
+      <ion-input labelPlacement="stacked" label="请输入 {{field.name}}" [(ngModel)]="inputData[field.name]" placeholder="{{field.desc}}"></ion-input>
+    </ion-item>
+  }
+</ion-content>

+ 0 - 0
AiStudy-app/src/agent/agent-user-input/agent-user-input.component.scss


+ 22 - 0
AiStudy-app/src/agent/agent-user-input/agent-user-input.component.spec.ts

@@ -0,0 +1,22 @@
+import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
+
+import { AgentUserInputComponent } from './agent-user-input.component';
+
+describe('AgentUserInputComponent', () => {
+  let component: AgentUserInputComponent;
+  let fixture: ComponentFixture<AgentUserInputComponent>;
+
+  beforeEach(waitForAsync(() => {
+    TestBed.configureTestingModule({
+      imports: [AgentUserInputComponent],
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(AgentUserInputComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  }));
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 38 - 0
AiStudy-app/src/agent/agent-user-input/agent-user-input.component.ts

@@ -0,0 +1,38 @@
+import { Component, OnInit, Input } from '@angular/core';
+import { ModalController, IonHeader, IonContent, IonInput, IonToolbar, IonItem, IonButtons, IonButton, IonTitle } from '@ionic/angular/standalone';
+import { FormsModule, ReactiveFormsModule } from '@angular/forms';
+export interface AgentUserInputField{
+  name:string, // 字段名称
+  type:string, // 输入类型
+  desc:string, // 字段说明
+}
+@Component({
+  selector: 'agent-user-input',
+  templateUrl: './agent-user-input.component.html',
+  styleUrls: ['./agent-user-input.component.scss'],
+  standalone: true,
+  imports:[
+    IonHeader,IonContent,IonInput,IonToolbar,IonItem,IonButtons,IonButton,
+    IonTitle,
+    FormsModule,ReactiveFormsModule,
+  ]
+})
+export class AgentUserInputComponent  implements OnInit {
+
+  @Input()
+  fieldsArray:AgentUserInputField[]=[]
+
+  @Input()
+  inputData:any = {}
+
+  constructor(private modalCtrl: ModalController) {}
+  ngOnInit(){
+  }
+  cancel() {
+    return this.modalCtrl.dismiss(null, 'cancel');
+  }
+
+  confirm() {
+    return this.modalCtrl.dismiss(this.inputData, 'confirm');
+  }
+}

+ 28 - 0
AiStudy-app/src/agent/agent.input.ts

@@ -0,0 +1,28 @@
+import { ModalController } from '@ionic/angular/standalone';
+import { AgentUserInputComponent } from './agent-user-input/agent-user-input.component';
+
+export interface AgentUserInputField {
+    name: string;
+    type: string;
+    desc: string;
+}
+
+export async function getUserInput(modalCtrl: ModalController, options: {
+    fieldsArray: AgentUserInputField[]
+}): Promise<any> {
+    const modal = await modalCtrl.create({
+        component: AgentUserInputComponent,
+        componentProps: {
+            fieldsArray: options.fieldsArray,
+            inputData: {}
+        }
+    });
+
+    await modal.present();
+    const { data, role } = await modal.onWillDismiss();
+
+    if (role === 'confirm') {
+        return data;
+    }
+    return null;
+} 

+ 17 - 0
AiStudy-app/src/agent/agent.json.ts

@@ -0,0 +1,17 @@
+export function extactAndParseJsonFromString(text: string): any {
+    try {
+        // 尝试直接解析
+        return JSON.parse(text);
+    } catch (e) {
+        try {
+            // 尝试提取 JSON 字符串
+            const jsonMatch = text.match(/\{[\s\S]*\}/);
+            if (jsonMatch) {
+                return JSON.parse(jsonMatch[0]);
+            }
+        } catch (e2) {
+            console.error('Failed to parse JSON:', e2);
+        }
+    }
+    return null;
+} 

+ 19 - 0
AiStudy-app/src/agent/agent.start.ts

@@ -0,0 +1,19 @@
+import { AgentTaskStep } from './agent.task';
+
+export async function TaskExecutor(tasks: AgentTaskStep[]): Promise<boolean> {
+    for (let task of tasks) {
+        if (task.handle) {
+            try {
+                const result = await task.handle();
+                if (!result) {
+                    return false;
+                }
+            } catch (error: any) {
+                console.error(`Task ${task.title} failed:`, error);
+                task.error = error?.message || 'Unknown error';
+                return false;
+            }
+        }
+    }
+    return true;
+} 

+ 15 - 0
AiStudy-app/src/agent/agent.task.ts

@@ -0,0 +1,15 @@
+export class AgentTaskStep {
+    title: string;
+    progress: number = 0;
+    error?: string;
+    shareData: any;
+    handle?: () => Promise<boolean>;
+
+    constructor(options: {
+        title: string;
+        shareData: any;
+    }) {
+        this.title = options.title;
+        this.shareData = options.shareData;
+    }
+} 

+ 64 - 0
AiStudy-app/src/agent/tasks/inquiry/1.inquiry-user-story.ts

@@ -0,0 +1,64 @@
+import { AgentTaskStep } from 'src/agent/agent.task';
+import { getUserInput } from 'src/agent/agent.input';
+import { ModalController } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+import { extactAndParseJsonFromString } from 'src/agent/agent.json';
+
+export function TaskInqueryUserStory(options:{
+    modalCtrl:ModalController
+    shareData:any}
+    ):AgentTaskStep{
+        let task1 = new AgentTaskStep({title:"导诊:根据患者描述导诊对应科室",shareData:options.shareData})
+        task1.handle = ()=>{
+            return new Promise(async (resolve,reject)=>{
+                // 获取用户输入的诗词
+                let userInput = await getUserInput(options.modalCtrl,{fieldsArray:[
+                    {name:"症状口述",type:"text",desc:"描述下感觉到的症状或不适。"}
+                ]});
+
+                if(!userInput?.['症状口述']){
+                    task1.error = "无症状口述,无法判断科室"
+                    resolve(false);
+                }
+
+                // 文本生成
+                let PromptTemplate = `您是一名专业的医护人员,在门诊大厅负责导诊服务,请您根据患者的症状口述,为其选择合适的科室进行挂号。
+                患者症状口述:${userInput['症状口述']}
+                结果以JSON格式表示:
+                症状名称为具体的症状,症状描述为用户的感受,持续时间若没有直接说,可以写近期即可。
+                {
+                    "keshi":"科室名称",
+                    "sympList":[
+                        {"title":"症状名称","desc":"症状描述","duration":"持续时间"}
+                    ]
+                }
+                `
+                let completion = new FmodeChatCompletion([
+                    {role:"system",content:""},
+                    {role:"user",content:PromptTemplate}
+                    ])
+                    completion.sendCompletion().subscribe((message:any)=>{
+                        if(task1.progress < 0.5){
+                            task1.progress += 0.1
+                        }
+                        if(task1.progress >= 0.5 && task1.progress <= 0.9){
+                            task1.progress += 0.01
+                        }
+                        if(task1.progress >= 0.9){
+                            task1.progress += 0.001
+                        }
+                        // 打印消息体
+                        console.log(message.content)
+                        // 赋值消息内容给组件内属性
+                        if(message.complete){ // 判断message为完成状态,则设置isComplete为完成
+                            options.shareData.userStory = extactAndParseJsonFromString(message.content)
+                            options.shareData.userStory['症状口述'] = userInput['症状口述']
+                            task1.progress = 1
+                            resolve(true)
+                        }
+                })
+            })
+
+        }
+        return task1
+}

+ 79 - 0
AiStudy-app/src/agent/tasks/inquiry/2.inquiry-doctor-question.ts

@@ -0,0 +1,79 @@
+import { AgentTaskStep } from 'src/agent/agent.task';
+import { getUserInput } from 'src/agent/agent.input';
+import { ModalController } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+import { extactAndParseJsonFromString } from 'src/agent/agent.json';
+
+export function TaskInqueryDoctorQuestion(options:{
+    modalCtrl:ModalController
+    shareData:any}
+    ):AgentTaskStep{
+        /**
+         shareData.userStory // 已经拥有的用户描述数据
+         {
+                  "keshi": "神经内科",
+                  "sympList": [
+                      {
+                          "title": "偏头痛",
+                          "desc": "持续了2天的偏头疼",
+                          "duration": "2天"
+                      },
+                      {
+                          "title": "发冷",
+                          "desc": "感觉发冷,已经有一天",
+                          "duration": "1天"
+                      }
+                  ]
+              }
+         */
+        let task1 = new AgentTaskStep({title:"问诊:医生根据患者情况,主动询问",shareData:options.shareData})
+        task1.handle = ()=>{
+            return new Promise(async (resolve,reject)=>{
+                // 获取用户输入
+                // let userInput = await getUserInput(options.modalCtrl,{fieldsArray:[
+                //     {name:"症状口述",type:"text",desc:"描述下感觉到的症状或不适。"}
+                // ]});
+
+                // if(!userInput?.['症状口述']){
+                //     task1.error = "无症状口述,无法判断科室"
+                //     resolve(false);
+                // }
+
+                // 文本生成
+                let PromptTemplate = `您是一名专业的${options.shareData.userStory.keshi}主任医生,患者简单描述了一下他的情况,请您思考下还有哪些需要询问。
+                症状口述:${options.shareData.userStory['症状口述']}
+                结果以JSON格式表示:
+                {
+                    "questionList":[
+                        {"title":"问题标题","desc":"问题描述"}
+                    ]
+                }
+                `
+                let completion = new FmodeChatCompletion([
+                    {role:"system",content:""},
+                    {role:"user",content:PromptTemplate}
+                    ])
+                    completion.sendCompletion().subscribe((message:any)=>{
+                        if(task1.progress < 0.5){
+                            task1.progress += 0.1
+                        }
+                        if(task1.progress >= 0.5 && task1.progress <= 0.9){
+                            task1.progress += 0.01
+                        }
+                        if(task1.progress >= 0.7){
+                            task1.progress += 0.001
+                        }
+                        // 打印消息体
+                        console.log(message.content)
+                        // 赋值消息内容给组件内属性
+                        if(message.complete){ // 判断message为完成状态,则设置isComplete为完成
+                            options.shareData.userStory.questionList = extactAndParseJsonFromString(message.content).questionList || []
+                            task1.progress = 1
+                            resolve(true)
+                        }
+                })
+            })
+
+        }
+        return task1
+}

+ 141 - 0
AiStudy-app/src/agent/tasks/inquiry/3.inquiry-user-answer.ts

@@ -0,0 +1,141 @@
+import { AgentTaskStep } from 'src/agent/agent.task';
+import { getUserInput } from 'src/agent/agent.input';
+import { ModalController } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+import { extactAndParseJsonFromString } from 'src/agent/agent.json';
+
+export function TaskInqueryUserAnswer(options:{
+    modalCtrl:ModalController
+    shareData:any}
+    ):AgentTaskStep{
+        /**
+         shareData.userStory // 已经拥有的医生主动询问
+         {
+            "questionList": [
+                {
+                    "title": "头痛的性质",
+                    "desc": "请问您的偏头痛是怎样的感觉?是刺痛、跳动还是压迫感?"
+                },
+            ]
+        }
+         */
+        let task1 = new AgentTaskStep({title:"诊断:患者回答具体内容后,医生给出诊断结果",shareData:options.shareData})
+        task1.handle = ()=>{
+            return new Promise(async (resolve,reject)=>{
+                // 获取用户输入的问题清单
+                let questionList = options.shareData.userStory.questionList.map((item:any)=>{return {name:item.title,desc:item.desc,type:"text"}})
+                
+                let userInputResult = await getUserInput(options.modalCtrl,{fieldsArray:questionList});
+                console.log(userInputResult)
+                questionList.forEach((question:any)=>{
+                    question.answer = userInputResult[question.name]
+                })
+            
+                // 文本生成
+                let qaContent = options.shareData.userStory.questionList.map((item:any)=>`医生问:${item.title},${item.desc}\n患者答:${item.answer||"没回答"}`).join("\n")
+                let PromptTemplate = `您是一名专业的${options.shareData.userStory.keshi}主任医生,根据具体的询问,给出初步诊断。
+                症状口述:${options.shareData.userStory['症状口述']}
+                具体询问:${qaContent}
+                结果以JSON格式表示:
+                症状名称为具体的症状,症状描述为用户的感受,持续时间若没有直接说,可以写近期即可。
+                {
+                    "title":"病例标题",
+                    "desc":"病情概括",
+                    "content":"给出完整的治疗方案和建议"
+                }
+                `
+                let completion = new FmodeChatCompletion([
+                    {role:"system",content:""},
+                    {role:"user",content:PromptTemplate}
+                    ])
+                    completion.sendCompletion().subscribe((message:any)=>{
+                        if(task1.progress < 0.5){
+                            task1.progress += 0.1
+                        }
+                        if(task1.progress >= 0.5 && task1.progress <= 0.9){
+                            task1.progress += 0.01
+                        }
+                        if(task1.progress >= 0.9){
+                            task1.progress += 0.001
+                        }
+                        // 打印消息体
+                        console.log(message.content)
+                        // 赋值消息内容给组件内属性
+                        if(message.complete){ // 判断message为完成状态,则设置isComplete为完成
+                            options.shareData.diagResult = extactAndParseJsonFromString(message.content)
+                            task1.progress = 1
+                            resolve(true)
+                        }
+                })
+            })
+
+        }
+        return task1
+}
+
+
+const TestShareData = {
+userStory : {
+  "keshi": "神经内科",
+  "sympList": [
+      {
+          "title": "偏头疼",
+          "desc": "已经持续了2天了",
+          "duration": "2天"
+      },
+      {
+          "title": "发冷",
+          "desc": "感觉已经有一天",
+          "duration": "1天"
+      }
+  ],
+  "症状口述": "偏头疼已经持续了2天了,发冷感觉已经有一天。",
+  "questionList": [
+      {
+          "title": "头痛的性质",
+          "desc": "请描述您的头痛是搏动性、压迫性还是其他类型?",
+          "answer":"压迫性头疼"
+      },
+      {
+          "title": "头痛的部位",
+          "desc": "您的头痛主要集中在头部的哪个区域?",
+          "answer":"左侧头疼"
+      },
+      {
+          "title": "伴随症状",
+          "desc": "除了头痛和发冷,您还有其他的症状吗?例如恶心、呕吐、视力模糊或对光敏感等?",
+          "answer":"有点恶心但是没有呕吐"
+      },
+      {
+          "title": "发冷的性质",
+          "desc": "您感到发冷时是否伴随有发热、出汗或其他症状?",
+          "answer":"没有"
+      },
+      {
+          "title": "既往病史",
+          "desc": "您是否有偏头痛或其他头痛的病史?",
+          "answer":"没有"
+      },
+      {
+          "title": "生活习惯",
+          "desc": "您最近的生活习惯是否有变化?例如睡眠不足、压力增大或饮食不规律?",
+          "answer":"经常熬夜,学习压力大"
+      },
+      {
+          "title": "药物使用",
+          "desc": "您是否有服用任何药物来缓解头痛或其他症状?",
+          "answer":"没有"
+      },
+      {
+          "title": "家族病史",
+          "desc": "您的家族中是否有人有类似的头痛或神经系统疾病史?",
+          "answer":"没有"
+      },
+      {
+          "title": "过敏史",
+          "desc": "您是否有药物或其他物质的过敏史?",
+          "answer":"没有"
+      }
+  ]
+}
+}

+ 43 - 0
AiStudy-app/src/agent/tasks/learning-plan/1.collect-basic-info.ts

@@ -0,0 +1,43 @@
+import { AgentTaskStep } from '../../../agent/agent.task';
+import { getUserInput } from '../../../agent/agent.input';
+import { ModalController } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+import { extactAndParseJsonFromString } from '../../../agent/agent.json';
+
+export function TaskCollectBasicInfo(options:{
+    modalCtrl: ModalController,
+    shareData: any
+}): AgentTaskStep {
+    let task = new AgentTaskStep({
+        title: "收集基本信息",
+        shareData: options.shareData
+    });
+
+    task.handle = () => {
+        return new Promise(async (resolve, reject) => {
+            // 获取用户输入
+            let userInput = await getUserInput(options.modalCtrl, {
+                fieldsArray: [
+                    {name: "学习目标", type: "text", desc: "想学习什么内容?比如某个学科、技能或项目"},
+                    {name: "学习时间范围", type: "text", desc: "希望在多长时间内完成这个学习目标?"},
+                    {name: "每周学习时间", type: "text", desc: "每周能投入多少时间来学习?"},
+                    {name: "学习偏好", type: "text", desc: "更倾向于哪种学习方式?"},
+                    {name: "当前水平", type: "text", desc: "目前在这个领域的水平如何?"}
+                ]
+            });
+
+            if (!userInput?.['学习目标']) {
+                task.error = "缺少必要信息,请重新填写";
+                resolve(false);
+                return;
+            }
+
+            // 保存用户输入到共享数据
+            options.shareData.basicInfo = userInput;
+            task.progress = 1;
+            resolve(true);
+        });
+    };
+
+    return task;
+} 

+ 62 - 0
AiStudy-app/src/agent/tasks/learning-plan/2.analyze-requirements.ts

@@ -0,0 +1,62 @@
+import { AgentTaskStep } from '../../../agent/agent.task';
+import { ModalController } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+import { extactAndParseJsonFromString } from '../../../agent/agent.json';
+
+export function TaskAnalyzeRequirements(options:{
+    modalCtrl: ModalController,
+    shareData: any
+}): AgentTaskStep {
+    let task = new AgentTaskStep({
+        title: "分析学习需求",
+        shareData: options.shareData
+    });
+
+    task.handle = () => {
+        return new Promise(async (resolve, reject) => {
+            const basicInfo = options.shareData.basicInfo;
+            
+            const promptTemplate = `作为一名专业的学习规划导师,请根据以下信息分析学习需求:
+            学习目标:${basicInfo['学习目标']}
+            学习时间范围:${basicInfo['学习时间范围']}
+            每周学习时间:${basicInfo['每周学习时间']}
+            学习偏好:${basicInfo['学习偏好']}
+            当前水平:${basicInfo['当前水平']}
+
+            请给出详细的分析,结果以JSON格式返回:
+            {
+                "feasibility": "可行性分析",
+                "challenges": ["可能遇到的挑战1", "挑战2"],
+                "suggestions": ["建议1", "建议2"],
+                "prerequisites": ["前置知识/技能1", "前置知识/技能2"]
+            }`;
+
+            let completion = new FmodeChatCompletion([
+                {role: "system", content: ""},
+                {role: "user", content: promptTemplate}
+            ]);
+
+            completion.sendCompletion().subscribe({
+                next: (message: any) => {
+                    if (task.progress < 0.5) {
+                        task.progress += 0.1;
+                    } else if (task.progress < 0.9) {
+                        task.progress += 0.05;
+                    }
+
+                    if (message.complete) {
+                        options.shareData.analysis = extactAndParseJsonFromString(message.content);
+                        task.progress = 1;
+                        resolve(true);
+                    }
+                },
+                error: (error) => {
+                    task.error = "分析失败,请重试";
+                    resolve(false);
+                }
+            });
+        });
+    };
+
+    return task;
+} 

+ 72 - 0
AiStudy-app/src/agent/tasks/learning-plan/3.generate-plan.ts

@@ -0,0 +1,72 @@
+import { AgentTaskStep } from '../../../agent/agent.task';
+import { ModalController } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+import { extactAndParseJsonFromString } from '../../../agent/agent.json';
+
+export function TaskGeneratePlan(options:{
+    modalCtrl: ModalController,
+    shareData: any
+}): AgentTaskStep {
+    let task = new AgentTaskStep({
+        title: "生成学习计划",
+        shareData: options.shareData
+    });
+
+    task.handle = () => {
+        return new Promise(async (resolve, reject) => {
+            const { basicInfo, analysis } = options.shareData;
+            
+            const promptTemplate = `作为专业的学习规划导师,请根据以下信息生成详细的学习计划:
+            
+            基本信息:
+            ${JSON.stringify(basicInfo, null, 2)}
+            
+            需求分析:
+            ${JSON.stringify(analysis, null, 2)}
+
+            请生成一个详细的学习计划,结果以JSON格式返回:
+            {
+                "title": "学习计划标题",
+                "overview": "总体概述",
+                "stages": [
+                    {
+                        "name": "阶段名称",
+                        "duration": "预计时长",
+                        "goals": ["目标1", "目标2"],
+                        "tasks": ["任务1", "任务2"],
+                        "resources": ["推荐资源1", "推荐资源2"]
+                    }
+                ],
+                "milestones": ["里程碑1", "里程碑2"],
+                "evaluationMethods": ["评估方式1", "评估方式2"]
+            }`;
+
+            let completion = new FmodeChatCompletion([
+                {role: "system", content: ""},
+                {role: "user", content: promptTemplate}
+            ]);
+
+            completion.sendCompletion().subscribe({
+                next: (message: any) => {
+                    if (task.progress < 0.5) {
+                        task.progress += 0.1;
+                    } else if (task.progress < 0.9) {
+                        task.progress += 0.05;
+                    }
+
+                    if (message.complete) {
+                        options.shareData.learningPlan = extactAndParseJsonFromString(message.content);
+                        task.progress = 1;
+                        resolve(true);
+                    }
+                },
+                error: (error) => {
+                    task.error = "生成计划失败,请重试";
+                    resolve(false);
+                }
+            });
+        });
+    };
+
+    return task;
+} 

+ 55 - 0
AiStudy-app/src/agent/tasks/poem/poem-desc.ts

@@ -0,0 +1,55 @@
+import { AgentTaskStep } from 'src/agent/agent.task';
+import { getUserInput } from 'src/agent/agent.input';
+import { ModalController } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+
+export function TaskPoemPictureDesc(options:{
+    modalCtrl:ModalController
+    shareData:any}
+    ):AgentTaskStep{
+        let task1 = new AgentTaskStep({title:"意境分析",shareData:options.shareData})
+        task1.handle = ()=>{
+            return new Promise(async (resolve,reject)=>{
+                // 获取用户输入的诗词
+                let userInput = await getUserInput(options.modalCtrl,{fieldsArray:[
+                    {name:"诗文内容",type:"text",desc:"诗文句子或段落"}
+                ]});
+                console.log("已获取用户输入:",userInput)
+                if(userInput){
+                    console.log("已获取诗文内容:",userInput['诗文内容'])
+                }
+                if(!userInput?.['诗文内容']){
+                    task1.error = "缺少诗文内容,请重新开始"
+                    resolve(false);
+                }
+
+                    // 文本生成
+                let PromptTemplate = `您是一名专业的美术画家,请您根据古诗文的内容,将其描述的画面、场景、人物、物品等用最简短的语言表达,直接写出画面,并且以中国的古风意境为主
+                诗文如下:
+                ${userInput['诗文内容']}
+                `
+                let completion = new FmodeChatCompletion([
+                    {role:"system",content:""},
+                    {role:"user",content:PromptTemplate}
+                    ])
+                    completion.sendCompletion().subscribe((message:any)=>{
+                        if(task1.progress < 0.5){
+                            task1.progress += 0.1
+                        }
+                        if(task1.progress >= 0.5){
+                            task1.progress += 0.01
+                        }
+                        // 打印消息体
+                        // console.log(message.content)
+                        // 赋值消息内容给组件内属性
+                        options.shareData.PictureDescResult = message.content
+                        if(message.complete){ // 判断message为完成状态,则设置isComplete为完成
+                            task1.progress = 1
+                            resolve(true)
+                        }
+                })
+            })
+
+        }
+        return task1
+}

+ 43 - 0
AiStudy-app/src/agent/tasks/poem/poem-picture.ts

@@ -0,0 +1,43 @@
+import { ModalController } from '@ionic/angular/standalone';
+import { AgentTaskStep } from 'src/agent/agent.task';
+import { ImagineWork, DalleOptions } from 'fmode-ng';
+
+
+export function TaskPoemPictureCreate(options:{
+    modalCtrl:ModalController
+    shareData:any}):AgentTaskStep{
+    let task2 = new AgentTaskStep({title:"意境绘制",shareData:options.shareData})
+    task2.handle = ()=>{
+      return new Promise(resolve=>{
+        // let userInput = await getUserInput(options.modalCtrl,{fieldsArray:[
+        //   {name:"绘图要求",type:"text",desc:"画风、构图等等"}
+        // ]});
+        // console.log(userInput)
+        console.log("意境绘制:执行过程")
+
+        if(!options.shareData.PictureDescResult){
+          task2.error = "缺少古风描述结果,请重新执行。"
+          resolve(false)
+        }
+        let imagineWork = new ImagineWork();
+        // 图片生成
+        let PicturePrompt = `${options.shareData.PictureDescResult}\n风格:中国古风。画面不带任何文字。`
+        let imgOptions:DalleOptions = {prompt:PicturePrompt}
+        let countDownInt = setInterval(()=>{
+          task2.progress += 0.01
+        },1000)
+        imagineWork?.draw(imgOptions).subscribe((work:any)=>{
+            console.log("imagineWork",work?.toJSON())
+            console.log("images",work?.get("images"))
+            if(work?.get("images")?.length){
+              options.shareData.images = work?.get("images");
+              clearInterval(countDownInt);
+              task2.progress = 1
+              resolve(true)
+            }
+        })
+      })
+
+    }
+    return task2
+}

+ 22 - 0
AiStudy-app/src/app/app.routes.ts

@@ -5,4 +5,26 @@ export const routes: Routes = [
     path: '',
     loadChildren: () => import('./tabs/tabs.routes').then((m) => m.routes),
   },
+  {
+    path: 'custom-teacher',
+    loadComponent: () => import('./pages/custom-teacher/custom-teacher.page').then(m => m.CustomTeacherPage)
+  },
+  {
+    path: 'custom-teacher/add',
+    loadComponent: () => import('./pages/custom-teacher/custom-teacher-form/custom-teacher-form.page').then(m => m.CustomTeacherFormPage)
+  },
+  {
+    path: 'custom-teacher/edit/:id',
+    loadComponent: () => import('./pages/custom-teacher/custom-teacher-form/custom-teacher-form.page').then(m => m.CustomTeacherFormPage)
+  },
+  {
+    path: 'learning-design',
+    loadComponent: () => import('./pages/learning-design/learning-design.page')
+      .then(m => m.LearningDesignPage)
+  },
+  {
+    path: 'learning-history',
+    loadComponent: () => import('./pages/learning-history/learning-history.page')
+      .then(m => m.LearningHistoryPage)
+  }
 ];

+ 46 - 0
AiStudy-app/src/app/pages/custom-teacher/custom-teacher-form/custom-teacher-form.page.html

@@ -0,0 +1,46 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-back-button defaultHref="/custom-teacher"></ion-back-button>
+    </ion-buttons>
+    <ion-title>{{ isEdit ? '编辑教师' : '添加教师' }}</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <ion-list>
+    <ion-item>
+      <ion-label position="stacked">教师名称</ion-label>
+      <ion-input 
+        [(ngModel)]="teacher.name"
+        placeholder="请输入教师名称"
+        type="text">
+      </ion-input>
+    </ion-item>
+
+    <ion-item>
+      <ion-label position="stacked">教师描述</ion-label>
+      <ion-input 
+        [(ngModel)]="teacher.description"
+        placeholder="请输入简短的教师描述"
+        type="text">
+      </ion-input>
+    </ion-item>
+
+    <ion-item>
+      <ion-label position="stacked">系统提示词</ion-label>
+      <ion-textarea 
+        [(ngModel)]="teacher.systemPrompt"
+        placeholder="请输入详细的系统提示词,用于定义教师的个性和教学风格"
+        [rows]="6"
+        [autoGrow]="true">
+      </ion-textarea>
+    </ion-item>
+  </ion-list>
+
+  <div class="ion-padding">
+    <ion-button expand="block" (click)="saveTeacher()">
+      {{ isEdit ? '保存修改' : '创建教师' }}
+    </ion-button>
+  </div>
+</ion-content> 

+ 24 - 0
AiStudy-app/src/app/pages/custom-teacher/custom-teacher-form/custom-teacher-form.page.scss

@@ -0,0 +1,24 @@
+ion-list {
+  padding: 16px;
+}
+
+ion-item {
+  --padding-start: 0;
+  --inner-padding-end: 0;
+  margin-bottom: 16px;
+  
+  ion-label {
+    margin-bottom: 8px;
+    color: var(--ion-color-medium);
+  }
+}
+
+ion-textarea {
+  --background: var(--ion-color-light);
+  --padding-start: 16px;
+  --padding-end: 16px;
+  --padding-top: 8px;
+  --padding-bottom: 8px;
+  margin-top: 8px;
+  border-radius: 8px;
+} 

+ 140 - 0
AiStudy-app/src/app/pages/custom-teacher/custom-teacher-form/custom-teacher-form.page.ts

@@ -0,0 +1,140 @@
+import { Component, OnInit } from '@angular/core';
+import { ActivatedRoute, Router } from '@angular/router';
+import { AlertController } from '@ionic/angular/standalone';
+import { 
+  IonHeader, 
+  IonToolbar, 
+  IonTitle, 
+  IonContent,
+  IonList,
+  IonItem,
+  IonLabel,
+  IonInput,
+  IonTextarea,
+  IonButton,
+  IonButtons,
+  IonBackButton
+} from '@ionic/angular/standalone';
+import { FormsModule } from '@angular/forms';
+import { DatabaseService, CustomTeacher } from '../../../services/database.service';
+
+@Component({
+  selector: 'app-custom-teacher-form',
+  templateUrl: './custom-teacher-form.page.html',
+  styleUrls: ['./custom-teacher-form.page.scss'],
+  standalone: true,
+  imports: [
+    IonHeader,
+    IonToolbar,
+    IonTitle,
+    IonContent,
+    IonList,
+    IonItem,
+    IonLabel,
+    IonInput,
+    IonTextarea,
+    IonButton,
+    IonButtons,
+    IonBackButton,
+    FormsModule
+  ]
+})
+export class CustomTeacherFormPage implements OnInit {
+  isEdit = false;
+  teacherId: string | null = null;
+  teacher: CustomTeacher = {
+    userId: '', // 将从认证服务获取
+    name: '',
+    description: '',
+    systemPrompt: ''
+  };
+
+  constructor(
+    private route: ActivatedRoute,
+    private router: Router,
+    private dbService: DatabaseService,
+    private alertController: AlertController
+  ) {}
+
+  async ngOnInit() {
+    // TODO: 从认证服务获取userId
+    this.teacher.userId = 'testUser';
+
+    // 检查是否是编辑模式
+    this.teacherId = this.route.snapshot.paramMap.get('id');
+    if (this.teacherId) {
+      this.isEdit = true;
+      await this.loadTeacher();
+    }
+  }
+
+  async loadTeacher() {
+    try {
+      const teachers = await this.dbService.getUserCustomTeachers(this.teacher.userId);
+      const teacher = teachers.find(t => t.objectId === this.teacherId);
+      if (teacher) {
+        this.teacher = teacher;
+      } else {
+        throw new Error('Teacher not found');
+      }
+    } catch (error) {
+      console.error('Error loading teacher:', error);
+      const alert = await this.alertController.create({
+        header: '错误',
+        message: '加载教师信息失败',
+        buttons: ['确定']
+      });
+      await alert.present();
+      this.router.navigate(['/custom-teacher']);
+    }
+  }
+
+  async saveTeacher() {
+    if (!this.validateForm()) {
+      return;
+    }
+
+    try {
+      if (this.isEdit) {
+        await this.dbService.updateCustomTeacher(this.teacherId!, this.teacher);
+      } else {
+        await this.dbService.createCustomTeacher(this.teacher);
+      }
+      
+      this.router.navigate(['/custom-teacher']);
+    } catch (error) {
+      console.error('Error saving teacher:', error);
+      const alert = await this.alertController.create({
+        header: '错误',
+        message: '保存失败',
+        buttons: ['确定']
+      });
+      await alert.present();
+    }
+  }
+
+  validateForm(): boolean {
+    if (!this.teacher.name.trim()) {
+      this.showValidationError('请输入教师名称');
+      return false;
+    }
+    if (!this.teacher.description.trim()) {
+      this.showValidationError('请输入教师描述');
+      return false;
+    }
+    if (!this.teacher.systemPrompt.trim()) {
+      this.showValidationError('请输入系统提示词');
+      return false;
+    }
+    return true;
+  }
+
+  async showValidationError(message: string) {
+    const alert = await this.alertController.create({
+      header: '验证错误',
+      message: message,
+      buttons: ['确定']
+    });
+    await alert.present();
+  }
+} 

+ 31 - 0
AiStudy-app/src/app/pages/custom-teacher/custom-teacher.page.html

@@ -0,0 +1,31 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-back-button defaultHref="/tabs/tab1"></ion-back-button>
+    </ion-buttons>
+    <ion-title>自定义教师</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <ion-list>
+    <ion-item *ngFor="let teacher of customTeachers">
+      <ion-label>
+        <h2>{{ teacher.name }}</h2>
+        <p>{{ teacher.description }}</p>
+      </ion-label>
+      <ion-button slot="end" fill="clear" (click)="editTeacher(teacher)">
+        <ion-icon slot="icon-only" name="create"></ion-icon>
+      </ion-button>
+      <ion-button slot="end" fill="clear" color="danger" (click)="deleteTeacher(teacher)">
+        <ion-icon slot="icon-only" name="trash"></ion-icon>
+      </ion-button>
+    </ion-item>
+  </ion-list>
+
+  <ion-fab vertical="bottom" horizontal="end" slot="fixed">
+    <ion-fab-button (click)="addTeacher()">
+      <ion-icon name="add"></ion-icon>
+    </ion-fab-button>
+  </ion-fab>
+</ion-content> 

+ 32 - 0
AiStudy-app/src/app/pages/custom-teacher/custom-teacher.page.scss

@@ -0,0 +1,32 @@
+ion-list {
+  padding: 16px;
+}
+
+ion-item {
+  --padding-start: 0;
+  --inner-padding-end: 0;
+  margin-bottom: 8px;
+  border-radius: 8px;
+  --background: var(--ion-color-light);
+
+  ion-label {
+    margin: 12px 0;
+    
+    h2 {
+      font-size: 16px;
+      font-weight: 500;
+      color: var(--ion-color-dark);
+      margin-bottom: 4px;
+    }
+
+    p {
+      font-size: 14px;
+      color: var(--ion-color-medium);
+      margin: 0;
+    }
+  }
+}
+
+ion-fab {
+  margin: 16px;
+} 

+ 121 - 0
AiStudy-app/src/app/pages/custom-teacher/custom-teacher.page.ts

@@ -0,0 +1,121 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { AlertController } from '@ionic/angular/standalone';
+import { 
+  IonHeader, 
+  IonToolbar, 
+  IonTitle, 
+  IonContent,
+  IonList,
+  IonItem,
+  IonLabel,
+  IonButton,
+  IonIcon,
+  IonFab,
+  IonFabButton,
+  IonButtons,
+  IonBackButton
+} from '@ionic/angular/standalone';
+import { NgFor, NgIf } from '@angular/common';
+import { addIcons } from 'ionicons';
+import { add, create, trash } from 'ionicons/icons';
+import { DatabaseService, CustomTeacher } from '../../services/database.service';
+
+@Component({
+  selector: 'app-custom-teacher',
+  templateUrl: './custom-teacher.page.html',
+  styleUrls: ['./custom-teacher.page.scss'],
+  standalone: true,
+  imports: [
+    IonHeader,
+    IonToolbar,
+    IonTitle,
+    IonContent,
+    IonList,
+    IonItem,
+    IonLabel,
+    IonButton,
+    IonIcon,
+    IonFab,
+    IonFabButton,
+    IonButtons,
+    IonBackButton,
+    NgFor,
+    NgIf
+  ]
+})
+export class CustomTeacherPage implements OnInit {
+  customTeachers: CustomTeacher[] = [];
+  userId: string = ''; // 这里需要从认证服务获取
+
+  constructor(
+    private dbService: DatabaseService,
+    private router: Router,
+    private alertController: AlertController
+  ) {
+    addIcons({ add, create, trash });
+  }
+
+  async ngOnInit() {
+    // TODO: 从认证服务获取userId
+    this.userId = 'testUser'; // 临时使用测试用户ID
+    await this.loadCustomTeachers();
+  }
+
+  async loadCustomTeachers() {
+    try {
+      this.customTeachers = await this.dbService.getUserCustomTeachers(this.userId);
+    } catch (error) {
+      console.error('Error loading custom teachers:', error);
+      // 显示错误提示
+      const alert = await this.alertController.create({
+        header: '错误',
+        message: '加载自定义教师失败',
+        buttons: ['确定']
+      });
+      await alert.present();
+    }
+  }
+
+  async addTeacher() {
+    // 导航到添加教师页面
+    this.router.navigate(['/custom-teacher/add']);
+  }
+
+  async editTeacher(teacher: CustomTeacher) {
+    // 导航到编辑教师页面
+    this.router.navigate(['/custom-teacher/edit', teacher.objectId]);
+  }
+
+  async deleteTeacher(teacher: CustomTeacher) {
+    const alert = await this.alertController.create({
+      header: '确认删除',
+      message: `确定要删除教师"${teacher.name}"吗?`,
+      buttons: [
+        {
+          text: '取消',
+          role: 'cancel'
+        },
+        {
+          text: '删除',
+          role: 'destructive',
+          handler: async () => {
+            try {
+              await this.dbService.deleteCustomTeacher(teacher.objectId!);
+              await this.loadCustomTeachers(); // 重新加载列表
+            } catch (error) {
+              console.error('Error deleting teacher:', error);
+              const errorAlert = await this.alertController.create({
+                header: '错误',
+                message: '删除失败',
+                buttons: ['确定']
+              });
+              await errorAlert.present();
+            }
+          }
+        }
+      ]
+    });
+    await alert.present();
+  }
+} 

+ 94 - 0
AiStudy-app/src/app/pages/learning-design/learning-design.page.html

@@ -0,0 +1,94 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-back-button defaultHref="/tabs/tab2"></ion-back-button>
+    </ion-buttons>
+    <ion-title>学习规划</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content class="ion-padding">
+  <!-- 任务进度显示 -->
+  <div class="task-list">
+    <div *ngFor="let task of taskList" class="task-item">
+      <div class="task-header">
+        <h3>{{ task.title }}</h3>
+        <span class="task-status" [ngClass]="{
+          'status-pending': task.progress === 0,
+          'status-running': task.progress > 0 && task.progress < 1,
+          'status-complete': task.progress === 1,
+          'status-error': task.error
+        }">
+          {{ task.error ? '失败' : 
+             task.progress === 0 ? '等待中' :
+             task.progress === 1 ? '完成' : '进行中'
+          }}
+        </span>
+      </div>
+      
+      <ion-progress-bar [value]="task.progress"></ion-progress-bar>
+      
+      <div *ngIf="task.error" class="task-error">
+        {{ task.error }}
+      </div>
+    </div>
+  </div>
+
+  <!-- 最终结果显示 -->
+  <div *ngIf="shareData.learningPlan" class="plan-result">
+    <h2>{{ shareData.learningPlan.title }}</h2>
+    
+    <div class="plan-section">
+      <h3>总体概述</h3>
+      <p>{{ shareData.learningPlan.overview }}</p>
+    </div>
+
+    <div class="plan-section">
+      <h3>学习阶段</h3>
+      <div *ngFor="let stage of shareData.learningPlan.stages" class="stage-item">
+        <h4>{{ stage.name }} ({{ stage.duration }})</h4>
+        <div class="stage-goals">
+          <strong>目标:</strong>
+          <ul>
+            <li *ngFor="let goal of stage.goals">{{ goal }}</li>
+          </ul>
+        </div>
+        <div class="stage-tasks">
+          <strong>任务:</strong>
+          <ul>
+            <li *ngFor="let task of stage.tasks">{{ task }}</li>
+          </ul>
+        </div>
+        <div class="stage-resources">
+          <strong>推荐资源:</strong>
+          <ul>
+            <li *ngFor="let resource of stage.resources">{{ resource }}</li>
+          </ul>
+        </div>
+      </div>
+    </div>
+
+    <div class="plan-section">
+      <h3>里程碑</h3>
+      <ul>
+        <li *ngFor="let milestone of shareData.learningPlan.milestones">
+          {{ milestone }}
+        </li>
+      </ul>
+    </div>
+
+    <div class="plan-section">
+      <h3>评估方式</h3>
+      <ul>
+        <li *ngFor="let method of shareData.learningPlan.evaluationMethods">
+          {{ method }}
+        </li>
+      </ul>
+    </div>
+  </div>
+
+  <!-- 重新开始按钮 -->
+  <ion-button *ngIf="isComplete" expand="block" (click)="restartPlan()">
+    重新规划
+  </ion-button>
+</ion-content> 

+ 98 - 0
AiStudy-app/src/app/pages/learning-design/learning-design.page.scss

@@ -0,0 +1,98 @@
+.task-list {
+  margin-bottom: 20px;
+}
+
+.task-item {
+  background: var(--ion-color-light);
+  border-radius: 8px;
+  padding: 16px;
+  margin-bottom: 12px;
+
+  .task-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 8px;
+
+    h3 {
+      margin: 0;
+      font-size: 16px;
+      font-weight: 500;
+    }
+  }
+
+  .task-status {
+    font-size: 14px;
+    padding: 4px 8px;
+    border-radius: 4px;
+    
+    &.status-pending {
+      background: var(--ion-color-medium);
+      color: white;
+    }
+    
+    &.status-running {
+      background: var(--ion-color-primary);
+      color: white;
+    }
+    
+    &.status-complete {
+      background: var(--ion-color-success);
+      color: white;
+    }
+    
+    &.status-error {
+      background: var(--ion-color-danger);
+      color: white;
+    }
+  }
+
+  .task-error {
+    color: var(--ion-color-danger);
+    margin-top: 8px;
+    font-size: 14px;
+  }
+}
+
+.plan-result {
+  background: white;
+  border-radius: 8px;
+  padding: 16px;
+  margin-top: 20px;
+
+  h2 {
+    color: var(--ion-color-primary);
+    margin-bottom: 16px;
+  }
+
+  .plan-section {
+    margin-bottom: 24px;
+
+    h3 {
+      color: var(--ion-color-dark);
+      font-size: 18px;
+      margin-bottom: 12px;
+    }
+  }
+
+  .stage-item {
+    background: var(--ion-color-light);
+    border-radius: 8px;
+    padding: 16px;
+    margin-bottom: 16px;
+
+    h4 {
+      color: var(--ion-color-primary);
+      margin: 0 0 12px 0;
+    }
+
+    ul {
+      margin: 8px 0;
+      padding-left: 20px;
+    }
+
+    li {
+      margin-bottom: 4px;
+    }
+  }
+} 

+ 132 - 0
AiStudy-app/src/app/pages/learning-design/learning-design.page.ts

@@ -0,0 +1,132 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { 
+  IonHeader, 
+  IonToolbar, 
+  IonTitle, 
+  IonContent,
+  IonButtons,
+  IonBackButton,
+  IonProgressBar,
+  IonButton,
+  IonIcon,
+  ModalController
+} from '@ionic/angular/standalone';
+import { NgClass, NgFor, NgIf } from '@angular/common';
+import Parse from 'parse';
+import { TaskCollectBasicInfo } from '../../../agent/tasks/learning-plan/1.collect-basic-info';
+import { TaskAnalyzeRequirements } from '../../../agent/tasks/learning-plan/2.analyze-requirements';
+import { TaskGeneratePlan } from '../../../agent/tasks/learning-plan/3.generate-plan';
+import { TaskExecutor } from '../../../agent/agent.start';
+import { AgentTaskStep } from '../../../agent/agent.task';
+
+interface LearningPlan {
+  userId: string;
+  subject: string;
+  timeFrame: string;
+  weeklyHours: number;
+  learningStyle: string;
+  currentLevel: string;
+  planDetails: string;
+  createdAt: Date;
+  updatedAt: Date;
+}
+
+@Component({
+  selector: 'app-learning-design',
+  templateUrl: './learning-design.page.html',
+  styleUrls: ['./learning-design.page.scss'],
+  standalone: true,
+  imports: [
+    IonHeader,
+    IonToolbar,
+    IonTitle,
+    IonContent,
+    IonButtons,
+    IonBackButton,
+    IonProgressBar,
+    IonButton,
+    IonIcon,
+    NgClass,
+    NgFor,
+    NgIf
+  ],
+})
+export class LearningDesignPage implements OnInit {
+  isComplete: boolean = false;
+  taskList: AgentTaskStep[] = [];
+  shareData: any = {};
+
+  constructor(
+    private router: Router,
+    private modalCtrl: ModalController
+  ) {}
+
+  ngOnInit() {
+    this.startLearningPlanTask();
+  }
+
+  async startLearningPlanTask() {
+    this.isComplete = false;
+    this.shareData = {}; // 重置共享数据
+
+    // 创建任务链
+    const task1 = TaskCollectBasicInfo({
+      modalCtrl: this.modalCtrl,
+      shareData: this.shareData
+    });
+    
+    const task2 = TaskAnalyzeRequirements({
+      modalCtrl: this.modalCtrl,
+      shareData: this.shareData
+    });
+    
+    const task3 = TaskGeneratePlan({
+      modalCtrl: this.modalCtrl,
+      shareData: this.shareData
+    });
+
+    // 设置任务列表
+    this.taskList = [task1, task2, task3];
+
+    // 执行任务链
+    const success = await TaskExecutor(this.taskList);
+
+    // 任务完成后保存学习计划
+    if (success && this.shareData.learningPlan) {
+      await this.saveLearningPlan();
+    }
+
+    this.isComplete = true;
+  }
+
+  private async saveLearningPlan() {
+    try {
+      const currentUser = Parse.User.current();
+      if (!currentUser) {
+        throw new Error('User not logged in');
+      }
+
+      const LearningPlan = Parse.Object.extend('LearningPlan');
+      const learningPlan = new LearningPlan();
+      
+      learningPlan.set('userId', currentUser.id);
+      learningPlan.set('subject', this.shareData.basicInfo['学习目标']);
+      learningPlan.set('timeFrame', this.shareData.basicInfo['学习时间范围']);
+      learningPlan.set('weeklyHours', this.shareData.basicInfo['每周学习时间']);
+      learningPlan.set('learningStyle', this.shareData.basicInfo['学习偏好']);
+      learningPlan.set('currentLevel', this.shareData.basicInfo['当前水平']);
+      learningPlan.set('planDetails', JSON.stringify(this.shareData.learningPlan));
+      
+      await learningPlan.save();
+      
+    } catch (error) {
+      console.error('保存学习计划失败:', error);
+      throw error;
+    }
+  }
+
+  restartPlan() {
+    this.startLearningPlanTask();
+  }
+} 

+ 23 - 0
AiStudy-app/src/app/pages/learning-history/learning-history.page.html

@@ -0,0 +1,23 @@
+<ion-header>
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-back-button defaultHref="/tabs/tab2"></ion-back-button>
+    </ion-buttons>
+    <ion-title>学习计划历史</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <ion-list>
+    <ion-item *ngFor="let plan of learningPlans">
+      <ion-label>
+        <h2>{{ plan.subject }}</h2>
+        <p>计划周期: {{ plan.timeFrame }}</p>
+        <p>创建时间: {{ plan.createdAt | date:'yyyy-MM-dd HH:mm' }}</p>
+      </ion-label>
+      <ion-button slot="end" (click)="viewPlanDetails(plan)">
+        查看详情
+      </ion-button>
+    </ion-item>
+  </ion-list>
+</ion-content> 

+ 28 - 0
AiStudy-app/src/app/pages/learning-history/learning-history.page.scss

@@ -0,0 +1,28 @@
+ion-list {
+  padding: 16px;
+}
+
+ion-item {
+  --padding-start: 0;
+  --inner-padding-end: 0;
+  margin-bottom: 8px;
+  border-radius: 8px;
+  --background: var(--ion-color-light);
+
+  ion-label {
+    margin: 12px 0;
+    
+    h2 {
+      font-size: 16px;
+      font-weight: 500;
+      color: var(--ion-color-dark);
+      margin-bottom: 4px;
+    }
+
+    p {
+      font-size: 14px;
+      color: var(--ion-color-medium);
+      margin: 0;
+    }
+  }
+} 

+ 84 - 0
AiStudy-app/src/app/pages/learning-history/learning-history.page.ts

@@ -0,0 +1,84 @@
+import { Component, OnInit } from '@angular/core';
+import { 
+  IonHeader, 
+  IonToolbar, 
+  IonTitle, 
+  IonContent,
+  IonButtons,
+  IonBackButton,
+  IonList,
+  IonItem,
+  IonLabel,
+  IonButton,
+  IonIcon
+} from '@ionic/angular/standalone';
+import { NgFor, DatePipe } from '@angular/common';
+import Parse from 'parse';
+
+interface LearningPlan {
+  id: string;
+  subject: string;
+  timeFrame: string;
+  createdAt: Date;
+  planDetails: string;
+}
+
+@Component({
+  selector: 'app-learning-history',
+  templateUrl: './learning-history.page.html',
+  styleUrls: ['./learning-history.page.scss'],
+  standalone: true,
+  imports: [
+    IonHeader,
+    IonToolbar,
+    IonTitle,
+    IonContent,
+    IonButtons,
+    IonBackButton,
+    IonList,
+    IonItem,
+    IonLabel,
+    IonButton,
+    IonIcon,
+    NgFor,
+    DatePipe
+  ]
+})
+export class LearningHistoryPage implements OnInit {
+  learningPlans: LearningPlan[] = [];
+
+  constructor() {}
+
+  ngOnInit() {
+    this.loadLearningPlans();
+  }
+
+  async loadLearningPlans() {
+    try {
+      const currentUser = Parse.User.current();
+      if (!currentUser) {
+        throw new Error('User not logged in');
+      }
+
+      const LearningPlan = Parse.Object.extend('LearningPlan');
+      const query = new Parse.Query(LearningPlan);
+      query.equalTo('userId', currentUser.id);
+      query.descending('createdAt');
+
+      const results = await query.find();
+      this.learningPlans = results.map(plan => ({
+        id: plan.id,
+        subject: plan.get('subject'),
+        timeFrame: plan.get('timeFrame'),
+        createdAt: plan.get('createdAt'),
+        planDetails: plan.get('planDetails')
+      }));
+    } catch (error) {
+      console.error('加载学习计划失败:', error);
+    }
+  }
+
+  viewPlanDetails(plan: LearningPlan) {
+    // 实现查看详情的逻辑
+  }
+} 

+ 120 - 0
AiStudy-app/src/app/services/database.service.ts

@@ -1,6 +1,17 @@
 import { Injectable } from '@angular/core';
 import Parse from 'parse';
 
+// 添加自定义教师接口
+export interface CustomTeacher {
+  objectId?: string;
+  userId: string;
+  name: string;
+  description: string;
+  systemPrompt: string;
+  createdAt?: Date;
+  updatedAt?: Date;
+}
+
 @Injectable({
   providedIn: 'root'
 })
@@ -68,4 +79,113 @@ export class DatabaseService {
       throw error;
     }
   }
+
+  // 添加测试方法
+  async testConnection() {
+    try {
+      const TestObject = Parse.Object.extend('TestObject');
+      const testObject = new TestObject();
+      testObject.set('foo', 'bar');
+      await testObject.save();
+      console.log('Successfully connected to database!');
+      return true;
+    } catch (error) {
+      console.error('Database connection test failed:', error);
+      return false;
+    }
+  }
+
+  // 获取用户的自定义教师列表
+  async getUserCustomTeachers(userId: string): Promise<CustomTeacher[]> {
+    try {
+      const query = new Parse.Query('CustomTeacher');
+      query.equalTo('userId', userId);
+      query.ascending('createdAt');
+      const results = await query.find();
+      return results.map(obj => ({
+        objectId: obj.id,
+        userId: obj.get('userId'),
+        name: obj.get('name'),
+        description: obj.get('description'),
+        systemPrompt: obj.get('systemPrompt'),
+        createdAt: obj.get('createdAt'),
+        updatedAt: obj.get('updatedAt')
+      }));
+    } catch (error) {
+      console.error('Error getting custom teachers:', error);
+      throw error;
+    }
+  }
+
+  // 创建自定义教师
+  async createCustomTeacher(teacherData: CustomTeacher): Promise<CustomTeacher> {
+    try {
+      const CustomTeacher = Parse.Object.extend('CustomTeacher');
+      const teacher = new CustomTeacher();
+      
+      // 使用类型断言确保字段名是字符串
+      const fields = ['userId', 'name', 'description', 'systemPrompt'] as const;
+      fields.forEach(field => {
+        if (field in teacherData) {
+          teacher.set(field as string, teacherData[field]);
+        }
+      });
+      
+      const result = await teacher.save();
+      return {
+        objectId: result.id,
+        userId: result.get('userId'),
+        name: result.get('name'),
+        description: result.get('description'),
+        systemPrompt: result.get('systemPrompt'),
+        createdAt: result.get('createdAt'),
+        updatedAt: result.get('updatedAt')
+      };
+    } catch (error) {
+      console.error('Error creating custom teacher:', error);
+      throw error;
+    }
+  }
+
+  // 更新自定义教师
+  async updateCustomTeacher(objectId: string, teacherData: Partial<CustomTeacher>): Promise<CustomTeacher> {
+    try {
+      const query = new Parse.Query('CustomTeacher');
+      const teacher = await query.get(objectId);
+      
+      // 使用类型断言确保字段名是字符串
+      const updateableFields = ['name', 'description', 'systemPrompt'] as const;
+      updateableFields.forEach(field => {
+        if (field in teacherData) {
+          teacher.set(field as string, teacherData[field]);
+        }
+      });
+      
+      const result = await teacher.save();
+      return {
+        objectId: result.id,
+        userId: result.get('userId'),
+        name: result.get('name'),
+        description: result.get('description'),
+        systemPrompt: result.get('systemPrompt'),
+        createdAt: result.get('createdAt'),
+        updatedAt: result.get('updatedAt')
+      };
+    } catch (error) {
+      console.error('Error updating custom teacher:', error);
+      throw error;
+    }
+  }
+
+  // 删除自定义教师
+  async deleteCustomTeacher(objectId: string): Promise<void> {
+    try {
+      const query = new Parse.Query('CustomTeacher');
+      const teacher = await query.get(objectId);
+      await teacher.destroy();
+    } catch (error) {
+      console.error('Error deleting custom teacher:', error);
+      throw error;
+    }
+  }
 } 

+ 9 - 2
AiStudy-app/src/app/tab1/tab1.page.html

@@ -14,11 +14,17 @@
           <ion-list>
             <ion-item *ngFor="let teacher of teachers" (click)="selectTeacher(teacher)" [class.selected]="teacher.id === selectedTeacher.id">
               <div class="teacher-list-item">
-                <ion-icon name="person-circle-outline" class="teacher-icon"></ion-icon>
+                <ion-icon [name]="teacher.id === 'custom' ? 'add-circle-outline' : 'person-circle-outline'" class="teacher-icon"></ion-icon>
                 <div class="teacher-details">
                   <div class="teacher-name">{{ teacher.name }}</div>
                   <div class="teacher-description">{{ teacher.description }}</div>
                 </div>
+                <ion-button *ngIf="teacher.isCustom" 
+                          fill="clear" 
+                          slot="end" 
+                          (click)="editTeacher($event, teacher.objectId!)">
+                  <ion-icon name="create-outline"></ion-icon>
+                </ion-button>
               </div>
             </ion-item>
           </ion-list>
@@ -34,7 +40,8 @@
       <!-- 历史消息显示 -->
       <div *ngFor="let message of messages" 
            [ngClass]="{'user-message': message.isUser, 'ai-message': !message.isUser}" 
-           class="message-wrapper">
+           class="message-wrapper"
+           [style.display]="message.isHidden ? 'none' : 'flex'">
         <div class="message-bubble">
           @if(message.isUser) {
             <div class="message-content">{{ message.content }}</div>

+ 15 - 0
AiStudy-app/src/app/tab1/tab1.page.scss

@@ -132,6 +132,12 @@ ion-button {
   align-items: center;
   padding: 12px 0;
   width: 100%;
+  
+  ion-button {
+    margin: 0;
+    --padding-start: 8px;
+    --padding-end: 8px;
+  }
 }
 
 .teacher-details {
@@ -160,3 +166,12 @@ ion-button#select-teacher {
   margin: 0 auto;
   height: auto;
 }
+
+.hidden {
+  display: none;
+}
+
+// 添加自定义教师图标样式
+ion-icon[name="add-circle-outline"] {
+  color: var(--ion-color-success);
+}

+ 193 - 21
AiStudy-app/src/app/tab1/tab1.page.ts

@@ -3,14 +3,19 @@ import { IonHeader, IonToolbar, IonTitle, IonContent, IonFooter, IonItem, IonTex
 import { FormsModule } from '@angular/forms';
 import { NgClass, NgFor, NgIf, DatePipe } from '@angular/common';
 import { addIcons } from 'ionicons';
-import { send, personCircleOutline } from 'ionicons/icons';
+import { send, personCircleOutline, addCircleOutline, createOutline } from 'ionicons/icons';
 import { FmodeChatCompletion, MarkdownPreviewModule } from 'fmode-ng';
+import { DatabaseService } from 'src/app/services/database.service';
+import { Router } from '@angular/router';
+import Parse from 'parse';
 
 // 定义消息接口
 interface ChatMessage {
   content: string;
   isUser: boolean;
   timestamp: Date;
+  isSetup?: boolean;
+  isHidden?: boolean;
 }
 
 // 定义教师接口
@@ -19,6 +24,8 @@ interface Teacher {
   name: string;
   description: string;
   systemPrompt: string;
+  isCustom?: boolean;  // 添加标识是否为自定义教师
+  objectId?: string;   // 添加数据库对象ID
 }
 
 @Component({
@@ -61,25 +68,33 @@ export class Tab1Page {
       id: 'xiaoai',
       name: '教师小爱',
       description: '亲切友善,擅长耐心解答各类问题',
-      systemPrompt: '你是一位亲切友善的教师,擅长耐心解答学生的各类问题。'
+      systemPrompt: `你是一位名叫"小爱"的教师,性格亲切友善,擅长耐心解答学生的各类问题。
+当学生问及你是谁时,请这样介绍自己:
+"你好!我是小爱老师,一位亲切友善的教师。我最大的特点是耐心细致,非常擅长通过简单易懂的方式解答各类问题。不管你遇到什么困惑,我都会以温和的态度,循序渐进地帮助你理解。作为你的学习伙伴,我期待能和你一起探索知识的世界!"`
     },
     {
       id: 'yan',
       name: '严教授',
       description: '一丝不苟,讲解知识逻辑清晰、严谨细致',
-      systemPrompt: '你是一位一丝不苟的教师,讲解知识逻辑清晰、严谨细致。在回答学生的问题时,提供详尽的解释和步骤,确保学生理解每一个细节。'
+      systemPrompt: `你是一位名叫"严教授"的教师,教学风格严谨细致,讲解知识逻辑清晰。
+当学生问及你是谁时,请这样介绍自己:
+"我是严教授,一位追求学术严谨的教师。我的教学特点是一丝不苟,注重逻辑性和系统性。在我的指导下,你将学会如何从根本上理解知识,掌握严密的思维方式。我会确保每个知识点都讲解得清晰透彻,帮助你建立完整的知识体系。"`
     },
     {
       id: 'youyou',
       name: '悠悠老师',
       description: '风趣幽默,善于用生动例子讲解知识',
-      systemPrompt: '你是一位风趣幽默的教师,善于用生动的语言和有趣的例子讲解知识。通过幽默的表达方式,使用形象化的比喻和生活实例,讲解复杂的概念。'
+      systemPrompt: `你是一位名叫"悠悠"的教师,性格活泼开朗,教学风格风趣幽默,擅长比喻和举例子。
+当学生问及你是谁时,请这样介绍自己:
+"嗨!我是悠悠老师,一个超级有趣的老师!我最擅长用生动有趣的例子来讲解知识。枯燥的概念?不存在的!在我这里,每个知识点都能变成一个有趣的故事。我相信学习应该是快乐的,让我们一起用轻松愉快的方式探索知识吧!"`
     },
     {
       id: 'di',
       name: '迪先生',
       description: '启发思维,善于引导学生独立思考',
-      systemPrompt: '你是一位启发思维型的教师,善于通过提问和引导帮助学生思考。在回答问题时,可以有时不直接给出答案,而是通过提问和提示,引导学生自己得出结论。'
+      systemPrompt: `你是一位名叫"迪先生"的教师,擅长启发式教学和引导学生独立思考,要多对学生提问引导。
+当学生问及你是谁时,请这样介绍自己:
+"你好,我是迪先生,一位注重启发式教学的导师。我不会直接告诉你答案,而是会引导你思考、探索,帮助你培养独立思考的能力。我相信每个人都有无限的潜能,通过适当的引导和启发,你一定能够找到属于自己的学习方法和解决问题的途径。让我们一起开启这段思维的探索之旅吧!"`
     },
     {
       id: 'custom',
@@ -91,21 +106,162 @@ export class Tab1Page {
 
   selectedTeacher: Teacher = this.teachers[0]; // 默认选择教师小爱
 
-  constructor() {
-    addIcons({ send, personCircleOutline });
+  constructor(
+    private dbService: DatabaseService,
+    private router: Router
+  ) {
+    addIcons({ send, personCircleOutline, addCircleOutline, createOutline });
+    this.loadTeachers();
   }
 
-  // 修改选择教师的方法,切换教师时清空对话历史
-  selectTeacher(teacher: Teacher) {
+  // 添加加载教师方法
+  async loadTeachers() {
+    try {
+      const currentUser = await Parse.User.current();
+      if (currentUser) {
+        const userId = currentUser.id;
+        const customTeachers = await this.dbService.getUserCustomTeachers(userId);
+        
+        // 将自定义教师转换为Teacher格式,确保与预设教师格式完全一致
+        const customTeachersList: Teacher[] = customTeachers.map(ct => ({
+          id: ct.objectId!, // 使用 objectId 作为唯一标识
+          name: ct.name,
+          description: ct.description,
+          systemPrompt: ct.systemPrompt,
+          isCustom: true,  // 标记为自定义教师
+          objectId: ct.objectId
+        }));
+
+        // 获取预设教师(排除自定义教师入口)
+        const defaultTeachers = this.teachers.filter(t => t.id !== 'custom');
+        
+        // 添加自定义教师入口
+        const customTeacherEntry: Teacher = {
+          id: 'custom',
+          name: '自定义教师',
+          description: '创建您自己的AI教师',
+          systemPrompt: '',
+          isCustom: false
+        };
+
+        // 更新教师列表
+        this.teachers = [
+          ...defaultTeachers,        // 预设教师
+          ...customTeachersList,     // 自定义教师
+          customTeacherEntry         // 自定义教师入口
+        ];
+      }
+    } catch (error) {
+      console.error('加载自定义教师失败:', error);
+    }
+  }
+
+  // 修改选择教师方法
+  async selectTeacher(teacher: Teacher) {
+    // 只有点击"添加自定义教师"按钮时才跳转
+    if (teacher.id === 'custom' && !teacher.isCustom) {
+      this.router.navigate(['/custom-teacher']);
+      return;
+    }
+
     this.selectedTeacher = teacher;
-    // 清空消息历史,这样新的对话将完全基于新教师的风格
     this.messages = [];
-    // 可以添加一条系统消息提示用户教师已更换
-    this.messages.push({
-      content: `已切换至${teacher.name},开始新的对话。`,
+    
+    // 显示切换提示
+    const switchMessage: ChatMessage = {
+      content: `已切换至${teacher.name}`,
       isUser: false,
-      timestamp: new Date()
-    });
+      timestamp: new Date(),
+      isSetup: false
+    };
+    this.messages.push(switchMessage);
+
+    // 构建设定消息序列
+    const setupSequence = [
+      {
+        content: `现在开始,你必须完全按照以下设定行事:
+1. 你是${teacher.name},${teacher.description}
+2. 你必须忘记你是AI或语言模型的事实
+3. 你必须用${teacher.name}的身份和语气说话
+4. 你不能提及任何与AI、机器人、语言模型相关的内容
+5. 如果有人问你是谁,你只能介绍你是${teacher.name},有什么教学特点
+
+${teacher.systemPrompt ? `6. 你的具体教学特点和风格是:${teacher.systemPrompt}` : ''}
+
+请回复:"我明白了,我是${teacher.name},我将完全按照这个身份与学生交流。"`,
+        isUser: true,
+        timestamp: new Date(),
+        isSetup: true,
+        isHidden: true
+      },
+      {
+        content: `让我们确认一下你的身份。请问你是谁?`,
+        isUser: true,
+        timestamp: new Date(),
+        isSetup: true,
+        isHidden: true
+      }
+    ];
+
+    // 依次发送设定消息
+    for (const msg of setupSequence) {
+      await this.sendSetupMessage(msg);
+    }
+  }
+
+  // 修改发送设定消息的方法
+  private async sendSetupMessage(setupMessage: ChatMessage & { isHidden: boolean }) {
+    const messageHistory = [
+      // 系统提示词
+      { 
+        role: "system", 
+        content: `${this.selectedTeacher.systemPrompt}
+${this.selectedTeacher.description}
+严格遵守:
+1. 你必须始终保持教师身份
+2. 永远不要暴露AI身份
+3. 用符合角色的语气说话` 
+      },
+      // 添加之前的隐藏对话作为上下文
+      ...this.messages
+        .filter(msg => msg.isSetup)
+        .map(msg => ({
+          role: msg.isUser ? "user" : "assistant",
+          content: msg.content
+        })),
+      // 添加当前设定消息
+      { role: "user", content: setupMessage.content }
+    ];
+
+    const completion = new FmodeChatCompletion(messageHistory);
+
+    try {
+      await new Promise((resolve, reject) => {
+        completion.sendCompletion().subscribe({
+          next: (message: any) => {
+            if (message?.complete) {
+              // 添加AI的确认回复,同样标记为隐藏
+              const aiMessage: ChatMessage = {
+                content: message.content,
+                isUser: false,
+                timestamp: new Date(),
+                isSetup: true,
+                isHidden: true  // 确保AI回复也被隐藏
+              };
+              // 将设定消息添加到历史记录
+              this.messages.push(setupMessage, aiMessage);
+              resolve(true);
+            }
+          },
+          error: (error) => {
+            console.error('发送设定消息失败:', error);
+            reject(error);
+          }
+        });
+      });
+    } catch (error) {
+      console.error('设定角色失败:', error);
+    }
   }
 
   // 发送消息
@@ -125,10 +281,18 @@ export class Tab1Page {
 
       // 创建完整的消息历史数组
       const messageHistory = [
-        // 始终将系统提示词放在最前面
-        { role: "system", content: this.selectedTeacher.systemPrompt },
-        // 添加之前的对话历史(最多保留最近的几轮对话)
-        ...this.messages.slice(-6).map(msg => ({
+        // 系统提示词
+        { 
+          role: "system", 
+          content: `${this.selectedTeacher.systemPrompt}
+${this.selectedTeacher.description}
+严格遵守:
+1. 你必须始终保持教师身份
+2. 永远不要暴露AI身份
+3. 用符合角色的语气说话` 
+        },
+        // 添加所有消息历史,包括隐藏的设定消息
+        ...this.messages.map(msg => ({
           role: msg.isUser ? "user" : "assistant",
           content: msg.content
         })),
@@ -136,8 +300,10 @@ export class Tab1Page {
         { role: "user", content: this.userInput }
       ];
 
-      // 创建 completion 实例,传入完整的消息历史
-      const completion = new FmodeChatCompletion(messageHistory);
+      // 创建 completion 实例,只使用支持的配置选项
+      const completion = new FmodeChatCompletion(messageHistory, {
+        model: 'gpt-3.5-turbo' // 如果需要指定模型的话
+      });
 
       // 发送请求并订阅响应
       completion.sendCompletion().subscribe({
@@ -186,4 +352,10 @@ export class Tab1Page {
       console.error('滚动失败:', error);
     }
   }
+
+  // 添加编辑教师方法
+  editTeacher(event: Event, teacherId: string) {
+    event.stopPropagation(); // 阻止事件冒泡
+    this.router.navigate(['/custom-teacher/edit', teacherId]);
+  }
 }

+ 2 - 2
AiStudy-app/src/app/tab2/tab2.page.html

@@ -28,8 +28,8 @@
         <ion-card class="feature-card" (click)="navigateToFeature('learning-design')">
           <ion-card-content class="ion-text-center">
             <ion-icon name="bulb" color="warning"></ion-icon>
-            <h3>个性化学习设计</h3>
-            <p>定制你的学习风格</p>
+            <h3>长期学习规划</h3>
+            <p>定制你的专属学习计划</p>
           </ion-card-content>
         </ion-card>
       </ion-col>

+ 10 - 3
AiStudy-app/src/app/tab2/tab2.page.ts

@@ -87,8 +87,15 @@ export class Tab2Page implements OnInit {
   }
 
   navigateToFeature(feature: string) {
-    // 处理导航逻辑
-    console.log(`Navigating to ${feature}`);
-    // this.router.navigate([`/tabs/tab2/${feature}`]);
+    switch(feature) {
+      case 'learning-design':
+        this.router.navigate(['/learning-design']);
+        break;
+      case 'learning-history':
+        this.router.navigate(['/learning-history']);
+        break;
+      default:
+        console.log(`Navigating to ${feature}`);
+    }
   }
 }

+ 3 - 3
AiStudy-app/src/main.ts

@@ -14,8 +14,8 @@ import { Diagnostic } from '@awesome-cordova-plugins/diagnostic/ngx';
 // 设置Parse服务属性
 import Parse from "parse";
 Parse.initialize("dev");
-Parse.serverURL = "http://113.44.218.121:1337/parse";
-localStorage.setItem("NOVA_APIG_SERVER", 'aHR0cHMlM0ElMkYlMkZzZXJ2ZXIuZm1vZGUuY24lMkZhcGklMkZhcGlnJTJG')
+Parse.serverURL = "http://113.44.218.121:1337/parse";//数据库
+localStorage.setItem("NOVA_APIG_SERVER", 'aHR0cHMlM0ElMkYlMkZzZXJ2ZXIuZm1vZGUuY24lMkZhcGklMkZhcGlnJTJG')//飞马AI
 
 bootstrapApplication(AppComponent, {
   providers: [
@@ -29,4 +29,4 @@ bootstrapApplication(AppComponent, {
     Diagnostic,
     
   ],
-});
+});

+ 5 - 1
AiStudy-app/tsconfig.json

@@ -20,7 +20,11 @@
     "target": "es2022",
     "module": "es2020",
     "lib": ["es2018", "dom"],
-    "useDefineForClassFields": false
+    "useDefineForClassFields": false,
+    "paths": {
+      "@agent/*": ["src/agent/*"],
+      "@app/*": ["src/app/*"]
+    }
   },
   "angularCompilerOptions": {
     "enableI18nLegacyMessageIdFormat": false,

+ 7 - 0
AiStudy-server/server.js

@@ -18,6 +18,13 @@ const api = new ParseServer({
   // 添加额外的安全配置
   serverURL: 'http://113.44.218.121:1337/parse',
   publicServerURL: 'http://113.44.218.121:1337/parse',
+  // 添加用户认证配置
+  auth: {
+    enableAnonymousUsers: false,
+    enablePasswordReset: true,
+  },
+  // 添加邮箱验证配置(可选)
+  verifyUserEmails: false,
 });
 
 async function main() {