增加右键菜单配置

增加setEditorValue、getEditorValue方法
修改updateContact传参
修复只选择表情发送按钮是灰色的问题
修复send之后toContactId丢失的问题
This commit is contained in:
fan
2021-01-30 21:03:15 +08:00
parent f56dc40075
commit 3603d0c03b
23 changed files with 825 additions and 184 deletions
+8 -10
View File
@@ -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>
+8 -3
View File
@@ -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>
+75 -10
View File
@@ -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>
+2 -18
View File
@@ -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>
+2
View File
@@ -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 &&
+5 -5
View File
@@ -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>
+70
View File
@@ -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){
}
}
+3
View File
@@ -0,0 +1,3 @@
import Vue from 'vue';
import Dropdown from './dropdown';
Vue.directive('dropdown',Dropdown);
+2 -1
View File
@@ -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,
+10 -7
View File
@@ -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.
+4 -1
View File
@@ -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);
}