itcast 3 ماه پیش
والد
کامیت
84f14a9594
33فایلهای تغییر یافته به همراه1805 افزوده شده و 146 حذف شده
  1. 64 7
      tailor-app/myapp/package-lock.json
  2. 1 0
      tailor-app/myapp/package.json
  3. 13 0
      tailor-app/myapp/src/app/app.routes.ts
  4. 45 2
      tailor-app/myapp/src/app/customization/customization.page.html
  5. 44 1
      tailor-app/myapp/src/app/customization/customization.page.scss
  6. 6 0
      tailor-app/myapp/src/app/customization/customization.page.ts
  7. 114 0
      tailor-app/myapp/src/app/kuai/kuai.component.html
  8. 0 0
      tailor-app/myapp/src/app/kuai/kuai.component.scss
  9. 22 0
      tailor-app/myapp/src/app/kuai/kuai.component.spec.ts
  10. 299 0
      tailor-app/myapp/src/app/kuai/kuai.component.ts
  11. 18 46
      tailor-app/myapp/src/app/swiper/swiper.component.html
  12. 79 27
      tailor-app/myapp/src/app/swiper/swiper.component.ts
  13. 23 0
      tailor-app/myapp/src/app/tag-input/tag-input.component.html
  14. 36 0
      tailor-app/myapp/src/app/tag-input/tag-input.component.scss
  15. 24 0
      tailor-app/myapp/src/app/tag-input/tag-input.component.spec.ts
  16. 39 0
      tailor-app/myapp/src/app/tag-input/tag-input.component.ts
  17. 197 0
      tailor-app/myapp/src/app/test-page/test-page.component.html
  18. 66 0
      tailor-app/myapp/src/app/test-page/test-page.component.scss
  19. 22 0
      tailor-app/myapp/src/app/test-page/test-page.component.spec.ts
  20. 169 0
      tailor-app/myapp/src/app/test-page/test-page.component.ts
  21. 0 29
      tailor-app/myapp/src/app/test/test.component.html
  22. 0 11
      tailor-app/myapp/src/app/test/test.component.ts
  23. 59 0
      tailor-app/myapp/src/app/tianqi/tianqi.component.html
  24. 104 0
      tailor-app/myapp/src/app/tianqi/tianqi.component.scss
  25. 22 0
      tailor-app/myapp/src/app/tianqi/tianqi.component.spec.ts
  26. 254 0
      tailor-app/myapp/src/app/tianqi/tianqi.component.ts
  27. BIN
      tailor-app/myapp/src/assets/img/quick.png
  28. BIN
      tailor-app/myapp/src/assets/img/tianqi.png
  29. BIN
      tailor-app/myapp/src/assets/tianqi.jpg
  30. 3 0
      tailor-app/myapp/src/index.html
  31. 81 23
      tailor-app/myapp/src/lib/ncloud.ts
  32. 1 0
      tailor-app/myapp/src/lib/user/modal-user-login/modal-user-login.component.ts
  33. BIN
      tailor-prod/202226701001 李铭沣 实验11.docx

+ 64 - 7
tailor-app/myapp/package-lock.json

@@ -25,6 +25,7 @@
         "@ionic/vue": "^8.4.0",
         "@ionic/vue-router": "^8.4.0",
         "@types/echarts": "^4.9.22",
+        "axios": "^1.7.9",
         "echarts": "^5.5.1",
         "fmode-ng": "^0.0.63",
         "ionicons": "^7.2.1",
@@ -7404,6 +7405,12 @@
         "node": ">=8"
       }
     },
+    "node_modules/asynckit": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz",
+      "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+      "license": "MIT"
+    },
     "node_modules/at-least-node": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz",
@@ -7469,13 +7476,14 @@
       }
     },
     "node_modules/axios": {
-      "version": "0.26.1",
-      "resolved": "https://registry.npmmirror.com/axios/-/axios-0.26.1.tgz",
-      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+      "version": "1.7.9",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-1.7.9.tgz",
+      "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==",
       "license": "MIT",
-      "peer": true,
       "dependencies": {
-        "follow-redirects": "^1.14.8"
+        "follow-redirects": "^1.15.6",
+        "form-data": "^4.0.0",
+        "proxy-from-env": "^1.1.0"
       }
     },
     "node_modules/axobject-query": {
@@ -8302,6 +8310,18 @@
         "node": ">=0.1.90"
       }
     },
+    "node_modules/combined-stream": {
+      "version": "1.0.8",
+      "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz",
+      "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+      "license": "MIT",
+      "dependencies": {
+        "delayed-stream": "~1.0.0"
+      },
+      "engines": {
+        "node": ">= 0.8"
+      }
+    },
     "node_modules/commander": {
       "version": "2.20.3",
       "resolved": "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz",
@@ -8999,6 +9019,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/delayed-stream": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz",
+      "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/depd": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz",
@@ -9644,6 +9673,16 @@
         "node": ">=0.12.7"
       }
     },
+    "node_modules/esdk-obs-browserjs/node_modules/axios": {
+      "version": "0.26.1",
+      "resolved": "https://registry.npmmirror.com/axios/-/axios-0.26.1.tgz",
+      "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==",
+      "license": "MIT",
+      "peer": true,
+      "dependencies": {
+        "follow-redirects": "^1.14.8"
+      }
+    },
     "node_modules/eslint": {
       "version": "8.57.1",
       "resolved": "https://registry.npmmirror.com/eslint/-/eslint-8.57.1.tgz",
@@ -10663,6 +10702,20 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/form-data": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.1.tgz",
+      "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==",
+      "license": "MIT",
+      "dependencies": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.8",
+        "mime-types": "^2.1.12"
+      },
+      "engines": {
+        "node": ">= 6"
+      }
+    },
     "node_modules/forwarded": {
       "version": "0.2.0",
       "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz",
@@ -13763,7 +13816,6 @@
       "version": "1.52.0",
       "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz",
       "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">= 0.6"
@@ -13773,7 +13825,6 @@
       "version": "2.1.35",
       "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz",
       "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "mime-db": "1.52.0"
@@ -15732,6 +15783,12 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/proxy-from-env": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
+      "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
+      "license": "MIT"
+    },
     "node_modules/prr": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/prr/-/prr-1.0.1.tgz",

+ 1 - 0
tailor-app/myapp/package.json

@@ -30,6 +30,7 @@
     "@ionic/vue": "^8.4.0",
     "@ionic/vue-router": "^8.4.0",
     "@types/echarts": "^4.9.22",
+    "axios": "^1.7.9",
     "echarts": "^5.5.1",
     "fmode-ng": "^0.0.63",
     "ionicons": "^7.2.1",

+ 13 - 0
tailor-app/myapp/src/app/app.routes.ts

@@ -33,4 +33,17 @@ export const routes: Routes = [
     path: 'test',
     loadComponent: () => import('./test/test.component').then( m => m.TestComponent)
   },
+  {
+    path: 'tianqi',
+    loadComponent: () => import('./tianqi/tianqi.component').then( m => m.TianqiComponent)
+  },
+  {
+    path: 'kuai',
+    loadComponent: () => import('./kuai/kuai.component').then( m => m.KuaiComponent)
+  },
+  {
+    path: 'testPage',
+    loadComponent: () =>import('./test-page/test-page.component').then((m) => m.TestPageComponent)
+      
+  },
 ];

+ 45 - 2
tailor-app/myapp/src/app/customization/customization.page.html

@@ -5,11 +5,39 @@
 </ion-header>
 
 <ion-content [fullscreen]="true">
-  <swiper></swiper>
+  <!-- <ion-grid>
+    <ion-row>
+      <ion-col>
+        <div >
+          <img src="assets/img/tianqi.png" alt="" (click)="opentianqi()">
+        <ion-button class="bai" expand="block" (click)="opentianqi()" >天气推荐</ion-button>
+        </div>
+      </ion-col>
+      <ion-col>
+        <div>
+          <img src="assets/img/quick.png" alt="" (click)="openquick()">
+        <ion-button class="bai"  expand="block" (click)="openquick()">快速制衣</ion-button>
+        </div>
 
+      </ion-col>
+    </ion-row>
+  </ion-grid> -->
+  <swiper></swiper>
 
+<div style="width: 90%;margin: auto;height: 110px;display: flex;justify-content: space-between;background-color: #f8f8f0;border: 1px solid #e7e7db;border-radius: 20px;overflow: hidden;" (click)="opentianqi()">
+ 
+  <div style="display: flex;justify-content: flex-start;flex-direction: column;margin-top: 23px;margin-left: 40px;position: relative;">
+    <div style="position: absolute;padding: 5px;color: #fff;background-color:#0ba9d4;top: -25px;left: -20px;border-radius: 7px;font-size: 12px;">品牌动态</div>
+        <h3 >今日推荐<span style="color: #38b4d6;font-size: 30px;font-weight: bolder;font-family:SimSun;">穿搭</span></h3>
+        <div style="color: #fff;">
+          <ion-button size="small" class="lan">点击了解-></ion-button>
+        </div>
+      </div>
+      <div><img src="assets/img/tianqi.png" style="height: 100px;display: flex;justify-content: flex-end;" alt="" ></div>
+    
+</div>
   <div>
-    <ion-button (click)="test()"></ion-button>
+    <!-- <ion-button (click)="test()"></ion-button> -->
     <ion-card id="designers">
       <ion-card-header>
         <ion-card-title>设计师</ion-card-title>
@@ -37,6 +65,21 @@
     </ion-card>
   </div>
 
+  <div style="width: 90%;margin: auto;height: 110px;display: flex;justify-content: space-between;background-color: #f1f2ef;border: 1px solid #e7e7db;border-radius: 20px;overflow: hidden;" (click)="openquick()">
+ 
+    <div style="display: flex;justify-content: flex-start;flex-direction: column;margin-top: 23px;margin-left: 40px;position: relative;" >
+    <div style="position: absolute;padding: 5px;color: #fff;background-color:#0ba9d4;top: -25px;left: -20px;border-radius: 7px;font-size: 12px;">品牌动态</div>
+
+          <h3 >一键<span style="color: #38b4d6;font-size: 30px;font-weight: bolder;font-family:SimSun;">定制</span></h3>
+          <div style="color: #fff;">
+            <ion-button size="small" class="lan">点击了解-></ion-button>
+          </div>
+        </div>
+        <div><img src="assets/img/quick.png" style="height: 100px;display: flex;justify-content: flex-end;" alt="" ></div>
+      
+  </div>
+
+
 
   <!-- <fm-markdown-preview class="content-style" [content]="picdetail" ></fm-markdown-preview> -->
   <!-- {{gptre}} -->

