uniapp仿微信聊天界面(vue3组合式版本)

打印 上一主题 下一主题

主题 891|帖子 891|积分 2673

先看效果图:



消息格式参照下方:

  1. <template>
  2.         <view class="chat-index">
  3.                 <scroll-view
  4.                         id="scrollview"
  5.                         class="scroll-style"
  6.                         :style="{height: `${windowHeight - inputHeight}rpx`}"
  7.                         scroll-y="true"
  8.                         :scroll-top="conf.scrollTop"
  9.                         @scrolltoupper="topRefresh"
  10.                         @click="touchClose"
  11.                 >
  12.                         <view id="msglistview" class="chat-body">
  13.                                 <view v-for="item,index in data.msgInfoList" :key="index">
  14.                                        
  15.                                         <!-- 消息发送时间 -->
  16.                                         <view class="time-box" v-if="item.showTime">
  17.                                                 <view class="time-style">
  18.                                                         <view>
  19.                                                                 {{ timeFormat(item.sendTime) }}
  20.                                                         </view>
  21.                                                 </view>
  22.                                         </view>
  23.                                        
  24.                                         <!-- 自己 -->
  25.                                         <view class="item self" v-if="item.scid == userInfo.scid">
  26.                                                
  27.                                                 <!-- 文本消息 -->
  28.                                                 <view class="content-text right" v-if="item.type=='text'">
  29.                                                         {{item.content}}
  30.                                                 </view>
  31.                                                
  32.                                                 <!-- 语音消息 -->
  33.                                                 <view class="content-text right" v-else-if="item.type=='voice'">
  34.                                                         <view style="display: flex;" @click="playSound(item.content)">
  35.                                                                 <text>{{ item.voiceLength }}''</text>
  36.                                                                 <image v-if="conf.playVoice" style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play_on.png"/>
  37.                                                                 <image v-else style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play.png"/>
  38.                                                         </view>
  39.                                                 </view>
  40.                                                
  41.                                                 <!-- 图片消息 -->
  42.                                                 <view class="content-img" v-else-if="item.type=='img'">
  43.                                                         <image class="img-style" :src="item.content"  mode="widthFix" :lazy-load="true"/>
  44.                                                 </view>
  45.                                                
  46.                                                 <!-- 视频消息 -->
  47.                                                 <view class="content-video" v-else>
  48.                                                         <video class="video-style" :src="item.content" />
  49.                                                 </view>
  50.                                                
  51.                                                 <!-- 头像 -->
  52.                                                 <image class="avatar" :src="userInfo.s_avatar" />
  53.                                         </view>
  54.                                        
  55.                                         <!-- 好友 -->
  56.                                         <view class="item Ai" v-else>
  57.                                                
  58.                                                 <!-- 头像 -->
  59.                                                 <image class="avatar" :src="userInfo.r_avatar" />
  60.                                                
  61.                                                 <!-- 文本消息 -->
  62.                                                 <view class="content-text left" v-if="item.type=='text'">
  63.                                                         {{item.content}}
  64.                                                 </view>
  65.                                                
  66.                                                 <!-- 语音消息 -->
  67.                                                 <view class="content-text left" v-else-if="item.type=='voice'">
  68.                                                         <view style="display: flex;" @click="playSound(item.content)">
  69.                                                                 <text>{{ item.voiceLength }}''</text>
  70.                                                                 <image v-if="conf.playVoice" style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play_on.png"/>
  71.                                                                 <image v-else style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play.png"/>
  72.                                                         </view>
  73.                                                 </view>
  74.                                                
  75.                                                 <!-- 图片消息 -->
  76.                                                 <view class="content-img" v-else-if="item.type=='img'">
  77.                                                         <image class="img-style" :src="item.content"  mode="widthFix" :lazy-load="true"/>
  78.                                                 </view>
  79.                                                
  80.                                                 <!-- 视频消息 -->
  81.                                                 <view class="content-video" v-else>
  82.                                                         <video class="video-style" :src="item.content" />
  83.                                                 </view>
  84.                                         </view>
  85.                                 </view>
  86.                         </view>
  87.                 </scroll-view>
  88.                
  89.                 <!-- 消息发送框 -->
  90.                 <view class="chat-bottom" :style="{height:`${inputHeight}rpx`}">
  91.                         <view class="input-msg-box" :style="{bottom:`${conf.keyboardHeight}rpx`}">
  92.                                
  93.                                 <!-- 输入框区域 -->
  94.                                 <view class="textarea-style">
  95.                                         <!-- 语音/文字输入 -->
  96.                                         <view class="voice-btn" @click="isVoice">
  97.                                                 <image class="icon-style"  v-if="conf.isVoice" src="../../static/icon/keyboard.png" />
  98.                                                 <image class="icon-style" v-else src="../../static/icon/voice.png" />       
  99.                                         </view>
  100.                                        
  101.                                         <!-- textarea输入框 -->
  102.                                         <view class="out_textarea_box" @click="() => conf.showMoreMenu=false">
  103.                                                 <textarea
  104.                                                         placeholder-class="textarea_placeholder"
  105.                                                         :style="{textAlign:(conf.textAreaDisabled?'center':'')}"
  106.                                                         v-model="sendMsg.text"
  107.                                                         maxlength="250"
  108.                                                         confirm-type="send"
  109.                                                         auto-height
  110.                                                         :placeholder="conf.textAreaText"
  111.                                                         :show-confirm-bar="false"
  112.                                                         :adjust-position="false"
  113.                                                         :disabled="conf.textAreaDisabled"
  114.                                                         @confirm="handleSend"
  115.                                                         @linechange="listenTextAreaHeight"
  116.                                                         @focus="scrollToBottom"
  117.                                                         @blur="scrollToBottom"
  118.                                                         @touchstart="handleTouchStart"
  119.                                                         @touchmove="handleTouchMove"
  120.                                                         @touchend="handleTouchEnd"
  121.                                                    />
  122.                                         </view>       
  123.                                                
  124.                                         <!-- 输入菜单 -->
  125.                                         <view class="more-btn">
  126.                                                 <image class="icon-style" src="../../static/icon/emoji.png" @click="handleSend"/>
  127.                                                 <image class="icon-style" style="margin-left: 20rpx;" src="../../static/icon/more.png" @click="showMoreMenuFunc"/>                               
  128.                                         </view>
  129.                        
  130.                                 </view>
  131.                                
  132.                                 <!-- 更多菜单 -->
  133.                                 <view :class="{'more-menu-box-max': conf.showMoreMenu,'more-menu-box-min': !conf.showMoreMenu}">
  134.                                         <view class="inner-menu-box">
  135.                                                 <view class="menu-box" @click="sendFile('choose','')">
  136.                                                         <view class="out-icon-area">
  137.                                                                 <image class="i-style" src="../../static/icon/photo.png" />
  138.                                                         </view>
  139.                                                         <view class="t-style">照片</view>
  140.                                                 </view>
  141.                                                 <view class="menu-box" @click="sendFile('shoot','')">
  142.                                                         <view class="out-icon-area">
  143.                                                                 <image class="i-style" src="../../static/icon/takePhoto.png" />
  144.                                                         </view>
  145.                                                         <view class="t-style">拍摄</view>
  146.                                                 </view>
  147.                                         </view>
  148.                                 </view>
  149.                                
  150.                         </view>
  151.                 </view>
  152.                
  153.                 <!-- 语音输入 -->
  154.                 <view class="voice-mask" v-show="voice.mask">
  155.                         <view class="inner-mask">
  156.                                 <view class="voice-progress-box" :style="{width:`${progressNum}`+'rpx'}">
  157.                                         <view class="third-icon"/>
  158.                                         <view class="progress-num">
  159.                                                 {{ voice.length }}s
  160.                                         </view>
  161.                                 </view>
  162.                                 <view class="cancel-btn" :class="{cancelBtn : voice.cancel}">
  163.                                         <image style="width: 60rpx;height: 60rpx;" src="../../static/icon/cancel-voice.png"></image>
  164.                                 </view>
  165.                                 <view class="show-tips">
  166.                                         上滑取消发送
  167.                                 </view>
  168.                                 <view class="bottom-area">
  169.                                         <image class="img-style" src="../../static/icon/icon-voice.png" />
  170.                                 </view>
  171.                         </view>
  172.                 </view>
  173.         </view>
  174. </template>
  175. <script setup>
  176. import { computed, getCurrentInstance, reactive, ref, onUpdated } from 'vue';
  177. import { onLoad } from '@dcloudio/uni-app';
  178. import properties from '@/properties/index.js';
  179. import timeMethod from '@/utils/timeMethod.js';
  180. const { proxy } = getCurrentInstance();
  181. const _this = proxy;
  182. const sendMsg = reactive({
  183.         text: ''
  184. })
  185. /* 接口数据 */
  186. const data = reactive({
  187.         msgInfoList: [],
  188.         pageNum: 1,
  189.         pageSize: 20,
  190.         pageNumCount: 0
  191. })
  192. /* 用户信息 */
  193. const userInfo = reactive({
  194.         scid: null,
  195.         rcid: null,
  196.         s_avatar: '',
  197.         r_avatar: ''
  198. })
  199. /* 配置项 */
  200. const conf = reactive({
  201.         keyboardHeight: 0,
  202.         bottomHeight: 150,
  203.         scrollTop: 0,
  204.         moreMenuHeight: 0,
  205.         judgeScrollToBottom: true,
  206.         showMoreMenu: false,
  207.         loading: false,
  208.         showMsgMenuBoxId: null,
  209.         showMoreMenu: false,
  210.         textAreaDisabled: false,
  211.         textAreaText: '',
  212.         isVoice: false,
  213.         showMoreMenu: false,
  214.         playVoice: false
  215. })
  216. /* 语音输入配置项 */
  217. const voice = reactive({
  218.         mask: false,
  219.         length: 0,
  220.         cancel: false,
  221.         startX: "",
  222.         startY: "",
  223.         timer: "",
  224.         recordInstance: "",
  225.         finished: false,
  226. })
  227. /* msg配置项 */               
  228. const msgConf = reactive({
  229.         timeSpace: 120,
  230.         initMsgTime: '',
  231.         msgId: 0,
  232.         latestTime: ''
  233. })       
  234. /**
  235. * 页面加载时调用
  236. */
  237. onLoad((e) => {
  238.         userInfo.scid =parseInt(uni.getStorageSync('cid'));
  239.         userInfo.rcid = parseInt(e.rcid);
  240.         voice.recordInstance = uni.getRecorderManager();
  241.         keyboardHeightChange();
  242.         listenMsg();
  243.         getAiUserInfo(parseInt(e.rcid));
  244.         getSelfUserInfo(uni.getStorageSync('cid'));
  245.         getAllMsg(parseInt(e.rcid));
  246.         readMsg(parseInt(e.rcid))
  247. })
  248. /**
  249. * 数据更新时调用
  250. */
  251. onUpdated(() => {
  252.         /* 页面更新时调用聊天消息定位到最底部 */
  253.         if (conf.judgeScrollToBottom) scrollToBottom();
  254. })
  255. /**
  256. * 计算属性
  257. */
  258. const windowHeight = computed(() => rpxTopx(uni.getSystemInfoSync().windowHeight))
  259. const inputHeight = computed(() => conf.bottomHeight + conf.keyboardHeight + conf.moreMenuHeight)
  260. const progressNum = computed(() => voice.length * 2 + 250)
  261. /**
  262. * px 转换 rpx
  263. */  
  264. const rpxTopx = (px) => {
  265.         const deviceWidth = uni.getSystemInfoSync().windowWidth;
  266.         let rpx = ( 750 / deviceWidth ) * Number(px);
  267.         return Math.floor(rpx);
  268. }
  269. /**
  270. * 监听聊天发送栏高度
  271. */
  272. const listenTextAreaHeight = () => {
  273.         setTimeout(()=>{
  274.                 let query = uni.createSelectorQuery();
  275.                 query.select('.input-msg-box').boundingClientRect();
  276.                 query.exec(res =>{
  277.                         conf.bottomHeight = rpxTopx(res[0].height);
  278.                 })
  279.         },200)
  280. }
  281. /**
  282. * 监听键盘高度
  283. */
  284. const keyboardHeightChange = () => {
  285.         uni.onKeyboardHeightChange(res => {
  286.                 conf.keyboardHeight = rpxTopx(res.height);
  287.                 if(conf.keyboardHeight <= 0) {
  288.                         conf.keyboardHeight = 0;
  289.                         conf.showMoreMenu = false;
  290.                 }
  291.         })
  292. }
  293. /**
  294. * 滑动到底部
  295. */
  296. const scrollToBottom = (e) => {
  297.         setTimeout(()=>{
  298.                 let query = uni.createSelectorQuery().in(_this);
  299.                 query.select('#scrollview').boundingClientRect();
  300.                 query.select('#msglistview').boundingClientRect();
  301.                 query.exec((res) =>{
  302.                         if(res[1].height > res[0].height){
  303.                                 conf.scrollTop = rpxTopx(res[1].height - res[0].height);
  304.                         }
  305.                 })
  306.         },200);
  307. }
  308. /**
  309. * 弹出更多菜单弹窗
  310. */
  311. const showMoreMenuFunc = () => {
  312.         conf.showMoreMenu = true;
  313.         conf.isVoice = false;
  314.         conf.textAreaText = '';
  315.         conf.moreMenuHeight = 350;
  316. }
  317. /**
  318. * websocket监听
  319. */
  320. const listenMsg = () => {
  321.         uni.onSocketMessage((res)=>{
  322.                 let resData = JSON.parse(res.data);
  323.                 data.msgInfoList.push(resData);
  324.         })
  325. }
  326. /**
  327. * 语音与输入切换
  328. */
  329. const isVoice = () => {
  330.         if (conf.isVoice) {
  331.                 conf.isVoice = false;
  332.                 conf.textAreaDisabled = false;
  333.                 conf.textAreaText = '';
  334.         } else {
  335.                 conf.isVoice = true;
  336.                 conf.textAreaDisabled = true;
  337.                 conf.textAreaText = '按住 说话';
  338.                 conf.showMoreMenu = false;
  339.                 conf.moreMenuHeight = 0;
  340.         }
  341.                
  342. }
  343. /**
  344. * 获取用户信息(自己)
  345. */
  346. const getSelfUserInfo = (cid) => _this.$http('/user/getUserInfo','GET',{'cid':cid}).then(res => {
  347.         userInfo.scid = cid;
  348.         userInfo.s_avatar = res.data.avatarUrl;
  349. })
  350. /**
  351. * 获取用户信息(好友)
  352. */
  353. const getAiUserInfo = (cid) => _this.$http('/user/getUserInfo','GET',{'cid':cid}).then(res => {
  354.         userInfo.rcid = cid;
  355.         userInfo.r_avatar = res.data.avatarUrl;
  356.         uni.setNavigationBarTitle({title:res.data.name});
  357. })
  358. /**
  359. * 上拉加载消息
  360. */
  361. const topRefresh = () => {
  362.         if (data.pageNum < data.pageNumCount) {
  363.                 data.pageNum++;
  364.                 conf.judgeScrollToBottom = false;
  365.                 conf.loading = true;
  366.                 getAllMsg(userInfo.rcid);
  367.         }
  368. }
  369. /**
  370. * 获取消息
  371. */
  372. const getAllMsg = (rcid) => {
  373.         _this.$http('/msg/getChatMsg','POST',{'scid':uni.getStorageSync('cid'),'rcid':rcid,'pageNum':data.pageNum,'pageSize':data.pageSize}).then(res => {
  374.                 data.pageNumCount = res.data.pagesNum;
  375.                 showMsgTime(res.data.list);
  376.                 msgConf.latestTime = data.msgInfoList.slice(-1)[0].sendTime;
  377.         })
  378. }
  379. /**
  380. * 已读消息
  381. */
  382. const readMsg = (rcid) => {
  383.         _this.$http('/msg/readMsg','POST',{'scid':rcid,'rcid':uni.getStorageSync('cid')})
  384. }
  385. /**
  386. * 控制消息时间是否展示
  387. */
  388. const showMsgTime = (msgData) => {
  389.         msgData.forEach(e => {
  390.                 e.showTime = false;
  391.                 data.msgInfoList.unshift(e);
  392.                 if (msgConf.msgId !== 0) {
  393.                         if (timeMethod.calculateTime(msgConf.initMsgTime,e.sendTime)/1000 > msgConf.timeSpace) {
  394.                                 data.msgInfoList.slice(0 - msgConf.msgId)[0].showTime = true;
  395.                         }
  396.                 }
  397.                 msgConf.initMsgTime = e.sendTime;
  398.                 msgConf.msgId++;
  399.         });
  400.         data.msgInfoList.slice(0 - (msgConf.msgId + 1))[0].showTime = true;
  401. }
  402. /**
  403. * 日期转换
  404. */
  405. const timeFormat = (time) => {
  406.         //时间格式化
  407.         const Time = timeMethod.getTime(time).split("T");
  408.         //当前消息日期属于周
  409.         const week = timeMethod.getDateToWeek(time);
  410.         //当前日期0时
  411.         const nti = timeMethod.setTimeZero(timeMethod.getNowTime());
  412.         //消息日期当天0时
  413.         const mnti = timeMethod.setTimeZero(timeMethod.getTime(time));
  414.         //计算日期差值
  415.         const diffDate = timeMethod.calculateTime(nti,mnti);
  416.         //本周一日期0时 (后面+1是去除当天时间)
  417.         const fwnti = timeMethod.setTimeZero(timeMethod.countDateStr(-timeMethod.getDateToWeek(timeMethod.getNowTime()).weekID + 1));
  418.         //计算周日期差值
  419.         const diffWeek = timeMethod.calculateTime(mnti,fwnti);
  420.        
  421.         if (diffDate === 0) {                                 //消息发送日期减去当天日期如果等于0则是当天时间
  422.                 return Time[1].slice(0,5);
  423.         } else if (diffDate < 172800000) { //当前日期减去消息发送日期小于2天(172800000ms)则是昨天-  一天最大差值前天凌晨00:00:00到今天晚上23:59:59
  424.                 return "昨天 " + Time[1].slice(0,5);
  425.         } else if (diffWeek >= 0) {                 //消息日期减去本周一日期大于0则是本周
  426.                 return week.weekName;
  427.         } else {                                                         //其他时间则是日期
  428.                 return Time[0].slice(5,10);
  429.         }
  430. }
  431. /**
  432. * 关闭消息操作菜单
  433. */
  434. const touchClose = () => {
  435.         conf.showBoxId = null;
  436.         conf.showMoreMenu = false;
  437.         conf.keyboardHeight = 0;
  438.         conf.moreMenuHeight = 0;
  439. }
  440. /**
  441. * 发送消息
  442. */
  443. const handleSend = () => {
  444.         conf.judgeScrollToBottom = true;
  445.         data.pageNum = 1;
  446.         /* 如果消息不为空 */
  447.         if(sendMsg.text.length !== 0){
  448.                 _this.$http("/msg/sendMsg","POST",{
  449.                         "scid":userInfo.scid,
  450.                         "rcid":userInfo.rcid,
  451.                         "type": "text",
  452.                         "content":sendMsg.text}).then(res => {
  453.                         if (res.status) {
  454.                                 if (timeMethod.calculateTime(res.data.sendTime,msgConf.latestTime)/1000 > msgConf.timeSpace) {
  455.                                         res.data.showTime = true;
  456.                                 } else {
  457.                                         res.data.showTime = false;
  458.                                 }
  459.                                 data.msgInfoList.push(res.data);
  460.                                 sendMsg.text = '';
  461.                         }
  462.                 })
  463.         }
  464. }
  465. /**
  466. * 长按开始录制语音
  467. */
  468. const handleTouchStart = (e) => {
  469.         if (conf.textAreaDisabled) {
  470.                 voice.finished = false;
  471.                 uni.getSetting({
  472.                         success(res) {
  473.                                 if (res.authSetting['scope.record'] === undefined) {
  474.                                         console.log("第一次授权")
  475.                                 } else if (!res.authSetting['scope.record']) {
  476.                                         uni.showToast({
  477.                                                 icon: "none",
  478.                                                 title: "点击右上角···进入设置开启麦克风授权!",
  479.                                                 duration: 2000
  480.                                         })
  481.                                 } else {                                               
  482.                                         voice.recordInstance.start();
  483.                                         voice.mask = true;
  484.                                         voice.isRecord = true;
  485.                                         voice.length = 1;
  486.                                         voice.startX = e.touches[0].pageX;
  487.                                         voice.startY = e.touches[0].pageY;
  488.                                         voice.timer = setInterval(() => {
  489.                                                 voice.length += 1;
  490.                                                 if(voice.length >= 60) {
  491.                                                         clearInterval(voice.timer);
  492.                                                         handleTouchEnd();
  493.                                                 }
  494.                                         },1000)       
  495.                                         //判断先结束按钮但是录制才开始时不会结束录制的条件;因为获取授权这儿存在延时;所以结束录制时可能还没开始录制
  496.                                         if (voice.finished && voice.mask) {
  497.                                                 handleTouchEnd();
  498.                                         }
  499.                                 }
  500.                         }
  501.                 })
  502.         }                       
  503. }
  504. /**
  505. * 长按滑动
  506. */
  507. const handleTouchMove = (e) => {
  508.         if (conf.textAreaDisabled) {
  509.                 if (voice.startY - e.touches[0].pageY > 80) {
  510.                         voice.cancel = true;
  511.                 }else {
  512.                         voice.cancel = false;
  513.                 }
  514.         }
  515. }
  516. /**
  517. * 语音录制结束
  518. */
  519. const handleTouchEnd = () => {
  520.         if (conf.textAreaDisabled) {
  521.                 voice.finished = true;
  522.                 voice.mask = false;
  523.                 clearInterval(voice.timer);
  524.                 voice.recordInstance.stop();
  525.                 voice.recordInstance.onStop((res) => {
  526.                         const message = {
  527.                                 voice:res.tempFilePath,
  528.                                 length:voice.length
  529.                         }
  530.                         if (!voice.cancel) {
  531.                                 if (voice.length>1) {
  532.                                         sendFile("voice",message);
  533.                                 } else {
  534.                                         uni.showToast({
  535.                                                 icon: 'none',
  536.                                                 title: "语音时间太短",
  537.                                                 duration: 1000
  538.                                         })
  539.                                 }
  540.                         }else {
  541.                                 voice.cancel = false;
  542.                         }
  543.                 })                                                                                                       
  544.         }
  545. }
  546. /**
  547. * 语音播放
  548. */
  549. const playSound = (url) => {
  550.         conf.playVoice = true;
  551.         let music = null;
  552.         music = uni.createInnerAudioContext();
  553.         music.src = url;
  554.         music.play();
  555.         music.onEnded(()=>{
  556.                 music = null;
  557.                 conf.playVoice = false;
  558.         })
  559. }
  560. /**
  561. * 发送文件
  562. */
  563. const sendFile = (type,data) => {
  564.         if (type === "choose") {
  565.                 uni.chooseMedia({
  566.                         count: 1,
  567.                         mediaType: ['image', 'video'],
  568.                         sourceType: ['album'],
  569.                         maxDuration: 30,
  570.                         success(res) {
  571.                                 let type = 'img';
  572.                                 if (res.tempFiles[0].fileType === 'image') {
  573.                                         type = 'img'
  574.                                 } else {
  575.                                         type = 'video'
  576.                                 }
  577.                                 uploadFile(res.tempFiles[0].tempFilePath,type)
  578.                         }
  579.                 })       
  580.         } else if (type === "shoot") {
  581.                 uni.chooseMedia({
  582.                         count: 1,
  583.                         mediaType: ['image', 'video'],
  584.                         sourceType: ['camera'],
  585.                         maxDuration: 30,
  586.                         success(res) {
  587.                                 let type = 'img';
  588.                                 if (res.tempFiles[0].fileType === 'image') {
  589.                                         type = 'img'
  590.                                 } else {
  591.                                         type = 'video'
  592.                                 }
  593.                                 uploadFile(res.tempFiles[0].tempFilePath,type)
  594.                         }
  595.                 })       
  596.         } else {
  597.                 uploadFile(data.voice,'voice')
  598.         }
  599. }
  600. /**
  601. * 上传文件
  602. */
  603. const uploadFile = (path,type) => {
  604.         let param = {"scid":userInfo.scid,"rcid":userInfo.rcid,"type":type};
  605.         if (type=='voice') {
  606.                 param = {"scid":userInfo.scid,"rcid":userInfo.rcid,"type":type,"voiceLength":voice.length};
  607.         }
  608.         uni.uploadFile({
  609.                 url: properties.appConf.url + "/msg/sendFileMsg",
  610.                 filePath: path,
  611.                 name: 'file',
  612.                 formData: param,
  613.                 header: {"Authorization": uni.getStorageSync('Authorization')},
  614.                 success(res) {
  615.                         let newMsg = JSON.parse(res.data)
  616.                         if (newMsg.status) {
  617.                                 if (timeMethod.calculateTime(newMsg.data.sendTime,msgConf.latestTime)/1000 > msgConf.timeSpace) {
  618.                                         newMsg.data.showTime = true;
  619.                                 } else {
  620.                                         newMsg.data.showTime = false;
  621.                                 }
  622.                                 data.msgInfoList.push(newMsg.data)
  623.                         }
  624.                 }
  625.         })
  626. }
  627. </script>
  628. <style lang="scss">
  629.        
  630. $chatContentbgc: #00ff7f;
  631. $chatBackground: #f0f0f0;
  632. center {
  633.         display: flex;
  634.         align-items: center;
  635.         justify-content: center;
  636. }
  637.        
  638. .chat-index {
  639.         height: 100vh;
  640.         background-color: $chatBackground;
  641.        
  642.         .scroll-style {
  643.                
  644.                 .chat-body {
  645.                         display: flex;
  646.                         flex-direction: column;
  647.                         padding-top: 23rpx;
  648.                        
  649.                         .time-box {
  650.                                 width: 100%;
  651.                                 height: 100rpx;
  652.                                 display: flex;
  653.                                 justify-content: center;
  654.                                 align-items: center;
  655.                                
  656.                                 .time-style {
  657.                                         font-size: 22rpx;
  658.                                         background-color: rgba(213, 213, 213, 0.3);;
  659.                                         padding: 5rpx 10rpx;
  660.                                         border-radius: 8rpx;
  661.                                         color: black;
  662.                                 }
  663.                         }
  664.                        
  665.                         .self {
  666.                                 justify-content: flex-end;
  667.                                 position: relative;
  668.                         }
  669.                        
  670.                         .Ai {
  671.                                 position: relative;
  672.                         }
  673.                        
  674.                         .item {
  675.                                 display: flex;
  676.                                 padding: 23rpx 30rpx;
  677.                                
  678.                                 .right {
  679.                                         background-color: $chatContentbgc;
  680.                                 }
  681.                                
  682.                                 .left {
  683.                                         background-color: #FFFFFF;
  684.                                 }
  685.                                
  686.                                 .right::after {
  687.                                         position: absolute;
  688.                                         display: inline-block;
  689.                                         content: '';
  690.                                         width: 0;
  691.                                         height: 0;
  692.                                         left: 100%;
  693.                                         top: 10px;
  694.                                         border: 12rpx solid transparent;
  695.                                         border-left: 12rpx solid $chatContentbgc;
  696.                                 }
  697.                                
  698.                                 .left::after {
  699.                                         position: absolute;
  700.                                         display: inline-block;
  701.                                         content: '';
  702.                                         width: 0;
  703.                                         height: 0;
  704.                                         top: 10px;
  705.                                         right: 100%;
  706.                                         border: 12rpx solid transparent;
  707.                                         border-right: 12rpx solid #FFFFFF;
  708.                                 }
  709.                                
  710.                                 .content-text {
  711.                                         position: relative;
  712.                                         max-width: 486rpx;
  713.                                         border-radius: 8rpx;
  714.                                         word-wrap: break-word;
  715.                                         padding: 24rpx 24rpx;
  716.                                         margin: 0 24rpx;
  717.                                         border-radius: 5px;
  718.                                         font-size: 32rpx;
  719.                                         font-family: PingFang SC;
  720.                                         font-weight: 500;
  721.                                         color: #333333;
  722.                                         line-height: 42rpx;       
  723.                                 }
  724.                                
  725.                                 .content-img {
  726.                                         margin: 0 24rpx;
  727.                                 }
  728.                                
  729.                                 .content-video {
  730.                                         margin: 0 24rpx;
  731.                                 }
  732.                                
  733.                                 .img-style {
  734.                                         width: 400rpx;
  735.                                         height: auto;
  736.                                         border-radius: 10rpx;
  737.                                 }
  738.                                
  739.                                 .video-style {
  740.                                         width: 400rpx;
  741.                                         height: 400rpx;
  742.                                 }
  743.                                
  744.                                 .avatar {
  745.                                         display: flex;
  746.                                         justify-content: center;
  747.                                         width: 78rpx;
  748.                                         height: 78rpx;
  749.                                         background: #fff;
  750.                                         border-radius: 50rpx;
  751.                                         overflow: hidden;
  752.                                        
  753.                                         image {
  754.                                                 align-self: center;
  755.                                         }
  756.                                 }
  757.                         }
  758.                 }
  759.         }
  760.        
  761.         .chat-bottom {
  762.                 width: 100%;
  763.                
  764.                 .input-msg-box {
  765.                         width: 100% ;
  766.                         min-height: 150rpx;
  767.                         position: fixed;
  768.                         bottom: 0;
  769.                         background: #e6e6e6;
  770.                        
  771.                         .textarea-style {
  772.                                 width: 100%;
  773.                                 padding-top: 20rpx;
  774.                                 display: flex;
  775.                                
  776.                                 .out_textarea_box {
  777.                                         width:65%;
  778.                                         min-height: 70rpx;
  779.                                         border-radius: 10rpx;
  780.                                         margin-left: 10rpx;
  781.                                         background: #f0f0f0;
  782.                                         display: flex;
  783.                                         align-items: center;
  784.                                        
  785.                                         textarea {
  786.                                                 width: 94%;
  787.                                                 padding: 0 3%;
  788.                                                 min-height: 42rpx;
  789.                                                 max-height: 200rpx;
  790.                                                 font-size: 32rpx;
  791.                                                 font-family: PingFang SC;
  792.                                                 color: #333333;
  793.                                         }
  794.                                 }
  795.                                
  796.                                 .voice-btn {
  797.                                         width: 10%;
  798.                                         @extend center;
  799.                                 }
  800.                                
  801.                                 .more-btn {
  802.                                         width: calc(25% - 25rpx);
  803.                                         margin-left: 10rpx;
  804.                                         @extend center;
  805.                                 }
  806.                                
  807.                                 .icon-style {
  808.                                         width: 50rpx;
  809.                                         height: 50rpx;
  810.                                 }
  811.                         }
  812.                        
  813.                         .more-menu-box-min {
  814.                                 width: 100%;
  815.                                 height: 0rpx;
  816.                                 display: none;
  817.                         }
  818.                        
  819.                         .more-menu-box-max {
  820.                                 height: 400rpx;
  821.                                 margin-top: 10rpx;
  822.                                 border-top: 1rpx solid #d6d6d6;
  823.                                 transition: height 1ms linear;
  824.                                 display: block;
  825.                                
  826.                                 .inner-menu-box {
  827.                                         width: calc(100% - 20rpx);
  828.                                         height: calc(360rpx - 10rpx);
  829.                                         padding: 10rpx;
  830.                                        
  831.                                         .menu-box {
  832.                                                 width: 150rpx;
  833.                                                 height: 150rpx;
  834.                                                 margin: 12rpx calc((100% - 600rpx) / 8);
  835.                                                 float: left;
  836.                                                
  837.                                                 .out-icon-area {
  838.                                                         width: 110rpx;
  839.                                                         height: 110rpx;
  840.                                                         background-color: #fff;
  841.                                                         border-radius: 20rpx;
  842.                                                         margin: 0 20rpx;
  843.                                                         @extend center;
  844.                                                        
  845.                                                         .i-style {
  846.                                                                 width: 60rpx;
  847.                                                                 height: 60rpx;
  848.                                                         }
  849.                                                 }
  850.                                                
  851.                                                 .t-style {
  852.                                                         font-size: 24rpx;
  853.                                                         font-weight: 400;
  854.                                                         text-align: center;
  855.                                                         margin-top: 10rpx;
  856.                                                         color: #717171;
  857.                                                 }
  858.                                         }
  859.                                 }
  860.                         }
  861.                        
  862.                 }
  863.         }
  864.        
  865.         .voice-mask{
  866.                 position:fixed;
  867.                 top:0;
  868.                 right:0;
  869.                 bottom:0;
  870.                 left:0;
  871.                 background-color: rgba(0,0,0,0.8);
  872.        
  873.                 .inner-mask {
  874.                         display: flex;
  875.                         flex-direction: column;
  876.                         align-items: center;
  877.                        
  878.                         .voice-progress-box {
  879.                                 min-width: 250rpx;
  880.                                 height: 150rpx;
  881.                                 margin-top: 60%;
  882.                                 border-radius: 50rpx;
  883.                                 background: #4df861;
  884.                                 position: relative;
  885.                                 @extend center;
  886.                                
  887.                                 .third-icon {
  888.                                         width: 0;
  889.                                         height: 0;
  890.                                         border: 15rpx solid transparent;
  891.                                         border-top: 15rpx solid #4df861;
  892.                                         position: absolute;
  893.                                         top: 100%;
  894.                                         left: 45%;
  895.                                        
  896.                                         .progress-num {
  897.                                                 font-size: 50rpx;
  898.                                                 font-weight: 600;
  899.                                         }
  900.                                 }
  901.                        
  902.                         }
  903.                        
  904.                         .cancel-btn {
  905.                                 width: 120rpx;
  906.                                 height: 120rpx;
  907.                                 clip-path: circle();
  908.                                 margin-top: 50%;
  909.                                 background: #080808;
  910.                                 @extend center;
  911.                         }
  912.                        
  913.                         .cancelBtn {
  914.                                 width: 150rpx;
  915.                                 height: 150rpx;
  916.                                 background-color: #ff0004;
  917.                                
  918.                         }
  919.                        
  920.                         .show-tips {
  921.                                 width: 100%;
  922.                                 margin-top: 50rpx;
  923.                                 text-align: center;
  924.                                 color: white;
  925.                                 animation: 4s opacity2 1s infinite;
  926.                                 font-size: 30rpx;
  927.                                 font-weight: 400;
  928.                                 font-family: sans-serif;
  929.                         }
  930.                        
  931.                         @keyframes opacity2{
  932.                                 0%{opacity:0}
  933.                                 50%{opacity:.8;}
  934.                                 100%{opacity:0;}
  935.                         }
  936.                        
  937.                         .bottom-area {
  938.                                 position: fixed;
  939.                                 bottom: 0rpx;
  940.                                 width: 100%;
  941.                                 height:190rpx;
  942.                                 border-top: #BABABB 8rpx solid;
  943.                                 border-radius: 300rpx 300rpx 0 0;
  944.                                 background-image: linear-gradient(#949794,#e1e3e1);
  945.                                 @extend center;
  946.                                
  947.                                 .img-style {
  948.                                         width: 50rpx;
  949.                                         height: 50rpx;
  950.                                 }
  951.                         }       
  952.                 }
  953.         }
  954. }
  955. </style>
复制代码
导入的时间工具包 timeMethod.js
  1. class TimeMethod {
  2.        
  3.         constructor() {}
  4.        
  5.         //日期格式化
  6.         addZero(data) {
  7.                 if (parseInt(data) < 10) {
  8.                         return "0" + String(data);
  9.                 }
  10.                 return data;
  11.         }       
  12.        
  13.         /**
  14.          * 获取当前日期
  15.          */
  16.         getNowTime() {
  17.                 const myDate = new Date();
  18.                 const year = myDate.getFullYear();
  19.                 const mouth = this.addZero(myDate.getMonth() + 1);
  20.                 const day = this.addZero(myDate.getDate());
  21.                 const hour = this.addZero(myDate.getHours());
  22.                 const minute = this.addZero(myDate.getMinutes());
  23.                 const second = this.addZero(myDate.getSeconds());
  24.                 return year + '-' + mouth + '-' + day + 'T' + hour+ ':' + minute+ ':' + second
  25.         }
  26.        
  27.         /**
  28.          * 根据时间返回标准字符串时间
  29.          * @param {Object} time
  30.          */
  31.         getTime(time) {
  32.                 const myDate = new Date(time);
  33.                 const year = myDate.getFullYear();
  34.                 const mouth = this.addZero(myDate.getMonth() + 1);
  35.                 const day = this.addZero(myDate.getDate());
  36.                 const hour = this.addZero(myDate.getHours());
  37.                 const minute = this.addZero(myDate.getMinutes());
  38.                 const second = this.addZero(myDate.getSeconds());
  39.                 return year + '-' + mouth + '-' + day + 'T' + hour+ ':' + minute+ ':' + second
  40.         }
  41.        
  42.         /**
  43.          * @param {Object} timestamp
  44.          * @param {Object} type
  45.          * 时间戳转时间
  46.          */
  47.         timestampToTime(timestamp,type) {
  48.                         if(String(timestamp).length===10) {
  49.                                 //时间戳为10位需*1000
  50.                                 var date = new Date(timestamp * 1000);
  51.                         }else {
  52.                                 var date = new Date(timestamp);
  53.                         }
  54.                 const Y = date.getFullYear() + '-';       
  55.                 const M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';       
  56.                 const D = date.getDate() + ' ';       
  57.                 const h = date.getHours() + ':';       
  58.                 const m = date.getMinutes() + ':';       
  59.                 const s = date.getSeconds();
  60.                         if(type==="date") {
  61.                                 return Y+M+D;
  62.                         }else {
  63.                                 return Y+M+D+h+m+s;
  64.                         }
  65.             }
  66.                
  67.                
  68.         /**
  69.          * @param {Object} time
  70.          * 时间转时间戳
  71.          */
  72.         timeToTimestamp(time) {
  73.                 //精确到秒,毫秒用000代替 :Date.parse(date);
  74.                 return new Date(time).getTime();
  75.         }
  76.        
  77.        
  78.         /**
  79.          * @param {Object} startTime
  80.          * @param {Object} endTime
  81.          * 日期计算
  82.          */
  83.         calculateTime(startTime,endTime) {
  84.                 return new Date(startTime) - new Date(endTime)
  85.         }
  86.        
  87.         /**
  88.          * @param {Object} time
  89.          * 日期转星期
  90.          */
  91.         getDateToWeek(time) {
  92.                 let weekArrayList = [
  93.                 {"weekID":7,"weekName":"星期日"},
  94.                 {"weekID":1,"weekName":"星期一"},
  95.                 {"weekID":2,"weekName":"星期二"},
  96.                 {"weekID":3,"weekName":"星期三"},
  97.                 {"weekID":4,"weekName":"星期四"},
  98.                 {"weekID":5,"weekName":"星期五"},
  99.                 {"weekID":6,"weekName":"星期六"}];
  100.                 return weekArrayList[new Date(time).getDay()]
  101.         }
  102.        
  103.         /**
  104.          * @param {Object} date
  105.          *  yyyy-MM-dd HH:mm:ss转为   yyyy-MM-ddTHH:mm:ss
  106.          */
  107.         timeFormat(date,type) {
  108.                 if (type == "T")
  109.                         return date.replace(" ","T")
  110.                 else
  111.                         return date.replace("T"," ")
  112.         }
  113.        
  114.         /**
  115.          * @param {Object} time
  116.          * 定时器
  117.          */
  118.         timeSleep(time) {
  119.                 return new Promise((resolve) => setTimeout(resolve,time))
  120.         }
  121.        
  122.        
  123.         /**
  124.          * 根据日期加减计算日期
  125.          * @param dayCount
  126.          */
  127.         countDateStr(dayCount) {
  128.                 let dd = new Date();
  129.                 dd.setDate(dd.getDate()+ dayCount);
  130.                 let ms = dd.getTime();
  131.                 return new Date(ms).toJSON();
  132.         }
  133.        
  134.         /**
  135.          * @param {Object} time
  136.          * 日期中时间置0
  137.          */
  138.         setTimeZero(time) {
  139.                 return time.slice(0,10) + 'T00:00:00.000+00:00';
  140.         }
  141. }
  142. export default new TimeMethod();
复制代码




免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

石小疯

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表