WebSocket私信聊天(无群聊天)
//php 后端
<?php
$server = new Swoole\WebSocket\Server("0.0.0.0", 9501);$server->on('open', function (Swoole\WebSocket\Server $server, $request) {//$server->push($request->fd, '欢迎进入我们的聊天室~');
});//接收客户端发送的信息
//$frame 是 Swoole\WebSocket\Frame 对象,包含了客户端发来的数据帧信息
//$frame->fd 代表客户端的唯一标识 客户id
//$frame->data 代表客户端发送的消息
$server->on('message', function (Swoole\WebSocket\Server $server, $frame) {//获取客户端发送的消息$type = json_decode($frame->data,true);if($type['type'] != 'active'){$result['data'] = $frame->data;$types = 1;$framedata = json_decode($frame->data,true);$openid = $framedata['openid'];$endopenid = $framedata['endopenid'];$imgtype = $framedata['imgtype'];$url = 'http://www.xxx.com/';$wxapp = '/app/index.php?i=2&t=0&v=1.01&from=wxapp&c=entry&a=wxapp';// 会话列表创建会话接口$socket_add = '&do=chat_socket_add&m=gc_school&';//会话信息查询接口$socket_find = '&do=chat_socket_find&m=gc_school&';//用户openid 绑定会话 fd 接口$user_fid = '&do=chat_user_find&m=gc_school&';//存储消息记录$socket_log = '&do=socket_log&m=gc_school&';//重置fd为0$socket_isfd = '&do=socket_isfd&m=gc_school&';//查询对方的fd$user_endfind = '&do=chat_user_endfind&m=gc_school&';//查询用户信息$Socket_users = '&do=Socket_users&m=gc_school&';//如果不存在会话id 就去创建会话ID
// var_dump(frame−>relation);die;if(frame->relation);die; if(frame−>relation);die;if(framedata[‘relation’]){
usess=′ids=′.usess = 'ids='.usess=′ids=′.framedata[‘relation’];
//如果存在会话id就去查询会话id的信息
$relation_url = url.url.url.wxapp.socketfind.socket_find.socketfind.usess;
relationdata=filegetcontents(relationdata = file_get_contents(relationdata=filegetcontents(relation_url); // 查询会话信息
}else{
usess=′openid=′.usess = 'openid='.usess=′openid=′.openid.‘&endopenid=’.$endopenid;
//拼接创建会话id信息并请求接口
$relation_url = url.url.url.wxapp.socketadd.socket_add.socketadd.usess;
relationdata=filegetcontents(relationdata = file_get_contents(relationdata=filegetcontents(relation_url); // 建立会话信息
}
jsonfd=jsondecode(json_fd = json_decode(jsonfd=jsondecode(relationdata,true);
//openid 和 fd 一起查询,以当前的fd为准每次发消息
if($openid){
fddata=′relation=′.fddata = 'relation='.fddata=′relation=′.json_fd[‘data’][‘id’].‘&fd=’.KaTeX parse error: Expected 'EOF', got '&' at position 12: frame->fd.'&̲openid='.openid;
$relationurlfd = url.url.url.wxapp.userfid.user_fid.userfid.fddata;
userfd=filegetcontents(user_fd = file_get_contents(userfd=filegetcontents(relationurlfd); // fd信息
}
//查询对方的fd
if($openid){
endfddata=′relation=′.endfddata = 'relation='.endfddata=′relation=′.json_fd[‘data’][‘id’].‘&fd=’.KaTeX parse error: Expected 'EOF', got '&' at position 12: frame->fd.'&̲openid='.openid;
$endrelationurlfd = url.url.url.wxapp.userendfind.user_endfind.userendfind.endfddata;
enduserfd=filegetcontents(enduser_fd = file_get_contents(enduserfd=filegetcontents(endrelationurlfd); // fd信息
}
endre=jsondecode(endre = json_decode(endre=jsondecode(enduser_fd,true);
// file_get_contents($url);
// file_get_contents();
re=jsondecode(re = json_decode(re=jsondecode(user_fd,true);
content=jsondecode(content = json_decode(content=jsondecode(frame->data,true);
socketlogdata=′relation=′.socket_logdata = 'relation='.socketlogdata=′relation=′.json_fd[‘data’][‘id’].‘&content=’.KaTeX parse error: Expected 'EOF', got '&' at position 21: …nt['content'].'&̲type='.framedata[‘typess’].‘&openid=’.KaTeX parse error: Expected 'EOF', got '&' at position 9: openid.'&̲endopenid='.endopenid.‘&imgtype=’.imgtype;//添加聊天记录if(imgtype; // 添加聊天记录 if(imgtype;//添加聊天记录if(type[‘type’] != ‘active’){
$relationurl = url.url.url.wxapp.socketlog.socket_log.socketlog.socket_logdata;
file_get_contents($relationurl); // fd
}
//查询用信息$fds = 'openid='.$openid;$users = $url.$wxapp.$Socket_users.$fds;$udata = file_get_contents($users);$uc = json_decode($udata,true);$content['users'] = $uc['data'];
// re=jsondecode(re = json_decode(re=jsondecode(result[‘data’],true);
// $re[‘users’] = $content;
result[′data′]=jsonencode(result['data'] = json_encode(result[′data′]=jsonencode(content);
// $result[‘data’][‘users’] = $content;
$data = [
‘msg’ => ‘操作成功’,
‘data’ => $result,
‘fd’ => frame−>fd,′errorcode′=>0,′types′=>frame->fd, 'error_code' => 0, 'types'=>frame−>fd,′errorcode′=>0,′types′=>types,
‘imgtype’=>$imgtype,
];// 检测fd 是否在线if ($server->isEstablished($endre['data']['fd'])) {$server->push($frame->fd, json_encode($data)); // 推给自己$server->push($endre['data']['fd'], json_encode($data)); // 推给对方}else{$fds = 'ids='.$endre['data']['id'];$isfd = $url.$wxapp.$socket_isfd.$fds;file_get_contents($isfd); // 推送对方用户不在线重置fd为0$server->push($frame->fd, json_encode($data)); // 推给自己}}else{$types = 0;$data = ['msg' => '操作成功','data' => [],'fd' => $frame->fd,'error_code' => 0,'types'=>$types,];$server->push($frame->fd, json_encode($data)); // 定时任务推送自己防止断线}});$server->on('close', function ($ser, $fd) {echo "client {$fd} closed\n";
});$server->start();
//具体消息接口这些东西需要自行写入目前逻辑仅代表个人观点 每次修改切记运行下php文件(不是访问切记)
宝塔反向代理
upstream websocket{
hash KaTeX parse error: Expected 'EOF', got '}' at position 87: …l_timeout=30s; }̲ server { list… https://$host$1 permanent;
}
server
{
# listen 80;
listen 443 ssl http2;
server_name socket.flyccampus.com;
index index.php index.html index.htm default.php default.htm default.html;
root /www/wwwroot/socket.flyccampus.com;
#SSL-START SSL相关配置,请勿删除或修改下一行带注释的404规则
#error_page 404/404.html;
ssl_certificate /www/server/panel/vhost/cert/socket.flyccampus.com/fullchain.pem;
ssl_certificate_key /www/server/panel/vhost/cert/socket.flyccampus.com/privkey.pem;
ssl_protocols TLSv1.1 TLSv1.2 TLSv1.3;
ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
add_header Strict-Transport-Security "max-age=31536000";
error_page 497 https://$host$request_uri;#SSL-END#ERROR-PAGE-START 错误页配置,可以注释、删除或修改
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END#PHP-INFO-START PHP引用配置,可以注释或修改
#清理缓存规则
location / {
if (!-e KaTeX parse error: Expected '}', got '#' at position 26: …lename) { #̲一级目录 rewri… /index.php?s=$1 last;
break;
}
#wss配置
client_max_body_size 100m;
proxy_redirect off;
proxy_set_header Host $host;# http请求的主机域名
proxy_set_header X-Real-IP $remote_addr;# 远程真实IP地址
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;#反向代理之后转发之前的IP地址
proxy_read_timeout 604800s;#websocket心跳时间,默认是60s
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection “Upgrade”;
proxy_pass http://websocket;#反向代理转发地址}location ~ /purge(/.*) {proxy_cache_purge cache_one $host$1$is_args$args;#access_log /www/wwwlogs/socket.flyccampus.com_purge_cache.log;
}
#引用反向代理规则,注释后配置的反向代理将无效
include /www/server/panel/vhost/nginx/proxy/socket.flyccampus.com/*.conf;include enable-php-00.conf;
#PHP-INFO-END#REWRITE-START URL重写规则引用,修改后将导致面板设置的伪静态规则失效
include /www/server/panel/vhost/rewrite/socket.flyccampus.com.conf;
#REWRITE-END#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{return 404;
}#一键申请SSL证书验证目录相关设置
location ~ \.well-known{allow all;
}access_log /www/wwwlogs/socket.flyccampus.com.log;
error_log /www/wwwlogs/socket.flyccampus.com.error.log;
}
//前端代码
utils/config.js
module.exports = {
// 请求域名 格式: https://您的域名
HTTP_REQUEST_URL:‘http://socket.flyccampus.com’,
// Socket链接 暂不做配置
WSS_SERVER_URL:‘wss://xxx.com’,
//JWT token 名称
TOKEN_NAME:‘token’,
//用户注册id 名称
USER_ID:‘uid’,
//用户注册openid 名称
OPEN_ID:‘openid’,
// 以下配置非开发者,无需修改
// 请求头
HEADER:{
‘content-type’: ‘application/json’
},
}
utils/websocket.js
import {WSS_SERVER_URL} from “config.js”
//定时标识
let timing = false
function connect(user, func) {
wx.connectSocket({
url: ${WSS_SERVER_URL}?type=ask&fid=${user.id}&tid=100
,
header: { ‘content-type’: ‘application/json’ },
success: function () {
console.log(‘websocket连接成功~’)
},
fail: function () {
console.log(‘websocket连接失败~’)
}
})
wx.onSocketOpen(function (res) {
// wx.showToast({
// title: ‘websocket已开通~’,
// icon: “success”,
// duration: 2000
// })
//接受服务器消息
wx.onSocketMessage(func);//func回调可以拿到服务器返回的数据
});
//启动心跳包
linkWebsocketXin(40000, true)
wx.onSocketError(function (res) {
wx.showToast({
title: ‘websocket连接失败,请检查!’,
icon: “none”,
duration: 1000
})
})
}
//心跳包
function linkWebsocketXin(time, status) {
if (status == true) {
timing = setInterval(function () {
console.log(“当前心跳已重新连接”);
//循环执行代码
wx.sendSocketMessage({
data: JSON.stringify({
type: ‘active’,
}),
fail(res) {
// console.log(res)
}
});
}, time) //循环时间,注意不要超过1分钟
} else {
//关闭定时器
clearInterval(timing);
console.log(“当前心跳已关闭”);
}
}
//发送消息
function send(msg) {
//关闭心跳包定时器
linkWebsocketXin(40000, false)
wx.sendSocketMessage({
data: msg,
success:res=>{
//重启心跳包
linkWebsocketXin(40000, true)
}
});
}
module.exports = {
connect: connect,
send: send,
linkWebsocketXin:linkWebsocketXin
}
utils/util.js
const formatTime = date => {
const year = date.getFullYear()
const month = date.getMonth() + 1
const day = date.getDate()
const hour = date.getHours()
const minute = date.getMinutes()
const second = date.getSeconds()
return ${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}
}
const formatNumber = n => {
n = n.toString()
return n[1] ? n : 0${n}
}
module.exports = {
formatTime
}
/聊天列表
chat.js
const app = getApp()
var websocket = require(‘…/…/…/utils/websocket.js’);
var utils = require(‘…/…/…/utils/util.js’);
import {HTTP_REQUEST_URL, HEADER, USER_ID, OPEN_ID} from “…/…/…/utils/config.js”
Page({
/**
页面的初始数据
/
data: {
newslist: [],
userInfo: {},
scrollTop: 0,
increase: false,//图片添加区域隐藏
aniStyle: true,//动画效果
message: “”,
previewImgList: [],
relation:0, // 会话id
openid:‘’,
endopenid:‘’,
img:‘’,
imgtype:0,
toView:‘msg-0’,
top:0,
pages:1,
moban:‘’
},
/*生命周期函数–监听页面加载
*/
onLoad: function (options) {
this.getfd()
this.getmoban()
this.setData({
openid: wx.getStorageSync(‘openid’) // 本用户的openid
})
if(options.relation){ // 如果有会话id
this.setData({
relation: options.relation // 赋值会话id
})
this.getlist()
}
if(this.data.relation == 0){
this.setData({
endopenid: options.openid // 赋值需要聊天人的openid
})
this.getrelation()
}var that = this
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo
})
}
//调通接口
websocket.connect(this.data.userInfo, function (res) {
console.log(res,‘—’)
var list = []
list = that.data.newslist
var data = JSON.parse(res.data)
console.log(data.types,‘datatype----’)
if(data.types != 0) {
var datas = JSON.parse(data.data.data)
console.log(datas.content,‘data----’)
// list = datas.content
// } else {
list.push(datas)
}
that.setData({
newslist: list
})
// that.bottom()
})
},
// 页面卸载
onUnload() {
wx.closeSocket();
// wx.showToast({
// title: '连接已断开~',
// icon: "none",
// duration: 2000
// })
},
onPullDownRefresh(){
console.log(111)
this.getpagedata()
},
getrelation(){
var that = this;
app.util.request({
url: ‘entry/wxapp/socket_users_socket’,
data: {
openid:that.data.openid,
endopenid:that.data.endopenid
},
success(res) {
console.log(res.data.data,‘—lll—’)
if(res.data.errno == 0){
that.setData({
relation: res.data.data.id // 赋值会话id
})
that.getlist()
}
}
})
},
//加载历史消息
getpagedata(){
var that = this;
app.util.request({
url: ‘entry/wxapp/socket_user_log’,
data: {
openid:that.data.openid,
relation:that.data.relation,
page:that.data.pages
},
success(res) {
console.log(res.data.data,‘—lll—’)
that.setData({
newslist: [
…res.data.data,
…that.data.newslist
],
// newslist:res.data.data,
pages:that.data.pages+=1
})
// setTimeout(()=>{
// that.pageScrollToBottom();
// },500)
}
})
},
getfd(){
let msg = {
type:‘active’,//咨询
openid:this.data.openid,
endopenid:this.data.endopenid,
relation:this.data.relation // 会话id
};
websocket.send(JSON.stringify(msg))
},
//图片上传
choose()
{
wx.chooseImage({
count: 1,
sizeType: [‘original’, ‘compressed’], //可以指定是原图还是压缩图,默认二者都有
sourceType: [‘album’], //从相册选择
success: (res) => {
console.log(‘path’,res)
this.uploads(res)
}})},uploads(res) {var that = this;wx.showLoading({title: '上传中',})wx.uploadFile({url: app.util.url() + 'c=entry&a=wxapp&do=ImgPost&m=gc_school', // 仅为示例,非真实的接口地址filePath: res.tempFilePaths[0],header: {'content-type': 'application/x-www-form-urlencoded'},name: 'file',success: (result) => {wx.hideLoading()var resu = JSON.parse(result.data)console.log(resu)if(resu.errno==0){that.setData({img:resu.data})that.setData({imgtype:1})that.send()}else{wx.showToast({title: resu.message,icon:'none'})}}})},
getlist(){
var that = this;
app.util.request({
url: ‘entry/wxapp/socket_user_log’,
data: {
openid:that.data.openid,
relation:that.data.relation,
page:that.data.pages
},
success(res) {
console.log(res.data.data,‘—lll—’)
that.setData({
newslist:res.data.data,
pages:2
})
setTimeout(()=>{
that.pageScrollToBottom();
},1000)
}
})
},
//图片预览
preview(e)
{
// this.setData({
// freshen:false
// })
console.log(e.currentTarget.dataset.index,‘—’)
var content = this.data.newslist[e.currentTarget.dataset.index].content;
console.log(content,‘—’)
wx.previewImage({
current:content,
urls: [content],
})
},
//事件处理函数
send: function () {
var flag = this
let uid = wx.getStorageSync(USER_ID)
if(this.data.img.trim() == “” && this.data.imgtype == 1){
wx.showToast({
title: ‘图片不能为空哦~’,
icon: “none”,
duration: 2000
})
return
}
if (this.data.message.trim() == "" && this.data.imgtype == 0) {wx.showToast({title: '消息不能为空哦~',icon: "none",duration: 2000})return
}setTimeout(function () {flag.setData({increase: false})}, 1000)if(flag.data.imgtype == 1){var msg = {content:this.data.img,date:utils.formatTime(new Date()),type:'ask',//咨询fid:uid,imgtype:1,tid:100,openid:this.data.openid,endopenid:this.data.endopenid,relation:this.data.relation // 会话id
};}else{var msg = {content:this.data.message,date:utils.formatTime(new Date()),type:'ask',//咨询fid:uid,imgtype:0,tid:100,openid:this.data.openid,endopenid:this.data.endopenid,relation:this.data.relation // 会话id
};}flag.setData({imgtype: 0})websocket.send(JSON.stringify(msg))/*websocket.send('{ "content": "' + this.data.message + '", "date": "' + utils.formatTime(new Date()) + '","type":"text", "nickName": "' + this.data.userInfo.nickName + '", "avatarUrl": "' + this.data.userInfo.avatarUrl + '" }')*///发完消息值改为空this.setData({message: '',
})setTimeout(()=>{flag.pageScrollToBottom();},1000)// this.bottom()
},
//监听input值的改变
bindChange(res) {
this.setData({
message: res.detail.value
})
},
cleanInput() {
//button会自动清空,所以不能再次清空而是应该给他设置目前的input值
this.setData({
message: this.data.message
})
},
increase() {
this.setData({
increase: true,
aniStyle: true
})
},
//点击空白隐藏message下选框
outbtn() {
this.setData({
increase: false,
aniStyle: true
})
},
//发送图片
chooseImage() {
var that = this
wx.chooseImage({
count: 1, // 默认9
sizeType: [‘original’, ‘compressed’], // 可以指定是原图还是压缩图,默认二者都有
sourceType: [‘album’, ‘camera’], // 可以指定来源是相册还是相机,默认二者都有
success: function (res) {
// 返回选定照片的本地文件路径列表,tempFilePath可以作为img标签的src属性显示图片
var tempFilePaths = res.tempFilePaths
// console.log(tempFilePaths)
wx.uploadFile({
url: ‘wss://www.xxx.cn’, //服务器地址
filePath: tempFilePaths[0],
name: ‘file’,
headers: {
‘Content-Type’: ‘form-data’
},
success: function (res) {
if (res.data) {
that.setData({
increase: false
})
websocket.send(‘{“images”:"’ + res.data + ‘“,“date”:”’ + utils.formatTime(new Date()) + ‘“,“type”:“image”,“nickName”:”’ + that.data.userInfo.nickName + ‘“,“avatarUrl”:”’ + that.data.userInfo.avatarUrl + ‘"}’)
that.bottom()
}
}
})
}
})
},
//图片预览
previewImg(e) {
var that = this
//必须给对应的wxml的image标签设置data-set=“图片路径”,否则接收不到
var res = e.target.dataset.src
var list = this.data.previewImgList //页面的图片集合数组
//判断res在数组中是否存在,不存在则push到数组中, -1表示res不存在
if (list.indexOf(res) == -1) {
this.data.previewImgList.push(res)
}
wx.previewImage({
current: res, // 当前显示图片的http链接
urls: that.data.previewImgList // 需要预览的图片http链接列表
})
},
//聊天消息始终显示最底端
bottom: function () {
this.setData({
message: ‘’,
})
var query = wx.createSelectorQuery()
query.select(‘#flag’).boundingClientRect()
query.selectViewport().scrollOffset()
query.exec(function (res) {
wx.pageScrollTo({
scrollTop: res[0].bottom // #the-id节点的下边界坐标
})
res[1].scrollTop // 显示区域的竖直滚动位置
})
},
pageScrollToBottom: function() {
wx.createSelectorQuery().select(‘#flag’).boundingClientRect(function(rect){
// 使页面滚动到底部
wx.pageScrollTo({
scrollTop: rect.bottom
})
}).exec()
},
getmoban(){
var that = this;
app.util.request({
url: ‘entry/wxapp/template_comment’,
data: {
},
success(res) {
console.log(res.data.data,‘—lll—’)
if(res.data.errno == 0){
that.setData({
moban: res.data.data.template_comment // 赋值会话id
})
that.getlist()
}
}
})
},
ticing(){
console.log("弹出框")
var self = this;
// self.chuo()wx.getSetting({withSubscriptions:true,success (res) {console.log("设置",res)console.log(res.subscriptionsSetting)}})wx.requestSubscribeMessage({tmplIds: [self.data.moban], // 此处可填写多个模板 ID,但低版本微信不兼容只能授权一个success (res) {self.chuo()}})
},
chuo(){
var that = this;
app.util.request({
url: ‘entry/wxapp/tsuisong’,
data: {
‘openid’:that.data.openid,
‘relation’:that.data.relation
},
success(res) {
console.log(res.data.data,‘—lll—’)
if(res.data.errno == 0){
that.setData({
moban: res.data.data.template_comment // 赋值会话id
})
that.getlist()
}
}
})
}
})
chat.json
{
“usingComponents”: {},
“enablePullDownRefresh”: true
}
chat.wxml
返回聊天
{{item.content}} {{item.date}} {{item.content}}
<view class="date">{{item.date}}</view>
发送
chat.wxss
/* pages/socks/socks.wxss */
page {
background-color: #f7f7f7;
height: 100%;
}
/* 聊天内容 /
.news {
padding-top: 30rpx;
text-align: center;
/ height:100%; */
box-sizing: border-box;
}
#flag {
margin-bottom: 100rpx;
height: 30rpx;
}
.chat-notice {
text-align: center;
font-size: 30rpx;
padding: 10rpx 0;
color: #666;
}
.historycon {
height: 100%;
width: 100%;
/* flex-direction: column; */
display: flex;
border-top: 0px;
}
/* 聊天 */
.chat-news {
width: 100%;
overflow: hidden;
}
.chat-news .my_right {
float: right;
/* right: 40rpx; */
padding: 10rpx 10rpx;
}
.chat-news .name {
margin-right: 10rpx;
}
.chat-news .you_left {
float: left;
/* left: 5rpx; */
padding: 10rpx 10rpx;
}
.selectImg {
display: inline-block;
width: 150rpx;
height: 150rpx;
margin-left: 50rpx;
}
.my_right .selectImg {
margin-right: 80rpx;
}
.new_img {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
vertical-align: middle;
margin-right: 10rpx;
}
.new_txt {
max-width: 300rpx;
display: inline-block;
border-radius: 6rpx;
line-height: 60rpx;
background-color: #95d4ff;
padding: 5rpx 20rpx;
margin: 0 10rpx;
margin-left: 50rpx;
}
.my_right .new_txt {
margin-right: 60rpx;
}
.you {
background-color: lightgreen;
}
.my {
border-color: transparent transparent transparent #95d4ff;
}
.you {
border-color: transparent #95d4ff transparent transparent;
}
.hei {
margin-top: 50px;
height: 20rpx;
}
.history {
height: 100%;
margin-top: 15px;
padding: 10rpx;
font-size: 14px;
line-height: 40px;
word-break: break-all;
}
::-webkit-scrollbar {
width: 0;
height: 0;
color: transparent;
z-index: -1;
}
/* 信息输入区域 */
.message {
position: fixed;
bottom: 0;
width: 100%;
}
.sendMessage {
height: 80rpx;
padding: 10rpx 10rpx;
background-color: #fff;
border-top: 2rpx solid #eee;
border-bottom: 2rpx solid #eee;
/z-index: 3;/
}
.sendMessage input {
float: left;
height: 42px;
line-height: 100%;
border-bottom: 1rpx solid #ccc;
padding: 0 10rpx;
font-size: 35rpx;
color: #666;
}
.sendMessage button {
float: right;
font-size: 35rpx;
}
.sendMessage view {
display: inline-block;
width: 80rpx;
height: 80rpx;
line-height: 80rpx;
font-size: 60rpx;
text-align: center;
color: #999;
border: 1rpx solid #ccc;
border-radius: 50%;
margin-left: 10rpx;
}
.increased {
width: 100%;
/* height: 150rpx; */
padding: 40rpx 30rpx;
background-color: #fff;
}
.increased .image {
width: 100rpx;
height: 100rpx;
border: 3rpx solid #ccc;
line-height: 100rpx;
text-align: center;
border-radius: 8rpx;
font-size: 35rpx;
}
@keyframes slidedown {
from {
transform: translateY(0);
}
to {
transform: translateY(100%);
}
}
.slidedown {
animation: slidedown 0.5s linear;
}
.slideup {
animation: slideup 0.5s linear;
}
@keyframes slideup {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
前端采用colorui样式插件
完成示例图
///注意会话列表是自己写的
有啥问题加我微信或者QQ1446891130 看到有空给你说一下
WebSocket私信聊天(无群聊天)相关推荐
- 手机浏览器打开QQ聊天和群聊天
普通QQ mqqwpa://im/chat?chat_type=wpa&uin=${qq}&version=1&src_type=web&web_src=oicqzon ...
- websocket以及nodejs联手打造的类qq群聊天室 教程 附 原代码
这次给大家分享的是我自己开发的多人聊天室,利用websocket 以及服务器端使用node.js 来让用户不需要刷新浏览器就可以获得实时更新. 如下面图所示的样子.开发出类QQ群聊天室的主界面,当然U ...
- 集群聊天服务器项目(四)——项目总结
集群聊天服务器项目总结 首先是就是项目介绍集群聊天服务器项目(零)--项目介绍中的内容,就不再次copy过来了 项目简单介绍 技术栈 环境和库依赖 按模块介绍整个项目 程序的主要模块是网络模块.业务模 ...
- 跟着源码一起学:手把手教你用WebSocket打造Web端IM聊天
本文作者芋艿,原题"芋道 Spring Boot WebSocket 入门",本次有修订和改动. 一.引言 WebSocket如今在Web端即时通讯技术应用里使用广泛,不仅用于传统 ...
- HTML5 之WebSocket入门demo和简易聊天室
HTML5 WebSocket WebSocket是HTML5开始提供的一种在单个 TCP 连接上进行全双工通讯的协议. 在WebSocket API中,浏览器和服务器只需要做一个握手的动作,然后,浏 ...
- Java基于Socket实现聊天、群聊、敏感词汇过滤功能
首先的话,这个代码主要是我很久以前写的,然后当时还有很多地方没有理解,现在再来看看这份代码,实在是觉得丑陋不堪,想改,但是是真的改都不好改了- 所以,写代码,规范真的很重要. 实现的功能: 用户私聊 ...
- springboot和netty整合的聊天室--群聊
一.创建项目 file-new-project-spring initializr-next 然后 添加这两个依赖 二.代码 DemoApplication package com.example.d ...
- 基于Tomcat7、Java、WebSocket的服务器推送聊天室
2019独角兽企业重金招聘Python工程师标准>>> 基于Tomcat7.Java.WebSocket的服务器推送聊天室 转载于:https://my.oschina.net/u/ ...
- C++搭建集群聊天室(十四):群聊功能
文章目录 群聊功能思路 放码过来 groupuser.hpp group.hpp groupmodel.hpp groupmodel.cpp 群聊功能思路 1.创建群聊,提交群信息,返回群号 2.拉取 ...
最新文章
- 在python中、下列代码的输出是什么-python面试题详细总结(附答案)
- 一个产品经理的自述:我在腾讯工作的这一年(转)
- RocksDB 6.0.2 发布,Facebook 推出的存储系统
- ASP.NET Core HTTP 管道中的那些事儿
- linux网络编程(一)网络基础传输知识
- ElasticSearch 2 (27) - 信息聚合系列之故事开始
- HTTP 和 HTTPS 两种传输协议各自含义是什么?二者使用有什么区别?
- redis cluster管理工具redis-trib.rb详解
- [软件工程学习笔记]个人java小程序---词频统计(二)
- CCF CSP202006-1 线性分类器
- isis协议_[IS-IS] IS-IS路由协议的基本知识及配置
- 51单片机(十)—— 8位数码管-数码管扫描
- R语言伴随矩阵的计算
- iView的表格做一个带斜线的表头
- hdu 1880 魔咒词典 (字符串哈希)
- 具有生物启发训练方法的物理深度学习:物理硬件的无梯度方法
- 不可见字符和表情包正则
- 读《大数据之路-阿里巴巴大数据实践》数据模型篇笔记
- 移动创维E900V21C救砖过程,附线刷刷机固件
- 杰理之杰理音箱方案选型【篇】