Browse Source

存储聊天消息

warrior 2 months ago
parent
commit
53a11bd6e9

+ 1 - 1
projects/live-app/src/app/components/pay-comp/pay-comp.component.html

@@ -42,7 +42,7 @@
         </div>
       </div>
       <div class="pay-footer">
-        <div class="order-num">订单编号{{orderNum}}</div>
+        <div class="order-num">订单编号{{tradeNo}}</div>
         <div class="pay-btn">去付款</div>
       </div>
     </div>

+ 2 - 3
projects/live-app/src/app/components/pay-comp/pay-comp.component.ts

@@ -15,10 +15,9 @@ declare var wx: any;
 })
 export class PayCompComponent implements OnInit {
   @Input('price') price!: number;
-  @Input('orderNum') orderNum: string = 'C20241215050401';
+  @Input('tradeNo') tradeNo: string = 'C20241215050401'; //支付单号
   @Output() onChange: EventEmitter<any> = new EventEmitter<any>();
-  tradeNo?: string; //支付单号
-  isOpen: boolean = false; //打开弹窗
+  isOpen: boolean = true; //打开弹窗
   checkpay: string = 'wxpay';
   userAgent?: string; //获取当前浏览器环境
 

+ 3 - 1
projects/live-app/src/modules/goods/vip/vip.component.html

@@ -112,4 +112,6 @@
   </div>
   }
 </ion-content>
-<app-pay-comp #paycomp></app-pay-comp>
+@if (showPay) {
+  <app-pay-comp [tradeNo]="tradeNo" #paycomp [price]="price"></app-pay-comp>
+}

+ 1 - 1
projects/live-app/src/modules/goods/vip/vip.component.scss

