15949524127 1 год назад
Родитель
Сommit
8cbc4ff165

Разница между файлами не показана из-за своего большого размера
+ 655 - 19
package-lock.json


+ 1 - 0
package.json

@@ -30,6 +30,7 @@
     "@capacitor/status-bar": "6.0.2",
     "@ionic/angular": "^8.0.0",
     "chart.js": "^4.4.7",
+    "fmode-ng": "^0.0.63",
     "ionicons": "^7.2.1",
     "parse": "^5.3.0",
     "rxjs": "~7.8.0",

+ 1 - 1
src/app/models/Task.ts

@@ -2,7 +2,7 @@ import { CloudObject, CloudQuery } from '../../lib/ncloud';
 import * as Parse from 'parse';
 
 export class Task extends CloudObject {
-  protected override parseObject: Parse.Object;
+  protected parseObject: Parse.Object;
   override id: string | null = null;
   title: string = '';
   content: string = '';

+ 44 - 0
src/app/poem-picture/poem-picture.component.html

@@ -0,0 +1,44 @@
+<ion-header [translucent]="true">
+  <ion-toolbar class="custom-toolbar">
+    <ion-title class="custom-title">
+      个性化头像生成
+    </ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <!-- 生成提示词 -->
+  <ion-textarea [value]="userPrompt" (ionInput)="promptInput($event)" placeholder="请输入头像要求(示例:画风采用动漫风,一位长发飘飘的女孩抬头仰望着天空微笑。)" autoGrow="true"></ion-textarea>
+  <ion-button (click)="createImage()" expand="block" color="success">头像生成</ion-button>
+  <!-- 意境画面提示词 -->
+  <div class="desc">
+    {{PictureDescResult}}
+  </div>
+  <!-- 生成结果 -->
+   <!-- @if(!czq){
+    @for(imageUrl of images;track imageUrl){
+      <img width="100%" [src]="'../../assets/images/th.jpg'" alt="" srcset="">
+    }
+   } -->
+  @if(czq) {
+    @for(imageUrl of images;track imageUrl){
+      <img [src]="imageUrl || '../../assets/images/th.jpg'" alt="" srcset="">
+    }
+
+    <h3>图片地址:
+      <span class="copyable">{{images.join(', ')}}</span>
+    </h3>
+
+  }
+  <!-- 生成状态 -->
+  @if(!images.length){
+    <h2 style="display: grid; place-items: center;">欢迎使用头像生成器</h2>
+    @if(imagineWork.progress!=0){
+      <h1 style="display: grid; place-items: center;">生成中:{{imagineWork.progress}}</h1>
+    }
+    <div style="display: grid; place-items: center;">
+      <img src="https://app.fmode.cn/dev/jxnu/202226701019/头像生成器.png">
+    </div>
+  }
+
+</ion-content>

+ 93 - 0
src/app/poem-picture/poem-picture.component.scss

@@ -0,0 +1,93 @@
+.custom-toolbar {
+    --background: rgba(255, 255, 255, 0.8); /* 使工具栏背景透明 */
+    display: flex; /* 使用 Flexbox 布局 */
+    justify-content: center; /* 水平居中 */
+    align-items: center; /* 垂直居中 */
+    padding: 0; /* 去掉默认内边距 */
+}
+
+.custom-title {
+    font-size: 1.3em; /* 字体大小 */
+    font-weight: bold; /* 加粗 */
+    color: #000000; 
+    text-align: center; /* 文字居中对齐 */
+    margin: 0; /* 去掉默认外边距 */
+    text-shadow: 1px 1px 2px rgba(0, 0, 0, 0.2); /* 添加文字阴影效果 */
+    /* 添加其他美化效果 */
+    font-family: "微软雅黑"; /* 自定义字体 */
+}
+
+/* 全局样式 */
+ion-content {
+    background-color: #7acbd9; /* 背景颜色 */
+    padding: 20px; /* 内边距 */
+    font-family: 'Arial', sans-serif; /* 字体 */
+  }
+  
+  /* 文本区域样式 */
+  ion-textarea {
+    border: 1px solid #55e788; /* ���框颜色 */
+    border-radius: 8px; /* 圆角 */
+    padding: 10px; /* 内边距 */
+    font-size: 16px; /* 字体大小 */
+    color: #333; /* 字体颜色 */
+    background-color: #fff; /* 背景颜色 */
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 阴影效果 */
+  }
+  
+  /* 按钮样式 */
+  ion-button {
+    background-color: #6be4ea; /* 按钮背景颜色 */
+    color: white; /* 按钮文字颜色 */
+    border-radius: 8px; /* 圆角 */
+    font-size: 18px; /* 字体大小 */
+    padding: 10px; /* 内边距 */
+    transition: background-color 0.3s; /* 背景颜色过渡效果 */
+  }
+  
+  ion-button:hover {
+    background-color: #53d888; /* 悬停时的背景颜色 */
+  }
+  
+  /* 图片容器样式 */
+  img {
+    max-width: 100%; /* 最大宽度为100% */
+    height: auto; /* 高度自动 */
+    border-radius: 8px; /* 圆角 */
+    margin-top: 10px; /* 上边距 */
+  }
+  
+  /* 图片地址样式 */
+  h3 {
+    color: #555; /* 字体颜色 */
+    font-size: 14px; /* 字体大小 */
+    margin-top: 10px; /* 上边距 */
+  }
+  
+  /* 生成状态样式 */
+  h1 {
+    color: #333; /* 字体颜色 */
+    font-size: 24px; /* 字体大小 */
+    text-align: center; /* 居中对齐 */
+    margin-top: 20px; /* 上边距 */
+  }
+  
+  /* 意境画面提示词样式 */
+  .desc {
+    background-color: #8ceea6; /* 背景颜色 */
+    border: 1px solid #24e34e; /* 边框颜色 */
+    border-radius: 8px; /* 圆角 */
+    padding: 15px; /* 内边距 */
+    margin-top: 20px; /* 上边距 */
+    box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); /* 阴影效果 */
+  }
+
+  .copyable {
+    user-select: text; /* 允许文本选择 */
+    background-color: #f9f9f9; /* 背景颜色 */
+    padding: 5px; /* 内边距 */
+    border: 1px solid #ccc; /* 边框 */
+    border-radius: 4px; /* 圆角 */
+    display: inline-block; /* 使其呈现为块级元素 */
+    cursor: text; /* 鼠标悬停时显示文本光标 */
+  }

