Browse Source

feat: update

0225172 7 months ago
parent
commit
43ad53e14d
100 changed files with 2923 additions and 199 deletions
  1. 5 0
      .vscode/extensions.json
  2. 3 0
      .vscode/settings.json
  3. 74 0
      ng-case/src/app/app-routing.module.ts
  4. 3 0
      ng-case/src/app/app.component.html
  5. 0 0
      ng-case/src/app/app.component.scss
  6. 21 0
      ng-case/src/app/app.component.spec.ts
  7. 16 0
      ng-case/src/app/app.component.ts
  8. 16 0
      ng-case/src/app/app.module.ts
  9. 4 0
      ng-case/src/app/explore-container/explore-container.component.html
  10. 27 0
      ng-case/src/app/explore-container/explore-container.component.scss
  11. 24 0
      ng-case/src/app/explore-container/explore-container.component.spec.ts
  12. 12 0
      ng-case/src/app/explore-container/explore-container.component.ts
  13. 14 0
      ng-case/src/app/explore-container/explore-container.module.ts
  14. 16 0
      ng-case/src/app/tab1/tab1-routing.module.ts
  15. 20 0
      ng-case/src/app/tab1/tab1.module.ts
  16. 56 0
      ng-case/src/app/tab1/tab1.page.html
  17. 4 0
      ng-case/src/app/tab1/tab1.page.scss
  18. 26 0
      ng-case/src/app/tab1/tab1.page.spec.ts
  19. 20 0
      ng-case/src/app/tab1/tab1.page.ts
  20. 16 0
      ng-case/src/app/tab2/tab2-routing.module.ts
  21. 20 0
      ng-case/src/app/tab2/tab2.module.ts
  22. 17 0
      ng-case/src/app/tab2/tab2.page.html
  23. 0 0
      ng-case/src/app/tab2/tab2.page.scss
  24. 26 0
      ng-case/src/app/tab2/tab2.page.spec.ts
  25. 12 0
      ng-case/src/app/tab2/tab2.page.ts
  26. 16 0
      ng-case/src/app/tab3/tab3-routing.module.ts
  27. 20 0
      ng-case/src/app/tab3/tab3.module.ts
  28. 17 0
      ng-case/src/app/tab3/tab3.page.html
  29. 0 0
      ng-case/src/app/tab3/tab3.page.scss
  30. 26 0
      ng-case/src/app/tab3/tab3.page.spec.ts
  31. 12 0
      ng-case/src/app/tab3/tab3.page.ts
  32. 41 0
      ng-case/src/app/tabs/tabs-routing.module.ts
  33. 19 0
      ng-case/src/app/tabs/tabs.module.ts
  34. 20 0
      ng-case/src/app/tabs/tabs.page.html
  35. 1 0
      ng-case/src/app/tabs/tabs.page.scss
  36. 26 0
      ng-case/src/app/tabs/tabs.page.spec.ts
  37. 12 0
      ng-case/src/app/tabs/tabs.page.ts
  38. 2 0
      ng-case/src/assets/3dmodel/.gitignore
  39. BIN
      ng-case/src/assets/icon/favicon.png
  40. 1 0
      ng-case/src/assets/shapes.svg
  41. 12 0
      ng-case/src/assets/swiper/swiper.min.css
  42. 3 0
      ng-case/src/environments/environment.prod.ts
  43. 16 0
      ng-case/src/environments/environment.ts
  44. 43 0
      ng-case/src/global.scss
  45. 29 0
      ng-case/src/index.html
  46. 12 0
      ng-case/src/main.ts
  47. 55 0
      ng-case/src/polyfills.ts
  48. 14 0
      ng-case/src/test.ts
  49. 2 0
      ng-case/src/theme/variables.scss
  50. 6 0
      ng-case/src/zone-flags.ts
  51. 89 3
      package-lock.json
  52. 2 0
      package.json
  53. 42 0
      server/case/promise.js
  54. 40 0
      server/fmode-parse-test.js
  55. 110 0
      server/fmode-parse.js
  56. 106 0
      server/import/BoleMbti.js
  57. 1 0
      server/import/libai/.gitignore
  58. 56 0
      server/import/libai/README.md
  59. 297 0
      server/import/libai/href.js
  60. 60 0
      server/import/libai/import.js
  61. 197 0
      server/import/libai/package-lock.json
  62. 14 0
      server/import/libai/package.json
  63. 112 0
      server/rest.js
  64. 0 32
      servers/REST.js
  65. 25 2
      src/app/app-routing.module.ts
  66. 1 80
      src/app/tab1/tab1.page.html
  67. 8 1
      src/app/tab1/tab1.page.ts
  68. 48 78
      src/app/tab3/tab3.page.html
  69. 35 2
      src/app/tab3/tab3.page.scss
  70. 24 1
      src/app/tab3/tab3.page.ts
  71. 100 0
      src/app/user/README.md
  72. 17 0
      src/app/user/auth.guard.spec.ts
  73. 10 0
      src/app/user/auth.guard.ts
  74. 17 0
      src/app/user/edit-info/edit-info-routing.module.ts
  75. 20 0
      src/app/user/edit-info/edit-info.module.ts
  76. 47 0
      src/app/user/edit-info/edit-info.page.html
  77. 0 0
      src/app/user/edit-info/edit-info.page.scss
  78. 17 0
      src/app/user/edit-info/edit-info.page.spec.ts
  79. 58 0
      src/app/user/edit-info/edit-info.page.ts
  80. 17 0
      src/app/user/login/login-routing.module.ts
  81. 20 0
      src/app/user/login/login.module.ts
  82. 33 0
      src/app/user/login/login.page.html
  83. 0 0
      src/app/user/login/login.page.scss
  84. 17 0
      src/app/user/login/login.page.spec.ts
  85. 107 0
      src/app/user/login/login.page.ts
  86. 17 0
      src/app/user/mine/mine-routing.module.ts
  87. 20 0
      src/app/user/mine/mine.module.ts
  88. 24 0
      src/app/user/mine/mine.page.html
  89. 0 0
      src/app/user/mine/mine.page.scss
  90. 17 0
      src/app/user/mine/mine.page.spec.ts
  91. 28 0
      src/app/user/mine/mine.page.ts
  92. 14 0
      src/app/user/user-routing.module.ts
  93. 14 0
      src/app/user/user.module.ts
  94. BIN
      src/app/user/用户模块.xmind
  95. 17 0
      src/modules/page/shouye-view/shouye-view-routing.module.ts
  96. 20 0
      src/modules/page/shouye-view/shouye-view.module.ts
  97. 50 0
      src/modules/page/shouye-view/shouye-view.page.html
  98. 57 0
      src/modules/page/shouye-view/shouye-view.page.scss
  99. 17 0
      src/modules/page/shouye-view/shouye-view.page.spec.ts
  100. 24 0
      src/modules/page/shouye-view/shouye-view.page.ts

+ 5 - 0
.vscode/extensions.json

@@ -0,0 +1,5 @@
+{
+    "recommendations": [
+      "ionic.ionic"
+    ]
+}

+ 3 - 0
.vscode/settings.json

@@ -0,0 +1,3 @@
+{
+  "typescript.preferences.autoImportFileExcludePatterns": ["@ionic/angular/common", "@ionic/angular/standalone"]
+}

+ 74 - 0
ng-case/src/app/app-routing.module.ts

@@ -0,0 +1,74 @@
+import { NgModule } from '@angular/core';
+import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
+
+const routes: Routes = [
+  {
+    path: '',
+    loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
+  },
+  {
+    path: 'user',
+    loadChildren: () => import('../../../src/app/user/user.module').then(m => m.UserModule)
+  },
+  {
+    path:"study",
+    children:[
+      {
+        path: 'tsdatatype',
+        loadChildren: () => import('../modules/study/case-angular/pages/tsdatatype/tsdatatype.module').then( m => m.TsdatatypePageModule)
+      },
+      {
+        path: 'slider',
+        loadChildren: () => import('../modules/study/case-js-module/pages/slider/slider.module').then( m => m.SliderPageModule)
+      },
+      {
+        path: 'case-edit',
+        loadChildren: () => import('../modules/study/case-angular/pages/case-edit/case-edit.module').then( m => m.CaseEditPageModule)
+      },
+      {
+        path: 'echarts',
+        loadChildren: () => import('../modules/study/case-echarts/case-echarts.module').then( m => m.CaseEchartsPageModule)
+      },
+      {
+        path: 'babylon',
+        loadChildren: () => import('../modules/babylon/pages/case-babylon/case-babylon.module').then( m => m.CaseBabylonPageModule)
+      },
+      {
+        path: 'parse',
+        loadChildren: () => import('../modules/study/case-js-module/case-parse/case-parse.module').then( m => m.CaseParsePageModule)
+      },
+      {
+        path: 'case-petmarket',
+        loadChildren: () => import('../modules/study/case-js-module/case-parse-petmarket/case-parse-petmarket.module').then( m => m.CaseParsePetmarketPageModule)
+      },
+      {
+        path: 'pet/:id',
+        loadChildren: () => import('../modules/study/case-js-module/case-pet-detail/case-pet-detail.module').then( m => m.CasePetDetailPageModule)
+      },
+      {
+        path: 'pet/edit/:id', // 编辑宠物
+        loadChildren: () => import('../modules/study/case-js-module/case-pet-edit/case-pet-edit.module').then( m => m.CasePetEditPageModule)
+      },
+    ]
+  },
+  {
+    path: 'aigc',
+    loadChildren: () => import('../modules/aigc/aigc.module').then(m => m.AigcModule)
+  },
+ 
+
+
+
+ 
+
+  
+
+ 
+];
+@NgModule({
+  imports: [
+    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
+  ],
+  exports: [RouterModule]
+})
+export class AppRoutingModule {}

+ 3 - 0
ng-case/src/app/app.component.html

@@ -0,0 +1,3 @@
+<ion-app>
+  <ion-router-outlet></ion-router-outlet>
+</ion-app>

+ 0 - 0
ng-case/src/app/app.component.scss


+ 21 - 0
ng-case/src/app/app.component.spec.ts

@@ -0,0 +1,21 @@
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { TestBed } from '@angular/core/testing';
+
+import { AppComponent } from './app.component';
+
+describe('AppComponent', () => {
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [AppComponent],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
+    }).compileComponents();
+  });
+
+  it('should create the app', () => {
+    const fixture = TestBed.createComponent(AppComponent);
+    const app = fixture.componentInstance;
+    expect(app).toBeTruthy();
+  });
+
+});

+ 16 - 0
ng-case/src/app/app.component.ts

@@ -0,0 +1,16 @@
+import { Component } from '@angular/core';
+import * as Parse from "parse";
+Parse.initialize("dev");
+(Parse as any).serverURL = 'http://web2023.fmode.cn:9999/parse'
+
+@Component({
+  selector: 'app-root',
+  templateUrl: 'app.component.html',
+  styleUrls: ['app.component.scss'],
+})
+export class AppComponent {
+  constructor() {
+
+
+  }
+}

+ 16 - 0
ng-case/src/app/app.module.ts

@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { BrowserModule } from '@angular/platform-browser';
+import { RouteReuseStrategy } from '@angular/router';
+
+import { IonicModule, IonicRouteStrategy } from '@ionic/angular';
+
+import { AppRoutingModule } from './app-routing.module';
+import { AppComponent } from './app.component';
+
+@NgModule({
+  declarations: [AppComponent],
+  imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
+  providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy }],
+  bootstrap: [AppComponent],
+})
+export class AppModule {}

+ 4 - 0
ng-case/src/app/explore-container/explore-container.component.html

@@ -0,0 +1,4 @@
+<div id="container">
+  <strong>{{ name }}</strong>
+  <p>Explore <a target="_blank" rel="noopener noreferrer" href="https://ionicframework.com/docs/components">UI Components</a></p>
+</div>

+ 27 - 0
ng-case/src/app/explore-container/explore-container.component.scss

@@ -0,0 +1,27 @@
+#container {
+  text-align: center;
+
+  position: absolute;
+  left: 0;
+  right: 0;
+  top: 50%;
+  transform: translateY(-50%);
+}
+
+#container strong {
+  font-size: 20px;
+  line-height: 26px;
+}
+
+#container p {
+  font-size: 16px;
+  line-height: 22px;
+
+  color: #8c8c8c;
+
+  margin: 0;
+}
+
+#container a {
+  text-decoration: none;
+}

+ 24 - 0
ng-case/src/app/explore-container/explore-container.component.spec.ts

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

+ 12 - 0
ng-case/src/app/explore-container/explore-container.component.ts

@@ -0,0 +1,12 @@
+import { Component, Input } from '@angular/core';
+
+@Component({
+  selector: 'app-explore-container',
+  templateUrl: './explore-container.component.html',
+  styleUrls: ['./explore-container.component.scss'],
+})
+export class ExploreContainerComponent {
+
+  @Input() name?: string;
+
+}

+ 14 - 0
ng-case/src/app/explore-container/explore-container.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { ExploreContainerComponent } from './explore-container.component';
+
+@NgModule({
+  imports: [ CommonModule, FormsModule, IonicModule],
+  declarations: [ExploreContainerComponent],
+  exports: [ExploreContainerComponent]
+})
+export class ExploreContainerComponentModule {}

+ 16 - 0
ng-case/src/app/tab1/tab1-routing.module.ts

@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { Tab1Page } from './tab1.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: Tab1Page,
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class Tab1PageRoutingModule {}

+ 20 - 0
ng-case/src/app/tab1/tab1.module.ts

@@ -0,0 +1,20 @@
+import { IonicModule } from '@ionic/angular';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Tab1Page } from './tab1.page';
+import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+
+import { Tab1PageRoutingModule } from './tab1-routing.module';
+
+@NgModule({
+  imports: [
+    IonicModule,
+    CommonModule,
+    FormsModule,
+    ExploreContainerComponentModule,
+    Tab1PageRoutingModule
+  ],
+  declarations: [Tab1Page]
+})
+export class Tab1PageModule {}

+ 56 - 0
ng-case/src/app/tab1/tab1.page.html

