Browse Source

新增国外用户认证

warrior 2 weeks ago
parent
commit
b036868a20

+ 1 - 0
android/app/src/main/AndroidManifest.xml

@@ -17,6 +17,7 @@
             android:label="@string/title_activity_main"
             android:theme="@style/AppTheme.NoActionBarLaunch"
             android:launchMode="singleTask"
+            android:hardwareAccelerated="true"
             android:exported="true">
 
             <intent-filter>

+ 4 - 4
package-lock.json

@@ -24,7 +24,7 @@
         "@capacitor/haptics": "^6.0.2",
         "@capacitor/ios": "^7.0.1",
         "@capacitor/keyboard": "^6.0.3",
-        "@capacitor/status-bar": "^6.0.2",
+        "@capacitor/status-bar": "^6.0.0",
         "@ionic/angular": "^8.4.0",
         "@ionic/angular-server": "^8.4.0",
         "@ionic/angular-toolkit": "^11.0.1",
@@ -2512,9 +2512,9 @@
       }
     },
     "node_modules/@capacitor/status-bar": {
-      "version": "6.0.2",
-      "resolved": "https://registry.npmmirror.com/@capacitor/status-bar/-/status-bar-6.0.2.tgz",
-      "integrity": "sha512-AmRIX6QvFemItlY7/69ARkIAqitRQqJ2qwgZmD1KqgFb78pH+XFXm1guvS/a8CuOOm/IqZ4ddDbl20yxtBqzGA==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmmirror.com/@capacitor/status-bar/-/status-bar-6.0.0.tgz",
+      "integrity": "sha512-Wo0ILugYlmENegKDgTzVCPjbvP8h1ObgHslLdgeVG643ViMS/diausHIq8e104WIKCXtKIELmQeYVp9mX7932g==",
       "peerDependencies": {
         "@capacitor/core": "^6.0.0"
       }

+ 1 - 1
package.json

@@ -27,7 +27,7 @@
     "@capacitor/haptics": "^6.0.2",
     "@capacitor/ios": "^7.0.1",
     "@capacitor/keyboard": "^6.0.3",
-    "@capacitor/status-bar": "^6.0.2",
+    "@capacitor/status-bar": "^6.0.0",
     "@ionic/angular": "^8.4.0",
     "@ionic/angular-server": "^8.4.0",
     "@ionic/angular-toolkit": "^11.0.1",

+ 20 - 0
projects/live-app/src/app/app.component.scss

@@ -7,3 +7,23 @@
   // position: absolute;
   // flex-direction: column;
 }
+
+/* 确保内容区域不被状态栏覆盖 */
+ion-app {
+  --ion-safe-area-top: 0;
+  --ion-safe-area-bottom: 0;
+  --ion-safe-area-left: 0;
+  --ion-safe-area-right: 0;
+}
+
+ion-content {
+  --ion-padding-top: env(safe-area-inset-top);
+  --ion-padding-bottom: env(safe-area-inset-bottom);
+  --ion-padding-start: env(safe-area-inset-left);
+  --ion-padding-end: env(safe-area-inset-right);
+}
+
+/* 确保内容区域不被状态栏覆盖 */
+ion-router-outlet {
+  padding-top: env(safe-area-inset-top);
+}

+ 45 - 36
projects/live-app/src/app/app.component.ts

@@ -1,22 +1,35 @@
-import { Component } from '@angular/core';
+import { Component, OnInit } from '@angular/core';
 import { IonApp, IonRouterOutlet } from '@ionic/angular/standalone';
-import { Camera } from '@capacitor/camera';
-import { Filesystem } from '@capacitor/filesystem';
 import Parse from 'parse';
+import { StatusBar, Style, StatusBarStyle } from '@capacitor/status-bar';
+import { BackgroundColorService } from '../services/background-color.service';
+import { Router } from '@angular/router';
 @Component({
   selector: 'app-root',
   standalone: true,
-  imports: [
-    IonApp,IonRouterOutlet
-  ],
+  imports: [IonApp, IonRouterOutlet],
   templateUrl: './app.component.html',
   styleUrl: './app.component.scss',
 })
