{{$t('home.title')}}
-
{{item.data}}
目录结构:
im | server | controllers |
index.js | ||
|
|||||
app.js controller.js package.json node_modules |
|||||
vue | src | api | index.js | ||
assets | css | index.css | |||
config |
index.js dev.js prod.js |
||||
lang |
index.js en.js zh.js |
||||
router | index.js | ||||
service | index.js | ||||
store |
modules index.js |
login.js
|
|||
util | locale.js | ||||
views | home.vue login.vue |
||||
|
|
||||
App.vue | |||||
main.js | |||||
static | css |
normalize.css | |||
img |
0.png 1.png 2.png 3.png 4.png 5.png 6.png 7.png 8.png 9.png |
||||
package.json |
im\server\controllers\index.js:
var userIndex = 0
async function loginName (ctx, next) {
let locale = ctx.cookies.get('locale')
let langMap = {
zh: '甲乙丙丁戊己庚辛壬癸',
en: 'ABCDEFGHIJ',
}
let names = langMap[`${locale}`]
let name = names[userIndex % 10]
ctx.response.body = JSON.stringify({
code: 1,
data: {name},
message: 'ok'
})
ctx.response.type = 'json'
}
async function login (ctx, next) {
let name = ctx.request.body.name
let user = {
id: ++userIndex,
name: name,
time: new Date().getTime(),
img: userIndex % 10
}
let value = Buffer.from(JSON.stringify(user)).toString('base64')
console.log(`Set cookie value: ${value}`)
ctx.cookies.set('name', value)
ctx.response.body = JSON.stringify({
code: 1,
data: user,
message: 'ok'
})
ctx.response.type = 'json'
}
async function logout (ctx, next) {
ctx.cookies.set('name', '')
}
module.exports = [
{
method: 'GET',
path: '/loginName',
fn: loginName
},
{
method: 'POST',
path: '/login',
fn: login
},
{
method: 'POST',
path: '/logout',
fn: logout
}
]
im\server\app.js:
const Koa = require('koa')
const app = new Koa()
const Cookies = require('cookies');
const bodyParser = require('koa-bodyparser')
const controller = require('./controller')
const server = app.listen(3000)
// 导入WebSocket模块
const WebSocket = require('ws')
// 引用Server类
const WebSocketServer = WebSocket.Server
// parse user from cookie:
app.use(async (ctx, next) => {
ctx.state.user = parseUser(ctx.cookies.get('name') || '')
ctx.state.userLocale = ctx.cookies.get('locale') || ''
console.log('ctx.state.user: ', ctx.state.user)
console.log('ctx.state.userLocale: ', ctx.state.userLocale)
await next()
})
function createWebSocketServer(server, onConnection, onMessage, onClose, onError) {
let wss = new WebSocketServer({server}) // 实例化
wss.broadcast = data => {
wss.clients.forEach(client => {
client.send(data)
})
}
wss.on('connection', ws => {
onConnection = onConnection || function () {
console.log('[WebSocket] connected.')
}
onMessage = onMessage || function (msg) {
console.log('[WebSocket] message received: ' + msg)
}
onClose = onClose || function (code, message) {
console.log(`[WebSocket] closed: ${code} - ${message}`)
}
onError = onError || function (err) {
console.log('[WebSocket] error: ' + err)
}
ws.on('message', onMessage)
ws.on('close', onClose)
ws.on('error', onError)
let user = parseUser(ws.upgradeReq)
if (!user) {
ws.close(4001, 'Invalid user')
}
ws.user = user
ws.wss = wss
onConnection.apply(ws)
})
console.log('WebSocketServer was attached.')
return wss
}
var messageIndex = 0
function createMessage(type, user, data) {
return JSON.stringify({
id: ++messageIndex,
type: type,
time: new Date().getTime(),
user: user,
data: data
})
}
function onConnect() {
let tip = {
zh: '加入聊天室',
en: 'Join the chat room'
}
let msg = createMessage('add', this.user, tip)
this.wss.broadcast(msg)
// build user list:
let users = this.wss.clients.map(client => client.user)
msg = createMessage('list', this.user, users)
this.wss.broadcast(msg)
}
function onMessage(message) {
console.log(message)
let msg = createMessage('chat', this.user, message)
this.wss.broadcast(msg)
}
function onClose() {
let tip = {
zh: '退出聊天室',
en: 'Quit chat room'
}
let msg = createMessage('del', this.user, tip)
this.wss.broadcast(msg)
}
function parseUser(obj) { // 解析用户
if (!obj) {
return
}
console.log('try parse: ' + obj)
let s = ''
if (typeof obj === 'string') {
s = obj
} else if (obj.headers) {
let cookies = new Cookies(obj, null)
s = cookies.get('name')
}
if (s) {
try {
let user = JSON.parse(Buffer.from(s, 'base64').toString())
console.log(`User: ${user.name}, ID: ${user.id}`)
return user
} catch (e) {
console.log(e)
}
}
}
app.use(bodyParser())
app.use(controller())
app.wss = createWebSocketServer(server, onConnect, onMessage, onClose)
console.log('http://127.0.0.1:3000')
im\server\controller.js:
// 导入fs
const fs = require('fs')
function addControllers(router, dir) {
fs.readdir(dir, (err, files) => {
if (err) throw err
files.filter(f => f.endsWith('.js')).forEach(f => {
let mappingList = require('./' + dir + '/' + f)
console.log(`Loading file: ${f} for router`)
mappingList.forEach(params => addMapping({router, ...params}))
})
})
}
function addMapping({router, method, path, fn}) {
switch(method) {
case 'GET':
router.get(path, fn)
console.log(`register URL mapping: GET ${path}`)
return
case 'POST':
router.post(path, fn)
console.log(`register URL mapping: POST ${path}`)
return
case 'PUT':
router.put(path, fn)
console.log(`register URL mapping: PUT ${path}`)
return
case 'DELETE':
router.del(path, fn)
console.log(`register URL mapping: DELETE ${path}`)
return
default:
console.log(`Invalid method: ${method} with path: ${path}`)
return
}
}
module.exports = function (dir) {
let controllers_dir = dir || 'controllers',
router = require('koa-router')()
addControllers(router, controllers_dir)
return router.routes()
}
im\server\package.json:
{
"name": "my-koa",
"version": "1.0.0",
"description": "koa",
"main": "app.js",
"scripts": {
"dev": "node app.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Gulinling",
"license": "ISC",
"dependencies": {
"ws": "1.1.1",
"koa": "2.0.0",
"koa-bodyparser": "3.2.0",
"koa-router": "7.0.0",
"nunjucks": "2.4.2",
"mime": "1.3.4",
"mz": "2.4.0"
}
}
im\vue\src\api\index.js:
import service from '@/service'
import config from '@/config'
export default {
login (params) {
return service.loop(config.login, params, 'post')
},
loginName () {
return service.loop(config.loginName, {}, 'get')
}
}
m\vue\src\assets\css\index.css:
html, body, #app{
height: 100%;
}
.container{
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
text-align: center;
}
im\vue\src\config\dev.js:
export const dev = {
login: 'login',
loginName: 'loginName'
}
im\vue\src\config\index.js:
import {dev} from './dev'
import {prod} from './prod'
const env = process.env.NODE_ENV
function getConfig (env) {
let config = null
switch (env) {
case 'development':
config = dev
break
case 'production':
config = prod
break
default:
config = dev
}
return config
}
export default {
...getConfig(env)
}
im\vue\src\config\prod.js:
export const prod = {
login: 'login',
loginName: 'loginName'
}
im\vue\src\lang\en.js:
export default {
home: {
title: 'chat room',
userList: 'User list',
chatRecord: 'Chat record'
},
login: {
title: 'Your Name'
}
}
im\vue\src\lang\index.js:
import Vue from 'vue'
import Element from 'element-ui'
import VueI18n from 'vue-i18n'
import locale from '@/util/locale'
import en from './en'
import zh from './zh'
import 'element-ui/lib/theme-chalk/index.css'
import './../../static/css/normalize.css'
import './../assets/css/index.css'
document.cookie = `locale = ${locale}`
Vue.use(VueI18n)
Vue.use(Element)
const i18n = new VueI18n({
locale,
messages: {
en,
zh
}
})
export default i18n
im\vue\src\lang\zh.js:
export default {
home: {
title: '聊天室',
userList: '用户列表',
chatRecord: '聊天记录'
},
login: {
title: '你的名字'
}
}
im\vue\src\router\index.js:
import Vue from 'vue'
import Router from 'vue-router'
import home from '@/views/home'
import login from '@/views/login'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: home.name,
component: home
},
{
path: '/login',
name: login.name,
component: login
}
]
})
im\vue\src\service\index.js:
import axios from 'axios' // 导入axios
const service = axios.create({ // 创建实例
baseURL: '/api/', // url 前缀
timeout: 3000 // 请求超时时间
})
service.interceptors.request.use( // 请求拦截器
config => {
config.headers['User-Token'] = 'toKen' // 让每个请求携带token
return config
},
error => {
Promise.reject(error) // 请求错误处理
}
)
service.interceptors.response.use( // 响应拦截器
response => { // 统一处理状态
const res = response.data
if (res.code !== 1 || response.status !== 200) { // 需自定义
return Promise.reject({ // 返回异常
code: res.code,
message: res.message
})
} else {
return res
}
},
error => { // 处理处理
return Promise.reject(error)
}
)
function get (url, params = {}) { // get请求
return new Promise((resolve, reject) => {
service({
url,
params,
method: 'get'
})
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
}
function post (url, data = {}) { // post请求
return new Promise((resolve, reject) => {
service({
url,
data,
method: 'post'
})
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
}
function loop (url, params = {}, method = 'get') { // 循环请求(get post)
return new Promise((resolve, reject) => {
serviceLoop({url, params, method, resolve, reject})
})
}
function serviceLoop ({url, params, method, resolve, reject}) {
let option = {
url,
method
}
method = method.toLocaleLowerCase()
if (method === 'get') {
option.params = params
} else if (method === 'post') {
option.data = params
} else {
throw new Error('目前只支持get和post')
}
service(option)
.then(res => {
resolve(res)
})
.catch(err => {
setTimeout(() => {
serviceLoop({url, params, method, resolve, reject})
}, 1000)
})
}
function fileUpload(url, data = {}) { // 文件上传
return new Promise((resolve, reject) => {
service({
url,
data,
method: 'post',
headers: { 'Content-Type': 'multipart/form-data' }
})
.then(res => {
resolve(res)
})
.catch(err => {
reject(err)
})
})
}
export default {
get,
post,
loop,
fileUpload
}
im\vue\src\store\index.js:
// 引入vue
import Vue from 'vue'
// 引入vuex
import Vuex from 'vuex'
// 导入仓库模块
import {login} from './modules/login.js'
// 使用vuex
Vue.use(Vuex)
const store = new Vuex.Store({
state: { // 1、state:创建初始化状态
count: 1 // 放置初始状态
},
mutations: { // 2、mutations:创建改变状态的方法
ADD (state, n) { // 状态变更函数一般大写
state.count += n
}
},
getters: { // 3、getters:提供外部获取state
count (state) {
return state.count
}
},
actions: { // 4、actions:创建驱动方法改变mutations
add ({commit}, data) { // 触发mutations中相应的方法一般小写
commit('ADD', data)
}
},
modules: {
login
}
})
// 5、输出store
export default store
im\vue\src\store\modules\login.js:
export const login = {
state: { // 1、state:创建初始化状态
user: {} // 放置初始状态
},
mutations: { // 2、mutations:创建改变状态的方法
updateUser (state, newVal) {
state.user = newVal
}
},
getters: { // 3、getters:提供外部获取state
user (state) {
return state.user
}
},
actions: { // 4、actions:创建驱动方法改变mutations
updateUser ({commit}, data) { // 触发mutations中相应的方法
commit('updateUser', data)
}
}
}
im\vue\src\util\locale.js:
function getBrowserLang () {
let locale = null
if (navigator.languages) {
locale = navigator.languages[0].toLocaleLowerCase()
} else {
locale = (navigator.language || navigator.userLanguage || navigator.browserLanguage || navigator.systemLanguage).toLocaleLowerCase()
}
if (~locale.indexOf('zh')) {
locale = 'zh'
} else if (~locale.indexOf('en')) {
locale = 'en'
} else {
locale = 'zh'
}
return locale
}
export default getBrowserLang()
im\vue\src\views\home.vue:
{{$t('home.title')}}
{{$t('home.userList')}}
-
{{$t('home.chatRecord')}}
-
{{item.data}}
im\vue\src\views\login.vue:
{{$t('login.title')}}
im\vue\src\App.vue:
im\vue\src\main.js:
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
import i18n from './lang'
Vue.config.productionTip = false
/* eslint-disable no-new */
window.vue = new Vue({
el: '#app',
router,
store,
i18n,
components: { App },
template: ' '
})
im\vue\static\css\normalize.css:http://necolas.github.io/normalize.css/
im\vue\static\img:0-9图片自己网上下载一些头像喽
im\vue\package.json:
{
"name": "vue",
"version": "1.0.0",
"description": "A Vue.js project",
"author": "",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js --host 10.17.143.11",
"start": "npm run dev",
"e2e": "node test/e2e/runner.js",
"test": "npm run unit && npm run e2e",
"lint": "eslint --ext .js,.vue src test/unit test/e2e/specs",
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.18.0",
"element-ui": "^2.4.7",
"vue": "^2.5.2",
"vue-i18n": "^8.1.0",
"vue-router": "^3.0.1",
"vuex": "^3.0.1"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-eslint": "^7.1.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"babel-register": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"cross-spawn": "^5.0.1",
"css-loader": "^0.28.0",
"eslint": "^4.15.0",
"eslint-config-standard": "^10.2.1",
"eslint-friendly-formatter": "^3.0.0",
"eslint-loader": "^1.7.1",
"eslint-plugin-import": "^2.7.0",
"eslint-plugin-node": "^5.2.0",
"eslint-plugin-promise": "^3.4.0",
"eslint-plugin-standard": "^3.0.1",
"eslint-plugin-vue": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"nightwatch": "^0.9.12",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"selenium-server": "^3.0.1",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}
需要你切换到server目录和vue目录安装依赖,运行需要先切换server执行npm run dev再切换vue执行同样的操作,至此完结撒花