Authored by 陈轩

Merge remote-tracking branch 'origin/develop' into develop

/**
* 微信分享签名
* 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);
});
... ...
... ... @@ -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: {
... ... @@ -93,19 +93,13 @@ const matchHeader = (url) => {
headerid: '-1' // 默认不显示头部
};
if (/\/product\/new/.test(url)) {
header = titleMap[1];
// header.xxx = '111';// 匹配到头类型以后,可修改里边的内容
return header;
}
if (/\/brands/.test(url) || /\/cate/.test(url)) {
header = titleMap[4];
header = titleMap[2];
alert(JSON.stringify(header));
return header;
}
if (/\/home\/mydetails$/.test(url)) {
if (/\/home\/mydetails/.test(url)) {
header = titleMap[1];
header.title.des = '个人信息';
return header;
... ... @@ -148,15 +142,15 @@ const matchHeader = (url) => {
header.ltitle = {
des: '商品',
action: origin + '/home/favorite'
}
};
header.rtitle = {
des: '品牌',
action: origin + '/home/favorite?tab=brand'
}
};
header.right = {
des: '编辑',
action: 'editModel'
}
};
return header;
}
... ... @@ -184,7 +178,7 @@ const matchHeader = (url) => {
header.right = {
des: '提交',
action: 'saveFeedback'
}
};
return header;
}
... ... @@ -200,13 +194,20 @@ const matchHeader = (url) => {
module.exports = (url) => {
if (yoho.isApp) {
let data = {
// 品牌 品类
// if (/\/brands/.test(url) || /\/cate/.test(url)) {
// return yoho.goTab({index: 1});
// }
// 个人中心首页
if (/\/home$/.test(url)) {
return yoho.goTab({index: 4});
}
yoho.goNewPage({
header: matchHeader(url),
url: /^(https?:)?\/\//i.test(url) ? url : origin + url
};
// console.log(data);
yoho.goNewPage(data);
});
} else {
location.href = url;
}
... ...
... ... @@ -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;
}
});
});
... ...
... ... @@ -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',
... ...
<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">
<input v-if="showInput" type="text" name="">
<div v-else class="input" @click="changeToInput()">
<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;
}
input {
width: 92%;
height: 55px;
padding: 10px;
}
}
</style>
<script>
module.exports = {
data() {
return {
showInput: false
};
},
methods: {
changeToInput() {
this.showInput = true;
}
}
};
</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;
... ...
... ... @@ -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;
... ... @@ -453,6 +457,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 +481,11 @@
this.entity.isCollect = 'N';
} else if (result.code === 403) {
// 未登陆
yoho.goLogin();
yoho.goLogin('', function() {
}, function() {
});
}
});
} else {
... ...
... ... @@ -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 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 {
... ...