lzy il y a 1 an
Parent
commit
84afb3c17b

Fichier diff supprimé car celui-ci est trop grand
+ 850 - 106
FilmDraw-app/package-lock.json


+ 1 - 0
FilmDraw-app/package.json

@@ -53,6 +53,7 @@
     "@angular/compiler-cli": "^18.0.0",
     "@angular/language-service": "^18.0.0",
     "@capacitor/cli": "6.2.0",
+    "@compodoc/compodoc": "^1.1.26",
     "@ionic/angular-toolkit": "^11.0.1",
     "@types/jasmine": "~5.1.0",
     "@typescript-eslint/eslint-plugin": "^6.0.0",

+ 5 - 1
FilmDraw-app/src/app/homepage/help/help.page.scss

@@ -4,10 +4,12 @@
     border-radius: 8px;
     background-color: #fff; // 列表背景
     box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 阴影效果
+    padding: 20px;
       }
   
     h2 {
       font-size: 24px;
+      margin-left: 20px;
       margin-bottom: 16px;
       color: #036a99 // 标题颜色
     }
@@ -15,10 +17,12 @@
     p {
         font-size: 14px;
         color: #666; // 问题内容颜色
+
         margin-top: 4px;
       }
   
     h3 {
+      margin-left: 20px;
       margin-top: 24px;
       margin-bottom: 8px;
       font-size: 20px;
@@ -26,7 +30,7 @@
     }
   
     ion-list {
-      margin-top: 16px;
+      margin: 20px;
       border-radius: 8px;
       background-color: #fff; // 列表背景
       box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 阴影效果

+ 3 - 1
FilmDraw-app/src/app/homepage/help/help.page.ts

@@ -1,7 +1,9 @@
 import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { IonItem,IonButton, IonButtons, IonContent, IonHeader, IonLabel, IonList, IonTitle, IonToolbar, NavController, IonIcon } from '@ionic/angular/standalone';
+import { 
+  IonItem,IonButton, IonButtons, IonContent, IonHeader, IonLabel, 
+  IonList, IonTitle, IonToolbar, NavController, IonIcon } from '@ionic/angular/standalone';
 import { close } from 'ionicons/icons';
 import { addIcons } from 'ionicons';
 

+ 39 - 9
FilmDraw-app/src/app/homepage/history/history.page.html

@@ -1,13 +1,43 @@
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
-    <ion-title>History</ion-title>
+    <ion-title>聊天历史记录</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="closeHistory()">
+        <ion-icon name="close"></ion-icon>
+      </ion-button>
+    </ion-buttons>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">History</ion-title>
-    </ion-toolbar>
-  </ion-header>
-</ion-content>
+<ion-content>
+  <section>
+    <ion-card>
+      <ion-list>
+        <ion-item *ngFor="let chat of chatList; let i = index">
+          <ion-label>
+            <h2 style="margin: 0;">聊天记录 {{ i + 1 }}</h2> <!-- 显示聊天记录的序号 -->
+            <ion-button (click)="toggleChatDetails(i)" class="chat">
+              {{ showDetails[i] ? '隐藏聊天内容' : '显示聊天内容' }}
+            </ion-button>
+            <ng-container *ngIf="showDetails[i]">
+              <ng-container *ngIf="chat.get('dialogue') && chat.get('dialogue').length > 0">
+                <div *ngFor="let dialogueItem of chat.get('dialogue')">
+                  <div *ngIf="dialogueItem.role && dialogueItem.content">
+                    <p style="font-size: 16px; line-height: 1.5; color: #333; margin: 8px 0;">
+                      <strong>{{ dialogueItem.role }}:</strong> {{ dialogueItem.content }}
+                      <br>
+                      <small style="color: #777;">{{ dialogueItem.createdAt | date: 'short' }}</small>
+                    </p>
+                  </div>
+                </div>
+              </ng-container>
+              <ng-container *ngIf="!(chat.get('dialogue') && chat.get('dialogue').length > 0)">
+                <p style="color: #999;">没有聊天记录</p>
+              </ng-container>
+            </ng-container>
+          </ion-label>
+        </ion-item>
+      </ion-list>
+    </ion-card>
+  </section>
+</ion-content>

+ 5 - 0
FilmDraw-app/src/app/homepage/history/history.page.scss

@@ -0,0 +1,5 @@
+.chat {
+    --background:#036a99;
+    color: white;
+    border-radius: 20px;
+  }

+ 75 - 4
FilmDraw-app/src/app/homepage/history/history.page.ts

@@ -1,20 +1,91 @@
 import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+import { 
+  IonItem,IonButton, IonButtons, IonContent, IonHeader, IonLabel, IonCard,
+  IonList, IonTitle, IonToolbar, NavController, IonIcon, ModalController } from '@ionic/angular/standalone';
+import { close } from 'ionicons/icons';
+import { addIcons } from 'ionicons';
+import { CloudObject, CloudUser, CloudQuery } from 'src/lib/ncloud';
+import { Router } from '@angular/router';
+import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-login.component';
+
+interface DialogueItem {
+  role: string;
+  content: string;
+  createdAt?: string;
+}
 
 @Component({
   selector: 'app-history',
   templateUrl: './history.page.html',
   styleUrls: ['./history.page.scss'],
   standalone: true,
-  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule]
+  imports: [
+    IonContent, IonHeader, IonTitle, IonCard, CommonModule, FormsModule,
+    IonToolbar,IonLabel,IonButton,IonButtons,IonList,IonItem,IonIcon,
+  ]
 })
 export class HistoryPage implements OnInit {
 
-  constructor() { }
+  constructor(
+    private navCtrl: NavController,
+    private modalCtrl:ModalController,
+    private router: Router) {
+    addIcons( {close})
+   }
+ 
+   closeHistory() {
+     this.navCtrl.back();
+   }
+
+   currentUser = new CloudUser();
+   async relogin()
+   {
+     let user = await openUserLoginModal(this.modalCtrl);
+     if(user?.id){
+       this.currentUser = user
+     }
+     if(!user?.id){
+       return
+     }
+   }
+
+   chatList: Array<CloudObject> = [];
+   showDetails: boolean[] = []; // 用于跟踪每个聊天记录的详细信息是否显示
+
+   async ChatList() {
+    const currentUser = new CloudUser();
+    await currentUser.current(); // 确保用户信息已加载
+    const userId = currentUser.id; // 获取当前用户的 ObjectId
+  
+    // 声明并初始化 query 变量
+    const query = new CloudQuery("FilmChat");
+    query.equalTo("user", currentUser.toPointer()); // 只获取当前用户的记录
+    
+    // 获取聊天记录
+    this.chatList = await query.find();
+  
+    // 处理聊天记录,移除第一条 user 内容
+    this.chatList.forEach(chat => {
+      const dialogueArray = chat.get('dialogue');
+      if (dialogueArray && dialogueArray.length > 0) {
+        const userIndex = dialogueArray.findIndex((item: DialogueItem) => item.role === 'user');
+        if (userIndex !== -1) {
+          dialogueArray.splice(userIndex, 1); // 移除第一条 user 内容
+        }
+      }
+    });
+        // 初始化显示状态
+        this.showDetails = new Array(this.chatList.length).fill(false);
+  }
+  toggleChatDetails(index: number) {
+    this.showDetails[index] = !this.showDetails[index]; // 切换显示状态
+  }
 
-  ngOnInit() {
+  async ngOnInit() {
+    await this.ChatList(); // 只调用一次 ChatList 方法
+    console.log(this.chatList); // 调试输出
   }
 
 }

+ 10 - 7
FilmDraw-app/src/app/homepage/like/like.page.html

@@ -1,13 +1,16 @@
-<ion-header [translucent]="true">
+<ion-header>
   <ion-toolbar>
-    <ion-title>Like</ion-title>
+    <ion-title>点赞记录</ion-title>
+    <ion-buttons slot="end">
+      <ion-button (click)="close()">
+        <ion-icon name="close"></ion-icon>
+      </ion-button>
+    </ion-buttons>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Like</ion-title>
+<ion-content class="one">
+    <ion-toolbar class="one">
+      <ion-title size="medium" class="one">敬请期待...</ion-title>
     </ion-toolbar>
-  </ion-header>
 </ion-content>

+ 6 - 0
FilmDraw-app/src/app/homepage/like/like.page.scss

@@ -0,0 +1,6 @@
+.one {
+background-color: white;
+text-align: left;
+margin: 0;
+padding: 0;
+}

+ 15 - 3
FilmDraw-app/src/app/homepage/like/like.page.ts

@@ -1,18 +1,30 @@
 import { Component, OnInit } from '@angular/core';
 import { CommonModule } from '@angular/common';
 import { FormsModule } from '@angular/forms';
-import { IonContent, IonHeader, IonTitle, IonToolbar } from '@ionic/angular/standalone';
+import { 
+  IonItem,IonButton, IonButtons, IonContent, IonHeader, IonLabel, 
+  IonList, IonTitle, IonToolbar, NavController, IonIcon } from '@ionic/angular/standalone';
+import { close } from 'ionicons/icons';
+import { addIcons } from 'ionicons';
 
 @Component({
   selector: 'app-like',
   templateUrl: './like.page.html',
   styleUrls: ['./like.page.scss'],
   standalone: true,
-  imports: [IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule]
+  imports: [IonContent, IonHeader, IonTitle,  CommonModule, FormsModule,
+    IonToolbar,IonLabel,IonButton,IonButtons,IonList,IonItem,IonIcon,
+  ]
 })
 export class LikePage implements OnInit {
 
-  constructor() { }
+  constructor(private navCtrl: NavController) {
+    addIcons( {close})
+   }
+
+  close() {
+    this.navCtrl.back();
+  }
 
   ngOnInit() {
   }

+ 0 - 33
FilmDraw-app/src/app/page-AIimagery/page-AIimagery.component.html

@@ -1,33 +0,0 @@
-<ion-content>
-
-  <!-- <h1>AI生成用户画像</h1>
-  
-  <h2>您常看的影视剧类型</h2>
-  <ion-input  [value]="type" (ionInput)="typeInput($event)"></ion-input>
-
-  <h2>您常看的影视剧风格</h2>
-  <ion-input  [value]="style" (ionInput)="styleInput($event)"></ion-input> -->
-
-  <h2>您最近看过的影视剧</h2>
-  <ion-input  [value]="last" (ionInput)="lastInput($event)"></ion-input>
-
-  <h2>您喜欢的演员</h2>
-  <ion-input  [value]="like" (ionInput)="likeInput($event)"></ion-input>
-
-  <h2>您喜欢的故事情节和走向</h2>
-  <ion-textarea [value]="detail" (ionInput)="detailInput($event)" placeholder="剧情走向,男女主性格等" autoGrow="true"></ion-textarea>
-  
-  <!-- 按钮:执行消息生成函数 -->
-  <ion-button (click)="sendMessage()" expand="block">推荐生成</ion-button>
-  
-  <!-- 展示:返回消息内容 -->
-  <!-- 消息传输过程中,实时预览 -->
-  @if(!isComplete){
-    <div>{{responseMsg}}</div>
-  }
-  <!-- 消息传输完成后,实时预览Markdown格式 -->
-  @if(isComplete){
-    <fm-markdown-preview class="content-style" [content]="responseMsg"></fm-markdown-preview>
-  }
-  
-</ion-content>

+ 0 - 79
FilmDraw-app/src/app/page-AIimagery/page-AIimagery.component.scss

@@ -1,79 +0,0 @@
-/* 设置页面的背景颜色 */
-ion-content {
-    --background: #f0f8ff; /* 浅蓝色背景 */
-    padding: 16px; /* 内容内边距 */
-    font-family: 'Comic Sans MS', cursive, sans-serif; /* 可爱的字体 */
-    position: relative; /* 允许绝对定位的子元素 */
-  }
-  
-  h1 {
-    font-size: 30px; /* 标题字体大小 */
-    color: #d85144; /* 可爱的粉色 */
-    // margin-bottom: 12px; /* 标题底部间距 */
-    // text-align: left; /* 左对齐 */
-    // text-shadow: 1px 1px 2px rgba(255, 105, 180, 0.5); /* 文字阴影 */
-  }
-  
-  /* 标题样式 */
-  h2 {
-    font-size: 26px; /* 标题字体大小 */
-    color: #ff6f61; /* 可爱的粉色 */
-    margin-bottom: 12px; /* 标题底部间距 */
-    text-align: left; /* 左对齐 */
-    // text-shadow: 1px 1px 2px rgba(255, 105, 180, 0.5); /* 文字阴影 */
-  }
-  
-  /* 输入框和文本区域样式 */
-  ion-input,
-  ion-textarea {
-    background-color: #ffffff; /* 白色背景 */
-    border: 2px solid #ff6f61; /* 粗虚线边框,粉色 */
-    border-radius: 12px; /* 圆角 */
-    padding: 12px; /* 内边距 */
-    font-size: 16px; /* 字体大小 */
-    margin-bottom: 20px; /* 输入框底部间距 */
-    transition: border-color 0.3s ease; /* 动画效果 */
-  }
-  
-  /* 输入框聚焦时的样式 */
-  ion-input:focus,
-  ion-textarea:focus {
-    border-color: #007bff; /* 聚焦时的边框颜色,蓝色 */
-    // box-shadow: 0 0 10px rgba(0, 123, 255, 0.5); /* 聚焦时的阴影效果 */
-  }
-  
-  /* 按钮样式 */
-  ion-button {
-    --background: #036a99;/* 按钮背景色,蓝色 */
-    color: white; /* 按钮字体颜色 */
-    --border-radius: 20px; /* 按钮圆角 */
-    font-size: 18px; /* 按钮字体大小 */
-    transition: background-color 0.3s ease, transform 0.2s; /* 动画效果 */
-  }
-  
-  /* 按钮悬停样式 */
-  ion-button:hover {
-    transform: scale(1.05); /* 悬停时放大按钮 */
-  }
-  
-  /* 消息展示区域样式 */
-  div {
-    background-color: #ffe4e1; /* 浅粉色背景 */
-    border-radius: 12px; /* 圆角 */
-    padding: 12px; /* 内边距 */
-    margin-top: 20px; /* 顶部间距 */
-    font-size: 16px; /* 字体大小 */
-    color: #333; /* 字体颜色 */
-    //border-left: 5px solid #ff6f61; /* 左侧边框,粉色 */
-    // box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
-  }
-  
-  /* Markdown 预览样式 */
-  fm-markdown-preview {
-    background-color: #ffffff; /* 白色背景 */
-    border-radius: 12px; /* 圆角 */
-    padding: 12px; /* 内边距 */
-    margin-top: 20px; /* 顶部间距 */
-    // box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); /* 阴影效果 */
-    // border: 2px #ff6f61; /* 虚线边框,粉色 */
-  }

+ 0 - 22
FilmDraw-app/src/app/page-AIimagery/page-AIimagery.component.spec.ts

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

+ 0 - 81
FilmDraw-app/src/app/page-AIimagery/page-AIimagery.component.ts

@@ -1,81 +0,0 @@
-import { Component, OnInit } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton, IonTextarea, IonInput } from '@ionic/angular/standalone';
-/** 引用:从fmode-ng库引用FmodeChatCompletion类 */
-import { FmodeChatCompletion,MarkdownPreviewModule } from 'fmode-ng';
-
-@Component({
-  selector: 'app-page-AIimagery',
-  templateUrl: './page-AIimagery.component.html',
-  styleUrls: ['./page-AIimagery.component.scss'],
-  standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonButton,IonTextarea,IonInput,
-    // 引入fm-markdown-preview组件模块
-    MarkdownPreviewModule
-  ],
-})
-export class PageAIimageryComponent  implements OnInit {
-
-  constructor() { }
-
-  ngOnInit() {}
-
-  // 用户输入提示词
-  type:string = "电视剧"
-  typeInput(ev:any){
-    this.type = ev.detail.value;
-  }
-   // 用户输入提示词
-   style:string = "古装/悬疑等"
-   styleInput(ev:any){
-     this.style = ev.detail.value;
-   }
-   // 用户输入提示词
-   last:string = " "
-   lastInput(ev:any){
-     this.last = ev.detail.value;
-   }
-   // 用户输入提示词
-   like:string = " "
-   likeInput(ev:any){
-     this.like = ev.detail.value;
-   }
-  // 用户输入提示词
-  detail:string = "请描述您期望的故事情节"
-  detailInput(ev:any){
-    this.detail = ev.detail.value;
-  }
-  // 属性:组件内用于展示消息内容的变量
-  responseMsg:any = ""
-  // 方法:实例化completion对象,传入消息数组,并订阅生成的可观察对象。
-  isComplete:boolean = false; // 定义完成状态属性,用来标记是否补全完成
-  sendMessage(){
-    console.log("create")
-
-    let PromptTemplate = `
-    请根据以下的用户描述生成该用户的用户画像。
-
-    1. 用户喜欢的影视类型(如:电影、电视剧):${this.type}
-    2. 用户喜欢的影视风格(如:动作、喜剧、爱情、科幻、悬疑等):${this.style}
-    3. 用户喜欢的演员:${this.like}
-    4. 用户对故事情节的偏好(如:喜欢紧张刺激的情节,或者温馨感人的故事):${this.detail}
-    5. 用户的观看历史(例如:最近观看过的影视剧):${this.last}
-
-    
-    请根据以上信息,生成一份该用户的用户画像。每个特征之间以顿号隔开。
-    `
-    
-    let completion = new FmodeChatCompletion([
-      {role:"system",content:""},
-      {role:"user",content:PromptTemplate}
-    ])
-    completion.sendCompletion().subscribe((message:any)=>{
-      // 打印消息体
-      console.log(message.content)
-      // 赋值消息内容给组件内属性
-      this.responseMsg = message.content
-      if(message?.complete){ // 判断message为完成状态,则设置isComplete为完成
-        this.isComplete = true
-      }
-    })
-  }
-}

