浏览代码

feat: FarmField save query

fmode 2 周之前
父节点
当前提交
4fc6cf4b5a
共有 3 个文件被更改,包括 561 次插入128 次删除
  1. 7 7
      myapp/src/app/tab3/tab3.page.html
  2. 134 121
      myapp/src/app/tab3/tab3.page.ts
  3. 420 0
      myapp/src/lib/ncloud.ts

+ 7 - 7
myapp/src/app/tab3/tab3.page.html

@@ -1,6 +1,6 @@
 <ion-header>
   <ion-toolbar color="primary">
-    <ion-title>我的农场</ion-title>
+    <ion-title (click)="initializeSampleData()">我的农场</ion-title>
     <ion-buttons slot="end">
       <ion-button (click)="toggleFieldForm()">
         <ion-icon [name]="showFieldForm ? 'close' : 'add'" slot="icon-only"></ion-icon>
@@ -21,17 +21,17 @@
           <ion-label position="floating">农田名称</ion-label>
           <ion-input formControlName="name" type="text"></ion-input>
         </ion-item>
-        
+
         <ion-item>
           <ion-label position="floating">面积(亩)</ion-label>
           <ion-input formControlName="area" type="number"></ion-input>
         </ion-item>
-        
+
         <ion-item>
           <ion-label position="floating">位置</ion-label>
           <ion-input formControlName="location" type="text"></ion-input>
         </ion-item>
-        
+
         <ion-item>
           <ion-label>当前作物</ion-label>
           <ion-select formControlName="currentCrop">
@@ -41,7 +41,7 @@
             <ion-select-option value="大豆">大豆</ion-select-option>
           </ion-select>
         </ion-item>
-        
+
         <ion-item>
           <ion-label>土壤类型</ion-label>
           <ion-select formControlName="soilType">
@@ -51,7 +51,7 @@
             <ion-select-option value="粘土">粘土</ion-select-option>
           </ion-select>
         </ion-item>
-        
+
         <div class="form-buttons">
           <ion-button type="button" fill="outline" (click)="cancelEdit()">取消</ion-button>
           <ion-button type="submit" [disabled]="!fieldForm.valid">保存</ion-button>
@@ -166,4 +166,4 @@
       </div>
     </ion-card-content>
   </ion-card>
-</ion-content>
+</ion-content>

+ 134 - 121
myapp/src/app/tab3/tab3.page.ts

@@ -1,6 +1,7 @@
 import { Component, OnInit } from '@angular/core';
 import { FormBuilder, FormGroup, Validators } from '@angular/forms';
 import { AlertController } from '@ionic/angular';
+import { CloudObject, CloudQuery } from 'src/lib/ncloud';
 
 interface FarmField {
   id: string;
@@ -43,81 +44,16 @@ interface GrowthRecord {
   standalone: false,
 })
 export class Tab3Page implements OnInit {
-  fields: FarmField[] = [
-    {
-      id: '1',
-      name: '北区小麦田',
-      area: 5.2,
-      location: '农场北侧',
-      currentCrop: '冬小麦',
-      daysPlanted: 45,
-      status: '需施肥',
-      nextTask: '明日追肥',
-      soilType: '粘壤土'
-    },
-    {
-      id: '2',
-      name: '南区玉米地',
-      area: 3.8,
-      location: '农场南侧',
-      currentCrop: '春玉米',
-      daysPlanted: 22,
-      status: '正常',
-      nextTask: '下周除草',
-      soilType: '砂壤土'
-    }
-  ];
-
-  tasks: FarmTask[] = [
-    {
-      id: '1',
-      fieldId: '1',
-      fieldName: '北区小麦田',
-      name: '追施氮肥',
-      type: 'fertilization',
-      dueDate: new Date(new Date().setDate(new Date().getDate() + 1)),
-      urgent: true,
-      completed: false
-    },
-    {
-      id: '2',
-      fieldId: '2',
-      fieldName: '南区玉米地',
-      name: '除草作业',
-      type: 'pestControl',
-      dueDate: new Date(new Date().setDate(new Date().getDate() + 7)),
-      urgent: false,
-      completed: false
-    }
-  ];
-
-  records: GrowthRecord[] = [
-    {
-      id: '1',
-      fieldId: '1',
-      fieldName: '北区小麦田',
-      date: new Date(new Date().setDate(new Date().getDate() - 3)),
-      activity: '病虫害检查',
-      notes: '发现少量蚜虫,已喷施防治',
-      images: [
-        'assets/images/wheat-pest1.jpg',
-        'assets/images/wheat-pest2.jpg'
-      ]
-    },
-    {
-      id: '2',
-      fieldId: '2',
-      fieldName: '南区玉米地',
-      date: new Date(new Date().setDate(new Date().getDate() - 5)),
-      activity: '追肥',
-      notes: '施用复合肥20kg/亩'
-    }
-  ];
+  fields: FarmField[] = []; // 初始化为空数组,从后端加载数据
+  tasks: FarmTask[] = [];
+  records: GrowthRecord[] = [];
 
   showFieldForm = false;
   editingField: FarmField | null = null;
   fieldForm: FormGroup;
 
+  private cloudQuery = new CloudQuery('FarmField');
+
   constructor(
     private fb: FormBuilder,
     private alertCtrl: AlertController
@@ -131,7 +67,133 @@ export class Tab3Page implements OnInit {
     });
   }
 
