first commit

This commit is contained in:
编码猿
2024-09-27 02:06:13 +08:00
commit 852d94fbb9
36760 changed files with 3274413 additions and 0 deletions

View File

@@ -0,0 +1 @@
a30393d4-dbc5-48a1-8a82-4a619c34bf47

View File

@@ -0,0 +1 @@
5323500b-635e-4f62-945e-99a28139e1fc

View File

@@ -0,0 +1,152 @@
<template>
<view class="chat_setting">
<view class="user_list border_bottom">
<view
class="user_item"
v-for="(item, index) in getOnlineUser"
:key="index"
>
<image class="user_img" :src="item.img"></image>
<view class="user_name">{{ item.name }}</view>
</view>
</view>
<uni-list class="border_bottom">
<uni-list-item
title="群聊名称"
:rightText="UserInfo.room"
></uni-list-item>
<uni-list-item
title="群昵称"
link
to="/pages/editName/editName"
:rightText="UserInfo.name"
></uni-list-item>
</uni-list>
<uni-list class="border_bottom">
<uni-list-item
title="显示群成员昵称"
:showSwitch="true"
:switchChecked="status"
@switchChange="showChange"
></uni-list-item>
</uni-list>
<view class="action_list">
<view class="action_list_item" @click="clear">清空聊天记录</view>
<view class="action_list_item" @click="exitChat">删除并退出</view>
</view>
</view>
</template>
<script>
import { mapMutations, mapGetters } from "vuex";
export default {
data() {
return {
UserInfo: {}, // 用户登录信息
};
},
onLoad() {
this.UserInfo = uni.getStorageSync("UserInfo");
},
created() {
console.log("getOnlineUser === : ", this.getOnlineUser);
},
methods: {
...mapMutations({
saveChatRecord: "SetChatRecord",
setUserName: "SetIsShowUserName",
}),
showChange(e) {
this.setUserName(e.value);
console.log("status === ", this.status);
},
exitChat() {
uni.showModal({
title: "提示",
content: "确定退出当前群聊吗?",
success: (res) => {
if (res.confirm) {
this._.socket.emit("UserDisconnect", this.UserInfo);
plus.runtime.quit();
}
},
});
},
clear() {
uni.showModal({
title: "提示",
content: "确定清除所有聊天记录吗?",
success: (res) => {
if (res.confirm) {
console.log("用户点击确定");
this.saveChatRecord("clear");
uni.showToast({
icon: "none",
title: "清除成功",
duration: 1000,
});
}
},
});
},
},
computed: {
...mapGetters({
getOnlineUser: "GetOnLineUser",
status: "GetIsShowUserName",
}),
},
};
</script>
<style lang="less" scoped>
.chat_setting {
/deep/ .uni-switch-input-checked {
border-color: #fea575 !important;
background-color: #fea575 !important;
}
.border_bottom {
border-bottom: 15.972rpx solid #ededed;
}
.user_list {
padding: 36rpx;
box-sizing: border-box;
display: flex;
flex-wrap: wrap;
.user_item {
width: 20%;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
margin-bottom: 38rpx;
.user_img {
width: 104rpx;
height: 104rpx;
margin-bottom: 14.58rpx;
border-radius: 12rpx;
}
.user_name {
color: #b0b0b0;
font-size: 26rpx;
}
}
}
.action_list {
.action_list_item {
height: 103rpx;
display: flex;
justify-content: center;
align-items: center;
color: #ef5852;
&:first-child {
border: 1rpx solid #e5e5e5;
}
}
}
}
</style>

View File

@@ -0,0 +1 @@
b9691411-bd75-4768-8d35-d555eb30e9bf

View File

@@ -0,0 +1,123 @@
<template>
<view class="editName">
<view class="content">
<view class="text_top">我在群里的昵称</view>
<view class="text_tips"
>昵称修改后,只会在此群显示,群内成员都可以看见。</view
>
<view class="editUserName">
<image :src="UserInfo.img"></image>
<input
class="uni-input"
v-model="NewUserName"
type="text"
@input="nameChange"
placeholder=""
value=""
/>
<icon
v-if="isShowClear"
type="clear"
size="15"
@click="NewUserName = ''"
/>
</view>
<div class="submit" :class="isActive ? 'active' : ''">完成</div>
</view>
</view>
</template>
<script>
export default {
data() {
return {
UserInfo: {}, // 用户登录信息
OldUserName: "",
NewUserName: "",
isActive: false,
isShowClear: true,
};
},
onLoad() {
this.UserInfo = uni.getStorageSync("UserInfo");
this.NewUserName = this.UserInfo.name;
this.OldUserName = this.UserInfo.name;
},
methods: {
nameChange() {
this.NewUserName.length == 0
? (this.isShowClear = false)
: (this.isShowClear = true);
if (this.NewUserName != this.OldUserName) {
this.isActive = true;
} else {
this.isActive = false;
}
},
},
};
</script>
<style lang="less" scoped>
.editName {
padding: 0 60rpx;
.content {
margin-top: 140rpx;
display: flex;
flex-direction: column;
align-items: center;
.text_top {
margin-bottom: 45rpx;
font-weight: 700;
font-size: 36rpx;
}
.text_tips {
text-align: center;
color: #191919;
font-size: 32rpx;
margin-bottom: 65rpx;
}
.editUserName {
width: 100%;
border-top: 0.5rpx solid #e5e5e5;
border-bottom: 0.5rpx solid #e5e5e5;
height: 105rpx;
display: flex;
align-items: center;
image {
width: 75rpx;
height: 75rpx;
border-radius: 12rpx;
margin-right: 30rpx;
}
input {
flex: 1;
height: 75rpx;
}
}
.active {
color: #fff !important;
background: #08c05f !important;
}
.submit {
width: 345rpx;
height: 75rpx;
background: #f2f2f2;
color: #c2c2c2;
display: flex;
align-items: center;
justify-content: center;
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 170rpx;
border-radius: 10rpx;
font-size: 32rpx;
}
}
}
</style>

