移动app
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<view class="rich-text-box">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<rich-text v-if="nodes.length" space="nbsp" :nodes="nodes" class="rich-text"></rich-text>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<web-view ref="web" @onPostMessage="onWebViewMsg" src="/static/app-plus/mp-html/uni-im-code-view-local.html" :style='{"height":webViewHeight}' class="web-view"></web-view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 引入markdown-it库
|
||||
import MarkdownIt from '@/uni_modules/uni-im/lib/markdown-it.min.js';
|
||||
|
||||
// hljs是由 Highlight.js 经兼容性修改后的文件,请勿直接升级。否则会造成uni-app-vue3-Android下有兼容问题
|
||||
import hljs from "@/uni_modules/uni-im/lib/highlight/highlight-uni.min.js";
|
||||
|
||||
// 引入html-parser.js库
|
||||
import parseHtml from '@/uni_modules/uni-im/lib/html-parser.js';
|
||||
|
||||
// 初始化 MarkdownIt库
|
||||
const markdownIt = MarkdownIt({
|
||||
// 在源码中启用 HTML 标签
|
||||
html: true,
|
||||
// 如果结果以 <pre ... 开头,内部包装器则会跳过。
|
||||
highlight: function(str, lang) {
|
||||
// if (lang && hljs.getLanguage(lang)) {
|
||||
// console.error('lang', lang)
|
||||
// try {
|
||||
// return '<pre class="hljs" style="tab-size: 4;padding: 5px 8px;margin: 5px 0;overflow: auto;"><code>' +
|
||||
// hljs.highlight('lang', str, true).value +
|
||||
// '</code></pre>';
|
||||
// } catch (__) {}
|
||||
// }
|
||||
try {
|
||||
return '<pre class="hljs" style="tab-size: 4;padding: 5px 8px;overflow: auto;"><code>' +
|
||||
hljs.highlightAuto(str).value +
|
||||
'</code></pre>';
|
||||
} catch (__) {}
|
||||
|
||||
return '<pre class="hljs" style="tab-size: 4;padding: 5px 8px;overflow: auto;"><code>' +
|
||||
markdownIt.utils.escapeHtml(str) + '</code></pre>';
|
||||
}
|
||||
})
|
||||
|
||||
let htmlString = ""
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
nodes:[],
|
||||
// #ifdef APP-NVUE
|
||||
webViewHeight:"100px"
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
props: {
|
||||
code: {
|
||||
type: String,
|
||||
default () {
|
||||
return `alert(5);`
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
code:{
|
||||
handler(code, oldValue) {
|
||||
// 判断markdown中代码块标识符的数量是否为偶数
|
||||
htmlString = markdownIt.render("``` \n\n" + code + " \n\n ```")
|
||||
// console.log('htmlString',htmlString);
|
||||
// #ifdef APP-NVUE
|
||||
this.setWebViewConetnt(htmlString)
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-NVUE
|
||||
this.nodes = htmlString
|
||||
// #endif
|
||||
},
|
||||
immediate:true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
methods: {
|
||||
// #ifdef APP-NVUE
|
||||
setWebViewConetnt(htmlString){
|
||||
if(this.$refs.web){
|
||||
// console.log('htmlString',htmlString);
|
||||
this.$refs.web.evalJs(`setHtml(${JSON.stringify(htmlString)})`)
|
||||
}
|
||||
},
|
||||
onWebViewMsg(e){
|
||||
// console.log(11,e);
|
||||
let [data] = e.detail.data
|
||||
if(data.action == 'onJSBridgeReady'){
|
||||
this.setWebViewConetnt(htmlString)
|
||||
}else if(data.action == 'onHeightChange'){
|
||||
this.webViewHeight = data.height
|
||||
}
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
/* #ifndef APP-NVUE */
|
||||
@import '@/uni_modules/uni-im/lib/highlight/github-dark.min.css';
|
||||
/* #endif */
|
||||
|
||||
.rich-text-box {
|
||||
width: 500rpx;
|
||||
position: relative;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
/* #ifdef H5 */
|
||||
.rich-text-box * {
|
||||
user-select:text;
|
||||
cursor: text;
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.rich-text{
|
||||
width: 500rpx;
|
||||
font-size: 14px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,283 @@
|
||||
<template>
|
||||
<view v-if="isShow">
|
||||
<view class="uni-im-control-mark" @touchstart="closeMe" @click="closeMe">
|
||||
</view>
|
||||
<view class="control" :style="{top:data.top,left:data.left,right:data.right}">
|
||||
<template v-if="data.msg.type == 'text'">
|
||||
<view class="control-item" @click="copyText">
|
||||
<!-- <uni-icons custom-prefix="uni-im-font" type="uni-im-copy" size="16" color="#FFF"></uni-icons> -->
|
||||
<text class="control-item-text">复制</text>
|
||||
</view>
|
||||
<view class="control-item" @click="answer">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- <uni-icons type="undo" size="18" color="#FFF"></uni-icons> -->
|
||||
<!-- #endif -->
|
||||
<text class="control-item-text">回复</text>
|
||||
</view>
|
||||
<view class="control-item" @click="revokeMsg" v-if="canRevoke()">
|
||||
<!-- #ifdef H5 -->
|
||||
<!-- <uni-icons type="undo" size="18" color="#FFF"></uni-icons> -->
|
||||
<!-- #endif -->
|
||||
<text class="control-item-text">撤回</text>
|
||||
</view>
|
||||
</template>
|
||||
<view class="control-item" @click="deleteMsg">
|
||||
<!-- <uni-icons custom-prefix="uni-im-font" type="uni-im-delete" size="16" color="#FFF"></uni-icons> -->
|
||||
<text class="control-item-text">删除</text>
|
||||
</view>
|
||||
<view class="control-item" @click="other">
|
||||
<!-- <uni-icons custom-prefix="uni-im-font" type="uni-im-share" size="16" color="#FFF"></uni-icons> -->
|
||||
<text class="control-item-text">转发</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view class="icon-box" :class="{isInTop:data.isInTop}" :style="{top:data.top}">
|
||||
<view class="icon" :style="{right:iconBoxRight,left:iconBoxLeft}"></view>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniIm from '@/uni_modules/uni-im/lib/main.js';
|
||||
let fn;
|
||||
export default {
|
||||
// #ifdef APP-NVUE
|
||||
beforeCreate() {
|
||||
// const domModule = uni.requireNativePlugin('dom')
|
||||
// domModule.addRule('fontFace', {
|
||||
// 'fontFamily': "uni-im-font",
|
||||
// 'src': "url('https://at.alicdn.com/t/c/font_3726059_lz2adc1jfve.ttf')"
|
||||
// });
|
||||
},
|
||||
// #endif
|
||||
data(){
|
||||
return {
|
||||
isShow:false,
|
||||
data: {
|
||||
top:'',
|
||||
left:'',
|
||||
right:'',
|
||||
width:'',
|
||||
msg:{},
|
||||
isInTop:false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
iconBoxLeft(){
|
||||
let n = parseInt(this.data.left)
|
||||
return n ? n*3/2+55+'px' : ''
|
||||
},
|
||||
iconBoxRight(){
|
||||
let n = parseInt(this.data.right)
|
||||
return n ? n*3/2+50+'px' : ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
isShow(isShow) {
|
||||
// #ifdef H5
|
||||
// 当蒙版弹出,鼠标右键就关闭uni-im-control
|
||||
window.addEventListener('contextmenu', (e)=>{
|
||||
if(fn){
|
||||
fn(e)
|
||||
}
|
||||
})
|
||||
if(isShow){
|
||||
setTimeout(()=> {
|
||||
fn = (e)=> {
|
||||
if(this.isShow){
|
||||
if(uniIm.isWidescreen){
|
||||
this.isShow = false
|
||||
}
|
||||
e.preventDefault()
|
||||
}
|
||||
}
|
||||
}, 0);
|
||||
}else{
|
||||
fn = false
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// console.log('this.isShow',this.isShow);
|
||||
},
|
||||
methods:{
|
||||
show(data){
|
||||
this.data = data
|
||||
this.isShow = true
|
||||
},
|
||||
copyText(){
|
||||
// console.log('setClipboardData');
|
||||
console.log('this.data',this.data);
|
||||
uni.setClipboardData({
|
||||
data:this.data.msg.body,
|
||||
complete:(e)=> {
|
||||
uni.hideToast()
|
||||
console.log(e);
|
||||
this.isShow = false
|
||||
}
|
||||
})
|
||||
},
|
||||
canRevoke() {
|
||||
let current_uid = uniCloud.getCurrentUserInfo().uid
|
||||
let {group_id,from_uid,conversation_id,create_time} = this.data.msg || {}
|
||||
// console.log('create_time',create_time);
|
||||
// console.log('from_uid current_uid',this.data.msg,from_uid,current_uid);
|
||||
|
||||
// 判断是不是群主
|
||||
let isGroupAdmin = false
|
||||
if(group_id){
|
||||
let conversation = uniIm.conversation.dataList.find(i=>i.id == conversation_id)
|
||||
isGroupAdmin = conversation.group_info.user_id == current_uid
|
||||
}
|
||||
|
||||
// console.log('isGroupAdmin',isGroupAdmin);
|
||||
|
||||
//如果是群主
|
||||
if(isGroupAdmin){
|
||||
return true
|
||||
}else{
|
||||
// 消息发送者为“当前用户”,且时间小于2分钟
|
||||
return from_uid == current_uid && ( Date.now() - create_time < 1000*60*2 )
|
||||
}
|
||||
},
|
||||
async revokeMsg(){
|
||||
// 再判断一遍防止,分钟在2分钟的时候右键了,然后到了第3分钟才点下的情况
|
||||
if(this.canRevoke()){
|
||||
await uniIm.conversation.revokeMsg(this.data.msg)
|
||||
}else{
|
||||
uni.showToast({
|
||||
title: '已超过2分钟,不能撤回',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
this.isShow = false
|
||||
// console.log('this.data.msg',this.data.msg);
|
||||
},
|
||||
async answer(){
|
||||
// console.log('answer')
|
||||
this.$emit('answer',this.data.msgIndex)
|
||||
this.isShow = false
|
||||
},
|
||||
async deleteMsg(){
|
||||
// #ifndef H5
|
||||
return this.other()
|
||||
// #endif
|
||||
this.data.msg.is_delete = true
|
||||
// 存到本地
|
||||
let conversation = await uniIm.conversation.get(this.data.msg.conversation_id)
|
||||
conversation.msgManager.localMsg.update(this.data.msg.unique_id,this.data.msg)
|
||||
this.isShow = false
|
||||
},
|
||||
other(){
|
||||
uni.showToast({
|
||||
title: '暂不支持',
|
||||
icon: 'none',
|
||||
complete: () => {
|
||||
this.isShow = false
|
||||
}
|
||||
});
|
||||
},
|
||||
closeMe(){
|
||||
setTimeout(()=>{
|
||||
this.isShow = false
|
||||
// console.log('closeMe');
|
||||
},300)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* #ifndef APP-NVUE */
|
||||
view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
/* #endif */
|
||||
.control{
|
||||
background-color:#252a30;
|
||||
height: 55px;
|
||||
// width: 375rpx;
|
||||
position:fixed;
|
||||
top:0;
|
||||
border-radius: 5px;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-items: center;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 9999;
|
||||
/* #endif */
|
||||
}
|
||||
.control-item{
|
||||
width: 100rpx;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.control-item-text{
|
||||
font-size: 12px;
|
||||
color:#FFFFFF;
|
||||
margin-top: 1px;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
/* #ifdef H5 */
|
||||
.control-item-text:hover{
|
||||
color:#4CD964;
|
||||
}
|
||||
/* #endif */
|
||||
.uni-im-control-mark{
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 750rpx;
|
||||
flex: 1;
|
||||
height: 9000px;
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100vw !important;
|
||||
height: 100vh !important;
|
||||
z-index: 9999;
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef H5 */
|
||||
max-height:100vh !important;
|
||||
max-width:100vw !important;
|
||||
/* #endif */
|
||||
background-color: rgba(0,0,0,0.1);
|
||||
}
|
||||
|
||||
/* #ifdef H5 */
|
||||
@media screen and (min-width:960px){
|
||||
.uni-im-control-mark{
|
||||
background-color: rgba(255,255,255,0.05);
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
.icon-box{
|
||||
width: 750rpx;
|
||||
height: 20px;
|
||||
position: fixed;
|
||||
transform:translate(0,-10px);
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
/* #ifndef APP-NVUE */
|
||||
z-index: 9999;
|
||||
/* #endif */
|
||||
}
|
||||
.icon{
|
||||
position: absolute;
|
||||
background-color: #252a30;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.isInTop{
|
||||
transform:translate(0,45px);
|
||||
}
|
||||
</style>
|
||||
Binary file not shown.
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<view>
|
||||
<text :style="{ color: color, 'font-size': iconSize }" @click="_onClick" class="uni-im-icons">{{unicode}}</text>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const getVal = (val) => {
|
||||
const reg = /^[0-9]*$/g
|
||||
return (typeof val === 'number' || reg.test(val) )? val + 'px' : val;
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
// import iconUrl from './uni-im-icons.ttf'
|
||||
const domModule = uni.requireNativePlugin('dom')
|
||||
domModule.addRule('fontFace', {
|
||||
'fontFamily': "uni-im-icons",
|
||||
// 'src': "url('"+iconUrl+"')"
|
||||
'src': "url('https://at.alicdn.com/t/c/font_3726059_20zdv1uemg2.ttf?t=1670230205644')"
|
||||
});
|
||||
// #endif
|
||||
export default {
|
||||
emits:['click'],
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
props: {
|
||||
code: {
|
||||
type: String,
|
||||
default(){
|
||||
return ''
|
||||
}
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 16
|
||||
},
|
||||
},
|
||||
computed:{
|
||||
unicode(){
|
||||
return unescape(`%u${this.code}`)
|
||||
},
|
||||
iconSize(){
|
||||
return getVal(this.size)
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
_onClick(e) {
|
||||
this.$emit('click',e)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.uni-im-icons {
|
||||
font-family: uni-im-icons !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
/* #ifndef APP-NVUE */
|
||||
@font-face {
|
||||
font-family: "uni-im-icons"; /* Project id 3726059 */
|
||||
src: url('https://at.alicdn.com/t/c/font_3726059_20zdv1uemg2.ttf?t=1670230205644') format('truetype');
|
||||
}
|
||||
.uni-im-share:before {
|
||||
content: "\e6c4";
|
||||
}
|
||||
|
||||
.uni-im-copy:before {
|
||||
content: "\e67e";
|
||||
}
|
||||
|
||||
.uni-im-delete:before {
|
||||
content: "\e63d";
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
@@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<cell :keep-scroll-position="true">
|
||||
<slot></slot>
|
||||
</cell>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view>
|
||||
<slot></slot>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
props: {
|
||||
},
|
||||
computed:{
|
||||
},
|
||||
methods:{
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
</style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<view class="list-root">
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<list class="list" :bounce="false" :render-reverse="true">
|
||||
<slot></slot>
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<!-- 解决APP端的滚动锚定问题,在最后一个cell 设置 keep-scroll-position 和 render-reverse-position -->
|
||||
<cell :keep-scroll-position="true" :render-reverse-position="true" ref="uni-im-list-last-item">
|
||||
<!-- 高度为0的 最后一个元素用于方便滚动到最后一个元素 -->
|
||||
</cell>
|
||||
<!-- #endif -->
|
||||
</list>
|
||||
<!-- <view :style="{height:paddingBottom}"></view> -->
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<scroll-view @scroll="scroll" :scroll-top="scrollTop" :scroll-into-view="scrollIntoView" :bounces="false"
|
||||
:enhanced="true" :fast-deceleration="true" :upper-threshold="10" :scroll-anchoring="true"
|
||||
:enable-passive="true" class="scroll-view" :scroll-y="true" :scroll-with-animation="false"
|
||||
:style="{paddingBottom}" :enable-flex="true"
|
||||
>
|
||||
<slot></slot>
|
||||
<view id="uni-im-list-last-item" key="uni-im-list-last-item">
|
||||
<!-- 高度为0的 最后一个元素用于方便滚动到最后一个元素 -->
|
||||
</view>
|
||||
</scroll-view>
|
||||
<!-- #endif -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {}
|
||||
},
|
||||
props: {
|
||||
scrollTop: {
|
||||
default: 0
|
||||
},
|
||||
scrollIntoView: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
paddingBottom: {
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
scroll(e) {
|
||||
this.$emit('scroll', e)
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* #ifndef APP-NVUE */
|
||||
.scroll-view{
|
||||
overflow-anchor: auto;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
height: 100vh;
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef H5 */
|
||||
height: calc(100vh - 44px);
|
||||
/* #endif */
|
||||
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* #ifdef APP-NVUE */
|
||||
.list-root,.list{
|
||||
flex: 1;
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
@@ -0,0 +1,489 @@
|
||||
<template>
|
||||
<view class="root">
|
||||
<uni-im-list v-if="msgList.length" class="uni-im-list" @scroll="onScroll" :style="{'height':listHeight}"
|
||||
:scrollTop="scrollTop" :scroll-into-view="scrollIntoView" :paddingBottom="paddingBottom" ref="uni-im-list">
|
||||
<uni-im-list-item v-for="(msg,index) in msgList" :key="msg._id" :ref="'item-'+index">
|
||||
<view class="item" :id="'item-'+index" @click="clickItem">
|
||||
<view v-if="index === 0" class="data-state-tip-box" @appear="loadMore">
|
||||
<template v-if="isSafariPc">
|
||||
<button v-if="hasMore" class="loadMore-btn" @click="loadMore">点击加载更多</button>
|
||||
<text v-else class="data-state-tip-text">没有更多历史消息</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- vue2下小程序端 加载状态如果使用组件会有 滚动锚定问题 其他端可以使用 uni-load-more -->
|
||||
<uni-icons v-if="hasMore" size="25px" color="#ccc" type="spinner-cycle"
|
||||
class="data-state-tip-icon"></uni-icons>
|
||||
<text class="data-state-tip-text">{{hasMore ? '正在加载历史消息' : '没有更多历史消息' }}</text>
|
||||
</template>
|
||||
</view>
|
||||
<view v-if="msg.type == 'system'" class="system-msg-box">
|
||||
<uni-im-msg-system :msg="msg"></uni-im-msg-system>
|
||||
</view>
|
||||
<view v-else class="msg-box" :class="{'active-index':index === activeIndex}">
|
||||
<!-- <text style="width: 750rpx;text-align: center;border: 1px solid #000;">{{'item-'+index}}</text> -->
|
||||
<uni-im-msg @showMsgById="showMsgById" :msg="msg" :self="current_uid() == msg.from_uid" :index="index"
|
||||
@showControl="showControl" @retriesSendMsg="retriesSendMsg" :equalPrevTime="equalPrevTime(index)"
|
||||
:avatar_file="conversation.avatar_file" :aboutMsg="getAboutMsg(msg.about_msg_id)" ref="uni-im-msg" class="uni-im-msg">
|
||||
</uni-im-msg>
|
||||
</view>
|
||||
</view>
|
||||
</uni-im-list-item>
|
||||
</uni-im-list>
|
||||
<uni-load-more v-if="msgList.length == 0" :status="hasMore?'loading':'noMore'" class="mg-15"
|
||||
:contentText='{"contentrefresh": "加载中","contentnomore": "- 没有聊天记录 -"}'></uni-load-more>
|
||||
|
||||
<!-- <view class="slider-box">
|
||||
val:{{val}} scrollTop:{{scrollTop}}
|
||||
<slider value="1" @change="sliderChange" min="1" max="14" step="1" />
|
||||
</view> -->
|
||||
|
||||
<!-- <view style="position: fixed;top: 100px;width: 400rpx;">
|
||||
paddingBottom:{{paddingBottom}}
|
||||
scrollTop:{{scrollTop}}
|
||||
msgList.length:{{msgList.length}}
|
||||
this.loadMore.lock:{{loadMore.lock}}
|
||||
scrollIntoView:{{scrollIntoView}}
|
||||
<input type="text" v-model="nextScrollIntoView" placeholder="nextScrollIntoView">
|
||||
<button @click="showLast">showLast</button>
|
||||
</view> -->
|
||||
<view v-if="call_list.length" class="showCallMe" @click="showCallMe">@回复我({{call_list.length}})</view>
|
||||
<!-- <button @click="showLast">showLast</button> -->
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniIm from '@/uni_modules/uni-im/lib/main.js';
|
||||
import utils from '@/uni_modules/uni-im/common/utils.js';
|
||||
import {
|
||||
store as uniIdStore
|
||||
} from '@/uni_modules/uni-id-pages/common/store';
|
||||
|
||||
import uniImList from './components/uni-im-list/uni-im-list';
|
||||
import uniImListItem from './components/uni-im-list-item/uni-im-list-item';
|
||||
|
||||
// 递归loadMore计数,防止死循环
|
||||
let loadMoreIndex = 0
|
||||
// 一页多少条数据
|
||||
let pageLimit = 10
|
||||
// 当前页面滚动条高度
|
||||
let currentScrollTop = 0
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
const nativePluginDom = uni.requireNativePlugin('dom')
|
||||
// #endif
|
||||
export default {
|
||||
components: {
|
||||
uniImList,
|
||||
uniImListItem
|
||||
},
|
||||
computed: {
|
||||
...uniIm.mapState(['systemInfo', 'isWidescreen', 'heartbeat']),
|
||||
loadState() {
|
||||
return this.hasMore ? '正在加载历史消息' : '没有更多历史消息'
|
||||
},
|
||||
msgList() {
|
||||
return this.conversation.msgList || []
|
||||
},
|
||||
isSafariPc() {
|
||||
// #ifdef H5
|
||||
return this.systemInfo.browserName == 'safari' && this.isWidescreen
|
||||
// #endif
|
||||
return false
|
||||
},
|
||||
listHeight(){
|
||||
// #ifdef APP-NVUE
|
||||
return this.systemInfo.windowHeight - parseInt(this.paddingBottom) - 44 +'px';
|
||||
// #endif
|
||||
return 'auto'
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
val: 0,
|
||||
conversation: {},
|
||||
scrollIntoView: '',
|
||||
nextScrollIntoView: '11',
|
||||
scrollTop: 0,
|
||||
hasMore: true,
|
||||
tasksList: [],
|
||||
call_list:[],
|
||||
activeIndex:''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'conversation.call_list'(call_list) {
|
||||
this.call_list = call_list
|
||||
}
|
||||
},
|
||||
props: {
|
||||
paddingBottom: {
|
||||
default: ''
|
||||
},
|
||||
conversationId: {
|
||||
default () {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
// for (var i = 0; i < 10; i++) {
|
||||
// this.msgList.unshift({
|
||||
// t:i
|
||||
// })
|
||||
// }
|
||||
},
|
||||
methods: {
|
||||
getAboutMsg(about_msg_id){
|
||||
return this.msgList.find(i => i._id == about_msg_id)
|
||||
},
|
||||
equalPrevTime(index){
|
||||
|
||||
const getFriendlyTime = (msg)=>{
|
||||
return utils.toFriendlyTime(msg.create_time || msg.client_create_time)
|
||||
}
|
||||
|
||||
if(index === 0){
|
||||
return false
|
||||
}else if (index == this.msgList.length-1){
|
||||
return false
|
||||
}else{
|
||||
return getFriendlyTime(this.msgList[index]) == getFriendlyTime(this.msgList[index - 1])
|
||||
}
|
||||
},
|
||||
async showCallMe(){
|
||||
let msgId = this.conversation.call_list.pop()
|
||||
console.log('msgId',msgId)
|
||||
this.showMsgById(msgId)
|
||||
},
|
||||
async showViewByIndex(index,duration=300){
|
||||
if(index == -1){
|
||||
return
|
||||
}
|
||||
// #ifdef APP-NVUE
|
||||
let target = this.$refs['item-'+index][0];
|
||||
// console.log('滚动到第', index, target);
|
||||
nativePluginDom.scrollToElement(target, {
|
||||
// animated: duration != 0,
|
||||
// offset: 999
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifndef APP-NVUE
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
let listHeight = this.systemInfo.windowHeight
|
||||
|
||||
// #ifdef H5
|
||||
if(uniIm.isWidescreen){
|
||||
listHeight = document.querySelector('.uni-im-list uni-scroll-view').clientHeight
|
||||
// console.log('listHeight',listHeight)
|
||||
}else{
|
||||
listHeight -= 44
|
||||
}
|
||||
// #endif
|
||||
|
||||
query.select('#item-'+index).boundingClientRect(data => {
|
||||
// console.log('showViewByIndex #item-'+index);
|
||||
if(!data){
|
||||
return
|
||||
}
|
||||
let val = currentScrollTop - listHeight + data.top + data.height + parseInt(this.paddingBottom)
|
||||
if(val < 0){
|
||||
val = 0
|
||||
}
|
||||
// 赋值为当前滚动条的高度
|
||||
this.scrollTop = currentScrollTop
|
||||
// 设置一个新值触发视图更新 -> 滚动
|
||||
this.$nextTick(()=>{
|
||||
this.scrollTop = val
|
||||
})
|
||||
}).exec()
|
||||
// #endif
|
||||
},
|
||||
async sliderChange(e){
|
||||
let index = e.detail.value
|
||||
console.log(index)
|
||||
this.val = index
|
||||
this.showViewByIndex(index)
|
||||
},
|
||||
async init() {
|
||||
this.conversation = await uniIm.conversation.get(this.conversationId)
|
||||
// init data --start
|
||||
this.scrollIntoView = ''
|
||||
this.scrollTop = 0
|
||||
currentScrollTop = 0
|
||||
this.hasMore = true
|
||||
this.tasksList = []
|
||||
this.loadMore.lock = false
|
||||
loadMoreIndex = 0
|
||||
// init data --end
|
||||
|
||||
|
||||
// 判断此会话的数据是否初始化(加载)过
|
||||
if (!this.conversation.isInit) {
|
||||
await this.loadMore({"showLast":true})
|
||||
} else {
|
||||
// #ifndef APP-NVUE
|
||||
this.$nextTick(()=>{
|
||||
this.showLast(300)
|
||||
})
|
||||
// #endif
|
||||
|
||||
// 给hasMore赋值
|
||||
if (this.msgList.length < pageLimit) {
|
||||
// 加载过且少于一页 说明没有更多历史数据,否则先假设有滚动到最后一条,用户再次滚动到顶时会根据接口响应数据重新设定
|
||||
this.hasMore = false
|
||||
}
|
||||
}
|
||||
},
|
||||
async loadMore(param = {"showLast":false}) {
|
||||
if (this.loadMore.lock || !this.hasMore) {
|
||||
// console.log('this.loadMore.lock,!this.hasMore', this.loadMore.lock,!this.hasMore);
|
||||
return []
|
||||
}
|
||||
this.loadMore.lock = true
|
||||
|
||||
let data = await this.conversation.msgManager.getMore() || [] //空数组避免切换过快引发请求bug
|
||||
// console.log('加载到数据:', data);
|
||||
this.hasMore = (data.length != 0)
|
||||
if (data.length) {
|
||||
// 添加到任务列再执行,解决滚动锚定的问题
|
||||
this.tasksList.push(async () => {
|
||||
// 重新获取会话对象,防止web pc端 切换太快引起的会话对象指向错误
|
||||
let conversation = await uniIm.conversation.get(data[0].conversation_id)
|
||||
if (!this.conversation.isInit) {
|
||||
// 清空数据,注意不能直接 = [],已被禁用,因为那样会破坏数据原型
|
||||
this.conversation.msgList.clear()
|
||||
}
|
||||
this.conversation.msgList.unshift(...data)
|
||||
// console.log('this.conversation.msgList',this.conversation.msgList.length);
|
||||
this.conversation.isInit = true
|
||||
this.$nextTick(async () => {
|
||||
|
||||
// console.log('loadMoreIndex',loadMoreIndex);
|
||||
this.loadMore.lock = false
|
||||
|
||||
if(param.showLast){
|
||||
this.showLast()
|
||||
}
|
||||
|
||||
loadMoreIndex++
|
||||
if (this.hasMore && this.msgList.length < pageLimit && loadMoreIndex < 3) {
|
||||
// console.log('不满一屏时,再loadMore一次');
|
||||
await this.loadMore({"showLast":true})
|
||||
}
|
||||
})
|
||||
// console.log('this.loadMore.lock-----',this.loadMore.lock);
|
||||
})
|
||||
await this.doTasksListBefore()
|
||||
}
|
||||
return data
|
||||
},
|
||||
async doTasksListBefore() {
|
||||
if (this.tasksList.length) {
|
||||
// 非APP端有滚动锚定问题,需要先滚动为非0高度再加载
|
||||
// #ifndef APP-NVUE
|
||||
if (currentScrollTop < 1 && !this.isSafariPc) {
|
||||
// console.log('小于1111');
|
||||
this.scrollTop = currentScrollTop
|
||||
return this.$nextTick( async() => {
|
||||
this.scrollTop = 1
|
||||
currentScrollTop = 1
|
||||
await this.doTasksList()
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
await this.doTasksList()
|
||||
}
|
||||
},
|
||||
showMsgById(showMsgById){
|
||||
// console.log('showMsgById',showMsgById)
|
||||
let index = this.msgList.findIndex(i=>i._id == showMsgById)
|
||||
|
||||
this.activeIndex = index
|
||||
setTimeout(()=>{
|
||||
this.activeIndex = ''
|
||||
}, 1500);
|
||||
|
||||
this.showViewByIndex(index)
|
||||
},
|
||||
async doTasksList() {
|
||||
// console.log("this.tasksList.length-----",this.tasksList.length)
|
||||
let length = this.tasksList.length
|
||||
for (let i = 0; i < length; i++) {
|
||||
let fun = this.tasksList.shift()
|
||||
if(typeof fun == 'function'){
|
||||
await fun()
|
||||
}
|
||||
}
|
||||
},
|
||||
showLast(duration = 300) {
|
||||
let mLength = this.msgList.length
|
||||
this.showViewByIndex(mLength -1)
|
||||
},
|
||||
onScroll(e) {
|
||||
currentScrollTop = e.detail.scrollTop
|
||||
|
||||
// #ifdef H5
|
||||
// pc 端 safari浏览器有滚动锚定问题走点击加载
|
||||
if (this.isSafariPc) {
|
||||
return
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (currentScrollTop < 300) {
|
||||
this.loadMore()
|
||||
}
|
||||
// 防抖
|
||||
let fun = () => {
|
||||
if (currentScrollTop < 300) {
|
||||
this.loadMore()
|
||||
}
|
||||
}
|
||||
debounce(fun,1500)()
|
||||
},
|
||||
showControl(e) {
|
||||
this.$emit('showControl', e)
|
||||
},
|
||||
retriesSendMsg(e) {
|
||||
this.$emit('retriesSendMsg', e)
|
||||
},
|
||||
//当前用户自己的uid
|
||||
current_uid() {
|
||||
return uniCloud.getCurrentUserInfo().uid;
|
||||
},
|
||||
clickItem(){
|
||||
this.$emit('clickItem')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let timers = []
|
||||
|
||||
function debounce(fn, delay) {
|
||||
return function() {
|
||||
if (timers.length) {
|
||||
timers.forEach(timer => clearTimeout(timer))
|
||||
}
|
||||
let timer = setTimeout(fn, delay);
|
||||
timers.push(timer)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.root,
|
||||
.uni-im-list {
|
||||
/* #ifndef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.item {
|
||||
margin: 15px 0;
|
||||
// border: solid 1px #f40000;
|
||||
}
|
||||
|
||||
.mg-15 {
|
||||
margin: 15px;
|
||||
}
|
||||
|
||||
.data-state-tip-box {
|
||||
// height: 35px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* #ifndef APP-NVUE */
|
||||
.data-state-tip-icon {
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
animation: rotation 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes rotation {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.data-state-tip-text {
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
font-size: 12px;
|
||||
margin: 0 5px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
/* #ifdef H5 */
|
||||
.loadMore-btn {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.loadMore-btn::after {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.system-msg-box {
|
||||
margin-bottom: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.msg-box{
|
||||
transition-property: background-color;
|
||||
transition-duration:2s;
|
||||
}
|
||||
.active-index{
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.slider-box{
|
||||
border: 1px solid #000;
|
||||
position: fixed;
|
||||
width: 750rpx;
|
||||
height: 55px;
|
||||
top: 60px;
|
||||
right: 0;
|
||||
z-index: 999;
|
||||
background-color: #FFF;
|
||||
}
|
||||
|
||||
.ask-line{
|
||||
width: 750rpx;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.showCallMe{
|
||||
background-color: #62caf8;
|
||||
border-radius:50px;
|
||||
padding:2px 15px;
|
||||
font-size: 12px;
|
||||
color: #FFF;
|
||||
position: fixed;
|
||||
right: 5px;
|
||||
top: 10px;
|
||||
/* #ifdef H5 */
|
||||
top: 55px;
|
||||
@media screen and (min-width:960px){
|
||||
top: calc(7vh + 80px);
|
||||
right: calc(50vw - 520px);
|
||||
}
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<template>
|
||||
<text class="system-msg">{{friendlyTime}} {{content}}</text>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import uniImUtils from '@/uni_modules/uni-im/common/utils.js';
|
||||
export default {
|
||||
props: {
|
||||
msg: {
|
||||
type: Object,
|
||||
default(){
|
||||
return {
|
||||
userList:[]
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
friendlyTime(){
|
||||
return uniImUtils.toFriendlyTime(this.msg.create_time || this.msg.client_create_time)
|
||||
},
|
||||
content() {
|
||||
// console.error(1212,this.msg)
|
||||
switch (this.msg.action){
|
||||
case "join-group-notice":
|
||||
return ''+ this.msg.body.user_list.map(item=>item.nickname).join(' , ') + ' 加入群聊'
|
||||
break;
|
||||
default:
|
||||
return this.msg.body
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.system-msg {
|
||||
background-color: #f2f2f2;
|
||||
color: #9d9e9d;
|
||||
font-size: 14px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 15rpx;
|
||||
border-radius: 8px;
|
||||
}
|
||||
</style>
|
||||
771
uni-im示例/uni_modules/uni-im/components/uni-im-msg/uni-im-msg.vue
Normal file
771
uni-im示例/uni_modules/uni-im/components/uni-im-msg/uni-im-msg.vue
Normal file
@@ -0,0 +1,771 @@
|
||||
<template>
|
||||
<view class="box" v-if="!msg.is_delete" @mouseenter="mouseIn = true" @mouseleave="mouseIn = false">
|
||||
<view class="friendlyTime">
|
||||
<text v-if="showDatetime" class="format-time-text">{{friendlyTime}}</text>
|
||||
</view>
|
||||
<view class="msg-box" :class="{reverse:self}">
|
||||
<template v-if="msg.is_revoke">
|
||||
<text class="revoke-text">已被撤回</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<cloud-image width="80rpx" height="80rpx" borderRadius="5px"
|
||||
:src="avatarUrl||'/uni_modules/uni-im/static/avatarUrl.png'" mode="widthFix" class="avatarUrl">
|
||||
</cloud-image>
|
||||
<view :class="{'reverse-align':self}">
|
||||
<text v-if="!self" :selectable="true" class="username">{{userInfo.nickname}}</text>
|
||||
<view v-if="msg.about_msg_id" class="cite-box">
|
||||
<text v-if="aboutMsg.is_revoke" class="cite-box-text">回复的消息已被撤回</text>
|
||||
<text v-else class="cite-box-text pointer" @click="showMsgById">{{getNicknameByUid(aboutMsg.from_uid)}}:{{aboutMsg.body}}</text>
|
||||
</view>
|
||||
<view class="msg-content" @longpress="showControl" ref="msg-content">
|
||||
<uni-icons v-if="self && msg.state != 100 && msgStateIcon" @click="retriesSendMsg"
|
||||
:color="msg.state===0?'#999':'#d22'" :type="msgStateIcon" class="msgStateIcon">
|
||||
</uni-icons>
|
||||
<cloud-image width="200rpx" height="200rpx" @click="previewImage" class="cloud-image"
|
||||
v-if="msg.type == 'image'" :src="msg.body.url" mode="aspectFill" />
|
||||
<view @click="playSound" v-if="msg.type == 'sound'"
|
||||
class="text selfText sound-box" :class="{reverse:!self}" :style="{width:soundBoxWidth}">
|
||||
<text class="sound-time">{{msg.body.time}}''</text>
|
||||
<view class="sound-icon-box" style="width: 18px;height: 18px;" :class="{rotate:!self}">
|
||||
<image v-if="soundPlayState" src="@/uni_modules/uni-im/static/sound-ing.gif"
|
||||
style="width: 18px;height: 18px;" mode="widthFix"></image>
|
||||
<uni-im-icons v-else :class="{'sound-icon-active':soundPlayState}" code="e6f5"
|
||||
size="18px" color="#000000"></uni-im-icons>
|
||||
</view>
|
||||
</view>
|
||||
<template v-if="msg.type == 'text'">
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view class="rich-text-box" :class="msgClass">
|
||||
<text v-if="msg.body == htmlString" class="msg-text">{{ msg.body }}</text>
|
||||
<rich-text v-else class="rich-text" @clickLink="clickLink" :nodes="nodes"></rich-text>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view class="text-box" :class="msgClass">
|
||||
<template v-if="msg.body == htmlString">
|
||||
<text class="msg-text">{{msg.body}}</text>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-for="(item,index) in nodes">
|
||||
<text :key="index" class="msg-text"
|
||||
v-if="item.type == 'text'">{{ item.text }}</text>
|
||||
<text :key="index" v-if="item.name == 'a'" @click="clickLink(item.attrs.href)"
|
||||
class="link msg-text">{{item.attrs.href}}</text>
|
||||
</template>
|
||||
</template>
|
||||
</view>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<template v-if="msg.type == 'code'">
|
||||
<uni-im-code-view :code="msg.body"></uni-im-code-view>
|
||||
</template>
|
||||
|
||||
<!-- <uni-link href="https://ext.dcloud.net.cn/" text="https://ext.dcloud.net.cn/"></uni-link> -->
|
||||
<view @click="playVideo" class="video-box" v-if="msg.type == 'video'">
|
||||
<image class="video-img" mode="aspectFill"
|
||||
:src="videoUrl+'?x-oss-process=video/snapshot,t_1000,f_jpg,w_200,m_fast,ar_auto'"></image>
|
||||
<view class="video-box-mark"></view>
|
||||
<uni-im-icons code="e650" size="35" color="#FFF" class="play-video-icon"></uni-im-icons>
|
||||
</view>
|
||||
<template v-if="msg.type == 'file'">
|
||||
<view class="file-msg-box" @click="downLoadFile">
|
||||
<view class="file-msg-info">
|
||||
<text class="file-msg-info-name">{{fileName}}</text>
|
||||
<text class="file-msg-info-size">{{fileSize}}</text>
|
||||
</view>
|
||||
<uni-im-icons code="e858" size="50" color="#EEEEEE" class="file-icon"></uni-im-icons>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
store as uniIdStore,
|
||||
} from '@/uni_modules/uni-id-pages/common/store'
|
||||
|
||||
import uniIm from '@/uni_modules/uni-im/lib/main.js';
|
||||
import utils from '@/uni_modules/uni-im/common/utils.js';
|
||||
|
||||
import parseHtml from '@/uni_modules/uni-im/lib/html-parser.js';
|
||||
|
||||
let audioContext = uniIm.audioContext
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
username: "用户名",
|
||||
videoUrl: '',
|
||||
soundPlayState: 0,
|
||||
mouseIn:false //鼠标在上面
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
// #ifdef H5
|
||||
// web端限制不选中文字时出现系统右键菜单
|
||||
let msgContent = this.$refs['msg-content']
|
||||
if (msgContent) {
|
||||
msgContent.$el.addEventListener('contextmenu', (e) => {
|
||||
if (!document.getSelection().toString()) {
|
||||
this.showControl(e)
|
||||
e.preventDefault()
|
||||
}
|
||||
})
|
||||
}
|
||||
// #endif
|
||||
|
||||
if (this.msg.type == 'video') {
|
||||
this.videoUrl = await this.getTempFileURL()
|
||||
} else if (this.msg.type == 'sound') {
|
||||
this.onPlay = async () => {
|
||||
// console.log('soundPlayStart------------------');
|
||||
let currentAudioUrl = await this.getTempFileURL()
|
||||
let src = uniIm.audioContext.src
|
||||
if (src == currentAudioUrl) {
|
||||
this.soundPlayState = 1
|
||||
}else{
|
||||
this.soundPlayState = 0
|
||||
}
|
||||
}
|
||||
audioContext.onPlay(this.onPlay);
|
||||
|
||||
this.soundPlayEnd = () => {
|
||||
// console.log('soundPlayEnd------------------');
|
||||
this.soundPlayState = 0
|
||||
}
|
||||
audioContext.onPause(this.soundPlayEnd);
|
||||
audioContext.onStop(this.soundPlayEnd);
|
||||
audioContext.onEnded(this.soundPlayEnd);
|
||||
audioContext.onError(this.soundPlayEnd);
|
||||
}
|
||||
},
|
||||
props: {
|
||||
msg: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
body: ""
|
||||
}
|
||||
}
|
||||
},
|
||||
aboutMsg:{
|
||||
type: [Object,null],
|
||||
default () {
|
||||
return {
|
||||
}
|
||||
}
|
||||
},
|
||||
self: {
|
||||
type: Boolean,
|
||||
default () {
|
||||
return false
|
||||
}
|
||||
},
|
||||
avatar_file: {
|
||||
type: [Object, String, Boolean],
|
||||
default () {
|
||||
return {
|
||||
url: "/uni_modules/uni-im/static/avatarUrl.png"
|
||||
}
|
||||
}
|
||||
},
|
||||
index: {
|
||||
type: Number
|
||||
},
|
||||
equalPrevTime:{
|
||||
type: Boolean,
|
||||
default () {
|
||||
return false
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
friendlyTime(){
|
||||
return utils.toFriendlyTime(this.msg.create_time || this.msg.client_create_time)
|
||||
},
|
||||
showDatetime(){
|
||||
return this.mouseIn || !this.equalPrevTime
|
||||
},
|
||||
userInfo() {
|
||||
return uniIm.usersInfo[this.msg.from_uid] || {}
|
||||
},
|
||||
msgStateIcon() {
|
||||
switch (this.msg.state) {
|
||||
case 0:
|
||||
// 发送中
|
||||
return 'spinner-cycle'
|
||||
break;
|
||||
case -100:
|
||||
// 发送失败
|
||||
return 'refresh-filled'
|
||||
break;
|
||||
case -200:
|
||||
// 禁止发送(内容不合法)
|
||||
return 'info-filled'
|
||||
break;
|
||||
default:
|
||||
return false
|
||||
break;
|
||||
}
|
||||
},
|
||||
mineId() {
|
||||
return uniCloud.getCurrentUserInfo().uid
|
||||
},
|
||||
msgClass() {
|
||||
var msgClass = ''
|
||||
if (this.msg.type == 'text') {
|
||||
this.msg.body += ''
|
||||
let textLength = this.msg.body.replace(/[\u0000-\u007f]/g, "a").replace(/[\u0080-\u07ff]/g, "aa")
|
||||
.replace(
|
||||
/[\u0800-\uffff]/g, "aa").length
|
||||
if (textLength > 30) {
|
||||
msgClass += ' exceed'
|
||||
}
|
||||
}
|
||||
if (this.self) {
|
||||
msgClass += ' self-text-box'
|
||||
}
|
||||
return msgClass
|
||||
},
|
||||
avatarUrl() {
|
||||
if (this.self) {
|
||||
// console.error('uniIdStore.userInfo',uniIdStore.userInfo)
|
||||
return uniIdStore.userInfo.avatar_file?.url
|
||||
} else {
|
||||
return this.userInfo.avatar_file?.url
|
||||
}
|
||||
},
|
||||
soundBoxWidth() {
|
||||
return uni.upx2px(750 / 60 * this.msg.body.time) + 50 + 'px'
|
||||
},
|
||||
htmlString() {
|
||||
if (this.msg.type != 'text') {
|
||||
return "";
|
||||
}
|
||||
let content = this.msg.body //.replace(/</g, "< ");
|
||||
if (/</g.test(content)) {
|
||||
return content
|
||||
}
|
||||
if (!content) {
|
||||
return "";
|
||||
}
|
||||
|
||||
// 找网址
|
||||
let urlPattern = /(https?:\/\/|www\.)[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]/g;
|
||||
content = content.replace(urlPattern, function(match) {
|
||||
var href = match;
|
||||
if (match.indexOf("http") == -1) {
|
||||
//如果不带http://开头就带上
|
||||
href = "http://" + match;
|
||||
}
|
||||
return '<a class="link" target="_blank" href="' + href + '">' + match + "</a> ";
|
||||
});
|
||||
/*
|
||||
// 手机号正则
|
||||
const regPhone = /(13[0-9]|14[5-9]|15[012356789]|166|17[0-8]|18[0-9]|19[8-9])[0-9]{8}/g;
|
||||
content = content.replace(regPhone, " <a href='tel:$&'>$&</a>")
|
||||
// 固定电话正则
|
||||
const regTel = /(([0\+]\d{2,3}-)?(0\d{2,3})-)(\d{7,8})(-(\d{3,}))?/g;
|
||||
content = content.replace(regTel, " <a href='tel:$&'>$&</a>")
|
||||
|
||||
// 邮箱正则
|
||||
const regMail = /([a-z0-9._-]+@[a-z0-9.-]+\.[a-z]{2,4})/ig;
|
||||
content = content.replace(regMail, " <a href='mailto:$&'>$&</a>")
|
||||
*/
|
||||
return content
|
||||
|
||||
},
|
||||
nodes() {
|
||||
if (this.msg.body == this.htmlString) {
|
||||
return
|
||||
}
|
||||
try {
|
||||
let nodes = parseHtml(this.htmlString)
|
||||
// console.log('nodes',nodes);
|
||||
nodes.map(item => {
|
||||
// console.log('item',item);
|
||||
if (item.attrs && item.attrs.class) {
|
||||
item.attrs.class += " msg-text"
|
||||
} else {
|
||||
item.attrs = {
|
||||
class: "msg-text"
|
||||
}
|
||||
}
|
||||
return item
|
||||
})
|
||||
return nodes
|
||||
} catch (e) {
|
||||
console.error('htmlString error:', e);
|
||||
return ''
|
||||
//TODO handle the exception
|
||||
}
|
||||
|
||||
},
|
||||
fileSize() {
|
||||
if (this.msg.type == 'file') {
|
||||
let size = this.msg.body.size
|
||||
if (size < Math.pow(1024, 1)) {
|
||||
return parseInt(size * 10) / 10 + 'B'
|
||||
} else if (size < Math.pow(1024, 2)) {
|
||||
return parseInt(size / Math.pow(1024, 1) * 10) / 10 + 'KB'
|
||||
} else if (size < Math.pow(1024, 3)) {
|
||||
return parseInt(size / Math.pow(1024, 2) * 10) / 10 + 'MB'
|
||||
} else {
|
||||
return 'err'
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
fileName() {
|
||||
if (this.msg.type == 'file') {
|
||||
let name = this.msg.body.name
|
||||
if (name.length < 30) {
|
||||
return name
|
||||
} else {
|
||||
return name.slice(0, 15) + '...' + name.slice(-15)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNicknameByUid(uid){
|
||||
let userInfo = uniIm.usersInfo[uid]
|
||||
if(userInfo){
|
||||
return userInfo.nickname
|
||||
}else{
|
||||
return ''
|
||||
}
|
||||
},
|
||||
showMsgById() {
|
||||
this.$emit('showMsgById', this.aboutMsg._id)
|
||||
},
|
||||
clickLink(href) {
|
||||
// console.log(href);
|
||||
// #ifdef APP-PLUS
|
||||
plus.runtime.openURL(href);
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
window.open(href)
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
uni.setClipboardData({
|
||||
data: href,
|
||||
complete: (e) => {
|
||||
// console.log(e);
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
async playSound() {
|
||||
audioContext.src = await this.getTempFileURL();
|
||||
// 下一个事件循环执行
|
||||
setTimeout(()=>{
|
||||
// console.log(78998797,audioContext);
|
||||
if (this.soundPlayState === 1) {
|
||||
// console.log('播放中,执行关闭');
|
||||
audioContext.stop()
|
||||
} else {
|
||||
audioContext.stop()
|
||||
audioContext.play();
|
||||
}
|
||||
},0)
|
||||
},
|
||||
async previewImage() {
|
||||
console.log(213);
|
||||
uni.showLoading();
|
||||
let url = await this.getTempFileURL()
|
||||
uni.previewImage({
|
||||
urls: [url],
|
||||
complete() {
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
},
|
||||
async playVideo() {
|
||||
let url = await this.getTempFileURL()
|
||||
uni.navigateTo({
|
||||
url: "/uni_modules/uni-im/pages/common/video/video?url=" + url,
|
||||
animationDuration: 300,
|
||||
animationType: "fade-in"
|
||||
})
|
||||
},
|
||||
async showControl(e) {
|
||||
|
||||
// console.log('showControl')
|
||||
|
||||
let msgContentDomInfo;
|
||||
// #ifndef APP-NVUE
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
await new Promise(callback => {
|
||||
query.selectAll('.msg-content').boundingClientRect(data => {
|
||||
msgContentDomInfo = data[0]
|
||||
// console.log('data--------', data);
|
||||
callback(msgContentDomInfo)
|
||||
}).exec();
|
||||
})
|
||||
// #endif
|
||||
|
||||
// #ifdef APP-NVUE
|
||||
let ref = this.$refs['msg-content']
|
||||
await new Promise(callback => {
|
||||
const dom = weex.requireModule('dom')
|
||||
dom.getComponentRect(ref, e => {
|
||||
msgContentDomInfo = e.size
|
||||
callback(e)
|
||||
})
|
||||
})
|
||||
// #endif
|
||||
|
||||
// // #ifndef APP-NVUE
|
||||
// e.stopPropagation()
|
||||
// e.preventDefault()
|
||||
// // #endif
|
||||
|
||||
this.$emit('showControl', {
|
||||
index: this.index,
|
||||
msgContentDomInfo,
|
||||
// #ifdef H5
|
||||
coordinate: {
|
||||
left: e.x,
|
||||
top: e.y
|
||||
}
|
||||
// #endif
|
||||
})
|
||||
// if (this.msg.type == 'text') {
|
||||
// uni.setClipboardData({
|
||||
// data: this.msg.body,
|
||||
// success: function() {
|
||||
// console.log('success');
|
||||
// uni.showToast({
|
||||
// title: '文本复制成功',
|
||||
// icon: 'none'
|
||||
// });
|
||||
// },
|
||||
// complete: (e) => {
|
||||
// console.log(e);
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
},
|
||||
retriesSendMsg() {
|
||||
// if (this.msg.state == -200) {
|
||||
// return uni.showToast({
|
||||
// title: '内容不合法',
|
||||
// icon: 'error'
|
||||
// });
|
||||
// }
|
||||
this.$emit('retriesSendMsg', this.msg)
|
||||
},
|
||||
async downLoadFile() {
|
||||
// #ifdef H5
|
||||
return window.open(this.msg.body.url)
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
|
||||
let url = await this.getTempFileURL()
|
||||
uni.downloadFile({
|
||||
url,
|
||||
success: (res) => {
|
||||
if (res.statusCode === 200) {
|
||||
// console.log('下载成功');
|
||||
// console.log(res.tempFilePath);
|
||||
uni.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
success: (res) => {
|
||||
// console.log('res',res);
|
||||
uni.openDocument({
|
||||
filePath: res.savedFilePath
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
async getTempFileURL(param){
|
||||
let fileid = param||this.msg.body.url
|
||||
// 如果不是fileid就直接返回。
|
||||
if (fileid.substring(0, 8) != "cloud://") {
|
||||
return fileid
|
||||
}
|
||||
let res = await uniCloud.getTempFileURL({
|
||||
fileList: [fileid]
|
||||
})
|
||||
return res.fileList[0].tempFileURL
|
||||
}
|
||||
},
|
||||
destroyed(){
|
||||
// console.log('uni-im-msg-destroyed');
|
||||
audioContext.offPlay(this.onPlay);
|
||||
audioContext.offPause(this.soundPlayEnd);
|
||||
audioContext.offStop(this.soundPlayEnd);
|
||||
audioContext.offEnded(this.soundPlayEnd);
|
||||
audioContext.offError(this.soundPlayEnd);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
/* #ifndef APP-NVUE */
|
||||
view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
.box{
|
||||
flex-direction: column;
|
||||
}
|
||||
.msg-box {
|
||||
/* #ifndef H5 */
|
||||
width: 750rpx;
|
||||
/* #endif */
|
||||
// flex: 1;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
padding: 0 16rpx;
|
||||
margin: 16rpx 0;
|
||||
position: relative;
|
||||
// border-style: solid;
|
||||
// border-width: 1px;
|
||||
// border-color: #2C405A;
|
||||
}
|
||||
|
||||
/* #ifdef H5 */
|
||||
.msg-box,
|
||||
.msg-box *
|
||||
{
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.video-box,
|
||||
.sound-box,
|
||||
.video-box,
|
||||
.file-msg-box,
|
||||
.cloud-image{
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* #endif */
|
||||
|
||||
.msg-content {
|
||||
margin: 0 16rpx;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
padding-left: 18rpx;
|
||||
}
|
||||
|
||||
.text-box {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: block;
|
||||
/* #endif */
|
||||
padding: 18rpx;
|
||||
border-radius: 5px;
|
||||
background-color: #FFFFFF;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-start;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.rich-text-box {
|
||||
padding: 18rpx;
|
||||
border-radius: 5px;
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.rich-text {
|
||||
background-color: transparent;
|
||||
width: 500rpx;
|
||||
}
|
||||
|
||||
.msg-text {
|
||||
font-size: 30rpx;
|
||||
justify-content: space-between;
|
||||
/* #ifndef APP-NVUE */
|
||||
word-break: break-all;
|
||||
user-select: text;
|
||||
cursor: text;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.link {
|
||||
// font-size: 16px;
|
||||
color: #007fff;
|
||||
/* #ifdef H5 */
|
||||
cursor: pointer;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.self-text-box {
|
||||
background-color: #4CD964;
|
||||
}
|
||||
|
||||
.exceed {
|
||||
width: 400rpx;
|
||||
}
|
||||
|
||||
|
||||
.selfIcon {
|
||||
//transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.msgStateIcon {
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
.video-box {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.video-img {
|
||||
width: 200rpx;
|
||||
height: 200rpx;
|
||||
}
|
||||
|
||||
.play-video-icon {
|
||||
position: absolute;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
top: 70rpx;
|
||||
left: 70rpx;
|
||||
}
|
||||
|
||||
.video-box-mark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.sound-box {
|
||||
flex-direction: row;
|
||||
background-color: #94EB6A;
|
||||
height: 44px;
|
||||
padding: 10px;
|
||||
width: 66px;
|
||||
border-radius: 5px;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sound-time {
|
||||
font-size: 14px;
|
||||
margin: 0 4px;
|
||||
}
|
||||
|
||||
.sound-icon-active {
|
||||
transform: option;
|
||||
opacity: 10;
|
||||
background-color: #007AFF;
|
||||
transition-property: background-color;
|
||||
transition-duration: 0.3s;
|
||||
transition-delay: 0.1s;
|
||||
transition-timing-function: cubic-bezier(0.25, 0.1, 0.25, 1.0);
|
||||
}
|
||||
|
||||
.file-msg-box {
|
||||
background-color: #FFFFFF;
|
||||
width: 500rpx;
|
||||
padding: 20rpx;
|
||||
border-radius: 8px;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.file-msg-info {
|
||||
width: 300rpx;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.file-msg-info-name {
|
||||
/* #ifndef APP-NVUE */
|
||||
word-wrap: break-word;
|
||||
/* #endif */
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.file-msg-info-size {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.reverse {
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.rotate {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.revoke-text {
|
||||
width: 750rpx;
|
||||
/* #ifdef H5 */
|
||||
width: 100vw;
|
||||
/* #endif */
|
||||
text-align: center;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.reverse-align{
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
/* 回复引用某条消息提示框 */
|
||||
.cite-box {
|
||||
padding: 5px 8px;
|
||||
margin: 3px 5px;
|
||||
background-color: #e3e3e3;
|
||||
color: #6a6a6a;
|
||||
border-radius: 5px;
|
||||
/* #ifdef H5 */
|
||||
max-width: 400rpx;
|
||||
/* #endif */
|
||||
/* #ifndef H5 */
|
||||
width: 400rpx;
|
||||
/* #endif */
|
||||
}
|
||||
|
||||
.cite-box-text {
|
||||
/* #ifndef APP-NVUE */
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
lines:2;
|
||||
text-overflow: ellipsis;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.friendlyTime {
|
||||
height: 22px;
|
||||
/* #ifndef APP-NVUE */
|
||||
// display: block;
|
||||
/* #endif */
|
||||
}
|
||||
.format-time-text{
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #999999;
|
||||
}
|
||||
/* #ifdef H5 */
|
||||
.pointer{
|
||||
cursor: pointer;
|
||||
}
|
||||
/* #endif */
|
||||
</style>
|
||||
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<view>
|
||||
<view :style="{'height':soundState?'':0,bottom:markBottom}" class="mark"></view>
|
||||
<view @touchmove="touchmove" @touchstart="soundStart" @touchend="soundEnd" @touchcancel="soundEnd"
|
||||
class="sound-buttom" :class="{soundState}">
|
||||
<view v-if="soundProgress" class="sound-progress" :style="{'width':soundProgress}"></view>
|
||||
<text class="sound-text">{{soundState?'录音中('+time+'s)':'按住 说话'}}</text>
|
||||
<view class="sound-tip" v-if="soundState">
|
||||
<text class="sound-tip-text" :style="{color:cancel?'#f70000':'#FFFFFF'}">{{cancel?'松手取消':'松手发送,上划取消'}}</text>
|
||||
<view class="closeIcon" :style="{'background-color':cancel?'#f70000':'#EEEEEE'}">
|
||||
<uni-im-icons code="e61a" size="10px" color="#FFFFFF"></uni-im-icons>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// #ifndef H5
|
||||
const recorderManager = uni.getRecorderManager();
|
||||
// #endif
|
||||
|
||||
import uniIm from '@/uni_modules/uni-im/lib/main.js';
|
||||
const systemInfo = uniIm.systemInfo
|
||||
|
||||
let soundInterval,soundPath,startTime;
|
||||
export default {
|
||||
emits: ['success'],
|
||||
data() {
|
||||
return {
|
||||
soundState:0,
|
||||
soundProgress:0,
|
||||
cancel:false,
|
||||
time:0,
|
||||
phoneBH:0
|
||||
}
|
||||
},
|
||||
computed:{
|
||||
markBottom(){
|
||||
let markBottom = 58;
|
||||
// #ifdef MP-WEIXIN
|
||||
markBottom += (systemInfo.screenHeight - systemInfo.safeArea.bottom)
|
||||
// #endif
|
||||
return markBottom + 'px'
|
||||
},
|
||||
},
|
||||
created() {
|
||||
// #ifndef H5
|
||||
recorderManager.onStop((res)=> {
|
||||
// console.log('recorderManager.onStop',{res});
|
||||
if(!this.cancel){
|
||||
if(this.time < 2){
|
||||
return uni.showToast({
|
||||
title: '语音时间过短',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
uni.showLoading({
|
||||
title: '上传中',
|
||||
mask: false
|
||||
});
|
||||
uniCloud.uploadFile({
|
||||
filePath:res.tempFilePath,
|
||||
cloudPath:'uni-im/' + uniCloud.getCurrentUserInfo().uid + '/sound/' + Date.now() + '.mp3',
|
||||
// fileType:"audio",
|
||||
success: (e) => {
|
||||
console.log('uniCloud.uploadFile-success',e,'success',{"url":e.fileID,time:this.time});
|
||||
try{
|
||||
this.$emit('success',{"url":e.fileID,time:this.time})
|
||||
}catch(e){
|
||||
console.log(e);
|
||||
//TODO handle the exception
|
||||
}
|
||||
uni.hideLoading()
|
||||
},
|
||||
fail: (e) =>{
|
||||
console.log(e);
|
||||
uni.showModal({
|
||||
content: JSON.stringify(e),
|
||||
showCancel: false,
|
||||
confirmText: '知道了'
|
||||
});
|
||||
},
|
||||
complete: (e) =>{
|
||||
console.log('complete',e);
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
}else{
|
||||
console.log('用户取消了录音功能','this.time:'+this.time);
|
||||
}
|
||||
});
|
||||
|
||||
recorderManager.onStart(e=>{
|
||||
// console.log(e);
|
||||
})
|
||||
recorderManager.onPause(e=>{
|
||||
// console.log(e);
|
||||
})
|
||||
recorderManager.onError(e=>{
|
||||
console.error(e);
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
touchmove(e){
|
||||
// #ifndef APP-NVUE
|
||||
let y = e.touches[0].clientY + systemInfo.safeArea.top + (systemInfo.screenHeight - systemInfo.safeArea.bottom)
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
let y = e.touches[0].screenY
|
||||
// #endif
|
||||
|
||||
|
||||
if(systemInfo.safeArea.bottom - y > 58){
|
||||
this.cancel = true
|
||||
}else{
|
||||
this.cancel = false
|
||||
}
|
||||
},
|
||||
soundStart(e){
|
||||
// 关闭正在播放的sound
|
||||
uniIm.audioContext.stop()
|
||||
this.time = 0
|
||||
|
||||
// #ifdef H5
|
||||
// 防止H5端 调试出现鼠标右键菜单
|
||||
window.oncontextmenu = function () { return false; }
|
||||
// #endif
|
||||
|
||||
// #ifdef H5
|
||||
return uni.showToast({
|
||||
title: 'h5端不支持语音功能',
|
||||
icon: 'none'
|
||||
});
|
||||
// #endif
|
||||
|
||||
// #ifndef H5
|
||||
recorderManager.start({
|
||||
sampleRate:16000,
|
||||
numberOfChannels:2,
|
||||
format:"mp3"
|
||||
});
|
||||
// #endif
|
||||
|
||||
startTime = Date.now()
|
||||
|
||||
console.log('soundStart');
|
||||
|
||||
//进度条
|
||||
this.soundState = 1
|
||||
soundInterval = setInterval(()=>{
|
||||
this.soundProgress = parseInt(this.soundProgress) + uni.upx2px(450/60) +'px'
|
||||
// console.log('this.soundProgress',this.soundProgress);
|
||||
this.time = parseInt((Date.now() - startTime) / 1000)
|
||||
},1000)
|
||||
// e.preventDefault();
|
||||
},
|
||||
soundEnd(){
|
||||
// #ifndef H5
|
||||
recorderManager.stop();
|
||||
// #endif
|
||||
console.log('soundEnd');
|
||||
clearInterval(soundInterval)
|
||||
setTimeout(()=> {
|
||||
this.soundState = 0
|
||||
this.soundProgress = 0
|
||||
this.cancel = false
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.sound-buttom {
|
||||
background-color: #ffffff;
|
||||
padding: 10px;
|
||||
width: 450rpx;
|
||||
height: 46px;
|
||||
// border-radius: 10px;
|
||||
font-size: 16px;
|
||||
align-items: flex-start;
|
||||
justify-content: center;
|
||||
/* #ifndef APP-NVUE */
|
||||
overflow:hidden;
|
||||
/* #endif */
|
||||
}
|
||||
.sound-text{
|
||||
position: relative;
|
||||
left: -20rpx;
|
||||
width: 450rpx;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
}
|
||||
.sound-tip{
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: 95px;
|
||||
width: 750rpx;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.sound-tip-text{
|
||||
margin-bottom: 10px;
|
||||
color: #999999;
|
||||
font-size: 14px;
|
||||
}
|
||||
.closeIcon{
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
background-color: #DDDDDD;
|
||||
border-radius: 100px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
.sound-progress {
|
||||
// border-radius: 10px;
|
||||
height: 44px;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding: 0;
|
||||
transition: width 0.2s linear;
|
||||
background-color: #2faf4c;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
.soundState{
|
||||
background-color: #efefef;
|
||||
}
|
||||
|
||||
.mark{
|
||||
width: 750rpx;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 57px;
|
||||
right: 0;
|
||||
background:rgba(0,0,0,0.7);
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user