msb_beta
ch 3 years ago
parent 1b46858f16
commit ced626f083

@ -1,45 +0,0 @@
/*
* @Author: ch
* @Date: 2022-05-05 14:40:00
* @LastEditors: ch
* @LastEditTime: 2022-05-05 18:07:18
* @Description: 根据git分支生成对应环境的环境变量
* 开发时如果环境变量换了可以不用重启服务直接运行node env.config.js即可
*/
const fs = require('fs');
const path = require('path');
const getRepoInfo = require('git-repo-info');
const envConfig = {
dev : {
base_url: 'dev'
},
test : {
base_url: 'xxx'
},
reslese : {
base_url: 'xxx'
},
prod : {
base_url: 'xxx'
}
}
const branch = getRepoInfo().branch; // 调用获取git信息
let curEnvConfig = {};
switch (branch){
case 'msb_test':
curEnvConfig = envConfig.test;
break;
case 'msb_beta':
curEnvConfig = envConfig.release;
break;
case 'msb_prod':
curEnvConfig = envConfig.prod;
break;
default:
curEnvConfig = envConfig.dev;
break;
}
fs.writeFileSync(`${path.resolve(__dirname, '../common/config')}/env.js`,
`const ENV = ${JSON.stringify(curEnvConfig)}; export default ENV;`);

@ -0,0 +1,35 @@
/*
* @Author: ch
* @Date: 2022-05-10 20:50:54
* @LastEditors: ch
* @LastEditTime: 2022-05-13 14:28:29
* @Description: file content
*/
const MSG_TYPE = {
// 文本消息
TXT: 1,
// 语音消息
VOICE: 2,
// 图片消息
IMG: 3,
// 视频消息
VIDEO: 4,
// 自定义类型
CUSTOM : 6,
// 提示消息
TIP : 7
}
const TYPE = {
// 给某个会话发送消息
SEND_MSG: 20,
// 创建会话
CRETAE_SEEION: 21,
// 会话心跳
SESSION_HEART : 22
}
export {
// 消息类型
MSG_TYPE
}

@ -0,0 +1,357 @@
export const emojiData = [
//笑脸
{
type: 'smiley',
name: '笑脸',
list: [
'😀',
'😁',
'😂',
'🤣',
'😃',
'😄',
'😅',
'😆',
'😉',
'😊',
'😋',
'😎',
'😍',
'😘',
'😗',
'😙',
'😚',
'🙂',
'🤗',
'🤩',
'🤔',
'🤨',
'😐',
'😑',
'😶',
'🙄',
'😏',
'😣',
'😥',
'😮',
'🤐',
'😯',
'😪',
'😫',
'😴',
'😌',
'😛',
'😜',
'😝',
'🤤',
'😒',
'😓',
'😔',
'😕',
'🙃',
'🤑',
'😲',
'🙁',
'😖',
'😞',
'😟',
'😤',
'😢',
'😭',
'😦',
'😧',
'😨',
'😩',
'🤯',
'😬',
'😰',
'😱',
'😳',
'🤪',
'😵',
'😡',
'😠',
'🤬',
'😷',
'🤒',
'🤕',
'🤢',
'🤮',
'🤧',
'😇',
'🤠',
'🤡',
'🤥',
'🤫',
'🤭',
'🧐',
'🤓',
'😈',
'👿',
'👹',
'👺',
'💀',
'👻',
'👽',
'🤖',
'💩',
],
},
//手势
{
type: 'gesture',
name: '手势',
list: [
'🤲',
'👐',
'🙌',
'👏',
'🤝',
'👍',
'👎',
'👊',
'🤛',
'🤜',
'🤞',
'🤟',
'🤘',
'👌',
'👈',
'👉',
'👆',
'👇',
'🤚',
'🖐',
'🖖',
'👋',
'🤙',
'💪',
// "🖕",
'🙏',
],
},
//人物
{
type: 'people',
name: '人物',
list: [
'👦',
'👧',
'👨',
'👩',
'👴',
'👵',
'👶',
'👱',
'👮',
'👲',
'👳',
'👷',
'👸',
'💂',
'🎅',
'👰',
'👼',
'💆',
'💇',
'🙍',
'🙎',
'🙅',
'🙆',
'💁',
'🙋',
'🙇',
'🙌',
'🙏',
'👤',
'👥',
'🚶',
'🏃',
'👯',
'💃',
'👫',
'👬',
'👭',
'💏',
'💑',
'👪',
],
},
//动物
{
type: 'animal',
name: '动物',
list: [
'🙈',
'🙉',
'🙊',
'🐵',
'🐒',
'🐶',
'🐕',
'🐩',
'🐺',
'🐱',
'😺',
'😸',
'😹',
'😻',
'😼',
'😽',
'🙀',
'😿',
'😾',
'🐈',
'🐯',
'🐅',
'🐆',
'🐴',
'🐎',
'🐮',
'🐂',
'🐃',
'🐄',
'🐷',
'🐖',
'🐗',
'🐽',
'🐏',
'🐑',
'🐐',
'🐪',
'🐫',
'🐘',
'🐭',
'🐁',
'🐀',
'🐹',
'🐰',
'🐇',
'🐻',
'🐨',
'🐼',
'🐾',
'🐔',
'🐓',
'🐣',
'🐤',
'🐥',
'🐦',
'🐧',
'🐸',
'🐊',
'🐢',
'🐍',
'🐲',
'🐉',
'🐳',
'🐋',
'🐬',
'🐟',
'🐠',
'🐡',
'🐙',
'🐚',
'🐌',
'🐛',
'🐜',
'🐝',
'🐞',
'🦋',
'🐁',
'🐂',
'🐅',
'🐇',
'🐉',
'🐍',
'🐎',
'🐐',
'🐒',
'🐓',
'🐕',
'🐖',
],
},
//其他
{
type: 'other',
name: '其他',
list: [
'🌹',
'🍀',
'🍎',
'💰',
'📱',
'🌙',
'🍁',
'🍂',
'🍃',
'🌷',
'💎',
'🔪',
'🔫',
'🏀',
'👄',
'👍',
'🔥',
'💘',
'💓',
'💔',
'💕',
'💖',
'💗',
'💙',
'💚',
'💛',
'💜',
'💝',
'💞',
'💟',
],
},
];
//emoji表情编码
export function utf16toEntities(str) {
const patt = /[\ud800-\udbff][\udc00-\udfff]/g; // 检测utf16字符正则
str = str.replace(patt, (char) => {
let H;
let L;
let code;
let s;
if (char.length === 2) {
H = char.charCodeAt(0); // 取出高位
L = char.charCodeAt(1); // 取出低位
code = (H - 0xd800) * 0x400 + 0x10000 + L - 0xdc00; // 转换算法
s = `&#${code};`;
} else {
s = char;
}
return s;
});
return str;
}
// 表情解码
export function entitiestoUtf16(strObj) {
const patt = /&#\d+;/g;
const arr = strObj.match(patt) || [];
let H;
let L;
let code;
for (let i = 0; i < arr.length; i += 1) {
code = arr[i];
code = code.replace('&#', '').replace(';', '');
// 高位
H = Math.floor((code - 0x10000) / 0x400) + 0xd800;
// 低位
L = ((code - 0x10000) % 0x400) + 0xdc00;
code = `&#${code};`;
const s = String.fromCharCode(H, L);
strObj = strObj.replace(code, s);
}
return strObj;
}

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-04-25 14:39:19
* @LastEditors: ch
* @LastEditTime: 2022-05-10 19:17:16
* @LastEditTime: 2022-05-14 18:06:30
* @Description: file content
*/
import store from "../store";
@ -12,8 +12,10 @@ const sessionList = (ctx) => {
sessionListFlag = true;
// 来新消息先查会话列表是否一存在,存在则加消息数。不存在则向会话列表加一个会话框
ctx.sessionVOS.forEach(item => {
item.lastMessage.createTimeStamp = formatDate(item.lastMessage.createTimeStamp, 'mm-dd hh:ii')
item.lastMessage.payload = JSON.parse(item.lastMessage.payload || {});
if (item.lastMessage) {
item.lastMessage.createTimeStamp = formatDate(item.lastMessage.createTimeStamp, 'mm-dd hh:ii')
item.lastMessage.payload = JSON.parse(item.lastMessage.payload || {});
}
let historyData = store.state.sessionData;
let hisIndex = historyData.findIndex(i => i.id === item.id);
if(hisIndex >= 0){
@ -32,14 +34,15 @@ const historyMsg = (ctx) => {
if(!ctx.length){
return false;
}
ctx.reverse();
// ctx.reverse();
let newData = store.state.sessionData;
const hisIdx = newData.findIndex(i => i.id === ctx[0].sessionId);
ctx.forEach(item => {
item.createTimeStamp = formatDate(item.createTimeStamp, 'mm-dd hh:ii')
item.createTimeStamp = formatDate(item.createTimeStamp, 'mm-dd hh:ii');
item.payload = JSON.parse(item.payload)
})
newData[hisIdx].messageList = ctx.concat(newData[hisIdx].messageList);
// newData[hisIdx].messageList = ctx.concat(newData[hisIdx].messageList);
newData[hisIdx].messageList = newData[hisIdx].messageList.concat(ctx);
store.commit('SET_SESSION_DATA', newData);
}
/**
@ -52,7 +55,7 @@ const sessionMsg = (ctx)=>{
return
}
ctx.payload = JSON.parse(ctx.payload || {});
ctx.createTimeStamp = formatDate(ctx.createTimeStamp, 'mm-dd hh:ii')
ctx.createTimeStamp = formatDate(ctx.createTimeStamp || new Date(), 'mm-dd hh:ii')
let historyData = store.state.sessionData;
const hisIndex = historyData.findIndex(i => i.id === ctx.sessionId);
// 不在当前会话框则全局消息加1
@ -62,7 +65,8 @@ const sessionMsg = (ctx)=>{
if(hisIndex >= 0){
// 存在会话往现有会话增加一条消息
const curHisData = historyData[hisIndex];
curHisData.messageList = [ ctx, ...(curHisData.messageList || [])]
// curHisData.messageList = [ ctx, ...(curHisData.messageList || [])]
curHisData.messageList.push(ctx); //= [ ctx, ...(curHisData.messageList || [])]
// 不在当前会话框则会话框消息加1
if(ctx.sessionId !== store.state.sessionMsgId){
curHisData.unreadCount++;
@ -81,43 +85,69 @@ const sessionMsg = (ctx)=>{
}]);
}
}
// 用户自己创建了一个会话
const createSessionMain = (ctx) => {
let historyData = store.state.sessionData;
const curHis = historyData.find(i => i.id === ctx.id);
store.commit('SET_SESSION_MSG_ID', ctx.id);
if (curHis) {
return false;
}
store.commit('SET_SESSION_DATA',[...historyData, {
...ctx,
unreadCount : 0,
messageList : []
}]);
}
export let MsbWebSkt = null;
const token = 'eyJhbGciOiJSUzI1NiJ9.eyJleHAiOjQwNzA4ODAwMDAsImlkIjo5LCJ0aW1lIjoxNjUyNTAwMDUyNTgwfQ.VAMmJTU1FAcw-vD39E_7EHoDaEdKD3A53L-kdcF6Hj_ygs1urs7xfTNsE2Z_Tb5wXRkS_j-qm0pGnRgfD2EQebc8Pcd28-Rp2W8o-CmCw2_uZUZbfUGeFRZ_ihsFCCpsepU7uEkA9L--cI_u3jw9xdNqzkiXYAVeAXsJ2j570bk'
export const MsbWebSktInit = () => {
return new Promise((resolve, reject) => {
MsbWebSkt = uni.connectSocket({
// url : `wss://you-gateway.mashibing.com/ws?client=${store.state.token}`, // url是websocket连接ip
url : `wss://k8s-horse-gateway.mashibing.cn/ws?client=${store.state.token}`, // url是websocket连接ip
// url : `wss://k8s-horse-gateway.mashibing.cn/ws?client=${store.state.token}`, // url是websocket连接ip
url : `ws://192.168.10.93:8090/ws?client=${token}&type=1`, // url是websocket连接ip
fail: e => {
reject(e)
}
});
MsbWebSkt.onOpen(() => {
MsbWebSkt.onMessage((res)=>{
MsbWebSkt.onMessage((res) => {
const data = JSON.parse(res.data || {});
if(data.code === 200){
if (data.code === 200) {
const ctx = data.content;
switch(data.traceType){
switch (data.traceType) {
// 会话列表
case 1 :
case 1:
sessionList(ctx);
break;
break;
// 历史消息
case 2 :
historyMsg(ctx)
break;
case 2:
case 23:
historyMsg(ctx);
break;
// 创建会话
case 21:
createSessionMain(ctx);
break;
// 会话消息
case 7 :
case 25:
case 20:
case 7:
sessionMsg(ctx)
break;
default :
break;
break;
default:
break;
}
}
})
resolve();
});
MsbWebSkt.onClose(() => {
console.log('断开。。。。。');
})
})

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2021-07-26 23:22:16
* @LastEditors: ch
* @LastEditTime: 2022-05-10 19:13:01
* @LastEditTime: 2022-05-11 15:56:00
* @Description: file content
*/
import Vue from 'vue';
@ -29,7 +29,6 @@ if(store.state.token){
});
setInterval(()=>{
console.log('-----');
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
@ -38,7 +37,7 @@ if(store.state.token){
})
})
}, 5000);
}, 5000000);
})
}

