微信小程序集成腾讯IM,实现实时音视频通话,1V1聊天

相关文章:

1.小程序聊天群,发送语音,文字,图片。

2.微信小程序集成腾讯IM,实现实时音视频通话,1V1聊天

3.云开发微信小程序聊天群

4.接入网易云信IM即时通讯的微信小程序聊天室

5.微信小程序聊天功能 WebSocket 实现发送文字,图片,语音以及WebSocket 常见问题解决方案

6.[微信小程序]聊天对话(文本,图片)的功能(完整代码附效果图)

 

效果图:

首先确保小程序基础库版本 2.2.1 或以上、及开发者工具 1.02.1808300 或以上。(才能支撑npm)

操作步骤:

1、打开cmd ,进入项目的根目录,初始化 npm。

初始化之后,能做小程序根目录看到一个package.json 的文件,就证明初始化成功,

2、在小程序项目根目录执行:

// IM 小程序 SDK
npm install tim-wx-sdk --save
// 发送图片、文件等消息需要的 COS SDK
npm install cos-wx-sdk-v5 --save

3、点击开发者工具右上角详情,勾选使用npm模块,不校验和合法域名。

4、打开小程序左上角,工具,点击构建npm,完成构建出来这些东西就算完成了

小程序实现代码:

js

// doc_yl/test/test.js
import TIM from 'tim-wx-sdk';
import COS from "cos-wx-sdk-v5";

var roomID, userID, to_user, userSig, to_user_head, conversationID;
// var userID = "user5";
// let to_user = 'user4';
// var userSig = "eJwtzEELgjAYxvHvsmshb5tbKnQICkwsMneoo7AVb2atTSuIvntDPT6-B-5fIvMyeGlLEkIDINN*o9L3Fs-Yc*e05ePhVF0Zg4oksxCAUQ5UDE*LjfbKI8bmsWB0UP0xaL0LCCOAsYEXX3WYvnMrD9212S2zonjE5famK3t6WgRcyf2xm2wyUad0vSC-P9irMVY_";

// "userID=user5&to_user=user4&userSig=eJwtzEELgjAYxvHvsmshb5tbKnQICkwsMneoo7AVb2atTSuIvntDPT6-B-5fIvMyeGlLEkIDINN*o9L3Fs-Yc*e05ePhVF0Zg4oksxCAUQ5UDE*LjfbKI8bmsWB0UP0xaL0LCCOAsYEXX3WYvnMrD9212S2zonjE5famK3t6WgRcyf2xm2wyUad0vSC-P9irMVY_&to_user_head=xxx.png&roomID=1002"
// var userID = "user4";
// let to_user = 'user5';
// var userSig = "eJwtzF0LwiAYhuH-4ulivTldOugwdpAQfUDQ2ZhWL5GY2lpE-z3ZdvhcNzxfclSHvDOeVITmQGbDRm1sxAsO-ArGsykEfW*cQ02qBQMoKAdajiXiwyTloiiWkksY1fQOffISmIDJAl7Ta4u2Cc8zFa4TsMs228zDXIFf39qmlqc67lVnS9V-*HtFfn*5vzDK";


// var conversationID = "C2C" + to_user;


