Authored by ccbikai(👎🏻🍜)

Merge branch 'master' into develop

@@ -31,10 +31,10 @@ const component = { @@ -31,10 +31,10 @@ const component = {
31 }); 31 });
32 }, 32 },
33 product(req, res, next) { 33 product(req, res, next) {
34 - const pid = req.params[0];// , goodsId = req.params[1]; 34 + const pid = req.params[0]; // , goodsId = req.params[1];
35 35
36 let params = { 36 let params = {
37 - productId: _.toString(pid), 37 + product_id: _.toString(pid),
38 uid: req.user.uid || 0 38 uid: req.user.uid || 0
39 }; 39 };
40 40
@@ -52,6 +52,17 @@ const component = { @@ -52,6 +52,17 @@ const component = {
52 res.json(result); 52 res.json(result);
53 }).catch(next); 53 }).catch(next);
54 }, 54 },
  55 + brand(req, res, next) {
  56 + const brandId = req.params[0];
  57 +
  58 + model.brand({
  59 + uid: req.user.uid || 0,
  60 + brand_id: brandId,
  61 + gender: req.query.gender || '1,2,3'
  62 + }).then(result => {
  63 + res.json(result);
  64 + }).catch(next);
  65 + },
55 66
56 /** 67 /**
57 * 加入购物车接口 68 * 加入购物车接口
@@ -72,7 +83,9 @@ const component = { @@ -72,7 +83,9 @@ const component = {
72 model.addToCart(params).then(result => { 83 model.addToCart(params).then(result => {
73 if (result.code === 200) { 84 if (result.code === 200) {
74 // 将 shopping_key 写入Cookie 85 // 将 shopping_key 写入Cookie
75 - res.cookie('_SPK', result.data.shopping_key, {maxAge: 86400 * 360}); 86 + res.cookie('_SPK', result.data.shopping_key, {
  87 + maxAge: 86400 * 360
  88 + });
76 } 89 }
77 res.json(result); 90 res.json(result);
78 }).catch(next); 91 }).catch(next);
@@ -6,6 +6,7 @@ @@ -6,6 +6,7 @@
6 'use strict'; 6 'use strict';
7 7
8 const api = global.yoho.API; 8 const api = global.yoho.API;
  9 +const camelCase = global.yoho.camelCase;
9 10
10 /** 11 /**
11 * 商品详情 12 * 商品详情
@@ -13,31 +14,38 @@ const api = global.yoho.API; @@ -13,31 +14,38 @@ const api = global.yoho.API;
13 const model = { 14 const model = {
14 product(params) { 15 product(params) {
15 return api.get('', Object.assign({ 16 return api.get('', Object.assign({
16 - method: 'h5.product.data'  
17 - }, params))  
18 - .then(result => {  
19 - // result.storage 的 数值 不可靠,循环累加 库存  
20 - let storage = 0; 17 + method: 'app.product.data'
  18 + }, params), {
  19 + code: 200
  20 + }).then(result => {
  21 + if (result.data) {
  22 + let storage = 0; // result.storage 的 数值 不可靠,循环累加 库存
  23 + const goodsList = result.data.goods_list;
21 24
22 - if (result.goodsList) {  
23 - for (let good of result.goodsList) {  
24 - for (let size of good.goodsSizeBoList) {  
25 - storage += size.goodsSizeStorageNum; 25 + if (goodsList) {
  26 + for (let good of goodsList) {
  27 + for (let size of good.size_list) {
  28 + storage += size.storage_number;
26 } 29 }
27 } 30 }
28 } 31 }
29 32
30 - result.storage = storage; 33 + result.data.storage = storage;
  34 + }
  35 +
31 return result; 36 return result;
32 - }); 37 + }).then(camelCase);
33 }, 38 },
34 -  
35 -  
36 intro(params) { 39 intro(params) {
37 return api.get('', Object.assign({ 40 return api.get('', Object.assign({
38 method: 'h5.product.intro' 41 method: 'h5.product.intro'
39 }, params)); 42 }, params));
40 }, 43 },
  44 + brand(params) {
  45 + return api.get('', Object.assign({
  46 + method: 'app.shop.queryShopsByBrandId'
  47 + }, params)).then(camelCase);
  48 + },
41 49
42 /** 50 /**
43 * 加入购物车接口 51 * 加入购物车接口
@@ -46,6 +46,7 @@ const detail = require(`${cRoot}/detail`); @@ -46,6 +46,7 @@ const detail = require(`${cRoot}/detail`);
46 router.get(/\/product\/pro_([\d]+)_([\d]+)\/(.*).html/, detail.index); // 商品详情routers 46 router.get(/\/product\/pro_([\d]+)_([\d]+)\/(.*).html/, detail.index); // 商品详情routers
47 router.get(/\/product\/product_([\d]+)\.json/, detail.product); 47 router.get(/\/product\/product_([\d]+)\.json/, detail.product);
48 router.get(/\/product\/intro_([\d]+)\.json/, detail.intro); 48 router.get(/\/product\/intro_([\d]+)\.json/, detail.intro);
  49 +router.get(/\/product\/brand_([\d]+)\.json/, detail.brand);
49 router.post(/\product\/cart.json/, detail.addToCart); 50 router.post(/\product\/cart.json/, detail.addToCart);
50 router.post(/\product\/favorite.json/, auth, detail.favorite); 51 router.post(/\product\/favorite.json/, auth, detail.favorite);
51 router.get(/\/product\/cart-count.json/, detail.getCartCount); 52 router.get(/\/product\/cart-count.json/, detail.getCartCount);
@@ -33,7 +33,7 @@ const getImgUrl = function(src, width = 300, height = 300, mode = 2) { @@ -33,7 +33,7 @@ const getImgUrl = function(src, width = 300, height = 300, mode = 2) {
33 }; 33 };
34 34
35 return dict[$0]; 35 return dict[$0];
36 - }).replace('http:', '') + '/interlace/1' : ''; 36 + }).replace(/https?:/, '') + '/interlace/1' : '';
37 }; 37 };
38 38
39 // 退换货 申请 成功, 打开 modal 39 // 退换货 申请 成功, 打开 modal
1 /* 1 /*
2 -* @Author: Targaryen  
3 -* @Date: 2016-08-02 17:29:52  
4 -* @Last Modified by: Targaryen  
5 -* @Last Modified time: 2016-08-02 17:42:26  
6 -*/ 2 + * @Author: Targaryen
  3 + * @Date: 2016-08-02 17:29:52
  4 + * @Last Modified by: Targaryen
  5 + * @Last Modified time: 2016-08-02 17:42:26
  6 + */
7 7
8 module.exports = (Vue) => { 8 module.exports = (Vue) => {
9 Vue.directive('lazy-html', function(html) { 9 Vue.directive('lazy-html', function(html) {
10 // TODO 首屏幕不使用 10 // TODO 首屏幕不使用
11 - html = html.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, '<img v-bind:src="\'$1\'">'); 11 + html = html.replace(/<img [^>]*src=['"]([^'"]+)[^>]*>/gi, '<img v-bind:src="\'$1\'">').replace(/https?:/gi, '');
12 this.el.innerHTML = html; 12 this.el.innerHTML = html;
13 this.vm.$compile(this.el); 13 this.vm.$compile(this.el);
14 }); 14 });
@@ -2,14 +2,19 @@ const Vue = require('vue'); @@ -2,14 +2,19 @@ const Vue = require('vue');
2 const lazyload = require('vue-lazyload'); 2 const lazyload = require('vue-lazyload');
3 const directive = require('common/vue-directive'); 3 const directive = require('common/vue-directive');
4 const app = require('editorial/detail.vue'); 4 const app = require('editorial/detail.vue');
  5 +const yoho = require('yoho');
5 6
6 require('common/vue-filter')(Vue); 7 require('common/vue-filter')(Vue);
7 -Vue.use(lazyload, { preLoad: 3 }); 8 +Vue.use(lazyload, {
  9 + preLoad: 3
  10 +});
8 Vue.use(directive); 11 Vue.use(directive);
9 12
10 -new Vue({ 13 +yoho.ready(() => {
  14 + new Vue({
11 el: '#app', 15 el: '#app',
12 components: { 16 components: {
13 app 17 app
14 } 18 }
  19 + });
15 }); 20 });
1 <template> 1 <template>
2 <div class="cate-page" id='cate-page'> 2 <div class="cate-page" id='cate-page'>
3 <div class="cate-container clearfix"> 3 <div class="cate-container clearfix">
4 - <div class="content" style="height: 522px;"> 4 + <div class="content">
5 <ul class="primary-level"> 5 <ul class="primary-level">
6 <li v-for="(index, ca) in cateNavLeftData" class="ellipsis" :class="{focus: index === leftcurrent}" class="p-level-item" @click='cateNavLeftFun(index, ca.relationParameter.sort, ca.categoryName)'> 6 <li v-for="(index, ca) in cateNavLeftData" class="ellipsis" :class="{focus: index === leftcurrent}" class="p-level-item" @click='cateNavLeftFun(index, ca.relationParameter.sort, ca.categoryName)'>
7 {{ca.categoryName}} 7 {{ca.categoryName}}
@@ -213,6 +213,8 @@ @@ -213,6 +213,8 @@
213 } 213 }
214 </style> 214 </style>
215 <script> 215 <script>
  216 + const $ = require('jquery');
  217 +
216 module.exports = { 218 module.exports = {
217 props: { 219 props: {
218 category: { 220 category: {
@@ -275,6 +277,16 @@ @@ -275,6 +277,16 @@
275 }, 277 },
276 created() { 278 created() {
277 this.categoryChangeHandler(); 279 this.categoryChangeHandler();
  280 +
  281 + window.addEventListener('touchstart', () => {
  282 + const c = $('.content');
  283 + const h = c.height();
  284 + const h1 = document.body.offsetHeight;
  285 +
  286 + if (h <= h1) {
  287 + c.css('height', h1);
  288 + }
  289 + });
278 } 290 }
279 }; 291 };
280 </script> 292 </script>
@@ -6,7 +6,7 @@ @@ -6,7 +6,7 @@
6 </div> 6 </div>
7 <div class="text-box"> 7 <div class="text-box">
8 <h3 class="line-clamp-2">{{entity.productName}}</h3> 8 <h3 class="line-clamp-2">{{entity.productName}}</h3>
9 - <h4>{{entity.productPriceBo && entity.productPriceBo.formatSalesPrice}}</h4> 9 + <h4>{{entity.formatSalesPrice !== '0' ? entity.formatSalesPrice : entity.formatMarketPrice}}</h4>
10 </div> 10 </div>
11 </div> 11 </div>
12 12
@@ -239,13 +239,13 @@ @@ -239,13 +239,13 @@
239 thumbnails[goods.colorId] = goods.colorImage; 239 thumbnails[goods.colorId] = goods.colorImage;
240 240
241 // 生成colorId 与 size的 映射 241 // 生成colorId 与 size的 映射
242 - colorSizes[goods.colorId] = goods.goodsSizeBoList.map((size)=> { 242 + colorSizes[goods.colorId] = goods.sizeList.map((size)=> {
243 if (!stocks[goods.colorId]) { 243 if (!stocks[goods.colorId]) {
244 stocks[goods.colorId] = 0; 244 stocks[goods.colorId] = 0;
245 } 245 }
246 246
247 // 默认选中有库存的第一个颜色尺码 247 // 默认选中有库存的第一个颜色尺码
248 - if (size.goodsSizeStorageNum > 0) { 248 + if (size.storageNumber > 0) {
249 if (!selection.color) { 249 if (!selection.color) {
250 self.selection.color = selection.color = { 250 self.selection.color = selection.color = {
251 text: goods.colorName, 251 text: goods.colorName,
@@ -254,24 +254,24 @@ @@ -254,24 +254,24 @@
254 }; 254 };
255 } 255 }
256 256
257 - if (!selection.size && size.goodsSizeStorageNum > 0) { 257 + if (!selection.size && size.storageNumber > 0) {
258 self.selection.size = selection.size = { 258 self.selection.size = selection.size = {
259 text: size.sizeName, 259 text: size.sizeName,
260 - value: size.goodsSizeSkuId,  
261 - goodsId: size.goodsId, 260 + value: size.productSku,
  261 + goodsId: goods.goodsId,
262 disabled: false 262 disabled: false
263 }; 263 };
264 264
265 } 265 }
266 } 266 }
267 // 计算所有尺码的库存 267 // 计算所有尺码的库存
268 - stocks[goods.colorId] += size.goodsSizeStorageNum; 268 + stocks[goods.colorId] += size.storageNumber;
269 269
270 return { 270 return {
271 text: size.sizeName, 271 text: size.sizeName,
272 - value: size.goodsSizeSkuId,  
273 - disabled: size.goodsSizeStorageNum === 0,  
274 - goodsId: size.goodsId 272 + value: size.productSku,
  273 + disabled: size.storageNumber === 0,
  274 + goodsId: goods.goodsId
275 }; 275 };
276 }); 276 });
277 277
@@ -286,14 +286,19 @@ @@ -286,14 +286,19 @@
286 this.selection.color = selection.color = this.colors[0]; 286 this.selection.color = selection.color = this.colors[0];
287 } 287 }
288 288
289 - this.sizes = colorSizes[selection.color.value];  
290 this.colorSizes = colorSizes; 289 this.colorSizes = colorSizes;
291 this.thumbnails = thumbnails; 290 this.thumbnails = thumbnails;
292 291
293 // 选择默认值 292 // 选择默认值
  293 + if (selection.color) {
  294 + this.sizes = colorSizes[selection.color.value];
294 this.$emit('feature:color.select', selection.color); 295 this.$emit('feature:color.select', selection.color);
  296 + }
  297 +
  298 + if (selection.size) {
295 this.$emit('feature:size.select', selection.size); 299 this.$emit('feature:size.select', selection.size);
296 } 300 }
  301 + }
