Authored by ccbikai

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

/**
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/07/25
*/
'use strict';
// const _ = require('lodash');
// const helpers = global.yoho.helpers;
const model = require('../models/detail');
/**
* 商品详情
*/
const component = {
index(req, res) {
res.render('detail', {
module: 'news',
page: 'detail',
newsId: req.params[0]
});
},
news(req, res, next) {
const id = req.params[0];
let params = {
// uid: req.user.uid || 8050378 // TODO: fix this hard coded uid
article_id: id,
client_type: 'h5'
};
model.index(params).then(result => {
res.json(result);
}).catch(next);
}
};
module.exports = component;
... ...
/**
* Created by PhpStorm.
* User: Targaryen
* Date: 2016/7/26
* Time: 17:22
*/
'use strict';
module.exports = {
index: (req, res) => {
res.render('index', {
module: 'news',
page: 'index'
});
}
};
... ...
/**
* sub app
* @author: Bi Kai<kai.bi@yoho.cn>
* @date: 2016/05/09
*/
const express = require('express');
const path = require('path');
const hbs = require('express-handlebars');
const app = express();
// set view engine
const 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.set('views', path.join(__dirname, 'views/action'));
app.engine('.hbs', hbs({
extname: '.hbs',
defaultLayout: 'layout',
layoutsDir: doraemon,
partialsDir: [path.join(__dirname, 'views/partial'), `${doraemon}/partial`],
helpers: global.yoho.helpers
}));
// router
app.use(require('./router'));
module.exports = app;
... ...
/**
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/07/25
*/
'use strict';
// const _ = require('lodash');
// const helpers = global.yoho.helpers;
const serviceAPI = global.yoho.ServiceAPI;
const URI_PACKAGE_ARTICLE = 'guang/service/v2/article/';
const co = require('co');
const camelCase = global.yoho.camelCase;
/**
* 资讯详情
*/
const model = {
index(params) {
return co(function*() {
const article = yield serviceAPI.get(URI_PACKAGE_ARTICLE + 'getArticle', params);
const content = yield serviceAPI.get(URI_PACKAGE_ARTICLE + 'getArticleContent', params);
const brands = yield serviceAPI.get(URI_PACKAGE_ARTICLE + 'getBrand', params);
const other = yield serviceAPI.get(URI_PACKAGE_ARTICLE + 'getOtherArticle', Object.assign({
tags: article.data.tag,
offset: 0,
limit: 3
}, params));
return camelCase([article, content, brands, other]);
});
}
};
module.exports = model;
... ...
/**
* router of sub app news
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/07/25
*/
'use strict';
const expressRouter = require('express').Router;
const cRoot = './controllers';
const router = expressRouter();
// 详情controller
const detail = require(`${cRoot}/detail`);
router.get(/\/([\d]+)(.*)/, detail.index); // 详情routers
router.get(/news_(\d+)\.json/, detail.news);
const news = require(`${cRoot}/index`);
router.get('', news.index);
module.exports = router;
... ...
<div id="app" class="news-page" data-news-id="{{newsId}}">
<app/>
</div>
... ...
<div class="index">
<index-box></index-box>
</div>
... ...
... ... @@ -59,7 +59,10 @@ module.exports = {
shopId: req.body.shopId,
favId: req.body.favId,
uid: req.user.uid,
type: 'shop'
// uid: '8050882',
type: 'shop',
isFav: req.body.isFav
}).then(result => {
res.json(result);
});
... ...
... ... @@ -109,13 +109,13 @@ module.exports = {
type: params.type
};
if (params.isFav) {
if (params.isFav === 'true') {
Object.assign(finalParams, {
method: 'app.favorite.add'
method: 'app.favorite.cancel'
});
} else {
Object.assign(finalParams, {
method: 'app.favorite.cancel'
method: 'app.favorite.add'
});
}
return api.get('', finalParams);
... ...
... ... @@ -31,13 +31,16 @@ const getShopData = params => {
/* 是 BLK 的店铺 */
Object.assign(finalResult, {
isBlkShop: true
isBlkShop: true,
shopId: result[0].data.shop_id
});
return api.all([
shopApi.getShopInfoData({
shopId: result[0].data.shop_id,
uid: params.uid
// uid: '8050882'
})
]).then(subResult => {
if (subResult[0].code === 200) {
... ... @@ -46,7 +49,8 @@ const getShopData = params => {
Object.assign(finalResult, {
brandLogo: subResult[0].data.shop_logo,
brandName: subResult[0].data.shop_name,
brandIntro: subResult[0].data.shop_intro
brandIntro: subResult[0].data.shop_intro,
isFav: subResult[0].data.is_favorite === 'Y'
});
/* TODO shop_template_type 待接口确认 */
... ...
... ... @@ -10,6 +10,7 @@ module.exports = app => {
app.use('/api', require('./apps/api'));
app.use('/product', require('./apps/product'));
app.use('/home', require('./apps/home'));
app.use('/news', require('./apps/news'));
// 组件示例
if (!app.locals.proEnv) {
... ...
... ... @@ -21,6 +21,7 @@
"dependencies": {
"bluebird": "^3.4.1",
"body-parser": "^1.15.2",
"co": "^4.6.0",
"connect-memcached": "^0.2.0",
"connect-multiparty": "^2.0.0",
"cookie-parser": "^1.4.3",
... ...
let Vue = require('yoho-vue');
const moment = require('moment');
/**
* 替换参数
... ... @@ -113,3 +113,11 @@ Vue.filter('convertTime', (value) => {
return Y + M + D + h + m + s;
});
/**
* 格式化时间
*/
Vue.filter('formatUnixTime', (value, format) => {
return moment.unix(value).format(format || 'YYYY-MM-DD HH:mm:ss');
});
... ...
const Vue = require('yoho-vue');
const lazyload = require('yoho-vue-lazyload');
const app = require('news/detail.vue');
new Vue({
el: '#app',
components: {
app
}
});
Vue.use(lazyload);
... ...
/**
* Created by PhpStorm.
* User: Targaryen
* Date: 2016/7/26
* Time: 17:37
*/
const Vue = require('yoho-vue');
const lazyload = require('yoho-vue-lazyload');
const infinitScroll = require('yoho-vue-infinite-scroll');
const indexBox = require('news/index-box.vue');
Vue.use(lazyload);
Vue.use(infinitScroll);
new Vue({
el: '#index',
components: {
indexBox
}
});
... ...
... ... @@ -21,6 +21,13 @@
</div>
</template>
<script>
const Vue = require('yoho-vue');
const lazyload = require('yoho-vue-lazyload');
const infinitScroll = require('yoho-vue-infinite-scroll');
Vue.use(lazyload);
Vue.use(infinitScroll);
let bus = require('common/vue-bus');
module.exports = {
... ... @@ -53,7 +60,7 @@
margin: 0;
padding: 0;
}
.card-large {
.card {
float: left;
... ... @@ -87,7 +94,7 @@
font-weight: normal;
}
}
.good-price {
color: #b0b0b0;
margin-right: 10px;
... ...
<template>
<div v-if="block.text" class="text-block">
{{{block.text.data.text}}}
</div>
<div v-if="block.singleImage">
<div v-for="(index, item) in block.singleImage.data">
<a :href="item.url">
<img :title="item.title"
:alt="item.alt"
:src="item.src | resize 750 469" width="375"/>
</a>
</div>
</div>
</template>
<style></style>
<script>
module.exports = {
props: {
block: Object
}
};
</script>
... ...
<template>
<div class="show-box no-padding no-top-border">
<div class="news-box">
<h1>{{article.articleTitle}}</h1>
<div class="status-bar">
<span class="icon icon-love"></span><span class="label">{{article.publishTime | formatUnixTime 'MM.DD HH:mm'}}</span>
<span class="icon icon-love"></span><span class="label">{{article.pageViews}}</span>
</div>
</div>
<div class="content-box">
<div v-for="(index, block) in content">
<content-block :block="block"></content-block>
</div>
</div>
</div>
<div class="show-box no-padding" v-if="recommendProducts">
<h2>相关推荐</h2>
<product-list :data="recommendProducts.goods"></product-list>
</div>
<div class="brand-list show-box" v-if="brands">
<h2>相关品牌</h2>
<ul>
<li v-for="item in brands">
<a :href="item.url"><img :src="item.thumb"></a>
</li>
</ul>
</div>
<div class="show-box" v-if="other">
<h2>相关文章</h2>
<div class="other-box" v-for="item in other">
<div>
<div class="image-box">
<a :href="item.url">
<img :src="item.thumb | resize 213 134"/>
</a>
</div>
<h3><a :href="item.url">{{item.title}}</a></h3>
<div class="sub-time">
<span class="icon icon-love"></span><span class="label">{{article.publishTime | formatUnixTime 'MM.DD HH:mm'}}</span>
</div>
<div class="clearfix"></div>
</div>
</div>
</div>
</template>
<style class="scss">
$bgcolor: #fff;
.news-page {
background: #f6f6f6;
}
.show-box {
background: $bgcolor;
margin-top: 30px;
border-bottom: 1px solid #eee;
&:not(.no-padding) {
padding: 30px;
}
&:not(.no-top-border) {
border-top: 1px solid #eee;
}
}
h1 {
font-size: 43px;
font-weight: bold;
overflow: hidden;
margin-top: 36px;
margin-bottom: 0;
}
.news-box {
padding: 30px;
background: $bgcolor;
}
.content-box {
background: #fff;
img {
width: 100%;
display: block;
}
p {
font-size: 28px;
}
.text-block {
padding: 30px;
}
}
.other-box {
margin-top: 40px;
.image-box {
float: left;
max-width: 214px;
max-height: 134px;
overflow: hidden;
img {
width: 214px;
}
}
h3 {
margin-top: 16px;
margin-left: 30px;
float: left;
max-width: 420px;
min-height: 90px;
font-size: 32px;
}
.sub-time {
margin-left: 30px;
float: left;
color: #b3b3b3;
.icon {
margin-right: 12px;
}
}
}
h2 {
text-align: center;
line-height: 100px;
}
.status-bar {
color: #ccc;
font-size: 24px;
margin-top: 30px;
span.icon {
margin-right: 12px;
}
span.label {
margin-right: 78px;
}
}
.brand-list {
li {
display: inline-block;
width: 171px;
text-align: center;
img {
max-width: 140px;
}
}
ul {
:not(:last-child) {
border-right: 1px solid #eee;
}
}
}
</style>
<script>
require('common/vue-filter');
module.exports = {
components: {
'content-block': require('./content-block.vue'),
'product-list': require('component/product/list.vue')
},
data() {
return {
article: {},
content: {},
brands: {},
other: {},
recommendProducts: []
};
},
created() {
const newsId = $('#app').data('newsId');
$.get(`/news/news_${newsId}.json`).then(result => {
const article = result[0],
content = result[1],
brands = result[2],
other = result[3];
if (article && article.code === 200 && article.data) {
this.article = article.data;
}
if (content && content.code === 200 && content.data) {
this.content = content.data;
this.recommendProducts = this.content.filter((block)=> {
return block && typeof block.goods === 'object';
});
}
if (brands && brands.code === 200 && brands.data) {
this.brands = brands.data;
}
if (other && other.code === 200 && content.data) {
this.other = other.data;
}
});
}
};
</script>
... ...
<template>
<div></div>
</template>
<style>
</style>
<script>
module.exports = {
data() {
return {
msg: 'hello vue'
};
}
};
</script>
... ...
<template>
<div class="share-bottom clearfix" v-bind:class="{ 'hidden': close }">
<!-- TODO iconfont 要换-->
<span class="icon close" @click="closeBottom()">&#xe609;</span>
<img v-lazy="">
<span class="icon close" @click="closeBottom()">&#xe610;</span>
<span class="icon blk-icon">&#xe60f;</span>
<a href="javascript:;" class="new-user">新用户送千元礼包</a>
<a href="http://m.yohoblk.com" class="download">立即下载</a>
</div>
... ... @@ -30,10 +28,17 @@
float: left;
}
.blk-icon {
margin-left: 50px;
margin-right: 20px;
float: left;
}
a {
border: #000 solid 2px;
padding: 10px;
border-radius: 40px;
float: left;
}
.new-user {
... ... @@ -43,6 +48,7 @@
.download {
float: right;
margin-right: 20px;
}
}
... ...
<template>
<div class="brand-share">
<div class="brand-top-box" v-bind:style="{ 'background-image': `url(${brandBg})` }"></div>
<div class="brand-title">{{ brandName }}</div>
<div class="brand-intro">{{ brandIntro }}</div>
<div class="brand-top-box" v-bind:style="{ 'background-image': `url(${shopInfo.brandBg})` }"></div>
<div class="brand-title">{{ shopInfo.brandName }}</div>
<div class="brand-intro">{{ shopInfo.brandIntro }}</div>
<div class="tip">进入 BLK 选购潮品</div>
<div class="icon arrow">&#xe602;</div>
<img v-lazy="brandBg | resize 752 365">
<a href="//m.yohoblk.com"><img v-lazy="shopInfo.brandBg | resize 752 365"></a>
</div>
<share-bottom></share-bottom>
</template>
... ... @@ -15,7 +15,7 @@
width: 100%;
height: 468px;
color: #fff;
background-color: #000;
background-color: #ccc;
position: relative;
}
... ... @@ -54,20 +54,36 @@
</style>
<script>
const shareBottom = require('product/shop/share-bottom.vue');
const qs = require('yoho-qs');
const tip = require('common/tip');
module.exports = {
/* TODO 数据需要从接口获取 */
data() {
return {
brandName: 'COLORMAD',
brandBg: 'http://7xwj52.com1.z0.glb.clouddn.com/brandbg.jpg',
brandIntro: 'COLORMAD坚持女性的时尚美丽不应为牺牲健康为代价,以健康,时尚,科技为理念,' +
'研发健康无伤害的新概念甲油。'
shopInfo: {}
};
},
methods: {
/* 获取店铺简介相关数据 */
getShopInfo() {
$.get({
url: '/product/get-shop-info',
data: { domain: qs.domain }
}).done(result => {
this.shopInfo = result;
}).fail(() => {
tip('网络错误');
});
}
},
components: {
shareBottom
},
created() {
this.getShopInfo();
}
};
</script>
... ...
... ... @@ -70,8 +70,11 @@
this.shopInfo.showBrandInfo = true;
this.shareData = {
title: result.brandName,
link: '/brand-share?domain=' + this.domain,
img: result.brandBg
des: result.brandIntro,
url: '/product/brand-share?domain=' + this.domain,
img: result.brandBg,
shopId: result.shopId, // 不是分享的参数,收藏店铺使用
isFav: result.isFav // 不是分享的参数,收藏店铺使用
};
} else {
this.shopInfo.showBrandInfo = false;
... ...
... ... @@ -15,7 +15,7 @@
width: 100%;
height: 468px;
color: #fff;
background-color: #000;
background-color: #ccc;
position: relative;
.brand-bottom {
... ...
... ... @@ -2,9 +2,10 @@
<div class="top-box clearfix">
<span class="icon back" @click="goBack()">&#xe606;</span>
<div class="right">
<span class="icon" v-bind:class="{'favorite': shareData.isFav}" @click="collectShop()">&#xe609;</span>
<span class="icon share" @click="goShare()">&#xe60e;</span>
<span class="icon filter" @click="showFilter()">&#xe60b;</span>
<span v-if="shareData.isFav" class="icon" @click="collectShop()">&#xe60d;</span>
<span v-else class="icon" @click="collectShop()">&#xe60c;</span>
<span class="icon" @click="goShare()">&#xe60e;</span>
<span class="icon" @click="showFilter()">&#xe60b;</span>
</div>
</div>
</template>
... ... @@ -38,10 +39,6 @@
height: 60px;
margin: 0 5px;
}
.favorite {
color: #000;
}
}
}
</style>
... ... @@ -59,6 +56,10 @@
},
methods: {
goShare() {
// 删除两个多余的参数,两个参数是收藏时使用的
delete this.shareData.shopId;
delete this.shareData.isFav;
yoho.goShare(this.shareData, function() {}, function() {});
},
goBack() {
... ... @@ -75,13 +76,12 @@
};
$.post({
url: '/collect-shop',
url: '/product/collect-shop',
data: data
}).done(result => {
tip(result.message);
if (result.code === 200) {
this.shareData.isFav = this.shareData.isFav !== true;
} else {
tip('网络错误');
this.shareData.isFav = !this.shareData.isFav;
}
}).fail(() => {
tip('网络错误');
... ...