+ 44 - 1
tailor-app/myapp/src/app/customization/customization.page.scss

@@ -100,6 +100,27 @@
     transform: translateX(-100%); /* 滚动到左侧 */
   }
 }
+.lan{
+    --background: #38b4d6;
+    --background-hover: #9ce0be;
+    --background-activated: #88f4be;
+    --background-focused: #88f4be;
+  
+    --color: #ffffff;
+  
+    --border-radius: 10px;
+    --border-color: #38b4d6;
+    --border-style: solid;
+    --border-width: 1px;
+  
+    --box-shadow: 0 2px 6px 0 rgb(0, 0, 0, 0.25);
+  
+    --ripple-color: deeppink;
+    --padding-left: 10px;
+    --padding-right: 10px;
+    --padding-top: 3px;
+    --padding-bottom: 3px;
+}
 ion-button {
   --background: #000;
   --background-hover: #9ce0be;
@@ -272,4 +293,26 @@ ion-thumbnail {
   ion-card:hover {
     transform: scale(1.02); /* 悬停时放大效果 */
     transition: transform 0.2s; /* 动画过渡效果 */
-  }
+  }
+
+  
+.bai {
+  --background: #f8f9ef;
+  --background-hover: #9ce0be;
+  --background-activated: #88f4be;
+  --background-focused: #88f4be;
+
+  --color:  #333333;
+
+  --border-radius: 15px;
+  --border-color: #fbfdef;
+  --border-style: solid;
+  --border-width: 1px;
+
+  --box-shadow: 0 2px 6px 0 rgb(0, 0, 0, 0.25);
+
+  --ripple-color: deeppink;
+
+  --padding-top: 10px;
+  --padding-bottom: 10px;
+}

+ 6 - 0
tailor-app/myapp/src/app/customization/customization.page.ts

@@ -36,7 +36,13 @@ addIcons({ camera, trendingUpOutline, sparklesOutline, cloudyOutline, diceOutlin
 export class CustomizationPage implements OnInit {
   public buffer = 0.06;
   public progress = 0;
+  opentianqi(){
+    this.router.navigate(['/tianqi']);
+  }
+  openquick(){
+    this.router.navigate(['/testPage']);
 
+  }
 
   // 修改需求分析
   async modifyRequire() {

+ 114 - 0
tailor-app/myapp/src/app/kuai/kuai.component.html

@@ -0,0 +1,114 @@
+<ion-header translucent="true">
+
+  <ion-toolbar>
+    <ion-title>制定个人健身计划</ion-title>
+    <ion-back-button></ion-back-button>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <div class="content">
+
+    <ion-button (click)="goBack()">
+      <ion-icon slot="icon-only" name="arrow-back"></ion-icon>
+    </ion-button>
+    健身目标选择
+    <div class="module">
+      <h2>请输入您的健身目标</h2>
+      <app-tag-input (tagsChanged)="onTagsChanged($event)"></app-tag-input>
+      <ion-item>
+        <ion-label position="floating">详细描述(可选,越详细计划越清晰哦!)</ion-label>
+        <ion-textarea [value]="goalDescription" (ionInput)="onGoalDescriptionChange($event)"
+          placeholder="例如:我想减脂,增强耐力..." auto-grow="true">
+        </ion-textarea>
+      </ion-item>
+    </div>
+
+    <!-- 偏好设置 -->
+    <div class="module">
+      <h3>偏好设置</h3>
+      <ion-grid>
+        <ion-row>
+          <ion-col size="6">
+            <ion-item>
+              <ion-label>锻炼方式(多选)</ion-label>
+              <ion-select multiple="true" (ionChange)="onExercisePreferenceChange($event)" cancelText="取消" okText="确认">
+                <ion-select-option value="cardio">有氧</ion-select-option>
+                <ion-select-option value="strength">力量</ion-select-option>
+                <ion-select-option value="flexibility">柔韧性</ion-select-option>
+              </ion-select>
+            </ion-item>
+          </ion-col>
+          <ion-col size="6">
+            <ion-item>
+              <ion-label>每周锻炼频率</ion-label>
+              <ion-select (ionChange)="onWorkoutFrequencyChange($event)" cancelText="取消" okText="确认">
+                <ion-select-option value="1">1次</ion-select-option>
+                <ion-select-option value="2">2次</ion-select-option>
+                <ion-select-option value="3">3次</ion-select-option>
+                <ion-select-option value="4">4次</ion-select-option>
+                <ion-select-option value="5">5次</ion-select-option>
+                <ion-select-option value="6">6次</ion-select-option>
+                <ion-select-option value="7">7次</ion-select-option>
+              </ion-select>
+            </ion-item>
+          </ion-col>
+        </ion-row>
+      </ion-grid>
+    </div>
+
+    <!-- 身体数据 -->
+    <div class="module">
+      <h3>身体数据</h3>
+      <ion-grid>
+        <ion-row>
+          <ion-col size="4">
+            <ion-item [class.empty]="!height" [class.filled]="height">
+              <ion-label position="floating">身高(cm)</ion-label>
+              <ion-input type="number" [value]="height" (ionInput)="onHeightChange($event)" placeholder="请输入身高"
+                required>
+              </ion-input>
+            </ion-item>
+          </ion-col>
+          <ion-col size="4">
+            <ion-item [class.empty]="!weight" [class.filled]="weight">
+              <ion-label position="floating">体重(kg)</ion-label>
+              <ion-input type="number" [value]="weight" (ionInput)="onWeightChange($event)" placeholder="请输入体重"
+                required>
+              </ion-input>
+            </ion-item>
+          </ion-col>
+          <ion-col size="4">
+            <ion-item [class.empty]="!age" [class.filled]="age">
+              <ion-label position="floating">年龄(age)</ion-label>
+              <ion-input type="number" [value]="age" (ionInput)="onAgeChange($event)" placeholder="请输入年龄" required>
+              </ion-input>
+            </ion-item>
+          </ion-col>
+        </ion-row>
+      </ion-grid>
+    </div>
+
+    <!-- 生成计划按钮 -->
+    <ion-button expand="full" (click)="generatePlan()" [disabled]="isGenerating">生成健身计划</ion-button>
+
+    <!-- 显示健身计划结果 -->
+    <div *ngIf="generatedPlan" class="result-container">
+      <h3>您的健身计划:</h3>
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>健身计划内容</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <div *ngIf="isGenerating">
+            <ion-spinner name="crescent"></ion-spinner>
+            <p>生成中...</p>
+          </div>
+          <div *ngIf="!isGenerating">
+            <fm-markdown-preview class="content-style" [content]="generatedPlan"></fm-markdown-preview>
+          </div>
+        </ion-card-content>
+      </ion-card>
+    </div>
+  </div>
+</ion-content>

+ 0 - 0
tailor-app/myapp/src/app/kuai/kuai.component.scss


+ 22 - 0
tailor-app/myapp/src/app/kuai/kuai.component.spec.ts

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

+ 299 - 0
tailor-app/myapp/src/app/kuai/kuai.component.ts

@@ -0,0 +1,299 @@
+
+
+import { Component, OnInit } from '@angular/core';
+import { IonicModule, ToastController } from '@ionic/angular';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { TagInputComponent } from '../tag-input/tag-input.component';
+import { addIcons } from 'ionicons';
+import { barbellOutline, personOutline, square, alarmOutline, arrowBack } from 'ionicons/icons';
+import { DalleOptions, FmodeChatCompletion, MarkdownPreviewModule } from 'fmode-ng';
+import { LoadingController } from '@ionic/angular';
+import { AlertController, IonThumbnail, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonImg, IonInput, IonItem, IonLabel, IonList, IonRow, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonToolbar, IonTitle, IonSpinner, IonTextarea } from '@ionic/angular/standalone';
+import { NavController } from '@ionic/angular';
+import { Router } from '@angular/router';
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
+import { IonBackButton } from '@ionic/angular/standalone';
+@Component({
+  selector: 'app-kuai',
+  templateUrl: './kuai.component.html',
+  styleUrls: ['./kuai.component.scss'],
+  standalone: true,
+  imports: [
+    IonTextarea,
+    IonSpinner,
+    IonTitle,
+    IonBackButton,
+    MarkdownPreviewModule,
+    IonSelectOption,
+    IonSelect,
+    IonThumbnail,
+    IonCardSubtitle,
+    IonImg,
+    IonCard,
+    IonCardTitle,
+    IonCardHeader,
+    IonCardContent,
+    IonButtons,
+    IonItem,
+    IonList,
+    IonIcon,
+    FormsModule,
+    CommonModule,
+    IonHeader,
+    IonToolbar,
+    IonContent,
+    IonSegment,
+    IonSegmentButton,
+    IonGrid,
+    IonRow,
+    IonCol,
+    IonButton,
+    IonLabel,
+    IonBadge,
+    IonInput,
+    TagInputComponent
+  ]  // 添加 CommonModule
+})
+export class KuaiComponent implements OnInit {
+  selectedTags: string[] = [];
+  exercisePreference: string = '';
+  workoutFrequency: string = '';
+  height: number | null = null;
+  weight: number | null = null;
+  age: number | null = null;
+  goalDescription: string = '';
+  generatedPlan: string = '';  // 用来存储生成的健身计划
+  isGenerating: boolean = false;  // 是否正在生成计划
+  messageList: any[] = [];  // 存储消息列表
+  constructor(private toastController: ToastController,
+    private loadingController: LoadingController,
+    private alertController: AlertController,
+    private navController: NavController,
+    private router: Router,
+    private navCtrl: NavController
+  ) {
+    addIcons({ personOutline, barbellOutline, alarmOutline, square, arrowBack });
+  }
+  ngOnInit() { }
+
+  onTagsChanged(tags: string[]) {
+    this.selectedTags = tags;
+    console.log('当前标签:', this.selectedTags);
+  }
+
+  onGoalDescriptionChange(event: any) {
+    this.goalDescription = event.detail.value;
+  }
+
+  onExercisePreferenceChange(event: any) {
+    this.exercisePreference = event.detail.value;
+  }
+
+  onWorkoutFrequencyChange(event: any) {
+    this.workoutFrequency = event.detail.value;
+  }
+
+  onHeightChange(event: any) {
+    this.height = event.detail.value;
+  }
+
+  onWeightChange(event: any) {
+    this.weight = event.detail.value;
+  }
+
+  onAgeChange(event: any) {
+    this.age = event.detail.value;
+  }
+
+  // 显示toast提示
+  async showToast(message: string) {
+    const toast = await this.toastController.create({
+      message: message,
+      duration: 2000,
+      position: 'top',
+      color: 'danger',
+    });
+    toast.present();
+  }
+
+  async generatePlan() {
+    // 检查身高和体重是否为空
+    if (!this.height || !this.weight) {
+      // 如果为空,显示提示信息
+      await this.showToast('身高或体重为空,无法生成个人健身计划');
+      return; // 退出函数,不继续生成健身计划
+    }
+    const loading = await this.loadingController.create({
+      message: '生成计划中...',
+      duration: 30000 // 加载时长可以根据实际需求调整
+    });
+    await loading.present();
+    this.generatedPlan = '';
+    this.messageList = [];
+    this.isGenerating = true;
+
+    console.log('生成健身计划', {
+      tags: this.selectedTags,
+      exercisePreference: this.exercisePreference,
+      workoutFrequency: this.workoutFrequency,
+      height: this.height,
+      weight: this.weight,
+      age: this.age,
+      goalDescription: this.goalDescription
+    });
+
+    let prompt = `您作为一名专业的健身计划定制大师,请帮我根据以下情况制定健身计划(健身计划请给每天的运动标上序号)。关键词:${this.selectedTags.join(",")},目标描述:${this.goalDescription},运动偏好:${this.exercisePreference},
+    健身频率每周:${this.workoutFrequency},身高:${this.height}cm,体重:${this.weight}kg,年龄:${this.age},注意:只需给出训练日计划,训练日只做四个运动`;
+
+    let messageList = new FmodeChatCompletion([
+      { role: "system", content: '' },
+      { role: "user", content: prompt }
+    ]);
+
+    messageList.sendCompletion().subscribe((message: any) => {
+      console.log(message.content);
+      this.generatedPlan = message.content;
+
+      if (message?.complete) {
+        this.isGenerating = false;
+        this.getJson()
+        loading.dismiss();
+      }
+    });
+  }
+  public gptre = ""
+  title: string = "123"
+  chatID = ""
+  complete: boolean = false
+  planArray: any
+  JSONcomplete: boolean = false
+  JSONdes = ""
+  JSONobject: { [key: string]: string } = {}
+  getJson() {
+    let promt = `请你以以下json格式分每天来整合文段内容:
+    {
+    "date":"周几运动",
+    "srcId": "plan001等等",
+    "trainingPart":"训练部位",
+    "trainingItems":"四个运动项目,以下面形式给出,形式如下"[
+  {
+    "item": "引体向上",
+    "reps": 8,
+    "sets": 4
+  },
+  {
+    "item": "划船",
+    "reps": 10,
+    "sets": 3
+  },
+  {
+    "item": "背部伸展",
+    "reps": 12,
+    "sets": 3
+  },
+  {
+    "item": "拉力器划船",
+    "reps": 12,
+    "sets": 3
+  }
+]"
+}
+    内容如下:${this.generatedPlan}
+`
+
+    let completion = new FmodeChatCompletion([
+      { role: "system", content: "" },
+      { role: "user", content: promt }
+    ])
+    completion.sendCompletion().subscribe((message: any) => {
+      console.log(message.content)
+      this.JSONdes = message.content;
+
+      
+    });
+  }
+  async showConfirmationAlert() {
+    const alert = await this.alertController.create({
+      header: '确认生成计划',
+      message: ` <div style="white-space: pre-wrap;">
+        <!-- 使用 fm-markdown-preview 渲染 Markdown 格式的内容 -->
+        <fm-markdown-preview class="content-style" [content]="generatedPlan"></fm-markdown-preview>
+      </div>`,
+      buttons: [
+        {
+          text: '放弃',
+          role: 'cancel',
+          handler: () => {
+            console.log('用户选择放弃');
+          }
+        },
+        {
+          text: '确认',
+          handler: () => {
+            // 这里可以调用 API 将数据存到数据库
+            console.log('用户确认', this.generatedPlan);
+            this.savePlanToDatabase(this.generatedPlan);
+          }
+        }
+      ]
+    });
+    await alert.present();
+  }
+  async savePlanToDatabase(plan: string) {
+
+    let currentUser = new CloudUser().toPointer();  // 获取当前用户实例
+    console.log(currentUser)
+    console.log('计划保存到数据库', plan);
+
+    // 查询当前用户的所有计划
+    const cloudQuery = new CloudQuery("fitPlan");
+    // 过滤出与当前用户关联的所有计划
+    cloudQuery.equalTo("user", currentUser)
+    try {
+      const userPlans = await cloudQuery.find();  // 获取当前用户的所有计划
+      console.log("找到的计划数量:", userPlans.length);
+
+      // 删除当前用户的所有计划
+      for (const planObj of userPlans) {
+        await planObj.destroy();  // 删除每个计划
+        console.log("已删除计划:", planObj);
+      }
+      this.planArray.forEach((plan: any) => {
+        plan.detail = this.generatedPlan; // 添加额外的计划内容
+        console.log(plan);
+
+        // 准备插入数据库的数据对象
+        let newPlan = {
+          "date": plan['date'],
+          "srcId": plan['srcId'],
+          "trainingPart": plan['trainingPart'],
+          "trainingItems": plan['trainingItems'],
+          "user": currentUser,  // 将当前用户指针关联到计划
+        };
+        // 创建新的 CloudObject 实例
+        let newObject = new CloudObject("fitPlan");
+        newObject.set(newPlan);
+        newObject.save()
+          .then(() => {
+            console.log("计划保存成功");
+          })
+          .catch((error: any) => {
+            console.error("保存计划时发生错误", error);
+          });
+      });
+      this.JSONcomplete = true;
+    } catch (error) {
+      console.error("查询或删除计划时发生错误", error);
+    }
+    this.goToPage("tab2")
+  }
+
+  goBack() {
+    this.navCtrl.back();
+  }
+  goToPage(page: string) {
+    // 更新选中的tab
+    this.router.navigate([`/tabs/${page}`]);  // 然后再进行路由跳转
+  }
+}

+ 18 - 46
tailor-app/myapp/src/app/swiper/swiper.component.html

@@ -61,8 +61,8 @@
     <!-- 可以继续添加更多行 -->
 </ion-grid>
       </div>
-      <div class="swiper-slide">Slide 2</div>
-      <div class="swiper-slide">Slide 3</div>
+      <!-- <div class="swiper-slide">Slide 2</div> -->
+      <!-- <div class="swiper-slide">Slide 3</div> -->
     </div>
     <!-- 如果需要分页器 -->
 
@@ -74,13 +74,13 @@
     <!-- <div class="swiper-scrollbar"></div> -->
   </div>
 
-  <ion-button (click)="scrollToElement()" expand="block">开始挑选您的专属设计师吧!</ion-button>
+  <ion-button (click)="scrollToElement()" expand="block" style="width: 90%;margin: auto;">开始挑选您的专属设计师吧!</ion-button>
 
 
-  <div class="swiper-container" id="swiper2">
+  <div class="swiper-container" id="swiper2" style="height: 480px;">
     <!-- <div  data-swiper-parallax="-23%" data-swiper-parallax-duration="3000" style="background-image: url('assets/img/3d.png');"></div> -->
     <div class="swiper-wrapper">
-      <div class="swiper-slide">
+      <!-- <div class="swiper-slide">
         <div class="title" data-swiper-parallax="-100">从右边100px开始进入</div>
         <div class="subtitle" data-swiper-parallax="-200">从右边200px开始进入</div>
         <div class="text" data-swiper-parallax="-300" data-swiper-parallax-duration="600">
@@ -88,65 +88,37 @@
         </div>
         <div data-swiper-parallax="0" data-swiper-parallax-opacity="0.5">透明度变化</div>
         <div data-swiper-parallax-scale="0.15">缩放变化</div>
-      </div>
-      <div class="swiper-slide">
-        <ion-list style="width: 90%;margin: auto;margin-top: 20px;">
+      </div> -->
+      <div class="swiper-slide" *ngFor="let design of designList"  >
+        <ion-card style="width: 90%;margin: auto;margin-top: 20px;padding: 10px;min-height: 390px;">
  
-          <ion-item>
       
               <ion-grid>
                 <ion-row>
-                    <div>
+                    <div style="display: flex;align-items: center;">
                       <ion-avatar
-                      style="width: 30px;height: 30px;display: inline-block;vertical-align: middle;margin-right: 10px;">
-                      <img src="assets/shapes.svg" alt="用户头像">
+                      style="width: 50px;height: 50px;display: inline-block;vertical-align: middle;margin-right: 10px;">
+                      <img [src]="design.avatar" alt="用户头像">
                     </ion-avatar>
-                    <h2 style="font-weight: bolder;display: inline-block;">小林</h2>
+                    <h2 style="font-weight: bolder;display: inline-block;">{{design.username}}</h2>
                     </div>
-                    <div style="margin-top: 10px;margin-bottom: 10px;">
-                      衣服非常合身,面料舒适,非常满意!这件服装简直是时尚与艺术的完美融合,让人一眼难忘!从设计到剪裁,每一个细节都透露着匠人的精湛技艺与独到眼光。面料质感丝滑,穿着舒适亲肤,色彩搭配既经典又不失前卫感,完美衬托出穿着者的独特气质。
+                    <div style="margin-top: 10px;margin-bottom: 10px;font-size: 17px;line-height: 40px;text-indent: 40px;">
+                      {{design.comment}}
                     </div>
                 </ion-row>
-                <ion-row>
+                <!-- <ion-row>
                   <img src="assets/img/shi.png" alt="定制案例">
       
-                </ion-row>
+                </ion-row> -->
               </ion-grid>
-          </ion-item>
-        </ion-list>
+        </ion-card>
       
 
       </div>
-      <div class="swiper-slide">Slide 3</div>
+      <!-- <div class="swiper-slide">Slide 3</div> -->
     </div>
   <div class="swiper-pagination"></div>
 
   </div>
 
 </div>
-<ion-grid>
-  <ion-row class="table-header">
-    <ion-col col-12 text-center>
-      <strong>服装设计方案表</strong>
-    </ion-col>
-  </ion-row>
-
-  <!-- 表格内容 -->
-  <ion-row>
-    <ion-col col-4 text-left><strong>名字</strong></ion-col>
-    <ion-col col-8 text-left>小明</ion-col>
-  </ion-row>
-  <ion-row>
-    <ion-col col-4 text-left><strong>年龄</strong></ion-col>
-    <ion-col col-8 text-left>25</ion-col>
-  </ion-row>
-  <ion-row>
-    <ion-col col-4 text-left><strong>设计方案</strong></ion-col>
-    <ion-col col-8 text-left>现代简约风格</ion-col>
-  </ion-row>
-  <ion-row>
-    <ion-col col-4 text-left><strong>颜色</strong></ion-col>
-    <ion-col col-8 text-left>蓝色</ion-col>
-  </ion-row>
-  <!-- 可以继续添加更多行 -->
-</ion-grid>

+ 79 - 27
tailor-app/myapp/src/app/swiper/swiper.component.ts

@@ -3,7 +3,7 @@ import { Component, OnInit } from '@angular/core';
 import { IonContent, IonHeader, IonTitle, IonToolbar, IonButton, IonLabel, IonItem, IonList, IonBackButton, IonButtons, IonIcon, IonItemDivider, IonAvatar, IonThumbnail, IonItemOptions, IonItemOption, IonItemSliding, IonInput, IonCheckbox, IonRadio, IonToggle, IonRadioGroup, IonSearchbar, IonSegment, IonSegmentButton, IonDatetime, IonFooter, IonCardContent, IonCardTitle, IonCardHeader, IonCard, IonCol, IonRow, IonGrid, IonChip, IonImg } from '@ionic/angular/standalone';
 declare let Swiper: any;
 import { CommonModule } from '@angular/common';
-import { CloudObject, CloudQuery } from 'src/lib/ncloud';
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
 interface Design {
   "goal": string,
   "style": string,
@@ -21,7 +21,9 @@ interface Design {
   "image": string,
   "user": string,
   "detail": string,
-  "comment": string
+  "comment": string,
+  "avatar":string,
+  "username":string
 
 
 }
@@ -143,9 +145,53 @@ export class SwiperComponent implements OnInit {
 
 
   }
+  avatar:string=""
+  async getUserByUsername(username: string):Promise<string> {
+    try {
+        // 等待用户数据的 Promise 解析
+        const user = await new CloudUser().getUserByUsername(username);
+
+        // 确保 user 存在并且包含数据
+        if (user && user.data) {
+            console.log("user.get('avatar'); ",user.get('avatar'));
+            let a:string=user.get('avatar')
+
+            return a; // 返回 avatar
+        } else {
+            return ""; // 如果没有找到 avatar,返回 null
+        }
+    } catch (error) {
+        console.error('Error fetching user data:', error);
+        return ""; // 处理错误并返回 null
+    }
+}
+
+async getUserByUsername2(username: string):Promise<string> {
+  try {
+      // 等待用户数据的 Promise 解析
+      const user = await new CloudUser().getUserByUsername(username);
+
+      // 确保 user 存在并且包含数据
+      if (user && user.data) {
+          console.log("user.get('avatar'); ",user.get('avatar'));
+          let a:string=user.get('realname')
+
+          return a; // 返回 avatar
+      } else {
+          return ""; // 如果没有找到 avatar,返回 null
+      }
+  } catch (error) {
+      console.error('Error fetching user data:', error);
+      return ""; // 处理错误并返回 null
+  }
+}
   count = 4
   designList:Array<Design> = []
   async loadDesigne() {
+
+   
+    
+
     let query = new CloudQuery("Designs");
     let designList: Array<CloudObject> = await query.find()
     
@@ -163,35 +209,41 @@ export class SwiperComponent implements OnInit {
     let bb = indices.slice(0, this.count).map(index => aa[index]);
     console.log(bb[0]['image']);
 
-    let cc:Array<Design>=bb.map(item=>{
-      let obj= {
-        "goal":item['goal'] ,
-        "style":item['style'] ,
-        "color":item['color'] ,
-        "feature":item['feature'] ,
-        "ACC":item['ACC'] ,
-        "rim":item['rim'] ,
-        "period":item['period'] ,
-        "clothing":item['clothing'] ,
-        "trousers":item['trousers'] ,
-        "shoe": item['shoe'],
-        "after":item['after'] ,
-        "texture":item['texture'] ,
-        "remark":item['remark'],
-        "image": item['image'],
-        "user":item['user'] ,
-        "detail":item['detail'] ,
-        "comment":item['comment'] 
-      
-      
-      }
-      return obj;
-    })
+    let cc:Array<Design>=await Promise.all(
+      bb.map(async item=>{
+        let avatar: string = await this.getUserByUsername(item['user']);
+        let username: string = await this.getUserByUsername2(item['user']);
+  
+        let obj= {
+          "goal":item['goal'] ,
+          "style":item['style'] ,
+          "color":item['color'] ,
+          "feature":item['feature'] ,
+          "ACC":item['ACC'] ,
+          "rim":item['rim'] ,
+          "period":item['period'] ,
+          "clothing":item['clothing'] ,
+          "trousers":item['trousers'] ,
+          "shoe": item['shoe'],
+          "after":item['after'] ,
+          "texture":item['texture'] ,
+          "remark":item['remark'],
+          "image": item['image'],
+          "user":item['user'] ,
+          "detail":item['detail'] ,
+          "comment":item['comment'] ,
+          "avatar":avatar,
+          "username":username
+        
+        }
+        return obj;
+      })
+    )
     
     this.designList = cc
     console.log(this.designList[0].detail);
     
-    console.log(designList);
+    console.log("this.designList::::",this.designList);
     
     
 

+ 23 - 0
tailor-app/myapp/src/app/tag-input/tag-input.component.html

@@ -0,0 +1,23 @@
+<ion-card>
+  <ion-card-header>
+    <ion-card-title>添加标签</ion-card-title>
+  </ion-card-header>
+
+  <ion-card-content>
+    <div class="input-container">
+      <ion-input [(ngModel)]="tagInput" placeholder="例如:欧美风,英伦风..." (keydown.enter)="addTag()"></ion-input>
+      <ion-button expand="full" (click)="addTag()">添加标签</ion-button>
+    </div>
+
+    <!-- 标签显示区域 -->
+    <div class="tag-container">
+      <ion-chip *ngFor="let tag of tags" class="tag-chip">
+        <ion-label>{{ tag }}</ion-label>
+        <!-- 删除按钮 -->
+        <ion-icon name="close" (click)="removeTag(tag)" class="close-icon"></ion-icon>
+      </ion-chip>
+    </div>
+
+    <p>{{tagInput}}</p>
+  </ion-card-content>
+</ion-card>

+ 36 - 0
tailor-app/myapp/src/app/tag-input/tag-input.component.scss

@@ -0,0 +1,36 @@
+.input-container {
+  display: flex;
+  align-items: center;
+  gap: 10px; 
+  margin-bottom: 10px; 
+}
+
+.tag-container {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+  margin-top: 10px;
+}
+
+.tag-chip {
+  display: flex;
+  align-items: center;
+  position: relative; 
+  background-color: var(--ion-color-light);
+}
+
+.close-icon {
+  position: absolute;
+  right: 5px; 
+  top: 50%; 
+  transform: translateY(-50%); 
+  cursor: pointer;
+  font-size: 12px; 
+  color: var(--ion-color-danger); 
+}
+
+ion-chip {
+  --padding-start: 10px;
+  --padding-end: 10px;
+  --border-radius: 20px;
+}

+ 24 - 0
tailor-app/myapp/src/app/tag-input/tag-input.component.spec.ts

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

+ 39 - 0
tailor-app/myapp/src/app/tag-input/tag-input.component.ts

@@ -0,0 +1,39 @@
+import { Component, Output, EventEmitter } from '@angular/core';
+import { IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonInput, IonButton, IonChip, IonLabel, IonIcon } from '@ionic/angular/standalone';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { addIcons } from 'ionicons';
+import { close, barbellOutline, personOutline, square, alarmOutline } from 'ionicons/icons';
+@Component({
+  selector: 'app-tag-input',
+  templateUrl: './tag-input.component.html',
+  styleUrls: ['./tag-input.component.scss'],
+  standalone: true,
+  imports: [IonCard, IonCardHeader, IonCardTitle, IonCardContent, IonInput, IonButton, IonChip, IonLabel, IonIcon, CommonModule, FormsModule]
+})
+
+export class TagInputComponent {
+  tags: string[] = [];  // 标签列表
+  tagInput: string = '';  // 输入框的值
+
+  // 定义一个事件输出,类型为string[]
+  @Output() tagsChanged: EventEmitter<string[]> = new EventEmitter<string[]>();
+
+  // 添加标签方法
+  addTag() {
+    if (this.tagInput && !this.tags.includes(this.tagInput)) {
+      this.tags.push(this.tagInput);  // 将标签添加到数组
+      this.tagsChanged.emit(this.tags);  // 发射更新后的标签列表
+      this.tagInput = '';  // 清空输入框
+    }
+  }
+
+  // 删除标签方法
+  removeTag(tag: string) {
+    this.tags = this.tags.filter(t => t !== tag);  // 从标签数组中移除
+    this.tagsChanged.emit(this.tags);  // 发射更新后的标签列表
+  }
+  constructor() {
+    addIcons({ close, personOutline, barbellOutline, alarmOutline, square });
+  }
+}

+ 197 - 0
tailor-app/myapp/src/app/test-page/test-page.component.html

@@ -0,0 +1,197 @@
+<ion-header translucent="true">
+
+  <ion-toolbar>
+    <ion-title>私人定制</ion-title>
+    <ion-back-button slot="start"></ion-back-button>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <div class="content">
+
+    
+
+    <div class="module">
+      <h2>请输入您的服装风格偏好</h2>
+      <app-tag-input (tagsChanged)="onTagsChanged($event)"></app-tag-input>
+      <ion-item>
+        <ion-label position="floating">详细描述(可选,越详细计划越清晰哦!)</ion-label>
+        <ion-textarea style="margin-top: 20px;" [value]="goalDescription" (ionInput)="onGoalDescriptionChange($event)"
+          placeholder="例如:我需要纽扣..." auto-grow="true">
+        </ion-textarea>
+      </ion-item>
+    </div>
+
+   
+
+    <!-- 身体数据 -->
+    <div class="module">
+      <h3>身体数据</h3>
+      <ion-grid>
+        <ion-row>
+          <ion-col size="4">
+            <ion-item >
+              <ion-label position="floating">身高(cm)</ion-label>
+              <ion-input style="margin-top: 20px;margin-bottom: 20px;" type="number" [value]="height" (ionInput)="onHeightChange($event)" placeholder="请输入身高"
+                required>
+              </ion-input>
+            </ion-item>
+          </ion-col>
+          <ion-col size="4">
+            <ion-item [class.empty]="!weight" [class.filled]="weight">
+              <ion-label position="floating">体重(kg)</ion-label>
+              <ion-input style="margin-top: 20px;margin-bottom: 20px;" type="number" [value]="weight" (ionInput)="onWeightChange($event)" placeholder="请输入体重"
+                required>
+              </ion-input>
+            </ion-item>
+          </ion-col>
+          <ion-col size="4">
+            <ion-item [class.empty]="!height" [class.filled]="height">
+              <ion-label position="floating">性别</ion-label>
+              <ion-select multiple="true" (ionChange)="onGenderChange($event)" cancelText="取消" okText="确认" style="margin-left: 10px;margin-bottom: 40px;">
+                <ion-select-option value="man">男</ion-select-option>
+                <ion-select-option value="woman">女</ion-select-option>
+              </ion-select>
+            </ion-item>
+          </ion-col>
+        </ion-row>
+        
+      </ion-grid>
+    </div>
+
+
+    <div class="module">
+      <h3>三围数据(可选)</h3>
+      <ion-grid>
+        
+        <ion-row>
+          <ion-col size="4">
+            <ion-item >
+              <ion-label position="floating">胸围(cm)</ion-label>
+              <ion-input style="margin-top: 20px;margin-bottom: 20px;" type="number" [value]="xiong" (ionInput)="onXiongChange($event)" placeholder="请输入胸围"
+                >
+              </ion-input>
+            </ion-item>
+          </ion-col>
+          <ion-col size="4">
+            <ion-item [class.empty]="!weight" [class.filled]="weight">
+              <ion-label position="floating">腰围(cm)</ion-label>
+              <ion-input style="margin-top: 20px;margin-bottom: 20px;" type="number" [value]="yao" (ionInput)="onYaoChange($event)" placeholder="请输入腰围"
+                >
+              </ion-input>
+            </ion-item>
+          </ion-col>
+          <ion-col size="4">
+            <ion-item [class.empty]="!height" [class.filled]="height">
+              <ion-label position="floating">臀围(cm)</ion-label>
+              <ion-input style="margin-top: 20px;margin-bottom: 20px;" type="number" [value]="tun" (ionInput)="onTunChange($event)" placeholder="请输入臀围" >
+              </ion-input>
+            </ion-item>
+          </ion-col>
+        </ion-row>
+      </ion-grid>
+    </div>
+
+
+    <div class="module">
+      <h3>偏爱颜色(可选)</h3>
+      <ion-grid>
+        
+        <ion-row>
+          
+          <ion-col size="12">
+            <div class="dot-container">
+              <div 
+                class="dot-wrapper" 
+                *ngFor="let color of colors; let i = index">
+                <div 
+                  class="dot" 
+                  [ngStyle]="{'background-color': color}" 
+                  [ngClass]="{'selected': selectedIndex === i}" 
+                  (click)="selectDot(i)">
+                </div>
+                <span class="color-name">{{ colorNames[i] }}</span>
+              </div>
+            </div>
+          </ion-col>
+        </ion-row>
+      </ion-grid>
+    </div>
+    
+    <ion-button expand="full" (click)="create()" style="margin-bottom: 30px;">获取所有输入</ion-button>
+
+
+
+
+    
+      <ion-grid>
+    
+        <ion-row>
+          <ion-card style="width: 100%;min-height: 300px;">
+            <ion-card-header>
+              服装细节
+          </ion-card-header>
+            <div style="margin: 20px;">
+             
+              @if(!isComplete){
+              {{gpt}}}
+              @if(isComplete){
+              <fm-markdown-preview class="content-style" [content]="gpt"></fm-markdown-preview>
+              }
+            </div>
+          </ion-card>
+        </ion-row>
+    
+      </ion-grid>
+    
+    
+    
+    
+    
+    
+        <ion-grid>
+    
+          <ion-row>
+            <ion-card style="width: 100%;min-height: 300px;">
+              <ion-card-header>
+                样图展示
+            </ion-card-header>
+              <div style="margin: 20px;">
+                
+                @if(images.length) {
+                  @for(imageUrl of images;track imageUrl){
+                  <img [src]="imageUrl" alt="" srcset="">
+                  }
+                  
+                  }
+              </div>
+            </ion-card>
+          </ion-row>
+        
+      
+        </ion-grid>
+    
+    
+    <!-- 生成计划按钮 -->
+    <!-- <ion-button expand="full" (click)="generatePlan()" [disabled]="isGenerating">生成健身计划</ion-button> -->
+
+    <!-- 显示健身计划结果 -->
+    <!-- <div *ngIf="generatedPlan" class="result-container">
+      <h3>您的健身计划:</h3>
+      <ion-card>
+        <ion-card-header>
+          <ion-card-title>健身计划内容</ion-card-title>
+        </ion-card-header>
+        <ion-card-content>
+          <div *ngIf="isGenerating">
+            <ion-spinner name="crescent"></ion-spinner>
+            <p>生成中...</p>
+          </div>
+          <div *ngIf="!isGenerating">
+            <fm-markdown-preview class="content-style" [content]="generatedPlan"></fm-markdown-preview>
+          </div>
+        </ion-card-content>
+      </ion-card>
+    </div> -->
+  </div>
+</ion-content>

+ 66 - 0
tailor-app/myapp/src/app/test-page/test-page.component.scss

@@ -0,0 +1,66 @@
+.content {
+  padding: 8px;
+}.dot-container {
+  display: flex; /* 使用 flexbox 来排列圆点 */
+  justify-content: center; /* 水平居中 */
+  align-items: flex-start; /* 垂直对齐到顶部 */
+  margin-top: 20px; /* 上边距 */
+}
+
+.dot-wrapper {
+  display: flex;
+  flex-direction: column; /* 垂直方向排列 */
+  align-items: center; /* 水平居中 */
+  margin: 0 10px; /* 圆点之间的间距 */
+}
+
+.dot {
+  width: 20px; /* 圆点的宽度 */
+  height: 20px; /* 圆点的高度 */
+  border-radius: 50%; /* 圆角设置为 50% 使其成为圆形 */
+  cursor: pointer; /* 鼠标悬停时显示为可点击 */
+}
+
+.selected {
+  border: 2px solid blue; /* 选中时的蓝色边框 */
+}
+
+.color-name {
+  text-align: center; /* 文本居中 */
+  margin-top: 5px; /* 圆点和颜色名称之间的间距 */
+}
+h2 {
+  margin-bottom: 8px;
+  font-size: 1.5em;
+}
+h3
+{
+   margin-bottom: 8px;
+  font-size: 1.2em; 
+}
+.module {
+  margin-bottom: 8px;
+  padding: 8px;
+  background-color: #f9f9f9;
+  border-radius: 8px;
+  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
+}
+ion-grid {
+  margin-bottom: 8px;
+}
+ion-label{
+    transition: transform 0.2s ease;
+}
+ion-input {
+  --padding-top: 20px;
+  --padding-bottom: 2px; 
+  font-size: 14px;
+}
+
+ion-col {
+  padding: 0 8px;
+}
+ion-button {
+  margin-top: 20px;
+  --background: #3880ff;
+}

+ 22 - 0
tailor-app/myapp/src/app/test-page/test-page.component.spec.ts

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

+ 169 - 0
tailor-app/myapp/src/app/test-page/test-page.component.ts

@@ -0,0 +1,169 @@
+import { Component, OnInit } from '@angular/core';
+import { IonicModule, ToastController } from '@ionic/angular';
+import { FormsModule } from '@angular/forms';
+import { CommonModule } from '@angular/common';
+import { TagInputComponent } from '../tag-input/tag-input.component';
+import { addIcons } from 'ionicons';
+import { barbellOutline, personOutline, square, alarmOutline, arrowBack } from 'ionicons/icons';
+import { DalleOptions, FmodeChatCompletion, ImagineWork, MarkdownPreviewModule } from 'fmode-ng';
+import { LoadingController } from '@ionic/angular';
+import { AlertController, IonThumbnail, IonBadge, IonButton, IonButtons, IonCard, IonCardContent, IonCardHeader, IonCardSubtitle, IonCardTitle, IonCol, IonContent, IonGrid, IonHeader, IonIcon, IonImg, IonInput, IonItem, IonLabel, IonList, IonRow, IonSegment, IonSegmentButton, IonSelect, IonSelectOption, IonToolbar, IonTitle, IonSpinner, IonTextarea } from '@ionic/angular/standalone';
+import { NavController } from '@ionic/angular';
+import { Router } from '@angular/router';
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
+import { IonBackButton } from '@ionic/angular/standalone';
+@Component({
+  selector: 'app-test-page',
+  templateUrl: './test-page.component.html',
+  styleUrls: ['./test-page.component.scss'],
+  standalone: true,
+  imports: [
+    IonTextarea,
+    IonSpinner,
+    IonTitle,
+    IonBackButton,
+    MarkdownPreviewModule,
+    IonSelectOption,
+    IonSelect,
+    IonThumbnail,
+    IonCardSubtitle,
+    IonImg,
+    IonCard,
+    IonCardTitle,
+    IonCardHeader,
+    IonCardContent,
+    IonButtons,
+    IonItem,
+    IonList,
+    IonIcon,
+    FormsModule,
+    CommonModule,
+    IonHeader,
+    IonToolbar,
+    IonContent,
+    IonSegment,
+    IonSegmentButton,
+    IonGrid,
+    IonRow,
+    IonCol,
+    IonButton,
+    IonLabel,
+    IonBadge,
+    IonInput,
+    TagInputComponent
+  ]  // 添加 CommonModule
+})
+export class TestPageComponent implements OnInit {
+  selectedTags: string[] | undefined;
+  gender: any;
+  ngOnInit(): void {
+  }
+  height: number | null = null;   // 身高
+  weight: number | null = null;    // 体重
+  xiong: number | null = null;     // 胸围
+  yao: number | null = null;       // 腰围
+  tun: number | null = null;       // 臀围
+  goalDescription: string = '';     // 目标描述
+  selectedIndex: number | null = null; // 选中的颜色索引
+  colors: string[] = ['red', 'green', 'blue', 'yellow', 'orange'];
+  colorNames: string[] = ['红色', '绿色', '蓝色', '黄色', '橙色'];
+
+  onHeightChange(event: any) {
+    this.height = event.target.value;
+  }
+
+  onWeightChange(event: any) {
+    this.weight = event.target.value;
+  }
+
+
+  onXiongChange(event: any) {
+    this.xiong = event.target.value;
+  }
+
+  onYaoChange(event: any) {
+    this.yao = event.target.value;
+  }
+
+  onTunChange(event: any) {
+    this.tun = event.target.value;
+  }
+
+  onGoalDescriptionChange(event: any) {
+    this.goalDescription = event.target.value;
+  }
+
+  selectDot(index: number) {
+    this.selectedIndex = index;
+    console.log('Selected color index:', index);
+    console.log('Selected color:', this.colors[index]);
+  }
+
+  getAllInputs() {
+    const inputs = {
+      height: this.height,
+      weight: this.weight,
+      xiong: this.xiong,
+      yao: this.yao,
+      tun: this.tun,
+      goalDescription: this.goalDescription,
+      selectedColor: this.selectedIndex !== null ? this.colors[this.selectedIndex] : null, // 如果没有选择,则为 null
+    };
+    console.log('All inputs:', inputs);
+    return inputs;
+  }
+  onTagsChanged(tags: string[]) {
+    this.selectedTags = tags;
+    console.log('当前标签:', this.selectedTags);
+  }
+  onGenderChange(ev: any) {
+    this.gender = ev.target.value;
+
+  }
+
+  gpt: string = ""
+  imagineWork: ImagineWork | undefined
+  images: Array<string> = []
+  isComplete:boolean=false
+  test(){
+    console.log(this.selectedTags);
+    
+  }
+  createImg() {
+    this.imagineWork = new ImagineWork();
+    let options: DalleOptions = { prompt: this.gpt }
+    this.imagineWork.draw(options).subscribe(async work => {
+      console.log("imagineWork", work?.toJSON())
+      console.log("images", work?.get("images"))
+      if (work?.get("images")?.length) {
+        this.images = work?.get("images");
+      }
+    })
+  }
+
+  create() {
+      let promt = `你作为一名专业的服装设计师,风格是:${this.selectedTags},描述是${this.goalDescription},
+    身高是${this.height},体重是${this.height},性别是${this.gender},胸围是${this.xiong},腰围是${this.yao},臀围是${this.tun}
+    爱好颜色是${this.selectedIndex !== null ? this.colors[this.selectedIndex] : null},请你联想并补充服装细节
+`
+
+    let completion = new FmodeChatCompletion([
+        { role: "system", content: "" },
+        { role: "user", content: promt }
+      ])
+    completion.sendCompletion().subscribe((message: any) => {
+
+        // 打印消息体
+        console.log(message.content)
+
+
+        // 赋值消息内容给组件内属性
+        this.gpt = message.content
+        if (message?.complete) {
+          this.isComplete=true
+          this.createImg()
+        }
+      }
+      )
+    }
+}

+ 0 - 29
tailor-app/myapp/src/app/test/test.component.html

@@ -9,35 +9,6 @@
 <ion-content [fullscreen]="true">
 
   
-  <div>
-    <ion-card id="designers">
-      <ion-card-header>
-        <ion-card-title>设计师</ion-card-title>
-        <ion-card-subtitle>设计师简介</ion-card-subtitle>
-      </ion-card-header>
-      <ion-card-content style="padding: 5px;">
-        <ion-list>
-          <ion-item *ngFor="let doctor of doctorList" lines="none">
-            <ion-thumbnail slot="start">
-              <img [src]="doctor.get('avater') || '../../assets/img/doctor7.png'" [alt]="doctor.get('name')" />
-            </ion-thumbnail>
-            <div class="doctor-info" style="width: 100px;">
-              <h3>{{ doctor.get('name') }}</h3>
-              <p>{{ doctor.get('title') }}</p>
-              <p>({{ doctor.get('age') }}岁)</p>
-            </div>
-            <div style="margin-left: 10px;">
-              <ion-button shape="round" size="small" (click)="openInquiry(doctor)"><ion-icon name="dice-outline"
-                  style="margin-right: 5px;"></ion-icon> 立刻定制</ion-button>
-
-            </div>
-          </ion-item>
-        </ion-list>
-      </ion-card-content>
-    </ion-card>
-  </div>
-
-
 
 
 

+ 0 - 11
tailor-app/myapp/src/app/test/test.component.ts

@@ -53,22 +53,11 @@ export class TestComponent  implements OnInit  {
 
   }
   ngOnInit(): void {
-    this.loadDoctorList()
 
   }
-async loadDoctorList() {
-    let query = new CloudQuery("Designer");
-    query.include("depart")
-    this.doctorList = await query.find()
-    console.log(this.doctorList);
 
 
 
-  }
-  doctorList: Array<CloudObject> = []
-  async openInquiry(doctor: CloudObject) {}
-
-
 
 
 

+ 59 - 0
tailor-app/myapp/src/app/tianqi/tianqi.component.html

@@ -0,0 +1,59 @@
+
+<ion-header>
+    <ion-toolbar>
+      <!-- <ion-button >
+        Click me
+      </ion-button> -->
+      <ion-back-button slot="start"></ion-back-button>
+    </ion-toolbar>
+  </ion-header>
+  <ion-content>
+  
+<ion-content>
+    
+<div class="box1">
+    <div class="box2">
+        <div>
+            <span id="crtp" class="span1">{{lives[0].temperature+ "°"}}</span>
+            <span id="interval_tp" class="span2">{{juti}}</span>
+        </div>
+        <span class="span3"><i class="layui-icon layui-icon-location" style="color:#65B0FF">{{lives[0].city}}</i><span id="coordinate"></span></span>
+    </div>
+    <div class="box3">
+        <span id="crdate" class="span4">{{time}}</span>
+        <div class="box4">
+            <img src="assets/tianqi.jpg">
+        </div>
+        <span id="weekday" class="span5">{{yulu}}</span>
+    </div>
+</div>
+<ion-button expand="block" style="width: 90%;display: block;margin: auto;margin-top: 20px;margin-bottom: 20px;" (click)="aa()">点我生成今天的推荐穿搭吧</ion-button>
+
+<ion-card>
+    <ion-card-header>
+        今日最推荐穿搭
+    </ion-card-header>
+    <ion-card-content>
+        @if(progress!=0 && progress!=1){
+            <ion-progress-bar  [value]="progress"></ion-progress-bar>
+
+          }
+          @if(!Complete){
+          {{gptre}}}
+          @if(Complete){
+          <fm-markdown-preview class="content-style" [content]="gptre"></fm-markdown-preview>
+          }
+    </ion-card-content>
+</ion-card>
+<ion-card style="min-height: 300px;" >
+    <ion-card-header>
+        样图展示
+    </ion-card-header>
+    @if(images.length) {
+        @for(imageUrl of images;track imageUrl){
+        <img style="max-width: 100%;margin: auto;" [src]="imageUrl" alt="" srcset="">
+        }
+        
+        }
+</ion-card>
+</ion-content>

+ 104 - 0
tailor-app/myapp/src/app/tianqi/tianqi.component.scss

@@ -0,0 +1,104 @@
+body {
+    background-color: aqua;
+}
+ 
+img {
+    max-width: 72px;
+    height: auto;
+}
+ 
+* {
+    box-sizing: border-box;
+    border-width: 0;
+    border-style: solid;
+    border-color: #e5e7eb;
+}
+
+.box1 {
+    margin: auto;
+    margin-top: 20px;
+    box-shadow: rgba(50, 50, 93, 0.25) 0px 2px 5px -1px, rgba(0, 0, 0, 0.3) 0px 1px 3px -1px;
+    display: flex;
+    position: relative;
+    width: 90%;
+    margin-right: 1.5rem;
+    background-color: white;
+    border-radius: 15px;
+    justify-content: space-between;
+    padding: 1.5rem;
+}
+ 
+.box2 {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-start;
+    justify-content: space-between;
+}
+ 
+.box3 {
+    display: flex;
+    flex-direction: column;
+    align-items: flex-end;
+}
+ 
+.box4 {
+    position: relative;
+    width: 72px;
+    margin-bottom: 0.75rem;
+}
+ 
+.span1 {
+    display: block;
+    font-size: 1.875rem;
+    line-height: 2.25rem;
+    margin-bottom: 0.25rem;
+    font-weight: 600;
+}
+ 
+.span2 {
+    --tw-text-opacity: 1;
+    color: rgb(102 102 102 / var(--tw-text-opacity));
+    font-size: 0.875rem;
+    line-height: 1.25rem;
+}
+ 
+.span3 {
+    display: flex;
+    align-items: center;
+}
+ 
+.span4 {
+    --tw-text-opacity: 1;
+    color: rgb(102 102 102 / var(--tw-text-opacity));
+    font-size: 0.875rem;
+    line-height: 1.25rem;
+    margin-bottom: 10px;
+}
+ 
+.span4 {
+    --tw-text-opacity: 1;
+    color: rgb(102 102 102 / var(--tw-text-opacity));
+    font-size: 0.875rem;
+    line-height: 1.25rem;
+}
+
+ion-button {
+    --background: #000;
+    --background-hover: #9ce0be;
+    --background-activated: #88f4be;
+    --background-focused: #88f4be;
+  
+    --color: #ecc422;
+  
+    --border-radius: 15px;
+    --border-color: #000;
+    --border-style: solid;
+    --border-width: 1px;
+  
+    --box-shadow: 0 2px 6px 0 rgb(0, 0, 0, 0.25);
+  
+    --ripple-color: deeppink;
+  
+    --padding-top: 10px;
+    --padding-bottom: 10px;
+  }

+ 22 - 0
tailor-app/myapp/src/app/tianqi/tianqi.component.spec.ts

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

+ 254 - 0
tailor-app/myapp/src/app/tianqi/tianqi.component.ts

@@ -0,0 +1,254 @@
+import { Component, OnInit } from '@angular/core';
+import { HttpClient } from '@angular/common/http';
+import { IonButton, IonCard, IonCardContent, IonCardHeader, IonContent, IonProgressBar, LoadingController } from '@ionic/angular/standalone';
+import { DalleOptions, FmodeChatCompletion, ImagineWork } from 'fmode-ng';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { NavigationExtras, Router } from '@angular/router';
+
+import { IonCardSubtitle,  IonTextarea, ModalController } from '@ionic/angular/standalone';
+import {  IonHeader, IonTitle, IonToolbar,  IonLabel, IonItem, IonList, IonBackButton, IonButtons, IonIcon, IonItemDivider, IonAvatar, IonThumbnail, IonItemOptions, IonItemOption, IonItemSliding, IonInput, IonCheckbox, IonRadio, IonToggle, IonRadioGroup, IonSearchbar, IonSegment, IonSegmentButton, IonDatetime, IonFooter,  IonCardTitle,  IonCol, IonRow, IonGrid, IonChip, IonImg } from '@ionic/angular/standalone';
+import { EditTagComponent } from '../edit-tag/edit-tag.component';
+import {  MarkdownPreviewModule } from 'fmode-ng';
+import { ChatPanelOptions, FmChatModalInput, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
+import { Icon } from 'ionicons/dist/types/components/icon/icon';
+import { camera, trendingUpOutline, sparklesOutline, cloudyOutline, diceOutline } from 'ionicons/icons';
+import { addIcons } from 'ionicons';
+import { CloudObject, CloudQuery, CloudUser } from 'src/lib/ncloud';
+import { SwiperComponent } from '../swiper/swiper.component';
+import { extactAndParseJsonFromString } from 'src/agent/agent.json';
+import { RouterModule } from '@angular/router';
+import { NavController } from '@ionic/angular';
+import { AlertController } from '@ionic/angular';
+import { openUserLoginModal } from 'src/lib/user/modal-user-login/modal-user-login.component';
+import { openCommentPostModal } from 'src/lib/user/modal-comment-post/modal-comment-post.component';
+@Component({
+  selector: 'app-tianqi',
+  templateUrl: './tianqi.component.html',
+  styleUrls: ['./tianqi.component.scss'],
+  standalone: true,
+  imports:[IonButton,IonContent,IonCard,IonCardContent,IonCardHeader,IonProgressBar,IonProgressBar,IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, IonButton, IonInput, EditTagComponent, IonTextarea, IonItem, IonList, MarkdownPreviewModule, IonIcon, IonContent, IonHeader, IonTitle, IonToolbar, CommonModule, FormsModule, IonButton, IonLabel, IonLabel, IonList, IonItem, IonBackButton, IonButtons, IonIcon, IonItemDivider, IonAvatar, IonThumbnail, IonItemOptions, IonItemSliding, IonItemOption, IonItemOptions, IonInput, IonCheckbox, IonRadio, IonToggle, IonRadioGroup, IonSearchbar, IonSegment, IonSegmentButton, IonDatetime, IonFooter, IonCardContent, IonCardTitle, IonCardHeader, IonCard, IonCol, IonRow, IonGrid, IonChip, IonImg, IonCardSubtitle, SwiperComponent, RouterModule,]
+})
+export class TianqiComponent  implements OnInit {
+  time: string="";
+  yulu: string="";
+  livesAll={
+    "status": "1",
+    "count": "1",
+    "info": "OK",
+    "infocode": "10000",
+    "forecasts": [
+        {
+            "city": "南昌市",
+            "adcode": "360100",
+            "province": "江西",
+            "reporttime": "2024-12-19 16:02:45",
+            "casts": [
+                {
+                    "date": "2024-12-19",
+                    "week": "4",
+                    "dayweather": "晴",
+                    "nightweather": "晴",
+                    "daytemp": "9",
+                    "nighttemp": "-2",
+                    "daywind": "东北",
+                    "nightwind": "东北",
+                    "daypower": "1-3",
+                    "nightpower": "1-3",
+                    "daytemp_float": "9.0",
+                    "nighttemp_float": "-2.0"
+                },
+                {
+                    "date": "2024-12-20",
+                    "week": "5",
+                    "dayweather": "晴",
+                    "nightweather": "晴",
+                    "daytemp": "12",
+                    "nighttemp": "0",
+                    "daywind": "东北",
+                    "nightwind": "东北",
+                    "daypower": "1-3",
+                    "nightpower": "1-3",
+                    "daytemp_float": "12.0",
+                    "nighttemp_float": "0.0"
+                },
+                {
+                    "date": "2024-12-21",
+                    "week": "6",
+                    "dayweather": "多云",
+                    "nightweather": "多云",
+                    "daytemp": "12",
+                    "nighttemp": "1",
+                    "daywind": "东北",
+                    "nightwind": "东北",
+                    "daypower": "1-3",
+                    "nightpower": "1-3",
+                    "daytemp_float": "12.0",
+                    "nighttemp_float": "1.0"
+                },
+                {
+                    "date": "2024-12-22",
+                    "week": "7",
+                    "dayweather": "多云",
+                    "nightweather": "阴",
+                    "daytemp": "9",
+                    "nighttemp": "2",
+                    "daywind": "东",
+                    "nightwind": "东",
+                    "daypower": "1-3",
+                    "nightpower": "1-3",
+                    "daytemp_float": "9.0",
+                    "nighttemp_float": "2.0"
+                }
+            ]
+        }
+    ]
+};
+  juti: string="";
+  loading: HTMLIonLoadingElement | null = null; 
+
+  constructor(private http: HttpClient,private loadingController: LoadingController,) {}
+
+  getAdcodeAndWeather() {
+    // 第一个请求,获取 adcode
+    this.http.get('https://restapi.amap.com/v3/ip?output=json&key=555ac0f9311d59145cdec7db32e27528')
+      .subscribe((response: any) => {
+        const adcode = response.adcode; // 获取 adcode 字段
+        if (adcode) {
+          this.getWeather(adcode); // 使用 adcode 获取天气信息
+        } else {
+          console.error('adcode not found in response:', response);
+        }
+      }, error => {
+        console.error('Error fetching adcode:', error);
+      });
+  }
+
+  getWeather(adcode: string) {
+    // 第二个请求,获取天气信息
+    const weatherUrl = `https://restapi.amap.com/v3/weather/weatherInfo?city=${adcode}&key=555ac0f9311d59145cdec7db32e27528`;
+    this.http.get(weatherUrl)
+      .subscribe((response: any) => {
+        this.lives = response.lives; // 获取 lives 数组
+        console.log('Weather lives data:', this.lives);
+        this.getAll(adcode)
+      }, error => {
+        console.error('Error fetching weather data:', error);
+      });
+  }
+  getAll(adcode: string){
+    const urlWeatherAll = "https://restapi.amap.com/v3/weather/weatherInfo?city=" +adcode + "&extensions=all&key=555ac0f9311d59145cdec7db32e27528";
+    this.http.get(urlWeatherAll)
+    .subscribe((response: any) => {
+      this.livesAll = response; // 获取 lives 数组
+      console.log('Weather livesAll data:', this.livesAll);
+      
+    }, error => {
+      console.error('Error fetching weather data:', error);
+    });
+  }
+  lives=[
+    {
+        "province": "江西",
+        "city": "南昌市",
+        "adcode": "360100",
+        "weather": "晴",
+        "temperature": "8",
+        "winddirection": "东北",
+        "windpower": "≤3",
+        "humidity": "39",
+        "reporttime": "2024-12-19 15:37:35",
+        "temperature_float": "8.0",
+        "humidity_float": "39.0"
+    }
+]
+  ngOnInit() {
+    // this.getAdcodeAndWeather()
+      const dateMap = ["周天","周一","周二","周三","周四","周五","周六"];
+      const weekdayMap = ["零","一","两","三","四","五"];
+      const urlIp = "https://restapi.amap.com/v3/ip?output=json&key=你的Key";
+      const date = new Date();
+      this.time=date.getMonth() + 1 + "-" + date.getDate() + " " + dateMap[date.getDay()]
+      if(date.getDay() == 6 || date.getDay() == 0){
+        this.yulu="好好享受周末吧~"
+      }
+      else{
+        this.yulu="再坚持" + weekdayMap[(6 - date.getDay())] + "天就到周末啦~"
+      }
+      const weather_log = this.lives[0]["weather"];
+      const daytemp = this.livesAll["forecasts"][0]["casts"][0]["daytemp"];
+      const nighttemp = this.livesAll["forecasts"][0]["casts"][0]["nighttemp"];
+      this.juti=nighttemp + "° " + "/ " + daytemp + "°" + "\xa0\xa0\xa0" + weather_log
+      console.log(this.juti);
+      
+    
+  }
+  public gptre:string = "";
+  public progress = 0;
+  public Complete=false
+  async aa(){
+    this.loading = await this.loadingController.create({
+      message: '生成计划中...',
+      // duration: 30000 // 加载时长可以根据实际需求调整
+    });
+
+    await this.loading.present();
+
+    let prompt = `
+      现在的温度是${this.lives[0].temperature+ "°"},
+      今天的温度范围和天气是${this.juti},请你作为一名专业服装设计师,
+      向我推荐适合今日的衣服和裤子,并联想补充衣服和裤子的具体细节
+        `;
+    
+        let completion = new FmodeChatCompletion([
+          { role: "system", content: "" },
+          { role: "user", content: prompt }
+        ]);
+        completion.sendCompletion().subscribe(
+          (message: any) => {
+          this.gptre=message.content
+            console.log("Received message:", message);
+            if(this.progress<0.97){
+              if(this.progress<0.5){
+                this.progress+=0.002
+              }
+              if(this.progress>=0.8){
+                this.progress+=0.001
+              }
+            }
+            if (message?.complete) {
+              this.Complete=true
+    
+              console.log("完成");
+              this.createImage()
+            } 
+          }
+        )
+  }
+imagineWork: ImagineWork | undefined
+  images: Array<string> = []
+  createImage(){
+    this.imagineWork = new ImagineWork();
+            let options: DalleOptions = { prompt: this.gptre }
+            this.imagineWork.draw(options).subscribe(async work => {
+              if(this.progress<0.5){
+                this.progress+=0.01
+              }
+              if(this.progress>=0.8){
+                this.progress+=0.001
+              }
+              console.log("imagineWork", work?.toJSON())
+              console.log("images", work?.get("images"))
+              if (work?.get("images")?.length) {
+                if (this.loading) {
+                  await this.loading.dismiss();
+                  this.loading = null; // 清空 loading 实例
+                }
+              this.progress=0
+
+                this.images = work?.get("images");
+                this.progress=0}
+  })}
+
+
+}

BIN
tailor-app/myapp/src/assets/img/quick.png


BIN
tailor-app/myapp/src/assets/img/tianqi.png


BIN
tailor-app/myapp/src/assets/tianqi.jpg


+ 3 - 0
tailor-app/myapp/src/index.html

@@ -27,3 +27,6 @@
  <!--Swiper-->
  <link rel="stylesheet" href="./assets/dist/css/swiper.min.css">
  <script src="./assets/dist/js/swiper.min.js"></script>
+ <link rel="stylesheet" href="https://www.layuicdn.com/layui-v2.5.6/css/layui.css">
+ <script src="https://www.layuicdn.com/layui-v2.5.6/layui.js"></script>
+ <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.7.1/jquery.min.js"></script>

+ 81 - 23
tailor-app/myapp/src/lib/ncloud.ts

@@ -2,8 +2,8 @@
 export class CloudObject {
     className: string;
     id: string | null = null;
-    createdAt:any;
-    updatedAt:any;
+    createdAt: any;
+    updatedAt: any;
     data: Record<string, any> = {};
 
     constructor(className: string) {
@@ -88,7 +88,7 @@ export class CloudQuery {
         this.className = className;
     }
 
-    include(...fileds:string[]) {
+    include(...fileds: string[]) {
         this.queryParams["include"] = fileds;
     }
     greaterThan(key: string, value: any) {
@@ -112,6 +112,10 @@ export class CloudQuery {
     }
 
     equalTo(key: string, value: any) {
+        // 确保 this.queryParams["where"] 已经初始化为对象
+        if (!this.queryParams["where"]) {
+            this.queryParams["where"] = {};  // 防止未初始化的情况
+        }
         this.queryParams["where"][key] = value;
     }
 
@@ -137,19 +141,19 @@ export class CloudQuery {
         let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}?`;
 
         let queryStr = ``
-        Object.keys(this.queryParams).forEach(key=>{
+        Object.keys(this.queryParams).forEach(key => {
             let paramStr = JSON.stringify(this.queryParams[key]);
-            if(key=="include"){
+            if (key == "include") {
                 paramStr = this.queryParams[key]?.join(",")
             }
-            if(queryStr) {
+            if (queryStr) {
                 url += `${key}=${paramStr}`;
-            }else{
+            } else {
                 url += `&${key}=${paramStr}`;
             }
         })
         // if (Object.keys(this.queryParams["where"]).length) {
-            
+
         // }
 
         const response = await fetch(url, {
@@ -165,7 +169,7 @@ export class CloudQuery {
 
         const json = await response?.json();
         let list = json?.results || []
-        let objList = list.map((item:any)=>this.dataToObj(item))
+        let objList = list.map((item: any) => this.dataToObj(item))
         return objList || [];
     }
 
@@ -197,7 +201,7 @@ export class CloudQuery {
         return null
     }
 
-    dataToObj(exists:any):CloudObject{
+    dataToObj(exists: any): CloudObject {
         let existsObject = new CloudObject(this.className);
         existsObject.set(exists);
         existsObject.id = exists.objectId;
@@ -213,7 +217,7 @@ export class CloudUser extends CloudObject {
         super("_User"); // 假设用户类在Parse中是"_User"
         // 读取用户缓存信息
         let userCacheStr = localStorage.getItem("NCloud/dev/User")
-        if(userCacheStr){
+        if (userCacheStr) {
             let userData = JSON.parse(userCacheStr)
             // 设置用户信息
             this.id = userData?.objectId;
@@ -222,7 +226,7 @@ export class CloudUser extends CloudObject {
         }
     }
 
-    sessionToken:string|null = ""
+    sessionToken: string | null = ""
     /** 获取当前用户信息 */
     async current() {
         if (!this.sessionToken) {
@@ -246,8 +250,44 @@ export class CloudUser extends CloudObject {
         // return result;
     }
 
+     /** 获取特定用户信息 */
+     async getUserByUsername(objectId: string) {
+        const url = `http://dev.fmode.cn:1337/parse/classes/_User?where=${JSON.stringify({ objectId })}`;
+
+        const response = await fetch(url, {
+            headers: {
+                "accept": "*/*",
+                "x-parse-application-id": "dev",
+                "x-parse-master-key": "devmk", // 使用您的 Master Key
+                "Content-Type": "application/json"
+            },
+            method: "GET",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+            return null; // 返回 null 表示未找到用户或发生错误
+        }
+
+        // 处理查询结果
+        if (result.results && result.results.length > 0) {
+            const userData = result.results[0]; // 获取第一个匹配的用户
+            const user = new CloudUser();
+            user.set(userData);
+            user.id = userData.objectId;
+            user.createdAt = userData.createdAt;
+            user.updatedAt = userData.updatedAt;
+            return user; // 返回找到的用户对象
+        }
+
+        return null; // 如果没有找到用户,返回 null
+    }
+
     /** 登录 */
-    async login(username: string, password: string):Promise<CloudUser|null> {
+    async login(username: string, password: string): Promise<CloudUser | null> {
         const response = await fetch(`http://dev.fmode.cn:1337/parse/login`, {
             headers: {
                 "x-parse-application-id": "dev",
@@ -262,14 +302,14 @@ export class CloudUser extends CloudObject {
             console.error(result?.error);
             return null;
         }
-        
+
         // 设置用户信息
         this.id = result?.objectId;
         this.sessionToken = result?.sessionToken;
         this.data = result; // 保存用户数据
         // 缓存用户信息
         console.log(result)
-        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
+        localStorage.setItem("NCloud/dev/User", JSON.stringify(result))
         return this;
     }
 
@@ -328,7 +368,7 @@ export class CloudUser extends CloudObject {
         // 设置用户信息
         // 缓存用户信息
         console.log(result)
-        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
+        localStorage.setItem("NCloud/dev/User", JSON.stringify(result))
         this.id = result?.objectId;
         this.sessionToken = result?.sessionToken;
         this.data = result; // 保存用户数据
@@ -338,20 +378,20 @@ export class CloudUser extends CloudObject {
     override async save() {
         let method = "POST";
         let url = `http://dev.fmode.cn:1337/parse/users`;
-    
+
         // 更新用户信息
         if (this.id) {
             url += `/${this.id}`;
             method = "PUT";
         }
-    
-        let data:any = JSON.parse(JSON.stringify(this.data))
+
+        let data: any = JSON.parse(JSON.stringify(this.data))
         delete data.createdAt
         delete data.updatedAt
         delete data.ACL
         delete data.objectId
         const body = JSON.stringify(data);
-        let headersOptions:any = {
+        let headersOptions: any = {
             "content-type": "application/json;charset=UTF-8",
             "x-parse-application-id": "dev",
             "x-parse-session-token": this.sessionToken, // 添加sessionToken以进行身份验证
@@ -363,7 +403,7 @@ export class CloudUser extends CloudObject {
             mode: "cors",
             credentials: "omit"
         });
-    
+
         const result = await response?.json();
         if (result?.error) {
             console.error(result?.error);
@@ -371,7 +411,25 @@ export class CloudUser extends CloudObject {
         if (result?.objectId) {
             this.id = result?.objectId;
         }
-        localStorage.setItem("NCloud/dev/User",JSON.stringify(this.data))
+        localStorage.setItem("NCloud/dev/User", JSON.stringify(this.data))
         return this;
     }
-}
+}
+
+
+
+// fetch("http://dev.fmode.cn:1337/parse/classes/_User?where={%22username%22:%22chengnan%22}", {
+//     "headers": {
+//       "accept": "*/*",
+//       "accept-language": "zh-CN,zh;q=0.9",
+//       "if-none-match": "W/\"fe-JIlchyuW4LZub+2xCTNV/QiJ0wc\"",
+//       "x-parse-application-id": "dev",
+//       "x-parse-master-key": "devmk"
+//     },
+//     "referrer": "http://localhost:4040/",
+//     "referrerPolicy": "strict-origin-when-cross-origin",
+//     "body": null,
+//     "method": "GET",
+//     "mode": "cors",
+//     "credentials": "omit"
+//   });

+ 1 - 0
tailor-app/myapp/src/lib/user/modal-user-login/modal-user-login.component.ts

@@ -44,6 +44,7 @@ export class ModalUserLoginComponent  implements OnInit {
     }
     let user:any = new CloudUser();
     user = await user.login(this.username,this.password);
+    
     if(user?.id){
        this.modalCtrl.dismiss(user,"confirm")
     }else{

BIN
tailor-prod/202226701001 李铭沣 实验11.docx