@ -228,7 +228,7 @@
}
},
{
"path": "pages/account/message/chat",
"path": "pages/account/message/chat/index",
"aliasPath" : "/messageChat",
"style": {
"navigationBarTitleText": "马士兵严选"

@ -3,7 +3,7 @@
components: { UiCell },: ch
* @Date: 2019-08-22 19:41:20
* @LastEditors: ch
* @LastEditTime: 2022-04-29 22:58:50
* @LastEditTime: 2022-05-13 17:52:27
* @Description: file content
-->
<template>
@ -47,7 +47,7 @@
<UiCell title="收货地址" @click="$Router.push('/addressList')">
<image slot="icon" class="cell--icon" src="@/static/common/dz.png" />
</UiCell>
<UiCell title="联系客服">
<UiCell title="联系客服" @click="$Router.push('/messageChat')">
<image slot="icon" class="cell--icon" src="@/static/account/kf.png" />
</UiCell>
<UiCell title="设置" class="cell--last" @click="$Router.push('/setting')">

@ -1,124 +0,0 @@
<!--
* @Author: ch
* @Date: 2022-03-26 14:32:03
* @LastEditors: ch
* @LastEditTime: 2022-04-28 23:43:35
* @Description: file content
-->
<template>
<view>
<view class="msgItem" v-for="item in msgData" :key="item.id">
<view class="msgItem--title">
<text>{{item.payload.title }} {{item.id}}</text>
<text class="msgItem--time">{{item.createTimeStamp}}</text>
</view>
<view class="msgItem--con">
<image class="msgItem--img" src="@/static/message/xt.png" mode="widthFix"/>
<view class="msgItem--desc">{{item.payload.content}}</view>
</view>
</view>
<u-loadmore status="loading" v-if="!msgData.length"/>
</view>
</template>
<script>
import {MsbWebSkt, createUUID} from '@/common/utils';
export default {
data(){
return {
}
},
computed:{
curSessionData(){
return this.$store.state.sessionData.find(i =>i.id == this.$store.state.sessionMsgId)
},
msgData (){
return this.curSessionData ? this.curSessionData.messageList : [];
}
},
onReachBottom(){
this.getHistoryMsg();
},
onShow(){
if(!this.$store.state.sessionMsgId){
this.$Router.back();
return false
}
this.getHistoryMsg();
this.readMsg();
},
methods:{
/**
* 获取历史消息
*/
getHistoryMsg(){
this.loading = true;
const lastMsg = this.msgData[this.msgData.length - 1] || {};
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "2",
content: {
sessionId : this.$store.state.sessionMsgId,
topMessageId : lastMsg.id || null
}
})
})
},
/**
* 已读消息
*/
readMsg(){
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "6",
content: {
sessionId : this.$store.state.sessionMsgId
}
})
});
//
const count = this.$store.state.sessionMsgCount - this.curSessionData.unreadCount;
//
this.curSessionData.unreadCount = 0;
this.$store.commit('SET_SESSION_MSG_COUNT', count)
}
}
}
</script>
<style lang="scss" scoped>
.msgItem{
padding: 40rpx 0 0 40rpx;
&--title{
display: flex;
justify-content: space-between;
align-items: center;
padding-right: 40rpx;
font-size: $font-size-lg;
color: $color-grey6;
}
&--time{
font-size: $font-size-sm;
color: $color-grey4;
}
&--con{
display: flex;
border-bottom: 1px solid $color-grey2;
padding: 40rpx 0;
}
&--img{
width: 140rpx;
height: 140rpx;
border-radius: 12rpx;
margin-right: 40rpx;
}
&--desc{
width: 455rpx;
font-size: $font-size-base;
line-height: 39rpx;
color: $color-grey5;
}
}
</style>

