Authored by 沈志敏

Merge branch 'develop' of git.yoho.cn:fe/yohoblk-wap into develop

... ... @@ -5,10 +5,12 @@ Name | Path | Note
品牌列表 | /brands |
品类 | /cate |
全部分类 | /cate-all |
商品列表 | /product/list?sort=1 |
品牌店铺 | /product/shop?domain=sctest1 |
品牌店铺分享页面 | /product/shop-share?domain=colormad |
商品详情 | /product/{productId} |
新品抢先看 | /product/new |
搜索页 | /product/search?query=xxx |
资讯列表 | /news |
资讯详情 | /news/{newsId} |
个人中心 | /home |
... ... @@ -19,6 +21,7 @@ Name | Path | Note
待收货 | /home/orders?type=4 |
订单详情 | /home/order-detail?orderCode=xxx |
退货申请 | /home/refund?orderId=160181661 |
换货申请 | /home/exchange?orderId=160181661 |
收藏商品 | /home/favorite |
收藏品牌 | /home/favorite?tab=brand |
yoho币 | /home/mycurrency |
... ...
/**
* 微信分享签名
* Bi Kai <kai.bi@yoho.cn>
*/
'use strict';
const wechatModel = require('../models/wechat');
exports.shareToken = (req, res, next) => {
wechatModel.calcSignature({
url: req.query.url || 'http://www.yohoblk.com/'
}).then((result) => {
res.jsonp(result);
}).catch(next);
};
... ...
'use strict';
/*
* 生成微信分享所需的签名
* bikai <kai.bi@yoho.cn>
* 2016.6.15
*/
const request = require('request-promise');
const Promise = require('bluebird');
const crypto = require('crypto');
const logger = global.yoho.logger;
const cache = global.yoho.cache;
// 此处请勿使用有货公众号的 appId, 此处使用的是 女生志 的appId
const appId = 'wxb52ec6a352f0b090';
const secret = '9fe6bedb0b7f30986a168c7fc44f34c0';
const sha1 = (str) => {
const generator = crypto.createHash('sha1');
generator.update(str);
return generator.digest('hex');
};
const accessTokenCacheKey = 'wechatShare:accessToken';
const ticketCacheKey = 'wechatShare:ticket';
// 微信 JS 接口签名校验工具 http://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=jsapisign
let _getAccessToken = Promise.coroutine(function* () {
let accessToken = yield cache.get(accessTokenCacheKey);
if (accessToken) {
return accessToken;
}
logger.info('get accessToken from wechat API');
return request({
url: 'https://api.weixin.qq.com/cgi-bin/token',
qs: {
grant_type: 'client_credential',
appid: appId,
secret: secret
},
json: true
}).then((res) => {
// accessToken 有效期 7200s,缓存 7100s
cache.set(accessTokenCacheKey, res.access_token, 7100).catch((err) => {
logger.error('set wechat accessToken cache error', JSON.stringify(err));
});
return res.access_token;
}).catch((err) => {
logger.error('get accessToken from wechat API error', JSON.stringify(err));
});
});
let _getTicket = Promise.coroutine(function* () {
let ticket = yield cache.get(ticketCacheKey);
if (ticket) {
return ticket;
}
logger.info('get ticket from wechat API');
return request({
url: 'https://api.weixin.qq.com/cgi-bin/ticket/getticket',
qs: {
access_token: yield _getAccessToken(),
type: 'jsapi'
},
json: true
}).then(res => {
// ticket 有效期 7200s,缓存 7100s
cache.set(ticketCacheKey, res.ticket, 7100).catch((err) => {
logger.error('set wechat Token cache error', JSON.stringify(err));
});
return res.ticket;
}).catch((err) => {
logger.error('get ticket from wechat API error', JSON.stringify(err));
});
});
let calcSignature = Promise.coroutine(function* (data) {
data = Object.assign({
nonceStr: Math.random().toString(36).substr(2, 15),
timestamp: Math.floor(Date.now() / 1000) + '',
ticket: yield _getTicket(),
appId: appId
}, data);
const str = `jsapi_ticket=${data.ticket}&noncestr=${data.nonceStr}&timestamp=${data.timestamp}&url=${data.url}`;
data.signature = sha1(str);
return data;
});
// 测试
// calcSignature({
// url: 'http://www.yohobuy.com/'
// }).then(console.log);
module.exports = {
calcSignature
};
... ...
... ... @@ -11,8 +11,11 @@ const router = require('express').Router(); // eslint-disable-line
var multipart = require('connect-multiparty');
var multipartMiddleware = multipart();
const uploadApi = require('./upload/upload.js');
const uploadApi = require('./controllers/upload.js');
const wechat = require('./controllers/wechat.js');
router.post('/upload/image', multipartMiddleware, uploadApi.uploadImg);
router.get('/wechat/share/token', wechat.shareToken);
module.exports = router;
... ...
... ... @@ -101,11 +101,11 @@ const getCateListData = params => {
sub: []
};
if (_.isEmpty(cate.sub)) {
item.url = helpers.urlFormat('/', {
item.url = helpers.urlFormat('/product/list', {
sort: item.sort,
sort_name: item.name,
gender: genderArr[categorykey]
}, 'list');
});
oneClass.ca.push(item);
return true;// equal continue;
}
... ... @@ -115,11 +115,11 @@ const getCateListData = params => {
id: item.id,
name: '全部' + item.name,
sort: item.sort,
url: helpers.urlFormat('/', {
url: helpers.urlFormat('/product/list', {
sort: item.sort,
sort_name: item.name,
gender: genderArr[categorykey]
}, 'list'),
}),
sub: []
});
... ... @@ -130,11 +130,11 @@ const getCateListData = params => {
sort: sub.relation_parameter.sort,
url: ''
};
subitem.url = helpers.urlFormat('/', {
subitem.url = helpers.urlFormat('/product/list', {
sort: subitem.sort,
sort_name: subitem.name,
gender: genderArr[categorykey]
}, 'list');
});
item.sub.push(subitem);
});
... ...
... ... @@ -45,9 +45,9 @@ const component = {
var gender = '';
if (data.gender === '1') {
gender = 'men'
gender = 'men';
} else if (data.gender === '2') {
gender = 'women'
gender = 'women';
}
res.render('mydetails', {
... ...
... ... @@ -19,7 +19,7 @@ exports.index = (req, res) => {
/* 获取 筛选配置 */
exports.fetchFilters = (req, res, next) => {
const params = Object.assign({
uid: req.user.uid,
uid: req.user.uid || 0,
page: 1,
order: 1,
yh_channel: 'all',
... ... @@ -34,10 +34,12 @@ exports.fetchFilters = (req, res, next) => {
/* 查询 产品列表 */
exports.fetchProducts = (req, res, next) => {
const params = Object.assign({
uid: req.user.uid,
page: 1,
order: 1
uid: req.user.uid || 0
}, req.query);
newModel.getNewData(params).catch(next);
newModel.getNewData(params)
.then(result => {
res.json(result);
})
.catch(next);
};
... ...
... ... @@ -17,25 +17,6 @@ exports.index = (req, res) => {
res.render('product-list', view);
};
/* 筛选的二级页面 */
exports.subFilter = (req, res) => {
const view = {
module: 'product',
page: 'filter-sub'
};
res.render('filter-sub', view);
};
/* 获取 筛选配置 */
exports.fetchFilters = (req, res, next) => {
const params = req.query;
searchModel.products(params)
.then(result => res.json(result))
.catch(next);
};
/* 查询 产品列表 method:GET */
exports.fetchProducts = (req, res, next) => {
const params = req.query;
... ...
/* 筛选的二级页面 */
exports.page = (req, res) => {
const view = {
module: 'product',
page: 'filter-sub'
};
res.render('filter-sub', view);
};
... ... @@ -11,11 +11,9 @@ exports.getNewData = (params) => {
method: 'app.search.newProduct',
}, params);
api.post('', params, {
cache: true,
code: 200
}).then(result => {
return api.get('', params).then(result => {
if (result.data) {
prettyFilter(result.data.filter);
result.data.productList = processProductList(result.data.productList);
result = camelCase(result);
}
... ... @@ -30,7 +28,7 @@ exports.getNewFilterData = (params) => {
method: 'app.search.newProduct',
}, params);
api.post('', params, {
return api.post('', params, {
cache: true,
code: 200
}).then(result => {
... ...
... ... @@ -13,11 +13,13 @@ const router = expressRouter();
// 产品 搜索 页面
const search = require(`${cRoot}/search`);
const newProduct = require(`${cRoot}/new`);
router.get('/search', search.index);
router.get('/search.json', search.fetchProducts); // ajax
// 新品页
const newProduct = require(`${cRoot}/new`);
router.get('/new', newProduct.index);
router.get('/new.json', newProduct.fetchProducts);
... ... @@ -26,12 +28,6 @@ const productList = require(`${cRoot}/product-list`);
router.get('/list', productList.index);
router.get('/list.json', productList.fetchProducts);
router.get('/list/filter', productList.subFilter);
// sub filter
const subFilter = require(`${cRoot}/sub-filter`);
router.get('/sub-filter', subFilter.page);
// 品牌店铺页面
const shop = require(`${cRoot}/shop`);
... ...
{{! 筛选的 二级菜单}}
<div id="filter-sub">
<filter-sub><filter-sub>
</div>
\ No newline at end of file
... ... @@ -6,11 +6,11 @@
*/
module.exports = app => {
app.use('/', require('./apps/channel'));
app.use('/api', require('./apps/api'));
app.use('/product', require('./apps/product'));
app.use('/home', require('./apps/home'));
app.use('/news', require('./apps/news'));
app.use('/', require('./apps/channel')); // 一级频道模块
app.use('/api', require('./apps/api')); // 各模块公有 API
app.use('/product', require('./apps/product')); // 商品模块
app.use('/home', require('./apps/home')); // 个人中心
app.use('/news', require('./apps/news')); // 资讯
// 组件示例
if (!app.locals.proEnv) {
... ...
... ... @@ -19,7 +19,7 @@ const titleMap = {
action: ''
},
title: {
des: '标题1',
des: 'BLK',
action: ''
}
},
... ... @@ -29,11 +29,11 @@ const titleMap = {
action: ''
},
title: {
des: '标题2',
des: 'BLK',
action: ''
},
right: {
action: ''
action: origin + '/home'
}
},
3: {
... ... @@ -42,12 +42,12 @@ const titleMap = {
action: ''
},
title: {
des: '标题3',
des: 'BLK',
action: ''
},
right: {
des: '提交',
action: ''
action: 'submitForm'
}
},
4: {
... ... @@ -67,16 +67,16 @@ const titleMap = {
action: ''
},
ltitle: {
des: '商品5',
action: ''
des: '商品',
action: origin + '/favorite'
},
rtitle: {
des: '品牌5',
action: ''
des: '品牌',
action: origin + '/favorite?tab=brand'
},
right: {
des: '编辑5',
action: ''
des: '编辑',
action: 'editModel'
}
},
6: {
... ... @@ -92,26 +92,15 @@ const matchHeader = (url) => {
let header = {
headerid: '-1' // 默认不显示头部
};
let path = url.split('?')[0];
if (/\/product\/new/.test(url)) {
header = titleMap[1];
// header.xxx = '111';// 匹配到头类型以后,可修改里边的内容
return header;
}
if (/\/brands/.test(url) || /\/cate/.test(url)) {
header = titleMap[4];
return header;
}
if (/\/home\/mydetails$/.test(url)) {
if (/\/home\/mydetails$/.test(path)) {
header = titleMap[1];
header.title.des = '个人信息';
return header;
}
if (/\/home\/orders/.test(url)) {
if (/\/home\/orders$/.test(path)) {
let des = '';
let u = url.split('?')[1];
... ... @@ -130,65 +119,48 @@ const matchHeader = (url) => {
return header;
}
if (/\/home\/order-detail/.test(url)) {
if (/\/home\/order-detail$/.test(path)) {
header = titleMap[2];
header.title.des = '订单详情';
header.title.right.action = origin + '/home/service';
return header;
}
if (/\/home\/refund\/orders/.test(url)) {
if (/\/home\/refund\/orders$/.test(path)) {
header = titleMap[1];
header.title.des = '退/换货';
return header;
}
if (/\/home\/favorite/.test(url)) {
header = titleMap[5];
header.ltitle = {
des: '商品',
action: origin + '/home/favorite'
}
header.rtitle = {
des: '品牌',
action: origin + '/home/favorite?tab=brand'
}
header.right = {
des: '编辑',
action: 'editModel'
}
return header;
}
if (/\/home\/mycurrency/.test(url)) {
if (/\/home\/mycurrency$/.test(path)) {
header = titleMap[1];
header.title.des = '有货币';
return header;
}
if (/\/home\/help$/.test(url)) {
if (/\/home\/help$/.test(path)) {
header = titleMap[1];
header.title.des = '帮助中心';
return header;
}
if (/\/home\/service$/.test(url)) {
if (/\/home\/service$/.test(path)) {
header = titleMap[1];
header.title.des = 'Yoho!Blk在线客服';
return header;
}
if (/\/home\/feedback/.test(url)) {
if (/\/home\/feedback$/.test(path)) {
header = titleMap[3];
header.title.des = '意见反馈';
header.right = {
des: '提交',
action: 'saveFeedback'
}
};
return header;
}
if (/\/home\/about/.test(url)) {
if (/\/home\/about$/.test(path)) {
header = titleMap[1];
header.title.des = '关于';
return header;
... ... @@ -200,13 +172,34 @@ const matchHeader = (url) => {
module.exports = (url) => {
if (yoho.isApp) {
let data = {
let path = url.split('?')[0];
// 个人中心收藏
if (/\/home\/favorite$/.test(path)) {
return yoho.goPageView({
header: titleMap[5]
});
}
// 个人中心首页
if (/\/home$/.test(path)) {
return yoho.goTab({index: 4});
}
// 资讯
if (/\/news$/.test(path)) {
return yoho.goTab({index: 3});
}
// 品牌 品类
if (/\/brands$/.test(path) || /\/cate$/.test(path)) {
return yoho.goTab({index: 1});
}
yoho.goNewPage({
header: matchHeader(url),
url: /^(https?:)?\/\//i.test(url) ? url : origin + url
};
// console.log(data);
yoho.goNewPage(data);
});
} else {
location.href = url;
}
... ...
/**
* Created by PhpStorm.
* User: Targaryen
* Date: 2016/7/29
* Time: 16:55
*/
const $ = require('yoho-jquery');
$.ajax({
url: '//res.wx.qq.com/open/js/jweixin-1.1.0.js',
dataType: 'script',
cache: true,
success: function() {
$.ajax({
url: '/api/wechat/share/token',
data: {
url: location.href
},
success: function(res) {
if (window.wx) {
window.wx.config({
debug: false,
appId: res.appId,
timestamp: res.timestamp,
nonceStr: res.nonceStr,
signature: res.signature,
jsApiList: [
'checkJsApi',
'onMenuShareTimeline',
'onMenuShareAppMessage',
'onMenuShareQQ',
'onMenuShareWeibo',
'hideMenuItems',
'showMenuItems',
'hideAllNonBaseMenuItem',
'showAllNonBaseMenuItem',
'translateVoice',
'startRecord',
'stopRecord',
'onRecordEnd',
'playVoice',
'pauseVoice',
'stopVoice',
'uploadVoice',
'downloadVoice',
'chooseImage',
'previewImage',
'uploadImage',
'downloadImage',
'getNetworkType',
'openLocation',
'getLocation',
'hideOptionMenu',
'showOptionMenu',
'closeWindow',
'scanQRCode',
'chooseWXPay',
'openProductSpecificView',
'addCard',
'chooseCard',
'openCard'
]
});
}
}
});
}
});
module.exports = (shareData) => {
if (window.wx) {
window.wx.ready(function() {
window.wx.onMenuShareAppMessage(shareData);
window.wx.onMenuShareTimeline(shareData);
window.wx.onMenuShareQQ(shareData);
window.wx.onMenuShareWeibo(shareData);
});
}
};
... ...
... ... @@ -18,9 +18,11 @@ const $ = require('yoho-jquery');
const interceptClick = require('common/intercept-click');
$(() => {
$('body').on('click', 'a[href]:not(".no-intercept")', function() {
$('body').on('click', 'a[href]', function() {
// 拦截跳转
interceptClick($(this).attr('href'));
return false;
if (!$(this).hasClass('no-intercept')) {
interceptClick($(this).attr('href'));
return false;
}
});
});
... ...
const Vue = require('yoho-vue');
const filterSub = require('component/product/filter/filter-sub.vue');
// TODO: 该mock数据 应该有 上级页面传过来
// const page = {
// 二级筛选的 类型
// type: 'brand',
// data: [ // 二级筛选的 数据
// { id: 1, name: '红色' },
// { id: 2, name: '红色' },
// { id: 3, name: '红色' }
// ],
// data: [{
// domain: 'converse',
// alif: 'C',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2015/12/09/14/02752d2839e7001a09ecf71dcc9a996387.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'converse',
// keyword: 'converse,街头,摇滚,百搭,朋克,简约,个性,自然,潮流,环保',
// id: 27,
// hotKeyword: 'converse',
// isHot: 'Y',
// nameEn: 'converse',
// nameCn: '匡威'
// }, {
// domain: 'eightguys',
// alif: 'E',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2013/07/02/19/01959c3b33245e16e786bda6b1e91234be.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Eight Guys',
// keyword: '8Guys,八个男人',
// id: 396,
// hotKeyword: '',
// isHot: 'Y',
// nameEn: 'eight guys',
// nameCn: 'Eight Guys'
// }, {
// domain: 'soulandsole',
// alif: 'S',
// ico: 'http://img13.static.yhbimg.com/brandLogo/2014/01/27/11/02cfe4044dfb82230c1868211b814d6192.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'SOUL&SOLE',
// keyword: '',
// id: 335,
// hotKeyword: '',
// isHot: '',
// nameEn: 'SOUL&SOLE ',
// nameCn: 'SOUL&SOLE '
// }, {
// domain: 'thething',
// alif: 'T',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/12/09/16/018d25bf4481ed2998bf33b294673a8535.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'THETHING',
// keyword: 'thething,tee,polo,衬衫,裤类,包类,配件类',
// id: 138,
// hotKeyword: 'thething',
// isHot: 'Y',
// nameEn: 'THETHING',
// nameCn: 'THETHING'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'roxy',
// alif: 'R',
// ico: 'http://img12.static.yhbimg.com/brandLogo/2012/05/28/13/0242bc746fa4ddbc9519c14c3b769b6774.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Roxy',
// keyword: 'roxy',
// id: 273,
// hotKeyword: '',
// isHot: '',
// nameEn: 'Roxy',
// nameCn: 'Roxy'
// }, {
// domain: 'natkiel',
// alif: 'N',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/11/17/10/01032e9fba6bc0770077f2c645bad46127.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Natkiel',
// keyword: '奈凯儿',
// id: 393,
// hotKeyword: '',
// isHot: 'N',
// nameEn: 'Natkiel',
// nameCn: 'Natkiel'
// }, {
// domain: 'natkiel',
// alif: 'N',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/11/17/10/01032e9fba6bc0770077f2c645bad46127.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Natkiel',
// keyword: '奈凯儿',
// id: 393,
// hotKeyword: '',
// isHot: 'N',
// nameEn: 'Natkiel',
// nameCn: 'Natkiel'
// }, {
// domain: 'natkiel',
// alif: 'N',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/11/17/10/01032e9fba6bc0770077f2c645bad46127.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Natkiel',
// keyword: '奈凯儿',
// id: 393,
// hotKeyword: '',
// isHot: 'N',
// nameEn: 'Natkiel',
// nameCn: 'Natkiel'
// }, {
// domain: 'natkiel',
// alif: 'N',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/11/17/10/01032e9fba6bc0770077f2c645bad46127.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Natkiel',
// keyword: '奈凯儿',
// id: 393,
// hotKeyword: '',
// isHot: 'N',
// nameEn: 'Natkiel',
// nameCn: 'Natkiel'
// }, {
// domain: 'natkiel',
// alif: 'N',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/11/17/10/01032e9fba6bc0770077f2c645bad46127.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Natkiel',
// keyword: '奈凯儿',
// id: 393,
// hotKeyword: '',
// isHot: 'N',
// nameEn: 'Natkiel',
// nameCn: 'Natkiel'
// }, {
// domain: 'natkiel',
// alif: 'N',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/11/17/10/01032e9fba6bc0770077f2c645bad46127.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Natkiel',
// keyword: '奈凯儿',
// id: 393,
// hotKeyword: '',
// isHot: 'N',
// nameEn: 'Natkiel',
// nameCn: 'Natkiel'
// }, {
// domain: 'natkiel',
// alif: 'N',
// ico: 'http://img11.static.yhbimg.com/brandLogo/2015/11/17/10/01032e9fba6bc0770077f2c645bad46127.jpg?imageMogr2/thumbnail/{width}x{height}/extent/{width}x{height}/background/d2hpdGU=/position/center/quality/80',
// name: 'Natkiel',
// keyword: '奈凯儿',
// id: 393,
// hotKeyword: '',
// isHot: 'N',
// nameEn: 'Natkiel',
// nameCn: 'Natkiel'
// }],
// val: 3 // 二级筛选 的值
// };
new Vue({
el: '#filter-sub',
data: function() {
return {};
},
components: {
filterSub
}
});
... ... @@ -34,7 +34,7 @@ const yoho = {
* @param success 调用成功的回调方法
* @param fail 调用失败的回调方法
*/
goTap(args, success, fail) {
goTab(args, success, fail) {
if (this.isApp) {
window.yohoInterface.triggerEvent(success || nullFun, fail || nullFun, {
method: 'go.tab',
... ... @@ -277,9 +277,12 @@ const yoho = {
* @param callback 回调
*/
addNativeMethod(name, callback) {
if (window.yohoInterface) {
window.yohoInterface[name] = callback;
}
// 延迟 500ms 注入
setTimeout(function() {
if (window.yohoInterface) {
window.yohoInterface[name] = callback;
}
}, 500);
}
};
... ...
<template>
<div class="cate-tab-fixed">
<tab v-bind:page="page"></tab>
</div>
<div class="cate-page" id='cate-page'>
<div class="cate-nav clearfix">
<ul>
... ... @@ -29,12 +26,6 @@
</div>
</template>
<style>
.cate-tab-fixed {
position: fixed;
top: 0;
left: 0;
}
.cate-page {
font-size: 36px;
font-family: helvetica, Arial, "黑体";
... ...
<template>
<tab v-bind:page="page"></tab>
<brand-search></brand-search>
<resources v-bind:content-code.sync="contentCode"></resources>
<brand-list v-bind:channel="channel"></brand-list>
</template>
... ... @@ -10,6 +11,7 @@
const tab = require('channel/tab.vue');
const resources = require('component/resources/index.vue');
const brandList = require('channel/brand-list.vue');
const brandSearch = require('channel/brand-search.vue');
module.exports = {
... ... @@ -21,6 +23,7 @@
},
components: {
tab,
brandSearch,
resources,
brandList
}
... ...
<template>
<div class="search">
<div v-else class="input" @click='yoho.goSearch()'>
<span class="icon icon-search"></span> Search
</div>
</div>
</template>
<style>
.search {
width: 100%;
height: 85px;
padding: 15px 0;
background-color: #f6f6f6;
text-align: center;
.input {
margin-left: auto;
margin-right: auto;
background-color: #fff;
text-align: center;
color: #b0b0b0;
height: 55px;
width: 92%;
font-size: 28px;
padding: 5px 0;
}
}
</style>
<script>
const yoho = require('yoho');
module.exports = {
data() {
return {
yoho
};
}
};
</script>
... ...
<template>
<tab v-bind:page="page"></tab>
<resources v-bind:content-code.sync="contentCode"></resources>
<div class="tab-top-fixed">
<tab v-bind:page="page"></tab>
</div>
<div class="resources">
<resources v-bind:content-code.sync="contentCode"></resources>
</div>
</template>
<style>
.tab-top-fixed {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 99;
}
.resources {
margin-top: 100px;
}
</style>
<script>
const contentCode = require('content-code');
const qs = require('yoho-qs');
... ...
... ... @@ -33,8 +33,13 @@
<style>
@import "../../scss/common/color";
html {
font-size: 60px !important;
}
.sidebar {
width: 100%;
margin-top: 40px;
background: $white;
overflow-x: hidden;
... ...
... ... @@ -37,10 +37,10 @@
padding-left: 30px;
padding-right: 30px;
width: 100%;
height: 60px;
height: 120px;
line-height: 60px;
font-size: 48px;
background-color: #fff;
background-color: transparent;
.icon,
.header-title {
vertical-align: middle;
... ... @@ -66,6 +66,7 @@
}
.header-gap {
height: 60px;
height: 120px;
background-color: #fff;
}
</style>
... ...
... ... @@ -10,7 +10,7 @@
<li class="filter-cate" v-for="classify in config" @click="entrySub($key)">
<i class="icon icon-right right"></i>
<span class="filter-cate-label">{{$key | filter-en-cn }}</span>
<span class="filter-cate-val">{{params[$key] && params[$key].name }}</span>
<span class="filter-cate-val">{{showLabel($key)}}</span>
</li>
</ul>
</div>
... ... @@ -50,26 +50,21 @@
this.overlay.show();
} else {
this.overlay.hide();
this.$set('params', {});
}
}
},
methods: {
clearVals: function() {
// remove all value
this.$set('selected', {});
this.$set('params', {});
},
/**
* 当二级筛选, 返回数据时, 调用该方法
* @param {[type]} cate [description]
* @param {[type]} val [description]
*/
setCateParams: function(cate, val) {
this.$set(`params.${cate}`, val);
},
okAction: function() {
this.$set('selected', Object.assign({}, this.selected,this.params));
bus.$emit('filter.change', {
val: this.params,
val: this.selected,
ref: this._uid
});
},
... ... @@ -78,8 +73,24 @@
this.$refs.filterSub.isVisible = true;
},
/**
* 当二级筛选, 返回数据时, 调用该方法
* @param {[type]} cate [description]
* @param {[type]} val [description]
*/
setParams: function(item) {
this.$set(`params.${this.subType}`, item);
},
showLabel: function(key){
const newSelected = this.params[key];
const oldSelected = this.selected[key];
if(newSelected) {
return newSelected.name || '';
} else {
return oldSelected && oldSelected.name || '';
}
}
},
filters: {
... ...
... ... @@ -10,7 +10,7 @@
<button class="button" @click='yoho.goNewPage({"url":"http://m.yohoblk.com"})'>新页面</button>
<button class="button" @click="yoho.goPay()">支付</button>
<button class="button" @click="yoho.goBack()">返回</button>
<button class="button" @click='yoho.goShare({"title":"标题","des":"描述","img":"http://7xwj52.com1.z0.glb.clouddn.com/brandbg.jpg","url":"http://m.yohoblk.com"})'>分享</button>
<button class="button" @click='yoho.goShare({"title":"标题","des":"描述","img":"http://7xwj52.com1.z0.glb.clouddn.com/brandbg.jpg","url":"http://m.yohoblk.com/product/shop/share?domain=sctest1"})'>分享</button>
<button class="button" @click='yoho.goSearch()'>搜索</button>
<button class="button" @click='yoho.goSetting()'>设置</button>
<button class="button" @click='yoho.goSetAvatar()'>设置头像</button>
... ...
... ... @@ -73,6 +73,8 @@
this.queryProductFeature(msg.pid);
});
yoho.addNativeMethod('submitForm', this.submit.bind(this));
// 获取 换货商品
$.ajax({
url: '/home/exchange/order',
... ... @@ -111,6 +113,11 @@
}
});
},
computed: {
selectedGoods() {
return this.goodsArr.filter(goods => goods.checked);
}
},
methods: {
changeAddress() {
yoho.goAddress({
... ...
... ... @@ -15,7 +15,7 @@
<div class="bottom clearfix">
<span class="icon time-icon">&#xe612;</span>
<span class="time">{{news.publishTime}}</span>
<span class="icon share" @click="share(news.title, news.intro, news.src, news.share.url)">&#xe611;</span>
<span class="icon icon-share share" @click="share(news.title, news.intro, news.src, news.share.url)"></span>
</div>
</div>
</div>
... ...
... ... @@ -61,9 +61,9 @@
methods: {
showcase: function() {
const opts = {
images: this.goods.map((item)=> {
images: this.goods.map((item) => {
return item.colorImage;
}),
}).filter(image => image),
index: this.$refs.swipe.index
};
... ...
... ... @@ -168,7 +168,7 @@
</show-box>
<div class="control-box">
<div class="control-box" v-if="isApp">
<button class="button control-button">
<span @click="yoho.goShopingCart()" style="position: relative;">
<i class="icon icon-bag"></i>
... ... @@ -191,6 +191,10 @@
</button>
</div>
<div v-if="!isApp">
<share-bottom></share-bottom>
</div>
<feature-selector :is-visible="showFeatureSelector" :entity="entity"
:on-add-to-cart="onAddToCart"></feature-selector>
</template>
... ... @@ -293,7 +297,7 @@
justify-content: space-around;
align-items: stretch;
position: fixed;
width: 100%;
width: 750px;
height: 99px;
bottom: 0;
... ... @@ -392,6 +396,7 @@
const app = $('#app');
const tip = require('common/tip');
const yoho = require('yoho');
const share = require('common/share');
require('yoho-vue-swipe/dist/vue-swipe.css');
... ... @@ -453,6 +458,7 @@
featureSelector: require('component/product/feature-selector.vue'),
showBox: require('./show-box.vue'),
topNav: require('./top-nav.vue'),
shareBottom: require('component/tool/share-bottom.vue'),
},
methods: {
/**
... ... @@ -476,7 +482,11 @@
this.entity.isCollect = 'N';
} else if (result.code === 403) {
// 未登陆
yoho.goLogin();
yoho.goLogin('', function() {
}, function() {
});
}
});
} else {
... ... @@ -516,6 +526,21 @@
}
});
share({
title: this.entity.productName,
link: location.href,
desc: '我在BLK发现了一个不错的商品,快来看看吧!',
imgUrl: this.firstImage.replace(/(\{width}|\{height}|\{mode})/g, function($0) {
const dict = {
'{width}': 300,
'{height}': 300,
'{mode}': 2
};
return dict[$0];
})
});
return result;
}).then((result)=> {
loadIntroDeferred = () => {
... ...
... ... @@ -13,9 +13,10 @@
.top-nav {
position: fixed;
z-index: 10;
font-size: 40px;
font-size: 50px;
padding: 30px;
width: 100%;
top: 40px;
.left {
float: left;
... ...
<template>
<div>
<template v-if="productList.length">
<order :config="orderConfig" :val="order">
</Sort>
<List :data="productList"></List>
</template>
<div class="empty-tip" v-if="empty">
<i class="icon icon-search"></i>
<p class="empty-tip-cn">未找到相关商品</p>
<p class="empty-tip-en">Did not find the relevant goods</p>
</div>
<cheader title="新品抢先看">
<i class="icon icon-filter" slot="right" @click="openFilter"></i>
</cheader>
<order :config="orderConfig" :val="order"></order>
<List :data="productList"></List>
<Filter :config="filterConfig" action="/product/list.json" v-ref:filter></Filter>
</div>
</template>
<script>
const $ = require('yoho-jquery');
const Vue = require('yoho-vue');
const lazyload = require('yoho-vue-lazyload');
const infinitScroll = require('yoho-vue-infinite-scroll');
const qs = require('yoho-qs');
const qs = require('yoho-qs/parse');
const bus = require('common/vue-bus');
const tip = require('common/tip');
const sort = require('component/product/order.vue');
const cheader = require('component/header.vue');
const order = require('component/product/order.vue');
const list = require('component/product/list.vue');
const filter = require('component/product/filter.vue');
let locationQuery = qs(decodeURIComponent(location.search.replace(/^\?/, '')));
Vue.use(lazyload);
Vue.use(infinitScroll);
require('common/vue-filter');
module.exports = {
el: '#product-new',
data: function() {
return {
orderConfig: global.orderConfig,
filterConfig: global.filterConfig,
sortName: locationQuery.sort_name,
orderConfig: [],
filterConfig: null,
// query
url: '/product/search.json',
order: null,
query: qs.query,
url: '/product/new.json',
order: '',
filter: {},
page: 0, // 未搜索 page=0; 全部加载完 page = totalPage; 无数据: page !=0 && productList.length=0
totalPage: null,
... ... @@ -50,21 +49,18 @@
inSearching: false // 请求中
};
},
computed: {
// 无数据
empty: function() {
return this.page !== 0 && !this.productList.length;
}
},
components: {
cheader,
list,
order
order,
filter
},
methods: {
search: function() {
const self = this;
const nextPage = this.page + 1;
console.log(nextPage);
if (this.inSearching) {
return;
}
... ... @@ -75,20 +71,22 @@
}
this.inSearching = true;
console.log(nextPage);
$.get(this.url, {
order: this.order, // 排序 信息
query: this.query,
$.get(this.url, Object.assign({
order: this.order,
page: nextPage
})
}, this.filter, locationQuery))
.done(res => {
if (res.code === 200) {
self.page = res.data.page;
self.totalPage = res.data.pageTotal;
self.$set('productList', self.productList.concat(res.data.productList));
if (!self.filterConfig) {
self.$set('filterConfig', res.data.filter);
}
}
})
.fail(() => {
.fail(error => {
tip('网络出错~');
})
.always(() => {
... ... @@ -96,6 +94,14 @@
});
},
openFilter() {
this.$refs.filter.isVisible = true;
},
// openFilterSub: function(classify) {
// console.log('TODO: open filter sub', classify);
// },
/**
* 清空数据(page=0) 重新搜索
*/
... ... @@ -106,9 +112,12 @@
}
},
watch: {
/* order 改变 都会触发 重新搜索 */
/* order 和 filter 改变 都会触发 重新搜索 */
order: function() {
this.research();
},
filter: function() {
this.research();
}
},
... ... @@ -120,36 +129,33 @@
self.search();
});
bus.$on('order.change', function({
val
}) {
bus.$on('order.change', function({val}) {
console.log(val);
self.order = val;
});
/**
* 筛选组件 筛选值变更,触发 filter.change事件
* 1. 重新搜索
* 2. 关闭 drawer 组件
*/
bus.$on('filter.change', function({val}) {
console.log(val);
let filter = {};
$.each(val, (type, item) => {
if (item.id) {
filter[type] = item.id;
}
});
self.$set('filter', filter);
self.$refs.filter.isVisible = false;
});
this.search();
}
};
</script>
<style>
.empty-tip {
margin-top: 380px;
color: #b0b0b0;
text-align: center;
.icon-search {
display: inline-block;
font-size: 200px;
margin-bottom: 56px;
}
}
.empty-tip-cn {
font-size: 34px;
margin-bottom: 30px;
}
.empty-tip-en {
font-size: 20px;
}
</style>
... ...
<template>
<div class="brand-share">
<div class="brand-top-box" v-bind:style="{ 'background-image': `url(${shopInfo.brandBg})` }"></div>
<img class="brand-top-box" v-bind:src="shopInfo.brandBg | resize 750 478">
<div class="brand-title">{{ shopInfo.brandName }}</div>
<div class="brand-intro">{{ shopInfo.brandIntro }}</div>
<div class="tip">进入 BLK 选购潮品</div>
... ... @@ -13,10 +13,6 @@
.brand-share {
.brand-top-box {
width: 100%;
height: 468px;
color: #fff;
background-color: #ccc;
position: relative;
}
.brand-title {
... ... @@ -56,6 +52,7 @@
const shareBottom = require('component/tool/share-bottom.vue');
const qs = require('yoho-qs');
const tip = require('common/tip');
const share = require('common/share');
module.exports = {
... ... @@ -64,6 +61,16 @@
shopInfo: {}
};
},
watch: {
shopInfo() {
share({
title: this.shopInfo.brandName,
link: location.href,
desc: this.shopInfo.shareSubTitle,
imgUrl: this.shopInfo.brandBg
});
}
},
methods: {
/* 获取店铺简介相关数据 */
... ...
... ... @@ -51,7 +51,7 @@
color: #000;
height: 120px;
top: 0;
padding: 70px 20px 10px;
padding: 55px 20px 10px;
}
</style>
... ...