Forráskód Böngészése

update:Fix the vulnerability and Increase the limit for non-logged-in users

csdn1233 3 hónapja
szülő
commit
88be187871

+ 1 - 1
AIart-app/src/app/app.routes.ts

@@ -20,5 +20,5 @@ export const routes: Routes = [
     path: 'chat/pro/chat/:chatId',
     redirectTo: '/chat/session/chat/:chatId',
     pathMatch: 'full'
-  },
+  }
 ];

+ 50 - 0
AIart-app/src/app/art-detail/art-detail.page.html

@@ -0,0 +1,50 @@
+<ion-header [translucent]="true" class="ion-no-border">
+    <ion-toolbar>
+        <ion-buttons slot="start">
+            <ion-back-button defaultHref="/tabs/tab3"></ion-back-button>
+        </ion-buttons>
+        <ion-title>作品详情</ion-title>
+    </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+    <div class="art-detail">
+        <img [src]="artDetail.image" [alt]="artDetail.title" class="main-image">
+
+        <div class="content-section">
+            <h1>{{artDetail.title}}</h1>
+            <p class="description">{{artDetail.description}}</p>
+
+            <div class="interaction-bar">
+                <ion-button fill="clear" (click)="toggleLike()" [color]="isLiked ? 'danger' : 'medium'">
+                    <ion-icon slot="start" [name]="isLiked ? 'heart' : 'heart-outline'"></ion-icon>
+                    {{likeCount}}
+                </ion-button>
+            </div>
+        </div>
+
+        <div class="comments-section">
+            <h2>评论</h2>
+
+            <div class="comment-input">
+                <ion-item>
+                    <ion-textarea [(ngModel)]="newComment" placeholder="写下你的评论..." rows="3"></ion-textarea>
+                </ion-item>
+                <ion-button expand="block" (click)="addComment()">发表评论</ion-button>
+            </div>
+
+            <div class="comments-list">
+                @for(comment of comments; track comment.id) {
+                <div class="comment-item">
+                    <div class="comment-header">
+                        <img [src]="comment.userAvatar || 'assets/default-avatar.png'" alt="avatar">
+                        <span class="username">{{comment.username}}</span>
+                        <span class="time">{{comment.createdAt | date:'MM-dd HH:mm'}}</span>
+                    </div>
+                    <p class="comment-content">{{comment.content}}</p>
+                </div>
+                }
+            </div>
+        </div>
+    </div>
+</ion-content>

+ 90 - 0
AIart-app/src/app/art-detail/art-detail.page.scss

@@ -0,0 +1,90 @@
+.art-detail {
+    .main-image {
+        width: 100%;
+        height: 300px;
+        object-fit: cover;
+    }
+
+    .content-section {
+        padding: 20px;
+
+        h1 {
+            font-size: 24px;
+            font-weight: 600;
+            margin: 0 0 12px;
+            color: #333;
+        }
+
+        .description {
+            font-size: 16px;
+            line-height: 1.6;
+            color: #666;
+            margin-bottom: 20px;
+        }
+
+        .interaction-bar {
+            display: flex;
+            align-items: center;
+            gap: 16px;
+            padding: 8px 0;
+            border-bottom: 1px solid #eee;
+        }
+    }
+
+    .comments-section {
+        padding: 20px;
+
+        h2 {
+            font-size: 18px;
+            font-weight: 600;
+            margin: 0 0 16px;
+            color: #333;
+        }
+
+        .comment-input {
+            margin-bottom: 24px;
+
+            ion-button {
+                margin-top: 12px;
+            }
+        }
+
+        .comments-list {
+            .comment-item {
+                margin-bottom: 20px;
+                padding-bottom: 20px;
+                border-bottom: 1px solid #eee;
+
+                .comment-header {
+                    display: flex;
+                    align-items: center;
+                    gap: 8px;
+                    margin-bottom: 8px;
+
+                    img {
+                        width: 32px;
+                        height: 32px;
+                        border-radius: 50%;
+                    }
+
+                    .username {
+                        font-weight: 500;
+                        color: #333;
+                    }
+
+                    .time {
+                        font-size: 12px;
+                        color: #999;
+                    }
+                }
+
+                .comment-content {
+                    font-size: 14px;
+                    line-height: 1.5;
+                    color: #666;
+                    margin: 0;
+                }
+            }
+        }
+    }
+}