let that, pageImResponse;
const sdkAppID = 1400325026;
let tim
Page({
  onLoad: function (options) {
    that = this;
    
let timOptions = {
  SDKAppID: sdkAppID // 接入时需要将0替换为您的即时通信 IM 应用的 SDKAppID
};
tim = TIM.create(timOptions); // SDK 实例通常用 tim 表示
tim.setLogLevel(0);
tim.registerPlugin({
  'cos-wx-sdk': COS
});
    userID = options.userID
    to_user = options.to_user
    userSig = options.userSig
    to_user_head = options.to_user_head
    roomID = options.roomID
    conversationID = "C2C" + to_user;
    console.log('11111111111111111111111111',to_user)
    this.setData({
      im_id: userID
    })
    that.im_on();
    let promise = tim.login({
      userID,
      userSig
    });
    promise.then(function (imResponse) {
      pageImResponse = imResponse;
      console.log('------登录成功', pageImResponse); // 登录成功
    }).catch(function (imError) {
      console.warn('------login error:', imError); // 登录失败的相关信息
    });
  },
  data: {
    vvvv: false,
    im_id: userID,
    inputValue: "",
    trtcConfig: {
      sdkAppID: sdkAppID, // 开通实时音视频服务创建应用后分配的 SDKAppID
      userID, // 用户 ID,可以由您的帐号系统指定
      userSig, // 身份签名,相当于登录密码的作用
      template: 'grid', // 画面排版模式
    },
    add: true
  },
  // 获取到焦点
  focus: function (e) {
    var that = this;
    console.log(e.detail.height)
    this.setData({
      focus: true,
      add: true,
      cross: false,
      input_bottom: e.detail.height
    })
  },
  // 失去焦点
  no_focus: function (e) {
    if (this.data.cross) {
      this.setData({
        focus: false,
        input_bottom: 240,
      })
    } else {
      this.setData({
        focus: false,
        input_bottom: 0
      })
    }
  },
  // 点击加号
  add_icon_click: function (e) {
    // e.target.id == 1 点击加号   ==2  点击 X
    if (e.target.id == 2) {
      this.setData({
        add: true,
        cross: false,
        input_bottom: 0
      })
    } else if (e.target.id == 1) {
      this.setData({
        add: false,
        cross: true,
        input_bottom: 240
      })
    }
  },
  // 输入框
  bindKeyInput: function (e) {
    if (e.detail.value == "") {
      this.setData({
        if_send: false,
        inputValue: e.detail.value
      })
    } else {
      this.setData({
        if_send: true,
        inputValue: e.detail.value
      })
    }
  },
  // 获取消息列表
  getList() {
    // 打开某个会话时,第一次拉取消息列表
      console.log('----------222222222打开某个会话时,第一次拉取消息列表')
    tim.getMessageList({
      conversationID: conversationID,
      count: 15
    }).then(function (imResponse) {
      console.log('----------222222222', imResponse)
      const messageList = imResponse.data.messageList; // 消息列表。
      const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
      const isCompleted = imResponse.data.isCompleted; // 表示是否已经拉完所有消息。

      that.setData({
        messageList
      })
      that.bottom()

      // 下拉查看更多消息
      // let promise = tim.getMessageList({ conversationID: conversationID, nextReqMessageID, count: 15 });
      // promise.then(function (imResponse) {
      //   const messageList = imResponse.data.messageList; // 消息列表。
      //   const nextReqMessageID = imResponse.data.nextReqMessageID; // 用于续拉,分页续拉时需传入该字段。
      //   const isCompleted = imResponse.data.isCompleted; // 表示是否已经拉完所有消息。
      // });
    });

  },
  // 发送视频通话
  sendv() {
    that.setData({
      vvvv: true
    }, () => {

      let trtcRoomContext = this.selectComponent('#trtcroom')
      let EVENT = trtcRoomContext.EVENT

      if (trtcRoomContext) {
        // 发布本地音频流和视频流
        trtcRoomContext.publishLocalVideo()
        trtcRoomContext.publishLocalAudio()

        // 监听远端用户的视频流的变更事件
        trtcRoomContext.on(EVENT.REMOTE_VIDEO_ADD, (event) => {
          console.error('--------监听远端用户的视频流的变更事件:', event)
          // 订阅(即播放)远端用户的视频流
          let v_userID = event.data.userID
          let streamType = event.data.streamType // 'main' or 'aux'            
          trtcRoomContext.subscribeRemoteVideo({
            userID: v_userID,
            streamType: streamType
          })
        })
        // 监听退出房间
        trtcRoomContext.on(EVENT.LOCAL_LEAVE, (event) => {
          console.error('--------监听退出房间:', event)
          that.setData({
            vvvv: false,
            cross: false,
            add: true,
            input_bottom: 0
          })
        })
        // 监听远端用户的音频流的变更事件
        trtcRoomContext.on(EVENT.REMOTE_AUDIO_ADD, (event) => {
          console.error('--------监听远端用户的音频流的变更事件:', event)
          // 订阅(即播放)远端用户的音频流
          let userID = event.data.userID
          trtcRoomContext.subscribeRemoteAudio(userID)
        })

        // 进入房间
        trtcRoomContext.enterRoom({
          roomID: roomID
        }).catch((res) => {
          console.error('--------room joinRoom 进房失败:', res)
          that.setData({
            vvvv: false
          })
        })
      }
    })
  },
  onUnload(){
    let promise = tim.logout();
promise.then(function(imResponse) {
  console.log(imResponse.data); // 登出成功
}).catch(function(imError) {
  console.warn('logout error:', imError);
});
    // tim=
  },
  // 监听IM
  im_on() {
    console.log('监听-------------------------')
    // 监听事件,例如:
    tim.on(TIM.EVENT.SDK_READY, function (event) {
      // 收到离线消息和会话列表同步完毕通知,接入侧可以调用 sendMessage 等需要鉴权的接口
      // event.name - TIM.EVENT.SDK_READY
      console.log('收到离线消息和会话列表同步完毕通知,接入侧可以调用')
      that.getList()

    });

    tim.on(TIM.EVENT.MESSAGE_RECEIVED, function (event) {
      // 收到推送的单聊、群聊、群提示、群系统通知的新消息,可通过遍历 event.data 获取消息列表数据并渲染到页面
      // event.name - TIM.EVENT.MESSAGE_RECEIVED
      // event.data - 存储 Message 对象的数组 - [Message]
      console.log('.----------收到推送的单聊、群聊、群提示、群系统通知的新消息', event.data)
      that.setMessageList(event.data[0])
    });

    tim.on(TIM.EVENT.MESSAGE_REVOKED, function (event) {
      // 收到消息被撤回的通知
      // event.name - TIM.EVENT.MESSAGE_REVOKED
      // event.data - 存储 Message 对象的数组 - [Message] - 每个 Message 对象的 isRevoked 属性值为 true
    });

    tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, function (event) {
      // 收到会话列表更新通知,可通过遍历 event.data 获取会话列表数据并渲染到页面
      // event.name - TIM.EVENT.CONVERSATION_LIST_UPDATED
      // event.data - 存储 Conversation 对象的数组 - [Conversation]
    });

    tim.on(TIM.EVENT.GROUP_LIST_UPDATED, function (event) {
      // 收到群组列表更新通知,可通过遍历 event.data 获取群组列表数据并渲染到页面
      // event.name - TIM.EVENT.GROUP_LIST_UPDATED
      // event.data - 存储 Group 对象的数组 - [Group]
    });

    tim.on(TIM.EVENT.GROUP_SYSTEM_NOTICE_RECEIVED, function (event) {
      // 收到新的群系统通知
      // event.name - TIM.EVENT.GROUP_SYSTEM_NOTICE_RECEIVED
      // event.data.type - 群系统通知的类型,详情请参见 GroupSystemNoticePayload 的 operationType 枚举值说明
      // event.data.message - Message 对象,可将 event.data.message.content 渲染到到页面
    });

    tim.on(TIM.EVENT.PROFILE_UPDATED, function (event) {
      // 收到自己或好友的资料变更通知
      // event.name - TIM.EVENT.PROFILE_UPDATED
      // event.data - 存储 Profile 对象的数组 - [Profile]
    });

    tim.on(TIM.EVENT.BLACKLIST_UPDATED, function (event) {
      // 收到黑名单列表更新通知
      // event.name - TIM.EVENT.BLACKLIST_UPDATED
      // event.data - 存储 userID 的数组 - [userID]
    });

    tim.on(TIM.EVENT.ERROR, function (event) {
      // 收到 SDK 发生错误通知,可以获取错误码和错误信息
      // event.name - TIM.EVENT.ERROR
      // event.data.code - 错误码
      // event.data.message - 错误信息
    });

    tim.on(TIM.EVENT.SDK_NOT_READY, function (event) {
      // 收到 SDK 进入 not ready 状态通知,此时 SDK 无法正常工作
      // event.name - TIM.EVENT.SDK_NOT_READY
    });

    tim.on(TIM.EVENT.KICKED_OUT, function (event) {
      // 收到被踢下线通知
      // event.name - TIM.EVENT.KICKED_OUT
      // event.data.type - 被踢下线的原因,例如:
      //    - TIM.TYPES.KICKED_OUT_MULT_ACCOUNT 多实例登录被踢
      //    - TIM.TYPES.KICKED_OUT_MULT_DEVICE 多终端登录被踢
      //    - TIM.TYPES.KICKED_OUT_USERSIG_EXPIRED 签名过期被踢
    });
  },
  // 发送图片
  sendImg() {
    // 小程序端发送图片
    // 1. 选择图片
    wx.chooseImage({
      sourceType: ['album'], // 从相册选择
      count: 1, // 只选一张,目前 SDK 不支持一次发送多张图片
      success: function (res) {
        // 2. 创建消息实例,接口返回的实例可以上屏
        let message = tim.createImageMessage({
          to: to_user,
          conversationType: TIM.TYPES.CONV_C2C,
          payload: {
            file: res
          },
          onProgress: function (event) {
            console.log('-------file uploading:', event)
          }
        });
        // 3. 发送图片
        let promise = tim.sendMessage(message);
        promise.then(function (imResponse) {
          // 发送成功
          console.log('----------发送图片:', imResponse);
          that.setMessageList(imResponse.data.message)
          that.setData({
            cross: false,
            add: true,
            input_bottom: 0
          })
        }).catch(function (imError) {
          // 发送失败
          console.warn('---------sendMessage error:', imError);
        });
      }
    })
  },
  // 添加聊天列表数据
  setMessageList(obj) {
    console.log('添加聊天列表数据')
    let messageList = that.data.messageList;
    messageList.push(obj)
    that.setData({
      messageList
    })
    that.bottom();
  },
  // 发送文字消息
  sendMessage() {
    console.log('----------', pageImResponse)
    if (pageImResponse) {

      // 1. 创建消息实例,接口返回的实例可以上屏
      let pageMessage = tim.createTextMessage({
        to: to_user,
        conversationType: TIM.TYPES.CONV_C2C,
        payload: {
          text: that.data.inputValue
        }
      });
      // 2. 发送消息
      tim.sendMessage(pageMessage).then(function (msg) {
        // 发送成功
        console.log('-----------------------111', msg);
        that.setMessageList(msg.data.message)
        that.setData({
          add: true,
          if_send: false,
          inputValue: ''
        })
      }).catch(function (imError) {
        // 发送失败
        console.log('-----------------------222', imError);
      });
    } else {
      setTimeout(() => {
        this.sendMessage()
      }, 500)
    }
  },
  onReady: function () {

  },

  // 获取hei的id节点然后屏幕焦点调转到这个节点  
  bottom: function () {
    var that = this;
    this.setData({
      scrollTop: 100000
    })
  },
  /**
   * 用户点击右上角分享
   */
  onShareAppMessage: function () {

  }
})