@ -0,0 +1,926 @@
<template>
<view class="chat-list" :class="{'loading':!loading,'isInput':isInput==true,'otherInput':otherInput==true}">
<view v-for="(item,index) in msgData" :key="item.id" :id="item.id" :class="{'nowAt':item.messageId == isNowAit}">
<!-- <view v-if="index>0&&item.sendTime - msgList[index-1].sendTime > 300000">
<view class="chat-item-hint">
<view style="background: none;">{{getTime(item.sendTime)}}</view>
</view>
</view> -->
<!-- 接收的消息 -->
<view class="chat-item-l" v-if="item.fromId != userInfo.id">
<view class="head-img">
<image class="head-img" src="@/static/message/xt.png"></image>
</view>
<view>
<view class="group-user-name clamp" v-if="imObject && imObject.type===2">
{{item.fromNickName||item.nickName}}
</view>
<!-- 文字类型消息 -->
<view v-if="item.type == msgType['txt']" class="chat-item-content" :class="{'jump-box':false}">
<!-- 普通文字消息 -->
<view>{{item.ctx}}</view>
</view>
<!-- 图片类型消息 -->
<view v-if="item.type == msgType['img']" class="chat-item-content-voice" >
<img class="des-img" mode='aspectFit' :src="item.ctx" @click="previewImg(item.ctx)">
</img>
</view>
<!-- 视频类型消息 -->
<view v-if="item.type == msgType['video']" class="chat-item-content-video" @click="clickVideo(item)">
<image class="v-icon" src="@/static/im/ship@2x.png" mode=""></image>
<view class="v-bottom">
<image class="v-b-icon" src="@/static/im/xiazai.png"></image>
</view>
</view>
</view>
</view>
<!-- 发送的消息 -->
<view class="chat-item-r" v-else >
<!-- 文字类型消息 -->
<view v-if="item.type == msgType['txt']" class="chat-item-content" :style="{'maxWidth':Width + 'px'}">
<view v-html="getUrl(item.content)"></view>
</view>
<!-- 图片类型消息 -->
<view v-if="item.type == msgType['img']" class="chat-item-content-voice" :style="{'maxWidth':Width + 'px'}">
<image class="des-img" mode='aspectFit' :src="item.ctx" @click="previewImg(item.ctx)">
</image>
</view>
<!-- 视频类型消息 -->
<view v-if="item.type == msgType['video']" class="chat-item-content-video" @click="clickVideo(item)">
<image class="v-icon" src="@/static/im/ship@2x.png" mode=""></image>
<view class="v-bottom">
<image class="v-b-icon" src="@/static/im/xiazai.png"></image>
</view>
</view>
<!-- 头像 -->
<view class="head-img">
<image class="head-img" src="@/static/message/xt.png"></image>
</view>
</view>
<view v-if="item.type === msgType['tips']">
<view class="chat-item-hint">
<view>{{item.ctx}}</view>
</view>
</view>
</view>
<!-- <view class="ait-jump" @click="aitJump"> -->
<view class="ait-jump" v-if="aitMsgId" @click="aitJump">
<image src="@/static/im/aitbtn1.png"></image>
<text>有人@</text>
<image @click.stop="aitMsgId=''" src="@/static/im/aitbtn2.png"></image>
</view>
<!-- <yinrh-menu-popup v-model="popup" :pop-data="menu" dynamic :x="x" :y="y" direction="row" theme="dark"
:placement="place" @tapPopup="tapPopup" /> -->
</view>
</template>
<script>
import {MSG_TYPE} from '@/common/dicts/im';
export default {
props: {
imObject: {
type: Object,
default: () => {}
},
isInput: {
type: Boolean,
default: false
},
otherInput: {
type: Boolean,
default: false
},
unReadCount: {
type: [String, Number],
default: 0
},
identity: {
type: [String, Number],
default: 0
}
},
data() {
return {
msgType : MSG_TYPE,
msgData :[
{id : 1, fromId : 9, type:1, ctx : 'ype 1文本消息 2语音消息 3图片消息 4视频消息 7提示消息'},
{id : 5, fromId : 3, type:1, ctx: 'ype 1文本消息 2语音消息 3图片消息 4视频消息 7提示消息'},
{id : 2, fromId : 9, type:7, ctx: '给你移动了客服'},
{id : 3, fromId : 2, type:3, ctx : 'https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/mall-product/product2bca55bb5c3adb4d02a7a76039d097eewww800-800.jpg'},
{id : 3, fromId : 2, type:3, ctx : 'https://msb-edu-dev.oss-cn-beijing.aliyuncs.com/uc/account-avatar/aitbtn1.png'},
// {id : 4, formId : 9, type:4},
],
Width: '',
userNo: '',
historyParams: {},
msgList: [],
pullLoad: true,
firstPage: true,
loading: true,
popup: false,
aitMsgId: '',
isNowAit: '',
menu: [{
title: '转发'
}, {
title: '撤回'
}, {
title: '收藏'
}, {
title: '复制'
}],
value: false, // menutrue-
place: 'bottom-end',
x: 0,
y: 0,
selectMsg: {},
//
innerAudioContext: {},
//
audioItem: {}
}
},
created() {
// let app = this;
// uni.getSystemInfo({
// success: function(res) {
// app.Width = res.windowWidth - 116;
// app.Height = res.windowHeight;
// }
// });
// app.innerAudioContext = uni.createInnerAudioContext();
// app.innerAudioContext.autoplay = true;
// app.innerAudioContext.onEnded(() => {
// app.stopAudio()
// })
// //
// app.userNo = uni.getStorageSync('loginToken').userNo
// // app.historyParams = {
// // sessionId: app.imObject.sessionId,
// // msgCount: 100,
// // msgId: '',
// // }
// app.firstPage = true;
// app.msgList = [];
// let msgData = this.imDB.where({
// sessionId: app.imObject.sessionId
// }).limit(100).orderBy('messageId', 'desc').get()
// msgData = msgData.reverse()
// if (app.unReadCount > 0 || msgData.length <= 0) {
// app.getHistoryMsg();
// app.$emit('clearUnReadCount')
// } else {
// app.randerList(msgData)
// app.firstPage = false;
// }
//pushlist
},
beforeDestroy() {
this.chatListOnload()
},
computed: {
imDB: function() {
return this.$store.state.imDB;
},
userInfo (){
return this.$store.state.userInfo
}
},
methods: {
aitJump() {
let app = this;
uni.createSelectorQuery().select(".chat-list").boundingClientRect(data => { //
uni.createSelectorQuery().select("#msg" + app.aitMsgId).boundingClientRect((res) => { //
uni.pageScrollTo({
duration: 0, //0uniapp bug
scrollTop: res.top - data.top, //
})
app.isNowAit = app.aitMsgId
app.aitMsgId = ''
}).exec()
}).exec();
},
chatListOnload() {
uni.$off('scoket')
this.innerAudioContext.stop();
// this.msgList = [];
},
reprot(item) {
},
getUrl(str) {
},
fnThrottle(method, delay, duration) {
},
getHistoryMsg() {
},
randerList(list) {
},
getContent(item) {
},
clickVideo(item) {
let app = this;
uni.showModal({
title: '提示',
content: '暂时无法播放视频,是否下载视频文件?',
success: function(res) {
if (res.confirm) {
app.download(item)
}
}
});
},
download(item) {
if (item.isLoading) {
return false;
}
this.downloadFile(item)
},
scrollTop() {
if (this.pullLoad) {
this.getHistoryMsg();
}
},
tapPopup(e) {
},
copy() {
},
deleteMessage() {
},
forwardMsg() {
},
previewImg(logourl) {
let _this = this;
let imgsArray = [];
imgsArray[0] = logourl
uni.previewImage({
current: 0,
urls: imgsArray
});
},
playAudio(item) {
},
stopAudio() {
},
getSize(num) {
},
getTime(time) {
return dateFormat(time, 'yyyy-MM-dd HH:mm')
},
jumpPages(type, id, flag) {
},
delUnReadCount(res) {
let app = this;
delUnReadCount({
sessionId: res.sessionId,
userId: app.userNo,
platform: 2
})
}
}
}
</script>
<script module="test" lang="renderjs">
export default {
mounted() {
window.addEventListener("click", function(e) {
if (e.target.id == 'isa') {
uni.navigateTo({
url: 'pages/open-url/index?src=' + e.target.innerHTML
})
}
});
},
}
</script>
<style lang="scss" scoped>
.chat-item-content {
color: #0062CC;
position: relative;
.file-loading {
width: 22rpx;
height: 22rpx;
position: absolute;
left: -50rpx;
}
.file-fail {
height: 80rpx;
width: 110rpx;
position: absolute;
left: -110rpx;
background: url('@/static/im/fail.png') no-repeat;
background-position-x: 20rpx;
background-position-y: top;
background-size: 50rpx 50rpx;
&::before {
position: absolute;
content: '发送失败';
font-size: 20rpx;
font-family: PingFang SC;
font-weight: 400;
color: #999999;
width: 200rpx;
top: 52rpx;
}
}
}
.otherInput {
padding-bottom: 500rpx !important;
}
.isInput {
// padding-bottom: 718rpx !important;
}
.loading {
opacity: 0;
}
.chat-list {
padding: 20rpx 32rpx;
height: 100%;
overflow-y: auto;
overflow-x: hidden;
.chat-item-time {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
font-size: 24rpx;
font-family: DIN;
font-weight: 500;
color: #69707F;
margin: 30rpx 0;
}
.chat-item-hint {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 20rpx;
view {
background: #EEEEEE;
border-radius: 25rpx;
padding: 12rpx 42rpx;
font-size: 24rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
}
.chat-item-l {
display: flex;
align-items: top;
margin: 20rpx 0;
.head-img {
width: 64rpx;
height: 64rpx;
margin-right: 20rpx;
border-radius: 50%;
}
.group-user-name {
font-size: 28rpx;
color: #999999;
padding-bottom: 12rpx;
max-width: 300rpx;
}
.jump-box {
position: relative;
padding-bottom: 56rpx !important;
}
.chat-item-content {
background: #FFFFFF;
border-radius: 2rpx 16rpx 16rpx 16rpx;
margin-right: 84rpx;
background-color: #fff;
box-sizing: border-box;
padding: 20rpx 30rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #000000;
display: flex;
align-items: center;
word-break: break-all;
white-space: pre-wrap;
.audio-icon {
width: 24rpx;
height: 32rpx;
}
}
.chat-item-content-video {
width: 287rpx;
height: 161rpx;
background: #DCDCDC;
border-radius: 2rpx 16rpx 16rpx 16rpx;
position: relative;
.v-icon {
position: absolute;
left: 120rpx;
top: 48rpx;
width: 46rpx;
height: 46rpx;
}
.v-bottom {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 40rpx;
background: rgba(0, 0, 0, 0.4);
border-radius: 2rpx 16rpx 16rpx 16rpx;
.v-b-icon {
position: absolute;
width: 23rpx;
height: 22rpx;
right: 20rpx;
bottom: 9rpx;
}
}
}
.chat-item-content-voice {
display: flex;
align-items: center;
width: auto;
max-width: 400rpx;
height: auto;
// height: 320rpx;
// background: #FB3A4E;
border-radius: 2rpx 2rpx 16rpx 16rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: 500;
color: #333333;
// padding: 30rpx;
.des-img,
.des-video {
// width: 100%;
height: 150rpx;
// margin-right: 15rpx;
}
}
.chat-item-content-datum {
width: 465rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #FFFFFF;
border-radius: 2rpx 16rpx 16rpx 16rpx;
margin-right: 84rpx;
background-color: #fff;
box-sizing: border-box;
padding: 35rpx 30rpx;
.datum-info {
display: flex;
flex-flow: column;
flex: 1;
.datum-title {
flex: 1;
width: 300rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 32rpx;
line-height: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
}
.datum-size {
margin-top: 15rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
}
}
.chat-item-content-picture {
display: flex;
align-items: center;
border-radius: 2rpx 16rpx 16rpx 16rpx;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
image {
width: 213rpx;
}
}
}
.chat-item-r {
display: flex;
align-items: top;
justify-content: flex-end;
margin: 20rpx 0;
>.head-img {
margin-left: 20rpx;
}
.head-img {
width: 64rpx;
height: 64rpx;
padding: 0;
border-radius: 50%;
}
.chat-item-content {
background: #FB3A4E;
border-radius: 16rpx 2rpx 16rpx 16rpx;
margin-left: 84rpx;
box-sizing: border-box;
padding: 20rpx 30rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #FFFFFF;
word-break: break-all;
white-space: pre-wrap;
display: flex;
align-items: center;
.audio-icon {
width: 24rpx;
height: 32rpx;
}
}
.chat-item-content-video {
width: 287rpx;
height: 161rpx;
background: #DCDCDC;
border-radius: 16rpx 2rpx 16rpx 16rpx;
position: relative;
.v-icon {
position: absolute;
left: 120rpx;
top: 48rpx;
width: 46rpx;
height: 46rpx;
}
.v-bottom {
position: absolute;
left: 0;
bottom: 0;
width: 100%;
height: 40rpx;
background: rgba(0, 0, 0, 0.4);
border-radius: 2rpx 2rpx 16rpx 16rpx;
.v-b-icon {
position: absolute;
width: 23rpx;
height: 22rpx;
right: 20rpx;
bottom: 9rpx;
}
}
}
.chat-item-content-voice {
display: flex;
align-items: center;
justify-content: flex-end;
width: auto;
max-width: 400rpx;
height: auto;
// height: 320rpx;
// background: #FB3A4E;
border-radius: 16rpx 2rpx 16rpx 16rpx;
font-size: 28rpx;
font-family: DIN;
font-weight: 500;
color: #333333;
// padding: 30rpx;
.des-img,
.des-video {
// width: 100%;
height: 150rpx;
// margin-right: 15rpx;
}
}
.chat-item-content-datum {
width: 465rpx;
display: flex;
align-items: center;
justify-content: space-between;
background: #FFFFFF;
border-radius: 16rpx 2rpx 16rpx 16rpx;
margin-left: 84rpx;
background-color: #fff;
box-sizing: border-box;
padding: 35rpx 30rpx;
position: relative;
.file-loading {
width: 22rpx;
height: 22rpx;
position: absolute;
left: -50rpx;
}
.file-fail {
height: 80rpx;
width: 110rpx;
position: absolute;
left: -110rpx;
background: url('@/static/im/fail.png') no-repeat;
background-position-x: 15rpx;
background-position-y: top;
background-size: 50rpx 50rpx;
&::before {
position: absolute;
content: '发送失败';
font-size: 20rpx;
font-family: PingFang SC;
font-weight: 400;
color: #999999;
width: 200rpx;
top: 52rpx;
}
}
.datum-info {
display: flex;
flex-flow: column;
flex: 1;
.datum-title {
flex: 1;
width: 300rpx;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
font-size: 32rpx;
line-height: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
}
.datum-size {
margin-top: 15rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
}
}
.chat-item-content-picture {
display: flex;
align-items: center;
border-radius: 16rpx 2rpx 16rpx 16rpx;
background-color: #fff;
box-sizing: border-box;
overflow: hidden;
image {
width: 213rpx;
}
}
}
}
.datum-type {
image {
width: 74rpx;
height: 77rpx;
}
}
.jump-ques {
color: #00CCFF;
}
.voice-mask {
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba($color: #000000, $alpha: 0.6);
z-index: 1003;
.vm-box {
width: 617rpx;
height: 514rpx;
background: #FFFFFF;
border-radius: 16rpx;
position: fixed;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
padding: 40rpx;
.vm-title {
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333333;
margin-bottom: 25rpx;
}
.vm-user {
display: flex;
align-items: center;
margin-bottom: 20rpx;
.vmu-img {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.vmu-txt {
font-size: 30rpx;
font-family: PingFang SC;
font-weight: bold;
color: #333333;
}
}
.vm-msg {
width: 377rpx;
height: 76rpx;
background: #FB3A4E;
border-radius: 2rpx 16rpx 16rpx 16rpx;
display: flex;
align-items: center;
padding-left: 30rpx;
margin-bottom: 20rpx;
.vmm-img {
width: 24rpx;
height: 32rpx;
transform: rotate(180deg);
}
.vmm-txt {
font-size: 28rpx;
font-family: DIN;
font-weight: 500;
color: #F3F5F6;
}
}
.vm-ipt {
width: 537rpx;
height: 68rpx;
background: #F6F6F6;
border-radius: 8rpx;
font-size: 28rpx;
color: #999;
padding-left: 20rpx;
}
.vm-btm {
height: 106rpx;
width: 100%;
position: absolute;
left: 0;
bottom: 0;
display: flex;
border-top: 1rpx solid #F5F6F6;
.vmb-cel,
.vmb-sub {
width: 50%;
height: 100%;
text-align: center;
line-height: 106rpx;
font-size: 32rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
}
.vmb-cel {
border-right: 1rpx solid #F5F6F6;
}
}
}
}
.chat-item-l {
/deep/.at_span {
&::after {
content: attr(data-name);
color: #FB3A4E;
}
}
}
.chat-item-r {
/deep/.at_span {
&::after {
content: attr(data-name);
color: #ffffff;
}
}
}
.ait-jump {
position: fixed;
right: 0rpx;
bottom: 200rpx;
width: 273rpx;
height: 78rpx;
background: #FFFFFF;
box-shadow: 0rpx 4rpx 30rpx 1rpx rgba(0, 0, 0, 0.06);
border-radius: 100rpx 0rpx 0rpx 100rpx;
z-index: 9999;
display: flex;
align-items: center;
padding: 0 30rpx;
image {
width: 16rpx;
height: 16rpx;
&:nth-child(1) {
margin-right: 20rpx;
}
&:last-child {
margin-left: auto;
}
}
text {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #FB3A4E;
}
}
.nowAt {
animation: isAt 3s 1;
-webkit-animation: isAt 3s 1;
/* Safari 和 Chrome */
}
@keyframes isAt {
0% {
background: #eee;
}
100% {
background: #fff;
}
}
</style>

@ -0,0 +1,316 @@
<!--
* @Author: ch
* @Date: 2022-05-11 11:45:08
* @LastEditors: ch
* @LastEditTime: 2022-05-14 18:35:40
* @Description: file content
-->
<template>
<view class="footer">
<GoodsInfo class="goods" v-if="simpleGoods.id && goodsShow" :goodsInfo="simpleGoods"
@send="sendGoods" @close="goodsShow=false" @click="$Router.back()"/>
<view class="operation">
<textarea class="operation--input" :focus="focus" v-model="msgCtx" @focus="moreShow = false" @click="emojiShow = false"/>
<view class="operation--btns">
<image class="operation--emoji" src="@/static/imxx/emoji.png" @click="emojiShow = !emojiShow"/>
<view class="operation--send" v-if="msgCtx.length" @click="send(msgCtx, MSG_TYPE.TXT)"></view>
<image class="operation--more" v-else src="@/static/imxx/more.png" @click="moreShow = true" />
</view>
</view>
<view class="emoji" v-if="emojiShow">
<view class="emoji--tab">
<text class="emoji--tab-item" :class="idx == curEmojiIdx && 'emoji--tab-item__active'"
v-for="(item, idx) in emojiData" :key="idx" @click="curEmojiIdx = idx">{{item.name}}</text>
</view>
<view class="emoji--icon">
<view class="emoji--icon-item" @click="handleAddEmoji(item)"
v-for="item in emojiData[curEmojiIdx].list" :key="item">{{emojiUtf16(item)}}</view>
</view>
</view>
<view class="other" v-if="moreShow">
<u-upload @afterRead="uploadImg" >
<view class="other--btn">
<image class="other--img" src="@/static/imxx/img.png"/>
<view>图片</view>
</view>
</u-upload>
<u-upload @afterRead="uploadVideo" compressed accept="video">
<view class="other--btn">
<image class="other--img" src="@/static/imxx/video.png"/>
<view>视频</view>
</view>
</u-upload>
</view>
</view>
</template>
<script>
import {MsbWebSkt, createUUID} from '@/common/utils';
import {MSG_TYPE} from '@/common/dicts/im';
import {Request} from '@/common/utils';
import {ApiPutUser} from '@/common/api/account';
import {ApiPostGetOssConfig} from '@/common/api/oss';
import UiButton from '@/components/UiButton.vue';
import {entitiestoUtf16, emojiData} from '@/common/plugins/emoji';
import GoodsInfo from './GoodsInfo.vue';
import OrderInfo from './OrderInfo.vue';
export default {
components: { UiButton, GoodsInfo, OrderInfo },
props: {
orderInfo : {
type : Object,
default : () => ({})
},
goodsInfo : {
type : Object,
default : () => ({})
}
},
data(){
return {
goodsShow : true,
orderShow : true,
moreShow : false,
focus : false,
msgCtx : '',
MSG_TYPE ,
emojiShow : false,
emojiData,
emojiUtf16 : entitiestoUtf16,
curEmojiIdx : 0
}
},
computed : {
simpleGoods (){
const {
startingPrice,
name,
id,
pictureList
} = this.goodsInfo;
const productImageUrl = pictureList ? pictureList[0] : '';
return {startingPrice,name,id, productImageUrl}
},
simpleOrder (){
const {orderId, payTypeDesc, payAmount, orderStatusDesc, orderNo, products} = this.orderInfo;
return {orderId, payTypeDesc, payAmount, orderStatusDesc, orderNo, ...(products ? products[0] : {})};
}
},
watch:{
simpleOrder(val){
if(val.orderId){
this.sendOrder();
}
}
},
methods:{
/**
* 统一发送函数
*/
send(val, type){
let payload = {};
switch(type){
case MSG_TYPE.CUSTOM :
payload = JSON.stringify(val)
break;
case MSG_TYPE.VIDEO:
case MSG_TYPE.IMG :
payload = {url : val};
break;
default:
payload = {text : val}
break;
}
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : '20',
content: {
toSessionId : this.$store.state.sessionMsgId,
payload : payload,
toId : 1,
type : type
}
})
});
this.msgCtx = '';
this.focus = false;
this.$nextTick(() => {
this.focus = true;
});
},
handleAddEmoji (data){
let str = ' ' + entitiestoUtf16(data) + ' ';
this.msgCtx += str;
},
sendGoods(){
this.send(this.simpleGoods, MSG_TYPE.CUSTOM);
this.goodsShow = false;
},
sendOrder(){
this.send(this.simpleOrder, MSG_TYPE.CUSTOM);
this.orderShow = false;
},
uploadImg(val){
const file = val.file;
this.uploadFile(file, MSG_TYPE.IMG);
},
uploadVideo(val){
const file = val.file;
this.uploadFile(file, MSG_TYPE.VIDEO);
},
/**
* 获取OSS鉴权信息
* configId 自定义文件夹 图片存储的文件夹名称
* serviceName 服务名
*/
async getOssCon(){
const {error, result} = await ApiPostGetOssConfig({
configId : 'im/',
serviceName : 'uc'
});
if(error){
uni.$u.toast(error.message);
return false
}
return result;
},
/**
* 上传文件
* 1拿到OSS信息上传
* 2成功后拼地址传发送消息
*/
async uploadFile(file, type){
const urlArr = file.url.split('/');
const fileName = file.name || urlArr[urlArr.length - 1];
const oss = await this.getOssCon();
uni.uploadFile({
name : 'file',
filePath : file.url,
url : oss.host,
formData : {
name : fileName,
key : `${oss.dir}${'${filename}'}`,
policy : oss.policy,
OSSAccessKeyId : oss.accessId,
success_action_status : 200,
signature : oss.signature
},
success : async (res)=>{
const fileUrl = `${oss.host}/${oss.dir}${fileName}`;
this.send(fileUrl, type);
}
})
}
}
}
</script>
<style lang="scss" scoped>
.footer{
position: fixed;
bottom: 0;
background: #fff;
left: 0;
right: 0;
margin-bottom: constant(safe-area-inset-bottom);
margin-bottom: env(safe-area-inset-bottom);
}
.goods{
width: 670rpx;
height: 200rpx;
position: absolute;
top: -220rpx;
left: 30rpx;
}
.operation{
display: flex;
height: 100rpx;
align-items: center;
padding: 0 40rpx;
&--input{
height: 70rpx;
padding: 18rpx 20rpx 10rpx;
line-height: 40rpx;
flex: 1;
background: #F3F4F6;
border-radius: 8rpx;
margin-right: 15rpx;
}
&--btns{
display: flex;
align-items: center;
}
&--more, &--emoji{
width: 50rpx;
height: 50rpx;
margin: 0 15rpx;
}
&--send{
width: 110rpx;
height: 60rpx;
line-height: 60rpx;
background: #FF875B;
color: #fff;
font-size: 30rpx;
text-align: center;
border-radius: 8rpx;
margin: 0 15rpx;
}
}
.emoji{
&--icon{
background: #f8f8f8;
padding: 10rpx 30rpx;
display: flex;
justify-content: flex-start;
flex-wrap: wrap;
height: 320rpx;
overflow-y: auto;
&-item{
margin:20rpx;
}
}
&--tab{
display: flex;
height: 50rpx;
line-height: 50rpx;
padding: 0 30rpx;
&-item{
padding: 0 20rpx;
font-size: $font-size-sm;
color: #999;
&__active{
background: #f8f8f8;
}
}
}
}
.other{
width: 100%;
display: flex;
justify-content: flex-start;
background: #f8f8f8;
padding: 30rpx 40rpx;
.u-upload{
flex: 0;
}
&--btn{
text-align: center;
margin-right: 60rpx;
color: #999;
}
&--img{
width: 120rpx;
height: 120rpx;
margin-bottom: 20rpx;
}
}
</style>

@ -0,0 +1,100 @@
<!--
* @Author: ch
* @Date: 2022-05-13 15:57:31
* @LastEditors: ch
* @LastEditTime: 2022-05-14 13:47:05
* @Description: file content
-->
<template>
<view class="product" @click="handleDetail">
<image class="product--img" :src="goodsInfo.productImageUrl" mode="aspectFit"/>
<view class="product--con">
<view class="product--con-title">{{goodsInfo.name}}</view>
<UiMoney class="product--con-price" :money="goodsInfo.startingPrice" float prefix suffix></UiMoney>
</view>
<template v-if="position === 'footer'">
<UiButton class="product--btn" size="min" type="gradual" @click="send"></UiButton>
<image class="product--close" src="@/static/imxx/close.png" @click="close"/>
</template>
</view>
</template>
<script>
import UiButton from '../../../../../components/UiButton.vue'
import UiMoney from '../../../../../components/UiMoney.vue'
export default {
components: { UiMoney, UiButton },
props : {
goodsInfo : {
type : Object,
default : ()=>({})
},
// footer msg
position : {
type : String,
default : 'footer'
}
},
methods : {
send(){
this.$emit('send');
},
close(){
this.$emit('close');
},
handleDetail(){
this.$emit('click')
}
}
}
</script>
<style lang="scss" scoped>
.product {
display: flex;
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
position: relative;
&--img{
width: 140rpx;
height: 140rpx;
}
&--con{
width: 300rpx;
margin-left: 30rpx;
&-title{
font-size: 28rpx;
line-height: 36rpx;
@include ellipses(2);
}
&-price{
margin-top: 36rpx;
}
}
&--btn{
width: 160rpx;
padding : 0;
position: absolute;
right: 30rpx;
bottom: 30rpx;
}
&--close{
width: 24rpx;
height: 24rpx;
position: absolute;
top: 30rpx;
right:30rpx;
}
}
/deep/{
.product--con-price{
text{
color: #FF512B;
font-size: 24rpx;
}
.ui-money--price{
font-size: 32rpx;
}
}
}
</style>

@ -0,0 +1,93 @@
<!--
* @Author: ch
* @Date: 2022-05-14 14:00:14
* @LastEditors: ch
* @LastEditTime: 2022-05-14 15:57:30
* @Description: file content
-->
<template>
<view class="order" @click="handleDetail">
<view class="order--status">{{orderInfo.orderStatusDesc}}</view>
<view class="order--con">
<image class="order--con-img" :src="orderInfo.productImageUrl" mode="aspectFit"/>
<view class="order--con-right">
<view class="order--con-title">{{orderInfo.productName}}</view>
<UiMoney class="order--con-price" :money="orderInfo.payAmount" float prefix suffix></UiMoney>
</view>
</view>
<view class="order--footer">订单编号{{orderInfo.orderNo}}</view>
</view>
</template>
<script>
import UiButton from '../../../../../components/UiButton.vue'
import UiMoney from '../../../../../components/UiMoney.vue'
export default {
components: { UiMoney, UiButton },
props : {
orderInfo : {
type : Object,
default : ()=>({})
},
// footer msg
position : {
type : String,
default : 'footer'
}
},
methods : {
handleDetail(){
this.$Router.push(`/orderDetail?id=${this.orderInfo.orderId}`)
}
}
}
</script>
<style lang="scss" scoped>
.order {
background: #fff;
border-radius: 16rpx;
&--status{
font-size: 28rpx;
padding: 30rpx 0 20rpx 30rpx;
}
&--con{
display: flex;
padding: 0 30rpx;
&-img{
width: 140rpx;
height: 140rpx;
}
&-right{
width: 300rpx;
margin-left: 30rpx;
}
&-title{
font-size: 28rpx;
line-height: 36rpx;
@include ellipses(2);
}
&-price{
margin-top: 36rpx;
}
}
&--footer{
color: #999;
border-top: 1px solid #eee;
padding: 20rpx 30rpx;
margin-top: 26rpx;
}
}
/deep/{
.order--con-price{
text{
color: #FF512B;
font-size: 24rpx;
}
.ui-money--price{
font-size: 32rpx;
}
}
}
</style>

@ -0,0 +1,913 @@
<template>
<!-- -->
<view class="chat-bottom" :style="{'bottom':isInput==true?getKeyHeight()+'px':'0px'}" @touchend='endAudio'>
<view class="bottom-operation">
<view class="goVoice" @click="setAudioShow">
<image src="../../../static/im/voice.png" mode=""></image>
</view>
<!-- <u-input type="textarea" maxlength="300" :clearable="false" height="80rpx" v-model="chatInfo" placeholder-style="color:#999;" placeholder="输入文字..." /> -->
<textarea v-if="!isIos()&&!audioShow" class="textarea" :class="{'textarea2':chatInfo}" maxlength="300"
v-model="chatInfo" @focus='onInput' placeholder-style="color:#999;" placeholder="输入文字..."
:auto-height='true' :adjust-position="false" />
<textarea v-if="isIos()&&!audioShow" class="textarea" :class="{'textarea2':chatInfo}" maxlength="300"
v-model="chatInfo" @focus='onInput' placeholder-style="color:#999;" placeholder="输入文字..."
:auto-height='true' />
<!-- <view class="textarea" v-if="audioShow"></view> -->
<view class="goexpression" @click="setEmojiShow">
<image src="../../../static/im/expression.png" mode=""></image>
</view>
<view v-show="chatInfo" class="send-btn" @touchend.prevent="sendMsg">
发送
</view>
<view class="more" v-show="!chatInfo" @click="setMoreShow">
<image src="../../../static/im/more.png" mode=""></image>
</view>
</view>
<view class="bottom-box">
<view v-show="emojiShow" class="emoji-box">
<view class="tools-box">
<text class="emoji" @click="setEmoji(item)"
v-for="(item,index) in getEmoji()">{{entitiestoUtf16(item)}}</text>
</view>
</view>
<view v-show="moreShow" class="operation-box">
<view class="operation-item" @click="goQuick">
<view class="item-btn">
<image src="../../../static/im/reply.png" mode=""></image>
</view>
<view class="item-hint">回复</view>
</view>
<view class="operation-item" @click="chooseImage">
<view class="item-btn">
<image src="../../../static/im/picture.png" mode=""></image>
</view>
<view class="item-hint">相册</view>
</view>
<view class="operation-item" @click="chooseFile">
<view class="item-btn">
<image src="../../../static/im/file.png" mode=""></image>
</view>
<view class="item-hint">文件</view>
</view>
<view class="operation-item" @click="chatCollect">
<view class="item-btn">
<image src="../../../static/im/collect.png"></image>
</view>
<view class="item-hint">收藏</view>
</view>
</view>
</view>
<u-popup v-model="showAitFlag" mode="bottom" border-radius="16" >
<!-- -->
<view class="ait-box" :style="{'height':isInput==true? (425 - keyHeight/2) +'px':'425px'}">
<view class="ab-title" >
<u-icon class='abt-close' name="arrow-down" color="#333333" size="24"></u-icon>
<text class="abt-txt">选择提醒的人</text>
</view>
<view class="ipt-wrp">
<image class="ipt-icon" src="@/static/sousuo.png"></image>
<input type="text" confirm-type="search" v-model="aitKeyword" class="ipt" placeholder="昵称搜索" >
</view>
<view class="ab-item" @click="setAit()">
<view class="abi-all">@</view>
<view class="abi-name">@所有人({{groupPeoList.length}})</view>
</view>
<view class="ab-item" @click="setAit(item)" v-for="(item,index) in groupPeoList" v-if="item.nickName.includes(aitKeyword)">
<image class="abi-img" :src="item.avatar"></image>
<view class="abi-name clamp">{{item.nickName}}</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import {
utf16toEntities,
entitiestoUtf16
} from '@/common/js/utils.js'
import emojiData from '@/common/js/emoji.js'
import {
sendMsg,
sendGroupMsg,
sendSpecialMessage,
sendTextMsg,
getUserByGroupId
} from '@/common/api-request/im.js'
import cCircle from "@/components/Your_Exios-Circle/Your_Exios-Circle.vue"
import api from '@/common/js/api.js'
const baseUrl = api.host['local'];
export default {
components: {
cCircle
},
props: {
toUserNo: {
type: [String, Number],
default: ''
},
sendType: {
type: [String, Number],
default: ''
},
isInput: {
type: Boolean,
default: false
},
keyHeight: {
type: Number,
default: 0
},
isScreen: {
type: Boolean,
default: false
}
},
data() {
return {
chatInfo: '',
userNo: '',
moreShow: false,
emojiShow: false,
audioShow: false,
showAitFlag: false,
groupPeoList: [],
recorderManager: {}, //
innerAudioContext: {}, //
voicePath: [], //
timer: null, //
audioText: '长按录音',
audioStart: false,
sendFlag: true, //
voiceCel: false,
stamp: 0, //
txtIndex: 0, //
aitKeyword: '',
isserchinput:false
};
},
created() {
let app = this;
app.userNo = uni.getStorageSync('loginToken').userNo
//
app.recorderManager = uni.getRecorderManager();
console.log('录音对象', app.recorderManager)
app.innerAudioContext = uni.createInnerAudioContext();
app.innerAudioContext.autoplay = true;
app.recorderManager.onStop(function(res) {
console.log('-------停止录音', res)
let now = new Date().getTime()
let obj = {
path: res.tempFilePath,
duration: parseInt((now - app.stamp) / 100) * 100
}
if (obj.duration < 1000) {
uni.showToast({
icon: 'none',
title: '录音太短了!'
})
setTimeout(() => {
app.cancelAudio()
}, 200)
} else {
//
if (!app.voiceCel) {
app.voicePath.push(obj);
app.sendAudio()
}
}
});
if (app.sendType === 2) {
getUserByGroupId(app.toUserNo).then(res => {
console.log('--------------获取群成员', res.data)
app.groupPeoList = res.data
app.groupPeoList.forEach((el, idx) => {
if (el.userNo == app.userNo) {
app.groupPeoList.splice(idx, 1)
}
})
})
}
},
beforeDestroy() {
let app = this;
if (app.innerAudioContext.stop) {
app.innerAudioContext.stop()
}
if (app.timer) {
clearInterval(app.timer)
app.timer = null;
}
},
onShow() {
},
methods: {
getserchInput(){
//
uni.onKeyboardHeightChange(res => {
if (res.height > 0) {
console.log(res.height,'禁停')
// app.keyHeight = res.height
// app.isInput = true;
} else {
// app.isInput = false;
}
})
},
setAit(item) {
let app = this;
app.showAitFlag = false;
//,便
let curEnd = app.chatInfo.slice(app.txtIndex)
let curStr = app.chatInfo.slice(0, app.txtIndex)
if (item) {
app.chatInfo = curStr + item.nickName + '\t' + curEnd
} else {
app.chatInfo = curStr + '所有人' + '\t' + curEnd
}
console.log(item)
},
//
getKeyHeight() {
// if (this.isIos()) {
// if (this.isScreen) {
// return (this.keyHeight - 35)
// } else {
// return (this.keyHeight - 5)
// }
// } else {
// // return this.keyHeight
// return 0
// }
return 0
},
async sendMsg() {
let app = this;
let ids = []
let isAt = false;
if (app.sendType === 2) {
//@\t
let reg = /@.*?\\t/gi;
let result = JSON.stringify(app.chatInfo).match(reg)
if (result) {
for (let i = 0; i < result.length; i++) {
let el = result[i];
let cur = el.slice(1, el.length - 2)
if (cur == '所有人') {
app.groupPeoList.forEach(val => {
ids.push(val.userNo)
})
break;
} else {
app.groupPeoList.forEach(val => {
if (val.nickName == cur) {
ids.push(val.userNo)
}
})
}
console.log(cur)
}
}
isAt = ids.length > 0 ? true : false;
}
if (!app.sendFlag) {
return false;
}
app.sendFlag = false;
let res;
if (!app.chatInfo) {
uni.showToast({
icon: 'none',
title: "请输入聊天内容!"
})
app.sendFlag = true;
return false;
}
if (app.sendType === 1) {
//
res = await sendTextMsg({
content: app.chatInfo,
fromUserNo: app.userNo,
toUserNo: app.toUserNo
})
} else {
//
res = await sendTextMsg({
content: app.chatInfo,
fromUserNo: app.userNo,
groupId: app.toUserNo,
atUsers: ids,
isAt: isAt
})
}
if (res.code === 200) {
app.chatInfo = '';
uni.$emit('scoket', JSON.stringify(res.data))
// app.audioShow = false;
// app.moreShow = false;
// app.emojiShow = false;
} else {
uni.showToast({
icon: 'none',
title: res.msg
})
}
app.sendFlag = true;
},
chooseImage() {
let app = this;
uni.chooseImage({
count: 1, //9
sizeType: ['original', 'compressed'], //
sourceType: ['album', 'camera'], //
success: function(res) {
console.log(res.tempFilePaths[0]);
let filePath = res.tempFilePaths[0]
app.uploadFile(filePath, 1)
}
});
},
chooseFile() {
let app = this;
if (this.isIos()) {
const iOSFileSelect = uni.requireNativePlugin('YangChuan-YCiOSFileSelect');
let params = {
"document-types": ["public.text", "public.zip", "public.movie", "public.data", "com.adobe.pdf",
"com.microsoft.word.doc", "com.adobe.postscript", "com.microsoft.excel.xls",
"com.adobe.encapsulated- postscript", "com.microsoft.powerpoint.ppt",
"com.adobe.photoshop- image", "com.microsoft.word.rtf",
"com.microsoft.advanced- systems-format", "com.microsoft.advanced- stream-redirector"
],
"isBase64": 0
}
iOSFileSelect.show(params, result => {
let filePath = result.url
console.log(result,'result')
let fileName = result.lastName
let fileSize = result.version
app.uploadFile(filePath, 4,fileName,fileSize)
})
} else {
const plugin = uni.requireNativePlugin('GuoWei-SelectFileModule')
plugin.chooseFile({
count: 1
},
result => {
let filePath = result.files[0].url
let fileName = result.files[0].name
let fileSize = result.files[0].size
console.log('-----im文件选择', result)
app.uploadFile(filePath, 4, fileName, fileSize)
}
)
}
},
startAudio() {
console.log('开始录音', this.recorderManager);
let app = this
clearInterval(app.timer)
app.timer = null;
app.stamp = new Date().getTime()
uni.vibrateShort({
success: function() {
console.log('开始录音2', this.recorderManager);
app.timer = setInterval(() => {
uni.showToast({
icon: 'none',
title: '录音时间过长!'
})
app.endAudio()
app.$emit('longStop')
}, 60000)
console.log('开始录音3', this.recorderManager);
app.recorderManager.start();
app.audioText = '松开停止'
}
});
},
endAudio(val = false) {
let app = this;
app.voiceCel = val;
if (app.timer) {
console.log('录音结束');
clearInterval(app.timer)
app.timer = null;
app.recorderManager.stop();
app.audioText = '点击播放'
}
},
playVoice() {
console.log('播放录音');
// console.log('this.voicePath', this.voicePath, this.audioStart);
if (!this.audioStart) {
this.audioStart = true
this.innerAudioContext.src = this.voicePath[0];
this.innerAudioContext.play();
this.audioText = '暂停播放'
} else if (this.audioStart) {
this.audioStart = false
this.innerAudioContext.stop()
this.audioText = '点击播放'
}
},
cancelAudio() {
let app = this;
// app.audioShow = false;
app.moreShow = false;
app.emojiShow = false;
app.timer = null;
app.innerAudioContext.stop()
app.voicePath.length > 0 ? app.voicePath.shift() : app.voicePath = []
app.audioStart = false;
app.audioText = '长按录音'
},
sendAudio() {
let app = this;
if (app.voicePath.length == 1) {
app.uploadFile(app.voicePath[0].path, 2)
}
},
uploadFile(filePath, type, fileName = '', fileSize = 0) {
let header = {}
let app = this;
if (uni.getStorageSync('loginToken')) {
let token = uni.getStorageSync('loginToken');
header['Authorization'] = token.tokenHead + token.token;
header['R-Authorization'] = token.tokenHead + token.refreshToken;
// header['Content-Type'] = 'multipart/form-data'
}
let scoketParams = {
isLoading: 'load',
fromUserNo: app.userNo,
toUserNo: app.sendType == 1 ? app.toUserNo : '',
groupId: app.sendType == 2 ? app.toUserNo : '',
type: type,
fileName: fileName,
fileSize: fileSize,
filePath: filePath,
audioSeconds: app.voicePath && app.voicePath.length > 0 ? app.voicePath[0].duration : ''
}
console.log('ios',scoketParams)
uni.$emit('scoket', JSON.stringify(scoketParams))
uni.uploadFile({
url: baseUrl + '/edu-im/file/upload',
filePath: filePath,
header: header,
name: "file",
success: function(res) {
let resData = JSON.parse(res.data).data
let params = {
content: resData.content,
fileName: resData.filename,
size: resData.size,
fromUserNo: app.userNo,
toUserNo: app.sendType == 1 ? app.toUserNo : '',
groupId: app.sendType == 2 ? app.toUserNo : '',
type: type,
audioSeconds: app.voicePath && app.voicePath.length > 0 ? app.voicePath[0]
.duration : ''
}
sendSpecialMessage(params).then(ret => {
console.log('ret', ret)
if (ret.code === 200) {
console.log('-----------------im发送图片', ret)
app.chatInfo = '';
uni.$emit('scoket', JSON.stringify(ret.data))
app.innerAudioContext.stop()
// app.audioShow = false;
app.moreShow = false;
app.emojiShow = false;
app.audioStart = false;
app.audioText = '长按录音'
if (app.voicePath.length > 1) {
app.voicePath.shift()
setTimeout(() => {
app.sendAudio()
}, 1000)
} else {
app.voicePath = []
}
} else {
uni.showToast({
icon: 'none',
title: ret.msg
})
}
})
},
fail: function(error) {
// uni.showToast({
// title: "", //
// duration: 3000, //
// icon: "none", //"success""loading"
// });
scoketParams.isLoading = 'fail'
uni.$emit('scoket', JSON.stringify(scoketParams))
}
})
},
chatCollect() {
uni.navigateTo({
url: '/pages/im/chat/chatCollect'
})
},
goQuick() {
uni.navigateTo({
url: '/pages/im/quick/index?sendType=' + this.sendType + '&toUserNo=' + this.toUserNo
})
},
getEmoji() {
return [...emojiData[0].list, ...emojiData[1].list, ...emojiData[4].list]
},
setEmoji(data) {
let str = ' ' + this.entitiestoUtf16(data) + ' '
this.chatInfo += str;
},
onInput() {
this.emojiShow = false;
this.audioShow = false;
this.moreShow = false;
this.$emit('setInput', false)
},
setMoreShow() {
this.moreShow = !this.moreShow;
this.emojiShow = false;
this.audioShow = false;
this.$emit('setInput', this.moreShow)
},
setAudioShow() {
this.audioShow = !this.audioShow;
this.emojiShow = false;
this.moreShow = false;
this.$emit('setInput', false)
// this.$emit('setInput', this.audioShow)
},
setEmojiShow() {
// this.chatInfo = '@\t@asdadad1\t2 '
// console.log(JSON.stringify(this.chatInfo))
// return false;
this.emojiShow = !this.emojiShow;
this.moreShow = false;
this.audioShow = false;
this.$emit('setInput', this.emojiShow)
},
utf16toEntities(val) {
return utf16toEntities(val)
},
entitiestoUtf16(val) {
return entitiestoUtf16(val)
},
},
watch: {
audioShow: function(n, o) {
this.$emit('onAudioShow', n)
},
chatInfo: function(n, o) {
let app = this;
if (app.sendType != 2) {
return false;
}
//
uni.getSelectedTextRange({
success: res => {
app.txtIndex = res.end;
//,便
let curEnd = n.slice(res.end)
let curStr = n.slice(0, res.end)
/*
新增艾特相关
*/
let oldStr1 = o.slice(0, res.end)
if (curStr == oldStr1 + '@') {
app.aitKeyword = '';
app.showAitFlag = true;
uni.hideKeyboard()
}
/*
删除艾特相关
*/
//
let oldStr = o.slice(0, res.end + 1)
//\t,
if (curStr.length < oldStr.length && curStr + '\t' == oldStr) {
//
let num = curStr.lastIndexOf('@')
let nowStr = curStr.slice(0, num)
app.chatInfo = nowStr + curEnd;
console.log('删除艾特')
}
}
})
}
}
}
</script>
<style lang="scss">
// .isInput {
// bottom: 570rpx !important;
// }
.chat-bottom {
position: fixed;
bottom: 0;
width: 100%;
min-height: 148rpx;
background-color: #fff;
display: flex;
flex-flow: column;
.bottom-operation {
width: 100%;
display: flex;
align-items: flex-end;
padding: 17px 30rpx;
}
.goVoice {
width: 52rpx;
height: 52rpx;
margin-right: 10rpx;
margin-bottom: 15rpx;
image {
width: 100%;
height: 100%;
}
}
.textarea {
height: 80rpx;
max-height: 200rpx !important;
overflow: auto;
width: 490rpx;
line-height: 80rpx;
background: #F8F8F8;
border-radius: 40rpx;
padding: 0 40rpx;
}
.textarea2 {
width: 420rpx;
line-height: 48rpx;
min-height: 80rpx;
/deep/.uni-textarea-textarea{
padding-top: 16rpx;
}
}
.audio {
flex: 1;
background: #F8F8F8;
border-radius: 39rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333;
height: 78rpx;
text-align: center;
line-height: 78rpx;
}
/deep/.u-input {
flex: 1;
background: #F8F8F8;
border-radius: 39rpx;
font-size: 28rpx;
font-family: PingFang SC;
font-weight: 500;
color: #333;
max-height: 200rpx !important;
overflow-y: auto;
.u-input__textarea {
display: flex;
align-items: center;
box-sizing: border-box;
padding: 20rpx 40rpx;
max-height: 200rpx;
}
}
.goexpression {
width: 52rpx;
height: 53rpx;
margin-left: 15rpx;
margin-right: 19rpx;
margin-bottom: 15rpx;
image {
width: 100%;
height: 100%;
}
}
.more {
width: 53rpx;
height: 53rpx;
margin-bottom: 15rpx;
image {
width: 100%;
height: 100%;
}
}
.send-btn {
display: flex;
align-items: center;
justify-content: center;
width: 120rpx;
height: 60rpx;
background-color: #fb3a4e;
color: #fff;
font-size: 28rpx;
border-radius: 10rpx;
margin-bottom: 15rpx;
transition: all .3s;
}
.operation-box {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-around;
background-color: #f8f8f8;
padding: 0 30rpx;
.operation-item {
width: 120rpx;
padding: 30rpx 0;
.item-btn {
display: flex;
align-items: center;
justify-content: center;
width: 120rpx;
height: 120rpx;
background-color: #fff;
border-radius: 16rpx;
image {
width: 50rpx;
height: 50rpx;
}
}
.item-hint {
text-align: center;
font-size: 24rpx;
font-family: PingFang SC;
font-weight: 500;
color: #999999;
margin-top: 13rpx;
}
}
}
}
.bottom-box {
max-height: 300rpx;
overflow-y: auto;
}
.emoji-box {
padding: 0 30rpx 30rpx;
.emoji {
margin: 10rpx;
font-size: 48rpx;
}
}
.audio-box {
height: 300rpx;
display: flex;
justify-content: center;
align-items: center;
.circle {
margin: 0 100rpx;
width: 150rpx;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.audio-icon {
width: 120rpx;
height: 120rpx;
border-radius: 50%;
margin-bottom: 20rpx;
}
}
}
.ait-box {
width: 750rpx;
height: 850rpx;
background-color: #fff;
z-index: 999;
.ab-title {
position: relative;
display: flex;
justify-content: center;
align-items: center;
margin-top: 40rpx;
.abt-close {
position: absolute;
top: 10rpx;
left: 40rpx;
}
.abt-txt {
font-size: 28rpx;
font-family: PingFang SC-Medium, PingFang SC;
font-weight: 500;
color: #000000;
}
}
.ab-item {
width: 750rpx;
height: 120rpx;
display: flex;
padding: 0 40rpx;
align-items: center;
&:hover {
background-color: #EEEEEE;
}
.abi-all {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 30rpx;
background-color: #FFEBED;
text-align: center;
line-height: 80rpx;
font-size: 36rpx;
font-family: SF Pro Display-Medium, SF Pro Display;
font-weight: 500;
color: #FB3A4E;
}
.abi-img {
width: 80rpx;
height: 80rpx;
border-radius: 50%;
margin-right: 30rpx;
}
.abi-name {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #333333;
}
}
}
.ipt-wrp {
margin: 18rpx auto;
position: relative;
width: 690rpx;
.ipt-icon {
position: absolute;
left: 28rpx;
height: 24rpx;
width: 24rpx;
top: 20rpx;
}
.ipt {
width: 100%;
height: 64rpx;
background: #f6f6f6;
border-radius: 32rpx;
font-size: 28rpx;
color: #999;
padding-left: 76rpx;
}
}
</style>

@ -0,0 +1,309 @@
<!--
* @Author: ch
* @Date: 2022-03-26 14:32:03
* @LastEditors: ch
* @LastEditTime: 2022-05-14 18:35:48
* @Description: file content
-->
<template>
<view class="main">
<template v-for="item in msgData">
<view class="send" :key="item.id" v-if="item.fromId == $store.state.userInfo.id">
<template v-if="item.type == MSG_TYPE.CUSTOM">
<GoodsInfo class="send--box" position="msg" v-if="item.payload.id" :goodsInfo="item.payload"/>
<OrderInfo class="send--box" position="msg" v-if="item.payload.orderId" :orderInfo="item.payload"/>
</template>
<view class="send--box send--box__txt" v-if="item.type == MSG_TYPE.TXT">{{item.payload.text}}</view>
<img class="send--box send--box__img" mode="aspectFit" v-if="item.type == MSG_TYPE.IMG"
:src="item.payload.url" @click="previewImg(item.payload.url)"/>
<view class="send--box" v-if="item.type == MSG_TYPE.VIDEO">
<video class="send--box__video" :src="item.payload.url"></video>
</view>
<image class="avatar" :src="item.fromAvatar || '@/static/message/xt.png'" mode="widthFix"/>
</view>
<view class="tips" :key="item.id" v-else-if="item.type == MSG_TYPE.TIP">
<view class="tips--box">{{item.payload.text}}</view>
</view>
<view class="receive" :key="item.id" v-else>
<image class="avatar" :src="item.fromAvatar || '@/static/message/xt.png'" mode="widthFix"/>
<view>
<view class="receive--name">{{item.fromNickname}}</view>
<template v-if="item.type == MSG_TYPE.CUSTOM">
<GoodsInfo class="receive--box" position="msg" v-if="item.payload.id" :goodsInfo="item.payload"/>
<OrderInfo class="receive--box" position="msg" v-if="item.payload.orderId" :orderInfo="item.payload"/>
</template>
<view class="receive--box receive--box__txt" v-if="item.type == MSG_TYPE.TXT">{{item.payload.text}}</view>
<img class="receive--box receive--box__img" mode="aspectFit" v-if="item.type == MSG_TYPE.IMG"
:src="item.payload.url" @click="previewImg(item.payload.url)"/>
<view class="receive--box" v-if="item.type == MSG_TYPE.VIDEO">
<video class="receive--box__video" :src="item.payload.url"></video>
</view>
</view>
</view>
</template>
<Footer :goodsInfo="goodsInfo" :orderInfo="orderInfo"></Footer>
</view>
</template>
<script>
import {MSG_TYPE} from '@/common/dicts/im';
import {MsbWebSkt, createUUID} from '@/common/utils';
import {ApiGetOrderDetail} from '@/common/api/order';
import {ApiGetGoodsDetail} from '@/common/api/goods';
import UiButton from '@/components/UiButton.vue';
import Footer from './components/Footer.vue';
import GoodsInfo from './components/GoodsInfo.vue';
import OrderInfo from './components/OrderInfo.vue';
export default {
components: { UiButton, Footer, GoodsInfo, OrderInfo },
data(){
return {
MSG_TYPE,
orderId : null,
goodsId : null,
sessionId : null,
orderInfo : {},
goodsInfo : {}
}
},
computed:{
curSessionData(){
return this.$store.state.sessionData.find(i =>i.id == this.$store.state.sessionMsgId) || {}
},
msgData (){
return this.curSessionData ? this.curSessionData.messageList : [];
}
},
watch:{
msgData(){
console.log('------+++-');
this.$nextTick(()=>{
uni.pageScrollTo({
scrollTop : 99999,
duration : 0
});
});
}
},
onPageScroll(res) {
// && !this.isInput
if (res.scrollTop <= 0) {
this.getHistoryMsg();
}
},
onLoad(){
if(!this.$store.state.token){
this.$Router.push('/login');
return false;
}
this.goodsId = this.$Route.query.goodsId;
this.orderId = this.$Route.query.orderId;
this.sessionId = this.$Route.query.sessionId;
if(this.goodsId){
this.createSessionMain();
this.getGoodsInfo();
}else if(this.orderId){
this.createSessionMain();
this.getOrderInfo();
}else if(this.sessionId){
this.$store.commit('SET_SESSION_MSG_ID',this.sessionId);
this.getHistoryMsg();
this.readMsg();
this.heartMsg();
}else{
this.createSessionMain();
}
},
methods:{
/**
* 创建会话主体
* 如果是从商品或订单进来需要创建会话
*/
createSessionMain(){
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : '21',
content : {
storeId : 1
}
}),
success:()=>{
//
setTimeout(()=>{
this.getHistoryMsg();
this.readMsg();
this.heartMsg();
},1000)
}
});
},
/**
* 发送心跳
*/
heartMsg(){
return false;
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : '22',
content : {
storeId : 1
}
}),
complete: () =>{
setTimeout(()=>{
this.heartMsg();
},1000000)
}
})
},
/**
* 获取历史消息
*/
getHistoryMsg(){
this.loading = true;
const lastMsg = this.msgData?.length ? this.msgData[0] : {};
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "23",
content: {
sessionId : this.$store.state.sessionMsgId,
topMessageId : lastMsg.id || null
}
})
})
},
/**
* 已读消息
*/
readMsg(){
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "6",
content: {
sessionId : this.$store.state.sessionMsgId
}
})
});
//
const count = this.$store.state.sessionMsgCount - this.curSessionData.unreadCount;
//
this.curSessionData.unreadCount = 0;
this.$store.commit('SET_SESSION_MSG_COUNT', count)
},
/**
* 从订单页进来查询订单信息
*/
async getOrderInfo(){
const {error, result} = await ApiGetOrderDetail(this.orderId);
if(error){
uni.$u.toast(error.message);
return false;
}
this.orderInfo = result;
},
/**
* 从商品进来查询查询商品信息
*/
async getGoodsInfo(){
const {error, result} = await ApiGetGoodsDetail({id : this.goodsId});
if(error){
uni.$u.toast(error.message);
return false;
}
this.goodsInfo = result;
},
previewImg(logourl) {
let imgsArray = [];
imgsArray[0] = logourl
uni.previewImage({
current: 0,
urls: imgsArray
});
}
}
}
</script>
<style lang="scss">
page{
background: $color-grey1;
padding-bottom: 140px;
}
</style>
<style lang="scss" scoped>
.main{
padding: 40rpx;
}
.avatar{
width: 70rpx;
height: 70rpx;
}
.receive{
display: flex;
justify-content: flex-start;
margin-top:30rpx ;
&--name{
margin:0 0 20rpx 30rpx;
font-size: 24rpx;
color: #333;
}
&--box{
max-width: 552rpx;
margin-left:30rpx;
&__txt{
padding: 20rpx 30rpx;
background: #fff;
border-radius: 16rpx;
color: #333;
font-size: 32rpx;
line-height: 40rpx;
}
&__img{
height: 140rpx;
}
}
}
.send{
display: flex;
justify-content: flex-end;
margin-top:30rpx ;
&--box{
max-width: 552rpx;
margin-right:30rpx;
&__txt{
padding: 20rpx 30rpx;
background: #FF875B;
border-radius: 16rpx;
color: #fff;
font-size: 32rpx;
line-height: 46rpx;
}
&__img{
height: 140rpx;
}
&__video{
max-width: 552rpx;
}
}
}
.tips{
margin-top:30rpx ;
display: flex;
justify-content: center;
&--box{
background: #eee;
color: #9E9E9E;
text-align: center;
border-radius: 30rpx;
padding: 10rpx 30rpx;
line-height: 32rpx;
}
}
</style>