@@ -0,0 +1,56 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>
+      教学案例库
+    </ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">教学案例库</ion-title>
+    </ion-toolbar>
+  </ion-header>
+
+  <ion-segment [(ngModel)]="currentTab">
+    <ion-segment-button value="static">
+      纯静态案例
+    </ion-segment-button>
+    <ion-segment-button value="parse">
+      微服务案例
+    </ion-segment-button>
+  </ion-segment>
+  <div *ngIf="safeHTML" [innerHTML]="safeHTML"></div>
+  
+  <ion-card>
+    <ion-card-title>纯静态案例</ion-card-title>
+    <ion-card-content>
+      <ion-button expand="block" routerLink="/study/tsdatatype">语法:TS数据类型与模板</ion-button>
+      <ion-button expand="block" routerLink="/study/case-edit">交互:输入编辑组件</ion-button>
+    </ion-card-content>
+  </ion-card>
+
+  <ion-card>
+    <ion-card-title>第三方组件(非Angular)</ion-card-title>
+    <ion-card-subtitle>
+      该类组件,一般为js组件,通过types实现的兼容调用
+    </ion-card-subtitle>
+    <ion-card-content>
+      <ion-button expand="block" routerLink="/study/slider">展示:Swiper轮播图插件</ion-button>
+      <ion-button expand="block" routerLink="/study/echarts">展示:Echart数据图表</ion-button>
+      <ion-button expand="block" routerLink="/study/babylon">展示:Babylon3D渲染</ion-button>
+    </ion-card-content>
+  </ion-card>
+
+  <ion-card>
+    <ion-card-title>微服务案例</ion-card-title>
+    <ion-card-content>
+      <ion-button expand="block" routerLink="/study/parse">展示:Pasre微服务-极简列表</ion-button>
+      <ion-button expand="block" routerLink="/study/case-petmarket">展示:Pasre微服务-复杂列表</ion-button>
+      <ion-button expand="block" routerLink="/tabs/tab2">示例:通讯录列表</ion-button>
+      <ion-button expand="block" routerLink="/aigc/chat">示例:AI对话示例</ion-button>
+    </ion-card-content>
+  </ion-card>
+
+</ion-content>

+ 4 - 0
ng-case/src/app/tab1/tab1.page.scss

@@ -0,0 +1,4 @@
+iframe{
+    width:100%;
+    height:100%;
+}

+ 26 - 0
ng-case/src/app/tab1/tab1.page.spec.ts

@@ -0,0 +1,26 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+
+import { Tab1Page } from './tab1.page';
+
+describe('Tab1Page', () => {
+  let component: Tab1Page;
+  let fixture: ComponentFixture<Tab1Page>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [Tab1Page],
+      imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(Tab1Page);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 20 - 0
ng-case/src/app/tab1/tab1.page.ts

@@ -0,0 +1,20 @@
+import { Component } from '@angular/core';
+import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser';
+
+@Component({
+  selector: 'app-tab1',
+  templateUrl: 'tab1.page.html',
+  styleUrls: ['tab1.page.scss']
+})
+export class Tab1Page {
+
+  currentTab:string = "static"
+  safeHTML: SafeResourceUrl|undefined
+  constructor(private domSan:DomSanitizer) {
+    this.safeHTML = domSan.bypassSecurityTrustHtml(``)
+  }
+  openGit(){
+    window.open(`http://git.fmode.cn:3000/nkkj/study-ng-contact`,"_blank")
+  }
+
+}

+ 16 - 0
ng-case/src/app/tab2/tab2-routing.module.ts

@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { Tab2Page } from './tab2.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: Tab2Page,
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class Tab2PageRoutingModule {}

+ 20 - 0
ng-case/src/app/tab2/tab2.module.ts

@@ -0,0 +1,20 @@
+import { IonicModule } from '@ionic/angular';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Tab2Page } from './tab2.page';
+import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+
+import { Tab2PageRoutingModule } from './tab2-routing.module';
+
+@NgModule({
+  imports: [
+    IonicModule,
+    CommonModule,
+    FormsModule,
+    ExploreContainerComponentModule,
+    Tab2PageRoutingModule
+  ],
+  declarations: [Tab2Page]
+})
+export class Tab2PageModule {}

+ 17 - 0
ng-case/src/app/tab2/tab2.page.html

@@ -0,0 +1,17 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>
+      Tab 2
+    </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>
+
+  <app-explore-container name="Tab 2 page"></app-explore-container>
+</ion-content>

+ 0 - 0
ng-case/src/app/tab2/tab2.page.scss


+ 26 - 0
ng-case/src/app/tab2/tab2.page.spec.ts

@@ -0,0 +1,26 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+
+import { Tab2Page } from './tab2.page';
+
+describe('Tab2Page', () => {
+  let component: Tab2Page;
+  let fixture: ComponentFixture<Tab2Page>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [Tab2Page],
+      imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(Tab2Page);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 12 - 0
ng-case/src/app/tab2/tab2.page.ts

@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-tab2',
+  templateUrl: 'tab2.page.html',
+  styleUrls: ['tab2.page.scss']
+})
+export class Tab2Page {
+
+  constructor() {}
+
+}

+ 16 - 0
ng-case/src/app/tab3/tab3-routing.module.ts

@@ -0,0 +1,16 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { Tab3Page } from './tab3.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: Tab3Page,
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class Tab3PageRoutingModule {}

+ 20 - 0
ng-case/src/app/tab3/tab3.module.ts

@@ -0,0 +1,20 @@
+import { IonicModule } from '@ionic/angular';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+import { Tab3Page } from './tab3.page';
+import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+
+import { Tab3PageRoutingModule } from './tab3-routing.module';
+
+@NgModule({
+  imports: [
+    IonicModule,
+    CommonModule,
+    FormsModule,
+    ExploreContainerComponentModule,
+    Tab3PageRoutingModule
+  ],
+  declarations: [Tab3Page]
+})
+export class Tab3PageModule {}

+ 17 - 0
ng-case/src/app/tab3/tab3.page.html

@@ -0,0 +1,17 @@
+<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>

+ 0 - 0
ng-case/src/app/tab3/tab3.page.scss


+ 26 - 0
ng-case/src/app/tab3/tab3.page.spec.ts

@@ -0,0 +1,26 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { IonicModule } from '@ionic/angular';
+
+import { ExploreContainerComponentModule } from '../explore-container/explore-container.module';
+
+import { Tab3Page } from './tab3.page';
+
+describe('Tab3Page', () => {
+  let component: Tab3Page;
+  let fixture: ComponentFixture<Tab3Page>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [Tab3Page],
+      imports: [IonicModule.forRoot(), ExploreContainerComponentModule]
+    }).compileComponents();
+
+    fixture = TestBed.createComponent(Tab3Page);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 12 - 0
ng-case/src/app/tab3/tab3.page.ts

@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-tab3',
+  templateUrl: 'tab3.page.html',
+  styleUrls: ['tab3.page.scss']
+})
+export class Tab3Page {
+
+  constructor() {}
+
+}

+ 41 - 0
ng-case/src/app/tabs/tabs-routing.module.ts

@@ -0,0 +1,41 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+import { TabsPage } from './tabs.page';
+import { authGuard } from 'src/modules/user/auth.guard';
+
+const routes: Routes = [
+  {
+    path: 'tabs',
+    component: TabsPage,
+    children: [
+      {
+        path: 'tab1',
+        loadChildren: () => import('../tab1/tab1.module').then(m => m.Tab1PageModule)
+      },
+      {
+        path: 'tab2',
+        canActivate:[authGuard],
+        loadChildren: () => import('../../modules/contact/contact-list/contact-list.module').then(mod => mod.ContactListPageModule)
+      },
+      {
+        path: 'tab3',
+        loadChildren: () => import('../../../../src/app/user/mine/mine.module').then(mod => mod.MinePageModule)
+      },
+      {
+        path: '',
+        redirectTo: '/tabs/tab1',
+        pathMatch: 'full'
+      }
+    ]
+  },
+  {
+    path: '',
+    redirectTo: '/tabs/tab1',
+    pathMatch: 'full'
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+})
+export class TabsPageRoutingModule {}

+ 19 - 0
ng-case/src/app/tabs/tabs.module.ts

@@ -0,0 +1,19 @@
+import { IonicModule } from '@ionic/angular';
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { TabsPageRoutingModule } from './tabs-routing.module';
+
+import { TabsPage } from './tabs.page';
+
+@NgModule({
+  imports: [
+    IonicModule,
+    CommonModule,
+    FormsModule,
+    TabsPageRoutingModule
+  ],
+  declarations: [TabsPage]
+})
+export class TabsPageModule {}

+ 20 - 0
ng-case/src/app/tabs/tabs.page.html

@@ -0,0 +1,20 @@
+<ion-tabs>
+
+  <ion-tab-bar slot="bottom">
+    <ion-tab-button tab="tab1" href="/tabs/tab1">
+      <ion-icon aria-hidden="true" name="document"></ion-icon>
+      <ion-label>说明</ion-label>
+    </ion-tab-button>
+
+    <ion-tab-button tab="tab2" href="/tabs/tab2">
+      <ion-icon aria-hidden="true" name="ellipse"></ion-icon>
+      <ion-label>通讯录</ion-label>
+    </ion-tab-button>
+
+    <ion-tab-button tab="tab3" href="/tabs/tab3">
+      <ion-icon aria-hidden="true" name="person"></ion-icon>
+      <ion-label>我的</ion-label>
+    </ion-tab-button>
+  </ion-tab-bar>
+
+</ion-tabs>

+ 1 - 0
ng-case/src/app/tabs/tabs.page.scss

@@ -0,0 +1 @@
+

+ 26 - 0
ng-case/src/app/tabs/tabs.page.spec.ts

@@ -0,0 +1,26 @@
+import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+
+import { TabsPage } from './tabs.page';
+
+describe('TabsPage', () => {
+  let component: TabsPage;
+  let fixture: ComponentFixture<TabsPage>;
+
+  beforeEach(async () => {
+    await TestBed.configureTestingModule({
+      declarations: [TabsPage],
+      schemas: [CUSTOM_ELEMENTS_SCHEMA],
+    }).compileComponents();
+  });
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(TabsPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 12 - 0
ng-case/src/app/tabs/tabs.page.ts

@@ -0,0 +1,12 @@
+import { Component } from '@angular/core';
+
+@Component({
+  selector: 'app-tabs',
+  templateUrl: 'tabs.page.html',
+  styleUrls: ['tabs.page.scss']
+})
+export class TabsPage {
+
+  constructor() {}
+
+}

+ 2 - 0
ng-case/src/assets/3dmodel/.gitignore

@@ -0,0 +1,2 @@
+*
+!.gitignore

BIN
ng-case/src/assets/icon/favicon.png


+ 1 - 0
ng-case/src/assets/shapes.svg

@@ -0,0 +1 @@
+<svg width="350" height="140" xmlns="http://www.w3.org/2000/svg" style="background:#f6f7f9"><g fill="none" fill-rule="evenodd"><path fill="#F04141" style="mix-blend-mode:multiply" d="M61.905-34.23l96.194 54.51-66.982 54.512L22 34.887z"/><circle fill="#10DC60" style="mix-blend-mode:multiply" cx="155.5" cy="135.5" r="57.5"/><path fill="#3880FF" style="mix-blend-mode:multiply" d="M208.538 9.513l84.417 15.392L223.93 93.93z"/><path fill="#FFCE00" style="mix-blend-mode:multiply" d="M268.625 106.557l46.332-26.75 46.332 26.75v53.5l-46.332 26.75-46.332-26.75z"/><circle fill="#7044FF" style="mix-blend-mode:multiply" cx="299.5" cy="9.5" r="38.5"/><rect fill="#11D3EA" style="mix-blend-mode:multiply" transform="rotate(-60 148.47 37.886)" x="143.372" y="-7.056" width="10.196" height="89.884" rx="5.098"/><path d="M-25.389 74.253l84.86 8.107c5.498.525 9.53 5.407 9.004 10.905a10 10 0 0 1-.057.477l-12.36 85.671a10.002 10.002 0 0 1-11.634 8.42l-86.351-15.226c-5.44-.959-9.07-6.145-8.112-11.584l13.851-78.551a10 10 0 0 1 10.799-8.219z" fill="#7044FF" style="mix-blend-mode:multiply"/><circle fill="#0CD1E8" style="mix-blend-mode:multiply" cx="273.5" cy="106.5" r="20.5"/></g></svg>

File diff suppressed because it is too large
+ 12 - 0
ng-case/src/assets/swiper/swiper.min.css


+ 3 - 0
ng-case/src/environments/environment.prod.ts

@@ -0,0 +1,3 @@
+export const environment = {
+  production: true
+};

+ 16 - 0
ng-case/src/environments/environment.ts

@@ -0,0 +1,16 @@
+// This file can be replaced during build by using the `fileReplacements` array.
+// `ng build` replaces `environment.ts` with `environment.prod.ts`.
+// The list of file replacements can be found in `angular.json`.
+
+export const environment = {
+  production: false
+};
+
+/*
+ * For easier debugging in development mode, you can import the following file
+ * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`.
+ *
+ * This import should be commented out in production mode because it will have a negative impact
+ * on performance if an error is thrown.
+ */
+// import 'zone.js/plugins/zone-error';  // Included with Angular CLI.

+ 43 - 0
ng-case/src/global.scss

@@ -0,0 +1,43 @@
+
+/*
+ * App Global CSS
+ * ----------------------------------------------------------------------------
+ * Put style rules here that you want to apply globally. These styles are for
+ * the entire app and not just one component. Additionally, this file can be
+ * used as an entry point to import other CSS/Sass files to be included in the
+ * output CSS.
+ * For more information on global stylesheets, visit the documentation:
+ * https://ionicframework.com/docs/layout/global-stylesheets
+ */
+
+/* Core CSS required for Ionic components to work properly */
+@import "@ionic/angular/css/core.css";
+
+/* Basic CSS for apps built with Ionic */
+@import "@ionic/angular/css/normalize.css";
+@import "@ionic/angular/css/structure.css";
+@import "@ionic/angular/css/typography.css";
+@import "@ionic/angular/css/display.css";
+
+/* Optional CSS utils that can be commented out */
+@import "@ionic/angular/css/padding.css";
+@import "@ionic/angular/css/float-elements.css";
+@import "@ionic/angular/css/text-alignment.css";
+@import "@ionic/angular/css/text-transformation.css";
+@import "@ionic/angular/css/flex-utils.css";
+
+/**
+ * Ionic Dark Mode
+ * -----------------------------------------------------
+ * For more info, please see:
+ * https://ionicframework.com/docs/theming/dark-mode
+ */
+
+/* @import "@ionic/angular/css/palettes/dark.always.css"; */
+/* @import "@ionic/angular/css/palettes/dark.class.css"; */
+// @import "@ionic/angular/css/palettes/dark.system.css";
+
+// 引用Swiper样式
+// @import url('swiper/swiper.scss');
+// @import "swiper/swiper.min.css"
+// @import "@ionic/angular/css/palettes/dark.always.css";

+ 29 - 0
ng-case/src/index.html

@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <meta charset="utf-8" />
+  <title>Ionic App</title>
+
+  <base href="/" />
+
+  <meta name="color-scheme" content="light dark" />
+  <meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no" />
+  <meta name="format-detection" content="telephone=no" />
+  <meta name="msapplication-tap-highlight" content="no" />
+
+  <link rel="icon" type="image/png" href="assets/icon/favicon.png" />
+
+  <!-- add to homescreen for ios -->
+  <meta name="apple-mobile-web-app-capable" content="yes" />
+  <meta name="apple-mobile-web-app-status-bar-style" content="black" />
+
+  <!-- Swiper轮播图样式 -->
+  <link rel="stylesheet" href="assets/swiper/swiper.min.css">
+</head>
+
+<body>
+  <app-root></app-root>
+</body>
+
+</html>

+ 12 - 0
ng-case/src/main.ts

@@ -0,0 +1,12 @@
+import { enableProdMode } from '@angular/core';
+import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
+
+import { AppModule } from './app/app.module';
+import { environment } from './environments/environment';
+
+if (environment.production) {
+  enableProdMode();
+}
+
+platformBrowserDynamic().bootstrapModule(AppModule)
+  .catch(err => console.log(err));

+ 55 - 0
ng-case/src/polyfills.ts

@@ -0,0 +1,55 @@
+/**
+ * This file includes polyfills needed by Angular and is loaded before the app.
+ * You can add your own extra polyfills to this file.
+ *
+ * This file is divided into 2 sections:
+ *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers.
+ *   2. Application imports. Files imported after ZoneJS that should be loaded before your main
+ *      file.
+ *
+ * The current setup is for so-called "evergreen" browsers; the last versions of browsers that
+ * automatically update themselves. This includes recent versions of Safari, Chrome (including
+ * Opera), Edge on the desktop, and iOS and Chrome on mobile.
+ *
+ * Learn more in https://angular.io/guide/browser-support
+ */
+
+/***************************************************************************************************
+ * BROWSER POLYFILLS
+ */
+
+/**
+ * By default, zone.js will patch all possible macroTask and DomEvents
+ * user can disable parts of macroTask/DomEvents patch by setting following flags
+ * because those flags need to be set before `zone.js` being loaded, and webpack
+ * will put import in the top of bundle, so user need to create a separate file
+ * in this directory (for example: zone-flags.ts), and put the following flags
+ * into that file, and then add the following code before importing zone.js.
+ * import './zone-flags';
+ *
+ * The flags allowed in zone-flags.ts are listed here.
+ *
+ * The following flags will work for all browsers.
+ *
+ * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
+ * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
+ * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
+ *
+ *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
+ *  with the following flag, it will bypass `zone.js` patch for IE/Edge
+ *
+ *  (window as any).__Zone_enable_cross_context_check = true;
+ *
+ */
+ 
+import './zone-flags';
+
+/***************************************************************************************************
+ * Zone JS is required by default for Angular itself.
+ */
+import 'zone.js';  // Included with Angular CLI.
+
+
+/***************************************************************************************************
+ * APPLICATION IMPORTS
+ */

+ 14 - 0
ng-case/src/test.ts

@@ -0,0 +1,14 @@
+// This file is required by karma.conf.js and loads recursively all the .spec and framework files
+
+import 'zone.js/testing';
+import { getTestBed } from '@angular/core/testing';
+import {
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting
+} from '@angular/platform-browser-dynamic/testing';
+
+// First, initialize the Angular testing environment.
+getTestBed().initTestEnvironment(
+  BrowserDynamicTestingModule,
+  platformBrowserDynamicTesting(),
+);

+ 2 - 0
ng-case/src/theme/variables.scss

@@ -0,0 +1,2 @@
+// For information on how to create your own theme, please see:
+// http://ionicframework.com/docs/theming/

+ 6 - 0
ng-case/src/zone-flags.ts

@@ -0,0 +1,6 @@
+/**
+ * Prevents Angular change detection from
+ * running with certain Web Component callbacks
+ */
+// eslint-disable-next-line no-underscore-dangle
+(window as any).__Zone_disable_customElements = true;

+ 89 - 3
package-lock.json

@@ -23,6 +23,7 @@
         "@capacitor/status-bar": "6.0.0",
         "@ionic/angular": "^8.0.0",
         "ionicons": "^7.0.0",
+        "parse": "^5.2.0",
         "rxjs": "~7.8.0",
         "swiper": "^11.1.4",
         "tslib": "^2.3.0",
@@ -41,6 +42,7 @@
         "@capacitor/cli": "6.1.0",
         "@ionic/angular-toolkit": "^11.0.1",
         "@types/jasmine": "~5.1.0",
+        "@types/parse": "^3.0.9",
         "@typescript-eslint/eslint-plugin": "^6.0.0",
         "@typescript-eslint/parser": "^6.0.0",
         "eslint": "^8.57.0",
@@ -2523,6 +2525,18 @@
         "node": ">=6.9.0"
       }
     },
+    "node_modules/@babel/runtime-corejs3": {
+      "version": "7.24.6",
+      "resolved": "https://registry.npmmirror.com/@babel/runtime-corejs3/-/runtime-corejs3-7.24.6.tgz",
+      "integrity": "sha512-tbC3o8uHK9xMgMsvUm9qGqxVpbv6yborMBLbDteHIc7JDNHsTV0vDMQ5j1O1NXvO+BDELtL9KgoWYaUVIVGt8w==",
+      "dependencies": {
+        "core-js-pure": "^3.30.2",
+        "regenerator-runtime": "^0.14.0"
+      },
+      "engines": {
+        "node": ">=6.9.0"
+      }
+    },
     "node_modules/@babel/template": {
       "version": "7.24.7",
       "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.24.7.tgz",
@@ -5288,6 +5302,15 @@
         "@types/node": "*"
       }
     },