wxml

<!--doc_yl/test/test.wxml-->

<!-- <button catchtap="sendv">发送视频通话</button> -->
<view class="video" wx:if="{{vvvv}}">
  <trtc-room id="trtcroom" config="{{trtcConfig}}"></trtc-room>
</view>
<block wx:if="{{!vvvv}}">
  <scroll-view class="history" scroll-y="true" scroll-with-animation scroll-top="{{scrollTop}}">

    <block wx:key="{{index}}" wx:for="{{messageList}}">
      <block wx:if="{{item.from==im_id}}">
        <view class='my_right new_txt' wx:if='{{item._elements[0].type=="TIMTextElem"||item._elements[0].type=="TIMImageElem"}}'>
          <!-- <view class='time' wx:if='{{item.time}}'>
          {{item.time}}
        </view> -->
          <view class='p_r page_r' style='margin-right: 25rpx;'>
            <view class='new_txt'>
              <view class='new_txt_my'>
                <view class='arrow'>
                  <em></em>
                  <span></span>
                </view>
                <text wx:if='{{item._elements[0].type=="TIMTextElem"}}' decode="true">{{item.payload.text}}</text>
                <image class="pay_img" mode="widthFix" wx:if='{{item._elements[0].type=="TIMImageElem"}}' src="{{item._elements[0].content.imageInfoArray[0].imageUrl}}"></image>
              </view>
            </view>
            <view class='new_img'>
              <open-data type="userAvatarUrl"></open-data>
            </view>
          </view>
          <view class='p_r page_r' style='margin-right: 25rpx;' wx:if='{{item.audio}}' bindtap='my_audio_click' data-id='{{index}}'>
            <view class='new_txt_my_2' style=' width:{{item.length}}px'>
              <image class='my_audio' src='/images/yuyin_icon.png'></image>
            </view>
            <span class='_span'></span>
            <open-data class='new_img' type="userAvatarUrl"></open-data>
          </view>
        </view>
      </block>
      <!-- <view class='you_left' id='id_{{allContentList.length}}'> -->
      <block wx:if="{{item.from!=im_id}}">
        <view class='you_left' wx:if='{{item._elements[0].type=="TIMTextElem"||item._elements[0].type=="TIMImageElem"}}' style='width:100%;' id='id_{{allContentList.length}}'>
          <!-- <view class='time' wx:if='{{item.messageTime}}'>
          {{item.messageTime}}
        </view> -->
          <view class='p_r' style='margin-left: 20rpx;'>
            <image class='new_img' src='/img/xingx.png'></image>
            <view class='new_txt'>
              <view class='new_txt_ai'>
                <view class='arrow'>
                  <em></em>
                  <span></span>
                </view>
                <!-- {{item.text}} -->
                <view class='ai_content'>
                  <text decode="true" wx:if='{{item._elements[0].type=="TIMTextElem"}}'>{{item.payload.text}}</text>
                  <image class="pay_img" mode="widthFix" wx:if='{{item._elements[0].type=="TIMImageElem"}}' src="{{item._elements[0].content.imageInfoArray[0].imageUrl}}"></image>
                </view>
              </view>
            </view>
          </view>
        </view>
      </block>
    </block>
  </scroll-view>
  <!-- 遮罩 -->
  <view class='zezhao' wx:if='{{cross}}' bindtap='add_icon_click' id='2'></view>
  <!-- 输入框 -->
  <view class='{{cross?"in_voice_icon":""}}'>
    <view class="sendmessage" wx:if='{{!cross}}' style='bottom:{{input_bottom}}px'>
      <input type="text" style='{{focus?"border-bottom: 1px solid #88DD4B;":""}}' adjust-position='{{false}}' cursor-spacing='5' bindinput="bindKeyInput" value='{{inputValue}}' focus='{{focus}}' bindblur='no_focus' bindfocus="focus" confirm-type="done" placeholder="请输入您要咨询的问题"
      />
      <view wx:if='{{if_send&&inputValue!=""}}' bindtap="sendMessage" class='user_input_text'>发送</view>
      <image class='add_icon' bindtap='add_icon_click' id='1' wx:if='{{add&&!if_send&&inputValue==""}}' src='/img/add.png'></image>
      <image class='add_icon' bindtap='add_icon_click' id='2' wx:if='{{cross}}' src='/img/add.png'></image>

    </view>
    <block wx:if='{{cross}}'>
      <view class="p_r">
        <view class='item1' bindtap='sendImg'>
          <image class='img' src='/img/camera-true.png'></image>
          <view class='text'>发送图片</view>
        </view>
        <view class='item2' bindtap='sendv'>
          <image class='img' src='/img/camera-true.png'></image>
          <view class='text'>视频聊天</view>
        </view>
      </view>
    </block>
  </view>

  <!-- <view class='zezhao' wx:if='{{add_icon_click}}' bindtap='add_icon_click'></view> -->
  <!-- <view class='in_voice_icon'>
  <view class="sendmessage_2">
    <input type="text" bindinput="bindKeyInput" adjust-position='{{false}}' value='{{inputValue}}' focus='{{focus}}' bindfocus="focus" confirm-type="done" placeholder="" />
    <image class='add_icon' bindtap='add_icon_click' src='/images/audio/cross37.png'></image>
  </view>
  <view class='item' bindtap='sendImg'>
    <image class='img' src='/images/yuyin_icon.png'></image>
    <view class='text'>人工客服</view>
  </view>
