546 lines
14 KiB
Vue
546 lines
14 KiB
Vue
<template>
|
||
<view class="index" :class="{ shake: isShake }">
|
||
<uni-nav-bar
|
||
right-icon="more"
|
||
:statusBar="true"
|
||
@clickRight="OpenChatSetting"
|
||
:title="`${UserInfo.room}(${RoomOnlineUser.length})`"
|
||
>
|
||
</uni-nav-bar>
|
||
|
||
<!-- 聊天记录显示窗口 -->
|
||
<scroll-view
|
||
id="scrollview"
|
||
class="chat_record"
|
||
scroll-y
|
||
:scroll-with-animation="true"
|
||
:scroll-top="scrollTop"
|
||
:style="{ height: style.contentViewHeight + 'px' }"
|
||
>
|
||
<view v-for="(item, index) in ChatRecord" :key="index">
|
||
<view
|
||
class="news"
|
||
v-if="item.from != 'system'"
|
||
:class="item.from == 'me' ? 'right' : ''"
|
||
>
|
||
<image class="news_img" :src="item.img"></image>
|
||
<view class="news_text">
|
||
<view v-if="status" class="news_name">{{ item.name }}</view>
|
||
<view v-if="item.message.includes('gif')" class="news_content">
|
||
<image
|
||
@click="previewImage(index)"
|
||
class="content_img"
|
||
:src="item.message"
|
||
></image>
|
||
</view>
|
||
|
||
<view v-else v-html="item.message"></view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="news" v-else>
|
||
<view class="system">{{ item.message }}</view>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<!-- 底部输入框 -->
|
||
<view class="bottom_operation" :style="{ height: operationHeight + 'rpx' }">
|
||
<view class="input_list">
|
||
<input
|
||
class="uni-input"
|
||
type="text"
|
||
v-model="UserMessage"
|
||
:cursor="cursorPosition"
|
||
confirm-type="send"
|
||
@confirm="SendMessage('text')"
|
||
@touchend.stop="touchendPrevent"
|
||
@focus="FocusEvent"
|
||
@blur="BlurEvent"
|
||
:cursor-spacing="10"
|
||
placeholder="请输入内容"
|
||
/>
|
||
</view>
|
||
|
||
<view class="menu">
|
||
<text
|
||
v-for="(item, index) in menuList"
|
||
:key="index"
|
||
@touchend.stop.prevent="menuItemClick(item, index)"
|
||
class="iconfont"
|
||
:class="[index == clickMenuIndex ? 'active' : '', item.icon]"
|
||
>
|
||
</text>
|
||
</view>
|
||
|
||
<view class="menu_popup" v-show="isShow">
|
||
<!--表情的菜单键盘-->
|
||
<view class="popup smiling_popup" v-show="popupStatus == 1">
|
||
<view v-for="(v, i) in emojis" :key="i">
|
||
<text
|
||
v-for="(item, index) in v"
|
||
:key="index"
|
||
@click="setInputText(item)"
|
||
>{{ item }}</text
|
||
>
|
||
</view>
|
||
</view>
|
||
|
||
<!--图片的菜单键盘-->
|
||
<view class="popup gallery_popup" v-show="popupStatus == 2">
|
||
<image
|
||
v-for="i in 13"
|
||
@click="SendMessage('img', i - 1)"
|
||
:src="`../../static/gif/${i - 1}.gif`"
|
||
:key="i - 1"
|
||
>
|
||
</image>
|
||
</view>
|
||
|
||
<!--气泡的菜单键盘-->
|
||
<view class="popup qipao_popup" v-show="popupStatus == 3">
|
||
<view
|
||
class="theme_item"
|
||
@click="selectTheme(v)"
|
||
:style="{ color: v.textColor, backgroundColor: v.background }"
|
||
v-for="(v, i) in theme"
|
||
:key="i"
|
||
>
|
||
{{ v.text }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import config from "@/config/index.js";
|
||
import { mapMutations, mapGetters } from "vuex";
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
newsContentStyle: {
|
||
backgroundColor: "",
|
||
color: "",
|
||
},
|
||
theme: config.theme,
|
||
cursorPosition: 0, // 光标的位置
|
||
isShake: false, // 是否抖动
|
||
scrollTop: "", // 页面滚动距离
|
||
style: {
|
||
contentViewHeight: 0,
|
||
itemHeight: 0,
|
||
},
|
||
UserInfo: {}, // 用户登录信息
|
||
UserMessage: "", // 用户输入的消息内容
|
||
emojis: config.emoji, // 表情
|
||
RoomOnlineUser: [], // 在线用户数
|
||
operationHeight: 160,
|
||
isShow: false, // 是否显示 底部弹出菜单
|
||
popupStatus: 1, // 1 表情,2 图片,3 气泡
|
||
clickMenuIndex: -1, // 点击后的下标
|
||
menuList: [
|
||
{
|
||
icon: "icon-smiling",
|
||
status: 1,
|
||
},
|
||
{
|
||
icon: "icon-gallery",
|
||
status: 2,
|
||
},
|
||
{
|
||
icon: "icon-xinbaniconshangchuan_huaban",
|
||
status: 3,
|
||
},
|
||
{
|
||
icon: "icon-shake",
|
||
status: 4,
|
||
},
|
||
],
|
||
};
|
||
},
|
||
onBackPress(options) {
|
||
uni.showModal({
|
||
title: "提示",
|
||
content: "确定退出当前群聊吗?",
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this._.socket.emit("UserDisconnect", this.UserInfo);
|
||
plus.runtime.quit();
|
||
}
|
||
},
|
||
});
|
||
return true;
|
||
// this.backButtonPress++;
|
||
// if (this.backButtonPress > 1) {
|
||
// console.log("退出")
|
||
//
|
||
// } else {
|
||
// plus.nativeUI.toast("1111再按一次退出应用");
|
||
// }
|
||
// setTimeout(function () {
|
||
// this.backButtonPress = 0;
|
||
// }, 1000);
|
||
// return true;
|
||
},
|
||
onLoad() {
|
||
this.UserInfo = uni.getStorageSync("UserInfo");
|
||
},
|
||
created() {
|
||
const res = uni.getSystemInfoSync(); //获取手机可使用窗口高度 api为获取系统信息同步接口
|
||
this.style.contentViewHeight =
|
||
res.windowHeight - (uni.getSystemInfoSync().screenWidth / 750) * 100 - 70; //像素 因为给出的是像素高度 然后我们用的是upx 所以换算一下
|
||
|
||
this.emitSocket();
|
||
this.onSocket();
|
||
},
|
||
methods: {
|
||
...mapMutations({
|
||
saveOnLineUser: "SetOnLineUser",
|
||
saveChatRecord: "SetChatRecord",
|
||
}),
|
||
OpenChatSetting() {
|
||
uni.navigateTo({
|
||
url: "/pages/ChatSetting/ChatSetting",
|
||
});
|
||
},
|
||
previewImage(index) {
|
||
// 获取消息数组中你点击的图片地址
|
||
var url = this.ChatRecord[index].message;
|
||
// 存储过滤后的聊天记录,保存纯图片地址数组
|
||
var imgArr = [];
|
||
this.ChatRecord.filter((v) => {
|
||
v.message.includes(".gif") ? imgArr.push(v.message) : null;
|
||
});
|
||
// 从新imgArr数组中获取地址的新下标
|
||
uni.previewImage({
|
||
current: imgArr.indexOf(url),
|
||
urls: imgArr,
|
||
});
|
||
},
|
||
selectTheme(item) {
|
||
this.newsContentStyle.backgroundColor = item.background;
|
||
this.newsContentStyle.color = item.textColor;
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: `${item.text} 切换成功`,
|
||
duration: 1000,
|
||
});
|
||
},
|
||
// 发送图片
|
||
SendMessage(type, imgindex) {
|
||
// 解决浅拷贝导致发最新消息时候改变历史消息的问题
|
||
var obj = JSON.parse(JSON.stringify(this.UserInfo));
|
||
|
||
if (type == "text") {
|
||
if (this.UserMessage.length != 0) {
|
||
obj.message = `<div class="news_content" style="color: ${this.newsContentStyle.color}; background: ${this.newsContentStyle.backgroundColor}">${this.UserMessage}</div>`;
|
||
}
|
||
} else {
|
||
obj.message = `../../static/gif/${imgindex}.gif`;
|
||
}
|
||
|
||
obj.from = "me";
|
||
this._.socket.emit("SendMessage", obj);
|
||
this.saveChatRecord(obj);
|
||
this.UserMessage = "";
|
||
this.scrollToBottom(); //创建后调用回到底部方法
|
||
},
|
||
scrollToBottom() {
|
||
let that = this;
|
||
let query = uni.createSelectorQuery();
|
||
query.selectAll(".news").boundingClientRect();
|
||
query.select("#scrollview").boundingClientRect();
|
||
query.exec((res) => {
|
||
that.style.itemHeight = 0;
|
||
res[0].forEach(
|
||
(rect) =>
|
||
(that.style.itemHeight =
|
||
that.style.itemHeight + rect.height + 99999)
|
||
); //获取所有内部子元素的高度
|
||
setTimeout(() => {
|
||
if (that.style.itemHeight > that.style.contentViewHeight - 100) {
|
||
//判断子元素高度是否大于显示高度
|
||
that.scrollTop =
|
||
that.style.itemHeight - that.style.contentViewHeight; //用子元素的高度减去显示的高度就获益获得序言滚动的高度
|
||
}
|
||
}, 100);
|
||
});
|
||
},
|
||
onSocket() {
|
||
this._.socket.on("WelcomeUser", (res) => {
|
||
var obj = JSON.parse(JSON.stringify(this.UserInfo));
|
||
obj.from = "system";
|
||
obj.message = res;
|
||
this.saveChatRecord(obj);
|
||
this.scrollToBottom();
|
||
});
|
||
|
||
this._.socket.on("RoomOnline", (res) => {
|
||
this.saveOnLineUser(res);
|
||
this.RoomOnlineUser = res;
|
||
});
|
||
|
||
this._.socket.on("SetMessage", (res) => {
|
||
console.log("收到的消息为:", res);
|
||
res.from = "other";
|
||
this.saveChatRecord(res);
|
||
this.scrollToBottom();
|
||
});
|
||
|
||
this._.socket.on("StartShake", (res) => {
|
||
this.isShake = true;
|
||
setTimeout(() => {
|
||
this.isShake = false;
|
||
}, 2000);
|
||
});
|
||
},
|
||
emitSocket() {
|
||
this._.socket.emit("JoinRoom", this.UserInfo);
|
||
},
|
||
touchendPrevent() {
|
||
if (this.isShow) {
|
||
this.closedMenu();
|
||
}
|
||
},
|
||
setInputText(emoji) {
|
||
console.log(emoji);
|
||
let str = this.UserMessage.substring(0, this.cursorPosition);
|
||
this.UserMessage = this.UserMessage.replace(str, str + emoji);
|
||
this.cursorPosition += 2;
|
||
},
|
||
// 获取焦点的时候
|
||
FocusEvent(event) {
|
||
this.closedMenu();
|
||
},
|
||
// 失去焦点
|
||
BlurEvent(event) {
|
||
this.cursorPosition = event.target.cursor;
|
||
console.log("失去焦点 this.cursorPosition : ", this.cursorPosition);
|
||
},
|
||
menuItemClick(item, index) {
|
||
if (item.status <= 3) {
|
||
this.popupStatus = item.status;
|
||
if (index != this.clickMenuIndex) {
|
||
uni.hideKeyboard();
|
||
this.clickMenuIndex = index;
|
||
this.isShow = true;
|
||
this.operationHeight = 620;
|
||
} else {
|
||
this.closedMenu();
|
||
}
|
||
} else {
|
||
this.closedMenu();
|
||
uni.showToast({
|
||
icon: "none",
|
||
title: "正在发送抖动..",
|
||
duration: 1000,
|
||
});
|
||
this._.socket.emit("Shake", this.UserInfo);
|
||
console.log("发送抖动");
|
||
}
|
||
},
|
||
closedMenu() {
|
||
this.isShow = false;
|
||
this.operationHeight = 160;
|
||
this.clickMenuIndex = -1;
|
||
},
|
||
},
|
||
computed: {
|
||
...mapGetters({
|
||
ChatRecord: "GetChatRecord",
|
||
status: "GetIsShowUserName",
|
||
}),
|
||
},
|
||
};
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
@keyframes test {
|
||
0% {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
25% {
|
||
transform: translateX(50px);
|
||
}
|
||
|
||
50% {
|
||
transform: translateX(0);
|
||
}
|
||
|
||
75% {
|
||
transform: translateX(-50px);
|
||
}
|
||
|
||
100% {
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
|
||
.shake {
|
||
animation: test 0.1s reverse infinite;
|
||
}
|
||
|
||
.index {
|
||
width: 100vw;
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
.chat_record {
|
||
flex: 1;
|
||
overflow-y: scroll;
|
||
padding: 40rpx;
|
||
box-sizing: border-box;
|
||
|
||
.right {
|
||
flex-direction: row-reverse;
|
||
|
||
.news_name {
|
||
text-align: right;
|
||
}
|
||
|
||
.news_img {
|
||
margin-left: 20rpx;
|
||
}
|
||
|
||
/deep/ .news_content {
|
||
background: #def3fc;
|
||
}
|
||
}
|
||
|
||
.news {
|
||
display: flex;
|
||
margin-bottom: 50rpx;
|
||
|
||
.system {
|
||
text-align: center;
|
||
margin: 0 auto;
|
||
color: #878d98;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.news_img {
|
||
width: 80rpx;
|
||
height: 80rpx;
|
||
border-radius: 100%;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.news_text {
|
||
.news_name {
|
||
margin-bottom: 18rpx;
|
||
color: #606061;
|
||
font-size: 28rpx;
|
||
}
|
||
|
||
/deep/ .content_img {
|
||
max-width: calc(100vw - 348rpx);
|
||
}
|
||
|
||
/deep/ .news_content {
|
||
font-size: 32rpx;
|
||
padding: 24rpx;
|
||
border-radius: 24rpx;
|
||
box-sizing: border-box;
|
||
background: #f3f3f4;
|
||
max-width: calc(100vw - 288rpx);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.bottom_operation {
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-sizing: border-box;
|
||
|
||
// height: 160rpx;
|
||
// height: 620rpx;
|
||
.input_list {
|
||
height: 80rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
background: #ebeef0;
|
||
padding: 0 20rpx;
|
||
padding-top: 10rpx;
|
||
|
||
.uni-input {
|
||
background: #fff;
|
||
flex: 1;
|
||
height: 90%;
|
||
border-radius: 30rpx;
|
||
font-size: 28rpx;
|
||
box-sizing: border-box;
|
||
padding-left: 30rpx;
|
||
}
|
||
}
|
||
|
||
.menu {
|
||
height: 80rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
background: #ebeef0;
|
||
padding: 0 20rpx;
|
||
.active {
|
||
color: #fea674 !important;
|
||
}
|
||
.iconfont {
|
||
font-size: 40rpx;
|
||
}
|
||
}
|
||
|
||
.menu_popup {
|
||
flex: 1;
|
||
background: #fff;
|
||
padding: 20rpx 20rpx;
|
||
overflow-y: scroll;
|
||
|
||
.popup {
|
||
width: 100%;
|
||
height: 100%;
|
||
}
|
||
|
||
.qipao_popup {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
align-content: flex-start;
|
||
overflow-y: scroll;
|
||
.theme_item {
|
||
text-align: center;
|
||
line-height: 90rpx;
|
||
border-radius: 12rpx;
|
||
font-size: 26rpx;
|
||
width: 30%;
|
||
height: 90rpx;
|
||
margin-right: 20rpx;
|
||
margin-bottom: 20rpx;
|
||
box-sizing: border-box;
|
||
box-shadow: 6rpx 8rpx 14rpx #929292;
|
||
}
|
||
}
|
||
|
||
.gallery_popup {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
|
||
image {
|
||
width: 160rpx;
|
||
height: 160rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/deep/ .uni-navbar--border {
|
||
border: none;
|
||
}
|
||
</style>
|