+    "node_modules/@types/parse": {
+      "version": "3.0.9",
+      "resolved": "https://registry.npmmirror.com/@types/parse/-/parse-3.0.9.tgz",
+      "integrity": "sha512-DGTHygc7krgmNAK8h42giwmAofCd9uv2++RD+zw6OmWI7AEnlTYZwEuWsx22SA2CSMQrZW8P2INHLpQbnQFUng==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/qs": {
       "version": "6.9.15",
       "resolved": "https://registry.npmmirror.com/@types/qs/-/qs-6.9.15.tgz",
@@ -7370,6 +7393,16 @@
         "url": "https://opencollective.com/core-js"
       }
     },
+    "node_modules/core-js-pure": {
+      "version": "3.37.1",
+      "resolved": "https://registry.npmmirror.com/core-js-pure/-/core-js-pure-3.37.1.tgz",
+      "integrity": "sha512-J/r5JTHSmzTxbiYYrzXg9w1VpqrYt+gexenBE9pugeyhwPZTAEJddyiReJWsLO6uNQ8xJZFbod6XC7KKwatCiA==",
+      "hasInstallScript": true,
+      "funding": {
+        "type": "opencollective",
+        "url": "https://opencollective.com/core-js"
+      }
+    },
     "node_modules/core-util-is": {
       "version": "1.0.3",
       "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
@@ -7514,6 +7547,12 @@
         "node": ">= 8"
       }
     },
+    "node_modules/crypto-js": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmmirror.com/crypto-js/-/crypto-js-4.2.0.tgz",
+      "integrity": "sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==",
+      "optional": true
+    },
     "node_modules/css-loader": {
       "version": "7.1.1",
       "resolved": "https://registry.npmmirror.com/css-loader/-/css-loader-7.1.1.tgz",
@@ -10092,6 +10131,11 @@
         "postcss": "^8.1.0"
       }
     },
+    "node_modules/idb-keyval": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmmirror.com/idb-keyval/-/idb-keyval-6.2.1.tgz",
+      "integrity": "sha512-8Sb3veuYCyrZL+VBt9LJfZjLUPWVvqn8tG28VqYNFCo43KHcKuq+b4EiXGeuaLAQWL2YmyDgMp2aSpH9JHsEQg=="
+    },
     "node_modules/ieee754": {
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/ieee754/-/ieee754-1.2.1.tgz",
@@ -13383,6 +13427,25 @@
         "node": ">=6"
       }
     },
+    "node_modules/parse": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmmirror.com/parse/-/parse-5.2.0.tgz",
+      "integrity": "sha512-FoD3kcLAQCw/2J1984sl3GUBzbHE2tA9mUcyw/EBWZ46WVZTzV+kjnA5tttXyzN4uodt21wSluzjbGnyLqreKw==",
+      "dependencies": {
+        "@babel/runtime-corejs3": "7.24.6",
+        "idb-keyval": "6.2.1",
+        "react-native-crypto-js": "1.0.0",
+        "uuid": "10.0.0",
+        "ws": "8.17.1",
+        "xmlhttprequest": "1.8.0"
+      },
+      "engines": {
+        "node": ">=18 <21"
+      },
+      "optionalDependencies": {
+        "crypto-js": "4.2.0"
+      }
+    },
     "node_modules/parse-imports": {
       "version": "2.1.1",
       "resolved": "https://registry.npmmirror.com/parse-imports/-/parse-imports-2.1.1.tgz",
@@ -13435,6 +13498,18 @@
         "node": ">= 0.10"
       }
     },
+    "node_modules/parse/node_modules/uuid": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmmirror.com/uuid/-/uuid-10.0.0.tgz",
+      "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
     "node_modules/parse5": {
       "version": "7.1.2",
       "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz",
@@ -14076,6 +14151,11 @@
       "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==",
       "dev": true
     },
+    "node_modules/react-native-crypto-js": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz",
+      "integrity": "sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA=="
+    },
     "node_modules/readable-stream": {
       "version": "3.6.2",
       "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-3.6.2.tgz",
@@ -14141,8 +14221,7 @@
     "node_modules/regenerator-runtime": {
       "version": "0.14.1",
       "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
-      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
-      "dev": true
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw=="
     },
     "node_modules/regenerator-transform": {
       "version": "0.15.2",
@@ -17345,7 +17424,6 @@
       "version": "8.17.1",
       "resolved": "https://registry.npmmirror.com/ws/-/ws-8.17.1.tgz",
       "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==",
-      "dev": true,
       "engines": {
         "node": ">=10.0.0"
       },
@@ -17393,6 +17471,14 @@
         "node": ">=8.0"
       }
     },