</view> -->
</block>

css

page {
  background-color: #f2f2f2;
  height: 100%;
  padding: 0 auto;
  margin: 0 auto;
}

swiper {
  height: 180rpx;
}

swiper swiper-item .slide-image {
  width: 100%;
  height: 180rpx;
}

.jia_img {
  height: 80rpx;
  width: 90rpx;
}

.time {
  text-align: center;
  padding: 5rpx 20rpx 5rpx 20rpx;
  border-radius: 10rpx;
  display: block;
  height: 38rpx;
  line-height: 38rpx;
  position: relative;
  margin: 0 auto;
  margin-bottom: 20rpx;
  width: 90rpx;
  color: white;
  font-size: 26rpx;
  background-color: #dedede;
}

.tab {
  bottom: 120rpx;
}

.tab_1 {
  position: fixed;
  bottom: 50rpx;
  width: 200rpx;
  font-size: 26rpx;
  left: 50%;
  margin-left: -45rpx;
  height: 100rpx;
}

.tab_2 {
  right: 30rpx;
  position: fixed;
}

/* 聊天 */

.my_right {
  float: right;
  margin-top: 30rpx;
  position: relative;
}

.my_audio {
  height: 60rpx;
  width: 60rpx;
  z-index: 2;
  position: relative;
  top: 10rpx;
  left: 20rpx;
}

