Newer
Older
XinYang_SanWei+RongYun / src / views / call / index.vue
@张强云笔记本 张强云笔记本 on 17 Dec 2021 18 KB 融云通话接入
<!-- 呼叫页面 -->
<template>
  <div id="rong-template-call">
    <div class="rong-box rong-call-box">
      <div class="rong-info">
        <p>
          当前拨号用户:
          <span>{{ params.userId }} - {{ $route.params.userId }}</span>
        </p>
        <!-- <p>群组 id: <span>{{params.groupId}}- {{$route.params.groupId}}</span></p> -->
        <!-- <el-button @click="test()">群聊测试</el-button> -->
      </div>
      <div class="rong-type-box" style="display: none">
        <!-- <div class="rong-type-box" v-if="callStep === CallStep.READY_TO_CALL"> -->
        <label for="">通话类型: </label>
        <select name="" id="" v-model="callType">
          <option value="1">单人</option>
          <option value="3">群组</option>
        </select>
      </div>
      <div class="rong-video-box">
        <div class="rong-video-list">
          <div
            class="rong-video-min"
            v-for="user in minUserList"
            v-video="user"
            :talktype="user.talkType"
          ></div>
        </div>
        <div
          v-if="maxUser"
          class="rong-video-max"
          v-video="maxUser"
          :talktype="maxUser.talkType"
        ></div>
      </div>
      <div class="rong-call-btns">
        <template v-if="callStep === CallStep.READY_TO_CALL">
          <button
            class="rong-call-video"
            title="点击进行视频通话"
            @click="inputCall(false)"
          ></button>
          <button
            class="rong-call-audio"
            title="点击进行音频通话"
            @click="inputCall(true)"
          ></button>
        </template>
        <template v-else-if="callStep === CallStep.CALLING">
          <button
            class="rong-call-hungup"
            title="点击挂断"
            @click="hungup"
          ></button>
          <button
            v-if="callType == 3"
            class="rong-call-invite"
            title="点击邀请"
            @click="invite"
          ></button>
          <button
            class="rong-call-mute"
            title="点击开启/关闭音频"
            :closed="isMuted"
            @click="mute"
          ></button>
          <button
            class="rong-call-video"
            closed
            title="点击开启/关闭视频"
            :closed="callInfo.mediaType !== 2"
            @click="setVideo"
          ></button>
        </template>
        <template v-else-if="callStep === CallStep.INVITED_TO_ANSWER">
          <button class="rong-call-accept" @click="accept"></button>
          <button class="rong-call-hungup" @click="reject"></button>
        </template>
      </div>
    </div>

    <!-- 群聊 -->
    <el-dialog title="群聊用户" :visible.sync="dialogVisible" width="30%">
      <el-checkbox
        :indeterminate="isIndeterminate"
        v-model="checkAll"
        @change="handleCheckAllChange"
        >全选当前用户
      </el-checkbox>
      <div style="margin: 15px 0"></div>

      <el-checkbox-group
        v-model="checkedCities"
        @change="handleCheckedCitiesChange"
      >
        <el-checkbox v-for="userId in cities" :label="userId" :key="userId">{{
          userId
        }}</el-checkbox>
      </el-checkbox-group>

      <span slot="footer" class="dialog-footer">
        <el-button @click="dialogVisible = false">取 消</el-button>
        <el-button type="primary" @click="sureMemer()">确 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import { getPeoplesMembers } from "@/services";
var RongIMLib = window.RongIMLib,
  RongCallLib = window.RongCallLib,
  win = window,
  RongCall = window.RongCall,
  setting = RongCall.setting,
  utils = RongCall.utils;

var dialog = RongCall.dialog,
  ConversationType = RongIMLib.CONVERSATION_TYPE,
  MediaType = {
    MEDIA_AUDIO: 1,
    MEDIA_VEDIO: 2,
    1: "MEDIA_AUDIO",
    2: "MEDIA_VEDIO",
  };
var isAutoClose = false;
// 通话阶段
var CallStep = {
  READY_TO_CALL: 1, // 准备拨打(最初状态)
  INVITED_TO_ANSWER: 2, // 被邀请接听, 其他人拨打, 己方选择接听或拒绝
  CALLING: 3, // 拨打中
};

var CallName = {};
CallName[MediaType.MEDIA_AUDIO] = "语音";
CallName[MediaType.MEDIA_VEDIO] = "视频";

// 将消息转化为调用 CallLib 需要参数
function messageToCallInfo(message) {
  return {
    conversationType: message.conversationType,
    targetId: message.targetId,
    mediaType: message.content.mediaType,
  };
}