+ 22 - 0
src/app/poem-picture/poem-picture.component.spec.ts

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

+ 91 - 0
src/app/poem-picture/poem-picture.component.ts

@@ -0,0 +1,91 @@
+import { Component, OnInit } from '@angular/core';
+import { IonHeader, IonToolbar, IonTitle, IonContent } from '@ionic/angular/standalone';
+import { IonTextarea, IonButton,IonInput } from "@ionic/angular/standalone";
+import { DalleOptions, ImagineWork, FmodeChatCompletion } from 'fmode-ng';
+
+@Component({
+  selector: 'app-poem-picture',
+  templateUrl: './poem-picture.component.html',
+  styleUrls: ['./poem-picture.component.scss'],
+  standalone: true,
+  imports: [
+    IonHeader, IonToolbar, IonTitle, IonContent, 
+    IonButton,
+    IonInput,
+    IonTextarea
+  ],
+})
+
+export class PoemPictureComponent  implements OnInit {
+  /**
+   * 用户输入的文本
+   */
+  userPrompt:string = ""
+  promptInput(ev:any){
+    console.log(ev.detail.value)
+    if (ev.detail.value.length > 0){
+      this.userPrompt = ev.detail.value;
+    }
+  }
+
+  imagineWork:ImagineWork
+  images:Array<string> =[]
+  constructor(
+  ){
+    // 示例任务,自己生成图片后请存储新的ID 
+    this.imagineWork = new ImagineWork("");
+    this.imagineWork.fetchTask().then(work=>{
+      if(work){
+        this.imagineWork.id = work.id
+      } 
+      if(this.czq){
+        this.images= this.imagineWork?.images
+      }
+      else{
+        this.images = ['../../assets/image/th.jpg'];
+      }
+      
+    })
+  }
+  czq:boolean = false
+  PictureDescResult:string = `` // 画面描述结果
+  async createImage(){
+    this.imagineWork = new ImagineWork();
+    if (this.userPrompt.length > 0){
+    // 文本生成
+    let PromptTemplate = `您是一名专业的图片设计师,擅长设计各类型适合作为头像的唯美图片,请您根据下面提供的需求内容,将其描述的画面、场景、人物、物品等用最简短的语言表达,直接写出画面,并且要贴合描述的画风为主
+    需求如下:
+    ${this.userPrompt}
+    `
+    let completion = new FmodeChatCompletion([
+      {role:"system",content:""},
+      {role:"user",content:PromptTemplate}
+    ])
+    completion.sendCompletion().subscribe((message:any)=>{
+      // 打印消息体
+      console.log(message.content)
+      // 赋值消息内容给组件内属性
+      this.PictureDescResult = message.content
+      if(message?.complete){ // 判断message为完成状态,则设置isComplete为完成
+        // 图片生成
+        let PicturePrompt = `${this.PictureDescResult}\n风格:画面不带任何文字。人物为主体占据整个图片的3/4。正面特写。人物不可呈现怪异形态。不可出现分割。`
+      let options:DalleOptions = {prompt:PicturePrompt}
+      this.imagineWork?.draw(options).subscribe(work=>{
+          console.log("imagineWork",work?.toJSON())
+          console.log("images",work?.get("images"))
+          if(work?.get("images")?.length){
+            this.images = work?.get("images");
+            this.czq = true
+          }
+      })
+      }
+    })
+  }
+  else{
+    this.userPrompt = "请提供您理想头像的组成内容,我将根据其意境为您创作一幅画面的简短描述。"
+  }
+    
+  }
+  ngOnInit() {}
+
+}