@@ -222,7 +222,7 @@
       justify-content: center;
       align-items: center;
       .open-pay{
-        background-color: #0054e9;
+        background-color: #ff5c64;
         color: #ffffff;
         padding: 8px 16px;
         border-radius: 4px;

+ 12 - 1
projects/live-app/src/modules/goods/vip/vip.component.ts

@@ -7,6 +7,7 @@ import { AuthService } from '../../../services/auth.service';
 import { DatePipe, CommonModule } from '@angular/common';
 import { AgreementComponent } from '../../login/agreement/agreement.component';
 import { PayCompComponent } from '../../../app/components/pay-comp/pay-comp.component';
+import { AccountService } from '../../../services/account.service';
 
 @Component({
   selector: 'app-vip',
@@ -32,10 +33,15 @@ export class VipComponent implements OnInit {
   registerAgreement: any;
   isCheck: boolean = false;
   currentGoods?: Parse.Object; //当前选择的会员
+  showPay: boolean = false;
+  price:number = 0;
+  tradeNo: string = ''; //支付单号
+
   constructor(
     private modalController: ModalController,
     private authSer: AuthService,
-    private datePipe: DatePipe
+    private datePipe: DatePipe,
+    private accServ: AccountService,
   ) {}
 
   ngOnInit() {
@@ -76,6 +82,7 @@ export class VipComponent implements OnInit {
     goods.include('services');
     this.goodsList = await goods.find();
     this.currentGoods = this.goodsList[0];
+    this.price = this.currentGoods?.get('price')
   }
   getAgreement() {
     let Agreement = new Parse.Query('ContractAgreement');
@@ -98,8 +105,12 @@ export class VipComponent implements OnInit {
   }
   onchang(data: Parse.Object) {
     this.currentGoods = data;
+    this.price = this.currentGoods?.get('price')
   }
   openpay() {
+    this.showPay = true
+    this.tradeNo = this.accServ.setTradeNo();
+    
     this.paycomp.isOpen = true;
   }
 }

+ 40 - 28
projects/live-app/src/modules/live/chat/chat.component.html

@@ -7,7 +7,9 @@
       ></ion-icon>
     </ion-buttons>
     <ion-title class="title">{{
-      uid !== "global_room" ? targetUser?.get("nickname") || profile?.get("name") : "世界频道"
+      uid !== "global_room"
+        ? targetUser?.get("nickname") || profile?.get("name")
+        : "世界频道"
     }}</ion-title>
     <ion-buttons slot="end" id="option-trigger">
       <ion-icon name="ellipsis-horizontal-outline"></ion-icon>
@@ -46,14 +48,14 @@
   <div #scrollMsg id="scrollMsg" class="scroll">
     @for (item of msgServe.messageMapList[uid]; track $index) {
     <div class="clearfix message-box">
-      @if ($index == 0 || item.create_time -
-      msgServe.messageMapList[uid][$index-1].create_time > 600) {
+      @if ($index == 0 || item.timestamp -
+      msgServe.messageMapList[uid][$index-1].timestamp > 600) {
       <div class="time-box">
         @if (item.istoday) {
-        <div class="time">{{ item.timeStamp | date : "HH:mm" }}</div>
+        <div class="time">{{ item.timestamp | date : "HH:mm" }}</div>
         }@else {
         <div class="time">
-          {{ item.timeStamp | date : "yyyy年MM月dd日 HH:mm" }}
+          {{ item.timestamp | date : "yyyy年MM月dd日 HH:mm" }}
         </div>
         }
       </div>
@@ -62,17 +64,24 @@
       <!-- 他人信息 -->
       @if (!item.is_self) {
       <div class="msg-bloak no_self">
-        <img class="avatar fl" mode="widthFix" src="{{ item.avatar }}" />
+        <img
+          class="avatar fl"
+          (click)="toUrl('user/profile/' + item.publisher)"
+          mode="widthFix"
+          src="{{ item.avatar }}"
+        />
         <!-- 文字消息 msg_type == 1 || text -->
         @if (item.msg_type == 1 || item.msg_type == 'text') {
-          <div class="msg-card">
-            @if(uid == 'global_room'){
-              <span class="text-item_status">{{ item.name || '新人' }}</span>
-            }
-            <div class="text-item fl text-item_left">
-              <p>{{ item.content }}</p>
-            </div>
+        <div class="msg-card">
+          @if(uid == 'global_room'){
+          <span class="text-item_status">{{
+            item.name || "用户" + item.publisher
+          }}</span>
+          }
+          <div class="text-item fl text-item_left">
+            <p>{{ item.content }}</p>
           </div>
+        </div>
         }
         <!-- 图片消息 msg_type == 2 || img -->
         @else if(item.msg_type == 2 || item.msg_type == 'img' || item.msg_type
@@ -82,7 +91,6 @@
           [src]="item.content"
           (click)="predivimg(item.content)"
         />
-        } @else if (item.msg_type == 3) {
         <div class="text-item fl text-item_left">
           <p>{{ item.content }}</p>
         </div>
@@ -127,17 +135,27 @@
       (keydown)="comfirmText($event)"
     >
     </ion-input>
-    <div class="tools" slot="end">
+    <div
+      [ngClass]="{
+        'tools-maxwid':
+          uid !== 'global_room' && profile?.get('identyType') === 'anchor',
+        tools: true
+      }"
+      slot="end"
+    >
       <ion-icon name="happy-outline" (click)="changeShowEmoji()"></ion-icon>
+      @if (uid !== 'global_room' && profile?.get("identyType") === 'anchor') {
       <ion-icon name="videocam-outline"></ion-icon>
-      <ion-icon name="gift-outline" (click)="isOpen = true"></ion-icon>
+      } @if (uid !== 'global_room' && profile?.get("identyType") === 'anchor') {
+      <ion-icon name="gift-outline" (click)="gift.openModal()"></ion-icon>
+      }
       <span class="splice">|</span>
       <!-- <div class="send">发送</div> -->
       <ion-button
         class="send"
         fill="outline"
         size="small"
-        [disabled]="text.length == 0"
+        [disabled]="text.length == 0 || disabled"
         (click)="comfirmText()"
         >发送</ion-button
       >
@@ -166,14 +184,8 @@
   </div>
   }
 </ion-footer>
-<ion-modal
-  (click)="isOpen = false"
-  (didDismiss)="dismiss($event)"
-  [isOpen]="isOpen"
-  [initialBreakpoint]="0.5"
-  [breakpoints]="[0, 0.5, 1]"
->
-  <ng-template>
-    <div class="block">敬请期待</div>
-  </ng-template>
-</ion-modal>
+<app-gift-modal
+  #gift
+  (sendEmit)="onSendGift()"
+  [toUid]="this.uid"
+></app-gift-modal>

+ 7 - 1
projects/live-app/src/modules/live/chat/chat.component.scss

@@ -91,7 +91,7 @@
 }
 .footer-tool {
   .tools {
-    width: 160px;
+    // width: 160px;
     display: flex;
     align-items: center;
     justify-content: space-between;
@@ -99,6 +99,12 @@
     ion-icon {
       font-size: 30px;
     }
+    .splice {
+      margin: 0 2px;
+    }
+  }
+  .tools-maxwid {
+    width: 160px;
   }
 }
 .emoji-content {

+ 31 - 17
projects/live-app/src/modules/live/chat/chat.component.ts

@@ -1,12 +1,11 @@
-import { DatePipe } from '@angular/common';
+import { DatePipe, CommonModule } from '@angular/common';
 import { HttpClient } from '@angular/common/http';
 import { Component, OnInit } from '@angular/core';
 import { FormsModule } from '@angular/forms';
-import { ActivatedRoute } from '@angular/router';
+import { ActivatedRoute, Router } from '@angular/router';
 import {
   IonicModule,
   LoadingController,
-  ModalController,
   ScrollDetail,
   ToastController,
 } from '@ionic/angular';
@@ -14,14 +13,20 @@ import {
 // var JIM = JIMServ.JIM;
 // var Events = JIMServ.Events;
 import * as Parse from 'parse';
+import { GiftModalComponent } from '../../../app/components/gift-modal/gift-modal.component';
 import { MessageService } from '../../../services/message.service';
-
 @Component({
   selector: 'app-chat',
   templateUrl: './chat.component.html',
   styleUrls: ['./chat.component.scss'],
   standalone: true,
-  imports: [IonicModule, FormsModule, DatePipe],
+  imports: [
+    IonicModule,
+    FormsModule,
+    DatePipe,
+    GiftModalComponent,
+    CommonModule,
+  ],
   providers: [DatePipe],
 })
 export class ChatComponent implements OnInit {
@@ -34,10 +39,11 @@ export class ChatComponent implements OnInit {
   height: number = 0;
   showEmoji: boolean = false;
   emojis: Array<any> = [];
-  isOpen: boolean = false;
   timer: any;
   currentScroll: number = 0; //滚动条位置
+  disabled: boolean = false;
   constructor(
+    private router: Router,
     private http: HttpClient,
     public datePipe: DatePipe,
     public toastController: ToastController,
@@ -78,8 +84,11 @@ export class ChatComponent implements OnInit {
     });
     loading.present();
     await this.getProfile();
-    await this.msgServe.initRTM(this.uid)
-    await this.msgServe.subscribeMessage(this.uid,{message:true,presence:true}) //订阅消息
+    await this.msgServe.initRTM(this.uid);
+    await this.msgServe.subscribeMessage(this.uid, {
+      message: true,
+      presence: true,
+    }); //订阅消息
     this.initEmoji();
     loading.dismiss();
   }
@@ -104,7 +113,7 @@ export class ChatComponent implements OnInit {
   }
   ngOnDestroy(): void {
     this.msgServe.pageFun = () => {};
-    this.msgServe.unsubscribeMessage(this.uid)
+    this.msgServe.unsubscribeMessage(this.uid);
   }
   initEmoji() {
     let emojiChar =
@@ -218,9 +227,7 @@ export class ChatComponent implements OnInit {
         duration: 1500,
       });
       loading.present();
-      this.timer = setTimeout(() => {
-
-      }, 500);
+      this.timer = setTimeout(() => {}, 500);
     }
   }
   //调起表情
@@ -242,21 +249,21 @@ export class ChatComponent implements OnInit {
     if (e && e.keyCode != 13) {
       return;
     }
+    this.disabled = true;
     if (this.text == '' && this.text.trim() == '') {
       this.presentToast('聊天内容不能为空');
+      this.disabled = false;
       return;
     }
     this.send({
       msg_type: 'text',
       content: this.text,
     });
-    this.text = '';
   }
   async send(param: { msg_type: string; content: string }) {
-    this.msgServe.publishMessage(param.content, this.uid);
-  }
-  dismiss(evt: any) {
-    this.isOpen = false;
+    await this.msgServe.publishMessage(param.content, this.uid);
+    this.text = '';
+    this.disabled = false;
   }
   predivimg<T>(value: string) {
     this.showPreView = true;
@@ -265,6 +272,10 @@ export class ChatComponent implements OnInit {
   onBack() {
     history.back();
   }
+  onSendGift() {
+    console.log('点击送出礼物');
+    // this.liveService.get_duration()
+  }
   async presentToast(title: string, time?: number, color?: string) {
     const toast = await this.toastController.create({
       message: title,
@@ -273,4 +284,7 @@ export class ChatComponent implements OnInit {
     });
     toast.present();
   }
+  toUrl(url: string) {
+    this.router.navigateByUrl(url);
+  }
 }

+ 119 - 55
projects/live-app/src/services/message.service.ts

@@ -35,56 +35,51 @@ export class MessageService {
   messageMapList: any = {
     global_room: [
       // 世界频道消息列表
-      {
-        is_self: true,
-        avatar:
-          'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
-        msg_type: 1,
-        content: 'nihao',
-        create_time: new Date(),
-        istoday: true,
-        timeStamp: new Date(),
-      },
-      {
-        is_self: true,
-        avatar:
-          'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
-        msg_type: 1,
-        content: `Use the pipe name to trace where the pipe is declared and used. To resolve this error: If the pipe is local to the NgModule, give it a unique name in the pipe's decorator and declared it in the NgModule. If the pipe is standalone or is declared in another NgModule, add it to the imports field of the standalone component or the current NgModule.`,
-        create_time: new Date(),
-        istoday: true,
-        timeStamp: new Date(),
-      },
-      {
-        is_self: true,
-        avatar:
-          'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
-        msg_type: 1,
-        content: `Use the pipe name to trace where the pipe is declared and used. To resolve this error: If the pipe is local to the NgModule, give it a unique name in the pipe's decorator and declared it in the NgModule. If the pipe is standalone or is declared in another NgModule, add it to the imports field of the standalone component or the current NgModule.`,
-        create_time: new Date(),
-        istoday: true,
-        timeStamp: new Date(),
-      },
-      {
-        is_self: true,
-        avatar:
-          'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
-        msg_type: 1,
-        content: `Use the pipe name to trace where the pipe is declared and used. To resolve this error: If the pipe is local to the NgModule, give it a unique name in the pipe's decorator and declared it in the NgModule. If the pipe is standalone or is declared in another NgModule, add it to the imports field of the standalone component or the current NgModule.`,
-        create_time: new Date(),
-        istoday: true,
-        timeStamp: new Date(),
-      },
-      {
-        is_self: false,
-        avatar:
-          'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
-        msg_type: 1,
-        content: 'nihao',
-        create_time: new Date(),
-        istoday: true,
-        timeStamp: new Date(),
-      },
+      // {
+      //   is_self: true,
+      //   avatar:
+      //     'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
+      //   msg_type: 1,
+      //   content: 'nihao',
+      //   istoday: true,
+      //   timestamp: new Date(),
+      // },
+      // {
+      //   is_self: true,
+      //   avatar:
+      //     'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
+      //   msg_type: 1,
+      //   content: `Use the pipe name to trace where the pipe is declared and used. To resolve this error: If the pipe is local to the NgModule, give it a unique name in the pipe's decorator and declared it in the NgModule. If the pipe is standalone or is declared in another NgModule, add it to the imports field of the standalone component or the current NgModule.`,
+      //   istoday: true,
+      //   timestamp: new Date(),
+      // },
+      // {
+      //   is_self: true,
+      //   avatar:
+      //     'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
+      //   msg_type: 1,
+      //   content: `Use the pipe name to trace where the pipe is declared and used. To resolve this error: If the pipe is local to the NgModule, give it a unique name in the pipe's decorator and declared it in the NgModule. If the pipe is standalone or is declared in another NgModule, add it to the imports field of the standalone component or the current NgModule.`,
+      //   istoday: true,
+      //   timestamp: new Date(),
+      // },
+      // {
+      //   is_self: true,
+      //   avatar:
+      //     'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
+      //   msg_type: 1,
+      //   content: `Use the pipe name to trace where the pipe is declared and used. To resolve this error: If the pipe is local to the NgModule, give it a unique name in the pipe's decorator and declared it in the NgModule. If the pipe is standalone or is declared in another NgModule, add it to the imports field of the standalone component or the current NgModule.`,
+      //   istoday: true,
+      //   timestamp: new Date(),
+      // },
+      // {
+      //   is_self: false,
+      //   avatar:
+      //     'https://file-cloud.fmode.cn/Qje9D4bqol/20241109/2t1lp0032258601.png',
+      //   msg_type: 1,
+      //   content: 'nihao',
+      //   istoday: true,
+      //   timestamp: new Date(),
+      // },
     ],
   };
 
@@ -329,6 +324,10 @@ export class MessageService {
           console.log('subscribeMessage', res);
           //订阅成功
           this.channelNameList[channelName] = true;
+          if (this.messageMapList[channelName]?.length === 0) {
+            this.messageMapList[channelName] = [];
+            this.getHistoryMessage(channelName)
+          }
           this.pageFun?.();
           resolve(true);
         })
@@ -338,6 +337,25 @@ export class MessageService {
         });
     });
   }
+
+  async getHistoryMessage(channelName: string) {
+    let query = new Parse.Query('MessageLog');
+    query.equalTo('channel', channelName);
+    query.descending('createdAt');
+    query.skip(this.messageMapList[channelName].length);
+    query.limit(50);
+    query.select('content','from_id');
+    let msgList = await query.find();
+    msgList.forEach((item: any) => {
+      let is_self = item?.get('from_id') == this.userId;
+      let data: any = item?.get('content');
+      data['is_self'] = is_self;
+      data['istoday'] = true;
+      data['timestamp'] = new Date(data.timestamp)
+      this.messageMapList[channelName].push(data)
+    });
+    // this.messageMapList[channelName].unshift(...msgList);
+  }
   /* 取消订阅 */
   unsubscribeMessage(channelName: string) {
     this.rtmClient
@@ -355,11 +373,30 @@ export class MessageService {
   async showMessage(param: any) {
     let userData = await this.getUserMetadata(param.publisher);
     let is_self = param.publisher == this.userId;
-    console.log(userData);
+    // console.log(userData);
     let message = JSON.parse(param.message);
     if (!this.messageMapList[param.channelName])
       this.messageMapList[param.channelName] = [];
-    this.messageMapList[param.channelName].push({
+    if (is_self) {
+      this.saveMeesage({
+        from_id: this.userId,
+        from_username: Parse.User.current()?.get('nickname') ?? '未知用户',
+        target_id: param.channelName,
+        // target_name: userData.nickname.value ?? '未知用户',
+        channel: param.channelName,
+        content: {
+          avatar:
+            userData.avatar.value ??
+            'https://file-cloud.fmode.cn/DXNgcD6zo6/20221202/j6p8kb034039.png',
+          msg_type: 1,
+          name: userData.nickname.value ?? '未知用户',
+          content: message?.text ?? '',
+          publisher: param.publisher,
+          timestamp: param.timestamp,
+        },
+      });
+    }
+    let data: any = {
       is_self: is_self,
       avatar:
         userData.avatar.value ??
@@ -367,12 +404,39 @@ export class MessageService {
       msg_type: 1,
       name: userData.nickname.value ?? '未知用户',
       content: message?.text ?? '',
-      create_time: new Date(param.timeStamp),
+      publisher: param.publisher,
+      timestamp: new Date(param.timestamp),
       istoday: true,
-      timeStamp: new Date(param.timeStamp),
-    });
+    };
+    this.messageMapList[param.channelName].push(data);
     this.pageFun?.();
   }
+
+  async saveMeesage(parse: {
+    from_id: string;
+    from_username: string;
+    target_id: string;
+    // target_name: string;
+    channel: string;
+    content: Object;
+  }) {
+    console.log(parse);
+    let obj = Parse.Object.extend('MessageLog');
+    let message = new obj();
+    message.set('from_id', parse.from_id);
+    message.set('from_username', parse.from_username);
+    message.set('target_id', parse.target_id);
+    // message.set('target_name', parse.target_name);
+    message.set('channel', parse.channel);
+    message.set('content', parse.content);
+    message.set('company', {
+      __type: 'Pointer',
+      className: 'Company',
+      objectId: this.company,
+    });
+    await message.save();
+  }
+
   async getUserMetadata(uid: string) {
     try {
       const result = await this.rtmClient?.storage.getUserMetadata({