+ 28 - 21
FilmDraw-app/src/app/tab2/tab2.page.scss

@@ -79,26 +79,33 @@ ion-icon {
     padding: 10px; // 设置卡片内容内边距
   }
 
-.re{
-    border-bottom: 1px solid #dee2e6; /* 分割线颜色 */
-}
+  // .re{
+  //   border-bottom: 1px solid #dee2e6; /* 分割线颜色 */
+  // }
 
   /* 推荐影视剧列表样式 */
-ion-list {
-  margin-top: 8px;
-}
- 
-ion-list ion-item {
-  padding: 12px 0;
-  border-bottom: none; /* 移除分割线 */
-}
- 
-ion-list ion-label h2 {
-  font-size: 18px; /* 电影标题字体大小 */
-  color: #343a40; /* 深灰色 */
-}
- 
-ion-list ion-label p {
-  color: #6c757d; /* 灰色 */
-  font-size: 14px; /* 描述信息字体大小 */
-}
+  ion-item.re {
+    padding: 10px; // 内边距
+    transition: transform 0.2s; // 动画效果
+    &:hover {
+      transform: scale(1.1); // 悬停时放大效果
+    }
+  
+    ion-label {
+      h2 {
+        font-size: 1.3rem; // 剧名字体大小
+        font-weight: bold; // 加粗
+        color: #333; // 剧名颜色
+      }
+  
+      p {
+        font-size: 0.9rem; // 其他信息字体大小
+        color: #666; // 其他信息颜色
+        margin: 4px 0; // 上下间距
+      }
+    }
+  }
+  
+  ion-spinner {
+    margin-left: 15px; // 加载指示器左边距
+  }

