Browse Source

update 直播

warrior 3 months ago
parent
commit
b3c47a3151
22 changed files with 668 additions and 30 deletions
  1. 2 0
      package.json
  2. BIN
      projects/live-app/public/img/实名 (2).png
  3. BIN
      projects/live-app/public/img/直播协议.png
  4. BIN
      projects/live-app/public/img/签署协议.png
  5. 1 0
      projects/live-app/src/index.html
  6. 5 0
      projects/live-app/src/moduls/account/account.modules.routes.ts
  7. 32 0
      projects/live-app/src/moduls/account/recharge/recharge.component.html
  8. 75 0
      projects/live-app/src/moduls/account/recharge/recharge.component.scss
  9. 24 0
      projects/live-app/src/moduls/account/recharge/recharge.component.spec.ts
  10. 312 0
      projects/live-app/src/moduls/account/recharge/recharge.component.ts
  11. 5 5
      projects/live-app/src/moduls/account/wattle/wattle.component.ts
  12. 9 2
      projects/live-app/src/moduls/tabs/my/my.component.html
  13. 1 1
      projects/live-app/src/moduls/tabs/my/my.component.ts
  14. 2 2
      projects/live-app/src/moduls/tabs/tabs/tabs.component.ts
  15. 5 0
      projects/live-app/src/moduls/user/anchor/anchor.component.html
  16. 9 0
      projects/live-app/src/moduls/user/anchor/anchor.component.scss
  17. 28 0
      projects/live-app/src/moduls/user/anchor/anchor.component.spec.ts
  18. 88 0
      projects/live-app/src/moduls/user/anchor/anchor.component.ts
  19. 1 1
      projects/live-app/src/moduls/user/certification/certification.component.html
  20. 3 3
      projects/live-app/src/moduls/user/certification/certification.component.ts
  21. 7 2
      projects/live-app/src/moduls/user/user.modules.routes.ts
  22. 59 14
      projects/live-app/src/services/live.service.ts

+ 2 - 0
package.json

@@ -18,6 +18,8 @@
     "@angular/platform-browser": "^18.0.0",
     "@angular/platform-browser-dynamic": "^18.0.0",
     "@angular/router": "^18.0.0",
+    "@ionic-native/core": "^5.36.0",
+    "@ionic-native/wechat": "^5.36.0",
     "@ionic/angular": "^8.4.0",
     "@ionic/angular-server": "^8.4.0",
     "@ionic/angular-toolkit": "^11.0.1",

BIN
projects/live-app/public/img/实名 (2).png


BIN
projects/live-app/public/img/直播协议.png


BIN
projects/live-app/public/img/签署协议.png


+ 1 - 0
projects/live-app/src/index.html

@@ -8,6 +8,7 @@
   <link rel="icon" type="image/x-icon" href="favicon.ico">
   <script type="module" src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.esm.js"></script>
   <script nomodule src="https://unpkg.com/ionicons@7.1.0/dist/ionicons/ionicons.js"></script>
+	<script type="text/javascript" src="https://res.wx.qq.com/open/js/jweixin-1.6.0.js"></script>
   <script src="./assets/js/AgoraRTC_N-4.14.0.js"></script>
 </head>
 <body>

+ 5 - 0
projects/live-app/src/moduls/account/account.modules.routes.ts

@@ -1,6 +1,7 @@
 import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';
 import { BankcardComponent } from './bankcard/bankcard.component';
+import { RechargeComponent } from './recharge/recharge.component';
 import { WattleComponent } from './wattle/wattle.component';
 const routes: Routes = [
   {
@@ -16,6 +17,10 @@ const routes: Routes = [
     path: 'bankcard',
     component: BankcardComponent,
   },
+  {
+    path: 'recharge',
+    component: RechargeComponent,
+  },
 ]
 @NgModule({
   imports: [RouterModule.forChild(routes)],

+ 32 - 0
projects/live-app/src/moduls/account/recharge/recharge.component.html

@@ -0,0 +1,32 @@
+<nav title="余额充值"></nav>
+<div class="top">
+  <!-- <div class="head">
+		<ion-icon name="chevron-back-outline" style="width: 24px;height: 24px;color:#fff; position: fixed;
+    top: 15px;
+    left: 10px;" (click)="back()"></ion-icon>
+		<div class="name">余额充值</div>
+	</div> -->
+
+  <div class="li">
+    <input
+      type="number"
+      [(ngModel)]="price"
+      maxlength="8"
+      name="price"
+      autocomplete="off"
+      placeholder="请输入充值金额"
+      class="input"
+    />
+  </div>
+
+  <!--<div class="confirm" (click)="updataOrder()">充值</div>-->
+  <div class="confirm" (click)="showPay()">充值</div>
+
+  <ion-alert
+    [isOpen]="modal"
+    trigger="present-alert"
+    header="Select your favorite color"
+    [buttons]="alertButtons"
+    [inputs]="alertInputs"
+  ></ion-alert>
+</div>

+ 75 - 0
projects/live-app/src/moduls/account/recharge/recharge.component.scss

@@ -0,0 +1,75 @@
+.top {
+	background-image: url("https://file-cloud.fmode.cn/uiZD6NisQm/20220831/g1bkbm102855.png");
+	background-repeat: no-repeat;
+	background-position: center top;
+	background-size: 100% 100%;
+	height: 100%;
+	padding-top: 80px;
+	width: 100%;
+	text-align: center;
+
+
+	.head {
+		padding: 15px 5px;
+		background: #292B2A;
+
+		.name {
+			color: #fff;
+			text-align: center;
+		}
+	}
+
+	.li {
+		display: flex;
+		align-items: center;
+		width: 93.3333vw;
+		padding: 2.6667vw;
+		margin: 0 auto;
+		margin-top: 20px;
+		border-radius: 2.6667vw;
+		font-size: 5.2667vw;
+		font-family: Source Han Sans CN;
+		font-weight: 500;
+		// color: #FFFFFF;
+		// background: #353C4D;
+		border: 1px solid #000000;
+
+		.input {
+			margin-left: 10px;
+			border-radius: 1.6vw;
+			border: none;
+			// background: #353c4d;
+			color: #000000;
+			font-size: 14px;
+			flex: 1;
+			width: 200px;
+			background: none;
+		}
+	}
+
+	.confirm {
+		width: 150px;
+		background: #92a1ff;
+		border-radius: 4px;
+		padding: 10px 0 10px 0;
+		margin: 0 auto;
+		margin-top: 70px;
+		color: #fff;
+	}
+
+	modal {
+		margin-top: -100px;
+
+		.confirm {
+			margin-top: 10px;
+			margin-bottom: -10px;
+			color: #fff;
+			text-align: center;
+			background: #d81e06;
+
+			&:active {
+				background: #a11d0b;
+			}
+		}
+	}
+}

+ 24 - 0
projects/live-app/src/moduls/account/recharge/recharge.component.spec.ts

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

+ 312 - 0
projects/live-app/src/moduls/account/recharge/recharge.component.ts

@@ -0,0 +1,312 @@
+import { Component, OnInit } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import * as Parse from "parse";
+import { HttpClient } from "@angular/common/http";
+import { Router } from "@angular/router";
+import { catchError } from "rxjs/operators";
+import { LoadingController, ToastController, Platform,IonicModule } from '@ionic/angular';
+import { Wechat } from "@ionic-native/wechat/ngx";
+import { NavComponent } from '../../../app/components/nav/nav.component';
+declare var wx: any;
+
+@Component({
+	selector: 'app-recharge',
+	templateUrl: './recharge.component.html',
+	styleUrls: ['./recharge.component.scss'],
+	standalone: true,
+  imports: [IonicModule,NavComponent,FormsModule],
+})
+export class RechargeComponent implements OnInit {
+	constructor(private http: HttpClient, public toastController: ToastController,
+		public loadingController: LoadingController,
+		// private wxSDK: Wechat,
+		private router: Router,
+		private platform: Platform,
+	) { }
+	price: any = 0
+	loading: any
+	company = localStorage.getItem('company')
+	account?: Parse.Object
+	public modal: boolean = false
+	public method: any = { value: 'wx', name: '微信支付' }
+	public alertInputs = [
+    {
+      label: '微信支付',
+      type: 'radio',
+      value: 'wx',
+    },
+    {
+      label: '其他支付',
+      type: 'radio',
+      value: 'zfb',
+    },
+  ];
+	public alertButtons = [
+    {
+      text: 'Cancel',
+      role: 'cancel',
+      handler: () => {
+        console.log('Alert canceled');
+      },
+    },
+    {
+      text: 'OK',
+      role: 'confirm',
+      handler: () => {
+        this.confirmRechare()
+      },
+    },
+  ];
+	public data: Array<object> = [
+		{ value: 'wx', name: '微信支付', icon: 'https://file-cloud.fmode.cn/uiZD6NisQm/20220810/kvp1el034906.png' },
+		{ value: 'zfb', name: '其他支付', icon: 'https://file-cloud.fmode.cn/uiZD6NisQm/20220810/6pr338034905.png' }
+	];
+	wxpayEnabled = false;
+	wxAppId = "";
+	ngOnInit() {
+		let ua = navigator.userAgent.toLowerCase();
+		let isWeixin = ua.indexOf('micromessenger') != -1;
+		if (isWeixin) {
+			this.getWXSignPackageInWechat();
+		}
+		this.getAccount()
+	}
+
+
+	// 获取微信签名
+	getWXSignPackageInWechat() {
+		let that = this;
+		let params = {
+			company: localStorage.getItem("company"),
+			href: encodeURIComponent(location.href.split("#")[0]),
+		};
+		this.http
+			.post(`https://test.fmode.cn/api/wechat/getconfig`, params)
+			.subscribe((response) => {
+				// 返回的签名信息
+				let res: any = response;
+				const signPackage = res.data;
+				that.wxAppId = signPackage.appid;
+				this.wxpayEnabled = true;
+				wx.config({
+					debug: false, // 开启调试模式
+					appId: signPackage.appid, // 必填,公众号的唯一标识
+					timestamp: signPackage.timestamp, // 必填,生成签名的时间戳
+					nonceStr: signPackage.nonceStr, // 必填,生成签名的随机串
+					signature: signPackage.signature, // 必填,签名,见附录1
+					jsApiList: [
+						"chooseWXPay", // JSAPI微信支付
+					], // 必填,需要使用的JS接口列表,所有JS接口列表见附录2
+				});
+				wx.ready(() => { });
+				wx.error(() => { });
+			});
+	}
+
+	back() {
+		history.back();
+	}
+	async getAccount() {
+		let Account = new Parse.Query("Account")
+		Account.include("user")
+		Account.equalTo("user", Parse.User.current()?.id)
+		this.account = await Account.first()
+		console.log(this.account);
+	}
+
+	onChange(event:any){
+		this.method = JSON.parse(JSON.stringify(event))
+		console.log(this.method);
+	}
+
+
+	async showPay() {
+		if (this.price <= 0 || isNaN(this.price)) {
+			return this.presentToast(`充值错误,请重新确认充值金额!`, 1500, "danger");
+		}
+		if (this.price > 100000) {
+			return this.presentToast('单笔充值最多不超过100000元!', 1500, 'danger')
+		}
+		this.modal = true
+		this.price = parseInt(this.price)
+	}
+
+	async confirmRechare() {
+		let now = new Date();
+		let tradeNo = "C" +
+			String(now.getFullYear()) +
+			(now.getMonth() + 1) +
+			now.getDate() +
+			now.getHours() +
+			now.getMinutes() +
+			now.getSeconds() +
+			Math.random().toString().slice(-6); //生成六位随机数
+		console.log('订单编号:', tradeNo);
+		console.log(this.method.value);
+
+		// if (this.method.value == 'outline') {
+		// 	await this.updataOrder(tradeNo, 'outline')
+		// 	this.presentToast(`请选择支付方式`, 1500, "success");
+		// 	return
+		// } else 
+		if (this.method.value == 'wx') {
+			let ua = navigator.userAgent.toLowerCase();
+
+			let isWeixin = ua.indexOf('micromessenger') != -1;
+			if (isWeixin) { // 微信支付
+				await this.wxPay(tradeNo)
+			} else if (this.platform.is("cordova")) { // APP 调起微信支付
+				this.presentToast(`APP内暂不支持微信支付请前往微信端网页进行支付`, 3000, "success");
+				return
+				await this.openWxPay(tradeNo)
+			} else {
+				this.presentToast(`微信支付请在微信进入应用`, 1500, "success");
+			}
+		} else {
+			this.presentToast(`待开通支付通道`, 1500, "success");
+		}
+	}
+
+	// 调起微信支付
+	async openWxPay(tradeNo:string) {
+		let loading = await this.loadingController.create({
+			cssClass: "my-custom-class",
+			message: "支付跳转...",
+			duration: 1500,
+		});
+		loading.present();
+		let params = {
+			company: this.company,
+			out_trade_no: tradeNo,
+			total_fee: this.price,
+		};
+		try {
+			this.http
+				.post(`https://server.fmode.cn/api/wxpay/newsdkorder`, params)
+				.pipe(
+					catchError(async (e) => { // 显示报错
+						loading.present();
+						await this.presentToast(`服务器请求错误,请稍后重试`, 1500, "danger");
+					})
+				)
+				.subscribe(async (response) => {
+					let payParams: any = response;
+					console.log(payParams);
+
+					// this.wxSDK
+					// 	.sendPaymentRequest({
+					// 		appid: payParams.appid, // merchant id
+					// 		partnerid: payParams.partnerid, // merchant id
+					// 		prepayid: payParams.prepayid, // prepay id
+					// 		package: payParams.package, // nonce
+					// 		noncestr: payParams.noncestr, // nonce
+					// 		timestamp: payParams.timestamp, // timestamp
+					// 		sign: payParams.sign, // signed string
+					// 	})
+					// 	.then(async (data) => {
+					// 		if (data) {
+					// 			loading.present();
+					// 			console.log('支付成功返回结果:', data);
+					// 			await this.updataOrder(tradeNo, '微信')
+					// 		}
+					// 	})
+					// 	.catch(async (error) => {
+					// 		loading.present();
+					// 		await this.presentToast(`支付失败`, 1500, "danger");
+					// 		console.log('调起微信失败', error);
+					// 	});
+				});
+		} catch (error) {
+			loading.present();
+			await this.presentToast(`支付失败`, 1500, "danger");
+			console.log(error);
+		}
+	}
+
+
+
+	// 微信浏览器内支付
+	async wxPay(tradeNo:string) {
+		let openid = localStorage.getItem('openid')
+		if (openid) {
+			let params = {
+				company: this.company,
+				out_trade_no: tradeNo,
+				total_fee: this.price,
+				openid: openid,
+				appid: "wxb4579e09e263afa9"
+			};
+			let that = this
+			this.http
+				.post(`https://test.fmode.cn/api/wxpay/neworder`, params)
+				.subscribe(async (response) => {
+					let accountLog: any = await this.updataOrder(tradeNo, '微信')
+					let payinfo: any = response;
+					wx.chooseWXPay({
+						timestamp: payinfo.timeStamp, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写。但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符
+						nonceStr: payinfo.nonceStr, // 支付签名随机串,不长于 32 位
+						package: payinfo.package, // 统一支付接口返回的prepay_id参数值,提交格式如:prepay_id=\*\*\*)
+						signType: "MD5", // 签名方式,默认为'SHA1',使用新版支付需传入'MD5'
+						paySign: payinfo.paySign, // 支付签名
+						success: async (res:any) => {
+							// 支付成功后的回调函数
+							if (res) {
+								accountLog.set("isVerified", true)
+								await accountLog.save()
+								that.presentToast(`充值成功`, 1500, "success");
+								that.modal = false
+								that.price = 0
+							}
+						},
+					});
+				});
+		}
+
+
+	}
+
+	async presentToast(title:string, time:number, color:string) {
+		const toast = await this.toastController.create({
+			message: title,
+			duration: time,
+			color: color
+		});
+		toast.present();
+	}
+	//充值记录
+	async updataOrder(tradeNo:string, type:string) {
+		let AccountLog = Parse.Object.extend('AccountLog')
+		let account = new AccountLog()
+		account.set("isVerified", false)
+		account.set("company", {
+			__type: "Pointer",
+			className: "Company",
+			objectId: this.company
+		})
+		account.set("targetAccount", {
+			__type: "Pointer",
+			className: "Account",
+			objectId: this.account?.id
+		})
+		account.set("fromName", "system")
+		account.set("fromAccountName", "system")
+		account.set("orderNumber", tradeNo)
+		account.set("desc", `${type}充值${this.price}`)
+		account.set("assetType", 'balance')
+		account.set("orderType", `${type}recharge`)
+		account.set("assetCount", this.price)
+		account.save().then((res: any) => {
+			return res
+			// res.set("isVerified", true)
+			// res.save().then(res => {
+			// 	this.presentToast(`充值成功`, 1500, "success");
+			// 	this.modal = false
+			// 	this.price = 0
+			// })
+		})
+	}
+
+	onClose() {
+		this.modal = false;
+	}
+}

+ 5 - 5
projects/live-app/src/moduls/account/wattle/wattle.component.ts

@@ -24,7 +24,7 @@ export class WattleComponent implements OnInit {
 
   }
   back() {
-    this.router.navigate(['metayoung/tabs/my'])
+    this.router.navigate(['account/tabs/my'])
   }
   async getAccount() {
     let id = Parse.User.current()?.id
@@ -43,16 +43,16 @@ export class WattleComponent implements OnInit {
   //   }
   // }
   record() {
-    this.router.navigate(['metayoung/record'])
+    this.router.navigate(['account/record'])
   }
   records() {
-    this.router.navigate(['metayoung/records'])
+    this.router.navigate(['account/records'])
   }
   recharge() {
-    this.router.navigate(['metayoung/recharge'])
+    this.router.navigate(['account/recharge'])
   }
   withdrawal() {
-    this.router.navigate(['metayoung/withdrawal'])
+    this.router.navigate(['account/withdrawal'])
   }
   toBank() {
     this.router.navigate(['account/bankcard'])

+ 9 - 2
projects/live-app/src/moduls/tabs/my/my.component.html

@@ -141,7 +141,14 @@
       </div>
     </div>
     <div class="list">
-      <div class="li" (click)="toUrl('/user/idcard')">
+      <div class="li" (click)="toUrl('/user/certification')">
+        <div class="li-lable">
+          <img src="/img/实名 (2).png" alt="" class="icon" />
+          实名认证
+        </div>
+        <ion-icon name="chevron-forward-outline"></ion-icon>
+      </div>
+      <div class="li" (click)="toUrl('/user/anchor')">
         <div class="li-lable">
           <img src="/img/成为主播.png" alt="" class="icon" />
           成为主播
@@ -178,7 +185,7 @@
       </div>
       <div class="li" (click)="showAgreement('liveAgreement')">
         <div class="li-lable">
-          <img src="/img/直播协议.png" alt="" class="icon" />
+          <img src="/img/签署协议.png" alt="" class="icon" />
           直播协议
         </div>
         <ion-icon name="chevron-forward-outline"></ion-icon>

+ 1 - 1
projects/live-app/src/moduls/tabs/my/my.component.ts

@@ -148,7 +148,7 @@ export class MyComponent implements OnInit {
             text: '去认证',
             handler: () => {
               console.log('Confirm Cancel: blah');
-              this.toUrl('/user/idcard')
+              this.toUrl('/user/auchor')
             },
           },
         ],

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

@@ -79,8 +79,8 @@ export class TabsComponent implements OnInit {
     query.equalTo('company', this.company);
     query.notEqualTo('isDeleted', true);
     let res = await query.first();
-    if (res && res.id) {
-      localStorage.setItem('profile', res.id);
+    if (res?.id) {
+      localStorage.setItem('profile', JSON.stringify(res.toJSON()));
       return;
     }
     // const alert = await this.alertController.create({

+ 5 - 0
projects/live-app/src/moduls/user/anchor/anchor.component.html

@@ -0,0 +1,5 @@
+<nav title="主播认证"></nav>
+<ion-content class="content"> 
+
+
+</ion-content>

+ 9 - 0
projects/live-app/src/moduls/user/anchor/anchor.component.scss

@@ -0,0 +1,9 @@
+.content {
+  // --background: #f8f8f8;
+  --padding-bottom: 5.3333vw;
+  background-image: url("https://file-cloud.fmode.cn/uiZD6NisQm/20220831/g1bkbm102855.png") !important;
+	background-repeat: no-repeat;
+	background-position: center top;
+	background-size: 100% 100%;
+  --background:#ffffff00;
+}

+ 28 - 0
projects/live-app/src/moduls/user/anchor/anchor.component.spec.ts

@@ -0,0 +1,28 @@
+/* tslint:disable:no-unused-variable */
+import { async, ComponentFixture, TestBed } from '@angular/core/testing';
+import { By } from '@angular/platform-browser';
+import { DebugElement } from '@angular/core';
+
+import { AnchorComponent } from './anchor.component';
+
+describe('AnchorComponent', () => {
+  let component: AnchorComponent;
+  let fixture: ComponentFixture<AnchorComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ AnchorComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(AnchorComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 88 - 0
projects/live-app/src/moduls/user/anchor/anchor.component.ts

@@ -0,0 +1,88 @@
+import { Component, OnInit, ViewChild } from '@angular/core';
+import { FormsModule } from '@angular/forms';
+import {
+  AlertController,
+  IonicModule,
+  LoadingController,
+  ModalController,
+  ToastController,
+} from '@ionic/angular';
+import { HttpService } from '../../../services/http.service';
+import * as utils from '../../../services/utils';
+import { AgreementComponent } from '../../login/agreement/agreement.component';
+import { NavComponent } from '../../../app/components/nav/nav.component';
+import { LiveService } from '../../../services/live.service';
+import * as Parse from 'parse';
+import { UploadComponent } from '../../../app/components/upload/upload.component';
+import { Router } from '@angular/router';
+
+@Component({
+  selector: 'app-anchor',
+  templateUrl: './anchor.component.html',
+  styleUrls: ['./anchor.component.scss'],
+  standalone: true,
+  imports: [IonicModule, FormsModule, NavComponent,UploadComponent],
+})
+export class AnchorComponent implements OnInit {
+  @ViewChild('upload') upload!: UploadComponent;
+  profile?: Parse.Object; //身份信息
+  formData: any = {
+    avatar: '',
+    name: '',
+    nickname: '',
+    sex: '',
+    age: '',
+    remark:''
+  };
+  mobile: string = '';
+  loading: any;
+  agreement: boolean = false;
+  registerAgreement: any;
+  company:any
+  constructor(
+    private modalController: ModalController,
+    public loadingCtrl: LoadingController,
+    public toastController: ToastController,
+    private router: Router,
+    private liveService: LiveService,
+  ) {
+    this.company = liveService.company;
+  }
+  ngOnInit() {
+    this.getAgreement();
+    this.getProfile();
+  }
+  async getProfile() {
+    if(!this.liveService.profile){
+      this.liveService.alertTips('请先完成实名认证','提示',()=>{
+        this.router.navigate(['/user/certification'],{replaceUrl:true});
+      })
+      return
+    }
+    let uid = Parse.User.current()?.id;
+    let query = new Parse.Query('Profile');
+    query.equalTo('user', uid);
+    query.notEqualTo('isDeleted', true);
+    this.profile = await query.first();
+    if (this.profile?.id) {}
+  }
+  getAgreement() {
+    let Agreement = new Parse.Query('ContractAgreement');
+    Agreement.equalTo('company', this.company);
+    Agreement.equalTo('type', 'register');
+    Agreement.first().then((res) => {
+      console.log(res);
+      this.registerAgreement = res;
+    });
+  }
+  async showAgreement() {
+    const modal = await this.modalController.create({
+      component: AgreementComponent,
+      cssClass: 'my-custom-class',
+      componentProps: {
+        agreement: this.registerAgreement,
+      },
+    });
+    return await modal.present();
+  }
+}

+ 1 - 1
projects/live-app/src/moduls/user/certification/certification.component.html

@@ -1,4 +1,4 @@
-<nav title="主播认证"></nav>
+<nav title="实名认证"></nav>
 <ion-content class="content">
   <div class="hred">
     <div class="hred-left">

+ 3 - 3
projects/live-app/src/moduls/user/certification/certification.component.ts

@@ -61,8 +61,8 @@ export class CertificationComponent implements OnInit {
     this.profile = await query.first();
     if (this.profile?.id) {
       this.isReal = this.profile.get('isCross');
-      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);
     }
   }
   async showAgreement() {
@@ -171,7 +171,7 @@ export class CertificationComponent implements OnInit {
     this.profile?.set('isCross', true);
     await this.profile?.save();
     if (this.profile?.id) {
-      localStorage.setItem('profile', this.profile.id);
+      localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
       this.loading.dismiss();
       this.isReal = true;
       await this.presentToast('认证成功', 1500, 'primary');

+ 7 - 2
projects/live-app/src/moduls/user/user.modules.routes.ts

@@ -1,6 +1,7 @@
 import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';
 import { AlbumComponent } from './album/album.component';
+import { AnchorComponent } from './anchor/anchor.component';
 import { CertificationComponent } from './certification/certification.component';
 import { FeedbackComponent } from './feedback/feedback.component';
 import { ProfileComponent } from './profile/profile.component';
@@ -8,17 +9,21 @@ import { SettingComponent } from './setting/setting.component';
 import { ShareComponent } from './share/share.component';
 const routes: Routes = [
   {
-    path: 'profile/:id',//实名
+    path: 'profile/:id',//主页
     component: ProfileComponent,
   },
   {
-    path: 'idcard',//实名
+    path: 'certification',//实名
     component: CertificationComponent,
   },
   {
     path: 'album',//相册
     component: AlbumComponent,
   },
+  {
+    path: 'anchor',//主播认证
+    component: AnchorComponent,
+  },
   {
     path: 'feedback',//意见反馈
     component: FeedbackComponent,

+ 59 - 14
projects/live-app/src/services/live.service.ts

@@ -48,6 +48,7 @@ export class LiveService {
     audioDevice: '',
     videoDevice: '',
   };
+  room?: Parse.Object; //直播间
   constructor(
     private http: HttpService,
     private aiServ: AiChatService,
@@ -63,8 +64,9 @@ export class LiveService {
     queryProfile.equalTo('user', Parse.User.current()?.id);
     queryProfile.notEqualTo('isDeleted', true);
     queryProfile.equalTo('isCross', true);
-    let r = await queryProfile.first();
-    this.profile = r?.id;
+    this.profile = await queryProfile.first();
+    this.profile?.id &&
+      localStorage.setItem('profile', JSON.stringify(this.profile.toJSON()));
   }
   /* 初始化Agora */
   initAgora() {
@@ -109,7 +111,10 @@ export class LiveService {
   }
 
   async getToken(room: Parse.Object) {
+    this.room = room;
     this.timer && clearTimeout(this.timer);
+    let remoteEle = document.getElementById('vice-video');
+    (remoteEle as any).style.display = 'none';
     //获取频道token记录
     if (room?.get('profile').id == this.profile) {
       this.UID = 111111;
@@ -181,10 +186,11 @@ export class LiveService {
     // await this.client.setClientRole('host');
     this.localTracks.audioTrack = data[1];
     this.localTracks.videoTrack = data[2];
-    console.log(this.localTracks);
+    // console.log(this.localTracks);
     let remoteEle = document.getElementById('vice-video');
     if (remoteEle) {
       remoteEle.textContent = '';
+      remoteEle.style.display = 'block';
     }
     this.localTracks.videoTrack.play('vice-video'); //播放自己视频渲染
     if (this.tools['audio']) {
@@ -192,27 +198,63 @@ export class LiveService {
     } else {
       this.localTracks.audioTrack.setEnabled(true);
     }
+    await this.joinReady();
+  }
+  /* 发布本地视频 */
+  async publishSelf() {
     await this.client.publish(Object.values(this.localTracks));
-    this.joinReady();
   }
   /* 订阅远程视频 */
   async joinReady() {
-    await Promise.all(
-      this.client.remoteUsers.map((item: any) => {
-        if (item.uid && item._video_added_ && item._videoTrack) {
-          console.log('已存在用户:', item);
-          this.client.subscribe(item);
+    await this.publishSelf();
+    this.client.remoteUsers.forEach((user: any) => {
+      console.log('remoteUsers', user.uid);
+      this.client.subscribe(user, 'audio').then((audioTrack: any) => {
+        this.user_published_list.add(user);
+        audioTrack.setVolume(0);
+        audioTrack.play();
+      });
+      this.client.subscribe(user, 'video').then((videoTrack: any) => {
+        let remoteEle = document.getElementById('video');
+        if (remoteEle) {
+          remoteEle.textContent = '';
         }
-      })
-    );
+        videoTrack.play('video');
+      });
+    });
+    // this.timer && clearInterval(this.timer);
+    // console.log(this.client.remoteUsers);
+    // const isAnchor = this.room?.get('user')?.id === Parse.User.current()?.id
+    // if(isAnchor){
+    //   console.log('当前用户身份:房主');
+    //   // this.localTracks.audioTrack.setEnabled(false);
+    //   // this.localTracks.videoTrack.setEnabled(false);
+    //   await this.publishSelf()
+    // }else{
+    //   console.log('当前用户身份:用户');
+    //   console.log('主播在线状态',this.client.remoteUsers.length);
+    //   if (location.pathname.indexOf('/live/link-room/') == -1) return;
+    //   /* 如果主播还没有上线,等待主播上线后发布 */
+    //   if(this.client.remoteUsers.length == 0){
+    //     setTimeout(() => {
+    //       this.joinReady()
+    //     }, 1000);
+    //     return
+    //   }
+    // }
     this.client.on('user-joined', (user: any) => {
       console.log(user, `${user.uid} 加入频道`);
     });
     this.client.on('user-published', async (user: any, mediaType: string) => {
+      /* 当用户进入直播间,主播重新推流视频:解决订阅者remoteUsers无法渲染问题 */
+      // if(isAnchor){
+      //   // await this.client.unpublish(Object.values(this.localTracks));
+      //   // await this.client.publish(Object.values(this.localTracks));
+      //   // this.localTracks.videoTrack.setEnabled(true);
+      //   // this.localTracks.audioTrack.setEnabled(true);
+      // }
       console.log('用户推流成功', user);
-      // if (user.uid == 333333) {
       await this.client.subscribe(user, mediaType);
-      // }
       let remoteEle = document.getElementById('video');
       if (remoteEle) {
         remoteEle.textContent = '';
@@ -240,6 +282,9 @@ export class LiveService {
         this.alertTips('对方已离开直播间');
       }
     });
+    // if(!isAnchor){
+    //   this.publishSelf()
+    // }
   }
   /* 停止音频推流 */
   async updatePublishedAudioTrack() {
@@ -265,7 +310,7 @@ export class LiveService {
         await this.client.unsubscribe(user, 'audio');
       } else {
         await this.client.subscribe(user, 'audio');
-        console.log('恢复');
+        console.log('恢复声音');
       }
     }
   }