View File

@@ -0,0 +1 @@
9a38ff25-ce1f-4bc0-bb92-8bda10572882

View File

@@ -0,0 +1,545 @@
<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>

View File

@@ -0,0 +1 @@
0cd595e0-f395-4e7e-adc2-21a9bdbc79ac

View File

@@ -0,0 +1,239 @@
<template>
<view class="login">
<image mode="widthFix" src="@/static/orage.png" class="right"></image>
<image mode="widthFix" src="@/static/blue.png" class="bottom"></image>
<view class="login_text">
<view class="text">iChat</view>
<view class="tips">还您纯粹的聊天体验..</view>
</view>
<view class="login_input">
<uni-icons v-if="SelectImgUrl.length == 0" type="plusempty" size="40" @click="open"></uni-icons>
<image @click="open" class="portraitImg" v-else :src="SelectImgUrl"></image>
<input class="user_name" v-model="UserName" type="text" placeholder="昵称" />
<input class="user_pwd" type="text" v-model="UserRoom" placeholder="群号" />
<image class="enter" src="@/static/enter.png" @click="EnterInfo"></image>
</view>
<uni-popup ref="popup" type="dialog">
<uni-popup-dialog type="info" title="请选择头像" :duration="2000"
@close="close" @confirm="confirm"
:before-close="true">
<view class="portrait_list">
<image mode="widthFix" :class="index == currentIndex ? 'select' : ''" v-for="(item, index) in config.portrait"
@click="SelectImg(item, index)" :src="'../../static/' + item" :key="index">
</image>
</view>
</uni-popup-dialog>
</uni-popup>
</view>
</template>
<script>
import config from "@/config/index.js";
export default {
data() {
return {
UserName: "",
UserRoom: "",
config: config,
currentIndex: 0,
SelectImgUrl: "",
};
},
created() {
this.onSocket();
},
methods: {
onSocket() {
this._.socket.on("LoginSuccess", (res) => {
uni.setStorage({
key: 'UserInfo',
data: res,
success: function() {
uni.redirectTo({
url: "/pages/index/index",
});
}
});
});
this._.socket.on("UserAlready", (res) => {
uni.showToast({
icon: "none",
title: res,
duration: 2000,
});
});
},
SelectImg(item, index) {
this.currentIndex = index;
this.SelectImgUrl = "../../static/" + item;
},
EnterInfo() {
if (this.SelectImgUrl.length == 0) {
uni.showToast({
icon: "none",
title: "头像必选",
duration: 2000,
});
return;
}
if (this.UserName.length == 0) {
uni.showToast({
icon: "none",
title: "昵称必填",
duration: 2000,
});
return;
}
if (this.UserRoom.length == 0) {
uni.showToast({
icon: "none",
title: "群号必填",
duration: 2000,
});
return;
}
this._.socket.emit("UserLogin", {
name: this.UserName,
room: this.UserRoom,
img: this.SelectImgUrl,
});
},
open() {
this.$refs.popup.open("center");
},
close() {
// TODO 做一些其他的事情,before-close 为true的情况下,手动执行 close 才会关闭对话框
// ...
this.$refs.popup.close();
},
/**
* 点击确认按钮触发
* @param {Object} done
* @param {Object} value
*/
confirm(value) {
this.$refs.popup.close();
},
},
};
</script>
<style lang="less" scope>
.login {
width: 100vw;
height: 100vh;
box-sizing: border-box;
.right {
position: fixed;
top: -120rpx;
transform: translateX(60%);
}
.bottom {
position: fixed;
transform: translateX(-85%);
bottom: 10rpx;
}
.login_text {
padding-top: 261.806rpx;
padding-left: 59.722rpx;
.text {
font-family: MicrosoftYaHei-Bold;
font-size: 69.444rpx;
color: #000000;
font-weight: 700;
}
.tips {
font-family: MicrosoftYaHei;
font-size: 34.722rpx;
color: #666666;
opacity: 0.67;
}
}
.login_input {
width: 626.389rpx;
margin: 0 auto;
margin-top: 120.111rpx;
/deep/ .input-placeholder {
font-family: MicrosoftYaHei;
font-size: 30.833rpx;
color: #999999;
}
.uni-icons {
margin: 0 auto;
display: inherit;
margin-bottom: 80rpx;
}
.portraitImg {
width: 200rpx;
height: 200rpx;
border-radius: 100%;
margin: 0 auto;
display: inherit;
}
// 输入框的公共方法,接受两个参数
// @bottom 底部边距
// @img 背景图地址
.pub_input(@bottom: 0px, @img) {
box-shadow: 0rpx 11.111rpx 40.417rpx 1.25rpx rgba(203, 203, 203, 0.58);
border-radius: 48.611rpx;
height: 100.222rpx;
background-color: #ffffff;
padding-left: 111.111rpx;
box-sizing: border-box;
margin-bottom: @bottom;
background: url("@/static/@{img}.png") no-repeat;
background-size: 34.028rpx;
background-position: 41.667rpx 50%;
}
.user_name {
.pub_input(48.611rpx, user);
margin-top: 90rpx;
}
.user_pwd {
.pub_input(117.361rpx, pwd);
}
}
.portrait_list {
display: flex;
flex-wrap: wrap;
image {
width: 80rpx;
height: 80rpx;
margin-bottom: 10rpx;
margin-right: 10rpx;
}
.select {
border: 1px solid #000000;
}
}
.enter {
width: 115.278rpx;
height: 70.417rpx;
float: right;
}
}
</style>