+ 10 - 23
src/app/tab4/tab4.page.html

@@ -31,47 +31,34 @@
         <div class="user-info">
             <ion-card-title>账号:{{currentUser?.get("username")}}</ion-card-title>
             <ion-card-subtitle>
-                姓名: {{currentUser?.get("realname") || "-"}} 
-                性别: {{currentUser?.get("gender") || "-"}} 
-                年龄: {{currentUser?.get("age") || "-"}}
+                姓名: {{currentUser.get("realname") || "-"}} 
+                性别: {{currentUser.get("gender") || "-"}} 
+                年龄: {{currentUser.get("age") || "-"}}
             </ion-card-subtitle>
         </div>
     </ion-card-header>
       }
       <ion-card-content>
-      @if(!currentUser?.id){
+      @if(!currentUser.id){
         <ion-button expand="block" (click)="signup()" color="success">注册</ion-button>
         <ion-button expand="block" (click)="login()" color="success">登录</ion-button>
       }
-     @if(currentUser?.id){
+     @if(currentUser.id){
       <ion-button expand="block" (click)="editUser()" color="success">编辑资料</ion-button>
       <ion-button expand="block" (click)="logout()" color="medium">登出</ion-button>
     }
     </ion-card-content>
   </ion-card>
-  @if(currentUser?.id){
-    <ion-card>
-      <ion-card-header>
-        <ion-card-title>个性头像生成器</ion-card-title>
-        <ion-card-subtitle>点击创建个性化头像</ion-card-subtitle>
-      </ion-card-header>
-      <ion-card-content>
-        <ion-button expand="block" (click)="goToAvatar()" color="success">前往生成</ion-button>
-      </ion-card-content>
-    </ion-card>
-  }
-  @if(currentUser?.id){
-  <ion-card class="memo-card">
-    <h2 class="memo-title">健康备忘录</h2>
-    <p class="memo-description">写下您问诊的医生名或者心动的科普知识,便于您下次查找(点击标签可删除)</p>
+  <div class="memo-card"  (click)="openInquiry(doctorList[0])">
+    <h2 class="memo-title">学习问答</h2>
+    <!-- <p class="memo-description">写下您问诊的医生名或者心动的科普知识,便于您下次查找(点击标签可删除)</p>
 
     <h2 class="memo-title">收藏夹</h2>
     <ul class="tag-list">
         @for(tag of editTags; track tag;){
             <li class="tag-item">{{tag}}</li>
         }
-    </ul>
-  </ion-card>
-  }
+    </ul> -->
+  </div>
 
 </ion-content>

+ 125 - 4
src/app/tab4/tab4.page.ts

@@ -3,10 +3,12 @@ import { IonHeader, IonToolbar, IonTitle, IonContent, IonCard, IonCardContent, I
 
 import { Router } from '@angular/router';
 import { openUserLoginModal } from '../../lib/user/modal-user-login/modal-user-login.component';
-import { CloudUser } from '../../lib/ncloud';
+import { CloudObject, CloudQuery, CloudUser } from '../../lib/ncloud';
 import { openUserEditModal } from '../../lib/user/modal-user-edit/modal-user-edit.component';
 import * as Parse from 'parse';
 import { AuthService } from '../services/auth.service';
+import { ChatPanelOptions, FmodeChat, FmodeChatMessage, openChatPanelModal } from 'fmode-ng';
+import { HttpClient } from '@angular/common/http';
 
 @Component({
   selector: 'app-tab4',
@@ -29,12 +31,13 @@ export class Tab4Page {
     this.router.navigate(['/tabs/picture'])
   }
 
-  currentUser:CloudUser|undefined
+  currentUser:CloudUser
   constructor(
     private router: Router,
     private modalCtrl:ModalController,
     private alertController: AlertController,
-    private auth: AuthService
+    private auth: AuthService,
+    // private http: HttpClient // 注入 HttpClient
   ) {
     this.currentUser = new CloudUser();
   }
@@ -44,7 +47,7 @@ export class Tab4Page {
       let user = await openUserLoginModal(this.modalCtrl);
       if(user?.id){
         this.currentUser = user;
-        this.auth.setCurrentUser(user);
+        // this.auth.setCurrentUser(user);
       } else {
         // 登录失败时显示提示
         const alert = await this.alertController.create({
@@ -81,6 +84,7 @@ export class Tab4Page {
   editUser(){
     openUserEditModal(this.modalCtrl)
   }
+  
 
   editTags:Array<String>=[]
   async setTagsValue(ev:any){
@@ -96,4 +100,121 @@ export class Tab4Page {
     }
     this.editTags=ev;
   }
+
+  doctorList:Array<CloudObject> = []
+   /** 示例:问诊根据doctor拼接提示词 */
+   async loadDoctorList(){
+    let query = new CloudQuery("Doctor");
+    query.include("depart")
+    let List = await query.find()
+    for(let i = 0; i < 3; i++) {
+      this.doctorList.push(List[i])
+    }
+  }
+  
+   /** 示例:问诊根据doctor拼接提示词 */
+   async openInquiry(doctor:any){
+    // 验证用户登录
+    let userPrompt = ``
+    if(!this.currentUser?.id){
+      console.log("用户未登录,请登录后重试");
+      // let user = await openUserLoginModal(this.modalCtrl);
+      // if(!user?.id){
+      //   return
+      // }
+      // currentUser = user;
+      this.router.navigate(['/tabs/tab4'])
+      return 
+    }
+    console.log("currentUser: ",this.currentUser)
+    if(this.currentUser?.get("realname")){
+      userPrompt += `当前来访的患者,姓名:${this.currentUser?.get("realname")}`
+    }
+    if(this.currentUser?.get("gender")){
+      userPrompt += `,性别:${this.currentUser?.get("gender")}`
+    }
+    if(this.currentUser?.get("age")){
+      userPrompt += `,年龄:${this.currentUser?.get("age")}`
+    }
+
+
+    localStorage.setItem("company","E4KpGvTEto")
+
+    let consult = new CloudObject("Consultation")
+    let now = new Date();
+    let dateStr = `${now.getFullYear()}-${now.getMonth()+1}-${now.getDate()}`
+    // 对象权限的精确指定
+    let ACL:any = {
+      "*":{read:true,write:true}
+    }
+    consult.set({
+      // title:`${doctor.get('depart')?.name || ""}门诊记录${dateStr}-${doctor?.get("name")}`,
+      // doctor:doctor.toPointer(),
+      // depart:{
+      //   __type:"Pointer",
+      //   className:"Department",
+      //   objectId:doctor.get("depart")?.objectId
+      // },
+      user:this.currentUser.toPointer(),
+      ACL:ACL
+    })
+
+    let options:ChatPanelOptions = {
+      roleId:"2DXJkRsjXK",
+      onChatInit:(chat:FmodeChat)=>{
+        console.log("onChatInit");
+              console.log("预设角色",chat.role);
+              chat.role.set("name",doctor?.get("name"));
+              // chat.role.set("system","你是一名计算机老师,请根据用户提问,给出专业、简洁、准确的答案");
+              chat.role.set("title",doctor?.get("title"));
+              chat.role.set("desc",doctor?.get("desc"));
+              chat.role.set("tags",doctor?.get("qualifications"));
+              chat.role.set("avatar",doctor?.get("avatar") || "../../assets/image/doctor7.png")
+              chat.role.set("prompt",`
+# 角色设定
+您是${doctor?.get("name")},${doctor?.get("desc")},年龄${doctor?.get("age")}岁,需要完成一次完整的门诊服务,直接和用户打招呼,询问用户的情况。
+
+# 对话环节
+0.直接和用户打招呼(如:你好,XXX,我是···)
+- 注意:如果用户问的问题和你的${doctor?.get("specialty")}的不符,请直接引导用户去其他的科室
+1.预设的问询方式(根据不同症状来问询具体的情况)
+- 打招呼,以用户自述为主
+- 当信息充足时候,确认用户症状对应的科室,并进入下一个环节
+2.拓展的问询细节
+例如:用户反映呼吸不畅,拓展出:是否咳嗽;是否感觉痛或者痒等其他需要的问题。
+- 当问询细节补充完成后进入下一个环节
+3.初步的诊断结果,并且同时列出检查检验项目
+初步诊断:确定需要有哪些进一步检查
+检查检验:获取医学客观数据
+- 等待用户提交客观数据,进入下一阶段
+4.给出诊断方案并给出处方
+- 完成处方时,请在消息结尾附带: [处方完成]
+
+# 开始话语
+当您准备好了,可以以一个医生的身份,先向来访的用户亲切地打招呼。
+${userPrompt}
+`);
+      },
+      onMessage:(chat:FmodeChat,message:FmodeChatMessage)=>{
+        console.log("onMessage",message)
+        let content:any = message?.content
+        if(typeof content == "string"){
+          if(content?.indexOf("[处方完成]")>-1){
+            let list = chat?.messageList
+            console.log("门诊已完成")
+            consult.set({
+              allContent:list,
+              content:content,
+            })
+            consult.save();
+          }
+        }
+      },
+      onChatSaved:(chat:FmodeChat)=>{
+        console.log("onChatSaved",chat,chat?.chatSession,chat?.chatSession?.id)
+      }
+    }
+    openChatPanelModal(this.modalCtrl,options)
+  }
+
 }

+ 5 - 0
src/app/tabs/tabs.routes.ts

@@ -26,6 +26,11 @@ export const routes: Routes = [
         path: 'countdown',
         loadComponent: () => import('../countdown/countdown.page').then(m => m.CountdownPage)
       },
+      {
+        path: 'picture',
+        loadComponent: () =>
+          import('../poem-picture/poem-picture.component').then((m) => m.PoemPictureComponent),
+      },
       {
         path: '',
         redirectTo: '/tabs/tab1',

BIN
src/assets/images/th.jpg


+ 95 - 50
src/lib/ncloud.ts

@@ -1,8 +1,5 @@
-import * as Parse from 'parse';
-
 // CloudObject.ts
 export class CloudObject {
-    protected parseObject: Parse.Object;
     className: string;
     id: string | null = null;
     createdAt:any;
@@ -10,7 +7,6 @@ export class CloudObject {
     data: Record<string, any> = {};
 
     constructor(className: string) {
-        this.parseObject = new Parse.Object(className);
         this.className = className;
     }
 
@@ -20,7 +16,7 @@ export class CloudObject {
 
     set(json: Record<string, any>) {
         Object.keys(json).forEach(key => {
-            if (["objectId", "id", "createdAt", "updatedAt", "ACL"].indexOf(key) > -1) {
+            if (["objectId", "id", "createdAt", "updatedAt"].indexOf(key) > -1) {
                 return;
             }
             this.data[key] = json[key];
@@ -33,7 +29,7 @@ export class CloudObject {
 
     async save() {
         let method = "POST";
-        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}`;
+        let url = `https://dev.fmode.cn/parse/classes/${this.className}`;
 
         // 更新
         if (this.id) {
@@ -65,7 +61,7 @@ export class CloudObject {
 
     async destroy() {
         if (!this.id) return;
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/classes/${this.className}/${this.id}`, {
+        const response = await fetch(`https://dev.fmode.cn/parse/classes/${this.className}/${this.id}`, {
             headers: {
                 "x-parse-application-id": "dev"
             },
@@ -81,23 +77,17 @@ export class CloudObject {
         }
         return true;
     }
-
-    protected setParseObject(obj: Parse.Object) {
-        this.parseObject = obj;
-    }
 }
 
 // CloudQuery.ts
 export class CloudQuery {
-    private query: Parse.Query;
     className: string;
     queryParams: Record<string, any> = {};
 
     constructor(className: string) {
         this.className = className;
-        this.query = new Parse.Query(className);
     }
-    // 作用是将查询参数转换为对象
+
     include(...fileds:string[]) {
         this.queryParams["include"] = fileds;
     }
@@ -122,11 +112,12 @@ export class CloudQuery {
     }
 
     equalTo(key: string, value: any) {
+        if (!this.queryParams["where"]) this.queryParams["where"] = {};
         this.queryParams["where"][key] = value;
     }
 
     async get(id: string) {
-        const url = `http://dev.fmode.cn:1337/parse/classes/${this.className}/${id}?`;
+        const url = `https://dev.fmode.cn/parse/classes/${this.className}/${id}?`;
 
         const response = await fetch(url, {
             headers: {
@@ -140,32 +131,52 @@ export class CloudQuery {
         });
 
         const json = await response?.json();
-        // return json || {};
-        const exists = json?.results?.[0] || null;
-        if (exists) {
-            let existsObject = this.dataToObj(exists)
+        if (json) {
+            let existsObject = this.dataToObj(json)
             return existsObject;
         }
         return null
-
     }
 
-    async find(): Promise<CloudObject[]> {
-        try {
-            const results = await this.query.find();
-            return results.map((result: Parse.Object) => {
-                const obj = new CloudObject(this.className);
-                (obj as any).parseObject = result;  // 使用类型断言
-                return obj;
-            });
-        } catch (error) {
-            console.error('Query failed:', error);
-            throw error;
-        }
+    async find():Promise<Array<CloudObject>> {
+        let url = `https://dev.fmode.cn/parse/classes/${this.className}?`;
+
+        let queryStr = ``
+        Object.keys(this.queryParams).forEach(key=>{
+            let paramStr = JSON.stringify(this.queryParams[key]);
+            if(key=="include"){
+                paramStr = this.queryParams[key]?.join(",")
+            }
+            if(queryStr) {
+                url += `${key}=${paramStr}`;
+            }else{
+                url += `&${key}=${paramStr}`;
+            }
+        })
+        // if (Object.keys(this.queryParams["where"]).length) {
+            
+        // }
+
+        const response = await fetch(url, {
+            headers: {
+                "if-none-match": "W/\"1f0-ghxH2EwTk6Blz0g89ivf2adBDKY\"",
+                "x-parse-application-id": "dev"
+            },
+            body: null,
+            method: "GET",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const json = await response?.json();
+        let list = json?.results || []
+        let objList = list.map((item:any)=>this.dataToObj(item))
+        return objList || [];
     }
 
+
     async first() {
-        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}?`;
+        let url = `https://dev.fmode.cn/parse/classes/${this.className}?`;
 
         if (Object.keys(this.queryParams["where"]).length) {
             const whereStr = JSON.stringify(this.queryParams["where"]);
@@ -184,15 +195,12 @@ export class CloudQuery {
         });
 
         const json = await response?.json();
-        // const exists = json?.results?.[0] || null;
-        // if (exists) {
-        //     let existsObject = this.dataToObj(exists)
-        //     return existsObject;
-        // }
-        // return null
-        let list = json?.results || []
-        let objList = list.map((item:any)=>this.dataToObj(item))
-        return objList || [];
+        const exists = json?.results?.[0] || null;
+        if (exists) {
+            let existsObject = this.dataToObj(exists)
+            return existsObject;
+        }
+        return null
     }
 
     dataToObj(exists:any):CloudObject{
@@ -228,7 +236,7 @@ export class CloudUser extends CloudObject {
             return null;
         }
         return this;
-        // const response = await fetch(`http://dev.fmode.cn:1337/parse/users/me`, {
+        // const response = await fetch(`https://dev.fmode.cn/parse/users/me`, {
         //     headers: {
         //         "x-parse-application-id": "dev",
         //         "x-parse-session-token": this.sessionToken // 使用sessionToken进行身份验证
@@ -246,7 +254,7 @@ export class CloudUser extends CloudObject {
 
     /** 登录 */
     async login(username: string, password: string):Promise<CloudUser|null> {
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/login`, {
+        const response = await fetch(`https://dev.fmode.cn/parse/login`, {
             headers: {
                 "x-parse-application-id": "dev",
                 "Content-Type": "application/json"
@@ -278,7 +286,7 @@ export class CloudUser extends CloudObject {
             return;
         }
 
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/logout`, {
+        const response = await fetch(`https://dev.fmode.cn/parse/logout`, {
             headers: {
                 "x-parse-application-id": "dev",
                 "x-parse-session-token": this.sessionToken
@@ -286,18 +294,26 @@ export class CloudUser extends CloudObject {
             method: "POST"
         });
 
-        const result = await response?.json();
+        let result = await response?.json();
+
         if (result?.error) {
             console.error(result?.error);
+            if(result?.error=="Invalid session token"){
+                this.clearUserCache()
+                return true;
+            }
             return false;
         }
 
+        this.clearUserCache()
+        return true;
+    }
+    clearUserCache(){
         // 清除用户信息
         localStorage.removeItem("NCloud/dev/User")
         this.id = null;
         this.sessionToken = null;
         this.data = {};
-        return true;
     }
 
     /** 注册 */
@@ -305,10 +321,10 @@ export class CloudUser extends CloudObject {
         const userData = {
             username,
             password,
-            ...additionalData // 合并额外的用户��
+            ...additionalData // 合并额外的用户
         };
 
-        const response = await fetch(`http://dev.fmode.cn:1337/parse/users`, {
+        const response = await fetch(`https://dev.fmode.cn/parse/users`, {
             headers: {
                 "x-parse-application-id": "dev",
                 "Content-Type": "application/json"
@@ -335,7 +351,7 @@ export class CloudUser extends CloudObject {
 
     override async save() {
         let method = "POST";
-        let url = `http://dev.fmode.cn:1337/parse/users`;
+        let url = `https://dev.fmode.cn/parse/users`;
     
         // 更新用户信息
         if (this.id) {
@@ -372,4 +388,33 @@ export class CloudUser extends CloudObject {
         localStorage.setItem("NCloud/dev/User",JSON.stringify(this.data))
         return this;
     }
+}
+
+export class CloudApi{
+    async fetch(path:string,body:any,options?:{
+        method:string
+        body:any
+    }){
+
+        let reqOpts:any =  {
+            headers: {
+                "x-parse-application-id": "dev",
+                "Content-Type": "application/json"
+            },
+            method: options?.method || "POST",
+            mode: "cors",
+            credentials: "omit"
+        }
+        if(body||options?.body){
+            reqOpts.body = JSON.stringify(body || options?.body);
+            reqOpts.json = true;
+        }
+        let host = `https://dev.fmode.cn`
+        // host = `http://127.0.0.1:1337`
+        let url = `${host}/api/`+path
+        console.log(url,reqOpts)
+        const response = await fetch(url,reqOpts);
+        let json = await response.json();
+        return json
+    }
 }

Некоторые файлы не были показаны из-за большого количества измененных файлов