-export class AppComponent {
+export class AppComponent implements OnInit {
   title = 'live-app';
-  constructor() {
+  constructor(
+    private router: Router,
+    private backgroundColorService: BackgroundColorService
+  ) {
     this.initParseService();
-    this.initDeviceService()
+    this.initStatusBar();
+  }
+  ngOnInit() {
+    // 初始化状态栏背景色
+    this.setStatusBarColor();
+
+    // 监听路由变化
+    this.router.events.subscribe((event) => {
+      if (event.constructor.name === 'NavigationEnd') {
+        this.setStatusBarColor();
+      }
+    });
   }
   initParseService() {
     Parse.initialize('ncloudmaster');
@@ -24,45 +37,41 @@ export class AppComponent {
     localStorage.setItem('company', 'Qje9D4bqol');
     this.saveParamsInvite();
   }
-  saveParamsInvite(){
-    let searchParams = this.searchParse()
-    let invite = searchParams?.get("invite");
-    invite && localStorage.setItem("invite",invite)
+  saveParamsInvite() {
+    let searchParams = this.searchParse();
+    let invite = searchParams?.get('invite');
+    invite && localStorage.setItem('invite', invite);
   }
-  searchParse(url?:string):URLSearchParams{
+  searchParse(url?: string): URLSearchParams {
     url = url || location.href;
     let u = new URL(url);
     return u.searchParams;
   }
-  async initDeviceService() {
+
+  async initStatusBar() {
     try {
-      // 请求相机权限
-      const cameraPermission = await Camera.requestPermissions();
-      if (cameraPermission.camera === 'granted') {
-        console.log('相机权限已授予');
-      } else {
-        console.log('相机权限被拒绝');
-      }
+      // 设置状态栏为沉浸式模式
+      // await StatusBar.setOverlaysWebView({ overlay: true });
 
-      // 请求文件系统权限
-      const filesystemPermission = await Filesystem.requestPermissions();
-      if (filesystemPermission.publicStorage === 'granted') {
-        console.log('文件系统权限已授予');
-      } else {
-        console.log('文件系统权限被拒绝');
-      }
-      await this.requestMicrophonePermission()
+      // 设置状态栏样式(可选)
+      await StatusBar.setStyle({ style: StatusBarStyle.Light });
+
+      // 设置状态栏背景颜色(可选)
+      // await StatusBar.setBackgroundColor({ color: '#000000' });
     } catch (error) {
-      console.error('请求权限时出错:', error);
+      console.error('设置状态栏时出错:', error);
     }
   }
-  async requestMicrophonePermission() {
+
+  async setStatusBarColor() {
     try {
-      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
-      console.log('麦克风权限已授予');
-      // 你可以在这里处理音频流
+      // 获取当前页面的背景色
+      const backgroundColor = this.backgroundColorService.getBackgroundColor();
+
+      // 设置状态栏背景色
+      await StatusBar.setBackgroundColor({ color: backgroundColor });
     } catch (error) {
-      console.error('麦克风权限被拒绝或请求失败:', error);
+      console.error('设置状态栏背景色时出错:', error);
     }
   }
 }

+ 1 - 0
projects/live-app/src/app/components/upload/upload.component.ts

@@ -102,6 +102,7 @@ export class UploadComponent implements OnInit {
   onDelete(index: number) {
     console.log(index);
     this.fileList.splice(index, 1);
+    this.active && this.onChange.emit(this.fileList);
   }
   async onAdd(e: any) {
     let files = e.target.files;

+ 4 - 1
projects/live-app/src/modules/tabs/home/home.component.ts

@@ -12,6 +12,7 @@ import {
 import { CommonModule, DatePipe } from '@angular/common';
 import { province } from '../../../services/address';
 import { FormsModule } from '@angular/forms';
+import { BackgroundColorService } from '../../../services/background-color.service';
 @Component({
   selector: 'app-home',
   templateUrl: './home.component.html',
@@ -90,7 +91,8 @@ export class HomeComponent implements OnInit {
     private connectTask: ConnectTaskService,
     private router: Router,
     private aiServ: AiChatService,
-    private datePipe: DatePipe
+    private datePipe: DatePipe,
+    private backgroundColorService: BackgroundColorService
   ) {
     province.unshift({
       provinceName: '全部',
@@ -108,6 +110,7 @@ export class HomeComponent implements OnInit {
   ngOnInit() {
     // this.activateRoute.paramMap.subscribe(async (params) => {
     this.refresh();
+    this.backgroundColorService.setBackgroundColor('#fe5559');
     // });
   }
   async refresh() {

+ 40 - 2
projects/live-app/src/modules/tabs/tabs/tabs.component.ts

@@ -6,11 +6,14 @@ import { IonicModule } from '@ionic/angular';
 
 // 全局消息服务和JIM插件
 import { OnInit } from '@angular/core';
-import { AlertController } from '@ionic/angular';
+// import { AlertController } from '@ionic/angular';
 import { FlutterCompComponent } from '../../../app/components/flutter-comp/flutter-comp.component';
 // import { MessageService } from '../../../services/message.service';
 // import { ConnectTaskService } from '../../../services/connectTask.service';
 
+import { Camera } from '@capacitor/camera';
+import { Filesystem } from '@capacitor/filesystem';
+
 @Component({
   selector: 'live-tabs',
   templateUrl: 'tabs.component.html',
@@ -77,7 +80,9 @@ export class TabsComponent implements OnInit {
   show: any;
   active: number = 0;
 
-  ngOnInit() {}
+  ngOnInit() {
+    this.initDeviceService()
+  }
 
   async presentAlert() {
     // let profile = localStorage.getItem('profile');
@@ -158,4 +163,37 @@ export class TabsComponent implements OnInit {
       this.allowNavi = true;
     }, 1000);
   }
+
+
+  async initDeviceService() {
+    try {
+      // 请求相机权限
+      const cameraPermission = await Camera.requestPermissions();
+      if (cameraPermission.camera === 'granted') {
+        console.log('相机权限已授予');
+      } else {
+        console.log('相机权限被拒绝');
+      }
+
+      // 请求文件系统权限
+      const filesystemPermission = await Filesystem.requestPermissions();
+      if (filesystemPermission.publicStorage === 'granted') {
+        console.log('文件系统权限已授予');
+      } else {
+        console.log('文件系统权限被拒绝');
+      }
+      await this.requestMicrophonePermission()
+    } catch (error) {
+      console.error('请求权限时出错:', error);
+    }
+  }
+  async requestMicrophonePermission() {
+    try {
+      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
+      console.log('麦克风权限已授予');
+      // 你可以在这里处理音频流
+    } catch (error) {
+      console.error('麦克风权限被拒绝或请求失败:', error);
+    }
+  }
 }

+ 35 - 5
projects/live-app/src/modules/user/certification/certification.component.html

@@ -4,7 +4,7 @@
     <div class="hred-left">
       {{ title }}
     </div>
-    <img src="http://file-cloud.fmode.cn/real-idcard.png" class="hred-img" />
+    <img src="https://file-cloud.fmode.cn/real-idcard.png" class="hred-img" />
   </div>
   @if (isReal) {
   <div class="tips">
@@ -20,7 +20,9 @@
     <div class="">{{ secretIdCard }}********</div>
   </div>
   <div class="agreement pflex">
-    <span (click)="showAgreement()">《{{registerAgreement?.get('title')}}》</span>
+    <span (click)="showAgreement()"
+      >《{{ registerAgreement?.get("title") }}》</span
+    >
   </div>
   } @else{
   <div class="h3">填写身份信息认证</div>
@@ -36,7 +38,7 @@
     />
   </div>
   <div class="li">
-    身份证号
+    {{cardtype == 'regions' ? '证件号码' : '身份证号'}}
     <input
       type="text"
       [(ngModel)]="idCard"
@@ -47,11 +49,39 @@
       autocomplete="new-password"
     />
   </div>
-  <div class="footer" (click)="check()">提交</div>
+
+  @if (cardtype == 'cn') {
+  <div class="footer" (click)="submit()">提交</div>
+  <div class="agreement">
+    <span class="abroad" (click)="cardtype = 'regions'">国外用户认证</span>
+  </div>
+  }@else {
+  <div class="li row">
+    请上传有效护照或外国永久居留证
+    <app-upload
+      (onChange)="changeFile($event)"
+      #upload
+      [maxlenght]="1"
+      [files]="eduImage ? [{ url: eduImage }] : []"
+      [fileWidth]="100"
+      [fileHeight]="160"
+      [boxWidth]="100"
+      [active]="true"
+      style="margin:10px auto;"
+    ></app-upload>
+  </div>
+  <div class="footer" (click)="submitRegions()">提交</div>
+  <div class="agreement">
+    <span class="abroad" (click)="cardtype = 'cn'">国内用户认证</span>
+  </div>
+  }
+
   <div class="agreement">
     <ion-checkbox color="primary" [(ngModel)]="agreement"></ion-checkbox>
     <div class="agreement-content">
-      阅读且同意<span (click)="showAgreement()">{{registerAgreement?.get('title')}}</span>
+      阅读且同意<span (click)="showAgreement()">{{
+        registerAgreement?.get("title")
+      }}</span>
     </div>
   </div>
   }

+ 4 - 0
projects/live-app/src/modules/user/certification/certification.component.scss

@@ -105,6 +105,10 @@
       flex: 1;
     }
   }
+  .row{
+    flex-direction: column;
+    align-items: start;
+  }
 
   .footer {
     margin: 26.6667vw auto 5.3333vw;

+ 114 - 30
projects/live-app/src/modules/user/certification/certification.component.ts

@@ -1,4 +1,4 @@
-import { Component, OnInit } from '@angular/core';
+import { Component, OnInit, ViewChild } from '@angular/core';
 import { FormsModule } from '@angular/forms';
 import {
   LoadingController,
@@ -10,16 +10,27 @@ import { identityCodeValid } from '../../../services/utils';
 import { AgreementComponent } from '../../login/agreement/agreement.component';
 import * as Parse from 'parse';
 import { NavComponent } from '../../../app/components/nav/nav.component';
-import { ionicStandaloneModules } from '../../ionic-standalone.modules';
+import {
+  AlertController,
+  ionicStandaloneModules,
+} from '../../ionic-standalone.modules';
+import { UploadComponent } from '../../../app/components/upload/upload.component';
 
 @Component({
   selector: 'app-certification',
   templateUrl: './certification.component.html',
   styleUrls: ['./certification.component.scss'],
   standalone: true,
-  imports: [...ionicStandaloneModules, FormsModule, NavComponent],
+  imports: [
+    ...ionicStandaloneModules,
+    FormsModule,
+    NavComponent,
+    UploadComponent,
+  ],
 })
 export class CertificationComponent implements OnInit {
+  @ViewChild('upload') upload!: UploadComponent;
+
   name: string = '';
   idCard: string = '';
   title: string =
@@ -33,10 +44,14 @@ export class CertificationComponent implements OnInit {
   agreement: boolean = false;
   registerAgreement: any;
   profile?: Parse.Object;
+  cardtype: 'regions' | 'cn' = 'cn'; // 身份证类型
+  eduImage: string = '';
+
   constructor(
     private service: HttpService,
     public loadCtrl: LoadingController,
     private modalController: ModalController,
+    private alertController: AlertController,
     public toastController: ToastController
   ) {}
 
@@ -61,10 +76,17 @@ export class CertificationComponent implements OnInit {
     this.profile = await query.first();
     if (this.profile?.id) {
       this.isReal = this.profile.get('isCross');
-      if (this.isReal)
+      if (this.isReal) {
         localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
-      this.secretIdCard = this.profile.get('idcard')?.slice(0, 6);
-      this.secretName = this.profile.get('name')?.slice(0, 1);
+        this.secretIdCard = this.profile.get('idcard')?.slice(0, 6);
+        this.secretName = this.profile.get('name')?.slice(0, 1);
+      } else if (this.profile.get('cardtype') === 'regions') {
+        this.cardtype = 'regions'
+        this.name = this.profile.get('name');
+        this.idCard = this.profile.get('idcard');
+        this.eduImage = this.profile.get('eduImage')
+        this.presentAlert('审核中',true);
+      }
     }
   }
   async showAgreement() {
@@ -87,10 +109,10 @@ export class CertificationComponent implements OnInit {
     toast.present();
   }
 
-  async check() {
-    if(!this.agreement){
-      this.presentToast('请先阅读且同意隐私协议',1500,'danger')
-      return
+  async submit() {
+    if (!this.agreement) {
+      this.presentToast('请先阅读且同意隐私协议', 1500, 'danger');
+      return;
     }
     this.loading = await this.loadCtrl.create();
     this.loading.present();
@@ -115,10 +137,14 @@ export class CertificationComponent implements OnInit {
         );
         return;
       }
-      if(this.getAgeFromIdCard(this.idCard) < 18){
+      if (this.getAgeFromIdCard(this.idCard) < 18) {
         this.loading.dismiss();
-        await this.presentToast('根据平台规定,仅限满18周岁用户使用。',1500,'danger')
-        return
+        await this.presentToast(
+          '根据平台规定,仅限满18周岁用户使用。',
+          1500,
+          'danger'
+        );
+        return;
       }
       let res: any = await this.service.postAuth(
         this.company,
@@ -129,8 +155,8 @@ export class CertificationComponent implements OnInit {
         let { isok } = res.data.result;
         console.log(isok);
         if (isok) {
-          let { sex,city } = res.data.result.IdCardInfor;
-          this.anthProfile(sex,city);
+          let { sex, city } = res.data.result.IdCardInfor;
+          this.anthProfile(sex, city);
           return;
         }
         this.loading.dismiss();
@@ -146,12 +172,13 @@ export class CertificationComponent implements OnInit {
     }, 500);
   }
 
-  async anthProfile(sex: string,city:string) {
+  async anthProfile(sex?: string, city?: string) {
     let user = Parse.User.current();
     let query = new Parse.Query('Profile');
     query.equalTo('idcard', this.idCard);
     query.equalTo('company', this.company);
     query.notEqualTo('isDeleted', true);
+    query.notEqualTo('user', Parse.User.current()?.id);
     let res = await query.first();
     if (res && res.id) {
       this.loading.dismiss();
@@ -179,19 +206,33 @@ export class CertificationComponent implements OnInit {
     }
     this.profile?.set('idcard', this.idCard);
     this.profile?.set('name', this.name);
-    this.profile?.set('sex', sex);
-    this.profile?.set('city', city);
-    this.profile?.set('isCross', true);
-    this.profile?.set('birthdate', String(this.getAgeFromIdCard(this.idCard)));
-    await this.profile?.save();
-    if (this.profile?.id) {
-      localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
-      this.loading.dismiss();
-      this.isReal = true;
-      await this.presentToast('认证成功', 1500, 'primary');
-      setTimeout(() => {
-        history.back();
-      }, 1500);
+    this.profile?.set('cardtype', this.cardtype);
+    if (this.cardtype === 'cn') {
+      this.profile?.set('sex', sex);
+      this.profile?.set('city', city);
+      this.profile?.set('isCross', true);
+      this.profile?.set(
+        'birthdate',
+        String(this.getAgeFromIdCard(this.idCard))
+      );
+      await this.profile?.save();
+      if (this.profile?.id) {
+        localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
+        this.loading.dismiss();
+        this.isReal = true;
+        await this.presentToast('认证成功', 1500, 'primary');
+        setTimeout(() => {
+          history.back();
+        }, 1500);
+      }
+    } else {
+      this.profile?.set('eduImage',this.eduImage)
+      await this.profile?.save();
+      if (this.profile?.id) {
+        localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
+        this.loading.dismiss();
+        this.presentAlert('提交成功,等待审核',true);
+      }
     }
   }
 
@@ -208,10 +249,53 @@ export class CertificationComponent implements OnInit {
     // 计算年龄
     let age = today.getFullYear() - birthDate.getFullYear();
     const monthDiff = today.getMonth() - birthDate.getMonth();
-    if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birthDate.getDate())) {
+    if (
+      monthDiff < 0 ||
+      (monthDiff === 0 && today.getDate() < birthDate.getDate())
+    ) {
       age--;
     }
     return age;
   }
 
+  async changeFile(e: any) {
+    this.eduImage = e[0]?.url;
+    console.log(this.eduImage);
+  }
+  async submitRegions() {
+    if (!this.agreement) {
+      this.presentToast('请先阅读且同意隐私协议', 1500, 'danger');
+      return;
+    }
+    this.loading = await this.loadCtrl.create();
+    this.loading.present();
+    this.time && clearTimeout(this.time);
+    this.time = setTimeout(async () => {
+      this.name = this.name.trim();
+      this.idCard = this.idCard.trim();
+      console.log(this.name, this.idCard);
+      if (!this.idCard || !this.name || !this.eduImage) {
+        this.loading.dismiss();
+        await this.presentToast('请填写完整信息', 1500, 'danger');
+        return;
+      }
+      this.anthProfile();
+    }, 500);
+  }
+  async presentAlert(msg: string, isBack?: boolean) {
+    const alert = await this.alertController.create({
+      header: '提示',
+      message: msg,
+      buttons: [
+        {
+          text: '好的',
+          role: 'confirm',
+          handler: () => {
+            isBack && history.back();
+          },
+        },
+      ],
+    });
+    await alert.present();
+  }
 }

+ 1 - 0
projects/live-app/src/modules/user/setting/setting.component.html

@@ -48,6 +48,7 @@
         interface="popover"
         placeholder="{{ formData.sex }}"
         [disabled]="true"
+        class="row"
       >
         <ion-select-option value="男">男</ion-select-option>
         <ion-select-option value="女">女</ion-select-option>

+ 2 - 2
projects/live-app/src/modules/user/setting/setting.component.scss

@@ -11,7 +11,7 @@ ion-content {
   --background: #ffffff;
   .portrait {
     background-color: white;
-    padding:4px 10px 4px 0;
+    padding:4px 10px 4px 0 !important;
     font-size: 4.2667vw;
     display: flex;
     justify-content: space-between;
@@ -36,7 +36,7 @@ ion-content {
     align-items: center;
     width: 100%;
     flex-shrink: 0;
-    // padding:0 4vw;
+    padding:0 4vw;
     // border: 0.2667vw solid #f6f6f6;
     .right {
       display: flex;

+ 16 - 0
projects/live-app/src/services/background-color.service.ts

@@ -0,0 +1,16 @@
+import { Injectable } from '@angular/core';
+
+@Injectable({
+  providedIn: 'root'
+})
+export class BackgroundColorService {
+  private backgroundColor: string = '#ffffff'; // 默认背景色
+
+  setBackgroundColor(color: string) {
+    this.backgroundColor = color;
+  }
+
+  getBackgroundColor(): string {
+    return this.backgroundColor;
+  }
+}