-  ngOnInit() {}
+  async ngOnInit() {
+    await this.loadFields();
+    await this.initializeSampleData(); // 可选:初始化示例数据
+  }
+
+  // 从后端加载农田数据
+  async loadFields() {
+    try {
+      const cloudFields = await this.cloudQuery.find();
+      this.fields = cloudFields.map(field => ({
+        id: field.id || '',
+        name: field.get('name'),
+        area: field.get('area'),
+        location: field.get('location'),
+        currentCrop: field.get('currentCrop'),
+        daysPlanted: field.get('daysPlanted') || 0,
+        status: field.get('status') || '正常',
+        soilType: field.get('soilType')
+      }));
+    } catch (error) {
+      console.error('加载农田数据失败:', error);
+    }
+  }
+
+  // 保存农田到后端
+  async saveField() {
+    if (this.fieldForm.invalid) return;
+
+    const fieldData = this.fieldForm.value;
+    const fieldObject = new CloudObject('FarmField');
+
+    if (this.editingField && this.editingField.id) {
+      // 更新现有农田
+      fieldObject.id = this.editingField.id;
+      fieldObject.set(fieldData);
+      await fieldObject.save();
+    } else {
+      // 添加新农田
+      fieldObject.set({
+        ...fieldData,
+        daysPlanted: 0,
+        status: '正常'
+      });
+      const savedField = await fieldObject.save();
+
+      this.fields.push({
+        id: savedField.id || '',
+        daysPlanted: 0,
+        status: '正常',
+        ...fieldData
+      });
+    }
+
+    await this.loadFields(); // 重新加载数据确保同步
+    this.cancelEdit();
+  }
+
+  // 删除农田
+  async deleteField(fieldId: string) {
+    const alert = await this.alertCtrl.create({
+      header: '确认删除',
+      message: '确定要删除这个农田地块吗?相关记录也将被删除',
+      buttons: [
+        {
+          text: '取消',
+          role: 'cancel'
+        },
+        {
+          text: '删除',
+          handler: async () => {
+            const fieldObject = new CloudObject('FarmField');
+            fieldObject.id = fieldId;
+            await fieldObject.destroy();
+
+            this.fields = this.fields.filter(f => f.id !== fieldId);
+            this.tasks = this.tasks.filter(t => t.fieldId !== fieldId);
+            this.records = this.records.filter(r => r.fieldId !== fieldId);
+          }
+        }
+      ]
+    });
+
+    await alert.present();
+  }
+
+  // 初始化示例数据(可选)
+  async initializeSampleData() {
+    const query = new CloudQuery('FarmField');
+    const existingFields = await query.find();
+
+    if (existingFields.length === 0) {
+      const sampleFields = [
+        {
+          name: '北区小麦田',
+          area: 5.2,
+          location: '农场北侧',
+          currentCrop: '冬小麦',
+          daysPlanted: 45,
+          status: '需施肥',
+          nextTask: '明日追肥',
+          soilType: '粘壤土'
+        },
+        {
+          name: '南区玉米地',
+          area: 3.8,
+          location: '农场南侧',
+          currentCrop: '春玉米',
+          daysPlanted: 22,
+          status: '正常',
+          nextTask: '下周除草',
+          soilType: '砂壤土'
+        }
+      ];
+
+      for (const field of sampleFields) {
+        const fieldObject = new CloudObject('FarmField');
+        fieldObject.set(field);
+        await fieldObject.save();
+      }
+
+      // 重新加载数据
+      await this.loadFields();
+    }
+  }
+
+
+  // 原有代码不变
 
   get totalArea(): number {
     return this.fields.reduce((sum, field) => sum + field.area, 0);
@@ -217,32 +279,6 @@ export class Tab3Page implements OnInit {
     this.showFieldForm = true;
   }
 
-  saveField() {
-    if (this.fieldForm.invalid) return;
-
-    const fieldData = this.fieldForm.value;
-    
-    if (this.editingField) {
-      // 更新现有农田
-      const index = this.fields.findIndex(f => f.id === this.editingField?.id);
-      if (index >= 0) {
-        this.fields[index] = {
-          ...this.fields[index],
-          ...fieldData
-        };
-      }
-    } else {
-      // 添加新农田
-      this.fields.push({
-        id: Date.now().toString(),
-        daysPlanted: 0,
-        status: '正常',
-        ...fieldData
-      });
-    }
-
-    this.cancelEdit();
-  }
 
   cancelEdit() {
     this.showFieldForm = false;
@@ -253,31 +289,8 @@ export class Tab3Page implements OnInit {
     });
   }
 
