no message

This commit is contained in:
kuaifan 2022-06-30 16:07:50 +08:00
parent 500ed3a4d7
commit a368e00b2f
10 changed files with 166 additions and 181 deletions

View File

@ -1122,6 +1122,39 @@
return {width, height}; return {width, height};
}, },
/**
* 阻止滑动穿透
* @param el
*/
scrollPreventThrough(el) {
if (!el) {
return;
}
if (el.getAttribute("data-prevent-through") === "yes") {
return;
}
el.setAttribute("data-prevent-through", "yes")
//
let targetY = null;
el.addEventListener('touchstart', function (e) {
targetY = Math.floor(e.targetTouches[0].clientY);
});
el.addEventListener('touchmove', function (e) {
// 检测可滚动区域的滚动事件,如果滑到了顶部或底部,阻止默认事件
let NewTargetY = Math.floor(e.targetTouches[0].clientY), //本次移动时鼠标的位置,用于计算
sTop = el.scrollTop, //当前滚动的距离
sH = el.scrollHeight, //可滚动区域的高度
lyBoxH = el.clientHeight; //可视区域的高度
if (sTop <= 0 && NewTargetY - targetY > 0) {
// 下拉页面到顶
e.preventDefault();
} else if (sTop >= sH - lyBoxH && NewTargetY - targetY < 0) {
// 上翻页面到底
e.preventDefault();
}
}, false);
},
/** /**
* 获取元素属性 * 获取元素属性
* @param el * @param el

View File

@ -1,141 +0,0 @@
const assetsFunctionUtils = {
/**
* 消息格式化处理
* @param text
* @param userid
* @returns {string|*}
*/
textMsgFormat(text, userid) {
if (!text) {
return ""
}
const atReg = new RegExp(`<span class="mention user" data-id="${userid}">`, "g")
text = text.trim().replace(/(\n\x20*){3,}/g, "\n\n");
text = text.replace(/&nbsp;/g, ' ')
text = text.replace(/<p><\/p>/g, '<p><br/></p>')
text = text.replace(/\{\{RemoteURL\}\}/g, $A.apiUrl('../'))
text = text.replace(atReg, `<span class="mention me" data-id="${userid}">`)
// 处理内容连接
if (/https*:\/\//.test(text)) {
text = text.split(/(<[^>]*>)/g).map(string => {
if (string && !/<[^>]*>/.test(string)) {
string = string.replace(/(https*:\/\/)((\w|=|\?|\.|\/|&|-|:|\+|%|;|#)+)/g, "<a href=\"$1$2\" target=\"_blank\">$1$2</a>")
}
return string;
}).join("")
}
// 处理图片显示尺寸
const array = text.match(/<img\s+[^>]*?>/g);
if (array) {
const widthReg = new RegExp("width=\"(\\d+)\""),
heightReg = new RegExp("height=\"(\\d+)\"")
array.some(res => {
const widthMatch = res.match(widthReg),
heightMatch = res.match(heightReg);
if (widthMatch && heightMatch) {
const width = parseInt(widthMatch[1]),
height = parseInt(heightMatch[1]),
maxSize = res.indexOf("emoticon") > -1 ? 150 : 220;
const scale = $A.scaleToScale(width, height, maxSize, maxSize);
const value = res
.replace(widthReg, `original-width="${width}" width="${scale.width}"`)
.replace(heightReg, `original-height="${height}" height="${scale.height}"`)
text = text.replace(res, value)
}
})
}
return text;
},
/**
* 获取文本消息图片
* @param text
* @returns {*[]}
*/
textImagesInfo(text) {
const baseUrl = $A.apiUrl('../');
const array = text.match(new RegExp(`<img[^>]*?>`, "g"));
const list = [];
if (array) {
const srcReg = new RegExp("src=([\"'])([^'\"]*)\\1"),
widthReg = new RegExp("(original-)?width=\"(\\d+)\""),
heightReg = new RegExp("(original-)?height=\"(\\d+)\"")
array.some(res => {
const srcMatch = res.match(srcReg),
widthMatch = res.match(widthReg),
heightMatch = res.match(heightReg);
if (srcMatch) {
list.push({
src: srcMatch[2].replace(/\{\{RemoteURL\}\}/g, baseUrl),
width: widthMatch ? widthMatch[2] : -1,
height: heightMatch ? heightMatch[2] : -1,
})
}
})
}
return list;
},
/**
* 消息简单描述
* @param data
* @returns {string|*}
*/
msgSimpleDesc(data) {
if ($A.isJson(data)) {
switch (data.type) {
case 'text':
return $A.getMsgTextPreview(data.msg.text)
case 'record':
return `[${$A.L('语音')}]`
case 'meeting':
return `[${$A.L('会议')}] ${data.msg.name}`
case 'file':
if (data.msg.type == 'img') {
return `[${$A.L('图片')}]`
}
return `[${$A.L('文件')}] ${data.msg.name}`
case 'tag':
return `[${$A.L(data.msg.action === 'remove' ? '取消标注' : '标注')}] ${assetsFunctionUtils.msgSimpleDesc(data.msg.data)}`
default:
return `[${$A.L('未知的消息')}]`
}
}
return '';
},
/**
* 阻止滑动穿透
* @param el
*/
scrollPreventThrough(el) {
if (!el) {
return;
}
if (el.getAttribute("data-prevent-through") === "yes") {
return;
}
el.setAttribute("data-prevent-through", "yes")
//
let targetY = null;
el.addEventListener('touchstart', function (e) {
targetY = Math.floor(e.targetTouches[0].clientY);
});
el.addEventListener('touchmove', function (e) {
// 检测可滚动区域的滚动事件,如果滑到了顶部或底部,阻止默认事件
let NewTargetY = Math.floor(e.targetTouches[0].clientY), //本次移动时鼠标的位置,用于计算
sTop = el.scrollTop, //当前滚动的距离
sH = el.scrollHeight, //可滚动区域的高度
lyBoxH = el.clientHeight; //可视区域的高度
if (sTop <= 0 && NewTargetY - targetY > 0) {
// 下拉页面到顶
e.preventDefault();
} else if (sTop >= sH - lyBoxH && NewTargetY - targetY < 0) {
// 上翻页面到底
e.preventDefault();
}
}, false);
},
}
module.exports = assetsFunctionUtils

View File

@ -391,6 +391,111 @@
return text.replace(/<[^>]+>/g,"") return text.replace(/<[^>]+>/g,"")
}, },
/**
* 消息格式化处理
* @param text
* @param userid
* @returns {string|*}
*/
formatTextMsg(text, userid) {
if (!text) {
return ""
}
const atReg = new RegExp(`<span class="mention user" data-id="${userid}">`, "g")
text = text.trim().replace(/(\n\x20*){3,}/g, "\n\n");
text = text.replace(/&nbsp;/g, ' ')
text = text.replace(/<p><\/p>/g, '<p><br/></p>')
text = text.replace(/\{\{RemoteURL\}\}/g, $A.apiUrl('../'))
text = text.replace(atReg, `<span class="mention me" data-id="${userid}">`)
// 处理内容连接
if (/https*:\/\//.test(text)) {
text = text.split(/(<[^>]*>)/g).map(string => {
if (string && !/<[^>]*>/.test(string)) {
string = string.replace(/(https*:\/\/)((\w|=|\?|\.|\/|&|-|:|\+|%|;|#)+)/g, "<a href=\"$1$2\" target=\"_blank\">$1$2</a>")
}
return string;
}).join("")
}
// 处理图片显示尺寸
const array = text.match(/<img\s+[^>]*?>/g);
if (array) {
const widthReg = new RegExp("width=\"(\\d+)\""),
heightReg = new RegExp("height=\"(\\d+)\"")
array.some(res => {
const widthMatch = res.match(widthReg),
heightMatch = res.match(heightReg);
if (widthMatch && heightMatch) {
const width = parseInt(widthMatch[1]),
height = parseInt(heightMatch[1]),
maxSize = res.indexOf("emoticon") > -1 ? 150 : 220;
const scale = $A.scaleToScale(width, height, maxSize, maxSize);
const value = res
.replace(widthReg, `original-width="${width}" width="${scale.width}"`)
.replace(heightReg, `original-height="${height}" height="${scale.height}"`)
text = text.replace(res, value)
}
})
}
return text;
},
/**
* 获取文本消息图片
* @param text
* @returns {*[]}
*/
getTextImagesInfo(text) {
const baseUrl = $A.apiUrl('../');
const array = text.match(new RegExp(`<img[^>]*?>`, "g"));
const list = [];
if (array) {
const srcReg = new RegExp("src=([\"'])([^'\"]*)\\1"),
widthReg = new RegExp("(original-)?width=\"(\\d+)\""),
heightReg = new RegExp("(original-)?height=\"(\\d+)\"")
array.some(res => {
const srcMatch = res.match(srcReg),
widthMatch = res.match(widthReg),
heightMatch = res.match(heightReg);
if (srcMatch) {
list.push({
src: srcMatch[2].replace(/\{\{RemoteURL\}\}/g, baseUrl),
width: widthMatch ? widthMatch[2] : -1,
height: heightMatch ? heightMatch[2] : -1,
})
}
})
}
return list;
},
/**
* 消息简单描述
* @param data
* @returns {string|*}
*/
getMsgSimpleDesc(data) {
if ($A.isJson(data)) {
switch (data.type) {
case 'text':
return $A.getMsgTextPreview(data.msg.text)
case 'record':
return `[${$A.L('语音')}]`
case 'meeting':
return `[${$A.L('会议')}] ${data.msg.name}`
case 'file':
if (data.msg.type == 'img') {
return `[${$A.L('图片')}]`
}
return `[${$A.L('文件')}] ${data.msg.name}`
case 'tag':
return `[${$A.L(data.msg.action === 'remove' ? '取消标注' : '标注')}] ${$A.getMsgSimpleDesc(data.msg.data)}`
default:
return `[${$A.L('未知的消息')}]`
}
}
return '';
},
/** /**
* 获取文件标题 * 获取文件标题
* @param file * @param file

View File

@ -4,7 +4,7 @@
<!-- 回复 --> <!-- 回复 -->
<div v-if="replyData" class="chat-reply"> <div v-if="replyData" class="chat-reply">
<UserAvatar :userid="replyData.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/> <UserAvatar :userid="replyData.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/>
<div class="reply-desc">{{formatMsgDesc(replyData)}}</div> <div class="reply-desc">{{$A.getMsgSimpleDesc(replyData)}}</div>
<i class="taskfont" @click.stop="onCancelReply">&#xe6e5;</i> <i class="taskfont" @click.stop="onCancelReply">&#xe6e5;</i>
</div> </div>
@ -132,7 +132,6 @@ import touchmouse from "../../../../directives/touchmouse";
import TransferDom from "../../../../directives/transfer-dom"; import TransferDom from "../../../../directives/transfer-dom";
import clickoutside from "../../../../directives/clickoutside"; import clickoutside from "../../../../directives/clickoutside";
import {Store} from "le5le-store"; import {Store} from "le5le-store";
import {scrollPreventThrough, msgSimpleDesc} from "../../../../functions/utils";
export default { export default {
name: 'ChatInput', name: 'ChatInput',
@ -526,7 +525,7 @@ export default {
containers[i].classList.remove("user-mention"); containers[i].classList.remove("user-mention");
containers[i].classList.remove("task-mention"); containers[i].classList.remove("task-mention");
containers[i].classList.add(mentionName); containers[i].classList.add(mentionName);
scrollPreventThrough(containers[i]); $A.scrollPreventThrough(containers[i]);
} }
this.getMentionSource(mentionChar, searchTerm, array => { this.getMentionSource(mentionChar, searchTerm, array => {
const values = []; const values = [];
@ -1146,10 +1145,6 @@ export default {
this.$emit('on-file', postFiles) this.$emit('on-file', postFiles)
} }
}, },
formatMsgDesc(data) {
return msgSimpleDesc(data);
},
} }
} }
</script> </script>

View File

@ -3,7 +3,7 @@
<div v-if="source.type === 'tag'" class="dialog-tag" @click="onViewTag"> <div v-if="source.type === 'tag'" class="dialog-tag" @click="onViewTag">
<div class="tag-user"><UserAvatar :userid="source.userid" :tooltipDisabled="source.userid == userId" :show-name="true" :show-icon="false"/></div> <div class="tag-user"><UserAvatar :userid="source.userid" :tooltipDisabled="source.userid == userId" :show-name="true" :show-icon="false"/></div>
{{$L(source.msg.action === 'remove' ? '取消标注' : '标注了')}} {{$L(source.msg.action === 'remove' ? '取消标注' : '标注了')}}
"{{formatMsgDesc(source.msg.data)}}" "{{$A.getMsgSimpleDesc(source.msg.data)}}"
</div> </div>
<template v-else> <template v-else>
<div class="dialog-avatar"> <div class="dialog-avatar">
@ -29,7 +29,6 @@
<script> <script>
import {mapState} from "vuex"; import {mapState} from "vuex";
import DialogView from "./DialogView"; import DialogView from "./DialogView";
import {msgSimpleDesc} from "../../../functions/utils";
export default { export default {
name: "DialogItem", name: "DialogItem",
@ -89,10 +88,6 @@ export default {
}, },
methods: { methods: {
formatMsgDesc(data) {
return msgSimpleDesc(data)
},
onViewTag() { onViewTag() {
this.onViewReply({ this.onViewReply({
msg_id: this.source.id, msg_id: this.source.id,

View File

@ -12,13 +12,13 @@
<!--回复--> <!--回复-->
<div v-if="!hideReply && msgData.reply_data" class="dialog-reply no-dark-content" @click="viewReply"> <div v-if="!hideReply && msgData.reply_data" class="dialog-reply no-dark-content" @click="viewReply">
<UserAvatar :userid="msgData.reply_data.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/> <UserAvatar :userid="msgData.reply_data.userid" :show-icon="false" :show-name="true" :tooltip-disabled="true"/>
<div class="reply-desc">{{formatMsgDesc(msgData.reply_data)}}</div> <div class="reply-desc">{{$A.getMsgSimpleDesc(msgData.reply_data)}}</div>
</div> </div>
<!--详情--> <!--详情-->
<div class="dialog-content" :class="contentClass"> <div class="dialog-content" :class="contentClass">
<!--文本--> <!--文本-->
<div v-if="msgData.type === 'text'" class="content-text no-dark-content"> <div v-if="msgData.type === 'text'" class="content-text no-dark-content">
<pre @click="viewText" v-html="textMsg(msgData.msg.text)"></pre> <pre @click="viewText" v-html="$A.formatTextMsg(msgData.msg.text, userId)"></pre>
</div> </div>
<!--文件--> <!--文件-->
<div v-else-if="msgData.type === 'file'" :class="`content-file ${msgData.msg.type}`"> <div v-else-if="msgData.type === 'file'" :class="`content-file ${msgData.msg.type}`">
@ -137,7 +137,6 @@ import WCircle from "../../../components/WCircle";
import {mapGetters, mapState} from "vuex"; import {mapGetters, mapState} from "vuex";
import {Store} from "le5le-store"; import {Store} from "le5le-store";
import longpress from "../../../directives/longpress"; import longpress from "../../../directives/longpress";
import {textMsgFormat, msgSimpleDesc} from "../../../functions/utils";
export default { export default {
name: "DialogView", name: "DialogView",
@ -315,10 +314,6 @@ export default {
}); });
}, },
textMsg(text) {
return textMsgFormat(text, this.userId);
},
recordStyle(info) { recordStyle(info) {
const {duration} = info; const {duration} = info;
const width = 50 + Math.min(180, Math.floor(duration / 150)); const width = 50 + Math.min(180, Math.floor(duration / 150));
@ -360,10 +355,6 @@ export default {
return {}; return {};
}, },
formatMsgDesc(data) {
return msgSimpleDesc(data)
},
playRecord() { playRecord() {
if (this.operateVisible) { if (this.operateVisible) {
return return

View File

@ -295,7 +295,6 @@ import ChatInput from "./ChatInput";
import VirtualList from 'vue-virtual-scroll-list-hi' import VirtualList from 'vue-virtual-scroll-list-hi'
import {Store} from "le5le-store"; import {Store} from "le5le-store";
import {textImagesInfo} from "../../../functions/utils";
export default { export default {
name: "DialogWrapper", name: "DialogWrapper",
@ -1267,7 +1266,7 @@ export default {
this.onViewPicture(target.currentSrc); this.onViewPicture(target.currentSrc);
} else { } else {
this.$store.state.previewImageIndex = 0; this.$store.state.previewImageIndex = 0;
this.$store.state.previewImageList = textImagesInfo(target.outerHTML); this.$store.state.previewImageList = $A.getTextImagesInfo(target.outerHTML);
} }
break; break;
@ -1347,7 +1346,7 @@ export default {
height: msg.height, height: msg.height,
}) })
} else if (type === 'text') { } else if (type === 'text') {
list.push(...textImagesInfo(msg.text)) list.push(...$A.getTextImagesInfo(msg.text))
} }
}) })
// //

View File

@ -75,7 +75,7 @@
</template> </template>
<div class="last-text"> <div class="last-text">
<em v-if="formatMsgEmojiDesc(dialog.last_msg)">{{formatMsgEmojiDesc(dialog.last_msg)}}</em> <em v-if="formatMsgEmojiDesc(dialog.last_msg)">{{formatMsgEmojiDesc(dialog.last_msg)}}</em>
<span>{{formatMsgDesc(dialog.last_msg)}}</span> <span>{{$A.getMsgSimpleDesc(dialog.last_msg)}}</span>
</div> </div>
</div> </div>
</div> </div>
@ -153,7 +153,6 @@ import {mapState} from "vuex";
import DialogWrapper from "./components/DialogWrapper"; import DialogWrapper from "./components/DialogWrapper";
import ScrollerY from "../../components/ScrollerY"; import ScrollerY from "../../components/ScrollerY";
import longpress from "../../directives/longpress"; import longpress from "../../directives/longpress";
import {msgSimpleDesc} from "../../functions/utils";
export default { export default {
components: {ScrollerY, DialogWrapper}, components: {ScrollerY, DialogWrapper},
@ -604,10 +603,6 @@ export default {
return null; return null;
}, },
formatMsgDesc(data) {
return msgSimpleDesc(data);
},
lastMsgReadDone(data) { lastMsgReadDone(data) {
if ($A.isJson(data)) { if ($A.isJson(data)) {
const {userid, percentage} = data; const {userid, percentage} = data;

View File

@ -148,7 +148,7 @@ export default {
break; break;
case 'privacy': case 'privacy':
window.open($A.apiUrl('../privacy.html')) this.openPrivacy();
break; break;
case 'index': case 'index':
@ -161,6 +161,24 @@ export default {
} }
}, },
openPrivacy() {
const url = $A.apiUrl('../privacy.html')
if (this.$isEEUiApp) {
$A.eeuiAppOpenPage({
pageType: 'app',
pageTitle: ' ',
url: 'web.js',
params: {
url,
browser: true,
showProgress: true,
},
});
} else {
window.open(url)
}
},
classNameRoute(path, divided) { classNameRoute(path, divided) {
return { return {
"active": this.windowLarge && this.routeName === `manage-setting-${path}`, "active": this.windowLarge && this.routeName === `manage-setting-${path}`,

View File

@ -8,7 +8,7 @@
<Drawio v-else-if="isType('drawio')" v-model="msgDetail.content" :title="msgDetail.msg.name" readOnly/> <Drawio v-else-if="isType('drawio')" v-model="msgDetail.content" :title="msgDetail.msg.name" readOnly/>
<Minder v-else-if="isType('mind')" :value="msgDetail.content" readOnly/> <Minder v-else-if="isType('mind')" :value="msgDetail.content" readOnly/>
<template v-else-if="isType('code')"> <template v-else-if="isType('code')">
<div v-if="isLongText(msgDetail.msg.name)" class="view-code" v-html="longTextFormat(msgDetail.content.content)"></div> <div v-if="isLongText(msgDetail.msg.name)" class="view-code" v-html="$A.formatTextMsg(msgDetail.content.content, userId)"></div>
<AceEditor v-else v-model="msgDetail.content.content" :ext="msgDetail.msg.ext" class="view-editor" readOnly/> <AceEditor v-else v-model="msgDetail.content.content" :ext="msgDetail.msg.ext" class="view-editor" readOnly/>
</template> </template>
<OnlyOffice v-else-if="isType('office')" v-model="officeContent" :code="officeCode" :documentKey="documentKey" readOnly/> <OnlyOffice v-else-if="isType('office')" v-model="officeContent" :code="officeCode" :documentKey="documentKey" readOnly/>
@ -64,7 +64,6 @@
<script> <script>
import Vue from 'vue' import Vue from 'vue'
import Minder from '../../components/Minder' import Minder from '../../components/Minder'
import {textMsgFormat} from "../../functions/utils";
import {mapState} from "vuex"; import {mapState} from "vuex";
import IFrame from "../manage/components/IFrame"; import IFrame from "../manage/components/IFrame";
Vue.use(Minder) Vue.use(Minder)
@ -186,10 +185,6 @@ export default {
isLongText(name) { isLongText(name) {
return /^LongText-/.test(name) return /^LongText-/.test(name)
}, },
longTextFormat(text) {
return textMsgFormat(text, this.userId)
},
} }
} }
</script> </script>