warrior 2 months ago
parent
commit
d017c91e37

BIN
projects/live-app/public/img/call.png


+ 3 - 0
projects/live-app/src/modules/account/order/order.component.html

@@ -22,4 +22,7 @@
     </div>
   </div>
   }
+  <ion-infinite-scroll (ionInfinite)="onIonInfinite($event)">
+    <ion-infinite-scroll-content></ion-infinite-scroll-content>
+  </ion-infinite-scroll>
 </ion-content>

+ 14 - 1
projects/live-app/src/modules/account/order/order.component.ts

@@ -4,6 +4,7 @@ import { ionicStandaloneModules } from '../../ionic-standalone.modules';
 import * as Parse from 'parse';
 import { CommonModule, DatePipe } from '@angular/common';
 import { Router } from '@angular/router';
+import { InfiniteScrollCustomEvent } from '@ionic/angular';
 @Component({
   selector: 'app-order',
   templateUrl: './order.component.html',
@@ -29,9 +30,21 @@ export class OrderComponent implements OnInit {
     query.skip(this.list.length)
     query.select('price','status','targetObject.name')
     query.descending('createdAt')
-    this.list = await query.find();
+    let log = await query.find();
+    this.list.push(...log)
+    return log
   }
   toUrl(id:string){
     this.router.navigate(['account/order/detail/'+id]);
   }
+
+  async onIonInfinite(ev:any) {
+    let result = await this.getOrder();
+    if(result.length == 0) {
+      (ev as InfiniteScrollCustomEvent).target.disabled = true;
+    }
+    setTimeout(() => {
+      (ev as InfiniteScrollCustomEvent).target.complete();
+    }, 500);
+  }
 }

+ 4 - 0
projects/live-app/src/modules/ionic-standalone.modules.ts

@@ -29,6 +29,8 @@ import {
   IonAlert,
   IonTextarea,
   // IonPickerLegacy,
+  IonInfiniteScroll,
+  IonInfiniteScrollContent,
 
   AlertController,
   ToastController,
@@ -68,6 +70,8 @@ export const ionicStandaloneModules = [
   IonAvatar,
   IonAlert,
   // IonPickerLegacy
+  IonInfiniteScroll,
+  IonInfiniteScrollContent
 ];
 
 export {

+ 27 - 0
projects/live-app/src/modules/live/call-log/call-log.component.html

@@ -0,0 +1,27 @@
+<nav title="视频接通记录"></nav>
+<ion-content class="content">
+  @for (item of list; track $index) {
+  <div class="log-item" (click)="toUrl(item.id)">
+    <div class="item-col">
+      <div class="title">{{ item?.get("title") }}</div>
+      <div class="desc">
+        {{ item?.get("createdAt") | date : "yyyy-MM-dd HH:ss" }}
+      </div>
+    </div>
+    <div class="item-col-right">
+      <div class="row">
+        <div class="desc">直播UID:{{ item?.get("uid") }}</div>
+        <div class="desc">
+          @if (item?.get("isLive")) {
+          <div class="tag"></div>
+          接通 }@else { 未接通 }
+        </div>
+      </div>
+      <ion-icon name="chevron-forward-outline"></ion-icon>
+    </div>
+  </div>
+  }
+  <ion-infinite-scroll (ionInfinite)="onIonInfinite($event)">
+    <ion-infinite-scroll-content></ion-infinite-scroll-content>
+  </ion-infinite-scroll>
+</ion-content>

+ 38 - 0
projects/live-app/src/modules/live/call-log/call-log.component.scss

@@ -0,0 +1,38 @@
+.log-item {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 6px 10px;
+  border-bottom: 1px solid #f3f3f3;
+  font-size: 14px;
+  .item-col {
+    .title{
+      margin-bottom: 4px;
+    }
+  }
+  .item-col-right {
+    display: flex;
+    align-items: center;
+    .row {
+      margin-right: 6px;
+      display: flex;
+      flex-direction: column;
+      align-items: flex-end;
+    }
+  }
+  .desc,
+  ion-icon {
+    color: #8b8b8b;
+  }
+  .desc {
+    display: flex;
+    align-items: center;
+    .tag {
+      width: 6px;
+      height: 6px;
+      background-color: #42d96b;
+      border-radius: 50%;
+      margin-right: 6px;
+    }
+  }
+}

+ 28 - 0
projects/live-app/src/modules/live/call-log/call-log.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 { CallLogComponent } from './call-log.component';
+
+describe('CallLogComponent', () => {
+  let component: CallLogComponent;
+  let fixture: ComponentFixture<CallLogComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ CallLogComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(CallLogComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 62 - 0
projects/live-app/src/modules/live/call-log/call-log.component.ts

@@ -0,0 +1,62 @@
+import { CommonModule, DatePipe } from '@angular/common';
+import { Component, OnInit } from '@angular/core';
+import { Router } from '@angular/router';
+import { NavComponent } from '../../../app/components/nav/nav.component';
+import { ionicStandaloneModules } from '../../ionic-standalone.modules';
+import * as Parse from 'parse';
+import { InfiniteScrollCustomEvent } from '@ionic/angular';
+
+@Component({
+  selector: 'app-call-log',
+  templateUrl: './call-log.component.html',
+  styleUrls: ['./call-log.component.scss'],
+  standalone: true,
+  imports: [...ionicStandaloneModules, NavComponent, CommonModule],
+  providers: [DatePipe],
+})
+export class CallLogComponent implements OnInit {
+  list: Array<any> = [];
+  constructor(private router: Router) {}
+
+  ngOnInit() {
+    this.getLiveLog();
+  }
+  async getLiveLog() {
+    let profile = JSON.parse(localStorage.getItem('profile') || '{}');
+    let queryParams: any = {
+      where: {
+        $or: [
+          {
+            user: Parse.User.current()?.id,
+          },
+          {
+            profile: profile?.objectId,
+          },
+        ],
+      },
+    };
+    let query = Parse.Query.fromJSON('LiveLog', queryParams);
+    query.notEqualTo('isDeleted', true);
+    query.limit(20);
+    query.skip(this.list.length);
+    query.include('profile');
+    query.descending('createdAt');
+    let log = await query.find();
+    this.list.push(...log)
+    return log
+  }
+
+  toUrl(id: string) {
+    this.router.navigate(['live/call-log/detail/' + id]);
+  }
+
+  async onIonInfinite(ev:any) {
+    let result = await this.getLiveLog();
+    if(result.length == 0) {
+      (ev as InfiniteScrollCustomEvent).target.disabled = true;
+    }
+    setTimeout(() => {
+      (ev as InfiniteScrollCustomEvent).target.complete();
+    }, 500);
+  }
+}

+ 54 - 0
projects/live-app/src/modules/live/call-log/detail/detail.component.html

@@ -0,0 +1,54 @@
+<nav title="订单详情"></nav>
+<ion-content class="content">
+  <div class="status-view">
+    <img
+      [src]="liveLog?.get('isLive') ? 'img/call.png' : 'img/warn.png'"
+      alt=""
+      class=""
+    />
+    @if (liveLog?.get("isLive")) {
+    <div class="text">接通</div>
+    <div class="desc">视频通话接通</div>
+    }@else {
+    <div class="text">未接通</div>
+    <div class="desc">视频通话未接通</div>
+    }
+  </div>
+  <div class="h3">通话详情</div>
+  <div class="card-container">
+    <div class="row">
+      <div class="label">通话标题:</div>
+      <div class="val">{{ liveLog?.get("title") }}</div>
+    </div>
+    <div class="row">
+      <div class="label">直播间:</div>
+      <div class="val">
+        {{ liveLog?.get("room")?.get("title") }}
+      </div>
+    </div>
+    <div class="row">
+      <div class="label">主播:</div>
+      <div class="val">
+        {{ liveLog?.get("profile")?.get("name") }}
+      </div>
+    </div>
+    <div class="row">
+      <div class="label">开始时间:</div>
+      <div class="val">
+        {{ liveLog?.get("createdAt") | date : "yyyy-MM-dd HH:ss" }}
+      </div>
+    </div>
+    <div class="row">
+      <div class="label">通话时长:</div>
+      <div class="val">
+        {{ timeLength | secondsToTime }}
+      </div>
+    </div>
+    <div class="row">
+      <div class="label">呼叫用户:</div>
+      <div class="val">
+        {{ liveLog?.get("user")?.get("name") }}
+      </div>
+    </div>
+  </div>
+</ion-content>

+ 39 - 0
projects/live-app/src/modules/live/call-log/detail/detail.component.scss

@@ -0,0 +1,39 @@
+.content {
+  --background: #fafafa;
+}
+.status-view {
+  background-color: white;
+  padding: 20px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  img {
+    width: 50px;
+    margin-bottom: 20px;
+  }
+  .text {
+    font-size: 16px;
+    margin: 10px 0;
+  }
+  .desc {
+    color: #8b8b8b;
+    font-size: 12px;
+  }
+}
+.h3 {
+  padding: 10px;
+}
+.card-container {
+  background: white;
+  padding: 10px;
+  .row {
+    display: flex;
+    margin-bottom: 10px;
+    font-size: 12px;
+    .label {
+      width: 120px;
+      color: #8b8b8b;
+    }
+  }
+}

+ 28 - 0
projects/live-app/src/modules/live/call-log/detail/detail.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 { DetailComponent } from './detail.component';
+
+describe('DetailComponent', () => {
+  let component: DetailComponent;
+  let fixture: ComponentFixture<DetailComponent>;
+
+  beforeEach(async(() => {
+    TestBed.configureTestingModule({
+      declarations: [ DetailComponent ]
+    })
+    .compileComponents();
+  }));
+
+  beforeEach(() => {
+    fixture = TestBed.createComponent(DetailComponent);
+    component = fixture.componentInstance;
+    fixture.detectChanges();
+  });
+
+  it('should create', () => {
+    expect(component).toBeTruthy();
+  });
+});

+ 34 - 0
projects/live-app/src/modules/live/call-log/detail/detail.component.ts

@@ -0,0 +1,34 @@
+import { Component, OnInit } from '@angular/core';
+import { CommonModule, DatePipe } from '@angular/common';
+import { ionicStandaloneModules } from '../../../ionic-standalone.modules';
+import { NavComponent } from '../../../../app/components/nav/nav.component';
+import * as Parse from 'parse';
+import { ActivatedRoute } from '@angular/router';
+import { AiChatService } from '../../../../services/aichart.service';
+import { SharedModule } from "../../../shared.module";
+@Component({
+  selector: 'app-detail',
+  templateUrl: './detail.component.html',
+  styleUrls: ['./detail.component.scss'],
+  standalone: true,
+  imports: [...ionicStandaloneModules, NavComponent, CommonModule, SharedModule],
+  providers: [DatePipe],
+})
+export class DetailComponent implements OnInit {
+  liveLog?: Parse.Object;
+  timeLength:number = 0
+  constructor(private activateRoute: ActivatedRoute,private aiServ:AiChatService) {}
+
+  ngOnInit() {
+    this.activateRoute.paramMap.subscribe(async (params) => {
+      let id: any = params.get('id');
+      let query = new Parse.Query('LiveLog');
+      // query.select('price','isPay', 'orderNum','status', 'targetObject.name','targetObject.validity');
+      query.include('profile', 'user','room');
+      query.get(id).then((res) => {
+        this.liveLog = res;
+      });
+      this.timeLength = await this.aiServ.getLiveActiveLog(id)
+    });
+  }
+}

+ 10 - 1
projects/live-app/src/modules/live/live.modules.routes.ts

@@ -1,5 +1,7 @@
 import { NgModule } from '@angular/core';
 import { RouterModule, Routes } from '@angular/router';
+import { CallLogComponent } from './call-log/call-log.component';
+import { DetailComponent } from './call-log/detail/detail.component';
 import { ChatComponent } from './chat/chat.component';
 import { LinkPageComponent } from './link-page/link-page.component';
 import { RoomManageComponent } from './room-manage/room-manage.component';
@@ -21,7 +23,14 @@ const routes: Routes = [
     path: 'room-manage',//直播房间管理
     component: RoomManageComponent,
   },
-  
+  {
+    path: 'call-log',//直播通话记录
+    component: CallLogComponent,
+  },
+  {
+    path: 'call-log/detail/:id',//直播通话详情
+    component: DetailComponent,
+  },
 ]
 @NgModule({
   imports: [RouterModule.forChild(routes)],

+ 0 - 1
projects/live-app/src/modules/tabs/home/home.component.scss

@@ -230,7 +230,6 @@
 }
 ion-modal {
   --height: auto;
-
   align-items: end;
 }
 

+ 4 - 2
projects/live-app/src/modules/tabs/my/my.component.html

@@ -54,8 +54,10 @@
         {{ userObj.vip?.name }}<img src="img/VIP.png" alt="" />
       </div>
       <div class="vip-date" (click)="toUrl('goods/vip')">
-        到期时间:{{ userObj.vip?.expiredAt | date : "yyyy-MM-dd"
-        }}<ion-icon name="chevron-forward"></ion-icon>
+        <!-- 到期时间:{{ userObj.vip?.expiredAt | date : "yyyy-MM-dd"
+        }} -->
+        剩余天数:{{userObj.vip?.daysLeft}}
+        <ion-icon name="chevron-forward"></ion-icon>
       </div>
     </div>
     }@else {

+ 24 - 2
projects/live-app/src/modules/tabs/notice/notice.component.html

@@ -61,7 +61,7 @@
             ></ion-icon>
           </ion-item>
 
-          <ion-item class="li" (click)="toUrl('/live/chat')">
+          <ion-item class="li" (click)="toUrl('/live/call-log')">
             <img
               src="img/通话记录.png"
               class="avatar"
@@ -94,7 +94,7 @@
               name="chevron-forward-outline"
             ></ion-icon>
           </ion-item>
-          <ion-item class="li" (click)="toUrl('/live/chat')">
+          <ion-item class="li" (click)="isOpen = true">
             <img
               src="img/小助手.png"
               class="avatar"
@@ -180,3 +180,25 @@
   header="删除与ta的聊天记录"
   [buttons]="alertButtons"
 ></ion-alert>
+<ion-modal
+  #modal
+  trigger="open-modal"
+  [isOpen]="isOpen"
+  (didDismiss)="isOpen = false"
+  [initialBreakpoint]="1"
+  [breakpoints]="[0, 1]"
+>
+  <ng-template>
+    <ion-toolbar>
+      <ion-buttons slot="start">
+        <ion-button (click)="isOpen = false">关闭</ion-button>
+      </ion-buttons>
+      <ion-buttons slot="end">
+        <ion-button (click)="download()">下载</ion-button>
+      </ion-buttons>
+    </ion-toolbar>
+    <div class="code">
+      <img [src]="codeUrl" alt="" />
+    </div>
+  </ng-template>
+</ion-modal>

+ 4 - 0
projects/live-app/src/modules/tabs/notice/notice.component.scss

@@ -94,3 +94,7 @@ ion-popover {
   font-size: 14px;
   color: #676767;
 }
+ion-modal {
+  --height: auto;
+  align-items: end;
+}

+ 51 - 1
projects/live-app/src/modules/tabs/notice/notice.component.ts

@@ -11,7 +11,7 @@ import { ionicStandaloneModules } from '../../ionic-standalone.modules';
   templateUrl: './notice.component.html',
   styleUrls: ['./notice.component.scss'],
   standalone: true,
-  imports: [SharedModule,...ionicStandaloneModules],
+  imports: [SharedModule, ...ionicStandaloneModules],
 })
 export class NoticeComponent implements OnInit {
   active: string = 'notice';
@@ -37,6 +37,10 @@ export class NoticeComponent implements OnInit {
   timer: any;
   showModal: boolean = false; //是否长按弹窗
   currentObject: any; //当前选择对象
+  isOpen: boolean = false;
+  codeUrl: string = '';
+  notices: Array<any> = [];
+
   constructor(
     private router: Router,
     private aiSer: AiChatService,
@@ -53,6 +57,21 @@ export class NoticeComponent implements OnInit {
     console.log(this.friends);
     // let resChat = await this.aiSer.getLinkUsers(uid);
     // resChat?.data && this.noticeList.push(...resChat.data);
+    this.getSetting();
+    this.getSysNotice()
+  }
+  async getSetting() {
+    let query = new Parse.Query('SiteConfig');
+    query.equalTo('company', this.aiSer.company);
+    query.notEqualTo('isDeleted', false);
+    query.select('wxCode');
+    let r = await query.first();
+    this.codeUrl = r?.get('wxCode');
+  }
+  async getSysNotice(){
+    let result = await this.aiSer.getSysNotice(Parse.User.current()?.id!)
+    this.notices = result.data
+    console.log(this.notices);
   }
   segmentChanged(e: any) {
     let { value } = e.detail;
@@ -94,4 +113,35 @@ export class NoticeComponent implements OnInit {
     if (this.showModal) return;
     this.router.navigate([url]);
   }
+  async download() {
+    let canvas: any = document.createElement('canvas');
+    canvas.height = '480';
+    canvas.width = '480';
+    let ctx = canvas.getContext('2d');
+    //绘制二维码
+    ctx.drawImage(await this.compileImage(this.codeUrl), 0, 0, 480, 480);
+    let tempSrc = canvas.toDataURL('image/png');
+    let dlLink: any = document.createElement('a');
+    if ('download' in dlLink) {
+      dlLink.style.visibility = 'hidden';
+      dlLink.href = tempSrc;
+      dlLink.download = '分享海报';
+      document.body.appendChild(dlLink);
+      dlLink.click();
+      document.body.removeChild(dlLink);
+    } else {
+      location.href = tempSrc;
+    }
+  }
+
+  compileImage(url: string): Promise<any> {
+    return new Promise((res) => {
+      let img = new Image();
+      img.src = url;
+      img.setAttribute('crossOrigin', 'anonymous');
+      img.onload = function () {
+        res(img);
+      };
+    });
+  }
 }

+ 26 - 0
projects/live-app/src/services/aichart.service.ts

@@ -190,6 +190,24 @@ export class AiChatService {
     `;
     return this.http.customSQL(sql);
   }
+
+
+  /* 获取系统通知 */
+  getSysNotice(uid: string): Promise<any> {
+    let sql = `SELECT
+    (CASE WHEN "user" = 'l0gF95BPLB' THEN '请求添加'+u.name+'为好友'
+	ELSE '收到'+u.name+'的好友申请' END) title,
+    (CASE WHEN "isPass" = TRUE THEN '200'
+     WHEN "isPass" = FALSE THEN '101'
+    ELSE '100' END) status
+    FROM "Friends"
+    WHERE "company" = '${this.company}'
+    AND (friend = '${uid}' OR "user" = '${uid}')
+    ORDER BY "createdAt" DESC`;
+    console.log(sql);
+    return this.http.customSQL(sql);
+  }
+
   async getLinkUsers(uid: string): Promise<any> {
     // let date = this.datePipe.transform(new Date(), 'yyyy-MM-dd');
     let sql = `SELECT * FROM (
@@ -250,6 +268,14 @@ export class AiChatService {
     return res.data;
   }
 
+  /* 获取直播时长 */
+  async getLiveActiveLog(logid: string) {
+    let sql = `SELECT SUM("duration") FROM "LiveActiveLog" WHERE "liveLog" = '${logid}'`;
+    let res: any = await this.http.customSQL(sql);
+    return res.data[0]?.sum;
+  }
+  
+
   async get_duration(rid: string, uid: string) {
     let url = 'https://server.fmode.cn/api/ailiao/remain_second';
     let params = {