Showing 100 changed files with 1087 additions and 3 deletions

Too many changes to show.

To preserve performance only 100 of 100+ files are displayed.

/**
* file description
*
* @author: liqi <qi.li@yoho.cn>
* @date: 2016/11/4
*/
'use strict';
/**
* 在线客服用户
*/
const index = (req, res) => {
res.render('service/client', {layout: false});
};
module.exports = {
index
};
... ...
/**
* sub app service
* @author: liqi <qi.li@yoho.cn>
* @date: 2016/11/04
*/
var express = require('express'),
path = require('path');
var app = express();
// set view engine
var doraemon = path.join(__dirname, '../../doraemon/views'); // parent view root
app.on('mount', function(parent) {
delete parent.locals.settings; // 不继承父 App 的设置
Object.assign(app.locals, parent.locals);
});
app.use(global.yoho.hbs({
extname: '.hbs',
defaultLayout: 'service',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial')],
views: path.join(__dirname, 'views/action'),
helpers: global.yoho.helpers
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/*
* @Author: Targaryen
* @Date: 2016-06-01 14:37:03
* @Last Modified by: Targaryen
* @Last Modified time: 2016-06-07 15:40:29
*/
'use strict';
const serviceApi = global.yoho.ServiceAPI;
const api = global.yoho.API;
const _ = require('lodash');
const config = global.yoho.config;
const yhChannel = {
boys: {
channel: '1'
},
girls: {
channel: '2'
},
kids: {
channel: '3'
},
lifestyle: {
channel: '4'
},
otltIdxDflt: {
channel: null
}
};
/**
* 获取奥莱活动列表接口
* @param {[int]} id 活动id 为空表示查询全部活动
* @param {[int ]} platform 活动平台 1--WEB,2--APP,3--WAP,4--IPAD
* @param {[int]} size 查询数量,默认查询全部
* @param {[int]} channel 频道: 1 || 2 || 3 || 4
* @param {[int]} type
* @return {[type]} 0 活动列表,1 限时嗨购 2 即将结束 3.即将上线
*/
exports.getOutletsActivityOrigin = (params) => {
let tempChannel = params.channel || 'boys';
return api.get('', {
method: 'app.outlets.activityGet',
id: params.id || null,
platform: params.platform || 1,
size: params.size || 0,
yh_channel: yhChannel[tempChannel].channel,
type: params.type || 0
}, config.apiCache);
};
/**
* 获取奥莱频道资源位数据
* @param {[object]} params
* @return {[type]}
*/
exports.getChannelResouceData = (params) => {
return serviceApi.get('operations/api/v5/resource/home', params, config.apiCache);
};
/**
* 获取奥莱潮品速递商品数据
* @param {[type]} params [description]
* @return {[type]} [description]
*/
exports.getOutletsTrendData = (params) => {
let tempChannel = params.channel || 'boys';
return api.get('', {
method: 'app.search.trend',
yh_channel: yhChannel[tempChannel].channel,
order: params.order || 's_s_desc,s_n_desc',
gender: params.gender || '1,3',
stocknumber: 1, // 过滤出库存 > 1的商品
limit: params.limit || 5,
outlets: params.outlets || 1 // 默认取奥莱商品
}, config.apiCache);
};
/**
* 获取奥莱商品列表 promise 对象
* @return {[type]} [description]
*/
exports.getOutletsGoodsList = (params) => {
// 频道
let tempChannel = params.channel || 'boys';
// 接口可接收的参数
let apiParams = ['outlets', 'page', 'limit', 'order', 'productSize', 'yh_channel', 'query',
'p_d', 'gender', 'msort', 'misort', 'sort', 'brand', 'color', 'size', 'saleType',
'breakSize', 'breakSort', 'productPool', 'price', 'method'];
// 初始化必填的接口参数
let tempParams = {
method: 'app.search.li',
outlets: 1,
page: params.page || 1,
limit: params.limit || 60,
order: params.order || 's_t_desc',
productSize: '384x511',
yh_channel: yhChannel[tempChannel].channel
};
_.forEach(apiParams, (paramsName) => {
if (params[paramsName]) {
tempParams[paramsName] = params[paramsName];
}
});
return api.get('', tempParams, config.apiCache);
};
... ...
/**
*
* @Author: zhoushaofeng
* @Date: 2016-06-02 15:50:47
* @Last Modified time: 2016-06-08 19:31:52
*/
'use strict';
const utils = '../../../utils';
const helpers = global.yoho.helpers;
const _ = require('lodash');
const productProcess = require(`${utils}/product-process`);
const url = require('url');
const queryString = require('querystring');
/**
* 热门分类
*/
const hotCategory = (data) => {
return {
hotCategory: {
name: data.name,
list: data.menuNav.list,
blocks: data.menuNav.blocks,
brands: data.imgs.slice(0, 1),
types: data.imgs.slice(1, 7)
}
};
};
/**
* 折扣文本切割
*/
const discountSplit = (text) => {
let endNum = 0,
i;
text = text || '';
for (i = 0; i < text.length; i++) {
if (/^([0-9]|\%)*$/.test(text[i])) {
endNum = i + 1;
}
}
return {
discount: text.slice(0, endNum),
discountText: text.slice(endNum, text.length)
};
};
/**
* 处理奥莱首页和频道底部分类
* @param {[type]} origin [description]
* @return {[type]} [description]
*/
const handleOutletsGoodsMenuData = (origin, params) => {
let dest = {
title: '最新折扣',
more: '/product/outlets/list',
menuList: [],
msort: [],
misort: []
};
_.forEach(origin, subValue => {
let oldParam = _.cloneDeep(params);
let goodsmenu = {};
let urlSuffix = {};
goodsmenu.name = subValue.categoryName;
urlSuffix = queryString.parse(url.parse(subValue.url).query);
if (!_.isEmpty(urlSuffix.msort) && urlSuffix.msort === oldParam.msort && urlSuffix.misort === oldParam.misort) {
goodsmenu.cur = true;
}
delete oldParam.msort;
delete oldParam.misort;
if (urlSuffix.msort) {
Object.assign(oldParam, {msort: urlSuffix.msort});
dest.msort.push(urlSuffix.msort);
}
if (urlSuffix.misort) {
Object.assign(oldParam, {misort: urlSuffix.misort});
dest.misort.push(urlSuffix.misort);
}
goodsmenu.href = '?' + queryString.stringify(oldParam) + '#otspool';
dest.menuList.push(goodsmenu);
});
return dest;
};
/**
* 分类导航
*/
const categoryNavigation = (data, params) => {
let goodsBoard = {};
goodsBoard.goodsMenu = handleOutletsGoodsMenuData(data, params);
return {goodsBoard: goodsBoard};
};
/**
* 处理楼层数据
* @param {[array]} list
* @return {[array]}
*/
exports.processFloor = (list, params) => {
const formatData = {};
let floorData;
list = list || [];
// 格式化数据
_.forEach(list, (floor) => {
switch (floor.template_name) {
case 'NL2R':
floorData = {topBanner: floor.data};
break;
case 'hotCategory':
floorData = hotCategory(floor.data);
break;
case 'categoryNavigation':
floorData = categoryNavigation(floor.data, params);
break;
default:
floorData = floor.data;
break;
}
Object.assign(formatData, floorData);
});
return formatData;
};
/**
* 处理奥莱资源位数据
* @param {[type]} origin [description]
* @return {[type]} [description]
*/
exports.handleOutletsBannersData = (origin, params) => {
var dest = {};
dest.limitedBuy = {};
dest.limitedBuy.extra = {};
_.forEach(origin.list, value => {
// 处理焦点图数据
if (value.template_name === 'focus') {
dest.mainBanner = {
list: value.data
};
}
// 处理三张小图数据
if (value.template_name === 'threePicture') {
dest.column = value.data;
}
// 处理右侧一张图片
if (value.template_name === 'single_image') {
dest.limitedBuy.extra.sourceImg = {
href: value.data[0].url,
img: value.data[0].src
};
}
// 处理热销推荐数据
if (value.template_name === 'recommendCategory') {
dest.limitedBuy.extra.hotType = {
title: _.isEmpty(value.data.title) ? '热销推荐' : value.data.title,
classify: []
};
_.forEach(value.data.categoryList, subValue => {
let category = {};
category.href = subValue.url + '&bannerImage=' + subValue.bannerImage;
category.name = subValue.navigateNamePC;
dest.limitedBuy.extra.hotType.classify.push(category);
});
}
// 处理品类导航数据
if (value.template_name === 'categoryNavigation') {
dest.goodsBoard = {
goodsMenu: handleOutletsGoodsMenuData(value.data, params)
};
}
});
return dest;
};
/**
* 处理奥莱活动数据
* @param {[type]} origin [description]
* @param {[type]} params [description]
* @return {[type]} [description]
*/
exports.handleOutletsActivityData = (origin, name) => {
let dest = {
name: name || '限时嗨购',
topic: []
};
// 处理奥莱活动列表数据
_.forEach(origin, value => {
let activity = {
href: helpers.urlFormat('/product/outlets/special/detail', {id: value.id}),
img: value.webCoverUrl,
logo: value.logoUrl,
title: value.title,
limit: parseInt(value.endLeftTime, 10) * 1000
};
if (value.promotionName) {
Object.assign(activity, discountSplit(value.promotionName));
}
dest.topic.push(activity);
});
return dest;
};
/**
* 处理奥莱活动专题数据
* @param {[type]} origin [description]
* @return {[type]} [description]
*/
exports.handleOutletsSpecilData = (origin) => {
let dest = {};
dest.mainBanner = {
src: helpers.image(origin.webUrl, 1920, 360)
};
dest.specialHead = {
logo: origin.logoUrl,
special: origin.title,
title: '全部商品',
limit: origin.endLeftTime * 1000
};
if (origin.promotionName) {
Object.assign(dest.specialHead, discountSplit(origin.promotionName));
}
return dest;
};
/**
* 处理潮品速递商品数据
* @param {[type]} origin [description]
* @return {[type]} [description]
*/
exports.handleOutletstrendGoodData = (origin) => {
let dest = {};
dest.title = '潮品推荐';
dest.goods = productProcess.processProductList(origin.product_list);
_.forEach(dest.goods, (value, key) => {
dest.goods[key].discount = (value.salesPrice / value.marketPrice * 10).toFixed(1) * 1;
});
return dest;
};
/**
* 处理即将上线品牌数据
* @param origin
* @returns {{}}
*/
exports.handleComeSoonData = (origin) => {
let dest = {
title: '上线预告'
};
dest.brands = [];
_.forEach(origin, value => {
dest.brands.push({
logo: value.logoUrl
});
});
return dest;
};
... ...
/*
* @Author: Targaryen
* @Date: 2016-05-30 16:20:03
* @Last Modified by: Targaryen
* @Last Modified time: 2016-06-08 19:56:39
*/
'use strict';
const utils = '../../../utils';
const api = global.yoho.API;
const list = require('./list');
const outletsProcess = require('./outlets-handler');
const outletsApi = require('./outlets-api');
const productProcess = require(`${utils}/product-process`);
const headerModel = require('../../../doraemon/models/header');
const _ = require('lodash');
const publicHandler = require('./public-handler');
const log = global.yoho.logger;
// 奥莱频道资源码
const channelCode = {
index: 'e0565dad65fb8da1f39bc1ac83fc8346',
boys: '2af513637dc5feeec7f6f8b52989b24b',
girls: 'd1b56b56c3a12dc1f2f83958680a0911',
kids: '0fd7ad594940f9ec5a03697317cf6521',
lifestyle: 'b3d3f55a26f130ac2b516b9fb3823711'
};
/**
* 获取奥莱首页数据
* @param {[type]} origin [description]
* @return {[type]} [description]
*/
exports.getOutletsIndexData = (params, channel) => {
return api.all([
headerModel.requestHeaderData('outlets'),
outletsApi.getChannelResouceData({content_code: channelCode.index}),
outletsApi.getOutletsActivityOrigin({type: '1', channel: channel}), // 获取限时活动列表
outletsApi.getOutletsActivityOrigin({type: '2', channel: channel}), // 获取即将结束列表
outletsApi.getOutletsTrendData({limit: '30', channel: channel}), // 获取潮流速递商品数据
outletsApi.getOutletsActivityOrigin({type: '3', channel: channel}) // tar add 16171552 即将上线数据
]).then(result => {
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true,
headType: 'outlets'
})
};
// 处理资源位数据
if (result[1].code === 200 && !_.isEmpty(result[1].data)) {
finalResult = Object.assign(finalResult,
outletsProcess.handleOutletsBannersData(result[1].data, params));
}
// 处理限时嗨购列表数据
if (result[2].code === 200 && !_.isEmpty(result[2].data)) {
finalResult.limitedBuy = Object.assign(finalResult.limitedBuy,
outletsProcess.handleOutletsActivityData(result[2].data, '限时嗨购'));
}
// 处理即将结束列表数据
if (result[3].code === 200 && !_.isEmpty(result[3].data)) {
finalResult.nearOver = outletsProcess.handleOutletsActivityData(result[3].data, '即将结束');
}
// 处理潮品推荐数据
if (result[4].code === 200 && !_.isEmpty(result[4].data)) {
finalResult.limitedBuy.extra.trendGood = outletsProcess.handleOutletstrendGoodData(result[4].data);
}
// 处理即将上架品牌数据
if (result[5].code === 200 && !_.isEmpty(result[5].data)) {
finalResult.limitedBuy.extra.comeSoon = outletsProcess.handleComeSoonData(result[5].data);
}
// 如果后台没配分类,不显示商品列表
if (!finalResult.goodsBoard.goodsMenu.msort && !finalResult.goodsBoard.goodsMenu.msort) {
log.info('no config index sort');
return finalResult;
}
// 获取底部商品数据
if (!params.msort && !params.misort) {
Object.assign(
params,
{msort: _.uniq(finalResult.goodsBoard.goodsMenu.msort).join()}
);
}
return api.all([
outletsApi.getOutletsGoodsList(Object.assign(
params,
{limit: params.limit || '100', channel: channel, method: 'app.search.category'}
))
]).then(data => {
// 处理底部商品数据
if (data[0].code === 200 && !_.isEmpty(data[0].data)) {
Object.assign(finalResult.goodsBoard, {
sort: Object.assign(publicHandler.handleSaleOptsData(params, data[0].data.total), {newPage: true}),
list: productProcess.processProductList(data[0].data.product_list)
});
// 添加锚点
if (finalResult.goodsBoard.sort.sortType) {
_.forEach(finalResult.goodsBoard.sort.sortType, (value, key) => {
finalResult.goodsBoard.sort.sortType[key].href += '#otspool';
});
if (finalResult.goodsBoard.sort.preHref) {
finalResult.goodsBoard.sort.preHref += '#otspool';
}
if (finalResult.goodsBoard.sort.nextHref) {
finalResult.goodsBoard.sort.nextHref += '#otspool';
}
}
finalResult.goodsBoard.footPager = publicHandler.handlePagerData(data[0].data.total, params);
}
return finalResult;
});
});
};
/**
* 获取奥莱频道页面数据
* @param {[object]} gender
* @return {[type]}
*/
exports.getOutletsChannelData = (params, channel) => {
// 频道资源位不存在
if (!channelCode[channel]) {
return Promise.reject(`outlets channel-(${channel}) resource not found`);
}
let channelData = {};
let apiArr = [
// 获取头部数据
headerModel.requestHeaderData('outlets'),
// 获取频道资源位
outletsApi.getChannelResouceData({content_code: channelCode[channel]}),
// 获取奥莱活动
outletsApi.getOutletsActivityOrigin({
platform: 1,
size: 0,
channel: channel,
type: 1
})
];
return api.all(apiArr).then(result => {
// 返回页面的数据
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true,
headType: 'outlets'
})
};
Object.assign(channelData, finalResult);
// 资源楼层
if (result[1].code === 200) {
Object.assign(channelData, outletsProcess.processFloor(result[1].data.list, params));
}
// 限时嗨购
if (result[2].code === 200 && !_.isEmpty(result[2].data)) {
channelData.nearOver = outletsProcess.handleOutletsActivityData(result[2].data, '限时嗨购');
}
// 如果后台没配分类,不显示商品列表
if (!channelData.goodsBoard || !channelData.goodsBoard.goodsMenu ||
(!channelData.goodsBoard.goodsMenu.msort && !channelData.goodsBoard.goodsMenu.msort)) {
log.info('no config ' + channel + 'channel sort');
return channelData;
}
// 获取底部商品数据
if (!params.msort || !params.misort) {
Object.assign(
params,
{msort: _.uniq(channelData.goodsBoard.goodsMenu.msort).join()}
);
}
return api.all([
outletsApi.getOutletsGoodsList(Object.assign(
params,
{limit: params.limit || '100', channel: channel, method: 'app.search.category'}
))
]).then(data => {
if (data[0].code === 200) {
if (!channelData.goodsBoard) {
channelData.goodsBoard = {};
}
channelData.goodsBoard.sort = publicHandler.handleSaleOptsData(params, data[0].data.total);
channelData.goodsBoard.sort.newPage = true;
channelData.goodsBoard.list = productProcess.processProductList(data[0].data.product_list);
channelData.goodsBoard.pager = publicHandler.handlePagerData(data[0].data.total, params);
// 添加锚点
if (channelData.goodsBoard.sort.sortType) {
_.forEach(channelData.goodsBoard.sort.sortType, (value, key) => {
channelData.goodsBoard.sort.sortType[key].href += '#otspool';
});
if (channelData.goodsBoard.sort.preHref) {
channelData.goodsBoard.sort.preHref += '#otspool';
}
if (channelData.goodsBoard.sort.nextHref) {
channelData.goodsBoard.sort.nextHref += '#otspool';
}
}
}
return channelData;
});
});
};
/**
* 获取奥莱活动页面数据
* @param {[object]}
* @return {[type]}
*/
exports.getOutletsSpecialData = (params, channel) => {
return api.all([
headerModel.requestHeaderData('outlets'),
outletsApi.getOutletsActivityOrigin({ // 获取活动信息
id: params.id
})
]).then(result => {
let specialData = {};
Object.assign(specialData, result[0]);
specialData.headerData.headType = 'outlets';
// 活动信息获取异常
if (result[1].code !== 200) {
return Promise.reject(`outlets special info not found-(ID:${params.id})`);
}
Object.assign(specialData,
outletsProcess.handleOutletsSpecilData(result[1].data[0]));
return api.all([
list.getListData(Object.assign({
productPool: result[1].data[0].productPoolId,
saleType: 4
}, params), channel)
]).then(data => {
specialData.specialHead.count = data[0].totalCount;
Object.assign(specialData, data[0]);
return specialData;
});
});
};
/**
* 获取奥莱分类页面数据
* @param {[type]} params [description]
* @param {[type]} channel [description]
* @return {[type]} [description]
*/
exports.getOutletsCategoryData = (params, channel) => {
return api.all([
headerModel.requestHeaderData('outlets')
]).then(result => {
let finalResult = {
headerData: Object.assign(result[0].headerData, {
header: true,
headType: 'outlets'
})
};
return api.all([
list.getListData(params, channel)
]).then(listResult => {
finalResult.saleList = listResult[0];
if (!_.isEmpty(params.bannerImage)) {
finalResult.saleList.topBanner = {
list: [
{
img: params.bannerImage
}
]
};
}
return finalResult;
});
});
};
... ...
/**
* router of sub app service
* @author: liqi <qi.li@yoho.cn>
* @date: 2016/11/04
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
// 在线客服controller
const client = require(`${cRoot}/client`);
// 在线客服
router.get('/client', client.index); // 在线客服
module.exports = router;
... ...
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{{title}}</title>
<meta name="keywords" content="{{keywords}}">
<meta name="description" content="{{description}}">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<link rel="stylesheet" href="//localhost:5002/css/service/lib/bootstrap.css"/>
<link rel="stylesheet" href="//localhost:5002/css/client.css">
<script src="//localhost:5002/js/service/lib/jquery.min.js"></script>
<script src="//localhost:5002/js/service/lib/jquery.ui.widget.js"></script>
<script src="//localhost:5002/js/service/lib/jquery.iframe-transport.js"></script>
<script src="//localhost:5002/js/service/lib/jquery.fileupload.js"></script>
<script src="//localhost:5002/js/service/lib/bootstrap.min.js"></script>
</head>
<body>
<div class="online-user">
<div class="header">
<div class="chat"></div>
<div class="logo"></div>
<div class="page-desc">有货在线客服</div>
</div>
<div class="chat-panel">
<div class="panel-left">
<div class="qr-code">
<span class="code"></span>
<div class="scan">
<span class="icon"></span>
<span class="tip">扫描二维码<br>现在手机客户端</span>
</div>
</div>
<img src="/img/service/adv.png" class="adv">
</div>
<div class="panel-main">
<div class="main-body">
<div class="msg-list">
<div class="list-item">
<span class="his-msg">近一周没有历史消息</span>
</div>
<div class="list-item guest">
<img src="/img/service/agent-avatar.png" class="avatar">
<div class="item-detail">
<span class="time">客服助手 10:15:37</span>
<div class="msg-bubble">
<span>我的快递还没到,神马情况啊!我的快递还没到,神马情况啊!</span>
</div>
</div>
</div>
<div class="list-item host">
<img src="/img/service/cus-avatar.png" class="avatar">
<div class="item-detail">
<span class="time">18823562175 10:14:37</span>
<div class="msg-bubble">
<span>单号:8027398592<br>
金额:¥859<br>
下单时间:2016-09-20 10:01:01<br>
状态:待发货
</span>
</div>
</div>
</div>
<div class="list-item">
<p class="push-tip">
<span class="tip">用户已经退出会话</span>
</p>
</div>
<div class="list-item">
<p class="push-tip">
<span class="tip">用户已经退出会话</span>
</p>
</div>
<div class="list-item">
<p class="push-tip">
<span class="tip">您向用户推送了一个评价申请</span>
</p>
</div>
</div>
</div>
<div class="main-footer">
<div class="msg-edit">
<div class="edit-left">
<div class="util">
<span class="icon"></span>
<span class="icon"><label for="sendImg"></label></span>
<span class="icon"></span>
</div>
<textarea class="text msg-area" placeholder="请描述您遇到的问题" spellcheck="false"></textarea>
</div>
<span class="send">发送</span>
<input id="sendImg" type="file" name="files[]" accept="image/bmp,image/jpeg,image/png" multiple>
</div>
</div>
</div>
<div class="panel-right">
<div class="right-head">
<span class="tab order active">订单信息</span>
<span class="tab qa">常见问题</span>
</div>
<div class="right-body order">
<div class="tab-content">
<div class="recent">
<span class="max">最近10笔订单</span>
<a class="more" href="/home/orders">MORE</a>
</div>
<div class="order-list">
<div class="item">
<div class="item-head">
<span class="time">2016-09-20 10:01:01</span>
<span class="order-no">订单号:<span class="red">8027398592</span></span>
</div>
<div class="item-body">
<img src="/img/service/product/prd-001.png" class="prd-img">
<div class="prd-desc">
<p class="prd-nm">Adidas Originals CLI CRYS 2.0 HD 男 连帽套头卫衣</p>
<p class="prd-other">
<span class="price">¥859</span>
<span class="quantity">数量:1</span>
</p>
</div>
</div>
<div class="item-footer">
<p class="order-amount">订单金额:¥859</p>
<div class="order-other">
<span class="state">订单状态:待发货</span>
<span class="btn send-order">发送订单</span>
</div>
</div>
</div>
<div class="list-tip">
<span class="tip">暂无订单</span>
</div>
</div>
</div>
</div>
<div class="right-body qa">
<div class="tab-content">
<div class="qa-list">
<div class="item">
<div class="q">请问有货退换货包运费吗?</div>
<div class="a">您好,非质量问题换货,你只需承担寄回商品的运
费哦,我司寄给您新的换货商品,我司承担运费。</div>
</div>
<div class="item">
<div class="q">请问有货退换货包运费吗?</div>
<div class="a">您好,非质量问题换货,你只需承担寄回商品的运
费哦,我司寄给您新的换货商品,我司承担运费。</div>
</div>
</div>
</div>
</div>
<div class="right-footer qa">
<p class="more-q">没找到想问的问题?点此查看<a class="red" href="/help-center">更多问题~</a></p>
<div class="chat-icon"></div>
</div>
</div>
</div>
</div>
<!--留言Modal-->
<div class="modal fade leave-msg" id="leaveMsg" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="cus-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<span class="title">留言</span>
</div>
<div class="cus-body">
<textarea maxlength="120" placeholder="请输入留言信息" spellcheck="false"></textarea>
</div>
<div class="cus-footer">
<button class="btn dark">提交</button>
<button class="btn light" data-dismiss="modal">取消</button>
</div>
</div>
</div>
</div>
<!--服务评价Modal-->
<div class="modal fade make-eval" id="makeEvaluation" tabindex="-1" role="dialog" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="cus-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<span class="title">请对此次服务进行评价</span>
</div>
<div class="cus-body">
<div class="stars">
<span class="star positive"></span>
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
<span class="star"></span>
<p class="star-text">一般</p>
</div>
<div class="discontent">
<div class="dis-row">
<span class="type">问题没有得到解决</span>
<span class="type">对客服态度不满意</span>
</div>
<div class="dis-row">
<span class="type">客服对业务不熟悉</span>
<span class="type chosen">话语不明确</span>
</div>
</div>
<textarea maxlength="50" placeholder="请您输入不满意的其他原因" spellcheck="false"></textarea>
</div>
<div class="cus-footer">
<button class="btn dark" data-dismiss="modal">提交</button>
</div>
</div>
</div>
</div>
<!-- 表情弹出层 -->
<div class="emoji-component" >
<div class="emo-row">
<div class="emo-item">:bowtie:</div>
<div class="emo-item">:blush:</div>
<div class="emo-item">:heart_eyes:</div>
<div class="emo-item">:relieved:</div>
<div class="emo-item">:stuck_out_tongue_winking_eye:</div>
<div class="emo-item">:kissing_smiling_eyes:</div>
<div class="emo-item">:frowning:</div>
<div class="emo-item">:confused:</div>
<div class="emo-item">:sweat_smile:</div>
<div class="emo-item">:pensive:</div>
<div class="emo-item">:cold_sweat:</div>
<div class="emo-item">:joy:</div>
<div class="emo-item">:tired_face:</div>
<div class="emo-item">:sleepy:</div>
<div class="emo-item">:dizzy_face:</div>
<div class="emo-item">:no_mouth:</div>
</div>
<div class="emo-row">
<div class="emo-item">:smile:</div>
<div class="emo-item">:smiley:</div>
<div class="emo-item">:kissing_heart:</div>
<div class="emo-item">:satisfied:</div>
<div class="emo-item">:stuck_out_tongue_closed_eyes:</div>
<div class="emo-item">:stuck_out_tongue:</div>
<div class="emo-item">:anguished:</div>
<div class="emo-item">:hushed:</div>
<div class="emo-item">:sweat:</div>
<div class="emo-item">:disappointed:</div>
<div class="emo-item">:persevere:</div>
<div class="emo-item">:astonished:</div>
<div class="emo-item">:angry:</div>
<div class="emo-item">:angry:</div>
<div class="emo-item">:yum:</div>
<div class="emo-item">:imp:</div>
</div>
<div class="emo-row">
<div class="emo-item">:laughing:</div>
<div class="emo-item">:relaxed:</div>
<div class="emo-item">:kissing_closed_eyes:</div>
<div class="emo-item">:grin:</div>
<div class="emo-item">:grinning:</div>
<div class="emo-item">:sleeping:</div>
<div class="emo-item">:open_mouth:</div>
<div class="emo-item">:expressionless:</div>
<div class="emo-item">:disappointed_relieved:</div>
<div class="emo-item">:confounded:</div>
<div class="emo-item">:cry:</div>
<div class="emo-item">:scream:</div>
<div class="emo-item">:rage:</div>
<div class="emo-item">:mask:</div>
<div class="emo-item">:smiling_imp:</div>
<div class="emo-item">:alien:</div>
</div>
<div class="emo-row">
<div class="emo-item">:smirk:</div>
<div class="emo-item">:flushed:</div>
<div class="emo-item">:wink:</div>
<div class="emo-item">:kissing:</div>
<div class="emo-item">:worried:</div>
<div class="emo-item">:grimacing:</div>
<div class="emo-item">:unamused:</div>
<div class="emo-item">:weary:</div>
<div class="emo-item">:fearful:</div>
<div class="emo-item">:sob:</div>
<div class="emo-item">:neckbeard:</div>
<div class="emo-item">:triumph:</div>
<div class="emo-item">:sunglasses:</div>
<div class="emo-item">:neutral_face:</div>
<div class="emo-item">:yellow_heart:</div>
<div class="emo-item">:+1:</div>
</div>
</div>
<script src="//localhost:5002/js/service/client.js"></script>
</body>
</html>
... ...
... ... @@ -18,4 +18,5 @@ module.exports = app => {
app.use('/brands', require('./apps/brands'));
app.use('/guang', require('./apps/guang'));
app.use('/cart', require('./apps/cart'));// 购物车
app.use('/service', require('./apps/service'));// 客服
};
... ...
... ... @@ -7,6 +7,7 @@
'use strict';
const gulp = require('gulp');
const sass = require('gulp-sass');
const gutil = require('gulp-util');
const ftp = require('gulp-ftp');
... ... @@ -109,7 +110,7 @@ const postcssPlugin = (et) => {
};
// default
gulp.task('default', ['postcss-dev', 'postcss-watch', 'webpack-dev-server']);
gulp.task('default', ['sass', 'sass-watch', 'postcss-dev', 'postcss-watch', 'webpack-dev-server']);
// ge
gulp.task('ge', ['postcss', 'webpack']);
... ... @@ -123,9 +124,20 @@ gulp.task('dist', ['ge'], () => {
.pipe(gutil.noop());
});
// scss预处理
gulp.task('sass', function() {
return gulp.src(['scss/service/*.scss'])
.pipe(sass().on('error', sass.logError))
.pipe(gulp.dest('scss/service'));
});
gulp.task('sass-watch', function() {
gulp.watch(['scss/service/*.scss', 'scss/service/helpers/*.scss'], ['sass']);
});
// postcss compile in dev
gulp.task('postcss-dev', () => {
return gulp.src('scss/index.css')
return gulp.src(['scss/index.css', 'scss/service/client.css'])
.pipe(sourcemaps.init())
.pipe(postcss(postcssPlugin(env.dev)))
.pipe(sourcemaps.write('.'))
... ... @@ -134,7 +146,7 @@ gulp.task('postcss-dev', () => {
// postcss file watch
gulp.task('postcss-watch', () => {
gulp.watch('scss/**/*.css', ['postcss-dev']);
gulp.watch(['scss/**/*.css', 'scss/service/*.css'], ['postcss-dev']);
});
// copy assets
... ...