-  async deleteField(fieldId: string) {
-    const alert = await this.alertCtrl.create({
-      header: '确认删除',
-      message: '确定要删除这个农田地块吗?相关记录也将被删除',
-      buttons: [
-        {
-          text: '取消',
-          role: 'cancel'
-        },
-        {
-          text: '删除',
-          handler: () => {
-            this.fields = this.fields.filter(f => f.id !== fieldId);
-            this.tasks = this.tasks.filter(t => t.fieldId !== fieldId);
-            this.records = this.records.filter(r => r.fieldId !== fieldId);
-          }
-        }
-      ]
-    });
-
-    await alert.present();
-  }
-
   viewTaskDetail(task: FarmTask) {
     console.log('查看任务详情:', task);
     // 实际应导航到任务详情页
   }
-}
+}

+ 420 - 0
myapp/src/lib/ncloud.ts

@@ -0,0 +1,420 @@
+// CloudObject.ts
+export class CloudObject {
+    className: string;
+    id: string | null = null;
+    createdAt:any;
+    updatedAt:any;
+    data: Record<string, any> = {};
+
+    constructor(className: string) {
+        this.className = className;
+    }
+
+    toPointer() {
+        return { "__type": "Pointer", "className": this.className, "objectId": this.id };
+    }
+
+    set(json: Record<string, any>) {
+        Object.keys(json).forEach(key => {
+            if (["objectId", "id", "createdAt", "updatedAt"].indexOf(key) > -1) {
+                return;
+            }
+            this.data[key] = json[key];
+        });
+    }
+
+    get(key: string) {
+        return this.data[key] || null;
+    }
+
+    async save() {
+        let method = "POST";
+        let url = `http://dev.fmode.cn:1337/parse/classes/${this.className}`;
+
+        // 更新
+        if (this.id) {
+            url += `/${this.id}`;
+            method = "PUT";
+        }
+
+        const body = JSON.stringify(this.data);
+        const response = await fetch(url, {
+            headers: {
+                "content-type": "application/json;charset=UTF-8",
+                "x-parse-application-id": "dev"
+            },
+            body: body,
+            method: method,
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+        }
+        if (result?.objectId) {
+            this.id = result?.objectId;
+        }
+        return this;
+    }
+
+    async destroy() {
+        if (!this.id) return;
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/classes/${this.className}/${this.id}`, {
+            headers: {
+                "x-parse-application-id": "dev"
+            },
+            body: null,
+            method: "DELETE",
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result) {
+            this.id = null;
+        }
+        return true;
+    }
+}
+
+// CloudQuery.ts
+export class CloudQuery {
+    className: string;
+    queryParams: Record<string, any> = {};
+
+    constructor(className: string) {
+        this.className = className;
+    }
+
+    include(...fileds:string[]) {
+        this.queryParams["include"] = fileds;
+    }
+    greaterThan(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$gt"] = value;
+    }
+
+    greaterThanAndEqualTo(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$gte"] = value;
+    }
+
+    lessThan(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$lt"] = value;
+    }
+
+    lessThanAndEqualTo(key: string, value: any) {
+        if (!this.queryParams["where"][key]) this.queryParams["where"][key] = {};
+        this.queryParams["where"][key]["$lte"] = value;
+    }
+
+    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 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();
+        if (json) {
+            let existsObject = this.dataToObj(json)
+            return existsObject;
+        }
+        return null
+    }
+
+    async find():Promise<Array<CloudObject>> {
+        let url = `http://dev.fmode.cn:1337/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}?`;
+
+        if (Object.keys(this.queryParams["where"]).length) {
+            const whereStr = JSON.stringify(this.queryParams["where"]);
+            url += `where=${whereStr}`;
+        }
+
+        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();
+        const exists = json?.results?.[0] || null;
+        if (exists) {
+            let existsObject = this.dataToObj(exists)
+            return existsObject;
+        }
+        return null
+    }
+
+    dataToObj(exists:any):CloudObject{
+        let existsObject = new CloudObject(this.className);
+        existsObject.set(exists);
+        existsObject.id = exists.objectId;
+        existsObject.createdAt = exists.createdAt;
+        existsObject.updatedAt = exists.updatedAt;
+        return existsObject;
+    }
+}
+
+// CloudUser.ts
+export class CloudUser extends CloudObject {
+    constructor() {
+        super("_User"); // 假设用户类在Parse中是"_User"
+        // 读取用户缓存信息
+        let userCacheStr = localStorage.getItem("NCloud/dev/User")
+        if(userCacheStr){
+            let userData = JSON.parse(userCacheStr)
+            // 设置用户信息
+            this.id = userData?.objectId;
+            this.sessionToken = userData?.sessionToken;
+            this.data = userData; // 保存用户数据
+        }
+    }
+
+    sessionToken:string|null = ""
+    /** 获取当前用户信息 */
+    async current() {
+        if (!this.sessionToken) {
+            console.error("用户未登录");
+            return null;
+        }
+        return this;
+        // const response = await fetch(`http://dev.fmode.cn:1337/parse/users/me`, {
+        //     headers: {
+        //         "x-parse-application-id": "dev",
+        //         "x-parse-session-token": this.sessionToken // 使用sessionToken进行身份验证
+        //     },
+        //     method: "GET"
+        // });
+
+        // const result = await response?.json();
+        // if (result?.error) {
+        //     console.error(result?.error);
+        //     return null;
+        // }
+        // return result;
+    }
+
+    /** 登录 */
+    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",
+                "Content-Type": "application/json"
+            },
+            body: JSON.stringify({ username, password }),
+            method: "POST"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            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))
+        return this;
+    }
+
+    /** 登出 */
+    async logout() {
+        if (!this.sessionToken) {
+            console.error("用户未登录");
+            return;
+        }
+
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/logout`, {
+            headers: {
+                "x-parse-application-id": "dev",
+                "x-parse-session-token": this.sessionToken
+            },
+            method: "POST"
+        });
+
+        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 = {};
+    }
+
+    /** 注册 */
+    async signUp(username: string, password: string, additionalData: Record<string, any> = {}) {
+        const userData = {
+            username,
+            password,
+            ...additionalData // 合并额外的用户数据
+        };
+
+        const response = await fetch(`http://dev.fmode.cn:1337/parse/users`, {
+            headers: {
+                "x-parse-application-id": "dev",
+                "Content-Type": "application/json"
+            },
+            body: JSON.stringify(userData),
+            method: "POST"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+            return null;
+        }
+
+        // 设置用户信息
+        // 缓存用户信息
+        console.log(result)
+        localStorage.setItem("NCloud/dev/User",JSON.stringify(result))
+        this.id = result?.objectId;
+        this.sessionToken = result?.sessionToken;
+        this.data = result; // 保存用户数据
+        return this;
+    }
+
+    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))
+        delete data.createdAt
+        delete data.updatedAt
+        delete data.ACL
+        delete data.objectId
+        const body = JSON.stringify(data);
+        let headersOptions:any = {
+            "content-type": "application/json;charset=UTF-8",
+            "x-parse-application-id": "dev",
+            "x-parse-session-token": this.sessionToken, // 添加sessionToken以进行身份验证
+        }
+        const response = await fetch(url, {
+            headers: headersOptions,
+            body: body,
+            method: method,
+            mode: "cors",
+            credentials: "omit"
+        });
+
+        const result = await response?.json();
+        if (result?.error) {
+            console.error(result?.error);
+        }
+        if (result?.objectId) {
+            this.id = result?.objectId;
+        }
+        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 = `http://dev.fmode.cn:1337`
+        // 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
+    }
+}