+    "node_modules/xmlhttprequest": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmmirror.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz",
+      "integrity": "sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/y18n": {
       "version": "5.0.8",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-5.0.8.tgz",

+ 2 - 0
package.json

@@ -28,6 +28,7 @@
     "@capacitor/status-bar": "6.0.0",
     "@ionic/angular": "^8.0.0",
     "ionicons": "^7.0.0",
+    "parse": "^5.2.0",
     "rxjs": "~7.8.0",
     "swiper": "^11.1.4",
     "tslib": "^2.3.0",
@@ -46,6 +47,7 @@
     "@capacitor/cli": "6.1.0",
     "@ionic/angular-toolkit": "^11.0.1",
     "@types/jasmine": "~5.1.0",
+    "@types/parse": "^3.0.9",
     "@typescript-eslint/eslint-plugin": "^6.0.0",
     "@typescript-eslint/parser": "^6.0.0",
     "eslint": "^8.57.0",

+ 42 - 0
server/case/promise.js

@@ -0,0 +1,42 @@
+
+async function main(){
+    // setTimeout(() => {
+    //     console.log(1)
+    // }, 500);
+    // setTimeout(() => {
+    //     console.log(2)
+    // }, 200);
+    // setTimeout(() => {
+    //     console.log(3)
+    // }, 100);
+    // setTimeout(() => {
+    //     console.log(4)
+    // }, 1000);
+    // return
+
+    // waitSeconds(500,()=>{console.log(1)}) // 500
+    // waitSeconds(200,()=>{console.log(2)}) // 700
+    // waitSeconds(100,()=>{console.log(3)}) // 800
+    // waitSeconds(1000,()=>{console.log(4)}) // 1800
+    // return
+    
+    let res1 = await waitSeconds(500,()=>{console.log(1)}) // 500
+    console.log(res1)
+    let res2 = await waitSeconds(200,()=>{console.log(2)}) // 700
+    console.log(res2)
+    let res3 = await waitSeconds(100,()=>{console.log(3)}) // 800
+    console.log(res3)
+    let res4 = await waitSeconds(1000,()=>{console.log(4)}) // 1800
+    console.log(res4)
+}
+
+function waitSeconds(duration,handle) {
+    return new Promise((resolve, reject) => {
+        setTimeout(() => {
+            handle()
+            resolve(`等待了${duration}ms`);
+        }, duration);
+    });
+}
+
+main()

+ 40 - 0
server/fmode-parse-test.js

@@ -0,0 +1,40 @@
+const { ParseObject } = require("./fmode-parse");
+
+async function main(){
+    // testFind()
+    // testSave()
+    testUpdate()
+}
+
+async function testUpdate(){
+    // 根据ID获取羊驼
+    let yangtuo = new ParseObject("Pet");
+    yangtuo = await yangtuo.get("XOeof1ppOS")
+    console.log(yangtuo)
+    // 根据有ID的对象,保存新属性
+    yangtuo.set({
+        price:300
+    })
+    yangtuo.save()
+
+}
+async function testFind(){
+        // 查询测试
+        let Pet = new ParseObject("Pet")
+        let list = await Pet.findAll()
+        console.log(list)
+    
+}
+async function testSave(){
+       // 创建测试
+       let yangtuo = new ParseObject("Pet")
+       yangtuo.set({
+           name:"小羊驼",
+           type:"羊驼",
+           price:30000
+       })
+       yangtuo = await yangtuo.save();
+       console.log(yangtuo) // id XOeof1ppOS
+       console.log(yangtuo.toJSON())
+}
+main()

+ 110 - 0
server/fmode-parse.js

@@ -0,0 +1,110 @@
+class ParseObject{
+    className
+    id
+    serverURL = "http://web2023.fmode.cn:9999/parse"
+    data = {}
+    toJSON(){
+        return this.data
+    }
+    constructor(className){
+        this.className = className
+    }
+    async get(id){
+        let response = await fetch(this.serverURL+"/classes/"+this.className+"/"+id, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          if(response?.status=="200"){
+            let json = await response.json()
+            this.id = json?.objectId;
+            delete json.objectId
+            delete json.createdAt
+            delete json.updatedAt
+            this.data = json;
+            return this
+          }else{
+            return []
+          }
+    }
+    async findAll(){
+        let response = await fetch(this.serverURL+"/classes/"+this.className, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "GET",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          // console.log(response)
+          // return []
+          if(response?.status=="200"){
+            let json = await response.json()
+            // console.log(json)
+            return json?.results || []
+          }else{
+            return []
+          }
+    }
+    set(data){
+        this.data = data
+    }
+    async save(){
+        let body = JSON.stringify(this.data)
+        let url = this.serverURL+"/classes/"+this.className // 创建URL
+        let method = "POST"
+        if(this.id){
+            url = url + "/" + this.id // 更新URL
+            method = "PUT"
+        }
+        console.log(url,method,body)
+        let response = await fetch(url, {
+            "headers": {
+                // "content-type": "text/plain;charset=UTF-8",
+                "x-parse-application-id": "dev"
+            },
+            "body": body,
+            "method": method,
+            "mode": "cors",
+            "credentials": "omit"
+        });
+        console.log(body)
+        let text = await response?.text();
+        console.log(text)
+        let json = await response?.json();
+        if(json?.objectId){
+            console.log(json)
+            this.id = json?.objectId
+            return this
+        }else{
+            return null
+        }
+    }
+    async delete(){
+        let response = await fetch(this.serverURL+"/classes/"+this.className+"/"+id, {
+            "headers": {
+                "x-parse-application-id": "dev"
+              },
+            "body": null,
+            "method": "DELETE",
+            "mode": "cors",
+            "credentials": "omit"
+          });
+          if(response?.status=="200"){
+            let json = await response.json()
+            return json
+          }else{
+            return []
+          }
+    }
+}
+module.exports.ParseObject = ParseObject
+
+class ParseQuery{
+    
+}

+ 106 - 0
server/import/BoleMbti.js

@@ -0,0 +1,106 @@
+// 导入BoleMbti脚本
+
+// 引用Parse JS SDK
+const Parse = require("parse/node");
+Parse.initialize("dev"); // 设置applicationId
+Parse.serverURL = "http://web2023.fmode.cn:9999/parse"; // 设置serverURL
+
+let mbtiData = {
+    "ISTJ": {
+        "personality": "ISTJ - 守护者",
+        "suitable_jobs": ["会计师", "行政助理", "工程师"]
+    },
+    "ISFJ": {
+        "personality": "ISFJ - 慈爱者",
+        "suitable_jobs": ["护士", "社会工作者", "图书管理员"]
+    },
+    "INFJ": {
+        "personality": "INFJ - 门徒",
+        "suitable_jobs": ["心理学家", "作家", "艺术家"]
+    },
+    "INTJ": {
+        "personality": "INTJ - 学者",
+        "suitable_jobs": ["科学家", "律师", "企业家"]
+    },
+    "ISTP": {
+        "personality": "ISTP - 冒险家",
+        "suitable_jobs": ["机械师", "飞行员", "摄影师"]
+    },
+    "ISFP": {
+        "personality": "ISFP - 艺术家",
+        "suitable_jobs": ["插画师", "音乐家", "花艺设计师"]
+    },
+    "INFP": {
+        "personality": "INFP - 梦想家",
+        "suitable_jobs": ["心理咨询师", "作家", "教师"]
+    },
+    "INTP": {
+        "personality": "INTP - 学者",
+        "suitable_jobs": ["程序员", "研究员", "工程师"]
+    },
+    "ESTP": {
+        "personality": "ESTP - 实干家",
+        "suitable_jobs": ["销售员", "运动员", "警察"]
+    },
+    "ESFP": {
+        "personality": "ESFP - 表演者",
+        "suitable_jobs": ["演员", "主持人", "舞蹈编导"]
+    },
+    "ENFP": {
+        "personality": "ENFP - 公关者",
+        "suitable_jobs": ["市场营销专员", "活动策划师", "公关顾问"]
+    },
+    "ENTP": {
+        "personality": "ENTP - 挑战者",
+        "suitable_jobs": ["企业顾问", "律师", "投资者"]
+    },
+    "ESTJ": {
+        "personality": "ESTJ - 主管",
+        "suitable_jobs": ["项目经理", "军官", "警察"]
+    },
+    "ESFJ": {
+        "personality": "ESFJ - 供应者",
+        "suitable_jobs": ["教育工作者", "医生", "社会工作者"]
+    },
+    "ENFJ": {
+        "personality": "ENFJ - 慈善家",
+        "suitable_jobs": ["心理咨询师", "教育家", "公益组织工作者"]
+    },
+    "ENTJ": {
+        "personality": "ENTJ - 指挥官",
+        "suitable_jobs": ["CEO", "政治家", "项目经理"]
+    }
+}
+
+function importAll(){
+    let MbtiList = []
+    Object.keys(mbtiData).forEach(key=>{
+        MbtiList.push({
+            result:key,
+            personality:mbtiData[key]?.personality,
+            suitableJobs:mbtiData[key]?.suitable_jobs,
+        })
+    })
+
+    console.log(MbtiList)
+    MbtiList.forEach(async mbti => {
+        // 查重
+        let exists = await checkExists(mbti)
+        if(exists?.id) return;
+        // 新增
+        let BoleMbti = Parse.Object.extend("BoleMbti");
+        let bm = new BoleMbti();
+        bm.set(mbti);
+        bm.save();
+    });
+}
+
+async function checkExists(mbti){
+    let query = new Parse.Query("BoleMbti");
+    query.equalTo("result",mbti?.result);
+    query.equalTo("personality",mbti?.personality);
+    await query.first();
+    return await query.first();
+}
+
+importAll()

+ 1 - 0
server/import/libai/.gitignore

@@ -0,0 +1 @@
+node_modules/

+ 56 - 0
server/import/libai/README.md

@@ -0,0 +1,56 @@
+# 示例:古诗文资料获取并转存数据库
+
+# 资料站点
+- 资料:https://www.gushicimingju.com/
+- 数据范式:Shige
+
+# 数据范式 Shige
+- title:String 题目
+- author:String 作者
+- dyansty:String 朝代
+- content:Stirng 内容
+- intro:String 简介
+- yiwen:String 译文(古译今)
+- note:String 注释
+- review:String 赏析
+
+# 任务1:爬取数据集,并形成Shige数组
+- 目标:唐诗三百首全集 https://www.gushicimingju.com/gushi/tangshisanbaishou/
+
+## 纯前端DOM批量分析
+- 右键,分析目标站点,同类DOM值的selector特点
+    - 选择器特征:.main-content内容区域所有的 a标签
+    - 字符串特征:href="/gushi/shi/586.html"
+``` js
+
+let ElArray = document.querySelectorAll(".main-content a")
+let list = []
+for(let i=0;i<ElArray.length;i++){
+    list[i] = ElArray[i]
+}
+// HTMLElement 转换成 href字符串 "https://www.gushicimingju.com/gushi/shi/113.html"
+console.log("DOM选择后,数量:",list.length)
+list = list.map(el=>el.href)
+// 过滤符合特征的链接地址
+list = list.filter(item=>(item?.indexOf("/gushi/shi/")>-1))
+console.log("字符过滤后,数量:",list.length)
+// 将list数据保存至href.js
+
+```
+
+## 后端的页面分析
+
+### 依赖环境
+https://www.npmjs.com/package/cheerio
+
+``` bash
+npm i cheerio -S
+```
+
+# 任务2:将数组批量导入系统
+- 参考代码:import.js
+    - 实现href链接html数据获取
+    - 通过cheerio进行DOM分析
+    - 通过css selector标记准确字段
+    - Parse.Query查重
+    - Parse.Object保存

+ 297 - 0
server/import/libai/href.js

@@ -0,0 +1,297 @@
+module.exports.GushiHrefList = [
+    "https://www.gushicimingju.com/gushi/shi/188017.html",
+    "https://www.gushicimingju.com/gushi/shi/52.html",
+    "https://www.gushicimingju.com/gushi/shi/102.html",
+    "https://www.gushicimingju.com/gushi/shi/5.html",
+    "https://www.gushicimingju.com/gushi/shi/57.html",
+    "https://www.gushicimingju.com/gushi/shi/103.html",
+    "https://www.gushicimingju.com/gushi/shi/279.html",
+    "https://www.gushicimingju.com/gushi/shi/109.html",
+    "https://www.gushicimingju.com/gushi/shi/100.html",
+    "https://www.gushicimingju.com/gushi/shi/101.html",
+    "https://www.gushicimingju.com/gushi/shi/110.html",
+    "https://www.gushicimingju.com/gushi/shi/111.html",
+    "https://www.gushicimingju.com/gushi/shi/118.html",
+    "https://www.gushicimingju.com/gushi/shi/113.html",
+    "https://www.gushicimingju.com/gushi/shi/119.html",
+    "https://www.gushicimingju.com/gushi/shi/481.html",
+    "https://www.gushicimingju.com/gushi/shi/114.html",
+    "https://www.gushicimingju.com/gushi/shi/539.html",
+    "https://www.gushicimingju.com/gushi/shi/540.html",
+    "https://www.gushicimingju.com/gushi/shi/121.html",
+    "https://www.gushicimingju.com/gushi/shi/541.html",
+    "https://www.gushicimingju.com/gushi/shi/542.html",
+    "https://www.gushicimingju.com/gushi/shi/544.html",
+    "https://www.gushicimingju.com/gushi/shi/545.html",
+    "https://www.gushicimingju.com/gushi/shi/546.html",
+    "https://www.gushicimingju.com/gushi/shi/547.html",
+    "https://www.gushicimingju.com/gushi/shi/548.html",
+    "https://www.gushicimingju.com/gushi/shi/549.html",
+    "https://www.gushicimingju.com/gushi/shi/550.html",
+    "https://www.gushicimingju.com/gushi/shi/551.html",
+    "https://www.gushicimingju.com/gushi/shi/552.html",
+    "https://www.gushicimingju.com/gushi/shi/553.html",
+    "https://www.gushicimingju.com/gushi/shi/117.html",
+    "https://www.gushicimingju.com/gushi/shi/149.html",
+    "https://www.gushicimingju.com/gushi/shi/556.html",
+    "https://www.gushicimingju.com/gushi/shi/394.html",
+    "https://www.gushicimingju.com/gushi/shi/554.html",
+    "https://www.gushicimingju.com/gushi/shi/555.html",
+    "https://www.gushicimingju.com/gushi/shi/557.html",
+    "https://www.gushicimingju.com/gushi/shi/95.html",
+    "https://www.gushicimingju.com/gushi/shi/563.html",
+    "https://www.gushicimingju.com/gushi/shi/448.html",
+    "https://www.gushicimingju.com/gushi/shi/564.html",
+    "https://www.gushicimingju.com/gushi/shi/449.html",
+    "https://www.gushicimingju.com/gushi/shi/227.html",
+    "https://www.gushicimingju.com/gushi/shi/558.html",
+    "https://www.gushicimingju.com/gushi/shi/559.html",
+    "https://www.gushicimingju.com/gushi/shi/560.html",
+    "https://www.gushicimingju.com/gushi/shi/561.html",
+    "https://www.gushicimingju.com/gushi/shi/562.html",
+    "https://www.gushicimingju.com/gushi/shi/483.html",
+    "https://www.gushicimingju.com/gushi/shi/207.html",
+    "https://www.gushicimingju.com/gushi/shi/208.html",
+    "https://www.gushicimingju.com/gushi/shi/204.html",
+    "https://www.gushicimingju.com/gushi/shi/565.html",
+    "https://www.gushicimingju.com/gushi/shi/566.html",
+    "https://www.gushicimingju.com/gushi/shi/567.html",
+    "https://www.gushicimingju.com/gushi/shi/280.html",
+    "https://www.gushicimingju.com/gushi/shi/568.html",
+    "https://www.gushicimingju.com/gushi/shi/569.html",
+    "https://www.gushicimingju.com/gushi/shi/570.html",
+    "https://www.gushicimingju.com/gushi/shi/571.html",
+    "https://www.gushicimingju.com/gushi/shi/572.html",
+    "https://www.gushicimingju.com/gushi/shi/573.html",
+    "https://www.gushicimingju.com/gushi/shi/173.html",
+    "https://www.gushicimingju.com/gushi/shi/14.html",
+    "https://www.gushicimingju.com/gushi/shi/139.html",
+    "https://www.gushicimingju.com/gushi/shi/574.html",
+    "https://www.gushicimingju.com/gushi/shi/105.html",
+    "https://www.gushicimingju.com/gushi/shi/575.html",
+    "https://www.gushicimingju.com/gushi/shi/576.html",
+    "https://www.gushicimingju.com/gushi/shi/473.html",
+    "https://www.gushicimingju.com/gushi/shi/120.html",
+    "https://www.gushicimingju.com/gushi/shi/21.html",
+    "https://www.gushicimingju.com/gushi/shi/150.html",
+    "https://www.gushicimingju.com/gushi/shi/25.html",
+    "https://www.gushicimingju.com/gushi/shi/7.html",
+    "https://www.gushicimingju.com/gushi/shi/375.html",
+    "https://www.gushicimingju.com/gushi/shi/386.html",
+    "https://www.gushicimingju.com/gushi/shi/459.html",
+    "https://www.gushicimingju.com/gushi/shi/577.html",
+    "https://www.gushicimingju.com/gushi/shi/578.html",
+    "https://www.gushicimingju.com/gushi/shi/259.html",
+    "https://www.gushicimingju.com/gushi/shi/30.html",
+    "https://www.gushicimingju.com/gushi/shi/83.html",
+    "https://www.gushicimingju.com/gushi/shi/579.html",
+    "https://www.gushicimingju.com/gushi/shi/580.html",
+    "https://www.gushicimingju.com/gushi/shi/581.html",
+    "https://www.gushicimingju.com/gushi/shi/583.html",
+    "https://www.gushicimingju.com/gushi/shi/543.html",
+    "https://www.gushicimingju.com/gushi/shi/584.html",
+    "https://www.gushicimingju.com/gushi/shi/585.html",
+    "https://www.gushicimingju.com/gushi/shi/451.html",
+    "https://www.gushicimingju.com/gushi/shi/65.html",
+    "https://www.gushicimingju.com/gushi/shi/586.html",
+    "https://www.gushicimingju.com/gushi/shi/587.html",
+    "https://www.gushicimingju.com/gushi/shi/10.html",
+    "https://www.gushicimingju.com/gushi/shi/376.html",
+    "https://www.gushicimingju.com/gushi/shi/588.html",
+    "https://www.gushicimingju.com/gushi/shi/589.html",
+    "https://www.gushicimingju.com/gushi/shi/384.html",
+    "https://www.gushicimingju.com/gushi/shi/590.html",
+    "https://www.gushicimingju.com/gushi/shi/591.html",
+    "https://www.gushicimingju.com/gushi/shi/592.html",
+    "https://www.gushicimingju.com/gushi/shi/383.html",
+    "https://www.gushicimingju.com/gushi/shi/75.html",
+    "https://www.gushicimingju.com/gushi/shi/593.html",
+    "https://www.gushicimingju.com/gushi/shi/16.html",
+    "https://www.gushicimingju.com/gushi/shi/594.html",
+    "https://www.gushicimingju.com/gushi/shi/476.html",
+    "https://www.gushicimingju.com/gushi/shi/595.html",
+    "https://www.gushicimingju.com/gushi/shi/596.html",
+    "https://www.gushicimingju.com/gushi/shi/597.html",
+    "https://www.gushicimingju.com/gushi/shi/477.html",
+    "https://www.gushicimingju.com/gushi/shi/467.html",
+    "https://www.gushicimingju.com/gushi/shi/598.html",
+    "https://www.gushicimingju.com/gushi/shi/485.html",
+    "https://www.gushicimingju.com/gushi/shi/599.html",
+    "https://www.gushicimingju.com/gushi/shi/484.html",
+    "https://www.gushicimingju.com/gushi/shi/115.html",
+    "https://www.gushicimingju.com/gushi/shi/600.html",
+    "https://www.gushicimingju.com/gushi/shi/601.html",
+    "https://www.gushicimingju.com/gushi/shi/602.html",
+    "https://www.gushicimingju.com/gushi/shi/480.html",
+    "https://www.gushicimingju.com/gushi/shi/603.html",
+    "https://www.gushicimingju.com/gushi/shi/604.html",
+    "https://www.gushicimingju.com/gushi/shi/605.html",
+    "https://www.gushicimingju.com/gushi/shi/606.html",
+    "https://www.gushicimingju.com/gushi/shi/607.html",
+    "https://www.gushicimingju.com/gushi/shi/608.html",
+    "https://www.gushicimingju.com/gushi/shi/609.html",
+    "https://www.gushicimingju.com/gushi/shi/610.html",
+    "https://www.gushicimingju.com/gushi/shi/611.html",
+    "https://www.gushicimingju.com/gushi/shi/612.html",
+    "https://www.gushicimingju.com/gushi/shi/614.html",
+    "https://www.gushicimingju.com/gushi/shi/615.html",
+    "https://www.gushicimingju.com/gushi/shi/616.html",
+    "https://www.gushicimingju.com/gushi/shi/617.html",
+    "https://www.gushicimingju.com/gushi/shi/618.html",
+    "https://www.gushicimingju.com/gushi/shi/619.html",
+    "https://www.gushicimingju.com/gushi/shi/620.html",
+    "https://www.gushicimingju.com/gushi/shi/621.html",
+    "https://www.gushicimingju.com/gushi/shi/144.html",
+    "https://www.gushicimingju.com/gushi/shi/622.html",
+    "https://www.gushicimingju.com/gushi/shi/623.html",
+    "https://www.gushicimingju.com/gushi/shi/624.html",
+    "https://www.gushicimingju.com/gushi/shi/129.html",
+    "https://www.gushicimingju.com/gushi/shi/625.html",
+    "https://www.gushicimingju.com/gushi/shi/169.html",
+    "https://www.gushicimingju.com/gushi/shi/626.html",
+    "https://www.gushicimingju.com/gushi/shi/130.html",
+    "https://www.gushicimingju.com/gushi/shi/627.html",
+    "https://www.gushicimingju.com/gushi/shi/628.html",
+    "https://www.gushicimingju.com/gushi/shi/629.html",
+    "https://www.gushicimingju.com/gushi/shi/630.html",
+    "https://www.gushicimingju.com/gushi/shi/631.html",
+    "https://www.gushicimingju.com/gushi/shi/632.html",
+    "https://www.gushicimingju.com/gushi/shi/633.html",
+    "https://www.gushicimingju.com/gushi/shi/634.html",
+    "https://www.gushicimingju.com/gushi/shi/635.html",
+    "https://www.gushicimingju.com/gushi/shi/465.html",
+    "https://www.gushicimingju.com/gushi/shi/636.html",
+    "https://www.gushicimingju.com/gushi/shi/638.html",
+    "https://www.gushicimingju.com/gushi/shi/639.html",
+    "https://www.gushicimingju.com/gushi/shi/640.html",
+    "https://www.gushicimingju.com/gushi/shi/69.html",
+    "https://www.gushicimingju.com/gushi/shi/641.html",
+    "https://www.gushicimingju.com/gushi/shi/642.html",
+    "https://www.gushicimingju.com/gushi/shi/643.html",
+    "https://www.gushicimingju.com/gushi/shi/644.html",
+    "https://www.gushicimingju.com/gushi/shi/645.html",
+    "https://www.gushicimingju.com/gushi/shi/646.html",
+    "https://www.gushicimingju.com/gushi/shi/457.html",
+    "https://www.gushicimingju.com/gushi/shi/385.html",
+    "https://www.gushicimingju.com/gushi/shi/647.html",
+    "https://www.gushicimingju.com/gushi/shi/648.html",
+    "https://www.gushicimingju.com/gushi/shi/458.html",
+    "https://www.gushicimingju.com/gushi/shi/378.html",
+    "https://www.gushicimingju.com/gushi/shi/649.html",
+    "https://www.gushicimingju.com/gushi/shi/650.html",
+    "https://www.gushicimingju.com/gushi/shi/651.html",
+    "https://www.gushicimingju.com/gushi/shi/652.html",
+    "https://www.gushicimingju.com/gushi/shi/653.html",
+    "https://www.gushicimingju.com/gushi/shi/654.html",
+    "https://www.gushicimingju.com/gushi/shi/655.html",
+    "https://www.gushicimingju.com/gushi/shi/656.html",
+    "https://www.gushicimingju.com/gushi/shi/657.html",
+    "https://www.gushicimingju.com/gushi/shi/658.html",
+    "https://www.gushicimingju.com/gushi/shi/659.html",
+    "https://www.gushicimingju.com/gushi/shi/660.html",
+    "https://www.gushicimingju.com/gushi/shi/661.html",
+    "https://www.gushicimingju.com/gushi/shi/662.html",
+    "https://www.gushicimingju.com/gushi/shi/428.html",
+    "https://www.gushicimingju.com/gushi/shi/663.html",
+    "https://www.gushicimingju.com/gushi/shi/664.html",
+    "https://www.gushicimingju.com/gushi/shi/497.html",
+    "https://www.gushicimingju.com/gushi/shi/665.html",
+    "https://www.gushicimingju.com/gushi/shi/123.html",
+    "https://www.gushicimingju.com/gushi/shi/122.html",
+    "https://www.gushicimingju.com/gushi/shi/666.html",
+    "https://www.gushicimingju.com/gushi/shi/308.html",
+    "https://www.gushicimingju.com/gushi/shi/667.html",
+    "https://www.gushicimingju.com/gushi/shi/125.html",
+    "https://www.gushicimingju.com/gushi/shi/668.html",
+    "https://www.gushicimingju.com/gushi/shi/407.html",
+    "https://www.gushicimingju.com/gushi/shi/669.html",
+    "https://www.gushicimingju.com/gushi/shi/670.html",
+    "https://www.gushicimingju.com/gushi/shi/671.html",
+    "https://www.gushicimingju.com/gushi/shi/468.html",
+    "https://www.gushicimingju.com/gushi/shi/469.html",
+    "https://www.gushicimingju.com/gushi/shi/111.html",
+    "https://www.gushicimingju.com/gushi/shi/15.html",
+    "https://www.gushicimingju.com/gushi/shi/672.html",
+    "https://www.gushicimingju.com/gushi/shi/673.html",
+    "https://www.gushicimingju.com/gushi/shi/674.html",
+    "https://www.gushicimingju.com/gushi/shi/482.html",
+    "https://www.gushicimingju.com/gushi/shi/479.html",
+    "https://www.gushicimingju.com/gushi/shi/22.html",
+    "https://www.gushicimingju.com/gushi/shi/388.html",
+    "https://www.gushicimingju.com/gushi/shi/276.html",
+    "https://www.gushicimingju.com/gushi/shi/486.html",
+    "https://www.gushicimingju.com/gushi/shi/675.html",
+    "https://www.gushicimingju.com/gushi/shi/676.html",
+    "https://www.gushicimingju.com/gushi/shi/677.html",
+    "https://www.gushicimingju.com/gushi/shi/678.html",
+    "https://www.gushicimingju.com/gushi/shi/432.html",
+    "https://www.gushicimingju.com/gushi/shi/679.html",
+    "https://www.gushicimingju.com/gushi/shi/680.html",
+    "https://www.gushicimingju.com/gushi/shi/116.html",
+    "https://www.gushicimingju.com/gushi/shi/681.html",
+    "https://www.gushicimingju.com/gushi/shi/682.html",
+    "https://www.gushicimingju.com/gushi/shi/683.html",
+    "https://www.gushicimingju.com/gushi/shi/307.html",
+    "https://www.gushicimingju.com/gushi/shi/684.html",
+    "https://www.gushicimingju.com/gushi/shi/685.html",
+    "https://www.gushicimingju.com/gushi/shi/686.html",
+    "https://www.gushicimingju.com/gushi/shi/687.html",
+    "https://www.gushicimingju.com/gushi/shi/688.html",
+    "https://www.gushicimingju.com/gushi/shi/689.html",
+    "https://www.gushicimingju.com/gushi/shi/427.html",
+    "https://www.gushicimingju.com/gushi/shi/690.html",
+    "https://www.gushicimingju.com/gushi/shi/29.html",
+    "https://www.gushicimingju.com/gushi/shi/691.html",
+    "https://www.gushicimingju.com/gushi/shi/32.html",
+    "https://www.gushicimingju.com/gushi/shi/692.html",
+    "https://www.gushicimingju.com/gushi/shi/693.html",
+    "https://www.gushicimingju.com/gushi/shi/694.html",
+    "https://www.gushicimingju.com/gushi/shi/297.html",
+    "https://www.gushicimingju.com/gushi/shi/8.html",
+    "https://www.gushicimingju.com/gushi/shi/23.html",
+    "https://www.gushicimingju.com/gushi/shi/695.html",
+    "https://www.gushicimingju.com/gushi/shi/24.html",
+    "https://www.gushicimingju.com/gushi/shi/696.html",
+    "https://www.gushicimingju.com/gushi/shi/697.html",
+    "https://www.gushicimingju.com/gushi/shi/613.html",
+    "https://www.gushicimingju.com/gushi/shi/698.html",
+    "https://www.gushicimingju.com/gushi/shi/699.html",
+    "https://www.gushicimingju.com/gushi/shi/700.html",
+    "https://www.gushicimingju.com/gushi/shi/701.html",
+    "https://www.gushicimingju.com/gushi/shi/35.html",
+    "https://www.gushicimingju.com/gushi/shi/184.html",
+    "https://www.gushicimingju.com/gushi/shi/462.html",
+    "https://www.gushicimingju.com/gushi/shi/702.html",
+    "https://www.gushicimingju.com/gushi/shi/703.html",
+    "https://www.gushicimingju.com/gushi/shi/704.html",
+    "https://www.gushicimingju.com/gushi/shi/705.html",
+    "https://www.gushicimingju.com/gushi/shi/707.html",
+    "https://www.gushicimingju.com/gushi/shi/164.html",
+    "https://www.gushicimingju.com/gushi/shi/708.html",
+    "https://www.gushicimingju.com/gushi/shi/709.html",
+    "https://www.gushicimingju.com/gushi/shi/710.html",
+    "https://www.gushicimingju.com/gushi/shi/167.html",
+    "https://www.gushicimingju.com/gushi/shi/711.html",
+    "https://www.gushicimingju.com/gushi/shi/712.html",
+    "https://www.gushicimingju.com/gushi/shi/713.html",
+    "https://www.gushicimingju.com/gushi/shi/127.html",
+    "https://www.gushicimingju.com/gushi/shi/715.html",
+    "https://www.gushicimingju.com/gushi/shi/714.html",
+    "https://www.gushicimingju.com/gushi/shi/716.html",
+    "https://www.gushicimingju.com/gushi/shi/124.html",
+    "https://www.gushicimingju.com/gushi/shi/717.html",
+    "https://www.gushicimingju.com/gushi/shi/718.html",
+    "https://www.gushicimingju.com/gushi/shi/719.html",
+    "https://www.gushicimingju.com/gushi/shi/720.html",
+    "https://www.gushicimingju.com/gushi/shi/721.html",
+    "https://www.gushicimingju.com/gushi/shi/471.html",
+    "https://www.gushicimingju.com/gushi/shi/723.html",
+    "https://www.gushicimingju.com/gushi/shi/724.html",
+    "https://www.gushicimingju.com/gushi/shi/470.html",
+    "https://www.gushicimingju.com/gushi/shi/725.html",
+    "https://www.gushicimingju.com/gushi/shi/726.html",
+    "https://www.gushicimingju.com/gushi/shi/196.html",
+    "https://www.gushicimingju.com/gushi/shi/396.html",
+    "https://www.gushicimingju.com/gushi/shi/487.html",
+    "https://www.gushicimingju.com/gushi/shi/426.html"
+]

+ 60 - 0
server/import/libai/import.js

@@ -0,0 +1,60 @@
+const cheerio = require('cheerio');
+const { GushiHrefList } = require("./href")
+
+// 引用Parse JS SDK
+const Parse = require("parse/node");
+Parse.initialize("dev"); // 设置applicationId
+Parse.serverURL = "http://web2023.fmode.cn:9999/parse"; // 设置serverURL
+
+
+// 实现分页抓取数据,并保存
+main()
+async function main(){
+    // let list = [GushiHrefList[0]]
+    let list = GushiHrefList
+    list.forEach(async href=>{
+        let gushi = await getGushiDataFromHref(href);
+        if(!gushi?.title || !gushi?.author) return;
+        // 查重
+        let exists = await checkExists(gushi);
+        if(exists?.id) return
+        // 新增
+        let Shige = Parse.Object.extend("Shige");
+        let sg = new Shige();
+        sg.set(gushi);
+        sg.save();
+    })
+}
+async function checkExists(gushi){
+    let query = new Parse.Query("Shige");
+    query.equalTo("title",gushi?.title);
+    query.equalTo("author",gushi?.author);
+    query.equalTo("dynasty",gushi?.dynasty);
+    await query.first();
+    return await query.first();
+}
+
+async function getGushiDataFromHref(href){
+    let gushi = {}
+
+    let response
+    try{
+        response = await fetch(href);
+    }catch(err){
+        console.log("失败:",href)
+        return {}
+    }
+    let html = await response.text()
+    const $ = cheerio.load(html);
+
+    gushi.title = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(1) > h1').text();
+    gushi.author = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(1) > div.author-simple-info > span:nth-child(3) > a').text();
+    gushi.dynasty = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(1) > div.author-simple-info > span:nth-child(1) > a').text();
+    gushi.content = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(1) > div.shici-content.check-more').html();
+    gushi.intro = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(5) > div:nth-child(2)').html();
+    gushi.yiwen = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(5) > div:nth-child(4)').html();
+    gushi.note = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(5) > div:nth-child(6)').html()
+    gushi.review = $('body > div.container.basic-page > div > div.more-container.col-md-8 > div:nth-child(5) > div:nth-child(8)').html()
+    console.log(href,gushi)
+    return gushi
+}

+ 197 - 0
server/import/libai/package-lock.json

@@ -0,0 +1,197 @@
+{
+  "name": "libai",
+  "version": "1.0.0",
+  "lockfileVersion": 3,
+  "requires": true,
+  "packages": {
+    "": {
+      "name": "libai",
+      "version": "1.0.0",
+      "license": "ISC",
+      "dependencies": {
+        "cheerio": "^1.0.0-rc.12"
+      }
+    },
+    "node_modules/boolbase": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/boolbase/-/boolbase-1.0.0.tgz",
+      "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="
+    },
+    "node_modules/cheerio": {
+      "version": "1.0.0-rc.12",
+      "resolved": "https://registry.npmmirror.com/cheerio/-/cheerio-1.0.0-rc.12.tgz",
+      "integrity": "sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==",
+      "dependencies": {
+        "cheerio-select": "^2.1.0",
+        "dom-serializer": "^2.0.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.0.1",
+        "htmlparser2": "^8.0.1",
+        "parse5": "^7.0.0",
+        "parse5-htmlparser2-tree-adapter": "^7.0.0"
+      },
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/cheerio?sponsor=1"
+      }
+    },
+    "node_modules/cheerio-select": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/cheerio-select/-/cheerio-select-2.1.0.tgz",
+      "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-select": "^5.1.0",
+        "css-what": "^6.1.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-select": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmmirror.com/css-select/-/css-select-5.1.0.tgz",
+      "integrity": "sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==",
+      "dependencies": {
+        "boolbase": "^1.0.0",
+        "css-what": "^6.1.0",
+        "domhandler": "^5.0.2",
+        "domutils": "^3.0.1",
+        "nth-check": "^2.0.1"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/css-what": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmmirror.com/css-what/-/css-what-6.1.0.tgz",
+      "integrity": "sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==",
+      "engines": {
+        "node": ">= 6"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/fb55"
+      }
+    },
+    "node_modules/dom-serializer": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/dom-serializer/-/dom-serializer-2.0.0.tgz",
+      "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==",
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.2",
+        "entities": "^4.2.0"
+      },
+      "funding": {
+        "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1"
+      }
+    },
+    "node_modules/domelementtype": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.3.0.tgz",
+      "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==",
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ]
+    },
+    "node_modules/domhandler": {
+      "version": "5.0.3",
+      "resolved": "https://registry.npmmirror.com/domhandler/-/domhandler-5.0.3.tgz",
+      "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==",
+      "dependencies": {
+        "domelementtype": "^2.3.0"
+      },
+      "engines": {
+        "node": ">= 4"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domhandler?sponsor=1"
+      }
+    },
+    "node_modules/domutils": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmmirror.com/domutils/-/domutils-3.1.0.tgz",
+      "integrity": "sha512-H78uMmQtI2AhgDJjWeQmHwJJ2bLPD3GMmO7Zja/ZZh84wkm+4ut+IUnUdRa8uCGX88DiVx1j6FRe1XfxEgjEZA==",
+      "dependencies": {
+        "dom-serializer": "^2.0.0",
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/domutils?sponsor=1"
+      }
+    },
+    "node_modules/entities": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmmirror.com/entities/-/entities-4.5.0.tgz",
+      "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==",
+      "engines": {
+        "node": ">=0.12"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/entities?sponsor=1"
+      }
+    },
+    "node_modules/htmlparser2": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmmirror.com/htmlparser2/-/htmlparser2-8.0.2.tgz",
+      "integrity": "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA==",
+      "funding": [
+        "https://github.com/fb55/htmlparser2?sponsor=1",
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fb55"
+        }
+      ],
+      "dependencies": {
+        "domelementtype": "^2.3.0",
+        "domhandler": "^5.0.3",
+        "domutils": "^3.0.1",
+        "entities": "^4.4.0"
+      }
+    },
+    "node_modules/nth-check": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmmirror.com/nth-check/-/nth-check-2.1.1.tgz",
+      "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==",
+      "dependencies": {
+        "boolbase": "^1.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/fb55/nth-check?sponsor=1"
+      }
+    },
+    "node_modules/parse5": {
+      "version": "7.1.2",
+      "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.1.2.tgz",
+      "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==",
+      "dependencies": {
+        "entities": "^4.4.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    },
+    "node_modules/parse5-htmlparser2-tree-adapter": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmmirror.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz",
+      "integrity": "sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g==",
+      "dependencies": {
+        "domhandler": "^5.0.2",
+        "parse5": "^7.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/inikulin/parse5?sponsor=1"
+      }
+    }
+  }
+}

