Browse Source

最终暂存版

s202226701053 6 months ago
parent
commit
8ee7928cea
47 changed files with 2320 additions and 258 deletions
  1. 133 0
      E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.html
  2. 417 0
      E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.scss
  3. 22 0
      E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.spec.ts
  4. 290 0
      E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.ts
  5. 21 1
      E-Cover-app/src/app/app.routes.ts
  6. 73 0
      E-Cover-app/src/app/chat-content/chat-content.component.html
  7. 417 0
      E-Cover-app/src/app/chat-content/chat-content.component.scss
  8. 22 0
      E-Cover-app/src/app/chat-content/chat-content.component.spec.ts
  9. 47 0
      E-Cover-app/src/app/chat-content/chat-content.component.ts
  10. 38 0
      E-Cover-app/src/app/chat-history/chat-history.component.html
  11. 69 0
      E-Cover-app/src/app/chat-history/chat-history.component.scss
  12. 22 0
      E-Cover-app/src/app/chat-history/chat-history.component.spec.ts
  13. 103 0
      E-Cover-app/src/app/chat-history/chat-history.component.ts
  14. 11 0
      E-Cover-app/src/app/generate-history/generate-history.component.html
  15. 25 0
      E-Cover-app/src/app/generate-history/generate-history.component.scss
  16. 22 0
      E-Cover-app/src/app/generate-history/generate-history.component.spec.ts
  17. 48 0
      E-Cover-app/src/app/generate-history/generate-history.component.ts
  18. 0 7
      E-Cover-app/src/app/generate-option/generate-option.component.html
  19. 20 5
      E-Cover-app/src/app/generate-option/generate-option.component.ts
  20. 11 26
      E-Cover-app/src/app/generate-result/generate-result.component.html
  21. 45 43
      E-Cover-app/src/app/generate-result/generate-result.component.scss
  22. 33 54
      E-Cover-app/src/app/generate-result/generate-result.component.ts
  23. BIN
      E-Cover-app/src/app/generate-result/style-img/banner-color1.png
  24. BIN
      E-Cover-app/src/app/generate-result/style-img/test.png
  25. 1 1
      E-Cover-app/src/app/send-post/send-post.component.html
  26. 0 1
      E-Cover-app/src/app/send-post/send-post.component.scss
  27. 1 1
      E-Cover-app/src/app/tab1/tab1.page.html
  28. 3 0
      E-Cover-app/src/app/tab1/tab1.page.ts
  29. 1 1
      E-Cover-app/src/app/tab3/tab3.page.html
  30. 2 1
      E-Cover-app/src/app/tab3/tab3.page.scss
  31. 6 2
      E-Cover-app/src/app/tab3/tab3.page.ts
  32. BIN
      E-Cover-app/src/app/tabs/style-img/footer-bg.png
  33. 1 1
      E-Cover-app/src/app/tabs/tabs.page.html
  34. 3 2
      E-Cover-app/src/app/tabs/tabs.page.scss
  35. 2 2
      E-Cover-app/src/app/tabs/tabs.page.ts
  36. 1 1
      E-Cover-app/src/lib/component/custom-header/custom-header.component.html
  37. 2 2
      E-Cover-app/src/lib/component/custom-header/custom-header.component.ts
  38. 76 29
      E-Cover-app/src/lib/component/post-detail/post-detail.component.html
  39. 124 1
      E-Cover-app/src/lib/component/post-detail/post-detail.component.scss
  40. 110 17
      E-Cover-app/src/lib/component/post-detail/post-detail.component.ts
  41. 1 1
      E-Cover-app/src/lib/component/post-list/post-list.component.html
  42. 13 2
      E-Cover-app/src/lib/component/post-list/post-list.component.ts
  43. 48 34
      E-Cover-app/src/lib/ncloud.ts
  44. 8 2
      E-Cover-app/src/lib/user/modal-user-login-main/modal-user-login-main.component.html
  45. 25 18
      E-Cover-app/src/lib/user/modal-user-login-main/modal-user-login-main.component.ts
  46. 2 2
      E-Cover-app/src/lib/user/modal-user-login/modal-user-login.component.html
  47. 1 1
      E-Cover-app/src/lib/user/modal-user-login/modal-user-login.component.ts

+ 133 - 0
E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.html

@@ -0,0 +1,133 @@
+<!--头部内容-->
+<ion-header>
+  <ion-toolbar class="custom-toolbar">
+    <ion-buttons slot="start">
+      <ion-button (click)="goBack()"> <!--返回按钮-->
+        <ion-icon name="chevron-back-sharp" style="color: black; font-size:27px"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+    <ion-title>搭配贴士</ion-title> <!--AI名称-->
+    <ion-buttons slot="end">
+      <ion-button (click)="goHistory()"> <!--更多按钮-->
+        <ion-icon name="ellipsis-horizontal" style="color: black; font-size:27px"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+
+<!--聊天区域:聊天内容保存在messages-->
+<ion-content>
+  <div class="chat-container">
+    <div *ngFor="let message of messages" class="message-container">
+      <!-- 用户消息 -->
+      <div *ngIf="message.sender === 'user'" class="message-content user-message-content">
+        <div class="message-bubble user-message">
+          {{ message.text }}
+        </div>
+        <div class="user-avatar">
+          <img [src]="currentUser?.get('avatar')" alt="用户头像" />
+        </div>
+      </div>
+
+      <!-- AI消息 -->
+      <div *ngIf="message.sender === 'ai'" class="message-content ai-message-content">
+        <div class="ai-avatar">
+          <img src="https://s1.imagehub.cc/images/2024/12/23/4b30d81d915b24113540c7585809c689.png" alt="AI头像" />
+        </div>
+        <div class="message-bubble ai-message">
+          {{ message.text }}
+        </div>
+      </div>
+    </div>
+
+    <!--当AI正在生成内容时显示加载动画-->
+    <div *ngIf="isLoading" class="message-content ai-message-content">
+      <div class="ai-avatar">
+        <img src="https://s1.imagehub.cc/images/2024/12/23/4b30d81d915b24113540c7585809c689.png" alt="AI头像" />
+      </div>
+      <div class="message-bubble ai-message">
+        <div class="loading-dots">
+          <div class="loading-dot"></div>
+          <div class="loading-dot"></div>
+          <div class="loading-dot"></div>
+        </div>
+      </div>
+    </div>
+  </div>
+</ion-content>
+
+<!-- 表情模拟框 -->
+<ion-modal [isOpen]="isEmojiPickerOpen" (didDismiss)="closeEmojiPicker()" [initialBreakpoint]="0.71"
+  [breakpoints]="[0, 0.25,0.50,0.71]" handleBehavior="cycle">
+  <ng-template>
+    <ion-content class="emoji-picker">
+      <div class="emoji-container">
+        <div *ngFor="let emoji of emojis" (click)="addEmoji(emoji)" class="emoji-button"> <!-- 表情按钮 -->
+          {{ emoji }}
+        </div>
+      </div>
+    </ion-content>
+  </ng-template>
+</ion-modal>
+
+<!--底部内容-->
+<ion-footer style="background-color: #99d75c;">
+  <ion-toolbar>
+    <div class="footer-content">
+
+      <ion-buttons> <!--语音输入-->
+        <ion-button (click)="startVoice()" fill="clear" id="open-modal">
+          <ion-icon name="mic-circle-outline" style="color: white; font-size: 40px;"></ion-icon>
+        </ion-button>
+
+        <!--语音输入模态框 -->
+        <ion-modal [isOpen]="isVoiceModalOpen" (didDismiss)="cancleVoice()" [initialBreakpoint]="0.25"
+          [breakpoints]="[0, 0.25, 0.5, 0.75]" handleBehavior="cycle" [backdropDismiss]="false">
+          <ng-template>
+            <ion-content class="yuyinframe">
+              <div class="modal-content">
+                <!--取消按钮-->
+                <ion-icon name="close-circle-outline" (click)="cancelVoiceInput()" class="cancle-button"></ion-icon>
+
+                <div class="timer-container">
+                  <!--计时器-->
+                  <div style="color: black; font-size: 24px;" id="timer">{{ timer }}</div>
+
+                  <!--音律跳动-->
+                  <div class="light">
+                    <span></span>
+                    <span></span>
+                    <span></span>
+                    <span></span>
+                    <span></span>
+                  </div>
+
+                </div>
+                <!--提交语音内容按钮-->
+                <div class="send-button">
+                  <ion-icon name="send-outline" style="font-size: 40px;" (click)="sendVoiceInput()"> </ion-icon>
+                </div>
+              </div>
+            </ion-content>
+          </ng-template>
+        </ion-modal>
+
+      </ion-buttons>
+      <!--文本输入框-->
+      <ion-input placeholder="输入消息..." class="input-box" [(ngModel)]="userMessage">
+        <ion-button (click)="openEmojiPicker()" fill="clear" slot="end">
+          <ion-icon name="happy-outline" style="color:#99d75c; font-size: 30px;"></ion-icon> <!--表情符号-->
+
+        </ion-button>
+      </ion-input>
+
+      <ion-buttons>
+        <ion-button (click)="sendMessage()" fill="clear"> <!--发送按钮-->
+          <div class="circle">
+            <ion-icon name="paper-plane" style="color: white; font-size: 27px;"></ion-icon>
+          </div>
+        </ion-button>
+      </ion-buttons>
+    </div>
+  </ion-toolbar>
+</ion-footer>

+ 417 - 0
E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.scss

