增加右键菜单配置
增加setEditorValue、getEditorValue方法 修改updateContact传参 修复只选择表情发送按钮是灰色的问题 修复send之后toContactId丢失的问题
This commit is contained in:
@@ -4,6 +4,14 @@ import { timeFormat,useScopedSlot } from "utils";
|
||||
export default {
|
||||
name: "LemonContact",
|
||||
components: {},
|
||||
inject: {
|
||||
IMUI: {
|
||||
from:'IMUI',
|
||||
default (){
|
||||
return this;
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
@@ -39,11 +47,9 @@ export default {
|
||||
<lemon-badge
|
||||
count={!this.simple ? contact.unread : 0}
|
||||
class="lemon-contact__avatar"
|
||||
native-on-click={e => this._handleBubbleClick(e, contact)}
|
||||
>
|
||||
<lemon-avatar
|
||||
size={40}
|
||||
native-on-click={e => this._handleAvatarClick(e, contact)}
|
||||
src={contact.avatar}
|
||||
/>
|
||||
</lemon-badge>,
|
||||
@@ -71,14 +77,6 @@ export default {
|
||||
_handleClick(e, data) {
|
||||
this.$emit("click", data);
|
||||
},
|
||||
_handleAvatarClick(e, data) {
|
||||
e.stopPropagation();
|
||||
this.$emit("avatar-click", data);
|
||||
},
|
||||
_handleBubbleClick(e, data) {
|
||||
e.stopPropagation();
|
||||
this.$emit("bubble-click", data);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script>
|
||||
import { toEmojiName,useScopedSlot,clearHtml } from "utils";
|
||||
import { toEmojiName,useScopedSlot,clearHtmlExcludeImg } from "utils";
|
||||
const exec = (val, command = "insertHTML") => {
|
||||
document.execCommand(command, false, val);
|
||||
};
|
||||
@@ -212,6 +212,7 @@ export default {
|
||||
_handleSelectEmoji(item) {
|
||||
this._focusLastRange();
|
||||
exec(`<img emoji-name="${item.name}" src="${item.src}"></img>`);
|
||||
this._checkSubmitDisabled();
|
||||
this._saveLastRange();
|
||||
},
|
||||
async selectFile(accept) {
|
||||
@@ -248,7 +249,7 @@ export default {
|
||||
return toEmojiName(this.$refs.textarea.innerHTML);
|
||||
},
|
||||
_checkSubmitDisabled() {
|
||||
this.submitDisabled = !this.$refs.textarea.innerText.trim();
|
||||
this.submitDisabled = !clearHtmlExcludeImg(this.$refs.textarea.innerHTML.trim());
|
||||
},
|
||||
_handleSend(e) {
|
||||
const text = this.getFormatValue();
|
||||
@@ -269,7 +270,11 @@ export default {
|
||||
initEmoji(data) {
|
||||
emojiData = data;
|
||||
this.$forceUpdate();
|
||||
}
|
||||
},
|
||||
setValue(val){
|
||||
this.$refs.textarea.innerHTML = val;
|
||||
this._checkSubmitDisabled();
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script>
|
||||
import { useScopedSlot, funCall, generateUUID,cloneDeep } from "utils";
|
||||
import { isFunction, isString, isEmpty } from "utils/validate";
|
||||
import dropdown from "../directives/dropdown";
|
||||
import {
|
||||
DEFAULT_MENUS,
|
||||
DEFAULT_MENU_LASTMESSAGES,
|
||||
@@ -68,6 +69,8 @@ export default {
|
||||
hideMessageTime:Boolean,
|
||||
sendKey:Function,
|
||||
sendText:String,
|
||||
contextmenu:Array,
|
||||
contactContextmenu:Array,
|
||||
user: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
@@ -153,7 +156,8 @@ export default {
|
||||
*/
|
||||
appendMessage(message,scrollToBottom = false) {
|
||||
if(allMessages[message.toContactId] === undefined){
|
||||
this.updateContact(message.toContactId, {
|
||||
this.updateContact({
|
||||
id:message.toContactId,
|
||||
unread: "+1",
|
||||
lastSendTime: message.sendTime,
|
||||
lastContent: this.lastContentRender(message)
|
||||
@@ -161,6 +165,7 @@ export default {
|
||||
}else{
|
||||
this._addMessage(message,message.toContactId, 1);
|
||||
const updateContact = {
|
||||
id:message.toContactId,
|
||||
lastContent: this.lastContentRender(message),
|
||||
lastSendTime: message.sendTime
|
||||
}
|
||||
@@ -171,7 +176,7 @@ export default {
|
||||
}else{
|
||||
updateContact.unread = '+1';
|
||||
}
|
||||
this.updateContact(message.toContactId,updateContact);
|
||||
this.updateContact(updateContact);
|
||||
}
|
||||
},
|
||||
_emitSend(message, next, file) {
|
||||
@@ -189,7 +194,8 @@ export default {
|
||||
const message = this._createMessage({ content: text });
|
||||
this.appendMessage(message,true);
|
||||
this._emitSend(message, () => {
|
||||
this.updateContact(message.toContactId, {
|
||||
this.updateContact({
|
||||
id:message.toContactId,
|
||||
lastContent: this.lastContentRender(message),
|
||||
lastSendTime: message.sendTime
|
||||
});
|
||||
@@ -216,7 +222,8 @@ export default {
|
||||
this._emitSend(
|
||||
message,
|
||||
() => {
|
||||
this.updateContact(message.toContactId, {
|
||||
this.updateContact({
|
||||
id:message.toContactId,
|
||||
lastContent: this.lastContentRender(message),
|
||||
lastSendTime: message.sendTime
|
||||
});
|
||||
@@ -356,6 +363,7 @@ export default {
|
||||
class={{
|
||||
"lemon-contact--active": this.currentContactId == props.contact.id
|
||||
}}
|
||||
v-dropdown_contact={this.contactContextmenu}
|
||||
props={props}
|
||||
on-click={click}
|
||||
scopedSlots={{default:slot}}
|
||||
@@ -394,7 +402,7 @@ export default {
|
||||
},
|
||||
_renderSidebar(children, name) {
|
||||
return (
|
||||
<div class="lemon-sidebar" v-show={this.activeSidebar == name}>
|
||||
<div class="lemon-sidebar" v-show={this.activeSidebar == name} on-scroll={this._handleSidebarScroll}>
|
||||
{children}
|
||||
</div>
|
||||
);
|
||||
@@ -487,6 +495,12 @@ export default {
|
||||
<h4>{curact.displayName}</h4>
|
||||
<lemon-button
|
||||
on-click={() => {
|
||||
if(isEmpty(curact.lastContent)){
|
||||
this.updateContact({
|
||||
id:curact.id,
|
||||
lastContent:' ',
|
||||
});
|
||||
}
|
||||
this.changeContact(curact.id, DEFAULT_MENU_LASTMESSAGES);
|
||||
}}
|
||||
>
|
||||
@@ -499,6 +513,9 @@ export default {
|
||||
);
|
||||
return nodes;
|
||||
},
|
||||
_handleSidebarScroll(){
|
||||
dropdown.hide();
|
||||
},
|
||||
_addContact(data, t) {
|
||||
const type = {
|
||||
0: "unshift",
|
||||
@@ -566,6 +583,7 @@ export default {
|
||||
}
|
||||
|
||||
this.currentContactId = contactId;
|
||||
if(!this.currentContactId) return false;
|
||||
this.$emit("change-contact", this.currentContact,this);
|
||||
if (isFunction(this.currentContact.renderContainer)) {
|
||||
return;
|
||||
@@ -606,12 +624,14 @@ export default {
|
||||
*/
|
||||
updateMessage(message) {
|
||||
if(!message.id) return false;
|
||||
delete message.toContactId;
|
||||
let historyMessage = this.findMessage(message.id);
|
||||
if(!historyMessage) return false;
|
||||
historyMessage = Object.assign(
|
||||
historyMessage,
|
||||
message
|
||||
message,
|
||||
{
|
||||
toContactId:historyMessage.toContactId
|
||||
}
|
||||
);
|
||||
return true;
|
||||
},
|
||||
@@ -769,11 +789,11 @@ export default {
|
||||
},
|
||||
/**
|
||||
* 修改联系人数据
|
||||
* @param {Contact} data 修改的数据,根据 data.id 查找联系人并覆盖传入的值
|
||||
* @param {Contact} data 修改的数据,根据 Contact.id 查找联系人并覆盖传入的值
|
||||
*/
|
||||
updateContact(contactId, data) {
|
||||
updateContact(data) {
|
||||
const contactId = data.id;
|
||||
delete data.id;
|
||||
delete data.toContactId;
|
||||
|
||||
const index = this.findContactIndexById(contactId);
|
||||
if (index !== -1) {
|
||||
@@ -812,6 +832,9 @@ export default {
|
||||
if(message) return message;
|
||||
}
|
||||
},
|
||||
findContact(contactId){
|
||||
return this.getContacts().find(({id})=>id == contactId);
|
||||
},
|
||||
/**
|
||||
* 返回所有联系人
|
||||
* @return {Array<Contact>}
|
||||
@@ -826,6 +849,12 @@ export default {
|
||||
getCurrentMessages() {
|
||||
return this.currentMessages;
|
||||
},
|
||||
setEditorValue(val){
|
||||
this.$refs.editor.setValue(this.replaceEmojiName(val));
|
||||
},
|
||||
getEditorValue(){
|
||||
return this.$refs.editor.getFormatValue();
|
||||
},
|
||||
/**
|
||||
* 返回所有消息
|
||||
* @return {Object<Contact.id,Message>}
|
||||
@@ -985,4 +1014,40 @@ bezier = cubic-bezier(0.645, 0.045, 0.355, 1)
|
||||
.lemon-menu
|
||||
.lemon-sidebar
|
||||
display none
|
||||
+b(lemon-dropdown)
|
||||
border-radius 4px
|
||||
font-size 14px
|
||||
font-variant tabular-nums
|
||||
line-height 1.5
|
||||
color rgba(0, 0, 0, 0.65)
|
||||
z-index 10
|
||||
background-color #fff
|
||||
border-radius 4px
|
||||
box-shadow 0 2px 8px rgba(0, 0, 0, 0.06)
|
||||
position absolute
|
||||
transform-origin 50% 150%
|
||||
box-sizing border-box
|
||||
user-select none
|
||||
overflow hidden
|
||||
+e(item)
|
||||
font-size 12px
|
||||
line-height 14px
|
||||
padding 8px 10px
|
||||
cursor pointer
|
||||
display flex
|
||||
align-items center
|
||||
color #444
|
||||
> span
|
||||
display inline-block
|
||||
flex none
|
||||
//max-width 100px
|
||||
ellipsis()
|
||||
&:hover
|
||||
background #f3f3f3
|
||||
color #000
|
||||
&:active
|
||||
background #e9e9e9
|
||||
+e(icon)
|
||||
font-size 16px
|
||||
margin-right 4px
|
||||
</style>
|
||||
|
||||
@@ -11,6 +11,7 @@ export default {
|
||||
}
|
||||
},
|
||||
props: {
|
||||
contextmenu:Array,
|
||||
message: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
@@ -66,6 +67,7 @@ export default {
|
||||
</div>
|
||||
<div class="lemon-message__content-flex">
|
||||
<div
|
||||
v-dropdown_message={this.IMUI.contextmenu}
|
||||
class="lemon-message__content"
|
||||
on-click={e => {
|
||||
this._emitClick(e, "content");
|
||||
@@ -93,7 +95,6 @@ export default {
|
||||
cursor: "pointer"
|
||||
}}
|
||||
/>
|
||||
{this._renderStatue(status)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -108,23 +109,6 @@ export default {
|
||||
_emitClick(e, key) {
|
||||
this.IMUI.$emit("message-click", e, key, this.message,this.IMUI);
|
||||
},
|
||||
_renderStatue(status) {
|
||||
// if (status == "going") {
|
||||
// return <i class="lemon-icon-loading lemonani-spin" />;
|
||||
// } else if (status == "failed") {
|
||||
// return (
|
||||
// <i
|
||||
// class="lemon-icon-prompt"
|
||||
// title="重发消息"
|
||||
// style={{
|
||||
// color: "#ff2525",
|
||||
// cursor: "pointer"
|
||||
// }}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
// return;
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script>
|
||||
import { hoursTimeFormat } from "utils";
|
||||
import dropdown from "../directives/dropdown";
|
||||
export default {
|
||||
name: "LemonMessages",
|
||||
components: {},
|
||||
@@ -110,6 +111,7 @@ export default {
|
||||
},
|
||||
async _handleScroll(e) {
|
||||
const { target } = e;
|
||||
dropdown.hide();
|
||||
if (
|
||||
target.scrollTop == 0 &&
|
||||
this._loading == false &&
|
||||
|
||||
@@ -51,7 +51,7 @@ export default {
|
||||
render() {
|
||||
return (
|
||||
<span style="position:relative">
|
||||
<transition name="slide-top">
|
||||
<transition name="lemon-slide-top">
|
||||
{this.visible && (
|
||||
<div
|
||||
class="lemon-popover"
|
||||
@@ -117,7 +117,7 @@ export default {
|
||||
z-index 10
|
||||
background-color #fff
|
||||
border-radius 4px
|
||||
box-shadow 0 2px 8px rgba(0, 0, 0, 0.15)
|
||||
box-shadow 0 2px 8px rgba(0, 0, 0, 0.08)
|
||||
position absolute
|
||||
transform-origin 50% 150%
|
||||
+e(content)
|
||||
@@ -135,9 +135,9 @@ export default {
|
||||
width 8px
|
||||
height 8px
|
||||
background #fff
|
||||
.slide-top-leave-active ,.slide-top-enter-active
|
||||
transition all .3s cubic-bezier(0.645, 0.045, 0.355, 1)
|
||||
.slide-top-enter, .slide-top-leave-to
|
||||
.lemon-slide-top-leave-active ,.lemon-slide-top-enter-active
|
||||
transition all .2s cubic-bezier(0.645, 0.045, 0.355, 1)
|
||||
.lemon-slide-top-enter, .lemon-slide-top-leave-to
|
||||
transform translateY(-10px) scale(.8)
|
||||
opacity 0
|
||||
</style>
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
import Vue from 'vue';
|
||||
import {isFunction,isEmpty} from 'utils/validate';
|
||||
let popover;
|
||||
|
||||
const hidePopover = ()=>{
|
||||
if(popover) popover.style.display = 'none'
|
||||
}
|
||||
const showPopover = ()=>{
|
||||
if(popover) popover.style.display = 'block'
|
||||
}
|
||||
document.addEventListener('click',(e)=>{
|
||||
hidePopover()
|
||||
});
|
||||
export default{
|
||||
hide:hidePopover,
|
||||
bind(el, binding, vnode){
|
||||
el.addEventListener('contextmenu',(e)=>{
|
||||
if(isEmpty(binding.value) || !Array.isArray(binding.value)) return;
|
||||
e.preventDefault();
|
||||
let component;
|
||||
let visibleItems = []
|
||||
if(binding.modifiers.message) component = vnode.context;
|
||||
else if(binding.modifiers.contact) component = vnode.child;
|
||||
if(!popover){
|
||||
popover = document.createElement('div');
|
||||
popover.className = 'lemon-dropdown';
|
||||
document.body.appendChild(popover);
|
||||
}
|
||||
popover.innerHTML = binding.value.map((item)=>{
|
||||
let visible;
|
||||
if(isFunction(item.visible)){
|
||||
visible = item.visible(component);
|
||||
}else{
|
||||
visible = item.visible === undefined ? true : item.visible;
|
||||
}
|
||||
|
||||
if(visible){
|
||||
visibleItems.push(item);
|
||||
const icon = item.icon ? `<i class="lemon-dropdown__icon ${item.icon}"></i>` : '';
|
||||
return `<div style="color:${item.color}" title="${item.text}" class="lemon-dropdown__item">${icon}<span>${item.text}</span></div>`;
|
||||
}
|
||||
return '';
|
||||
}).join("");
|
||||
popover.style.top = `${e.pageY}px`;
|
||||
popover.style.left = `${e.pageX}px`;
|
||||
|
||||
popover.childNodes.forEach((node,index)=>{
|
||||
const {click,render} = visibleItems[index];
|
||||
node.addEventListener('click',(e)=>{
|
||||
e.stopPropagation();
|
||||
if(isFunction(click)) click(e,component,hidePopover);
|
||||
});
|
||||
|
||||
if(isFunction(render)){
|
||||
const ins = Vue.extend({render:(h)=>{
|
||||
return render(h,component,hidePopover);
|
||||
}});
|
||||
const renderComponent = new ins().$mount();
|
||||
node.querySelector('span').innerHTML = renderComponent.$el.outerHTML;
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
showPopover();
|
||||
});
|
||||
},
|
||||
inserted(el, binding, vnode){
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import Vue from 'vue';
|
||||
import Dropdown from './dropdown';
|
||||
Vue.directive('dropdown',Dropdown);
|
||||
+2
-1
@@ -14,7 +14,8 @@ import lemonMessageEvent from "./components/message/event";
|
||||
|
||||
import LemonIMUI from "./components/index";
|
||||
import "./styles/common/index.styl";
|
||||
const version = "0.1";
|
||||
import './directives';
|
||||
const version = "1.4.2";
|
||||
const components = [
|
||||
LemonIMUI,
|
||||
LemonContact,
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
// @font-face {
|
||||
// font-family: 'lemon-icons';
|
||||
// src: url('//at.alicdn.com/t/font_1312162_neqltsj20an.eot');
|
||||
// src: url('//at.alicdn.com/t/font_1312162_neqltsj20an.eot?#iefix') format('embedded-opentype'),
|
||||
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.woff2') format('woff2'),
|
||||
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.woff') format('woff'),
|
||||
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.ttf') format('truetype'),
|
||||
// url('//at.alicdn.com/t/font_1312162_neqltsj20an.svg#iconfont') format('svg');
|
||||
// }
|
||||
@font-face {
|
||||
font-family: 'lemon-icons';
|
||||
src: url('//at.alicdn.com/t/font_1312162_neqltsj20an.eot');
|
||||
src: url('//at.alicdn.com/t/font_1312162_neqltsj20an.eot?#iefix') format('embedded-opentype'),
|
||||
url('//at.alicdn.com/t/font_1312162_neqltsj20an.woff2') format('woff2'),
|
||||
url('//at.alicdn.com/t/font_1312162_neqltsj20an.woff') format('woff'),
|
||||
url('//at.alicdn.com/t/font_1312162_neqltsj20an.ttf') format('truetype'),
|
||||
url('//at.alicdn.com/t/font_1312162_neqltsj20an.svg#iconfont') format('svg');
|
||||
|
||||
src:url('../fonts/icon.woff') format('woff');
|
||||
}
|
||||
[class^='lemon-icon-'],
|
||||
[class*=' lemon-icon-']
|
||||
|
||||
Binary file not shown.
@@ -74,7 +74,10 @@ export function arrayIntersect(a, b) {
|
||||
export function clearHtml(str){
|
||||
return str.replace(/<.*?>/ig,"");
|
||||
}
|
||||
|
||||
//清除字符串内的所有HTML标签,除了IMG
|
||||
export function clearHtmlExcludeImg(str){
|
||||
return str.replace(/<(?!img).*?>/ig, "");
|
||||
}
|
||||
export function error(text) {
|
||||
throw new Error(text);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user