+ 14 - 0
server/import/libai/package.json

@@ -0,0 +1,14 @@
+{
+  "name": "libai",
+  "version": "1.0.0",
+  "description": "- 资料:https://www.gushicimingju.com/\r - 数据范式:Shige",
+  "main": "href.js",
+  "scripts": {
+    "test": "echo \"Error: no test specified\" && exit 1"
+  },
+  "author": "",
+  "license": "ISC",
+  "dependencies": {
+    "cheerio": "^1.0.0-rc.12"
+  }
+}

+ 112 - 0
server/rest.js

@@ -0,0 +1,112 @@
+
+// Pet GET 获取全部宠物
+async function getPet(){
+  let response = await fetch("http://web2023.fmode.cn:9999/parse/classes/Pet?", {
+    "headers": {
+      "accept": "*/*",
+      "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+      "if-none-match": "W/\"19f-gvNjdCzTNrgDv/sckM9+BRABBVY\"",
+      "x-parse-application-id": "dev"
+    },
+    "referrer": "http://127.0.0.1:4040/",
+    "referrerPolicy": "strict-origin-when-cross-origin",
+    "body": null,
+    "method": "GET",
+    "mode": "cors",
+    "credentials": "omit"
+  });
+  // console.log(response)
+  // return []
+  if(response?.status=="200"){
+    let json = await response.json()
+    // console.log(json)
+    return json?.results || []
+  }else{
+    return []
+  }
+}
+
+async function main(){
+  // let petList = await getPet()
+  // console.log(petList)
+  // let list = await ClassesGet("Pet")
+  // console.log(list)
+
+  let list = await ClassesGet("Student")
+  console.log(list)
+}
+main()
+
+// 通过函数封装,实现多个表的查询
+async function ClassesGet(className){
+  let response = await fetch("http://web2023.fmode.cn:9999/parse/classes/"+className, {
+    "headers": {
+      "accept": "*/*",
+      "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+      "if-none-match": "W/\"19f-gvNjdCzTNrgDv/sckM9+BRABBVY\"",
+      "x-parse-application-id": "dev"
+    },
+    "referrer": "http://127.0.0.1:4040/",
+    "referrerPolicy": "strict-origin-when-cross-origin",
+    "body": null,
+    "method": "GET",
+    "mode": "cors",
+    "credentials": "omit"
+  });
+  // console.log(response)
+  // return []
+  if(response?.status=="200"){
+    let json = await response.json()
+    // console.log(json)
+    return json?.results || []
+  }else{
+    return []
+  }
+}
+
+// // Pet POST 创建宠物
+// fetch("http://web2023.fmode.cn:9999/parse/classes/Pet", {
+//   "headers": {
+//     "accept": "*/*",
+//     "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+//     "content-type": "text/plain;charset=UTF-8",
+//     "x-parse-application-id": "dev"
+//   },
+//   "referrer": "http://127.0.0.1:4040/",
+//   "referrerPolicy": "strict-origin-when-cross-origin",
+//   "body": '{"name":"小强","type":"蟑螂","price":0.2}',
+//   "method": "POST",
+//   "mode": "cors",
+//   "credentials": "omit"
+// });
+
+// // Pet PUT 修改
+// fetch("http://web2023.fmode.cn:9999/parse/classes/Pet/bl34OtuBYm", {
+//   "headers": {
+//     "accept": "*/*",
+//     "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+//     "content-type": "text/plain;charset=UTF-8",
+//     "x-parse-application-id": "dev"
+//   },
+//   "referrer": "http://127.0.0.1:4040/",
+//   "referrerPolicy": "strict-origin-when-cross-origin",
+//   "body": "{\"price\":0.09}",
+//   "method": "PUT",
+//   "mode": "cors",
+//   "credentials": "omit"
+// });
+
+// // Pet DELETE 删除
+// fetch("http://web2023.fmode.cn:9999/parse/classes/Pet/bl34OtuBYm", {
+//   "headers": {
+//     "accept": "*/*",
+//     "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
+//     "x-parse-application-id": "dev"
+//   },
+//   "referrer": "http://127.0.0.1:4040/",
+//   "referrerPolicy": "strict-origin-when-cross-origin",
+//   "body": null,
+//   "method": "DELETE",
+//   "mode": "cors",
+//   "credentials": "omit"
+// });

