|
@@ -0,0 +1,1384 @@
|
|
|
+<script>
|
|
|
+import {
|
|
|
+ useScopedSlot,
|
|
|
+ funCall,
|
|
|
+ generateUUID,
|
|
|
+ clearHtmlExcludeImg,
|
|
|
+} from "../utils";
|
|
|
+import { isFunction, isString, isEmpty } from "../utils/validate";
|
|
|
+import contextmenu from "../directives/contextmenu";
|
|
|
+import {
|
|
|
+ DEFAULT_MENUS,
|
|
|
+ DEFAULT_MENU_LASTMESSAGES,
|
|
|
+ DEFAULT_MENU_CONTACTS,
|
|
|
+} from "../utils/constant";
|
|
|
+import lastContentRender from "../lastContentRender";
|
|
|
+
|
|
|
+import MemoryCache from "../utils/cache/memory";
|
|
|
+
|
|
|
+let allMessages = {};
|
|
|
+const emojiMap = {};
|
|
|
+const toPx = val => {
|
|
|
+ return isString(val) ? val : `${val}px`;
|
|
|
+};
|
|
|
+const toPoint = str => {
|
|
|
+ return str.replace("%", "") / 100;
|
|
|
+};
|
|
|
+
|
|
|
+let renderDrawerContent = () => {};
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: "LemonRecords",
|
|
|
+ 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: false,
|
|
|
+ },
|
|
|
+ loadingText: [String, Function],
|
|
|
+ loadendText: [String, Function],
|
|
|
+ /**
|
|
|
+ * 消息时间格式化规则
|
|
|
+ */
|
|
|
+ messageTimeFormat: Function,
|
|
|
+ /**
|
|
|
+ * 联系人最新消息时间格式化规则
|
|
|
+ */
|
|
|
+ contactTimeFormat: Function,
|
|
|
+ /**
|
|
|
+ * 初始化时是否隐藏抽屉
|
|
|
+ */
|
|
|
+ hideDrawer: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true,
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 是否隐藏导航按钮上的头像
|
|
|
+ */
|
|
|
+ hideMenuAvatar: Boolean,
|
|
|
+ hideMenu: Boolean,
|
|
|
+ /**
|
|
|
+ * 是否隐藏消息列表内的联系人名字
|
|
|
+ */
|
|
|
+ hideMessageName: Boolean,
|
|
|
+ /**
|
|
|
+ * 是否隐藏消息列表内的发送时间
|
|
|
+ */
|
|
|
+ hideMessageTime: Boolean,
|
|
|
+ showContext:Boolean,
|
|
|
+ sendKey: Function,
|
|
|
+ wrapKey: Function,
|
|
|
+ sendText: String,
|
|
|
+ contextmenu: Array,
|
|
|
+ contactContextmenu: Array,
|
|
|
+ avatarCricle: Boolean,
|
|
|
+ user: {
|
|
|
+ type: Object,
|
|
|
+ default: () => {
|
|
|
+ return {};
|
|
|
+ },
|
|
|
+ },
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ this.CacheContactContainer = new MemoryCache();
|
|
|
+ this.CacheMenuContainer = new MemoryCache();
|
|
|
+ this.CacheMessageLoaded = new MemoryCache();
|
|
|
+ this.CacheDraft = new MemoryCache();
|
|
|
+ return {
|
|
|
+ drawerVisible: !this.hideDrawer,
|
|
|
+ currentContactId: null,
|
|
|
+ currentNewContactId: null,
|
|
|
+ currentMessages: [],
|
|
|
+ activeSidebar: DEFAULT_MENU_LASTMESSAGES,
|
|
|
+ contacts: [],
|
|
|
+ conversations:[],
|
|
|
+ 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.conversations.find(item => item.conversationId == this.currentContactId) || {};
|
|
|
+ },
|
|
|
+ //获取当前联系人
|
|
|
+ currentNewContact() {
|
|
|
+ return this.contacts.find(item => item.id == this.currentNewContactId) || {};
|
|
|
+ },
|
|
|
+ currentMenu() {
|
|
|
+ return this.menus.find(item => item.name == this.activeSidebar) || {};
|
|
|
+ },
|
|
|
+ currentIsDefSidebar() {
|
|
|
+ return DEFAULT_MENUS.includes(this.activeSidebar);
|
|
|
+ },
|
|
|
+ lastMessages() {
|
|
|
+ // var data = this.contacts.filter(item => !isEmpty(item.lastContent));
|
|
|
+ // data.sort((a1, a2) => {
|
|
|
+ // return a2.lastSendTime - a1.lastSendTime;
|
|
|
+ // });
|
|
|
+ var data=null;
|
|
|
+ if(!!this.conversations){
|
|
|
+ data=this.conversations;
|
|
|
+ }
|
|
|
+ return data;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ activeSidebar() {},
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ _menuIsContacts() {
|
|
|
+ return this.activeSidebar == DEFAULT_MENU_CONTACTS;
|
|
|
+ },
|
|
|
+ _menuIsMessages() {
|
|
|
+ return this.activeSidebar == DEFAULT_MENU_LASTMESSAGES;
|
|
|
+ },
|
|
|
+ _createMessage(message) {
|
|
|
+ return {
|
|
|
+ ...{
|
|
|
+ id: generateUUID(),
|
|
|
+ type: "text",
|
|
|
+ status: "going",
|
|
|
+ sendTime: new Date().getTime(),
|
|
|
+ toContactId: this.currentContactId,
|
|
|
+ fromUser: {
|
|
|
+ ...this.user,
|
|
|
+ },
|
|
|
+ },
|
|
|
+ ...message,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 新增一条消息
|
|
|
+ */
|
|
|
+ appendMessage(message, scrollToBottom = false) {
|
|
|
+ let messageList = allMessages[message.toContactId];
|
|
|
+ // 如果是自己的消息需要push,发送的消息不再增加未读条数
|
|
|
+ let conversation=this.findConversation(message.toContactId);
|
|
|
+ let unread=conversation.unread!=undefined && conversation.unread>0?(conversation.unread+1):1;
|
|
|
+ if (message.type == 'event'){
|
|
|
+ // if(this.user.id == message.fromUser.id){}
|
|
|
+ unread = 0;
|
|
|
+ }
|
|
|
+ if (messageList === undefined) {
|
|
|
+ //console.log("qxj appendMessage messageList:"+messageList+",message"+JSON.stringify(message));
|
|
|
+ conversation.id=message.toContactId;
|
|
|
+ conversation.unread = unread;
|
|
|
+ conversation.lastSendTime=message.sendTime;
|
|
|
+ conversation.lastContent=this.lastContentRender(message);
|
|
|
+ this.updateContact(conversation);
|
|
|
+ } else {
|
|
|
+ // 如果消息存在则不再添加
|
|
|
+ let hasMsg = messageList.some(({id})=>id == message.id);
|
|
|
+ if (hasMsg) return;
|
|
|
+ this._addMessage(message, message.toContactId, 1);
|
|
|
+
|
|
|
+ conversation.lastContent=this.lastContentRender(message);
|
|
|
+ conversation.lastSendTime=message.sendTime;
|
|
|
+ if (message.toContactId == this.currentContactId) {
|
|
|
+ conversation.unread=0;
|
|
|
+ if (scrollToBottom == true) {
|
|
|
+ this.messageViewToBottom();
|
|
|
+ }
|
|
|
+ this.CacheDraft.remove(message.toContactId);
|
|
|
+ } else {
|
|
|
+ conversation.unread = unread;
|
|
|
+ }
|
|
|
+ this.updateContact(conversation);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ _emitSend(message, next, file) {
|
|
|
+ this.$emit(
|
|
|
+ "send",
|
|
|
+ message,
|
|
|
+ (replaceMessage = { status: "succeed" }) => {
|
|
|
+ next();
|
|
|
+ this.updateMessage(Object.assign(message, replaceMessage));
|
|
|
+ },
|
|
|
+ file,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _handleSend(text) {
|
|
|
+ const message = this._createMessage({ content: text });
|
|
|
+ this.appendMessage(message, true);
|
|
|
+ this._emitSend(message, () => {
|
|
|
+ this.updateContact({
|
|
|
+ id: message.toContactId,
|
|
|
+ lastContent: this.lastContentRender(message),
|
|
|
+ lastSendTime: message.sendTime,
|
|
|
+ });
|
|
|
+ this.CacheDraft.remove(message.toContactId);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ _handleUpload(file) {
|
|
|
+ const imageTypes = ["image/gif", "image/jpeg", "image/png"];
|
|
|
+ let joinMessage;
|
|
|
+ if (imageTypes.includes(file.type)) {
|
|
|
+ joinMessage = {
|
|
|
+ type: "image",
|
|
|
+ content: URL.createObjectURL(file),
|
|
|
+ };
|
|
|
+ } else {
|
|
|
+ joinMessage = {
|
|
|
+ type: "file",
|
|
|
+ fileSize: file.size,
|
|
|
+ fileName: file.name,
|
|
|
+ content: "",
|
|
|
+ };
|
|
|
+ }
|
|
|
+ const message = this._createMessage(joinMessage);
|
|
|
+ this.appendMessage(message, true);
|
|
|
+ this._emitSend(
|
|
|
+ message,
|
|
|
+ () => {
|
|
|
+ this.updateContact({
|
|
|
+ id: message.toContactId,
|
|
|
+ lastContent: this.lastContentRender(message),
|
|
|
+ lastSendTime: message.sendTime,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ file,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _handleRemoteImage(url) {
|
|
|
+ let joinMessage = {
|
|
|
+ type: "image",
|
|
|
+ content: url,
|
|
|
+ };
|
|
|
+ const message = this._createMessage(joinMessage);
|
|
|
+ this.appendMessage(message, true);
|
|
|
+ this._emitSend(
|
|
|
+ message,
|
|
|
+ () => {
|
|
|
+ this.updateContact({
|
|
|
+ id: message.toContactId,
|
|
|
+ lastContent: this.lastContentRender(message),
|
|
|
+ lastSendTime: message.sendTime,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ null,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _pickImg(){
|
|
|
+ this.$emit("pick-image");
|
|
|
+ },
|
|
|
+ _emitPullMessages(next) {
|
|
|
+ //console.log("qxj currentContact:"+JSON.stringify(this.currentContact));
|
|
|
+ this._changeContactLock = true;
|
|
|
+ this.$emit("pull-messages",
|
|
|
+ this.currentContact,
|
|
|
+ (messages = [], isEnd = false) => {
|
|
|
+ this._addMessage(messages, this.currentContactId, 0);
|
|
|
+ this.CacheMessageLoaded.set(this.currentContactId, isEnd);
|
|
|
+ if (isEnd == true) this.$refs.messages.loaded();
|
|
|
+ this.updateCurrentMessages();
|
|
|
+ this._changeContactLock = false;
|
|
|
+ next(isEnd);
|
|
|
+ },
|
|
|
+ this,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _emitPullUpMessages(next) {
|
|
|
+ this._changeContactLock = true;
|
|
|
+ this.$emit("pull-up-messages",
|
|
|
+ this.currentContact,
|
|
|
+ (messages = [], isEnd = false) => {
|
|
|
+ this._addMessage(messages, this.currentContactId, 1);
|
|
|
+ this.CacheMessageLoaded.set(this.currentContactId, isEnd);
|
|
|
+ if (isEnd == true) this.$refs.messages.loaded();
|
|
|
+ this.updateCurrentMessages();
|
|
|
+ this._changeContactLock = false;
|
|
|
+ next(isEnd);
|
|
|
+ },
|
|
|
+ this,
|
|
|
+ );
|
|
|
+ },
|
|
|
+ clearCacheContainer(name) {
|
|
|
+ this.CacheContactContainer.remove(name);
|
|
|
+ this.CacheMenuContainer.remove(name);
|
|
|
+ },
|
|
|
+ _renderWrapper(children) {
|
|
|
+ return (
|
|
|
+ <div
|
|
|
+ style={{
|
|
|
+ width: toPx(this.width),
|
|
|
+ height: toPx(this.height),
|
|
|
+ }}
|
|
|
+ ref="wrapper"
|
|
|
+ class={[
|
|
|
+ "lemon-wrapper",
|
|
|
+ `lemon-wrapper--theme-${this.theme}`,
|
|
|
+ { "lemon-wrapper--simple": this.simple },
|
|
|
+ this.drawerVisible && "lemon-wrapper--drawer-show",
|
|
|
+ ]}
|
|
|
+ >
|
|
|
+ {children}
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _renderMenu() {
|
|
|
+ const menuItem = this._renderMenuItem();
|
|
|
+ return (
|
|
|
+ <div class="lemon-menu" v-show={!this.hideMenu}>
|
|
|
+ {
|
|
|
+ <lemon-avatar
|
|
|
+ v-show={!this.hideMenuAvatar}
|
|
|
+ on-click={e => {
|
|
|
+ this.$emit("menu-avatar-click", e);
|
|
|
+ }}
|
|
|
+ class="lemon-menu__avatar"
|
|
|
+ src={this.user.avatar}
|
|
|
+ />
|
|
|
+ }
|
|
|
+ {menuItem.top}
|
|
|
+ {this.$slots.menu}
|
|
|
+ <div class="lemon-menu__bottom">
|
|
|
+ {this.$slots["menu-bottom"]}
|
|
|
+ {menuItem.bottom}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _renderMenuAvatar() {
|
|
|
+ return;
|
|
|
+ },
|
|
|
+ _renderMenuItem() {
|
|
|
+ const top = [];
|
|
|
+ const bottom = [];
|
|
|
+ this.menus.forEach(item => {
|
|
|
+ const { name, title, unread, render, click } = item;
|
|
|
+ const node = (
|
|
|
+ <div
|
|
|
+ class={[
|
|
|
+ "lemon-menu__item",
|
|
|
+ { "lemon-menu__item--active": this.activeSidebar == name },
|
|
|
+ ]}
|
|
|
+ on-click={() => {
|
|
|
+ funCall(click, () => {
|
|
|
+ if (name) this.changeMenu(name);
|
|
|
+ });
|
|
|
+ }}
|
|
|
+ title={title}
|
|
|
+ >
|
|
|
+ <lemon-badge count={unread}>{render(item)}</lemon-badge>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ item.isBottom === true ? bottom.push(node) : top.push(node);
|
|
|
+ });
|
|
|
+ return {
|
|
|
+ top,
|
|
|
+ bottom,
|
|
|
+ };
|
|
|
+ },
|
|
|
+ _renderSidebarMessage() {
|
|
|
+ return this._renderSidebar(
|
|
|
+ [
|
|
|
+ useScopedSlot(this.$scopedSlots["sidebar-message-top"], null, this),
|
|
|
+ this.lastMessages.map(contact => {
|
|
|
+ return this._renderContact(
|
|
|
+ {
|
|
|
+ contact,
|
|
|
+ timeFormat: this.contactTimeFormat,
|
|
|
+ },
|
|
|
+ () => this.changeContact(contact.conversationId),
|
|
|
+ this.$scopedSlots["sidebar-message"],
|
|
|
+ );
|
|
|
+ }),
|
|
|
+ ],
|
|
|
+ DEFAULT_MENU_LASTMESSAGES,
|
|
|
+ useScopedSlot(
|
|
|
+ this.$scopedSlots["sidebar-message-fixedtop"],
|
|
|
+ null,
|
|
|
+ this,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _renderContact(props, onClick, slot) {
|
|
|
+ const {
|
|
|
+ click: customClick,
|
|
|
+ renderContainer,
|
|
|
+ id: contactId,
|
|
|
+ } = props.contact;
|
|
|
+ const click = () => {
|
|
|
+ funCall(customClick, () => {
|
|
|
+ onClick();
|
|
|
+ this._customContainerReady(
|
|
|
+ renderContainer,
|
|
|
+ this.CacheContactContainer,
|
|
|
+ contactId,
|
|
|
+ );
|
|
|
+ });
|
|
|
+ };
|
|
|
+ return (
|
|
|
+ <lemon-contact
|
|
|
+ class={{
|
|
|
+ "lemon-contact--active":this._menuIsMessages()?this.currentContactId == props.contact.conversationId:(this._menuIsContacts()?this.currentNewContactId == props.contact.id:false)
|
|
|
+ }}
|
|
|
+ v-lemon-contextmenu_contact={this.contactContextmenu}
|
|
|
+ props={props}
|
|
|
+ on-click={click}
|
|
|
+ scopedSlots={{ default: slot }}
|
|
|
+ />
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _renderSidebarContact() {
|
|
|
+ let prevIndex;
|
|
|
+ return this._renderSidebar(
|
|
|
+ [
|
|
|
+ useScopedSlot(this.$scopedSlots["sidebar-contact-top"], null, this),
|
|
|
+ this.contacts.map(contact => {
|
|
|
+ if (!contact.index) return;
|
|
|
+ contact.index = contact.index.replace(/\[[0-9]*\]/, "");
|
|
|
+ const node = [
|
|
|
+ contact.index !== prevIndex && (
|
|
|
+ <p class="lemon-sidebar__label">{contact.index}</p>
|
|
|
+ ),
|
|
|
+ this._renderContact(
|
|
|
+ {
|
|
|
+ contact: contact,
|
|
|
+ simple: true,
|
|
|
+ },
|
|
|
+ () => {
|
|
|
+ console.log("qxj contact click");
|
|
|
+ this.currentNewContactId=contact.id;
|
|
|
+ //this.changeContact(contact.id);
|
|
|
+ },
|
|
|
+ this.$scopedSlots["sidebar-contact"],
|
|
|
+ ),
|
|
|
+ ];
|
|
|
+ prevIndex = contact.index;
|
|
|
+ return node;
|
|
|
+ }),
|
|
|
+ ],
|
|
|
+ DEFAULT_MENU_CONTACTS,
|
|
|
+ useScopedSlot(
|
|
|
+ this.$scopedSlots["sidebar-contact-fixedtop"],
|
|
|
+ null,
|
|
|
+ this,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _renderSidebar(children, name, fixedtop) {
|
|
|
+ return (
|
|
|
+ <div
|
|
|
+ class="lemon-sidebar"
|
|
|
+ v-show={this.activeSidebar == name}
|
|
|
+ on-scroll={this._handleSidebarScroll}
|
|
|
+ >
|
|
|
+ <div class="lemon-sidebar__fixed-top">{fixedtop}</div>
|
|
|
+ <div class="lemon-sidebar__scroll">{children}</div>
|
|
|
+ </div>
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _renderDrawer() {
|
|
|
+ return this._menuIsMessages() && this.currentContactId ? (
|
|
|
+ <div class="lemon-drawer" ref="drawer">
|
|
|
+ {renderDrawerContent(this.currentContact)}
|
|
|
+ {useScopedSlot(this.$scopedSlots.drawer, "", this.currentContact)}
|
|
|
+ </div>
|
|
|
+ ) : (
|
|
|
+ ""
|
|
|
+ );
|
|
|
+ },
|
|
|
+ _isContactContainerCache(name) {
|
|
|
+ return name.startsWith("contact#");
|
|
|
+ },
|
|
|
+ _renderContainer() {
|
|
|
+ const nodes = [];
|
|
|
+ const cls = "lemon-container";
|
|
|
+ const curact = this.currentContact;
|
|
|
+ const curactNew = this.currentNewContact;
|
|
|
+ let defIsShow = true;
|
|
|
+ for (const name in this.CacheContactContainer.get()) {
|
|
|
+ const show = curactNew.id == name && this.currentIsDefSidebar;
|
|
|
+ if(show)defIsShow = !show;
|
|
|
+ nodes.push(
|
|
|
+ <div class={cls} v-show={show}>
|
|
|
+ {this.CacheContactContainer.get(name)}
|
|
|
+ </div>,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ for (const name in this.CacheMenuContainer.get()) {
|
|
|
+ nodes.push(
|
|
|
+ <div
|
|
|
+ class={cls}
|
|
|
+ v-show={this.activeSidebar == name && !this.currentIsDefSidebar}
|
|
|
+ >
|
|
|
+ {this.CacheMenuContainer.get(name)}
|
|
|
+ </div>,
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
+ //聊天消息View
|
|
|
+ nodes.push(
|
|
|
+ <div
|
|
|
+ class={cls}
|
|
|
+ v-show={this._menuIsMessages() && defIsShow && curact.id}
|
|
|
+ >
|
|
|
+ <div class="lemon-container__title">
|
|
|
+ {useScopedSlot(
|
|
|
+ this.$scopedSlots["message-title"],
|
|
|
+ <div class="lemon-container__displayname">
|
|
|
+ {curact.displayName}
|
|
|
+ </div>,
|
|
|
+ curact,
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
+ <div class="lemon-vessel">
|
|
|
+ <div class="lemon-vessel__left">
|
|
|
+ <lemon-messages
|
|
|
+ ref="messages"
|
|
|
+ loading-text={this.loadingText}
|
|
|
+ loadend-text={this.loadendText}
|
|
|
+ hide-time={this.hideMessageTime}
|
|
|
+ hide-name={this.hideMessageName}
|
|
|
+ show-context={this.showContext}
|
|
|
+ time-format={this.messageTimeFormat}
|
|
|
+ reverse-user-id={this.user.id}
|
|
|
+ on-reach-top={this._emitPullMessages}
|
|
|
+ on-reach-bottom={this._emitPullUpMessages}
|
|
|
+ messages={this.currentMessages}
|
|
|
+ />
|
|
|
+
|
|
|
+ </div>
|
|
|
+
|
|
|
+ </div>
|
|
|
+ </div>,
|
|
|
+ );
|
|
|
+ nodes.push(
|
|
|
+ <div class={cls} v-show={!curact.id && this.currentIsDefSidebar}>
|
|
|
+ {this.$slots.cover}
|
|
|
+ </div>,
|
|
|
+ );
|
|
|
+
|
|
|
+ //联系人View
|
|
|
+ nodes.push(
|
|
|
+ <div
|
|
|
+ class={cls}
|
|
|
+ v-show={this._menuIsContacts() && defIsShow && curactNew.id}>
|
|
|
+ {useScopedSlot(
|
|
|
+ this.$scopedSlots["contact-info"],
|
|
|
+ <div class="lemon-contact-info">
|
|
|
+ <lemon-avatar src={curactNew.avatar} size={90} />
|
|
|
+ <h4>{curactNew.displayName}</h4>
|
|
|
+ <lemon-button
|
|
|
+ on-click={() => {
|
|
|
+ if (isEmpty(curactNew.lastContent)) {
|
|
|
+ this.updateContact({
|
|
|
+ id: curactNew.id,
|
|
|
+ lastContent: " ",
|
|
|
+ });
|
|
|
+ }
|
|
|
+ this.changeContact(curactNew.conversationId, DEFAULT_MENU_LASTMESSAGES);
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 发送消息
|
|
|
+ </lemon-button>
|
|
|
+ </div>,
|
|
|
+ curactNew,
|
|
|
+ )}
|
|
|
+ </div>,
|
|
|
+ );
|
|
|
+ return nodes;
|
|
|
+ },
|
|
|
+ _handleSidebarScroll() {
|
|
|
+ contextmenu.hide();
|
|
|
+ },
|
|
|
+ _addContact(data, t) {
|
|
|
+ const type = {
|
|
|
+ 0: "unshift",
|
|
|
+ 1: "push",
|
|
|
+ }[t];
|
|
|
+ this.contacts[type](data);
|
|
|
+ },
|
|
|
+ _addMessage(data, contactId, t) {
|
|
|
+ const type = {
|
|
|
+ 0: "unshift",
|
|
|
+ 1: "push",
|
|
|
+ }[t];
|
|
|
+ if (!Array.isArray(data)) data = [data];
|
|
|
+ allMessages[contactId] = allMessages[contactId] || [];
|
|
|
+ allMessages[contactId][type](...data);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 设置最新消息DOM
|
|
|
+ * @param {String} messageType 消息类型
|
|
|
+ * @param {Function} render 返回消息 vnode
|
|
|
+ */
|
|
|
+ setLastContentRender(messageType, render) {
|
|
|
+ lastContentRender[messageType] = render;
|
|
|
+ },
|
|
|
+ lastContentRender(message) {
|
|
|
+ if (!isFunction(lastContentRender[message.type])) {
|
|
|
+ console.error(
|
|
|
+ `not found '${
|
|
|
+ message.type
|
|
|
+ }' of the latest message renderer,try to use ‘setLastContentRender()’`,
|
|
|
+ );
|
|
|
+ return "";
|
|
|
+ }
|
|
|
+ return lastContentRender[message.type].call(this, message);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 将字符串内的 EmojiItem.name 替换为 img
|
|
|
+ * @param {String} str 被替换的字符串
|
|
|
+ * @return {String} 替换后的字符串
|
|
|
+ */
|
|
|
+ emojiNameToImage(str) {
|
|
|
+ return str.replace(/\[!(\w+)\]/gi, (str, match) => {
|
|
|
+ const file = match;
|
|
|
+ return emojiMap[file]
|
|
|
+ ? `<img emoji-name="${match}" src="${emojiMap[file]}" />`
|
|
|
+ : `[!${match}]`;
|
|
|
+ });
|
|
|
+ },
|
|
|
+ emojiImageToName(str) {
|
|
|
+ return str.replace(/<img emoji-name=\"([^\"]*?)\" [^>]*>/gi, "[!$1]");
|
|
|
+ },
|
|
|
+ updateCurrentMessages() {
|
|
|
+ if (!allMessages[this.currentContactId]){
|
|
|
+ allMessages[this.currentContactId] = [];
|
|
|
+ }
|
|
|
+ //console.log("qxj allMessages:"+JSON.stringify(allMessages));
|
|
|
+ this.currentMessages = allMessages[this.currentContactId];
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 将当前聊天窗口滚动到底部
|
|
|
+ */
|
|
|
+ messageViewToBottom() {
|
|
|
+ this.$refs.messages.scrollToBottom();
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 设置联系人的草稿信息
|
|
|
+ */
|
|
|
+ setDraft(cid, editorValue) {
|
|
|
+ if (isEmpty(cid) || isEmpty(editorValue)) return false;
|
|
|
+ const conversation = this.findConversation(cid);
|
|
|
+ let lastContent = conversation.lastContent;
|
|
|
+ if (isEmpty(conversation)) return false;
|
|
|
+ if (this.CacheDraft.has(cid)) {
|
|
|
+ lastContent = this.CacheDraft.get(cid).lastContent;
|
|
|
+ }
|
|
|
+ this.CacheDraft.set(cid, {
|
|
|
+ editorValue,
|
|
|
+ lastContent,
|
|
|
+ });
|
|
|
+
|
|
|
+ var tempDraft=this.CacheDraft.get(cid);
|
|
|
+ //console.log("qxj editorValue:"+editorValue+",tempDraft:"+JSON.stringify(tempDraft)+"lastContent:"+lastContent);
|
|
|
+
|
|
|
+ this.updateContact({
|
|
|
+ id: cid,
|
|
|
+ lastContent: `<span style="color:red;">[草稿]</span><span>${this.lastContentRender(
|
|
|
+ { type: "text", content: editorValue },
|
|
|
+ )}</span>`,
|
|
|
+ });
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 清空联系人草稿信息
|
|
|
+ */
|
|
|
+ clearDraft(contactId) {
|
|
|
+ const draft = this.CacheDraft.get(contactId);
|
|
|
+ if (draft) {
|
|
|
+ const currentContent = this.findConversation(contactId).lastContent;
|
|
|
+ console.log("qxj currentContent:"+currentContent+",lastContent:"+JSON.stringify(draft));
|
|
|
+ //if (currentContent.indexOf('<span style="color:red;">[草稿]</span>') === 0) {
|
|
|
+ this.updateContact({
|
|
|
+ id: contactId,
|
|
|
+ lastContent: draft.lastContent
|
|
|
+ });
|
|
|
+ //}
|
|
|
+ this.CacheDraft.remove(contactId);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 改变聊天对象
|
|
|
+ * @param contactId 联系人 id
|
|
|
+ */
|
|
|
+ async changeContact(contactId, menuName) {
|
|
|
+ if (menuName) {
|
|
|
+ this.changeMenu(menuName);
|
|
|
+ } else {
|
|
|
+ if (this._changeContactLock || this.currentContactId == contactId){
|
|
|
+ console.log("qxj _changeContactLock currentContactId:"+this.currentContactId+" contactId:"+contactId);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ //保存上个聊天目标的草稿
|
|
|
+ if (this.currentContactId) {
|
|
|
+ const editorValue = clearHtmlExcludeImg(this.getEditorValue()).trim();
|
|
|
+ if (editorValue) {
|
|
|
+ this.setDraft(this.currentContactId, editorValue);
|
|
|
+ this.setEditorValue();
|
|
|
+ } else {
|
|
|
+ this.clearDraft(this.currentContactId);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.currentContactId = contactId;
|
|
|
+ if (!this.currentContactId) return false;
|
|
|
+ this.$emit("change-conversation", this.currentContact, this);
|
|
|
+ if (isFunction(this.currentContact.renderContainer) || this.activeSidebar == DEFAULT_MENU_CONTACTS) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ //填充草稿内容
|
|
|
+ const draft = this.CacheDraft.get(contactId);
|
|
|
+ if (draft) this.setEditorValue(draft.editorValue);
|
|
|
+
|
|
|
+ if (this.CacheMessageLoaded.has(contactId)) {
|
|
|
+ this.$refs.messages.loaded();
|
|
|
+ } else {
|
|
|
+ this.$refs.messages.resetLoadState();
|
|
|
+ }
|
|
|
+ if (!allMessages[contactId]) {
|
|
|
+ this.updateCurrentMessages();
|
|
|
+ this._emitPullMessages(isEnd => {
|
|
|
+ this.messageViewToBottom();
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ setTimeout(() => {
|
|
|
+ this.updateCurrentMessages();
|
|
|
+ this.messageViewToBottom();
|
|
|
+ }, 0);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 删除一条聊天消息
|
|
|
+ * @param messageId 消息 id
|
|
|
+ * @param contactId 联系人 id
|
|
|
+ */
|
|
|
+ removeMessage(messageId) {
|
|
|
+ const message = this.findMessage(messageId);
|
|
|
+ if (!message) return false;
|
|
|
+ const index = allMessages[message.toContactId].findIndex(
|
|
|
+ ({ id }) => id == messageId,
|
|
|
+ );
|
|
|
+ allMessages[message.toContactId].splice(index, 1);
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 修改聊天一条聊天消息
|
|
|
+ * @param {Message} data 根据 data.id 查找聊天消息并覆盖传入的值
|
|
|
+ * @param contactId 联系人 id
|
|
|
+ */
|
|
|
+ updateMessage(message) {
|
|
|
+ if (!message.id) return false;
|
|
|
+ let historyMessage = this.findMessage(message.id);
|
|
|
+ if (!historyMessage) return false;
|
|
|
+ historyMessage = Object.assign(historyMessage, message, {
|
|
|
+ toContactId: historyMessage.toContactId,
|
|
|
+ });
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 手动更新对话消息
|
|
|
+ * @param {String} messageId 消息ID,如果为空则更新当前聊天窗口的所有消息
|
|
|
+ */
|
|
|
+ forceUpdateMessage(messageId) {
|
|
|
+ if (!messageId) {
|
|
|
+ this.$refs.messages.$forceUpdate();
|
|
|
+ } else {
|
|
|
+ const components = this.$refs.messages.$refs.message;
|
|
|
+ if (components) {
|
|
|
+ const messageComponent = components.find(
|
|
|
+ com => com.$attrs.message.id == messageId,
|
|
|
+ );
|
|
|
+ if (messageComponent) messageComponent.$forceUpdate();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ _customContainerReady(render, cacheDrive, key) {
|
|
|
+ if (isFunction(render) && !cacheDrive.has(key)) {
|
|
|
+ cacheDrive.set(key, render.call(this));
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 切换左侧按钮
|
|
|
+ * @param {String} name 按钮 name
|
|
|
+ */
|
|
|
+ changeMenu(name) {
|
|
|
+ this.$emit("change-menu", name);
|
|
|
+ this.activeSidebar = name;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 初始化编辑框的 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(data) {
|
|
|
+ let flatData = [];
|
|
|
+ this.$refs.editor.initEmoji(data);
|
|
|
+ if (data[0].label) {
|
|
|
+ data.forEach(item => {
|
|
|
+ flatData.push(...item.children);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ flatData = data;
|
|
|
+ }
|
|
|
+ flatData.forEach(({ name, src }) => (emojiMap[name] = src));
|
|
|
+ },
|
|
|
+ initEditorTools(data) {
|
|
|
+ //this.editorTools = data;
|
|
|
+ this.editorTools = data;
|
|
|
+ //this.$refs.editor.initTools(data);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 初始化左侧按钮
|
|
|
+ * @param {Array<Menu>} data 按钮数据
|
|
|
+ */
|
|
|
+ initMenus(data) {
|
|
|
+ const defaultMenus = [
|
|
|
+ {
|
|
|
+ name: DEFAULT_MENU_LASTMESSAGES,
|
|
|
+ title: "聊天",
|
|
|
+ unread: 0,
|
|
|
+ click: null,
|
|
|
+ render: menu => {
|
|
|
+ return <i class="lemon-icon-message" />;
|
|
|
+ },
|
|
|
+ isBottom: false,
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: DEFAULT_MENU_CONTACTS,
|
|
|
+ title: "通讯录",
|
|
|
+ unread: 0,
|
|
|
+ click: null,
|
|
|
+ render: menu => {
|
|
|
+ return <i class="lemon-icon-addressbook" />;
|
|
|
+ },
|
|
|
+ isBottom: false,
|
|
|
+ },
|
|
|
+ ];
|
|
|
+ let menus = [];
|
|
|
+ if (Array.isArray(data)) {
|
|
|
+ const indexMap = {
|
|
|
+ messages: 0,
|
|
|
+ contacts: 1,
|
|
|
+ };
|
|
|
+ const indexKeys = Object.keys(indexMap);
|
|
|
+ menus = data.map(item => {
|
|
|
+ if (indexKeys.includes(item.name)) {
|
|
|
+ return {
|
|
|
+ ...defaultMenus[indexMap[item.name]],
|
|
|
+ ...item,
|
|
|
+ ...{ renderContainer: null },
|
|
|
+ };
|
|
|
+ }
|
|
|
+ if (item.renderContainer) {
|
|
|
+ this._customContainerReady(
|
|
|
+ item.renderContainer,
|
|
|
+ this.CacheMenuContainer,
|
|
|
+ item.name,
|
|
|
+ );
|
|
|
+ }
|
|
|
+ return item;
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ menus = defaultMenus;
|
|
|
+ }
|
|
|
+ this.menus = menus;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 初始化联系人数据
|
|
|
+ * @param {Array<Contact>} data 联系人列表
|
|
|
+ */
|
|
|
+ initContacts(data) {
|
|
|
+ this.contacts = data;
|
|
|
+ this.sortContacts();
|
|
|
+ if(!!this.contacts>0){
|
|
|
+ this.currentNewContactId=(this.contacts[0]).id;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 初始化会话数据
|
|
|
+ * @param {Array<Contact>} data 会话列表
|
|
|
+ */
|
|
|
+ initConversations(data) {
|
|
|
+ this.conversations = data;
|
|
|
+ this.sortConversations();
|
|
|
+
|
|
|
+ },
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 使用 联系人的 index 值进行排序
|
|
|
+ */
|
|
|
+ sortContacts() {
|
|
|
+ this.contacts.sort((a, b) => {
|
|
|
+ if (!a.index) return;
|
|
|
+ return a.index.localeCompare(b.index);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ sortConversations() {
|
|
|
+ this.conversations.sort((a, b) => {
|
|
|
+ if (!a.index) return;
|
|
|
+ return a.index.localeCompare(b.index);
|
|
|
+ });
|
|
|
+ },
|
|
|
+ appendContact(contact) {
|
|
|
+ if (isEmpty(contact.id) || isEmpty(contact.displayName)) {
|
|
|
+ console.error("id | displayName cant be empty");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (this.hasContact(contact.id)) return true;
|
|
|
+ this.contacts.push(
|
|
|
+ Object.assign(
|
|
|
+ {
|
|
|
+ id: "",
|
|
|
+ displayName: "",
|
|
|
+ avatar: "",
|
|
|
+ index: "",
|
|
|
+ unread: 0,
|
|
|
+ lastSendTime: "",
|
|
|
+ lastContent: "",
|
|
|
+ },
|
|
|
+ contact,
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+
|
|
|
+ appendConversation(conversation) {
|
|
|
+ if (isEmpty(conversation.conversationId) || isEmpty(conversation.displayName)) {
|
|
|
+ console.error("id | displayName cant be empty");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (this.hasConversation(conversation.conversationId)) return true;
|
|
|
+ let insConversation=Object.assign({
|
|
|
+ conversationId: "",
|
|
|
+ displayName: "",
|
|
|
+ avatar: "",
|
|
|
+ index: "",
|
|
|
+ unread: 1,
|
|
|
+ lastSendTime: "",
|
|
|
+ lastContent: "",
|
|
|
+ },conversation
|
|
|
+ );
|
|
|
+ console.log("qxj insConversation:"+JSON.stringify(insConversation));
|
|
|
+ this.conversations.unshift(insConversation);
|
|
|
+ // this.updateContact(insConversation);
|
|
|
+ //this.conversations.push(insConversation);
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ removeContact(id) {
|
|
|
+ const index = this.findContactIndexById(id);
|
|
|
+ if (index === -1) return false;
|
|
|
+ this.contacts.splice(index, 1);
|
|
|
+ this.CacheDraft.remove(id);
|
|
|
+ this.CacheMessageLoaded.remove(id);
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ removeConversations(conversationId) {
|
|
|
+ const index = this.findConversationIndexById(conversationId);
|
|
|
+ if (index === -1) return false;
|
|
|
+ this.conversations.splice(index, 1);
|
|
|
+ // this.CacheDraft.remove(id);
|
|
|
+ // this.CacheMessageLoaded.remove(id);
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ topPopConversations(conversation,isNotDel) { //置顶
|
|
|
+ if(!isNotDel){
|
|
|
+ this.removeConversations(conversation.conversationId);
|
|
|
+ }
|
|
|
+ this.conversations.unshift(conversation);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 修改会话数据
|
|
|
+ * @param {Contact} data 修改的数据,根据 Contact.id 查找联系人并覆盖传入的值
|
|
|
+ */
|
|
|
+ updateContact(data) {
|
|
|
+ const conversationId = data.conversationId;
|
|
|
+ const index = this.findConversationIndexById(conversationId);
|
|
|
+ if (index !== -1) {
|
|
|
+ // if(isString(data.unread)) {
|
|
|
+ // if (unread.indexOf("+") === 0 || unread.indexOf("-") === 0) {
|
|
|
+ // data.unread =parseInt(unread) + parseInt(this.conversations[index].unread);
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ this.$set(this.conversations, index, {
|
|
|
+ ...this.conversations[index],
|
|
|
+ ...data,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ updateContact1(data) {
|
|
|
+ const contactId = data.id;
|
|
|
+ delete data.id;
|
|
|
+ const index = this.findContactIndexById(contactId);
|
|
|
+ if (index !== -1) {
|
|
|
+ const { unread } = data;
|
|
|
+ if (isString(unread)) {
|
|
|
+ if (unread.indexOf("+") === 0 || unread.indexOf("-") === 0) {
|
|
|
+ data.unread =parseInt(unread) + parseInt(this.contacts[index].unread);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.$set(this.contacts, index, {
|
|
|
+ ...this.contacts[index],
|
|
|
+ ...data,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 根据 id 查找联系人的索引
|
|
|
+ * @param contactId 联系人 id
|
|
|
+ * @return {Number} 联系人索引,未找到返回 -1
|
|
|
+ */
|
|
|
+ findContactIndexById(contactId) {
|
|
|
+ return this.contacts.findIndex(item => item.id == contactId);
|
|
|
+ },
|
|
|
+ findConversationIndexById(conversationId) {
|
|
|
+ return this.conversations.findIndex(item => item.conversationId == conversationId);
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 根据 id 查找判断是否存在联系人
|
|
|
+ * @param contactId 联系人 id
|
|
|
+ * @return {Boolean}
|
|
|
+ */
|
|
|
+ hasContact(contactId) {
|
|
|
+ return this.findContactIndexById(contactId) !== -1;
|
|
|
+ },
|
|
|
+ hasConversation(conversationId) {
|
|
|
+ return this.findConversationIndexById(conversationId) !== -1;
|
|
|
+ },
|
|
|
+ findMessage(messageId) {
|
|
|
+ for (const key in allMessages) {
|
|
|
+ const message = allMessages[key].find(({ id }) => id == messageId);
|
|
|
+ if (message) return message;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ findContact(contactId) {
|
|
|
+ return this.getContacts().find(({ id }) => id == contactId);
|
|
|
+ },
|
|
|
+ findConversation(currentContactId) {
|
|
|
+ return this.conversations.find(item => item.conversationId == currentContactId) || {};
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 返回所有联系人
|
|
|
+ * @return {Array<Contact>}
|
|
|
+ */
|
|
|
+ getContacts() {
|
|
|
+ return this.contacts;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 返回所有会话
|
|
|
+ * @return {Array<Contact>}
|
|
|
+ */
|
|
|
+ getConversations() {
|
|
|
+ return this.conversations;
|
|
|
+ },
|
|
|
+ //返回当前聊天窗口联系人信息
|
|
|
+ getCurrentContact() {
|
|
|
+ return this.currentContact;
|
|
|
+ },
|
|
|
+ getCurrentMessages() {
|
|
|
+ return this.currentMessages;
|
|
|
+ },
|
|
|
+ setEditorValue(val = "") {
|
|
|
+ if (!isString(val)) return false;
|
|
|
+ this.$refs.editor.setValue(this.emojiNameToImage(val));
|
|
|
+ },
|
|
|
+ getEditorValue() {
|
|
|
+ return this.$refs.editor.getFormatValue();
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 清空某个联系人的消息,切换到该联系人时会重新触发pull-messages事件
|
|
|
+ */
|
|
|
+ clearMessages(contactId) {
|
|
|
+ if (contactId) {
|
|
|
+ delete allMessages[contactId];
|
|
|
+ this.CacheMessageLoaded.remove(contactId);
|
|
|
+ this.CacheDraft.remove(contactId);
|
|
|
+ } else {
|
|
|
+ allMessages = {};
|
|
|
+ this.CacheMessageLoaded.remove();
|
|
|
+ this.CacheDraft.remove();
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 返回所有消息
|
|
|
+ * @return {Object<Contact.id,Message>}
|
|
|
+ */
|
|
|
+ getMessages(contactId) {
|
|
|
+ return (contactId ? allMessages[contactId] : allMessages) || [];
|
|
|
+ },
|
|
|
+ changeDrawer(params) {
|
|
|
+ this.drawerVisible = !this.drawerVisible;
|
|
|
+ if (this.drawerVisible == true) this.openDrawer(params);
|
|
|
+ },
|
|
|
+ // openDrawer(data) {
|
|
|
+ // renderDrawerContent = data || new Function();
|
|
|
+ // this.drawerVisible = true;
|
|
|
+ // },
|
|
|
+ openDrawer(params) {
|
|
|
+ renderDrawerContent = isFunction(params)
|
|
|
+ ? params
|
|
|
+ : params.render || new Function();
|
|
|
+ const wrapperWidth = this.$refs.wrapper.clientWidth;
|
|
|
+ const wrapperHeight = this.$refs.wrapper.clientHeight;
|
|
|
+ let width = params.width || 200;
|
|
|
+ let height = params.height || wrapperHeight;
|
|
|
+ let offsetX = params.offsetX || 0;
|
|
|
+ let offsetY = params.offsetY || 0;
|
|
|
+ const position = params.position || "right";
|
|
|
+ if (isString(width)) width = wrapperWidth * toPoint(width);
|
|
|
+ if (isString(height)) height = wrapperHeight * toPoint(height);
|
|
|
+ if (isString(offsetX)) offsetX = wrapperWidth * toPoint(offsetX);
|
|
|
+ if (isString(offsetY)) offsetY = wrapperHeight * toPoint(offsetY);
|
|
|
+
|
|
|
+ this.$refs.drawer.style.width = `${width}px`;
|
|
|
+ this.$refs.drawer.style.height = `${height}px`;
|
|
|
+
|
|
|
+ let left = 0;
|
|
|
+ let top = 0;
|
|
|
+ let shadow = "";
|
|
|
+ if (position == "right") {
|
|
|
+ left = wrapperWidth;
|
|
|
+ } else if (position == "rightInside") {
|
|
|
+ left = wrapperWidth - width;
|
|
|
+ shadow = `-15px 0 16px -14px rgba(0,0,0,0.08)`;
|
|
|
+ } else if (position == "center") {
|
|
|
+ left = wrapperWidth / 2 - width / 2;
|
|
|
+ top = wrapperHeight / 2 - height / 2;
|
|
|
+ shadow = `0 0 20px rgba(0,0,0,0.08)`;
|
|
|
+ }
|
|
|
+ left += offsetX;
|
|
|
+ top += offsetY + -1;
|
|
|
+ this.$refs.drawer.style.top = `${top}px`;
|
|
|
+ this.$refs.drawer.style.left = `${left}px`;
|
|
|
+ this.$refs.drawer.style.boxShadow = shadow;
|
|
|
+
|
|
|
+ this.drawerVisible = true;
|
|
|
+ },
|
|
|
+ closeDrawer() {
|
|
|
+ this.drawerVisible = false;
|
|
|
+ },
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+</script>
|
|
|
+<style lang="stylus">
|
|
|
+bezier = cubic-bezier(0.645, 0.045, 0.355, 1)
|
|
|
+@import '../styles/utils/index'
|
|
|
+
|
|
|
++b(lemon-wrapper)
|
|
|
+ display flex
|
|
|
+ font-size 14px
|
|
|
+ font-family "Microsoft YaHei"
|
|
|
+ //mask-image radial-gradient(circle, white 100%, black 100%)
|
|
|
+ background #efefef
|
|
|
+ transition all .4s bezier
|
|
|
+ position relative
|
|
|
+ p
|
|
|
+ margin 0
|
|
|
+ img
|
|
|
+ vertical-align middle
|
|
|
+ border-style none
|
|
|
++b(lemon-menu)
|
|
|
+ flex-column()
|
|
|
+ align-items center
|
|
|
+ width 60px
|
|
|
+ background #1d232a
|
|
|
+ padding 15px 0
|
|
|
+ position relative
|
|
|
+ user-select none
|
|
|
+ +e(bottom)
|
|
|
+ flex-column()
|
|
|
+ position absolute
|
|
|
+ bottom 0
|
|
|
+ +e(avatar)
|
|
|
+ margin-bottom 20px
|
|
|
+ cursor pointer
|
|
|
+ +e(item)
|
|
|
+ color #999
|
|
|
+ cursor pointer
|
|
|
+ padding 14px 10px
|
|
|
+ max-width 100%
|
|
|
+ +m(active)
|
|
|
+ color #0fd547
|
|
|
+ &:hover:not(.lemon-menu__item--active)
|
|
|
+ color #eee
|
|
|
+ word-break()
|
|
|
+ > *
|
|
|
+ font-size 24px
|
|
|
+ .ant-badge-count
|
|
|
+ display inline-block
|
|
|
+ padding 0 4px
|
|
|
+ height 18px
|
|
|
+ line-height 16px
|
|
|
+ min-width 18px
|
|
|
+ .ant-badge-count
|
|
|
+ .ant-badge-dot
|
|
|
+ box-shadow 0 0 0 1px #1d232a
|
|
|
++b(lemon-sidebar)
|
|
|
+ width 250px
|
|
|
+ background #efefef
|
|
|
+ display flex
|
|
|
+ flex-direction column
|
|
|
+ +e(scroll)
|
|
|
+ overflow-y auto
|
|
|
+ scrollbar-light()
|
|
|
+ +e(label)
|
|
|
+ padding 6px 14px 6px 14px
|
|
|
+ color #666
|
|
|
+ font-size 12px
|
|
|
+ margin 0
|
|
|
+ text-align left
|
|
|
+ +b(lemon-contact--active)
|
|
|
+ background #d9d9d9
|
|
|
++b(lemon-container)
|
|
|
+ flex 1
|
|
|
+ flex-column()
|
|
|
+ background #f4f4f4
|
|
|
+ word-break()
|
|
|
+ position relative
|
|
|
+ z-index 10
|
|
|
+ +e(title)
|
|
|
+ padding 15px 15px
|
|
|
+ +e(displayname)
|
|
|
+ font-size 16px
|
|
|
++b(lemon-vessel)
|
|
|
+ display flex
|
|
|
+ flex 1
|
|
|
+ min-height 100px
|
|
|
+ +e(left)
|
|
|
+ display flex
|
|
|
+ flex-direction column
|
|
|
+ flex 1
|
|
|
+ +e(right)
|
|
|
+ flex none
|
|
|
++b(lemon-messages)
|
|
|
+ flex 1
|
|
|
+ height auto
|
|
|
++b(lemon-drawer)
|
|
|
+ position absolute
|
|
|
+ top 0
|
|
|
+ overflow hidden
|
|
|
+ background #f6f6f6
|
|
|
+ z-index 11
|
|
|
+ display none
|
|
|
++b(lemon-wrapper)
|
|
|
+ +m(drawer-show)
|
|
|
+ +b(lemon-drawer)
|
|
|
+ display block
|
|
|
++b(lemon-contact-info)
|
|
|
+ flex-column()
|
|
|
+ justify-content center
|
|
|
+ align-items center
|
|
|
+ height 100%
|
|
|
+ h4
|
|
|
+ font-size 16px
|
|
|
+ font-weight normal
|
|
|
+ margin 10px 0 20px 0
|
|
|
+ user-select none
|
|
|
+.lemon-wrapper--theme-blue
|
|
|
+ .lemon-message__content
|
|
|
+ background #f3f3f3
|
|
|
+ &::before
|
|
|
+ border-right-color #f3f3f3
|
|
|
+ .lemon-message--reverse .lemon-message__content
|
|
|
+ background #e6eeff
|
|
|
+ &::before
|
|
|
+ border-left-color #e6eeff
|
|
|
+ .lemon-container
|
|
|
+ background #fff
|
|
|
+ .lemon-sidebar
|
|
|
+ background #f9f9f9
|
|
|
+ .lemon-contact
|
|
|
+ background #f9f9f9
|
|
|
+ &:hover:not(.lemon-contact--active)
|
|
|
+ background #f1f1f1
|
|
|
+ &--active
|
|
|
+ background #e9e9e9
|
|
|
+ .lemon-menu
|
|
|
+ background #096bff
|
|
|
+ .lemon-menu__item
|
|
|
+ color rgba(255,255,255,0.4)
|
|
|
+ &:hover:not(.lemon-menu__item--active)
|
|
|
+ color rgba(255,255,255,0.6)
|
|
|
+ &--active
|
|
|
+ color #fff
|
|
|
+ text-shadow 0 0 10px rgba(2,48,118,0.4)
|
|
|
+.lemon-wrapper--simple
|
|
|
+ .lemon-menu
|
|
|
+ .lemon-sidebar
|
|
|
+ display none
|
|
|
+.lemon-wrapper--simple
|
|
|
+ .lemon-menu
|
|
|
+ .lemon-sidebar
|
|
|
+ display none
|
|
|
++b(lemon-contextmenu)
|
|
|
+ border-radius 4px
|
|
|
+ font-size 14px
|
|
|
+ font-variant tabular-nums
|
|
|
+ line-height 1.5
|
|
|
+ color rgba(0, 0, 0, 0.65)
|
|
|
+ z-index 9999
|
|
|
+ background-color #fff
|
|
|
+ border-radius 6px
|
|
|
+ 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
|
|
|
+ min-width 120px
|
|
|
+ +e(item)
|
|
|
+ font-size 14px
|
|
|
+ line-height 16px
|
|
|
+ padding 10px 15px
|
|
|
+ cursor pointer
|
|
|
+ display flex
|
|
|
+ align-items center
|
|
|
+ color #333
|
|
|
+ > 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>
|