// 获取挂断原因
function getHungupReason(reason, message) {
  var reasonPrompt;
  var senderUserId = message.senderUserId;
  switch (reason) {
    case 8:
      reasonPrompt = "其他设备已处理";
      break;
    case 11:
      reasonPrompt = `${senderUserId} 已取消`;
      break;
    case 12:
      reasonPrompt = `${senderUserId} 已拒绝`;
      break;
    case 13:
      reasonPrompt = `${senderUserId} 已挂断`;
      break;
    case 14:
      reasonPrompt = `${senderUserId} 忙碌中`;
      break;
    case 15:
      reasonPrompt = `${senderUserId} 未接听`;
      break;
    default:
      reasonPrompt = "未知原因挂断";
  }
  return reasonPrompt;
}

// 己方挂断后的提示
function getSummaryText(status, message) {
  var senderUserId = message.senderUserId;
  var text;
  switch (status) {
    case 1:
      text = "己方已取消";
      break;
    case 2:
      text = "己方已拒绝";
      break;
    case 3:
      text = "己方挂断";
      break;
    case 4:
      text = `收到 ${senderUserId} 的音视频邀请, 但己方忙碌中, 不处理`;
      break;
    case 5:
      text = "己方未接听";
      break;
    default:
      text = "未知原因";
  }
  return text;
}

function mediaTypeToTalkType(mediaType) {
  return mediaType === MediaType.MEDIA_AUDIO ? 0 : 1;
}

var commandEvents = {
  // 监听其他人邀请自己
  InviteMessage: function (message, context) {
    var conversationType =
      message.conversationType === ConversationType.PRIVATE ? "单聊" : "群聊";
    context.$message(
      `${message.senderUserId} 邀请您进行${
        CallName[message.content.mediaType]
      }通话(${conversationType})`
    );
    context.callInfo = messageToCallInfo(message);
    context.callStep = CallStep.INVITED_TO_ANSWER;
  },
  MemberModifyMessage: function (message, context) {
    if (message.content.inviteUserIds.indexOf(context.selfUserId) !== -1) {
      var conversationType =
        message.conversationType === ConversationType.PRIVATE ? "单聊" : "群聊";
      context.$message(
        `${message.senderUserId} 邀请您进行${
          CallName[message.content.mediaType]
        }通话(${conversationType})`
      );
      context.callInfo = messageToCallInfo(message);
      context.callStep = CallStep.INVITED_TO_ANSWER;
    }
  },
  HungupMessage: function (message, context) {
    var reason = getHungupReason(message.content.reason, message);
    context.$message(reason);
    var params = {
      conversationType: message.conversationType,
      targetId: message.targetId,
    };
    isAutoClose = true;
    if (message.content.reason) {
      RongCallLib.hungup(params, function (error) {
        // 置为最初的准备拨打状态
        context.callStep = CallStep.READY_TO_CALL;
      });
    }
  },
  MediaModifyMessage: function (message, context) {
    var senderUserId = message.senderUserId,
      mediaType = message.content.mediaType;
    context.userList.forEach(function (user) {
      if (user.userId === senderUserId) {
        user.talkType = mediaTypeToTalkType(mediaType);
      }
    });
  },
  SummaryMessage: function (message, context) {
    // 每次拨通或者挂断都会触发此方法
    var status = message.content.status;

    if (!isAutoClose) {
      var promptText = getSummaryText(status, message);
      context.$message(promptText);
      if (status === 5) {
        // 自己未接听, 回到初始状态
        context.callStep = CallStep.READY_TO_CALL;
      }
    }

    if (status === 15) {
      context.callStep = CallStep.READY_TO_CALL;
    }
  },
};

var videoChangedEvents = {
  added: function (detail, context) {
    context.userList.push(detail);
  },
  removed: function (detail, context) {
    context.userList = utils.removeArray(detail, context.userList, "userId");
  },
  leave: function (detail, context) {
    context.userList = [];
  },
};

async function getMembers(currentUserList, selfUserId) {
  let members = await getPeoplesMembers();
  console.log("members", members);
  members = members.filter(function (user) {
    var currentUserIds = currentUserList.map(function (user) {
      return user.id || user.userId;
    });
    return user.id !== selfUserId && currentUserIds.indexOf(user.id) === -1;
  });
  console.log("members", members);
  return win.Promise.resolve(members);
}

function call(callParams) {
  var context = this;
  RongCallLib.call(callParams, function (error) {
    // 置为通话中状态
    context.callStep = error ? context.callStep : CallStep.CALLING;
  });
  context.callInfo = callParams;
}