+ 1 - 0
FilmDraw-app/src/app/tab2/tab2.page.ts

@@ -64,6 +64,7 @@ export class Tab2Page {
   
     responseMsg: any = "";
     isComplete: boolean = false; // 定义完成状态属性,用来标记是否补全完成
+    
     chatList: Array<CloudObject> = [];
   
     async ChatList() {

+ 0 - 5
FilmDraw-app/src/app/tabs/tabs.routes.ts

@@ -26,11 +26,6 @@ export const routes: Routes = [
         loadComponent: () =>
           import('../tab4/tab4.page').then((m) => m.Tab4Page),
       },
-      {
-        path: 'AIimagery',
-        loadComponent: () =>
-          import('../page-AIimagery/page-AIimagery.component').then((m) => m.PageAIimageryComponent),
-      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

+ 3 - 0
FilmDraw-app/tsconfig.doc.json

@@ -0,0 +1,3 @@
+{    
+     "include": ["src/**/*.ts"]
+}

+ 3 - 8
FilmDraw-prod/UML.md

@@ -17,19 +17,14 @@ bio:String(个人简介)
 avatar:String
 imagery:String(用户画像)
 
-2.Film
+2.FilmRecommendation
 objectId: String (默认)
 createdAt: Date (默认)
+user: Pointer
 title: String(剧名)
 genre: String(题材)
 grade: String(评分)
 desc: String(描述)
-
-3.FilmRecommendation
-objectId: String (默认)
-createdAt: Date (默认)
-user: Pointer
-recommendedFilms: Array<Pointer>
 ```
 
 ## AI陪聊助手模块
@@ -221,7 +216,7 @@ FilmPost "1" --> "*" FilmPostRating : has
     @startuml
     actor User
     participant "AI陪聊助手" as ChatBot
-    participant "用户画像数据库" as UserProfileDB
+    participant "用户数据库" as UserProfileDB
     participant "聊天记录数据库" as ChatHistoryDB
     participant "推荐系统" as RecommendationSystem
 

+ 3 - 3
README.md

@@ -1,4 +1,4 @@
-# 剧伴项目仓库
+# 影心绘项目仓库
 
-- DramaComponion-app 前端代码
-- DramaComponion-prod 产品文档 
+- FilmDraw-app 前端代码
+- FilmDraw-prod 产品文档 

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff