Authored by 陈轩

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

... ... @@ -10,3 +10,6 @@
<hr>
Cookies:<br>
{{{cookies}}}
<hr>
<input type="date">
... ...
... ... @@ -39,6 +39,12 @@ const component = {
res.render('index', _.merge(result, data[1]));
}).catch(next);
},
mydetails: (req, res) => {
res.render('mydetails', {
module: 'home',
page: 'mydetails'
});
},
help: (req, res, next) => {
homeModel.getHelpInfo().then(helpList => {
res.render('help', {
... ... @@ -104,8 +110,6 @@ const component = {
}
}).catch(next);
},
// 关于我们
aboutUs: (req, res) => {
res.render('about-us', {
module: 'home',
... ...
... ... @@ -33,7 +33,7 @@ const refund = {
res.render('logistics', {
module: 'home',
page: 'logistics',
company_list: result ? JSON.stringify(result.data) : ""
company_list: result ? JSON.stringify(result.data) : ''
});
}).catch(next);
},
... ...
... ... @@ -50,6 +50,7 @@ router.get('/exchange', exchange.exchange);
router.get('/exchange/order', exchange.order); // AJAX 获取订单 商品
router.get('/exchange/delivery', exchange.delivery); // AJAX 获取 退货方式
router.get('/mydetails', home.mydetails); // 个人信息设置
router.get('/about-us', home.aboutUs); // 个人中心 - 关于我们
... ...
<div class="personal-details" id="details">
<mydetails gender="women" birthday="1990.07.17"></mydetails>
</div>
... ...
/**
*
* @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>
... ...
/**
* 产品搜索 controller
* @author 陈轩 <xuan.chen@yoho.cn>
*/
'use strict';
const newModel = require('../models/new');
/* 最新商品 页面 */
exports.index = (req, res) => {
const view = {
module: 'product',
page: 'new'
};
res.render('new', view);
};
/* 获取 筛选配置 */
exports.fetchFilters = (req, res, next) => {
const params = Object.assign({
uid: req.user.uid,
page: 1,
order: 1,
yh_channel: 'all',
channel: 'all',
}, req.query);
newModel.getNewData(params).then(result => {
res.json(result);
}).catch(next);
};
/* 查询 产品列表 */
exports.fetchProducts = (req, res, next) => {
const params = Object.assign({
uid: req.user.uid,
page: 1,
order: 1
}, req.query);
newModel.getNewData(params).catch(next);
};
... ...
const api = global.yoho.API;
const camelCase = global.yoho.camelCase;
const prettyFilter = require(`${global.utils}/beautify/filters`);
const processProductList = require(`${global.utils}/beautify/product`);
/* 查询最新产品列表 */
exports.getNewData = (params) => {
params = Object.assign({
method: 'app.search.newProduct',
}, params);
api.post('', params, {
cache: true,
code: 200
}).then(result => {
if (result.data) {
result.data.productList = processProductList(result.data.productList);
result = camelCase(result);
}
return result;
});
};
/* 查询最新产品筛选条件 */
exports.getNewFilterData = (params) => {
params = Object.assign({
method: 'app.search.newProduct',
}, params);
api.post('', params, {
cache: true,
code: 200
}).then(result => {
let filterConfig = {};
if (result.data) {
prettyFilter(result.data.filter);
filterConfig = camelCase(result.data.filter);
}
return {
code: result.code,
data: filterConfig
};
});
};
... ...
... ... @@ -17,6 +17,8 @@ const search = require(`${cRoot}/search`);
router.get('/search', search.index);
router.get('/search.json', search.fetchProducts); // ajax
router.get('/new', search.index);
router.get('/new.json', search.fetchProducts);
// 产品 列表页
const productList = require(`${cRoot}/list`);
... ...
<div id="product-new"></div>
... ...
... ... @@ -15,8 +15,8 @@ module.exports = {
port: 6004,
siteUrl: '//m.yohoblk.com',
domains: {
api: 'http://192.168.102.205:8080/gateway/',
service: 'http://192.168.102.205:8080/gateway/'
api: 'http://devapi.yoho.cn:58078/',
service: 'http://devservice.yoho.cn:58077/'
},
subDomains: {
host: '.m.yohoblk.com',
... ...
... ... @@ -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 Mydetails = require('home/mydetails.vue');
new Vue({
el: '#details',
components: {
Mydetails
}
});
... ...
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
}
});
... ...
const Vue = require('yoho-vue');
const search = require('product/search/index.vue');
new Vue(search);
... ...
.personal-details {
width: 100%;
height: auto;
overflow: hidden;
margin-top: 20px;
background-color: #fff;
ul {
width: 95%;
height: auto;
overflow: hidden;
float: right;
li {
&:first-of-type {
height: 100px;
line-height: 100px;
}
height: 80px;
border-bottom: 1px solid #e0e0e0;
.details-icon {
float: right;
height: 90px;
margin-right: 15px;
.icon {
vertical-align: middle;
color: #b0b0b0;
}
}
.user-avatar {
display: inline-block;
width: 100%;
height: 100%;
background-image: resolve("home/user-icon.png");
background-size: 100%;
}
.head-portrait {
width: 90px;
height: 90px;
overflow: hidden;
border-radius: 50%;
border: 1px solid #eee;
vertical-align: middle;
}
> label {
width: 100%;
height: 100%;
line-height: 80px;
font-size: 32px;
margin-right: 8%;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
display: inline-block;
.details-nickname,
.details-gender,
.details-birthday {
text-align: right;
float: right;
margin-right: 40px;
color: #b0b0b0;
}
}
}
}
}
... ...
... ... @@ -2,6 +2,7 @@
@import "help";
@import "feedback";
@import "fav";
@import "details";
@import "about-us";
@import "coin";
@import "logistics";
... ...
... ... @@ -36,7 +36,7 @@
$.ajax({
url: '/channel/channel.json'
}).then(res => {
if (res.data.length) {
if (res.data && res.data.length) {
const channel = [];
res.data.forEach(c => {
... ...
... ... @@ -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;
... ...
... ... @@ -36,7 +36,7 @@
props: ['brandUrl'],
data() {
return {
nullbox : 'hide',
nullbox: 'hide',
busy: false,
editmodel: false,
page: 0,
... ... @@ -46,13 +46,13 @@
methods: {
loadMore: function() {
let _this = this;
this.busy = true;
this.busy = true;
$.ajax({
url: '/home/favorite/favpaging',
data: {
page : ++_this.page,
tab : "brand"
page: ++_this.page,
tab: 'brand'
}
}).then(result => {
if (result.isend) {
... ... @@ -62,12 +62,12 @@
}
if (result.list.length) {
result.list.forEach(function(o){
result.list.forEach(function(o) {
_this.brandData.push(o);
});
}
_this.nullbox = _this.brandData.length ? "hide" : "";
_this.nullbox = _this.brandData.length ? 'hide' : '';
}).fail(() => {
tip('网络错误');
});
... ... @@ -80,17 +80,19 @@
},
showDelBtn(index) {
this.hideDelBth();
var delBtn = $("#del-" + index);
var width = delBtn.width();
$("#li-" + index).css("transform","translateX(-" + width + "px)");
let delBtn = $('#del-' + index);
let width = delBtn.width();
$('#li-' + index).css('transform', 'translateX(-' + width + 'px)');
},
hideDelBth() {
this.brandData.forEach(function(d, index){
$("#li-" + index).css("transform","translateX(0px)");
})
this.brandData.forEach(function(d, index) {
$('#li-' + index).css('transform', 'translateX(0px)');
});
},
delItem(index, id) {
let _this = this;
$.ajax({
method: 'POST',
url: '/home/del-favdel',
... ...
... ... @@ -44,7 +44,7 @@
props: ['productUrl'],
data() {
return {
nullbox : 'hide',
nullbox: 'hide',
busy: false,
editmodel: false,
page: 0,
... ... @@ -54,12 +54,12 @@
methods: {
loadMore: function() {
let _this = this;
this.busy = true;
this.busy = true;
$.ajax({
url: '/home/favorite/favpaging',
data: {
page : ++_this.page
page: ++_this.page
}
}).then(result => {
if (result.isend) {
... ... @@ -69,12 +69,12 @@
}
if (result.list.length) {
result.list.forEach(function(o){
result.list.forEach(function(o) {
_this.productData.push(o);
});
}
_this.nullbox = _this.productData.length ? "hide" : "";
_this.nullbox = _this.productData.length ? 'hide' : '';
}).fail(() => {
tip('网络错误');
});
... ... @@ -87,17 +87,19 @@
},
showDelBtn(index) {
this.hideDelBth();
var delBtn = $("#del-" + index);
var width = delBtn.width();
$("#li-" + index).css("transform","translateX(-" + width + "px)");
let delBtn = $('#del-' + index);
let width = delBtn.width();
$('#li-' + index).css('transform', 'translateX(-' + width + 'px)');
},
hideDelBth() {
this.productData.forEach(function(d, index){
$("#li-" + index).css("transform","translateX(0px)");
})
this.productData.forEach(function(d, index) {
$('#li-' + index).css('transform', 'translateX(0px)');
});
},
delItem(index, id) {
let _this = this;
$.ajax({
method: 'POST',
url: '/home/del-favdel',
... ...
<template>
<ul>
<li>
<label>头像
<span class="details-icon">
<span class="head-portrait user-avatar" data-avatar="{{head_ico}}"></span>
<span class="icon icon-right"></span>
</span>
</label>
</li>
<li>
<label>昵称<input class="details-nickname" v-model='nickname'></label>
</li>
<li>
<label>性别<span class="details-gender">{{ gender }}</span></label>
</li>
<li>
<label>生日<span class="details-birthday">{{ birthday }}</span></label>
</li>
</ul>
</template>
<script>
module.exports = {
props: ['head_ico', 'nickname', 'gender', 'birthday'],
data() {
return {
};
}
};
</script>
... ...
... ... @@ -14,7 +14,6 @@
</template>
<script>
const $ = require('yoho-jquery');
const indexList = require('component/tool/index-list.vue');
module.exports = {
... ... @@ -31,28 +30,32 @@
},
methods: {
search: function() {
var inputname = this.inputname;
let inputname = this.inputname;
if (!inputname) {
this.showData = this.company_list;
return;
}
var filter = {};
for (var k in this.company_list) {
this.company_list[k].forEach(function(d){
let filter = {};
for (let k in this.company_list) {
this.company_list[k].forEach(function(d) {
if (d.company_name.indexOf(inputname) > -1) {
if (!filter[k]) filter[k] = [];
if (!filter[k]) {
filter[k] = [];
}
filter[k].push(d);
}
})
});
}
this.showData = filter;
},
select: function(company_id, company_name) {
select: function(companyId, companyName) {
this.$dispatch('changeView', {
view: 'logistics',
company_id: company_id,
company_name: company_name
company_id: companyId,
company_name: companyName
});
// 重置列表
... ...
... ... @@ -26,19 +26,19 @@
};
},
methods: {
companylist: function(){
companylist: function() {
this.$dispatch('changeView', {
view: 'logisticsCompany'
});
},
submit: function(){
submit: function() {
if (!this.company_name) {
tip("请选择快递公司");
tip('请选择快递公司');
return false;
}
if (!this.num || !/^[0-9]*$/.test(this.num)) {
tip("请输入正确的快递单号");
return false;
tip('请输入正确的快递单号');
return false;
}
$.ajax({
... ... @@ -50,7 +50,7 @@
num: this.num
}
}).then(function(res) {
if ($.type(res) !== 'object') {
if ($.type(res) !== 'object') {
res = {};
}
if (res.code !== 200) {
... ... @@ -67,4 +67,4 @@
}
};
</script>
\ No newline at end of file
</script>
... ...
<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>
<template v-if="productList.length">
<Sort :config="sortConfig" :val="sort">
</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>
</div>
</template>
<script>
const Vue = require('yoho-vue');
const lazyload = require('yoho-vue-lazyload');
const infinitScroll = require('yoho-vue-infinite-scroll');
const qs = require('yoho-qs');
const bus = require('common/vue-bus');
const tip = require('common/tip');
const sort = require('component/product/sort.vue');
const list = require('component/product/list.vue');
Vue.use(lazyload);
Vue.use(infinitScroll);
require('common/vue-filter');
module.exports = {
el: '#product-new',
data: function() {
return {
sortConfig: global.sortConfig,
filterConfig: global.filterConfig,
// query
url: '/product/search.json',
sort: null,
query: qs.query,
page: 0, // 未搜索 page=0; 全部加载完 page = totalPage; 无数据: page !=0 && productList.length=0
totalPage: null,
// 产品列表
productList: [],
// state
inSearching: false // 请求中
};
},
computed: {
// 无数据
empty: function() {
return this.page !== 0 && !this.productList.length;
}
},
components: {
list,
sort
},
methods: {
search: function() {
const self = this;
const nextPage = this.page + 1;
if (this.inSearching) {
return;
}
// page = 0, 始终执行搜索
if (this.page && nextPage > this.totalPage) {
return;
}
this.inSearching = true;
console.log(nextPage);
$.get(this.url, {
order: this.sort, // 排序 信息
query: this.query,
page: nextPage
})
.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));
}
})
.fail(() => {
tip('网络出错~');
})
.always(() => {
self.inSearching = false;
});
},
/**
* 清空数据(page=0) 重新搜索
*/
research: function() {
this.page = 0;
this.$set('productList', []);
this.search();
}
},
watch: {
/* sort 改变 都会触发 重新搜索 */
sort: function() {
this.research();
}
},
created: function() {
const self = this;
bus.$on('list.paging', function() {
self.search();
});
bus.$on('sort.change', function({
val
}) {
self.sort = val;
});
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>
... ...