基础功能上传

This commit is contained in:
邰广银 2017-10-27 19:35:03 +08:00
parent 3dba4147ea
commit 3c78c7f4c2
10 changed files with 745 additions and 24 deletions

5
config.js Normal file
View File

@ -0,0 +1,5 @@
const APP = {
"KEFUUUID":'chat-kefu-admin'
};
module.exports = APP;

111
io/io.js
View File

@ -6,6 +6,7 @@
var redis = require('../utils/redis');
var msgType = require('./messageTpye');
var ioSvc = require('./ioHelper').ioSvc;
var AppConfig = require('../config');
//服务端连接
function ioServer(io) {
@ -20,6 +21,13 @@ function ioServer(io) {
}
});
Array.prototype.remove = function(val) {
var index = this.indexOf(val);
if (index > -1) {
this.splice(index, 1);
}
};
io.on('connection', function (socket) {
console.log('SocketIO有新的连接!');
@ -28,22 +36,76 @@ function ioServer(io) {
//用户与Socket进行绑定
socket.on('login', function (uid) {
console.log(uid+'登录成功');
//通知用户上线
if(uid != AppConfig.KEFUUUID){
redis.get(AppConfig.KEFUUUID,function (err,sid) {
if(err){
console.error(err);
}
if(sid){
redis.get('online_count',function (err,val) {
if(err){
console.error(err);
}
if(!val){
val = 0;
}
if(typeof val == 'string'){
val = parseInt(val);
}
var info = {
"uid":uid,
"name":'客户'+val,
"type":'online'
};
io.to(sid).emit('update-users',info);
});
}
});
redis.get('user-uuids',function (err,uuids) {
if(err){
console.error(err);
}
if(uuids){
uuids =JSON.parse(uuids);
}else{
uuids = [];
}
if(uuids.indexOf(uid) == -1){
uuids.push(uid);
uuids = JSON.stringify(uuids);
redis.set('user-uuids',uuids,null,function (err,ret) {
if(err){
console.error(err);
}
});
}
});
}
redis.set(uid,socket.id,null,function (err,ret) {
if(err){
console.error(err);
}
});
redis.set(socket.id,uid,null,function (err,ret) {
if(err){
console.error(err);
}
});
});
//断开事件
socket.on('disconnect', function() {
console.log("与服务其断开");
_self.updateOnlieCount(false);
redis.get(socket.id,function (err,val) {
if(err){
console.error(err);
@ -59,6 +121,43 @@ function ioServer(io) {
console.error(err);
}
});
//通知用户下线
if(val != AppConfig.KEFUUUID){
redis.get(AppConfig.KEFUUUID,function (err,sid) {
if(err){
console.error(err);
}
if(sid){
var info = {
"uid":val,
"name":'客户下线',
"type":'offline'
};
io.to(sid).emit('update-users',info);
}
});
redis.get('user-uuids',function (err,uuids) {
if(err){
console.error(err);
}
if(uuids){
uuids =JSON.parse(uuids);
}else{
uuids = [];
}
if(uuids.indexOf(val) != -1){
uuids.remove(val);
uuids = JSON.stringify(uuids);
redis.set('user-uuids',uuids,null,function (err,ret) {
if(err){
console.error(err);
}
});
}
});
}
});
});
@ -70,7 +169,11 @@ function ioServer(io) {
//监听客户端发送的信息,实现消息转发到各个其他客户端
socket.on('message',function(msg){
if(msg.type == msgType.messageType.public){
socket.broadcast.emit("message",msg.content);
var mg = {
"uid" : msg.from_uid ,
"content": msg.content
};
socket.broadcast.emit("message",mg);
}else if(msg.type == msgType.messageType.private){
var uid = msg.uid;
redis.get(uid,function (err,sid) {
@ -79,7 +182,11 @@ function ioServer(io) {
}
if(sid){
//给指定的客户端发送消息
io.sockets.socket(sid).emit('message', msg.content);
var mg = {
"uid" : msg.from_uid,
"content": msg.content
};
io.to(sid).emit('message',mg);
}
});
}

View File

@ -193,4 +193,5 @@ a {
line-height: 44px;
font-size: 14px;
color: #858e99;
cursor: pointer;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

View File

@ -1,6 +1,7 @@
$(function(){
//Socket.IO 连接
var socket = io.connect('http://'+document.domain+':9010');
var uuid = '';
function insert_client_html(time,content){
var tpl = '<div class="msg-box">'+
@ -33,17 +34,36 @@ $(function(){
$(".msg-container").append(tpl);
}
//聊天窗口自动滚到底
function scrollToBottom() {
var div = document.getElementById('msg-container');
div.scrollTop = div.scrollHeight;
}
$("#btnSend").click(function(){
var date = dateFormat();
var msg = $("#textarea").val();
insert_client_html(date,msg);
if(msg){
var msg_sender = {
"type":'private',
"uid":'chat-kefu-admin',
"content":msg,
"from_uid":uuid
};
socket.emit('message', msg_sender);
insert_client_html(date,msg);
scrollToBottom();
$("#textarea").val('');
}
});
//连接服务器
socket.on('connect', function () {
console.log('连接成功...');
var uuid = 'chat'+ guid();
//uuid = 'chat'+ guid();
var fp1 = new Fingerprint();
uuid = fp1.get();
console.log('连接成功...'+uuid);
socket.emit('login', uuid);
});
@ -54,6 +74,7 @@ $(function(){
// */
socket.on('message', function(msg){
insert_agent_html(dateFormat(),msg.content);
scrollToBottom();
});
});

293
public/js/fingerprint.js Executable file
View File

@ -0,0 +1,293 @@
/*
* fingerprintJS 0.5.5 - Fast browser fingerprint library
* https://github.com/Valve/fingerprintjs
* Copyright (c) 2013 Valentin Vasilyev (valentin.vasilyev@outlook.com)
* Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
;(function (name, context, definition) {
if (typeof module !== 'undefined' && module.exports) { module.exports = definition(); }
else if (typeof define === 'function' && define.amd) { define(definition); }
else { context[name] = definition(); }
})('Fingerprint', this, function () {
'use strict';
var Fingerprint = function (options) {
var nativeForEach, nativeMap;
nativeForEach = Array.prototype.forEach;
nativeMap = Array.prototype.map;
this.each = function (obj, iterator, context) {
if (obj === null) {
return;
}
if (nativeForEach && obj.forEach === nativeForEach) {
obj.forEach(iterator, context);
} else if (obj.length === +obj.length) {
for (var i = 0, l = obj.length; i < l; i++) {
if (iterator.call(context, obj[i], i, obj) === {}) return;
}
} else {
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
if (iterator.call(context, obj[key], key, obj) === {}) return;
}
}
}
};
this.map = function(obj, iterator, context) {
var results = [];
// Not using strict equality so that this acts as a
// shortcut to checking for `null` and `undefined`.
if (obj == null) return results;
if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);
this.each(obj, function(value, index, list) {
results[results.length] = iterator.call(context, value, index, list);
});
return results;
};
if (typeof options == 'object'){
this.hasher = options.hasher;
this.screen_resolution = options.screen_resolution;
this.screen_orientation = options.screen_orientation;
this.canvas = options.canvas;
this.ie_activex = options.ie_activex;
} else if(typeof options == 'function'){
this.hasher = options;
}
};
Fingerprint.prototype = {
get: function(){
var keys = [];
keys.push(navigator.userAgent);
keys.push(navigator.language);
keys.push(screen.colorDepth);
if (this.screen_resolution) {
var resolution = this.getScreenResolution();
if (typeof resolution !== 'undefined'){ // headless browsers, such as phantomjs
keys.push(resolution.join('x'));
}
}
keys.push(new Date().getTimezoneOffset());
keys.push(this.hasSessionStorage());
keys.push(this.hasLocalStorage());
keys.push(this.hasIndexDb());
//body might not be defined at this point or removed programmatically
if(document.body){
keys.push(typeof(document.body.addBehavior));
} else {
keys.push(typeof undefined);
}
keys.push(typeof(window.openDatabase));
keys.push(navigator.cpuClass);
keys.push(navigator.platform);
keys.push(navigator.doNotTrack);
keys.push(this.getPluginsString());
if(this.canvas && this.isCanvasSupported()){
keys.push(this.getCanvasFingerprint());
}
if(this.hasher){
return this.hasher(keys.join('###'), 31);
} else {
return this.murmurhash3_32_gc(keys.join('###'), 31);
}
},
/**
* JS Implementation of MurmurHash3 (r136) (as of May 20, 2011)
*
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
* @see http://github.com/garycourt/murmurhash-js
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
* @see http://sites.google.com/site/murmurhash/
*
* @param {string} key ASCII only
* @param {number} seed Positive integer only
* @return {number} 32-bit positive integer hash
*/
murmurhash3_32_gc: function(key, seed) {
var remainder, bytes, h1, h1b, c1, c2, k1, i;
remainder = key.length & 3; // key.length % 4
bytes = key.length - remainder;
h1 = seed;
c1 = 0xcc9e2d51;
c2 = 0x1b873593;
i = 0;
while (i < bytes) {
k1 =
((key.charCodeAt(i) & 0xff)) |
((key.charCodeAt(++i) & 0xff) << 8) |
((key.charCodeAt(++i) & 0xff) << 16) |
((key.charCodeAt(++i) & 0xff) << 24);
++i;
k1 = ((((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16))) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = ((((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16))) & 0xffffffff;
h1 ^= k1;
h1 = (h1 << 13) | (h1 >>> 19);
h1b = ((((h1 & 0xffff) * 5) + ((((h1 >>> 16) * 5) & 0xffff) << 16))) & 0xffffffff;
h1 = (((h1b & 0xffff) + 0x6b64) + ((((h1b >>> 16) + 0xe654) & 0xffff) << 16));
}
k1 = 0;
switch (remainder) {
case 3: k1 ^= (key.charCodeAt(i + 2) & 0xff) << 16;
case 2: k1 ^= (key.charCodeAt(i + 1) & 0xff) << 8;
case 1: k1 ^= (key.charCodeAt(i) & 0xff);
k1 = (((k1 & 0xffff) * c1) + ((((k1 >>> 16) * c1) & 0xffff) << 16)) & 0xffffffff;
k1 = (k1 << 15) | (k1 >>> 17);
k1 = (((k1 & 0xffff) * c2) + ((((k1 >>> 16) * c2) & 0xffff) << 16)) & 0xffffffff;
h1 ^= k1;
}
h1 ^= key.length;
h1 ^= h1 >>> 16;
h1 = (((h1 & 0xffff) * 0x85ebca6b) + ((((h1 >>> 16) * 0x85ebca6b) & 0xffff) << 16)) & 0xffffffff;
h1 ^= h1 >>> 13;
h1 = ((((h1 & 0xffff) * 0xc2b2ae35) + ((((h1 >>> 16) * 0xc2b2ae35) & 0xffff) << 16))) & 0xffffffff;
h1 ^= h1 >>> 16;
return h1 >>> 0;
},
// https://bugzilla.mozilla.org/show_bug.cgi?id=781447
hasLocalStorage: function () {
try{
return !!window.localStorage;
} catch(e) {
return true; // SecurityError when referencing it means it exists
}
},
hasSessionStorage: function () {
try{
return !!window.sessionStorage;
} catch(e) {
return true; // SecurityError when referencing it means it exists
}
},
hasIndexDb: function () {
try{
return !!window.indexedDB;
} catch(e) {
return true; // SecurityError when referencing it means it exists
}
},
isCanvasSupported: function () {
var elem = document.createElement('canvas');
return !!(elem.getContext && elem.getContext('2d'));
},
isIE: function () {
if(navigator.appName === 'Microsoft Internet Explorer') {
return true;
} else if(navigator.appName === 'Netscape' && /Trident/.test(navigator.userAgent)){// IE 11
return true;
}
return false;
},
getPluginsString: function () {
if(this.isIE() && this.ie_activex){
return this.getIEPluginsString();
} else {
return this.getRegularPluginsString();
}
},
getRegularPluginsString: function () {
return this.map(navigator.plugins, function (p) {
var mimeTypes = this.map(p, function(mt){
return [mt.type, mt.suffixes].join('~');
}).join(',');
return [p.name, p.description, mimeTypes].join('::');
}, this).join(';');
},
getIEPluginsString: function () {
if(window.ActiveXObject){
var names = ['ShockwaveFlash.ShockwaveFlash',//flash plugin
'AcroPDF.PDF', // Adobe PDF reader 7+
'PDF.PdfCtrl', // Adobe PDF reader 6 and earlier, brrr
'QuickTime.QuickTime', // QuickTime
// 5 versions of real players
'rmocx.RealPlayer G2 Control',
'rmocx.RealPlayer G2 Control.1',
'RealPlayer.RealPlayer(tm) ActiveX Control (32-bit)',
'RealVideo.RealVideo(tm) ActiveX Control (32-bit)',
'RealPlayer',
'SWCtl.SWCtl', // ShockWave player
'WMPlayer.OCX', // Windows media player
'AgControl.AgControl', // Silverlight
'Skype.Detection'];
// starting to detect plugins in IE
return this.map(names, function(name){
try{
new ActiveXObject(name);
return name;
} catch(e){
return null;
}
}).join(';');
} else {
return ""; // behavior prior version 0.5.0, not breaking backwards compat.
}
},
getScreenResolution: function () {
var resolution;
if(this.screen_orientation){
resolution = (screen.height > screen.width) ? [screen.height, screen.width] : [screen.width, screen.height];
}else{
resolution = [screen.height, screen.width];
}
return resolution;
},
getCanvasFingerprint: function () {
var canvas = document.createElement('canvas');
var ctx = canvas.getContext('2d');
// https://www.browserleaks.com/canvas#how-does-it-work
var txt = 'http://valve.github.io';
ctx.textBaseline = "top";
ctx.font = "14px 'Arial'";
ctx.textBaseline = "alphabetic";
ctx.fillStyle = "#f60";
ctx.fillRect(125,1,62,20);
ctx.fillStyle = "#069";
ctx.fillText(txt, 2, 15);
ctx.fillStyle = "rgba(102, 204, 0, 0.7)";
ctx.fillText(txt, 4, 17);
return canvas.toDataURL();
}
};
return Fingerprint;
});

173
public/js/server/index.js Normal file
View File

@ -0,0 +1,173 @@
//一般直接写在一个js文件中
layui.use(['layer', 'form', 'jquery'], function () {
var layer = layui.layer
, form = layui.form
, $ = layui.jquery;
var currentUUID = '';
var uuid = '';
var socket = io.connect('http://'+document.domain+':9010');
var uuids = [];
Array.prototype.remove = function(val) {
var index = this.indexOf(val);
if (index > -1) {
this.splice(index, 1);
}
};
//页面初始化函数
function init() {
$(".admin-index").addClass("layui-this");
var height = document.body.clientHeight - 262;
$(".message-container").css("height", height);
window.onresize = function(){
var height = document.body.clientHeight - 262;
$(".message-container").css("height", height);
}
}
//聊天窗口自动滚到底
function scrollToBottom() {
var div = document.getElementById('message-container');
div.scrollTop = div.scrollHeight;
}
function insert_section(uid) {
var html = '<section class="user-section" id="section-'+ uid +'"></section>';
$(".message-container").append(html);
}
function insert_agent_html(content){
var date = dateFormat();
var html = ' <div class="message-agent">\n' +
' <div class="message-agent-time-sender message-time-sender">\n' +
' <span class="message-agent-time">' + date + '</span>\n' +
' <span class="">我</span>\n' +
' </div>\n' +
' <div class="message-agent-content message-content">\n' +
' <div>' + content + '</div>\n' +
' </div>\n' +
' </div>';
$('#section-'+currentUUID).append(html);
}
function insert_client_html(uid,content){
var date = dateFormat();
var html = '<div class="message-client">\n' +
' <div class="message-time-sender">\n' +
' <span class="message-client-time">' + date + '</span>\n' +
' <span class="">客户</span>\n' +
' </div>\n' +
' <div class="message-client-content message-content">\n' +
' <div>' + content + '</div>\n' +
' </div>\n' +
' </div>';
$('#section-'+uid).append(html);
}
function insert_user_html(id,name) {
var html = '<div class="user-info layui-row" id="' + id + '">\n' +
' <div class="layui-col-xs3 user-avatar">\n' +
' <img src="/images/server/mine_fill.png">\n' +
' </div>\n' +
' <div class="layui-col-xs9">' + name + '</div>\n' +
' </div>';
$('.chat-user').append(html);
}
function getUsers() {
$.get('/users',function (data) {
if(data.code == 200){
$('.chat-user').html('');
var data = data.data;
var count = 1;
data.forEach(function (uid) {
insert_user_html(uid,'客户' + count++);
//创建聊天section
insert_section(uid);
});
if(data.length > 0 && !currentUUID){
currentUUID = data[0];
}
$(".user-info").css("background","#ffffff");
$("#"+currentUUID).css("background","#f2f3f5");
$(".user-section").hide();
$("#section-"+currentUUID).show();
}
});
}
$(".btnMsgSend").click(function(){
var msg = $("#msg-send-textarea").val();
if(msg){
var msg_sender = {
"type":'private',
"uid":currentUUID,
"content":msg,
"from_uid":uuid
};
socket.emit('message', msg_sender);
insert_agent_html(msg);
scrollToBottom();
$("#msg-send-textarea").val('');
}
});
//连接服务器
socket.on('connect', function () {
console.log('连接成功...');
uuid = 'chat-kefu-admin';
socket.emit('login', uuid);
});
//后端推送来消息时
socket.on('message', function(msg){
insert_client_html(msg.uid,msg.content);
scrollToBottom();
});
//后端推送来消息时
socket.on('update-users', function(msg){
if(msg.type == 'offline'){
$("#"+msg.uid).remove();
uuids.remove(msg.uid);
}else if(msg.type == 'online'){
if(!currentUUID){
currentUUID = msg.uid;
}
if(uuids.indexOf(msg.uid) == -1){
uuids.push(msg.uid);
insert_user_html(msg.uid,msg.name);
//创建聊天section
insert_section(msg.uid);
}
}
});
//更新用户在线数
socket.on('update_online_count', function(msg){
$(".friend-head-right").html( (msg.online_count - 1) + '人' );
});
//切换用户
$(document).on('click','.user-info',function(){
var uid = $(this).attr("id");
currentUUID = uid;
$(".user-info").css("background","#ffffff");
$("#"+uid).css("background","#f2f3f5");
$(".user-section").hide();
$("#section-"+uid).show();
});
init();
getUsers();
});

View File

@ -1,9 +1,22 @@
var express = require('express');
var router = express.Router();
var redis = require('../utils/redis');
/* GET users listing. */
router.get('/', function(req, res, next) {
res.send('respond with a resource');
redis.get('user-uuids',function (err,uuids) {
if(err){
console.error(err);
return res.send({code:400,msg:'获取失败'});
}
if(uuids){
uuids =JSON.parse(uuids);
}else{
uuids = [];
}
return res.send({code:200,msg:'获取成功',data:uuids});
});
});
module.exports = router;

View File

@ -25,7 +25,7 @@
</header>
<!-- 这里是页面内容区 -->
<div class="content">
<div class="content-block msg-container">
<div class="content-block msg-container" id="msg-container">
</div>
@ -44,6 +44,7 @@
<script type='text/javascript' src='//g.alicdn.com/sj/lib/zepto/zepto.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/sm.min.js' charset='utf-8'></script>
<script type='text/javascript' src='//g.alicdn.com/msui/sm/0.6.2/js/sm-extend.min.js' charset='utf-8'></script>
<script src="/js/fingerprint.js"></script>
<script src="/socket.io/socket.io.js"></script>
<script type='text/javascript' src='js/common.js' charset='utf-8'></script>
<script type='text/javascript' src='js/client/client.js' charset='utf-8'></script>

View File

@ -1,6 +1,6 @@
<% include header.ejs %>
<style>
.friend{
.friend {
position: relative;
float: left;
width: 300px;
@ -8,13 +8,15 @@
background: #fbfcfe;
border-right: 1px solid #e9e9e9;
}
.chat{
.chat {
position: relative;
float: left;
width: calc(100% - 301px);
height: 100%;
}
.friend-head{
.friend-head {
display: block;
cursor: pointer;
height: 51px;
@ -26,32 +28,137 @@
border-top: 1px solid #e9e9e9;
border-bottom: 1px solid #e9e9e9;
}
.friend-head-right{
.friend-head-right {
float: right;
}
.message-agent,.message-client{
overflow: auto;
margin-bottom: 10px;
margin-top: 10px;
}
.message-container{
padding: 0 20px 0 20px;
overflow: auto;
}
.message-time-sender{
line-height: 1;
margin-bottom: 5px;
font-size: 12px;
color: #adb2bb;
}
.message-agent-time-sender{
text-align: right;
}
.message-content{
border-radius: 3px;
padding: 8px 12px;
font-size: 13px;
line-height: 22px;
word-wrap: break-word;
word-break: break-all;
border: 1px dashed transparent;
}
.message-agent-content{
position: relative;
float: right;
background-color: #f0f1f3;
color: #161e26;
}
.message-client-content{
position: relative;
float: left ;
background-color: #e7f4ff;
color: #161e26;
}
.message-sender{
position: absolute;
bottom: 0;
height: 150px;
border-top: 1px solid #e9e9e9;
width: 100%;
}
.sender-editor{
height: 100%;
}
.message-sender textarea{
resize: none;
border: none;
border-radius: 6px;
font-size: 15px;
color: #161e26;
line-height: 22px;
background-color: #fff;
height: calc(100% - 40px);
padding: 20px;
width: calc(100% - 40px);
}
.btnMsgSend{
z-index: 1;
position: absolute;
right: 15px;
bottom: 15px;
}
.user-info{
height: 60px;
background: #ffffff;
line-height: 60px;
font-size: 16px;
color: #767d85;
border-bottom: 1px solid #e9e9e9;
}
.chat-user{
overflow: auto;
}
.user-info img{
width: 50px;
}
.user-info .user-avatar{
text-align: center;
}
</style>
<div class="layui-fluid">
<div class="friend">
<div class="friend-head">
<span class="friend-head-right">0 / 2</span>
<span class="friend-head-right">0</span>
<span class="help-my-chat">我的对话</span>
</div>
<div class="chat-user">
</div>
</div>
<div class="chat">
1111
<div class="message-container" id="message-container">
</div>
<div class="message-sender">
<div class="sender-editor">
<textarea placeholder="请输入" id="msg-send-textarea"></textarea>
<div class="layui-btn layui-btn-normal btnMsgSend">发送</div>
</div>
</div>
</div>
</div>
<script src="/layui/layui.js"></script>
<script>
//一般直接写在一个js文件中
layui.use(['layer', 'form','jquery'], function(){
var layer = layui.layer
,form = layui.form
,$ = layui.jquery;
$(".admin-index").addClass("layui-this");
});
</script>
<script src="/socket.io/socket.io.js"></script>
<script type='text/javascript' src='js/common.js' charset='utf-8'></script>
<script src="/js/server/index.js"> </script>
<% include footer.ejs %>