function accept() {
  var context = this;
  var callInfo = context.callInfo; // callInfo 在监听到 InviteMessage 时赋值, 格式见 messageToCallInfo 方法
  RongCallLib.accept(callInfo, function (error) {
    // 置为通话中状态
    context.callStep = error ? context.callStep : CallStep.CALLING;
  });
}

function invite() {
  var context = this;
  var callInfo = context.callInfo,
    inviteParams = {
      conversationType: ConversationType.GROUP,
      targetId: callInfo.targetId,
      inviteUserIds: [],
      mediaType: callInfo.mediaType,
    };
  getMembers(context.userList, context.selfUserId)
    .then(function (members) {
      dialog.selectUser({
        userList: members,
        confirmed: function (selectedUserList) {
          var selectedIds = selectedUserList.map(function (user) {
            return user.id;
          });
          inviteParams.inviteUserIds = selectedIds;
          RongCallLib.invite(inviteParams);
        },
      });
    })
    .catch(function () {
      context.$message("获取群组成员失败");
    });
}

function reject() {
  var context = this;
  var callInfo = context.callInfo; // callInfo 在监听到 InviteMessage 时赋值, 格式见 messageToCallInfo 方法
  RongCallLib.reject(callInfo, function (error) {
    // 置为最初的准备拨打状态
    context.callStep = error ? context.callStep : CallStep.READY_TO_CALL;
  });
}

function hungup() {
  var context = this;
  var callInfo = context.callInfo;
  RongCallLib.hungup(callInfo, function (error) {
    // 置为最初的准备拨打状态
    context.callStep = error ? context.callStep : CallStep.READY_TO_CALL;
  });
}

function mute() {
  var isMuted = this.isMuted;
  var event = isMuted ? RongCallLib.unmute : RongCallLib.mute;
  event();
  this.isMuted = !isMuted;
}

function setVideo() {
  var callInfo = this.callInfo,
    mediaType = callInfo.mediaType;
  var event =
    mediaType === MediaType.MEDIA_AUDIO
      ? RongCallLib.audioToVideo
      : RongCallLib.videoToAudio;
  event();
  this.callInfo.mediaType =
    mediaType === MediaType.MEDIA_AUDIO
      ? MediaType.MEDIA_VEDIO
      : MediaType.MEDIA_AUDIO;
}