+ 0 - 32
servers/REST.js

@@ -1,32 +0,0 @@
-// Get 获取全部旅游示例
-fetch("http://web2023.fmode.cn:9999/parse/classes/LvYouYou?", {
-    "headers": {
-      "accept": "*/*",
-      "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
-      "if-none-match": "W/\"22c-ai8YYo9kqZEKt0rDxqtJt6feQDU\"",
-      "x-parse-application-id": "dev"
-    },
-    "referrer": "http://127.0.0.1:4040/",
-    "referrerPolicy": "strict-origin-when-cross-origin",
-    "body": null,
-    "method": "GET",
-    "mode": "cors",
-    "credentials": "omit"
-  });
-
-
-// Post 创建新的旅游示例
-fetch("http://web2023.fmode.cn:9999/parse/classes/LvYouYou", {
-    "headers": {
-      "accept": "*/*",
-      "accept-language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6",
-      "content-type": "text/plain;charset=UTF-8",
-      "x-parse-application-id": "dev"
-    },
-    "referrer": "http://127.0.0.1:4040/",
-    "referrerPolicy": "strict-origin-when-cross-origin",
-    "body": "{\"name\":\"哈尔滨\",\"prices\":\"6500\"}",
-    "method": "POST",
-    "mode": "cors",
-    "credentials": "omit"
-  });

+ 25 - 2
src/app/app-routing.module.ts

@@ -1,7 +1,9 @@
 import { NgModule } from '@angular/core';
 import { PreloadAllModules, RouterModule, Routes } from '@angular/router';
 
+
 const routes: Routes = [
+
   {
     path: '',
     loadChildren: () => import('./tabs/tabs.module').then(m => m.TabsPageModule)
@@ -9,11 +11,32 @@ const routes: Routes = [
   {
     path: 'contact',
     loadChildren: () => import('./contact/contact.module').then( m => m.ContactPageModule)
-  }
+  },
+  {
+    path: 'tab1',
+    children: [
+      {
+        path: 'shouye-view',
+        loadChildren: () => import('../modules/page/shouye-view/shouye-view.module').then(m => m.ShouyeViewPageModule)
+      }
+    ]
+  },
+  {
+    path: 'tab3',
+    children: [
+      {
+        path: 'login',
+        loadChildren: () => import('../modules/user/edit-info/edit-info.module').then(m => m.EditInfoPageModule)
+      }
+    ]
+  },
+  
+
 ];
 @NgModule({
   imports: [
-    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules })
+    RouterModule.forRoot(routes, { preloadingStrategy: PreloadAllModules }),
+   
   ],
   exports: [RouterModule]
 })

+ 1 - 80
src/app/tab1/tab1.page.html

@@ -32,88 +32,9 @@
           </div>
         </ng-container>
       </div>
-  <!-- <div class="middle-section">
-    
-    <ion-card class="order-card">
-      <ion-card-header class="centered-header">
-        <ion-icon name="map-outline" class="large-icon"></ion-icon>
-        <ion-card-subtitle>找攻略</ion-card-subtitle>
-      </ion-card-header>
-    </ion-card>
-
-    <ion-card class="points-card">
-      <ion-card-header class="centered-header">
-        <ion-icon name="flame-outline" class="large-icon"></ion-icon>
-        <ion-card-subtitle>当季热门</ion-card-subtitle>
-      </ion-card-header>
-
-    </ion-card>
-
-    <ion-card class="medals-card">
-      <ion-card-header class="centered-header">
-        <ion-icon name="newspaper-outline" class="large-icon"></ion-icon>
-        <ion-card-subtitle>看游记</ion-card-subtitle>
-      </ion-card-header>
-      
-    </ion-card>
-
-    <ion-card class="create-card">
-      <ion-card-header  class="centered-header">
-        <ion-icon name="people-outline" class="large-icon"></ion-icon>
-        <ion-card-subtitle>找搭子</ion-card-subtitle>
-      </ion-card-header>
-    </ion-card>
-
-      <ion-card class="create-card">
-        <ion-card-header  class="centered-header">
-          <ion-icon name="bed-outline" class="large-icon"></ion-icon>
-          <ion-card-subtitle>订酒店</ion-card-subtitle>
-        </ion-card-header>
-      </ion-card>
-    
-  </div>
-
- <div class="middle-section">
-  <ion-card class="order-card">
-    <ion-card-header class="centered-header">
-      <ion-icon name="happy-outline" class="large-icon"></ion-icon>
-      <ion-card-subtitle>亲子游</ion-card-subtitle>
-    </ion-card-header>
-    
-  </ion-card>
-
-  <ion-card class="points-card">
-    <ion-card-header class="centered-header">
-      <ion-icon name="car-outline" class="large-icon"></ion-icon>
-      <ion-card-subtitle>自驾游</ion-card-subtitle>
-    </ion-card-header>
-  </ion-card>
-
-  <ion-card class="medals-card">
-    <ion-card-header class="centered-header">
-      <ion-icon name="flag-outline" class="large-icon"></ion-icon>
-      <ion-card-subtitle>国内游</ion-card-subtitle>
-    </ion-card-header>
-  </ion-card>
-
-  <ion-card class="medals-card">
-    <ion-card-header class="centered-header">
-      <ion-icon name="airplane-outline" class="large-icon"></ion-icon>
-      <ion-card-subtitle>海外游</ion-card-subtitle>
-    </ion-card-header>
-  </ion-card>
-
-  <ion-card class="create-card">
-    <ion-card-header  class="centered-header">
-      <ion-icon name="compass-outline" class="large-icon"></ion-icon>
-      <ion-card-subtitle>周边游</ion-card-subtitle>
-    </ion-card-header>
-  </ion-card>
-
-  </div> -->
 
   <!-- 推荐内容展示 -->
-  <ion-card>
+  <ion-card (click)="navigateToShouyeViewPage()">
     <ion-card-header>
       推荐内容
     </ion-card-header>

+ 8 - 1
src/app/tab1/tab1.page.ts

@@ -1,5 +1,6 @@
 import { Component } from '@angular/core';
 import Swiper from 'swiper';
