lemon-imui-vue3
Version:
> npm install lemon-imui-vue3 -S
1,806 lines (1,805 loc) • 49.6 kB
JavaScript
import { createVNode as n, Transition as X, withDirectives as f, vShow as g, resolveComponent as d, createTextVNode as j, isVNode as O, mergeProps as D, capitalize as Z, resolveDirective as K, reactive as q } from "vue";
function Q(e) {
return Object.prototype.toString.call(e) === "[object Object]";
}
function p(e) {
return typeof e == "string";
}
function J(e) {
return (/* @__PURE__ */ new Date()).getTime() - e < 864e5;
}
function C(e) {
return !!(!e || Array.isArray(e) && e.length == 0 || Q(e) && Object.values(e).length == 0);
}
function b(e) {
return e && typeof e == "function";
}
const N = [], ee = {
hover(e) {
},
focus(e) {
e.addEventListener("focus", (t) => {
this.changeVisible();
}), e.addEventListener("blur", (t) => {
this.changeVisible();
});
},
click(e) {
e.stopPropagation(), T.hide(), this.changeVisible();
},
contextmenu(e) {
e.addEventListener("contextmenu", (t) => {
t.preventDefault(), this.changeVisible();
});
}
}, P = {
name: "LemonPopover",
props: {
trigger: {
type: String,
default: "click",
validator(e) {
return Object.keys(ee).includes(e);
}
}
},
data() {
return this.defaultElm = null, {
popoverStyle: {},
visible: !1
};
},
created() {
document.addEventListener("click", this._documentClickEvent), N.push(this.close);
},
mounted() {
},
render() {
return n("span", {
style: "position:relative"
}, [n(X, {
name: "lemon-slide-top"
}, {
default: () => [this.visible && n("div", {
class: "lemon-popover",
ref: "popover",
style: this.popoverStyle,
onClick: (e) => e.stopPropagation()
}, [n("div", {
class: "lemon-popover__content"
}, [this.$slots.content && this.$slots.content()]), n("div", {
class: "lemon-popover__arrow"
}, null)])]
}), n("span", {
ref: "trigger",
onClick: (e) => {
e.stopPropagation(), T.hide(), this.changeVisible();
}
}, [this.$slots.default && this.$slots.default()])]);
},
destroyed() {
document.removeEventListener("click", this._documentClickEvent);
},
computed: {},
watch: {
async visible(e) {
if (e) {
await this.$nextTick();
const {
trigger: t,
popover: s
} = this.$refs;
this.popoverStyle = {
top: `-${s.offsetHeight + 10}px`,
left: `${t.offsetWidth / 2 - s.offsetWidth / 2}px`
};
}
}
},
methods: {
_documentClickEvent(e) {
e.stopPropagation(), this.visible && this.close();
},
changeVisible() {
this.visible ? this.close() : this.open();
},
open() {
this.closeAll(), this.visible = !0;
},
closeAll() {
N.forEach((e) => e());
},
close() {
this.visible = !1;
}
}
};
let u;
const E = () => {
u && (u.style.display = "none");
}, te = () => {
u && (u.style.display = "block");
};
document.addEventListener("click", (e) => {
E();
});
const T = {
hide: E,
bind(e, t, s) {
e.addEventListener(
t.modifiers.click ? "click" : "contextmenu",
(i) => {
if (C(t.value) || !Array.isArray(t.value))
return;
t.modifiers.click && i.stopPropagation(), i.preventDefault(), P.methods.closeAll();
let a, o = [];
t.modifiers.message ? a = s.context : t.modifiers.contact && (a = s.child), u || (u = document.createElement("div"), u.className = "lemon-contextmenu", document.body.appendChild(u)), u.innerHTML = t.value.map((r) => {
let c;
if (b(r.visible) ? c = r.visible(a) : c = r.visible === void 0 ? !0 : r.visible, c) {
o.push(r);
const l = r.icon ? `<i class="lemon-contextmenu__icon ${r.icon}"></i>` : "";
return `<div style="color:${r.color}" title="${r.text}" class="lemon-contextmenu__item">${l}<span>${r.text}</span></div>`;
}
return "";
}).join(""), u.style.top = `${i.pageY}px`, u.style.left = `${i.pageX}px`, u.childNodes.forEach((r, c) => {
const { click: l, render: v } = o[c];
r.addEventListener("click", (y) => {
y.stopPropagation(), b(l) && l(y, a, E);
});
}), te();
}
);
}
}, se = {
name: "LemonTabs",
props: {
activeIndex: String
},
data() {
return {
active: this.activeIndex
};
},
mounted() {
this.active || (this.active = this.$slots["tab-pane"][0].data.attrs.index);
},
render() {
const e = [], t = [];
return this.$slots["tab-pane"].map((s) => {
const {
tab: i,
index: a
} = s.data.attrs;
e.push(f(n("div", {
class: "lemon-tabs-content__pane"
}, [s]), [[g, this.active == a]])), t.push(n("div", {
class: ["lemon-tabs-nav__item", this.active == a && "lemon-tabs-nav__item--active"],
onClick: () => this._handleNavClick(a)
}, [i]));
}), n("div", {
class: "lemon-tabs"
}, [n("div", {
class: "lemon-tabs-content"
}, [e]), n("div", {
class: "lemon-tabs-nav"
}, [t])]);
},
methods: {
_handleNavClick(e) {
this.active = e;
}
}
};
const ne = {
name: "LemonButton",
props: {
color: {
type: String,
default: "default"
},
disabled: Boolean
},
render() {
return n("button", {
class: ["lemon-button", `lemon-button--color-${this.color}`],
disabled: this.disabled,
type: "button"
}, [this.$slots.default && this.$slots.default()]);
},
methods: {}
};
const ie = {
name: "LemonBadge",
props: {
count: [Number, Boolean],
overflowCount: {
type: Number,
default: 99
}
},
render() {
return n("span", {
class: "lemon-badge"
}, [this.$slots.default && this.$slots.default(), this.count !== 0 && this.count !== void 0 && n("span", {
class: ["lemon-badge__label", this.isDot && "lemon-badge__label--dot"]
}, [this.label])]);
},
computed: {
isDot() {
return this.count === !0;
},
label() {
return this.isDot ? "" : this.count > this.overflowCount ? `${this.overflowCount}+` : this.count;
}
},
methods: {}
};
const ae = {
name: "LemonAvatar",
inject: ["IMUI"],
props: {
src: String,
icon: {
type: String,
default: "lemon-icon-people"
},
circle: {
type: Boolean,
default() {
return this && this.IMUI ? this.IMUI.avatarCricle : !1;
}
},
size: {
type: Number,
default: 32
}
},
data() {
return {
imageFinishLoad: !0
};
},
render() {
return n("span", {
style: this.style,
class: ["lemon-avatar", {
"lemon-avatar--circle": this.circle
}],
onClick: (e) => this.$emit("click", e)
}, [(this.imageFinishLoad || !this.src) && n("i", {
class: this.icon
}, null), n("img", {
src: this.src,
onLoad: this._handleLoad
}, null)]);
},
computed: {
style() {
const e = `${this.size}px`;
return {
width: e,
height: e,
lineHeight: e,
fontSize: `${this.size / 2}px`
};
}
},
methods: {
_handleLoad() {
this.imageFinishLoad = !1;
}
}
};
function m(e, t, s) {
return e ? e(s) : t;
}
function M(e) {
return e < 10 ? `0${e}` : e;
}
function B(e) {
const t = new Date(e), s = /* @__PURE__ */ new Date(), i = (l) => l.getFullYear(), a = (l) => `${l.getMonth() + 1}-${l.getDate()}`, o = i(t), r = i(s);
let c;
return o !== r ? c = "y年m月d日 h:i" : `${o}-${a(t)}` == `${r}-${a(s)}` ? c = "h:i" : c = "m月d日 h:i", H(e, c);
}
function H(e, t) {
t || (t = "y-m-d h:i:s"), e ? e = new Date(e) : e = /* @__PURE__ */ new Date();
const s = [
e.getFullYear().toString(),
M((e.getMonth() + 1).toString()),
M(e.getDate().toString()),
M(e.getHours().toString()),
M(e.getMinutes().toString()),
M(e.getSeconds().toString())
], i = "ymdhis";
for (let a = 0; a < s.length; a++)
t = t.replace(i.charAt(a), s[a]);
return t;
}
function A(e, t) {
b(e) ? e(() => {
t();
}) : t();
}
function oe(e) {
return e.replace(/<.*?>/gi, "");
}
function z(e) {
return e.replace(/<(?!img).*?>/gi, "");
}
function re(e) {
if (e == null || e == "")
return "0 Bytes";
var t = ["B", "K", "M", "G", "T", "P", "E", "Z", "Y"], s = 0, i = parseFloat(e);
s = Math.floor(Math.log(i) / Math.log(1024));
var a = i / Math.pow(1024, s);
return a = parseFloat(a.toFixed(2)), a + t[s];
}
function le() {
var e = (/* @__PURE__ */ new Date()).getTime();
window.performance && typeof window.performance.now == "function" && (e += performance.now());
var t = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(s) {
var i = (e + Math.random() * 16) % 16 | 0;
return e = Math.floor(e / 16), (s == "x" ? i : i & 3 | 8).toString(16);
});
return t;
}
const ce = {
name: "LemonContact",
components: {},
inject: {
IMUI: {
from: "IMUI",
default() {
return this;
}
}
},
data() {
return {};
},
props: {
contact: Object,
simple: Boolean,
timeFormat: {
type: Function,
default(e) {
return H(e, J(e) ? "h:i" : "y/m/d");
}
}
},
render() {
return n("div", {
class: ["lemon-contact", {
"lemon-contact--name-center": this.simple
}],
title: this.contact.displayName,
onClick: (e) => this._handleClick(e, this.contact)
}, [m(this.$slots.default, this._renderInner(), this.contact)]);
},
created() {
},
mounted() {
},
computed: {},
watch: {},
methods: {
_renderInner() {
const {
contact: e
} = this;
return [n(d("lemon-badge"), {
count: this.simple ? 0 : e.unread,
class: "lemon-contact__avatar"
}, {
default: () => [n(d("lemon-avatar"), {
size: 40,
src: e.avatar
}, null)]
}), n("div", {
class: "lemon-contact__inner"
}, [n("p", {
class: "lemon-contact__label"
}, [n("span", {
class: "lemon-contact__name"
}, [e.displayName]), !this.simple && n("span", {
class: "lemon-contact__time"
}, [this.timeFormat(e.lastSendTime)])]), !this.simple && n("p", {
class: "lemon-contact__content"
}, [p(e.lastContent) ? n("span", {
innerHTML: e.lastContent
}, null) : e.lastContent])])];
},
_handleClick(e, t) {
this.$emit("click", t);
}
}
};
function de(e) {
return typeof e == "function" || Object.prototype.toString.call(e) === "[object Object]" && !O(e);
}
const L = (e, t) => {
document.execCommand(e, !1, t);
}, $ = window.getSelection();
let _, I = [];
const he = {
name: "LemonEditor",
inject: {
IMUI: {
from: "IMUI",
default() {
return this;
}
}
},
components: {},
props: {
tools: {
type: Array,
default: () => []
},
sendText: {
type: String,
default: "发 送"
},
wrapKey: {
type: Function,
default: function(e) {
return e.keyCode == 13 && e.ctrlKey == !1 && e.shiftKey == !1;
}
},
sendKey: {
type: Function,
default(e) {
return e.keyCode == 13 && e.ctrlKey === !0;
}
}
},
data() {
return this.clipboardBlob = null, {
//剪切板图片URL
clipboardUrl: "",
submitDisabled: !0,
//proxyTools: [],
accept: ""
};
},
created() {
},
mounted() {
},
render() {
const e = [], t = [];
return this.proxyTools.forEach(({
name: s,
title: i,
render: a,
click: o,
isRight: r
}) => {
o = o || new Function();
const c = ["lemon-editor__tool-item", {
"lemon-editor__tool-item--right": r
}];
let l;
s == "emoji" ? l = I.length == 0 ? "" : n(d("lemon-popover"), {
class: "lemon-editor__emoji"
}, {
content: () => this._renderEmojiTabs(),
default: () => n("div", {
class: c,
title: i
}, [a()])
}) : l = n("div", {
class: c,
onClick: o,
title: i
}, [a()]), r ? t.push(l) : e.push(l);
}), n("div", {
class: "lemon-editor"
}, [this.clipboardUrl && n("div", {
class: "lemon-editor__clipboard-image"
}, [n("img", {
src: this.clipboardUrl
}, null), n("div", null, [n(d("lemon-button"), {
style: {
marginRight: "10px"
},
onClick: this.closeClipboardImage,
color: "grey"
}, {
default: () => [j("取消")]
}), n(d("lemon-button"), {
onClick: this.sendClipboardImage
}, {
default: () => [j("发送图片")]
})])]), n("input", {
style: "display:none",
type: "file",
multiple: "multiple",
ref: "fileInput",
accept: this.accept,
onChange: this._handleChangeFile
}, null), n("div", {
class: "lemon-editor__tool"
}, [n("div", {
class: "lemon-editor__tool-left"
}, [e]), n("div", {
class: "lemon-editor__tool-right"
}, [t])]), n("div", {
class: "lemon-editor__inner"
}, [n("div", {
class: "lemon-editor__input",
ref: "textarea",
contenteditable: "true",
onKeyup: this._handleKeyup,
onKeydown: this._handleKeydown,
onPaste: this._handlePaste,
onClick: this._handleClick,
spellcheck: "false"
}, null)]), n("div", {
class: "lemon-editor__footer"
}, [n("div", {
class: "lemon-editor__tip"
}, [m(this.IMUI.$slots["editor-footer"], "使用 ctrl + enter 快捷发送消息")]), n("div", {
class: "lemon-editor__submit"
}, [n(d("lemon-button"), {
disabled: this.submitDisabled,
onClick: this._handleSend
}, {
default: () => [this.sendText]
})])])]);
},
computed: {
proxyTools() {
if (!this.tools)
return [];
const e = [{
name: "emoji",
title: "表情",
click: null,
render: (s) => n("i", {
class: "lemon-icon-emoji"
}, null)
}, {
name: "uploadFile",
title: "文件上传",
click: () => this.selectFile("*"),
render: (s) => n("i", {
class: "lemon-icon-folder"
}, null)
}, {
name: "uploadImage",
title: "图片上传",
click: () => this.selectFile("image/*"),
render: (s) => n("i", {
class: "lemon-icon-image"
}, null)
}];
let t = [];
if (Array.isArray(this.tools)) {
const s = {
emoji: 0,
uploadFile: 1,
uploadImage: 2
}, i = Object.keys(s);
t = this.tools.map((a) => i.includes(a.name) ? {
...e[s[a.name]],
...a
} : a);
} else
t = e;
return t;
}
},
methods: {
closeClipboardImage() {
this.clipboardUrl = "", this.clipboardBlob = null;
},
sendClipboardImage() {
this.clipboardBlob && (this.$emit("upload", this.clipboardBlob), this.closeClipboardImage());
},
saveRangeToLast() {
_ || (_ = document.createRange()), _.selectNodeContents(textarea.value), _.collapse(!1), $.removeAllRanges(), $.addRange(_);
},
inertContent(e, t = !1) {
t && saveRangeToLast(), this.focusRange(), L("insertHTML", e), this.saveRange();
},
saveRange() {
_ = $.getRangeAt(0);
},
focusRange() {
this.$refs.textarea.focus(), _ && ($.removeAllRanges(), $.addRange(_));
},
_handleClick() {
this.saveRange();
},
_renderEmojiTabs() {
const e = (t) => t.map((s) => n("img", {
src: s.src,
title: s.title,
class: "lemon-editor__emoji-item",
onClick: () => this._handleSelectEmoji(s)
}, null));
if (I[0].label) {
const t = I.map((s, i) => n("div", {
slot: "tab-pane",
index: i,
tab: s.label
}, [e(s.children)]));
return n(d("lemon-tabs"), {
style: "width: 412px"
}, de(t) ? t : {
default: () => [t]
});
} else
return n("div", {
class: "lemon-tabs-content",
style: "width:406px"
}, [e(I)]);
},
_handleSelectEmoji(e) {
this.inertContent(`<img emoji-name="${e.name}" src="${e.src}"></img>`), this._checkSubmitDisabled();
},
async selectFile(e) {
this.accept = e, await this.$nextTick(), this.$refs.fileInput.click();
},
_handlePaste(e) {
e.preventDefault();
const t = e.clipboardData || window.clipboardData, s = t.getData("Text");
if (s)
this.submitDisabled = !1, window.clipboardData ? this.$refs.textarea.innerHTML = s : L("insertText", s);
else {
const {
blob: i,
blobUrl: a
} = this._getClipboardBlob(t);
this.clipboardBlob = i, this.clipboardUrl = a;
}
},
_getClipboardBlob(e) {
let t, s;
for (var i = 0; i < e.items.length; ++i)
e.items[i].kind == "file" && e.items[i].type.indexOf("image/") !== -1 && (t = e.items[i].getAsFile(), s = (window.URL || window.webkitURL).createObjectURL(t));
return {
blob: t,
blobUrl: s
};
},
_handleKeyup(e) {
this.saveRange(), this._checkSubmitDisabled();
},
_handleKeydown(e) {
(e.keyCode == 13 || e.keyCode == 13 && e.shiftKey) && e.preventDefault(), this.wrapKey(e) && (e.preventDefault(), L("insertLineBreak")), this.submitDisabled == !1 && this.sendKey(e) && this._handleSend();
},
getFormatValue() {
return this.IMUI.emojiImageToName(this.$refs.textarea.innerHTML);
},
_checkSubmitDisabled() {
this.submitDisabled = !z(this.$refs.textarea.innerHTML.trim());
},
_handleSend(e) {
const t = this.getFormatValue();
this.$emit("send", t), this.clear(), this._checkSubmitDisabled();
},
_handleChangeFile(e) {
const {
fileInput: t
} = this.$refs;
Array.from(t.files).forEach((s) => {
this.$emit("upload", s);
}), t.value = "";
},
clear() {
this.$refs.textarea.innerHTML = "";
},
initEmoji(e) {
I = e, this.$forceUpdate();
},
setValue(e) {
this.$refs.textarea.innerHTML = this.IMUI.emojiNameToImage(e), this._checkSubmitDisabled();
}
}
};
const Y = {
name: "lemonMessageText",
inheritAttrs: !1,
inject: ["IMUI"],
render() {
return n(d("lemon-message-basic"), D({
class: "lemon-message-text"
}, this.$attrs), {
content: (e) => {
const t = this.IMUI.emojiNameToImage(e.content);
return n("span", {
innerHTML: t
}, null);
}
});
}
};
const W = {
name: "lemonMessageFile",
inheritAttrs: !1,
render() {
return n(d("lemon-message-basic"), {
class: "lemon-message-file",
props: {
...this.$attrs
},
scopedSlots: {
content: (e) => [n("div", {
class: "lemon-message-file__inner"
}, [n("p", {
class: "lemon-message-file__name"
}, [e.fileName]), n("p", {
class: "lemon-message-file__byte"
}, [re(e.fileSize)])]), n("div", {
class: "lemon-message-file__sfx"
}, [n("i", {
class: "lemon-icon-attah"
}, null)])]
}
}, null);
}
};
const G = {
name: "lemonMessageImage",
inheritAttrs: !1,
render() {
return n(d("lemon-message-basic"), D({
class: "lemon-message-image"
}, this.$attrs), {
content: (e) => n("img", {
src: e.content
}, null)
});
}
};
const ue = {
LemonMessageText: Y,
LemonMessageFile: W,
LemonMessageImage: G
}, me = {
name: "LemonMessages",
props: {
//是否隐藏消息发送人昵称
hideName: Boolean,
//是否隐藏显示消息时间
hideTime: Boolean,
reverseUserId: [String, Number],
timeRange: {
type: Number,
default: 1
},
timeFormat: {
type: Function,
default(e) {
return B(e);
}
},
loadingText: {
type: [String, Function]
},
loadendText: {
type: [String, Function],
default: "暂无更多消息"
},
messages: {
type: Array,
default: () => []
}
},
data() {
return this._lockScroll = !1, {
_loading: !1,
_loadend: !1
};
},
render() {
return n("div", {
class: "lemon-messages",
ref: "wrap",
onScroll: this._handleScroll
}, [n("div", {
class: ["lemon-messages__load", `lemon-messages__load--${this._loadend ? "end" : "ing"}`]
}, [n("span", {
class: "lemon-messages__loadend"
}, [p(this.loadendText) ? this.loadendText : this.loadendText()]), n("span", {
class: "lemon-messages__loading"
}, [this.loadingText ? p(this.loadingText) ? this.loadingText : this.loadingText() : n("i", {
class: "lemon-icon-loading lemonani-spin"
}, null)])]), this.messages.map((e, t) => {
const s = [], i = `LemonMessage${Z(e.type)}`, a = this.messages[t - 1];
a && this.msecRange && e.sendTime - a.sendTime > this.msecRange && s.push(n(d("lemon-message-event"), {
message: {
id: "__time__",
type: "event",
content: B(e.sendTime)
}
}, null));
let o;
e.type == "event" ? o = {
message: e
} : o = {
timeFormat: this.timeFormat,
message: e,
reverse: this.reverseUserId == e.fromUser.id,
hideTime: this.hideTime,
hideName: this.hideName
};
const r = ue[i];
return s.push(n(r, D({
ref: "message",
refInFor: !0
}, o), null)), s;
})]);
},
computed: {
msecRange() {
return this.timeRange * 1e3 * 60;
}
},
watch: {},
methods: {
loaded() {
this._loadend = !0, this.$forceUpdate();
},
resetLoadState() {
this._lockScroll = !0, this._loading = !1, this._loadend = !1, setTimeout(() => {
this._lockScroll = !1;
}, 200);
},
async _handleScroll(e) {
if (this._lockScroll)
return;
console.log(123);
const {
target: t
} = e;
if (T.hide(), t.scrollTop == 0 && this._loading == !1 && this._loadend == !1) {
this._loading = !0, await this.$nextTick();
const s = t.scrollHeight;
this.$emit("reach-top", async (i) => {
await this.$nextTick(), t.scrollTop = t.scrollHeight - s, this._loading = !1, this._loadend = !!i;
});
}
},
async scrollToBottom() {
await this.$nextTick();
const {
wrap: e
} = this.$refs;
e && (e.scrollTop = e.scrollHeight);
}
},
created() {
},
mounted() {
}
};
const fe = {
name: "lemonMessageBasic",
inject: {
IMUI: {
from: "IMUI",
default() {
return this;
}
}
},
props: {
contextmenu: Array,
message: {
type: Object,
default: () => ({})
},
timeFormat: {
type: Function,
default: () => ""
},
reverse: Boolean,
hideName: Boolean,
hideTime: Boolean
},
data() {
return {};
},
render() {
const {
fromUser: e,
status: t,
sendTime: s
} = this.message, i = this.hideName == !0 && this.hideTime == !0;
return n("div", {
class: ["lemon-message", `lemon-message--status-${t}`, {
"lemon-message--reverse": this.reverse,
"lemon-message--hide-title": i
}]
}, [n("div", {
class: "lemon-message__avatar"
}, [n(d("lemon-avatar"), {
size: 36,
shape: "square",
src: e.avatar,
onClick: (a) => {
this._emitClick(a, "avatar");
}
}, null)]), n("div", {
class: "lemon-message__inner"
}, [n("div", {
class: "lemon-message__title"
}, [this.hideName == !1 && n("span", {
onClick: (a) => {
this._emitClick(a, "displayName");
}
}, [e.displayName]), this.hideTime == !1 && n("span", {
class: "lemon-message__time",
onClick: (a) => {
this._emitClick(a, "sendTime");
}
}, [this.timeFormat(s)])]), n("div", {
class: "lemon-message__content-flex"
}, [f(n("div", {
class: "lemon-message__content",
onClick: (a) => {
this._emitClick(a, "content");
}
}, [m(this.$slots.content, null, this.message)]), [[K("lemon-contextmenu"), this.IMUI.contextmenu, void 0, {
message: !0
}]]), n("div", {
class: "lemon-message__content-after"
}, [m(this.IMUI.$slots["message-after"], null, this.message)]), n("div", {
class: "lemon-message__status",
onClick: (a) => {
this._emitClick(a, "status");
}
}, [n("i", {
class: "lemon-icon-loading lemonani-spin"
}, null), n("i", {
class: "lemon-icon-prompt",
title: "重发消息",
style: {
color: "#ff2525",
cursor: "pointer"
}
}, null)])])])]);
},
created() {
},
mounted() {
},
computed: {},
watch: {},
methods: {
_emitClick(e, t) {
this.IMUI.$emit("message-click", e, t, this.message, this.IMUI);
}
}
};
const pe = {
name: "lemonMessageEvent",
inheritAttrs: !1,
inject: ["IMUI"],
render() {
const {
content: e
} = this.$attrs.message;
return n("div", {
class: "lemon-message lemon-message-event"
}, [n("span", {
class: "lemon-message-event__content",
onClick: (t) => this._emitClick(t, "content")
}, [e])]);
},
methods: {
_emitClick(e, t) {
this.IMUI.$emit("message-click", e, t, this.$attrs.message, this.IMUI);
}
}
};
const x = "messages", w = "contacts", ge = [x, w], F = {
file(e) {
return "[文件]";
},
image(e) {
return "[图片]";
},
text(e) {
return this.emojiNameToImage(oe(e.content));
},
event(e) {
return "[通知]";
}
};
class S {
constructor() {
this.table = {};
}
get(t) {
return t ? this.table[t] : this.table;
}
set(t, s) {
this.table[t] = s;
}
// setOnly(key, val) {
// if (!this.has(key)) this.set(key, val);
// }
remove(t) {
t ? delete this.table[t] : this.table = {};
}
has(t) {
return !!this.table[t];
}
}
function _e(e) {
return typeof e == "function" || Object.prototype.toString.call(e) === "[object Object]" && !O(e);
}
let h = q({});
const U = {}, R = (e) => p(e) ? e : `${e}px`, k = (e) => e.replace("%", "") / 100;
let V = () => {
};
const Ce = {
name: "LemonImui",
provide() {
return {
IMUI: this
};
},
props: {
width: {
type: [String, Number],
default: 850
},
height: {
type: [String, Number],
default: 580
},
theme: {
type: String,
default: "default"
},
simple: {
type: Boolean,
default: !1
},
loadingText: [String, Function],
loadendText: [String, Function],
/**
* 消息时间格式化规则
*/
messageTimeFormat: Function,
/**
* 联系人最新消息时间格式化规则
*/
contactTimeFormat: Function,
/**
* 初始化时是否隐藏抽屉
*/
hideDrawer: {
type: Boolean,
default: !0
},
/**
* 是否隐藏导航按钮上的头像
*/
hideMenuAvatar: Boolean,
hideMenu: Boolean,
/**
* 是否隐藏消息列表内的联系人名字
*/
hideMessageName: Boolean,
/**
* 是否隐藏消息列表内的发送时间
*/
hideMessageTime: Boolean,
sendKey: Function,
wrapKey: Function,
sendText: String,
contextmenu: Array,
contactContextmenu: Array,
avatarCricle: Boolean,
user: {
type: Object,
default: () => ({})
}
},
data() {
return this.CacheContactContainer = new S(), this.CacheMenuContainer = new S(), this.CacheMessageLoaded = new S(), this.CacheDraft = new S(), {
drawerVisible: !this.hideDrawer,
currentContactId: null,
currentMessages: [],
activeSidebar: x,
contacts: [],
menus: [],
editorTools: [{
name: "emoji"
}, {
name: "uploadFile"
}, {
name: "uploadImage"
}]
};
},
render() {
return this._renderWrapper([this._renderMenu(), this._renderSidebarMessage(), this._renderSidebarContact(), this._renderContainer(), this._renderDrawer()]);
},
created() {
this.initMenus();
},
async mounted() {
await this.$nextTick();
},
computed: {
currentContact() {
return this.contacts.find((e) => e.id == this.currentContactId) || {};
},
currentMenu() {
return this.menus.find((e) => e.name == this.activeSidebar) || {};
},
currentIsDefSidebar() {
return ge.includes(this.activeSidebar);
},
lastMessages() {
const e = this.contacts.filter((t) => !C(t.lastContent));
return e.sort((t, s) => s.lastSendTime - t.lastSendTime), e;
}
},
watch: {
activeSidebar() {
}
},
methods: {
_menuIsContacts() {
return this.activeSidebar == w;
},
_menuIsMessages() {
return this.activeSidebar == x;
},
_createMessage(e) {
return {
id: le(),
type: "text",
status: "going",
sendTime: (/* @__PURE__ */ new Date()).getTime(),
toContactId: this.currentContactId,
fromUser: {
...this.user
},
...e
};
},
/**
* 新增一条消息
*/
appendMessage(e, t = !1) {
let s = "+1", i = h[e.toContactId];
if ((e.type == "event" || this.user.id == e.fromUser.id) && (s = "+0"), i === void 0)
this.updateContact({
id: e.toContactId,
unread: s,
lastSendTime: e.sendTime,
lastContent: this.lastContentRender(e)
});
else {
if (i.some(({
id: r
}) => r == e.id))
return;
this._addMessage(e, e.toContactId, 1);
const o = {
id: e.toContactId,
lastContent: this.lastContentRender(e),
lastSendTime: e.sendTime
};
e.toContactId == this.currentContactId ? (t == !0 && this.messageViewToBottom(), this.CacheDraft.remove(e.toContactId)) : o.unread = s, this.updateContact(o);
}
},
_emitSend(e, t, s) {
this.$emit("send", e, (i = {
status: "succeed"
}) => {
t(), this.updateMessage(Object.assign(e, i));
}, s);
},
_handleSend(e) {
const t = this._createMessage({
content: e
});
this.appendMessage(t, !0), this._emitSend(t, () => {
this.updateContact({
id: t.toContactId,
lastContent: this.lastContentRender(t),
lastSendTime: t.sendTime
}), this.CacheDraft.remove(t.toContactId);
});
},
_handleUpload(e) {
const t = ["image/gif", "image/jpeg", "image/png"];
let s;
t.includes(e.type) ? s = {
type: "image",
content: URL.createObjectURL(e)
} : s = {
type: "file",
fileSize: e.size,
fileName: e.name,
content: ""
};
const i = this._createMessage(s);
this.appendMessage(i, !0), this._emitSend(i, () => {
this.updateContact({
id: i.toContactId,
lastContent: this.lastContentRender(i),
lastSendTime: i.sendTime
});
}, e);
},
_emitPullMessages(e) {
this._changeContactLock = !0, this.$emit("pull-messages", this.currentContact, (t = [], s = !1) => {
this._addMessage(t, this.currentContactId, 0), this.CacheMessageLoaded.set(this.currentContactId, s), s == !0 && this.$refs.messages.loaded(), this.updateCurrentMessages(), this._changeContactLock = !1, e(s);
}, this);
},
clearCacheContainer(e) {
this.CacheContactContainer.remove(e), this.CacheMenuContainer.remove(e);
},
_renderWrapper(e) {
return n("div", {
style: {
width: R(this.width),
height: R(this.height)
},
ref: "wrapper",
class: ["lemon-wrapper", `lemon-wrapper--theme-${this.theme}`, {
"lemon-wrapper--simple": this.simple
}, this.drawerVisible && "lemon-wrapper--drawer-show"]
}, [e]);
},
_renderMenu() {
const e = this._renderMenuItem();
return f(n("div", {
class: "lemon-menu"
}, [f(n(d("lemon-avatar"), {
onClick: (t) => {
this.$emit("menu-avatar-click", t);
},
class: "lemon-menu__avatar",
src: this.user.avatar
}, null), [[g, !this.hideMenuAvatar]]), e.top, this.$slots.menu, n("div", {
class: "lemon-menu__bottom"
}, [this.$slots["menu-bottom"], e.bottom])]), [[g, !this.hideMenu]]);
},
_renderMenuAvatar() {
},
_renderMenuItem() {
const e = [], t = [];
return this.menus.forEach((s) => {
let i;
const {
name: a,
title: o,
unread: r,
render: c,
click: l
} = s, v = n("div", {
class: ["lemon-menu__item", {
"lemon-menu__item--active": this.activeSidebar == a
}],
onClick: () => {
A(l, () => {
a && this.changeMenu(a);
});
},
title: o
}, [n(d("lemon-badge"), {
count: r
}, _e(i = c(s)) ? i : {
default: () => [i]
})]);
s.isBottom === !0 ? t.push(v) : e.push(v);
}), {
top: e,
bottom: t
};
},
_renderSidebarMessage() {
return this._renderSidebar([m(this.$slots["sidebar-message-top"], null, this), this.lastMessages.map((e) => this._renderContact({
contact: e,
timeFormat: this.contactTimeFormat
}, () => this.changeContact(e.id), this.$slots["sidebar-message"]))], x, m(this.$slots["sidebar-message-fixedtop"], null, this));
},
_renderContact(e, t, s) {
const {
click: i,
renderContainer: a,
id: o
} = e.contact, r = () => {
A(i, () => {
t(), this._customContainerReady(a, this.CacheContactContainer, o);
});
};
return f(n(d("lemon-contact"), D({
class: {
"lemon-contact--active": this.currentContactId == e.contact.id
}
}, e, {
onClick: r,
scopedSlots: {
default: s
}
}), null), [[K("lemon-contextmenu"), this.contactContextmenu, void 0, {
contact: !0
}]]);
},
_renderSidebarContact() {
let e;
return this._renderSidebar([m(this.$slots["sidebar-contact-top"], null, this), this.contacts.map((t) => {
if (!t.index)
return;
t.index = t.index.replace(/\[[0-9]*\]/, "");
const s = [t.index !== e && n("p", {
class: "lemon-sidebar__label"
}, [t.index]), this._renderContact({
contact: t,
simple: !0
}, () => {
this.changeContact(t.id);
}, this.$slots["sidebar-contact"])];
return e = t.index, s;
})], w, m(this.$slots["sidebar-contact-fixedtop"], null, this));
},
_renderSidebar(e, t, s) {
return f(n("div", {
class: "lemon-sidebar",
"on-scroll": this._handleSidebarScroll
}, [n("div", {
class: "lemon-sidebar__fixed-top"
}, [s]), n("div", {
class: "lemon-sidebar__scroll"
}, [e])]), [[g, this.activeSidebar == t]]);
},
_renderDrawer() {
return this._menuIsMessages() && this.currentContactId ? n("div", {
class: "lemon-drawer",
ref: "drawer"
}, [V(this.currentContact), m(this.$slots.drawer, "", this.currentContact)]) : "";
},
_isContactContainerCache(e) {
return e.startsWith("contact#");
},
_renderContainer() {
const e = [], t = "lemon-container", s = this.currentContact;
let i = !0;
for (const a in this.CacheContactContainer.get()) {
const o = s.id == a && this.currentIsDefSidebar;
o && (i = !o), e.push(f(n("div", {
class: t
}, [this.CacheContactContainer.get(a)]), [[g, o]]));
}
for (const a in this.CacheMenuContainer.get())
e.push(f(n("div", {
class: t
}, [this.CacheMenuContainer.get(a)]), [[g, this.activeSidebar == a && !this.currentIsDefSidebar]]));
return e.push(f(n("div", {
class: t
}, [n("div", {
class: "lemon-container__title"
}, [m(this.$slots["message-title"], n("div", {
class: "lemon-container__displayname"
}, [s.displayName]), s)]), n("div", {
class: "lemon-vessel"
}, [n("div", {
class: "lemon-vessel__left"
}, [n(d("lemon-messages"), {
ref: "messages",
"loading-text": this.loadingText,
"loadend-text": this.loadendText,
"hide-time": this.hideMessageTime,
"hide-name": this.hideMessageName,
"time-format": this.messageTimeFormat,
"reverse-user-id": this.user.id,
onReachTop: this._emitPullMessages,
messages: this.currentMessages
}, null), n(d("lemon-editor"), {
ref: "editor",
tools: this.editorTools,
sendText: this.sendText,
sendKey: this.sendKey,
wrapKey: this.wrapKey,
onSend: this._handleSend,
onUpload: this._handleUpload
}, null)]), n("div", {
class: "lemon-vessel__right"
}, [m(this.$slots["message-side"], null, s)])])]), [[g, this._menuIsMessages() && i && s.id]])), e.push(f(n("div", {
class: t
}, [this.$slots.cover]), [[g, !s.id && this.currentIsDefSidebar]])), e.push(f(n("div", {
class: t
}, [m(this.$slots["contact-info"], n("div", {
class: "lemon-contact-info"
}, [n(d("lemon-avatar"), {
src: s.avatar,
size: 90
}, null), n("h4", null, [s.displayName]), n(d("lemon-button"), {
onClick: () => {
C(s.lastContent) && this.updateContact({
id: s.id,
lastContent: " "
}), this.changeContact(s.id, x);
}
}, {
default: () => [j("发送消息")]
})]), s)]), [[g, this._menuIsContacts() && i && s.id]])), e;
},
_handleSidebarScroll() {
T.hide();
},
_addContact(e, t) {
const s = {
0: "unshift",
1: "push"
}[t];
this.contacts[s](e);
},
_addMessage(e, t, s) {
const i = {
0: "unshift",
1: "push"
}[s];
Array.isArray(e) || (e = [e]), h[t] = h[t] || [], h[t][i](...e);
},
/**
* 设置最新消息DOM
* @param {String} messageType 消息类型
* @param {Function} render 返回消息 vnode
*/
setLastContentRender(e, t) {
F[e] = t;
},
lastContentRender(e) {
return b(F[e.type]) ? F[e.type].call(this, e) : (console.error(`not found '${e.type}' of the latest message renderer,try to use ‘setLastContentRender()’`), "");
},
/**
* 将字符串内的 EmojiItem.name 替换为 img
* @param {String} str 被替换的字符串
* @return {String} 替换后的字符串
*/
emojiNameToImage(e) {
return e.replace(/\[!(\w+)\]/gi, (t, s) => {
const i = s;
return U[i] ? `<img emoji-name="${s}" src="${U[i]}" />` : `[!${s}]`;
});
},
emojiImageToName(e) {
return e.replace(/<img emoji-name=\"([^\"]*?)\" [^>]*>/gi, "[!$1]");
},
updateCurrentMessages() {
h[this.currentContactId] || (h[this.currentContactId] = []), this.currentMessages = h[this.currentContactId];
},
/**
* 将当前聊天窗口滚动到底部
*/
messageViewToBottom() {
this.$refs.messages.scrollToBottom();
},
/**
* 设置联系人的草稿信息
*/
setDraft(e, t) {
if (C(e) || C(t))
return !1;
const s = this.findContact(e);
let i = s.lastContent;
if (C(s))
return !1;
this.CacheDraft.has(e) && (i = this.CacheDraft.get(e).lastContent), this.CacheDraft.set(e, {
editorValue: t,
lastContent: i
}), this.updateContact({
id: e,
lastContent: `<span style="color:red;">[草稿]</span><span>${this.lastContentRender({
type: "text",
content: t
})}</span>`
});
},
/**
* 清空联系人草稿信息
*/
clearDraft(e) {
const t = this.CacheDraft.get(e);
t && (this.findContact(e).lastContent.indexOf('<span style="color:red;">[草稿]</span>') === 0 && this.updateContact({
id: e,
lastContent: t.lastContent
}), this.CacheDraft.remove(e));
},
/**
* 改变聊天对象
* @param contactId 联系人 id
*/
async changeContact(e, t) {
if (t)
this.changeMenu(t);
else if (this._changeContactLock || this.currentContactId == e)
return !1;
if (this.currentContactId) {
const i = z(this.getEditorValue()).trim();
i ? (this.setDraft(this.currentContactId, i), this.setEditorValue()) : this.clearDraft(this.currentContactId);
}
if (this.currentContactId = e, !this.currentContactId)
return !1;
if (this.$emit("change-contact", this.currentContact, this), b(this.currentContact.renderContainer) || this.activeSidebar == w)
return;
const s = this.CacheDraft.get(e);
s && this.setEditorValue(s.editorValue), this.CacheMessageLoaded.has(e) ? this.$refs.messages.loaded() : this.$refs.messages.resetLoadState(), h[e] ? setTimeout(() => {
this.updateCurrentMessages(), this.messageViewToBottom();
}, 0) : (this.updateCurrentMessages(), this._emitPullMessages((i) => {
this.messageViewToBottom();
}));
},
/**
* 删除一条聊天消息
* @param messageId 消息 id
* @param contactId 联系人 id
*/
removeMessage(e) {
const t = this.findMessage(e);
if (!t)
return !1;
const s = h[t.toContactId].findIndex(({
id: i
}) => i == e);
return h[t.toContactId].splice(s, 1), !0;
},
/**
* 修改聊天一条聊天消息
* @param {Message} data 根据 data.id 查找聊天消息并覆盖传入的值
* @param contactId 联系人 id
*/
updateMessage(e) {
if (!e.id)
return !1;
let t = this.findMessage(e.id);
return t ? (t = Object.assign(t, e, {
toContactId: t.toContactId
}), !0) : !1;
},
/**
* 手动更新对话消息
* @param {String} messageId 消息ID,如果为空则更新当前聊天窗口的所有消息
*/
forceUpdateMessage(e) {
if (!e)
this.$refs.messages.$forceUpdate();
else {
const t = this.$refs.messages.$refs.message;
if (t) {
const s = t.find((i) => i.$attrs.message.id == e);
s && s.$forceUpdate();
}
}
},
_customContainerReady(e, t, s) {
b(e) && !t.has(s) && t.set(s, e.call(this));
},
/**
* 切换左侧按钮
* @param {String} name 按钮 name
*/
changeMenu(e) {
this.$emit("change-menu", e), this.activeSidebar = e;
},
/**
* 初始化编辑框的 Emoji 表情列表,是 Lemon-editor.initEmoji 的代理方法
* @param {Array<Emoji,EmojiItem>} data emoji 数据
* Emoji = {label: 表情,children: [{name: wx,title: 微笑,src: url}]} 分组
* EmojiItem = {name: wx,title: 微笑,src: url} 无分组
*/
initEmoji(e) {
let t = [];
this.$refs.editor.initEmoji(e), e[0].label ? e.forEach((s) => {
t.push(...s.children);
}) : t = e, t.forEach(({
name: s,
src: i
}) => U[s] = i);
},
initEditorTools(e) {
this.editorTools = e;
},
/**
* 初始化左侧按钮
* @param {Array<Menu>} data 按钮数据
*/
initMenus(e) {
const t = [{
name: x,
title: "聊天",
unread: 0,
click: null,
render: (i) => n("i", {
class: "lemon-icon-message"
}, null),
isBottom: !1
}, {
name: w,
title: "通讯录",
unread: 0,
click: null,
render: (i) => n("i", {
class: "lemon-icon-addressbook"
}, null),
isBottom: !1
}];
let s = [];
if (Array.isArray(e)) {
const i = {
messages: 0,
contacts: 1
}, a = Object.keys(i);
s = e.map((o) => a.includes(o.name) ? {
...t[i[o.name]],
...o,
renderContainer: null
} : (o.renderContainer && this._customContainerReady(o.renderContainer, this.CacheMenuContainer, o.name), o));
} else
s = t;
this.menus = s;
},
/**
* 初始化联系人数据
* @param {Array<Contact>} data 联系人列表
*/
initContacts(e) {
this.contacts = e, this.sortContacts();
},
/**
* 使用 联系人的 index 值进行排序
*/
sortContacts() {
this.contacts.sort((e, t) => {
if (e.index)
return e.index.localeCompare(t.index);
});
},
appendContact(e) {
return C(e.id) || C(e.displayName) ? (console.error("id | displayName cant be empty"), !1) : (this.hasContact(e.id) || this.contacts.push(Object.assign({
id: "",
displayName: "",
avatar: "",
index: "",
unread: 0,
lastSendTime: "",
lastContent: ""
}, e)), !0);
},
removeContact(e) {
const t = this.findContactIndexById(e);
return t === -1 ? !1 : (this.contacts.splice(t, 1), this.CacheDraft.remove(e), this.CacheMessageLoaded.remove(e), !0);
},
/**
* 修改联系人数据
* @param {Contact} data 修改的数据,根据 Contact.id 查找联系人并覆盖传入的值
*/
updateContact(e) {
const t = e.id;
delete e.id;
const s = this.findContactIndexById(t);
if (s !== -1) {
const {
unread: i
} = e;
p(i) && (i.indexOf("+") === 0 || i.indexOf("-") === 0) && (e.unread = parseInt(i) + parseInt(this.contacts[s].unread));
for (const a in e)
this.contacts[s][a] = e[a];
}
},
/**
* 根据 id 查找联系人的索引
* @param contactId 联系人 id
* @return {Number} 联系人索引,未找到返回 -1
*/
findContactIndexById(e) {
return this.contacts.findIndex((t) => t.id == e);
},
/**
* 根据 id 查找判断是否存在联系人
* @param contactId 联系人 id
* @return {Boolean}
*/
hasContact(e) {
return this.findContactIndexById(e) !== -1;
},
findMessage(e) {
for (const t in h) {
const s = h[t].find(({
id: i
}) => i == e);
if (s)
return s;
}
},
findContact(e) {
return this.getContacts().find(({
id: t
}) => t == e);
},
/**
* 返回所有联系人
* @return {Array<Contact>}
*/
getContacts() {
return this.contacts;
},
//返回当前聊天窗口联系人信息
getCurrentContact() {
return this.currentContact;
},
getCurrentMessages() {
return this.currentMessages;
},
setEditorValue(e = "") {
if (!p(e))
return !1;
this.$refs.editor.setValue(this.emojiNameToImage(e));
},
getEditorValue() {
return this.$refs.editor.getFormatValue();
},
/**
* 清空某个联系人的消息,切换到该联系人时会重新触发pull-messages事件
*/
clearMessages(e) {
return e ? (delete h[e], this.CacheMessageLoaded.remove(e), this.CacheDraft.remove(e)) : (h = {}, this.CacheMessageLoaded.remove(), this.CacheDraft.remove()), !0;
},
/**
* 返回所有消息
* @return {Object<Contact.id,Message>}
*/
getMessages(e) {
return (e ? h[e] : h) || [];
},
changeDrawer(e) {
this.drawerVisible = !this.drawerVisible, this.drawerVisible == !0 && this.openDrawer(e);
},
// openDrawer(data) {
// renderDrawerContent = data || new Function();
// this.drawerVisible = true;
// },
openDrawer(e) {
V = b(e) ? e : e.render || new Function();
const t = this.$refs.wrapper.clientWidth, s = this.$refs.wrapper.clientHeight;
let i = e.width || 200, a = e.height || s, o = e.offsetX || 0, r = e.offsetY || 0;
const c = e.position || "right";
p(i) && (i = t * k(i)), p(a) && (a = s * k(a)), p(o) && (o = t * k(o)), p(r) && (r = s * k(r)), this.$refs.drawer.style.width = `${i}px`, this.$refs.drawer.style.height = `${a}px`;
let l = 0, v = 0, y = "";
c == "right" ? l = t : c == "rightInside" ? (l = t - i, y = "-15px 0 16px -14px rgba(0,0,0,0.08)") : c == "center" && (l = t / 2 - i / 2, v = s / 2 - a / 2, y = "0 0 20px rgba(0,0,0,0.08)"), l += o, v += r + -1, this.$refs.drawer.style.top = `${v}px`, this.$refs.drawer.style.left = `${l}px`, this.$refs.drawer.style.boxShadow = y, this.drawerVisible = !0;
},
closeDrawer() {
this.drawerVisible = !1;
}
}
};
const xe = "1.4.2", ve = [
Ce,
ce,
me,
he,
ae,
ie,
ne,
P,
se,
fe,
Y,
G,
W,
pe
], be = (e) => {
e.directive("LemonContextmenu", T), ve.forEach((t) => {
e.component(t.name, t);
});
};
typeof window < "u" && window.Vue && be(window.Vue);
export {
be as install,
xe as version
};