first
This commit is contained in:
commit
b34d1ca5f0
|
|
@ -0,0 +1,39 @@
|
|||
//app.js
|
||||
App({
|
||||
onLaunch: function () {
|
||||
// 展示本地存储能力
|
||||
var logs = wx.getStorageSync('logs') || []
|
||||
logs.unshift(Date.now())
|
||||
wx.setStorageSync('logs', logs)
|
||||
|
||||
// 登录
|
||||
wx.login({
|
||||
success: res => {
|
||||
// 发送 res.code 到后台换取 openId, sessionKey, unionId
|
||||
}
|
||||
})
|
||||
// 获取用户信息
|
||||
wx.getSetting({
|
||||
success: res => {
|
||||
if (res.authSetting['scope.userInfo']) {
|
||||
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
|
||||
wx.getUserInfo({
|
||||
success: res => {
|
||||
// 可以将 res 发送给后台解码出 unionId
|
||||
this.globalData.userInfo = res.userInfo
|
||||
|
||||
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
|
||||
// 所以此处加入 callback 以防止这种情况
|
||||
if (this.userInfoReadyCallback) {
|
||||
this.userInfoReadyCallback(res)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
globalData: {
|
||||
userInfo: null
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"pages": ["pages/index/index", "pages/chat/index"],
|
||||
"window": {
|
||||
"backgroundTextStyle": "light",
|
||||
"navigationBarBackgroundColor": "#fff",
|
||||
"navigationBarTitleText": "weapp.socket.io chat demo",
|
||||
"navigationBarTextStyle": "black"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
@import './styles/weui.wxss';
|
||||
|
||||
page {
|
||||
background-color: #f8f8f8;
|
||||
font-size: 16px;
|
||||
font-family: -apple-system-font, Helvetica Neue, Helvetica, sans-serif;
|
||||
}
|
||||
.page__hd {
|
||||
padding: 40px;
|
||||
}
|
||||
.page__bd {
|
||||
padding-bottom: 40px;
|
||||
}
|
||||
.page__bd_spacing {
|
||||
padding-left: 15px;
|
||||
padding-right: 15px;
|
||||
}
|
||||
|
||||
.page__ft {
|
||||
padding-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.page__title {
|
||||
text-align: left;
|
||||
font-size: 20px;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.page__desc {
|
||||
margin-top: 5px;
|
||||
color: #888888;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
|
@ -0,0 +1,238 @@
|
|||
const io = require('../../utils/weapp.socket.io.js')
|
||||
|
||||
//index.js
|
||||
//获取应用实例
|
||||
const app = getApp()
|
||||
|
||||
/**
|
||||
* 生成一条聊天室的消息的唯一 ID
|
||||
*/
|
||||
function msgUuid() {
|
||||
if (!msgUuid.next) {
|
||||
msgUuid.next = 0
|
||||
}
|
||||
return 'msg-' + ++msgUuid.next
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成聊天室的系统消息
|
||||
*/
|
||||
function createSystemMessage(content) {
|
||||
return { id: msgUuid(), type: 'system', content }
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成聊天室的聊天消息
|
||||
*/
|
||||
function createUserMessage(content, user, isMe) {
|
||||
const color = getUsernameColor(user)
|
||||
return { id: msgUuid(), type: 'speak', content, user, isMe, color }
|
||||
}
|
||||
|
||||
var COLORS = [
|
||||
'#e21400',
|
||||
'#91580f',
|
||||
'#f8a700',
|
||||
'#f78b00',
|
||||
'#58dc00',
|
||||
'#287b00',
|
||||
'#a8f07a',
|
||||
'#4ae8c4',
|
||||
'#3b88eb',
|
||||
'#3824aa',
|
||||
'#a700ff',
|
||||
'#d300e7',
|
||||
]
|
||||
|
||||
// Gets the color of a username through our hash function
|
||||
function getUsernameColor(username) {
|
||||
// Compute hash code
|
||||
var hash = 7
|
||||
for (var i = 0; i < username.length; i++) {
|
||||
hash = username.charCodeAt(i) + (hash << 5) - hash
|
||||
}
|
||||
// Calculate color
|
||||
var index = Math.abs(hash % COLORS.length)
|
||||
return COLORS[index]
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
inputContent: 'Hello everybody',
|
||||
messages: [],
|
||||
lastMessageId: 'none',
|
||||
},
|
||||
|
||||
onLoad: function() {},
|
||||
|
||||
/**
|
||||
* 页面渲染完成后,启动聊天室
|
||||
* */
|
||||
onReady() {
|
||||
wx.setNavigationBarTitle({ title: 'weapp.socket.io 聊天室演示' })
|
||||
if (!this.pageReady) {
|
||||
this.pageReady = true
|
||||
this.enter()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 后续后台切换回前台的时候,也要重新启动聊天室
|
||||
*/
|
||||
onShow() {
|
||||
if (this.pageReady && !this.socket) {
|
||||
this.enter()
|
||||
}
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
this.quit()
|
||||
},
|
||||
|
||||
quit() {
|
||||
if (this.socket) {
|
||||
this.socket.close()
|
||||
this.socket = null
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 启动聊天室
|
||||
*/
|
||||
enter() {
|
||||
this.pushMessage(createSystemMessage('正在登录...'))
|
||||
|
||||
// 如果登录过,会记录当前用户在 this.me 上
|
||||
if (!this.me) {
|
||||
wx.getUserInfo({
|
||||
success: res => {
|
||||
this.me = res.userInfo
|
||||
this.createConnect()
|
||||
},
|
||||
})
|
||||
} else {
|
||||
this.createConnect()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 通用更新当前消息集合的方法
|
||||
*/
|
||||
updateMessages(updater) {
|
||||
var messages = this.data.messages
|
||||
updater(messages)
|
||||
|
||||
this.setData({ messages })
|
||||
|
||||
// 需要先更新 messagess 数据后再设置滚动位置,否则不能生效
|
||||
var lastMessageId = messages.length
|
||||
? messages[messages.length - 1].id
|
||||
: 'none'
|
||||
this.setData({ lastMessageId })
|
||||
},
|
||||
|
||||
/**
|
||||
* 追加一条消息
|
||||
*/
|
||||
pushMessage(message) {
|
||||
this.updateMessages(messages => messages.push(message))
|
||||
},
|
||||
|
||||
/**
|
||||
* 替换上一条消息
|
||||
*/
|
||||
amendMessage(message) {
|
||||
this.updateMessages(messages => messages.splice(-1, 1, message))
|
||||
},
|
||||
|
||||
/**
|
||||
* 删除上一条消息
|
||||
*/
|
||||
popMessage() {
|
||||
this.updateMessages(messages => messages.pop())
|
||||
},
|
||||
|
||||
changeInputContent: function(e) {
|
||||
this.setData({
|
||||
inputContent: e.detail.value,
|
||||
})
|
||||
},
|
||||
|
||||
sendMessage: function(e) {
|
||||
const msg = e.detail.value
|
||||
if (!msg) {
|
||||
return
|
||||
}
|
||||
this.socket.emit('new message', msg)
|
||||
this.pushMessage(createUserMessage(msg, this.me.nickName))
|
||||
this.setData({ inputContent: null })
|
||||
},
|
||||
|
||||
createConnect: function(e) {
|
||||
this.amendMessage(createSystemMessage('正在加入群聊...'))
|
||||
|
||||
const socket = (this.socket = io(
|
||||
'https://vast-plateau-30681.herokuapp.com/',
|
||||
))
|
||||
|
||||
/**
|
||||
* Aboud connection
|
||||
*/
|
||||
socket.on('connect', () => {
|
||||
this.popMessage()
|
||||
this.pushMessage(createSystemMessage('You joined'))
|
||||
})
|
||||
|
||||
socket.on('connect_error', d => {
|
||||
this.pushMessage(createSystemMessage(`connect_error: ${d}`))
|
||||
})
|
||||
|
||||
socket.on('connect_timeout', d => {
|
||||
this.pushMessage(createSystemMessage(`connect_timeout: ${d}`))
|
||||
})
|
||||
|
||||
socket.on('disconnect', reason => {
|
||||
this.pushMessage(createSystemMessage(`disconnect: ${reason}`))
|
||||
})
|
||||
|
||||
socket.on('reconnect', attemptNumber => {
|
||||
this.pushMessage(
|
||||
createSystemMessage(`reconnect success: ${attemptNumber}`),
|
||||
)
|
||||
})
|
||||
|
||||
socket.on('reconnect_failed', () => {
|
||||
this.pushMessage(createSystemMessage('reconnect_failed'))
|
||||
})
|
||||
|
||||
socket.on('error', err => {
|
||||
this.pushMessage(createSystemMessage(`error: ${err}`))
|
||||
})
|
||||
|
||||
/**
|
||||
* About chat
|
||||
*/
|
||||
socket.on('login', d => {})
|
||||
|
||||
socket.on('new message', d => {
|
||||
const { username, message } = d
|
||||
this.pushMessage(createUserMessage(message, username))
|
||||
})
|
||||
|
||||
socket.on('user joined', d => {
|
||||
this.pushMessage(createSystemMessage(`${d.username} joined`))
|
||||
this.pushMessage(createSystemMessage(`当前共有 ${d.numUsers} 人`))
|
||||
})
|
||||
|
||||
socket.on('user left', d => {
|
||||
this.pushMessage(createSystemMessage(`${d.username} left`))
|
||||
this.pushMessage(createSystemMessage(`当前共有 ${d.numUsers} 人`))
|
||||
})
|
||||
|
||||
socket.on('typing', d => {})
|
||||
|
||||
socket.on('stop typing', d => {})
|
||||
|
||||
socket.emit('add user', this.me.nickName)
|
||||
},
|
||||
})
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<view class="page-wrap">
|
||||
<scroll-view class="chat-container" scroll-y scroll-into-view="{{lastMessageId}}" scroll-top="9999999999">
|
||||
<view wx:for="{{messages}}" wx:for-item="message" wx:key="id" id="{{message.id}}" class="message {{message.type}}">
|
||||
<view wx:if="{{message.type == 'speak'}}" class="user-message {{message.isMe ? 'me' : 'other'}}">
|
||||
<view class="text">
|
||||
<view class="nickname" style="color: {{message.color}};">{{message.user}}</view>
|
||||
<view class="content">{{message.content}}</view>
|
||||
</view>
|
||||
</view>
|
||||
<view wx:if="{{message.type == 'system'}}" class="system-message">
|
||||
{{message.content}}
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
<view class="input-panel">
|
||||
<input type="text" value="{{inputContent}}" bindchange="changeInputContent" class="send-input" confirm-type="send" confirm-hold="true" bindconfirm="sendMessage"></input>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,144 @@
|
|||
.page-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #ebebeb;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.chat-container {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.system-message {
|
||||
font-size: 14px;
|
||||
line-height: 14px;
|
||||
color: #fff;
|
||||
background: #cecece;
|
||||
border-radius: 4px;
|
||||
display: inline-block;
|
||||
padding: 8rpx 15rpx;
|
||||
margin: 30rpx 0 10rpx;
|
||||
}
|
||||
.user-message {
|
||||
margin: 38rpx 20rpx 0;
|
||||
text-align: left;
|
||||
font-size: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 84rpx;
|
||||
height: 84rpx;
|
||||
border: #a5a5a7 1rpx solid;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.text {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
}
|
||||
.user-message.other .text {
|
||||
margin-left: 19rpx;
|
||||
}
|
||||
|
||||
.user-message.other .text view {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.text .nickname {
|
||||
color: #737373;
|
||||
font-size: 14px;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
.text .content {
|
||||
font-size: 30rpx;
|
||||
line-height: 36rpx;
|
||||
padding: 20rpx;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.text .nickname {
|
||||
font-size: 36rpx;
|
||||
}
|
||||
|
||||
.user-message.other .text .content::after,
|
||||
.user-message.other .text .content::before {
|
||||
right: 100%;
|
||||
border-right-style: solid;
|
||||
}
|
||||
|
||||
.input-panel {
|
||||
height: 100rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 13rpx 20rpx 0;
|
||||
background: #f5f5f7;
|
||||
border-top: #d7d7d9 1rpx solid;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.send-input {
|
||||
flex: 1;
|
||||
height: 72rpx;
|
||||
background: #fff;
|
||||
border: #ddd 1rpx solid;
|
||||
border-radius: 3px;
|
||||
/* margin-right: 20rpx; */
|
||||
box-sizing: border-box;
|
||||
/* padding: 0 10rpx; */
|
||||
}
|
||||
|
||||
.me .nickname {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.avatar {
|
||||
width: 100rpx;
|
||||
height: 100rpx;
|
||||
}
|
||||
.text .content {
|
||||
font-size: 36rpx;
|
||||
line-height: 44rpx;
|
||||
padding: 20rpx;
|
||||
position: relative;
|
||||
}
|
||||
.text .nickname {
|
||||
font-size: 42rpx;
|
||||
}
|
||||
.user-message.other .text .content::before {
|
||||
top: 22rpx;
|
||||
border-right-color: #ccc;
|
||||
}
|
||||
.user-message.other .text .content::after {
|
||||
border: 14rpx solid transparent;
|
||||
top: 23rpx;
|
||||
border-right-color: #fff;
|
||||
}
|
||||
|
||||
.input-panel {
|
||||
height: 120rpx;
|
||||
box-sizing: border-box;
|
||||
padding: 13rpx 20rpx 0;
|
||||
background: #f5f5f7;
|
||||
border-top: #d7d7d9 1rpx solid;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.send-input {
|
||||
flex: 1;
|
||||
height: 92rpx;
|
||||
background: #fff;
|
||||
border: #ddd 1rpx solid;
|
||||
border-radius: 3px;
|
||||
/* margin-right: 20rpx; */
|
||||
box-sizing: border-box;
|
||||
/* padding: 0 10rpx; */
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
// pages/home/index.js
|
||||
Page({
|
||||
|
||||
/**
|
||||
* 页面的初始数据
|
||||
*/
|
||||
data: {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面加载
|
||||
*/
|
||||
onLoad: function (options) {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面初次渲染完成
|
||||
*/
|
||||
onReady: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面显示
|
||||
*/
|
||||
onShow: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面隐藏
|
||||
*/
|
||||
onHide: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 生命周期函数--监听页面卸载
|
||||
*/
|
||||
onUnload: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面相关事件处理函数--监听用户下拉动作
|
||||
*/
|
||||
onPullDownRefresh: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 页面上拉触底事件的处理函数
|
||||
*/
|
||||
onReachBottom: function () {
|
||||
|
||||
},
|
||||
|
||||
/**
|
||||
* 用户点击右上角分享
|
||||
*/
|
||||
onShareAppMessage: function () {
|
||||
|
||||
}
|
||||
})
|
||||
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<view class="page">
|
||||
<view class="page__hd">
|
||||
<view class="page__title">聊天室 Demo</view>
|
||||
<view class="page__desc">本 Demo 主要为开发者演示 weapp.socket.io 的基本用法</view>
|
||||
</view>
|
||||
<view class="page__bd page__bd_spacing">
|
||||
<navigator url="/pages/chat/index" hover-class="weui-cell_active" class="chat-btn">进入聊天室</navigator>
|
||||
</view>
|
||||
</view>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
.chat-btn {
|
||||
background-color: white;
|
||||
width: 80%;
|
||||
margin: auto;
|
||||
height: 80rpx;
|
||||
border-radius: 10rpx;
|
||||
text-align: center;
|
||||
line-height: 80rpx;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"description": "项目配置文件。",
|
||||
"packOptions": {
|
||||
"ignore": []
|
||||
},
|
||||
"setting": {
|
||||
"urlCheck": false,
|
||||
"es6": true,
|
||||
"postcss": true,
|
||||
"minified": true,
|
||||
"newFeature": true
|
||||
},
|
||||
"compileType": "miniprogram",
|
||||
"libVersion": "1.9.98",
|
||||
"appid": "wxfbd99d2dee1708f9",
|
||||
"projectname": "weapp.socket.io.demo",
|
||||
"isGameTourist": false,
|
||||
"condition": {
|
||||
"search": {
|
||||
"current": -1,
|
||||
"list": []
|
||||
},
|
||||
"conversation": {
|
||||
"current": -1,
|
||||
"list": []
|
||||
},
|
||||
"game": {
|
||||
"currentL": -1,
|
||||
"list": []
|
||||
},
|
||||
"miniprogram": {
|
||||
"current": -1,
|
||||
"list": []
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,19 @@
|
|||
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: formatTime
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue