lzy 1 год назад
Родитель
Сommit
1280a3fcdc

+ 0 - 6
FilmDraw-app/src/app/tab1/tab1.page.html

@@ -12,12 +12,6 @@
 </ion-header>
 
 <ion-content [fullscreen]="true">
-  <!-- <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">圈子</ion-title>
-    </ion-toolbar>
-  </ion-header> -->
-
   <!-- 热门话题区 -->
   <section>
     <ion-card>

+ 54 - 1
FilmDraw-app/src/app/tab3/tab3.page.html

@@ -1,4 +1,57 @@
 <ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title size="large">沉浸互动</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <!-- AI陪聊搭子区域 -->
+    <section>
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>AI陪聊搭子</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <div class="chat-partner-area">
+            <img src="assets/avatar.png" alt="头像" class="avatar" />
+            <div class="description">
+              <h2>小嫣</h2>
+              <p>欢迎与我聊天,分享你的想法和感受。</p>
+            </div>
+            <ion-button (click)="opendazi()">开始聊天</ion-button>
+          </div>
+        </ion-card-content>
+      </ion-card>
+    </section>
+
+
+  <!-- 角色互动区域 -->
+  <section>
+    <ion-card>
+      <ion-card-header>
+        <ion-card-title>角色互动</ion-card-title>
+      </ion-card-header>
+      <ion-card-content>
+        <ion-list>
+    <ion-item *ngFor="let role of roles">
+      <ion-avatar slot="start">
+        <img [src]="role.avatar" alt="{{ role.name }} 头像">
+      </ion-avatar>
+      <ion-label>
+        <h2>{{ role.name }}</h2>
+        <p>{{ role.description }}</p>
+      </ion-label>
+      <ion-button (click)="startRoleChat(role)">开始聊天</ion-button>
+    </ion-item>
+  </ion-list>
+      </ion-card-content>
+    </ion-card>
+  </section>
+
+  
+
+
+<!-- <ion-header [translucent]="true">
   <ion-toolbar>
     <ion-title size="large">AI追剧搭子</ion-title>
   </ion-toolbar>
@@ -13,4 +66,4 @@
   <h1>示例:门诊问诊的智能体示例(ChatPanel组件)</h1>
   <ion-button (click)="opendazi()">追剧搭子</ion-button>
 
-</ion-content>
+</ion-content> -->

+ 107 - 0
FilmDraw-app/src/app/tab3/tab3.page.scss

@@ -8,4 +8,111 @@ ion-title {
 
 ion-header {
   background-color: #3880ff; // 设置头部背景色
+}
+
+
+ion-card {
+  margin: 10px; // 设置卡片之间的间距
+  border-radius: 10px; // 设置卡片圆角
+  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); // 添加阴影效果
+}
+
+ion-card-header {
+  background-color: #d8e5fa; // 设置头部背景色
+  color: white; // 设置头部文字颜色
+}
+
+ion-card-title {
+  font-size: 1.2em; // 设置卡片标题字体大小
+  font-weight: bold; // 设置卡片标题字体加粗
+}
+
+ion-item {
+  --ion-item-background: transparent; // 设置列表项背景透明
+}
+
+ion-label {
+  color: #333; // 设置标签文字颜色
+}
+
+
+
+h2 {
+  font-size: 1em; // 设置二级标题字体大小
+  margin: 0; // 去掉默认外边距
+}
+
+p {
+  font-size: 0.9em; // 设置段落字体大小
+  color: #666; // 设置段落文字颜色
+}
+
+ion-list {
+  padding: 0; // 去掉列表内边距
+}
+
+ion-card-content {
+  padding: 10px; // 设置卡片内容内边距
+}
+
+ion-button {
+  --background: #3880ff; // 设置按钮背景色
+  --color: white; // 设置按钮文字颜色
+  margin-top: 10px; // 设置按钮与上方内容的间距
+  border-radius: 20px; // 设置按钮圆角
+}
+
+
+.chat-partner-area {
+  display: flex;
+  align-items: center;
+  margin-bottom: 20px;
+  border: 1px solid #ccc;
+  border-radius: 10px;
+  padding: 15px;
+
+  .avatar {
+    width: 60px;
+    height: 60px;
+    border-radius: 50%;
+    margin-right: 15px;
+  }
+
+  .description {
+    flex-grow: 1;
+  }
+
+  ion-button {
+    border-radius: 20px;
+  }
+}
+
+.role-interaction-area {
+  h2 {
+    margin-bottom: 15px;
+  }
+
+  .role-container {
+    display: flex;
+    align-items: center;
+    margin-bottom: 15px;
+    border: 1px solid #ccc;
+    border-radius: 10px;
+    padding: 15px;
+
+    .avatar {
+      width: 50px;
+      height: 50px;
+      border-radius: 50%;
+      margin-right: 10px;
+    }
+
+    .role-description {
+      flex-grow: 1;
+    }
+
+    ion-button {
+      border-radius: 20px;
+    }
+  }
 }