export default {
  name: "rong-template-call",
  data() {
    return {
      userList: [],
      selectUserId: "",
      selectUserShow: true,
      userId: this.$route.params.userId ? this.$route.params.userId : "1",
      params: "",
      callStep: CallStep.READY_TO_CALL,
      callType: ConversationType.PRIVATE, // 通话类型, 默认为单聊
      /**
       * 通话信息. 给 callInfo 赋值的地方有:
       * 1. 发送 call 成功后, 存储当前通话信息
       * 2. 接收到 InviteMessage 后, 存储通话信息
       */
      callInfo: {},
      targetId: "",
      isMuted: false,
      dialogVisible: false,
      callBool: false,
      checkAll: false,
      checkedCities: [],
      cities: [],
      isIndeterminate: true,
    };
  },
  directives: {
    video: function (el, binding) {
      var user = binding.value;
      var video = user.data;
      el.appendChild(video);
      video.play();
    },
  },
  computed: {
    CallStep: function () {
      return CallStep;
    },
    // 大窗口用户
    maxUser: function () {
      var context = this;
      var maxUser;
      context.userList.forEach(function (user) {
        if (user.userId === context.selfUserId) {
          maxUser = user;
        }
      });
      if (maxUser) {
        maxUser.talkType = mediaTypeToTalkType(context.callInfo.mediaType);
      }
      return maxUser;
    },
    // 小窗口用户列表
    minUserList: function () {
      var context = this,
        maxUser = context.maxUser || {};
      return context.userList.filter(function (user) {
        return user.userId !== maxUser.userId;
      });
    },
    selfUserId: function () {
      return this.params.userId;
    },
  },
  methods: {
    async test() {
      let { members } = await getPeoplesMembers();
      let newMemers = [];
      members = members.map((items) => {
        if (items.id !== this.targetId) {
          newMemers.push(items.id || items.userId);
        }
      });
      this.cities = newMemers;
      this.dialogVisible = true;
    },
    handleCheckAllChange(val) {
      this.checkedCities = val ? this.cities : [];
      this.isIndeterminate = false;
    },
    handleCheckedCitiesChange(value) {
      let checkedCount = value.length;
      this.checkAll = checkedCount === this.cities.length;
      this.isIndeterminate =
        checkedCount > 0 && checkedCount < this.cities.length;
    },
    sureMemer() {
      // 群聊
      var context = this;
      var params = context.params,
        groupId = params.groupId;

      var callParams = {
        conversationType: ConversationType.GROUP,
        targetId: groupId,
        inviteUserIds: [],
        mediaType: mediaType,
      };
      getMembers(context.userList, context.selfUserId)
        .then(function (members) {
          dialog.selectUser({
            userList: members,
            confirmed: function (selectedUserList) {
              var selectedIds = selectedUserList.map(function (user) {
                return user.id;
              });
              callParams.inviteUserIds = selectedIds;
              context.call(callParams);
            },
          });
        })
        .catch(function () {
          context.$message("获取群组成员失败");
        });
    },
    inputCall(bool) {
      // 输入用户id
      var targetId = this.$route.query.phone || "";

      if (targetId !== "") {
        // url中有参数phone,则进行直接呼叫用户
        this.callBool = bool;
        this.startCall();
        return false;
      }

      this.$prompt("请输入用户id", "温馨提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        inputErrorMessage: "用户id",
      }).then(({ value }) => {
        // 用户id
        this.targetId = value;
        this.callBool = bool;
        this.startCall();
      });
    },
    /**
     * @param {boolean} isOnlyAudio 是否仅以音频发起
     */
    startCall: function (isOnlyAudio) {
      var mediaType = isOnlyAudio
        ? MediaType.MEDIA_AUDIO
        : MediaType.MEDIA_VEDIO;
      if (this.callType == ConversationType.GROUP) {
        this.startGroupCall(mediaType);
      } else {
        // 视频
        this.startPrivateCall(mediaType);
      }
    },
    startPrivateCall: function (mediaType) {
      var targetId = this.$route.query.phone || "";
      if (targetId === "") {
        targetId = this.targetId;
      }

      targetId &&
        this.call({
          conversationType: ConversationType.PRIVATE,
          targetId: targetId,
          inviteUserIds: [targetId],
          mediaType: mediaType,
        });
    },
    startGroupCall: function (mediaType) {
      var context = this;
      var params = context.$route.params,
        groupId = params.groupId;

      var callParams = {
        conversationType: ConversationType.GROUP,
        targetId: groupId,
        inviteUserIds: [],
        mediaType: mediaType,
      };
      getMembers(context.userList, context.selfUserId)
        .then(function (members) {
          dialog.selectUser({
            userList: members,
            confirmed: function (selectedUserList) {
              var selectedIds = selectedUserList.map(function (user) {
                return user.id;
              });
              callParams.inviteUserIds = selectedIds;
              context.call(callParams);
            },
          });
        })
        .catch(function () {
          context.$message("获取群组成员失败");
        });
    },
    async login(callBack) {
      var userId = this.userId;
      let _this = this;
      var loginDetail;

      let result = await this.$axios.login({
        userId,
      });

      if (result.code !== 200) {
        _this.$message("登录失败, 请检查 CallLib Demo Server 是否启动");
      } else {
        setting.token = result.token;
        this.targetId = result.userId;
        loginDetail = result;
        let RongCall = window.RongCall;
        RongCall.initIM(setting);

        _this.params = loginDetail;
        callBack ? callBack(this) : null;
      }
    },
    initLib() {
      var context = this;

      // 初始化
      RongCallLib = RongCall.initCallLib(context.selfUserId);
      // 注册命令(消息)监听
      RongCallLib.commandWatch(function (message) {
        var event = commandEvents[message.messageType];
        event && event(message, context);
        console.log("received message", message);
      });

      // 注册音视频节点监听
      RongCallLib.videoWatch(function (result) {
        var event = videoChangedEvents[result.type];
        event && event(result, context);
        console.log("video changed", result);
      });

      let phone = this.$route.query.phone || "";

      if (phone !== "") {
        // 默认视频通话
        context.$message("正在拨通视频,请稍后...");
        setTimeout(() => {
          this.startCall(false);
        }, 2000);
      }
    },
    call: call,
    accept: accept,
    invite: invite,
    reject: reject,
    hungup: hungup,
    mute: mute,
    setVideo: setVideo,
  },
  mounted() {
    let params = this.$route.params;
    // 原始代码登录 获取到的数据
    console.log("原始登录:", params);
    params = Object.keys(params);
    if (params.length === 0) {
      // 有phone,直接进行拨打
      // this.login(this.initLib);
    } else {
      // 通过login页面进入的场景
      this.initLib();
    }
  },
};
</script>

<style scoped>
</style>