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 += '
';
+ }
+
+ 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 += '
';
+ }
+ 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 += ' ';
+ }
+
+ 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 += ' ';
+ }
+
+ 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 @@
从左侧列表打开对话
-
-
+
+
+