+ 23 - 2
FilmDraw-app/src/app/tab3/tab3.page.ts

@@ -1,6 +1,10 @@
 import { Component } from '@angular/core';
 import { Router } from '@angular/router';
-import { IonHeader, IonToolbar, IonTitle, IonContent, ModalController, IonButton } from '@ionic/angular/standalone';
+import { CommonModule } from '@angular/common';
+import { IonHeader, IonToolbar, IonTitle, IonContent, ModalController, IonButton,
+  IonCard, IonCardHeader, IonCardTitle, IonCardContent,
+  IonList, IonItem, IonLabel, IonAvatar, IonInput, IonIcon
+ } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
 import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
 // import { ModalAudioMessageComponent } from 'fmode-ng/lib/aigc/chat/chat-modal-input/modal-audio-message/modal-audio-message.component';
@@ -14,12 +18,29 @@ import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openCh
     IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent,
     IonButton,
     // ASR语音输入模块
-    FmChatModalInput,
+    FmChatModalInput, 
     // ModalAudioMessageComponent
+    CommonModule, IonCard, IonCardHeader, IonCardTitle, IonCardContent,
+  IonList, IonItem, IonLabel, IonAvatar, IonInput, IonIcon
   ]
 })
 export class Tab3Page {
 
+  roles = [
+    { name: '角色一', description: '这是角色一的描述', avatar: 'assets/role1.png' },
+    { name: '角色二', description: '这是角色二的描述', avatar: 'assets/role2.png' },
+    { name: '角色三', description: '这是角色三的描述', avatar: 'assets/role3.png' },
+  ];
+
+  startChat() {
+    this.router.navigate(['/chat']); // 跳转到聊天页面
+  }
+
+  startRoleChat(role: any) {
+    this.router.navigate(['/role-chat', { role: role.name }]); // 跳转到角色聊天页面
+  }
+
+
   constructor(
     private modalCtrl:ModalController,
     private router:Router,

+ 111 - 1
FilmDraw-prod/UML.md

@@ -1,3 +1,113 @@
+# 一、Schema范式设计
+
+## 个性化推荐模块
+
+- 表设计
+```
+1._User表
+objectId: String (默认)
+createdAt: Date (默认)
+age: Number
+gender: String
+watchHistory: Array<Pointer>
+ratingHistory: Array<Pointer>
+
+2.Film表
+objectId: String (默认)
+createdAt: Date (默认)
+title: String
+genre: String
+contentFeatures: Array
+
+3.FilmRating表
+objectId: String (默认)
+createdAt: Date (默认)
+user: Pointer
+film: Pointer
+ratingValue: Number
+comment: String (可选)
+
+4.FilmRecommendation
+objectId: String (默认)
+createdAt: Date (默认)
+user: Pointer
+recommendedFilms: Array<Pointer>
+```
+
+## AI陪聊助手模块
+
+- 表设计
+```
+1._User表
+objectId: String (默认)
+createdAt: Date (默认)
+username: String
+chatHistory: Array<Pointer>
+
+2.Film表
+objectId: String (默认)
+createdAt: Date (默认)
+title: String
+genre: String
+contentFeatures: Array
+plotSummary: String
+
+3.FilmRole表
+objectId: String (默认)
+createdAt: Date (默认)
+film: Pointer
+name: String
+description: String
+roleTraits: Array
+
+4.FilmChat表
+objectId: String (默认)
+createdAt: Date (默认)
+user: Pointer
+character: Pointer
+dialogue: String
+timestamp: Date
+```
+
+## 社区模块
+
+- 表设计
+```
+1._User表
+objectId: String (默认)
+createdAt: Date (默认)
+username: String
+postHistory: Array<Pointer>
+commentHistory: Array<Pointer>
+likeHistory: Array<Pointer>
+
+2.FilmPost表
+
+objectId: String (默认)
+createdAt: Date (默认)
+user: Pointer
+content: String
+likesCount: Number
+commentsCount: Number
+status: String (例如 "pending", "approved", "rejected")
+
+3.FilmComment表
+objectId: String (默认)
+createdAt: Date (默认)
+post: Pointer
+user: Pointer
+content: String
+status: String (例如 "pending", "approved", "rejected")
+
+4.FilmLike表
+objectId: String (默认)
+createdAt: Date (默认)
+user: Pointer
+post: Pointer
+comment: Pointer (可选,若点赞的是评论)
+```
+
+
 # 类图
 ```
 1. 个性化推荐模块
@@ -252,7 +362,7 @@ Moderator "1" -- "0..*" Post
 
 
 # AI陪聊助手模块
-- AI陪聊搭子根据不同影视剧的剧情与用户进行流畅、自然的对话交流,为用户提供个性化的影视剧解读
+- AI陪聊搭子根据影视剧的剧情与用户进行流畅、自然的对话交流,为用户提供个性化的影视剧解读
 - 用户可以选择不同角色的智能体互动,AI塑造不同角色的智能体与用户聊天(此功能为可选功能)
 
 ## 类图

+ 132 - 0
FilmDraw-server/lib/ncloud.js

@@ -0,0 +1,132 @@
+class CloudObject{
+    id
+    className
+    data = {}
+    constructor(className){
+        this.className = className
+    }
+
+    set(json){
+        Object.keys(json).forEach(key=>{
+            if(["objectId","id","createdAt","updatedAt","ACL"].indexOf(key)>-1){
+                return
+            }
+            this.data[key] = json[key]
+        })
+    }
+    get(key){
+        return this.data[key] || null
+    }
+    async save(){
+        let method = "POST"
+        let url = "http://dev.fmode.cn:1337/parse/classes/" + this.className
+        // 更新
+        if(this.id){
+            url = "/"+this.id
+            method = "PUT"
+        } 
+        let body = JSON.stringify(this.data)
+        let response = await fetch(url, {
+            "headers": {
+              "content-type": "application/json;charset=UTF-8",
+              "x-parse-application-id": "dev"
+            },
+            "body": body,
+            "method": method,
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          let result = await response?.json();
+          if(result?.objectId){this.id = result?.objectId}
+          return this
+    }
+    async destory(){
+        if(!this.id) return
+        let response = await fetch("http://dev.fmode.cn:1337/parse/classes/Doctor/"+this.id, {
+            "headers": {
+              "x-parse-application-id": "dev"
+            },
+            "body": null,
+            "method": "DELETE",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          let result = await response?.json();
+          if(result){
+            this.id = null
+        }
+        return true
+    }
+}
+
+class CloudQuery{
+    className
+    constructor(className){
+        this.className = className
+    }
+
+    whereOptions = {}
+    greaterThan(key,value){
+        if(!this.whereOptions[key]) this.whereOptions[key] = {}
+        this.whereOptions[key]["$gt"] = value
+    }
+    greaterThanAndEqualTo(key,value){
+        if(!this.whereOptions[key]) this.whereOptions[key] = {}
+        this.whereOptions[key]["$gte"] = value
+    }
+    lessThan(key,value){
+        if(!this.whereOptions[key]) this.whereOptions[key] = {}
+        this.whereOptions[key]["$lt"] = value
+    }
+    lessThanAndEqualTo(key,value){
+        if(!this.whereOptions[key]) this.whereOptions[key] = {}
+        this.whereOptions[key]["$lte"] = value
+    }
+    equalTo(key,value){
+        this.whereOptions[key] = value
+    }
+
+    async get(id){
+        let url = "http://dev.fmode.cn:1337/parse/classes/"+this.className+"/"+id+"?"
+
+        let response = await fetch(url, {
+            "headers": {
+            "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+            "x-parse-application-id": "dev"
+            },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+        });
+        let json = await response?.json();
+        return json || {}
+    }
+    async find(){
+        let url = "http://dev.fmode.cn:1337/parse/classes/"+this.className+"?"
+        
+        if(Object.keys(this.whereOptions)?.length){
+            let whereStr = JSON.stringify(this.whereOptions)
+            url += `where=${whereStr}`
+        }
+
+        let response = await fetch(url, {
+            "headers": {
+            "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+            "x-parse-application-id": "dev"
+            },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+        });
+        let json = await response?.json();
+        return json?.results || []
+    }
+    first(){
+
+    }
+}
+
+module.exports.CloudObject = CloudObject
+module.exports.CloudQuery = CloudQuery

+ 28 - 0
FilmDraw-server/migration/import-data.js

@@ -0,0 +1,28 @@
+const { CloudQuery, CloudObject } = require("../lib/ncloud");
+testCRUD()
+// testQuery()
+
+async function testQuery(){
+    let query = new CloudQuery("FilmRole")
+    // query.equalTo("gender","女")
+    // query.greaterThanAndEqualTo("age",40)
+    // query.lessThan("age",41)
+    let list = await query.find();
+    console.log(list)
+}
+
+async function testCRUD(){
+    // 基本的增删查改测试
+    let query = new CloudQuery("FilmRole")
+    let FilmRoleList = await query.find();
+    console.log("FilmRolelist count",FilmRoleList?.length)
+
+    let newFilmRole = new CloudObject("FilmRole")
+    newFilmRole.set({"name":"123"})
+    
+    newFilmRole = await newFilmRole.save(newFilmRole)
+    console.log("newFilmRole",newFilmRole)
+
+    await newFilmRole.destory()
+    console.log("newFilmRole 已删除",newFilmRole)
+}