.you_left {
  margin-top: 30rpx;
  float: left;
  position: relative;
  padding-left: 5rpx;
}

.new_img {
  width: 85rpx;
  height: 85rpx;
  overflow: hidden;
  position: absolute;
  right: 30rpx;
}

.page_r {
  float: right;
}

.new_txt {
  min-width: 380rpx;
  width: 460rpx;
  word-break: break-all;
}
.pay_img{
  max-width: 400rpx;
}
.new_txt_my {
  border-radius: 7rpx;
  background: #9fe75a;
  position: relative;
  right: 120rpx;
  min-height: 50rpx;
  padding: 17rpx 30rpx 17rpx 30rpx;
  float: right;
  border: 1px solid #d0d0d0;
}

.new_txt_my .arrow {
  position: absolute;
  z-index: 2;
  width: 40rpx;
  right: -38rpx;
}

.new_txt_my .arrow em {
  position: absolute;
  border-style: solid;
  border-width: 15rpx;
  border-color: transparent transparent transparent #d0d0d0;
  top: 1rpx;
}

.new_txt_my .arrow span {
  position: absolute;
  top: 5rpx;
  border-style: solid;
  border-width: 15rpx;
  border-color: transparent transparent transparent #9fe75a;
}

.new_txt_my_2 {
  word-break: break-all;
  border-radius: 7rpx;
  background: #9fe75a;
  min-width: 330rpx;
  max-width: 530rpx;
  padding: 17rpx 30rpx 17rpx 30rpx;
  float: right;
}