297 }, 302 },
298 components: { 303 components: {
299 featureOptions: require('./feature-options.vue') 304 featureOptions: require('./feature-options.vue')
@@ -378,7 +383,7 @@ @@ -378,7 +383,7 @@
378 return; 383 return;
379 } 384 }
380 385
381 - const param = Object.assign({goodsId: this.entity.id}, this.selection); 386 + const param = Object.assign({goodsId: this.entity.productId}, this.selection);
382 387
383 this.onAddToCart(param, this); 388 this.onAddToCart(param, this);
384 }, 389 },
@@ -159,9 +159,8 @@ @@ -159,9 +159,8 @@
159 // 查询 商品 feature 159 // 查询 商品 feature
160 queryProductFeature(pid) { 160 queryProductFeature(pid) {
161 $.get(`/product/product_${pid}.json`).then(result => { 161 $.get(`/product/product_${pid}.json`).then(result => {
162 - this.entity = result; 162 + this.entity = result.data || {};
163 this.showFeatureSelector = true; 163 this.showFeatureSelector = true;
164 - return result;  
165 }); 164 });
166 }, 165 },
167 166
@@ -4,25 +4,25 @@ @@ -4,25 +4,25 @@
4 <image-carousel :goods="entity.goodsList"></image-carousel> 4 <image-carousel :goods="entity.goodsList"></image-carousel>
5 <div class="title-box"> 5 <div class="title-box">
6 <h1 class="line-clamp-2">{{entity.productName}}</h1> 6 <h1 class="line-clamp-2">{{entity.productName}}</h1>
7 - <i class="price" v-if="entity.productPriceBo.marketPrice > entity.productPriceBo.salesPrice"  
8 - :class="{'strike-through': entity.productPriceBo.salesPrice > 0}">{{entity.productPriceBo.formatMarketPrice}}</i> 7 + <i class="price" v-if="entity.marketPrice > entity.salesPrice"
  8 + :class="{'strike-through': entity.salesPrice > 0}">{{entity.formatMarketPrice}}</i>
9 9
10 - <i v-if="entity.productPriceBo.salesPrice > 0"  
11 - :class="{price: true, highlight: entity.productPriceBo.marketPrice > entity.productPriceBo.salesPrice}">  
12 - {{entity.productPriceBo.formatSalesPrice}} 10 + <i v-if="entity.salesPrice > 0"
  11 + :class="{price: true, highlight: entity.marketPrice > entity.salesPrice}">
  12 + {{entity.formatSalesPrice !== '0' ? entity.formatSalesPrice : entity.formatMarketPrice}}
13 </i> 13 </i>
14 </div> 14 </div>
15 </show-box> 15 </show-box>
16 16
17 - <show-box class="brand" v-if="entity.brand">  
18 - <img :src="entity.brand.brandIco | resize 110 68"/> 17 + <show-box class="brand" v-if="brand">
  18 + <img :src="brand.brandIco | resize 110 68"/>
19 19
20 - <h2>{{entity.brand.brandName}}</h2> 20 + <h2>{{brand.brandName}}</h2>
21 <div class="brand-go"> 21 <div class="brand-go">
22 <span>进入店铺</span> 22 <span>进入店铺</span>
23 <span class="icon icon-right"></span> 23 <span class="icon icon-right"></span>
24 </div> 24 </div>
25 - <a :href="entity.brand.brandDomain | brandUrl"></a> 25 + <a :href="brand.brandDomain | brandUrl"></a>
26 </show-box> 26 </show-box>
27 27
28 <show-box v-if="intro.productDescBo"> 28 <show-box v-if="intro.productDescBo">
@@ -125,7 +125,7 @@ @@ -125,7 +125,7 @@
125 <ul v-for="item in intro.productMaterialList"> 125 <ul v-for="item in intro.productMaterialList">
126 <div> 126 <div>
127 <div class="image-box"> 127 <div class="image-box">
128 - <img :src="item.imageUrl" width="86" height="35"/> 128 + <img :src="item.imageUrl | resize 86 35" width="86" height="35"/>
129 </div> 129 </div>
130 <div class="text-box"> 130 <div class="text-box">
131 <div>{{item.caption}}</div> 131 <div>{{item.caption}}</div>
@@ -153,10 +153,10 @@ @@ -153,10 +153,10 @@
153 <h2>商品详情</h2> 153 <h2>商品详情</h2>
154 <i>DETAILS</i> 154 <i>DETAILS</i>
155 155
156 - <p v-if="entity.brand && entity.brand.brandIntro" v-lazy-html="entity.brand.brandIntro"> 156 + <p v-if="brand && brand.brandIntro" v-lazy-html="brand.brandIntro">
157 </p> 157 </p>
158 158
159 - <p v-if="entity.brand && intro.productIntroBo" v-lazy-html="intro.productIntroBo.productIntro"> 159 + <p v-if="brand && intro.productIntroBo" v-lazy-html="intro.productIntroBo.productIntro">
160 </p> 160 </p>
161 161
162 </show-box> 162 </show-box>
@@ -412,11 +412,8 @@ @@ -412,11 +412,8 @@
412 yoho: yoho, 412 yoho: yoho,
413 intro: {}, 413 intro: {},
414 firstImage: '', 414 firstImage: '',
  415 + brand: null,