@ -2,11 +2,11 @@
* @Author: ch
* @Date: 2022-03-22 16:13:00
* @LastEditors: ch
* @LastEditTime: 2022-04-28 23:02:54
* @LastEditTime: 2022-05-14 16:32:09
* @Description: file content
-->
<template>
<view>
<view >
<BsEmpty tips="暂时还没有消息哦~" v-if="!$store.state.sessionData.length"
:icon="require('@/static/message/empty.png')" />
<view class="msgItem" v-for="item in $store.state.sessionData" :key="item.id"
@ -15,10 +15,15 @@
<view class="msgItem--con">
<view>
<text class="msgItem--title">{{item.fromNickname}}</text>
<text class="msgItem--text">{{item.lastMessage.payload.content}}</text>
<text class="msgItem--text" v-if="item.lastMessage">
<template v-if="item.lastMessage.type == msgType.TXT">{{item.lastMessage.payload.content || item.lastMessage.payload.text}}</template>
<template v-if="item.lastMessage.type == msgType.CUSTOM">[]</template>
<template v-if="item.lastMessage.type == msgType.IMG">[]</template>
<template v-if="item.lastMessage.type == msgType.VIDEO">[]</template>
</text>
</view>
<view class="msgItem--right">
<text class="msgItem--time">{{item.lastMessage.createTimeStamp}}</text>
<text class="msgItem--time" v-if="item.lastMessage">{{item.lastMessage.createTimeStamp}}</text>
<u-badge class="msgItem--badge" :value="item.unreadCount"></u-badge>
</view>
</view>
@ -27,17 +32,29 @@
</template>
<script>
import BsEmpty from '../../../components/BsEmpty.vue';
import {MSG_TYPE} from '@/common/dicts/im';
export default {
components: { BsEmpty },
data (){
return {
msgType : MSG_TYPE,
sessionData : this.$store.state.sessionData
}
},
onShow(){
if(!this.$store.state.token){
this.$Router.push('/login');
}
},
methods:{
openMsg(item){
this.$store.commit('SET_SESSION_MSG_ID',item.id);
this.$Router.push(`/messageList`);
this.$store.commit('SET_SESSION_MSG_ID', item.id);
if(item.type === 3){
this.$Router.push(`/messageChat?sessionId=${item.id}`);
}else{
this.$Router.push(`/messageSystem`);
}
}
}
}