.new_txt_ai {
  max-width: 460rpx;
  border-radius: 7rpx;
  left: 20rpx;
  background-color: #fff;
  position: relative;
  border: 1px solid #d0d0d0;
  float: left;
}

.new_txt_ai .arrow {
  position: relative;
  width: 40rpx;
  left: -30rpx;
}

.new_txt_ai .arrow em {
  position: absolute;
  border-style: solid;
  border-width: 15rpx;
  top: 20rpx;
  border-color: transparent #d0d0d0 transparent transparent;
}

.new_txt_ai .arrow span {
  position: absolute;
  top: 20rpx;
  border-style: solid;
  border-width: 15rpx;
  border-color: transparent #fff transparent transparent;
  left: 2rpx;
}

.ai_content {
  word-break: break-all;
  padding: 17rpx 30rpx 17rpx 30rpx;
  /* max-width:  */
}

.sanjiao {
  top: 25rpx;
  position: relative;
  width: 0px;
  height: 0px;
  border-width: 15rpx;
  border-style: solid;
}

.my {
  border-color: transparent transparent transparent #9fe75a;
}

.you {
  border-color: transparent #fff transparent transparent;
}

._span {
  border-color: #fff transparent transparent;
  top: -17px;
}

.is_ai_btn {
  border-radius: 0 0 7px 7px;
  border-top: 1px solid #d0d0d0;
  background: white;
  position: relative;
  bottom: 0;
  left: 0;
  width: 100%;
  height: 80rpx;
  line-height: 80rpx;
  display: flex;
  flex-direction: row;
  text-align: center;
}

