yukilaaar 6 ماه پیش
والد
کامیت
0c0acc09c9

+ 37 - 13
travel-app/src/app/tab2/tab2.page.html

@@ -1,17 +1,41 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 2
-    </ion-title>
+<ion-header>
+  <ion-toolbar class="custom-toolbar">
+    <ion-title class="ion-text-center">AI助手</ion-title>
   </ion-toolbar>
 </ion-header>
 
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 2</ion-title>
-    </ion-toolbar>
-  </ion-header>
+<ion-content class="custom-content">
+  <ion-card class="input-card">
+    <ion-card-content>
+      <ion-input
+        class="custom-input"
+        name="text"
+        type="text"
+        [(ngModel)]="page2"
+        placeholder="时间"
+        (focus)="clearPlaceholder()"
+      ></ion-input>
+    </ion-card-content>
+  </ion-card>
 
-  <app-explore-container name="Tab 2 page"></app-explore-container>
-</ion-content>
+  <!-- “请输入您的需求”盒子 -->
+  <ion-card class="input-card">
+    <ion-card-content>
+      <ion-textarea
+        [(ngModel)]="userPrompt"
+        placeholder="请输入文本提示词"
+        autoGrow="true"
+        class="custom-textarea"
+      ></ion-textarea>
+    </ion-card-content>
+  </ion-card>
+
+  <!-- 按钮:执行消息生成函数 -->
+  <ion-button (click)="sendMessage()" expand="block" class="custom-button">
+    旅游路线
+  </ion-button>
+
+  <!-- 展示:返回消息内容 -->
+  <div *ngIf="!isComplete" class="response-message">{{responseMsg}}</div>
+  <fm-markdown-preview *ngIf="isComplete" class="content-style" [content]="responseMsg"></fm-markdown-preview>
+</ion-content>

+ 41 - 0
travel-app/src/app/tab2/tab2.page.scss

@@ -0,0 +1,41 @@
+.custom-toolbar {
+    --background: #D6CCC2; // 设置页眉背景颜色为紫色
+    --color: white; // 设置页眉文字颜色为白色
+  }
+  
+  .custom-toolbar ion-title {
+    font-size: 1.5rem; // 可以根据需要调整字体大小
+  }
+  ion-content.custom-content {
+    --background: #F5EBE0; // 替换#your-color-code为你想要的颜色
+  }
+  
+  .custom-button {
+    --background: #d0a87d; // 替换#your-color-code为你想要的颜色
+  }
+
+
+.input {
+  width: 100%;
+  max-width: 220px;
+  height: 45px;
+  padding: 12px;
+  border-radius: 12px;
+  border: 1.5px solid lightgrey;
+  outline: none;
+  transition: all 0.3s cubic-bezier(0.19, 1, 0.22, 1);
+  box-shadow: 0px 0px 20px -18px;
+}
+
+.input:hover {
+  border: 2px solid lightgrey;
+  box-shadow: 0px 0px 20px -17px;
+}
+
+.input:active {
+  transform: scale(0.95);
+}
+
+.input:focus {
+  border: 2px solid grey;
+}

+ 8 - 4
travel-app/src/app/tab2/tab2.page.spec.ts

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

+ 58 - 6
travel-app/src/app/tab2/tab2.page.ts

