➕ socket.io
This commit is contained in:
parent
1239a951e8
commit
b6767f3b31
@ -76,9 +76,8 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue'
|
||||
import {mapState} from 'vuex'
|
||||
import {isBrowser} from '~/environment'
|
||||
import Vue from 'vue';
|
||||
import {mapState} from 'vuex';
|
||||
|
||||
export default {
|
||||
name: "Comment",
|
||||
|
28
io/index.js
Normal file
28
io/index.js
Normal file
@ -0,0 +1,28 @@
|
||||
import http from 'http'
|
||||
import socketIO from 'socket.io'
|
||||
|
||||
export default function () {
|
||||
this.nuxt.hook('render:before', (renderer) => {
|
||||
const server = http.createServer(this.nuxt.renderer.app)
|
||||
const io = socketIO(server)
|
||||
|
||||
// overwrite nuxt.server.listen()
|
||||
this.nuxt.server.listen = (port, host) => new Promise(resolve => server.listen(port || 3000, host || 'localhost', resolve))
|
||||
// close this server on 'close' event
|
||||
this.nuxt.hook('close', () => new Promise(server.close))
|
||||
|
||||
// Add socket.io events
|
||||
const messages = []
|
||||
io.on('connection', (socket) => {
|
||||
socket.on('last-messages', function (fn) {
|
||||
console.log('messages-client', messages);
|
||||
fn(messages.slice(-50))
|
||||
})
|
||||
socket.on('all', function (message) {
|
||||
console.log('message-client', message);
|
||||
messages.push(message)
|
||||
socket.broadcast.emit('new-message', message)
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
@ -65,6 +65,7 @@ export default {
|
||||
** Nuxt.js modules
|
||||
*/
|
||||
modules: [
|
||||
'~/io',
|
||||
'@nuxtjs/axios',
|
||||
'@nuxtjs/proxy',
|
||||
'js-cookie',
|
||||
|
816
package-lock.json
generated
816
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -12,12 +12,13 @@
|
||||
"@chenfengyuan/vue-qrcode": "^1.0.2",
|
||||
"@nuxtjs/axios": "^5.12.0",
|
||||
"cookieparser": "^0.1.0",
|
||||
"cross-env": "^7.0.2",
|
||||
"element-ui": "^2.13.2",
|
||||
"express": "^4.17.1",
|
||||
"js-cookie": "^2.2.1",
|
||||
"nuxt": "^2.14.0",
|
||||
"save-svg-as-png": "^1.4.17",
|
||||
"socket.io": "^2.3.0",
|
||||
"socket.io-client": "^2.3.0",
|
||||
"vditor": "^3.4.1",
|
||||
"vuejs-avataaars": "^4.0.1"
|
||||
},
|
||||
|
276
pages/chats/_nickname.vue
Normal file
276
pages/chats/_nickname.vue
Normal file
@ -0,0 +1,276 @@
|
||||
<template>
|
||||
<el-row class="wrapper">
|
||||
<el-col>
|
||||
<div id="contentEditor"></div>
|
||||
</el-col>
|
||||
<el-col style="margin-top: 1rem;padding-right:3rem;text-align: right;">
|
||||
<el-button type="primary" :loading="loading" @click="send">发送</el-button>
|
||||
</el-col>
|
||||
<el-col style="margin-top: 2rem;">
|
||||
<el-col v-for="message in messages" :key="message.dataId">
|
||||
<el-col v-if="message.from === user.nickname">
|
||||
<el-col :span="22" style="text-align: right;">
|
||||
<div class="from-message">{{message.content}}</div>
|
||||
</el-col>
|
||||
<el-col :span="2" style="text-align: right;">
|
||||
<el-avatar :src="user.avatarURL"></el-avatar>
|
||||
</el-col>
|
||||
</el-col>
|
||||
<el-col v-else>
|
||||
<el-col :span="2">
|
||||
<el-avatar :src="to.avatarURL"></el-avatar>
|
||||
</el-col>
|
||||
<el-col :span="22" style="text-align: left;">
|
||||
<div class="to-message">{{message.content}}</div>
|
||||
</el-col>
|
||||
</el-col>
|
||||
</el-col>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import {mapState} from 'vuex';
|
||||
import socket from '~/plugins/socket.io.js';
|
||||
|
||||
export default {
|
||||
name: "Chat",
|
||||
asyncData() {
|
||||
return new Promise(resolve =>
|
||||
socket.emit('last-messages', messages => resolve({messages}))
|
||||
)
|
||||
},
|
||||
computed: {
|
||||
...mapState({
|
||||
user: state => state.userInfo
|
||||
})
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
contentEditor: null,
|
||||
tokenURL: {},
|
||||
drawer: false,
|
||||
direction: 'btt',
|
||||
initEditor: false,
|
||||
isShow: true,
|
||||
loading: false,
|
||||
to: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
messages(value) {
|
||||
console.log(value);
|
||||
}
|
||||
},
|
||||
beforeMount() {
|
||||
socket.on('new-message', (message) => {
|
||||
this.messages.push(message)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
_initEditor(data) {
|
||||
let _ts = this;
|
||||
let toolbar;
|
||||
if (window.innerWidth < 768) {
|
||||
toolbar = [
|
||||
'emoji',
|
||||
'headings',
|
||||
'bold',
|
||||
'italic',
|
||||
'strike',
|
||||
'link',
|
||||
'|',
|
||||
'list',
|
||||
'ordered-list',
|
||||
'check',
|
||||
'outdent',
|
||||
'indent',
|
||||
'|',
|
||||
'quote',
|
||||
'line',
|
||||
'code',
|
||||
'inline-code',
|
||||
'insert-before',
|
||||
'insert-after',
|
||||
'|',
|
||||
'upload',
|
||||
'record',
|
||||
'table',
|
||||
'|',
|
||||
'undo',
|
||||
'redo',
|
||||
'|',
|
||||
'edit-mode',
|
||||
'content-theme',
|
||||
'code-theme',
|
||||
{
|
||||
name: 'more',
|
||||
toolbar: [
|
||||
'fullscreen',
|
||||
'both',
|
||||
'format',
|
||||
'preview',
|
||||
'info',
|
||||
'help',
|
||||
],
|
||||
}]
|
||||
}
|
||||
return new Vue.Vditor(data.id, {
|
||||
toolbar,
|
||||
mode: 'sv',
|
||||
tab: '\t',
|
||||
cache: {
|
||||
enable: this.postId ? false : true,
|
||||
id: this.postId ? this.postId : '',
|
||||
},
|
||||
after() {
|
||||
_ts.contentEditor.setValue(data.value ? data.value : '');
|
||||
},
|
||||
preview: {
|
||||
markdown: {
|
||||
toc: true,
|
||||
},
|
||||
delay: 500,
|
||||
mode: data.mode,
|
||||
/*url: `${process.env.Server}/api/console/markdown`,*/
|
||||
parse: (element) => {
|
||||
if (element.style.display === 'none') {
|
||||
return
|
||||
}
|
||||
// LazyLoadImage();
|
||||
// Vue.Vditor.highlightRender({style:'github'}, element, document);
|
||||
}
|
||||
},
|
||||
upload: {
|
||||
max: 10 * 1024 * 1024,
|
||||
url: this.tokenURL?.URL,
|
||||
linkToImgUrl: this.tokenURL?.URL,
|
||||
token: this.tokenURL?.token,
|
||||
filename: name => name.replace(/\?|\\|\/|:|\||<|>|\*|\[|\]|\s+/g, '-')
|
||||
},
|
||||
height: data.height,
|
||||
counter: 102400,
|
||||
resize: {
|
||||
enable: data.resize,
|
||||
},
|
||||
lang: this.$store.state.locale,
|
||||
placeholder: data.placeholder,
|
||||
})
|
||||
},
|
||||
send() {
|
||||
let _ts = this;
|
||||
const message = {
|
||||
to: _ts.to.nickname,
|
||||
from: _ts.user.nickname,
|
||||
dataType: 1,
|
||||
dataId: new Date().getTime(),
|
||||
content: _ts.contentEditor.getValue()
|
||||
}
|
||||
_ts.messages.push(message);
|
||||
_ts.contentEditor.setValue('')
|
||||
socket.emit('all', message);
|
||||
}
|
||||
},
|
||||
async mounted() {
|
||||
let _ts = this;
|
||||
_ts.$store.commit('setActiveMenu', 'post-article');
|
||||
if (_ts.user) {
|
||||
const responseData = await _ts.$axios.$get('/api/upload/token');
|
||||
if (responseData) {
|
||||
_ts.$set(_ts, 'tokenURL', {
|
||||
token: responseData.uploadToken || '',
|
||||
URL: responseData.uploadURL || '',
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if (!_ts.initEditor) {
|
||||
_ts.$set(_ts, 'initEditor', true);
|
||||
setTimeout(function () {
|
||||
_ts.contentEditor = _ts._initEditor({
|
||||
id: 'contentEditor',
|
||||
mode: 'both',
|
||||
height: 200,
|
||||
placeholder: '', //this.$t('inputContent', this.$store.state.locale)
|
||||
resize: false,
|
||||
value: ''
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
let to = {
|
||||
nickname: _ts.$route.params.nickname,
|
||||
avatarURL: _ts.$route.params.avatarURL,
|
||||
}
|
||||
_ts.$set(_ts, 'to', to);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import "~vditor/src/assets/scss/index.scss";
|
||||
|
||||
.from-message {
|
||||
float: right;
|
||||
width: 200px;
|
||||
min-height: 40px;
|
||||
margin: 10px;
|
||||
background-color: skyblue;
|
||||
border-bottom-color: skyblue;
|
||||
/*为了给after伪元素自动继承*/
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
padding: 5px 12px 5px 12px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.to-message {
|
||||
float: left;
|
||||
width: 200px;
|
||||
min-height: 40px;
|
||||
margin: 10px;
|
||||
background-color: skyblue;
|
||||
border-bottom-color: skyblue;
|
||||
/*为了给after伪元素自动继承*/
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
line-height: 18px;
|
||||
padding: 5px 12px 5px 12px;
|
||||
box-sizing: border-box;
|
||||
border-radius: 6px;
|
||||
position: relative;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.from-message::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: -5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-top: -5px;
|
||||
background: inherit;
|
||||
/*自动继承父元素的背景*/
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
/** 通过对小正方形旋转45度解决 **/
|
||||
.to-message::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: -5px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
margin-top: -5px;
|
||||
background: inherit;
|
||||
/*自动继承父元素的背景*/
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
</style>
|
@ -11,6 +11,9 @@
|
||||
<img v-else class="card-profile-img" src="https://static.rymcu.com/article/1578475481946.png">
|
||||
<h3 class="mb-3">{{user.nickname}}</h3>
|
||||
<p class="mb-4" v-html="user.signature"></p>
|
||||
<div v-if="oauth && oauth.idUser !== user.idUser">
|
||||
<el-button @click="gotoChats">聊天</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
@ -58,7 +61,8 @@
|
||||
...mapState({
|
||||
user: state => state.user.data,
|
||||
articles: state => state.user.articles,
|
||||
portfolios: state => state.user.portfolios
|
||||
portfolios: state => state.user.portfolios,
|
||||
oauth: state => state.oauth
|
||||
})
|
||||
},
|
||||
data() {
|
||||
@ -75,6 +79,12 @@
|
||||
},
|
||||
handleToggleTab(key) {
|
||||
this.$set(this, 'activeTab', key);
|
||||
},
|
||||
gotoChats() {
|
||||
let _ts = this;
|
||||
_ts.$router.push({
|
||||
path: `/chats/${_ts.user.nickname}`
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
8
plugins/socket.io.js
Normal file
8
plugins/socket.io.js
Normal file
@ -0,0 +1,8 @@
|
||||
import io from 'socket.io-client'
|
||||
import apiConfig from '~/config/api.config'
|
||||
|
||||
const socket = io(apiConfig.SOCKET, {
|
||||
transports: ['websocket']
|
||||
})
|
||||
|
||||
export default socket
|
@ -1,6 +1,8 @@
|
||||
const { Nuxt, Builder } = require('nuxt')
|
||||
|
||||
const http = require('http')
|
||||
const app = require('express')()
|
||||
const socketio = require('socket.io')
|
||||
const isProd = (process.env.NODE_ENV === 'production')
|
||||
const port = process.env.PORT || 3000
|
||||
|
||||
@ -8,6 +10,8 @@ const port = process.env.PORT || 3000
|
||||
const config = require('./nuxt.config.js')
|
||||
config.dev = !isProd
|
||||
const nuxt = new Nuxt(config)
|
||||
const server = new http.Server(app)
|
||||
const io = socketio(server, { transports: ['websocket'] })
|
||||
|
||||
// 用 Nuxt.js 渲染每个路由
|
||||
app.use(nuxt.render)
|
||||
@ -26,3 +30,19 @@ function listen () {
|
||||
app.listen(port, '0.0.0.0')
|
||||
console.log('Server listening on `localhost:' + port + '`.')
|
||||
}
|
||||
|
||||
// Socket.io
|
||||
const messages = []
|
||||
io.on('connection', (socket) => {
|
||||
socket.on('last-messages', function (fn) {
|
||||
console.log('messages-server', messages);
|
||||
fn(messages.slice(-50))
|
||||
});
|
||||
|
||||
socket.on('all', function (message) {
|
||||
console.log('message-server', message);
|
||||
messages.push(message)
|
||||
socket.broadcast.emit('new-message', message)
|
||||
});
|
||||
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user