.is_ai_btn view {
  width: 50%;
}

.is_ai_btn image {
  width: 32rpx;
  position: relative;
  top: 4rpx;
  height: 32rpx;
}

.is_ai_btn .two {
  border-left: 1px solid #d0d0d0;
}

.yes_problem_log {
  border-top: 1px solid #d0d0d0;
  height: 80rpx;
  text-align: center;
  line-height: 80rpx;
}

.voice_icon {
  width: 60rpx;
  height: 60rpx;
  margin: 0 auto;
  padding: 10rpx 10rpx 10rpx 10rpx;
}

.add_icon {
  width: 70rpx;
  height: 70rpx;
  margin: 0 auto;
  padding: 20rpx 10rpx 10rpx 15rpx;
  /* transform: rotate(45deg); */
}

.voice_ing {
  width: 90%;
  height: 75rpx;
  line-height: 85rpx;
  text-align: center;
  border-radius: 15rpx;
  border: 1px solid #d0d0d0;
}

.zezhao {
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: 100%;
  background: rgba(0, 0, 0, 0.5);
}

.in_voice_icon {
  z-index: 3;
  left: 0;
  bottom: 0;
  width: 100%;
  position: absolute;
  height: 500rpx;
  background: #f8f8f8;
}

.in_voice_icon .item1 {
  position: relative;
  left: 20%;
  margin-top: 180rpx;
  text-align: center;
  width: 120rpx;
}
.p_r{
  display: flex;
  flex-direction: row;
}
.video{
  position: fixed;
  top: 0;
  bottom: 0;
}
.in_voice_icon .item2 {
  position: relative;
  left: 50%;
  margin-top: 180rpx;
  text-align: center;
  width: 120rpx;
}

.in_voice_icon .img {
  width: 120rpx;
  height: 120rpx;
  border-radius: 15rpx;
}

.in_voice_icon .text {
  font-size: 32rpx;
  margin-top: 20rpx;
  background: white;
  width: 200rpx;
  margin-left: -40rpx;
  border-radius: 15rpx;
  height: 80rpx;
  line-height: 80rpx;
}

.sendmessage {
  width: 100%;
  z-index: 2;
  display: flex;
  position: fixed;
  bottom: 0px;
  background-color: #f8f8f8;
  flex-direction: row;
  height: 100rpx;
}

.sendmessage input {
  width: 78%;
  height: 80rpx;
  line-height: 80rpx;
  font-size: 28rpx;
  margin-top: 10rpx;
  margin-left: 20rpx;
  border-bottom: 1px solid #d0d0d0;
  padding-left: 20rpx;
}

.sendmessage .user_input_text {
  border: 1px solid white;
  width: 130rpx;
  height: 80rpx;
  background: #0c0;
  color: white;
  line-height: 80rpx;
  margin-top: 10rpx;
  text-align: center;
  font-size: 28rpx;
}

.hei {
  height: 20rpx;
}

.history {
  /* height: 73%; */
  height: 88%;
  display: flex;
  font-size: 14px;
  line-height: 50rpx;
  position: relative;
  top: 20rpx;
}

.icno_kf {
  position: fixed;
  bottom: 160rpx;
  margin: 0 auto;
  text-align: center;
  left: 50%;
  margin-left: -40rpx;
  width: 100rpx;
  height: 100rpx;
  border-radius: 50%;
}

 

已标记关键词 清除标记
©️2020 CSDN 皮肤主题: 成长之路 设计师:Amelia_0503 返回首页
实付 49.90元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值