Files
ClassContent/项目合集/聊天室/app版本/代码/fontend/pages/index/index.vue
2024-09-27 02:06:13 +08:00

546 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>