415 entity: { 416 entity: {
416 - brand: {  
417 - brandName: '',  
418 - brandIco: ''  
419 - },  
420 productPriceBo: { 417 productPriceBo: {
421 formatMarketPrice: '' 418 formatMarketPrice: ''
422 } 419 }
@@ -480,7 +477,7 @@ @@ -480,7 +477,7 @@
480 toggleFavorite: function() { 477 toggleFavorite: function() {
481 $.post('/product/favorite.json', { 478 $.post('/product/favorite.json', {
482 operation: this.entity.isCollect === 'Y' ? 'remove' : 'add', 479 operation: this.entity.isCollect === 'Y' ? 'remove' : 'add',
483 - id: this.entity.productPriceBo.productId 480 + id: this.entity.productId
484 }).then((result)=> { 481 }).then((result)=> {
485 if (result.code === 200) { 482 if (result.code === 200) {
486 tip(this.entity.isCollect === 'Y' ? '取消收藏成功' : '收藏成功'); 483 tip(this.entity.isCollect === 'Y' ? '取消收藏成功' : '收藏成功');
@@ -516,9 +513,10 @@ @@ -516,9 +513,10 @@
516 513
517 // 读取基础数据 514 // 读取基础数据
518 $.get(`/product/product_${pid}.json`).then((result) => { 515 $.get(`/product/product_${pid}.json`).then((result) => {
519 - // TODO: 异常处理  
520 - this.entity = result;  
521 - 516 + if (!result.data) {
  517 + return;
  518 + }
  519 + this.entity = result.data;
522 if (this.entity.storage === 0 || this.entity.status === 0) { 520 if (this.entity.storage === 0 || this.entity.status === 0) {
523 this.isSoldOut = true; 521 this.isSoldOut = true;
524 } 522 }
@@ -546,12 +544,23 @@ @@ -546,12 +544,23 @@
546 }) 544 })
547 }); 545 });
548 546
549 - return result;  
550 - }).then((result)=> { 547 + return result.data;
  548 + }).then((data)=> {
  549 + if (data) {
551 // 读取商品详情 550 // 读取商品详情
552 - $.get(`/product/product/intro_${pid}.json`, {skn: result.productPriceBo.productSkn}).then(intro => { 551 + $.get(`/product/product/intro_${pid}.json`, {skn: data.productSkn}).then(intro => {
553 this.intro = intro; 552 this.intro = intro;
  553 + if (this.intro.sizeImage) {
  554 + this.intro.sizeImage = this.intro.sizeImage.replace(/https?:/, '');
  555 + }
554 }); 556 });
  557 +
  558 + if (data.brandInfo && data.brandInfo.brandId) {
  559 + $.get(`/product/product/brand_${data.brandInfo.brandId}.json`).then(brand => {
  560 + this.brand = brand.data && brand.data.length ? brand.data[0] : {};
  561 + });
  562 + }
  563 + }
555 }) 564 })
556 .always(() => { 565 .always(() => {
557 this.isReady = true; 566 this.isReady = true;
@@ -64,7 +64,7 @@ module.exports = (list, options) => { @@ -64,7 +64,7 @@ module.exports = (list, options) => {
64 64
65 _.forEach(list, (product) => { 65 _.forEach(list, (product) => {
66 // 商品信息有问题,则不显示 66 // 商品信息有问题,则不显示
67 - if (!product.productId || !product.goodsList || !product.goodsList.length) { 67 + if (!product || !product.productId || !product.goodsList || !product.goodsList.length) {
68 return; 68 return;
69 } 69 }
70 70