Authored by Aiden Xu

商品详情

/**
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/07/19
*/
'use strict';
// const _ = require('lodash');
// const helpers = global.yoho.helpers;
/**
* 频道选择页
*/
const component = {
index: (req, res) => {
res.render('detail', {
module: 'product',
page: 'detail'
});
}
};
module.exports = component;
... ...
/**
* 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;
... ...
/**
* router of sub app product
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/07/19
*/
'use strict';
const router = require('express').Router(); // eslint-disable-line
const cRoot = './controllers';
// 商品详情controller
const detail = require(`${cRoot}/detail`);
router.get(/\/pro_([\d]+)_([\d]+)\/(.*)/, detail.index); // 商品详情routers
module.exports = router;
... ...
<div id="app" class="product-page">
<app/>
</div>
... ...
/**
* 路由分发
* @author: xuqi<qi.xu@yoho.cn>
*
* @author: Aiden Xu<aiden.xu@yoho.cn>
* @date: 2016/4/27
*/
... ... @@ -10,4 +11,6 @@ module.exports = app => {
if (app.locals.devEnv) {
app.use('/example', require('./apps/example'));
}
app.use('/product', require('./apps/product'));
};
... ...
... ... @@ -107,6 +107,8 @@ class Overlay {
overflow: 'auto'
});
}
this.settings.onClose();
}
}
... ...
const Vue = require('yoho-vue');
const app = require('product/detail.vue');
require('../common/overlay');
new Vue({
el: '#app',
components: {
app: app
}
});
... ...
@charset "utf-8";
@import "common/index";
@import "example/index";
@import "product/index";
... ...
.product-page {
background: #f6f6f6;
}
... ...
.feature-options {
display: inline-block;
}
.feature-button {
width: 88px;
height: 88px;
min-height: inherit;
min-width: inherit;
margin-right: 20px;
}
... ...
.feature-selector {
background: #FFFFFF;
width: 100%;
height: 608px;
bottom: 0;
position: fixed;
padding: 20px 30px 30px 30px;
z-index: 1001;
transform: translate3d(0, 100%, 0);
transition: all 0.1s ease-in-out;
.header {
height: 120px;
h3 {
margin: 0;
max-height: 60px;
font-weight: 300;
}
h4 {
color: #b0b0b0;
font-weight: 200;
font-size: 30px;
margin-top: 32px;
margin-bottom: 0;
}
.image-box {
width: 90px;
height: 120px;
display: inline-block;
}
.text-box {
display: inline-block;
margin-left: 24px;
max-width: 512px;
}
}
hr {
border: none;
border-top: 1px solid #F0F0F0;
margin-top: 30px;
margin-bottom: 20px;
}
ul {
list-style: none;
border: none;
margin-left: 18px;
margin-top: 30px;
margin-bottom: 0;
padding: 0;
}
li {
display: inline-block;
}
section {
h4 {
margin: 0;
font-size: 25px;
line-height: 80px;
display: inline-block;
}
}
.add-to-cart {
width: 100%;
margin-top: 50px;
font-size: 27px;
}
&.slide-in {
transform: translate3d(0, 0, 0);
}
}
... ...
<style src="./css/feature-options.css"></style>
<template>
<ul class="feature-options">
<li v-for="item in options">
<button v-bind:class="{ 'button-solid': selection === item.value}"
v-on:click="selectOption(item.value)"
class="button feature-button">
{{item.text}}
</button>
</li>
</ul>
</template>
<script>
module.exports = {
props: {
options: [Object]
},
data() {
return {
selection: ''
};
},
methods: {
selectOption: function(opt) {
this.selection = opt;
}
}
};
</script>
... ...
<style src="./css/feature-selector.css"></style>
<template>
<div class="feature-selector" v-bind:class="{ 'slide-in': isVisible }">
<div class="header">
<div class="image-box">
<img src=""/>
</div>
<div class="text-box">
<h3>{{title}}</h3>
<h4>¥{{price}}</h4>
</div>
</div>
<hr>
<div>
<section>
<h4>颜色</h4>
<feature-options :options="colors"></feature-options>
</section>
<section>
<h4>尺码</h4>
<feature-options :options="sizes"></feature-options>
</section>
<button v-on:click="addToCart(product.id)" class="button button-solid add-to-cart">加入购物袋</button>
</div>
</div>
</template>
<script>
module.exports = {
init() {
},
props: {
isVisible: Boolean
},
watch: {
isVisible() {
const self = this;
if (this.isVisible) {
this.overlay = $.overlay({
onClose: function() {
self.isVisible = false;
}
});
this.overlay.show();
} else {
this.overlay.hide();
this.$parent.$emit('featureselector.close');
}
}
},
data() {
return {
title: 'Supreme经典夹克Supreme经典夹克Supreme经典夹克Supreme经典夹克',
price: '1289.00',
colors: [
{
text: '炫酷黑',
value: '#333333'
},
{
text: '象牙白',
value: '#EFEFEF'
},
{
text: '深海蓝',
value: '#330000'
}
],
sizes: [
{
text: 'S',
value: 'S'
},
{
text: 'M',
value: 'M'
},
{
text: 'L',
value: 'L'
}
]
};
},
components: {
'feature-options': require('./feature-options.vue')
}
};
</script>
... ...
.show-box .brand {
max-height: 108px;
line-height: 48px;
overflow: hidden;
img {
vertical-align: middle;
}
h2 {
font-size: 28px;
vertical-align: middle;
margin-left: 30px;
}
a {
float: right;
margin-top: 12px;
font-size: 28px;
color: #b0b0b0;
display: inline-block;
vertical-align: middle;
}
}
i.info {
font-style: normal;
color: #b0b0b0;
margin-top: 24px;
display: block;
font-size: 18px;
}
.image-box {
background: #ffffff;
}
.title-box {
text-align: center;
margin-bottom: 50px;
max-height: 195px;
h1 {
text-align: center;
font-size: 30px;
line-height: 48px;
font-weight: normal;
max-width: 580px;
margin: 30px auto 30px auto;
}
i.price {
color: #b0b0b0;
font-size: 32px;
font-weight: lighter;
font-style: normal;
}
}
.control-box {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: stretch;
position: fixed;
width: 100%;
height: 99px;
bottom: 0;
.control-button {
min-width: 100px;
border: none;
border-top: 1px solid #CCC;
.icon {
font-size: 40px;
}
}
.control-button:first-child {
border-right: 1px solid #CCC;
}
.button-solid {
font-size: 26px;
}
}
.horizon-wrapper {
overflow-x: scroll;
}
.table {
border-collapse: collapse;
th {
background: #f6f6f6;
}
th, td {
border: 1px solid #eeeeee;
min-width: 162px;
line-height: 66px;
text-align: center;
vertical-align: middle;
}
}
... ...
.image-carousel {
width: 100%;
height: 1000px;
background-color: #EFEFEF;
}
... ...
.show-box {
margin-top: 20px;
background: #ffffff;
border-top: 1px solid #eeeeee;
border-bottom: 1px solid #eeeeee;
padding: 30px;
hr {
border: none;
border-bottom: 1px solid #eeeeee;
margin: 32px 0 20px 0;
}
h2 {
margin: 0;
font-size: 32px;
text-align: left;
color: #000000;
font-weight: normal;
display: inline-block;
+ i {
font-size: 14px;
font-style: normal;
color: #b0b0b0;
font-weight: li;
}
}
&.first-box {
margin-top: 0;
padding: 0;
}
&.last-box {
margin-bottom: 99px;
}
}
... ...
<style src="./css/detail.css"></style>
<template>
<show-box :is-first="true">
<div>
<image-carousel></image-carousel>
</div>
<div class="title-box">
<h1>{{title}}</h1>
<i class="price">¥{{price}}</i>
</div>
</show-box>
<show-box>
<div class="brand">
<img src="" width="55" height="34"/>
<h2>Brand Name</h2>
<a href="#">
进入店铺
<span class="icon icon-right"></span>
</a>
</div>
</show-box>
<show-box>
<h2>商品信息</h2>
<i>DESCRIPTION</i>
<hr>
</show-box>
<show-box>
<h2>尺码信息</h2>
<i>SIZE INFO</i>
<hr>
<i class="info">提示:左滑查看完整表格信息</i>
</show-box>
<show-box>
<h2>测量方式</h2>
<i>MEASUREMENT METHOD</i>
<hr>
</show-box>
<show-box>
<h2>模特试穿</h2>
<i>REFERENCE</i>
<hr>
<div class="horizon-wrapper">
<table class="table">
<thead>
<th>模特</th>
<th>身高</th>
<th>体重</th>
<th>三围</th>
<th>三围</th>
</thead>
<tbody>
<td>Oliver</td>
<td>175</td>
<td>51</td>
<td>78/70/87</td>
<td>78/70/87</td>
</tbody>
</table>
</div>
<i class="info">提示:左滑查看完整表格信息</i>
</show-box>
<show-box>
<h2>商品材质</h2>
<i>MATERIALS</i>
<hr>
<div>
<div class="image-box">
<img src="#" width="86" height="35"/>
</div>
<div class="text-box">
<div>涤纶</div>
<div>Terylene</div>
</div>
</div>
<p>
用各种洗涤剂,可手洗机洗,但不宜氯漂,宜阴干,避免曝晒,以免深色衣物褪色,在日光下晾晒时,将里面朝外。浸泡时间不能太长,避免褪色。
</p>
</show-box>
<show-box :is-last="true">
<h2>商品详情</h2>
<i>DETAILS</i>
</show-box>
<div class="control-box">
<button class="button control-button" style="flex: 1"><span class="icon icon-bag"></span></button>
<button class="button control-button" style="flex: 1"><span class="icon icon-love"></span></button>
<button class="button button-solid" style="flex: 2" v-on:click="showAddToCart()">加入购物袋</button>
</div>
<feature-selector :is-visible="showFeatureSelector"></feature-selector>
</template>
<script>
module.exports = {
data() {
return {
title: 'Supereme经典中长款Supereme经典中长款Supereme经典中长款',
price: '1289',
showFeatureSelector: false
};
},
components: {
'image-carousel': require('./image-carousel.vue'),
'feature-selector': require('component/product/feature-selector.vue'),
'show-box': require('./show-box.vue')
},
methods: {
showAddToCart: function() {
this.showFeatureSelector = true;
}
},
created() {
const self = this;
this.$on('featureselector.close', function() {
self.showFeatureSelector = false;
});
}
};
</script>
... ...
<style src="./css/image-carousel.css"></style>
<template>
<div class="image-carousel">
<img src="" alt="">
</div>
</template>
<script>
module.exports = {
data() {
return {
};
},
components: {
}
};
</script>
... ...
<style src="./css/show-box.css"></style>
<template>
<div class="show-box" v-bind:class="{ 'first-box': isFirst, 'last-box': isLast }">
<slot></slot>
</div>
</template>
<script>
module.exports = {
props: {
isFirst: [Boolean],
isLast: [Boolean]
},
data() {
return {
};
}
};
</script>
... ...