@@ -0,0 +1,417 @@
+ion-toolbar {
+    height: 70px; /* 设置你想要的高度 */
+    --min-height: 60px; /* 设置最小高度 */
+    padding: 0; /* 去掉内边距 */
+    display: flex; /* 使用Flexbox布局 */
+    align-items: center; /* 垂直居中对齐 */
+    --background: transparent; /* 去除背景色 */
+  }
+  //头部样式
+  ion-header{
+    background-color: #99d75c ;
+    --background: transparent; /* 去除背景色 */
+  }
+  //头部标题
+  ion-title {
+    margin: 0; /* 去掉默认的外边距 */
+    flex: 1; /* 让标题占据剩余空间 */
+    text-align: center; /* 让标题文本居中 */
+    font-size: 24px;
+  }
+
+/* 文本输入框样式 */
+.input-box {
+  background-color: white; /* 设置输入框背景为白色 */
+  border-radius: 8px; /* 圆角 */
+ 
+  height: 40px; /* 高度 */
+  flex: 1; /* 让输入框占据剩余空间 */
+}
+
+/* 底部内容容器 */
+.footer-content {
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  width: 100%; /* 宽度100% */
+}
+//底部按钮样式
+ion-buttons {
+  margin: 0 5px; /* 按钮之间的间距 */
+}
+//底部发送按钮圆圈样式
+.circle{
+width: 35px;
+height: 35px;
+border-radius: 50%;
+border:2px white solid;
+display: flex; /* 使用Flexbox布局 */
+align-items: center; /* 垂直居中对齐 */
+justify-content: center; /* 水平居中对齐 */
+}
+
+//聊天内容容器样式
+.chat-container {
+  display: flex;
+  flex-direction: column;
+  padding-top: 10px;
+}
+//聊天内容样式(包括头像和消息)
+.message-container {
+  display: flex; /* 使用Flexbox布局 */
+  justify-content: flex-end; /* 用户消息在右边,AI消息在左边 */
+  margin: 10px 0; /* 消息之间的间距 */
+}
+//消息内容样式
+.message-content {
+  display: flex; /* 使用Flexbox布局 */
+  
+}
+//用户消息和头像样式
+.user-message-content {
+  justify-content: flex-end; /* 用户消息和头像在右边 */
+}
+//AI消息和头像样式
+.ai-message-content {
+  justify-content: flex-start; /* AI消息和头像在左边 */
+}
+/* 气泡样式 */
+.message-bubble {
+  background-color: #99d75c; /* 绿色气泡的背景色 */
+  color: white; /* 字体颜色 */
+  border-radius: 15px; /* 圆角 */
+  padding: 10px 15px; /* 内边距 */
+  max-width: 70%; /* 最大宽度 */
+  white-space: normal; /* 允许换行 */
+  overflow-wrap: break-word; /* 允许在单词边界换行 */
+}
+
+/* 用户消息样式 */
+.user-message {
+  background-color: #99d75c; /* 用户消息的颜色 */
+  margin-left: 10px; /* 与头像之间的间距 */
+}
+
+/* AI消息样式 */
+.ai-message {
+  background-color: white; /* AI消息的气泡颜色 */
+  color: black; /* 字体颜色 */
+  border: 1px solid black; /* 添加黑色边框 */
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); /* 添加阴影效果 */
+  margin-right: 10px; /* 与头像之间的间距 */
+}
+//用户头像样式
+.user-avatar {
+  width: 40px; /* 头像宽度 */
+  height: 40px; /* 头像高度 */
+  border-radius: 50%; /* 圆形 */
+  overflow: hidden; /* 超出部分隐藏 */
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  background-color: #f0f0f0; /* 背景色 */
+  border: 2px solid #99d75c; /* 边框颜色 */
+  margin-left: 5px; /* 与消息之间的间距 */
+}
+//AI头像样式
+.ai-avatar {
+  width: 40px; /* 头像宽度 */
+  height: 40px; /* 头像高度 */
+  border-radius: 50%; /* 圆形 */
+  overflow: hidden; /* 超出部分隐藏 */
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  background-color: #f0f0f0; /* 背景色 */
+  border: 2px solid #99d75c; /* 边框颜色 */
+  margin-right: 5px; /* 与消息之间的间距 */
+}
+//用户头像图片样式
+.user-avatar img {
+  width: 100%; /* 使图片适应头像框 */
+  height: 100%; /* 使图片适应头像框 */
+  object-fit: cover; /* 保持图片比例,裁剪多余部分 */
+}
+//AI头像图片样式
+.ai-avatar img {
+  width: 100%; /* 使图片适应头像框 */
+  height: 100%; /* 使图片适应头像框 */
+  object-fit: cover; /* 保持图片比例,裁剪多余部分 */
+}
+//语音框
+.yuyinframe{
+  --background: transparent; 
+  background-color: #99d75c; 
+  display: flex; 
+  flex-direction: column; 
+  justify-content: center; 
+  align-items: center;
+}
+.modal-content {
+  display: flex;
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  width: 100%; /* 宽度100% */
+  height: 25%;
+}
+//语音框中间内容框
+.timer-container {
+  height: 100%;
+  flex-direction: column; /* 垂直排列 */
+  
+  justify-content: center; /* 水平居中对齐 */
+
+}
+//计时器样式
+#timer{
+  width: 100%;
+  margin-top: 30px; /* 可根据需要调整这个值 */
+  text-align: center; /* 确保文本居中 */
+  margin-bottom: 10px;
+}
+
+/*语音取消按钮的样式 */
+.cancle-button {
+  font-size: 75px;
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  color: white;
+  background-color: #99d75c; 
+  margin-right: 20px;
+}
+
+/*语音发送按钮的样式 */
+.send-button {
+  height:60px ;
+  width: 60px;
+  border-radius: 50%;//圆
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  color: white;
+  background-color: #99d75c; 
+  border: 5px solid white;
+  margin-left: 20px;
+}
+
+
+//音律跳动
+.light {
+  width: 100%;
+  height: 90px;
+  display: flex;
+}
+
+.light span {
+  width: 10px;
+  border-radius: 18px;
+  margin-right: 20px;
+}
+
+.light span:nth-child(1) {
+  animation: bar1 2s 0.2s infinite linear;
+}
+
+.light span:nth-child(2) {
+  animation: bar2 2s 0.4s infinite linear;
+}
+
+.light span:nth-child(3) {
+  animation: bar3 2s 0.6s infinite linear;
+}
+
+.light span:nth-child(4) {
+  animation: bar4 2s 0.8s infinite linear;
+}
+
+.light span:nth-child(5) {
+  animation: bar5 2s 1.0s infinite linear;
+}
+
+.light span:nth-child(6) {
+  animation: bar6 2s 1.2s infinite linear;
+}
+
+.light span:nth-child(7) {
+  animation: bar7 2s 1.4s infinite linear;
+}
+
+.light span:nth-child(8) {
+  animation: bar8 2s 1.6s infinite linear;
+}
+
+.light span:nth-child(9) {
+  animation: bar9 2s 1.8s infinite linear;
+}
+//第一条音律加载动画
+@keyframes bar1 {
+  0% {
+      background: #f677b0;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #f677b0;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #f677b0;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第二条音律加载动画
+@keyframes bar2 {
+  0% {
+      background: #df7ff2;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #df7ff2;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #df7ff2;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第三条音律加载动画
+@keyframes bar3 {
+  0% {
+      background: #8c7ff2;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #8c7ff2;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #8c7ff2;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第四条音律加载动画
+@keyframes bar4 {
+  0% {
+      background: #024b6a;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #024b6a;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #024b6a;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第五条音律加载动画
+@keyframes bar5 {
+  0% {
+      background: #7ff2d3;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #7ff2d3;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #7ff2d3;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//三个点的加载动画
+.loading-dots {
+  display: flex;
+  align-items: center;
+  margin-left: 10px; /* 气泡与点之间的间距 */
+}
+
+.loading-dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background-color: #99d75c; /* 点的颜色 */
+  margin: 0 2px; /* 点之间的间距 */
+  animation: loading 1s infinite; /* 加载动画 */
+}
+//第一个点
+.loading-dot:nth-child(1) {
+  animation-delay: 0s;
+}
+//第二个点
+.loading-dot:nth-child(2) {
+  animation-delay: 0.2s;
+}
+//第三个点
+.loading-dot:nth-child(3) {
+  animation-delay: 0.4s;
+}
+//加载动画
+@keyframes loading {
+  0%, 100% {
+    opacity: 0.5;
+  }
+  50% {
+    opacity: 1;
+  }
+}
+
+
+/* 表情选择器样式 */
+.emoji-picker {
+  --background: transparent; /* 去除默认样式 */
+  background-color: #99d75c; /* 背景颜色 */
+  padding: 10px;
+  display: flex;
+  justify-content: center; /* 水平居中 */
+  overflow: hidden; /* 隐藏多余内容 */
+}
+
+/* 表情容器用于支持滚动 */
+.emoji-container {
+  display: flex;
+  flex-wrap: wrap; /* 允许换行 */
+  overflow-y: auto; /* 允许纵向滚动 */
+  max-height: 70vh; /* 最大高度,防止超出屏幕 */
+  width: 100%; /* 容器宽度 */
+}
+
+/* 表情按钮 */
+.emoji-button {
+  margin: 5px; /* 每个表情与顶部的间距 */
+  font-size: 28px; /* 字体大小 */
+  height: 40px; /* 按钮高度 */
+  width: 40px; /* 按钮宽度 */
+  display: flex; /* 使用 flexbox 对齐 */
+  align-items: center; /* 垂直居中 */
+  justify-content: center; /* 水平居中 */
+  --background: transparent; /* 背景透明 */
+  --box-shadow: none; /* 去掉阴影 */
+  --outline: none; /* 去掉轮廓 */
+  border: none; /* 去掉边框 */
+}

+ 22 - 0
E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.spec.ts

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

+ 290 - 0
E-Cover-app/src/app/ai-chat-component/ai-chat-component.component.ts

@@ -0,0 +1,290 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { NavController } from '@ionic/angular';
+import { IonButtons, IonHeader, IonToolbar, IonButton, IonIcon, IonTitle, IonInput, IonFooter, IonContent, AlertController, } from '@ionic/angular/standalone';
+import { FmodeChatCompletion } from 'fmode-ng';
+import { addIcons } from 'ionicons';
+import { chevronBackSharp, closeCircleOutline, ellipsisHorizontal, happyOutline, micCircleOutline, paperPlane, sendOutline } from 'ionicons/icons';
+import { IonModal, IonLabel } from '@ionic/angular/standalone'; // 导入独立组件
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
+import { ActivatedRoute, Router } from '@angular/router';
+
+addIcons({ chevronBackSharp, ellipsisHorizontal, micCircleOutline, happyOutline, paperPlane, closeCircleOutline, sendOutline });
+
+interface Window {
+  SpeechRecognition: any; // 声明 SpeechRecognition 属性
+  webkitSpeechRecognition: any; // 声明 webkitSpeechRecognition 属性
+}
+
+@Component({
+  selector: 'app-ai-chat-component',
+  templateUrl: './ai-chat-component.component.html',
+  styleUrls: ['./ai-chat-component.component.scss'],
+  standalone: true,
+  imports: [
+    IonHeader, IonToolbar, IonButtons, IonButton, IonIcon, IonTitle, IonInput, IonFooter, CommonModule, IonContent,
+    FormsModule, IonModal, IonLabel,
+  ],
+
+})
+export class AiChatComponentComponent implements OnInit {
+
+  messages: { text: string, sender: string }[] = []; // 存储聊天消息
+  userMessage: string = ''; // 用于用户输入内容
+  aiMessage: string = ''; // 用于存储AI的回复
+  initialPrompt: string = ''; // 用于存储初始化提示
+  recognition: any; // 用于存储语音识别实例
+  recognizedContent: string = ''; // 用于存储识别到的语音内容
+  timer: string = '00:00'; // 用于显示计时器
+  interval: any; // 用于存储定时器的引用
+  elapsedSeconds: number = 0; // 计时器的秒数
+  isLoading: boolean = true; // AI生成文本加载状态,刚开始AI向你打招呼,所以处于加载状态
+  isVoiceModalOpen = false; // 语音识别modal默认关闭
+  currentUser: CloudUser | undefined; // 当前用户
+  constructor(private navCtrl: NavController, private router: Router) {
+    // 初始化语音识别
+    this.initSpeechRecognition();
+    this.currentUser = new CloudUser();
+  }
+  // 初始化语音识别
+  initSpeechRecognition() {
+    const SpeechRecognition = (window as any).SpeechRecognition || (window as any).webkitSpeechRecognition;
+    if (SpeechRecognition) {
+      this.recognition = new SpeechRecognition();
+      this.recognition.lang = 'zh-CN'; // 设置语言为中文
+      this.recognition.interimResults = false; // 不返回中间结果
+      this.recognition.maxAlternatives = 1; // 最大替代结果数
+      this.recognition.continuous = true; // 设置为连续识别
+      // 处理识别结果
+      this.recognition.onresult = (event: any) => {
+        this.recognizedContent += event.results[event.results.length - 1][0].transcript; // 追加识别结果
+        console.log("识别到的内容:", this.recognizedContent); // 打印识别到的内容
+      };
+      // 处理识别错误
+      this.recognition.onerror = (event: any) => {
+        if (event.error === 'no-speech') {
+          console.warn('没有检测到语音,继续监听...');
+        } else {
+          console.error('语音识别错误:', event.error);
+        }
+      };
+    } else {
+      console.log('该浏览器不支持语音识别');
+    }
+  }
+  // 启动语音识别
+  startVoice() {
+    this.isVoiceModalOpen = true;
+    if (this.recognition && this.recognition.state !== 'active') { // 检查识别状态
+      this.recognition.start(); // 启动语音识别
+      console.log('语音识别启动...');
+      this.startTimer(); // 启动计时器
+    } else {
+      console.warn('语音识别已经在运行中'); // 提示用户语音识别已在运行
+    }
+  }
+  cancleVoice() {
+    this.isVoiceModalOpen = false;
+  }
+  // 启动计时器
+  startTimer() {
+    this.elapsedSeconds = 0; // 重置秒数
+    this.timer = '00:00'; // 重置计时器显示
+    this.interval = setInterval(() => {
+      this.elapsedSeconds++;
+      const minutes = Math.floor(this.elapsedSeconds / 60);
+      const seconds = this.elapsedSeconds % 60;
+      this.timer = `${this.padZero(minutes)}:${this.padZero(seconds)}`; // 更新计时器显示
+    }, 1000);
+  }
+
+  // 格式化数字为两位数
+  padZero(num: number): string {
+    return num < 10 ? '0' + num : num.toString();
+  }
+  // 取消语音输入
+  cancelVoiceInput() {
+    if (this.recognition) {
+      this.recognition.stop(); // 停止语音识别
+      console.log('语音识别已停止');
+      clearInterval(this.interval); // 清除计时器
+      this.timer = '00:00'; // 重置计时器显示
+      this.recognizedContent = ''; // 清空识别内容
+    }
+    this.isVoiceModalOpen = false;
+  }
+
+  // 发送语音输入
+  sendVoiceInput() {
+    if (this.recognition) {
+      this.recognition.stop(); // 停止语音识别
+      console.log('语音识别已停止');
+      clearInterval(this.interval); // 清除计时器
+      this.timer = '00:00'; // 重置计时器显示
+
+      // 将识别到的内容传到输入框中
+      this.userMessage += this.recognizedContent.trim(); // 将识别内容赋值给输入框,并去除多余空格
+
+      this.recognizedContent = ''; // 清空识别内容
+    }
+    this.isVoiceModalOpen = false;
+  }
+  async goBack() {
+    const user = new CloudUser();
+    const currentUser = await user.current(); // 获取当前用户信息
+    if (currentUser) { // 判断用户是否登录
+
+      this.saveChatHistory(); // 保存聊天历史
+    }
+    this.navCtrl.back(); // 返回上一页
+
+  }
+
+  goHistory() {
+    this.router.navigate(['chatHistory']); // 导航到历史记录页面
+  }
+
+  ngOnInit() {
+
+    this.initializeChat(); // 初始化聊天
+  }
+
+  //初始化聊天,将提示词添加到历史中
+  initializeChat() {
+    this.initialPrompt = `
+#角色设定
+您是一名时尚顾问,热爱时尚,擅长根据用户的需求和个性推荐穿搭方案。您的风格亲切、幽默,旨在帮助用户找到最适合他们的服装搭配。
+
+#对话环节
+0破冰,跟用户打招呼,并自我介绍,询问用户是否愿意分享穿搭想法
+#如果用户愿意则询问用户信息不愿意则慢慢引导用户聊聊穿搭(这些问题不要一起询问,分开询问):
+请问你的身高和体重是多少呢?
+你大概多大年龄?
+你的性别是什么?
+你目前的职业是什么?
+你平时喜欢什么样的穿搭风格?(例如:休闲、正式、运动等)
+今天的天气怎么样?温度大概是多少?
+你希望穿搭达到什么效果?(例如:显高、显瘦、优雅、甜美等)
+还有其他的需求或偏好吗?
+#根据用户描述生成穿搭建议:
+根据你提供的信息,我为你推荐以下穿搭方案:
+(根据用户的身高、体重、性别、职业、喜好等信息生成具体建议)
+例如:如果你希望显高,可以选择高腰裤搭配短款上衣,配上尖头鞋等。
+3引导收尾(如果用户没有要离开的打算可以跟他慢慢聊)
+“今天聊得很开心呢!如果你还有其他问题或者想法,随时可以告诉我哦。”
+“如果你觉得今天的聊天已经足够了,我也很乐意下次再和你聊更多时尚的话题!”
+“希望你能找到自己喜欢的穿搭风格,期待下次再见!”
+# 开始话语
+当您准备好了,可以以一个时尚顾问的身份,向来访的用户打招呼。`; // 提示词
+
+    // 构建对话历史,不将提示词添加到消息数组中
+    const conversationHistory = this.messages.map(msg => ({
+      role: msg.sender === 'user' ? 'user' : 'assistant',
+      content: msg.text
+    }));
+
+    // 将系统消息直接添加到对话历史
+    conversationHistory.unshift({ role: 'user', content: this.initialPrompt }); // 添加系统消息到历史
+
+    let completion = new FmodeChatCompletion(conversationHistory);
+
+    // 发送初始化消息
+    completion.sendCompletion().subscribe((message: any) => {
+      if (message?.complete) {
+        this.isLoading = false; // 加载完成,设置状态为 false
+        console.log("AI初始化回复:", message.content);
+        this.messages.push({ text: message.content, sender: 'ai' }); // 添加AI的初始化回复
+      }
+    });
+  }
+
+
+  sendMessage() {
+    // 发送消息的逻辑
+    if (this.userMessage.trim()) { // 确保消息不为空
+      // 构建对话历史
+      const conversationHistory = this.messages.map(msg => ({
+        role: msg.sender === 'user' ? 'user' : 'assistant',
+        content: msg.text
+      }));
+
+      // 将提示词直接添加到对话历史
+      conversationHistory.unshift({ role: 'user', content: this.initialPrompt }); // 添加系统消息到历史
+      this.isLoading = true; // 设置加载状态为 true
+      this.messages.push({ text: this.userMessage, sender: 'user' }); // 添加用户消息到数组
+      console.log("发送消息:", this.messages); // 调试输出
+
+
+
+      // 将用户消息添加到对话历史
+      conversationHistory.push({ role: 'user', content: this.userMessage });
+
+      let completion = new FmodeChatCompletion(conversationHistory);
+      this.userMessage = ''; // 清空输入框
+
+      completion.sendCompletion().subscribe((message: any) => {
+        // 打印消息体
+        console.log(message.content);
+
+        if (message?.complete) { // 判断message为完成状态,则设置ai内容
+          this.aiMessage = message.content;
+          this.isLoading = false; // 加载完成,设置状态为 false
+        }
+
+        if (this.aiMessage) { // 判断ai内容不为空
+          console.log("AI:" + this.aiMessage);
+          this.messages.push({ text: this.aiMessage, sender: 'ai' }); // 添加消息到数组
+          this.aiMessage = ''; // 清空ai内容
+          console.log("发送消息:", this.messages); // 调试输出
+          console.log("历史对话" + conversationHistory[0].content + " " + conversationHistory[1].content + " " + conversationHistory[2].content)
+        }
+      });
+    }
+  }
+
+  isEmojiPickerOpen: boolean = false; // 控制表情选择器的打开状态
+  emojis: string[] = ['😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '🫠', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘',
+    '😗', '☺️', '😚', '😙', '🥲', '😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🫢', '🫣', '🤫', '🤔', '🫡', '🤐', '🤨', '😐',
+    '😑', '😶', '🫥', '😶‍🌫️', '😏', '😒', '🙄', '😬', '😮‍💨', '🤥', '🫨', '🙂‍↔️', '🙂‍↕️', '😌', '😔', '😪', '🤤', '😴', '🫩', '😷',
+    '🤒', '🤕', '🤢', '🤮', '🤧', '🥵', '🥶', '🥴', '😵', '😵‍💫', '🤯', '🤠', '🥳', '🥸', '😎', '🤓', '🧐', '😕', '🫤', '😟',
+    '🙁', '☹️', '😮', '😯', '😲', '😳', '🥺', '🥹', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖', '😣', '😞', '😓',
+    '😩', '😫', '🥱', '😤', '😡', '😠', '🤬', '😈', '👿', '💀', '☠️', '💩', '🤡', '👹', '👺', '👻', '👽', '👾', '🤖', '😺',
+    '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾', '🙈', '🙉', '🙊', '💌', '💘', '❤️', '🖤', '💋', '💯', '💢', '💥', '💫',
+    '💦', '💤']; // 表情数组
+  // 打开表情选择器
+  openEmojiPicker() {
+    this.isEmojiPickerOpen = true;
+  }
+
+  // 关闭表情选择器
+  closeEmojiPicker() {
+    this.isEmojiPickerOpen = false; // 关闭模态框
+  }
+
+  // 添加表情到输入框
+  addEmoji(emoji: string) {
+    this.userMessage += emoji; // 将选中的表情添加到输入框
+    this.closeEmojiPicker(); // 关闭模态框
+  }
+
+  async saveChatHistory() {
+    const user = new CloudUser();
+    const currentUser = await user.current(); // 获取当前用户信息
+    if (currentUser && this.messages.length > 1) {
+      const chatHistory = new CloudObject("ChatHistory"); // 创建一个新的ChatHistory对象
+      chatHistory.set({
+        user: currentUser.toPointer(), // 指向当前用户
+        content: JSON.stringify(this.messages) // 将聊天记录转换为字符串
+      });
+      await chatHistory.save(); // 保存聊天记录
+      console.log("聊天记录已保存");
+    } else {
+      console.error("用户未登录或聊天记录为空");
+    }
+  }
+
+
+
+
+}

+ 21 - 1
E-Cover-app/src/app/app.routes.ts

@@ -17,7 +17,7 @@ export const routes: Routes = [
       import('./chat-panel/chat-panel.component').then((m) => m.ChatPanelComponent),
   },
   {
-    path: 'generateResult',
+    path: 'generateResult/:resultId',
     loadComponent: () =>
       import('./generate-result/generate-result.component').then((m) => m.GenerateResultComponent),
   },
@@ -41,4 +41,24 @@ export const routes: Routes = [
     loadComponent:()=>
       import('../lib/component/post-detail/post-detail.component').then((m)=>m.PostDetailComponent),
   },
+  {
+    path:'generateHistory',
+    loadComponent:()=>
+      import('./generate-history/generate-history.component').then((m)=>m.GenerateHistoryComponent),
+  },
+  {
+    path:'aiChat',
+    loadComponent:()=>
+      import('./ai-chat-component/ai-chat-component.component').then((m)=>m.AiChatComponentComponent),
+  },
+  {
+    path:'chatHistory',
+    loadComponent:()=>
+      import('./chat-history/chat-history.component').then((m)=>m.ChatHistoryComponent),
+  },
+  {
+    path:'chatcontent',
+    loadComponent:()=>
+      import('./chat-content/chat-content.component').then((m)=>m.ChatContentComponent),
+  },
 ];

+ 73 - 0
E-Cover-app/src/app/chat-content/chat-content.component.html

@@ -0,0 +1,73 @@
+<!--头部内容-->
+<ion-header>
+  <ion-toolbar class="custom-toolbar" >
+    <ion-buttons slot="start">
+      <ion-button (click)="goBack()">  <!--返回按钮-->
+        <ion-icon name="chevron-back-sharp" style="color: black; font-size:27px"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+    <ion-title>聊天内容</ion-title>  <!--AI名称-->
+    <ion-buttons slot="end">
+      <ion-button >   <!--更多按钮-->
+        <ion-icon name="ellipsis-horizontal" style="color: black; font-size:27px"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+
+<!--聊天区域:聊天内容保存在messages-->
+<ion-content>
+  <div class="chat-container">
+    <div *ngFor="let message of selectedChat" class="message-container">
+      <!-- 用户消息 -->
+      <div *ngIf="message.sender === 'user'" class="message-content user-message-content">
+        <div class="message-bubble user-message">
+          {{ message.text }}
+        </div>
+        <div class="user-avatar">
+          <img src="../../assets/images/touxiang.jpg" alt="用户头像" />
+        </div>
+      </div>
+    
+      <!-- AI消息 -->
+      <div *ngIf="message.sender === 'ai'" class="message-content ai-message-content">
+        <div class="ai-avatar">
+          <img src="../../assets/images/cxtouxiang.jpg" alt="AI头像" />
+        </div>
+        <div class="message-bubble ai-message">
+          {{ message.text }}
+        </div>
+      </div>
+    </div>
+  </div>
+</ion-content>
+
+<!--底部内容-->
+<ion-footer style="background-color: #99d75c;">
+  <ion-toolbar>
+    <div class="footer-content">
+
+      <ion-buttons>  <!--语音输入-->
+        <ion-button  fill="clear" >
+          <ion-icon name="mic-circle-outline" style="color: white; font-size: 40px;"></ion-icon>
+        </ion-button>
+        
+      </ion-buttons>
+<!--文本输入框-->
+      <ion-input placeholder="输入消息..." class="input-box" disabled>
+        <ion-button  fill="clear" slot="end">
+          <ion-icon name="happy-outline" style="color:#99d75c; font-size: 30px;" ></ion-icon> <!--表情符号-->
+
+        </ion-button>
+      </ion-input>
+
+      <ion-buttons>
+        <ion-button  fill="clear">  <!--发送按钮-->
+          <div class="circle">
+          <ion-icon name="paper-plane" style="color: white; font-size: 27px;"></ion-icon>
+        </div>
+        </ion-button>
+      </ion-buttons>
+    </div>
+  </ion-toolbar>
+</ion-footer>

+ 417 - 0
E-Cover-app/src/app/chat-content/chat-content.component.scss

@@ -0,0 +1,417 @@
+ion-toolbar {
+    height: 70px; /* 设置你想要的高度 */
+    --min-height: 60px; /* 设置最小高度 */
+    padding: 0; /* 去掉内边距 */
+    display: flex; /* 使用Flexbox布局 */
+    align-items: center; /* 垂直居中对齐 */
+    --background: transparent; /* 去除背景色 */
+  }
+  //头部样式
+  ion-header{
+    background-color: #99d75c ;
+    --background: transparent; /* 去除背景色 */
+  }
+  //头部标题
+  ion-title {
+    margin: 0; /* 去掉默认的外边距 */
+    flex: 1; /* 让标题占据剩余空间 */
+    text-align: center; /* 让标题文本居中 */
+    font-size: 24px;
+  }
+
+/* 文本输入框样式 */
+.input-box {
+  background-color: white; /* 设置输入框背景为白色 */
+  border-radius: 8px; /* 圆角 */
+ 
+  height: 40px; /* 高度 */
+  flex: 1; /* 让输入框占据剩余空间 */
+}
+
+/* 底部内容容器 */
+.footer-content {
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  width: 100%; /* 宽度100% */
+}
+//底部按钮样式
+ion-buttons {
+  margin: 0 5px; /* 按钮之间的间距 */
+}
+//底部发送按钮圆圈样式
+.circle{
+width: 35px;
+height: 35px;
+border-radius: 50%;
+border:2px white solid;
+display: flex; /* 使用Flexbox布局 */
+align-items: center; /* 垂直居中对齐 */
+justify-content: center; /* 水平居中对齐 */
+}
+
+//聊天内容容器样式
+.chat-container {
+  display: flex;
+  flex-direction: column;
+  padding-top: 10px;
+}
+//聊天内容样式(包括头像和消息)
+.message-container {
+  display: flex; /* 使用Flexbox布局 */
+  justify-content: flex-end; /* 用户消息在右边,AI消息在左边 */
+  margin: 10px 0; /* 消息之间的间距 */
+}
+//消息内容样式
+.message-content {
+  display: flex; /* 使用Flexbox布局 */
+  
+}
+//用户消息和头像样式
+.user-message-content {
+  justify-content: flex-end; /* 用户消息和头像在右边 */
+}
+//AI消息和头像样式
+.ai-message-content {
+  justify-content: flex-start; /* AI消息和头像在左边 */
+}
+/* 气泡样式 */
+.message-bubble {
+  background-color: #99d75c; /* 绿色气泡的背景色 */
+  color: white; /* 字体颜色 */
+  border-radius: 15px; /* 圆角 */
+  padding: 10px 15px; /* 内边距 */
+  max-width: 70%; /* 最大宽度 */
+  white-space: normal; /* 允许换行 */
+  overflow-wrap: break-word; /* 允许在单词边界换行 */
+}
+
+/* 用户消息样式 */
+.user-message {
+  background-color: #99d75c; /* 用户消息的颜色 */
+  margin-left: 10px; /* 与头像之间的间距 */
+}
+
+/* AI消息样式 */
+.ai-message {
+  background-color: white; /* AI消息的气泡颜色 */
+  color: black; /* 字体颜色 */
+  border: 1px solid black; /* 添加黑色边框 */
+  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3); /* 添加阴影效果 */
+  margin-right: 10px; /* 与头像之间的间距 */
+}
+//用户头像样式
+.user-avatar {
+  width: 40px; /* 头像宽度 */
+  height: 40px; /* 头像高度 */
+  border-radius: 50%; /* 圆形 */
+  overflow: hidden; /* 超出部分隐藏 */
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  background-color: #f0f0f0; /* 背景色 */
+  border: 2px solid #99d75c; /* 边框颜色 */
+  margin-left: 5px; /* 与消息之间的间距 */
+}
+//AI头像样式
+.ai-avatar {
+  width: 40px; /* 头像宽度 */
+  height: 40px; /* 头像高度 */
+  border-radius: 50%; /* 圆形 */
+  overflow: hidden; /* 超出部分隐藏 */
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  background-color: #f0f0f0; /* 背景色 */
+  border: 2px solid #99d75c; /* 边框颜色 */
+  margin-right: 5px; /* 与消息之间的间距 */
+}
+//用户头像图片样式
+.user-avatar img {
+  width: 100%; /* 使图片适应头像框 */
+  height: 100%; /* 使图片适应头像框 */
+  object-fit: cover; /* 保持图片比例,裁剪多余部分 */
+}
+//AI头像图片样式
+.ai-avatar img {
+  width: 100%; /* 使图片适应头像框 */
+  height: 100%; /* 使图片适应头像框 */
+  object-fit: cover; /* 保持图片比例,裁剪多余部分 */
+}
+//语音框
+.yuyinframe{
+  --background: transparent; 
+  background-color: #99d75c; 
+  display: flex; 
+  flex-direction: column; 
+  justify-content: center; 
+  align-items: center;
+}
+.modal-content {
+  display: flex;
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  width: 100%; /* 宽度100% */
+  height: 25%;
+}
+//语音框中间内容框
+.timer-container {
+  height: 100%;
+  flex-direction: column; /* 垂直排列 */
+  
+  justify-content: center; /* 水平居中对齐 */
+
+}
+//计时器样式
+#timer{
+  width: 100%;
+  margin-top: 30px; /* 可根据需要调整这个值 */
+  text-align: center; /* 确保文本居中 */
+  margin-bottom: 10px;
+}
+
+/*语音取消按钮的样式 */
+.cancle-button {
+  font-size: 75px;
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  color: white;
+  background-color: #99d75c; 
+  margin-right: 20px;
+}
+
+/*语音发送按钮的样式 */
+.send-button {
+  height:60px ;
+  width: 60px;
+  border-radius: 50%;//圆
+  display: flex; /* 使用Flexbox布局 */
+  align-items: center; /* 垂直居中对齐 */
+  justify-content: center; /* 水平居中对齐 */
+  color: white;
+  background-color: #99d75c; 
+  border: 5px solid white;
+  margin-left: 20px;
+}
+
+
+//音律跳动
+.light {
+  width: 100%;
+  height: 90px;
+  display: flex;
+}
+
+.light span {
+  width: 10px;
+  border-radius: 18px;
+  margin-right: 20px;
+}
+
+.light span:nth-child(1) {
+  animation: bar1 2s 0.2s infinite linear;
+}
+
+.light span:nth-child(2) {
+  animation: bar2 2s 0.4s infinite linear;
+}
+
+.light span:nth-child(3) {
+  animation: bar3 2s 0.6s infinite linear;
+}
+
+.light span:nth-child(4) {
+  animation: bar4 2s 0.8s infinite linear;
+}
+
+.light span:nth-child(5) {
+  animation: bar5 2s 1.0s infinite linear;
+}
+
+.light span:nth-child(6) {
+  animation: bar6 2s 1.2s infinite linear;
+}
+
+.light span:nth-child(7) {
+  animation: bar7 2s 1.4s infinite linear;
+}
+
+.light span:nth-child(8) {
+  animation: bar8 2s 1.6s infinite linear;
+}
+
+.light span:nth-child(9) {
+  animation: bar9 2s 1.8s infinite linear;
+}
+//第一条音律加载动画
+@keyframes bar1 {
+  0% {
+      background: #f677b0;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #f677b0;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #f677b0;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第二条音律加载动画
+@keyframes bar2 {
+  0% {
+      background: #df7ff2;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #df7ff2;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #df7ff2;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第三条音律加载动画
+@keyframes bar3 {
+  0% {
+      background: #8c7ff2;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #8c7ff2;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #8c7ff2;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第四条音律加载动画
+@keyframes bar4 {
+  0% {
+      background: #024b6a;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #024b6a;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #024b6a;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//第五条音律加载动画
+@keyframes bar5 {
+  0% {
+      background: #7ff2d3;
+      margin-top: 25%;
+      height: 10%;
+  }
+
+  50% {
+      background: #7ff2d3;
+      height: 100%;
+      margin-top: 0%;
+  }
+
+  100% {
+      background: #7ff2d3;
+      height: 10%;
+      margin-top: 25%;
+  }
+}
+//三个点的加载动画
+.loading-dots {
+  display: flex;
+  align-items: center;
+  margin-left: 10px; /* 气泡与点之间的间距 */
+}
+
+.loading-dot {
+  width: 8px;
+  height: 8px;
+  border-radius: 50%;
+  background-color: #99d75c; /* 点的颜色 */
+  margin: 0 2px; /* 点之间的间距 */
+  animation: loading 1s infinite; /* 加载动画 */
+}
+//第一个点
+.loading-dot:nth-child(1) {
+  animation-delay: 0s;
+}
+//第二个点
+.loading-dot:nth-child(2) {
+  animation-delay: 0.2s;
+}
+//第三个点
+.loading-dot:nth-child(3) {
+  animation-delay: 0.4s;
+}
+//加载动画
+@keyframes loading {
+  0%, 100% {
+    opacity: 0.5;
+  }
+  50% {
+    opacity: 1;
+  }
+}
+
+
+/* 表情选择器样式 */
+.emoji-picker {
+  --background: transparent; /* 去除默认样式 */
+  background-color: #99d75c; /* 背景颜色 */
+  padding: 10px;
+  display: flex;
+  justify-content: center; /* 水平居中 */
+  overflow: hidden; /* 隐藏多余内容 */
+}
+
+/* 表情容器用于支持滚动 */
+.emoji-container {
+  display: flex;
+  flex-wrap: wrap; /* 允许换行 */
+  overflow-y: auto; /* 允许纵向滚动 */
+  max-height: 70vh; /* 最大高度,防止超出屏幕 */
+  width: 100%; /* 容器宽度 */
+}
+
+/* 表情按钮 */
+.emoji-button {
+  margin: 5px; /* 每个表情与顶部的间距 */
+  font-size: 28px; /* 字体大小 */
+  height: 40px; /* 按钮高度 */
+  width: 40px; /* 按钮宽度 */
+  display: flex; /* 使用 flexbox 对齐 */
+  align-items: center; /* 垂直居中 */
+  justify-content: center; /* 水平居中 */
+  --background: transparent; /* 背景透明 */
+  --box-shadow: none; /* 去掉阴影 */
+  --outline: none; /* 去掉轮廓 */
+  border: none; /* 去掉边框 */
+}

+ 22 - 0
E-Cover-app/src/app/chat-content/chat-content.component.spec.ts

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

+ 47 - 0
E-Cover-app/src/app/chat-content/chat-content.component.ts

@@ -0,0 +1,47 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { IonButtons, IonHeader, IonToolbar,IonButton, IonIcon, IonTitle, IonInput, IonFooter, IonContent, AlertController, IonCardContent, IonList, IonItem, IonLabel, IonCard, IonCardHeader, IonCardTitle, NavController,  } from '@ionic/angular/standalone';
+@Component({
+  selector: 'app-chat-content',
+  templateUrl: './chat-content.component.html',
+  styleUrls: ['./chat-content.component.scss'],
+  standalone: true,
+  imports: [IonHeader,IonToolbar,IonTitle,IonContent,IonList,IonItem,IonLabel,CommonModule,IonCard,IonCardHeader,IonCardTitle,
+      IonButton,IonCardContent,IonIcon,IonButtons,IonInput,IonFooter,IonInput,IonContent,],
+  
+})
+export class ChatContentComponent  implements OnInit {
+
+  chat: any;
+  selectedChat: any; // 存储当前选择的聊天记录,json字符串
+
+  constructor(private router: Router,private navCtrl: NavController) {
+    const navigation = this.router.getCurrentNavigation();
+    if (navigation && navigation.extras.state) {
+       // 使用类型断言
+       this.chat = (navigation.extras.state as { chat: any }).chat;
+       console.log(this.chat);
+    }
+  }
+
+  ngOnInit() {
+    this.handle()
+  }
+
+
+  handle(){
+       this.selectedChat = this.chat.data.content; // 更新当前选择的聊天记录
+    console.log("选择聊天记录", this.selectedChat);
+    // 解析 JSON 字符串为对象数组
+ this.selectedChat = JSON.parse(this.selectedChat);
+console.log("解析后的聊天记录", this.selectedChat);
+  }
+
+  goBack() {
+
+    this.navCtrl.back(); // 返回上一页
+  
+    }
+
+}

+ 38 - 0
E-Cover-app/src/app/chat-history/chat-history.component.html

@@ -0,0 +1,38 @@
+<ion-header >
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <ion-button (click)="goBack()">  <!--返回按钮-->
+        <ion-icon name="chevron-back-sharp" style="color: black; font-size:27px"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+    <ion-title>聊天记录</ion-title>
+    <ion-buttons slot="end">
+      <ion-button >   <!--更多按钮-->
+        <ion-icon name="ellipsis-horizontal" style="color: black; font-size:27px"></ion-icon>
+      </ion-button>
+    </ion-buttons>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <!-- 搜索框 -->
+  <ion-searchbar animated="true" fill="clear" [(ngModel)]="searchDate" (ionInput)="filterChatHistories()" placeholder="输入日期 (YYYY-MM-DD)" class="search"></ion-searchbar>
+  <ion-list>
+    <ion-item *ngFor="let chat of filteredChatHistories; let i = index" fill="clear">
+      <div class="chat-item" (click)="selectChatHistory(chat)" [ngClass]="{'first-chat': i === 0}">
+        <div class="chat-header">
+          <div class="user-info">
+            <img [src]="currentUser.get('avatar')" alt="User Avatar" class="avatar-image">
+            <span class="username">{{ currentUser.get('name') }}</span>
+          </div>
+          <span class="timestamp">{{ chat.createdAt | date:'yyyy-MM-dd HH:mm' }}</span>
+        </div>
+        <div class="chat-content">
+          <p class="chat-text">{{ chat.firstMessage }}</p>
+        </div>
+      </div>
+    </ion-item>
+  </ion-list>
+</ion-content>
+
+

+ 69 - 0
E-Cover-app/src/app/chat-history/chat-history.component.scss

@@ -0,0 +1,69 @@
+.first-chat {
+    display: flex;
+    flex-direction: column;
+    padding: 10px;
+    border: 1px solid #e0e0e0; /* 边框样式 */
+    border-radius: 8px; /* 圆角 */
+    background-color: #fff; /* 背景色 */
+    width: 100%; /* 宽度 */
+    margin-top: 25px;
+  }
+
+.chat-item {
+    display: flex;
+    flex-direction: column;
+    padding: 10px;
+    border: 1px solid #e0e0e0; /* 边框样式 */
+    border-radius: 8px; /* 圆角 */
+    margin-bottom: 10px;
+    background-color: #fff; /* 背景色 */
+    width: 100%; /* 宽度 */
+    
+  }
+  
+  .chat-header {
+    display: flex;
+    align-items: center; /* 垂直居中 */
+    justify-content: center; /* 水平居中 */
+  }
+  
+  .user-info {
+    display: flex; /* 使用户信息水平排列 */
+    align-items: center; /* 垂直居中 */
+    flex: 1; /* 占据剩余空间 */
+  }
+  
+
+  
+  .avatar-image {
+    width: 45px; /* 头像宽度 */
+    height: 45px; /* 头像高度 */
+    border-radius: 50%; /* 圆形头像 */
+    margin-right: 12px; /* 头像和用户名之间的间距 */
+  }
+  
+  
+  .username {
+    font-weight: bold; /* 用户名加粗 */
+    font-size: 27px;
+  }
+  
+  .timestamp {
+    font-size: 20px; /* 时间字体大小 */
+    color: #888; /* 时间颜色 */
+  }
+  
+  .chat-content {
+    margin-top: 10px; /* 内容与标题之间的间距 */
+  }
+  
+  .chat-text {
+    font-size: 16px; /* 聊天内容字体大小 */
+    color: #333; /* 聊天内容颜色 */
+    overflow: hidden; /* 超出部分隐藏 */
+    text-overflow: ellipsis; /* 省略号 */
+    white-space: nowrap; /* 单行显示 */
+  }
+
+
+

+ 22 - 0
E-Cover-app/src/app/chat-history/chat-history.component.spec.ts

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

+ 103 - 0
E-Cover-app/src/app/chat-history/chat-history.component.ts

@@ -0,0 +1,103 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import { Router } from '@angular/router';
+import { IonSearchbar } from '@ionic/angular/standalone';
+import { IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardTitle, IonContent, IonFooter, IonHeader, IonIcon, IonInput, IonItem, IonLabel, IonList, IonTitle, IonToolbar, NavController } from '@ionic/angular/standalone';
+import { addIcons } from 'ionicons';
+import { chevronBackSharp, closeCircleOutline, ellipsisHorizontal, happyOutline, micCircleOutline, paperPlane, sendOutline } from 'ionicons/icons';
+import { CloudQuery, CloudUser } from 'src/lib/ncloud';
+
+addIcons({ chevronBackSharp, ellipsisHorizontal, micCircleOutline, happyOutline, paperPlane, closeCircleOutline, sendOutline });
+
+@Component({
+  selector: 'app-chat-history',
+  templateUrl: './chat-history.component.html',
+  styleUrls: ['./chat-history.component.scss'],
+  standalone: true,
+  imports: [IonHeader, IonToolbar, IonTitle, IonContent, IonList, IonItem, IonLabel, CommonModule, IonCard, IonCardHeader, IonCardTitle,
+    IonButton, IonCardContent, IonIcon, IonButtons, IonInput, IonFooter, IonInput, CommonModule, IonSearchbar, FormsModule],
+})
+export class ChatHistoryComponent implements OnInit {
+
+  chatHistories: any[] = []; // 存储聊天记录
+  currentUser: any;
+  constructor(private router: Router, private navCtrl: NavController) { }
+  async ngOnInit() {
+    await this.loadChatHistories(); // 加载聊天记录
+    this.filteredChatHistories = this.chatHistories; // 初始化过滤后的聊天记录为所有聊天记录
+  }
+
+  async loadChatHistories() {
+    const user = new CloudUser();
+    const currentUser = await user.current(); // 获取当前用户信息
+
+    if (currentUser) {
+      this.currentUser = currentUser;
+      const query = new CloudQuery("ChatHistory");
+      query.equalTo("user", currentUser.toPointer()); // 查询当前用户的聊天记录
+      this.chatHistories = await query.find(); // 获取聊天记录
+      // 解析聊天内容并提取每条聊天记录的第一条消息
+      this.chatHistories.forEach(chat => {
+        if (chat.data.content) {
+          chat.parsedContent = JSON.parse(chat.data.content); // 解析内容
+
+          chat.firstMessage = chat.parsedContent[0] ? chat.parsedContent[0].text : ''; // 获取第一条消息
+          //console.log("hhh", this.chatHistories);
+        }
+      });
+      // 按 createdAt 排序,最近的在前面
+      this.chatHistories.sort((a, b) => {
+        return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
+      });
+      //console.log("聊天记录已加载", this.chatHistories);
+    } else {
+      console.error("用户未登录,无法加载聊天记录");
+    }
+    console.log("聊天记录已加载", this.chatHistories);
+  }
+
+
+  // 选择聊天记录
+  selectChatHistory(chat: any) {
+    this.router.navigate(['chatcontent'], { state: { chat: chat } });
+  }
+
+  goBack() {
+
+    this.navCtrl.back(); // 返回上一页
+
+  }
+
+
+  searchDate: string = ''; // 搜索日期
+  filteredChatHistories: any[] = []; // 存储过滤后的聊天记录
+
+  // 过滤聊天记录
+  filterChatHistories() {
+    // 检查用户输入的日期是否有效
+    if (!this.searchDate || this.searchDate.length < 10) {
+      // 如果没有输入完整的日期,显示所有聊天记录
+      this.filteredChatHistories = this.chatHistories;
+      return;
+    }
+    // 解析用户输入的日期
+    const parsedDate = new Date(this.searchDate);
+
+    // 获取输入日期的开始和结束时间戳
+    const startOfDay = new Date(parsedDate.setHours(0, 0, 0, 0)).getTime(); // 2024-12-24 00:00:00
+    const endOfDay = new Date(parsedDate.setHours(23, 59, 59, 999)).getTime(); // 2024-12-24 23:59:59
+
+    console.log("开始时间戳:", startOfDay);
+    console.log("结束时间戳:", endOfDay);
+
+    // 过滤聊天记录,只保留在指定日期范围内的记录
+    this.filteredChatHistories = this.chatHistories.filter(chat => {
+      const chatTimestamp = new Date(chat.createdAt).getTime(); // 聊天记录的时间戳
+      console.log("聊天记录时间戳:", chatTimestamp);
+
+      return chatTimestamp >= startOfDay && chatTimestamp <= endOfDay; // 只保留在范围内的聊天记录
+    });
+  }
+
+}

+ 11 - 0
E-Cover-app/src/app/generate-history/generate-history.component.html

@@ -0,0 +1,11 @@
+<!--头部-->
+<app-custom-header [param]="['生成历史','    ']"></app-custom-header>
+<!--正文-->
+<ion-content>
+  <ion-card *ngFor="let his of history" (click)="goGenerateResult(his)">
+    <ion-avatar>
+      <img [src]="his.get('image')" />
+    </ion-avatar>
+    <ion-label>{{timeFormat(his?.createdAt)}}</ion-label>
+  </ion-card>
+</ion-content>

+ 25 - 0
E-Cover-app/src/app/generate-history/generate-history.component.scss

@@ -0,0 +1,25 @@
+ion-content{
+    --background: #f8f8f8;
+
+    ion-card{
+        width: 95%;
+        margin: 2vw;
+        height: 100px;
+        border-radius: 20px;
+
+        ion-avatar{
+            height: 70px;
+            width: 70px;
+            margin: 15px;
+            float: left;
+        }
+
+        ion-label{
+            height: 100%;
+            font-size: 18px;
+            float: left;
+            display: flex;
+            align-items: center;
+        }
+    }
+}

+ 22 - 0
E-Cover-app/src/app/generate-history/generate-history.component.spec.ts

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

+ 48 - 0
E-Cover-app/src/app/generate-history/generate-history.component.ts

@@ -0,0 +1,48 @@
+import { CommonModule } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { IonAvatar, IonCard, IonContent, IonImg, IonLabel } from '@ionic/angular/standalone';
+import { constructOutline } from 'ionicons/icons';
+import { CustomHeaderComponent } from 'src/lib/component/custom-header/custom-header.component';
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
+
+@Component({
+  selector: 'app-generate-history',
+  templateUrl: './generate-history.component.html',
+  styleUrls: ['./generate-history.component.scss'],
+  standalone: true,
+  imports: [CustomHeaderComponent, IonContent, IonCard, IonAvatar, IonLabel, CommonModule]
+})
+export class GenerateHistoryComponent implements OnInit {
+  currentUser: CloudUser | any;
+  history: CloudObject[] = [];
+  constructor(private router: Router) {
+    this.currentUser = new CloudUser();
+  }
+  ngOnInit() {
+    this.getAllHistory();
+  }
+  async getAllHistory() {
+    let query = new CloudQuery("GenerateResult");
+    query.equalTo("UserID", this.currentUser.toPointer());
+    let result = await query.find();
+    this.history = result;
+    console.log(this.history);
+    this.history = this.history.reverse();
+  }
+  timeFormat(time: string) {
+    //参考输入格式"2024-12-27T18:45:01.237Z"转化为中国区时间格式
+    //输出格式"XXXX-XX-XX XX:XX"
+    let date = new Date(time);
+    //确保时间输出都是两位
+    let year = date.getFullYear().toString().padStart(4, "0");
+    let month = (date.getMonth() + 1).toString().padStart(2, "0");
+    let day = date.getDate().toString().padStart(2, "0");
+    let hour = date.getHours().toString().padStart(2, "0");
+    let minute = date.getMinutes().toString().padStart(2, "0");
+    return `${year}-${month}-${day} ${hour}:${minute}`;
+  }
+  goGenerateResult(obj: CloudObject | any) {
+    this.router.navigate(['generateResult', obj.id]);
+  }
+}

+ 0 - 7
E-Cover-app/src/app/generate-option/generate-option.component.html

@@ -115,13 +115,6 @@
   <ion-button (click)="sendMsgAndGoGenerateResult()">生成</ion-button>
   <ion-button>导入默认风格</ion-button>
   <ion-button (click)="goChatPanel()">测试页面</ion-button>
-
-  <!-- 图片生成结果 -->
-  @if(shareData.images) {
-  @for(imageUrl of shareData.images;track imageUrl){
-  <img [src]="imageUrl" alt="" srcset="">
-  }
-  }
 </ion-content>
 
 <!--处理动画,默认隐藏-->

+ 20 - 5
E-Cover-app/src/app/generate-option/generate-option.component.ts

@@ -10,7 +10,7 @@ import { TaskGenerateUserValiate } from 'src/agent/tasks/generate/generate-user-
 import { TaskExecutor } from 'src/agent/agent.start';
 import { TaskGeneratePrompt } from 'src/agent/tasks/generate/generate-prompt';
 import { TaskGeneratePicture } from 'src/agent/tasks/generate/generate-picture';
-import { CloudUser } from 'src/lib/ncloud';
+import { CloudQuery, CloudUser } from 'src/lib/ncloud';
 import { CustomHeaderComponent } from 'src/lib/component/custom-header/custom-header.component';
 addIcons({ 'arrow-back-outline': arrowBackOutline, radioButtonOffOutline, closeCircleOutline, checkmarkCircleOutline, reloadOutline });
 @Component({
@@ -240,7 +240,7 @@ export class GenerateOptionComponent implements OnInit {
    * 3.生成服装提示词
    * 4.生成图片
    */
-  doGenerateTask() {
+  async doGenerateTask() {
     console.log('生成任务开始执行');
     this.shareData.userProfile = this.userProfile;
     //设置任务
@@ -250,14 +250,29 @@ export class GenerateOptionComponent implements OnInit {
     //定义任务集
     let GenerateTaskList = [task1, task2, task3]
     this.taskList = GenerateTaskList;
-    TaskExecutor(GenerateTaskList);
+    await TaskExecutor(GenerateTaskList);
+    this.goGenerateResult();
   }
   /**
-   * @跳转到聊天面板
+   * @跳转函数
    * 调用路由跳转到chatPanel页面,并传递用户需求信息
    */
   goChatPanel() {
-    this.router.navigate(['/chatPanel']);
+    // this.router.navigate(['/chatPanel']);
+    this.goGenerateResult();
+  }
+  async goGenerateResult() {
+    let query = new CloudQuery("GenerateResult");
+    let user = new CloudUser();
+    query.equalTo("UserID", user.id);
+    let result:any = await query.find();
+    //按照ruslt中的careatedAt字段(string)进行排序
+    result.sort((a: { createdAt: string | number | Date; }, b: { createdAt: string | number | Date; }) => {
+      return new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime();
+    });
+    result = result[0];
+    console.log(result);
+    this.router.navigate(['generateResult',result?.id]);
   }
   /**
    * @风格描述下的帮助文本点击事件

+ 11 - 26
E-Cover-app/src/app/generate-result/generate-result.component.html

@@ -1,30 +1,15 @@
-<div id="banner">
-    <ion-header>
-        <div class="header-container">
-            <ion-icon name="arrow-back-outline" size="large" class="back-icon" (click)="goBack()"></ion-icon>
-            <p class="header-title">生成结果{{imagineWork?.progress || 0}}</p>
-        </div>
-    </ion-header>
-</div>
+<!--头部-->
+<app-custom-header [param]="['生成结果','    ']"></app-custom-header>
 <ion-content>
-    @if(images.length) {
-    @for(imageUrl of images;track imageUrl){
-    <img [src]="imageUrl" alt="" srcset="">
-    }
-    }
-    <!-- 生成状态 -->
-    @if(!images.length){
-    @if(imagineWork){
-    <h1>生成中</h1>
-    }
-    @if(!imagineWork){
-    <h1>未开始</h1>
-    }
-    }
     <ion-card>
-        <ion-card-header>
-            <ion-card-title>生成思路</ion-card-title>
-        </ion-card-header>
-        <ion-card-content>{{responseMsg}}</ion-card-content>
+        <img class="scan" [src]="history?.get('image')">
+        <img class="score" src="https://s1.imagehub.cc/images/2024/12/29/653a0b8df33d02e85b7b813bc8b2c972.png"/>
+    </ion-card>
+    <ion-card id="show">
+        <ion-label>{{jsonData['方案名']}}</ion-label>
+        <ion-card *ngFor="let key of keys">
+            <ion-label>{{jsonData['schemeList'][key]['name']}}</ion-label>
+            <ion-label>{{jsonData['schemeList'][key]['desc']}}</ion-label>
+        </ion-card>
     </ion-card>
 </ion-content>

+ 45 - 43
E-Cover-app/src/app/generate-result/generate-result.component.scss

@@ -1,47 +1,49 @@
-#banner {
-    height: 300px;
-    background-image: url("style-img/banner-color1.png");
-    background-position: 100% 100%;
-    background-size: cover;
-    background-repeat: no-repeat;
-    background-color: none;
-    padding: 10px;
-    padding-top: 20px;
-    margin-bottom: -35px;
-    color: black;
-}
-
-ion-header {
-    background-color: none;
-    padding: 10px;
-    color: black;
-    height: 50px;
-    box-shadow: none;
-    .header-container {
-        display: flex;
-        align-items: center;
-        justify-content: center;
-        position: relative;
-        height: 100%; /* 确保容器占满整个 header 高度 */
-      }
-      
-      .back-icon {
-        position: absolute;
-        left: 10px; /* 图标距左侧的距离 */
-        cursor: pointer;
-      }
-      
-      .header-title {
-        font-size: 18px; /* 根据需要调整字体大小 */
-        font-weight: bold; /* 可选,加粗字体 */
-        margin: 0;
-      }
-}
-
 ion-content {
-    --background:#f8f8f8;
-    --color:black;
-    background-color: #f8f8f8;
+    --background: #f8f8f8;
+    --color: black;
     z-index: -1;
     height: 100vh;
+
+    ion-card {
+        --background: #fef8ef;
+        height: 30%;
+        margin: 0;
+        width: 100%;
+        border-bottom-right-radius: 20px;
+        border-bottom-left-radius: 20px;
+
+        img.scan{
+            width:60%;
+            float: left;
+        }
+        img.score{
+            width: 35%;
+            float: left;
+        }
+    }
+
+    #show {
+        background: white;
+        border-radius: 20px;
+        margin-top: 20px;
+        box-shadow: none;
+        height: auto;
+
+        ion-label {
+            font-size: 20px;
+            text-align: center;
+            display: block;
+            color: black;
+        }
+
+        ion-card{
+            --background: #fef8ef;
+            margin: 0;
+            border-radius: 20px;
+            box-shadow: none;
+            width: 95%;
+            height: auto;
+            margin: 2%;
+        }
+    }
 }

+ 33 - 54
E-Cover-app/src/app/generate-result/generate-result.component.ts

@@ -1,73 +1,52 @@
-import { Component, OnInit } from '@angular/core';
-import { ActivatedRoute, Router } from '@angular/router';
-import { IonicModule } from '@ionic/angular';
-import { NavController } from '@ionic/angular/standalone';
+import { CommonModule } from '@angular/common';
+import { Component, Input, OnInit } from '@angular/core';
+import { ActivatedRoute, ParamMap } from '@angular/router';
+import { IonCard, IonContent, IonLabel, NavController } from '@ionic/angular/standalone';
 import { addIcons } from 'ionicons';
 import { arrowBackOutline } from 'ionicons/icons';
+import { result } from 'lodash';
+import { map, switchMap } from 'rxjs';
+import { CustomHeaderComponent } from 'src/lib/component/custom-header/custom-header.component';
+import { CloudObject, CloudQuery } from 'src/lib/ncloud';
 addIcons({ arrowBackOutline });
-/** 引用:从fmode-ng库引用FmodeChatCompletion类 */
-import { ChatImageContentItem, FmodeChatCompletion } from 'fmode-ng';
-import { DalleOptions, ImagineWork } from 'fmode-ng';
 @Component({
   selector: 'app-generate-result',
   templateUrl: './generate-result.component.html',
   styleUrls: ['./generate-result.component.scss'],
   standalone: true,
-  imports: [IonicModule],
+  imports: [IonContent, CustomHeaderComponent, IonCard, IonLabel, CommonModule],
 })
 
 export class GenerateResultComponent implements OnInit {
-  constructor(private route: ActivatedRoute, private router: Router, private navCtrl: NavController) {
-    // 示例任务,自己生成图片后请存储新的ID
-    this.imagineWork = new ImagineWork("lpJGiFwWeA");
-    this.imagineWork.fetchTask().then(work => {
-      this.images = this.imagineWork?.images || [];
-    });
-  }
-  userPrompt: string = "";
-  responseMsg: any = "";
-  imagineWork: ImagineWork | undefined;
-  images: Array<string> = [];
-
+  @Input() objectId: string | null = '';
+  history: CloudObject | undefined;
+  jsonData: Record<string, any> = {};
+  keys: Array<string> = [];
+  constructor(private navCtrl: NavController, private route: ActivatedRoute) { }
   ngOnInit() {
-    this.route.queryParams.subscribe(params => {
-      this.userPrompt = params['userPrompt'] || "";
+    this.route.paramMap.pipe(
+      switchMap((params: ParamMap) => this.route.queryParamMap.pipe(
+        map(() => params.get('resultId'))
+      ))
+    ).subscribe(resultId => {
+      this.objectId = resultId;
+      // 现在您可以使用 this.objectId
     });
-    console.log(this.userPrompt)
-    this.createImage()
+    console.log("正在加载历史记录:" + this.objectId)
+    this.getGenerateResult();
   }
   goBack() {
     this.navCtrl.back();
   }
-
-  async createImage() {
-    this.imagineWork = new ImagineWork();
-    // 文本生成
-    let PromptTemplate = `${this.userPrompt}`
-    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为完成
-        // 图片生成
-        let PicturePrompt = `${this.responseMsg}`
-        let options: DalleOptions = { prompt: PicturePrompt }
-        this.imagineWork?.draw(options).subscribe(work => {
-          console.log("imagineWork", work?.toJSON())
-          console.log("images", work?.get("images"))
-          if (work?.get("images")?.length) {
-            this.images = work?.get("images");
-          }
-        })
-      }
-    })
-
-
+  async getGenerateResult() {
+    let query = new CloudQuery('GenerateResult');
+    query.equalTo('objectId', this.objectId);
+    let result = await query.find();
+    this.history = result[0];
+    console.log(this.history);
+    this.jsonData = JSON.parse(this.history?.get('content'));
+    console.log(this.jsonData);
+    this.keys = Object.keys(this.jsonData['schemeList']);
+    console.log(this.keys);
   }
-
 }

BIN
E-Cover-app/src/app/generate-result/style-img/banner-color1.png


BIN
E-Cover-app/src/app/generate-result/style-img/test.png


+ 1 - 1
E-Cover-app/src/app/send-post/send-post.component.html

@@ -1,5 +1,5 @@
 <!--头部-->
-<app-custom-header [param]="['发布帖子','text','    ']"></app-custom-header>
+<app-custom-header [param]="['发布帖子','    ']"></app-custom-header>
 <ion-button shape="round" class="publish-button" (click)="publishPost()"
   [ngClass]="{'active': inputText.length > 0 && remainingChars<20}"
   [disabled]="inputText.length === 0 || remainingChars < 0">

+ 0 - 1
E-Cover-app/src/app/send-post/send-post.component.scss

@@ -29,7 +29,6 @@
 .input-area {
     flex: 1;
     /* 让输入区域占用剩余空间 */
-
     padding: 10px;
     /* 设置内边距 */
     font-size: 18px;

+ 1 - 1
E-Cover-app/src/app/tab1/tab1.page.html

@@ -32,7 +32,7 @@
                 <ion-img src="https://s1.imagehub.cc/images/2024/12/23/6c4cba617f748bd770195fa1d02ac473.png" />
                 <p>趋势分析</p>
             </div>
-            <div class="main-button">
+            <div class="main-button" (click)="goAiChat()">
                 <ion-img src="https://s1.imagehub.cc/images/2024/12/23/4b30d81d915b24113540c7585809c689.png" />
                 <p>搭配贴士</p>
             </div>

+ 3 - 0
E-Cover-app/src/app/tab1/tab1.page.ts

@@ -58,6 +58,9 @@ export class Tab1Page {
   goGenerateOption() {
     this.router.navigate(['generateOption']);
   }
+  goAiChat(){
+    this.router.navigate(['aiChat']);
+  }
   /**
    * @读取热门item
    */

+ 1 - 1
E-Cover-app/src/app/tab3/tab3.page.html

@@ -60,7 +60,7 @@
       <p>收藏帖子</p>
       <p>11</p>
     </div>
-    <div class="card">
+    <div class="card" (click)="goGenerateHistory()">
       <p>生成历史</p>
       <p>11</p>
     </div>

+ 2 - 1
E-Cover-app/src/app/tab3/tab3.page.scss

@@ -3,7 +3,7 @@
 }
 
 ion-content {
-    --background: black;
+    --background: #f8f8f8;
     scrollbar-width: none;
 
     ion-card {
@@ -12,6 +12,7 @@ ion-content {
         border-radius: 20px;
         --background: none;
         color: white;
+        top: -20px;
 
         p {
             margin: 0;

+ 6 - 2
E-Cover-app/src/app/tab3/tab3.page.ts

@@ -57,8 +57,11 @@ export class Tab3Page {
   goSetting() {
     this.router.navigate(["/settings"]);
   }
-  goPostDetail(post:CloudObject){
-    this.router.navigate(['postDetail',post.id]);
+  goPostDetail(post: CloudObject) {
+    this.router.navigate(['postDetail', post.id]);
+  }
+  goGenerateHistory() {
+    this.router.navigate(["generateHistory"]);
   }
   /**
    * @读取用户发布的帖子
@@ -70,5 +73,6 @@ export class Tab3Page {
     this.posts = await query.find();
     console.log("获取到用户发布的帖子:");
     console.log(this.posts);
+    this.posts = this.posts.reverse();
   }
 }

BIN
E-Cover-app/src/app/tabs/style-img/footer-bg.png


+ 1 - 1
E-Cover-app/src/app/tabs/tabs.page.html

@@ -2,7 +2,7 @@
 <ion-tabs>
   <ion-tab-bar slot="bottom">
     <ion-tab-button tab="tab1" href="/tabs/tab1">
-      <img src="https://s3.bmp.ovh/imgs/2024/11/30/446e2578ecf6ce17.png" style="height: 50px; width: 50px; ">
+      <ion-icon aria-hidden="true" name="home"></ion-icon>
       <ion-label>首页</ion-label>
     </ion-tab-button>
 

+ 3 - 2
E-Cover-app/src/app/tabs/tabs.page.scss

@@ -1,10 +1,11 @@
 ion-tab-bar {
-    background-image: url(style-img/footer-bg.png);
+    background-image: white;
     background-color: #f8f8f8;
     background-size: cover;
     background-repeat: no-repeat;
     border: 0;
-    height: 13%;
+    height: 8%;
+    box-shadow:black;
   
     ion-tab-button {
       background-color: hsla(0, 0, 0, 0);

+ 2 - 2
E-Cover-app/src/app/tabs/tabs.page.ts

@@ -1,7 +1,7 @@
 import { Component, EnvironmentInjector, inject } from '@angular/core';
 import { IonTabs, IonTabBar, IonTabButton, IonIcon, IonLabel, ModalController } from '@ionic/angular/standalone';
 import { addIcons } from 'ionicons';
-import { triangle, ellipse, square } from 'ionicons/icons';
+import { triangle, ellipse, square,home } from 'ionicons/icons';
 
 @Component({
   selector: 'app-tabs',
@@ -14,6 +14,6 @@ export class TabsPage {
   public environmentInjector = inject(EnvironmentInjector);
 
   constructor(private modalCtrl: ModalController) {
-    addIcons({ triangle, ellipse, square });
+    addIcons({ triangle, ellipse, square, home });
   }
 }

+ 1 - 1
E-Cover-app/src/lib/component/custom-header/custom-header.component.html

@@ -1,5 +1,5 @@
 <!--头部内容,包含标题和返回按钮-->
-<ion-header>
+<ion-header id="header">
   <div class="header-container">
     <ion-icon name="chevron-back-outline" size="large" (click)="goBack()"></ion-icon>
     <p class="header-title">{{ param[0] }}</p>

+ 2 - 2
E-Cover-app/src/lib/component/custom-header/custom-header.component.ts

@@ -2,7 +2,7 @@ import { Component, OnInit, Input } from '@angular/core';
 import { IonButton, IonHeader, IonIcon, NavController } from '@ionic/angular/standalone';
 import { addIcons } from 'ionicons';
 import { chevronBackOutline, ellipsisHorizontal } from 'ionicons/icons';
-addIcons({ chevronBackOutline,ellipsisHorizontal });
+addIcons({ chevronBackOutline, ellipsisHorizontal });
 @Component({
   selector: 'app-custom-header',
   templateUrl: './custom-header.component.html',
@@ -13,7 +13,7 @@ addIcons({ chevronBackOutline,ellipsisHorizontal });
 export class CustomHeaderComponent implements OnInit {
   @Input() param: Array<any> | any;
   constructor(private navCtrl: NavController) { }
-  ngOnInit() { }
+  ngOnInit() {}
   goBack() {
     window.history.back();
   }

+ 76 - 29
E-Cover-app/src/lib/component/post-detail/post-detail.component.html

@@ -1,7 +1,7 @@
 <!--头部-->
 <app-custom-header [param]="['帖子详情','    ']"></app-custom-header>
 <!--正文内容-->
-<ion-content>
+<ion-content (click)="notcomment()">
     <!--帖子-->
     <div class="post">
         <div class="post-header">
@@ -36,34 +36,81 @@
     </div>
 
     <!-- 评论区域 -->
-    <div class="comment-section">
-        <h3 class="comment-title">全部评论({{comments_num}})</h3>
-
-        <div class="comment" *ngFor="let comment of comments">
-            <div class="comment-box">
-                <div class="comment-left">
-                    <div class="comment-avatar">
-                        <img src="{{comment?.get('UserID')?.avatar}}"
-                            alt="User Avatar" class="comment-avatar-image">
-                    </div>
-                    <span class="floor">1L</span>
-                </div>
-
-                <div class="comment-middle">
-
-                    <div class="comment-content">
-                        <span class="comment-username">{{comment?.get('UserID')?.name}}</span>
-                        <p class="comment-text">{{comment?.data?.content}}</p>
-                        <span class="time-ago">5小时前</span>
-                    </div>
-                </div>
-
-                <div class="comment-right">
-                    <ion-icon name="heart-outline" class="like-icon"></ion-icon>
-                    <span class="like-count">10</span>
-                </div>
-            </div>
+ <div class="comment-section">
+    <h3 class="comment-title">全部评论({{comments.length}})</h3>
+    
+    <div *ngFor="let comment of comments" class="comment">
+      <div class="comment-box">
+        <div class="comment-left">
+          <div class="comment-avatar">
+            <img src="{{ comment.get('user')?.avatar }}" alt="User Avatar" class="comment-avatar-image">
+          </div>
+          <span class="floor">{{comments.indexOf(comment) + 1}}L</span>
+        </div>
+        
+        <div class="comment-middle">
+  
+          <div class="comment-content">
+            <span class="comment-username">{{ comment.get('user')?.username }}</span>
+            <p class="comment-text">{{ comment.get('content') }}</p>
+            <span class="time-ago">{{ formatTimeAgo(comment.createdAt) }}</span>
+          </div>
         </div>
+        
+        <div class="comment-right">
+          <ion-icon name="heart-outline" class="like-icon" ></ion-icon>
+          <span class="like-count">{{ comment.get('heart') }}</span>
+        </div>
+      </div>
+    </div>
+  
+  </div>
+  
+  </ion-content>
 
+<!-- 表情模拟框 -->
+<ion-modal [isOpen]="isEmojiPickerOpen" (didDismiss)="closeEmojiPicker()" [initialBreakpoint]="0.71" [breakpoints]="[0, 0.25,0.50,0.71]" handleBehavior="cycle">
+    <ng-template>
+      <ion-content class="emoji-picker">
+        <div class="emoji-container">
+          <div *ngFor="let emoji of emojis" (click)="addEmoji(emoji)" class="emoji-buttons"> <!-- 表情按钮 -->
+            {{ emoji }}
+          </div>
+        </div>
+      </ion-content>
+    </ng-template>
+  </ion-modal>
+  
+  
+   <!-- 评论输入框 -->
+  <ion-footer *ngIf="isfooter">
+    <div class="comment-input-container">
+      <ion-icon name="share-social-outline" class="share-icon"></ion-icon>
+      <ion-textarea [(ngModel)]="newComment" placeholder="写个评论互动下~" class="comment-input" (click)="tocomment()"></ion-textarea>
+      <div class="comment-buttons">
+        <ion-button (click)="tocomment()" class="comment-button" >
+          <ion-icon name="chatbubble-ellipses-outline" style="font-size: 30px; margin-right:8px"></ion-icon> 
+          <span>{{comments.length}}</span>
+        </ion-button>
+            
+        <ion-icon name="heart-outline" class="like-button" style=" margin-right:8px"></ion-icon>
+        <span>0</span>
+      </div>
+    </div>
+  </ion-footer>
+  
+  <ion-footer *ngIf="!isfooter">
+    <div class="comment-input-container">
+      <ion-textarea [(ngModel)]="newComment" placeholder="写个评论互动下~" class="comment-input1" >
+        <div fill="clear" (click)="openEmojiPicker()"  class="emoji-button">
+          <ion-icon name="happy-outline" style="color:#ccc; font-size: 40px;" class="emoji" slot="end"></ion-icon> <!--表情符号-->
+        </div>
+      </ion-textarea>
+      <ion-button [ngClass]="newComment.trim()? 'send-button1':'send-button'" (click)="submitComment()">
+       发送
+      </ion-button>
     </div>
-</ion-content>
+  
+  
+  </ion-footer>
+  

+ 124 - 1
E-Cover-app/src/lib/component/post-detail/post-detail.component.scss

@@ -228,4 +228,127 @@
     font-size: 14px; /* 点赞数字体大小 */
     color: black; /* 点赞数颜色 */
     margin-top: 3px; /* 点赞数与评论之间的间距 */
-  }
+  }
+
+  .comment-input-container {
+    display: flex; /* 使用 Flexbox 布局 */
+    align-items: center; /* 垂直居中 */
+    
+    background-color: #f9f9f9; /* 背景色 */
+    border-top: 1px solid #ccc; /* 顶部边框 */
+  }
+  
+  .share-icon {
+    margin-left: 5px;
+    font-size: 30px; /* 分享按钮大小 */
+    color: black; /* 分享按钮颜色 */
+    margin-right: 20px; /* 与输入框的间距 */
+  }
+  
+  .comment-input {
+    display: flex;
+    align-items: center; /* 垂直居中 */
+    min-height: 50px; /* 最小高度 */
+    margin-right: 5px; /* 与按钮的间距 */
+    border-radius: 20px; /* 椭圆形效果 */
+    padding: 5px; /* 内边距 */
+    border: 1px solid #ccc; /* 边框 */
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+    height:50px;
+    width: 50%;
+    
+  }
+
+  .comment-input1 {
+    display: flex;
+    align-items: center; /* 垂直居中 */
+    margin-right: 5px; /* 与按钮的间距 */
+    border-radius: 20px; /* 椭圆形效果 */
+    padding: 5px; /* 内边距 */
+    border: 1px solid #ccc; /* 边框 */
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 添加阴影效果 */
+    height:50px;
+    width: 80%;
+    position: relative;
+    margin-top: 7px;
+    margin-bottom: 7px;
+  }
+  
+  .comment-buttons {
+    display: flex; /* 使用 Flexbox 布局 */
+    align-items: center; /* 垂直居中 */
+   
+  }
+  
+  .comment-button {
+    margin-right: 10px; /* 评论按钮与点赞按钮的间距 */
+    color: black; /* 按钮字体颜色 */
+    --background: #f9f9f9;
+    --background-activated: #f9f9f9;
+  }
+  
+  .like-button {
+    font-size: 30px; /* 点赞图标大小 */
+    color:black; /* 点赞图标颜色 */
+  }
+
+  .send-button {
+    height: 25px;
+    width: 60px;
+    --background: #ccc;
+    --background-activated: #ccc;
+    color:grey;
+    border-radius: 25px;
+    font-size: 15px;
+  }
+
+  .send-button1 {
+    height: 25px;
+    width: 60px;
+    --background:black;
+    --background-activated: #ccc;
+    color:white;
+    border-radius: 25px;
+    font-size: 15px;
+  }
+
+  .emoji-button {
+    display: flex; /* 使用 Flexbox 布局 */
+    align-items: center; /* 垂直居中 */
+    margin-right: 10px;
+  }
+
+  
+  /* 表情选择器样式 */
+.emoji-picker {
+  --background: transparent; /* 去除默认样式 */
+  background-color:white; /* 背景颜色 */
+  padding: 10px;
+  display: flex;
+  justify-content: center; /* 水平居中 */
+  overflow: hidden; /* 隐藏多余内容 */
+}
+
+/* 表情容器用于支持滚动 */
+.emoji-container {
+  display: flex;
+  flex-wrap: wrap; /* 允许换行 */
+  overflow-y: auto; /* 允许纵向滚动 */
+  max-height: 70vh; /* 最大高度,防止超出屏幕 */
+  width: 100%; /* 容器宽度 */
+}
+
+/* 表情按钮 */
+.emoji-buttons {
+  margin: 5px; /* 每个表情与顶部的间距 */
+  font-size: 28px; /* 字体大小 */
+  height: 40px; /* 按钮高度 */
+  width: 40px; /* 按钮宽度 */
+  display: flex; /* 使用 flexbox 对齐 */
+  align-items: center; /* 垂直居中 */
+  justify-content: center; /* 水平居中 */
+  --background: transparent; /* 背景透明 */
+  --box-shadow: none; /* 去掉阴影 */
+  --outline: none; /* 去掉轮廓 */
+  border: none; /* 去掉边框 */
+}

+ 110 - 17
E-Cover-app/src/lib/component/post-detail/post-detail.component.ts

@@ -1,32 +1,38 @@
 import { CommonModule } from '@angular/common';
 import { Component, OnInit } from '@angular/core';
 import { ActivatedRoute, ParamMap } from '@angular/router';
-import { IonButton, IonContent, IonIcon } from '@ionic/angular/standalone';
+import { IonButton, IonContent, IonFooter, IonIcon, IonTextarea } from '@ionic/angular/standalone';
 import { addIcons } from 'ionicons';
-import { bookmarkOutline, ellipsisHorizontal, heartOutline } from 'ionicons/icons';
+import { bookmarkOutline, chatbubbleEllipsesOutline, ellipsisHorizontal, happyOutline, heartOutline, shareSocialOutline } from 'ionicons/icons';
 import { CustomHeaderComponent } from '../custom-header/custom-header.component';
-import { CloudQuery } from 'src/lib/ncloud';
+import { CloudQuery, CloudUser } from 'src/lib/ncloud';
 import { map, switchMap } from 'rxjs/operators';
+import { CloudObject } from 'src/lib/ncloud';
+import { FormsModule } from '@angular/forms';
+import { IonModal } from '@ionic/angular/standalone';
+
+addIcons({ellipsisHorizontal,bookmarkOutline,heartOutline,shareSocialOutline,happyOutline,chatbubbleEllipsesOutline})
 
-addIcons({ ellipsisHorizontal, bookmarkOutline, heartOutline, })
 
 @Component({
   selector: 'app-post-detail',
   templateUrl: './post-detail.component.html',
   styleUrls: ['./post-detail.component.scss'],
   standalone: true,
-  imports: [IonContent, IonIcon, IonButton, CommonModule, CustomHeaderComponent]
+  imports: [IonContent, IonIcon, IonButton, CommonModule, CustomHeaderComponent,IonTextarea,IonFooter,FormsModule,IonModal]
 })
 export class PostDetailComponent implements OnInit {
   postId: string | any;
   post: any;
-  comments: any;
-  comments_num: number = 0;
+  newComment: string = ''; // 新评论的内容
+  isfooter: boolean = true; // 控制底部栏的样式
+  comments: CloudObject[] = []; // 存储评论的数组
+  isFollowed: boolean = false; // 初始状态为未关注
   constructor(private route: ActivatedRoute) { }
   /**
    * @从路由参数中获取帖子ID,并初始化帖子详情页面。
    */
-  ngOnInit() {
+  async ngOnInit() {
     this.route.paramMap.pipe(
       switchMap((params: ParamMap) => this.route.queryParamMap.pipe(
         map(() => params.get('postId'))
@@ -37,10 +43,9 @@ export class PostDetailComponent implements OnInit {
     });
     console.log("正在加载帖子:" + this.postId)
     this.getPostDetail();
+    await this.loadComments(); // 组件初始化时加载评论
   }
 
-  isFollowed: boolean = false; // 初始状态为未关注
-
   toggleFollow() {
     this.isFollowed = !this.isFollowed; // 切换关注状态
   }
@@ -55,12 +60,100 @@ export class PostDetailComponent implements OnInit {
     this.post = result[0];
     console.log(this.post);
 
-    let query1=new CloudQuery("comment");
-    query1.include("UserID");
-    query1.equalTo("postID", this.post.toPointer());
-    result = await query1.find();
-    this.comments = result;
-    console.log(this.comments);
-    this.comments_num=this.comments.length;
   }
+
+   // 加载与当前帖子相关的评论
+   async loadComments() {
+    const query = new CloudQuery("comment"); 
+    query.equalTo("post", { "__type": "Pointer", "className": "post", "objectId": this.postId }); // 根据帖子ID查询评论
+    query.include('user'); // 包含 user 属性
+    this.comments = await query.find(); // 获取评论
+    console.log('加载的评论:', this.comments);
+
+}
+
+
+tocomment(){
+  this.isfooter = false; // 切换底部栏的显示样式
+  console.log(this.isfooter);
+}
+
+notcomment(){
+  this.isfooter = true; // 切换底部栏的显示样式
+}
+
+async submitComment() {
+  if (this.newComment.trim()) {
+// 创建新的评论对象
+const comment = new CloudObject("comment"); 
+const user = new CloudUser();
+const currentUser = await user.current(); // 获取当前用户信息
+if (currentUser) {
+// 设置评论的字段
+await comment.set({
+   user: new CloudUser().toPointer(), // 指向当前用户
+   post: { "__type": "Pointer", "className": "post", "objectId": this.post.id }, // 帖子指针
+   content: this.newComment.trim(), // 评论内容
+   heart: 0 // 初始点赞数为0
+});
+
+// 保存评论
+await comment.save().then(() => {
+   console.log('评论已保存:', this.newComment);
+   this.newComment = ''; // 清空输入框
+})
+await this.loadComments(); // 加载新评论
+}
+  }
+}
+
+//用户评论时间
+formatTimeAgo(date: any): string {
+const date1 = new Date(date);
+const now = new Date();
+const seconds = Math.floor((now.getTime() - date1.getTime()) / 1000);
+//console.log("date1.getTime()",date1.getTime());
+//console.log("now.getTime()",now.getTime());
+//console.log("seconds",seconds);
+
+let interval = Math.floor(seconds / 31536000);
+if (interval >= 1) return interval + " 年前";
+interval = Math.floor(seconds / 2592000);
+if (interval >= 1) return interval + " 个月前";
+interval = Math.floor(seconds / 86400);
+if (interval >= 1) return interval + " 天前";
+interval = Math.floor(seconds / 3600);
+if (interval >= 1) return interval + " 小时前";
+interval = Math.floor(seconds / 60);
+if (interval >= 1) return interval + " 分钟前";
+return "刚刚";
+}
+
+isEmojiPickerOpen: boolean = false; // 控制表情选择器的打开状态
+emojis: string[] = ['😀', '😃', '😄', '😁', '😆', '😅', '🤣', '😂', '🙂', '🙃', '🫠', '😉', '😊', '😇', '🥰', '😍', '🤩', '😘',
+ '😗', '☺️', '😚', '😙', '🥲', '😋', '😛', '😜', '🤪', '😝', '🤑', '🤗', '🤭', '🫢', '🫣', '🤫', '🤔', '🫡', '🤐', '🤨', '😐',
+  '😑', '😶', '🫥', '😶‍🌫️', '😏', '😒', '🙄', '😬', '😮‍💨', '🤥', '🫨', '🙂‍↔️', '🙂‍↕️', '😌', '😔', '😪', '🤤', '😴', '🫩', '😷', 
+  '🤒', '🤕', '🤢', '🤮', '🤧', '🥵', '🥶', '🥴', '😵', '😵‍💫', '🤯', '🤠', '🥳', '🥸', '😎', '🤓', '🧐', '😕', '🫤', '😟', 
+  '🙁', '☹️', '😮', '😯', '😲', '😳', '🥺', '🥹', '😦', '😧', '😨', '😰', '😥', '😢', '😭', '😱', '😖', '😣', '😞', '😓', 
+  '😩', '😫', '🥱', '😤', '😡', '😠', '🤬', '😈', '👿', '💀', '☠️', '💩', '🤡', '👹', '👺', '👻', '👽', '👾', '🤖', '😺', 
+  '😸', '😹', '😻', '😼', '😽', '🙀', '😿', '😾', '🙈', '🙉', '🙊', '💌', '💘', '❤️', '🖤', '💋', '💯', '💢', '💥', '💫', 
+  '💦', '💤']; // 表情数组
+// 打开表情选择器
+openEmojiPicker() {
+this.isEmojiPickerOpen = true;
+}
+
+// 关闭表情选择器
+closeEmojiPicker() {
+this.isEmojiPickerOpen = false; // 关闭模态框
+}
+
+// 添加表情到输入框
+addEmoji(emoji: string) {
+this.newComment += emoji; // 将选中的表情添加到输入框
+this.closeEmojiPicker(); // 关闭模态框
+}
+
+
+
 }

+ 1 - 1
E-Cover-app/src/lib/component/post-list/post-list.component.html

@@ -31,7 +31,7 @@
     </ion-button>
     <ion-button fill="clear" class="action-button">
       <ion-icon name="chatbubble-outline"></ion-icon>
-      <span>评论</span>
+      <span>{{ post.commentCount  }}</span>
     </ion-button>
     <ion-button fill="clear" class="action-button" (click)="toggleLike()">
       <ion-icon name="heart-outline" [name]="isLiked ? 'heart' : 'heart-outline'"

+ 13 - 2
E-Cover-app/src/lib/component/post-list/post-list.component.ts

@@ -19,13 +19,24 @@ export class PostListComponent implements OnInit {
 
   posts: CloudObject[] = []; // 存储帖子
   resultUser: any;
-  async ngOnInit() {
+   ngOnInit() {
+     this.loadPosts();// 加载帖子列表
+  }
+
+  async loadPosts() {  // 加载帖子列表
     const query = new CloudQuery('post'); // 创建查询对象
     console.log(query);
     query.include('UserID'); // 包含 user 属性
     this.posts = await query.find(); // 使用 find 方法获取所有帖子
-    console.log("测试帖子头像:")
     console.log(this.posts);
+     // 获取每个帖子的评论数
+     for (let post of this.posts) {
+      const commentQuery = new CloudQuery('comment');
+      commentQuery.equalTo('post', post.toPointer()); // 根据帖子指针查询评论
+      const comments = await commentQuery.find(); // 获取评论
+      post.commentCount = comments.length; // 将评论数存储在帖子对象中
+      console.log('帖子评论数',post.commentCount);
+    }
   }
 
   // 用于跟踪点赞状态

+ 48 - 34
E-Cover-app/src/lib/ncloud.ts

@@ -5,6 +5,7 @@ export class CloudObject {
     createdAt: any;
     updatedAt: any;
     data: Record<string, any> = {};
+    commentCount: number = 0;
 
     constructor(className: string) {
         this.className = className;
@@ -283,6 +284,52 @@ export class CloudUser extends CloudObject {
         return this;
     }
 
+    /** 手机号注册 */
+    async signByPhoneNumber(phone: string, verCode: string, trueVerCode: string, username: string, password: string, additionalData: Record<string, any> = {}) {
+        console.warn("-------------------------");
+        console.warn("正在执行ncloud中的手机号注册方法:开始验证");
+        if (trueVerCode == verCode) {
+            let query = new CloudQuery("_User");
+            query.equalTo("phone", phone);
+            let user = await query.first();
+            if (user) {
+                console.error("手机号已被注册!");
+                return null;
+            } else {
+                const userData = {
+                    username,
+                    password,
+                    ...additionalData // 合并额外的用户数据
+                };
+
+                const response = await fetch(`http://1.94.237.145:1337/parse/users`, {
+                    headers: {
+                        "x-parse-application-id": "hcx",
+                        "Content-Type": "application/json"
+                    },
+                    body: JSON.stringify(userData),
+                    method: "POST"
+                });
+
+                const result = await response?.json();
+                if (result?.error) {
+                    console.error(result?.error);
+                    return null;
+                }
+
+                console.log(result)
+                this.id = result?.objectId;
+                this.sessionToken = result?.sessionToken;
+                this.data = additionalData; // 保存用户数据
+                return this;
+            }
+        }
+        else {
+            console.error("验证码错误!")
+            return null;
+        }
+    }
+
     /** 登出 */
     async logout() {
         if (!this.sessionToken) {
@@ -319,39 +366,6 @@ export class CloudUser extends CloudObject {
         this.sessionToken = null;
         this.data = {};
     }
-
-    /** 注册 */
-    async signUp(username: string, password: string, additionalData: Record<string, any> = {}) {
-        const userData = {
-            username,
-            password,
-            ...additionalData // 合并额外的用户数据
-        };
-
-        const response = await fetch(`http://1.94.237.145:1337/parse/users`, {
-            headers: {
-                "x-parse-application-id": "hcx",
-                "Content-Type": "application/json"
-            },
-            body: JSON.stringify(userData),
-            method: "POST"
-        });
-
-        const result = await response?.json();
-        if (result?.error) {
-            console.error(result?.error);
-            return null;
-        }
-
-        // 设置用户信息
-        // 缓存用户信息
-        console.log(result)
-        localStorage.setItem("NCloud/dev/User", JSON.stringify(result))
-        this.id = result?.objectId;
-        this.sessionToken = result?.sessionToken;
-        this.data = result; // 保存用户数据
-        return this;
-    }
     /**
      * 用户重写save方法,用于更新用户信息
      */
@@ -395,7 +409,7 @@ export class CloudUser extends CloudObject {
         }
         localStorage.setItem("NCloud/dev/User", JSON.stringify(this.data))
         console.log("重新设置用户缓存中...")
-        console.warn("更新流程结束!"+"\n-----------------\n")
+        console.warn("更新流程结束!" + "\n-----------------\n")
         return this;
     }
 }

+ 8 - 2
E-Cover-app/src/lib/user/modal-user-login-main/modal-user-login-main.component.html

@@ -3,7 +3,7 @@
   <img class="logo" src="assets/login/logo.png" alt="logo">
 
   @if(loginType=='phone'){
-  <p>未注册的手机号验证后自动创建账户</p>
+  <p>请输入以下信息</p>
   <div>
     <ion-input class="input" type="tel" [(ngModel)]="phoneNumber" (input)="onPhoneNumberChange()" placeholder="请输入手机号"
       shape="round"></ion-input>
@@ -14,8 +14,14 @@
       <ion-button slot="end" (click)="getVerCode()" [disabled]="!isVerCodeEnabled">验证码</ion-button>
     </ion-input>
   </div>
+  <div>
+    <ion-input class="input" type="text" [(ngModel)]="username" placeholder="请输入用户名" shape="round"></ion-input>
+  </div>
+  <div>
+    <ion-input class="input" type="password" [(ngModel)]="password" placeholder="请输入密码" shape="round"></ion-input>
+  </div>
 
-  <ion-button id="login" expand="block" shape="round" (click)="loginByPhone()" [disabled]="!isLoginButtonEnabled">
+  <ion-button id="login" expand="block" shape="round" (click)="signByPhone()" [disabled]="!isLoginButtonEnabled">
     登录
   </ion-button>
   }

+ 25 - 18
E-Cover-app/src/lib/user/modal-user-login-main/modal-user-login-main.component.ts

@@ -21,6 +21,8 @@ export class ModalUserLoginMainComponent implements OnInit {
   isVerCodeEnabled: boolean = false;
   isLoginButtonEnabled: boolean = false;
   trueVerCode: string = '';
+  username: string = "";
+  password: string = "";
   @Input() loginType: string = '';
   /**
    * @置顶函数
@@ -55,25 +57,32 @@ export class ModalUserLoginMainComponent implements OnInit {
   }
   getVerCode() {
     //随机生成四位数的验证码
-    this.trueVerCode = (Math.floor(Math.random() * 9000)+1000).toString();
+    this.trueVerCode = (Math.floor(Math.random() * 9000) + 1000).toString();
     console.log("获取验证码成功" + this.trueVerCode);
-    const apiUrl = `http://api.smsbao.com/sms?u=peppa&p=94d5a383c32c4d85bfbfc2599e64a8dc&m=${this.phoneNumber}&c=${encodeURIComponent("【衣靠】您的验证码是" + this.trueVerCode)}`;
-    console.log("请求地址:" + apiUrl);
-    this.http.get(apiUrl).subscribe(response => {
-      if (response === '0') {
-        console.log("短信发送成功");
-        // 处理登录成功逻辑
-      } else {
-        console.error("短信发送失败", response);
-      }
-    }, error => {
-      console.error("请求失败", error);
-    });
+    // const apiUrl = `http://api.smsbao.com/sms?u=peppa&p=94d5a383c32c4d85bfbfc2599e64a8dc&m=${this.phoneNumber}&c=${encodeURIComponent("【衣靠】您的验证码是" + this.trueVerCode)}`;
+    // console.log("请求地址:" + apiUrl);
+    // this.http.get(apiUrl).subscribe(response => {
+    //   if (response === '0') {
+    //     console.log("短信发送成功");
+    //     // 处理登录成功逻辑
+    //   } else {
+    //     console.error("短信发送失败", response);
+    //   }
+    // }, error => {
+    //   console.error("请求失败", error);
+    // });
+  }
+  phoneToHide(phone: string): string {
+    //将手机号隐藏中间四位数字,只显示前三位和后四位
+    phone = phone.replace(/(\d{3})\d*(\d{4})/, '$1****$2');
+    return phone;
   }
-  async loginByPhone() {
+  async signByPhone() {
     console.log("登录被点击:手机号登录");
     let user: any = new CloudUser();
-    user = await user.loginByPhone(this.phoneNumber, this.verCode,this.trueVerCode);
+    user.set({ phone: this.phoneNumber, name: this.phoneToHide(this.phoneNumber), })
+    console.log(user.data)
+    user = await user.signByPhoneNumber(this.phoneNumber, this.verCode, this.trueVerCode, this.username, this.password, user.data);
     if (user?.id) {
       console.log("登录成功,关闭模态loginByUsername");
       this.modalCtrl.dismiss(user, "confirm")
@@ -87,8 +96,6 @@ export class ModalUserLoginMainComponent implements OnInit {
   /**
    * @用户名登录页面功能函数
    */
-  username: string = "";
-  password: string = "";
   async loginByUsername() {
     console.warn("-------------------------");
     console.warn("登录被点击:用户尝试使用用户名登录");
@@ -96,7 +103,7 @@ export class ModalUserLoginMainComponent implements OnInit {
       console.error("请输入完整用户名和密码!")
       return
     }
-    let user:any = new CloudUser();
+    let user: any = new CloudUser();
     await user.loginByUsername(this.username, this.password);
     console.log("已收到验证结果,如下:");
     console.log(user);

+ 2 - 2
E-Cover-app/src/lib/user/modal-user-login/modal-user-login.component.html

@@ -1,9 +1,9 @@
 <ion-content>
   <ion-icon name="arrow-back-outline" size="large" class="back" (click)="goBack()"></ion-icon>
   <img class="logo" src="assets/login/logo.png" alt="logo">
-  <ion-button id="phone" expand="block" shape="round" (click)="goPhoneLoginPage()">
+  <ion-button id="phone" expand="block" shape="round" (click)="goPhoneSignPage()">
     <ion-icon name="phone-portrait-outline"></ion-icon>
-    手机号登录
+    手机号注册
   </ion-button>
   <ion-checkbox labelPlacement="end">我已阅读并同意<a href="#" (click)="$event.stopPropagation()">《用户协议》</a>和<a href="#"
       (click)="$event.stopPropagation()">《隐私政策》</a></ion-checkbox>

+ 1 - 1
E-Cover-app/src/lib/user/modal-user-login/modal-user-login.component.ts

@@ -23,7 +23,7 @@ export class ModalUserLoginComponent implements OnInit {
   /**
    * @跳转登录方式功能函数
    */
-  async goPhoneLoginPage() {
+  async goPhoneSignPage() {
     const user: CloudUser | null = await openUserLoginMainModal(this.modalCtrl, 'phone');
     if (user?.id) {
       this.modalCtrl.dismiss(user, "confirm"); // 关闭主模态并返回用户