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 @@
e60a1464-bf3c-4ee0-9434-6c62f318d64c

Binary file not shown.

View File

@@ -0,0 +1 @@
14579d7a-2c39-4e34-aa23-f746a279d08c

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 360 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 331 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File

@@ -0,0 +1 @@
747eaff2-e827-4d03-8f63-dd4f1938e90a

View File

@@ -0,0 +1 @@
02563c8e-a4b6-4112-bdfb-88f42ed95a2e

View File

@@ -0,0 +1,15 @@
{
"name": "backend",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"dev": "node-dev socket.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"socket.io": "^4.1.3"
}
}

View File

@@ -0,0 +1,88 @@
const { createServer } = require("http");
const { Server } = require("socket.io");
const httpServer = createServer();
const io = new Server(httpServer);
// 保存群列表
var GroupList = {};
// 保存聊天记录
var HistoryList = {};
io.on("connection", (socket) => {
console.log("a user connected", socket.id);
socket.on("UserLogin", (res) => {
// 如果已经有房间了
if (GroupList.hasOwnProperty(res.room)) {
console.log("1 如果已经有房间了");
// 判断房间是否已经有这个用户名了
var isHave = GroupList[res.room].filter((v) => {
return v.name == res.name;
});
// 没有这个用户
if (isHave.length == 0) {
console.log("2 没有这个用户");
// 将用户加入房间数组
GroupList[res.room].push({
name: res.name,
img: res.img,
});
socket.emit("LoginSuccess", res);
} else {
console.log("3 有这个用户");
socket.emit("UserAlready", "用户名已存在,请改名!");
}
} else {
console.log("4 没有房间");
GroupList[res.room] = [];
// 将用户加入房间数组
GroupList[res.room].push({
name: res.name,
img: res.img,
});
socket.emit("LoginSuccess", res);
}
});
socket.on("JoinRoom", (res) => {
// 将当前 socket 加入群聊
socket.join(res.room);
// 向房间内的所有人发送欢迎信息
io.in(res.room).emit("WelcomeUser", `${res.name} 加入群聊`);
io.in(res.room).emit("RoomOnline", GroupList[res.room]);
});
socket.on("SendMessage", (res) => {
console.log("后端收到的消息为:", res);
socket.to(res.room).emit("SetMessage", res);
});
socket.on("Shake", (res) => {
console.log("Shake");
socket.to(res.room).emit("StartShake", true);
});
socket.on("UserDisconnect", (res) => {
console.log("有个客户端掉线了");
if (GroupList.hasOwnProperty(res.room)) {
console.log("===============");
GroupList[res.room] = GroupList[res.room].filter((v) => {
return v.name != res.name;
});
io.in(res.room).emit("WelcomeUser", `${res.name} 离开群聊`);
// 更新在线人数
io.in(res.room).emit("RoomOnline", GroupList[res.room]);
}
});
});
httpServer.listen(3000);

View File

@@ -0,0 +1 @@
1bd5ecb6-2bd6-41e6-bd3f-e71c659739bf

View File

@@ -0,0 +1,20 @@
{ // launch.json 配置了启动调试时相关设置configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
// launchtype项可配置值为local或remote, local代表前端连本地云函数remote代表前端连云端云函数
"version": "0.0",
"configurations": [{
"app-plus" :
{
"launchtype" : "local"
},
"default" :
{
"launchtype" : "local"
},
"h5" :
{
"launchtype" : "local"
},
"type" : "uniCloud"
}
]
}

View File

@@ -0,0 +1 @@
492f431c-4084-491a-b6ca-86f1cd36c554

View File