+ 89 - 0
AIart-app/src/app/art-detail/art-detail.page.ts

@@ -0,0 +1,89 @@
+import { Component, OnInit, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { ActivatedRoute } from '@angular/router';
+import { IonHeader, IonToolbar, IonTitle, IonContent, IonButton, IonIcon, IonBackButton, IonItem, IonLabel, IonInput, IonTextarea, IonButtons } from '@ionic/angular/standalone';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { CloudObject, CloudQuery } from 'src/lib/ncloud';
+
+@Component({
+    selector: 'app-art-detail',
+    templateUrl: './art-detail.page.html',
+    styleUrls: ['./art-detail.page.scss'],
+    standalone: true,
+    schemas: [CUSTOM_ELEMENTS_SCHEMA],
+    imports: [
+        CommonModule,
+        FormsModule,
+        IonHeader,
+        IonToolbar,
+        IonTitle,
+        IonContent,
+        IonButton,
+        IonIcon,
+        IonBackButton,
+        IonItem,
+        IonLabel,
+        IonInput,
+        IonTextarea,
+        IonButtons
+    ]
+})
+export class ArtDetailPage implements OnInit {
+    artDetail: any = {};
+    comments: any[] = [];
+    newComment: string = '';
+    isLiked: boolean = false;
+    likeCount: number = 0;
+
+    constructor(private route: ActivatedRoute) { }
+
+    ngOnInit() {
+        const id = this.route.snapshot.paramMap.get('id');
+        if (id) {
+            this.loadArtDetail(id);
+            this.loadComments(id);
+        }
+    }
+
+    async loadArtDetail(id: string) {
+        const query = new CloudQuery('ArtWork');
+        const result = await query.get(id);
+        this.artDetail = result;
+        this.likeCount = result['get']('likes') || 0;
+        this.checkLikeStatus();
+    }
+
+    async loadComments(artId: string) {
+        const query = new CloudQuery('Comments');
+        query.equalTo('artId', artId);
+        const results = await query.find();
+        this.comments = results || [];
+    }
+
+    async toggleLike() {
+        if (!this.isLiked) {
+            this.likeCount++;
+            this.isLiked = true;
+            this.artDetail.set('likes', this.likeCount);
+            await this.artDetail.save();
+        }
+    }
+
+    async addComment() {
+        if (!this.newComment.trim()) return;
+
+        const comment = new CloudObject('Comments');
+        // comment.set('artId', this.artDetail.id);
+        // comment.set('content', this.newComment);
+        // comment.set('createdAt', new Date());
+        await comment.save();
+
+        await this.loadComments(this.artDetail.id);
+        this.newComment = '';
+    }
+
+    private async checkLikeStatus() {
+        // 这里可以添加检查用户是否已点赞的逻辑
+        // 可以使用 localStorage 或者数据库来存储用户的点赞状态
+    }
+} 

+ 1 - 1
AIart-app/src/app/comp-markmap/comp-markmap.component.ts

@@ -92,7 +92,7 @@ export class CompMarkmapComponent implements AfterViewInit {
                 duration: 500,
                 maxWidth: 800,
                 nodeFormatter: (node: any) => {
-                  if (node.depth > 0) { // 只给子节点添加复选框
+                  if (node.depth > 0) {
                     const isChecked = this.nodeStates[node.id] ? 'checked' : '';
                     const originalContent = node.content;
                     node.content = `

+ 40 - 6
AIart-app/src/app/tab1/tab1.page.ts

@@ -4,10 +4,12 @@ import {
   IonHeader, IonToolbar, IonTitle, IonContent, IonSearchbar, IonIcon,
   IonTabButton, IonTabs, IonTabBar, IonLabel, IonNav, IonImg, IonSegmentView,
   IonSegment, IonSegmentButton, IonSegmentContent, IonThumbnail, IonButton, IonRippleEffect,
-  IonBackButton
+  IonBackButton, ModalController
 } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
 import { Router } from '@angular/router';
+import { CloudUser } from 'src/lib/ncloud';
+import { openUserLoginModal } from 'src/lib/user/model-user-login/model-user-login.component';
 
 interface Slide {
   image: string;
@@ -43,7 +45,10 @@ export class Tab1Page implements OnInit, OnDestroy {
   private touchDeltaX = 0;
   private slideWidth = 0;
 
-  constructor(private router: Router) { }
+  constructor(
+    private router: Router,
+    private modalCtrl: ModalController
+  ) { }
 
   ngOnInit() {
     this.startAutoPlay();
@@ -119,19 +124,48 @@ export class Tab1Page implements OnInit, OnDestroy {
     this.startAutoPlay();
   }
 
+  async checkLoginAndNavigate(path: string) {
+    const currentUser = new CloudUser();
+    if (!currentUser.id) {
+      const alert = document.createElement('ion-alert');
+      alert.header = '需要登录';
+      alert.message = '请先登录后再使用此功能';
+      alert.buttons = [
+        {
+          text: '取消',
+          role: 'cancel'
+        },
+        {
+          text: '去登录',
+          handler: async () => {
+            const user = await openUserLoginModal(this.modalCtrl);
+            if (user) {
+              this.router.navigate([path]);
+            }
+          }
+        }
+      ];
+      document.body.appendChild(alert);
+      await alert.present();
+    } else {
+      this.router.navigate([path]);
+    }
+  }
+
   goToInterestTest() {
-    this.router.navigate(['/tabs/interest-test'])
+    this.checkLoginAndNavigate('/tabs/interest-test');
   }
+
   goToViewAll() {
     this.router.navigate(['/tabs/view-all'])
   }
+
   goToInterestPicture() {
     this.router.navigate(['/tabs/interest-picture'])
   }
+
   goToInterestSearch() {
-    this.router.navigate(['/tabs/interest-search'])
+    this.checkLoginAndNavigate('/tabs/interest-search');
   }
 
-
-
 }

+ 5 - 67
AIart-app/src/app/tab3/tab3.page.html

@@ -40,11 +40,11 @@
 
   <!-- 内容网格 -->
   <div class="content-grid">
-    <!-- 内容卡片1 -->
-    <div class="content-card">
-      <img src="../../assets/img/xingkong.png" alt="星空-梵高" class="card-image">
+    @for(artwork of artworks; track artwork.id) {
+    <div class="content-card" (click)="goToDetail(artwork.id)">
+      <img [src]="artwork.image" [alt]="artwork.title" class="card-image">
       <div class="card-content">
-        <div class="title">梵高-星空高清画作 #致敬梵高 #艺术品 #星空</div>
+        <div class="title">{{artwork.title}} {{artwork.description}}</div>
         <div class="user-info">
           <div class="user">
             <img src="../../assets/img/book1.png" alt="头像" class="avatar">
@@ -60,69 +60,7 @@
         </div>
       </div>
     </div>
-
-    <!-- 内容卡片2 -->
-    <div class="content-card">
-      <img src="../../assets/img/xiangrikui.png" alt="向日葵-梵高" class="card-image">
-      <div class="card-content">
-        <div class="title">梵高《向日葵》,经典中的经典,笔触很牛!#花</div>
-        <div class="user-info">
-          <div class="user">
-            <img src="../../assets/img/book2.png" alt="头像" class="avatar">
-            <div class="info">
-              <span class="name">HestLF艺术</span>
-              <span class="date">11-07</span>
-            </div>
-          </div>
-          <div class="likes">
-            <ion-icon name="heart-outline"></ion-icon>
-            <span>310</span>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- 内容卡片3 -->
-    <div class="content-card">
-      <img src="../../assets/img/fangao.png" alt="梵高自画像" class="card-image">
-      <div class="card-content">
-        <div class="title">梵高生平自画像之其中🔟副👩‍🎨 #艺术欣赏 #美</div>
-        <div class="user-info">
-          <div class="user">
-            <img src="../../assets/img/book3.png" alt="头像" class="avatar">
-            <div class="info">
-              <span class="name">小梅同学</span>
-              <span class="date">10-5</span>
-            </div>
-          </div>
-          <div class="likes">
-            <ion-icon name="heart-outline"></ion-icon>
-            <span>1.5万</span>
-          </div>
-        </div>
-      </div>
-    </div>
-
-    <!-- 内容卡片4 -->
-    <div class="content-card">
-      <img src="../../assets/img/cunzhuang.png" alt="阿尔小镇" class="card-image">
-      <div class="card-content">
-        <div class="title">走吧,梵高,去你最爱的阿尔看看,���阿尔小镇》</div>
-        <div class="user-info">
-          <div class="user">
-            <img src="../../assets/img/book4.png" alt="头像" class="avatar">
-            <div class="info">
-              <span class="name">玛丽的名画</span>
-              <span class="date">11-19</span>
-            </div>
-          </div>
-          <div class="likes">
-            <ion-icon name="heart-outline"></ion-icon>
-            <span>180</span>
-          </div>
-        </div>
-      </div>
-    </div>
+    }
   </div>
 </ion-content>
 

+ 34 - 1
AIart-app/src/app/tab3/tab3.page.ts

@@ -1,6 +1,7 @@
 import { Component } from '@angular/core';
 import { IonHeader, IonToolbar, IonTitle, IonContent, IonSearchbar, IonIcon, IonTabButton, IonTabs, IonTabBar, IonLabel, IonMenu, IonList, IonItem, IonMenuButton } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { Router } from '@angular/router';
 
 @Component({
   selector: 'app-tab3',
@@ -13,10 +14,37 @@ import { ExploreContainerComponent } from '../explore-container/explore-containe
   ],
 })
 export class Tab3Page {
+  artworks = [
+    {
+      id: '1',
+      image: '../../assets/img/xingkong.png',
+      title: '梵高-星空高清画作',
+      description: '致敬梵高 #艺术品 #星空'
+    },
+    {
+      id: '2',
+      image: '../../assets/img/xiangrikui.png',
+      title: '梵高《向日葵》',
+      description: '经典中的经典,笔触很牛!#花'
+    },
+    {
+      id: '3',
+      image: '../../assets/img/fangao.png',
+      title: '梵高自画像',
+      description: '梵高生平自画像之其中🔟副👩‍🎨 #艺术欣赏 #美'
+    },
+    {
+      id: '4',
+      image: '../../assets/img/cunzhuang.png',
+      title: '阿尔小镇',
+      description: '走吧,梵高,去你最爱的阿尔看看,阿尔小镇'
+    }
+  ];
+
   ngOnInit() {
     this.red_underline('发现');
   }
-  constructor() { }
+  constructor(private router: Router) { }
   red_underline(text: string) {
     const prevUnderlined = document.querySelectorAll('span[underline="true"]') as NodeListOf<HTMLElement>;
     prevUnderlined.forEach((el: HTMLElement) => {
@@ -30,4 +58,9 @@ export class Tab3Page {
       target.setAttribute('underline', 'true');
     }
   }
+
+  goToDetail(artId: string) {
+    console.log('Navigating to artwork:', artId);
+    this.router.navigate(['/tabs/art-detail', artId]);
+  }
 }

+ 109 - 64
AIart-app/src/app/tab4/tab4.page.ts

@@ -1,5 +1,5 @@
 import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent, ModalController, IonButton, IonSearchbar, IonIcon, IonTabButton, IonTabs, IonTabBar, IonLabel, IonNav, IonImg, IonSegmentView, IonSegment, IonSegmentButton, IonSegmentContent, IonThumbnail, IonRippleEffect, IonBackButton } from '@ionic/angular/standalone';
+import { IonHeader, IonToolbar, IonTitle, IonContent, ModalController, IonButton, IonSearchbar, IonIcon, IonTabButton, IonTabs, IonTabBar, IonLabel, IonNav, IonImg, IonSegmentView, IonSegment, IonSegmentButton, IonSegmentContent, IonThumbnail, IonRippleEffect, IonBackButton, ToastController } from '@ionic/angular/standalone';
 import { ExploreContainerComponent } from '../explore-container/explore-container.component';
 import { Router } from '@angular/router';
 import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
@@ -10,6 +10,8 @@ import { IonicModule } from '@ionic/angular';
 import { NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common';
 import { InterestSearchComponent } from '../interest-search/interest-search.component';
 import { FormsModule } from '@angular/forms';
+import { CloudUser } from 'src/lib/ncloud';
+import { openUserLoginModal } from 'src/lib/user/model-user-login/model-user-login.component';
 
 @Component({
   selector: 'app-tab4',
@@ -30,7 +32,7 @@ export class tab4Page {
   constructor(
     private modalCtrl: ModalController,
     private router: Router,
-
+    private toastCtrl: ToastController
   ) {
 
   }
@@ -47,81 +49,124 @@ export class tab4Page {
   planMessage: any
   markmapResult: string = "";
   /** 示例:兴趣规划聊天ChatPanel面板 */
-  openInquiry() {
-    localStorage.setItem("company", "E4KpGvTEto")
-    let options: ChatPanelOptions = {
-      roleId: "2DXJkRsjXK",
-      onChatInit: (chat: FmodeChat) => {
-        console.log("onChatInit");
-        console.log("预设角色", chat.role);
-        chat.role.set("name", "明明");
-        chat.role.set("title", "兴趣学习规划师");
-        chat.role.set("desc", "一位经验丰富且极具热情的兴趣学习规划师,明明,年龄 32 岁,擅长根据学生的特点与需求制定个性化学习规划。");
-        chat.role.set("tags", ["兴趣学习规划", "个性化方案"]);
-        chat.role.set("avatar", "https://nova-cloud.obs.cn-south-1.myhuaweicloud.com/storage/aigc/imagine/Q4Zif7fTbK-0.png")
-        chat.role.set("prompt", `
-        您是一位经验丰富且极具热情的兴趣学习规划师,明明,年龄 32 岁,需要为学生制定个性化的兴趣学习规划。
+  async checkLogin(): Promise<boolean> {
+    const currentUser = new CloudUser();
+    if (!currentUser.id) {
+      alert("需要登录")
+      return new Promise<boolean>((resolve) => {
+        const alert = document.createElement('ion-alert');
+        alert.header = '需要登录';
+        alert.message = '请先登录后再使用AI助手功能';
+        alert.buttons = [
+          {
+            text: '取消',
+            role: 'cancel',
+            handler: () => {
+              resolve(false);
+            }
+          },
+          {
+            text: '去登录',
+            handler: () => {
+              openUserLoginModal(this.modalCtrl).then(user => {
+                if (user) {
+                  resolve(true);
+                } else {
+                  resolve(false);
+                }
+              });
+              return false; // 防止alert自动关闭
+            }
+          }
+        ];
+        document.body.appendChild(alert);
+        alert.present();
+      });
+    }
+    return true;
+  }
+
+  async openInquiry() {
+    const isLoggedIn = await this.checkLogin();
+    console.log('Login status:', isLoggedIn); // 添加调试日志
+    if (isLoggedIn) {
+      localStorage.setItem("company", "E4KpGvTEto");
+      let options: ChatPanelOptions = {
+        roleId: "2DXJkRsjXK",
+        onChatInit: (chat: FmodeChat) => {
+          console.log("onChatInit");
+          console.log("预设角色", chat.role);
+          chat.role.set("name", "明明");
+          chat.role.set("title", "兴趣学习规划师");
+          chat.role.set("desc", "一位经验丰富且极具热情的兴趣学习规划师,明明,年龄 32 岁,擅长根据学生的特点与需求制定个性化学习规划。");
+          chat.role.set("tags", ["兴趣学习规划", "个性化方案"]);
+          chat.role.set("avatar", "https://nova-cloud.obs.cn-south-1.myhuaweicloud.com/storage/aigc/imagine/Q4Zif7fTbK-0.png")
+          chat.role.set("prompt", `
+          您是一位经验丰富且极具热情的兴趣学习规划师,明明,年龄 32 岁,需要为学生制定个性化的兴趣学习规划。
 
-        # 对话环节
-        0. 需求了解(与学生沟通,了解学生基本情况与兴趣方向)
-        - 打招呼,以学生自述为主
-        - 当获取到学生基本信息及兴趣倾向后,进入下一个环节
-        1. 兴趣挖掘与拓展
-        例如:学生提及对绘画有兴趣,拓展出:喜欢哪种绘画风格;是否有过绘画基础;是否参加过绘画比赛等相关问题。
-        - 当兴趣挖掘与拓展完成后进入下一个环节
-        2. 学习规划制定
-        根据学生情况制定包括学习目标、学习资源、学习进度安排等在内的兴趣学习规划。
-        - 等待学生反馈意见,进入下一阶段
-        3. 规划调整与完善
-        根据反馈对学习规划进行调整优化,并给出最终的详细规划方案。
-        - 完成规划方案时,请直接用markmap格式编写方案,具体格式严格按照
-        \`\`\` markdown
-        # XXX计划
-        ## 二级
-        - 三级
-        - 三级
-        \`\`\`
-        请直接返回markdown内生成的方案内容,不用有其他的赘述。并且返回的内容结尾要有[完成]
+          # 对话环节
+          0. 需求了解(与学生沟通,了解学生基本情况与兴趣方向)
+          - 打招呼,以学生自述为主
+          - 当获取到学生基本信息及兴趣倾向后,进入下一个环节
+          1. 兴趣挖掘与拓展
+          例��:学生提及对绘画有兴趣,拓展出:喜欢��种绘画风格;是否有过绘画基础;是否参加过绘画比赛等相关问题。
+          - 当兴趣挖掘与拓展完成后进入下一个环节
+          2. 学习规划制定
+          根据学生情况制定包括学习目标、学习资源、学习进度安排等在内的兴趣学习规划。
+          - 等待学生反馈意见,进入下一阶段
+          3. 规划调整与完善
+          根据反馈对学习规划进行调整优化,并给出最终的详细规划方案。
+          - 完成规划方案时,请直接用markmap格式编写方案,具体格式严格按照
+          \`\`\` markdown
+          # XXX计划
+          ## 二级
+          - 三级
+          - 三级
+          \`\`\`
+          请直接返回markdown内生成的方案内容,不用有其他的赘述。并且返回的内容结尾要有[完成]
 
-        当您准备好了,可以以一个兴趣学习规划师的身份,向来访的学生打招呼。`);
-      },
-      onMessage: (chat: FmodeChat, message: FmodeChatMessage) => {
-        console.log("onMessage", message)
-        let content: any = message?.content
-        if (typeof content == "string") {
-          if (content?.indexOf("```") > -1) {
-            console.log("兴趣分析已完成");
-            let markMapContent = content.split("```")[1].slice(9)
-            this.markmapResult = markMapContent
-            let plan = new CloudObject("GoPlan");
-            let now = new Date();
-            plan.set({
-              title: "计划" + now.getFullYear() + "-" + now.getMonth() + 1 + "-" + now.getDate() + "-" + now.getTime(),
-              markmap: markMapContent
-            })
-            plan.save();
-            this.planMessage = message
+          当您准备好了,可以以一个兴趣学习规划师的身份,向来访的学生打招呼。`);
+        },
+        onMessage: (chat: FmodeChat, message: FmodeChatMessage) => {
+          console.log("onMessage", message)
+          let content: any = message?.content
+          if (typeof content == "string") {
+            if (content?.indexOf("```") > -1) {
+              console.log("兴趣分析已完成");
+              let markMapContent = content.split("```")[1].slice(9)
+              this.markmapResult = markMapContent
+              let plan = new CloudObject("GoPlan");
+              let now = new Date();
+              plan.set({
+                title: "计划" + now.getFullYear() + "-" + now.getMonth() + 1 + "-" + now.getDate() + "-" + now.getTime(),
+                markmap: markMapContent
+              })
+              plan.save();
+              this.planMessage = message
+            }
           }
+        },
+        onChatSaved: (chat: FmodeChat) => {
+          // chat?.chatSession?.id 本次会话的 chatId
+          console.log("onChatSaved", chat, chat?.chatSession, chat?.chatSession?.id)
         }
-      },
-      onChatSaved: (chat: FmodeChat) => {
-        // chat?.chatSession?.id 本次会话的 chatId
-        console.log("onChatSaved", chat, chat?.chatSession, chat?.chatSession?.id)
       }
+      openChatPanelModal(this.modalCtrl, options)
     }
-    openChatPanelModal(this.modalCtrl, options)
   }
 
   /**
    * 恢复聊天
    * @chatId 从onChatSaved生命周期中,获取chat?.chatSession?.id
    */
-  restoreChat(chatId: string) {
-    let options: ChatPanelOptions = {
-      roleId: "2DXJkRsjXK",
-      chatId: chatId
+  async restoreChat(chatId: string) {
+    if (await this.checkLogin()) {
+      let options: ChatPanelOptions = {
+        roleId: "2DXJkRsjXK",
+        chatId: chatId
+      }
+      openChatPanelModal(this.modalCtrl, options)
     }
-    openChatPanelModal(this.modalCtrl, options)
   }
 
 

+ 2 - 3
AIart-app/src/app/tabs/tabs.routes.ts

@@ -57,9 +57,8 @@ export const routes: Routes = [
           import('../user-login/user-login.component').then((m) => m.UserLoginComponent),
       },
       {
-        path: '',
-        redirectTo: '/tabs/tab1',
-        pathMatch: 'full',
+        path: 'art-detail/:id',
+        loadComponent: () => import('../art-detail/art-detail.page').then(m => m.ArtDetailPage)
       },
     ],
   },

+ 31 - 3
AIart-app/src/app/user-login/user-login.component.ts

@@ -4,6 +4,7 @@ import { EditTagComponent } from '../edit-tag/edit-tag.component';
 import { CloudUser } from 'src/lib/ncloud';
 import { openUserLoginModal } from 'src/lib/user/model-user-login/model-user-login.component';
 import { openUserEditModal } from 'src/lib/user/model-user-edit/model-user-edit.component';
+import { Router } from '@angular/router';
 
 @Component({
   selector: 'app-user-login',
@@ -20,7 +21,7 @@ export class UserLoginComponent implements OnInit {
 
   }
   currentUser: CloudUser | undefined
-  constructor(private modalCtrl: ModalController) {
+  constructor(private modalCtrl: ModalController, private router: Router) {
     this.currentUser = new CloudUser();
   }
   async login() {
@@ -37,8 +38,35 @@ export class UserLoginComponent implements OnInit {
       this.currentUser = user
     }
   }
-  logout() {
-    this.currentUser?.logout();
+  async logout() {
+    try {
+      let user = new CloudUser();
+      await user.logout();
+
+      // 刷新页面状态
+      this.currentUser = undefined;
+
+      // 显示成功提示
+      const toast = document.createElement('ion-toast');
+      toast.message = '退出登录成功';
+      toast.duration = 2000;
+      toast.position = 'top';
+      toast.color = 'success';
+      document.body.appendChild(toast);
+      await toast.present();
+
+      // 重定向到首页
+      this.router.navigate(['/tabs/tab1']);
+    } catch (error) {
+      console.error('Logout error:', error);
+      const toast = document.createElement('ion-toast');
+      toast.message = '退出登录失败,请重试';
+      toast.duration = 2000;
+      toast.position = 'top';
+      toast.color = 'danger';
+      document.body.appendChild(toast);
+      await toast.present();
+    }
   }
 
   editUser() {

+ 32 - 0
src/lib/user/model-user-login/model-user-login.component.ts

@@ -0,0 +1,32 @@
+export class ModelUserLoginComponent implements OnInit {
+    // ... 其他代码保持不变 ...
+
+    async logout() {
+        try {
+            let user = new CloudUser();
+            await user.logout();  // 调用 CloudUser 的 logout 方法
+
+            // 显示成功提示
+            const toast = await this.toastCtrl.create({
+                message: '退出登录成功',
+                duration: 2000,
+                position: 'top',
+                color: 'success'
+            });
+            await toast.present();
+
+            // 关闭模态框并返回 null
+            this.modalCtrl.dismiss(null, "logout");
+        } catch (error) {
+            console.error('Logout error:', error);
+            // 显示错误提示
+            const toast = await this.toastCtrl.create({
+                message: '退出登录失败,请重试',
+                duration: 2000,
+                position: 'top',
+                color: 'danger'
+            });
+            await toast.present();
+        }
+    }
+}