uniapp仿微信聊天界面效果實例(vue3組合式版本)
先看效果圖:
消息格式參照下方:
<template> <view class="chat-index"> <scroll-view id="scrollview" class="scroll-style" :style="{height: `${windowHeight - inputHeight}rpx`}" scroll-y="true" :scroll-top="conf.scrollTop" @scrolltoupper="topRefresh" @click="touchClose" > <view id="msglistview" class="chat-body"> <view v-for="item,index in data.msgInfoList" :key="index"> <!-- 消息發(fā)送時間 --> <view class="time-box" v-if="item.showTime"> <view class="time-style"> <view> {{ timeFormat(item.sendTime) }} </view> </view> </view> <!-- 自己 --> <view class="item self" v-if="item.scid == userInfo.scid"> <!-- 文本消息 --> <view class="content-text right" v-if="item.type=='text'"> {{item.content}} </view> <!-- 語音消息 --> <view class="content-text right" v-else-if="item.type=='voice'"> <view style="display: flex;" @click="playSound(item.content)"> <text>{{ item.voiceLength }}''</text> <image v-if="conf.playVoice" style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play_on.png"/> <image v-else style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play.png"/> </view> </view> <!-- 圖片消息 --> <view class="content-img" v-else-if="item.type=='img'"> <image class="img-style" :src="item.content" mode="widthFix" :lazy-load="true"/> </view> <!-- 視頻消息 --> <view class="content-video" v-else> <video class="video-style" :src="item.content" /> </view> <!-- 頭像 --> <image class="avatar" :src="userInfo.s_avatar" /> </view> <!-- 好友 --> <view class="item Ai" v-else> <!-- 頭像 --> <image class="avatar" :src="userInfo.r_avatar" /> <!-- 文本消息 --> <view class="content-text left" v-if="item.type=='text'"> {{item.content}} </view> <!-- 語音消息 --> <view class="content-text left" v-else-if="item.type=='voice'"> <view style="display: flex;" @click="playSound(item.content)"> <text>{{ item.voiceLength }}''</text> <image v-if="conf.playVoice" style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play_on.png"/> <image v-else style="width: 42rpx;height: 42rpx;" src="../../static/icon/voice_play.png"/> </view> </view> <!-- 圖片消息 --> <view class="content-img" v-else-if="item.type=='img'"> <image class="img-style" :src="item.content" mode="widthFix" :lazy-load="true"/> </view> <!-- 視頻消息 --> <view class="content-video" v-else> <video class="video-style" :src="item.content" /> </view> </view> </view> </view> </scroll-view> <!-- 消息發(fā)送框 --> <view class="chat-bottom" :style="{height:`${inputHeight}rpx`}"> <view class="input-msg-box" :style="{bottom:`${conf.keyboardHeight}rpx`}"> <!-- 輸入框區(qū)域 --> <view class="textarea-style"> <!-- 語音/文字輸入 --> <view class="voice-btn" @click="isVoice"> <image class="icon-style" v-if="conf.isVoice" src="../../static/icon/keyboard.png" /> <image class="icon-style" v-else src="../../static/icon/voice.png" /> </view> <!-- textarea輸入框 --> <view class="out_textarea_box" @click="() => conf.showMoreMenu=false"> <textarea placeholder-class="textarea_placeholder" :style="{textAlign:(conf.textAreaDisabled?'center':'')}" v-model="sendMsg.text" maxlength="250" confirm-type="send" auto-height :placeholder="conf.textAreaText" :show-confirm-bar="false" :adjust-position="false" :disabled="conf.textAreaDisabled" @confirm="handleSend" @linechange="listenTextAreaHeight" @focus="scrollToBottom" @blur="scrollToBottom" @touchstart="handleTouchStart" @touchmove="handleTouchMove" @touchend="handleTouchEnd" /> </view> <!-- 輸入菜單 --> <view class="more-btn"> <image class="icon-style" src="../../static/icon/emoji.png" @click="handleSend"/> <image class="icon-style" style="margin-left: 20rpx;" src="../../static/icon/more.png" @click="showMoreMenuFunc"/> </view> </view> <!-- 更多菜單 --> <view :class="{'more-menu-box-max': conf.showMoreMenu,'more-menu-box-min': !conf.showMoreMenu}"> <view class="inner-menu-box"> <view class="menu-box" @click="sendFile('choose','')"> <view class="out-icon-area"> <image class="i-style" src="../../static/icon/photo.png" /> </view> <view class="t-style">照片</view> </view> <view class="menu-box" @click="sendFile('shoot','')"> <view class="out-icon-area"> <image class="i-style" src="../../static/icon/takePhoto.png" /> </view> <view class="t-style">拍攝</view> </view> </view> </view> </view> </view> <!-- 語音輸入 --> <view class="voice-mask" v-show="voice.mask"> <view class="inner-mask"> <view class="voice-progress-box" :style="{width:`${progressNum}`+'rpx'}"> <view class="third-icon"/> <view class="progress-num"> {{ voice.length }}s </view> </view> <view class="cancel-btn" :class="{cancelBtn : voice.cancel}"> <image style="width: 60rpx;height: 60rpx;" src="../../static/icon/cancel-voice.png"></image> </view> <view class="show-tips"> 上滑取消發(fā)送 </view> <view class="bottom-area"> <image class="img-style" src="../../static/icon/icon-voice.png" /> </view> </view> </view> </view> </template> <script setup> import { computed, getCurrentInstance, reactive, ref, onUpdated } from 'vue'; import { onLoad } from '@dcloudio/uni-app'; import properties from '@/properties/index.js'; import timeMethod from '@/utils/timeMethod.js'; const { proxy } = getCurrentInstance(); const _this = proxy; const sendMsg = reactive({ text: '' }) /* 接口數(shù)據(jù) */ const data = reactive({ msgInfoList: [], pageNum: 1, pageSize: 20, pageNumCount: 0 }) /* 用戶信息 */ const userInfo = reactive({ scid: null, rcid: null, s_avatar: '', r_avatar: '' }) /* 配置項 */ const conf = reactive({ keyboardHeight: 0, bottomHeight: 150, scrollTop: 0, moreMenuHeight: 0, judgeScrollToBottom: true, showMoreMenu: false, loading: false, showMsgMenuBoxId: null, showMoreMenu: false, textAreaDisabled: false, textAreaText: '', isVoice: false, showMoreMenu: false, playVoice: false }) /* 語音輸入配置項 */ const voice = reactive({ mask: false, length: 0, cancel: false, startX: "", startY: "", timer: "", recordInstance: "", finished: false, }) /* msg配置項 */ const msgConf = reactive({ timeSpace: 120, initMsgTime: '', msgId: 0, latestTime: '' }) /** * 頁面加載時調(diào)用 */ onLoad((e) => { userInfo.scid =parseInt(uni.getStorageSync('cid')); userInfo.rcid = parseInt(e.rcid); voice.recordInstance = uni.getRecorderManager(); keyboardHeightChange(); listenMsg(); getAiUserInfo(parseInt(e.rcid)); getSelfUserInfo(uni.getStorageSync('cid')); getAllMsg(parseInt(e.rcid)); readMsg(parseInt(e.rcid)) }) /** * 數(shù)據(jù)更新時調(diào)用 */ onUpdated(() => { /* 頁面更新時調(diào)用聊天消息定位到最底部 */ if (conf.judgeScrollToBottom) scrollToBottom(); }) /** * 計算屬性 */ const windowHeight = computed(() => rpxTopx(uni.getSystemInfoSync().windowHeight)) const inputHeight = computed(() => conf.bottomHeight + conf.keyboardHeight + conf.moreMenuHeight) const progressNum = computed(() => voice.length * 2 + 250) /** * px 轉(zhuǎn)換 rpx */ const rpxTopx = (px) => { const deviceWidth = uni.getSystemInfoSync().windowWidth; let rpx = ( 750 / deviceWidth ) * Number(px); return Math.floor(rpx); } /** * 監(jiān)聽聊天發(fā)送欄高度 */ const listenTextAreaHeight = () => { setTimeout(()=>{ let query = uni.createSelectorQuery(); query.select('.input-msg-box').boundingClientRect(); query.exec(res =>{ conf.bottomHeight = rpxTopx(res[0].height); }) },200) } /** * 監(jiān)聽鍵盤高度 */ const keyboardHeightChange = () => { uni.onKeyboardHeightChange(res => { conf.keyboardHeight = rpxTopx(res.height); if(conf.keyboardHeight <= 0) { conf.keyboardHeight = 0; conf.showMoreMenu = false; } }) } /** * 滑動到底部 */ const scrollToBottom = (e) => { setTimeout(()=>{ let query = uni.createSelectorQuery().in(_this); query.select('#scrollview').boundingClientRect(); query.select('#msglistview').boundingClientRect(); query.exec((res) =>{ if(res[1].height > res[0].height){ conf.scrollTop = rpxTopx(res[1].height - res[0].height); } }) },200); } /** * 彈出更多菜單彈窗 */ const showMoreMenuFunc = () => { conf.showMoreMenu = true; conf.isVoice = false; conf.textAreaText = ''; conf.moreMenuHeight = 350; } /** * websocket監(jiān)聽 */ const listenMsg = () => { uni.onSocketMessage((res)=>{ let resData = JSON.parse(res.data); data.msgInfoList.push(resData); }) } /** * 語音與輸入切換 */ const isVoice = () => { if (conf.isVoice) { conf.isVoice = false; conf.textAreaDisabled = false; conf.textAreaText = ''; } else { conf.isVoice = true; conf.textAreaDisabled = true; conf.textAreaText = '按住 說話'; conf.showMoreMenu = false; conf.moreMenuHeight = 0; } } /** * 獲取用戶信息(自己) */ const getSelfUserInfo = (cid) => _this.$http('/user/getUserInfo','GET',{'cid':cid}).then(res => { userInfo.scid = cid; userInfo.s_avatar = res.data.avatarUrl; }) /** * 獲取用戶信息(好友) */ const getAiUserInfo = (cid) => _this.$http('/user/getUserInfo','GET',{'cid':cid}).then(res => { userInfo.rcid = cid; userInfo.r_avatar = res.data.avatarUrl; uni.setNavigationBarTitle({title:res.data.name}); }) /** * 上拉加載消息 */ const topRefresh = () => { if (data.pageNum < data.pageNumCount) { data.pageNum++; conf.judgeScrollToBottom = false; conf.loading = true; getAllMsg(userInfo.rcid); } } /** * 獲取消息 */ const getAllMsg = (rcid) => { _this.$http('/msg/getChatMsg','POST',{'scid':uni.getStorageSync('cid'),'rcid':rcid,'pageNum':data.pageNum,'pageSize':data.pageSize}).then(res => { data.pageNumCount = res.data.pagesNum; showMsgTime(res.data.list); msgConf.latestTime = data.msgInfoList.slice(-1)[0].sendTime; }) } /** * 已讀消息 */ const readMsg = (rcid) => { _this.$http('/msg/readMsg','POST',{'scid':rcid,'rcid':uni.getStorageSync('cid')}) } /** * 控制消息時間是否展示 */ const showMsgTime = (msgData) => { msgData.forEach(e => { e.showTime = false; data.msgInfoList.unshift(e); if (msgConf.msgId !== 0) { if (timeMethod.calculateTime(msgConf.initMsgTime,e.sendTime)/1000 > msgConf.timeSpace) { data.msgInfoList.slice(0 - msgConf.msgId)[0].showTime = true; } } msgConf.initMsgTime = e.sendTime; msgConf.msgId++; }); data.msgInfoList.slice(0 - (msgConf.msgId + 1))[0].showTime = true; } /** * 日期轉(zhuǎn)換 */ const timeFormat = (time) => { //時間格式化 const Time = timeMethod.getTime(time).split("T"); //當(dāng)前消息日期屬于周 const week = timeMethod.getDateToWeek(time); //當(dāng)前日期0時 const nti = timeMethod.setTimeZero(timeMethod.getNowTime()); //消息日期當(dāng)天0時 const mnti = timeMethod.setTimeZero(timeMethod.getTime(time)); //計算日期差值 const diffDate = timeMethod.calculateTime(nti,mnti); //本周一日期0時 (后面+1是去除當(dāng)天時間) const fwnti = timeMethod.setTimeZero(timeMethod.countDateStr(-timeMethod.getDateToWeek(timeMethod.getNowTime()).weekID + 1)); //計算周日期差值 const diffWeek = timeMethod.calculateTime(mnti,fwnti); if (diffDate === 0) { //消息發(fā)送日期減去當(dāng)天日期如果等于0則是當(dāng)天時間 return Time[1].slice(0,5); } else if (diffDate < 172800000) { //當(dāng)前日期減去消息發(fā)送日期小于2天(172800000ms)則是昨天- 一天最大差值前天凌晨00:00:00到今天晚上23:59:59 return "昨天 " + Time[1].slice(0,5); } else if (diffWeek >= 0) { //消息日期減去本周一日期大于0則是本周 return week.weekName; } else { //其他時間則是日期 return Time[0].slice(5,10); } } /** * 關(guān)閉消息操作菜單 */ const touchClose = () => { conf.showBoxId = null; conf.showMoreMenu = false; conf.keyboardHeight = 0; conf.moreMenuHeight = 0; } /** * 發(fā)送消息 */ const handleSend = () => { conf.judgeScrollToBottom = true; data.pageNum = 1; /* 如果消息不為空 */ if(sendMsg.text.length !== 0){ _this.$http("/msg/sendMsg","POST",{ "scid":userInfo.scid, "rcid":userInfo.rcid, "type": "text", "content":sendMsg.text}).then(res => { if (res.status) { if (timeMethod.calculateTime(res.data.sendTime,msgConf.latestTime)/1000 > msgConf.timeSpace) { res.data.showTime = true; } else { res.data.showTime = false; } data.msgInfoList.push(res.data); sendMsg.text = ''; } }) } } /** * 長按開始錄制語音 */ const handleTouchStart = (e) => { if (conf.textAreaDisabled) { voice.finished = false; uni.getSetting({ success(res) { if (res.authSetting['scope.record'] === undefined) { console.log("第一次授權(quán)") } else if (!res.authSetting['scope.record']) { uni.showToast({ icon: "none", title: "點擊右上角···進入設(shè)置開啟麥克風(fēng)授權(quán)!", duration: 2000 }) } else { voice.recordInstance.start(); voice.mask = true; voice.isRecord = true; voice.length = 1; voice.startX = e.touches[0].pageX; voice.startY = e.touches[0].pageY; voice.timer = setInterval(() => { voice.length += 1; if(voice.length >= 60) { clearInterval(voice.timer); handleTouchEnd(); } },1000) //判斷先結(jié)束按鈕但是錄制才開始時不會結(jié)束錄制的條件;因為獲取授權(quán)這兒存在延時;所以結(jié)束錄制時可能還沒開始錄制 if (voice.finished && voice.mask) { handleTouchEnd(); } } } }) } } /** * 長按滑動 */ const handleTouchMove = (e) => { if (conf.textAreaDisabled) { if (voice.startY - e.touches[0].pageY > 80) { voice.cancel = true; }else { voice.cancel = false; } } } /** * 語音錄制結(jié)束 */ const handleTouchEnd = () => { if (conf.textAreaDisabled) { voice.finished = true; voice.mask = false; clearInterval(voice.timer); voice.recordInstance.stop(); voice.recordInstance.onStop((res) => { const message = { voice:res.tempFilePath, length:voice.length } if (!voice.cancel) { if (voice.length>1) { sendFile("voice",message); } else { uni.showToast({ icon: 'none', title: "語音時間太短", duration: 1000 }) } }else { voice.cancel = false; } }) } } /** * 語音播放 */ const playSound = (url) => { conf.playVoice = true; let music = null; music = uni.createInnerAudioContext(); music.src = url; music.play(); music.onEnded(()=>{ music = null; conf.playVoice = false; }) } /** * 發(fā)送文件 */ const sendFile = (type,data) => { if (type === "choose") { uni.chooseMedia({ count: 1, mediaType: ['image', 'video'], sourceType: ['album'], maxDuration: 30, success(res) { let type = 'img'; if (res.tempFiles[0].fileType === 'image') { type = 'img' } else { type = 'video' } uploadFile(res.tempFiles[0].tempFilePath,type) } }) } else if (type === "shoot") { uni.chooseMedia({ count: 1, mediaType: ['image', 'video'], sourceType: ['camera'], maxDuration: 30, success(res) { let type = 'img'; if (res.tempFiles[0].fileType === 'image') { type = 'img' } else { type = 'video' } uploadFile(res.tempFiles[0].tempFilePath,type) } }) } else { uploadFile(data.voice,'voice') } } /** * 上傳文件 */ const uploadFile = (path,type) => { let param = {"scid":userInfo.scid,"rcid":userInfo.rcid,"type":type}; if (type=='voice') { param = {"scid":userInfo.scid,"rcid":userInfo.rcid,"type":type,"voiceLength":voice.length}; } uni.uploadFile({ url: properties.appConf.url + "/msg/sendFileMsg", filePath: path, name: 'file', formData: param, header: {"Authorization": uni.getStorageSync('Authorization')}, success(res) { let newMsg = JSON.parse(res.data) if (newMsg.status) { if (timeMethod.calculateTime(newMsg.data.sendTime,msgConf.latestTime)/1000 > msgConf.timeSpace) { newMsg.data.showTime = true; } else { newMsg.data.showTime = false; } data.msgInfoList.push(newMsg.data) } } }) } </script> <style lang="scss"> $chatContentbgc: #00ff7f; $chatBackground: #f0f0f0; center { display: flex; align-items: center; justify-content: center; } .chat-index { height: 100vh; background-color: $chatBackground; .scroll-style { .chat-body { display: flex; flex-direction: column; padding-top: 23rpx; .time-box { width: 100%; height: 100rpx; display: flex; justify-content: center; align-items: center; .time-style { font-size: 22rpx; background-color: rgba(213, 213, 213, 0.3);; padding: 5rpx 10rpx; border-radius: 8rpx; color: black; } } .self { justify-content: flex-end; position: relative; } .Ai { position: relative; } .item { display: flex; padding: 23rpx 30rpx; .right { background-color: $chatContentbgc; } .left { background-color: #FFFFFF; } .right::after { position: absolute; display: inline-block; content: ''; width: 0; height: 0; left: 100%; top: 10px; border: 12rpx solid transparent; border-left: 12rpx solid $chatContentbgc; } .left::after { position: absolute; display: inline-block; content: ''; width: 0; height: 0; top: 10px; right: 100%; border: 12rpx solid transparent; border-right: 12rpx solid #FFFFFF; } .content-text { position: relative; max-width: 486rpx; border-radius: 8rpx; word-wrap: break-word; padding: 24rpx 24rpx; margin: 0 24rpx; border-radius: 5px; font-size: 32rpx; font-family: PingFang SC; font-weight: 500; color: #333333; line-height: 42rpx; } .content-img { margin: 0 24rpx; } .content-video { margin: 0 24rpx; } .img-style { width: 400rpx; height: auto; border-radius: 10rpx; } .video-style { width: 400rpx; height: 400rpx; } .avatar { display: flex; justify-content: center; width: 78rpx; height: 78rpx; background: #fff; border-radius: 50rpx; overflow: hidden; image { align-self: center; } } } } } .chat-bottom { width: 100%; .input-msg-box { width: 100% ; min-height: 150rpx; position: fixed; bottom: 0; background: #e6e6e6; .textarea-style { width: 100%; padding-top: 20rpx; display: flex; .out_textarea_box { width:65%; min-height: 70rpx; border-radius: 10rpx; margin-left: 10rpx; background: #f0f0f0; display: flex; align-items: center; textarea { width: 94%; padding: 0 3%; min-height: 42rpx; max-height: 200rpx; font-size: 32rpx; font-family: PingFang SC; color: #333333; } } .voice-btn { width: 10%; @extend center; } .more-btn { width: calc(25% - 25rpx); margin-left: 10rpx; @extend center; } .icon-style { width: 50rpx; height: 50rpx; } } .more-menu-box-min { width: 100%; height: 0rpx; display: none; } .more-menu-box-max { height: 400rpx; margin-top: 10rpx; border-top: 1rpx solid #d6d6d6; transition: height 1ms linear; display: block; .inner-menu-box { width: calc(100% - 20rpx); height: calc(360rpx - 10rpx); padding: 10rpx; .menu-box { width: 150rpx; height: 150rpx; margin: 12rpx calc((100% - 600rpx) / 8); float: left; .out-icon-area { width: 110rpx; height: 110rpx; background-color: #fff; border-radius: 20rpx; margin: 0 20rpx; @extend center; .i-style { width: 60rpx; height: 60rpx; } } .t-style { font-size: 24rpx; font-weight: 400; text-align: center; margin-top: 10rpx; color: #717171; } } } } } } .voice-mask{ position:fixed; top:0; right:0; bottom:0; left:0; background-color: rgba(0,0,0,0.8); .inner-mask { display: flex; flex-direction: column; align-items: center; .voice-progress-box { min-width: 250rpx; height: 150rpx; margin-top: 60%; border-radius: 50rpx; background: #4df861; position: relative; @extend center; .third-icon { width: 0; height: 0; border: 15rpx solid transparent; border-top: 15rpx solid #4df861; position: absolute; top: 100%; left: 45%; .progress-num { font-size: 50rpx; font-weight: 600; } } } .cancel-btn { width: 120rpx; height: 120rpx; clip-path: circle(); margin-top: 50%; background: #080808; @extend center; } .cancelBtn { width: 150rpx; height: 150rpx; background-color: #ff0004; } .show-tips { width: 100%; margin-top: 50rpx; text-align: center; color: white; animation: 4s opacity2 1s infinite; font-size: 30rpx; font-weight: 400; font-family: sans-serif; } @keyframes opacity2{ 0%{opacity:0} 50%{opacity:.8;} 100%{opacity:0;} } .bottom-area { position: fixed; bottom: 0rpx; width: 100%; height:190rpx; border-top: #BABABB 8rpx solid; border-radius: 300rpx 300rpx 0 0; background-image: linear-gradient(#949794,#e1e3e1); @extend center; .img-style { width: 50rpx; height: 50rpx; } } } } } </style>
導(dǎo)入的時間工具包 timeMethod.js
class TimeMethod { constructor() {} //日期格式化 addZero(data) { if (parseInt(data) < 10) { return "0" + String(data); } return data; } /** * 獲取當(dāng)前日期 */ getNowTime() { const myDate = new Date(); const year = myDate.getFullYear(); const mouth = this.addZero(myDate.getMonth() + 1); const day = this.addZero(myDate.getDate()); const hour = this.addZero(myDate.getHours()); const minute = this.addZero(myDate.getMinutes()); const second = this.addZero(myDate.getSeconds()); return year + '-' + mouth + '-' + day + 'T' + hour+ ':' + minute+ ':' + second } /** * 根據(jù)時間返回標(biāo)準(zhǔn)字符串時間 * @param {Object} time */ getTime(time) { const myDate = new Date(time); const year = myDate.getFullYear(); const mouth = this.addZero(myDate.getMonth() + 1); const day = this.addZero(myDate.getDate()); const hour = this.addZero(myDate.getHours()); const minute = this.addZero(myDate.getMinutes()); const second = this.addZero(myDate.getSeconds()); return year + '-' + mouth + '-' + day + 'T' + hour+ ':' + minute+ ':' + second } /** * @param {Object} timestamp * @param {Object} type * 時間戳轉(zhuǎn)時間 */ timestampToTime(timestamp,type) { if(String(timestamp).length===10) { //時間戳為10位需*1000 var date = new Date(timestamp * 1000); }else { var date = new Date(timestamp); } const Y = date.getFullYear() + '-'; const M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-'; const D = date.getDate() + ' '; const h = date.getHours() + ':'; const m = date.getMinutes() + ':'; const s = date.getSeconds(); if(type==="date") { return Y+M+D; }else { return Y+M+D+h+m+s; } } /** * @param {Object} time * 時間轉(zhuǎn)時間戳 */ timeToTimestamp(time) { //精確到秒,毫秒用000代替 :Date.parse(date); return new Date(time).getTime(); } /** * @param {Object} startTime * @param {Object} endTime * 日期計算 */ calculateTime(startTime,endTime) { return new Date(startTime) - new Date(endTime) } /** * @param {Object} time * 日期轉(zhuǎn)星期 */ getDateToWeek(time) { let weekArrayList = [ {"weekID":7,"weekName":"星期日"}, {"weekID":1,"weekName":"星期一"}, {"weekID":2,"weekName":"星期二"}, {"weekID":3,"weekName":"星期三"}, {"weekID":4,"weekName":"星期四"}, {"weekID":5,"weekName":"星期五"}, {"weekID":6,"weekName":"星期六"}]; return weekArrayList[new Date(time).getDay()] } /** * @param {Object} date * yyyy-MM-dd HH:mm:ss轉(zhuǎn)為 yyyy-MM-ddTHH:mm:ss */ timeFormat(date,type) { if (type == "T") return date.replace(" ","T") else return date.replace("T"," ") } /** * @param {Object} time * 定時器 */ timeSleep(time) { return new Promise((resolve) => setTimeout(resolve,time)) } /** * 根據(jù)日期加減計算日期 * @param dayCount */ countDateStr(dayCount) { let dd = new Date(); dd.setDate(dd.getDate()+ dayCount); let ms = dd.getTime(); return new Date(ms).toJSON(); } /** * @param {Object} time * 日期中時間置0 */ setTimeZero(time) { return time.slice(0,10) + 'T00:00:00.000+00:00'; } } export default new TimeMethod();
總結(jié)
到此這篇關(guān)于uniapp仿微信聊天界面的文章就介紹到這了,更多相關(guān)uniapp仿微信聊天界面內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
基于vue-simple-uploader封裝文件分片上傳、秒傳及斷點續(xù)傳的全局上傳插件功能
這篇文章主要介紹了基于vue-simple-uploader封裝文件分片上傳、秒傳及斷點續(xù)傳的全局上傳插件,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-02-02Vue循環(huán)遍歷選項賦值到對應(yīng)控件的實現(xiàn)方法
這篇文章主要介紹了Vue-循環(huán)遍歷選項賦值到對應(yīng)控件的實現(xiàn)方法啊,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-06-06淺析Vue.js中v-bind v-model的使用和區(qū)別
v-model 指令在表單控件元素上創(chuàng)建雙向數(shù)據(jù)綁定,所謂雙向綁定。這篇文章主要介紹了Vue.js中v-bind v-model的使用和區(qū)別,需要的朋友可以參考下2018-12-12詳解Vue.js之視圖和數(shù)據(jù)的雙向綁定(v-model)
本篇文章主要介紹了Vue.js之視圖和數(shù)據(jù)的雙向綁定(v-model),使用v-model指令,使得視圖和數(shù)據(jù)實現(xiàn)雙向綁定,有興趣的可以了解一下2017-06-06VUE在for循環(huán)里面根據(jù)內(nèi)容值動態(tài)的加入class值的方法
這篇文章主要介紹了VUE在for循環(huán)里面根據(jù)內(nèi)容值動態(tài)的加入class值的方法,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下2018-08-08vue組件文檔(.md)中如何自動導(dǎo)入示例(.vue)詳解
這篇文章主要給大家介紹了關(guān)于vue組件文檔(.md)中如何自動導(dǎo)入示例(.vue)的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-01-01