@ -2,11 +2,11 @@
* @Author: ch
* @Date: 2022-03-26 14:32:03
* @LastEditors: ch
* @LastEditTime: 2022-04-28 23:43:35
* @LastEditTime: 2022-05-10 21:21:18
* @Description: file content
-->
<template>
<view>
<view class="main">
<view class="msgItem" v-for="item in msgData" :key="item.id">
<view class="msgItem--title">
<text>{{item.payload.title }} {{item.id}}</text>

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-23 17:27:21
* @LastEditors: ch
* @LastEditTime: 2022-05-09 17:30:27
* @LastEditTime: 2022-05-13 15:43:04
* @Description: file content
-->
<template>
@ -144,7 +144,13 @@ export default {
this.showSkuPopup = true
},
service(){
uni.$u.toast('客服休息中,遇到喜欢的宝贝就下单吧~')
this.$Router.push({
path : '/messageChat',
query: {
goodsId : this.goods.id
}
})
// uni.$u.toast('~')
}
}

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-22 15:36:46
* @LastEditors: ch
* @LastEditTime: 2022-05-05 20:32:00
* @LastEditTime: 2022-05-14 18:22:38
* @Description: file content
-->
<template>
@ -31,7 +31,7 @@
</view>
</template>
<script>
import { IsPhone } from '@/common/utils';
import { IsPhone,MsbWebSkt, MsbWebSktInit, createUUID } from '@/common/utils';
import { ApiGetCode, ApiPostLogin } from '@/common/api/index';
import UiButton from '../components/UiButton.vue';
export default {
@ -103,6 +103,26 @@ export default {
return false;
}
this.$store.commit('SET_TOKEN',result.token);
MsbWebSktInit().then(()=>{
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "1",
content: { sysId : "1"}
})
});
setInterval(()=>{
MsbWebSkt.send({
data : JSON.stringify({
traceId : createUUID(),
traceType : "0",
content: { text : "ping"}
})
})
}, 5000000);
})
this.goBack();
},
/**

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-04-15 17:46:10
* @LastEditors: ch
* @LastEditTime: 2022-05-07 19:08:54
* @LastEditTime: 2022-05-13 17:57:38
* @Description: file content
-->
<template>
@ -10,7 +10,7 @@
<!-- 支付过后都可查看物流 -->
<UiButton class="footer--btn" v-if="orderInfo.orderStatus >= 4" @click="$Router.push(`/logisitcsInfo?orderId=${orderInfo.orderId}`)"></UiButton>
<!-- 交易关闭可以联系客服 -->
<UiButton class="footer--btn" v-if="orderInfo.orderStatus === 2"></UiButton>
<UiButton class="footer--btn" v-if="orderInfo.orderStatus === 2" @click="service"></UiButton>
<!-- 已发货可以确认收货 -->
<UiButton class="footer--btn" v-if="orderInfo.orderStatus === 4" type="gradual" @click="receive"></UiButton>
<!-- 待支付可以取消支付订单 -->
@ -124,6 +124,14 @@ export default {
// }
// })
},
service(){
this.$Router.push({
path : '/messageChat',
query : {
orderId : this.orderInfo.orderId
}
})
}
}
}

@ -2,7 +2,7 @@
* @Author: ch
* @Date: 2022-03-31 14:26:09
* @LastEditors: ch
* @LastEditTime: 2022-04-29 23:05:59
* @LastEditTime: 2022-05-13 15:04:20
* @Description: file content
-->
<template>
@ -35,14 +35,14 @@
</view>
</template>
<script>
import UiGoodsInfo from '../../../components/UiGoodsInfo.vue'
import UiWhiteBox from '../../../components/UiWhiteBox.vue'
import UiButton from '../../../components/UiButton.vue'
import {ApiGetOrderDetail, ApiPutCancelOrder} from '@/common/api/order'
import StatusTips from './components/StatusTips.vue'
import OrderInfo from './components/OrderInfo.vue'
import LogisitcsInfo from './components/LogisitcsInfo.vue'
import Operation from './components/Operation.vue'
import UiGoodsInfo from '@/components/UiGoodsInfo.vue';
import UiWhiteBox from '@/components/UiWhiteBox.vue';
import UiButton from '@/components/UiButton.vue';
import {ApiGetOrderDetail, ApiPutCancelOrder} from '@/common/api/order';
import StatusTips from './components/StatusTips.vue';
import OrderInfo from './components/OrderInfo.vue';
import LogisitcsInfo from './components/LogisitcsInfo.vue';
import Operation from './components/Operation.vue';
export default {
components: { UiGoodsInfo, UiWhiteBox, UiButton, StatusTips, OrderInfo, LogisitcsInfo, Operation },
data(){

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 872 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 900 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 795 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 473 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 815 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 404 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

@ -93,4 +93,17 @@ $uni-spacing-col-base: 8px;
$uni-spacing-col-lg: 12px;
@mixin ellipsis() {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
@mixin ellipses($val) {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: $val;
-webkit-box-orient: vertical;
}

Loading…
Cancel
Save