diff --git a/config.js b/config.js index 3ceb278..27da586 100644 --- a/config.js +++ b/config.js @@ -1,5 +1,9 @@ const APP = { - "KEFUUUID":'chat-kefu-admin' + "KEFUUUID":'chat-kefu-admin', + "QINIU":{ + "accessKey":"your access key", + "secretKey":"your secret key" + } }; module.exports = APP; \ No newline at end of file diff --git a/io/io.js b/io/io.js index 3d6a9c2..234290b 100644 --- a/io/io.js +++ b/io/io.js @@ -193,7 +193,7 @@ function ioServer(io) { //监听客户端发送的信息,实现消息转发到各个其他客户端 socket.on('message',function(msg){ - msgModel.add(msg.from_uid,msg.uid,msg.content,function (err) { + msgModel.add(msg.from_uid,msg.uid,msg.content,msg.chat_type,msg.image,function (err) { if(err){ console.error(err); } @@ -201,7 +201,9 @@ function ioServer(io) { if(msg.type == msgType.messageType.public){ var mg = { "uid" : msg.from_uid , - "content": msg.content + "content": msg.content, + "chat_type" : msg.chat_type?msg.chat_type:'text', + "image":msg.image }; socket.broadcast.emit("message",mg); }else if(msg.type == msgType.messageType.private){ @@ -214,7 +216,9 @@ function ioServer(io) { //给指定的客户端发送消息 var mg = { "uid" : msg.from_uid, - "content": msg.content + "content": msg.content, + "chat_type" : msg.chat_type?msg.chat_type:'text', + "image":msg.image }; io.to(sid).emit('message',mg); } diff --git a/model/message.js b/model/message.js index 3f99fc0..f112adb 100644 --- a/model/message.js +++ b/model/message.js @@ -6,16 +6,20 @@ var MessageSchema = new Schema({ from_uid : { type:String ,index: true}, to_uid : { type:String ,index: true}, content : { type:String }, + chat_type : { type:String,default:'text'}, + image : { type:String,default:''}, time : { type:Date, default:Date.now } }); var MessageModel = mongoose.model("message", MessageSchema); -function add(from_uid,to_uid,content,callback) { +function add(from_uid,to_uid,content,chat_type,image,callback) { var info = { "from_uid" : from_uid, "to_uid" : to_uid, - "content" : content + "content" : content, + "chat_type" : chat_type, + "image" : image, }; var msgModel = new MessageModel(info); msgModel.save(function(err, res){ diff --git a/package.json b/package.json index 66d62d6..888cfdd 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "socket.io":"~2.0.4", "socket.io-client":"^2.0.4", "redis":"^2.8.0", - "mongoose":"^4.12.5" + "mongoose":"^4.12.5", + "qiniu":"^7.1.1" } } diff --git a/public/css/client.css b/public/css/client.css index 4c7904a..16c6afa 100644 --- a/public/css/client.css +++ b/public/css/client.css @@ -157,19 +157,21 @@ a { } .footer .input-holder .text-holder { + float: left; + width: calc(100% - 3rem); position: relative; padding-right: 50px; padding-left: 4px; overflow: hidden; zoom: 1; + height: 100%; } .footer .input-holder .text-holder #textarea { display: block; width: 100%; - height: 32px; - padding: 5px; - margin-top: 12px; + height: 100%; + padding: 15px; overflow-x: hidden; overflow-y: auto; resize: none; @@ -202,4 +204,32 @@ a { } .content-block { margin: 0; +} + +.emoji-image{ + float: left; + width: 3rem; + text-align: center; + margin-top: 0.85rem; +} + +.emoji-image .icon{ + width: 1.2rem; + height: 1.2rem; +} + +.msg-client-img{ + text-align: right; +} + +.msg-client-img img{ + width: 10rem; +} + +.msg-agent-img{ + text-align: left; +} + +.msg-agent-img img{ + width: 10rem; } \ No newline at end of file diff --git a/public/images/client/emoji.png b/public/images/client/emoji.png new file mode 100644 index 0000000..2d53437 Binary files /dev/null and b/public/images/client/emoji.png differ diff --git a/public/images/client/picture.png b/public/images/client/picture.png new file mode 100644 index 0000000..9c5adff Binary files /dev/null and b/public/images/client/picture.png differ diff --git a/public/js/client/client.js b/public/js/client/client.js index 28f03d9..6805211 100644 --- a/public/js/client/client.js +++ b/public/js/client/client.js @@ -5,42 +5,67 @@ $(function(){ }); var uuid = ''; - function insert_client_html(content,datetime){ + function insert_client_html(msg){ var time = dateFormat(); - if(datetime){ - time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(datetime)); + if(msg.datetime){ + time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(msg.datetime)); + } + if(!msg.chat_type){ + msg.chat_type = 'text'; } var tpl = '
'+ '
'+ - '
' + time + ' 我' + '
'+ - '
'+ + '
' + time + ' 我' + '
'; + + if(msg.chat_type == "text"){ + tpl += '
'+ ''+ - '
' + content + '
'+ + '
' + msg.content + '
'+ ''+ - '
'+ - '
'+ + '
'; + }else if(msg.chat_type == "image"){ + tpl += '
' + + ' ' + + ' photo'+ + ' ' + + '
'; + } + + tpl += '
'+ ''; $(".msg-container").append(tpl); } - function insert_agent_html(content,datetime){ + function insert_agent_html(msg){ var time = dateFormat(); - if(datetime){ - time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(datetime)); + if(msg.datetime){ + time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(msg.datetime)); + } + if(!msg.chat_type){ + msg.chat_type = 'text'; } var tpl = '
'+ '
'+ '
'+ ''+ '
'+ - '
' + time + ' 客服' + '
'+ - '
'+ - ''+ - ''+ - '
' + content + '
'+ - '
'+ - '
'+ + '
' + time + ' 客服' + '
'; + + if(msg.chat_type == "text"){ + tpl += '
'+ + ''+ + ''+ + '
' + msg.content + '
'+ '
'; + }else if(msg.chat_type == "image"){ + tpl += '
' + + ' ' + + ' photo'+ + ' ' + + '
'; + } + tpl += '
'+ + ''; $(".msg-container").append(tpl); } @@ -56,9 +81,9 @@ $(function(){ if(data.code == 200){ data.data.reverse().forEach(function (msg) { if(msg.from_uid == uid){ - insert_client_html(msg.content,msg.time); + insert_client_html(msg); }else{ - insert_agent_html(msg.content,msg.time); + insert_agent_html(msg); } scrollToBottom(); @@ -75,15 +100,76 @@ $(function(){ "type":'private', "uid":'chat-kefu-admin', "content":msg, - "from_uid":uuid + "from_uid":uuid, + "chat_type":'text' }; socket.emit('message', msg_sender); - insert_client_html(msg); + insert_client_html(msg_sender); scrollToBottom(); $("#textarea").val(''); } }); + $(".picture-upload").click(function () { + var uploader = Qiniu.uploader({ + runtimes: 'html5,flash,html4', // 上传模式,依次退化 + browse_button: 'pickfiles', // 上传选择的点选按钮,必需 + uptoken_url: '/uptoken', // Ajax请求uptoken的Url,强烈建议设置(服务端提供) + get_new_uptoken: false, // 设置上传文件的时候是否每次都重新获取新的uptoken + domain: 'http://kefuimg.chinameyer.com/', // bucket域名,下载资源时用到,必需 + container: 'btn-uploader', // 上传区域DOM ID,默认是browser_button的父元素 + max_file_size: '10mb', // 最大文件体积限制 + flash_swf_url: 'path/of/plupload/Moxie.swf', //引入flash,相对路径 + max_retries: 3, // 上传失败最大重试次数 + dragdrop: false, // 开启可拖曳上传 + drop_element: 'btn-uploader', // 拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传 + chunk_size: '4mb', // 分块上传时,每块的体积 + auto_start: true, // 选择文件后自动上传,若关闭需要自己绑定事件触发上传 + unique_names: true, + init: { + 'FilesAdded': function(up, files) { + plupload.each(files, function(file) { + // 文件添加进队列后,处理相关的事情 + }); + }, + 'BeforeUpload': function(up, file) { + // 每个文件上传前,处理相关的事情 + }, + 'UploadProgress': function(up, file) { + // 每个文件上传时,处理相关的事情 + }, + 'FileUploaded': function(up, file, info) { + // 查看简单反馈 + var domain = up.getOption('domain'); + var res = JSON.parse(info); + var sourceLink = domain +"/"+ res.key; + + var msg_sender = { + "type":'private', + "uid":'chat-kefu-admin', + "content":'图片消息', + "from_uid":uuid, + "chat_type":'image', + "image":sourceLink + }; + socket.emit('message', msg_sender); + insert_client_html(msg_sender); + scrollToBottom(); + + + }, + 'Error': function(up, err, errTip) { + //上传出错时,处理相关的事情 + $.toast("上传失败"); + }, + 'UploadComplete': function() { + //队列文件处理完毕后,处理相关的事情 + } + } + }); + + }); + //连接服务器 socket.on('connect', function () { //uuid = 'chat'+ guid(); @@ -106,7 +192,7 @@ $(function(){ // content 消息 // */ socket.on('message', function(msg){ - insert_agent_html(msg.content); + insert_agent_html(msg); scrollToBottom(); }); }); \ No newline at end of file diff --git a/public/js/server/index.js b/public/js/server/index.js index a2d11c2..b94a749 100644 --- a/public/js/server/index.js +++ b/public/js/server/index.js @@ -17,11 +17,11 @@ layui.use(['layer', 'form', 'jquery'], function () { function init() { $(".admin-index").addClass("layui-this"); - var height = document.body.clientHeight - 262; + var height = document.body.clientHeight - 292; $(".message-container").css("height", height); window.onresize = function(){ - var height = document.body.clientHeight - 262; + var height = document.body.clientHeight - 292; $(".message-container").css("height", height); }; @@ -50,39 +50,64 @@ layui.use(['layer', 'form', 'jquery'], function () { get_message(uid); } - function insert_agent_html(uid,content,datetime){ + function insert_agent_html(msg){ var time = dateFormat(); - if(datetime){ - time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(datetime)); + if(msg.datetime){ + time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(msg.datetime)); + } + if(!msg.chat_type){ + msg.chat_type = 'text'; } var html = '
\n' + '
\n' + ' ' + time + '\n' + ' \n' + - '
\n' + - '
\n' + - '
' + content + '
\n' + - '
\n' + - '
'; - $('#section-'+uid).append(html); + ' \n'; + + if(msg.chat_type == "text"){ + html += '
\n' + + '
' + msg.content + '
\n' + + '
\n' ; + }else if(msg.chat_type == "image"){ + html += '
' + + ' ' + + ' photo'+ + ' ' + + '
'; + } + + html += ' '; + $('#section-'+msg.uid).append(html); } - function insert_client_html(uid,content,datetime){ + function insert_client_html(msg){ var time = dateFormat(); - if(datetime){ - time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(datetime)); + if(msg.datetime){ + time = dateFormat("yyyy-MM-dd hh:mm:ss",new Date(msg.datetime)); + } + if(!msg.chat_type){ + msg.chat_type = 'text'; } var html = '
\n' + '
\n' + ' ' + time + '\n' + ' 客户\n' + - '
\n' + - '
\n' + - '
' + content + '
\n' + - '
\n' + - '
'; - $('#section-'+uid).append(html); + ' \n' ; + if(msg.chat_type == "text"){ + html += '
\n' + + '
' + msg.content + '
\n' + + '
\n' ; + }else if(msg.chat_type == "image"){ + html += '
' + + ' ' + + ' photo'+ + ' ' + + '
'; + } + + html += ' '; + $('#section-'+msg.uid).append(html); } function insert_user_html(id,name) { @@ -133,10 +158,11 @@ layui.use(['layer', 'form', 'jquery'], function () { "type":'private', "uid":currentUUID, "content":msg, - "from_uid":uuid + "from_uid":uuid, + "chat_type":'text' }; socket.emit('message', msg_sender); - insert_agent_html(currentUUID,msg); + insert_agent_html(msg_sender); scrollToBottom(); $("#msg-send-textarea").val(''); } @@ -176,9 +202,11 @@ layui.use(['layer', 'form', 'jquery'], function () { if(data.code == 200){ data.data.reverse().forEach(function (msg) { if(msg.from_uid == uid){ - insert_client_html(msg.from_uid,msg.content,msg.time); + msg.uid = msg.from_uid; + insert_client_html(msg); }else{ - insert_agent_html(msg.to_uid,msg.content,msg.time); + msg.uid = msg.to_uid; + insert_agent_html(msg); } scrollToBottom(); @@ -191,6 +219,64 @@ layui.use(['layer', 'form', 'jquery'], function () { msg_send(); }); + $(".picture-upload").click(function () { + var uploader = Qiniu.uploader({ + runtimes: 'html5,flash,html4', // 上传模式,依次退化 + browse_button: 'pickfiles', // 上传选择的点选按钮,必需 + uptoken_url: '/uptoken', // Ajax请求uptoken的Url,强烈建议设置(服务端提供) + get_new_uptoken: false, // 设置上传文件的时候是否每次都重新获取新的uptoken + domain: 'http://kefuimg.chinameyer.com/', // bucket域名,下载资源时用到,必需 + container: 'btn-uploader', // 上传区域DOM ID,默认是browser_button的父元素 + max_file_size: '10mb', // 最大文件体积限制 + flash_swf_url: 'path/of/plupload/Moxie.swf', //引入flash,相对路径 + max_retries: 3, // 上传失败最大重试次数 + dragdrop: false, // 开启可拖曳上传 + drop_element: 'btn-uploader', // 拖曳上传区域元素的ID,拖曳文件或文件夹后可触发上传 + chunk_size: '4mb', // 分块上传时,每块的体积 + auto_start: true, // 选择文件后自动上传,若关闭需要自己绑定事件触发上传 + unique_names: true, + init: { + 'FilesAdded': function(up, files) { + plupload.each(files, function(file) { + // 文件添加进队列后,处理相关的事情 + }); + }, + 'BeforeUpload': function(up, file) { + // 每个文件上传前,处理相关的事情 + }, + 'UploadProgress': function(up, file) { + // 每个文件上传时,处理相关的事情 + }, + 'FileUploaded': function(up, file, info) { + // 查看简单反馈 + var domain = up.getOption('domain'); + var res = JSON.parse(info); + var sourceLink = domain +"/"+ res.key; + + var msg_sender = { + "type":'private', + "uid":currentUUID, + "content":'图片消息', + "from_uid":uuid, + "chat_type":'image', + "image":sourceLink + }; + socket.emit('message', msg_sender); + insert_agent_html(msg_sender); + scrollToBottom(); + }, + 'Error': function(up, err, errTip) { + //上传出错时,处理相关的事情 + $.toast("上传失败"); + }, + 'UploadComplete': function() { + //队列文件处理完毕后,处理相关的事情 + } + } + }); + + }); + //连接服务器 socket.on('connect', function () { console.log('连接成功...'); @@ -205,7 +291,7 @@ layui.use(['layer', 'form', 'jquery'], function () { //后端推送来消息时 socket.on('message', function(msg){ - insert_client_html(msg.uid,msg.content); + insert_client_html(msg); scrollToBottom(); msg_notification(msg); }); @@ -230,12 +316,12 @@ layui.use(['layer', 'form', 'jquery'], function () { var index = uuids.indexOf(msg.uid); if( index == -1){ uuids.push(msg.uid); - insert_user_html(msg.uid,msg.name + '#'+ (uuids.length + 1)); + insert_user_html(msg); //创建聊天section insert_section(msg.uid); }else{ if($(".chat-user").find("#"+msg.uid).length == 0){ - insert_user_html(msg.uid,msg.name + '#'+ (uuids.length + 1)); + insert_user_html(msg); //创建聊天section insert_section(msg.uid); } diff --git a/routes/index.js b/routes/index.js index 5b8ea83..ccef424 100644 --- a/routes/index.js +++ b/routes/index.js @@ -1,5 +1,7 @@ var express = require('express'); var router = express.Router(); +var AppConfig = require('../config'); +var qiniu = require('qiniu'); router.get('/', function(req, res, next) { res.render('./server/index'); @@ -21,4 +23,15 @@ router.get('/admin/setup', function(req, res, next) { res.render('./server/setup'); }); +router.get('/uptoken', function(req, res, next) { + var mac = new qiniu.auth.digest.Mac(AppConfig.QINIU.accessKey, AppConfig.QINIU.secretKey); + var options = { + scope: 'kefu', + expires: 7200 + }; + var putPolicy = new qiniu.rs.PutPolicy(options); + var uploadToken=putPolicy.uploadToken(mac); + res.send({"uptoken":uploadToken}); +}); + module.exports = router; diff --git a/views/client/index.ejs b/views/client/index.ejs index 95ac746..5c8bf04 100644 --- a/views/client/index.ejs +++ b/views/client/index.ejs @@ -1,53 +1,60 @@ - - - - 客服系统 - - - - + + + 客服系统 + + + + - - - + + + -
+
- -
- -

客服系统

-
- -
-
+ +
+ +

客服系统

+
+ +
+
- -
- -
+
+
+
+ + +
+ +
+ + 发送 +
+
+
+
-
- - - - - - - - +
+ + + + + + + + + + + + diff --git a/views/server/index.ejs b/views/server/index.ejs index 817bf58..94ec739 100644 --- a/views/server/index.ejs +++ b/views/server/index.ejs @@ -81,14 +81,14 @@ .message-sender{ position: absolute; bottom: 0; - height: 150px; + height: 180px; border-top: 1px solid #e9e9e9; width: 100%; } .sender-editor{ - height: 100%; + height: calc(100% - 40px); } .message-sender textarea{ @@ -152,6 +152,28 @@ display: none; } + .msg-client-img{ + text-align: left; + } + + .msg-client-img img{ + width: 10rem; + } + + .msg-agent-img{ + text-align: right; + } + + .msg-agent-img img{ + width: 10rem; + } + + .message-emoji-picture{ + height: 40px; + line-height: 40px; + padding-left: 20px; + } +
@@ -169,9 +191,12 @@
从左侧列表打开对话
-
+
+ + +
发送
@@ -179,7 +204,9 @@
- + + +