@@ -1,16 +1,68 @@
-import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
-import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { Component, OnInit } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { IonicModule } from '@ionic/angular';
+import { FormsModule } from '@angular/forms';
+
+// 假设的AI服务,需要替换为实际的飞码AI服务连接代码
+import { FmodeChatCompletion,MarkdownPreviewModule } from 'fmode-ng';
 
 @Component({
   selector: 'app-tab2',
   templateUrl: 'tab2.page.html',
   styleUrls: ['tab2.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent]
+  imports: [
+    CommonModule,
+    IonicModule,
+    FormsModule,
+    MarkdownPreviewModule
+  ]
 })
-export class Tab2Page {
-
+export class Tab2Page implements OnInit {
+  page2: string = ""; // 用户输入的游玩时间
+  userPrompt: string = ""; // 用户输入的需求提示
+  responseMsg: string = ""; // 用于展示AI返回的消息内容
+  isComplete:boolean = false; // 定义完成状态属性,用来标记是否补全完成
   constructor() {}
 
+  ngOnInit() {}
+
+  // 更新游玩时间
+  page2Input(ev: any) {
+    this.page2 = ev.detail.value;
+  }
+
+  // 更新用户需求提示
+  promptInput(ev: any) {
+    this.userPrompt = ev.detail.value;
+  }
+
+
+  clearPlaceholder() {
+    if (this.page2 === "时间") {
+      this.page2 = ""; // 清空输入框内容
+    }
+  }
+
+  // 发送消息到AI服务并获取响应
+  sendMessage() {
+    console.log("creating AI message...");
+
+    let PromptTemplate = `您作为一名专业的南昌导游,请您根据用户描述的需求,给出旅游路线,用户游玩的时间是${this.page2}。以下是用户的口述:${this.userPrompt}`;
+
+    let completion = new FmodeChatCompletion([
+      { role: "system", content: "你是一名友好的AI助手" },
+      { role: "user", content: PromptTemplate }
+    ]);
+
+    completion.sendCompletion().subscribe((message:any)=>{
+      // 打印消息体
+      console.log(message.content)
+      // 赋值消息内容给组件内属性
+      this.responseMsg = message.content
+      if(message?.complete){ // 判断message为完成状态,则设置isComplete为完成
+        this.isComplete = true
+      }
+    })
+  }
 }

+ 80 - 16
travel-app/src/app/tab3/tab3.page.html

@@ -1,17 +1,81 @@
-<ion-header [translucent]="true">
-  <ion-toolbar>
-    <ion-title>
-      Tab 3
-    </ion-title>
-  </ion-toolbar>
-</ion-header>
-
-<ion-content [fullscreen]="true">
-  <ion-header collapse="condense">
-    <ion-toolbar>
-      <ion-title size="large">Tab 3</ion-title>
-    </ion-toolbar>
-  </ion-header>
-
-  <app-explore-container name="Tab 3 page"></app-explore-container>
+
+<ion-content class="custom-content" padding>
+  <form (ngSubmit)="publishPost()" #postForm="ngForm">
+    
+    <div class="input-container">
+      <ion-item lines="none">
+        <div class="upload-container">
+          <!-- 上传文件盒子 -->
+          <div *ngIf="selectedFiles.length === 0" class="upload-box" (click)="fileInput.click()">
+            <ion-icon name="add" class="upload-icon"></ion-icon>
+            <input type="file" #fileInput (change)="onFilesSelected($event)" accept="image/*,video/*" multiple style="display: none;" />
+          </div>
+
+          <!-- 显示已上传的文件 -->
+          
+    <ion-item *ngIf="selectedFiles.length > 0" lines="none">
+      <div *ngFor="let file of selectedFiles" style="position: relative; width: 100%; overflow: hidden; height: 100px; margin-bottom: 10px;">
+        <ng-container *ngIf="file.isImage; else videoTemplate">
+          <img [src]="file.src" style="width: auto; height: 100%; border-radius: 10px;" >
+        </ng-container>
+        <ng-template #videoTemplate>
+          <video [src]="file.src" style="width: auto; height: 100%; border-radius: 10px;" controls></video>
+        </ng-template>
+        <ion-button (click)="removeFile(file)" fill="clear" style="position: absolute; top: 5px; right: 5px;">
+          <ion-icon name="close"></ion-icon>
+        </ion-button>
+      </div>
+    </ion-item>
+  </div>
+</ion-item>
+</div>
+
+    <div class="input-container">
+      <input type="text" id="input-title" [(ngModel)]="postTitle" name="title" required>
+      <label for="input-title" class="label">标题</label>
+      <div class="underline"></div>
+    </div>
+
+    <div class="input-container">
+      <textarea id="input-content" [(ngModel)]="postContent" name="content" required></textarea>
+      <label for="input-content" class="label">正文</label>
+      <div class="underline"></div>
+    </div>
+
+    <ion-item class="custom-didian">
+      <ion-icon name="location"></ion-icon>
+      <ion-label>标记地点</ion-label>
+      <ion-select interface="popover" [(ngModel)]="location" name="location" slot="end">
+        <ion-select-option *ngFor="let city of cities" [value]="city">{{ city }}</ion-select-option>
+      </ion-select>
+    </ion-item>
+
+    <!-- 公开可见开关 -->
+    <div class="input-container">
+      <ion-item class="custom-select">
+        <ion-icon name="lock-open"></ion-icon>
+        <ion-label>可见性</ion-label>
+        <ion-select [(ngModel)]="visibility" name="publicVisibility" slot="end">
+          <ion-select-option value="none">全部可见</ion-select-option>
+          <ion-select-option value="onlyTo">仅给谁看</ion-select-option>
+          <ion-select-option value="mutualFriends">仅互关好友可见</ion-select-option>
+          <ion-select-option value="onlyMe">仅自己可见</ion-select-option>
+        </ion-select>
+      </ion-item>
+    </div>
+
+    <!-- 高级选项 -->
+    <ion-item class="custom-gaoji" lines="none">
+      <ion-label>高级选项</ion-label>
+      <ion-toggle slot="end">保存到相册</ion-toggle><br /><br />
+  </ion-item>
+
+  </form>
 </ion-content>
+
+
+<ion-footer>
+  <ion-button (click)="sendMessage()" expand="block" class="custom-button">
+    发布笔记
+  </ion-button>
+</ion-footer>

+ 146 - 0
travel-app/src/app/tab3/tab3.page.scss

@@ -0,0 +1,146 @@
+ion-content.custom-content {
+    --background: #F5EBE0; // 替换#your-color-code为你想要的颜色
+  }
+  
+  .custom-button {
+    --background: #d0a87d; // 替换#your-color-code为你想要的颜色
+  }
+  
+  ion-item {
+    --background: #F5EBE0;
+    --padding-start:5px;
+    --padding-end:5px;
+  }
+  
+  /* 输入容器 */
+  .input-container {
+    position: relative;
+    margin: 30px 0; /* 添加上下间距 */
+  }
+  
+  /* 输入框样式 */
+  .input-container input[type="text"],
+  .input-container textarea {
+    font-size: 20px;
+    width: 100%;
+    border: none;
+    border-bottom: 2px solid #ccc;
+    padding: 5px 0;
+    background-color: transparent;
+    outline: none;
+  }
+  
+  /* 标签样式 */
+  .input-container .label {
+    position: absolute;
+    top: 0;
+    left: 0;
+    color: #ccc;
+    transition: all 0.3s ease;
+    pointer-events: none;
+  }
+  
+  /* 输入框聚焦和有效时标签样式 */
+  .input-container input[type="text"]:focus ~ .label,
+  .input-container input[type="text"]:valid ~ .label,
+  .input-container textarea:focus ~ .label,
+  .input-container textarea:valid ~ .label {
+    top: -20px;
+    font-size: 16px;
+    color: #333;
+  }
+  
+  /* 下划线样式 */
+  .input-container .underline {
+    position: absolute;
+    bottom: 0;
+    left: 0;
+    height: 2px;
+    width: 100%;
+    background-color: #333;
+    transform: scaleX(0);
+    transition: all 0.3s ease;
+  }
+  
+  /* 输入框聚焦和有效时下划线样式 */
+  .input-container input[type="text"]:focus ~ .underline,
+  .input-container input[type="text"]:valid ~ .underline,
+  .input-container textarea:focus ~ .underline,
+  .input-container textarea:valid ~ .underline {
+    transform: scaleX(1);
+  }
+  
+  /* 上传容器样式 */
+  .upload-container {
+    display: flex; /* 使用 Flexbox 布局 */
+    align-items: center; /* 垂直居中对齐 */
+  }
+  
+  /* 上传盒子样式 */
+  .upload-box {
+    display: flex; /* 使用flex布局使加号居中 */
+    justify-content: center; /* 水平居中 */
+    align-items: center; /* 垂直居中 */
+    border: 2px dashed rgba(228, 232, 236, 0.7); /* 边框样式,使用透明度 */
+    border-radius: 10px; /* 圆角 */
+    padding: 20px; /* 内边距,确保加号图标不被挤压 */
+    text-align: center; /* 文字居中 */
+    background-color: rgba(242, 246, 250, 0.5); /* 背景颜色,使用透明度 */
+    cursor: pointer; /* 鼠标悬停时显示手型 */
+    transition: background-color 0.3s; /* 背景颜色过渡效果 */
+    height: 100px; /* 设置上传框的高度 */
+    margin-right: 10px; /* 盒子与图片之间的间距 */
+  }
+  
+  /* 悬停时改变背景颜色 */
+  .upload-box:hover {
+    background-color: rgba(236, 243, 244, 0.7); /* 悬停时改变背景颜色,使用透明度 */
+  }
+  
+  /* 加号图标样式 */
+  .upload-icon {
+    font-size: 50px; /* 加号图标的大小 */
+    color: #26292b; /* 加号图标的颜色 */
+  }
+  
+  /* 文件预览容器 */
+  .file-preview-container {
+    
+    flex-wrap: wrap; /* 允许换行 */
+    gap: 10px; /* 设置间距 */
+  }
+  
+  /* 文件预览样式 */
+  .file-preview {
+    position: relative; /* 使文件预览相对定位 */
+    width: 100px; /* 设置固定宽度 */
+    height: 100px; /* 设置固定高度 */
+    overflow: hidden; /* 隐藏溢出 */
+    flex: 0 0 auto; /* 不放大也不缩小,基础大小 */
+  }
+  
+  .file-image {
+    width: 100%; /* 使图片宽度适应 */
+    height: 100%; /* 使图片高度适应 */
+    object-fit: cover; /* 保持比例并裁剪超出部分 */
+    border-radius: 10px; /* 圆角 */
+  }
+  
+  .custom-select {
+    font-size: 14px; /* 设置字体大小 */
+    color: rgba(74, 72, 72, 0.7); /* 设置字体颜色为淡色 */
+    margin: -30px 0px; /* 设置可见性项的底部边距 */
+  }
+  
+  .custom-gaoji {
+    font-size: 12px; /* 设置字体大小 */
+    color: rgba(74, 72, 72, 0.7); 
+    margin: -10px 0; 
+  }
+  
+  .custom-didian {
+    font-size: 14px; /* 设置字体大小 */
+    color: rgba(74, 72, 72, 0.7); /* 设置字体颜色为淡色 */
+    margin: -30px 0px;
+  }
+  

+ 79 - 7
travel-app/src/app/tab3/tab3.page.ts

@@ -1,16 +1,88 @@
 import { Component } from '@angular/core';
-import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
-import { ExploreContainerComponent } from '../explore-container/explore-container.component';
+import { CommonModule } from '@angular/common';
+import { IonicModule } from '@ionic/angular';
+import { FormsModule } from '@angular/forms';
+import { addIcons } from 'ionicons';
+import { add,close,location,lockOpen} from 'ionicons/icons';
+
 
 @Component({
   selector: 'app-tab3',
-  templateUrl: 'tab3.page.html',
-  styleUrls: ['tab3.page.scss'],
+  templateUrl: './tab3.page.html',
+  styleUrls: ['./tab3.page.scss'],
   standalone: true,
-  imports: [IonHeader, IonToolbar, IonTitle, IonContent, ExploreContainerComponent]
+  imports: [
+    CommonModule,
+    IonicModule,
+    FormsModule,
+  ]
 })
 export class Tab3Page {
+  postTitle: string = '';
+  postContent: string = '';
+  selectedFiles: { src: string | ArrayBuffer | null, isImage: boolean }[] = []; // 用于存储多个文件信息
+  location: string = ''; // 新增属性,用于存储地点
+  isPublic: boolean = true; // 新增属性,用于存储公开可见状态
+  saveToAlbum: boolean = false; // 新增属性,用于存储是否保存到相册
+  visibility: string = 'none'; // 初始化可见性变量,默认值可以根据需求设定
+
+  cities: string[] = [
+    '北京', '上海', '广州', '深圳', '杭州', '成都', '武汉',
+    '南京', '西安', '重庆', '天津', '青岛', '沈阳', '郑州',
+    '合肥', '厦门', '福州', '哈尔滨', '长春', '济南', '南昌',
+    // ... 继续添加更多城市
+  ];
+
+  constructor() {
+    addIcons({ add,close,location,lockOpen});
+  }
+
+
+  onFilesSelected(event: Event) {
+    // 清空已选文件数组
+    this.selectedFiles = [];
+
+    const files = (event.target as HTMLInputElement).files;
+    if (files) {
+      for (let i = 0; i < files.length; i++) {
+        const file = files[i];
+        const reader = new FileReader();
+        reader.onload = () => {
+          this.selectedFiles.push({ src: reader.result, isImage: file.type.startsWith('image/') }); // 将文件信息添加到数组中
+        };
+        reader.readAsDataURL(file);
+      }
+    }
+  }
+
+  removeFile(fileToRemove: { src: string | ArrayBuffer | null, isImage: boolean }) {
+    this.selectedFiles = this.selectedFiles.filter(file => file !== fileToRemove); // 从数组中移除指定文件
+  }
 
-  constructor() {}
+  sendMessage() {}
 
-}
+  publishPost() {
+    if (this.postTitle && this.postContent) {
+      // 这里可以将帖子数据发送到服务器
+      console.log('发布帖子:', {
+        title: this.postTitle,
+        content: this.postContent,
+        files: this.selectedFiles,
+        location: this.location, // 新增属性
+        isPublic: this.isPublic, // 新增属性
+        saveToAlbum: this.saveToAlbum, // 新增属性
+        visibility: this.visibility, // 将可见性信息包含在发布的数据中
+      });
+      // 清空表单
+      this.postTitle = '';
+      this.postContent = '';
+      this.selectedFiles = [];
+      this.location = ''; // 清空地点
+      this.isPublic = true; // 重置公开可见状态
+      this.saveToAlbum = false; // 重置保存到相册状态
+      this.visibility = 'none'; // 重置可见性
+    } else {
+      console.log('标题和正文不能为空');
+    }
+  }
+}

+ 6 - 8
travel-app/src/main.ts

@@ -5,8 +5,6 @@ import { IonicRouteStrategy, provideIonicAngular } from '@ionic/angular/standalo
 import { routes } from './app/app.routes';
 import { AppComponent } from './app/app.component';
 
-
-
 // 引用HttpClient方法
 import { provideHttpClient } from '@angular/common/http';
 // 引用移动端授权检测供应器
@@ -18,17 +16,17 @@ Parse.serverURL = "https://server.fmode.cn/parse";
 localStorage.setItem("NOVA_APIG_SERVER", 'aHR0cHMlM0ElMkYlMkZzZXJ2ZXIuZm1vZGUuY24lMkZhcGklMkZhcGlnJTJG')
 
 // 注意:替换Token 根据Token设置Parse服务帐套权限
-Parse.User.become('r:E4KpGvTEto-137671720751732983628')
+Parse.User.become("r:E4KpGvTEto-139700736071734318549")
+
 
-// 'r:E4KpGvTEto-137671720751732983628'
 bootstrapApplication(AppComponent, {
   providers: [
     { provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
     provideIonicAngular(),
-    provideRouter(routes, withPreloading(PreloadAllModules)),
-    // 添加HttpClient供应器
-    provideHttpClient(),
-    // 添加Diagnostic
+     // 添加HttpClient供应器
+     provideHttpClient(),
+      // 添加Diagnostic
     Diagnostic,
+    provideRouter(routes, withPreloading(PreloadAllModules)),
   ],
 });

+ 1 - 1
travel-app/tsconfig.json

@@ -2,9 +2,9 @@
 {
   "compileOnSave": false,
   "compilerOptions": {
-    "allowSyntheticDefaultImports":true,
     "baseUrl": "./",
     "outDir": "./dist/out-tsc",
+    "allowSyntheticDefaultImports":true,
     "forceConsistentCasingInFileNames": true,
     "strict": true,
     "noImplicitOverride": true,