|
@@ -1,33 +1,72 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="chat-body-components" v-if="!isChat">
|
|
|
|
|
|
|
+ <div class="chat-body-components" v-if="!chatData">
|
|
|
<div class="empty-data">
|
|
<div class="empty-data">
|
|
|
<img src="https://lf-govern-cdn-tos.bytedcdn.com/obj/life-governance-cdn/life-im-pc/static/image/empty.efbaf24f.png" />
|
|
<img src="https://lf-govern-cdn-tos.bytedcdn.com/obj/life-governance-cdn/life-im-pc/static/image/empty.efbaf24f.png" />
|
|
|
请从左侧列表选择一个用户开始聊天
|
|
请从左侧列表选择一个用户开始聊天
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="chat-body-components" v-else>
|
|
|
|
|
|
|
+ <div class="chat-body-components" @paste="handlePaste" v-else>
|
|
|
<div class="custom-info">
|
|
<div class="custom-info">
|
|
|
- <div class="name">与xxx对话中</div>
|
|
|
|
|
|
|
+ <div class="name">与{{chatData.user.nickname}}对话中</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="chat-content">
|
|
|
|
|
|
|
+ <div class="chat-content" ref="messageListRef" @scroll="handleScroll">
|
|
|
<div class="chat-content-body">
|
|
<div class="chat-content-body">
|
|
|
- <div class="chat-loading-sk" v-for="(item,indx) in 20" v-if="userLoading">
|
|
|
|
|
- <el-skeleton :animated="true">
|
|
|
|
|
- <template #template>
|
|
|
|
|
- <div class="chat-sk-flex">
|
|
|
|
|
- <el-skeleton-item variant="image" style="width: 40px; height: 40px" />
|
|
|
|
|
- <div class="sk-right">
|
|
|
|
|
- <el-skeleton-item variant="h3" style="width: 50%" />
|
|
|
|
|
- <div style="margin-top: 5px;">
|
|
|
|
|
- <el-skeleton-item variant="text" style="width: 30%;" />
|
|
|
|
|
|
|
+ <div class="scroll-div-msg" style="padding: 10px;">
|
|
|
|
|
+ <div class="text_26 color__ msg-time" v-if="history.loading">加载中...</div>
|
|
|
|
|
+ <div class="text_26 color__ msg-time" v-if="history.end">没有更多啦~</div>
|
|
|
|
|
+ <template v-for="(item, index) in history.messages">
|
|
|
|
|
+ <div class="msgContent" :id="'msg-' + item.messageId">
|
|
|
|
|
+ <div class="text_26 color__ msg-time">
|
|
|
|
|
+ {{ renderMessageDate(item, index) }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="message" :class="[item.userType]">
|
|
|
|
|
+ <div v-if="item.userType === 'friend'">
|
|
|
|
|
+ <el-image style="width: 40px;height: 40px;" :src="chatData.user.avatar" class="avatar" mode="widthFix"></el-image>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="message-con">
|
|
|
|
|
+ <div v-if="item.msgType == 'text'">
|
|
|
|
|
+ <div v-html="renderTextMessage(item.content)"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="m-address" v-if="item.msgType == 'address'">
|
|
|
|
|
+ <div class="image"><el-image style="width: 40px;" :src="item.content.img" mode="widthFix"></el-image></div>
|
|
|
|
|
+ <div class="infos">
|
|
|
|
|
+ <div class="title">{{item.content.name}}</div>
|
|
|
|
|
+ <div class="desc">{{item.content.order?item.content.order:'-'}}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="m-address" v-if="item.msgType == 'order'">
|
|
|
|
|
+ <div class="image"><el-image style="width: 40px;" :src="item.content.img" mode="widthFix"></el-image></div>
|
|
|
|
|
+ <div class="infos">
|
|
|
|
|
+ <div class="title">{{item.content.name}}</div>
|
|
|
|
|
+ <div class="desc">{{item.content.order?item.content.order:'-'}}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="m-image" v-if="item.msgType == 'image'">
|
|
|
|
|
+ <el-image style="width: 100px;height: 100px;" :src="item.content" :preview-src-list="[item.content]" :preview-teleported="true"></el-image>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="m-address" v-if="item.msgType == 'pay'">
|
|
|
|
|
+ <div class="image"><el-image style="width: 40px;" :src="item.content.img" mode="widthFix"></el-image></div>
|
|
|
|
|
+ <div class="infos">
|
|
|
|
|
+ <div class="title">{{item.content.name}}</div>
|
|
|
|
|
+ <div class="price">¥ {{item.content.price?item.content.price:'-'}}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="" v-if="item.msgType == 'quick'">
|
|
|
|
|
+ <div class="quickCon">{{item.content}}</div>
|
|
|
|
|
+ <div class="quickList" v-if="item.list.length > 0">
|
|
|
|
|
+ <div class="list-items" v-for="(qItem,indx) in item.list" @click="quickQs(qItem)">
|
|
|
|
|
+ {{qItem.question}}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <div class="nickname" v-if="item.userType === 'self'">{{ item.selfName }}</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="time">
|
|
|
|
|
- <el-skeleton-item variant="text" />
|
|
|
|
|
|
|
+ <div v-if="item.userType === 'self'">
|
|
|
|
|
+ <el-image style="width: 40px;height: 40px;" :src="item.avatar" class="avatar" mode="widthFix"></el-image>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </template>
|
|
|
|
|
- </el-skeleton>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
@@ -65,7 +104,7 @@
|
|
|
</el-popover>
|
|
</el-popover>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- <div class="send-body">
|
|
|
|
|
|
|
+ <div class="send-body" @dragenter="handleDragEnter" @dragover="handleDragOver" @dragleave="handleDragLeave" @drop="handleDrop">
|
|
|
<el-input
|
|
<el-input
|
|
|
type="textarea"
|
|
type="textarea"
|
|
|
:rows="5"
|
|
:rows="5"
|
|
@@ -82,22 +121,393 @@
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ <el-dialog v-model="previewDialogVisible" title="图片预览" width="500px" destroy-on-close :close-on-click-modal="false">
|
|
|
|
|
+ <div class="preview-image-wrapper">
|
|
|
|
|
+ <el-image :src="previewImageUrl" :preview-src-list="[previewImageUrl]" :preview-teleported="true"></el-image>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <el-button @click="previewDialogVisible=false" >取 消</el-button>
|
|
|
|
|
+ <el-button type="primary" :loading="uploading" @click="confirmUpload">确定发送</el-button>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </el-dialog>
|
|
|
|
|
+ <el-upload ref="uploadRef" action="" :auto-upload="false" :file-list="fileList" :on-change="handleFileChange" :http-request="customUpload" style="display: none" />
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
|
|
+import {
|
|
|
|
|
+ mapState
|
|
|
|
|
+} from 'vuex';
|
|
|
|
|
+import {
|
|
|
|
|
+ show,
|
|
|
|
|
+ formatDate,
|
|
|
|
|
+ throttle,
|
|
|
|
|
+ openimg,
|
|
|
|
|
+ getLocation,
|
|
|
|
|
+ to as tofn
|
|
|
|
|
+} from '@/util/index.js';
|
|
|
|
|
+import { EmojiDecoder, emojiMap } from '@/util/EmojiDecoder.js';
|
|
|
|
|
+const emojiUrl = 'https://imgcache.qq.com/open/qcloud/tim/assets/emoji/';
|
|
|
|
|
+const decoder = new EmojiDecoder(emojiUrl, emojiMap);
|
|
|
|
|
+import { Push } from "@/utils/push";
|
|
|
export default {
|
|
export default {
|
|
|
data () {
|
|
data () {
|
|
|
return {
|
|
return {
|
|
|
userLoading:true,
|
|
userLoading:true,
|
|
|
inputMessage:"",
|
|
inputMessage:"",
|
|
|
- isChat:false
|
|
|
|
|
|
|
+ isChat:false,
|
|
|
|
|
+ emojiList:[],
|
|
|
|
|
+ history: {
|
|
|
|
|
+ messages: [],
|
|
|
|
|
+ allLoaded: false,
|
|
|
|
|
+ size:15,
|
|
|
|
|
+ page:1,
|
|
|
|
|
+ loading:false,
|
|
|
|
|
+ end:false
|
|
|
|
|
+ },
|
|
|
|
|
+ avatar:{
|
|
|
|
|
+ self:"",
|
|
|
|
|
+ friend:""
|
|
|
|
|
+ },
|
|
|
|
|
+ previewDialogVisible: false, // 预览弹窗显示状态
|
|
|
|
|
+ previewImageUrl: '', // 预览图片临时 URL
|
|
|
|
|
+ pendingFile: null, // 待上传的文件对象(Blob)
|
|
|
|
|
+ uploading: false, // 上传中状态
|
|
|
|
|
+ fileList: [], // el-upload 的文件列表
|
|
|
|
|
+ isDragover:false,
|
|
|
|
|
+ uploadImg:"",
|
|
|
|
|
+ pushData: {
|
|
|
|
|
+ obj:null,
|
|
|
|
|
+ auth:"http://192.168.3.16:9881/plugin/webman/push/auth",
|
|
|
|
|
+ url: 'ws://192.168.3.16:3131',
|
|
|
|
|
+ },
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
|
|
+ computed: mapState({
|
|
|
|
|
+ //显示时间
|
|
|
|
|
+ renderMessageDate() {
|
|
|
|
|
+ return (message, index) => {
|
|
|
|
|
+ if (this.history.messages[index + 1]?.timestamp - message.timestamp > 3 * 60 * 1000) {
|
|
|
|
|
+ return formatDate(message.timestamp, 'timestamp');
|
|
|
|
|
+ }
|
|
|
|
|
+ return '';
|
|
|
|
|
+ };
|
|
|
|
|
+ },
|
|
|
|
|
+ }),
|
|
|
props: {
|
|
props: {
|
|
|
- userInfo: { type: Object, default: () => {} }
|
|
|
|
|
|
|
+ userInfo: { type: Object, default: () => {} },
|
|
|
|
|
+ chatData: { type: Object, default: () => {} }
|
|
|
|
|
+ },
|
|
|
|
|
+ created(){
|
|
|
|
|
+ this.initPush();
|
|
|
|
|
+ },
|
|
|
|
|
+ watch: {
|
|
|
|
|
+ chatData: {
|
|
|
|
|
+ handler(newVal) {
|
|
|
|
|
+ if (newVal) {
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ this.$refs.messageListRef?.addEventListener('scroll', this.handleScroll)
|
|
|
|
|
+ })
|
|
|
|
|
+ this.history.messages = [];
|
|
|
|
|
+ this.history.page = 1;
|
|
|
|
|
+ this.history.allLoaded = false;
|
|
|
|
|
+ this.getMessage()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ immediate: true
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ beforeDestroy() {
|
|
|
|
|
+ this.$refs.messageListRef.removeEventListener('scroll', this.handleScroll)
|
|
|
},
|
|
},
|
|
|
methods: {
|
|
methods: {
|
|
|
|
|
+ initPush(){
|
|
|
|
|
+ var _this = this;
|
|
|
|
|
+ if (!this.pushData.obj) {
|
|
|
|
|
+ this.pushData.obj = new Push({
|
|
|
|
|
+ "url":this.pushData.url,
|
|
|
|
|
+ "app_key":"265c33b73d5c04f918978577df2c48d2",
|
|
|
|
|
+ "auth":this.pushData.auth
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+ var user_channel = this.pushData.obj.subscribe('service-' + this.userInfo.userId);
|
|
|
|
|
+ user_channel.on('message', function (data) {
|
|
|
|
|
+ _this.playAudio()
|
|
|
|
|
+ setTimeout(() => {
|
|
|
|
|
+ _this.$emit("pushNew",data)
|
|
|
|
|
+ }, 200);
|
|
|
|
|
+ if (_this.chatData && _this.chatData.openid == data.openid) { // 当前窗口实时推送
|
|
|
|
|
+ // console.log("推送消息",data);
|
|
|
|
|
+ const pushData = _this.formatMsg(data);
|
|
|
|
|
+ _this.history.messages.push(pushData);
|
|
|
|
|
+ _this.scrollToBottom()
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ playAudio(){
|
|
|
|
|
+ const audio = new Audio('/img/msn.mp3');
|
|
|
|
|
+ audio.play().catch(error => {
|
|
|
|
|
+ console.error("播放音频时出错:", error);
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ handleScroll(e){
|
|
|
|
|
+ const { scrollTop, clientHeight, scrollHeight } = e.target
|
|
|
|
|
+ const isBottom = scrollTop + clientHeight >= scrollHeight - 5 // 5px 缓冲区
|
|
|
|
|
+ const isTop = scrollTop <= 5
|
|
|
|
|
+ // console.log("滚动",this.history.allLoaded)
|
|
|
|
|
+ if (isBottom && !this.history.allLoaded) {
|
|
|
|
|
+ // console.log('到达底部')
|
|
|
|
|
+ // 加载更多等操作
|
|
|
|
|
+ }
|
|
|
|
|
+ if (isTop && !this.history.allLoaded) {
|
|
|
|
|
+ this.getMessage()
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ async uploadSuccess(data){
|
|
|
|
|
+ var resp = await this.sendMessageApi("image",data.data.src);
|
|
|
|
|
+ if (!resp) {
|
|
|
|
|
+ return ;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.sendMessageData(data.data.src,"image");
|
|
|
|
|
+ this.uploadImg = "";
|
|
|
|
|
+ },
|
|
|
|
|
+ // ---------- 2. 拖拽事件 ----------
|
|
|
|
|
+ handleDragEnter(event) {
|
|
|
|
|
+ event.preventDefault(); // 必须阻止,才能让 drop 触发
|
|
|
|
|
+ this.isDragover = true;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleDragOver(event) {
|
|
|
|
|
+ event.preventDefault(); // 必须阻止,允许放置
|
|
|
|
|
+ this.isDragover = true;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ handleDragLeave(event) {
|
|
|
|
|
+ // 注意:dragleave 可能因子元素触发,需检查相关目标
|
|
|
|
|
+ if (!this.$el.contains(event.relatedTarget)) {
|
|
|
|
|
+ this.isDragover = false;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
|
|
|
|
|
+ handleDrop(event) {
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ this.isDragover = false;
|
|
|
|
|
+
|
|
|
|
|
+ const files = event.dataTransfer.files;
|
|
|
|
|
+ if (files.length > 0) {
|
|
|
|
|
+ const file = files[0];
|
|
|
|
|
+ if (file.type.startsWith('image/')) {
|
|
|
|
|
+ this.showPreview(file);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.$message.warning('请拖拽图片文件');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ showPreview(file) {
|
|
|
|
|
+ const url = URL.createObjectURL(file);
|
|
|
|
|
+ this.pendingFile = file;
|
|
|
|
|
+ this.previewImageUrl = url;
|
|
|
|
|
+ this.previewDialogVisible = true;
|
|
|
|
|
+ },
|
|
|
|
|
+ handlePaste(event){
|
|
|
|
|
+ const items = event.clipboardData?.items;
|
|
|
|
|
+ if (!items) return;
|
|
|
|
|
+ for (const item of items) {
|
|
|
|
|
+ if (item.type.startsWith('image/')) {
|
|
|
|
|
+ event.preventDefault(); // 阻止默认粘贴行为
|
|
|
|
|
+
|
|
|
|
|
+ const file = item.getAsFile();
|
|
|
|
|
+ if (file) {
|
|
|
|
|
+ // 生成临时预览 URL
|
|
|
|
|
+ // const url = URL.createObjectURL(file);
|
|
|
|
|
+ this.showPreview(file)
|
|
|
|
|
+ }
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ async confirmUpload() {
|
|
|
|
|
+ if (!this.pendingFile) {
|
|
|
|
|
+ this.$message.warning('没有待上传的文件');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.uploading = true;
|
|
|
|
|
+ const formData = new FormData();
|
|
|
|
|
+ formData.append('file', this.pendingFile);
|
|
|
|
|
+ var resp = await this.$API.common.upload.post(formData)
|
|
|
|
|
+ if(resp.code !== 200) {
|
|
|
|
|
+ this.$message.error('发送失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ var send = await this.sendMessageApi("image",resp.data.src);
|
|
|
|
|
+ if (!send) {
|
|
|
|
|
+ return ;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.sendMessageData(resp.data.src,"image");
|
|
|
|
|
+ this.uploading = false;
|
|
|
|
|
+ this.previewDialogVisible = false;
|
|
|
|
|
+ this.clearFile();
|
|
|
|
|
+ },
|
|
|
|
|
+ clearFile() {
|
|
|
|
|
+ if (this.previewImageUrl) {
|
|
|
|
|
+ URL.revokeObjectURL(this.previewImageUrl);
|
|
|
|
|
+ }
|
|
|
|
|
+ this.pendingFile = null;
|
|
|
|
|
+ this.previewImageUrl = '';
|
|
|
|
|
+ this.fileList = [];
|
|
|
|
|
+ },
|
|
|
|
|
+ handleKeyCode(event){
|
|
|
|
|
+ if (event.keyCode == 13) {
|
|
|
|
|
+ if (!event.ctrlKey) {
|
|
|
|
|
+ this.sendMessage()
|
|
|
|
|
+ event.preventDefault();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.inputMessage += "\n";
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ return ;
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ sendMessageData(content,type){
|
|
|
|
|
+ const message = {
|
|
|
|
|
+ content: content,
|
|
|
|
|
+ userType: 'self',
|
|
|
|
|
+ avatar: this.userInfo.logo,
|
|
|
|
|
+ selfName: this.userInfo.truename,
|
|
|
|
|
+ friendName: '-',
|
|
|
|
|
+ msgType:type,
|
|
|
|
|
+ list:[],
|
|
|
|
|
+ messageId: Date.now(),
|
|
|
|
|
+ timestamp: Date.now(),
|
|
|
|
|
+ };
|
|
|
|
|
+ this.history.messages.push(message);
|
|
|
|
|
+ this.scrollToBottom()
|
|
|
|
|
+ },
|
|
|
|
|
+ async sendMessage(){
|
|
|
|
|
+ if (!this.inputMessage) return ;
|
|
|
|
|
+ var resp = await this.sendMessageApi("text",this.inputMessage);
|
|
|
|
|
+ if (!resp) {
|
|
|
|
|
+ this.inputMessage = "";
|
|
|
|
|
+ return ;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.sendMessageData(this.inputMessage,"text");
|
|
|
|
|
+ this.inputMessage = ""
|
|
|
|
|
+ },
|
|
|
|
|
+ renderTextMessage(text) {
|
|
|
|
|
+ if (!text) return "";
|
|
|
|
|
+ text = text.replace(/\n/g, '');
|
|
|
|
|
+ if (!text) return '<span>' + '[未知内容]' + '</span>';
|
|
|
|
|
+ return '<span>' + decoder.decode(text) + '</span>';
|
|
|
|
|
+ },
|
|
|
|
|
+ getMessage(){
|
|
|
|
|
+ let get = async ()=>{
|
|
|
|
|
+ this.history.allLoaded = true;
|
|
|
|
|
+ this.history.end = false;
|
|
|
|
|
+ this.history.loading = true;
|
|
|
|
|
+ var resp = await this.$API.serChat.msg.get({"page":this.history.page,"size":this.history.size,"openid":this.chatData.openid})
|
|
|
|
|
+ this.history.loading = false;
|
|
|
|
|
+ if (this.history.size > resp.data.rows.length) {
|
|
|
|
|
+ this.history.end = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ if (resp.data.rows.length > 0) {
|
|
|
|
|
+ var msgData = resp.data.rows;
|
|
|
|
|
+ var pushData = [];
|
|
|
|
|
+ if (msgData.length > 0) {
|
|
|
|
|
+ msgData.forEach(item => pushData.push(this.formatMsg(item)))
|
|
|
|
|
+ }
|
|
|
|
|
+ this.history.messages = [...pushData,...this.history.messages];
|
|
|
|
|
+ if (this.history.page == 1) {
|
|
|
|
|
+ this.scrollToBottom();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ const container = this.$refs.messageListRef;
|
|
|
|
|
+ const oldScrollHeight = container.scrollHeight;
|
|
|
|
|
+ const oldScrollTop = container.scrollTop;
|
|
|
|
|
+ const newScrollHeight = container.scrollHeight;
|
|
|
|
|
+ const addedHeight = newScrollHeight - oldScrollHeight;
|
|
|
|
|
+ container.scrollTop = oldScrollTop + addedHeight;
|
|
|
|
|
+ }
|
|
|
|
|
+ // this.history.messages = this.history.messages.concat(pushData);
|
|
|
|
|
+ this.history.page ++;
|
|
|
|
|
+ this.history.allLoaded = false;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ this.history.allLoaded = true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ get();
|
|
|
|
|
+ },
|
|
|
|
|
+ scrollToBottom() {
|
|
|
|
|
+ this.$nextTick(() => {
|
|
|
|
|
+ const container = this.$refs.messageListRef;
|
|
|
|
|
+ if (container) {
|
|
|
|
|
+ container.scrollTop = container.scrollHeight;
|
|
|
|
|
+ }
|
|
|
|
|
+ });
|
|
|
|
|
+ },
|
|
|
|
|
+ formatMsg(item){
|
|
|
|
|
+ var payload = "";
|
|
|
|
|
+ if (item.type == 'text') {
|
|
|
|
|
+ payload = item.content
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.type == 'order') {
|
|
|
|
|
+ var order = JSON.parse(item.content)
|
|
|
|
|
+ payload = order
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.type == 'pay') {
|
|
|
|
|
+ var order = JSON.parse(item.content)
|
|
|
|
|
+ payload = order
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.type == 'address') {
|
|
|
|
|
+ var order = JSON.parse(item.content)
|
|
|
|
|
+ payload = order
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.type == 'image') {
|
|
|
|
|
+ payload = item.content
|
|
|
|
|
+ }
|
|
|
|
|
+ const message = {
|
|
|
|
|
+ content: payload,
|
|
|
|
|
+ selfName: item.nick?item.nick.truename:'-',
|
|
|
|
|
+ friendName: item.user.nickname,
|
|
|
|
|
+ userType: item.userType,
|
|
|
|
|
+ avatar: item.avatar,
|
|
|
|
|
+ msgType:item.type,
|
|
|
|
|
+ list:[],
|
|
|
|
|
+ messageId: item.msgId,
|
|
|
|
|
+ timestamp: item.time,
|
|
|
|
|
+ };
|
|
|
|
|
+ return message
|
|
|
|
|
+ },
|
|
|
|
|
+ async sendMessageApi(type,content){
|
|
|
|
|
+ var resp = await this.$API.serChat.text.post({type:type,content: content,groupId: this.chatData.poi_id,openid:this.chatData.openid})
|
|
|
|
|
+ if (resp.code !== 1){
|
|
|
|
|
+ this.$message.error(resp.msg)
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ this.$emit("success",{'type':type,'content':content,'time':resp.data.time})
|
|
|
|
|
+ // this.chatContact[this.checkChat].last.type = type;
|
|
|
|
|
+ // this.chatContact[this.checkChat].last.content = (type=='text'?this.truncateByBytes(content,18):content);
|
|
|
|
|
+ // this.chatContact[this.checkChat].last.create_at = resp.data.time;
|
|
|
|
|
+ return true;
|
|
|
|
|
+ },
|
|
|
|
|
+ initEmoji() {
|
|
|
|
|
+ let newList = [];
|
|
|
|
|
+ let i = 1;
|
|
|
|
|
+ for (let key in emojiMap) {
|
|
|
|
|
+ let obj = {};
|
|
|
|
|
+ obj['index'] = i;
|
|
|
|
|
+ obj['text'] = key;
|
|
|
|
|
+ obj['image'] = emojiMap[key];
|
|
|
|
|
+ i++;
|
|
|
|
|
+ newList.push(obj);
|
|
|
|
|
+ }
|
|
|
|
|
+ var list = [];
|
|
|
|
|
+ let A = newList.filter((item, index) => {
|
|
|
|
|
+ return (index + 1) % 8 === 0;
|
|
|
|
|
+ });
|
|
|
|
|
+ A.unshift({ index: 0 });
|
|
|
|
|
+ A.push({ index: newList.length });
|
|
|
|
|
+ this.emojiList = A.map((item, index) => {
|
|
|
|
|
+ return newList.slice(A[index].index, A[index + 1]?.index || newList.length);
|
|
|
|
|
+ });
|
|
|
|
|
+ // this.scroll({ detail: { scrollTop: 10 } });
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
</script>
|
|
</script>
|
|
@@ -111,6 +521,13 @@ export default {
|
|
|
width: 170px;
|
|
width: 170px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+.msg-time {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+ color: #a3a3a3;
|
|
|
|
|
+ line-height: 100rpx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ font-size: 24rpx;
|
|
|
|
|
+}
|
|
|
.chat-body-components{
|
|
.chat-body-components{
|
|
|
background-color: #f8f8f8;
|
|
background-color: #f8f8f8;
|
|
|
display: flex;
|
|
display: flex;
|
|
@@ -164,4 +581,96 @@ export default {
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+.scroll-view-msg{
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column-reverse;
|
|
|
|
|
+ flex-wrap: nowrap;
|
|
|
|
|
+ align-content: flex-start;
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ align-items: stretch;
|
|
|
|
|
+}
|
|
|
|
|
+.message {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: flex-start;
|
|
|
|
|
+ margin-bottom: 15px;
|
|
|
|
|
+ .quickList{
|
|
|
|
|
+ margin-top: 10px;
|
|
|
|
|
+ .list-items {
|
|
|
|
|
+ color: blue;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ line-height: 24px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ .avatar {
|
|
|
|
|
+ width: 40px;
|
|
|
|
|
+ height: 40px;
|
|
|
|
|
+ border-radius: 5px;
|
|
|
|
|
+ margin-right: 15px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .message-con {
|
|
|
|
|
+ min-height: 40px;
|
|
|
|
|
+ max-width: 80vw;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+ font-size: 14px;
|
|
|
|
|
+ line-height: 1.3;
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ border-radius: 5px;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ }
|
|
|
|
|
+ .nickname{
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ margin-top: 5px;
|
|
|
|
|
+ }
|
|
|
|
|
+ &.self {
|
|
|
|
|
+ justify-content: flex-end;
|
|
|
|
|
+ .avatar {
|
|
|
|
|
+ margin: 0 0 0 15px;
|
|
|
|
|
+ }
|
|
|
|
|
+ .message-con {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ &::after {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ content: '';
|
|
|
|
|
+ width: 0;
|
|
|
|
|
+ height: 0;
|
|
|
|
|
+ border: 8px solid transparent;
|
|
|
|
|
+ border-left: 8px solid #fff;
|
|
|
|
|
+ right: -14px;
|
|
|
|
|
+ top: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ &.friend {
|
|
|
|
|
+ .message-con {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ &::after {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ content: '';
|
|
|
|
|
+ width: 0;
|
|
|
|
|
+ height: 0;
|
|
|
|
|
+ border: 8px solid transparent;
|
|
|
|
|
+ border-right: 8px solid #fff;
|
|
|
|
|
+ left: -14px;
|
|
|
|
|
+ top: 12px;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.m-address{
|
|
|
|
|
+ display: flex;align-items: center;gap: 10px;min-width: 260px;
|
|
|
|
|
+ &.self{
|
|
|
|
|
+ transform: rotate(180);
|
|
|
|
|
+ }
|
|
|
|
|
+ .image {
|
|
|
|
|
+ image{width: 120upx;}
|
|
|
|
|
+ }
|
|
|
|
|
+ .infos{
|
|
|
|
|
+ flex: 1;overflow: hidden;
|
|
|
|
|
+ .title{font-size: 14px;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;}
|
|
|
|
|
+ .desc{font-size: 12px;color: #999;margin-top: 10px;}
|
|
|
|
|
+ .price{font-size: 14px;color: #f00;margin-top: 10px;}
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
</style>
|
|
</style>
|