+import { Router } from '@angular/router';
 
 @Component({
   selector: 'app-tab1',
@@ -30,9 +31,15 @@ export class Tab1Page {
 
   chunkedCards: any[] = [];
 
-  constructor() {
+  constructor(private router: Router) {
     this.chunkSize(this.cards);
   }
+ 
+  //跳转到新页面
+  navigateToShouyeViewPage() {
+    this.router.navigate(['/tab1/shouye-view']);
+  }
+
 
   chunkSize(cards: any[]) {
     const chunkSize: number = 5;

+ 48 - 78
src/app/tab3/tab3.page.html

@@ -1,7 +1,7 @@
 <ion-header [translucent]="true">
 
   <ion-toolbar>
-    <ion-buttons slot="start">
+    <ion-buttons slot="start" (click)="GoToLoginPage()">
       <ion-button>
         <ion-icon name="settings-outline"></ion-icon>
       </ion-button>
@@ -18,32 +18,24 @@
 
 <ion-content>
   <!-- 个人信息 -->
-  <ion-card>
-
     <ion-card-content>
 
-      <ion-row>
-        <ion-col size="3">
-          <ion-avatar>
-            <ion-img src="https://via.placeholder.com/150"></ion-img>
+      <ion-row class="user-info-row">
+        <ion-col size="auto" class="enlarge-col">
+          <ion-avatar (click)="fileInput.click()">
+            <ion-img [src]="userImage" ></ion-img>
           </ion-avatar>
-        </ion-col>
-        <ion-col size="9">
-          <ion-label>用户名:示例用户</ion-label>
-          <!-- id、ip -->
-          <ion-row>
-            <ion-col>
-              <ion-label>ID: 23284787</ion-label>
-            </ion-col>
-            <ion-col>
-              <ion-label>IP属地: 南昌</ion-label>
-            </ion-col>
-          </ion-row>
-        </ion-col>
+          <input type="file" #fileInput (change)="onFileSelected($event)" style="display: none">
 
+        </ion-col>
+        <ion-col size="auto" class="info-col">
+          <ion-label><h1>用户名:示例用户</h1></ion-label>
+          <ion-label>ID: 23284787</ion-label>
+          &nbsp;
+          <ion-label>IP属地: 南昌</ion-label>
+        </ion-col> 
       </ion-row>
 
-     
       <ion-row>
         <ion-col>
           <ion-label>关注: 7</ion-label>
@@ -55,11 +47,10 @@
           <ion-label>获赞: 0</ion-label>
         </ion-col>
       </ion-row>
-
     </ion-card-content>
-  </ion-card>
 
-  <!-- <div class="middle-section">
+    
+  <div class="middle-section">
     <ion-card *ngFor="let card of cards" class="order-card">
       <ion-card-header>
         <span style="font-size: 16px;">{{ card.title }}</span>
@@ -68,61 +59,40 @@
         <span style="font-size: 14px;">{{ card.content }}</span>
       </ion-card-content>
     </ion-card>
-  </div> -->
-  <div class="middle-section">
-    <ion-card *ngFor="let card of cards" class="order-card">
-      <ion-card-header>
-        <div style="display: flex; align-items: center;">
-          <ion-icon name="{{ card.icon }}" style="font-size: 20px; margin-right: 5px;"></ion-icon>
-          <span style="font-size: 16px;">{{ card.title }}</span>
-        </div>
-      </ion-card-header>
-      <ion-card-content style="margin-top: -10px;">
-        <span style="font-size: 14px;">{{ card.content }}</span>
-      </ion-card-content>
-    </ion-card>
-  </div>
+  </div> 
 
-  <div>
-  <!-- 内容 -->
-  <ion-card>
-    <ion-card-header>
-      内容
-    </ion-card-header>
-    <ion-card-content>
-      这里是一些收藏内容的示例数据
-    </ion-card-content>
-  </ion-card>
-
-  <!-- 收藏 -->
-  <ion-card>
-    <ion-card-header>
-      收藏
-    </ion-card-header>
-    <ion-card-content>
-      这里是一些收藏详情的示例数据
-    </ion-card-content>
-  </ion-card>
-
-  <!-- 喜欢 -->
-  <ion-card>
-    <ion-card-header>
-      喜欢
-    </ion-card-header>
-    <ion-card-content>
-      这里是一些喜欢详情的示例数据
-    </ion-card-content>
-  </ion-card>
 
-  <!-- 设置与反馈 -->
-  <ion-card>
-    <ion-card-header>
-      设置与反馈
-    </ion-card-header>
-    <ion-card-content>
-      这里是一些设置选项和意见反馈的示例数据
-    </ion-card-content>
-  </ion-card>
-  </div>
+  <ion-content>
+    <ion-segment [(ngModel)]="currentTab">
+      <ion-segment-button value="unpublished">
+        未发布
+      </ion-segment-button>
+      <ion-segment-button value="published">
+        已发布
+      </ion-segment-button>
+      <ion-segment-button value="liked">
+        点赞
+      </ion-segment-button>
+      <ion-segment-button value="favorited">
+        收藏
+      </ion-segment-button>
+    </ion-segment>
+  
+    <div *ngIf="currentTab === 'unpublished'">
+      <p>未发布内容</p>
+    </div>
   
+    <div *ngIf="currentTab === 'published'">
+      <p>已发布内容</p>
+    </div>
+  
+    <div *ngIf="currentTab === 'liked'">
+      <p>点赞内容</p>
+    </div>
+  
+    <div *ngIf="currentTab === 'favorited'">
+      <p>收藏内容</p>
+    </div>
+  </ion-content>
+
 </ion-content>

+ 35 - 2
src/app/tab3/tab3.page.scss

@@ -3,9 +3,28 @@
     justify-content: space-around;
     overflow-x: auto;
     white-space: nowrap;
-    margin-top: 20px; /* 添加顶部间距 */
+    margin-top: 0px; /* 添加顶部间距 */
+ 
+    .user-info-row {
+      display: flex;
+      align-items: center;
+    }
   
-    .order-card {
+    .info-col {
+      margin-left: 10px; // 设置头像和信息之间的间距
+      text-align: right;
+
+    }
+    .enlarge-col {
+      width: 100px; // 设置宽度为200px(根据需要调整)
+      height: 100px; // 设置高度为200px(根据需要调整)
+    }
+
+    .ion-avatar {
+      border-radius: 50%; // 将头像设置为圆形
+    }
+
+    .order-card {   //卡片
       width: 110px;
       height: 80px;
       margin: 5px;
@@ -14,6 +33,20 @@
       align-items: center;
       justify-content: center;
     }
+   
+
+    .middle-section {
+      ion-card {
+        margin-bottom: 10px;
+        display: none; /* 默认隐藏所有内容部分 */
+      }
+
+      ion-card.active {
+        display: block; /* 显示选中的内容部分 */
+      }
+    }
+
+
   }
 
 

+ 24 - 1
src/app/tab3/tab3.page.ts

@@ -1,4 +1,5 @@
 import { Component } from '@angular/core';
+import { Router } from '@angular/router';
 
 @Component({
   selector: 'app-tab3',
@@ -13,6 +14,28 @@ export class Tab3Page {
     { title: '我的勋章', content: '2枚勋章', icon: 'medal-outline' }
   ];
 
-  constructor() {}
+ 
+  currentTab: string = 'unpublished';
+  userImage: string = 'https://via.placeholder.com/150'; // 默认图片地址
+
+  onFileSelected(event: Event) {
+    const inputElement = event.target as HTMLInputElement;
+   if (inputElement.files && inputElement.files.length > 0) {
+    const file = inputElement.files[0];
+    const reader = new FileReader();
+    reader.onload = () => {
+      if (reader.result) {
+        this.userImage = reader.result as string;
+      }
+    };
+    reader.readAsDataURL(file);
+  }
+
+  }
+
+  constructor(private router: Router) {}
+  GoToLoginPage(){
+    this.router.navigate(['./tab3/login'])
+  }
 
 }

+ 100 - 0
src/app/user/README.md

@@ -0,0 +1,100 @@
+# 用户模块
+
+# 功能结构
+- 用户模块
+
+- 模块内功能
+    - 页面
+        - 登录/注册
+            - 用户的注册及登录
+                - 忘记密码
+        - 资料编辑
+            - 文字
+            - 图片编辑
+        - 我的
+            - 登陆状态
+                - 登录
+                    - 会员卡片
+                - 未登录
+                    - 占位卡片
+
+- 模块外功能
+    - 限制访问
+        - 路由守卫
+    - 数据关联
+        - 读取当前用户数据
+            - localStorage
+                - 本地存储
+        - 由XXX用户创建的
+
+
+
+# 安装依赖
+``` bash
+npm i -S parse
+npm i -D @types/parse
+```
+配置 根目录/tsconfig.json
+``` json
+  "compilerOptions": {
+    "allowSyntheticDefaultImports": true,
+  }
+```
+
+# 设置微服务地址+参数
+- app.component.ts 根组件顶部增加
+``` ts
+// 引用Parse JS SDK
+import Parse from "parse";
+Parse.initialize("dev"); // 设置applicationId
+Parse.serverURL = "http://web2023.fmode.cn:9999/parse"; // 设置serverURL
+```
+
+# 复制user整个模块目录
+- 创建/src/modules目录
+- 将案例项目/src/modules/user目录,复制到自己项目/src/modules/user
+
+# 配置全局user路由
+src\app\app-routing.module.ts
+``` ts
+const routes: Routes = [
+  // 添加:
+  {
+    path: 'user',
+    loadChildren: () => import('../modules/user/user.module').then(m => m.UserModule)
+  }
+];
+```
+
+# 配置单页Tabs我的路由
+- src\app\tabs\tabs-routing.module.ts
+``` ts
+// 新增在tab3后面
+    {
+        path: 'mine',
+        loadChildren: () => import('../../modules/user/mine/mine.module').then(m => m.MinePageModule)
+    },
+```
+- src\app\tabs\tabs.page.html
+``` html
+<ion-tab-button tab="mine" href="/tabs/mine">
+      <ion-icon aria-hidden="true" name="person"></ion-icon>
+      <ion-label>我的</ion-label>
+</ion-tab-button>
+```
+
+# 路由守卫 AuthGuard用法
+
+``` ts
+// 引用路由守卫
+import { authGuard } from 'src/modules/user/auth.guard';
+
+
+
+{
+        path: 'tab2',
+        // 此处增加路由守卫
+        canActivate:[authGuard],
+        loadChildren: () => import('../../modules/contact/contact-list/contact-list.module').then(mod => mod.ContactListPageModule)
+    },
+```

+ 17 - 0
src/app/user/auth.guard.spec.ts

@@ -0,0 +1,17 @@
+import { TestBed } from '@angular/core/testing';
+import { CanActivateFn } from '@angular/router';
+
+import { authGuard } from './auth.guard';
+
+describe('authGuard', () => {
+  const executeGuard: CanActivateFn = (...guardParameters) => 
+      TestBed.runInInjectionContext(() => authGuard(...guardParameters));
+
+  beforeEach(() => {
+    TestBed.configureTestingModule({});
+  });
+
+  it('should be created', () => {
+    expect(executeGuard).toBeTruthy();
+  });
+});

+ 10 - 0
src/app/user/auth.guard.ts

@@ -0,0 +1,10 @@
+import { CanActivateFn } from '@angular/router';
+import Parse from "parse";
+export const authGuard: CanActivateFn = (route, state) => {
+  let user = Parse.User.current()
+  if(user?.id){
+    return true
+  }else{
+    return false;
+  }
+};

+ 17 - 0
src/app/user/edit-info/edit-info-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { EditInfoPage } from './edit-info.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: EditInfoPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class EditInfoPageRoutingModule {}

+ 20 - 0
src/app/user/edit-info/edit-info.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { EditInfoPageRoutingModule } from './edit-info-routing.module';
+
+import { EditInfoPage } from './edit-info.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    EditInfoPageRoutingModule
+  ],
+  declarations: [EditInfoPage]
+})
+export class EditInfoPageModule {}

+ 47 - 0
src/app/user/edit-info/edit-info.page.html

@@ -0,0 +1,47 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>资料编辑</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+  <ion-header collapse="condense">
+    <ion-toolbar>
+      <ion-title size="large">资料编辑</ion-title>
+    </ion-toolbar>
+  </ion-header>
+
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>{{currentUser?.get('username')}} - {{currentUser?.id}}</ion-card-title>
+    </ion-card-header>
+    <ion-card-content>
+      <ion-list>
+        <ion-item>
+          <ion-input label="姓名" type="text" [(ngModel)]="userInfo.name"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-input label="手机" type="tel" [(ngModel)]="userInfo.mobile"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-select label="性别" [(ngModel)]="userInfo.gender">
+            <ion-select-option value="男">男</ion-select-option>
+            <ion-select-option value="女">女</ion-select-option>
+          </ion-select>
+        </ion-item>
+        <ion-item>
+          <ion-label>生日</ion-label>
+          <ion-datetime-button datetime="birthday"></ion-datetime-button>
+          <ion-modal [keepContentsMounted]="true">
+            <ng-template>
+              <ion-datetime id="birthday" displayFormat="MM/DD/YYYY" [(ngModel)]="userInfo.birthday"></ion-datetime>
+            </ng-template>
+          </ion-modal>
+        </ion-item>
+      </ion-list>
+    </ion-card-content>
+  </ion-card>
+
+  <ion-button expand="block" (click)="save()">保存</ion-button>
+  <ion-button expand="block" (click)="cancel()">取消</ion-button>
+</ion-content>

+ 0 - 0
src/app/user/edit-info/edit-info.page.scss


+ 17 - 0
src/app/user/edit-info/edit-info.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { EditInfoPage } from './edit-info.page';
+
+describe('EditInfoPage', () => {
+  let component: EditInfoPage;
+  let fixture: ComponentFixture<EditInfoPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(EditInfoPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 58 - 0
src/app/user/edit-info/edit-info.page.ts

@@ -0,0 +1,58 @@
+import { Component, OnInit } from '@angular/core';
+import { NavController } from '@ionic/angular';
+import * as Parse from 'parse';
+
+@Component({
+  selector: 'app-edit-info',
+  templateUrl: './edit-info.page.html',
+  styleUrls: ['./edit-info.page.scss'],
+})
+export class EditInfoPage implements OnInit {
+
+  userInfo: any = {
+    name: '',
+    mobile: '',
+    gender: '',
+    birthday: ''
+  };
+  currentUser:Parse.User|undefined
+  constructor(private navController: NavController) {}
+
+  ngOnInit() {
+    this.currentUser = Parse.User.current();
+    if (this.currentUser) {
+      // 修改uesrInfo赋值逻辑,仅加载被编辑的字段属性值
+      let json = this.currentUser.toJSON();
+      for (const key in json) {
+        if (this.userInfo.hasOwnProperty(key)) {
+          this.userInfo[key] = json[key]
+        }
+      }
+    }
+    console.log(this.userInfo)
+  }
+
+  save() {
+    this.currentUser = Parse.User.current();
+    if (this.currentUser) {
+      console.log(this.userInfo)
+      this.userInfo.birthday = this.userInfo.birthday || new Date()
+      this.userInfo.birthday = new Date(this.userInfo.birthday);
+      for (const key in this.userInfo) {
+        if (this.userInfo.hasOwnProperty(key)) {
+          this.currentUser.set(key, this.userInfo[key]);
+        }
+      }
+      this.currentUser.save().then(() => {
+        this.navController.back();
+      }).catch((error) => {
+        console.error('Error saving user data: ', error);
+      });
+    }
+  }
+
+  cancel() {
+    this.navController.back();
+  }
+
+}

+ 17 - 0
src/app/user/login/login-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { LoginPage } from './login.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: LoginPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class LoginPageRoutingModule {}

+ 20 - 0
src/app/user/login/login.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { LoginPageRoutingModule } from './login-routing.module';
+
+import { LoginPage } from './login.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    LoginPageRoutingModule
+  ],
+  declarations: [LoginPage]
+})
+export class LoginPageModule {}

+ 33 - 0
src/app/user/login/login.page.html

@@ -0,0 +1,33 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>登录/注册</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+
+  <ion-card>
+    <ion-card-header>
+      <ion-card-title>登录/注册</ion-card-title>
+    </ion-card-header>
+  
+    <ion-card-content>
+
+      <ion-list [inset]="true">
+        <ion-item>
+          <ion-input [(ngModel)]="username" label="账号" placeholder="请输入用户名"></ion-input>
+        </ion-item>
+        <ion-item>
+          <ion-input [(ngModel)]="password" label="密码" type="password" placeholder="请输入密码"></ion-input>
+        </ion-item>
+      </ion-list>
+     
+    </ion-card-content>
+  
+    <ion-button (click)="login()" fill="clear" (click)="goToEditInfoPage()">登录</ion-button>
+    <ion-button (click)="register()" fill="clear">注册</ion-button>
+  </ion-card>
+
+  <!-- 新增路由返回逻辑,执行back函数 -->
+  <ion-button expand="block" (click)="back()">返回</ion-button>
+</ion-content>

+ 0 - 0
src/app/user/login/login.page.scss


+ 17 - 0
src/app/user/login/login.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { LoginPage } from './login.page';
+
+describe('LoginPage', () => {
+  let component: LoginPage;
+  let fixture: ComponentFixture<LoginPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(LoginPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 107 - 0
src/app/user/login/login.page.ts

@@ -0,0 +1,107 @@
+import { Component, OnInit } from '@angular/core';
+import { AlertController, NavController } from '@ionic/angular';
+//新增
+import { Router } from '@angular/router';
+import * as Parse from "parse"
+// 引用Router服务
+@Component({
+  selector: 'app-login',
+  templateUrl: './login.page.html',
+  styleUrls: ['./login.page.scss'],
+})
+
+
+export class LoginPage {
+
+  constructor(private router: Router) {}
+
+  //新增
+  goToEditInfoPage() {
+    this.router.navigate(['./edit-info']);}
+
+}
+
+
+// export class LoginPage implements OnInit {
+//   username:string = ""
+//   password:string = ""
+//   constructor(private router: Router,
+//     // 新增:Router服务,用于路由跳转
+//     private navCtrl:NavController,
+//     private alertController:AlertController
+//   ) { }
+
+//   ngOnInit() {
+//   }
+
+//   async login(){
+//     let user
+//     try {
+//       user = await Parse.User.logIn(this.username,this.password)
+//     } catch (error:any) {
+//       let message:string = ""
+//       // 新增提示词详情,根据Parse.User.login方法返回的不同英文提示词,增加对应的中文内容转换
+//       if(error?.message.indexOf("is required")>-1){
+//         message = "必须输入账号或邮箱"
+//       }
+//       if(error?.message.indexOf("Invalid username")>-1){
+//         message = "账号或密码错误,请检查"
+//       }
+//       this.presentAlert({
+//         header:"登录失败",
+//         subHeader:"状态码:"+error.code,
+//         message:message || error.message
+//       })
+//     }
+//     console.log(user)
+//     if(user?.id){
+//       this.navCtrl.back()
+//     }
+//   }
+//   async register(){
+//     let user = new Parse.User()
+//     user.set("username",this.username)
+//     user.set("password",this.password)
+//     try {
+//         let result = await user.signUp();
+//         console.log(result)
+//         if(result?.id){
+//           this.navCtrl.back()
+//         }
+//         // Hooray! Let them use the app now.
+//     } catch (error:any) {
+//         // 新增提示词详情,根据Parse.User.signUp方法返回的不同英文提示词,增加对应的中文内容转换
+//         let message:string = ""
+//         if(error?.message.indexOf("already exists")>-1){
+//           message = "该账号已存在请修改后重试"
+//         }
+//         if(error?.message.indexOf("empty")>-1){
+//           message = "账号不能为空请输入后重试"
+//         }
+//         this.presentAlert({
+//           header:"注册失败",
+//           subHeader:"状态码:"+error.code,
+//           message:message || error.message
+//         })
+//     }
+//   }
+
+//   async presentAlert(options:{header:string,subHeader:string,message:string}) {
+//     const alert = await this.alertController.create({
+//       header: options?.header,
+//       subHeader: options?.subHeader,
+//       message: options?.message,
+//       buttons: ['好的'],
+//     });
+
+//     await alert.present();
+//   }
+
+//   /**
+//    * 返回上级页面函数
+//    * @desc
+//    */
+//   back(){
+//     this.navCtrl.back()
+//   }
+// }

+ 17 - 0
src/app/user/mine/mine-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { MinePage } from './mine.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: MinePage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class MinePageRoutingModule {}

+ 20 - 0
src/app/user/mine/mine.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { MinePageRoutingModule } from './mine-routing.module';
+
+import { MinePage } from './mine.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    MinePageRoutingModule
+  ],
+  declarations: [MinePage]
+})
+export class MinePageModule {}

+ 24 - 0
src/app/user/mine/mine.page.html

@@ -0,0 +1,24 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-title>我的</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content [fullscreen]="true">
+
+  <ion-card>
+    <img alt="" src="https://ionicframework.com/docs/img/demos/card-media.png" />
+    <ion-card-header>
+      <ion-card-title>{{user?.get("username") || '未登录'}}</ion-card-title>
+      <ion-card-subtitle *ngIf="!user?.id">请您登陆后继续使用</ion-card-subtitle>
+      <ion-card-subtitle *ngIf="user?.id">{{user?.get("name")}}-{{user?.get("gender")}}</ion-card-subtitle>
+    </ion-card-header>
+ 
+    <!-- 新增:根据用户状态,显示登录/登出按钮,执行跳转或登出函数 -->
+    <ion-button *ngIf="!user?.id" fill="clear" routerLink="/user/login">登录</ion-button>
+    <ion-button *ngIf="user?.id" fill="clear" routerLink="/user/edit/info">编辑资料</ion-button>
+    <ion-button *ngIf="user?.id" fill="clear" (click)="logout()">登出</ion-button>
+  </ion-card>
+
+
+</ion-content>

+ 0 - 0
src/app/user/mine/mine.page.scss


+ 17 - 0
src/app/user/mine/mine.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { MinePage } from './mine.page';
+
+describe('MinePage', () => {
+  let component: MinePage;
+  let fixture: ComponentFixture<MinePage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(MinePage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 28 - 0
src/app/user/mine/mine.page.ts

@@ -0,0 +1,28 @@
+import { Component, OnInit } from '@angular/core';
+// 由于Parse本身是js库,在ts中加载需要通过 * as Parse转换一下
+import Parse from "parse"
+@Component({
+  selector: 'app-mine',
+  templateUrl: './mine.page.html',
+  styleUrls: ['./mine.page.scss'],
+})
+export class MinePage implements OnInit {
+
+  constructor() {
+   
+  }
+
+  // 由于Parse.User.current()是随着localStorage变化的属性
+  // 为了避免首次复制后用户状态变化,页面不同步,通过get方法实现实时获取
+  user:Parse.User|undefined
+  async ngOnInit() {
+      this.user = await Parse.User.current()
+      setInterval(async ()=>{
+      this.user = await Parse.User.current()
+    },1000)
+  }
+  logout(){
+    Parse.User.logOut();
+  }
+
+}

+ 14 - 0
src/app/user/user-routing.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { RouterModule, Routes } from '@angular/router';
+
+const routes: Routes = [
+    {path: 'login', loadChildren: () => import('./login/login.module').then(mod => mod.LoginPageModule)},
+    {path: 'mine', loadChildren: () => import('./mine/mine.module').then(mod => mod.MinePageModule)},
+    {path: 'edit/info', loadChildren: () => import('./edit-info/edit-info.module').then(mod => mod.EditInfoPageModule)},
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule]
+})
+export class UserRoutingModule { }

+ 14 - 0
src/app/user/user.module.ts

@@ -0,0 +1,14 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+
+import { UserRoutingModule } from './user-routing.module';
+
+
+@NgModule({
+  declarations: [],
+  imports: [
+    CommonModule,
+    UserRoutingModule
+  ]
+})
+export class UserModule { }

BIN
src/app/user/用户模块.xmind


+ 17 - 0
src/modules/page/shouye-view/shouye-view-routing.module.ts

@@ -0,0 +1,17 @@
+import { NgModule } from '@angular/core';
+import { Routes, RouterModule } from '@angular/router';
+
+import { ShouyeViewPage } from './shouye-view.page';
+
+const routes: Routes = [
+  {
+    path: '',
+    component: ShouyeViewPage
+  }
+];
+
+@NgModule({
+  imports: [RouterModule.forChild(routes)],
+  exports: [RouterModule],
+})
+export class ShouyeViewPageRoutingModule {}

+ 20 - 0
src/modules/page/shouye-view/shouye-view.module.ts

@@ -0,0 +1,20 @@
+import { NgModule } from '@angular/core';
+import { CommonModule } from '@angular/common';
+import { FormsModule } from '@angular/forms';
+
+import { IonicModule } from '@ionic/angular';
+
+import { ShouyeViewPageRoutingModule } from './shouye-view-routing.module';
+
+import { ShouyeViewPage } from './shouye-view.page';
+
+@NgModule({
+  imports: [
+    CommonModule,
+    FormsModule,
+    IonicModule,
+    ShouyeViewPageRoutingModule
+  ],
+  declarations: [ShouyeViewPage]
+})
+export class ShouyeViewPageModule {}

+ 50 - 0
src/modules/page/shouye-view/shouye-view.page.html

@@ -0,0 +1,50 @@
+<ion-header [translucent]="true">
+  <ion-toolbar>
+    <ion-buttons slot="start">
+      <button (click)="navigateBackToTab1()">Back</button>
+    </ion-buttons>
+    <ion-title>桂林+阳朔旅游攻略</ion-title>
+  </ion-toolbar>
+</ion-header>
+
+<ion-content>
+  <!-- 攻略分享文章区域 -->
+  <div class="article-container">
+    <div class="article-header">
+      <img src="https://tse1-mm.cn.bing.net/th/id/OIP-C.Ltwg5DrGKW0f3Yv9F4EdXQHaEi?w=295&h=181&c=7&r=0&o=5&dpr=1.3&pid=1.7" alt="发布者头像">
+      <p>爱旅游的123</p>
+    </div>
+
+    
+    <div class="article-content">
+      <img src="https://tse1-mm.cn.bing.net/th/id/OIP-C.Ltwg5DrGKW0f3Yv9F4EdXQHaEi?w=295&h=181&c=7&r=0&o=5&dpr=1.3&pid=1.7" alt="文章图片">
+      <h1 class="article-title">桂林+阳朔3天两晚旅游攻略</h1>
+      <div class="details-row">
+        <p>游玩时间: 2天</p>
+        <p>人均成本(元): 580</p>
+      </div>
+
+        <p>第一天:桂林
+         早上:抵达桂林,入住酒店<br>
+         上午:漓江游船,欣赏桂林山水风光<br>
+         中午:品尝桂林米粉或阳朔啤酒鱼等地道美食(人均约50元)<br>
+         下午:参观象鼻山、九马画山,感受桂林的山水画意境(门票费用约100元)<br>
+         晚上:漫步两江四湖,欣赏夜色中的桂林城
+        <br>
+         第二天:阳朔<br>
+         早上:前往阳朔,入住酒店<br>
+         上午:骑行漓江边,感受阳朔的田园风光(租车费用约100元)<br>
+         中午:在西街品尝阳朔美食(人均约50元)<br>
+         下午:徒步漓江边,探索阳朔的溶洞和奇石(门票费用约80元)<br>
+         晚上:欣赏阳朔的灯光秀和夜市,购买纪念品
+        <br>
+         餐饮费用:三天的餐饮费用(人均约150元)<br>
+         景点门票费用:景点门票费用(约180元)
+        </p>
+    </div>
+    <div class="article-actions">
+      <ion-icon name="heart-outline"></ion-icon> <!-- 空心点赞图标 -->
+      <ion-icon name="star-outline"></ion-icon> <!-- 空心收藏图标 -->
+      <ion-icon name="chatbox-outline"></ion-icon> <!-- 空心评论图标 -->
+    </div>
+  </div>

+ 57 - 0
src/modules/page/shouye-view/shouye-view.page.scss

@@ -0,0 +1,57 @@
+.article-container {
+    display: flex;
+    flex-direction: column;
+    margin: 10px;
+    padding: 10px;
+    border: 1px solid #ccc;
+    border-radius: 5px;
+    align-items: flex-start;
+
+    .details-row {
+        display: flex;
+        justify-content: space-between;
+        width: 100%;
+      }
+      
+    .article-header {
+      display: flex;
+      align-items: center;
+      margin-bottom: 10px;
+  
+      img {
+        width: 50px;
+        height: 50px;
+        border-radius: 50%;
+        margin-right: 10px;
+      }
+  
+      p {
+        font-weight: bold;
+      }
+    }
+  
+    .article-content {
+      img {
+        width: 100%;
+        border-radius: 5px;
+        margin-bottom: 10px;
+      }
+  
+      .article-title {
+        font-size: 1.5em;
+        margin-bottom: 10px;
+      }
+    }
+  
+    .article-actions {
+        display: flex;
+        justify-content: space-around;
+        align-items: center;
+        margin-top: 10px;
+      
+        ion-icon {
+          font-size: 1.5em;
+          margin: 0 40px; /* 调整图标之间的间距 */
+        }
+      }
+  }

+ 17 - 0
src/modules/page/shouye-view/shouye-view.page.spec.ts

@@ -0,0 +1,17 @@
+import { ComponentFixture, TestBed } from '@angular/core/testing';
+import { ShouyeViewPage } from './shouye-view.page';
+
+describe('ShouyeViewPage', () => {
+  let component: ShouyeViewPage;
+  let fixture: ComponentFixture<ShouyeViewPage>;
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(ShouyeViewPage);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 24 - 0
src/modules/page/shouye-view/shouye-view.page.ts

@@ -0,0 +1,24 @@
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+
+@Component({
+  selector: 'app-shouye-view',
+  templateUrl: './shouye-view.page.html',
+  styleUrls: ['./shouye-view.page.scss'],
+})
+export class ShouyeViewPage implements OnInit {
+  
+
+  recommendationContent: string = '【桂林+阳朔】船在水中走 人在画中游 | 三天旅行攻略 去看小砂糖橘';
+  recommendationImage: string = 'https://tse3-mm.cn.bing.net/th/id/OIP-C.5HjektOIikSBU1vw0JVKwgHaE8?w=305&h=180&c=7&r=0&o=5&dpr=1.3&pid=1.7';
+
+  constructor(private router: Router) { }
+
+  navigateBackToTab1() {
+    this.router.navigate(['/tabs/tab1']);
+  }
+  
+  ngOnInit() {
+  }
+
+}

Some files were not shown because too many files changed in this diff