@@ -0,0 +1,17 @@
<script>
export default {
onLaunch: function() {
console.log('App Launch')
},
onShow: function() {
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
@import "~@/static/fonts/iconfont.css";
</style>

View File

@@ -0,0 +1 @@
48dc8a72-8ce9-420e-b1f4-7961ff96b5af

View File

@@ -0,0 +1,143 @@
export default {
socketUrl: "http://192.168.31.100:3000",
theme: [
// 默认色 浅蓝黑字
{ background: "#f3f3f4", textColor: "#000", text: "默认配色" },
// 蓝底白字
{ background: "#03a9e3", textColor: "#fff", text: "QQ风格" },
// 白底黑字
{ background: "#fff", textColor: "#000", text: "白纸黑字" },
// 黑底白字
{ background: "#000", textColor: "#fff", text: "夜间专享" },
// 微信绿底黑字
{ background: "#94ed68", textColor: "#000", text: "微信风格" },
// 基佬紫
{ background: "#9d07e9", textColor: "#fff", text: "基佬紫" },
// 活力橙
{ background: "#fea674", textColor: "#000", text: "活力橙" },
// 青青草原
{ background: "#5ad6de", textColor: "#fff", text: "青青草原" },
// 土豪金
{ background: "#f2ca5b", textColor: "#663d00", text: "土豪金" },
// 红红火火
{ background: "#ff1459", textColor: "#fff", text: "红红火火" },
// 暗淡灰
{ background: "#5f5f5f", textColor: "#fff", text: "暗淡灰" },
// 少女粉
{ background: "#e1b0a7", textColor: "#fff", text: "少女粉" },
// 巧克力灰
{ background: "#624118", textColor: "#fff", text: "巧克力灰" },
],
portrait: [
"1.jpg",
"2.jpg",
"3.gif",
"4.gif",
"5.gif",
"6.jpg",
"7.jpg",
"8.jpg",
"9.jpg",
"10.jpg",
],
emoji: [
[
"😀",
"😁",
"😂",
"😃",
"😄",
"😅",
"😆",
"😉",
"😊",
"😋",
"😎",
"😍",
"😘",
"😗",
"😙",
"😚",
"😇",
"😐",
"😑",
"😶",
"😏",
"😣",
"😥",
"😮",
"😯",
"😪",
"😫",
"😴",
"😌",
"😛",
"😜",
"😝",
"😒",
"😓",
"😔",
"😕",
"😲",
"😷",
"😖",
"😞",
"😟",
"😤",
"😢",
"😭",
"😦",
"😧",
"😨",
"😬",
"😰",
"😱",
"😳",
"😵",
"😡",
"😠",
],
[
"👦",
"👧",
"👨",
"👩",
"👴",
"👵",
"👶",
"👱",
"👮",
"👲",
"👳",
"👷",
"👸",
"💂",
"🎅",
"👰",
"👼",
"💆",
"💇",
"🙍",
"🙎",
"🙅",
"🙆",
"💁",
"🙋",
"🙇",
"🙌",
"🙏",
"👤",
"👥",
"🚶",
"🏃",
"👯",
"💃",
"👫",
"👬",
"👭",
"💏",
"💑",
"👪",
],
],
};

View File

@@ -0,0 +1,15 @@
import Vue from 'vue'
import App from './App'
import store from './store'
import socket from "./socket/socket.js"
Vue.config.productionTip = false
Vue.prototype._ = socket
App.mpType = 'app'
const app = new Vue({
...App,
store
})
app.$mount()

View File

@@ -0,0 +1,71 @@
{
"name" : "backend",
"appid" : "",
"description" : "",
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "",
"setting" : {
"urlCheck" : false
},
"usingComponents" : true
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
}
}

View File

@@ -0,0 +1,15 @@
{
"name": "fontend",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"@hyoga/uni-socket.io": "^3.0.4"
}
}

View File

@@ -0,0 +1,46 @@
{
"pages": [ //pages数组中第一项表示应用启动页参考https://uniapp.dcloud.io/collocation/pages
{
"path": "pages/login/login",
"style": {
"navigationBarTitleText": "",
"enablePullDownRefresh": false,
"navigationStyle": "custom"
}
},
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "uni-app",
"navigationStyle": "custom",
"app-plus": {
"softinputMode": "adjustResize"
}
}
},
{
"path" : "pages/ChatSetting/ChatSetting",
"style" :
{
"navigationBarTitleText": "聊天信息",
"enablePullDownRefresh": false
}
},
{
"path" : "pages/editName/editName",
"style" :
{
"navigationBarTitleText": "",
"enablePullDownRefresh": false
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTitleText": "uni-app",
"navigationBarBackgroundColor": "#F8F8F8",
"backgroundColor": "#F8F8F8"
}
}

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>

View File

@@ -0,0 +1 @@
e0a704cf-4291-4ed7-98df-e4be7c12b317

View File

@@ -0,0 +1,34 @@
import io from '@hyoga/uni-socket.io';
class socket {
constructor() {
this.socket = null;
this.init();
}
init () {
this.socket = io('http://192.168.31.101:3000', {
query: {},
transports: [ 'websocket', 'polling' ],
timeout: 5000,
});
this.onEvent();
}
onEvent() {
this.socket.on("connect", () => {
console.log("链接建立成功...")
uni.showToast({
icon: "none",
title: "debug 成功链接服务器...",
duration: 1000,
});
});
this.socket.on("error", (msg) => {
console.log('链接错误:', msg);
})
}
}
export default new socket();

View File

@@ -0,0 +1 @@
b9fb2da1-3625-4b5f-9fd7-5032264ea9a1

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

View File

@@ -0,0 +1 @@
cb43735b-4184-4127-9e46-a8fe063b1a2c

View File

@@ -0,0 +1,29 @@
@font-face {
font-family: "iconfont"; /* Project id 2772003 */
src: url('~@/static/fonts/iconfont.ttf?t=1630012923948') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 40rpx;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-smiling:before {
content: "\e63f";
}
.icon-shake:before {
content: "\e600";
}
.icon-xinbaniconshangchuan_huaban:before {
content: "\e601";
}
.icon-gallery:before {
content: "\e62a";
}

View File

@@ -0,0 +1 @@
04f584af-a28c-42a3-925f-e2e20c712161

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 135 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 266 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

View File

@@ -0,0 +1 @@
66fa4d1b-31ae-4e42-ac49-1e4eb536a28d

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 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: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Some files were not shown because too many files have changed in this diff Show More