Merge branch 'develop'

This commit is contained in:
zoujiandong 2024-03-26 10:39:52 +08:00
commit ad6217b294
4321 changed files with 139488 additions and 27044 deletions

View File

@ -0,0 +1 @@
{"containers":[],"config":{}}

View File

@ -1,5 +1,5 @@
// Pages/index/index.js
import { API } from './../../utils/network/api'
import { API } from '../../utils/network/api'
const api = new API()
const app = getApp()
Page({
@ -98,22 +98,9 @@ Page({
let usertype = wx.getStorageSync('usertype');
this.guideTo(usertype)
}
// let isEntry=wx.getStorageSync('hasEntry');
// console.log("onshow:"+isEntry)
// if(!isEntry){
// this.setData({
// showEntryTip:true
// });
// }else{
// this.setData({
// showEntryTip:false
// });
// }
},
goCheck(e){
console.log(e);
let usertype = e.currentTarget.dataset.usertype;
let url = e.currentTarget.dataset.url;
wx.setStorageSync('usertype', usertype);
@ -139,6 +126,7 @@ Page({
url: url
})
}else{
console.log(app.globalData.login_url)
app.go(app.globalData.login_url);
}
//test git

View File

@ -3,6 +3,7 @@
"usingComponents": {
"t-image": "tdesign-miniprogram/image/image",
"van-overlay": "@vant/weapp/overlay/index",
"van-icon": "@vant/weapp/icon/index",
"van-dialog": "@vant/weapp/dialog/index"
}
}

View File

@ -1,20 +1,3 @@
<!--
此文件为开发者工具生成,生成时间: 2023/2/2 下午6:54:52
使用方法:
在 E:\WeChatProjects\test_miniprogram\Pages\index\index.wxml 引入模板
```
<import src="index.skeleton.wxml"/>
<template is="skeleton" wx:if="{{loading}}" />
```
在 E:\WeChatProjects\test_miniprogram\Pages\index\index.wxss 中引入样式
```
@import "./index.skeleton.wxss";
```
更多详细信息可以参考文档https://developers.weixin.qq.com/miniprogram/dev/devtools/skeleton.html
-->
<template name="skeleton">
<view class="sk-container">
<view class="title sk-transparent sk-text-14-2857-913 sk-text">请选择您的身份</view>

View File

@ -1,13 +1,4 @@
/*
此文件为开发者工具生成,生成时间: 2023/2/2 下午6:54:52
在 E:\WeChatProjects\test_miniprogram\Pages\index\index.wxss 中引入样式
```
@import "./index.skeleton.wxss";
```
更多详细信息可以参考文档https://developers.weixin.qq.com/miniprogram/dev/devtools/skeleton.html
*/
.sk-transparent {
color: transparent !important;
}

View File

@ -33,7 +33,7 @@
<view class="privacyBox">
<view class="title">温馨提示</view>
<view class="entrymsg" >
<view> 亲爱的用户,感谢您信任并使用肝胆相照互联网医院医师端!我们依据最新法律法规的要求,制定了<text class="navigator" bindtap="go" data-url="/Pages/agreement_page/index?agreement_id=9">《隐私协议》</text>。请您仔细阅《隐私协议》,并确认了解我们对您的个人信息处理原则。</view>
<view> 亲爱的用户,感谢您信任并使用肝胆相照互联网医院医师端!我们依据最新法律法规的要求,制定了<text class="navigator" bindtap="go" data-url="/user/pages/agreement_page/index?agreement_id=9">《隐私协议》</text>。请您仔细阅《隐私协议》,并确认了解我们对您的个人信息处理原则。</view>
<view>如您同意《隐私协议》,请点击“同意”开始使用我们的产品和服务。</view>
</view>
<view class="btnbox">

View File

@ -5,9 +5,9 @@
<text class="name">{{info.user_name}}</text>
</view>
<view class="content">
<view class="my" bindtap="go" data-url="/Pages/yaoshi/info/info">个人中心</view>
<view class="my" bindtap="go" data-url="/user/pages/yaoshi/info/info">个人中心</view>
<view class="item_list">
<view class="item" bindtap="go" data-url="/Pages/yaoshi/authentication/authentication">
<view class="item" bindtap="go" data-url="/user/pages/yaoshi/authentication/authentication">
<van-icon name="user-o" size="50rpx" color="#55B0E5"/>
<text class="item_name">实名认证</text>
</view>

View File

@ -1,50 +0,0 @@
page{
background-color: #F6F6F6;
}
.container{
width: 100vw;
}
.item_list{
width: 92vw;
margin: 20rpx auto;
background-color: #fff;
border-radius: 20rpx;
}
.item{
display: flex;
padding: 20rpx;
flex-direction: column;
}
.item:not(:last-child){
border-bottom: 1px solid #E7E7E7;
}
.top{
display: flex;
justify-content: space-between;
align-items: center;
flex: 1;
}
.name{
flex: 1;
font-size: 30rpx;
color: #666666;
}
.start{
flex: 1;
display: flex;
justify-content: flex-end;
}
.content{
flex: 2;
font-size: 34rpx;
color: #333333;
margin: 20rpx 0;
}
.date{
flex: 1;
font-size: 28rpx;
color: #666666;
}

View File

@ -22,14 +22,15 @@ Page({
avg_response_time: 0,
number_of_fans: 0,
avatar: api.getStaticHost()+"/applet/doctor/static/images/default_photo.png",
introduction_status:'',
reject_prescription_number: false,//处方管理小红点
},
static_host: api.getStaticHost(),
iden_auth_status_txt: "未认证",
multi_point_status_txt: "未认证",
shiming_status_url: "",
iden_auth_status_url: "/Pages/yishi/identity/index",
multi_point_status_url: "/Pages/yishi/zhiye_identity/index",
iden_auth_status_url: "/user/pages/yishi/identity/index",
multi_point_status_url: "/user/pages/yishi/zhiye_identity/index",
duration: 500,
interval: 5000,
swiperList: [],
@ -63,31 +64,31 @@ Page({
case_status: 0,
case_module: [0,1,2,3,4,5,6,7,8],
case_text: "请您先完成实名认证",
case_url: "/Pages/yishi/identity/index"
case_url: "/user/pages/yishi/identity/index"
},
{
case_status: 1,
case_module: [0,1,2,3,4,5,6,7,8],
case_text: "请您先医师身份认证",
case_url: "/Pages/yishi/identity/index?selected_tab=1"
case_url: "/user/pages/yishi/identity/index?selected_tab=1"
},
{
case_status: 2,
case_module: [0,1,2,3,4,5,6,7,8],
case_text: "请您先绑定结算银行卡",
case_url: "/Pages/yishi/bankcard/index"
case_url: "/user/pages/yishi/bankcard/index"
},
{
case_status: 3,
case_module: [7],
case_text: "请您先进行多点执业认证",
case_url: "/Pages/yishi/zhiye_identity/index"
case_url: "/user/pages/yishi/zhiye_identity/index"
},
{
case_status: 4,
case_module: [7],
case_text: "请您先进行多点执业认证",
case_url: "/Pages/yishi/zhiye_identity/index"
case_url: "/user/pages/yishi/zhiye_identity/index"
},
{
case_status: 5,
@ -159,7 +160,7 @@ Page({
wx.setStorageSync('overlay_show', "1")
}
app.go("/Pages/yishi/manual_detail/index?manual_id=1")
app.go("/user/pages/yishi/manual_detail/index?manual_id=1")
},
cancelJieSuanDialog(){
this.setData({
@ -241,7 +242,7 @@ Page({
this.getMenuButtonBound();
api.yiShiIndex().then(response => {
// console.log(response);
console.log(response);
let avatar = response.data.info.avatar;
this.setData({
"info.user_name": response.data.info.user_name,
@ -249,6 +250,7 @@ Page({
"info.accepting_inquiry_num": response.data.info.accepting_inquiry_num,
"info.praise_rate": response.data.info.praise_rate,
"info.avg_response_time": response.data.info.avg_response_time,
"info.introduction_status":response.data.info.introduction_status,
"info.number_of_fans": response.data.info.number_of_fans,
doctor_inquiry_config:response.data.doctor_inquiry_config
})
@ -285,13 +287,13 @@ Page({
//iden_auth_status 0:未认证 1:认证通过 2:审核中 3:认证失败
if(iden_auth_status == 0) this.setData({iden_auth_status_txt: "未认证"});
if(iden_auth_status == 1) this.setData({iden_auth_status_txt: "已认证", iden_auth_status_url: "/Pages/yishi/attestation/index"});
if(iden_auth_status == 1) this.setData({iden_auth_status_txt: "已认证", iden_auth_status_url: "/user/pages/yishi/attestation/index"});
if(iden_auth_status == 2) this.setData({iden_auth_status_txt: "审核中"});
if(iden_auth_status == 3) this.setData({iden_auth_status_txt: "认证失败"});
//multi_point_status 0:未认证 1:认证通过 2:审核中 3:认证失败
if(multi_point_status == 0) this.setData({multi_point_status_txt: "未认证"});
if(multi_point_status == 1) this.setData({multi_point_status_txt: "已认证", multi_point_status_url: "/Pages/yishi/practicing/index"});
if(multi_point_status == 1) this.setData({multi_point_status_txt: "已认证", multi_point_status_url: "/user/pages/yishi/practicing/index"});
if(multi_point_status == 2) this.setData({multi_point_status_txt: "审核中"});
if(multi_point_status == 3) this.setData({multi_point_status_txt: "认证失败"});
@ -338,7 +340,7 @@ Page({
if(open_with == 1){//小程序页面
app.go(banner_link);
}else if(open_with == 2){//webview 页面
app.go("/Pages/webpage/index?src="+banner_link);
app.go("/user/pages/webpage/index?src="+banner_link);
}
},
onChangeAD(e) {
@ -366,7 +368,7 @@ Page({
if(idcard_status != 1){
this.setData({
dialog_content: "请您先完成实名认证",
shiming_status_url: "/Pages/yishi/identity/index",
shiming_status_url: "/user/pages/yishi/identity/index",
dialog_visible: true,
});
return;
@ -374,7 +376,7 @@ Page({
if(iden_auth_status != 1){
this.setData({
dialog_content: "请您先医师身份认证",
shiming_status_url: "/Pages/yishi/identity/index?selected_tab=1",
shiming_status_url: "/user/pages/yishi/identity/index?selected_tab=1",
dialog_visible: true,
});
return;
@ -382,7 +384,7 @@ Page({
if(is_bind_bank != 1){
this.setData({
dialog_content: "请您先绑定结算银行卡",
shiming_status_url: "/Pages/yishi/bankcard/index",
shiming_status_url: "/user/pages/yishi/bankcard/index",
dialog_visible: true,
});
return;
@ -412,13 +414,7 @@ Page({
let multi_point_status = this.data.info.multi_point_status;
//绑定结算银行卡
var is_bind_bank = this.data.info.is_bind_bank;
//是否参加专家图文接诊0:否 1:是)
// let is_img_expert_reception = this.data.info.is_img_expert_reception;
// //是否参加专家快速接诊0:否 1:是)
// let is_img_quick_reception = this.data.info.is_img_quick_reception;
// //是否参加专家公益接诊0:否 1:是)
// let is_img_welfare_reception = this.data.info.is_img_welfare_reception;
let usertype = wx.getStorageSync('usertype');
let userID = wx.getStorageSync('user_id_'+usertype);
@ -437,8 +433,8 @@ Page({
"info.info_shiming_status_txt": "未认证",
"info.info_shiming_status": "info_shiming_status_no",
dialog_content: "请您先完成实名认证",
shiming_status_url: "/Pages/yishi/identity/index",
dialog_url: "/Pages/yishi/identity/index",
shiming_status_url: "/user/pages/yishi/identity/index",
dialog_url: "/user/pages/yishi/identity/index",
default_dialog_show: true
});
return false;
@ -453,8 +449,8 @@ Page({
this.setData({
"info.info_shiming_status_txt": "认证失败",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/identity/index",
dialog_url: "/Pages/yishi/identity/index",
shiming_status_url: "/user/pages/yishi/identity/index",
dialog_url: "/user/pages/yishi/identity/index",
dialog_content: "实名认证失败",
default_dialog_show: true
});
@ -470,8 +466,8 @@ Page({
this.setData({
"info.info_shiming_status_txt": "前往医师身份认证",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/identity/index?selected_tab=1",
dialog_url: "/Pages/yishi/identity/index?selected_tab=1",
shiming_status_url: "/user/pages/yishi/identity/index?selected_tab=1",
dialog_url: "/user/pages/yishi/identity/index?selected_tab=1",
dialog_content: "请您先医师身份认证",
default_dialog_show: true
});
@ -486,8 +482,8 @@ Page({
this.setData({
"info.info_shiming_status_txt": "审核中",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/identity/index?selected_tab=1",
dialog_url: "/Pages/yishi/identity/index?selected_tab=1",
shiming_status_url: "/user/pages/yishi/identity/index?selected_tab=1",
dialog_url: "/user/pages/yishi/identity/index?selected_tab=1",
// dialog_content: "您的医师身份认证正在审核中",
// default_dialog_show: true
});
@ -502,8 +498,8 @@ Page({
this.setData({
"info.info_shiming_status_txt": "认证失败",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/identity/index?selected_tab=1",
dialog_url: "/Pages/yishi/identity/index?selected_tab=1",
shiming_status_url: "/user/pages/yishi/identity/index?selected_tab=1",
dialog_url: "/user/pages/yishi/identity/index?selected_tab=1",
dialog_content: "您的医师身份认证失败",
// default_dialog_show: true
});
@ -520,8 +516,8 @@ Page({
this.setData({
"info.info_shiming_status_txt": "绑定结算银行卡",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/bankcard/index",
dialog_url: "/Pages/yishi/bankcard/index",
shiming_status_url: "/user/pages/yishi/bankcard/index",
dialog_url: "/user/pages/yishi/bankcard/index",
dialog_content: "请您先绑定结算银行卡",
default_dialog_show: true
});
@ -538,7 +534,7 @@ Page({
this.setData({
"info.info_shiming_status_txt": "前往多点执业认证",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/zhiye_identity/index",
shiming_status_url: "/user/pages/yishi/zhiye_identity/index",
dialog_content: "请您先进行多点执业认证",
// default_dialog_show: true
});
@ -554,7 +550,7 @@ Page({
this.setData({
"info.info_shiming_status_txt": "审核中",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/zhiye_identity/index",
shiming_status_url: "/user/pages/yishi/zhiye_identity/index",
dialog_content: "您的多点执业认证正在审核中",
// default_dialog_show: true
});
@ -570,7 +566,7 @@ Page({
this.setData({
"info.info_shiming_status_txt": "认证失败",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/zhiye_identity/index",
shiming_status_url: "/user/pages/yishi/zhiye_identity/index",
dialog_content: "您的多点执业认证失败",
// default_dialog_show: true
});
@ -584,7 +580,7 @@ Page({
});
wx.setStorageSync('jiesuan_dialog_show', true);
}
// (is_img_expert_reception + is_img_quick_reception + is_img_welfare_reception) == 0
let flag=false;
if(this.data.doctor_inquiry_config){
flag=this.data.doctor_inquiry_config.every((item)=>{
@ -600,7 +596,7 @@ Page({
this.setData({
"info.info_shiming_status_txt": "开启在线问诊",
"info.info_shiming_status": "info_shiming_status_yes",
shiming_status_url: "/Pages/yishi/onlinesetup/index",
shiming_status_url: "/user/pages/yishi/onlinesetup/index",
// default_dialog_show: true
});
return false;
@ -620,29 +616,6 @@ Page({
});
return false;
}
//开启在线问诊
// var is_img_expert_reception = this.data.info.is_img_expert_reception;
// if(is_img_expert_reception == 0 ){
// if(!this.data.default_dialog_show){
// this.setData({
// // dialog_visible: true,
// });
// }
// this.setData({
// "info.info_shiming_status_txt": "开启在线问诊",
// "info.info_shiming_status": "info_shiming_status_yes",
// shiming_status_url: "/Pages/yishi/onlinesetup/index",
// dialog_content: "请您先前往在线问诊管理中,开通在线问诊",
// default_dialog_show: true
// })
// return false;
// }
this.setData({
"info.info_shiming_status_txt": "去接诊",
shiming_status_url: "/Pages/yishi/wenzhen_v2/wenzhen",

View File

@ -3,9 +3,9 @@
"usingComponents": {
"t-avatar": "tdesign-miniprogram/avatar/avatar",
"van-button": "@vant/weapp/button/index",
"t-swiper": "tdesign-miniprogram/swiper/swiper",
"t-badge": "tdesign-miniprogram/badge/badge",
"t-dialog": "tdesign-miniprogram/dialog/dialog",
"van-icon": "@vant/weapp/icon/index",
"van-image": "@vant/weapp/image/index",
"t-skeleton": "tdesign-miniprogram/skeleton/skeleton",
"van-overlay": "@vant/weapp/overlay/index"

View File

@ -10,7 +10,7 @@
<view class="info_box">
<view class="info">
<view class="info_top">
<view class="info_photo" bindtap="go" data-url="/Pages/yishi/myinfo/index">
<view class="info_photo" bindtap="go" data-url="/user/pages/yishi/myinfo/index">
<!-- 跳转我的信息 -->
<t-avatar alt="头像" class="avatar" size="medium" image="{{info.avatar}}" wx:if="{{!skeleton_loading}}"/>
<!-- 骨架屏 -->
@ -56,7 +56,7 @@
<view class="renzheng_left" wx:if="{{skeleton_loading}}">
<t-skeleton theme="image" row-col="{{[1]}}" animation="gradient" loading="{{skeleton_loading}}" />
</view>
<view class="renzheng_left" bindtap="go" data-url="/Pages/yishi/identity/index" wx:if="{{!skeleton_loading}}">
<view class="renzheng_left" bindtap="go" data-url="/user/pages/yishi/identity/index" wx:if="{{!skeleton_loading}}">
<view class="renzheng_content_left">
<view class="renzheng_content_name">医师身份认证</view>
<view class="renzheng_content_status">
@ -75,7 +75,7 @@
<view class="renzheng_right" wx:if="{{skeleton_loading}}">
<t-skeleton theme="image" row-col="{{[1,1,1]}}" animation="gradient" loading="{{skeleton_loading}}" />
</view>
<view class="renzheng_right" bindtap="gozhiye" data-url="/Pages/yishi/zhiye_identity/index" wx:if="{{!skeleton_loading}}">
<view class="renzheng_right" bindtap="gozhiye" data-url="/user/pages/yishi/zhiye_identity/index" wx:if="{{!skeleton_loading}}">
<view class="renzheng_content_left">
<view class="renzheng_content_name">多点执业认证</view>
<view class="renzheng_content_status">
@ -94,18 +94,6 @@
</view>
<!-- 广告版块 -->
<view class="ad">
<!-- <t-swiper
height="110rpx"
duration="{{duration}}"
interval="{{interval}}"
navigation="{{ { type: 'dots' } }}"
list="{{swiperList}}"
bind:click="onTapAD"
bind:change="onChangeAD"
data-url="123"
wx:if="{{!skeleton_loading}}"
>
</t-swiper> -->
<swiper class="swiper" indicator-dots="{{true}}" circular="{{true}}" indicator-active-color="#3CC7C0" autoplay="{{true}}" interval="3000">
<block wx:for="{{banner}}" wx:key="*this">
<swiper-item>
@ -118,15 +106,16 @@
</view>
<!-- 功能模块 -->
<view class="fun_box">
<view bindtap="go" data-moudle="3" data-url="/Pages/yishi/myprofile/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<view bindtap="go" data-moudle="3" data-url="/user/pages/yishi/myprofile/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<van-image class="fun_box_item_img"
fit="heightFix"
height="70rpx"
src="{{static_host}}/applet/doctor/static/images/yishi/jianjie.png"
/>
<text class="fun_box_item_txt">个人简介管理</text>
<view class="introicon" wx:if="{{info.introduction_status==3}}">!</view>
</view>
<view bindtap="go" data-moudle="4" data-url="/Pages/yishi/onlinesetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<view bindtap="go" data-moudle="4" data-url="/user/pages/yishi/onlinesetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<van-image class="fun_box_item_img"
fit="widthFix"
width="70rpx"
@ -134,7 +123,7 @@
/>
<text class="fun_box_item_txt">在线问诊管理</text>
</view>
<view bindtap="go" data-moudle="5" data-url="/Pages/yishi/kuaisusetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<view bindtap="go" data-moudle="5" data-url="/user/pages/yishi/kuaisusetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<van-image class="fun_box_item_img"
fit="widthFix"
width="70rpx"
@ -142,7 +131,7 @@
/>
<text class="fun_box_item_txt">快速问诊管理</text>
</view>
<view bindtap="go" data-moudle="6" data-url="/Pages/yishi/yizhensetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<view bindtap="go" data-moudle="6" data-url="/user/pages/yishi/yizhensetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<van-image class="fun_box_item_img"
fit="widthFix"
width="70rpx"
@ -150,7 +139,7 @@
/>
<text class="fun_box_item_txt">公益问诊管理</text>
</view>
<view bindtap="go" data-moudle="7" data-url="/Pages/yishi/chufangsetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<view bindtap="go" data-moudle="7" data-url="/user/pages/yishi/chufangsetup/index" class="fun_box_item" style="height: {{funbox_height}}rpx;">
<van-image class="fun_box_item_img"
fit="widthFix"
width="70rpx"
@ -210,19 +199,7 @@
<view slot="cancel-btn" class="dialog dialog_cancel_btn" bindtap="cancelContactDialog">
取消
</view>
<!-- <view slot="confirm-btn" class="dialog dialog_confirm_btn" open-type="contact">
<t-button theme="primary" size="large" open-type="contact">确认</t-button>
</view> -->
</t-dialog>
<!-- <van-dialog
title="立即联系客服"
show="{{ contactKeFu }}"
show-cancel-button
confirm-button-color="#3CC7C0"
confirm-button-open-type="contact"
/> -->
<van-overlay show="{{ overlay_show }}" z-index="999999999999">
<van-image class="overlay_show"
fit="widthFix"
@ -245,7 +222,6 @@
确定
</view>
</t-dialog>
</view>

View File

@ -189,6 +189,7 @@ page{
background-color: #fff;
}
.fun_box_item{
position: relative;
width: 40vw;
height: 110rpx;
background-color: #FAFAFA;
@ -252,6 +253,18 @@ page{
border-left: 2px solid var(--td-border-color, #E9E9E9);
border-top-color: #E9E9E9 !important;
}
.overlay_show{
.introicon{
top:2rpx;
right:-2rpx;
width:32rpx;
height:32rpx;
display: flex;
align-items: center;
color:#fff;
font-size: 24rpx;
justify-content: center;
border-radius: 50%;
background-color: red;
position: absolute;
}

View File

@ -1,7 +0,0 @@
<!-- 操作手册 -->
<te-nav-bar navbar-data='{{navbarData}}' ></te-nav-bar>
<view class="container">
<view class="item_list">
<van-cell size="large" url="/Pages/yishi/manual_detail/index?manual_id={{item.manual_id}}" custom-style="border-radius: 20rpx;margin-bottom: 20rpx;" title="{{item.title}}" is-link wx:for="{{list}}" />
</view>
</view>

View File

@ -3,22 +3,22 @@
<view class="title" style="top: {{stateHeight}}px;height: {{navHeight}}px;line-height: {{navHeight}}px;">{{ title }}</view>
<view class="info_box">
<!-- 跳转我的信息 -->
<view class="photo" bindtap="go" data-url="/Pages/yishi/myinfo/index">
<view class="photo" bindtap="go" data-url="/user/pages/yishi/myinfo/index">
<t-avatar class="avatar" size="large" image="{{avatar}}" />
</view>
<view class="info">
<view class="namebox">
<view class="name" bindtap="go" data-url="/Pages/yishi/myinfo/index">{{user_name}} <text decode="true">&nbsp;{{doctor_title}}</text></view>
<view class="iconbox" bindtap="go" data-url="/Pages/yishi/myinfo/index">
<view class="name" bindtap="go" data-url="/user/pages/yishi/myinfo/index">{{user_name}} <text decode="true">&nbsp;{{doctor_title}}</text></view>
<view class="iconbox" bindtap="go" data-url="/user/pages/yishi/myinfo/index">
<text>设置</text>
<van-icon name="arrow" class="arrow"/>
</view>
</view>
<view class="renzheng">
<view class="yishi {{iden_auth_status==1?'active':''}}" bindtap="go" data-url="/Pages/yishi/identity/index">{{iden_auth_status==1?'已医师认证':'未医师认证'}}></view>
<view class="zhiye {{multi_point_status==1?'active':''}}" bindtap="go" data-url="/Pages/yishi/zhiye_identity/index">{{multi_point_status==1?'已多点执业认证':'未多点执业认证'}}></view>
<view class="yishi {{iden_auth_status==1?'active':''}}" bindtap="go" data-url="/user/pages/yishi/identity/index">{{iden_auth_status==1?'已医师认证':'未医师认证'}}></view>
<view class="zhiye {{multi_point_status==1?'active':''}}" bindtap="go" data-url="/user/pages/yishi/zhiye_identity/index">{{multi_point_status==1?'已多点执业认证':'未多点执业认证'}}></view>
</view>
</view>
@ -40,7 +40,7 @@
</view>
</view>
<view class="account_top_right">
<navigator url="/Pages/yishi/cash/index" open-type="navigate" hover-class="other-navigator-hover">
<navigator url="/user/pages/yishi/cash/index" open-type="navigate" hover-class="other-navigator-hover">
<van-button custom-style="text-indent: 20rpx;letter-spacing: 20rpx;text-align: center;height:70rpx;border-radius: 40rpx;padding: 0rpx 44rpx;" color="linear-gradient(310deg, #FF931A 0%, #FF931A 0%, #FCB75D 100%);font-size: 32rpx;margin-bottom:20rpx">
提现
</van-button>
@ -54,11 +54,11 @@
</view> -->
<view class="content">
<van-cell size="large" url="/Pages/yishi/medince_list/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="药品清单" is-link />
<van-cell size="large" url="/Pages/yishi/myaccount/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="我的账户" is-link />
<van-cell size="large" url="/Pages/yishi/bankcard/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="我的银行卡" is-link />
<van-cell size="large" url="/Pages/yishi/mycard/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="我的名片" is-link />
<van-cell size="large" url="/Pages/yishi/appraise/index?doctor_id={{doctor_id}}" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="患者评价" is-link />
<van-cell size="large" url="/Pages/yishi/quickreply/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="常用语" is-link border="{{ false }}" />
<van-cell size="large" url="/user/pages/yishi/medince_list/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="药品清单" is-link />
<van-cell size="large" url="/user/pages/yishi/myaccount/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="我的账户" is-link />
<van-cell size="large" url="/user/pages/yishi/bankcard/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="我的银行卡" is-link />
<van-cell size="large" url="/user/pages/yishi/mycard/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="我的名片" is-link />
<van-cell size="large" url="/user/pages/yishi/appraise/index?doctor_id={{doctor_id}}" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="患者评价" is-link />
<van-cell size="large" url="/user/pages/yishi/quickreply/index" link-type="navigateTo" title-style="font-size: 34rpx;color: rgba(0,0,0,0.65);" title="常用语" is-link border="{{ false }}" />
</view>
</view>

View File

@ -1,8 +0,0 @@
{
"component": true,
"usingComponents": {
"te-nav-bar": "/commpents/te_navbar",
"myprofile": "/commpents/myprofile/index",
"van-button": "@vant/weapp/button/index"
}
}

View File

@ -1,67 +0,0 @@
<te-nav-bar navbar-data='{{navbarData}}' ></te-nav-bar>
<view class="container">
<view class="switch_box">
<view class="switch_box_top">
<view class="switch_box_item" bindtap="changeON">
<view class="switch_box_item_name">接诊开关</view>
<view class="switch_box_item_btn">
<van-switch
active-color="#3CC7C0"
inactive-color="#E7E7E7"
size="40rpx"
checked="{{ info.is_open==1 }}" />
</view>
</view>
<view class="switch_box_note">{{ note }}</view>
</view>
<view class="help">
<van-cell size="large" url="/Pages/yishi/manual_detail/index?manual_id=6" custom-style="font-size:32rpx;" title="图文问诊操作手册" is-link border="{{false}}" />
</view>
</view>
<view class="checked_box">
<view class="price_title">价格设置</view>
<view class="price_steup_box">
<van-cell size="large" url="/Pages/yishi/onlinesetupprice/index" custom-style="font-size:32rpx;" title="每日接诊数量" is-link border="{{true}}" >
<view class="cell_value">
{{info.work_num_day}}
</view>
</van-cell>
<van-cell size="large" url="/Pages/yishi/onlinesetupprice/index" custom-style="font-size:32rpx;" title="问诊单价" is-link border="{{false}}" >
<view class="cell_value">
<text style="color: red;">{{info.inquiry_price}}</text>
<text style="color: #000;"> 元</text>
</view>
</van-cell>
</view>
</view>
<view class="bottom">
<text class="bottom_txt">
提示:服务开启后,患者按照所设置的价格向您购买图文问诊服务
1、接诊后患者可与您在{{config.duration/60}}小时内进行{{config.times_number}}回合沟通
2、若开启公益问诊图文问诊价格以公益问诊价格为准
3、每日接诊数量和问诊价格每日仅限调整1次每月仅限调整5次
4、价格修改后立即生效不影响修改之前已生成的问诊订单
</text>
</view>
<view class="go">
<van-button disabled="{{info.is_open!=1}}" bind:click="putDoctorInquiryConfig" color="#3CC7C0" custom-style="border-radius: 20rpx;font-size:30rpx;" type="primary" block>去接诊</van-button>
</view>
<t-toast id="t-toast" />
<t-dialog
visible="{{ sub_visible }}"
title="温馨提示"
content="您已关闭订阅消息通知,请点击“确认”按钮在设置中打开订阅通知。"
>
<view slot="cancel-btn" class="dialog dialog_cancel_btn" bindtap="subcancelDialog">
取消
</view>
<view slot="confirm-btn" class="dialog dialog_confirm_btn" bindtap="subconfirmDialog">
确定
</view>
</t-dialog>
</view>

View File

@ -1,34 +0,0 @@
<te-nav-bar navbar-data='{{navbarData}}' ></te-nav-bar>
<view class="checked_box">
<view class="price_steup_box">
<view class="price_steup_box_top">
<view class="price_steup_box_top_title">每日接诊数量</view>
<view class="price_steup_box_top_stepper">
<van-stepper value="{{ info.work_num_day }}" min="0" max="{{info.work_num_day > config.max_work_num_day?info.work_num_day:config.max_work_num_day}}" integer bind:change="onStepperChange" />
</view>
</view>
<view class="price_steup_box_bottom">
<view class="price_steup_box_bottom_title">问诊单价</view>
<view class="price_steup_box_bottom_num">
<input class="weui-input" bindblur="onPriceChange" type="digit" value="{{ info.inquiry_price }}" placeholder="请输入问诊单价" />
<text class="price">元</text>
</view>
</view>
</view>
</view>
<view class="bottom">
<text class="bottom_txt">
提示:服务开启后,患者按照所设置的价格向您购买图文问诊服务
1、接诊后患者可与您在{{config.duration/60}}小时内进行{{config.times_number}}回合沟通
2、若开启公益问诊图文问诊价格以公益问诊价格为准
3、每日接诊数量和问诊价格每日仅限调整1次每月仅限调整5次
4、价格修改后立即生效不影响修改之前已生成的问诊订单
</text>
</view>
<view class="go">
<van-button bind:click="updateConfig" color="#3CC7C0" custom-style="border-radius: 20rpx;font-size:30rpx;" type="primary" block>确定</van-button>
</view>
<t-toast id="t-toast" />

View File

@ -41,25 +41,6 @@ Page({
dot_5: false,
},
onLoad() {
// if(wx.$TUIKit){
// wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.MESSAGE_RECEIVED, this.$onMessageReceived, this);
// }else{
// app.imInit().then(res => {
// // console.log("wenzhen onload imInit");
// wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.MESSAGE_RECEIVED, this.$onMessageReceived, this);
// });
// }
//监听网络状态变化事件
wx.onNetworkStatusChange(function (res) {
// console.log("onNetworkStatusChange from wenzhen: ")
// console.log(res.isConnected)
// console.log(res.networkType)
// wx.showToast({
// title: '网络类型'+res.networkType,
// })
})
},
initInterval(){
let _this = this;
@ -77,7 +58,11 @@ Page({
_this.setData({
has_data: true
})
_this.selectComponent('#tabs').resize();
try {
_this.selectComponent('#tabs').resize();
} catch (y) {
}
let getCurrentName = _this.selectComponent('#tabs').getCurrentName();
let now_message_inquiry_type = _this.data.now_message_inquiry_type;
if(getCurrentName != now_message_inquiry_type){
@ -90,7 +75,6 @@ Page({
this.setData({
unreadnnum_inter: unreadnnum_inter
})
// console.log("app.globalData.unreadnnum_inter: ", app.globalData.unreadnnum_inter);
},
onShow: function () {
//console.log("onShow from wenzhen V2")
@ -123,7 +107,6 @@ Page({
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.MESSAGE_RECEIVED, this.$onMessageReceived, this);
}else{
app.imInit().then(res => {
// console.log("wenzhen onload imInit");
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.SDK_READY, this.onSDKReady, this);
wx.$TUIKit.on(wx.$TUIKitTIM.EVENT.MESSAGE_RECEIVED, this.$onMessageReceived, this);
});
@ -134,7 +117,10 @@ Page({
if (typeof this.getTabBar === 'function' && this.getTabBar()) {
this.getTabBar().stopInterval()
}
clearInterval(this.data.unreadnnum_inter)
if(this.data.unreadnnum_inter){
clearInterval(this.data.unreadnnum_inter);
}
this.setData({
hasOnShow: false,
data_list_1: [],
@ -173,19 +159,15 @@ Page({
this.getConversationList();
},
getConversationList(){
// console.log("getConversationListgetConversationListgetConversationList");
app.imInit().then(res => {
// console.log("wenzhen onload imInit: ", res);
wx.$TUIKit.getConversationList().then((imResponse) => {
// console.log("imResponse.data.conversationList from wenzhen_v2: ", imResponse.data.conversationList);
//console.log("监听会话接口列表")
console.log(imResponse.data.conversationList)
this.setData({
conversationList: imResponse.data.conversationList,
});
}).then(() => {
// console.log("开始查询订单状态")
this.formatStatus();
}).catch(error => {
console.error(error)
@ -198,107 +180,20 @@ Page({
})
let url = e.currentTarget.dataset.url;
let name = e.currentTarget.dataset.name;
wx.setStorageSync(name, 0);
if(name){
wx.setStorageSync(name, 0);
}
app.go(url);
},
$onMessageReceived(value) {
const message = value.data[0];
// console.log("message from wenzhen: ",message)
this.getConversationList();
// let cloudCustomData = JSON.parse(message.cloudCustomData);
// let inquiry_type = cloudCustomData.inquiry_type;
// let order_inquiry_id = cloudCustomData.order_inquiry_id;
// let type = message.type;
// let message_txt = "新消息";
// console.log(type)
// switch (type) {
// case TIM.TYPES.MSG_TEXT:
// console.log("文本消息");
// message_txt = message.payload.text;
// break;
// case TIM.TYPES.MSG_IMAGE:
// console.log("图片消息");
// message_txt = "[图片]";
// break;
// case TIM.TYPES.MSG_AUDIO:
// console.log("音频消息")
// message_txt = "[语言]";
// break;
// case TIM.TYPES.MSG_VIDEO:
// console.log("视频消息");
// message_txt = "[视频]";
// break;
// case TIM.TYPES.MSG_FILE:
// console.log("文件消息")
// message_txt = "[文件]";
// break;
// case TIM.TYPES.MSG_CUSTOM:
// console.log("自定义消息")
// break;
// case TIM.TYPES.MSG_MERGER:
// console.log("合并消息")
// break;
// case TIM.TYPES.MSG_LOCATION:
// console.log("位置消息")
// message_txt = "[位置]";
// break;
// case TIM.TYPES.MSG_FACE:
// console.log("表情消息")
// message_txt = "[表情]";
// break;
// default:
// break;
// }
// let list_name = "data_list_" + inquiry_type;
// let _index = this.data[list_name].findIndex(item => {
// return item.order_inquiry_id == order_inquiry_id;
// })
// let new_list = [];
// //没有当前会话
// if(_index > -1){
// let target = list_name + "[" + _index + "].last_message_content.Text";
// let target_dot = list_name + "[" + _index + "].message_dot";
// console.log("target: ",target);
// this.setData({
// [target]: message_txt,
// [target_dot]: true
// })
// new_list.push(this.data[list_name][_index]);//先放入目标
// }else{
// let item = {};
// item.inquiry_status = message.inquiry_status;
// let txt = {};
// txt.Text= message_txt;
// item.last_message_content = txt;
// item.message_send_time = message.message_send_time;
// item.message_seq = message.message_seq;
// item.message_type = message.message_type;
// item.order_inquiry_id = message.order_inquiry_id;
// item.patient_age = message.patient_age;
// item.patient_name = message.patient_name;
// item.patient_sex = message.patient_sex;
// item.to_user_id = message.to_user_id;
// item.message_dot = true;
// new_list.push(item);
// }
// //置顶
// this.data[list_name].forEach((item, index) => {
// if(index != _index){
// new_list.push(item)
// }
// })
// this.setData({
// [list_name]: new_list
// })
},
formatStatus(){
let order_inquiry_id_list = [];
order_inquiry_id_list.push("");//初始化一个,防止失败
console.log(this.data.conversationList.length)
console.log(this.data.conversationList)
let arr=this.data.conversationList.filter(item => item.conversationID != 'C2Cadministrator');
arr.forEach(item => {
@ -308,6 +203,7 @@ Page({
let lastMessage = item.lastMessage;
if(!lastMessage) return;
let cloudCustomData = lastMessage.cloudCustomData;
if(!cloudCustomData) return;
let cloudCustomDataJson;
try {
@ -320,11 +216,21 @@ Page({
return;
}
let order_inquiry_id = cloudCustomDataJson.order_inquiry_id;
order_inquiry_id_list.push(order_inquiry_id);//准备去请求后端那订单状态
})
let conversationList = arr; //this.data.conversationList;
//获取医生问诊消息用户属性
api.getDoctorInquiryMessageAttr({order_inquiry_ids: order_inquiry_id_list.join(",")}).then(response => {
let order_inquiry_ids='';
order_inquiry_id_list.forEach(item=>{
if(order_inquiry_ids){
order_inquiry_ids+=','+item;
}else{
order_inquiry_ids+=item
}
});
//console.log(order_inquiry_ids)
api.getDoctorInquiryMessageAttr({order_inquiry_ids: order_inquiry_ids}).then(response => {
let new_conversationList = [];
if(response.data.length > 0){
conversationList.forEach(item => {
@ -361,12 +267,12 @@ Page({
new_conversationList.push(item);
})
};
console.log(new_conversationList);
//console.log(new_conversationList);
this.setData({
conversationList: new_conversationList
})
}).then(() => {
// console.log("开始格式化formatConversationList")
//console.log("开始格式化formatConversationList")
this.formatConversationList();
}).catch(errors => {console.error(errors);})
},
@ -390,7 +296,7 @@ Page({
let userID = wx.getStorageSync('user_id_'+usertype);
let totalUnreadCount = 0;
let message_inquiry_type = this.data.message_inquiry_type;
console.log(this.data.conversationList);
//console.log(this.data.conversationList);
this.data.conversationList.forEach(item => {
let conversationID = item.conversationID;
if(conversationID == "C2Cadministrator") return;//如果是管理员消息直接跳过
@ -404,6 +310,7 @@ Page({
let order_inquiry_id = cloudCustomDataJson.order_inquiry_id;
if(!order_inquiry_id) return;
let inquiry_type = cloudCustomDataJson.inquiry_type;
let inquiry_mode=cloudCustomDataJson.inquiry_mode;
if(!inquiry_type) return;
if(inquiry_type > 5) return;
if(inquiry_type < message_inquiry_type){
@ -428,6 +335,15 @@ Page({
}
lastMessage.messageForShow = text;
}
if(lastMessage.type=="TIMCustomElem"){
let payload = lastMessage.payload;
if(payload && payload.data) {
// console.log("payload: ",payload.data);
let payloadJson = JSON.parse(payload.data);
session_item.custommessagetype =payloadJson.message_type?payloadJson.message_type:'';
}
}
if(lastMessage.isRevoked){
if(lastMessage.fromAccount == app.globalData.config.userID){
lastMessage.messageForShow = "你撤回了一条消息";
@ -441,6 +357,7 @@ Page({
session_item.message_type = type;
session_item.order_inquiry_id = order_inquiry_id;
session_item.inquiry_type = inquiry_type;
session_item.inquiry_mode = inquiry_mode;
session_item.patient_age = patient_family_data.patient_age;
session_item.patient_name = patient_family_data.patient_name;
session_item.patient_sex = patient_family_data.patient_sex;
@ -465,6 +382,7 @@ Page({
session_item.message_dot = false;
}
}
session_list[inquiry_type - 1].push(session_item);
console.log("--------------------")
console.log(session_list)
@ -473,7 +391,7 @@ Page({
wx.setStorageSync(userID+'_wenzhen_info', totalUnreadCount);
console.log(session_data_list_3)
console.log(session_data_list_1)
this.setData({
data_list_1: session_data_list_1,
data_list_2: session_data_list_2,
@ -499,7 +417,6 @@ Page({
this.setData({
show: false
})
// console.log("开始订阅消息");
// wx.requestSubscribeMessage({
// tmplIds: ['jhYUf91ULCTX_f69hazqAYwImdFf8ELasRAwB6X-MTM'],
@ -517,8 +434,10 @@ Page({
//from=end代表从问诊接触过来from=prescription 代表从处方管理进来
//主要处理有新的问诊还停留在老的聊天问诊里的bug。
if(from){
url = e.currentTarget.dataset.url+"?from_account="+from_account+"&order_inquiry_id="+order_inquiry_id+"&inquiry_type="+inquiry_type+"&from="+from;
}else{
url = e.currentTarget.dataset.url+"?from_account="+from_account+"&order_inquiry_id="+order_inquiry_id+"&inquiry_type="+inquiry_type;
}
@ -536,6 +455,8 @@ Page({
// console.log("开始接诊");
// console.log(response);
}).then(() => {
console.log(e)
console.log("gochat")
this.goChat(e);
}).catch(errors => {
console.error(errors);
@ -609,8 +530,6 @@ Page({
params.page = this.data.current_page + 1;
this.setData({data_list_6_loading: true})
api.getDoctorInquiryFinishMessage(params).then(response => {
// console.log(response);
this.setData({
"data_list_6": data_list_6.concat(response.data.data),
current_page: response.data.current_page,

View File

@ -13,8 +13,7 @@
"van-tabs": "@vant/weapp/tabs/index",
"t-dialog": "tdesign-miniprogram/dialog/dialog",
"van-divider": "@vant/weapp/divider/index",
"van-loading": "@vant/weapp/loading/index",
"van-dialog": "@vant/weapp/dialog/index"
"van-loading": "@vant/weapp/loading/index"
},
"enablePullDownRefresh": true,
"onReachBottomDistance": 100

View File

@ -4,13 +4,13 @@
<te-nav-bar navbar-data='{{navbarData}}' ></te-nav-bar>
<view class="container">
<view class="top">
<view class="top_left" bindtap="go" data-name="service_notice_unreadnnum" data-url="/Pages/yishi/service_notice/index">
<view class="top_left" bindtap="go" data-name="service_notice_unreadnnum" data-url="/user/pages/yishi/service_notice/index">
<t-badge dot="{{service_notice_unreadnnum > 0}}" offset="{{ [1, 1] }}" class="wrapper">
<van-image src="{{static_host}}/applet/doctor/static/images/yishi/service_notice.png" fit="heightFix" height="108rpx" aria-label="notice" />
</t-badge>
服务通知
</view>
<view class="top_right" bindtap="go" data-name="system_notice_unreadnnum" data-url="/Pages/yishi/system_notice/index">
<view class="top_right" bindtap="go" data-name="system_notice_unreadnnum" data-url="/user/pages/yishi/system_notice/index">
<t-badge offset="{{ [1, 1] }}" class="wrapper" wx:if="{{system_notice_unreadnnum == ''}}">
<van-image src="{{static_host}}/applet/doctor/static/images/yishi/system_notice.png" fit="heightFix" height="108rpx" aria-label="notice" />
</t-badge>
@ -29,123 +29,6 @@
</view>
<van-loading size="24px" wx:if="{{data_list_6_loading}}">加载中...</van-loading>
</view>
<!-- <van-button color="#3CC7C0" custom-style="width: 92vw;margin: 0 auto;border-radius: 10rpx;" block wx:if="{{ !has_data }}">开通图文问诊</van-button> -->
<!-- <view class="tabs" wx:if="{{ has_data }}" >
<t-tabs
default-value="{{ message_inquiry_type }}"
sticky
bind:change="onTabsChange"
bind:scroll="onStickyScroll"
t-class="custom-tabs"
theme="line"
swipeable="{{false}}"
custom-style="font-size: 32rpx"
>
<t-tab-panel label="在线问诊" value="1" wx:if="{{data_list_1.length > 0}}">
<wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_1 }}"
/>
<van-empty description="暂无数据" wx:if="{{data_list_1.length == 0}}" />
</t-tab-panel>
<t-tab-panel label="快速问诊" value="2" wx:if="{{data_list_2.length > 0}}">
<wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
from_account="{{item.from_account}}"
wx:for="{{ data_list_2 }}"
/>
<van-empty description="暂无数据" wx:if="{{data_list_2.length == 0}}" />
</t-tab-panel>
<t-tab-panel label="公益问诊" value="3" wx:if="{{data_list_3.length > 0}}">
<wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_3 }}"
/>
<van-empty description="暂无数据" wx:if="{{data_list_3.length == 0}}" />
</t-tab-panel>
<t-tab-panel label="问诊购药" value="4" wx:if="{{data_list_4.length > 0}}">
<wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_4 }}"
/>
<van-empty description="暂无数据" wx:if="{{data_list_4.length == 0}}" />
</t-tab-panel>
<t-tab-panel label="问诊结束" value="5" wx:if="{{data_list_5.length > 0}}">
<wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_5 }}"
/>
<van-empty description="暂无数据" wx:if="{{data_list_5.length == 0}}" />
</t-tab-panel>
</t-tabs>
</view> -->
<view class="tabs" wx:if="{{ has_data==true }}">
<van-tabs
color="#3CC7C0"
@ -157,36 +40,21 @@
wx:if="{{ has_data }}"
>
<van-tab title="在线问诊" dot="{{dot_1}}" name="1" wx:if="{{data_list_1.length > 0}}">
<!-- <wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_1 }}"
/> -->
<!-- aa -->
<view class="content" wx:for="{{ data_list_1 }}" wx:key="order_inquiry_id">
<view class="content_1">
<view class="name">
<view class="parallelogram {{item.inquiry_mode==1?'':item.inquiry_mode==2?'video':item.inquiry_mode==6?'yinan':'other'}}">{{item.inquiry_mode==1?'图文':item.inquiry_mode==2?'视频':item.inquiry_mode==6?'疑难问诊':item.inquiry_mode==7?'附赠回复':'其他'}}</view>
<text style="font-size: 34rpx;color: #333;">就诊人:</text>
{{item.patient_name}} {{item.patient_sex==1?'男':'女'}}{{item.patient_age}}<t-badge dot="{{item.message_dot || item.inquiry_status==3}}" offset="{{ [-4, 4] }}" content="岁" />
</view>
<view class="date"> {{item.message_send_time}}</view>
</view>
<view class="content_2">{{item.last_message_content.Text}}</view>
<!-- {{item.last_message_content.message_type}} -->
<view class="content_2">{{item.custommessagetype==13?'[附赠回复]':item.last_message_content.Text}}</view>
<view class="content_3">
<view class="status">{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}</view>
<view class="btn" wx:if="{{ item.inquiry_status == 3 }}">
<van-button bind:click="go" data-url="/Pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button bind:click="go" data-url="/user/pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button type="default"
bindtap="tabShow"
data-show_dialog_from_account="{{item.from_account}}"
@ -195,8 +63,8 @@
custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;margin-left:20rpx;" color="#3CC7C0">去接诊</van-button>
</view>
<view class="btn" wx:if="{{ item.inquiry_status >= 4 }}">
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/Pages/yishi/chat/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
<!-- <van-button bind:click="go" data-url="/Pages/yishi/chat_session/index" plain custom-style="width:200rpx; border-radius: 10rpx;" color="#3CC7C0">会话列表</van-button> -->
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/TUIChatService/pages/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
<!-- <van-button bind:click="go" data-url="/user/pages/yishi/chat_session/index" plain custom-style="width:200rpx; border-radius: 10rpx;" color="#3CC7C0">会话列表</van-button> -->
</view>
</view>
<view class="content_4" wx:if="{{ item.inquiry_status == 3 }}">
@ -207,22 +75,6 @@
<!-- aa -->
</van-tab>
<van-tab title="快速问诊" dot="{{dot_2}}" name="2" wx:if="{{data_list_2.length > 0}}">
<!-- <wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_2 }}"
/> -->
<view class="content" wx:for="{{ data_list_2 }}" wx:key="order_inquiry_id">
<view class="content_1">
<view class="name">
@ -235,7 +87,7 @@
<view class="content_3">
<view class="status">{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}</view>
<view class="btn" wx:if="{{ item.inquiry_status == 3 }}">
<van-button bind:click="go" data-url="/Pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button bind:click="go" data-url="/user/pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button type="default" bindtap="tabShow"
data-show_dialog_from_account="{{item.from_account}}"
data-show_dialog_order_inquiry_id="{{item.order_inquiry_id}}"
@ -243,8 +95,7 @@
custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;margin-left:20rpx;" color="#3CC7C0">去接诊</van-button>
</view>
<view class="btn" wx:if="{{ item.inquiry_status >= 4 }}">
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/Pages/yishi/chat/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
<!-- <van-button bind:click="go" data-url="/Pages/yishi/chat_session/index" plain custom-style="width:200rpx; border-radius: 10rpx;" color="#3CC7C0">会话列表</van-button> -->
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/TUIChatService/pages/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
</view>
</view>
<view class="content_4" wx:if="{{ item.inquiry_status == 3 }}">
@ -254,24 +105,9 @@
</view>
</van-tab>
<van-tab title="公益问诊" dot="{{dot_3}}" name="3" wx:if="{{data_list_3.length > 0}}">
<!-- <wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_3 }}"
/> -->
<view class="content" wx:for="{{ data_list_3 }}" wx:key="order_inquiry_id">
<view class="content_1">
<view class="name">
<text style="font-size: 34rpx;color: #333;">就诊人:</text>
{{item.patient_name}} {{item.patient_sex==1?'男':'女'}}{{item.patient_age}}<t-badge dot="{{item.message_dot || item.inquiry_status==3}}" offset="{{ [-4, 4] }}" content="岁" />
@ -282,7 +118,7 @@
<view class="content_3">
<view class="status">{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}</view>
<view class="btn" wx:if="{{ item.inquiry_status == 3 }}">
<van-button bind:click="go" data-url="/Pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button bind:click="go" data-url="/user/pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button type="default" bindtap="tabShow"
data-show_dialog_from_account="{{item.from_account}}"
data-show_dialog_order_inquiry_id="{{item.order_inquiry_id}}"
@ -290,8 +126,7 @@
custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;margin-left:20rpx;" color="#3CC7C0">去接诊</van-button>
</view>
<view class="btn" wx:if="{{ item.inquiry_status >= 4 }}">
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/Pages/yishi/chat/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
<!-- <van-button bind:click="go" data-url="/Pages/yishi/chat_session/index" plain custom-style="width:200rpx; border-radius: 10rpx;" color="#3CC7C0">会话列表</van-button> -->
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/TUIChatService/pages/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
</view>
</view>
<view class="content_4" wx:if="{{ item.inquiry_status == 3 }}">
@ -301,22 +136,6 @@
</view>
</van-tab>
<van-tab title="问诊购药" dot="{{dot_4}}" name="4" wx:if="{{data_list_4.length > 0}}">
<!-- <wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_4 }}"
/> -->
<view class="content" wx:for="{{ data_list_4 }}" wx:key="order_inquiry_id">
<view class="content_1">
<view class="name">
@ -329,7 +148,7 @@
<view class="content_3">
<view class="status">{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}</view>
<view class="btn" wx:if="{{ item.inquiry_status == 3 }}">
<van-button bind:click="go" data-url="/Pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button bind:click="go" data-url="/user/pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button type="default" bindtap="tabShow"
data-show_dialog_from_account="{{item.from_account}}"
data-show_dialog_order_inquiry_id="{{item.order_inquiry_id}}"
@ -337,8 +156,7 @@
custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;margin-left:20rpx;" color="#3CC7C0">去接诊</van-button>
</view>
<view class="btn" wx:if="{{ item.inquiry_status >= 4 }}">
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/Pages/yishi/chat/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
<!-- <van-button bind:click="go" data-url="/Pages/yishi/chat_session/index" plain custom-style="width:200rpx; border-radius: 10rpx;" color="#3CC7C0">会话列表</van-button> -->
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/TUIChatService/pages/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
</view>
</view>
<view class="content_4" wx:if="{{ item.inquiry_status == 3 }}">
@ -347,23 +165,7 @@
</view>
</view>
</van-tab>
<van-tab title="报告解读" dot="{{dot_5}}" name="5" wx:if="{{data_list_5.length > 0}}">
<!-- <wenzhen-data
name="{{ item.patient_name }}"
sex="{{ item.patient_sex==1?'男':'女' }}"
age="{{ item.patient_age }}"
date="{{ item.message_send_time }}"
desc="{{ item.last_message_content.Text }}"
status="{{ item.inquiry_status }}"
order_inquiry_id="{{ item.order_inquiry_id }}"
inquiry_type="{{item.inquiry_type}}"
from_account="{{item.from_account}}"
status_text="{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}"
note="{{ item.inquiry_status==3?'不接诊24小时后自动取消':'' }}"
message_dot="{{item.message_dot}}"
wx:for="{{ data_list_2 }}"
/> -->
<van-tab title="报告解读" dot="{{dot_5}}" name="5" wx:if="{{data_list_5.length > 0}}">
<view class="content" wx:for="{{ data_list_5 }}" wx:key="order_inquiry_id">
<view class="content_1">
<view class="name">
@ -376,7 +178,7 @@
<view class="content_3">
<view class="status">{{ item.inquiry_status==1?'待支付':item.inquiry_status==2?'待分配':item.inquiry_status==3?'待接诊':item.inquiry_status==4?'接诊中':item.inquiry_status==5?'已完成':item.inquiry_status==6?'已结束':item.inquiry_status==7?'已取消':'其他' }}</view>
<view class="btn" wx:if="{{ item.inquiry_status == 3 }}">
<van-button bind:click="go" data-url="/Pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button bind:click="go" data-url="/user/pages/yishi/case/index?order_inquiry_id={{item.order_inquiry_id}}" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">查看病历</van-button>
<van-button type="default" bindtap="tabShow"
data-show_dialog_from_account="{{item.from_account}}"
data-show_dialog_order_inquiry_id="{{item.order_inquiry_id}}"
@ -384,8 +186,7 @@
custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;margin-left:20rpx;" color="#3CC7C0">去接诊</van-button>
</view>
<view class="btn" wx:if="{{ item.inquiry_status >= 4 }}">
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/Pages/yishi/chat/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
<!-- <van-button bind:click="go" data-url="/Pages/yishi/chat_session/index" plain custom-style="width:200rpx; border-radius: 10rpx;" color="#3CC7C0">会话列表</van-button> -->
<van-button bind:click="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}" data-url="/TUIChatService/pages/index" plain custom-style="padding:0 35rpx; height:67rpx; border-radius: 10rpx;" color="#3CC7C0">问诊详情</van-button>
</view>
</view>
<view class="content_4" wx:if="{{ item.inquiry_status == 3 }}">不接诊72小时后自动取消</view>
@ -394,7 +195,9 @@
<van-tab title="问诊结束" name="6" wx:if="{{data_list_6.length > 0}}">
<view class="content" wx:for="{{ data_list_6 }}" wx:key="order_inquiry_id" bindtap="goChat" data-from_account="{{item.from_account}}" data-inquiry_type="{{item.inquiry_type}}" data-order_inquiry_id="{{item.order_inquiry_id}}"
data-from="end"
data-url="/Pages/yishi/chat/index">
data-url="/TUIChatService/pages/index">
<!-- data-url="/TUIChatKit/pages/index" -->
<view class="content_1" >
<view class="name">
<text style="font-size: 34rpx;color: #333;">就诊人:</text>
@ -428,7 +231,7 @@
bind:close="onClose"
content="为保障服务质量,请您查看患者病历后再去接诊"
>
<view slot="cancel-btn" class="dialog dialog_cancel_btn" bindtap="go" data-from_account="{{show_dialog_from_account}}" data-url="/Pages/yishi/case/index?order_inquiry_id={{show_dialog_order_inquiry_id}}" >去查看</view>
<view slot="confirm-btn" class="dialog dialog_confirm_btn" bindtap="postDoctorInquiry" data-from_account="{{show_dialog_from_account}}" data-inquiry_type="{{show_dialog_inquiry_type}}" data-order_inquiry_id="{{show_dialog_order_inquiry_id}}" data-url="/Pages/yishi/chat/index" >去接诊</view>
<view slot="cancel-btn" class="dialog dialog_cancel_btn" bindtap="go" data-from_account="{{show_dialog_from_account}}" data-url="/user/pages/yishi/case/index?order_inquiry_id={{show_dialog_order_inquiry_id}}" >去查看</view>
<view slot="confirm-btn" class="dialog dialog_confirm_btn" bindtap="postDoctorInquiry" data-from_account="{{show_dialog_from_account}}" data-inquiry_type="{{show_dialog_inquiry_type}}" data-order_inquiry_id="{{show_dialog_order_inquiry_id}}" data-url="/TUIChatService/pages/index" >去接诊</view>
</t-dialog>
</view>

View File

@ -77,7 +77,7 @@ page{
font-size: 30rpx;
color: #666666;
height: 50rpx;
line-height: 50rpx;
align-items: center;
display: flex;
}
.date{
@ -142,3 +142,37 @@ page{
align-items: center;
flex-direction: column;
}
.parallelogram {
margin-top: 4rpx;
margin-left: 10rpx;
margin-right:15rpx;
padding:0 20rpx;
height: 44rpx;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
color: #fff;
font-size: 28rpx;
position: relative;
text-decoration: none;
}
/* 添加伪元素作为平行四边形的角点 */
.parallelogram::before {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: -1;
background:#007cff;
transform: skewX(-20deg);
}
.parallelogram.video.parallelogram::before{
background:#00c986;
}
.parallelogram.yinan.parallelogram::before{
background:#ffb700;
}

View File

@ -1,23 +0,0 @@
<!-- 问诊订单 -->
<te-nav-bar navbar-data='{{navbarData}}' ></te-nav-bar>
<view class="container">
<view class="order_list">
<view class="order_item" wx:for="{{order_list}}">
<view class="order_item_content" data-index="{{index}}" bindtap="selectOrder">
<!-- 1:专家问诊 2:快速问诊 3:公益问诊 4:问诊购药) -->
<view class="order_item_content_top">{{item.inquiry_type==1?'专家问诊':item.inquiry_type==2?'快速问诊':item.inquiry_type==3?'公益问诊':item.inquiry_type==4?'问诊购药':item.inquiry_type==5?'糖组检测':'未知'}}</view>
<view class="order_item_content_data">
<view class="order_id">订单编号:<text style="font-size: 30rpx;color: #666666;">{{item.inquiry_no }}</text></view>
<view class="order_id">就诊患者:<text style="font-size: 30rpx;color: #666666;">{{item.patient_name}}{{item.patient_sex==1?'男':'女'}}{{item.patient_age}}岁)</text></view>
<view class="order_id">接诊时间:<text style="font-size: 30rpx;color: #666666;">{{item.reception_time}}</text></view>
<view class="order_id">结束时间:<text style="font-size: 30rpx;color: #666666;">{{item.finish_time}}</text></view>
<view class="order_id">订单金额:<text style="font-size: 30rpx;color: #666666;">¥{{item.amount_total}}</text></view>
<view class="order_id">预计收入:<text style="font-size: 30rpx;color: #666666;">¥{{item.expected_amount_total}}</text></view>
</view>
</view>
</view>
</view>
<van-empty description="暂无数据" wx:if="{{order_list.length == 0}}" />
<van-divider contentPosition="center" wx:if="{{order_list.length > 0 && current_page == last_page}}">到底了~</van-divider>
</view>

53
TUICallKit/README.md Normal file
View File

@ -0,0 +1,53 @@
# TUICallkit 组件接入说明
TUICallkit 是小程序音视频通话 UI 组件,通过编写几行代码,就可以为您的小程序应用添加音视频通话功能。
**低于 v3.0.4 版本,参考的 [readme](https://www.npmjs.com/package/@tencentcloud/call-uikit-wechat/v/1.4.4)。**
## 环境准备
- 微信 App iOS 最低版本要求8.0.40
- 微信 App Android 最低版本要求8.0.40
- 小程序基础库最低版本要求2.10.0
- 由于小程序测试号不具备 live-pusher 和 live-player 的使用权限,请使用企业小程序账号申请相关权限进行开发
- 由于微信开发者工具不支持原生组件(即 live-pusher 和 live-player 标签),需要在真机上进行运行体验
## 特性
- ⚡️ 功能全面 —— 支持单人/多人/音频/视频通话、支持视频转音频通话、支持自由选择通话设备
- 🎨 灵活样式 —— 组件开源,可复用逻辑,自定义 UI 样式
- 🛠 优秀生态 —— 与 [TUIKit](https://cloud.tencent.com/document/product/269/79737) 协同使用,可以在 [TIM](https://cloud.tencent.com/document/product/269) 会话中直接发起音视频通话
- 🌍 跨平台 —— 支持 Android、iOS、Web、小程序、Flutter、UniApp 等 [多个开发平台](https://cloud.tencent.com/document/product/647/78742)
- ☁️ 低延迟 —— 腾讯云全球链路资源储备,保证国际链路端到端平均时延 < 300ms
- 🤙🏻 低卡顿 —— 抗丢包率超过 80%、抗网络抖动超过 1000ms弱网环境仍顺畅稳定
- 🌈 高品质 —— 支持 720P、1080P 高清画质70% 丢包率仍可正常视频
## 目录结构
```
TUICallkit
├─ debug // 用来本地测试时生成 userSig
├─ src // UI 组件源码
├─ Components // 呼叫中 UI 组件
└─ TUICallService // UI 组件的逻辑
└─ adapter-vue.ts // 用来适配 vue2、vue3 的适配层
└─ index.ts // UI 组件逻辑的入口
├─ types
├─ package.json
├─ README.md
├─ tuicall-uikit-vue.es.js
├─ tuicall-uikit-vue.umd.js
```
## 使用指引
为方便您的使用,本组件配套多篇使用指引:
- 如果您想要了解 TUICallKit请阅读 [组件介绍 TUICallKit](https://cloud.tencent.com/document/product/647/78742)。
- 如果您想把我们的功能直接嵌入到您的项目中,请阅读 [快速接入 TUICallKit](https://cloud.tencent.com/document/product/647/78912)。
- 如果您想要了解详细 API请阅读 [API 概览](https://cloud.tencent.com/document/product/647/78759)。
## 附录
- 如果你遇到了困难,可以先参阅 [常见问题](https://cloud.tencent.com/document/product/647/78733)。
- 如果发现了示例代码的 bug欢迎提交 issue。
- 欢迎加入 QQ 群:**646165204**,进行技术交流和反馈~
-

View File

@ -0,0 +1,252 @@
import { TUICallKitServer,TUIStore, StoreName, NAME } from "../index";
const {
CALL_STATUS,
CALL_ROLE,
CALL_MEDIA_TYPE,
LOCAL_USER_INFO,
REMOTE_USER_INFO_LIST,
CALLER_USER_INFO,
IS_GROUP,
CALL_DURATION,
PUSHER,
PLAYER,
BIG_SCREEN_USER_ID,
IS_EAR_PHONE,
LOCAL_VIDEO,
MYSELF,
TOAST_INFO,
} = NAME;
Component({
properties: {},
data: {
callStatus: TUIStore.getData(StoreName.CALL, CALL_STATUS), // 通话状态
isGroupCall: TUIStore.getData(StoreName.CALL, IS_GROUP), // 是否群通话
callMediaType: TUIStore.getData(StoreName.CALL, CALL_MEDIA_TYPE), // 通话类型
callDuration: TUIStore.getData(StoreName.CALL, CALL_DURATION), // 通话时长
callRole: TUIStore.getData(StoreName.CALL, CALL_ROLE), // 通话角色
isEarPhone: TUIStore.getData(StoreName.CALL, IS_EAR_PHONE), // 声音模式 听筒/扬声器
bigScreenUserId: TUIStore.getData(StoreName.CALL, BIG_SCREEN_USER_ID), // 大屏显示的用户
localUserInfo: TUIStore.getData(StoreName.CALL, LOCAL_USER_INFO), // 本地用户信息
callerUserInfo: TUIStore.getData(StoreName.CALL, CALLER_USER_INFO),// 邀请者用户信息
remoteUserInfoList: TUIStore.getData(StoreName.CALL, REMOTE_USER_INFO_LIST), // 远端用户信息
pusher: TUIStore.getData(StoreName.CALL, PUSHER), // TRTC 本地流
playerList: TUIStore.getData(StoreName.CALL, PLAYER), // TRTC 远端流
playerProcess: {}, // 经过处理的的远端流(多人通话)
timer:null
},
methods: {
// 监听通话状态变更回调
handleCallStatusChange(value) {
this.setData({
callStatus: value,
});
if(value=='idle'){
this.sendCustom();
}
// console.log("通话状态该笔:"+value);
// console.log(this.data.callerUserInfo);
// console.log(this.data.localUserInfo);
},
//纠正音视频电话不能携带自定义消息,结束后,主动发条消息
sendCustom(){
let THIS=this;
let {timer}=this.data;
if(timer){
clearTimeout(timer);
return false;
};
wx.getStorage({
key: 'patientInfo',
success (res) {
let patientInfo=res.data;
let chat=TUICallKitServer.getTim();
let message = chat.createCustomMessage({
to:patientInfo.patient_user_id,
conversationType:"C2C",
payload: {
data:JSON.stringify({message_type:20,title:'[电话]'}),
description:'',
extension:''
},
cloudCustomData:JSON.stringify(patientInfo)
});
let time=setTimeout(()=>{
let promise = chat.sendMessage(message, {
messageControlInfo: {
excludedFromUnreadCount: true, // 消息不更新会话 unreadCount消息存漫游
}
});
promise.then((imResponse)=>{
console.log('发送成功');
console.log(imResponse);
}).catch(function(imError) {
// 发送失败
console.warn('sendMessage error:', imError);
});
},1000);
THIS.setData({
timer:time
})
}
})
},
// 监听是否群组通话变更回调
handleIsGroupChange(value) {
this.setData({
isGroupCall: value,
});
},
// 监听通话类型变更回调
handleCallMediaTypeChange(value) {
this.setData({
callMediaType: value,
});
},
// 监听通话角色变更回调
handleCallRoleChange(value) {
this.setData({
callRole: value,
});
},
// 监听通话时长变更回调
handleCallDurationChange(value) {
this.setData({
callDuration: value,
});
},
// 监听声音模式变更回调
handleEarPhoneChange(value) {
this.setData({
isEarPhone: value,
});
},
// 监听大屏显示的用户变更回调
handleScreenChange(value) {
if (value === LOCAL_VIDEO) {
this.setData({
bigScreenUserId: true,
});
} else {
this.setData({
bigScreenUserId: false,
});
}
},
// 监听本地用户信息变更回调
handleLocalUserInfoChange(value) {
this.setData({
localUserInfo: value,
});
},
// 监听到邀请者信息变更回调
handleCallerUserInfoChange(value) {
this.setData({
callerUserInfo: value,
});
},
// 监听远端用户信息变更回调
handleRemoteUserInfoListChange(value) {
this.setData({
remoteUserInfoList: value,
});
},
// 监听 TRTC 本地流变更回调
handlePusherChange(value) {
this.setData({
pusher: value,
});
},
// 监听 TRTC 远端流变更回调
handlePlayerListChange(value) {
if (this.data.isGroupCall) {
const convertToPlayer = this.convertToObj(value);
this.setData({
playerProcess: convertToPlayer,
});
} else {
this.setData({
playerList: value,
});
}
},
// 监听到弹窗信息变更回调
handleToastInfoChange(value) {
if (value.text) {
this.showToast(value.text, value.type || "info");
}
},
showToast(value, type) {
switch (type) {
case "info":
wx.showToast({
title: value,
icon: "none",
});
break;
default:
break;
}
},
convertToObj(arr = []) {
const tempObject = {};
for (let i = 0; i < arr.length; i++) {
tempObject[arr[i].userID] = arr[i];
}
return tempObject;
},
},
// 生命周期方法
lifetimes: {
attached() {
let that = this;
TUIStore.watch(
StoreName.CALL,
{
[CALL_STATUS]: this.handleCallStatusChange.bind(that),
[IS_GROUP]: this.handleIsGroupChange.bind(that),
[CALL_MEDIA_TYPE]: this.handleCallMediaTypeChange.bind(that),
[CALL_DURATION]: this.handleCallDurationChange.bind(that),
[CALL_ROLE]: this.handleCallRoleChange.bind(that),
[IS_EAR_PHONE]: this.handleEarPhoneChange.bind(that),
[BIG_SCREEN_USER_ID]: this.handleScreenChange.bind(that),
[LOCAL_USER_INFO]: this.handleLocalUserInfoChange.bind(that),
[CALLER_USER_INFO]: this.handleCallerUserInfoChange.bind(that),
[REMOTE_USER_INFO_LIST]: this.handleRemoteUserInfoListChange.bind(that),
[TOAST_INFO]: this.handleToastInfoChange.bind(that),
[PUSHER]: this.handlePusherChange.bind(that),
[PLAYER]: this.handlePlayerListChange.bind(that),
},
{
notifyRangeWhenWatch: MYSELF,
}
);
},
async detached() {
let that = this;
TUICallKitServer.getTUICallEngineInstance().handleExceptionExit();
TUIStore.unwatch(StoreName.CALL, {
[CALL_STATUS]: this.handleCallStatusChange.bind(that),
[IS_GROUP]: this.handleIsGroupChange.bind(that),
[CALL_MEDIA_TYPE]: this.handleCallMediaTypeChange.bind(that),
[CALL_DURATION]: this.handleCallDurationChange.bind(that),
[CALL_ROLE]: this.handleCallRoleChange.bind(that),
[IS_EAR_PHONE]: this.handleEarPhoneChange.bind(that),
[BIG_SCREEN_USER_ID]: this.handleScreenChange.bind(that),
[LOCAL_USER_INFO]: this.handleLocalUserInfoChange.bind(that),
[CALLER_USER_INFO]: this.handleCallerUserInfoChange.bind(that),
[REMOTE_USER_INFO_LIST]: this.handleRemoteUserInfoListChange.bind(that),
[TOAST_INFO]: this.handleToastInfoChange.bind(that),
[PUSHER]: this.handlePusherChange.bind(that),
[PLAYER]: this.handlePlayerListChange.bind(that),
});
},
},
});

View File

@ -0,0 +1,9 @@
{
"component": true,
"usingComponents": {
"SingleCall":"./component/SingleCall/SingleCall",
"GroupCall":"./component/GroupCall/GroupCall"
},
"navigationStyle": "custom",
"disableScroll": true
}

View File

@ -0,0 +1,29 @@
<view wx:if="{{callStatus !== 'idle'}}" class="TUICall-container">
<SingleCall
wx:if="{{!isGroupCall}}"
callRole="{{callRole}}"
callStatus ='{{callStatus}}'
pusher="{{pusher}}"
bigScreenUserId="{{bigScreenUserId}}"
playerList="{{playerList}}"
callDuration="{{callDuration}}"
callMediaType="{{callMediaType}}"
localUserInfo="{{localUserInfo}}"
remoteUserInfoList="{{remoteUserInfoList}}"
isEarPhone="{{isEarPhone}}"
></SingleCall>
<GroupCall
wx:if="{{isGroupCall}}"
callRole="{{callRole}}"
callStatus ='{{callStatus}}'
callMediaType="{{callMediaType}}"
callDuration="{{callDuration}}"
pusher="{{pusher}}"
playerList="{{playerList}}"
localUserInfo="{{localUserInfo}}"
callerUserInfo="{{callerUserInfo}}"
remoteUserInfoList="{{remoteUserInfoList}}"
playerProcess="{{playerProcess}}"
isEarPhone="{{isEarPhone}}"
></GroupCall>
</view>

View File

@ -0,0 +1,10 @@
.TUICall-container {
width: 100vw;
height: 100vh;
overflow: hidden;
position: absolute;
top: 0;
left: 0;
z-index: 10;
margin: 0;
}

View File

@ -0,0 +1,116 @@
import { TUICallKitServer } from "../../../TUICallService/index";
const PATH = '../../../static';
Component({
properties: {
callRole: {
type: String,
},
callStatus: {
type: String,
},
callMediaType: {
type: Number,
},
callDuration: {
type: String,
},
pusher: {
type: Object,
},
playerList: {
type: Array,
},
localUserInfo: {
type: Object,
},
callerUserInfo: {
type: Object
},
remoteUserInfoList: {
type: Array,
},
playerProcess: {
type: Object,
},
isEarPhone: {
type: Boolean,
},
},
data: {
renderStreamList: [],
IMG_DEFAULT_AVATAR:`${PATH}/default_avatar.png`,
IMG_LOADING:`${PATH}/loading.png`,
IMG_HANGUP:`${PATH}/hangup.png`,
IMG_ACCEPT: `${PATH}/dialing.png`,
IMG_SPEAKER_FALSE:`${PATH}/speaker-false.png`,
IMG_SPEAKER_TRUE:`${PATH}/speaker-true.png`,
IMG_AUDIO_TRUE:`${PATH}/audio-true.png`,
IMG_AUDIO_FALSE:`${PATH}/audio-false.png`,
IMG_CAMERA_TRUE:`${PATH}/camera-true.png`,
IMG_CAMERA_FALSE:`${PATH}/camera-false.png`,
IMG_TRANS:`${PATH}/trans.png`,
IMG_SWITCH_CAMERA:`${PATH}/switch_camera.png`,
},
observers: {
"localUserInfo, remoteUserInfoList": function (
localUserInfo,
remoteUserInfoList
) {
this.setData({
renderStreamList: [localUserInfo, ...remoteUserInfoList],
});
},
},
methods: {
async accept() {
await TUICallKitServer.accept();
},
async hangup() {
await TUICallKitServer.hangup();
},
async reject() {
await TUICallKitServer.reject();
},
async switchCamera() {
await TUICallKitServer.switchCamera();
},
async microPhoneHandler() {
if (this.data.localUserInfo.isAudioAvailable) {
await TUICallKitServer.closeMicrophone();
} else {
await TUICallKitServer.openMicrophone();
}
},
async cameraHandler() {
if (this.data.localUserInfo.isVideoAvailable) {
await TUICallKitServer.closeCamera();
} else {
await TUICallKitServer.openCamera();
}
},
async toggleSoundMode() {
await TUICallKitServer.setSoundMode();
},
pusherStateChangeHandler(e) {
TUICallKitServer._tuiCallEngine._pusherStateChangeHandler(e);
},
pusherNetStatus(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherErrorHandler(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._pusherAudioVolumeNotify(e);
},
playerStateChange(e) {
TUICallKitServer._tuiCallEngine._playerStateChange(e);
},
playNetStatus(e) {
TUICallKitServer._tuiCallEngine._playNetStatus(e);
},
playerAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._playerAudioVolumeNotify(e);
},
},
});

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,285 @@
<!-- 语音通话 -->
<view class="incoming-call audio-call transition-animation" wx:if="{{callMediaType === 1}}">
<!-- 主叫方呼叫状态 -->
<swiper class="swiper" wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{item.userId !== localUserInfo.userId}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</swiper-item>
</block>
</swiper>
<!-- 被叫方呼叫状态 -->
<view wx:if="{{callRole !== 'caller' && callStatus === 'calling' }}">
<view class="invite-calling-single">
<image class="avatar" src="{{callerUserInfo.avatar || IMG_DEFAULT_AVATAR}}" id="{{renderStreamList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{callerUserInfo.nick || callerUserInfo.userId}}</view>
<view class="invite-txt">邀请你参加多人通话</view>
</view>
<view class="invite-other-txt">参与通话的有:</view>
<view class="invite-other-list">
<view class="invite-other-item" wx:for="{{renderStreamList}}" wx:key="item">
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-other-item-name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
<!-- 主叫/被叫方通话状态 -->
<view wx:if="{{callStatus === 'connected' }}">
<view>
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">
{{item.displayUserInfo || item.nick || item.userId}}
</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
<view class="pusher-audio">
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">
{{item.displayUserInfo || item.nick || item.userId}}
</view>
</view>
<view wx:else>
<!-- 本地流 -->
<view wx:if="{{item.userId===localUserInfo.userId}}" class="pusher-audio" data-screen="pusher" catch:tap="toggleViewSize">
<live-pusher class="pusher-audio" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">我</view>
</view>
</view>
<!-- 远端流 -->
<view class="pusher-audio" wx:else>
<live-player wx:if="{{playerProcess[item.userId]}}" wx:if="{{ playerProcess[item.userId].hasAudio || playerProcess[item.userId].hasVideo }}" class="{{callMediaType === 1 ? 'pusher-audio' : 'pusher-ownvideo'}}" id="{{playerProcess[item.userId].id}}" data-userid="{{playerProcess[item.userId].userID}}" data-streamid="{{playerProcess[item.userId].streamID}}" data-streamtype="{{playerProcess[item.userId].streamType}}" src="{{playerProcess[item.userId].src}}" mode="RTC" autoplay="{{playerProcess[item.userId].autoplay}}" mute-audio="{{playerProcess[item.userId].muteAudio}}" mute-video="{{playerProcess[item.userId].muteVideo}}" orientation="{{playerProcess[item.userId].orientation}}" object-fit="{{playerProcess[item.userId].objectFit}}" background-mute="{{playerProcess[item.userId].enableBackgroundMute}}" min-cache="{{playerProcess[item.userId].minCache}}" max-cache="{{playerProcess[item.userId].maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{playerProcess[item.userId].enableRecvMessage}}" auto-pause-if-navigate="{{playerProcess[item.userId].autoPauseIfNavigate}}" auto-pause-if-open-native="{{playerProcess[item.userId].autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
</view>
<!-- 呼叫阶段按钮 -->
<view wx:if="{{callStatus === 'calling'}}" class="footer">
<view wx:if="{{callRole !== 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px;color: #666666">挂断</view>
</view>
<view class="button-container">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<view style="margin-top:10px;color: #666666">接听</view>
</view>
</view>
<view wx:if="{{callRole === 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px;color: #666666">挂断</view>
</view>
</view>
</view>
<!-- 通话阶段按钮 -->
<view wx:if="{{callStatus === 'connected'}}" class="handle-btns">
<view class="other-view black">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view>麦克风</view>
</view>
<view class="button-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view>挂断</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text>扬声器</text>
</view>
</view>
</view>
</view>
<!-- 视频通话 -->
<view class="invite-call audio-call transition-animation" wx:if="{{callMediaType === 2}}">
<!-- 通话状态 -->
<view wx:if="{{callStatus === 'connected' }}">
<swiper class="swiper" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{!item.isEnter}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<view wx:else>
<!-- 本地流 -->
<view wx:if="{{item.userId === localUserInfo.userId}}" class="play-item" data-screen="pusher">
<live-pusher class="pusher-ownvideo" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">我</view>
</view>
</view>
<!-- 远端流 -->
<view class="play-item" wx:else>
<live-player class="pusher-ownvideo" wx:if="{{playerProcess[item.userId]}}" wx:if="{{ playerProcess[item.userId].hasAudio || playerProcess[item.userId].hasVideo }}" id="{{playerProcess[item.userId].id}}" data-userid="{{playerProcess[item.userId].userID}}" data-streamid="{{playerProcess[item.userId].streamID}}" data-streamtype="{{playerProcess[item.userId].streamType}}" src="{{playerProcess[item.userId].src}}" mode="RTC" autoplay="{{playerProcess[item.userId].autoplay}}" mute-audio="{{playerProcess[item.userId].muteAudio}}" mute-video="{{playerProcess[item.userId].muteVideo}}" orientation="{{playerProcess[item.userId].orientation}}" object-fit="{{playerProcess[item.userId].objectFit}}" background-mute="{{playerProcess[item.userId].enableBackgroundMute}}" min-cache="{{playerProcess[item.userId].minCache}}" max-cache="{{playerProcess[item.userId].maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{playerProcess[item.userId].enableRecvMessage}}" auto-pause-if-navigate="{{playerProcess[item.userId].autoPauseIfNavigate}}" auto-pause-if-open-native="{{playerProcess[item.userId].autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
<view class="player-control">
<image src="{{item.avatar || IMG_DEFAULT_AVATAR}}"></image>
<view class="name">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
</view>
</swiper-item>
</block>
</swiper>
</view>
<!-- 邀请信息 -->
<view class="invite-calling">
<!-- 主叫方 -->
<swiper class="swiper" wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" indicator-dots="{{renderStreamList.length/4 > 1}}" indicator-color="white" indicator-active-color="black">
<block wx:for="{{(renderStreamList.length)/4}}" wx:key="*this" wx:for-index="pos">
<swiper-item class="invite-calling-list">
<view wx:for="{{renderStreamList}}" wx:key="userID" class="invite-calling-item" wx:if="{{index >= pos*4 && index < pos*4+4}}">
<view id="{{item.userId}}" class="invite-calling-item-message" wx:if="{{item.userId !== localUserInfo.userId}}">
<view class="invite-calling-item-loadimg">
<image src="{{IMG_LOADING}}"></image>
</view>
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-calling-item-id">{{item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</swiper-item>
</block>
</swiper>
<!-- 被叫方 -->
<view wx:if="{{callRole !== 'caller' && callStatus === 'calling' }}">
<view class="invite-calling-single">
<image class="avatar" src="{{callerUserInfo.avatar || IMG_DEFAULT_AVATAR}}" id="{{renderStreamList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{ callerUserInfo.nick || callerUserInfo.userId }}</view>
<view class="invite-txt">邀请你参加多人通话</view>
</view>
<view class="invite-other-txt">参与通话的有:</view>
<view class="invite-other-list">
<view class="invite-other-item" wx:for="{{renderStreamList}}" wx:key="item">
<image class="avatar" src="{{item.avatar || IMG_DEFAULT_AVATAR}}" binderror="handleErrorImage" />
<view class="invite-other-item-name">{{ item.displayUserInfo || item.nick || item.userId}}</view>
</view>
</view>
</view>
<!-- 呼叫阶段按钮 -->
<view wx:if="{{callStatus === 'calling' }}" class="footer">
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item">
<view class="btn-container">
<view class="call-operate" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
</view>
<text style="color: #666666">挂断</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole !== 'caller'}}">
<view class="btn-operate-item">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<text style="color: #666666">挂断</text>
</view>
<view class="btn-operate-item">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<text style="color: #666666">接听</text>
</view>
</view>
</view>
<!-- 通话阶段按钮 -->
<view wx:if="{{callStatus === 'connected' }}" class="handle-btns">
<view class="other-view white">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable ? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view class="white">麦克风</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text class="white">扬声器</text>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="cameraHandler">
<image class="btn-image" src="{{localUserInfo.isVideoAvailable? IMG_CAMERA_TRUE: IMG_CAMERA_FALSE}} "></image>
</view>
<text class="white">摄像头</text>
</view>
</view>
<view class="btn-list">
<view class="btn-list-item other-view">
<view class="btn-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view wx:if="{{pusher.enableCamera}}" class="invite-calling-header-left">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="toggleSwitchCamera" />
</view>
</view>
<text class="white">挂断</text>
</view>
</view>
</view>
</view>
</view>

View File

@ -0,0 +1,754 @@
.transition-animation {
transform: translateY(-100%);
animation: slideInDown 0.5s ease forwards;
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
.footer {
position: absolute;
bottom: 5vh;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-operate {
display: flex;
justify-content: space-between;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.btn-operate-item text {
font-size: 14px;
color: #f0e9e9;
padding: 5px;
letter-spacing: 0;
font-weight: 400;
}
.call-switch text {
padding: 5px;
color: #f0e9e9;
font-size: 14px;
}
.call-operate {
width: 8vh;
height: 8vh;
border-radius: 8vh;
margin: 0 15vw;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.tips {
font-size: 20px;
color: #ffffff;
letter-spacing: 0;
margin: 0 auto;
font-weight: 600;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tips-subtitle {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #ffffff;
letter-spacing: 0;
text-align: right;
font-weight: 400;
}
.invite-call {
position: absolute;
top: 0;
z-index: 100;
width: 100%;
height: 187px;
}
.invite-call .local-video {
width: 100px;
height: 187px;
}
.invite-call .invite-calling {
position: absolute;
top: 0;
z-index: 101;
width: 100vw;
height: 100vh;
}
.invite-calling-header {
margin-top: 107px;
display: flex;
justify-content: flex-end;
padding: 0 16px;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: 0;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.invite-calling-header-right {
display: flex;
align-items: center;
}
.invite-calling-header-message {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.invite-calling-header-right image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling .footer {
position: absolute;
bottom: 5vh;
width: 100%;
}
.invite-calling .btn-operate {
display: flex;
justify-content: center;
align-items: center;
}
.hidden {
display: none;
}
.trtc-calling {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
z-index: 99;
}
.audio-call {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
z-index: 100;
background: #ffffff;
}
.audio-call > .btn-operate {
display: flex;
justify-content: center;
}
.audio-call > image {
width: 40vw;
height: 40vw;
display: block;
margin: 20vw 30vw;
margin-top: 40vw;
}
.invite-calling-single > image {
width: 120px;
height: 120px;
border-radius: 12px;
display: block;
margin: 120px auto 15px;
}
.invite-calling-single .tips {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.invite-calling-single .tips-subtitle {
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #97989c;
letter-spacing: 0;
font-weight: 400;
text-align: center;
}
.swiper {
margin-top: 107px;
min-height: 374px;
}
.invite-calling-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: flex-start;
}
.invite-calling-item {
flex: 0.5;
min-width: 50%;
height: 187px;
position: relative;
}
.invite-calling-item image {
width: 100%;
height: 100%;
}
.invite-calling-item-message {
position: absolute;
top: 0;
left: 0;
float: left;
background: rgba(0, 0, 0, 0.6);
width: 100%;
height: 100%;
z-index: 2;
}
.invite-calling-item-loadimg {
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
width: 40px;
height: 40px;
-webkit-transform: rotate(360deg);
animation: rotation 2s linear infinite;
-moz-animation: rotation 2s linear infinite;
-webkit-animation: rotation 2s linear infinite;
-o-animation: rotation 2s linear infinite;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.invite-calling-item-loadimg image {
width: 100%;
height: 100%;
}
.invite-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.avatar {
background-color: black;
}
/* 被叫者 */
.invite-txt {
width: 126px;
height: 20px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #333333;
letter-spacing: 0;
margin: 16px auto 60px auto;
}
.invite-other-txt {
width: 112px;
height: 20px;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 14px;
color: #333333;
letter-spacing: 0;
margin: 0 auto 24px auto;
}
.invite-other-list {
position: absolute;
left: 14vw;
margin-top: 0 auto;
display: flex;
flex-wrap: wrap;
width: 272px;
justify-content: center;
flex-wrap: wrap;
}
.invite-other-item {
flex: 0.25;
text-align: center;
max-width: 64px;
margin: 2px;
}
.invite-other-item image {
border-radius: 10%;
max-width: 64px;
height: 64px;
}
.invite-other-item-name {
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #666666;
letter-spacing: 0;
line-height: 18px;
}
/* 全屏设置 */
.TUICalling-connected-layout {
width: 100%;
height: 100%;
}
.TUICalling-connected-video {
width: 100%;
height: 180%;
}
/* 本地音频 */
.stream-box {
float: left;
width: 187px;
height: 187px;
position: absolute;
top: 13vh;
}
/* 远端音频列表 */
.swiper {
margin-top: 107px;
min-height: 374px;
}
.invite-calling-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: flex-start;
}
.invite-calling-item {
flex: 0.5;
min-width: 50%;
height: 187px;
position: relative;
}
.invite-calling-item image {
width: 100%;
height: 100%;
}
/* 本地视频 */
.play-item {
width: 100%;
height: 187px;
position: relative;
background-color: #000000;
}
.pusher-ownvideo {
width: 100%;
height: 100%;
}
/* 远端视频列表 */
.swiper-list {
min-height: 189px;
}
.player-list {
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: center;
}
.player-item {
flex: 0.5;
min-width: 50%;
min-height: 187px;
}
/* 音量图标 */
.player-control {
background-color: rgba(0, 0, 0, 0.4);
border-radius: 0 6px 6px 0;
color: #fff;
z-index: 999;
position: absolute;
bottom: 0px;
left: 0px;
display: flex;
align-items: center;
height: 32px;
max-width: 50%;
z-index: 99;
}
.player-control image {
width: 32px;
height: 32px;
}
.player-control .name {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 10px;
margin-right: 10px;
flex: 1;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.control-buttons {
display: flex;
}
.icon-button {
margin-right: 10px;
}
/* 菜单 */
.handle-btns {
position: absolute;
bottom: 44px;
width: 100vw;
z-index: 3;
display: flex;
flex-direction: column;
}
.handle-btns .btn-list {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-normal {
width: 8vh;
height: 8vh;
box-sizing: border-box;
display: flex;
flex-direction: column;
/* background: white; */
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-image {
width: 100%;
height: 100%;
background: none;
}
.btn-hangup {
width: 8vh;
height: 8vh;
/*background: #f75c45;*/
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-hangup > .btn-image {
width: 100%;
height: 100%;
background: none;
}
.TRTCCalling-call-audio {
width: 100%;
height: 100%;
}
.btn-footer {
position: relative;
}
.btn-footer .multi-camera {
width: 32px;
height: 32px;
}
.btn-footer .camera {
width: 64px;
height: 64px;
position: fixed;
left: 16px;
top: 107px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.7);
}
.btn-footer .camera .camera-image {
width: 32px;
height: 32px;
}
.audio {
padding-top: 15vh;
background: #ffffff;
}
.pusher-audio {
width: 0;
height: 0;
}
.player-audio {
width: 0;
height: 0;
}
.other-view {
display: flex;
flex-direction: column;
align-items: center;
font-size: 18px;
letter-spacing: 0;
font-weight: 400;
padding: 16px;
}
.white {
font-weight: 400;
font-size: 14px;
color: #666666;
padding: 5px;
}
.black {
color: #000000;
padding: 5px;
}
.TRTCCalling-call-audio-box {
margin-top: 100px;
display: flex;
flex-wrap: wrap;
width: 100%;
justify-content: center;
}
.TRTCCalling-calling-list {
flex: 0.5;
/*设置最小宽度,才会让元素排不下,导致换行排列*/
min-width: 50%;
min-height: 187px;
position: relative;
}
.TRTCCalling-calling-list image {
width: 100%;
height: 100%;
}
.TRTCCalling-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.btn-list-item {
flex: 1;
display: flex;
justify-content: center;
padding: 16px 0;
}
.btn-image-small {
transform: scale(0.7);
}
.avatar {
background: #dddddd;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: -88px;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.call-switch text {
padding: 0;
font-size: 14px;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-operate-item text {
padding: 8px 0;
font-size: 18px;
color: #666666;
letter-spacing: 0;
font-weight: 400;
font-size: 14px;
}
.invite-calling-item-message {
position: absolute;
top: 0;
left: 0;
float: left;
background: rgba(0, 0, 0, 0.6);
width: 100%;
height: 100%;
z-index: 2;
}
.invite-calling-item-loadimg {
position: absolute;
left: calc(50% - 20px);
top: calc(50% - 20px);
width: 40px;
height: 40px;
-webkit-transform: rotate(360deg);
animation: rotation 2s linear infinite;
-moz-animation: rotation 2s linear infinite;
-webkit-animation: rotation 2s linear infinite;
-o-animation: rotation 2s linear infinite;
}
@-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(360deg);
}
}
.invite-calling-item-loadimg image {
width: 100%;
height: 100%;
}
.invite-calling-item-id {
position: absolute;
left: 2%;
bottom: 2%;
font-family: PingFangSC-Regular;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}

View File

@ -0,0 +1,132 @@
import { TUICallKitServer } from "../../../TUICallService/index";
const PATH = '../../../static';
Component({
properties: {
callRole: {
type: String,
},
callStatus: {
type: String,
observer(callStatus) {
console.log("改编状态:"+callStatus);
if(callStatus=='CONNECTED'){
TUICallKitServer.enableMuteMode(false);
}
}
},
callMediaType: {
type: Number,
},
callDuration: {
type: String,
},
pusher: {
type: Object,
},
playerList: {
type: Array,
},
localUserInfo: {
type:Object
},
remoteUserInfoList: {
type: Array,
},
isEarPhone: {
type: Boolean,
},
bigScreenUserId: {
type: Boolean
}
},
lifetimes: {
moved(){
//console.log('moved')
},
async detached(){
}
},
pageLifetimes: {
show() {
},
hide() {
// this.hangup()
},
},
data:{
IMG_DEFAULT_AVATAR:`${PATH}/default_avatar.png`,
IMG_HANGUP:`${PATH}/hangup.png`,
IMG_ACCEPT: `${PATH}/dialing.png`,
IMG_SPEAKER_FALSE:`${PATH}/speaker-false.png`,
IMG_SPEAKER_TRUE:`${PATH}/speaker-true.png`,
IMG_AUDIO_TRUE:`${PATH}/doctor_audio_true.png`,
IMG_AUDIO_FALSE:`${PATH}/doctor_audio_false.png`,
IMG_CAMERA_TRUE:`${PATH}/doctor_camera_true.png`,
IMG_CAMERA_FALSE:`${PATH}/doctor_camera_false.png`,
IMG_TRANS:`${PATH}/doctor_trans.png`,
IMG_SWITCH_CAMERA:`${PATH}/doctor_switch_camera.png`,
patient_avatar:wx.getStorageSync('patient_avatar'),
patient_name:wx.getStorageSync('patientInfo')?wx.getStorageSync('patientInfo').patient_family_data.patient_name:''
},
methods: {
async accept() {
await TUICallKitServer.accept();
},
async hangup() {
await TUICallKitServer.hangup();
},
async reject() {
await TUICallKitServer.reject();
},
async switchCamera() {
await TUICallKitServer.switchCamera();
},
async switchCallMediaType() {
await TUICallKitServer.switchCallMediaType();
},
async microPhoneHandler() {
if (this.data.localUserInfo.isAudioAvailable) {
await TUICallKitServer.closeMicrophone();
} else {
await TUICallKitServer.openMicrophone();
}
},
async cameraHandler() {
if (this.data.localUserInfo.isVideoAvailable) {
await TUICallKitServer.closeCamera();
} else {
await TUICallKitServer.openCamera();
}
},
async toggleSoundMode() {
await TUICallKitServer.setSoundMode();
},
toggleViewSize() {
TUICallKitServer.switchScreen(this.data.bigScreenUserId ? 'player':'localVideo')
},
pusherStateChangeHandler(e) {
TUICallKitServer._tuiCallEngine._pusherStateChangeHandler(e);
},
pusherNetStatus(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherErrorHandler(e) {
TUICallKitServer._tuiCallEngine._pusherNetStatus(e);
},
pusherAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._pusherAudioVolumeNotify(e);
},
playerStateChange(e) {
TUICallKitServer._tuiCallEngine._playerStateChange(e);
},
playNetStatus(e) {
TUICallKitServer._tuiCallEngine._playNetStatus(e);
},
playerAudioVolumeNotify(e) {
TUICallKitServer._tuiCallEngine._playerAudioVolumeNotify(e);
},
},
});

View File

@ -0,0 +1,4 @@
{
"component": true,
"usingComponents": {}
}

View File

@ -0,0 +1,249 @@
<!-- 音频通话 -->
<view class="{{callMediaType === 1?'audio-call':'invite-call' }} transition-animation">
<!-- 本地流 -->
<view class="{{callMediaType === 1 ? 'pusher-audio' : (bigScreenUserId || callStatus === 'calling' ? 'pusher-video' : 'player')}}" data-screen="pusher" catch:tap="toggleViewSize">
<live-pusher class="{{callMediaType === 1?'pusher-audio':'live'}}" url="{{pusher.url}}" mode="{{pusher.mode}}" autopush="{{true}}" enable-camera="{{pusher.enableCamera}}" enable-mic="{{true}}" muted="{{!pusher.enableMic}}" enable-agc="{{true}}" enable-ans="{{true}}" enable-ear-monitor="{{pusher.enableEarMonitor}}" auto-focus="{{pusher.enableAutoFocus}}" zoom="{{pusher.enableZoom}}" min-bitrate="{{pusher.minBitrate}}" max-bitrate="{{pusher.maxBitrate}}" video-width="{{pusher.videoWidth}}" video-height="{{pusher.videoHeight}}" beauty="{{pusher.beautyLevel}}" whiteness="{{pusher.whitenessLevel}}" orientation="{{pusher.videoOrientation}}" aspect="{{pusher.videoAspect}}" device-position="{{pusher.frontCamera}}" remote-mirror="{{pusher.enableRemoteMirror}}" local-mirror="{{pusher.localMirror}}" background-mute="{{pusher.enableBackgroundMute}}" audio-quality="{{pusher.audioQuality}}" audio-volume-type="{{pusher.audioVolumeType}}" audio-reverb-type="{{pusher.audioReverbType}}" waiting-image="{{pusher.waitingImage}}" beauty-style="{{pusher.beautyStyle}}" filter="{{pusher.filter}}" bindstatechange="pusherStateChangeHandler" bindnetstatus="pusherNetStatus" binderror="pusherErrorHandler" bindaudiovolumenotify="pusherAudioVolumeNotify" />
</view>
<!-- 远端流 -->
<view wx:for="{{playerList}}" wx:key="streamID" class="view-container player-container" catch:tap="toggleViewSize">
<view class="{{callMediaType === 1 ? 'player-audio' : (!bigScreenUserId ? 'pusher-video' : 'player')}}" data-screen="player">
<live-player class="live" wx:if="{{ item.hasAudio || item.hasVideo }}" id="{{item.id}}" data-userid="{{item.userID}}" data-streamid="{{item.streamID}}" data-streamtype="{{item.streamType}}" src="{{item.src}}" mode="RTC" autoplay="{{item.autoplay}}" mute-audio="{{item.muteAudio}}" mute-video="{{item.muteVideo}}" orientation="{{item.orientation}}" object-fit="{{item.objectFit}}" background-mute="{{item.enableBackgroundMute}}" min-cache="{{item.minCache}}" max-cache="{{item.maxCache}}" sound-mode="{{isEarPhone?'ear':'speaker'}}" enable-recv-message="{{item.enableRecvMessage}}" auto-pause-if-navigate="{{item.autoPauseIfNavigate}}" auto-pause-if-open-native="{{item.autoPauseIfOpenNative}}" bindstatechange="playerStateChange" bindfullscreenchange="playerFullscreenChange" bindnetstatus="playNetStatus" bindaudiovolumenotify="playerAudioVolumeNotify" />
</view>
</view>
<!-- 语言通话邀请信息 -->
<view wx:if="{{callMediaType === 1}}" class="invite-calling-single">
<image class="avatar" src="{{patient_avatar || IMG_DEFAULT_AVATAR}}" id="{{remoteUserInfoList[0].userId}}" binderror="handleErrorImage" />
<view class="tips">{{remoteUserInfoList[0].displayUserInfo}}</view>
<view wx:if="{{callRole === 'caller' && callStatus === 'calling' }}" class="tips-subtitle">等待对方接受</view>
</view>
<!-- 视频通话邀请信息 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 2}}" class="invite-calling">
<view class="invite-calling-header">
<view class="other-view white" wx:if="{{callRole == 'caller'}}">
<text>{{patient_name}}</text>
</view>
<view class="invite-calling-header-right">
<view class="invite-calling-header-message">
<text class="tips-subtitle" wx:if="{{callRole !== 'caller'}}" style="text-align: right;margin-bottom: 10rpx;">XXXX</text>
<!-- <label class="tips">
{{remoteUserInfoList[0].displayUserInfo}}
</label> -->
<text class="tips-subtitle" wx:if="{{callRole !== 'caller'}}">邀请你视频通话</text>
<!-- <text class="tips-subtitle" wx:else>等待对方接受</text> -->
</view>
<image class="avatar" src="{{patient_avatar || IMG_DEFAULT_AVATAR}}" id="{{remoteUserInfoList[0].userId}}" binderror="handleErrorImage" />
</view>
</view>
</view>
<!-- 语音通话呼叫状态按钮 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 1}}" class="footer">
<view wx:if="{{callRole !== 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px">挂断</view>
</view>
<view class="button-container">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<view style="margin-top:10px">接听</view>
</view>
</view>
<view wx:if="{{callRole === 'caller'}}" class="btn-operate">
<view class="button-container">
<view class="call-operate" style="background-color: red" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
<view style="margin-top:10px">挂断</view>
</view>
</view>
</view>
<!-- 视频通话呼叫状态按钮 -->
<view wx:if="{{callStatus === 'calling' && callMediaType === 2}}" class="invite-calling">
<view class="handle-btns">
<!-- <view class="btn-operate-item call-switch" catch:tap="switchCallMediaType">
<view class="call-operate">
<image src="{{IMG_TRANS}}" />
</view>
<text>切到语音通话</text>
</view> -->
<view class="btn-list">
<view wx:if="{{localUserInfo.isVideoAvailable && callRole == 'caller'}}" class="button-container">
<view class="btn-normal ">
<image class="btn-image newimg" src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="switchCamera" />
</view>
<view class="white">切换</view>
</view>
<view class="button-container" bindtap="switchCallMediaType" wx:if="{{callRole == 'caller'}}">
<view class="btn-normal">
<image class="btn-image newimg" src="{{IMG_TRANS}} "></image>
</view>
<view class="white">切换至语音</view>
</view>
<view class="button-container" bindtap="microPhoneHandler" wx:if="{{callRole == 'caller'}}">
<view class="btn-normal">
<image class="btn-image newimg" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view class="white">语音</view>
</view>
<!-- <view class="button-container" bindtap="toggleSoundMode">
<view class="btn-normal">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text class="white">扬声器</text>
</view> -->
<!-- <view class="button-container" bindtap="cameraHandler">
<view class="btn-normal">
<image class="btn-image" src="{{localUserInfo.isVideoAvailable ? IMG_CAMERA_TRUE: IMG_CAMERA_FALSE}} "></image>
</view>
<text class="white">摄像头</text>
</view> -->
</view>
<view class="btn-list" style="justify-content: center;width:65%;margin:0 auto;">
<view class="btn-list-item other-view" >
<view class="btn-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
</view>
<text class="white">挂断</text>
</view>
<!-- <view class="btn-list-item other-view" >
<view class="btn-container">
<view class="btn-hangup" bindtap="accept">
<image class="btn-image" src="{{IMG_ACCEPT}}"></image>
</view>
</view>
<text class="white">接听</text>
</view> -->
</view>
</view>
</view>
<!-- <view wx:if="{{callStatus === 'calling' && callMediaType === 2}}" class="invite-calling" >
<view class="footer" style="display: flex;">
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="invite-calling-header-left">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="switchCamera" />
</view>
</view>
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item call-switch" catch:tap="switchCallMediaType">
<view class="call-operate">
<image src="{{IMG_TRANS}}" />
</view>
<text>切到语音通话</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole === 'caller'}}">
<view class="btn-operate-item">
<view class="btn-container">
<view class="call-operate" catch:tap="hangup">
<image src="{{IMG_HANGUP}}" />
</view>
</view>
<text>挂断</text>
</view>
</view>
<view class="btn-operate" wx:if="{{callRole !== 'caller'}}">
<view class="btn-operate-item">
<view class="call-operate" style="background-color: red" catch:tap="reject">
<image src="{{IMG_HANGUP}}" />
</view>
<text>挂断</text>
</view>
<view class="btn-operate-item">
<view class="call-operate" catch:tap="accept">
<image src="{{IMG_ACCEPT}}" />
</view>
<text>接听</text>
</view>
</view>
</view>
</view> -->
<!-- 语音通话接通状态按钮 -->
<view wx:if="{{callStatus === 'connected' && callMediaType === 1}}" class="handle-btns">
<view class="other-view black">
<text>{{callDuration}}</text>
</view>
<view class="btn-list">
<view class="button-container">
<view class="btn-normal" bindtap="microPhoneHandler">
<image class="btn-image" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view>语音</view>
</view>
<view class="button-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view>挂断</view>
</view>
<view class="button-container">
<view class="btn-normal" bindtap="toggleSoundMode">
<image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image>
</view>
<text>扬声器</text>
</view>
</view>
</view>
<!-- 视频通话接听状态按钮 -->
<view wx:if="{{callStatus === 'connected' && callMediaType === 2}}" class="invite-calling">
<view class="handle-btns">
<view class="other-view white" style="margin-bottom: 50rpx;">
<text>{{callDuration}}</text>
</view>
<!-- <view class="btn-operate-item call-switch" catch:tap="switchCallMediaType">
<view class="call-operate">
<image src="{{IMG_TRANS}}" />
</view>
<text>切到语音通话</text>
</view> -->
<view class="btn-list" style="width:85%;margin:0 auto;">
<view class="button-container" bindtap="switchCamera" wx:if="{{localUserInfo.isVideoAvailable}}">
<view class="btn-normal">
<image class="btn-image newimg" src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" src="{{IMG_SWITCH_CAMERA}} "></image>
</view>
<text class="white">切换</text>
</view>
<view class="button-container" bindtap="hangup">
<view class="btn-normal" style="width:8vh;height:8vh">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
<!-- <image class="btn-image" src="{{isEarPhone ? IMG_SPEAKER_FALSE: IMG_SPEAKER_TRUE}} "></image> -->
</view>
<text class="white">挂断</text>
</view>
<view class="button-container" bindtap="microPhoneHandler">
<view class="btn-normal">
<image class="btn-image newimg" src="{{localUserInfo.isAudioAvailable? IMG_AUDIO_TRUE: IMG_AUDIO_FALSE}} "></image>
</view>
<view class="white">静音</view>
</view>
</view>
<!-- <view class="btn-list">
<view class="btn-list-item other-view">
<view class="btn-container">
<view class="btn-hangup" bindtap="hangup">
<image class="btn-image" src="{{IMG_HANGUP}}"></image>
</view>
<view wx:if="{{localUserInfo.isVideoAvailable}}" class="invite-calling-connected">
<image src="{{IMG_SWITCH_CAMERA}}" data-device="{{pusher.frontCamera}}" catch:tap="switchCamera" />
</view>
</view>
<text class="white">挂断</text>
</view>
</view> -->
</view>
</view>
</view>

View File

@ -0,0 +1,519 @@
.footer {
position: absolute;
bottom: 5vh;
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-operate {
display: flex;
justify-content: space-between;
z-index: 9999;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 20px;
}
.btn-operate-item text {
font-size: 14px;
color: #f0e9e9;
padding: 5px;
letter-spacing: 0;
font-weight: 400;
}
.call-switch text {
padding: 5px;
color: #f0e9e9;
font-size: 14px;
}
.call-operate {
width: 8vh;
height: 8vh;
border-radius: 8vh;
margin: 0 15vw;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.tips {
font-size: 20px;
color: #ffffff;
letter-spacing: 0;
margin: 0 auto;
/* text-shadow: 0 1px 2px rgba(0,0,0,0.40); */
font-weight: 600;
max-width: 150px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.tips-subtitle {
font-family: PingFangSC-Regular;
font-size: 14px;
color: #ffffff;
letter-spacing: 0;
text-align: right;
/* text-shadow: 0 1px 2px rgba(0,0,0,0.30); */
font-weight: 400;
}
.invite-call {
position: absolute;
top: 0;
z-index: 100;
width: 100vw;
height: 100vh;
background-color: black;
}
.transition-animation {
transform: translateY(-100%);
animation: slideInDown 0.5s ease forwards;
}
@keyframes slideInDown {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
.invite-call .local-video {
width: 100vw;
height: 100vh;
}
.invite-call .invite-calling {
position: absolute;
top: 0;
z-index: 101;
width: 100vw;
height: 100vh;
}
.invite-calling-header {
margin-top: 87px;
display: flex;
justify-content: flex-end;
padding: 0 16px;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-header-left {
position: absolute;
right: 0;
}
.invite-calling-header-left image {
width: 32px;
height: 32px;
}
.invite-calling-header-right {
display: flex;
align-items: center;
}
.invite-calling-header-message {
display: flex;
flex-direction: column;
padding: 0 16px;
}
.invite-calling-header-right image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling .footer {
position: absolute;
bottom: 5vh;
width: 100%;
display: flex;
flex-direction: row;
}
.invite-calling .btn-operate {
display: flex;
justify-content: center;
align-items: center;
}
.hidden {
display: none;
}
.trtc-calling {
width: 100vw;
height: 100vh;
overflow: hidden;
margin: 0;
z-index: 99;
}
.audio-call {
width: 100vw;
height: 100vh;
position: absolute;
top: 0;
z-index: 100;
background: #ffffff;
}
.audio-call > .btn-operate {
display: flex;
justify-content: center;
}
.audio-call > image {
width: 40vw;
height: 40vw;
display: block;
margin: 20vw 30vw;
margin-top: 40vw;
}
.invite-calling-single > image {
width: 120px;
height: 120px;
border-radius: 12px;
display: block;
margin: 140px auto 15px;
/* margin: 20vw 30vw; */
}
.invite-calling-single .tips {
width: 100%;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.invite-calling-single .tips-subtitle {
height: 20px;
font-family: PingFangSC-Regular;
font-size: 14px;
color: #97989c;
letter-spacing: 0;
font-weight: 400;
text-align: center;
}
.invite-calling-list {
justify-content: flex-start;
}
.invite-calling-item {
position: relative;
margin: 0 12px;
}
.invite-calling-item image {
width: 100px;
height: 100px;
border-radius: 12px;
}
.invite-calling-item-message {
position: absolute;
background: rgba(0, 0, 0, 0.5);
width: 100px;
height: 100px;
top: 0;
left: 0;
z-index: 2;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.avatar {
background: #dddddd;
}
.player {
position: absolute;
right: 16px;
top: 107px;
width: 100px;
height: 178px;
padding: 16px;
z-index: 999;
}
.pusher-video {
position: absolute;
width: 100%;
height: 100%;
z-index: 1;
}
.stream-box {
position: relative;
float: left;
width: 50vw;
height: 260px;
/* background-color: #f75c45; */
z-index: 3;
}
.handle-btns {
position: absolute;
bottom: 44px;
width: 100vw;
z-index: 3;
display: flex;
flex-direction: column;
}
.handle-btns .btn-list {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
}
.button-container {
display: flex;
flex-direction: column;
text-align: center;
}
.btn-normal {
/* width: 8vh;
height: 8vh; */
box-sizing: border-box;
display: flex;
flex-direction: column;
/* background: white; */
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-image {
width: 100%;
height: 100%;
background: none;
}
.btn-image.newimg{
background: rgba(0,0,0,0.45);
width:56rpx;
height:56rpx;
padding:25rpx;
border-radius: 50%;
}
.btn-hangup {
width: 8vh;
height: 8vh;
/*background: #f75c45;*/
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
}
.btn-hangup > .btn-image {
width: 100%;
height: 100%;
background: none;
}
.TRTCCalling-call-audio {
width: 100%;
height: 100%;
}
.btn-footer {
position: relative;
}
.btn-footer .multi-camera {
width: 32px;
height: 32px;
}
.btn-footer .camera {
width: 64px;
height: 64px;
position: fixed;
left: 16px;
top: 107px;
display: flex;
justify-content: center;
align-items: center;
background: rgba(255, 255, 255, 0.7);
}
.btn-footer .camera .camera-image {
width: 32px;
height: 32px;
}
.TUICalling-connected-layout {
width: 100%;
height: 100%;
}
.audio {
padding-top: 15vh;
background: #ffffff;
}
.pusher-audio {
width: 0;
height: 0;
}
.player-audio {
width: 0;
height: 0;
}
.live {
width: 100%;
height: 100%;
}
.other-view {
display: flex;
flex-direction: column;
align-items: center;
font-size: 18px;
letter-spacing: 0;
font-weight: 400;
padding: 16px;
}
.white {
color: #f0e9e9;
padding: 5px;
font-size: 14px;
}
.black {
color: #000000;
padding: 5px;
}
.TRTCCalling-call-audio-box {
display: flex;
flex-wrap: wrap;
justify-content: center;
}
.mutil-img {
justify-content: flex-start !important;
}
.TRTCCalling-call-audio-img {
display: flex;
flex-direction: column;
align-items: center;
}
.TRTCCalling-call-audio-img > image {
width: 25vw;
height: 25vw;
margin: 0 4vw;
border-radius: 4vw;
position: relative;
}
.TRTCCalling-call-audio-img text {
font-size: 20px;
color: #333333;
letter-spacing: 0;
font-weight: 500;
}
.btn-list-item {
flex: 1;
display: flex;
justify-content: center;
padding: 16px 0;
}
.btn-image-small {
transform: scale(0.7);
}
.avatar {
background: #dddddd;
}
.btn-container {
display: flex;
align-items: center;
position: relative;
}
.invite-calling-connected {
position: absolute;
right: -88px;
}
.invite-calling-connected image {
width: 32px;
height: 32px;
}
.call-switch .call-operate {
width: 4vh;
height: 3vh;
}
.call-operate image {
width: 100%;
height: 100%;
background: none;
}
.call-switch text {
padding: 0;
font-size: 14px;
}
.btn-operate-item {
display: flex;
flex-direction: column;
align-items: center;
}
.btn-operate-item text {
padding: 8px 0;
font-size: 18px;
color: #ffffff;
letter-spacing: 0;
font-weight: 400;
font-size: 14px;
}

View File

@ -0,0 +1,15 @@
import { IBellParams } from '../interface/index';
export declare class BellContext {
private _bellContext;
private _isMuteBell;
private _calleeBellFilePath;
private _callRole;
private _callStatus;
constructor();
setBellSrc(): void;
setBellProperties(bellParams: IBellParams): void;
play(): Promise<void>;
stop(): Promise<void>;
setBellMute(enable: boolean): Promise<void>;
destroy(): void;
}

View File

@ -0,0 +1,106 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.BellContext = void 0;
const index_1 = require("../const/index");
const common_utils_1 = require("../utils/common-utils");
const DEFAULT_CALLER_BELL_FILEPATH = '/TUICallKit/static/phone_dialing.mp3';
const DEFAULT_CALLEE_BELL_FILEPATH = '/TUICallKit/static/phone_ringing.mp3';
class BellContext {
constructor() {
this._bellContext = null;
this._isMuteBell = false;
this._calleeBellFilePath = DEFAULT_CALLEE_BELL_FILEPATH;
this._callRole = index_1.CallRole.UNKNOWN;
this._callStatus = index_1.CallStatus.IDLE;
// @ts-ignore
this._bellContext = wx.createInnerAudioContext();
this._bellContext.loop = true;
}
setBellSrc() {
// @ts-ignore
const fs = wx.getFileSystemManager();
try {
let playBellFilePath = DEFAULT_CALLER_BELL_FILEPATH;
if (this._callRole === index_1.CallRole.CALLEE) {
playBellFilePath = this._calleeBellFilePath || DEFAULT_CALLEE_BELL_FILEPATH;
}
fs.readFileSync(playBellFilePath, 'utf8', 0);
this._bellContext.src = playBellFilePath;
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to setBellSrc, ${error}`);
}
}
setBellProperties(bellParams) {
this._callRole = bellParams.callRole || this._callRole;
this._callStatus = bellParams.callStatus || this._callStatus;
this._calleeBellFilePath = bellParams.calleeBellFilePath || this._calleeBellFilePath;
// undefined/false || isMuteBell => isMuteBell (不符合预期)
this._isMuteBell = (0, common_utils_1.isUndefined)(bellParams.isMuteBell) ? this._isMuteBell : bellParams.isMuteBell;
}
play() {
return __awaiter(this, void 0, void 0, function* () {
try {
if (this._callStatus !== index_1.CallStatus.CALLING) {
return;
}
this.setBellSrc();
if (this._callRole === index_1.CallRole.CALLEE && !this._isMuteBell) {
yield this._bellContext.play();
}
if (this._callRole === index_1.CallRole.CALLER) {
yield this._bellContext.play();
}
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to play audio file, ${error}`);
}
});
}
stop() {
return __awaiter(this, void 0, void 0, function* () {
try {
this._bellContext.stop();
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to stop audio file, ${error}`);
}
});
}
setBellMute(enable) {
return __awaiter(this, void 0, void 0, function* () {
if (this._callStatus !== index_1.CallStatus.CALLING && this._callRole !== index_1.CallRole.CALLEE) {
return;
}
if (enable) {
yield this.stop();
}
else {
yield this.play();
}
});
}
destroy() {
try {
this._isMuteBell = false;
this._calleeBellFilePath = '';
this._callRole = index_1.CallRole.UNKNOWN;
this._callStatus = index_1.CallStatus.IDLE;
this._bellContext.destroy();
this._bellContext = null;
}
catch (error) {
console.warn(`${index_1.NAME.PREFIX}Failed to destroy, ${error}`);
}
}
}
exports.BellContext = BellContext;

View File

@ -0,0 +1,86 @@
import { CallStatus, NAME, CallRole } from '../const/index';
import { IBellParams } from '../interface/index';
import { isUndefined } from '../utils/common-utils';
const DEFAULT_CALLER_BELL_FILEPATH = '/TUICallKit/static/phone_dialing.mp3';
const DEFAULT_CALLEE_BELL_FILEPATH = '/TUICallKit/static/phone_ringing.mp3';
export class BellContext {
private _bellContext: any = null;
private _isMuteBell: boolean = false;
private _calleeBellFilePath: string = DEFAULT_CALLEE_BELL_FILEPATH;
private _callRole: string = CallRole.UNKNOWN;
private _callStatus: string = CallStatus.IDLE;
constructor() {
// @ts-ignore
this._bellContext = wx.createInnerAudioContext();
this._bellContext.loop = true;
}
setBellSrc() {
// @ts-ignore
const fs = wx.getFileSystemManager();
try {
let playBellFilePath = DEFAULT_CALLER_BELL_FILEPATH;
if (this._callRole === CallRole.CALLEE) {
playBellFilePath = this._calleeBellFilePath || DEFAULT_CALLEE_BELL_FILEPATH;
}
fs.readFileSync(playBellFilePath, 'utf8', 0);
this._bellContext.src = playBellFilePath;
} catch (error) {
console.warn(`${NAME.PREFIX}Failed to setBellSrc, ${error}`);
}
}
setBellProperties(bellParams: IBellParams) {
this._callRole = bellParams.callRole || this._callRole;
this._callStatus = bellParams.callStatus || this._callStatus;
this._calleeBellFilePath = bellParams.calleeBellFilePath || this._calleeBellFilePath;
// undefined/false || isMuteBell => isMuteBell (不符合预期)
this._isMuteBell = isUndefined(bellParams.isMuteBell) ? this._isMuteBell : bellParams.isMuteBell;
}
async play() {
try {
if (this._callStatus !== CallStatus.CALLING) {
return ;
}
this.setBellSrc();
if (this._callRole === CallRole.CALLEE && !this._isMuteBell) {
await this._bellContext.play();
}
if (this._callRole === CallRole.CALLER) {
await this._bellContext.play();
}
} catch (error) {
console.warn(`${NAME.PREFIX}Failed to play audio file, ${error}`);
}
}
async stop() {
try {
this._bellContext.stop();
} catch (error) {
console.warn(`${NAME.PREFIX}Failed to stop audio file, ${error}`);
}
}
async setBellMute(enable: boolean) {
if (this._callStatus !== CallStatus.CALLING && this._callRole !== CallRole.CALLEE) {
return;
}
if (enable) {
await this.stop();
} else {
await this.play();
}
}
destroy() {
try {
this._isMuteBell = false;
this._calleeBellFilePath = '';
this._callRole = CallRole.UNKNOWN;
this._callStatus = CallStatus.IDLE;
this._bellContext.destroy();
this._bellContext = null;
} catch (error) {
console.warn(`${NAME.PREFIX}Failed to destroy, ${error}`);
}
}
}

View File

@ -0,0 +1,99 @@
import { ITUICallService, ICallParams, IGroupCallParams, ICallbackParam, ISelfInfoParams, IInviteUserParams, IJoinInGroupCallParams, IInitParams } from '../interface/ICallService';
import { LanguageType, LOG_LEVEL, VideoDisplayMode, VideoResolution } from '../const/index';
import { ITUIGlobal } from '../interface/ITUIGlobal';
import { ITUIStore } from '../interface/ITUIStore';
declare const TUIGlobal: ITUIGlobal;
declare const TUIStore: ITUIStore;
export { TUIGlobal, TUIStore };
export default class TUICallService implements ITUICallService {
static instance: TUICallService;
_tuiCallEngine: any;
private _tim;
private _TUICore;
private _timerId;
private _startTimeStamp;
private _bellContext;
private _defaultOfflinePushInfo;
constructor();
static getInstance(): TUICallService;
init(params: IInitParams): Promise<void>;
destroyed(): Promise<void>;
call(callParams: ICallParams): Promise<void>;
groupCall(groupCallParams: IGroupCallParams): Promise<void>;
inviteUser(params: IInviteUserParams): Promise<void>;
joinInGroupCall(params: IJoinInGroupCallParams): Promise<void>;
getTUICallEngineInstance(): any;
setLogLevel(level: LOG_LEVEL): void;
setLanguage(language: LanguageType): void;
enableFloatWindow(enable: boolean): void;
setSelfInfo(params: ISelfInfoParams): Promise<void>;
setCallingBell(filePath?: string): Promise<void>;
enableMuteMode(enable: boolean): Promise<void>;
accept(): Promise<void>;
hangup(): Promise<void>;
reject(): Promise<void>;
openCamera(videoViewDomID: string): Promise<void>;
closeCamera(): Promise<void>;
openMicrophone(): Promise<void>;
closeMicrophone(): Promise<void>;
switchScreen(userId: string): void;
switchCallMediaType(): Promise<void>;
switchCamera(): Promise<void>;
setSoundMode(type?: string): void;
getTim(): any;
private _addListenTuiCallEngineEvent;
private _removeListenTuiCallEngineEvent;
private _handleError;
private _handleNewInvitationReceived;
private _handleUserAccept;
private _handleUserEnter;
private _callerChangeToConnected;
private _handleUserLeave;
private _unNormalEventsManager;
private _handleInviteeReject;
private _handleNoResponse;
private _handleLineBusy;
private _handleCallingCancel;
private _handleCallingEnd;
private _handleSDKReady;
private _handleKickedOut;
private _handleCallTypeChange;
private _messageSentByMe;
private _handleUserUpdate;
private _handleCallError;
beforeCalling: ((...args: any[]) => void) | undefined;
afterCalling: ((...args: any[]) => void) | undefined;
onMinimized: ((...args: any[]) => void) | undefined;
onMessageSentByMe: ((...args: any[]) => void) | undefined;
kickedOut: ((...args: any[]) => void) | undefined;
statusChanged: ((...args: any[]) => void) | undefined;
setCallback(params: ICallbackParam): void;
toggleMinimize(): void;
private _executeExternalBeforeCalling;
private _executeExternalAfterCalling;
setVideoDisplayMode(displayMode: VideoDisplayMode): void;
setVideoResolution(resolution: VideoResolution): Promise<void>;
private _handleExceptionExit;
private _setLocalUserInfoAudioVideoAvailable;
private _updateCallStoreBeforeCall;
private _updateCallStoreAfterCall;
private _resetCurrentDevice;
private _resetCallStore;
private _noDevicePermissionToast;
private _startTimer;
private _updateCallDuration;
private _stopTimer;
private _deleteRemoteUser;
private _analyzeEventData;
getGroupMemberList(count: number, offset: number): Promise<any>;
getGroupProfile(): Promise<any>;
private _handleCallStatusChange;
private _watchTUIStore;
private _unwatchTUIStore;
bindTUICore(TUICore: any): void;
private _callTUIService;
onNotifyEvent(eventName: string, subKey: string): Promise<void>;
onCall(method: String, params: any): Promise<void>;
private _handleTUICoreOnClick;
onGetExtension(extensionID: string, params: any): any[];
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,991 @@
import { TUICore, TUILogin, TUIConstants, ExtensionInfo } from '@tencentcloud/tui-core';
import {
ITUICallService,
ICallParams,
IGroupCallParams,
IUserInfo,
ICallbackParam,
ISelfInfoParams,
IBellParams,
IInviteUserParams,
IJoinInGroupCallParams,
IInitParams,
} from '../interface/ICallService';
import {
StoreName,
CallStatus,
CallMediaType,
NAME,
CALL_DATA_KEY,
LanguageType,
CallRole,
LOG_LEVEL,
VideoDisplayMode,
VideoResolution,
StatusChange,
AudioCallIcon,
VideoCallIcon,
ErrorCode,
ErrorMessage,
AudioPlayBackDevice,
CameraPosition,
} from '../const/index';
// @ts-ignore
import { TUICallEngine, EVENT as TUICallEvent } from 'tuicall-engine-wx';
import { CallTips, t } from '../locales/index';
import { initAndCheckRunEnv, beforeCall, handlePackageError } from './miniProgram';
import { BellContext } from './bellContext';
import { VALIDATE_PARAMS, avoidRepeatedCall, paramValidate } from '../utils/validate/index';
import { handleRepeatedCallError, handleNoDevicePermissionError, formatTime, performanceNow } from '../utils/common-utils';
import { getMyProfile, getRemoteUserProfile, generateText, generateStatusChangeText, getGroupMemberList, getGroupProfile } from './utils';
import timer from '../utils/timer';
import { ITUIGlobal } from '../interface/ITUIGlobal';
import { ITUIStore } from '../interface/ITUIStore';
import TuiGlobal from '../TUIGlobal/tuiGlobal';
import TuiStore from '../TUIStore/tuiStore';
const TUIGlobal: ITUIGlobal = TuiGlobal.getInstance();
const TUIStore: ITUIStore = TuiStore.getInstance();
const version = '2.1.1';
export { TUIGlobal, TUIStore };
export default class TUICallService implements ITUICallService {
static instance: TUICallService;
public _tuiCallEngine: any;
private _tim: any = null;
private _TUICore: any = null;
private _timerId: number = -1;
private _startTimeStamp: number = performanceNow();
private _bellContext: any = null;
private _defaultOfflinePushInfo = {
title: '',
description: t('you have a new call'),
};
constructor() {
console.log(`${NAME.PREFIX}version: ${version}`);
this._watchTUIStore();
this._bellContext = new BellContext();
// 下面TUICore注册事件注册组件服务注册界面拓展
TUICore.registerEvent(TUIConstants.TUILogin.EVENT.LOGIN_STATE_CHANGED, TUIConstants.TUILogin.EVENT_SUB_KEY.USER_LOGIN_SUCCESS, this);
TUICore.registerService(TUIConstants.TUICalling.SERVICE.NAME, this);
TUICore.registerExtension(TUIConstants.TUIChat.EXTENSION.INPUT_MORE.EXT_ID, this);
}
static getInstance() {
if (!TUICallService.instance) {
TUICallService.instance = new TUICallService();
}
return TUICallService.instance;
}
@avoidRepeatedCall()
@paramValidate(VALIDATE_PARAMS.init)
public async init(params: IInitParams) {
try {
if (this._tuiCallEngine) return;
// @ts-ignore
let { userID, tim, userSig, sdkAppID, SDKAppID, isFromChat } = params;
if (this._TUICore) {
sdkAppID = this._TUICore.SDKAppID;
tim = this._TUICore.tim;
}
this._tim = tim;
console.log(`${NAME.PREFIX}init sdkAppId: ${sdkAppID || SDKAppID}, userId: ${userID}`);
this._tuiCallEngine = TUICallEngine.createInstance({
tim,
// @ts-ignore
sdkAppID: sdkAppID || SDKAppID, // 兼容传入 SDKAppID 的问题
callkitVersion: version,
chat: isFromChat || false,
});
this._addListenTuiCallEngineEvent();
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { userId: userID });
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { userId: userID });
await this._tuiCallEngine.login({ userID, userSig, assetsPath: '' }); // web && mini
} catch (error) {
console.error(`${NAME.PREFIX}init failed, error: ${error}.`);
throw error;
}
}
// component destroy
public async destroyed() {
try {
const currentCallStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
if (currentCallStatus !== CallStatus.IDLE) {
throw new Error(`please destroyed when status is idle, current status: ${currentCallStatus}`);
}
if (this._tuiCallEngine) {
this._removeListenTuiCallEngineEvent();
await this._tuiCallEngine.destroyInstance();
this._tuiCallEngine = null;
}
} catch (error) {
console.error(`${NAME.PREFIX}destroyed failed, error: ${error}.`);
throw error;
}
}
// ===============================【通话操作】===============================
@avoidRepeatedCall()
@paramValidate(VALIDATE_PARAMS.call)
public async call(callParams: ICallParams) {
try {
const { type, userID, offlinePushInfo } = callParams;
if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) !== CallStatus.IDLE) return;
await this._updateCallStoreBeforeCall(type, [{ userId: userID }]);
this._executeExternalBeforeCalling(); // 执行外部传入的 beforeCall 方法
callParams.offlinePushInfo = { ...this._defaultOfflinePushInfo, ...offlinePushInfo };
const response = await this._tuiCallEngine.call(callParams);
await this._updateCallStoreAfterCall([userID], response);
} catch (error: any) {
this._handleCallError(error, 'call');
}
};
@avoidRepeatedCall()
@paramValidate(VALIDATE_PARAMS.groupCall)
public async groupCall(groupCallParams: IGroupCallParams) {
try {
const { userIDList, type, groupID, offlinePushInfo } = groupCallParams;
if (TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS) !== CallStatus.IDLE) return;
const remoteUserInfoList = userIDList.map(userId => ({ userId }));
await this._updateCallStoreBeforeCall(type, remoteUserInfoList, groupID);
this._executeExternalBeforeCalling();
groupCallParams.offlinePushInfo = { ...this._defaultOfflinePushInfo, ...offlinePushInfo };
const response = await this._tuiCallEngine.groupCall(groupCallParams);
await this._updateCallStoreAfterCall(userIDList, response);
} catch (error: any) {
this._handleCallError(error, 'groupCall');
}
}
@avoidRepeatedCall()
@paramValidate(VALIDATE_PARAMS.inviteUser)
public async inviteUser(params: IInviteUserParams) {
try {
const { userIDList } = params;
let inviteUserInfoList = await getRemoteUserProfile(userIDList, this.getTim(), TUIStore);
const remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, [...remoteUserInfoList, ...inviteUserInfoList]);
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, [...remoteUserInfoList, ...inviteUserInfoList]);
this._tuiCallEngine && await this._tuiCallEngine.inviteUser(params);
} catch (error: any) {
console.error(`${NAME.PREFIX}inviteUser failed, error: ${error}.`);
this._resetCallStore();
throw error;
}
}
@avoidRepeatedCall()
@paramValidate(VALIDATE_PARAMS.joinInGroupCall)
public async joinInGroupCall(params: IJoinInGroupCallParams) {
try {
const updateStoreParams = {
[NAME.CALL_ROLE]: CallRole.CALLEE,
[NAME.IS_GROUP]: true,
[NAME.CALL_STATUS]: CallStatus.CONNECTED,
[NAME.CALL_MEDIA_TYPE]: params.type,
[NAME.GROUP_ID]: params.groupID,
[NAME.ROOM_ID]: params.roomID,
};
TUIStore.updateStore(updateStoreParams, StoreName.CALL);
const response = await this._tuiCallEngine.joinInGroupCall(params);
(params.type === CallMediaType.VIDEO) && await this.openCamera(NAME.LOCAL_VIDEO);
TUIStore.update(StoreName.CALL, NAME.IS_CLICKABLE, true);
this._startTimer();
TUIStore.update(StoreName.CALL, NAME.PUSHER, response);
this.setSoundMode(params.type === CallMediaType.AUDIO ? AudioPlayBackDevice.EAR : AudioPlayBackDevice.SPEAKER);
const localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { ...localUserInfo, isEnter: true });
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { ...localUserInfo, isEnter: true });
this._setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO);
} catch (error) {
console.error(`${NAME.PREFIX}joinInGroupCall failed, error: ${error}.`);
this._resetCallStore();
throw error;
}
}
// ===============================【其它对外接口】===============================
public getTUICallEngineInstance(): any {
return this?._tuiCallEngine || null;
}
public setLogLevel(level: LOG_LEVEL) {
this?._tuiCallEngine?.setLogLevel(level);
}
@paramValidate(VALIDATE_PARAMS.setLanguage)
public setLanguage(language: LanguageType) {
if (language && Object.values(LanguageType).includes(language)) {
TUIStore.update(StoreName.CALL, NAME.LANGUAGE, language);
}
}
@paramValidate(VALIDATE_PARAMS.enableFloatWindow)
public enableFloatWindow(enable: boolean) {
TUIStore.update(StoreName.CALL, NAME.ENABLE_FLOAT_WINDOW, enable);
}
@paramValidate(VALIDATE_PARAMS.setSelfInfo)
public async setSelfInfo(params: ISelfInfoParams) {
const { nickName, avatar } = params;
try {
await this._tuiCallEngine.setSelfInfo(nickName, avatar);
} catch (error) {
console.error(`${NAME.PREFIX}setSelfInfo failed, error: ${error}.`);
}
}
// 修改默认铃声:只支持本地铃声文件,不支持在线铃声文件;修改铃声修改的是被叫的铃声
@paramValidate(VALIDATE_PARAMS.setCallingBell)
public async setCallingBell(filePath?: string) {
let isCheckFileExist: boolean = true;
if (!isCheckFileExist) {
console.warn(`${NAME.PREFIX}setCallingBell failed, filePath: ${filePath}.`);
return ;
}
const bellParams: IBellParams = { calleeBellFilePath: filePath };
this._bellContext.setBellProperties(bellParams);
}
@paramValidate(VALIDATE_PARAMS.enableMuteMode)
public async enableMuteMode(enable: boolean) {
try {
const bellParams: IBellParams = { isMuteBell: enable };
this._bellContext.setBellProperties(bellParams);
await this._bellContext.setBellMute(enable);
} catch (error) {
console.warn(`${NAME.PREFIX}enableMuteMode failed, error: ${error}.`);
}
}
// =============================【内部按钮操作方法】=============================
@avoidRepeatedCall()
public async accept() {
try {
const response = await this._tuiCallEngine.accept();
if (response) {
// 小程序接通时会进行授权弹框, 状态需要放在 accept 后, 否则先接通后再拉起权限设置
TUIStore.update(StoreName.CALL, NAME.CALL_STATUS, CallStatus.CONNECTED);
this._callTUIService({ message: response?.data?.message });
TUIStore.update(StoreName.CALL, NAME.IS_CLICKABLE, true);
this._startTimer();
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
(callMediaType === CallMediaType.VIDEO) && await this.openCamera(NAME.LOCAL_VIDEO);
response.pusher && TUIStore.update(StoreName.CALL, NAME.PUSHER, response.pusher);
this.setSoundMode(callMediaType === CallMediaType.AUDIO ? AudioPlayBackDevice.EAR : AudioPlayBackDevice.SPEAKER);
const localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { ...localUserInfo, isEnter: true });
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { ...localUserInfo, isEnter: true });
this._setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO); // web && mini default open audio
}
} catch (error) {
if (handleRepeatedCallError(error)) return;
this._noDevicePermissionToast(error, CallMediaType.AUDIO);
this._resetCallStore();
}
}
@avoidRepeatedCall()
public async hangup() {
try {
const response = await this._tuiCallEngine.hangup();
response?.forEach((item) => {
if (item?.code === 0) {
this._callTUIService({ message: item?.data?.message });
}
});
} catch (error) {
console.debug(error);
}
this._resetCallStore();
}
@avoidRepeatedCall()
public async reject() {
try {
const response = await this._tuiCallEngine.reject();
if (response?.code === 0) {
this._callTUIService({ message: response?.data?.message });
}
} catch (error) {
console.debug(error);
}
this._resetCallStore();
}
@avoidRepeatedCall()
public async openCamera(videoViewDomID: string) {
try {
await this._tuiCallEngine.openCamera();
this._setLocalUserInfoAudioVideoAvailable(true, NAME.VIDEO);
} catch (error: any) {
this._noDevicePermissionToast(error, CallMediaType.VIDEO);
console.error(`${NAME.PREFIX}openCamera error: ${error}.`);
}
}
@avoidRepeatedCall()
public async closeCamera() {
try {
await this._tuiCallEngine.closeCamera();
this._setLocalUserInfoAudioVideoAvailable(false, NAME.VIDEO);
} catch (error: any) {
console.error(`${NAME.PREFIX}closeCamera error: ${error}.`);
}
}
@avoidRepeatedCall()
public async openMicrophone() {
try {
await this._tuiCallEngine.openMicrophone();
this._setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO);
} catch (error: any) {
console.error(`${NAME.PREFIX}openMicrophone failed, error: ${error}.`);
}
}
@avoidRepeatedCall()
public async closeMicrophone() {
try {
await this._tuiCallEngine.closeMicrophone();
this._setLocalUserInfoAudioVideoAvailable(false, NAME.AUDIO);
} catch (error: any) {
console.error(`${NAME.PREFIX}closeMicrophone failed, error: ${error}.`);
}
}
@avoidRepeatedCall()
public switchScreen(userId: string) {
if(!userId) return;
TUIStore.update(StoreName.CALL, NAME.BIG_SCREEN_USER_ID, userId);
}
// support video to audio; not support audio to video
@avoidRepeatedCall()
public async switchCallMediaType() {
try {
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
if (callMediaType === CallMediaType.AUDIO) {
console.warn(`${NAME.PREFIX}switchCallMediaType failed, ${callMediaType} not support.`);
return;
}
const response = await this._tuiCallEngine.switchCallMediaType(CallMediaType.AUDIO);
if (response?.code === 0) {
this._callTUIService({ message: response?.data?.message });
}
TUIStore.update(StoreName.CALL, NAME.CALL_MEDIA_TYPE, CallMediaType.AUDIO);
const isGroup = TUIStore.getData(StoreName.CALL, NAME.IS_GROUP);
const oldStatus = isGroup ? StatusChange.CALLING_GROUP_VIDEO : StatusChange.CALLING_C2C_VIDEO;
const newStatus = generateStatusChangeText(TUIStore);
this.statusChanged && this.statusChanged({ oldStatus, newStatus });
this.setSoundMode(AudioPlayBackDevice.EAR);
} catch (error: any) {
console.error(`${NAME.PREFIX}switchCallMediaType failed, error: ${error}.`);
}
}
@avoidRepeatedCall()
public async switchCamera() {
const currentPosition = TUIStore.getData(StoreName.CALL, NAME.CAMERA_POSITION);
const targetPosition = currentPosition === CameraPosition.BACK ? CameraPosition.FRONT : CameraPosition.BACK;
try {
await this._tuiCallEngine.switchCamera(targetPosition);
TUIStore.update(StoreName.CALL, NAME.CAMERA_POSITION, targetPosition);
} catch (error) {
console.error(`${NAME.PREFIX}_switchCamera failed, error: ${error}.`);
}
}
@avoidRepeatedCall()
public setSoundMode(type?: string): void {
try {
let isEarPhone = TUIStore.getData(StoreName.CALL, NAME.IS_EAR_PHONE);
const soundMode = type || (isEarPhone ? AudioPlayBackDevice.SPEAKER : AudioPlayBackDevice.EAR); // UI 层切换时传参数
this._tuiCallEngine?.selectAudioPlaybackDevice(soundMode);
if (type) {
isEarPhone = type === AudioPlayBackDevice.EAR;
} else {
isEarPhone = !isEarPhone;
}
TUIStore.update(StoreName.CALL, NAME.IS_EAR_PHONE, isEarPhone);
} catch (error) {
console.error(`${NAME.PREFIX}setSoundMode failed, error: ${error}.`);
}
}
// 切前后置 miniProgram, 切扬声器
public getTim() {
if (this._tim) return this._tim;
if (!this._tuiCallEngine) {
console.warn(`${NAME.PREFIX}getTim warning: _tuiCallEngine Instance is not available.`);
return null;
}
return this._tuiCallEngine?.tim || this._tuiCallEngine?.getTim(); // mini support getTim interface
}
// ==========================【TUICallEngine 事件处理】==========================
private _addListenTuiCallEngineEvent() {
if (!this._tuiCallEngine) {
console.warn(`${NAME.PREFIX}add engine event listener failed, engine is empty.`);
return;
}
this._tuiCallEngine.on(TUICallEvent.ERROR, this._handleError, this);
this._tuiCallEngine.on(TUICallEvent.INVITED, this._handleNewInvitationReceived, this); // 收到邀请事件
this._tuiCallEngine.on(TUICallEvent.USER_ACCEPT, this._handleUserAccept, this); // 主叫收到被叫接通事件
this._tuiCallEngine.on(TUICallEvent.USER_ENTER, this._handleUserEnter, this); // 有用户进房事件
this._tuiCallEngine.on(TUICallEvent.USER_LEAVE, this._handleUserLeave, this); // 有用户离开通话事件
this._tuiCallEngine.on(TUICallEvent.REJECT, this._handleInviteeReject, this); // 主叫收到被叫的拒绝通话事件
this._tuiCallEngine.on(TUICallEvent.NO_RESP, this._handleNoResponse, this); // 主叫收到被叫的无应答事件
this._tuiCallEngine.on(TUICallEvent.LINE_BUSY, this._handleLineBusy, this); // 主叫收到被叫的忙线事件
this._tuiCallEngine.on(TUICallEvent.CALLING_CANCEL, this._handleCallingCancel, this); // 主被叫在通话未建立时, 收到的取消事件
this._tuiCallEngine.on(TUICallEvent.SDK_READY, this._handleSDKReady, this); // SDK Ready 回调
this._tuiCallEngine.on(TUICallEvent.KICKED_OUT, this._handleKickedOut, this); // 未开启多端登录时, 多端登录收到的被踢事件
this._tuiCallEngine.on(TUICallEvent.MESSAGE_SENT_BY_ME, this._messageSentByMe, this);
this._tuiCallEngine.on(TUICallEvent.CALL_END, this._handleCallingEnd, this); // 主被叫在通话结束时, 收到的通话结束事件
// @ts-ignore
this._tuiCallEngine.on(TUICallEvent.CALL_MODE, this._handleCallTypeChange, this);
// @ts-ignore
this._tuiCallEngine.on(TUICallEvent.USER_UPDATE, this._handleUserUpdate, this); // mini: user data update
}
private _removeListenTuiCallEngineEvent() {
this._tuiCallEngine.off(TUICallEvent.ERROR, this._handleError);
this._tuiCallEngine.off(TUICallEvent.INVITED, this._handleNewInvitationReceived);
this._tuiCallEngine.off(TUICallEvent.USER_ACCEPT, this._handleUserAccept);
this._tuiCallEngine.off(TUICallEvent.USER_ENTER, this._handleUserEnter);
this._tuiCallEngine.off(TUICallEvent.USER_LEAVE, this._handleUserLeave);
this._tuiCallEngine.off(TUICallEvent.REJECT, this._handleInviteeReject);
this._tuiCallEngine.off(TUICallEvent.NO_RESP, this._handleNoResponse);
this._tuiCallEngine.off(TUICallEvent.LINE_BUSY, this._handleLineBusy);
this._tuiCallEngine.off(TUICallEvent.CALLING_CANCEL, this._handleCallingCancel);
this._tuiCallEngine.off(TUICallEvent.SDK_READY, this._handleSDKReady);
this._tuiCallEngine.off(TUICallEvent.KICKED_OUT, this._handleKickedOut);
this._tuiCallEngine.off(TUICallEvent.MESSAGE_SENT_BY_ME, this._messageSentByMe);
this._tuiCallEngine.off(TUICallEvent.CALL_END, this._handleCallingEnd);
// @ts-ignore
this._tuiCallEngine.off(TUICallEvent.CALL_MODE, this._handleCallTypeChange); // 切换通话事件 miniProgram CALL_MODE
// @ts-ignore
this._tuiCallEngine.off(TUICallEvent.USER_UPDATE, this._handleUserUpdate); // mini: user data update
}
private _handleError(event: any): void {
const { code, message } = event || {};
const index = Object.values(ErrorCode).indexOf(code);
let callTips = '';
if (index !== -1) {
const key = Object.keys(ErrorCode)[index];
callTips = t(ErrorMessage[key]);
callTips && TUIStore.update(StoreName.CALL, NAME.TOAST_INFO, { text: callTips, type: NAME.ERROR });
}
this._executeExternalAfterCalling();
console.error(`${NAME.PREFIX}_handleError, errorCode: ${code}; errorMessage: ${callTips || message}.`);
}
private async _handleNewInvitationReceived(event: any) {
console.log(`${NAME.PREFIX}onCallReceived event data: ${JSON.stringify(event)}.`);
const { sponsor = '', isFromGroup, callMediaType, inviteData = {}, calleeIdList = [], groupID = '' } = this._analyzeEventData(event);
const currentUserInfo: IUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
const remoteUserIdList: string[] = [sponsor, ...calleeIdList.filter((userId: string) => userId !== currentUserInfo.userId)];
const type = callMediaType || inviteData.callType;
const callTipsKey = type === CallMediaType.AUDIO ? CallTips.CALLEE_CALLING_AUDIO_MSG : CallTips.CALLEE_CALLING_VIDEO_MSG;
let updateStoreParams = {
[NAME.CALL_ROLE]: CallRole.CALLEE,
[NAME.IS_GROUP]: isFromGroup,
[NAME.CALL_STATUS]: CallStatus.CALLING,
[NAME.CALL_MEDIA_TYPE]: type,
[NAME.CALL_TIPS]: t(callTipsKey),
[NAME.CALLER_USER_INFO]: { userId: sponsor },
[NAME.GROUP_ID]: groupID,
};
initAndCheckRunEnv();
const pusher = { enableCamera: type === CallMediaType.VIDEO, enableMic: true }; // mini 默认打开麦克风
updateStoreParams = { ...updateStoreParams, [NAME.PUSHER]: pusher };
TUIStore.updateStore(updateStoreParams, StoreName.CALL);
this._executeExternalBeforeCalling();
this.statusChanged && this.statusChanged({ oldStatus: StatusChange.IDLE, newStatus: StatusChange.BE_INVITED });
const remoteUserInfoList = await getRemoteUserProfile(remoteUserIdList, this.getTim(), TUIStore);
const [userInfo] = remoteUserInfoList.filter((userInfo: IUserInfo) => userInfo.userId === sponsor);
remoteUserInfoList.length > 0 && TUIStore.updateStore({
[NAME.REMOTE_USER_INFO_LIST]: remoteUserInfoList,
[NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST]: remoteUserInfoList,
[NAME.CALLER_USER_INFO]: {
userId: sponsor,
nick: userInfo?.nick || '',
avatar: userInfo?.avatar || '',
displayUserInfo: userInfo?.remark || userInfo?.nick || sponsor,
},
}, StoreName.CALL);
}
private _handleUserAccept(event: any): void {
this._callerChangeToConnected();
TUIStore.update(StoreName.CALL, NAME.TOAST_INFO, t('answered'));
console.log(`${NAME.PREFIX}accept event data: ${JSON.stringify(event)}.`);
}
private async _handleUserEnter(event: any): Promise<void> {
this._callerChangeToConnected();
const { userID: userId, data } = this._analyzeEventData(event);
data?.playerList && TUIStore.update(StoreName.CALL, NAME.PLAYER, data.playerList);
let remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
const isInRemoteUserList = remoteUserInfoList.find(item => item?.userId === userId);
if (!isInRemoteUserList) {
remoteUserInfoList.push({ userId });
if (remoteUserInfoList.length > 0) {
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, remoteUserInfoList);
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, remoteUserInfoList);
}
const [userInfo] = await getRemoteUserProfile([userId], this.getTim(), TUIStore);
remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
remoteUserInfoList.forEach((obj) => {
if (obj?.userId === userId) {
obj = Object.assign(obj, userInfo);
}
});
}
remoteUserInfoList = remoteUserInfoList.map((obj: IUserInfo) => {
if (obj.userId === userId) obj.isEnter = true;
return obj;
});
if (remoteUserInfoList.length > 0) {
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, remoteUserInfoList);
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, remoteUserInfoList);
}
console.log(`${NAME.PREFIX}userEnter event data: ${JSON.stringify(event)}.`);
}
private _callerChangeToConnected() {
const callRole = TUIStore.getData(StoreName.CALL, NAME.CALL_ROLE);
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
if (callStatus === CallStatus.CALLING && callRole === CallRole.CALLER) {
TUIStore.update(StoreName.CALL, NAME.CALL_STATUS, CallStatus.CONNECTED);
this._startTimer();
}
}
private _handleUserLeave(event: any): void {
console.log(`${NAME.PREFIX}userLeave event data: ${JSON.stringify(event)}.`);
const { data, userID: userId } = this._analyzeEventData(event);
data?.playerList && TUIStore.update(StoreName.CALL, NAME.PLAYER, data.playerList);
if (TUIStore.getData(StoreName.CALL, NAME.IS_GROUP)) {
const remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
const prefix: string = (remoteUserInfoList.find(obj => obj.userId === userId) || {}).displayUserInfo || userId;
const text = generateText(TUIStore, CallTips.END_CALL, prefix);
TUIStore.update(StoreName.CALL, NAME.TOAST_INFO, { text });
}
userId && this._deleteRemoteUser([userId]);
}
private _unNormalEventsManager(event: any, eventName: TUICallEvent): void {
console.log(`${NAME.PREFIX}${eventName} event data: ${JSON.stringify(event)}.`);
const isGroup = TUIStore.getData(StoreName.CALL, NAME.IS_GROUP);
const remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
switch (eventName) {
case TUICallEvent.REJECT:
case TUICallEvent.LINE_BUSY: {
const { userID: userId } = this._analyzeEventData(event);
let callTipsKey = eventName === TUICallEvent.REJECT ? CallTips.OTHER_SIDE_REJECT_CALL : CallTips.OTHER_SIDE_LINE_BUSY;
let text = generateText(TUIStore, callTipsKey);
if (isGroup) {
const prefix: string = (remoteUserInfoList.find(obj => obj.userId === userId) || {}).displayUserInfo || userId;
callTipsKey = eventName === TUICallEvent.REJECT ? CallTips.REJECT_CALL : CallTips.IN_BUSY;
text = generateText(TUIStore, callTipsKey, prefix);
}
TUIStore.update(StoreName.CALL, NAME.TOAST_INFO, { text });
userId && this._deleteRemoteUser([userId]);
break;
}
case TUICallEvent.NO_RESP: {
const { userIDList = [] } = this._analyzeEventData(event);
const callTipsKey = isGroup ? CallTips.TIMEOUT : CallTips.CALL_TIMEOUT;
const userInfoList: string[] = userIDList.map(userId => {
const userInfo: IUserInfo = remoteUserInfoList.find(obj => obj.userId === userId) || {};
return userInfo.displayUserInfo || userId;
});
const text = isGroup ? generateText(TUIStore, callTipsKey, userInfoList.join()) : generateText(TUIStore, callTipsKey);
TUIStore.update(StoreName.CALL, NAME.TOAST_INFO, { text });
userIDList.length > 0 && this._deleteRemoteUser(userIDList);
break;
}
case TUICallEvent.CALLING_CANCEL: {
// TUIStore.update(StoreName.CALL, NAME.TOAST_INFO, { text: generateText(TUIStore, CallTips.CANCEL) });
this._resetCallStore();
break;
}
}
}
private _handleInviteeReject(event: any): void {
this._unNormalEventsManager(event, TUICallEvent.REJECT);
}
private _handleNoResponse(event: any): void {
this._unNormalEventsManager(event, TUICallEvent.NO_RESP);
}
private _handleLineBusy(event: any): void {
this._unNormalEventsManager(event, TUICallEvent.LINE_BUSY);
}
private _handleCallingCancel(event: any): void {
this._executeExternalAfterCalling();
this._unNormalEventsManager(event, TUICallEvent.CALLING_CANCEL);
}
private _handleCallingEnd(event: any): void {
console.log(`${NAME.PREFIX}callEnd event data: ${JSON.stringify(event)}.`);
this._executeExternalAfterCalling();
this._resetCallStore();
}
// SDK_READY 后才能调用 tim 接口, 否则登录后立刻获取导致调用接口失败. v2.27.4+、v3 接口 login 后会抛出 SDK_READY
private async _handleSDKReady(event: any): Promise<void> {
let localUserInfo: IUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
localUserInfo = await getMyProfile(localUserInfo.userId, this.getTim(), TUIStore);
this._defaultOfflinePushInfo.title = localUserInfo?.displayUserInfo;
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, localUserInfo);
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, localUserInfo);
}
private _handleKickedOut(event: any): void {
console.log(`${NAME.PREFIX}kickOut event data: ${JSON.stringify(event)}.`);
this.kickedOut && this.kickedOut(event);
TUIStore.update(StoreName.CALL, NAME.CALL_TIPS, generateText(TUIStore, CallTips.KICK_OUT));
this._resetCallStore();
}
private _handleCallTypeChange(event: any): void {
const { newCallType, type } = this._analyzeEventData(event);
TUIStore.update(StoreName.CALL, NAME.CALL_MEDIA_TYPE, newCallType || type);
this.setSoundMode(AudioPlayBackDevice.EAR);
}
private _messageSentByMe(event: any): void {
const message = event?.data;
this.onMessageSentByMe && this.onMessageSentByMe(message);
}
// ==========================【 miniProgram 私有事件】==========================
private _handleUserUpdate(event: any): void {
const data = this._analyzeEventData(event);
data?.pusher && TUIStore.update(StoreName.CALL, NAME.PUSHER, data.pusher);
data?.playerList && TUIStore.update(StoreName.CALL, NAME.PLAYER, data.playerList);
}
// 处理 “呼叫” 抛出的异常
private _handleCallError(error: any, methodName?: string) {
if (handleRepeatedCallError(error)) return;
handlePackageError(error); // 无套餐提示, 小程序 engine 不抛出 onError
this._noDevicePermissionToast(error, CallMediaType.AUDIO);
console.error(`${NAME.PREFIX}${methodName} failed, error: ${error}.`);
this._resetCallStore();
throw error;
}
// ========================【原 Web CallKit 提供的方法】========================
public beforeCalling: ((...args: any[]) => void) | undefined; // 原来
public afterCalling: ((...args: any[]) => void) | undefined;
public onMinimized: ((...args: any[]) => void) | undefined;
public onMessageSentByMe: ((...args: any[]) => void) | undefined;
public kickedOut: ((...args: any[]) => void) | undefined;
public statusChanged: ((...args: any[]) => void) | undefined;
public setCallback(params: ICallbackParam) {
const { beforeCalling, afterCalling, onMinimized, onMessageSentByMe, kickedOut, statusChanged } = params;
beforeCalling && (this.beforeCalling = beforeCalling);
afterCalling && (this.afterCalling = afterCalling);
onMinimized && (this.onMinimized = onMinimized);
onMessageSentByMe && (this.onMessageSentByMe = onMessageSentByMe);
kickedOut && (this.kickedOut = kickedOut);
statusChanged && (this.statusChanged = statusChanged);
}
public toggleMinimize() {
const isMinimized = TUIStore.getData(StoreName.CALL, NAME.IS_MINIMIZED);
TUIStore.update(StoreName.CALL, NAME.IS_MINIMIZED, !isMinimized);
console.log(`${NAME.PREFIX}toggleMinimize: ${isMinimized} -> ${!isMinimized}.`);
this.onMinimized && this.onMinimized(isMinimized, !isMinimized);
}
private _executeExternalBeforeCalling(): void {
this.beforeCalling && this.beforeCalling();
}
private _executeExternalAfterCalling(): void {
this.afterCalling && this.afterCalling();
}
// ========================【TUICallKit 组件属性设置方法】========================
@paramValidate(VALIDATE_PARAMS.setVideoDisplayMode)
public setVideoDisplayMode(displayMode: VideoDisplayMode) {
TUIStore.update(StoreName.CALL, NAME.DISPLAY_MODE, displayMode);
}
@paramValidate(VALIDATE_PARAMS.setVideoResolution)
public async setVideoResolution(resolution: VideoResolution) {
try {
if (!resolution) return;
TUIStore.update(StoreName.CALL, NAME.VIDEO_RESOLUTION, resolution);
await this._tuiCallEngine.setVideoQuality(resolution);
} catch (error) {
console.warn(`${NAME.PREFIX}setVideoResolution failed, error: ${error}.`);
}
}
// =========================【 miniProgram 私有公共方法】=========================
// 处理用户异常退出的情况,处理了右滑退出,以及返回退出的情况。
private async _handleExceptionExit() {
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
const callRole = TUIStore.getData(StoreName.CALL, NAME.CALL_ROLE);
// 在呼叫状态下, 被叫调用 reject主叫调用 hangup
if (callStatus === CallStatus.CALLING) {
callRole === CallRole.CALLER && await this.hangup();
callRole === CallRole.CALLEE && await this.reject();
}
// 在通话状态下, 统一调用 hangup 接口
callStatus === CallStatus.CONNECTED && await this.hangup();
}
private _setLocalUserInfoAudioVideoAvailable(isAvailable: boolean, type: string) {
let localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
if (type === NAME.AUDIO) {
localUserInfo = { ...localUserInfo, isAudioAvailable: isAvailable };
}
if (type === NAME.VIDEO) {
localUserInfo = { ...localUserInfo, isVideoAvailable: isAvailable };
}
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, localUserInfo);
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, localUserInfo);
}
private async _updateCallStoreBeforeCall(type: number, remoteUserInfoList: IUserInfo[], groupID?: string): Promise<void> {
const callTips = groupID || TUIStore.getData(StoreName.CALL, NAME.IS_MINIMIZED) ? CallTips.CALLER_GROUP_CALLING_MSG : CallTips.CALLER_CALLING_MSG;
let updateStoreParams: any = {
[NAME.CALL_MEDIA_TYPE]: type,
[NAME.CALL_ROLE]: CallRole.CALLER,
[NAME.REMOTE_USER_INFO_LIST]: remoteUserInfoList,
[NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST]: remoteUserInfoList,
[NAME.IS_GROUP]: !!groupID,
[NAME.CALL_TIPS]: t(callTips),
[NAME.GROUP_ID]: groupID
};
const pusher = { enableCamera: type === CallMediaType.VIDEO, enableMic: true }; // mini 默认打开麦克风
updateStoreParams = { ...updateStoreParams, [NAME.PUSHER]: pusher };
TUIStore.updateStore(updateStoreParams, StoreName.CALL);
const callStatus = await beforeCall(type, this); // 如果没有权限, 此时为 false. 因此需要在 call 后设置为 calling. 和 web 存在差异
console.log(`${NAME.PREFIX}mini beforeCall return callStatus: ${callStatus}.`);
TUIStore.update(StoreName.CALL, NAME.CALL_STATUS, callStatus);
const remoteUserInfoLists = await getRemoteUserProfile(remoteUserInfoList.map(obj => obj.userId), this.getTim(), TUIStore);
if (remoteUserInfoLists.length > 0) {
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, remoteUserInfoLists);
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, remoteUserInfoLists);
}
}
private async _updateCallStoreAfterCall(userIdList: string[], response: any) {
if (response) {
TUIStore.update(StoreName.CALL, NAME.IS_CLICKABLE, true);
TUIStore.update(StoreName.CALL, NAME.ROOM_ID, response.roomID);
this._callTUIService({ message: response?.data?.message });
response.pusher && TUIStore.update(StoreName.CALL, NAME.PUSHER, response.pusher);
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
(callMediaType === CallMediaType.VIDEO) && await this.openCamera(NAME.LOCAL_VIDEO);
this.setSoundMode(callMediaType === CallMediaType.AUDIO ? AudioPlayBackDevice.EAR : AudioPlayBackDevice.SPEAKER);
TUIStore.update(StoreName.CALL, NAME.CALL_STATUS, CallStatus.CALLING); // 小程序未授权时, 此时状态为 idle; web 直接设置为 calling
const localUserInfo = TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO);
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, { ...localUserInfo, isEnter: true });
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, { ...localUserInfo, isEnter: true });
this._setLocalUserInfoAudioVideoAvailable(true, NAME.AUDIO); // web && mini, default open audio
} else {
this._resetCallStore();
}
}
private _resetCurrentDevice() {
// 挂断后,重置当前摄像头,麦克风和扬声器为默认设备
const { cameraList, microphoneList, speakerList } = TUIStore.getData(StoreName.CALL, NAME.DEVICE_LIST);
TUIStore.update(
StoreName.CALL,
NAME.DEVICE_LIST,
{ microphoneList, cameraList, speakerList,
currentCamera: cameraList?.[0] || {},
currentMicrophone: microphoneList?.[0] || {},
currentSpeaker: speakerList?.[0] || {}
},
);
}
private _resetCallStore() {
const oldStatusStr = generateStatusChangeText(TUIStore);
this._stopTimer();
// localUserInfo, language 在通话结束后不需要清除
// callStatus 清除需要通知; isMinimized 也需要通知basic-vue3 中切小窗关闭后, 再呼叫还是小窗, 因此需要通知到组件侧)
// isGroup 也不清除(engine 先抛 cancel 事件, 再抛 reject 事件)
// displayMode、videoResolution 也不能清除, 组件不卸载, 这些属性也需保留, 否则采用默认值.
// enableFloatWindow 不清除:开启/关闭悬浮窗功能。
let notResetOrNotifyKeys = Object.keys(CALL_DATA_KEY).filter((key) => {
switch (CALL_DATA_KEY[key]) {
case NAME.CALL_STATUS:
case NAME.LANGUAGE:
case NAME.IS_GROUP:
case NAME.DISPLAY_MODE:
case NAME.VIDEO_RESOLUTION:
case NAME.ENABLE_FLOAT_WINDOW:
case NAME.LOCAL_USER_INFO:
case NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN: {
return false;
}
default: {
return true;
}
}
});
notResetOrNotifyKeys = notResetOrNotifyKeys.map(key => CALL_DATA_KEY[key]);
TUIStore.reset(StoreName.CALL, notResetOrNotifyKeys);
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
callStatus !== CallStatus.IDLE && TUIStore.reset(StoreName.CALL, [NAME.CALL_STATUS], true); // callStatus reset need notify
TUIStore.reset(StoreName.CALL, [NAME.IS_MINIMIZED], true); // isMinimized reset need notify
TUIStore.reset(StoreName.CALL, [NAME.IS_EAR_PHONE], true); // isEarPhone reset need notify
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO, {
...TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO),
isVideoAvailable: false,
isAudioAvailable: false,
});
TUIStore.update(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN, {
...TUIStore.getData(StoreName.CALL, NAME.LOCAL_USER_INFO_EXCLUDE_VOLUMN),
isVideoAvailable: false,
isAudioAvailable: false,
});
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, []);
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, []);
this._resetCurrentDevice();
const newStatusStr = generateStatusChangeText(TUIStore);
if (oldStatusStr !== newStatusStr) {
this.statusChanged && this.statusChanged({ oldStatus: oldStatusStr, newStatus: newStatusStr });
}
}
private _noDevicePermissionToast(error: any, type: CallMediaType): void {
if (handleNoDevicePermissionError(error)) {
let text = '';
if (type === CallMediaType.AUDIO) {
text = generateText(TUIStore, CallTips.NO_MICROPHONE_DEVICE_PERMISSION);
}
if (type === CallMediaType.VIDEO) {
text = generateText(TUIStore, CallTips.NO_CAMERA_DEVICE_PERMISSION);
}
text && TUIStore.update(StoreName.CALL, NAME.TOAST_INFO, { text, type: NAME.ERROR });
console.error(`${NAME.PREFIX}call failed, error: ${error.message}.`);
}
}
// 通话时长更新
private _startTimer(): void {
if (this._timerId === -1) {
this._startTimeStamp = performanceNow();
this._timerId = timer.run(NAME.TIMEOUT, this._updateCallDuration.bind(this), { delay: 1000 });
}
}
private _updateCallDuration(): void {
const callDurationNum = Math.round((performanceNow() - this._startTimeStamp) / 1000); // miniProgram stop timer when background
const callDurationStr = formatTime(callDurationNum);
TUIStore.update(StoreName.CALL, NAME.CALL_DURATION, callDurationStr);
}
private _stopTimer(): void {
if (this._timerId !== -1) {
timer.clearTask(this._timerId);
this._timerId = -1;
}
}
private _deleteRemoteUser(userIdList: string[]): void {
if (userIdList.length === 0) return;
let remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
userIdList.forEach((userId) => {
remoteUserInfoList = remoteUserInfoList.filter((obj: IUserInfo) => obj.userId !== userId);
});
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST, remoteUserInfoList);
TUIStore.update(StoreName.CALL, NAME.REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST, remoteUserInfoList);
}
private _analyzeEventData(event: any): any {
return event?.data || {}; // mini INVITED
}
// =========================【调用 chat api】=========================
// 获取群成员
public async getGroupMemberList(count: number, offset: number) {
const groupID = TUIStore.getData(StoreName.CALL, NAME.GROUP_ID);
let groupMemberList = await getGroupMemberList(groupID, this.getTim(), count, offset);
return groupMemberList;
}
// 获取群信息
public async getGroupProfile() {
const groupID: string = TUIStore.getData(StoreName.CALL, NAME.GROUP_ID);
return await getGroupProfile(groupID, this.getTim());
}
// =========================【监听 TUIStore 中的状态】=========================
private _handleCallStatusChange = async (value: CallStatus) => {
try {
const bellParams: IBellParams = {
callRole: TUIStore.getData(StoreName.CALL, NAME.CALL_ROLE),
callStatus: TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS),
};
this._bellContext.setBellProperties(bellParams);
if (value === CallStatus.CALLING) {
await this?._bellContext?.play();
} else {
// 状态变更通知
if (value === CallStatus.CONNECTED) {
const isGroup = TUIStore.getData(StoreName.CALL, NAME.IS_GROUP);
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
const remoteUserInfoList = TUIStore.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST);
const oldStatus = isGroup ? StatusChange.DIALING_GROUP : StatusChange.DIALING_C2C;
TUIStore.update(StoreName.CALL, NAME.CALL_TIPS, '');
this.statusChanged && this.statusChanged({ oldStatus, newStatus: generateStatusChangeText(TUIStore) });
if (!isGroup && callMediaType === CallMediaType.VIDEO) {
this.switchScreen(remoteUserInfoList[0].domId);
}
}
await this?._bellContext?.stop();
}
} catch (error) {
console.warn(`${NAME.PREFIX}handleCallStatusChange, ${error}.`);
}
};
private _watchTUIStore() {
TUIStore?.watch(StoreName.CALL, {
[NAME.CALL_STATUS]: this._handleCallStatusChange,
});
}
private _unwatchTUIStore() {
TUIStore?.unwatch(StoreName.CALL, {
[NAME.CALL_STATUS]: this._handleCallStatusChange,
});
}
// =========================【web 融合 chat 提供的方法】=========================
public bindTUICore(TUICore: any) {
this._TUICore = TUICore;
}
// 下面方法用于接入 TUICore
private _callTUIService(params) {
const { message } = params || {};
TUICore.callService({
serviceName: TUIConstants.TUIChat.SERVICE.NAME,
method: TUIConstants.TUIChat.SERVICE.METHOD.UPDATE_MESSAGE_LIST,
params: { message },
});
}
public async onNotifyEvent(eventName: string, subKey: string) {
try {
if (eventName === TUIConstants.TUILogin.EVENT.LOGIN_STATE_CHANGED) {
if (subKey === TUIConstants.TUILogin.EVENT_SUB_KEY.USER_LOGIN_SUCCESS) {
// TUICallkit 收到登录成功时执行自己的业务逻辑处理
// @ts-ignore
const { chat, userID, userSig, SDKAppID } = TUILogin.getContext();
await this.init({ tim: chat, userID, userSig, sdkAppID: SDKAppID, isFromChat: true });
} else if (subKey === TUIConstants.TUILogin.EVENT_SUB_KEY.USER_LOGOUT_SUCCESS) {
await this.destroyed();
}
}
} catch (error) {
console.error(`${NAME.PREFIX}TUICore onNotifyEvent failed, error: ${error}.`);
}
}
public async onCall(method: String, params: any) {
if (method === TUIConstants.TUICalling.SERVICE.METHOD.START_CALL) {
await this._handleTUICoreOnClick(params, params.type);
}
}
private async _handleTUICoreOnClick(options, type: CallMediaType) {
try {
const { groupID, userIDList = [], ...rest } = options;
if (groupID) {
await this.groupCall({ groupID, userIDList, type, ...rest });
} else if (userIDList.length === 1) {
await this.call({ userID: userIDList[0], type, ...rest });
}
} catch (error: any) {
console.debug(error);
}
}
public onGetExtension(extensionID: string, params: any) {
if (extensionID === TUIConstants.TUIChat.EXTENSION.INPUT_MORE.EXT_ID) {
const list = [];
const audioCallExtension: ExtensionInfo = {
weight: 1000,
text: '语音通话',
icon: AudioCallIcon,
data: {
name: 'voiceCall',
},
listener: {
onClicked: async options => await this._handleTUICoreOnClick(options, options.type || CallMediaType.AUDIO), // 点击时发起通话
},
};
const videoCallExtension: ExtensionInfo = {
weight: 900,
text: '视频通话',
icon: VideoCallIcon,
data: {
name: 'videoCall',
},
listener: {
onClicked: async options => await this._handleTUICoreOnClick(options, options.type || CallMediaType.VIDEO), // 点击时发起通话
},
};
if (!params?.filterVoice) {
list.push(audioCallExtension);
}
if (!params?.filterVideo) {
list.push(videoCallExtension);
}
return list;
}
}
}

View File

@ -0,0 +1,6 @@
import { CallMediaType, CallStatus } from '../const/index';
export declare function initialUI(): void;
export declare function checkRunPlatform(): void;
export declare function initAndCheckRunEnv(): void;
export declare function beforeCall(type: CallMediaType, that: any): Promise<CallStatus.IDLE | CallStatus.CALLING>;
export declare function handlePackageError(error: any): void;

View File

@ -0,0 +1,76 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.handlePackageError = exports.beforeCall = exports.initAndCheckRunEnv = exports.checkRunPlatform = exports.initialUI = void 0;
const index_1 = require("../const/index");
function initialUI() {
// 收起键盘
// @ts-ignore
wx.hideKeyboard({
complete: () => { },
});
}
exports.initialUI = initialUI;
;
// 检测运行时环境, 当是微信开发者工具时, 提示用户需要手机调试
function checkRunPlatform() {
// @ts-ignore
const systemInfo = wx.getSystemInfoSync();
if (systemInfo.platform === 'devtools') {
// 当前运行在微信开发者工具里
// @ts-ignore
wx.showModal({
icon: 'none',
title: '运行环境提醒',
content: '微信开发者工具不支持原生推拉流组件(即 <live-pusher> 和 <live-player> 标签),请使用真机调试或者扫码预览。',
showCancel: false,
});
}
}
exports.checkRunPlatform = checkRunPlatform;
;
function initAndCheckRunEnv() {
initialUI(); // miniProgram 收起键盘, 隐藏 tabBar
checkRunPlatform(); // miniProgram 检测运行时环境
}
exports.initAndCheckRunEnv = initAndCheckRunEnv;
function beforeCall(type, that) {
return __awaiter(this, void 0, void 0, function* () {
try {
initAndCheckRunEnv();
// 检查设备权限
const deviceMap = {
microphone: true,
camera: type === index_1.CallMediaType.VIDEO,
};
const hasDevicePermission = yield that._tuiCallEngine.deviceCheck(deviceMap); // miniProgram 检查设备权限
return hasDevicePermission ? index_1.CallStatus.CALLING : index_1.CallStatus.IDLE;
}
catch (error) {
console.debug(error);
return index_1.CallStatus.IDLE;
}
});
}
exports.beforeCall = beforeCall;
// 套餐问题提示, 小程序最低需要群组通话版, 1v1 通话版本使用 TRTC 就会报错
function handlePackageError(error) {
if ((error === null || error === void 0 ? void 0 : error.code) === -1002) {
// @ts-ignore
wx.showModal({
icon: 'none',
title: 'error',
content: (error === null || error === void 0 ? void 0 : error.message) || '',
showCancel: false,
});
}
}
exports.handlePackageError = handlePackageError;

View File

@ -0,0 +1,55 @@
import { CallMediaType, CallStatus } from '../const/index';
export function initialUI() {
// 收起键盘
// @ts-ignore
wx.hideKeyboard({
complete: () => {},
});
};
// 检测运行时环境, 当是微信开发者工具时, 提示用户需要手机调试
export function checkRunPlatform() {
// @ts-ignore
const systemInfo = wx.getSystemInfoSync();
if (systemInfo.platform === 'devtools') {
// 当前运行在微信开发者工具里
// @ts-ignore
wx.showModal({
icon: 'none',
title: '运行环境提醒',
content: '微信开发者工具不支持原生推拉流组件(即 <live-pusher> 和 <live-player> 标签),请使用真机调试或者扫码预览。',
showCancel: false,
});
}
};
export function initAndCheckRunEnv() {
initialUI(); // miniProgram 收起键盘, 隐藏 tabBar
checkRunPlatform(); // miniProgram 检测运行时环境
}
export async function beforeCall(type: CallMediaType, that: any) {
try {
initAndCheckRunEnv();
// 检查设备权限
const deviceMap = {
microphone: true,
camera: type === CallMediaType.VIDEO,
};
const hasDevicePermission = await that._tuiCallEngine.deviceCheck(deviceMap); // miniProgram 检查设备权限
return hasDevicePermission ? CallStatus.CALLING : CallStatus.IDLE;
} catch (error) {
console.debug(error);
return CallStatus.IDLE;
}
}
// 套餐问题提示, 小程序最低需要群组通话版, 1v1 通话版本使用 TRTC 就会报错
export function handlePackageError(error) {
if (error?.code === -1002) {
// @ts-ignore
wx.showModal({
icon: 'none',
title: 'error',
content: error?.message || '',
showCancel: false,
});
}
}

View File

@ -0,0 +1,9 @@
import { IUserInfo } from '../interface/ICallService';
import { ITUIStore } from '../interface/ITUIStore';
export declare function setDefaultUserInfo(userId: string, domId?: string): IUserInfo;
export declare function getMyProfile(myselfUserId: string, tim: any, TUIStore: any): Promise<IUserInfo>;
export declare function getRemoteUserProfile(userIdList: Array<string>, tim: any, TUIStore: any): Promise<any>;
export declare function generateText(TUIStore: ITUIStore, key: string, prefix?: string, suffix?: string): string;
export declare function generateStatusChangeText(TUIStore: ITUIStore): string;
export declare function getGroupMemberList(groupID: string, tim: any, count: any, offset: any): Promise<any>;
export declare function getGroupProfile(groupID: string, tim: any): Promise<any>;

View File

@ -0,0 +1,167 @@
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.getGroupProfile = exports.getGroupMemberList = exports.generateStatusChangeText = exports.generateText = exports.getRemoteUserProfile = exports.getMyProfile = exports.setDefaultUserInfo = void 0;
const index_1 = require("../const/index");
const index_2 = require("../locales/index");
// 设置默认的 UserInfo 信息
function setDefaultUserInfo(userId, domId) {
const userInfo = {
userId,
nick: '',
avatar: '',
remark: '',
displayUserInfo: '',
isAudioAvailable: false,
isVideoAvailable: false,
isEnter: false,
domId: domId || userId,
};
return domId ? userInfo : Object.assign(Object.assign({}, userInfo), { isEnter: false }); // localUserInfo 没有 isEnter, remoteUserInfoList 有 isEnter
}
exports.setDefaultUserInfo = setDefaultUserInfo;
// 获取个人用户信息
function getMyProfile(myselfUserId, tim, TUIStore) {
var _a, _b, _c, _d, _e;
return __awaiter(this, void 0, void 0, function* () {
let localUserInfo = setDefaultUserInfo(myselfUserId, index_1.NAME.LOCAL_VIDEO);
try {
if (!tim)
return localUserInfo;
const res = yield tim.getMyProfile();
const currentLocalUserInfo = TUIStore === null || TUIStore === void 0 ? void 0 : TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.LOCAL_USER_INFO); // localUserInfo may have been updated
if ((res === null || res === void 0 ? void 0 : res.code) === 0) {
localUserInfo = Object.assign(Object.assign(Object.assign({}, localUserInfo), currentLocalUserInfo), { userId: (_a = res === null || res === void 0 ? void 0 : res.data) === null || _a === void 0 ? void 0 : _a.userID, nick: (_b = res === null || res === void 0 ? void 0 : res.data) === null || _b === void 0 ? void 0 : _b.nick, avatar: (_c = res === null || res === void 0 ? void 0 : res.data) === null || _c === void 0 ? void 0 : _c.avatar, displayUserInfo: ((_d = res === null || res === void 0 ? void 0 : res.data) === null || _d === void 0 ? void 0 : _d.nick) || ((_e = res === null || res === void 0 ? void 0 : res.data) === null || _e === void 0 ? void 0 : _e.userID) });
}
return localUserInfo;
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getMyProfile failed, error: ${error}.`);
return localUserInfo;
}
});
}
exports.getMyProfile = getMyProfile;
// 获取远端用户列表信息
function getRemoteUserProfile(userIdList, tim, TUIStore) {
return __awaiter(this, void 0, void 0, function* () {
let remoteUserInfoList = userIdList.map((userId) => setDefaultUserInfo(userId));
try {
if (!tim)
return remoteUserInfoList;
const res = yield tim.getFriendProfile({ userIDList: userIdList });
if (res.code === 0) {
const { friendList = [], failureUserIDList = [] } = res.data;
let unFriendList = failureUserIDList.map((obj) => obj.userID);
if (failureUserIDList.length > 0) {
const res = yield tim.getUserProfile({ userIDList: failureUserIDList.map((obj) => obj.userID) });
if ((res === null || res === void 0 ? void 0 : res.code) === 0) {
unFriendList = (res === null || res === void 0 ? void 0 : res.data) || [];
}
}
const currentRemoteUserInfoList = TUIStore === null || TUIStore === void 0 ? void 0 : TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.REMOTE_USER_INFO_LIST); // remoteUserInfoList may have been updated
const tempFriendIdList = friendList.map((obj) => obj.userID);
const tempUnFriendIdList = unFriendList.map((obj) => obj.userID);
remoteUserInfoList = userIdList.map((userId) => {
var _a, _b, _c, _d, _e, _f, _g;
const defaultUserInfo = setDefaultUserInfo(userId);
const friendListIndex = tempFriendIdList.indexOf(userId);
const unFriendListIndex = tempUnFriendIdList.indexOf(userId);
let remark = '';
let nick = '';
let displayUserInfo = '';
let avatar = '';
if (friendListIndex !== -1) {
remark = ((_a = friendList[friendListIndex]) === null || _a === void 0 ? void 0 : _a.remark) || '';
nick = ((_c = (_b = friendList[friendListIndex]) === null || _b === void 0 ? void 0 : _b.profile) === null || _c === void 0 ? void 0 : _c.nick) || '';
displayUserInfo = remark || nick || defaultUserInfo.userId || '';
avatar = ((_e = (_d = friendList[friendListIndex]) === null || _d === void 0 ? void 0 : _d.profile) === null || _e === void 0 ? void 0 : _e.avatar) || '';
}
if (unFriendListIndex !== -1) {
nick = ((_f = unFriendList[unFriendListIndex]) === null || _f === void 0 ? void 0 : _f.nick) || '';
displayUserInfo = nick || defaultUserInfo.userId || '';
avatar = ((_g = unFriendList[unFriendListIndex]) === null || _g === void 0 ? void 0 : _g.avatar) || '';
}
const userInfo = currentRemoteUserInfoList.find(subObj => subObj.userId === userId) || {};
return Object.assign(Object.assign(Object.assign({}, defaultUserInfo), userInfo), { remark, nick, displayUserInfo, avatar });
});
}
return remoteUserInfoList;
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getRemoteUserProfile failed, error: ${error}.`);
return remoteUserInfoList;
}
});
}
exports.getRemoteUserProfile = getRemoteUserProfile;
// 生成弹框提示文案
function generateText(TUIStore, key, prefix, suffix) {
const isGroup = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_GROUP);
let callTips = `${(0, index_2.t)(key)}`;
if (isGroup) {
callTips = prefix ? `${prefix} ${callTips}` : callTips;
callTips = suffix ? `${callTips} ${suffix}` : callTips;
}
return callTips;
}
exports.generateText = generateText;
// 生成 statusChange 抛出的字符串
function generateStatusChangeText(TUIStore) {
const callStatus = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_STATUS);
if (callStatus === index_1.CallStatus.IDLE) {
return index_1.StatusChange.IDLE;
}
const isGroup = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.IS_GROUP);
if (callStatus === index_1.CallStatus.CALLING) {
return isGroup ? index_1.StatusChange.DIALING_GROUP : index_1.StatusChange.DIALING_C2C;
}
const callMediaType = TUIStore.getData(index_1.StoreName.CALL, index_1.NAME.CALL_MEDIA_TYPE);
if (isGroup) {
return callMediaType === index_1.CallMediaType.AUDIO ? index_1.StatusChange.CALLING_GROUP_AUDIO : index_1.StatusChange.CALLING_GROUP_VIDEO;
}
return callMediaType === index_1.CallMediaType.AUDIO ? index_1.StatusChange.CALLING_C2C_AUDIO : index_1.StatusChange.CALLING_C2C_VIDEO;
}
exports.generateStatusChangeText = generateStatusChangeText;
// 获取群组[offset, count + offset]区间成员
function getGroupMemberList(groupID, tim, count, offset) {
return __awaiter(this, void 0, void 0, function* () {
let groupMemberList = [];
try {
const res = yield tim.getGroupMemberList({ groupID, count, offset });
if (res.code === 0) {
return res.data.memberList || groupMemberList;
}
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getGroupMember failed, error: ${error}.`);
return groupMemberList;
}
});
}
exports.getGroupMemberList = getGroupMemberList;
// 获取 IM 群信息
function getGroupProfile(groupID, tim) {
return __awaiter(this, void 0, void 0, function* () {
let groupProfile = {};
try {
const res = yield tim.getGroupProfile({ groupID });
if (res.code === 0) {
return res.data.group || groupProfile;
}
}
catch (error) {
console.error(`${index_1.NAME.PREFIX}getGroupProfile failed, error: ${error}.`);
return groupProfile;
}
});
}
exports.getGroupProfile = getGroupProfile;

View File

@ -0,0 +1,142 @@
import { NAME, StoreName, CallStatus, StatusChange, CallMediaType } from '../const/index';
import { IUserInfo } from '../interface/ICallService';
import { ITUIStore } from '../interface/ITUIStore';
import { t } from '../locales/index';
// 设置默认的 UserInfo 信息
export function setDefaultUserInfo(userId: string, domId?: string): IUserInfo {
const userInfo: IUserInfo = {
userId,
nick: '',
avatar: '',
remark: '',
displayUserInfo: '',
isAudioAvailable: false,
isVideoAvailable: false,
isEnter: false,
domId: domId || userId,
};
return domId ? userInfo : { ...userInfo, isEnter: false }; // localUserInfo 没有 isEnter, remoteUserInfoList 有 isEnter
}
// 获取个人用户信息
export async function getMyProfile(myselfUserId: string, tim: any, TUIStore: any): Promise<IUserInfo> {
let localUserInfo: IUserInfo = setDefaultUserInfo(myselfUserId, NAME.LOCAL_VIDEO);
try {
if (!tim) return localUserInfo;
const res = await tim.getMyProfile();
const currentLocalUserInfo = TUIStore?.getData(StoreName.CALL, NAME.LOCAL_USER_INFO); // localUserInfo may have been updated
if (res?.code === 0) {
localUserInfo = {
...localUserInfo,
...currentLocalUserInfo,
userId: res?.data?.userID,
nick: res?.data?.nick,
avatar: res?.data?.avatar,
displayUserInfo: res?.data?.nick || res?.data?.userID,
};
}
return localUserInfo;
} catch (error) {
console.error(`${NAME.PREFIX}getMyProfile failed, error: ${error}.`);
return localUserInfo;
}
}
// 获取远端用户列表信息
export async function getRemoteUserProfile(userIdList: Array<string>, tim: any, TUIStore: any): Promise<any> {
let remoteUserInfoList: IUserInfo[] = userIdList.map((userId: string) => setDefaultUserInfo(userId));
try {
if (!tim) return remoteUserInfoList;
const res = await tim.getFriendProfile({ userIDList: userIdList });
if (res.code === 0) {
const { friendList = [], failureUserIDList = [] } = res.data;
let unFriendList: IUserInfo[] = failureUserIDList.map((obj: any) => obj.userID);
if (failureUserIDList.length > 0) {
const res = await tim.getUserProfile({ userIDList: failureUserIDList.map((obj: any) => obj.userID) });
if (res?.code === 0) {
unFriendList = res?.data || [];
}
}
const currentRemoteUserInfoList = TUIStore?.getData(StoreName.CALL, NAME.REMOTE_USER_INFO_LIST); // remoteUserInfoList may have been updated
const tempFriendIdList: string[] = friendList.map((obj: any) => obj.userID);
const tempUnFriendIdList: string[] = unFriendList.map((obj: any) => obj.userID);
remoteUserInfoList = userIdList.map((userId: string) => {
const defaultUserInfo: IUserInfo = setDefaultUserInfo(userId);
const friendListIndex: number = tempFriendIdList.indexOf(userId);
const unFriendListIndex: number = tempUnFriendIdList.indexOf(userId);
let remark = '';
let nick = '';
let displayUserInfo = '' ;
let avatar = '';
if (friendListIndex !== -1) {
remark = friendList[friendListIndex]?.remark || '';
nick = friendList[friendListIndex]?.profile?.nick || '';
displayUserInfo = remark || nick || defaultUserInfo.userId || '';
avatar = friendList[friendListIndex]?.profile?.avatar || '';
}
if (unFriendListIndex !== -1) {
nick = unFriendList[unFriendListIndex]?.nick || '';
displayUserInfo = nick || defaultUserInfo.userId || '';
avatar = unFriendList[unFriendListIndex]?.avatar || '';
}
const userInfo = currentRemoteUserInfoList.find(subObj => subObj.userId === userId) || {};
return { ...defaultUserInfo, ...userInfo, remark, nick, displayUserInfo, avatar };
});
}
return remoteUserInfoList;
} catch (error) {
console.error(`${NAME.PREFIX}getRemoteUserProfile failed, error: ${error}.`);
return remoteUserInfoList;
}
}
// 生成弹框提示文案
export function generateText(TUIStore: ITUIStore, key: string, prefix?: string, suffix?: string): string {
const isGroup = TUIStore.getData(StoreName.CALL, NAME.IS_GROUP);
let callTips = `${t(key)}`;
if (isGroup) {
callTips = prefix ? `${prefix} ${callTips}` : callTips;
callTips = suffix ? `${callTips} ${suffix}` : callTips;
}
return callTips;
}
// 生成 statusChange 抛出的字符串
export function generateStatusChangeText(TUIStore: ITUIStore): string {
const callStatus = TUIStore.getData(StoreName.CALL, NAME.CALL_STATUS);
if (callStatus === CallStatus.IDLE) {
return StatusChange.IDLE;
}
const isGroup = TUIStore.getData(StoreName.CALL, NAME.IS_GROUP);
if (callStatus === CallStatus.CALLING) {
return isGroup ? StatusChange.DIALING_GROUP : StatusChange.DIALING_C2C;
}
const callMediaType = TUIStore.getData(StoreName.CALL, NAME.CALL_MEDIA_TYPE);
if (isGroup) {
return callMediaType === CallMediaType.AUDIO ? StatusChange.CALLING_GROUP_AUDIO : StatusChange.CALLING_GROUP_VIDEO;
}
return callMediaType === CallMediaType.AUDIO ? StatusChange.CALLING_C2C_AUDIO : StatusChange.CALLING_C2C_VIDEO;
}
// 获取群组[offset, count + offset]区间成员
export async function getGroupMemberList(groupID: string, tim: any, count, offset) {
let groupMemberList = [];
try {
const res = await tim.getGroupMemberList({ groupID, count, offset });
if (res.code === 0) {
return res.data.memberList || groupMemberList;
}
} catch(error) {
console.error(`${NAME.PREFIX}getGroupMember failed, error: ${error}.`);
return groupMemberList;
}
}
// 获取 IM 群信息
export async function getGroupProfile(groupID: string, tim: any): Promise<any> {
let groupProfile = {};
try {
const res = await tim.getGroupProfile({ groupID });
if (res.code === 0) {
return res.data.group || groupProfile;
}
} catch(error) {
console.error(`${NAME.PREFIX}getGroupProfile failed, error: ${error}.`);
return groupProfile;
}
}

View File

@ -0,0 +1,21 @@
import { ITUIGlobal } from '../interface/ITUIGlobal';
export default class TUIGlobal implements ITUIGlobal {
static instance: TUIGlobal;
global: any;
isPC: boolean;
isH5: boolean;
isWeChat: boolean;
isApp: boolean;
isUniPlatform: boolean;
isOfficial: boolean;
isWIN: boolean;
isMAC: boolean;
constructor();
/**
* TUIGlobal
* @returns {TUIGlobal}
*/
static getInstance(): TUIGlobal;
initEnv(): void;
initOfficial(SDKAppID: number): void;
}

View File

@ -0,0 +1,40 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const env_1 = require("../utils/env");
class TUIGlobal {
constructor() {
this.global = env_1.APP_NAMESPACE;
this.isPC = false;
this.isH5 = false;
this.isWeChat = false;
this.isApp = false;
this.isUniPlatform = false;
this.isOfficial = false;
this.isWIN = false;
this.isMAC = false;
this.initEnv();
}
/**
* 获取 TUIGlobal 实例
* @returns {TUIGlobal}
*/
static getInstance() {
if (!TUIGlobal.instance) {
TUIGlobal.instance = new TUIGlobal();
}
return TUIGlobal.instance;
}
initEnv() {
this.isPC = env_1.IS_PC;
this.isH5 = env_1.IS_H5;
this.isWeChat = env_1.IN_WX_MINI_APP;
this.isApp = env_1.IN_UNI_NATIVE_APP && !env_1.IN_WX_MINI_APP; // uniApp 打包小程序时 IN_UNI_NATIVE_APP 为 true所以此处需要增加条件
this.isUniPlatform = env_1.IN_UNI_APP;
this.isWIN = env_1.IS_WIN;
this.isMAC = env_1.IS_MAC;
}
initOfficial(SDKAppID) {
this.isOfficial = (SDKAppID === 1400187352 || SDKAppID === 1400188366);
}
}
exports.default = TUIGlobal;

View File

@ -0,0 +1,43 @@
import { APP_NAMESPACE, IS_PC, IS_H5, IN_WX_MINI_APP, IN_UNI_NATIVE_APP, IN_UNI_APP, IS_MAC, IS_WIN } from '../utils/env';
import { ITUIGlobal } from '../interface/ITUIGlobal';
export default class TUIGlobal implements ITUIGlobal {
static instance: TUIGlobal;
public global: any = APP_NAMESPACE;
public isPC: boolean = false;
public isH5: boolean = false;
public isWeChat: boolean = false;
public isApp: boolean = false;
public isUniPlatform: boolean = false;
public isOfficial: boolean = false;
public isWIN: boolean = false;
public isMAC: boolean = false;
constructor() {
this.initEnv();
}
/**
* TUIGlobal
* @returns {TUIGlobal}
*/
static getInstance() {
if (!TUIGlobal.instance) {
TUIGlobal.instance = new TUIGlobal();
}
return TUIGlobal.instance;
}
initEnv() {
this.isPC = IS_PC;
this.isH5 = IS_H5;
this.isWeChat = IN_WX_MINI_APP;
this.isApp = IN_UNI_NATIVE_APP && !IN_WX_MINI_APP; // uniApp 打包小程序时 IN_UNI_NATIVE_APP 为 true所以此处需要增加条件
this.isUniPlatform = IN_UNI_APP;
this.isWIN = IS_WIN;
this.isMAC = IS_MAC;
}
initOfficial(SDKAppID: number) {
this.isOfficial = (SDKAppID === 1400187352 || SDKAppID === 1400188366);
}
}

View File

@ -0,0 +1,8 @@
import { ICallStore } from '../interface/ICallStore';
export default class CallStore {
defaultStore: ICallStore;
store: ICallStore;
update(key: keyof ICallStore, data: any): void;
getData(key: string | undefined): any;
reset(keyList?: Array<string>): void;
}

View File

@ -0,0 +1,65 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("../const/index");
const common_utils_1 = require("../utils/common-utils");
class CallStore {
constructor() {
this.defaultStore = {
callStatus: index_1.CallStatus.IDLE,
callRole: index_1.CallRole.UNKNOWN,
callMediaType: index_1.CallMediaType.UNKNOWN,
localUserInfo: { userId: '' },
localUserInfoExcludeVolume: { userId: '' },
remoteUserInfoList: [],
remoteUserInfoExcludeVolumeList: [],
callerUserInfo: { userId: '' },
isGroup: false,
callDuration: '00:00:00',
callTips: '',
toastInfo: { text: '' },
isMinimized: false,
enableFloatWindow: false,
bigScreenUserId: '',
language: (0, common_utils_1.getLanguage)(),
isClickable: false,
deviceList: { cameraList: [], microphoneList: [], currentCamera: {}, currentMicrophone: {} },
showPermissionTip: false,
groupID: '',
roomID: 0,
cameraPosition: index_1.CameraPosition.FRONT,
// TUICallKit 组件上的属性
displayMode: index_1.VideoDisplayMode.COVER,
videoResolution: index_1.VideoResolution.RESOLUTION_480P,
showSelectUser: false,
// 小程序相关属性
pusher: {},
player: [],
isEarPhone: false, // 是否是听筒, 默认: false
};
this.store = Object.assign({}, this.defaultStore);
}
;
update(key, data) {
switch (key) {
// case 'callTips':
// break;
default:
// resolve "Type 'any' is not assignable to type 'never'.ts", ref: https://github.com/microsoft/TypeScript/issues/31663
this.store[key] = data;
}
}
getData(key) {
if (!key)
return this.store;
return this.store[key];
}
// reset call store
reset(keyList = []) {
if (keyList.length === 0) {
keyList = Object.keys(this.store);
}
const resetToDefault = keyList.reduce((acc, key) => (Object.assign(Object.assign({}, acc), { [key]: this.defaultStore[key] })), {});
this.store = Object.assign(Object.assign(Object.assign({}, this.defaultStore), this.store), resetToDefault);
}
}
exports.default = CallStore;

View File

@ -0,0 +1,66 @@
import { CallStatus, CallRole, CallMediaType, VideoDisplayMode, VideoResolution, CameraPosition } from '../const/index';
import { ICallStore } from '../interface/ICallStore';
import { getLanguage } from '../utils/common-utils';
export default class CallStore {
public defaultStore: ICallStore = {
callStatus: CallStatus.IDLE,
callRole: CallRole.UNKNOWN,
callMediaType: CallMediaType.UNKNOWN,
localUserInfo: { userId: '' },
localUserInfoExcludeVolume: { userId: '' },
remoteUserInfoList: [],
remoteUserInfoExcludeVolumeList: [],
callerUserInfo: { userId: '' },
isGroup: false,
callDuration: '00:00:00', // 通话时长
callTips: '', // 通话提示的信息. 例如: '等待谁接听', 'xxx 拒绝通话', 'xxx 挂断通话'
toastInfo: { text: '' }, // 远端用户挂断、拒绝、超时、忙线等的 toast 提示信息
isMinimized: false, // 用来记录当前是否悬浮窗模式
enableFloatWindow: false, // 开启/关闭悬浮窗功能设置为false通话界面左上角的悬浮窗按钮会隐藏
bigScreenUserId: '', // 当前大屏幕显示的 userID 用户
language: getLanguage(), // en, zh-cn
isClickable: false, // 是否可点击, 用于按钮增加 loading 效果,不可点击
deviceList: { cameraList: [], microphoneList: [], currentCamera: {}, currentMicrophone: {} },
showPermissionTip: false,
groupID: '',
roomID: 0,
cameraPosition: CameraPosition.FRONT, // 前置或后置值为front, back
// TUICallKit 组件上的属性
displayMode: VideoDisplayMode.COVER, // 设置预览远端的画面显示模式
videoResolution: VideoResolution.RESOLUTION_480P,
showSelectUser: false,
// 小程序相关属性
pusher: {},
player: [],
isEarPhone: false, // 是否是听筒, 默认: false
};
public store: ICallStore = Object.assign({}, this.defaultStore);;
public update(key: keyof ICallStore, data: any): void {
switch (key) {
// case 'callTips':
// break;
default:
// resolve "Type 'any' is not assignable to type 'never'.ts", ref: https://github.com/microsoft/TypeScript/issues/31663
(this.store[key] as any) = data as any;
}
}
public getData(key: string | undefined): any {
if (!key) return this.store;
return this.store[key as keyof ICallStore];
}
// reset call store
public reset(keyList: Array<string> = []) {
if (keyList.length === 0) {
keyList = Object.keys(this.store);
}
const resetToDefault = keyList.reduce((acc, key) => ({ ...acc, [key]: this.defaultStore[key as keyof ICallStore] }), {});
this.store = {
...this.defaultStore,
...this.store,
...resetToDefault,
};
}
}

View File

@ -0,0 +1,50 @@
import { ITUIStore, IOptions, Task } from '../interface/ITUIStore';
import { StoreName } from '../const/index';
export default class TUIStore implements ITUIStore {
static instance: TUIStore;
task: Task;
private storeMap;
private timerId;
constructor();
/**
* TUIStore
* @returns {TUIStore}
*/
static getInstance(): TUIStore;
/**
* UI
* @param {StoreName} storeName store
* @param {IOptions} options
* @param {Object} params
* @param {String} params.notifyRangeWhenWatch , 'all' - key ; 'myself' - key ;
*/
watch(storeName: StoreName, options: IOptions, params?: any): void;
/**
* UI
* @param {StoreName} storeName store
* @param {IOptions} options ,
*/
unwatch(storeName: StoreName, options: IOptions): void;
/**
* store messageList
* @param {StoreName} storeName store
* @param {string} key key
* @param {unknown} data
*/
update(storeName: StoreName, key: string, data: unknown): void;
/**
* Store
* @param {StoreName} storeName store
* @param {string} key key
* @returns {Any}
*/
getData(storeName: StoreName, key: string): any;
/**
* UI
* @param {StoreName} storeName store
* @param {string} key key
*/
private notify;
reset(storeName: StoreName, keyList?: Array<string>, isNotificationNeeded?: boolean): void;
updateStore(params: any, name?: StoreName): void;
}

View File

@ -0,0 +1,155 @@
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const index_1 = require("../const/index");
const callStore_1 = __importDefault(require("./callStore"));
const common_utils_1 = require("../utils/common-utils");
class TUIStore {
constructor() {
this.timerId = -1;
this.storeMap = {
[index_1.StoreName.CALL]: new callStore_1.default(),
};
// todo 此处后续优化结构后调整
this.task = {}; // 保存监听回调列表
}
/**
* 获取 TUIStore 实例
* @returns {TUIStore}
*/
static getInstance() {
if (!TUIStore.instance) {
TUIStore.instance = new TUIStore();
}
return TUIStore.instance;
}
/**
* UI 组件注册监听回调
* @param {StoreName} storeName store 名称
* @param {IOptions} options 监听信息
* @param {Object} params 扩展参数
* @param {String} params.notifyRangeWhenWatch 注册时监听时的通知范围, 'all' - 通知所有注册该 key 的监听; 'myself' - 通知本次注册该 key 的监听; 默认不通知
*/
watch(storeName, options, params) {
if (!this.task[storeName]) {
this.task[storeName] = {};
}
const watcher = this.task[storeName];
Object.keys(options).forEach((key) => {
const callback = options[key];
if (!watcher[key]) {
watcher[key] = new Map();
}
watcher[key].set(callback, 1);
const { notifyRangeWhenWatch } = params || {};
// 注册监听后, 通知所有注册该 key 的监听,使用 'all' 时要特别注意是否对其他地方的监听产生影响
if (notifyRangeWhenWatch === index_1.NAME.ALL) {
this.notify(storeName, key);
}
// 注册监听后, 仅通知自己本次监听该 key 的数据
if (notifyRangeWhenWatch === index_1.NAME.MYSELF) {
const data = this.getData(storeName, key);
callback.call(this, data);
}
});
}
/**
* UI 取消组件监听回调
* @param {StoreName} storeName store 名称
* @param {IOptions} options 监听信息,包含需要取消的回掉等
*/
unwatch(storeName, options) {
// todo 该接口暂未支持unwatch掉同一类如仅传入store注销掉该store下的所有callback同样options仅传入key注销掉该key下的所有callback
// options的callback function为必填参数后续修改
if (!this.task[storeName]) {
return;
}
;
// if (isString(options)) {
// // 移除所有的监听
// if (options === '*') {
// const watcher = this.task[storeName];
// Object.keys(watcher).forEach(key => {
// watcher[key].clear();
// });
// } else {
// console.warn(`${NAME.PREFIX}unwatch warning: options is ${options}`);
// }
// return;
// }
const watcher = this.task[storeName];
Object.keys(options).forEach((key) => {
watcher[key].delete(options[key]);
});
}
/**
* 通用 store 数据更新messageList 的变更需要单独处理
* @param {StoreName} storeName store 名称
* @param {string} key 变更的 key
* @param {unknown} data 变更的数据
*/
update(storeName, key, data) {
var _a;
// 基本数据类型时, 如果相等, 就不进行更新, 减少不必要的 notify
if ((0, common_utils_1.isString)(data) || (0, common_utils_1.isNumber)(data) || (0, common_utils_1.isBoolean)(data)) {
const currentData = this.storeMap[storeName]['store'][key]; // eslint-disable-line
if (currentData === data)
return;
}
(_a = this.storeMap[storeName]) === null || _a === void 0 ? void 0 : _a.update(key, data);
this.notify(storeName, key);
}
/**
* 获取 Store 数据
* @param {StoreName} storeName store 名称
* @param {string} key 待获取的 key
* @returns {Any}
*/
getData(storeName, key) {
var _a;
return (_a = this.storeMap[storeName]) === null || _a === void 0 ? void 0 : _a.getData(key);
}
/**
* UI 组件注册监听回调
* @param {StoreName} storeName store 名称
* @param {string} key 变更的 key
*/
notify(storeName, key) {
if (!this.task[storeName]) {
return;
}
const watcher = this.task[storeName];
if (watcher[key]) {
const callbackMap = watcher[key];
const data = this.getData(storeName, key);
for (const [callback] of callbackMap.entries()) {
callback.call(this, data);
}
}
}
reset(storeName, keyList = [], isNotificationNeeded = false) {
if (storeName in this.storeMap) {
const store = this.storeMap[storeName];
// reset all
if (keyList.length === 0) {
keyList = Object.keys(store === null || store === void 0 ? void 0 : store.store);
}
store.reset(keyList);
if (isNotificationNeeded) {
keyList.forEach((key) => {
this.notify(storeName, key);
});
}
}
}
// 批量修改多个 key-value
updateStore(params, name) {
const storeName = name ? name : index_1.StoreName.CALL;
Object.keys(params).forEach((key) => {
this.update(storeName, key, params[key]);
});
}
}
exports.default = TUIStore;

View File

@ -0,0 +1,157 @@
import { ITUIStore, IOptions, Task } from '../interface/ITUIStore';
import { StoreName, NAME } from '../const/index';
import CallStore from './callStore';
import { isString, isNumber, isBoolean } from '../utils/common-utils';
export default class TUIStore implements ITUIStore {
static instance: TUIStore;
public task: Task;
private storeMap: Partial<Record<StoreName, any>>;
private timerId: number = -1;
constructor() {
this.storeMap = {
[StoreName.CALL]: new CallStore(),
};
// todo 此处后续优化结构后调整
this.task = {} as Task; // 保存监听回调列表
}
/**
* TUIStore
* @returns {TUIStore}
*/
static getInstance() {
if (!TUIStore.instance) {
TUIStore.instance = new TUIStore();
}
return TUIStore.instance;
}
/**
* UI
* @param {StoreName} storeName store
* @param {IOptions} options
* @param {Object} params
* @param {String} params.notifyRangeWhenWatch , 'all' - key ; 'myself' - key ;
*/
watch(storeName: StoreName, options: IOptions, params?: any) {
if (!this.task[storeName]) {
this.task[storeName] = {};
}
const watcher = this.task[storeName];
Object.keys(options).forEach((key) => {
const callback = options[key];
if (!watcher[key]) {
watcher[key] = new Map();
}
watcher[key].set(callback, 1);
const { notifyRangeWhenWatch } = params || {};
// 注册监听后, 通知所有注册该 key 的监听,使用 'all' 时要特别注意是否对其他地方的监听产生影响
if (notifyRangeWhenWatch === NAME.ALL) {
this.notify(storeName, key);
}
// 注册监听后, 仅通知自己本次监听该 key 的数据
if (notifyRangeWhenWatch === NAME.MYSELF) {
const data = this.getData(storeName, key);
callback.call(this, data);
}
});
}
/**
* UI
* @param {StoreName} storeName store
* @param {IOptions} options ,
*/
unwatch(storeName: StoreName, options: IOptions) {
// todo 该接口暂未支持unwatch掉同一类如仅传入store注销掉该store下的所有callback同样options仅传入key注销掉该key下的所有callback
// options的callback function为必填参数后续修改
if (!this.task[storeName]) {
return;
};
// if (isString(options)) {
// // 移除所有的监听
// if (options === '*') {
// const watcher = this.task[storeName];
// Object.keys(watcher).forEach(key => {
// watcher[key].clear();
// });
// } else {
// console.warn(`${NAME.PREFIX}unwatch warning: options is ${options}`);
// }
// return;
// }
const watcher = this.task[storeName];
Object.keys(options).forEach((key: string) => {
watcher[key].delete(options[key]);
});
}
/**
* store messageList
* @param {StoreName} storeName store
* @param {string} key key
* @param {unknown} data
*/
update(storeName: StoreName, key: string, data: unknown) {
// 基本数据类型时, 如果相等, 就不进行更新, 减少不必要的 notify
if (isString(data) || isNumber(data) || isBoolean(data)) {
const currentData = this.storeMap[storeName]['store'][key]; // eslint-disable-line
if (currentData === data) return;
}
this.storeMap[storeName]?.update(key, data);
this.notify(storeName, key);
}
/**
* Store
* @param {StoreName} storeName store
* @param {string} key key
* @returns {Any}
*/
getData(storeName: StoreName, key: string) {
return this.storeMap[storeName]?.getData(key);
}
/**
* UI
* @param {StoreName} storeName store
* @param {string} key key
*/
private notify(storeName: StoreName, key: string) {
if (!this.task[storeName]) {
return;
}
const watcher = this.task[storeName];
if (watcher[key]) {
const callbackMap = watcher[key];
const data = this.getData(storeName, key);
for (const [callback] of callbackMap.entries()) {
callback.call(this, data);
}
}
}
public reset(storeName: StoreName, keyList: Array<string> = [], isNotificationNeeded = false) {
if (storeName in this.storeMap) {
const store = this.storeMap[storeName];
// reset all
if (keyList.length === 0) {
keyList = Object.keys(store?.store);
}
store.reset(keyList);
if (isNotificationNeeded) {
keyList.forEach((key) => {
this.notify(storeName, key);
});
}
}
}
// 批量修改多个 key-value
public updateStore(params: any, name?: StoreName): void {
const storeName = name ? name : StoreName.CALL;
Object.keys(params).forEach((key) => {
this.update(storeName, key, params[key]);
});
}
}

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,98 @@
/**
* @property {String} call 1v1 +
* @property {String} CUSTOM Store
*/
export declare enum StoreName {
CALL = "call",
CUSTOM = "custom"
}
/**
* @property {String} idle
* @property {String} connecting
* @property {String} connected
*/
export declare enum CallMediaType {
UNKNOWN = 0,
AUDIO = 1,
VIDEO = 2
}
/**
* @property {String} caller
* @property {String} callee
*/
export declare enum CallRole {
UNKNOWN = "unknown",
CALLEE = "callee",
CALLER = "caller"
}
/**
* @property {String} idle
* @property {String} calling
* @property {String} connected
*/
export declare enum CallStatus {
IDLE = "idle",
CALLING = "calling",
CONNECTED = "connected"
}
/**
*
* 使 cover ; 使 contain
* @property {String} contain
* @property {String} cover
* @property {String} fill ( v4.12.1 )
*/
export declare enum VideoDisplayMode {
CONTAIN = "contain",
COVER = "cover",
FILL = "fill"
}
/**
*
* @property {String} 480p
* @property {String} 720p
* @property {String} 1080p
*/
export declare enum VideoResolution {
RESOLUTION_480P = "480p",
RESOLUTION_720P = "720p",
RESOLUTION_1080P = "1080p"
}
export declare enum LanguageType {
EN = "en",
'ZH-CN' = "zh-cn",
JA_JP = "ja_JP"
}
export type TDeviceList = {
cameraList: any[];
microphoneList: any[];
currentCamera: any;
currentMicrophone: any;
};
export declare const StatusChange: {
readonly IDLE: "idle";
readonly BE_INVITED: "be-invited";
readonly DIALING_C2C: "dialing-c2c";
readonly DIALING_GROUP: "dialing-group";
readonly CALLING_C2C_AUDIO: "calling-c2c-audio";
readonly CALLING_C2C_VIDEO: "calling-c2c-video";
readonly CALLING_GROUP_AUDIO: "calling-group-audio";
readonly CALLING_GROUP_VIDEO: "calling-group-video";
};
/**
* @property {String} ear
* @property {String} speaker
*/
export declare enum AudioPlayBackDevice {
EAR = "ear",
SPEAKER = "speaker"
}
export declare enum DeviceType {
MICROPHONE = "microphone",
CAMERA = "camera",
SPEAKER = "speaker"
}
export declare enum CameraPosition {
FRONT = 0,
BACK = 1
}

View File

@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.CameraPosition = exports.DeviceType = exports.AudioPlayBackDevice = exports.StatusChange = exports.LanguageType = exports.VideoResolution = exports.VideoDisplayMode = exports.CallStatus = exports.CallRole = exports.CallMediaType = exports.StoreName = void 0;
/**
* @property {String} call 1v1 通话 + 群组通话
* @property {String} CUSTOM 自定义 Store
*/
var StoreName;
(function (StoreName) {
StoreName["CALL"] = "call";
StoreName["CUSTOM"] = "custom";
})(StoreName = exports.StoreName || (exports.StoreName = {}));
/**
* @property {String} idle 空闲
* @property {String} connecting 呼叫等待中
* @property {String} connected 通话中
*/
var CallMediaType;
(function (CallMediaType) {
CallMediaType[CallMediaType["UNKNOWN"] = 0] = "UNKNOWN";
CallMediaType[CallMediaType["AUDIO"] = 1] = "AUDIO";
CallMediaType[CallMediaType["VIDEO"] = 2] = "VIDEO";
})(CallMediaType = exports.CallMediaType || (exports.CallMediaType = {}));
/**
* @property {String} caller 主叫
* @property {String} callee 被叫
*/
var CallRole;
(function (CallRole) {
CallRole["UNKNOWN"] = "unknown";
CallRole["CALLEE"] = "callee";
CallRole["CALLER"] = "caller";
})(CallRole = exports.CallRole || (exports.CallRole = {}));
/**
* @property {String} idle 空闲
* @property {String} calling 呼叫等待中
* @property {String} connected 通话中
*/
var CallStatus;
(function (CallStatus) {
CallStatus["IDLE"] = "idle";
CallStatus["CALLING"] = "calling";
CallStatus["CONNECTED"] = "connected";
})(CallStatus = exports.CallStatus || (exports.CallStatus = {}));
/**
* 视频画面显示模式
* 播放视频流默认使用 cover 模式; 播放屏幕共享流默认使用 contain 模式
* @property {String} contain 优先保证视频内容全部显示视频尺寸等比缩放直至视频窗口的一边与视窗边框对齐如果视频尺寸与显示视窗尺寸不一致在保持长宽比的前提下将视频进行缩放后填满视窗缩放后的视频四周会有一圈黑边
* @property {String} cover 优先保证视窗被填满视频尺寸等比缩放直至整个视窗被视频填满如果视频长宽与显示窗口不同则视频流会按照显示视窗的比例进行周边裁剪或图像拉伸后填满视窗
* @property {String} fill 保证视窗被填满的同时保证视频内容全部显示但是不保证视频尺寸比例不变视频的宽高会被拉伸至和视窗尺寸一致(该选项值自 v4.12.1 开始支持)
*/
var VideoDisplayMode;
(function (VideoDisplayMode) {
VideoDisplayMode["CONTAIN"] = "contain";
VideoDisplayMode["COVER"] = "cover";
VideoDisplayMode["FILL"] = "fill";
})(VideoDisplayMode = exports.VideoDisplayMode || (exports.VideoDisplayMode = {}));
/**
* 视频分辨率
* @property {String} 480p
* @property {String} 720p
* @property {String} 1080p
*/
var VideoResolution;
(function (VideoResolution) {
VideoResolution["RESOLUTION_480P"] = "480p";
VideoResolution["RESOLUTION_720P"] = "720p";
VideoResolution["RESOLUTION_1080P"] = "1080p";
})(VideoResolution = exports.VideoResolution || (exports.VideoResolution = {}));
// 支持的语言
var LanguageType;
(function (LanguageType) {
LanguageType["EN"] = "en";
LanguageType["ZH-CN"] = "zh-cn";
LanguageType["JA_JP"] = "ja_JP";
})(LanguageType = exports.LanguageType || (exports.LanguageType = {}));
/* === 【原来 TUICallKit 对外暴露】=== */
// 原来 web callKit 定义通知外部状态变更的变量, 对外暴露
exports.StatusChange = {
IDLE: "idle",
BE_INVITED: "be-invited",
DIALING_C2C: "dialing-c2c",
DIALING_GROUP: "dialing-group",
CALLING_C2C_AUDIO: "calling-c2c-audio",
CALLING_C2C_VIDEO: "calling-c2c-video",
CALLING_GROUP_AUDIO: "calling-group-audio",
CALLING_GROUP_VIDEO: "calling-group-video",
};
/* === 【小程序使用】=== */
/**
* @property {String} ear 听筒
* @property {String} speaker 免提
*/
var AudioPlayBackDevice;
(function (AudioPlayBackDevice) {
AudioPlayBackDevice["EAR"] = "ear";
AudioPlayBackDevice["SPEAKER"] = "speaker";
})(AudioPlayBackDevice = exports.AudioPlayBackDevice || (exports.AudioPlayBackDevice = {}));
;
var DeviceType;
(function (DeviceType) {
DeviceType["MICROPHONE"] = "microphone";
DeviceType["CAMERA"] = "camera";
DeviceType["SPEAKER"] = "speaker";
})(DeviceType = exports.DeviceType || (exports.DeviceType = {}));
var CameraPosition;
(function (CameraPosition) {
CameraPosition[CameraPosition["FRONT"] = 0] = "FRONT";
CameraPosition[CameraPosition["BACK"] = 1] = "BACK";
})(CameraPosition = exports.CameraPosition || (exports.CameraPosition = {}));

View File

@ -0,0 +1,107 @@
/**
* @property {String} call 1v1 +
* @property {String} CUSTOM Store
*/
export enum StoreName {
CALL = 'call',
CUSTOM = 'custom'
}
/**
* @property {String} idle
* @property {String} connecting
* @property {String} connected
*/
export enum CallMediaType {
UNKNOWN,
AUDIO,
VIDEO,
}
/**
* @property {String} caller
* @property {String} callee
*/
export enum CallRole {
UNKNOWN = 'unknown',
CALLEE = 'callee',
CALLER = 'caller',
}
/**
* @property {String} idle
* @property {String} calling
* @property {String} connected
*/
export enum CallStatus {
IDLE = 'idle',
CALLING = 'calling',
CONNECTED = 'connected',
}
/**
*
* 使 cover ; 使 contain
* @property {String} contain
* @property {String} cover
* @property {String} fill ( v4.12.1 )
*/
export enum VideoDisplayMode {
CONTAIN = 'contain',
COVER = 'cover',
FILL = 'fill',
}
/**
*
* @property {String} 480p
* @property {String} 720p
* @property {String} 1080p
*/
export enum VideoResolution {
RESOLUTION_480P = '480p',
RESOLUTION_720P = '720p',
RESOLUTION_1080P = '1080p',
}
// 支持的语言
export enum LanguageType {
EN = 'en',
'ZH-CN' = 'zh-cn',
JA_JP = 'ja_JP',
}
export type TDeviceList = {
cameraList: any[],
microphoneList: any[],
currentCamera: any,
currentMicrophone: any,
};
/* === 【原来 TUICallKit 对外暴露】=== */
// 原来 web callKit 定义通知外部状态变更的变量, 对外暴露
export const StatusChange = {
IDLE: "idle",
BE_INVITED: "be-invited",
DIALING_C2C: "dialing-c2c",
DIALING_GROUP: "dialing-group",
CALLING_C2C_AUDIO: "calling-c2c-audio",
CALLING_C2C_VIDEO: "calling-c2c-video",
CALLING_GROUP_AUDIO: "calling-group-audio",
CALLING_GROUP_VIDEO: "calling-group-video",
} as const;
/* === 【小程序使用】=== */
/**
* @property {String} ear
* @property {String} speaker
*/
export enum AudioPlayBackDevice {
EAR = 'ear',
SPEAKER = 'speaker',
};
export enum DeviceType {
MICROPHONE = 'microphone',
CAMERA = 'camera',
SPEAKER = 'speaker',
}
export enum CameraPosition {
FRONT = 0,
BACK = 1,
}

View File

@ -0,0 +1,2 @@
export declare const ErrorCode: any;
export declare const ErrorMessage: any;

View File

@ -0,0 +1,22 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ErrorMessage = exports.ErrorCode = void 0;
// 错误码
exports.ErrorCode = {
SWITCH_TO_AUDIO_CALL_FAILED: 60001,
SWITCH_TO_VIDEO_CALL_FAILED: 60002,
MICROPHONE_UNAVAILABLE: 60003,
CAMERA_UNAVAILABLE: 60004,
BAN_DEVICE: 60005,
NOT_SUPPORTED_WEBRTC: 60006,
ERROR_BLACKLIST: 20007,
};
exports.ErrorMessage = {
SWITCH_TO_AUDIO_CALL_FAILED: 'switchToAudioCall-call-failed',
SWITCH_TO_VIDEO_CALL_FAILED: 'switchToVideoCall-call-failed',
MICROPHONE_UNAVAILABLE: 'microphone-unavailable',
CAMERA_UNAVAILABLE: 'camera-unavailable',
BAN_DEVICE: 'ban-device',
NOT_SUPPORTED_WEBRTC: 'not-supported-webrtc',
ERROR_BLACKLIST: 'blacklist-user-tips'
};

View File

@ -0,0 +1,20 @@
// 错误码
export const ErrorCode: any = {
SWITCH_TO_AUDIO_CALL_FAILED: 60001,
SWITCH_TO_VIDEO_CALL_FAILED: 60002,
MICROPHONE_UNAVAILABLE: 60003,
CAMERA_UNAVAILABLE: 60004,
BAN_DEVICE: 60005,
NOT_SUPPORTED_WEBRTC: 60006,
ERROR_BLACKLIST: 20007,
} as const;
export const ErrorMessage: any = {
SWITCH_TO_AUDIO_CALL_FAILED: 'switchToAudioCall-call-failed',
SWITCH_TO_VIDEO_CALL_FAILED: 'switchToVideoCall-call-failed',
MICROPHONE_UNAVAILABLE: 'microphone-unavailable',
CAMERA_UNAVAILABLE: 'camera-unavailable',
BAN_DEVICE: 'ban-device',
NOT_SUPPORTED_WEBRTC: 'not-supported-webrtc',
ERROR_BLACKLIST: 'blacklist-user-tips'
} as const;

View File

@ -0,0 +1,12 @@
export * from './call';
export * from './error';
export * from './log';
export declare const CALL_DATA_KEY: any;
export declare const NAME: any;
export declare const AudioCallIcon = "https://web.sdk.qcloud.com/component/TUIKit/assets/call.png";
export declare const VideoCallIcon = "https://web.sdk.qcloud.com/component/TUIKit/assets/call-video-reverse.svg";
export declare const MAX_NUMBER_ROOM_ID = 2147483647;
export declare enum PLATFORM {
MAC = "mac",
WIN = "win"
}

View File

@ -0,0 +1,62 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.PLATFORM = exports.MAX_NUMBER_ROOM_ID = exports.VideoCallIcon = exports.AudioCallIcon = exports.NAME = exports.CALL_DATA_KEY = void 0;
__exportStar(require("./call"), exports);
__exportStar(require("./error"), exports);
__exportStar(require("./log"), exports);
// import { keys } from 'ts-transformer-keys';
// import { ICallStore } from '../interface/store';
// console.warn('--> ', keys<ICallStore>())
exports.CALL_DATA_KEY = {
CALL_STATUS: 'callStatus',
CALL_ROLE: 'callRole',
CALL_MEDIA_TYPE: 'callMediaType',
LOCAL_USER_INFO: 'localUserInfo',
LOCAL_USER_INFO_EXCLUDE_VOLUMN: 'localUserInfoExcludeVolume',
REMOTE_USER_INFO_LIST: 'remoteUserInfoList',
REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST: 'remoteUserInfoExcludeVolumeList',
CALLER_USER_INFO: 'callerUserInfo',
IS_GROUP: 'isGroup',
CALL_DURATION: 'callDuration',
CALL_TIPS: 'callTips',
TOAST_INFO: 'toastInfo',
IS_MINIMIZED: 'isMinimized',
ENABLE_FLOAT_WINDOW: 'enableFloatWindow',
BIG_SCREEN_USER_ID: 'bigScreenUserId',
LANGUAGE: 'language',
IS_CLICKABLE: 'isClickable',
DISPLAY_MODE: 'displayMode',
VIDEO_RESOLUTION: 'videoResolution',
PUSHER: 'pusher',
PLAYER: 'player',
IS_EAR_PHONE: 'isEarPhone',
SHOW_PERMISSION_TIP: 'SHOW_PERMISSION_TIP',
GROUP_ID: 'groupID',
ROOM_ID: 'roomID',
SHOW_SELECT_USER: 'showSelectUser',
};
exports.NAME = Object.assign({ PREFIX: '【CallService】', AUDIO: 'audio', VIDEO: 'video', LOCAL_VIDEO: 'localVideo', ERROR: 'error', TIMEOUT: 'timeout', RAF: 'raf', INTERVAL: 'interval', DEFAULT: 'default', BOOLEAN: 'boolean', STRING: 'string', NUMBER: 'number', OBJECT: 'object', ARRAY: 'array', FUNCTION: 'function', UNDEFINED: "undefined", ALL: 'all', MYSELF: 'myself', DEVICE_LIST: 'deviceList', CAMERA_POSITION: 'cameraPosition' }, exports.CALL_DATA_KEY);
exports.AudioCallIcon = 'https://web.sdk.qcloud.com/component/TUIKit/assets/call.png';
exports.VideoCallIcon = 'https://web.sdk.qcloud.com/component/TUIKit/assets/call-video-reverse.svg';
exports.MAX_NUMBER_ROOM_ID = 2147483647;
var PLATFORM;
(function (PLATFORM) {
// eslint-disable-next-line no-unused-vars
PLATFORM["MAC"] = "mac";
// eslint-disable-next-line no-unused-vars
PLATFORM["WIN"] = "win";
})(PLATFORM = exports.PLATFORM || (exports.PLATFORM = {}));

View File

@ -0,0 +1,70 @@
export * from './call';
export * from './error';
export * from './log';
// import { keys } from 'ts-transformer-keys';
// import { ICallStore } from '../interface/store';
// console.warn('--> ', keys<ICallStore>())
export const CALL_DATA_KEY: any = {
CALL_STATUS: 'callStatus',
CALL_ROLE: 'callRole',
CALL_MEDIA_TYPE: 'callMediaType',
LOCAL_USER_INFO: 'localUserInfo',
LOCAL_USER_INFO_EXCLUDE_VOLUMN: 'localUserInfoExcludeVolume',
REMOTE_USER_INFO_LIST: 'remoteUserInfoList',
REMOTE_USER_INFO_EXCLUDE_VOLUMN_LIST: 'remoteUserInfoExcludeVolumeList',
CALLER_USER_INFO: 'callerUserInfo',
IS_GROUP: 'isGroup',
CALL_DURATION: 'callDuration',
CALL_TIPS: 'callTips',
TOAST_INFO: 'toastInfo',
IS_MINIMIZED: 'isMinimized',
ENABLE_FLOAT_WINDOW: 'enableFloatWindow',
BIG_SCREEN_USER_ID: 'bigScreenUserId',
LANGUAGE: 'language',
IS_CLICKABLE: 'isClickable',
DISPLAY_MODE: 'displayMode',
VIDEO_RESOLUTION: 'videoResolution',
PUSHER: 'pusher',
PLAYER: 'player',
IS_EAR_PHONE: 'isEarPhone',
SHOW_PERMISSION_TIP: 'SHOW_PERMISSION_TIP',
GROUP_ID: 'groupID',
ROOM_ID: 'roomID',
SHOW_SELECT_USER: 'showSelectUser',
};
export const NAME = {
PREFIX: '【CallService】',
AUDIO: 'audio',
VIDEO: 'video',
LOCAL_VIDEO: 'localVideo',
ERROR: 'error',
TIMEOUT: 'timeout',
RAF: 'raf',
INTERVAL: 'interval',
DEFAULT: 'default',
BOOLEAN: 'boolean',
STRING: 'string',
NUMBER: 'number',
OBJECT: 'object',
ARRAY: 'array',
FUNCTION: 'function',
UNDEFINED: "undefined",
ALL: 'all',
MYSELF: 'myself',
DEVICE_LIST: 'deviceList',
CAMERA_POSITION: 'cameraPosition',
...CALL_DATA_KEY,
};
export const AudioCallIcon = 'https://web.sdk.qcloud.com/component/TUIKit/assets/call.png';
export const VideoCallIcon = 'https://web.sdk.qcloud.com/component/TUIKit/assets/call-video-reverse.svg';
export const MAX_NUMBER_ROOM_ID = 2147483647;
export enum PLATFORM {
// eslint-disable-next-line no-unused-vars
MAC = 'mac',
// eslint-disable-next-line no-unused-vars
WIN = 'win',
}

View File

@ -0,0 +1,7 @@
export declare enum LOG_LEVEL {
NORMAL = 0,
RELEASE = 1,
WARNING = 2,
ERROR = 3,
NONE = 4
}

View File

@ -0,0 +1,13 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.LOG_LEVEL = void 0;
/* eslint-disable */
// 唯一一个变量格式有问题的, 但是为了和原来 TUICallKit 对外暴露的保持一致
var LOG_LEVEL;
(function (LOG_LEVEL) {
LOG_LEVEL[LOG_LEVEL["NORMAL"] = 0] = "NORMAL";
LOG_LEVEL[LOG_LEVEL["RELEASE"] = 1] = "RELEASE";
LOG_LEVEL[LOG_LEVEL["WARNING"] = 2] = "WARNING";
LOG_LEVEL[LOG_LEVEL["ERROR"] = 3] = "ERROR";
LOG_LEVEL[LOG_LEVEL["NONE"] = 4] = "NONE";
})(LOG_LEVEL = exports.LOG_LEVEL || (exports.LOG_LEVEL = {}));

View File

@ -0,0 +1,9 @@
/* eslint-disable */
// 唯一一个变量格式有问题的, 但是为了和原来 TUICallKit 对外暴露的保持一致
export enum LOG_LEVEL {
NORMAL = 0, // 普通级别,日志量较多,接入时建议使用
RELEASE = 1, // release级别SDK 输出关键信息,生产环境时建议使用
WARNING = 2, // 告警级别SDK 只输出告警和错误级别的日志
ERROR = 3, // 错误级别SDK 只输出错误级别的日志
NONE = 4, // 无日志级别SDK 将不打印任何日志
}

5
TUICallKit/TUICallService/index.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
import TUICallService, { TUIGlobal, TUIStore } from './CallService/index';
import { StoreName, NAME, CallRole, CallMediaType, CallStatus, StatusChange, VideoResolution, VideoDisplayMode, AudioPlayBackDevice } from './const/index';
import { t } from './locales/index';
declare const TUICallKitServer: TUICallService;
export { TUIGlobal, TUIStore, StoreName, TUICallKitServer, NAME, CallStatus, CallRole, CallMediaType, StatusChange, VideoResolution, VideoDisplayMode, AudioPlayBackDevice, t, };

View File

@ -0,0 +1,44 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.t = exports.AudioPlayBackDevice = exports.VideoDisplayMode = exports.VideoResolution = exports.StatusChange = exports.CallMediaType = exports.CallRole = exports.CallStatus = exports.NAME = exports.TUICallKitServer = exports.StoreName = exports.TUIStore = exports.TUIGlobal = void 0;
const index_1 = __importStar(require("./CallService/index"));
Object.defineProperty(exports, "TUIGlobal", { enumerable: true, get: function () { return index_1.TUIGlobal; } });
Object.defineProperty(exports, "TUIStore", { enumerable: true, get: function () { return index_1.TUIStore; } });
const index_2 = require("./const/index");
Object.defineProperty(exports, "StoreName", { enumerable: true, get: function () { return index_2.StoreName; } });
Object.defineProperty(exports, "NAME", { enumerable: true, get: function () { return index_2.NAME; } });
Object.defineProperty(exports, "CallRole", { enumerable: true, get: function () { return index_2.CallRole; } });
Object.defineProperty(exports, "CallMediaType", { enumerable: true, get: function () { return index_2.CallMediaType; } });
Object.defineProperty(exports, "CallStatus", { enumerable: true, get: function () { return index_2.CallStatus; } });
Object.defineProperty(exports, "StatusChange", { enumerable: true, get: function () { return index_2.StatusChange; } });
Object.defineProperty(exports, "VideoResolution", { enumerable: true, get: function () { return index_2.VideoResolution; } });
Object.defineProperty(exports, "VideoDisplayMode", { enumerable: true, get: function () { return index_2.VideoDisplayMode; } });
Object.defineProperty(exports, "AudioPlayBackDevice", { enumerable: true, get: function () { return index_2.AudioPlayBackDevice; } });
const index_3 = require("./locales/index");
Object.defineProperty(exports, "t", { enumerable: true, get: function () { return index_3.t; } });
// 实例化
const TUICallKitServer = index_1.default.getInstance();
exports.TUICallKitServer = TUICallKitServer;

View File

@ -0,0 +1,23 @@
import TUICallService, { TUIGlobal, TUIStore } from './CallService/index';
import { StoreName, NAME, CallRole, CallMediaType, CallStatus, StatusChange, VideoResolution, VideoDisplayMode, AudioPlayBackDevice } from './const/index';
import { t } from './locales/index';
// 实例化
const TUICallKitServer = TUICallService.getInstance();
// 输出产物
export {
TUIGlobal,
TUIStore,
StoreName,
TUICallKitServer,
NAME,
CallStatus,
CallRole,
CallMediaType,
StatusChange,
VideoResolution,
VideoDisplayMode,
AudioPlayBackDevice,
t,
};

View File

@ -0,0 +1,93 @@
import { CallStatus, CallRole } from '../const/index';
/**
* @interface ITUICallService
*/
export interface ITUICallService {
/**
* Service
* @function
* @private
*/
init(params: any): void;
/**
* 1v1
* @function
* @param {SwitchUserStatusParams} options
*/
call(callParams: ICallParams): void;
}
type SDKAppID = {
SDKAppID: number;
} | {
sdkAppID: number;
};
export interface IInitParamsBase {
userID: string;
userSig: string;
tim?: any;
isFromChat?: boolean;
}
export type IInitParams = IInitParamsBase & SDKAppID;
export interface ICallParams {
userID: string;
type: number;
roomID?: number;
userData?: string;
timeout?: number;
offlinePushInfo?: IOfflinePushInfo;
}
export interface IGroupCallParams {
userIDList: Array<string>;
type: number;
groupID: string;
roomID?: number;
userData?: string;
timeout?: number;
offlinePushInfo?: IOfflinePushInfo;
}
export interface IUserInfo {
userId: string;
nick?: string;
avatar?: string;
remark?: string;
displayUserInfo?: string;
isAudioAvailable?: boolean;
isVideoAvailable?: boolean;
volume?: number;
isEnter?: boolean;
domId?: string;
}
export interface IOfflinePushInfo {
title?: string;
description?: string;
androidOPPOChannelID?: string;
extension: String;
}
export interface ICallbackParam {
beforeCalling?: (...args: any[]) => void;
afterCalling?: (...args: any[]) => void;
onMinimized?: (...args: any[]) => void;
onMessageSentByMe?: (...args: any[]) => void;
kickedOut?: (...args: any[]) => void;
statusChanged?: (...args: any[]) => void;
}
export interface ISelfInfoParams {
nickName: string;
avatar: string;
}
export interface IBellParams {
callRole?: CallRole;
callStatus?: CallStatus;
isMuteBell?: boolean;
calleeBellFilePath?: string;
}
export interface IInviteUserParams {
userIDList: string[];
offlinePushInfo?: IOfflinePushInfo;
}
export interface IJoinInGroupCallParams {
type: number;
groupID: string;
roomID: number;
}
export {};

View File

@ -0,0 +1,97 @@
import { CallStatus, CallRole } from '../const/index';
/**
* @interface ITUICallService
*/
export interface ITUICallService {
/**
* Service
* @function
* @private
*/
init(params: any): void;
/**
* 1v1
* @function
* @param {SwitchUserStatusParams} options
*/
call(callParams: ICallParams): void;
}
type SDKAppID = { SDKAppID: number; } | { sdkAppID: number; };
export interface IInitParamsBase {
userID: string;
userSig: string;
tim?: any;
isFromChat?: boolean;
}
export type IInitParams = IInitParamsBase & SDKAppID;
// call params interface
export interface ICallParams {
userID: string;
type: number;
roomID?: number;
userData?: string;
timeout?: number;
offlinePushInfo?: IOfflinePushInfo;
}
// groupCall params interface
export interface IGroupCallParams {
userIDList: Array<string>;
type: number;
groupID: string;
roomID?: number;
userData?: string;
timeout?: number;
offlinePushInfo?: IOfflinePushInfo;
}
// userInfo interface
export interface IUserInfo {
userId: string;
nick?: string;
avatar?: string;
remark?: string;
displayUserInfo?: string; // 远端用户信息展示: remark -> nick -> userId, 简化 UI 组件; 本地用户信息展示: nick -> userId
isAudioAvailable?: boolean; // 用来设置: 麦克风是否打开
isVideoAvailable?: boolean; // 用来设置: 摄像头是否打开
volume?: number;
isEnter?: boolean; // 远端用户, 用来控制预览远端是否显示 loading 效果; 本地用户, 用来控制 "呼叫"、"接通" 接通后显示的 loading 效果
domId?: string; // 播放流 dom 节点, localUserInfo 的 domId = 'localVideo'; remoteUserInfo 的 domId 就是 userId
}
export interface IOfflinePushInfo {
title?: string,
description?: string,
androidOPPOChannelID?: string,
extension: String
}
export interface ICallbackParam {
beforeCalling?: (...args: any[]) => void;
afterCalling?: (...args: any[]) => void;
onMinimized?: (...args: any[]) => void;
onMessageSentByMe?: (...args: any[]) => void;
kickedOut?: (...args: any[]) => void;
statusChanged?: (...args: any[]) => void;
}
export interface ISelfInfoParams {
nickName: string;
avatar: string;
}
export interface IBellParams {
callRole?: CallRole;
callStatus?: CallStatus;
isMuteBell?: boolean;
calleeBellFilePath?: string;
}
export interface IInviteUserParams {
userIDList: string[];
offlinePushInfo?: IOfflinePushInfo;
}
export interface IJoinInGroupCallParams {
type: number;
groupID: string;
roomID: number;
}

View File

@ -0,0 +1,36 @@
import { CallStatus, CallRole, CallMediaType, VideoDisplayMode, VideoResolution, TDeviceList, CameraPosition } from '../const/index';
import { IUserInfo } from './index';
export interface IToastInfo {
text: string;
type?: string;
}
export interface ICallStore {
callStatus: CallStatus;
callRole: CallRole;
callMediaType: CallMediaType;
localUserInfo: IUserInfo;
localUserInfoExcludeVolume: IUserInfo;
remoteUserInfoList: Array<IUserInfo>;
remoteUserInfoExcludeVolumeList: Array<IUserInfo>;
callerUserInfo: IUserInfo;
isGroup: boolean;
callDuration: string;
callTips: string;
toastInfo: IToastInfo;
isMinimized: boolean;
enableFloatWindow: boolean;
bigScreenUserId: string;
language: string;
isClickable: boolean;
showPermissionTip: boolean;
deviceList: TDeviceList;
groupID: string;
roomID: number;
cameraPosition: CameraPosition;
displayMode: VideoDisplayMode;
videoResolution: VideoResolution;
pusher: any;
player: any[];
isEarPhone: boolean;
showSelectUser: boolean;
}

View File

@ -0,0 +1,41 @@
import { CallStatus, CallRole, CallMediaType, VideoDisplayMode, VideoResolution, TDeviceList, CameraPosition } from '../const/index';
import { IUserInfo } from './index';
export interface IToastInfo {
text: string;
type?: string; // 默认 info 通知, 取值: 'info' | 'warn' | 'success' | 'error'
}
export interface ICallStore {
callStatus: CallStatus; // 当前的通话状态, 默认: 'idle'
callRole: CallRole; // 通话角色, 默认: 'callee'
callMediaType: CallMediaType; // 通话类型
localUserInfo: IUserInfo; // 自己的信息, 默认: { userId: '' }
localUserInfoExcludeVolume: IUserInfo; // 不包含音量的当前用户信息
remoteUserInfoList: Array<IUserInfo>; // 远端用户信息列表, 默认: []
remoteUserInfoExcludeVolumeList: Array<IUserInfo>; // 不包含音量的远端用户信息列表
// 被叫在未接通时,展示主叫的 userId、头像。但是如果主叫进入通话后再挂断此时被叫无法知道主叫的信息了。
// 因为目前 store 中仅提供了 remoteUserInfoList 数据,主叫离开后,被叫就没有主叫的信息了。因此考虑在 store 中增加 callerUserInfo 字段。
callerUserInfo: IUserInfo;
isGroup: boolean; // 是否是群组通话, 默认: false
callDuration: string; // 通话时长, 默认: '00:00:00'
callTips: string; // 通话提示的信息. 例如: '等待谁接听', 'xxx 拒绝通话', 'xxx 挂断通话'
toastInfo: IToastInfo; // 远端用户挂断、拒绝、超时、忙线等的 toast 提示信息
isMinimized: boolean; // 当前是否悬浮窗模式, 默认: false
enableFloatWindow: boolean, // 开启/关闭悬浮窗功能,默认: false
bigScreenUserId: string, // 当前大屏幕显示的 userID 用户
language: string; // 当前语言
isClickable: boolean; // 按钮是否可点击(呼叫后, '挂断' 按钮不可点击, 发送信令后才可以点击)
showPermissionTip: boolean; // 设备权限弹窗是否展示(如果有麦克风权限为 false如果没有麦克风也没有摄像头权限为 true
deviceList: TDeviceList;
groupID: string;
roomID: number;
cameraPosition: CameraPosition; // 前置或后置值为front, back
// TUICallKit 组件上的属性
displayMode: VideoDisplayMode; // 设置预览远端的画面显示模式, 默认: VideoDisplayMode.COVER
videoResolution: VideoResolution; // 设置视频分辨率, 默认: VideoResolution.RESOLUTION_480P
// 小程序相关属性
pusher: any;
player: any[];
isEarPhone: boolean; // 是否是听筒, 默认: false
showSelectUser: boolean;
}

View File

@ -0,0 +1,36 @@
/**
* @interface TUIGlobal
* @property {Object} global wxuniwindow
* @property {Boolean} isPC true pc
* @property {Boolean} isH5 true H5
* @property {Boolean} isWeChat true
* @property {Boolean} isApp true uniapp native app
* @property {Boolean} isUniPlatform true uniapp
* @property {Boolean} isOfficial true Demo
* @property {Boolean} isWIN true window系统pc
* @property {Boolean} isMAC true mac os系统pc
*/
export interface ITUIGlobal {
global: any;
isPC: boolean;
isH5: boolean;
isWeChat: boolean;
isApp: boolean;
isUniPlatform: boolean;
isOfficial: boolean;
isWIN: boolean;
isMAC: boolean;
/**
* TUIGlobal
* @function
* @private
*/
initEnv(): void;
/**
* isOfficial
* @function
* @param {number} SDKAppID SDKAppID
* @private
*/
initOfficial(SDKAppID: number): void;
}

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,38 @@
/**
* @interface TUIGlobal
* @property {Object} global wxuniwindow
* @property {Boolean} isPC true pc
* @property {Boolean} isH5 true H5
* @property {Boolean} isWeChat true
* @property {Boolean} isApp true uniapp native app
* @property {Boolean} isUniPlatform true uniapp
* @property {Boolean} isOfficial true Demo
* @property {Boolean} isWIN true window系统pc
* @property {Boolean} isMAC true mac os系统pc
*/
export interface ITUIGlobal {
global: any; // 挂在 wx、uni、window 对象
isPC: boolean;
isH5: boolean;
isWeChat: boolean;
isApp: boolean;
isUniPlatform: boolean;
isOfficial: boolean;
isWIN: boolean;
isMAC: boolean;
/**
* TUIGlobal
* @function
* @private
*/
initEnv(): void;
/**
* isOfficial
* @function
* @param {number} SDKAppID SDKAppID
* @private
*/
initOfficial(SDKAppID: number): void;
}

View File

@ -0,0 +1,75 @@
import { StoreName } from '../const/index';
export type Task = Record<StoreName, Record<string, Map<(data?: unknown) => void, 1>>>;
export interface IOptions {
[key: string]: (newData?: any) => void;
}
/**
* @class TUIStore
* @property {ICustomStore} customStore store API
*/
export interface ITUIStore {
task: Task;
/**
* UI
* @function
* @param {StoreName} storeName store
* @param {IOptions} options UI
* @param {Object} params
* @param {String} params.notifyRangeWhenWatch
* @example
* // UI 层监听会话列表更新通知
* let onConversationListUpdated = function(conversationList) {
* console.warn(conversationList);
* }
* TUIStore.watch(StoreName.CONV, {
* conversationList: onConversationListUpdated,
* })
*/
watch(storeName: StoreName, options: IOptions, params?: any): void;
/**
* UI
* @function
* @param {StoreName} storeName store
* @param {IOptions} options
* @example
* // UI 层取消监听会话列表更新通知
* TUIStore.unwatch(StoreName.CONV, {
* conversationList: onConversationListUpdated,
* })
*/
unwatch(storeName: StoreName, options: IOptions | string): void;
/**
* store
* @function
* @param {StoreName} storeName store
* @param {String} key key
* @private
*/
getData(storeName: StoreName, key: string): any;
/**
* store
* - 使 store API
* @function
* @param {StoreName} storeName store
* @param {String} key key
* @example
* // UI 层更新自定义 Store 数据
* TUIStore.update(StoreName.CUSTOM, 'customKey', 'customData')
*/
update(storeName: StoreName, key: string, data: unknown): void;
/**
* store
* @function
* @param {StoreName} storeName store
* @param {Array<string>} keyList reset keyList
* @param {boolean} isNotificationNeeded
* @private
*/
reset: (storeName: StoreName, keyList?: Array<string>, isNotificationNeeded?: boolean) => void;
/**
* key-value
* @param {Object} params key-value object
* @param {StoreName} storeName store
*/
updateStore: (params: any, name?: StoreName) => void;
}

View File

@ -0,0 +1,2 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });

View File

@ -0,0 +1,85 @@
import { StoreName } from '../const/index';
// 此处 Map 的 value = 1 为占位作用
export type Task = Record<StoreName, Record<string, Map<(data?: unknown) => void, 1>>>
export interface IOptions {
[key: string]: (newData?: any) => void;
}
/**
* @class TUIStore
* @property {ICustomStore} customStore store API
*/
export interface ITUIStore {
task: Task;
/**
* UI
* @function
* @param {StoreName} storeName store
* @param {IOptions} options UI
* @param {Object} params
* @param {String} params.notifyRangeWhenWatch
* @example
* // UI 层监听会话列表更新通知
* let onConversationListUpdated = function(conversationList) {
* console.warn(conversationList);
* }
* TUIStore.watch(StoreName.CONV, {
* conversationList: onConversationListUpdated,
* })
*/
watch(storeName: StoreName, options: IOptions, params?: any): void;
/**
* UI
* @function
* @param {StoreName} storeName store
* @param {IOptions} options
* @example
* // UI 层取消监听会话列表更新通知
* TUIStore.unwatch(StoreName.CONV, {
* conversationList: onConversationListUpdated,
* })
*/
unwatch(storeName: StoreName, options: IOptions | string): void;
/**
* store
* @function
* @param {StoreName} storeName store
* @param {String} key key
* @private
*/
getData(storeName: StoreName, key: string): any;
/**
* store
* - 使 store API
* @function
* @param {StoreName} storeName store
* @param {String} key key
* @example
* // UI 层更新自定义 Store 数据
* TUIStore.update(StoreName.CUSTOM, 'customKey', 'customData')
*/
update(storeName: StoreName, key: string, data: unknown): void;
/**
* store
* @function
* @param {StoreName} storeName store
* @param {Array<string>} keyList reset keyList
* @param {boolean} isNotificationNeeded
* @private
*/
reset: (storeName: StoreName, keyList?: Array<string>, isNotificationNeeded?: boolean) => void;
/**
* key-value
* @param {Object} params key-value object
* @param {StoreName} storeName store
*/
updateStore: (params: any, name?: StoreName) => void;
}

View File

@ -0,0 +1,4 @@
export * from './ICallService';
export * from './ICallStore';
export * from './ITUIGlobal';
export * from './ITUIStore';

View File

@ -0,0 +1,20 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __exportStar = (this && this.__exportStar) || function(m, exports) {
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
};
Object.defineProperty(exports, "__esModule", { value: true });
__exportStar(require("./ICallService"), exports);
__exportStar(require("./ICallStore"), exports);
__exportStar(require("./ITUIGlobal"), exports);
__exportStar(require("./ITUIStore"), exports);

View File

@ -0,0 +1,4 @@
export * from './ICallService';
export * from './ICallStore';
export * from './ITUIGlobal';
export * from './ITUIStore';

View File

@ -0,0 +1,101 @@
export declare const en: {
hangup: string;
reject: string;
accept: string;
camera: string;
microphone: string;
speaker: string;
'open camera': string;
'close camera': string;
'open microphone': string;
'close microphone': string;
'video-to-audio': string;
'other side reject call': string;
'reject call': string;
cancel: string;
'other side line busy': string;
'in busy': string;
'call timeout': string;
'no response from the other side': string;
'end call': string;
'caller calling message': string;
'callee calling video message': string;
'callee calling audio message': string;
'no microphone access': string;
'no camera access': string;
'invite member': string;
'Invited group call': string;
waiting: string;
me: string;
'browser-authorization': string;
'mac-privacy': string;
'win-privacy': string;
'mac-preferences': string;
'win-preferences': string;
'Please enter userID': string;
'View more': string;
'people selected': string;
'Select all': string;
Cancel: string;
Done: string;
'camera enabled': string;
'camera disabled': string;
'microphone enabled': string;
'microphone disabled': string;
'speaker phone': string;
'ear piece': string;
'wait to be called': string;
answered: string;
'people in the call': string;
'failed to obtain speakers': string;
'you have a new call': string;
'Those involved': string;
call: string;
'video-call': string;
'audio-call': string;
search: string;
'search-result': string;
'no-user': string;
'member-not-added': string;
'input-phone-userID': string;
'not-login': string;
'login-status-expire': string;
'experience-multi-call': string;
'not-support-multi-call': string;
userID: string;
'already-enter': string;
'camera-opened': string;
'camera-closed': string;
'microphone-opened': string;
'microphone-closed': string;
timeout: string;
'kick out': string;
'image-resolution': string;
'default-image-resolution': string;
'invited-person': string;
'be-rejected': string;
'be-no-response': string;
'be-line-busy': string;
'be-canceled': string;
'voice-call-end': string;
'video-call-end': string;
'method-call-failed': string;
'failed-to-obtain-permission': string;
'environment-detection-failed': string;
'switchToAudioCall-call-failed': string;
'switchToVideoCall-call-failed': string;
'microphone-unavailable': string;
'camera-unavailable': string;
'ban-device': string;
'not-supported-webrtc': string;
'blacklist-user-tips': string;
'is-already-calling': string;
'need-init': string;
"can't call yourself": string;
'Use-phone-and-computer': string;
'Wechat scan right QR code': string;
'Scan the QR code above': string;
'accept-error': string;
'accept-device-error': string;
'call-error': string;
};

View File

@ -0,0 +1,110 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.en = void 0;
exports.en = {
// 按钮文案
'hangup': 'Hang up',
'reject': 'Decline',
'accept': 'Accept',
'camera': 'Camera',
'microphone': 'Microphone',
'speaker': 'speaker',
'open camera': 'Open Camera',
'close camera': 'Close Camera',
'open microphone': 'Open Microphone',
'close microphone': 'Close Microphone',
'video-to-audio': 'Switch to audio',
// 通话结果
'other side reject call': 'other side reject call',
'reject call': 'Reject Call',
'cancel': 'Cancel Call',
'other side line busy': 'other side line busy',
'in busy': 'in busy',
'call timeout': 'call timeout',
'no response from the other side': 'no response from the other side',
'end call': 'end call',
// 通话提示语
'caller calling message': 'Awaiting response',
'callee calling video message': 'invites you to a video call',
'callee calling audio message': 'invites you to a voice call',
'no microphone access': 'no microphone access',
'no camera access': 'no camera access',
'invite member': 'Invite Member',
'Invited group call': 'invites you to a group call',
'waiting': 'Calling...',
'me': '(me)',
// 弹出层文案
'browser-authorization': 'Browser authorization',
'mac-privacy': 'System Preferences -> Security and Privacy -> Privacy',
'win-privacy': 'Setting -> Privacy and Security -> App permissions',
'mac-preferences': 'Open System Preferences',
'win-preferences': 'Open Setting',
'Please enter userID': 'Please enter userID',
'View more': 'View more',
'people selected': 'people selected',
'Select all': 'Select all',
'Cancel': 'Cancel',
'Done': 'Done',
// UI3.0 新增
'camera enabled': 'Camera On',
'camera disabled': 'Camera Off',
'microphone enabled': 'Unmuted',
'microphone disabled': 'Muted',
'speaker phone': 'Speaker',
'ear piece': 'Earpiece',
'wait to be called': 'Waiting',
'answered': 'Connected',
'people in the call': ' other(s) in the call',
'failed to obtain speakers': 'failed to obtain speakers',
'you have a new call': 'You have a new call',
// 待废弃文案
'Those involved': 'Those involved in the call are',
'call': 'call',
'video-call': 'video call',
'audio-call': 'audio call',
'search': 'search',
'search-result': 'search result',
'no-user': 'user not found',
'member-not-added': 'member not added',
'input-phone-userID': 'phone number or userID',
'not-login': 'not logged in',
'login-status-expire': 'login status is invalid, please refresh the page and try again',
'experience-multi-call': 'experience multi-person calls, please download the full-featured demo: ',
'not-support-multi-call': 'multi-person call interface is not open',
'userID': 'userID',
'already-enter': 'entered the call',
'camera-opened': 'Camera on',
'camera-closed': 'Camera off',
'microphone-opened': 'Mic on',
'microphone-closed': 'Mic off',
'timeout': 'timeout',
'kick out': 'kick out',
'image-resolution': 'Resolution',
'default-image-resolution': 'Default',
'invited-person': 'Invite',
'be-rejected': 'Call declined, ',
'be-no-response': 'No response, ',
'be-line-busy': 'Line busy, ',
'be-canceled': 'The call is canceled, ',
'voice-call-end': 'Voice call ended',
'video-call-end': 'Video call ended',
'method-call-failed': 'Failed to sync the operation',
'failed-to-obtain-permission': 'Failed to obtain permissions',
'environment-detection-failed': 'Failed to check the environment',
'switchToAudioCall-call-failed': 'switch to audio call method failed',
'switchToVideoCall-call-failed': 'switch to video call method failed',
'microphone-unavailable': 'No mic found',
'camera-unavailable': 'No camera found',
'ban-device': 'Device access denied',
'not-supported-webrtc': 'Your current environment does not support WebRTC',
'blacklist-user-tips': 'The identifier is in blacklist. Failed to send this message!',
'is-already-calling': 'TUICallKit is already on a call',
'need-init': 'Before initiating a call with TUICallKit, ensure that the TUICallKitServer.init() method has executed successfully. ',
"can't call yourself": "Can't call yourself",
'Use-phone-and-computer': 'Use your mobile phone and computer to experience video calls',
'Wechat scan right QR code': 'Wechat scan right QR code',
'Scan the QR code above': 'Scan the QR code above',
'accept-error': 'Accept failed',
'accept-device-error': 'Accept failed, unable to auth calling device',
'call-error': 'Start call failed',
};

View File

@ -0,0 +1,107 @@
export const en = {
// 按钮文案
'hangup': 'Hang up',
'reject': 'Decline',
'accept': 'Accept',
'camera': 'Camera',
'microphone': 'Microphone',
'speaker': 'speaker',
'open camera': 'Open Camera',
'close camera': 'Close Camera',
'open microphone': 'Open Microphone',
'close microphone': 'Close Microphone',
'video-to-audio': 'Switch to audio',
// 通话结果
'other side reject call': 'other side reject call',
'reject call': 'Reject Call',
'cancel': 'Cancel Call',
'other side line busy': 'other side line busy',
'in busy': 'in busy',
'call timeout': 'call timeout',
'no response from the other side': 'no response from the other side',
'end call': 'end call',
// 通话提示语
'caller calling message': 'Awaiting response',
'callee calling video message': 'invites you to a video call',
'callee calling audio message': 'invites you to a voice call',
'no microphone access': 'no microphone access',
'no camera access': 'no camera access',
'invite member': 'Invite Member',
'Invited group call': 'invites you to a group call',
'waiting': 'Calling...',
'me': '(me)',
// 弹出层文案
'browser-authorization': 'Browser authorization',
'mac-privacy': 'System Preferences -> Security and Privacy -> Privacy',
'win-privacy': 'Setting -> Privacy and Security -> App permissions',
'mac-preferences': 'Open System Preferences',
'win-preferences': 'Open Setting',
'Please enter userID': 'Please enter userID',
'View more': 'View more',
'people selected': 'people selected',
'Select all': 'Select all',
'Cancel': 'Cancel',
'Done': 'Done',
// UI3.0 新增
'camera enabled': 'Camera On',
'camera disabled': 'Camera Off',
'microphone enabled': 'Unmuted',
'microphone disabled': 'Muted',
'speaker phone': 'Speaker',
'ear piece': 'Earpiece',
'wait to be called': 'Waiting',
'answered': 'Connected',
'people in the call': ' other(s) in the call',
'failed to obtain speakers': 'failed to obtain speakers',
'you have a new call': 'You have a new call',
// 待废弃文案
'Those involved': 'Those involved in the call are',
'call': 'call',
'video-call': 'video call',
'audio-call': 'audio call',
'search': 'search',
'search-result': 'search result',
'no-user': 'user not found',
'member-not-added': 'member not added',
'input-phone-userID': 'phone number or userID',
'not-login': 'not logged in',
'login-status-expire': 'login status is invalid, please refresh the page and try again',
'experience-multi-call': 'experience multi-person calls, please download the full-featured demo: ',
'not-support-multi-call': 'multi-person call interface is not open',
'userID': 'userID',
'already-enter': 'entered the call',
'camera-opened': 'Camera on',
'camera-closed': 'Camera off',
'microphone-opened': 'Mic on',
'microphone-closed': 'Mic off',
'timeout': 'timeout',
'kick out': 'kick out',
'image-resolution': 'Resolution',
'default-image-resolution': 'Default',
'invited-person': 'Invite',
'be-rejected': 'Call declined, ',
'be-no-response': 'No response, ',
'be-line-busy': 'Line busy, ',
'be-canceled': 'The call is canceled, ',
'voice-call-end': 'Voice call ended',
'video-call-end': 'Video call ended',
'method-call-failed': 'Failed to sync the operation',
'failed-to-obtain-permission': 'Failed to obtain permissions',
'environment-detection-failed': 'Failed to check the environment',
'switchToAudioCall-call-failed': 'switch to audio call method failed',
'switchToVideoCall-call-failed': 'switch to video call method failed',
'microphone-unavailable': 'No mic found',
'camera-unavailable': 'No camera found',
'ban-device': 'Device access denied',
'not-supported-webrtc': 'Your current environment does not support WebRTC',
'blacklist-user-tips': 'The identifier is in blacklist. Failed to send this message!',
'is-already-calling': 'TUICallKit is already on a call',
'need-init': 'Before initiating a call with TUICallKit, ensure that the TUICallKitServer.init() method has executed successfully. ',
"can't call yourself": "Can't call yourself", // eslint-disable-line
'Use-phone-and-computer': 'Use your mobile phone and computer to experience video calls',
'Wechat scan right QR code': 'Wechat scan right QR code',
'Scan the QR code above': 'Scan the QR code above',
'accept-error': 'Accept failed',
'accept-device-error': 'Accept failed, unable to auth calling device',
'call-error': 'Start call failed',
};

View File

@ -0,0 +1,10 @@
export declare const CallTips: any;
export declare const languageData: languageDataType;
export declare function t(key: any): string;
interface languageItemType {
[key: string]: string;
}
interface languageDataType {
[key: string]: languageItemType;
}
export {};

View File

@ -0,0 +1,52 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.t = exports.languageData = exports.CallTips = void 0;
const index_1 = require("../CallService/index");
const index_2 = require("../const/index");
const en_1 = require("./en");
const zh_cn_1 = require("./zh-cn");
const ja_JP_1 = require("./ja_JP");
exports.CallTips = {
OTHER_SIDE: 'other side',
CANCEL: 'cancel',
OTHER_SIDE_REJECT_CALL: 'other side reject call',
REJECT_CALL: 'reject call',
OTHER_SIDE_LINE_BUSY: 'other side line busy',
IN_BUSY: 'in busy',
CALL_TIMEOUT: 'call timeout',
END_CALL: 'end call',
TIMEOUT: 'timeout',
KICK_OUT: 'kick out',
CALLER_CALLING_MSG: 'caller calling message',
CALLER_GROUP_CALLING_MSG: 'wait to be called',
CALLEE_CALLING_VIDEO_MSG: 'callee calling video message',
CALLEE_CALLING_AUDIO_MSG: 'callee calling audio message',
NO_MICROPHONE_DEVICE_PERMISSION: 'no microphone access',
NO_CAMERA_DEVICE_PERMISSION: 'no camera access',
};
exports.languageData = {
en: en_1.en,
'zh-cn': zh_cn_1.zh,
ja_JP: ja_JP_1.ja_JP,
};
// language translate
function t(key) {
var _a;
const language = index_1.TUIStore.getData(index_2.StoreName.CALL, index_2.NAME.LANGUAGE);
// eslint-disable-next-line
for (const langKey in exports.languageData) {
if (langKey === language) {
const currentLanguage = exports.languageData[langKey];
// eslint-disable-next-line
for (const sentenceKey in currentLanguage) {
if (sentenceKey === key) {
return currentLanguage[sentenceKey];
}
}
}
}
const enString = (_a = key['en']) === null || _a === void 0 ? void 0 : _a.key; // eslint-disable-line
console.error(`${index_2.NAME.PREFIX}translation is not found: ${key}.`);
return enString;
}
exports.t = t;

View File

@ -0,0 +1,57 @@
import { TUIStore } from '../CallService/index';
import { NAME, StoreName } from '../const/index';
import { en } from './en';
import { zh } from './zh-cn';
import { ja_JP } from './ja_JP';
export const CallTips: any = {
OTHER_SIDE: 'other side',
CANCEL: 'cancel',
OTHER_SIDE_REJECT_CALL: 'other side reject call',
REJECT_CALL: 'reject call',
OTHER_SIDE_LINE_BUSY: 'other side line busy',
IN_BUSY: 'in busy',
CALL_TIMEOUT: 'call timeout',
END_CALL: 'end call',
TIMEOUT: 'timeout',
KICK_OUT: 'kick out',
CALLER_CALLING_MSG: 'caller calling message',
CALLER_GROUP_CALLING_MSG: 'wait to be called',
CALLEE_CALLING_VIDEO_MSG: 'callee calling video message',
CALLEE_CALLING_AUDIO_MSG: 'callee calling audio message',
NO_MICROPHONE_DEVICE_PERMISSION: 'no microphone access',
NO_CAMERA_DEVICE_PERMISSION: 'no camera access',
};
export const languageData: languageDataType = {
en,
'zh-cn': zh,
ja_JP,
};
// language translate
export function t(key: any): string {
const language = TUIStore.getData(StoreName.CALL, NAME.LANGUAGE);
// eslint-disable-next-line
for (const langKey in languageData) {
if (langKey === language) {
const currentLanguage = languageData[langKey];
// eslint-disable-next-line
for (const sentenceKey in currentLanguage) {
if (sentenceKey === key) {
return currentLanguage[sentenceKey];
}
}
}
}
const enString = key['en']?.key; // eslint-disable-line
console.error(`${NAME.PREFIX}translation is not found: ${key}.`);
return enString;
}
interface languageItemType {
[key: string]: string;
}
interface languageDataType {
[key: string]: languageItemType;
}

View File

@ -0,0 +1,99 @@
export declare const ja_JP: {
hangup: string;
reject: string;
accept: string;
camera: string;
microphone: string;
speaker: string;
'other side reject call': string;
'reject call': string;
cancel: string;
'other side line busy': string;
'in busy': string;
'call timeout': string;
'end call': string;
'caller calling message': string;
'callee calling video message': string;
'callee calling audio message': string;
'no microphone access': string;
'no camera access': string;
'invite member': string;
'browser-authorization': string;
'mac-privacy': string;
'win-privacy': string;
'mac-preferences': string;
'win-preferences': string;
'Please enter userID': string;
'View more': string;
'people selected': string;
'Select all': string;
Cancel: string;
Done: string;
'open camera': string;
'close camera': string;
'open microphone': string;
'close microphone': string;
'camera enabled': string;
'camera disabled': string;
'microphone enabled': string;
'microphone disabled': string;
'speaker phone': string;
'wait to be called': string;
answered: string;
'people in the call': string;
'failed to obtain speakers': string;
'you have a new call': string;
timeout: string;
'kick out': string;
'Invited group call': string;
'Those involved': string;
call: string;
'video-call': string;
'audio-call': string;
search: string;
'search-result': string;
'Wechat scan right QR code': string;
'Use-phone-and-computer': string;
'Scan the QR code above': string;
'no-user': string;
'member-not-added': string;
'not-login': string;
'login-status-expire': string;
'experience-multi-call': string;
'not-support-multi-call': string;
'input-phone-userID': string;
userID: string;
'already-enter': string;
waiting: string;
'camera-opened': string;
'camera-closed': string;
'microphone-opened': string;
'microphone-closed': string;
'image-resolution': string;
'default-image-resolution': string;
'invited-person': string;
'video-to-audio': string;
me: string;
'be-rejected': string;
'be-no-response': string;
'be-line-busy': string;
'be-canceled': string;
'voice-call-end': string;
'video-call-end': string;
'method-call-failed': string;
'failed-to-obtain-permission': string;
'environment-detection-failed': string;
'switchToAudioCall-call-failed': string;
'switchToVideoCall-call-failed': string;
'microphone-unavailable': string;
'camera-unavailable': string;
'ban-device': string;
'not-supported-webrtc': string;
'blacklist-user-tips': string;
'is-already-calling': string;
'need-init': string;
"can't call yourself": string;
'accept-error': string;
'accept-device-error': string;
'call-error': string;
};

View File

@ -0,0 +1,108 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ja_JP = void 0;
exports.ja_JP = {
// 按钮文案
'hangup': '通話終了',
'reject': '拒否',
'accept': '応答',
'camera': 'カメラ',
'microphone': 'マイク',
'speaker': 'スピーカー',
// 通话结果
'other side reject call': '通話が拒否されました',
'reject call': '通話拒否',
'cancel': '通話をキャンセル',
'other side line busy': '相手が通話中です',
'in busy': '通話中',
'call timeout': '呼び出しタイムアウト',
'end call': '通話終了',
// 通话提示语
'caller calling message': '応答を待っています',
'callee calling video message': 'ビデオ通話に招待されました',
'callee calling audio message': '音声通話に招待されました',
'no microphone access': 'マイクにアクセスできません',
'no camera access': 'カメラにアクセスできません',
'invite member': 'メンバーを招待する',
// 弹出层文案
'browser-authorization': 'ブラウザ認証',
'mac-privacy': 'システム環境設定 -> セキュリティとプライバシー ->プライバシー',
'win-privacy': '設定 -> セキュリティとプライバシー ->アプリのアクセス許可',
'mac-preferences': 'システム環境設定を開く',
'win-preferences': 'システム設定を開く',
'Please enter userID': 'ユーザーIDを入力してください',
'View more': 'もっと見る',
'people selected': '人が選択されました',
'Select all': 'すべて選択',
'Cancel': 'キャンセル',
'Done': '完了',
// UI3.0文案
'open camera': 'オープンカメラ',
'close camera': 'カメラを閉じる',
'open microphone': 'オープンマイク',
'close microphone': 'マイクを閉じる',
'camera enabled': 'カメラオン',
'camera disabled': 'カメラオフ',
'microphone enabled': 'マイクオン',
'microphone disabled': 'マイクオフ',
'speaker phone': 'スピーカーオン',
'wait to be called': '待機中',
'answered': '接続済み',
'people in the call': '通話に参加している人たち',
'failed to obtain speakers': 'スピーカーが見つかりません',
'you have a new call': '新しい通話があります',
// 待废弃文案
'timeout': 'タイムアウト',
'kick out': 'キックアウトされました',
'Invited group call': 'グループ通話に招待されました。',
'Those involved': '参加者:',
'call': '通話',
'video-call': 'ビデオ通話',
'audio-call': '音声通話',
'search': '検索',
'search-result': '検索結果',
'Wechat scan right QR code': 'WeChatで右側にあるQRコードを読み取ります。',
'Use-phone-and-computer': '携帯電話とコンピュータを使用してビデオ通話を体験してください',
'Scan the QR code above': '上のQRコードを読み取ります。',
'no-user': 'ユーザーが見つかりませんでした',
'member-not-added': 'メンバーが追加されていません',
'not-login': 'ログインしていません',
'login-status-expire': 'ログインの有効期限が過ぎています。ページを更新してもう一度お試しください',
'experience-multi-call': '複数人で同時に音声通話できるグループ通話機能を体験するには、全機能のデモをダウンロードしてください',
'not-support-multi-call': 'グループ通話インターフェイスが開いていません',
'input-phone-userID': '携帯電話番号/ユーザーIDを入力してください',
'userID': 'ユーザーID',
'already-enter': 'すでに通話に参加しています',
'waiting': '応答を待っています...',
'camera-opened': 'カメラがオンになっています',
'camera-closed': 'カメラがオフになっています',
'microphone-opened': 'マイクがオンになっています',
'microphone-closed': 'マイクがオフになっています',
'image-resolution': '解像度',
'default-image-resolution': 'デフォルト解像度',
'invited-person': 'メンバーを招待',
'video-to-audio': '音声通話に切り替えます',
'me': '(自分)',
'be-rejected': '通話が拒否されました, ',
'be-no-response': '応答なし, ',
'be-line-busy': '相手が通話中です, ',
'be-canceled': '相手が通話をキャンセルしました',
'voice-call-end': '音声通話が終了しました',
'video-call-end': 'ビデオ通話が終了しました',
'method-call-failed': '操作の同期に失敗しました',
'failed-to-obtain-permission': '権限の取得に失敗しました',
'environment-detection-failed': '環境の検出に失敗しました',
'switchToAudioCall-call-failed': '音声通話に切り替えることはできません',
'switchToVideoCall-call-failed': 'ビデオ通話に切り替えることはできません',
'microphone-unavailable': '使用できるマイクがありません',
'camera-unavailable': '使用できるカメラがありません',
'ban-device': 'デバイスへのアクセスが拒否されました',
'not-supported-webrtc': '現在の環境はWebRTCをサポートしていません',
'blacklist-user-tips': 'ユーザーはブラックリストに登録され、通話が開始できませんでした',
'is-already-calling': 'TUICallKit はすでに通話中です',
'need-init': 'TUICallKitで通話を開始する前に、TUICallKitServer.init() メソッドが正常に実行されたことを確認してください。',
"can't call yourself": '自分に電話をかけることができません',
'accept-error': '接続できませんでした',
'accept-device-error': '接続できませんでした。発信側デバイスを認証できません',
'call-error': '通話が開始できませんでした',
};

View File

@ -0,0 +1,105 @@
export const ja_JP = {
// 按钮文案
'hangup': '通話終了',
'reject': '拒否',
'accept': '応答',
'camera': 'カメラ',
'microphone': 'マイク',
'speaker': 'スピーカー',
// 通话结果
'other side reject call': '通話が拒否されました',
'reject call': '通話拒否',
'cancel': '通話をキャンセル',
'other side line busy': '相手が通話中です',
'in busy': '通話中',
'call timeout': '呼び出しタイムアウト',
'end call': '通話終了',
// 通话提示语
'caller calling message': '応答を待っています',
'callee calling video message': 'ビデオ通話に招待されました',
'callee calling audio message': '音声通話に招待されました',
'no microphone access': 'マイクにアクセスできません',
'no camera access': 'カメラにアクセスできません',
'invite member': 'メンバーを招待する',
// 弹出层文案
'browser-authorization': 'ブラウザ認証',
'mac-privacy': 'システム環境設定 -> セキュリティとプライバシー ->プライバシー',
'win-privacy': '設定 -> セキュリティとプライバシー ->アプリのアクセス許可',
'mac-preferences': 'システム環境設定を開く',
'win-preferences': 'システム設定を開く',
'Please enter userID': 'ユーザーIDを入力してください',
'View more': 'もっと見る',
'people selected': '人が選択されました',
'Select all': 'すべて選択',
'Cancel': 'キャンセル',
'Done': '完了',
// UI3.0文案
'open camera': 'オープンカメラ',
'close camera': 'カメラを閉じる',
'open microphone': 'オープンマイク',
'close microphone': 'マイクを閉じる',
'camera enabled': 'カメラオン',
'camera disabled': 'カメラオフ',
'microphone enabled': 'マイクオン',
'microphone disabled': 'マイクオフ',
'speaker phone': 'スピーカーオン',
'wait to be called': '待機中',
'answered': '接続済み',
'people in the call': '通話に参加している人たち',
'failed to obtain speakers': 'スピーカーが見つかりません',
'you have a new call': '新しい通話があります',
// 待废弃文案
'timeout': 'タイムアウト',
'kick out': 'キックアウトされました',
'Invited group call': 'グループ通話に招待されました。',
'Those involved': '参加者:',
'call': '通話',
'video-call': 'ビデオ通話',
'audio-call': '音声通話',
'search': '検索',
'search-result': '検索結果',
'Wechat scan right QR code': 'WeChatで右側にあるQRコードを読み取ります。',
'Use-phone-and-computer': '携帯電話とコンピュータを使用してビデオ通話を体験してください',
'Scan the QR code above': '上のQRコードを読み取ります。',
'no-user': 'ユーザーが見つかりませんでした',
'member-not-added': 'メンバーが追加されていません',
'not-login': 'ログインしていません',
'login-status-expire': 'ログインの有効期限が過ぎています。ページを更新してもう一度お試しください',
'experience-multi-call': '複数人で同時に音声通話できるグループ通話機能を体験するには、全機能のデモをダウンロードしてください',
'not-support-multi-call': 'グループ通話インターフェイスが開いていません',
'input-phone-userID': '携帯電話番号/ユーザーIDを入力してください',
'userID': 'ユーザーID',
'already-enter': 'すでに通話に参加しています',
'waiting': '応答を待っています...',
'camera-opened': 'カメラがオンになっています',
'camera-closed': 'カメラがオフになっています',
'microphone-opened': 'マイクがオンになっています',
'microphone-closed': 'マイクがオフになっています',
'image-resolution': '解像度',
'default-image-resolution': 'デフォルト解像度',
'invited-person': 'メンバーを招待',
'video-to-audio': '音声通話に切り替えます',
'me': '(自分)',
'be-rejected': '通話が拒否されました, ',
'be-no-response': '応答なし, ',
'be-line-busy': '相手が通話中です, ',
'be-canceled': '相手が通話をキャンセルしました',
'voice-call-end': '音声通話が終了しました',
'video-call-end': 'ビデオ通話が終了しました',
'method-call-failed': '操作の同期に失敗しました',
'failed-to-obtain-permission': '権限の取得に失敗しました',
'environment-detection-failed': '環境の検出に失敗しました',
'switchToAudioCall-call-failed': '音声通話に切り替えることはできません',
'switchToVideoCall-call-failed': 'ビデオ通話に切り替えることはできません',
'microphone-unavailable': '使用できるマイクがありません',
'camera-unavailable': '使用できるカメラがありません',
'ban-device': 'デバイスへのアクセスが拒否されました',
'not-supported-webrtc': '現在の環境はWebRTCをサポートしていません',
'blacklist-user-tips': 'ユーザーはブラックリストに登録され、通話が開始できませんでした',
'is-already-calling': 'TUICallKit はすでに通話中です',
'need-init': 'TUICallKitで通話を開始する前に、TUICallKitServer.init() メソッドが正常に実行されたことを確認してください。',
"can't call yourself": '自分に電話をかけることができません',
'accept-error': '接続できませんでした',
'accept-device-error': '接続できませんでした。発信側デバイスを認証できません',
'call-error': '通話が開始できませんでした',
};

View File

@ -0,0 +1,96 @@
export declare const zh: {
hangup: string;
reject: string;
accept: string;
camera: string;
microphone: string;
speaker: string;
'open camera': string;
'close camera': string;
'open microphone': string;
'close microphone': string;
'video-to-audio': string;
'other side reject call': string;
'reject call': string;
cancel: string;
'other side line busy': string;
'in busy': string;
'call timeout': string;
'end call': string;
'caller calling message': string;
'callee calling video message': string;
'callee calling audio message': string;
'no microphone access': string;
'no camera access': string;
'invite member': string;
'Invited group call': string;
'Those involved': string;
waiting: string;
me: string;
'browser-authorization': string;
'mac-privacy': string;
'win-privacy': string;
'mac-preferences': string;
'win-preferences': string;
'Please enter userID': string;
'View more': string;
'people selected': string;
'Select all': string;
Cancel: string;
Done: string;
'camera enabled': string;
'camera disabled': string;
'microphone enabled': string;
'microphone disabled': string;
'speaker phone': string;
'ear piece': string;
'wait to be called': string;
answered: string;
'people in the call': string;
'failed to obtain speakers': string;
'you have a new call': string;
timeout: string;
'kick out': string;
call: string;
'video-call': string;
'audio-call': string;
search: string;
'search-result': string;
'Wechat scan right QR code': string;
'Use-phone-and-computer': string;
'Scan the QR code above': string;
'no-user': string;
'member-not-added': string;
'not-login': string;
'login-status-expire': string;
'experience-multi-call': string;
'not-support-multi-call': string;
'input-phone-userID': string;
userID: string;
'already-enter': string;
'image-resolution': string;
'default-image-resolution': string;
'invited-person': string;
'be-rejected': string;
'be-no-response': string;
'be-line-busy': string;
'be-canceled': string;
'voice-call-end': string;
'video-call-end': string;
'method-call-failed': string;
'failed-to-obtain-permission': string;
'environment-detection-failed': string;
'switchToAudioCall-call-failed': string;
'switchToVideoCall-call-failed': string;
'microphone-unavailable': string;
'camera-unavailable': string;
'ban-device': string;
'not-supported-webrtc': string;
'blacklist-user-tips': string;
'is-already-calling': string;
'need-init': string;
"can't call yourself": string;
'accept-error': string;
'accept-device-error': string;
'call-error': string;
};

View File

@ -0,0 +1,105 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.zh = void 0;
exports.zh = {
// 按钮文案
'hangup': '挂断',
'reject': '拒绝',
'accept': '接受',
'camera': '摄像头',
'microphone': '麦克风',
'speaker': '扬声器',
'open camera': '打开摄像头',
'close camera': '关闭摄像头',
'open microphone': '打开麦克风',
'close microphone': '关闭麦克风',
'video-to-audio': '转语音通话',
// 通话结果
'other side reject call': '对方已拒绝',
'reject call': '拒绝通话',
'cancel': '取消通话',
'other side line busy': '对方忙线',
'in busy': '正在忙',
'call timeout': '呼叫超时',
'end call': '结束通话',
// 通话提示语
'caller calling message': '等待对方接受邀请',
'callee calling video message': '邀请你视频通话',
'callee calling audio message': '邀请你语音通话',
'no microphone access': '没有麦克风权限',
'no camera access': '没有摄像头权限',
'invite member': '邀请成员',
'Invited group call': '邀请你加入多人通话',
'Those involved': '参与通话的有:',
'waiting': '等待接听...',
'me': '(我)',
// 弹出层文案
'browser-authorization': '浏览器授权',
'mac-privacy': '系统偏好设置 -> 安全与隐私 -> 隐私',
'win-privacy': '设置 -> 隐私和安全性 -> 应用权限',
'mac-preferences': '打开系统偏好设置',
'win-preferences': '打开系统设置',
'Please enter userID': '请输入 userID',
'View more': '查看更多',
'people selected': '人已选中',
'Select all': '全选',
'Cancel': '取消',
'Done': '完成',
// UI3.0 新增
'camera enabled': '摄像头已开',
'camera disabled': '摄像头已关',
'microphone enabled': '麦克风已开',
'microphone disabled': '麦克风已关',
'speaker phone': '扬声器已开',
'ear piece': '扬声器已关',
'wait to be called': '等待接听',
'answered': '已接通',
'people in the call': '人参与通话',
'failed to obtain speakers': '无法获取扬声器',
'you have a new call': '您有一个新的通话',
// 待废弃文案
'timeout': '超时',
'kick out': '被踢',
'call': '通话',
'video-call': '视频通话',
'audio-call': '音频通话',
'search': '搜索',
'search-result': '搜索结果',
'Wechat scan right QR code': '微信扫右二维码',
'Use-phone-and-computer': '用手机与电脑互打体验视频通话',
'Scan the QR code above': '扫描上方二维码',
'no-user': '未搜索到用户',
'member-not-added': '未添加成员',
'not-login': '未登录',
'login-status-expire': '登录状态已失效,请刷新网页重试',
'experience-multi-call': '体验多人通话请下载全功能demo:',
'not-support-multi-call': '多人通话接口未开放',
'input-phone-userID': '请输入手机号/用户ID',
'userID': '用户ID',
'already-enter': '已经进入当前通话',
'image-resolution': '分辨率',
'default-image-resolution': '默认分辨率',
'invited-person': '添加成员',
'be-rejected': '对方已拒绝,',
'be-no-response': '对方无应答,',
'be-line-busy': '对方忙线中,',
'be-canceled': '对方已取消',
'voice-call-end': '语音通话结束',
'video-call-end': '视频通话结束',
'method-call-failed': '同步操作失败',
'failed-to-obtain-permission': '权限获取失败',
'environment-detection-failed': '环境检测失败',
'switchToAudioCall-call-failed': '切语音调用失败',
'switchToVideoCall-call-failed': '切视频调用失败',
'microphone-unavailable': '没有可用的麦克风设备',
'camera-unavailable': '没有可用的摄像头设备',
'ban-device': '用户禁止使用设备',
'not-supported-webrtc': '当前环境不支持 WebRTC',
'blacklist-user-tips': '发起通话失败,被对方拉入黑名单,禁止发起!',
'is-already-calling': 'TUICallKit 已在通话状态',
'need-init': 'TUICallKit 发起通话前需保证 TUICallKitServer.init() 方法执行成功',
"can't call yourself": '不能呼叫自己',
'accept-error': '接通失败',
'accept-device-error': '接通失败,通话设备获取失败',
'call-error': '发起通话失败',
};

View File

@ -0,0 +1,102 @@
export const zh = {
// 按钮文案
'hangup': '挂断',
'reject': '拒绝',
'accept': '接受',
'camera': '摄像头',
'microphone': '麦克风',
'speaker': '扬声器',
'open camera': '打开摄像头',
'close camera': '关闭摄像头',
'open microphone': '打开麦克风',
'close microphone': '关闭麦克风',
'video-to-audio': '转语音通话',
// 通话结果
'other side reject call': '对方已拒绝',
'reject call': '拒绝通话',
'cancel': '取消通话',
'other side line busy': '对方忙线',
'in busy': '正在忙',
'call timeout': '呼叫超时',
'end call': '结束通话',
// 通话提示语
'caller calling message': '等待对方接受邀请',
'callee calling video message': '邀请你视频通话',
'callee calling audio message': '邀请你语音通话',
'no microphone access': '没有麦克风权限',
'no camera access': '没有摄像头权限',
'invite member': '邀请成员',
'Invited group call': '邀请你加入多人通话',
'Those involved': '参与通话的有:',
'waiting': '等待接听...',
'me': '(我)',
// 弹出层文案
'browser-authorization': '浏览器授权',
'mac-privacy': '系统偏好设置 -> 安全与隐私 -> 隐私',
'win-privacy': '设置 -> 隐私和安全性 -> 应用权限',
'mac-preferences': '打开系统偏好设置',
'win-preferences': '打开系统设置',
'Please enter userID': '请输入 userID',
'View more': '查看更多',
'people selected': '人已选中',
'Select all': '全选',
'Cancel': '取消',
'Done': '完成',
// UI3.0 新增
'camera enabled': '摄像头已开',
'camera disabled': '摄像头已关',
'microphone enabled': '麦克风已开',
'microphone disabled': '麦克风已关',
'speaker phone': '扬声器已开',
'ear piece': '扬声器已关',
'wait to be called': '等待接听',
'answered': '已接通',
'people in the call': '人参与通话',
'failed to obtain speakers': '无法获取扬声器',
'you have a new call': '您有一个新的通话',
// 待废弃文案
'timeout': '超时',
'kick out': '被踢',
'call': '通话',
'video-call': '视频通话',
'audio-call': '音频通话',
'search': '搜索',
'search-result': '搜索结果',
'Wechat scan right QR code': '微信扫右二维码',
'Use-phone-and-computer': '用手机与电脑互打体验视频通话',
'Scan the QR code above': '扫描上方二维码',
'no-user': '未搜索到用户',
'member-not-added': '未添加成员',
'not-login': '未登录',
'login-status-expire': '登录状态已失效,请刷新网页重试',
'experience-multi-call': '体验多人通话请下载全功能demo:',
'not-support-multi-call': '多人通话接口未开放',
'input-phone-userID': '请输入手机号/用户ID',
'userID': '用户ID',
'already-enter': '已经进入当前通话',
'image-resolution': '分辨率',
'default-image-resolution': '默认分辨率',
'invited-person': '添加成员',
'be-rejected': '对方已拒绝,',
'be-no-response': '对方无应答,',
'be-line-busy': '对方忙线中,',
'be-canceled': '对方已取消',
'voice-call-end': '语音通话结束',
'video-call-end': '视频通话结束',
'method-call-failed': '同步操作失败',
'failed-to-obtain-permission': '权限获取失败',
'environment-detection-failed': '环境检测失败',
'switchToAudioCall-call-failed': '切语音调用失败',
'switchToVideoCall-call-failed': '切视频调用失败',
'microphone-unavailable': '没有可用的麦克风设备',
'camera-unavailable': '没有可用的摄像头设备',
'ban-device': '用户禁止使用设备',
'not-supported-webrtc': '当前环境不支持 WebRTC',
'blacklist-user-tips': '发起通话失败,被对方拉入黑名单,禁止发起!',
'is-already-calling': 'TUICallKit 已在通话状态',
'need-init': 'TUICallKit 发起通话前需保证 TUICallKitServer.init() 方法执行成功',
"can't call yourself": '不能呼叫自己', // eslint-disable-line
'accept-error': '接通失败',
'accept-device-error': '接通失败,通话设备获取失败',
'call-error': '发起通话失败',
};

Some files were not shown because too many files have changed in this diff Show More