聊天记录导出

This commit is contained in:
liuye
2023-06-30 10:59:43 +08:00
parent 9e5c080c4d
commit 60b14432b6
2 changed files with 963 additions and 630 deletions

View File

@@ -26,6 +26,8 @@
"dui": "file:ui", "dui": "file:ui",
"echarts": "^5.1.2", "echarts": "^5.1.2",
"hash.js": "^1.1.7", "hash.js": "^1.1.7",
"html2canvas": "^1.4.1",
"jspdf": "^2.5.1",
"mp4box": "^0.4.1", "mp4box": "^0.4.1",
"print-js": "^1.0.63", "print-js": "^1.0.63",
"serialize-javascript": "^6.0.0", "serialize-javascript": "^6.0.0",

View File

@@ -1,13 +1,28 @@
<template> <template>
<ai-list v-loading="isLoading" class="detail"> <ai-list v-loading="isLoading" class="detail">
<template slot="title"> <template slot="title">
<ai-title title="会话存档详情" isShowBack isShowBottomBorder @onBackClick="cancel(false)"></ai-title> <ai-title
title="会话存档详情"
isShowBack
isShowBottomBorder
@onBackClick="cancel(false)"
></ai-title>
</template> </template>
<template #left> <template #left>
<div class="addressBook-left"> <div class="addressBook-left">
<div class="addressBook-left__title"> <div class="addressBook-left__title">
<h2 :class="[tabIndex == 1 ? 'tab-active' : '']" @click="typeClick(1)">群聊会话</h2> <h2
<h2 :class="[tabIndex == 0 ? 'tab-active' : '']" @click="typeClick(0)">私聊会话</h2> :class="[tabIndex == 1 ? 'tab-active' : '']"
@click="typeClick(1)"
>
群聊会话
</h2>
<h2
:class="[tabIndex == 0 ? 'tab-active' : '']"
@click="typeClick(0)"
>
私聊会话
</h2>
</div> </div>
<div class="addressBook-left__list--title"> <div class="addressBook-left__list--title">
<el-input <el-input
@@ -15,141 +30,251 @@
:placeholder="tabIndex == 1 ? '请输入群名称' : '请输入昵称'" :placeholder="tabIndex == 1 ? '请输入群名称' : '请输入昵称'"
v-model="searchName" v-model="searchName"
clearable clearable
@clear="current = 1, searchName = '', getListInit()" @clear="(current = 1), (searchName = ''), getListInit()"
v-throttle="() => {(current = 1), getListInit();}" v-throttle="
suffix-icon="iconfont iconSearch"> () => {
(current = 1), getListInit();
}
"
suffix-icon="iconfont iconSearch"
>
</el-input> </el-input>
</div> </div>
<div class="addressBook-left__list--wrapper"> <div class="addressBook-left__list--wrapper">
<div class="addressBook-left__list--item" v-for="(item, index) in list" :key="index" :class="leftActiveIndex == index ? 'active' : ''" @click="leftClick(index)"> <div
class="addressBook-left__list--item"
v-for="(item, index) in list"
:key="index"
:class="leftActiveIndex == index ? 'active' : ''"
@click="leftClick(index)"
>
<div v-if="tabIndex == 1" class="flex-left"> <div v-if="tabIndex == 1" class="flex-left">
<img src="./img/group-img.png" alt=""> <img src="./img/group-img.png" alt="" />
<p>{{item.roomName}}</p> <p>{{ item.roomName }}</p>
</div> </div>
<div v-else class="flex-left"> <div v-else class="flex-left">
<img :src="item.toUserAvatar" alt=""> <img :src="item.toUserAvatar" alt="" />
<p>{{item.toUserName}}</p> <p>{{ item.toUserName }}</p>
</div>
<div
class="flex-right"
:class="`type` + item.roomType"
v-if="tabIndex == 1 && item.roomType > 0"
>
{{ item.roomType == 1 ? "内部" : "外部" }}
</div>
<div
class="flex-right"
:class="`type` + item.toUserType"
v-if="tabIndex != 1 && item.toUserType > 0"
>
{{ item.toUserType == 1 ? "内部" : "外部" }}
</div> </div>
<div class="flex-right" :class="`type`+item.roomType" v-if="tabIndex == 1 && item.roomType > 0">{{item.roomType == 1 ? '内部' : '外部'}}</div>
<div class="flex-right" :class="`type`+item.toUserType" v-if="tabIndex != 1 && item.toUserType > 0">{{item.toUserType == 1 ? '内部' : '外部'}}</div>
</div> </div>
<AiEmpty v-if="!list.length"></AiEmpty> <AiEmpty v-if="!list.length"></AiEmpty>
</div> </div>
<el-pagination class="pagination" <el-pagination
class="pagination"
layout="prev, pager, next" layout="prev, pager, next"
:total="total" @current-change="currentChange" :current-page="current" :page-size="20"> :total="total"
@current-change="currentChange"
:current-page="current"
:page-size="20"
>
</el-pagination> </el-pagination>
</div> </div>
</template> </template>
<template slot="content"> <template slot="content">
<div class="content-right-title"> <div class="content-right-title">
<div class="tab-content"> <div class="tab-content">
<h2 v-for="(item, index) in msgTypeList" :key="index" :class="msgType == index ? 'tab-active' : ''" @click="msgTypeClick(index)">{{item.name}}</h2> <h2
v-for="(item, index) in msgTypeList"
:key="index"
:class="msgType == index ? 'tab-active' : ''"
@click="msgTypeClick(index)"
>
{{ item.name }}
</h2>
</div> </div>
<div class="search-content"> <div class="search-content">
<el-date-picker v-model="time" size="small" type="daterange" value-format="yyyy-MM-dd" <el-date-picker
range-separator="" start-placeholder="开始日期" end-placeholder="结束日期" @change="onChange"> v-model="time"
size="small"
type="daterange"
value-format="yyyy-MM-dd"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="onChange"
>
</el-date-picker> </el-date-picker>
<el-input size="small" placeholder="输入搜索内容" v-model="searchMsg" clearable <el-input
@clear="msgCurrent = 1, searchMsg = '', getMsgListInit()" suffix-icon="iconfont iconSearch" size="small"
v-throttle="() => {(msgCurrent = 1), getMsgListInit();}"/> placeholder="输入搜索内容"
v-model="searchMsg"
clearable
@clear="(msgCurrent = 1), (searchMsg = ''), getMsgListInit()"
suffix-icon="iconfont iconSearch"
v-throttle="
() => {
(msgCurrent = 1), getMsgListInit();
}
"
/>
<!-- <ai-download :instance="instance" url="/app/appconvenientaddressbook/export" :params="search" fileName="会话存档" <!-- <ai-download :instance="instance" url="/app/appconvenientaddressbook/export" :params="search" fileName="会话存档"
:disabled="msgList.length == 0"> :disabled="msgList.length == 0">
<el-button icon="iconfont iconExported" :disabled="msgList.length == 0">导出</el-button> <el-button icon="iconfont iconExported" :disabled="msgList.length == 0">导出</el-button>
</ai-download> --> </ai-download> -->
<el-button icon="iconfont iconExported" :disabled="msgList.length == 0" style="margin-left:8px;">导出</el-button> <el-button
icon="iconfont iconExported"
:disabled="msgList.length == 0"
style="margin-left: 8px"
@click="handleExport"
>导出</el-button
>
</div> </div>
</div> </div>
<div class="content-right-info" @scroll='msgScroll'> <div class="content-right-info" @scroll="msgScroll">
<div ref="pdf">
<div v-for="(item, index) in msgList" :key="index"> <div v-for="(item, index) in msgList" :key="index">
<div class="item" :class="item.userId == id ? 'item-right' : 'item-left'"> <div
class="item"
:class="item.userId == id ? 'item-right' : 'item-left'"
>
<!-- <p class="time" v-if="index == 0">{{item.msgSendTime}}</p> --> <!-- <p class="time" v-if="index == 0">{{item.msgSendTime}}</p> -->
<p class="user-name">{{item.userName}}<span>{{item.msgSendTime}}</span></p> <p class="user-name">
{{ item.userName }}<span>{{ item.msgSendTime }}</span>
</p>
<div class="item-content-flex"> <div class="item-content-flex">
<!-- <i class="el-icon-warning" v-if="item.userId == id"></i> --> <!-- <i class="el-icon-warning" v-if="item.userId == id"></i> -->
<img :src="item.userAvatar" alt="" class="user-img" v-if="item.userId != id"> <img
<img src="./img/user-img.png" alt="" class="user-img" v-if="item.userId != id && !item.userAvatar"> :src="item.userAvatar"
alt=""
class="user-img"
v-if="item.userId != id"
/>
<img
src="./img/user-img.png"
alt=""
class="user-img"
v-if="item.userId != id && !item.userAvatar"
/>
<div class="content" v-if="item.msgType == 'text'"> <div class="content" v-if="item.msgType == 'text'">
<span></span> <span></span>
<p>{{item.content}}</p> <p>{{ item.content }}</p>
</div> </div>
<div class="img-list" v-if="item.msgType == 'image'"> <div class="img-list" v-if="item.msgType == 'image'">
<img :src="item.sdkFileUrl" alt="" v-viewer> <img :src="item.sdkFileUrl" alt="" v-viewer />
</div> </div>
<div class="voice-info" v-if="item.msgType == 'voice'"> <div class="voice-info" v-if="item.msgType == 'voice'">
<ai-audio :src="item.sdkFileUrl" skin="flat" /> <ai-audio :src="item.sdkFileUrl" skin="flat" />
</div> </div>
<video style="width: 300px; object-fit: fill;" controls :src="item.sdkFileUrl" v-if="item.msgType == 'video'"></video> <video
style="width: 300px; object-fit: fill"
controls
:src="item.sdkFileUrl"
v-if="item.msgType == 'video'"
></video>
<ai-file-list v-if="item.msgType == 'file'" <ai-file-list
v-if="item.msgType == 'file'"
:fileList="item.files" :fileList="item.files"
:fileOps="{ name: 'name', size: 'fileSizeStr' }" :fileOps="{ name: 'name', size: 'fileSizeStr' }"
></ai-file-list> ></ai-file-list>
<div class="revoke-text" v-if="item.msgType == 'revoke'">{{item.userName}}{{item.msgSendTime.substring(0, 16)}}撤回了一条消息</div> <div class="revoke-text" v-if="item.msgType == 'revoke'">
{{ item.userName
}}{{ item.msgSendTime.substring(0, 16) }}撤回了一条消息
</div>
<div class="revoke-text" v-if="item.msgType == 'disagree'">对方不同意会话存档内容你将无法继续提供服务</div> <div class="revoke-text" v-if="item.msgType == 'disagree'">
对方不同意会话存档内容,你将无法继续提供服务
</div>
<div class="revoke-text" v-if="item.msgType == 'agree'">对方同意会话存档内容你可以继续提供服务</div> <div class="revoke-text" v-if="item.msgType == 'agree'">
对方同意会话存档内容,你可以继续提供服务
</div>
<div class="card-info" v-if="item.msgType == 'card'"> <div class="card-info" v-if="item.msgType == 'card'">
<div class="top"> <div class="top">
<div class="card-left"> <div class="card-left">
<h3>{{item.cardCorpName}}</h3> <h3>{{ item.cardCorpName }}</h3>
<p>{{item.cardUserName}}</p> <p>{{ item.cardUserName }}</p>
<!-- <div>{{item.cardUserId}}</div> --> <!-- <div>{{item.cardUserId}}</div> -->
</div> </div>
<div class="card-right"> <div class="card-right">
<img :src="item.cardUserAvatar" alt="" v-if="item.cardUserAvatar"> <img
<img src="./img/user-img.png" alt="" v-else> :src="item.cardUserAvatar"
alt=""
v-if="item.cardUserAvatar"
/>
<img src="./img/user-img.png" alt="" v-else />
</div> </div>
</div> </div>
<div class="bottom">个人名片</div> <div class="bottom">个人名片</div>
</div> </div>
<img :src="item.sdkFileUrl" alt="" v-if="item.msgType == 'emotion'" :style="[{width: item.width/2+'px'}, {height: item.height/2+'px'}]"> <img
:src="item.sdkFileUrl"
alt=""
v-if="item.msgType == 'emotion'"
:style="[
{ width: item.width / 2 + 'px' },
{ height: item.height / 2 + 'px' },
]"
/>
<div class="map-info" v-if="item.msgType == 'location'"> <div class="map-info" v-if="item.msgType == 'location'">
<div :id="`map${index}`" class="map-content"></div> <div :id="`map${index}`" class="map-content"></div>
<div class="address-text"> <div class="address-text">
<p>{{item.title}}</p> <p>{{ item.title }}</p>
<p>{{item.address}}</p> <p>{{ item.address }}</p>
</div> </div>
</div> </div>
<div class="card-info" v-if="item.msgType == 'weapp'"> <div class="card-info" v-if="item.msgType == 'weapp'">
<div class="top"> <div class="top">
<div class="card-left"> <div class="card-left">
<h3>{{item.description}}</h3> <h3>{{ item.description }}</h3>
<p>{{item.title}}</p> <p>{{ item.title }}</p>
<div>{{item.username}}{{item.displayname}}</div> <div>{{ item.username }}{{ item.displayname }}</div>
</div> </div>
</div> </div>
<div class="bottom">小程序</div> <div class="bottom">小程序</div>
</div> </div>
<div class="card-info pointer" v-if="item.msgType == 'link'" @click="openLink(item)"> <div
class="card-info pointer"
v-if="item.msgType == 'link'"
@click="openLink(item)"
>
<div class="top"> <div class="top">
<div class="card-left"> <div class="card-left">
<p>{{item.title}}</p> <p>{{ item.title }}</p>
<div>{{item.username}}</div> <div>{{ item.username }}</div>
</div> </div>
<div class="card-right" v-if="item.imageUrl"> <div class="card-right" v-if="item.imageUrl">
<img :src="item.imageUrl" alt="" > <img :src="item.imageUrl" alt="" />
</div> </div>
</div> </div>
<div class="bottom">分享链接</div> <div class="bottom">分享链接</div>
</div> </div>
<img :src="item.userAvatar" alt="" class="user-img" v-if="item.userId == id"> <img
:src="item.userAvatar"
alt=""
class="user-img"
v-if="item.userId == id"
/>
<!-- <i class="el-icon-warning" v-if="item.userId != id"></i> --> <!-- <i class="el-icon-warning" v-if="item.userId != id"></i> -->
</div> </div>
</div> </div>
</div> </div>
</div>
<AiEmpty v-if="!msgList.length"></AiEmpty> <AiEmpty v-if="!msgList.length"></AiEmpty>
</div> </div>
<!-- <el-pagination class="msg-list-pagination" <!-- <el-pagination class="msg-list-pagination"
@@ -161,27 +286,174 @@
</template> </template>
<script> <script>
import { mapState } from 'vuex' import { mapState } from "vuex";
import Viewer from 'v-viewer' import Viewer from "v-viewer";
import AMapLoader from '@amap/amap-jsapi-loader' import AMapLoader from "@amap/amap-jsapi-loader";
import Vue from 'vue' import Vue from "vue";
Vue.use(Viewer); Vue.use(Viewer);
import html2canvas from "html2canvas";
import jsPDF from "jspdf";
import { Loading } from "element-ui";
export default { const PdfLoader = (ele, pdfFileName) => {
name: 'Detail', let loadingInstance = Loading.service({ fullscreen: true });
ele.style.fontFamily = "宋体";
ele.style.padding = "0 20px";
// 预留一定的时间给dom页面渲染完成 如果你能保证dom已经渲染完成了包括图片 才去进行下面转化操作那就可以不用这个延迟器)
setTimeout(() => {
html2canvas(ele, {
// dpi: 300, // 清晰度
scale: 1, // 将Canvas放大倍数 可以获得更具清晰的图片内容
// !!!注意如果你生成的元素内容非常多是一个非常长列表 建议scale不要写太高或者删除这个属性 因为html2canvas会吧内容转成
// base64 会有一定的内容上限 最终返回没有base64编码目前我尝试过生成55页的PDF估计上限在70-100页
useCORS: true, //是否允许跨域
allowTaint: false,
height: ele.offsetHeight,
width: ele.offsetWidth,
windowWidth: document.body.scrollWidth,
windowHeight: document.body.scrollHeight,
}).then((canvas) => {
//未生成pdf的html页面高度
var leftHeight = canvas.height;
var a4Width = 595.28;
var a4Height = 841.89;
//一页pdf显示html页面生成的canvas高度;
var a4HeightRef = Math.floor((canvas.width / a4Width) * a4Height);
//pdf页面偏移
var position = 0;
var pageData = canvas.toDataURL("image/jpeg", 1.0); // 生成的base64 如果你只是要图片 到这里就可以拿到base64图片编码可以查一下base64转二进制 使用new FormData对象传给后端到服务器
var pdf = new jsPDF("x", "pt", "a4"); //生成A4内容大小的pdf每页 更多参数配置可以看看下面的网站
// https://blog.csdn.net/weixin_42333548/article/details/107630706
var index = 1,
canvas1 = document.createElement("canvas"),
height;
pdf.setDisplayMode("fullwidth", "continuous", "FullScreen");
// 处理 pdf 上一页 与 下一页内容之间交叉不好看的断点样式
// 并且把内容转成二进制 生成pdf文件
function createImpl(canvas) {
if (leftHeight > 0) {
index++;
var checkCount = 0;
if (leftHeight > a4HeightRef) {
var i = position + a4HeightRef;
for (i = position + a4HeightRef; i >= position; i--) {
var isWrite = true;
for (var j = 0; j < canvas.width; j++) {
var c = canvas.getContext("2d").getImageData(j, i, 1, 1).data;
if (c[0] != 0xff || c[1] != 0xff || c[2] != 0xff) {
isWrite = false;
break;
}
}
if (isWrite) {
checkCount++;
if (checkCount >= 10) {
break;
}
} else {
checkCount = 0;
}
}
height =
Math.round(i - position) || Math.min(leftHeight, a4HeightRef);
if (height <= 0) {
height = a4HeightRef;
}
} else {
height = leftHeight;
}
canvas1.width = canvas.width;
canvas1.height = height;
var ctx = canvas1.getContext("2d");
ctx.drawImage(
canvas,
0,
position,
canvas.width,
height,
0,
0,
canvas.width,
height
);
if (position != 0) {
pdf.addPage();
}
pdf.addImage(
canvas1.toDataURL("image/jpeg", 1.0),
"JPEG",
0,
0,
a4Width,
(a4Width / canvas1.width) * height
);
leftHeight -= height;
position += height;
if (leftHeight > 0) {
//给pdf文件 添加全屏水印
// const base64 = ''; // 吧你要添加的水印内容搞成一张小图片 然后手动去转成base64编码 放在这里就可以了
// for (let i = 0; i < 6; i++) {
// for (let j = 0; j < 5; j++) {
// const left = (j * 120) + 20;
// pdf.addImage(base64, 'JPEG', left, i * 150, 20, 30);
// };
// };
pdf.addImage(pageData, "JPEG", 0, i * 150, 20, 30);
setTimeout(createImpl, 500, canvas);
} else {
pdfSave();
}
}
}
//当内容未超过pdf一页显示的范围无需分页
if (leftHeight < a4HeightRef) {
pdf.addImage(
pageData,
"JPEG",
0,
0,
a4Width,
(a4Width / canvas.width) * leftHeight
);
pdfSave();
} else {
try {
pdf.deletePage(0);
setTimeout(createImpl, 500, canvas);
} catch (err) {
console.log(err);
}
}
function pdfSave() {
// pdf文件生成完毕 自动下载到客户本地
pdf.save(pdfFileName + ".pdf");
loadingInstance.close();
}
});
}, 500);
};
export default {
name: "Detail",
props: { props: {
instance: Function, instance: Function,
dict: Object, dict: Object,
params: Object params: Object,
}, },
data () { data() {
return { return {
isLoading: false, isLoading: false,
tabIndex: 1, tabIndex: 1,
searchName: '', searchName: "",
time: [], time: [],
search: { search: {
name: '' name: "",
}, },
current: 1, current: 1,
total: 0, total: 0,
@@ -193,75 +465,81 @@
msgList: [], msgList: [],
msgType: 0, msgType: 0,
msgTypeList: [ msgTypeList: [
{name: '全部', value: ''}, { name: "全部", value: "" },
{name: '图片/视频', value: 'imagevideo'}, { name: "图片/视频", value: "imagevideo" },
{name: '语音', value: 'voice'}, { name: "语音", value: "voice" },
{name: '文件', value: 'file'}, { name: "文件", value: "file" },
], ],
searchMsg: '', searchMsg: "",
id: '' id: "",
} };
}, },
computed: { computed: {
...mapState(['user']), ...mapState(["user"]),
}, },
created () { created() {
this.isLoading = true this.isLoading = true;
if (this.params && this.params.id) { if (this.params && this.params.id) {
this.id = this.params.id this.id = this.params.id;
this.getList() this.getList();
} }
// this.getList() // this.getList()
}, },
methods: { methods: {
msgScroll(e) { msgScroll(e) {
if(e.target.scrollTop == 0) { if (e.target.scrollTop == 0) {
if(this.msgCurrent > this.msgPages) { if (this.msgCurrent > this.msgPages) {
return this.$message('已加载完成,没有更多数据'); return this.$message("已加载完成,没有更多数据");
}else { } else {
this.msgCurrent ++ this.msgCurrent++;
this.isLoading = true this.isLoading = true;
this.getMsgList() this.getMsgList();
} }
} }
}, },
getListInit() { getListInit() {
this.isLoading = true this.isLoading = true;
this.current = 1 this.current = 1;
this.leftActiveIndex = 0 this.leftActiveIndex = 0;
this.getList() this.getList();
}, },
getList () { getList() {
this.instance.post(`/app/appsessionarchiveindex/list`, null, { this.instance
.post(`/app/appsessionarchiveindex/list`, null, {
params: { params: {
userId: this.id, userId: this.id,
type: this.tabIndex, type: this.tabIndex,
size: 20, size: 20,
current: this.current, current: this.current,
toUserName: this.tabIndex == 1 ? '' : this.searchName, toUserName: this.tabIndex == 1 ? "" : this.searchName,
roomName: this.tabIndex == 1 ? this.searchName : '', roomName: this.tabIndex == 1 ? this.searchName : "",
} },
}).then(res => {
if (res.code === 0) {
this.list = res.data.records
this.total = res.data.total || 0
this.getMsgListInit()
}
this.isLoading = false
}).catch(() => {
this.isLoading = false
}) })
.then((res) => {
if (res.code === 0) {
this.list = res.data.records;
this.total = res.data.total || 0;
this.getMsgListInit();
}
this.isLoading = false;
})
.catch(() => {
this.isLoading = false;
});
}, },
getMsgListInit() { getMsgListInit() {
this.isLoading = true this.isLoading = true;
this.msgCurrent = 1 this.msgCurrent = 1;
this.msgPages = 2 this.msgPages = 2;
this.getMsgList() this.getMsgList();
}, },
getMsgList() { getMsgList() {
var preveHeight = document.querySelector('.content-right-info').scrollHeight var preveHeight = document.querySelector(
this.instance.post(`/app/appsessionarchiveinfo/list`, null, { ".content-right-info"
).scrollHeight;
this.instance
.post(`/app/appsessionarchiveinfo/list`, null, {
params: { params: {
userId: this.id, userId: this.id,
type: this.tabIndex, type: this.tabIndex,
@@ -269,43 +547,91 @@
current: this.msgCurrent, current: this.msgCurrent,
msgType: this.msgTypeList[this.msgType].value, msgType: this.msgTypeList[this.msgType].value,
// msgType: 'file', // msgType: 'file',
toUserId: this.list[this.leftActiveIndex].type == 1 ? '' : this.list[this.leftActiveIndex].toUserId, toUserId:
roomId: this.list[this.leftActiveIndex].type == 1 ? this.list[this.leftActiveIndex].roomId : '', this.list[this.leftActiveIndex].type == 1
? ""
: this.list[this.leftActiveIndex].toUserId,
roomId:
this.list[this.leftActiveIndex].type == 1
? this.list[this.leftActiveIndex].roomId
: "",
content: this.searchMsg, content: this.searchMsg,
startTime: this.time ? this.time[0] : '', startTime: this.time ? this.time[0] : "",
endTime: this.time ? this.time[1] : '' endTime: this.time ? this.time[1] : "",
} },
}).then(res => { })
.then((res) => {
if (res.code === 0) { if (res.code === 0) {
res.data.records.map((item, index) => { res.data.records.map((item, index) => {
if(item.msgType == 'file') { if (item.msgType == "file") {
item.files = [{url: item.sdkFileUrl, accessUrl: item.sdkFileUrl, name: item.sdkFileName, fileSizeStr: item.fileSizeStr}] item.files = [
{
url: item.sdkFileUrl,
accessUrl: item.sdkFileUrl,
name: item.sdkFileName,
fileSizeStr: item.fileSizeStr,
},
];
} }
if(item.msgType == 'location') { if (item.msgType == "location") {
this.initMap(item.lng, item.lat, item.zoom, index) this.initMap(item.lng, item.lat, item.zoom, index);
} }
}) if (item.msgType == "image") {
this.msgList = this.msgCurrent > 1 ? [ ...res.data.records, ...this.msgList]: res.data.records item.src = this.getBase64Image(item.sdkFileUrl);
this.msgPages = res.data.pages || 2 console.log(item.src);
}
});
this.msgList =
this.msgCurrent > 1
? [...res.data.records, ...this.msgList]
: res.data.records;
this.msgPages = res.data.pages || 2;
this.$nextTick(() => { this.$nextTick(() => {
if(this.msgCurrent == 1) { if (this.msgCurrent == 1) {
document.querySelector('.content-right-info').scrollTo(0, 999999999) document
}else { .querySelector(".content-right-info")
var height = document.querySelector('.content-right-info').scrollHeight - preveHeight .scrollTo(0, 999999999);
document.querySelector('.content-right-info').scrollTop = height } else {
var height =
document.querySelector(".content-right-info").scrollHeight -
preveHeight;
document.querySelector(".content-right-info").scrollTop =
height;
} }
});
})
} }
this.isLoading = false this.isLoading = false;
}).catch(() => {
this.isLoading = false
}) })
.catch(() => {
this.isLoading = false;
});
},
getBase64Image(url) {
function newImg(url) {
return new Promise((resolve, reject) => {
let image = new Image();
image.crossOrigin = "Anonymous";
image.src = url;
image.onload = function () {
resolve(image);
};
});
}
return newImg(url).then((img) => {
let canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
let ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, img.width, img.height);
let ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
let dataURL = canvas.toDataURL("image/" + ext);
return dataURL;
});
}, },
initMap(lng, lat, zoom, index) { initMap(lng, lat, zoom, index) {
AMapLoader.load({ AMapLoader.load({
key: '54a02a43d9828a8f9cd4f26fe281e74e', key: "54a02a43d9828a8f9cd4f26fe281e74e",
version: '2.0', version: "2.0",
}).then((AMap) => { }).then((AMap) => {
this.map = new AMap.Map(`map${index}`, { this.map = new AMap.Map(`map${index}`, {
resizeEnable: true, resizeEnable: true,
@@ -313,54 +639,62 @@
center: [lng, lat], center: [lng, lat],
zoom: zoom, zoom: zoom,
scrollWheel: false, scrollWheel: false,
}) });
}) });
}, },
typeClick(index) { typeClick(index) {
this.tabIndex = index this.tabIndex = index;
this.msgType = 0 this.msgType = 0;
this.getListInit() this.getListInit();
}, },
onChange() { onChange() {
this.getMsgListInit() this.getMsgListInit();
}, },
currentChange(e) { currentChange(e) {
this.current = e this.current = e;
this.getList() this.getList();
}, },
msgCurrentChange(e) { msgCurrentChange(e) {
this.msgCurrent = e this.msgCurrent = e;
this.getMsgList() this.getMsgList();
}, },
leftClick(index) { leftClick(index) {
this.leftActiveIndex = index this.leftActiveIndex = index;
this.msgType = 0 this.msgType = 0;
this.getMsgListInit() this.getMsgListInit();
}, },
msgTypeClick(index) { msgTypeClick(index) {
this.msgType = index this.msgType = index;
this.getMsgListInit() this.getMsgListInit();
}, },
openLink(row) { openLink(row) {
document.write('<a href="" target="new"></a>'); document.write('<a href="" target="new"></a>');
window.open(row.linkUrl, "new"); window.open(row.linkUrl, "new");
}, },
cancel () { handleExport() {
this.$emit('change', { this.loading = true;
type: 'List', var fileName =
isRefresh: true this.tabIndex == 1
}) ? this.list[this.leftActiveIndex].roomName
} : this.list[this.leftActiveIndex].toUserName;
} PdfLoader(this.$refs.pdf, fileName);
} },
cancel() {
this.$emit("change", {
type: "List",
isRefresh: true,
});
},
},
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.detail { .detail {
.addressBook-left { .addressBook-left {
width: 300px; width: 300px;
height: auto; height: auto;
background: #FAFAFB; background: #fafafb;
position: relative; position: relative;
.addressBook-left__title { .addressBook-left__title {
@@ -381,8 +715,8 @@
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
&.tab-active { &.tab-active {
color: #2266FF; color: #2266ff;
border-bottom: 2px solid #2266FF; border-bottom: 2px solid #2266ff;
} }
} }
} }
@@ -433,19 +767,19 @@
border-radius: 4px; border-radius: 4px;
} }
.type1 { .type1 {
background-color: #EAF4FF; background-color: #eaf4ff;
color: #267EF0; color: #267ef0;
} }
.type2 { .type2 {
background-color: #FDEEE1; background-color: #fdeee1;
color: #FB7D29; color: #fb7d29;
} }
} }
.addressBook-left__list--item:hover { .addressBook-left__list--item:hover {
background-color: #E8EFFF; background-color: #e8efff;
} }
.active { .active {
background-color: #E8EFFF; background-color: #e8efff;
color: #26f; color: #26f;
} }
} }
@@ -457,7 +791,7 @@
.addressBook-left__list--search { .addressBook-left__list--search {
flex: 1; flex: 1;
:deep( input ){ :deep(input) {
width: 100%; width: 100%;
} }
} }
@@ -477,7 +811,7 @@
} }
} }
:deep( .ai-list__content--right ){ :deep(.ai-list__content--right) {
flex: 1; flex: 1;
min-width: 0; min-width: 0;
margin-left: 1px; margin-left: 1px;
@@ -486,7 +820,7 @@
.ai-list__content--right-wrapper { .ai-list__content--right-wrapper {
width: 100%; width: 100%;
position: relative; position: relative;
padding: 0!important; padding: 0 !important;
height: 100%; height: 100%;
} }
} }
@@ -515,8 +849,8 @@
border-bottom: 2px solid transparent; border-bottom: 2px solid transparent;
&.tab-active { &.tab-active {
color: #2266FF; color: #2266ff;
border-bottom: 2px solid #2266FF; border-bottom: 2px solid #2266ff;
} }
} }
} }
@@ -548,7 +882,7 @@
color: #666; color: #666;
font-size: 12px; font-size: 12px;
line-height: 20px; line-height: 20px;
padding:0 0 0 64px; padding: 0 0 0 64px;
span { span {
display: inline-block; display: inline-block;
margin: 0 12px; margin: 0 12px;
@@ -605,7 +939,7 @@
line-height: 44px; line-height: 44px;
padding: 0 6px; padding: 0 6px;
border-radius: 4px; border-radius: 4px;
background-color: #EEE; background-color: #eee;
color: #999; color: #999;
margin-top: 4px; margin-top: 4px;
} }
@@ -672,7 +1006,7 @@
left: 0; left: 0;
z-index: 9999; z-index: 9999;
width: 600px; width: 600px;
background-color: rgba(0, 0, 0, .7); background-color: rgba(0, 0, 0, 0.7);
p { p {
color: #fff; color: #fff;
line-height: 34px; line-height: 34px;
@@ -699,7 +1033,6 @@
margin-left: 16px; margin-left: 16px;
} }
} }
} }
.item-right { .item-right {
width: 100%; width: 100%;
@@ -716,7 +1049,7 @@
.content { .content {
background-color: #90e287; background-color: #90e287;
span { span {
border-left-color:#90e287; border-left-color: #90e287;
right: -18px; right: -18px;
} }
} }
@@ -727,7 +1060,6 @@
text-align: right; text-align: right;
} }
} }
} }
} }
.addressBook-left__list--wrapper::-webkit-scrollbar, .addressBook-left__list--wrapper::-webkit-scrollbar,
@@ -737,12 +1069,12 @@
.addressBook-left__list--wrapper::-webkit-scrollbar-thumb, .addressBook-left__list--wrapper::-webkit-scrollbar-thumb,
.content-right-info::-webkit-scrollbar-thumb { .content-right-info::-webkit-scrollbar-thumb {
border-radius: 10px; border-radius: 10px;
background: rgba(0,0,0,0.2); background: rgba(0, 0, 0, 0.2);
} }
.addressBook-left__list--wrapper::-webkit-scrollbar-track .addressBook-left__list--wrapper::-webkit-scrollbar-track
.content-right-info::-webkit-scrollbar-track { .content-right-info::-webkit-scrollbar-track {
border-radius: 0; border-radius: 0;
background: rgba(0,0,0,0.1); background: rgba(0, 0, 0, 0.1);
} }
.msg-list-pagination { .msg-list-pagination {
position: absolute; position: absolute;
@@ -750,6 +1082,5 @@
width: calc(100% - 32px); width: calc(100% - 32px);
text-align: center; text-align: center;
} }
} }
</style> </style>