1245 lines
32 KiB
Vue
1245 lines
32 KiB
Vue
<template>
|
||
<div class="flex">
|
||
<el-container class="ov-hidden flex-10 full-height ">
|
||
<PanelHeader ref="panelHeader" :data="params" :online="isOnline" :keyboard="inputEvent"
|
||
@event="handleHeaderEvent" />
|
||
<!-- 主体信息 -->
|
||
<el-main class="main-box no-padding">
|
||
<div id="lumenChatPanel" class="talks-container lum-scrollbar" @scroll="talkPanelScroll($event)">
|
||
<!-- 数据加载状态栏 -->
|
||
<div class="loading-toolbar">
|
||
<span v-if="loadRecord.status == 0" class="color-blue">
|
||
<i class="el-icon-loading" /> 正在加载数据中...
|
||
</span>
|
||
<span v-if="loadRecord.status == 1" class="pointer color-blue" @click="loadChatRecords">
|
||
<i class="el-icon-bottom" /> 查看更多消息...
|
||
</span>
|
||
<span v-if="loadRecord.status == 2"> 没有更多消息了... </span>
|
||
</div>
|
||
|
||
<!-- 消息主体 -->
|
||
<div v-for="(item, idx) in records" :key="idx">
|
||
<!-- 群消息 -->
|
||
<div v-if="item.messageType == 9" class="message-box">
|
||
<invite-message @cat="catFriendDetail" :invite="item.invite" />
|
||
</div>
|
||
|
||
<!-- 撤回消息 -->
|
||
<div v-else-if="item.is_revoke == 1" class="message-box">
|
||
<revoke-message :item="item" />
|
||
</div>
|
||
|
||
<div v-else-if="item.messageType == 0" class="message-box">
|
||
<system-text-message :content="item.content" />
|
||
</div>
|
||
|
||
<!-- 其它对话消息 -->
|
||
<div v-else class="message-box record-box" :class="{
|
||
'direction-rt': item.float == 'right',
|
||
'checkbox-border': multiSelect.isOpen === true,
|
||
}">
|
||
<aside v-show="multiSelect.isOpen" class="checkbox-column">
|
||
<i class="el-icon-success" :class="{ selected: verifyMultiSelect(item.id) }"
|
||
@click="triggerMultiSelect(item.id)" />
|
||
</aside>
|
||
<aside class="avatar-column">
|
||
<div class="avatar-box" v-if="item.float == 'right'">
|
||
<face :text="face" v-if="face"></face>
|
||
<face-null :text="name" v-else></face-null>
|
||
</div>
|
||
<div class="avatar-box" v-if="item.float == 'left'">
|
||
<face :text="toUser.face" v-if="toUser.face"></face>
|
||
<face-null :text="toUser.name" v-else></face-null>
|
||
</div>
|
||
</aside>
|
||
<main class="main-column">
|
||
<div class="talk-content">
|
||
<span class="nickname">
|
||
{{ item.float == "right" ? name : toUser.name }} |
|
||
{{ unixToDate(item.createTime, "MM月dd日 hh:mm") }}
|
||
</span>
|
||
<!-- 文本消息 -->
|
||
<div v-if="item.messageType == 'MESSAGE'" style="background-color: #d0e9ff;color: black;"
|
||
class="text-message" :class="{
|
||
left: item.float == 'left',
|
||
right: item.float == 'right',
|
||
}">
|
||
<div class="arrow"></div>
|
||
<pre v-if="!emojistwo.includes(item.text)" v-html="item.text" />
|
||
<pre v-if="emojistwo.includes(item.text)" v-html="textReplaceEmoji(item.text)" />
|
||
</div>
|
||
|
||
<div v-if="item.messageType == 'GOODS' && item.text != null" class="goodsStyle " :class="{
|
||
left: item.float == 'left',
|
||
right: item.float == 'right',
|
||
}">
|
||
<div class="base" @click="linkToGoods(item.text.goodsId, item.text.id)">
|
||
<div>
|
||
<img :src="item.text.thumbnail" class="image" />
|
||
</div>
|
||
<div>
|
||
<div class="goods_name">
|
||
<el-tooltip class="item" effect="dark" :content="item.text.goodsName" placement="top-start">
|
||
<a> {{ item.text.goodsName }} </a>
|
||
</el-tooltip>
|
||
</div>
|
||
<div class="price">
|
||
<span>¥{{ item.text.price }}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div v-if="item.messageType == 'ORDER' && item.text != null" class="oderStyle" :class="{
|
||
left: item.float == 'left',
|
||
right: item.float == 'right',
|
||
}">
|
||
<div class="oedersn">
|
||
<el-tooltip class="item" effect="dark" :content="item.text.sn" placement="top-start">
|
||
<a> 订单号:{{ item.text.sn }} </a>
|
||
</el-tooltip>
|
||
</div>
|
||
<div class="baseTwo">
|
||
<img :src="item.text.groupImages" style="height: 100px;width: 100px;margin-top: 10px;" />
|
||
<span class="orderGoodsName" @click="linkToOrders(item.text.sn)">{{ item.text.groupName }}</span>
|
||
<span class="orderGoodsTime">{{ item.text.paymentTime }}</span>
|
||
<span class="orderFlowPrice">
|
||
订单金额:¥{{ item.text.flowPrice }}
|
||
</span>
|
||
<span class="order_status"
|
||
:style="{ 'color': item.text.orderStatus == 'CANCELLED' || item.text.orderStatus == 'UNPAID' || item.text.orderStatus == ' TAKE' ? '#5a606b' : '#f23030' }">{{
|
||
item.text.orderStatus == 'CANCELLED' ? '已取消' : item.text.orderStatus == 'UNPAID' ? '未付款' :
|
||
item.text.orderStatus ==
|
||
'PAID' ? '已付款' : item.text.orderStatus == 'UNDELIVERED' ? '待发货' : item.text.orderStatus ==
|
||
'DELIVERED'
|
||
? '已发货' : item.text.orderStatus == ' COMPLETED' ? '已完成' : item.text.orderStatus == ' TAKE' ?
|
||
'待校验' : ''
|
||
}}</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</main>
|
||
</div>
|
||
|
||
<!-- 消息时间 -->
|
||
<div v-show="compareTime(idx, item.createTime)" class="datetime no-select"
|
||
v-text="sendTime(unixToDate(item.createTime, 'yyyy-MM-dd hh:mm'))" />
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 置底按钮 -->
|
||
<transition name="el-fade-in-linear">
|
||
<div v-show="tipsBoard" class="tips-board pointer" @click="talkPanelScrollBottom">
|
||
<!-- <SvgMentionDown class="svg" /> -->
|
||
<span>回到底部</span>
|
||
</div>
|
||
</transition>
|
||
|
||
<!-- 新消息气泡 -->
|
||
<div v-show="tipsBoard && unreadMessage.num" class="talk-bubble pointer no-select"
|
||
@click="talkPanelScrollBottom">
|
||
<i class="el-icon-chat-dot-round" />
|
||
<span>新消息({{ unreadMessage.num }}条)</span>
|
||
<span>
|
||
#{{ unreadMessage.nickname }}#
|
||
{{ unreadMessage.content }}
|
||
</span>
|
||
</div>
|
||
</el-main>
|
||
|
||
<!-- 页脚信息 -->
|
||
<el-footer class="footer-box" height="180">
|
||
<template v-if="multiSelect.isOpen === false">
|
||
<MeEditor @send="submitSendMessage" @keyboard-event="onKeyboardEvent" />
|
||
</template>
|
||
<template v-else>
|
||
<PanelToolbar v-model="multiSelect.items.length" @event="handleMultiMode" />
|
||
</template>
|
||
</el-footer>
|
||
<el-aside width="200px">
|
||
|
||
</el-aside>
|
||
|
||
</el-container>
|
||
|
||
<!-- 消息管理器 -->
|
||
<transition name="el-fade-in-linear">
|
||
<TalkSearchRecord v-if="findChatRecord" :params="{
|
||
talk_type: params.talk_type,
|
||
receiver_id: params.receiver_id,
|
||
title: params.nickname,
|
||
}" @close="findChatRecord = false" />
|
||
</transition>
|
||
|
||
<!-- 链接信息 -->
|
||
<OtherLink :toUser="toUser" :id="id" :goodsParams="goodsParams" class="flex-4" />
|
||
</div>
|
||
</template>
|
||
<script>
|
||
import { textReplaceLink } from "@/utils/functions";
|
||
import { textReplaceEmoji, emojistwo } from "@/utils/emojis";
|
||
import OtherLink from "@/components/chat/panel/OtherLink.vue";
|
||
import { mapState, mapGetters } from "vuex";
|
||
import TalkSearchRecord from "@/components/chat/TalkSearchRecord";
|
||
import MeEditor from "@/components/editor/MeEditor";
|
||
import PanelHeader from "./PanelHeader";
|
||
import PanelToolbar from "./PanelToolbar";
|
||
import SocketInstance from "@/im-server/socket-instance";
|
||
import { SvgMentionDown } from "@/core/icons";
|
||
import { formatTime, parseTime, copyTextToClipboard } from "@/utils/functions";
|
||
|
||
import {
|
||
ServeTalkRecords,
|
||
ServeForwardRecords,
|
||
ServeRemoveRecords,
|
||
ServeRevokeRecords,
|
||
} from "@/api/chat";
|
||
|
||
export default {
|
||
name: "TalkEditorPanel",
|
||
components: {
|
||
MeEditor,
|
||
OtherLink,
|
||
TalkSearchRecord,
|
||
SvgMentionDown,
|
||
PanelToolbar,
|
||
PanelHeader,
|
||
},
|
||
props: {
|
||
// 对话相关参数
|
||
params: {
|
||
type: Object,
|
||
default: function () {
|
||
return {
|
||
// 消息来源(1:好友私信 2:群聊)
|
||
talk_type: 0,
|
||
// 消息接收者ID(好友ID或者群聊ID)
|
||
receiver_id: 0,
|
||
nickname: "",
|
||
};
|
||
},
|
||
},
|
||
goodsParams: {
|
||
type: Object,
|
||
default: null,
|
||
},
|
||
|
||
// 用户是否在线
|
||
isOnline: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
},
|
||
data () {
|
||
return {
|
||
// 记录加载相关参数
|
||
textReplaceEmoji,
|
||
emojistwo,
|
||
textReplaceLink,
|
||
loadRecord: {
|
||
status: 0,
|
||
minRecord: 0,
|
||
pageSize: 10,
|
||
pageNumber: 0,
|
||
},
|
||
|
||
// 多选相关操作
|
||
multiSelect: {
|
||
isOpen: false,
|
||
items: [],
|
||
mode: 0,
|
||
},
|
||
|
||
// 群组Box
|
||
group: {
|
||
panel: false,
|
||
notice: false,
|
||
},
|
||
|
||
// 键盘输入事件
|
||
keyboardEvent: {
|
||
isShow: false,
|
||
time: 0,
|
||
},
|
||
|
||
// 聊天记录管理器数据
|
||
findChatRecord: false,
|
||
|
||
// 置底按钮是否显示
|
||
tipsBoard: false,
|
||
};
|
||
},
|
||
|
||
computed: {
|
||
...mapGetters(["talkItems"]),
|
||
...mapState({
|
||
name: (state) => state.user.name,
|
||
face: (state) => state.user.face,
|
||
unreadMessage: (state) => state.talks.unreadMessage,
|
||
inputEvent: (state) => state.notify.inputEvent,
|
||
id: (state) => state.user.id,
|
||
records: (state) => state.dialogue.records,
|
||
index_name: (state) => state.dialogue.index_name,
|
||
toUser: (state) => state.user.toUser,
|
||
}),
|
||
},
|
||
watch: {
|
||
// 监听面板传递参数
|
||
params () {
|
||
this.loadRecord.minRecord = 0;
|
||
this.tipsBoard = false;
|
||
this.multiSelect = {
|
||
isOpen: false,
|
||
items: [],
|
||
mode: 0,
|
||
};
|
||
this.loadChatRecords();
|
||
|
||
},
|
||
},
|
||
mounted () {
|
||
this.loadChatRecords();
|
||
},
|
||
methods: {
|
||
parseTime,
|
||
sendTime: formatTime,
|
||
|
||
// 处理 Header 组件事件
|
||
handleHeaderEvent (event_name) {
|
||
switch (event_name) {
|
||
case "history":
|
||
this.findChatRecord = true;
|
||
break;
|
||
case "notice":
|
||
this.group.notice = true;
|
||
break;
|
||
case "setting":
|
||
this.group.panel = true;
|
||
break;
|
||
}
|
||
},
|
||
// linkTo(type,val){
|
||
// if(type === 'GOODS'){
|
||
// let routeUrl = this.$router.resolve({
|
||
// path: '/goodsDetail',
|
||
// query: { skuId:val.id, goodsId:val.goodsId }
|
||
// });
|
||
// window.open(routeUrl.href, '_blank');
|
||
// }
|
||
// },
|
||
// #TODO 冗余代码
|
||
|
||
formatDateToString (date) {
|
||
var year = date.getFullYear();
|
||
var month = date.getMonth() + 1;
|
||
var day = date.getDate();
|
||
if (month < 10) month = "0" + month;
|
||
if (day < 10) day = "0" + day;
|
||
return year + "-" + month + "-" + day;
|
||
},
|
||
|
||
/**
|
||
* 将unix时间戳转换为指定格式
|
||
* @param unix 时间戳【秒】
|
||
* @param format 转换格式
|
||
* @returns {*|string}
|
||
*/
|
||
unixToDate (unix, format) {
|
||
if (!unix) return unix;
|
||
let _format = format || "yyyy-MM-dd hh:mm:ss";
|
||
const d = new Date(unix);
|
||
const o = {
|
||
"M+": d.getMonth() + 1,
|
||
"d+": d.getDate(),
|
||
"h+": d.getHours(),
|
||
"m+": d.getMinutes(),
|
||
"s+": d.getSeconds(),
|
||
"q+": Math.floor((d.getMonth() + 3) / 3),
|
||
S: d.getMilliseconds(),
|
||
};
|
||
if (/(y+)/.test(_format))
|
||
_format = _format.replace(
|
||
RegExp.$1,
|
||
(d.getFullYear() + "").substr(4 - RegExp.$1.length)
|
||
);
|
||
for (const k in o)
|
||
if (new RegExp("(" + k + ")").test(_format))
|
||
_format = _format.replace(
|
||
RegExp.$1,
|
||
RegExp.$1.length === 1
|
||
? o[k]
|
||
: ("00" + o[k]).substr(("" + o[k]).length)
|
||
);
|
||
return _format;
|
||
},
|
||
|
||
formateDateAndTimeToString (date) {
|
||
var hours = date.getHours();
|
||
var mins = date.getMinutes();
|
||
var secs = date.getSeconds();
|
||
var msecs = date.getMilliseconds();
|
||
if (hours < 10) hours = "0" + hours;
|
||
if (mins < 10) mins = "0" + mins;
|
||
if (secs < 10) secs = "0" + secs;
|
||
if (msecs < 10) secs = "0" + msecs;
|
||
return (
|
||
this.formatDateToString(date) + " " + hours + ":" + mins + ":" + secs
|
||
);
|
||
},
|
||
// #冗余代码结束
|
||
|
||
// 回车键发送消息回调事件
|
||
submitSendMessage (content) {
|
||
const record = {
|
||
operation_type: "MESSAGE",
|
||
to: this.params.receiver_id,
|
||
from: this.id,
|
||
message_type: "MESSAGE",
|
||
context: content,
|
||
talk_id: this.params.talkId,
|
||
};
|
||
// if (record.messageType == 'MESSAGE"') {
|
||
// record.text = this.textReplaceEmoji(record.content)
|
||
// }
|
||
SocketInstance.emit("event_talk", record);
|
||
|
||
this.$store.commit("UPDATE_TALK_ITEM", {
|
||
index_name: this.index_name,
|
||
draft_text: "",
|
||
});
|
||
|
||
/**
|
||
* 插入数据
|
||
*/
|
||
const insterChat = {
|
||
createTime: this.formateDateAndTimeToString(new Date()),
|
||
fromUser: this.id,
|
||
toUser: record.to,
|
||
isRead: false,
|
||
messageType: "MESSAGE",
|
||
text: content,
|
||
float: "right",
|
||
};
|
||
// console.log("插入对话记录",'')
|
||
// 插入对话记录
|
||
this.$store.commit("PUSH_DIALOGUE", insterChat);
|
||
// 获取聊天面板元素节点
|
||
let el = document.getElementById("lumenChatPanel");
|
||
|
||
// 判断的滚动条是否在底部
|
||
let isBottom =
|
||
Math.ceil(el.scrollTop) + el.clientHeight >= el.scrollHeight;
|
||
|
||
if (isBottom || record.to == this.id) {
|
||
this.$nextTick(() => {
|
||
el.scrollTop = el.scrollHeight;
|
||
});
|
||
} else {
|
||
this.$store.commit("SET_TLAK_UNREAD_MESSAGE", {
|
||
content: content,
|
||
nickname: record.name,
|
||
});
|
||
}
|
||
},
|
||
|
||
// 推送编辑事件消息
|
||
onKeyboardEvent (text) {
|
||
this.$store.commit("UPDATE_TALK_ITEM", {
|
||
index_name: this.index_name,
|
||
draft_text: text,
|
||
});
|
||
|
||
// 判断当前对话是否属于私聊信息
|
||
if (this.params.talk_type == 2 || !this.isOnline) return;
|
||
|
||
// 判断是否推送键盘输入事件消息
|
||
if (!this.$store.state.settings.keyboardEventNotify) {
|
||
return false;
|
||
}
|
||
|
||
let time = new Date().getTime();
|
||
// 判断在两秒内是否已推送事件
|
||
if (this.keyboardEvent.time != 0 && time - this.keyboardEvent.time < 2000)
|
||
return;
|
||
|
||
this.keyboardEvent.time = time;
|
||
|
||
// 调用父类Websocket组件发送消息
|
||
SocketInstance.emit("event_keyboard", {
|
||
sender_id: this.id,
|
||
receiver_id: this.params.receiver_id,
|
||
});
|
||
},
|
||
|
||
|
||
|
||
// 加载用户聊天详情信息
|
||
loadChatRecords () {
|
||
if (this.loadRecord.pageNumber === 0 || this.params.clickFlag) {
|
||
this.loadRecord.pageNumber = 1
|
||
this.params.clickFlag = false
|
||
} else {
|
||
this.loadRecord.pageNumber = this.loadRecord.pageNumber + 1
|
||
}
|
||
const user_id = this.id;
|
||
const data = {
|
||
pageNumber: this.loadRecord.pageNumber,
|
||
pageSize: this.loadChatRecords.pageSize,
|
||
talkId: this.params.talkId,
|
||
};
|
||
this.loadRecord.status = 0;
|
||
|
||
let el = document.getElementById('lumenChatPanel')
|
||
let scrollHeight = el.scrollHeight
|
||
ServeTalkRecords(data).then((res) => {
|
||
// 防止点击切换过快消息返回延迟,导致信息错误
|
||
// console.log("读取历史数据", res);
|
||
const records = res.result.map((item) => {
|
||
let key = new Date().getTime();
|
||
item.float = "center";
|
||
if (item.toUser > 0) {
|
||
item.float = item.fromUser == user_id ? "right" : "left";
|
||
}
|
||
if (item.messageType == 'GOODS') {
|
||
item.text = JSON.parse(item.text)
|
||
}
|
||
// if (item.messageType == 'MESSAGE"') {
|
||
// item.text = this.textReplaceEmoji(item.text)
|
||
// }
|
||
if (item.messageType == 'ORDER') {
|
||
item.text = JSON.parse(item.text)
|
||
}
|
||
return { ...item, [key]: key };
|
||
});
|
||
this.$store.commit("UNSHIFT_DIALOGUE", records);
|
||
records.length
|
||
? (this.loadRecord.status = 1)
|
||
: (this.loadRecord.status = 2);
|
||
this.$nextTick(() => {
|
||
// if (data.record_id == 0 || !data.record_id) {
|
||
if (data.record_id == 0 || data.pageNumber == 1) {
|
||
el.scrollTop = el.scrollHeight
|
||
} else {
|
||
el.scrollTop = el.scrollHeight - scrollHeight
|
||
}
|
||
})
|
||
});
|
||
},
|
||
|
||
// 多选处理方式
|
||
handleMultiMode (value) {
|
||
if (value == "close") {
|
||
this.closeMultiSelect();
|
||
return false;
|
||
}
|
||
|
||
if (this.multiSelect.items.length <= 1) {
|
||
return false;
|
||
}
|
||
|
||
if (value == "forward" || value == "merge_forward") {
|
||
this.multiSelect.mode = value == "forward" ? 1 : 2;
|
||
if (this.verifyMultiSelectType(3)) {
|
||
this.$notify({
|
||
title: "消息转发",
|
||
message: "会话记录不支持合并转发",
|
||
});
|
||
return false;
|
||
}
|
||
|
||
this.$contacts({
|
||
confirm: this.confirmSelectContacts,
|
||
});
|
||
} else if (value == "delete") {
|
||
this.multiSelect.mode = 3;
|
||
|
||
// 批量删除
|
||
let ids = this.multiSelect.items;
|
||
ServeRemoveRecords({
|
||
talk_type: this.params.talk_type,
|
||
receiver_id: this.params.receiver_id,
|
||
record_id: ids.join(","),
|
||
}).then((res) => {
|
||
if (res.code == 200) {
|
||
this.delRecords(ids).closeMultiSelect();
|
||
}
|
||
});
|
||
}
|
||
},
|
||
|
||
// 确认消息转发联系人事件
|
||
confirmSelectContacts (data) {
|
||
let user_ids = [];
|
||
let group_ids = [];
|
||
data.forEach((item) => {
|
||
if (item.type == 1) {
|
||
user_ids.push(item.id);
|
||
} else {
|
||
group_ids.push(item.id);
|
||
}
|
||
});
|
||
|
||
ServeForwardRecords({
|
||
forward_mode: this.multiSelect.mode,
|
||
talk_type: this.params.talk_type,
|
||
receiver_id: this.params.receiver_id,
|
||
records_ids: this.multiSelect.items,
|
||
receive_user_ids: user_ids,
|
||
receive_group_ids: group_ids,
|
||
}).then((res) => {
|
||
if (res.code == 200) {
|
||
this.closeMultiSelect();
|
||
this.$notify({
|
||
title: "消息转发",
|
||
message: "消息转发成功...",
|
||
type: "success",
|
||
});
|
||
}
|
||
});
|
||
},
|
||
|
||
// 处理消息时间是否显示
|
||
compareTime (index, datetime) {
|
||
if (datetime == undefined) {
|
||
return false;
|
||
}
|
||
if (typeof datetime == "number") {
|
||
datetime = this.unixToDate(datetime, "yyyy-MM-dd hh:mm");
|
||
}
|
||
|
||
if (this.records[index].is_revoke == 1) {
|
||
return false;
|
||
}
|
||
|
||
datetime = datetime.replace(/-/g, "/");
|
||
let time = Math.floor(Date.parse(datetime) / 1000);
|
||
let currTime = Math.floor(new Date().getTime() / 1000);
|
||
|
||
// 当前时间5分钟内时间不显示
|
||
if (currTime - time < 300) return false;
|
||
|
||
// 判断是否是最后一条消息,最后一条消息默认显示时间
|
||
if (index == this.records.length - 1) {
|
||
return true;
|
||
}
|
||
|
||
let nextDate = this.records[index + 1].createTime.replace(/-/g, "/");
|
||
|
||
return !(
|
||
parseTime(new Date(datetime), "{y}-{m}-{d} {h}:{i}") ==
|
||
parseTime(new Date(nextDate), "{y}-{m}-{d} {h}:{i}")
|
||
);
|
||
},
|
||
|
||
// 查看好友用户信息
|
||
catFriendDetail (value) {
|
||
this.$user(value);
|
||
},
|
||
|
||
// 撤回消息
|
||
revokeRecords (index, item) {
|
||
ServeRevokeRecords({
|
||
record_id: item.id,
|
||
}).then((res) => {
|
||
if (res.code == 200) {
|
||
this.$store.commit("UPDATE_DIALOGUE", { id: item.id, is_revoke: 1 });
|
||
}
|
||
});
|
||
},
|
||
|
||
// 删除消息
|
||
removeRecords (index, item) {
|
||
let receiver_id = item.receiver_id;
|
||
if (item.talk_type == 1 && item.user_id != this.id) {
|
||
receiver_id = item.user_id;
|
||
}
|
||
|
||
ServeRemoveRecords({
|
||
talk_type: item.talk_type,
|
||
receiver_id: receiver_id,
|
||
record_id: item.id.toString(),
|
||
}).then((res) => {
|
||
if (res.code == 200) {
|
||
this.$store.commit("DELETE_DIALOGUE", index);
|
||
}
|
||
});
|
||
},
|
||
|
||
// 从列表中删除记录
|
||
delRecords (arr) {
|
||
this.$store.commit("BATCH_DELETE_DIALOGUE", arr);
|
||
return this;
|
||
},
|
||
|
||
// 开启多选模式
|
||
openMultiSelect () {
|
||
this.multiSelect.isOpen = true;
|
||
},
|
||
|
||
// 关闭多选模式
|
||
closeMultiSelect () {
|
||
this.multiSelect.isOpen = false;
|
||
this.multiSelect.items = [];
|
||
},
|
||
|
||
// 判断记录是否选中
|
||
verifyMultiSelect (records_id) {
|
||
return this.multiSelect.items.indexOf(records_id) >= 0;
|
||
},
|
||
|
||
// 触发多选事件
|
||
triggerMultiSelect (records_id) {
|
||
let i = this.multiSelect.items.indexOf(records_id);
|
||
if (i >= 0) {
|
||
this.multiSelect.items.splice(i, 1);
|
||
} else {
|
||
if (this.multiSelect.items.length >= 30) {
|
||
this.$notify({
|
||
title: "温馨提示",
|
||
message: "批量操作最大支持30条数据...",
|
||
});
|
||
return false;
|
||
}
|
||
this.multiSelect.items.push(records_id);
|
||
}
|
||
},
|
||
|
||
// 验证是否存在选择的指定类型的消息
|
||
verifyMultiSelectType (type) {
|
||
return this.records.some((item) => {
|
||
return this.verifyMultiSelect(item.id) && item.messageType == type;
|
||
});
|
||
},
|
||
|
||
// 消息点击右键触发自定义菜单
|
||
onCopy (idx, item, event) {
|
||
let menus = [];
|
||
let content = "";
|
||
if (document.getElementById("copy_class_" + item.id)) {
|
||
content = document.getElementById("copy_class_" + item.id).innerText;
|
||
}
|
||
|
||
if (content) {
|
||
menus.push({
|
||
label: "复制",
|
||
icon: "el-icon-document-copy",
|
||
customClass: "cus-contextmenu-item",
|
||
onClick: () => {
|
||
copyTextToClipboard(content);
|
||
},
|
||
});
|
||
}
|
||
|
||
if (item.user_id == this.id) {
|
||
let time =
|
||
new Date().getTime() - Date.parse(item.createTime.replace(/-/g, "/"));
|
||
if (Math.floor(time / 1000 / 60) < 2) {
|
||
menus.push({
|
||
label: "撤回",
|
||
icon: "el-icon-s-flag",
|
||
customClass: "cus-contextmenu-item",
|
||
onClick: () => {
|
||
this.revokeRecords(idx, item);
|
||
},
|
||
});
|
||
}
|
||
}
|
||
|
||
menus.push({
|
||
label: "删除",
|
||
icon: "el-icon-delete",
|
||
customClass: "cus-contextmenu-item",
|
||
onClick: () => {
|
||
this.removeRecords(idx, item);
|
||
},
|
||
});
|
||
|
||
menus.push({
|
||
label: "引用",
|
||
icon: "el-icon-connection",
|
||
customClass: "cus-contextmenu-item",
|
||
onClick: () => { },
|
||
});
|
||
|
||
menus.push({
|
||
label: "多选",
|
||
icon: "el-icon-finished",
|
||
customClass: "cus-contextmenu-item",
|
||
onClick: () => {
|
||
this.openMultiSelect();
|
||
},
|
||
});
|
||
|
||
// 判断是否是图片消息
|
||
if (item.messageType == 2 && item.file.file_type == 1) {
|
||
menus.push({
|
||
label: "收藏",
|
||
icon: "el-icon-picture",
|
||
customClass: "cus-contextmenu-item",
|
||
onClick: () => {
|
||
this.$store.commit("SAVE_USER_EMOTICON", {
|
||
record_id: item.id,
|
||
});
|
||
},
|
||
});
|
||
}
|
||
|
||
this.$contextmenu({
|
||
items: menus,
|
||
event,
|
||
customClass: "cus-contextmenu",
|
||
zIndex: 3,
|
||
minWidth: 120,
|
||
});
|
||
|
||
this.closeMultiSelect();
|
||
event.preventDefault();
|
||
},
|
||
|
||
hideChatGroup () {
|
||
this.group.panel = false;
|
||
},
|
||
|
||
// 修改群聊免打扰状态
|
||
disturbChange (detail) {
|
||
this.$store.commit("UPDATE_TALK_ITEM", {
|
||
index_name: `2_${this.params.receiver_id}`,
|
||
is_disturb: parseInt(detail.status),
|
||
});
|
||
},
|
||
|
||
// 退出群聊回调事件
|
||
quitGroupSuccess () {
|
||
this.$emit("close-talk");
|
||
},
|
||
|
||
// 同步群信息
|
||
syncGroupInfo (groupInfo) {
|
||
this.$refs.panelHeader.setGroupNum(groupInfo.members_num);
|
||
},
|
||
|
||
// 对话面板滚动事件
|
||
talkPanelScroll (e) {
|
||
if (e.target.scrollTop == 0 && this.loadRecord.status == 1) {
|
||
this.loadChatRecords();
|
||
return;
|
||
}
|
||
|
||
const height = e.target.scrollTop + e.target.clientHeight + 5;
|
||
this.tipsBoard = height < e.target.scrollHeight;
|
||
if (this.tipsBoard == false && this.unreadMessage.num > 0) {
|
||
this.$store.commit("CLEAR_TLAK_UNREAD_MESSAGE");
|
||
}
|
||
},
|
||
|
||
// 聊天版本滚动到底部
|
||
talkPanelScrollBottom () {
|
||
let el = document.getElementById("lumenChatPanel");
|
||
el.scrollTop = el.scrollHeight;
|
||
},
|
||
},
|
||
};
|
||
</script>
|
||
<style lang="less" scoped>
|
||
.order_status {
|
||
height: 30px;
|
||
width: 60px;
|
||
background: #ffeded;
|
||
margin-right: 20px;
|
||
text-align: center;
|
||
line-height: 25px;
|
||
margin-left: 15px;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.oderStyle {
|
||
border: 1px solid #f2f2f2;
|
||
width: 330px;
|
||
border-radius: 4px;
|
||
|
||
.oedersn {
|
||
margin: 10px 0 10px 5px;
|
||
width: 300px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
}
|
||
|
||
.goodsStyle {
|
||
border: 1px solid #f2f2f2;
|
||
width: 300px;
|
||
height: 120px;
|
||
display: flex;
|
||
border-radius: 4px;
|
||
|
||
.goods_name {
|
||
color: black;
|
||
width: 150px;
|
||
font-size: 15px;
|
||
color: #333333;
|
||
margin-top: 30px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.image {
|
||
height: 70px;
|
||
margin-top: 3px;
|
||
width: 70px;
|
||
background-size: cover;
|
||
margin: 20px;
|
||
}
|
||
|
||
.price {
|
||
color: #999;
|
||
margin-top: 20px;
|
||
}
|
||
|
||
.base {
|
||
display: flex;
|
||
}
|
||
}
|
||
|
||
.orderSn {
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap
|
||
}
|
||
|
||
.orderGoodsName {
|
||
width: 200px;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
// white-space: nowrap;
|
||
position: absolute;
|
||
margin-top: 10px;
|
||
margin-left: 10px;
|
||
}
|
||
|
||
.orderGoodsTime {
|
||
margin-left: 10px;
|
||
color: #999;
|
||
position: absolute;
|
||
margin-top: 70px;
|
||
}
|
||
|
||
.orderFlowPrice {
|
||
color: #999;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.main-box {
|
||
position: relative;
|
||
}
|
||
|
||
/* 面板页脚 */
|
||
.footer-box {
|
||
height: 160px !important;
|
||
padding: 0;
|
||
border-top: 1px solid #f5f5f5;
|
||
}
|
||
|
||
/* 侧边栏css */
|
||
.sidebar-box {
|
||
position: absolute;
|
||
width: 350px;
|
||
height: 100%;
|
||
top: 0px;
|
||
right: -350px;
|
||
z-index: 1;
|
||
background: white;
|
||
transition: all 0.5s ease-in-out;
|
||
-moz-transition: all 0.5s ease-in-out;
|
||
-webkit-transition: all 0.5s ease-in-out;
|
||
-o-transition: all 0.5s ease-in-out;
|
||
|
||
&.show {
|
||
right: 0;
|
||
box-shadow: 0 0 14px #e2e1e1;
|
||
}
|
||
}
|
||
|
||
.tips-board {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
position: absolute;
|
||
left: 0;
|
||
right: 0;
|
||
margin: 0 auto;
|
||
bottom: 20px;
|
||
height: 30px;
|
||
width: 100px;
|
||
border-radius: 20px;
|
||
font-size: 12px;
|
||
background-color: #fff;
|
||
box-shadow: 0 2.5px 10px 0 rgba(31, 35, 41, 0.1);
|
||
color: #1f2329;
|
||
|
||
span {
|
||
margin-left: 5px;
|
||
margin-top: -2px;
|
||
}
|
||
|
||
.svg {
|
||
width: 10px;
|
||
height: 10px;
|
||
color: black;
|
||
}
|
||
}
|
||
|
||
|
||
// .base {
|
||
// margin-top: 5px;
|
||
// height: 120px;
|
||
// display: flex;
|
||
|
||
// div {
|
||
// width: 100px;
|
||
// // overflow: hidden;
|
||
// // text-overflow: ellipsis;
|
||
// margin-top: 8px;
|
||
// // white-space: nowrap;
|
||
// }
|
||
|
||
// .image {
|
||
// height: 100px;
|
||
// margin-top: 3px;
|
||
// width: 100px
|
||
// }
|
||
|
||
// }
|
||
|
||
.talk-bubble {
|
||
position: absolute;
|
||
left: 0px;
|
||
bottom: 20px;
|
||
max-width: 300px;
|
||
height: 40px;
|
||
line-height: 40px;
|
||
border-radius: 0 20px 20px 0;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
color: white;
|
||
padding: 0 15px 0 30px;
|
||
font-size: 13px;
|
||
background: #409eff;
|
||
|
||
i {
|
||
font-size: 22px;
|
||
position: absolute;
|
||
left: 5px;
|
||
top: 9px;
|
||
}
|
||
}
|
||
|
||
.talks-container {
|
||
height: 100%;
|
||
width: 100%;
|
||
box-sizing: border-box;
|
||
padding: 10px 15px 30px;
|
||
overflow-y: auto;
|
||
|
||
.message-box {
|
||
width: 100%;
|
||
min-height: 30px;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.loading-toolbar {
|
||
height: 30px;
|
||
line-height: 30px;
|
||
margin: 5px 0;
|
||
text-align: center;
|
||
user-select: none;
|
||
font-size: 13px;
|
||
color: #cec4c4;
|
||
|
||
.color-blue {
|
||
color: #409eff;
|
||
}
|
||
}
|
||
|
||
.datetime {
|
||
height: 30px;
|
||
line-height: 30px;
|
||
color: #ccc9c9;
|
||
font-size: 12px;
|
||
text-align: center;
|
||
margin: 5px 0;
|
||
}
|
||
|
||
.record-box {
|
||
display: flex;
|
||
flex-direction: row;
|
||
transition: 0.5s ease;
|
||
|
||
.checkbox-column {
|
||
display: flex;
|
||
justify-content: center;
|
||
flex-basis: 40px;
|
||
flex-shrink: 0;
|
||
order: 1;
|
||
user-select: none;
|
||
padding-top: 25px;
|
||
|
||
i {
|
||
color: #ccc;
|
||
cursor: pointer;
|
||
font-size: 22px;
|
||
|
||
&.selected {
|
||
color: #409eff !important;
|
||
}
|
||
}
|
||
}
|
||
|
||
.avatar-column {
|
||
width: 40px;
|
||
flex-basis: 40px;
|
||
flex-shrink: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
order: 2;
|
||
user-select: none;
|
||
padding-top: 22px;
|
||
flex-direction: column;
|
||
}
|
||
|
||
.main-column {
|
||
flex: 1 auto;
|
||
order: 3;
|
||
position: relative;
|
||
box-sizing: border-box;
|
||
padding: 5px;
|
||
overflow: hidden;
|
||
|
||
.talk-title {
|
||
display: flex;
|
||
align-items: center;
|
||
height: 15px;
|
||
margin-bottom: 2px;
|
||
font-size: 10px;
|
||
user-select: none;
|
||
color: #a7a0a0;
|
||
opacity: 0;
|
||
transition: 0.5s ease;
|
||
|
||
&.show {
|
||
opacity: 1 !important;
|
||
}
|
||
|
||
span {
|
||
transform: scale(0.9);
|
||
}
|
||
}
|
||
|
||
.talk-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
box-sizing: border-box;
|
||
width: 100%;
|
||
|
||
.nickname {
|
||
font-size: 12px;
|
||
color: #a7a0a0;
|
||
margin: -4px 0 4px -8px;
|
||
transform: scale(0.9);
|
||
}
|
||
}
|
||
|
||
&:hover {
|
||
.talk-title {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
}
|
||
|
||
&.direction-rt {
|
||
.avatar-column {
|
||
order: 3;
|
||
}
|
||
|
||
.main-column {
|
||
order: 2;
|
||
|
||
.talk-title {
|
||
justify-content: flex-end;
|
||
}
|
||
|
||
.talk-content {
|
||
align-items: flex-end;
|
||
}
|
||
}
|
||
}
|
||
|
||
&.checkbox-border {
|
||
border: 1px dashed #c4c4ec;
|
||
|
||
&:hover {
|
||
border-color: #409eff;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
@bg-left-color: #f5f5f5;
|
||
@bg-right-color: #ffffff;
|
||
|
||
.text-message {
|
||
position: relative;
|
||
min-width: 30px;
|
||
min-height: 30px;
|
||
border-radius: 5px;
|
||
padding: 5px;
|
||
|
||
.arrow {
|
||
position: absolute;
|
||
width: 0;
|
||
height: 0;
|
||
font-size: 0;
|
||
border: 5px solid;
|
||
top: 6px;
|
||
left: -10px;
|
||
}
|
||
|
||
&.max-width {
|
||
max-width: calc(100% - 50px);
|
||
}
|
||
|
||
&.left {
|
||
color: #3a3a3a;
|
||
background: @bg-left-color;
|
||
|
||
.arrow {
|
||
border-color: transparent @bg-left-color transparent transparent;
|
||
}
|
||
}
|
||
|
||
&.right {
|
||
color: #fff;
|
||
background: @bg-right-color;
|
||
|
||
.arrow {
|
||
right: -10px;
|
||
left: unset;
|
||
border-color: transparent transparent transparent @bg-right-color;
|
||
}
|
||
}
|
||
|
||
pre {
|
||
white-space: pre-wrap;
|
||
overflow: hidden;
|
||
word-break: break-word;
|
||
word-wrap: break-word;
|
||
font-size: 15px;
|
||
padding: 3px 10px;
|
||
font-family: "Microsoft YaHei";
|
||
line-height: 25px;
|
||
}
|
||
}
|
||
</style>
|