Merge remote-tracking branch 'main/dev' into dev

This commit is contained in:
陈嘉涵
2017-09-11 09:43:57 +08:00
22 changed files with 1981 additions and 222 deletions
+4 -1
View File
@@ -34,6 +34,7 @@ import Radio from './radio';
import RadioGroup from './radio-group';
import Row from './row';
import Search from './search';
import Sku from './sku';
import Step from './step';
import Stepper from './stepper';
import Steps from './steps';
@@ -50,7 +51,7 @@ import TreeSelect from './tree-select';
import Uploader from './uploader';
import Waterfall from './waterfall';
const version = '0.9.1';
const version = '0.9.2';
const components = [
Actionsheet,
Area,
@@ -85,6 +86,7 @@ const components = [
RadioGroup,
Row,
Search,
Sku,
Step,
Stepper,
Steps,
@@ -152,6 +154,7 @@ export {
RadioGroup,
Row,
Search,
Sku,
Step,
Stepper,
Steps,
+34
View File
@@ -0,0 +1,34 @@
<template>
<div class="van-sku-actions">
<button v-if="showAddCartBtn" class="van-sku__add-cart-btn" @click="onAddCartClicked">
加入购物车
</button>
<button class="van-sku__buy-btn" @click="onBuyClicked">
{{ buyText }}
</button>
</div>
</template>
<script>
export default {
name: 'van-sku-actions',
props: {
skuEventBus: Object,
showAddCartBtn: Boolean,
buyText: {
type: String,
default: '立即购买'
}
},
methods: {
onAddCartClicked() {
this.skuEventBus.$emit('sku:addCart');
},
onBuyClicked() {
this.skuEventBus.$emit('sku:buy');
}
}
};
</script>
+65
View File
@@ -0,0 +1,65 @@
<template>
<div class="van-sku-header">
<div class="van-sku-header__img-wrap">
<img class="van-sku__goods-img" :src="goodsImg">
</div>
<div class="van-sku-header__goods-info">
<div class="van-sku__goods-name">{{ goods.title }}</div>
<div class="van-sku__goods-price"><span class="van-sku__price-symbol"></span><span class="van-sku__price-num">{{ price }}</span></div>
<span class="van-sku__close-icon" @click="onCloseClicked"></span>
</div>
</div>
</template>
<script>
import find from 'lodash/find';
import urlHelper from 'zan-utils/url/helper';
export default {
name: 'van-sku-header',
props: {
skuEventBus: Object,
sku: Object,
selectedSku: Object,
selectedSkuComb: Object,
goods: Object
},
computed: {
skuTree() {
return this.sku.tree;
},
goodsImg() {
const s1Id = this.selectedSku.s1;
const skuImg = this.getSkuImg(s1Id);
return skuImg || this.goods.picture;
},
price() {
if (this.selectedSkuComb) {
return (this.selectedSkuComb.price / 100).toFixed(2);
}
// sku.price是一个格式化好的价格区间
return this.sku.price;
}
},
methods: {
onCloseClicked() {
this.skuEventBus.$emit('sku:close');
},
getSkuImg(id) {
if (!id) return;
// 目前skuImg都挂载在skuTree中s1那类sku上
const treeItem = find(this.skuTree, (treeItem) => treeItem.k_s === 's1') || {};
const matchedSku = find(treeItem.v, (skuValue) => skuValue.id === id);
if (matchedSku && matchedSku.imgUrl) {
return urlHelper.getCdnImageUrl(matchedSku.imgUrl);
}
}
}
};
</script>
+156
View File
@@ -0,0 +1,156 @@
<template>
<van-cell-group>
<template v-for="(message, index) in internalMessages">
<template v-if="message.type === 'image'"></template>
<van-field v-else-if="message.multiple == '1'"
:key="index"
:required="message.required == '1'"
:label="message.name"
placeholder="点击填写段落文本"
type="textarea"
v-model="messageValues[index]">
</van-field>
<van-field v-else
:key="index"
:required="message.required == '1'"
:label="message.name"
:placeholder="PLACEHOLDER_MAP[message.type]"
:type="getType(message)"
v-model="messageValues[index]">
</van-field>
</template>
</van-cell-group>
</template>
<script>
import isEmpty from 'lodash/isEmpty';
import Field from '../../field';
import CellGroup from '../../cell-group';
import validateEmail from 'zan-utils/validate/email';
import validateNumber from 'zan-utils/validate/number';
const PLACEHOLDER_MAP = {
'id_no': '输入18位身份证号码',
text: '输入文本',
tel: '输入数字',
email: '输入邮箱',
date: '点击选择日期',
time: '点击选择时间'
};
export default {
name: 'van-sku-messages',
components: {
[Field.name]: Field,
[CellGroup.name]: CellGroup
},
props: {
messages: Array
},
data() {
return {
PLACEHOLDER_MAP
};
},
// for debug
// watch: {
// messageValues() {
// console.log(this.messageValues);
// }
// },
computed: {
internalMessages() {
if (Object.prototype.toString.call(this.messages) === '[object Array]') {
return this.messages;
}
return [];
},
messageValues() {
const messageValues = [];
this.internalMessages.forEach((message, index) => {
messageValues[index] = '';
});
return messageValues;
}
},
methods: {
getType({ type, datetime }) {
if (type === 'id_no') return 'text';
return datetime > 0 ? 'datetime-local' : type;
},
getMessages() {
const messages = {};
this.messageValues.forEach((value, index) => {
if (this.internalMessages[index].datetime > 0) {
value = value.replace(/T/g, ' ');
}
messages[`message_${index}`] = value;
});
return messages;
},
getCartMessages() {
const messages = {};
this.messageValues.forEach((value, index) => {
const message = this.internalMessages[index];
if (message.datetime > 0) {
value = value.replace(/T/g, ' ');
}
messages[message.name] = value;
});
return messages;
},
validateMessages() {
const values = this.messageValues;
for (let i = 0; i < values.length; i++) {
const value = values[i];
const message = this.internalMessages[i];
if (isEmpty(value)) {
// 必填字段的校验
if (message.required == '1') { // eslint-disable-line
if (message.type === 'image') {
continue;
// return `请上传${message.name}`;
} else {
return `请填写${message.name}`;
}
}
} else {
// if (message.type == 'image') {
// loaded = _this.$el.find('#ipt-' + j).data('uploaded');
// if (loaded == 'false' || !loaded) {
// return '图片还在上传中,请稍等。。';
// }
// }
if (message.type === 'tel' && !validateNumber(value)) {
return '请填写正确的数字格式留言';
}
if (message.type === 'email' && !validateEmail(value)) {
return '请填写正确的邮箱';
}
if (message.type === 'id_no' && (value.length < 15 || value.length > 18)) {
return '请填写正确的身份证号码';
}
}
if (value.length > 200) {
return `${message.name} 写的太多了<br/>不要超过200字`;
}
}
}
}
};
</script>
+36
View File
@@ -0,0 +1,36 @@
<template>
<div class="van-sku-row">
<div class="van-sku-row__title">{{ skuRow.k }}</div>
<div class="van-sku-row__items">
<slot name="sku-row-items">
<van-sku-row-item
v-for="(skuValue, index) in skuRow.v"
:key="index"
:skuKeyStr="skuRow.k_s"
:skuValue="skuValue"
:skuEventBus="$attrs.skuEventBus"
:selectedSku="$attrs.selectedSku"
:skuList="$attrs.skuList">
</van-sku-row-item>
</slot>
</div>
</div>
</template>
<script>
import SkuRowItem from './SkuRowItem';
export default {
name: 'van-sku-row',
inheritAttrs: false,
components: {
[SkuRowItem.name]: SkuRowItem
},
props: {
skuRow: Object
}
};
</script>
+54
View File
@@ -0,0 +1,54 @@
<template>
<span v-if="isChoosable"
@click="onSkuSelected"
:class="{ 'van-sku-row__item': true, 'van-sku-row__item--active': isChoosed }">
{{ skuValue.name }}
</span>
<span v-else class="van-sku-row__item van-sku-row__item--disabled">{{ skuValue.name }}</span>
</template>
<script>
import assign from 'lodash/assign';
import filter from 'lodash/filter';
import keys from 'lodash/keys';
import every from 'lodash/every';
export default {
name: 'van-sku-row-item',
props: {
skuEventBus: Object,
skuValue: Object,
skuList: Array,
selectedSku: Object,
skuKeyStr: String
},
computed: {
isChoosed() {
return this.skuValue.id === this.selectedSku[this.skuKeyStr];
},
isChoosable() {
const matchedSku = assign({}, this.selectedSku, {
[this.skuKeyStr]: this.skuValue.id
});
const skusToCheck = filter(keys(matchedSku), skuKey => matchedSku[skuKey] !== '');
const filteredSku = filter(this.skuList, sku => {
return every(skusToCheck, function(skuKey) {
// 后端给的skuValue.id有时候是数字有时候是字符串,全等会匹配不上
return matchedSku[skuKey] == sku[skuKey]; // eslint-disable-line
});
});
const stock = filteredSku.reduce((total, sku) => (total += sku.stock_num), 0);
return stock > 0;
}
},
methods: {
onSkuSelected() {
this.skuEventBus.$emit('sku:select', assign({}, this.skuValue, { skuKeyStr: this.skuKeyStr }));
}
}
};
</script>
+100
View File
@@ -0,0 +1,100 @@
<template>
<div class="van-sku-stepper-stock">
<div class="van-sku-stepper-container">
<div class="van-sku__stepper-title">{{ stepperTitle }}</div>
<van-stepper v-model="currentNum" :min="1" :max="stepperLimit" class="van-sku__stepper" @overlimit="handleOverLimit"></van-stepper>
</div>
<div v-if="!hideStock" class="van-sku__stock">剩余{{ stock }}</div>
<div v-if="quota > 0" class="van-sku__quota">每人限购{{ quota }}</div>
</div>
</template>
<script>
import Stepper from '../../stepper';
import { LIMIT_TYPE } from '../constants';
const { QUOTA_LIMIT, STOCK_LIMIT } = LIMIT_TYPE;
export default {
name: 'van-sku-stepper',
components: {
[Stepper.name]: Stepper
},
props: {
skuEventBus: Object,
sku: Object,
selectedSku: Object,
selectedSkuComb: Object,
selectedNum: Number,
quota: Number,
quotaUsed: Number,
hideStock: {
type: Boolean,
default: false
},
stepperTitle: {
type: String,
default: '购买数量'
}
},
data() {
return {
currentNum: this.selectedNum,
// 购买限制类型: 限购/库存
limitType: STOCK_LIMIT
};
},
watch: {
currentNum(num) {
this.skuEventBus.$emit('sku:numChange', num);
},
stepperLimit(limit) {
if (limit < this.currentNum) {
this.currentNum = limit;
}
}
},
computed: {
stock() {
if (this.selectedSkuComb) {
return this.selectedSkuComb.stock_num;
}
return this.sku.stock_num;
},
stepperLimit() {
const quotaLimit = this.quota - this.quotaUsed;
let limit;
// 无限购时直接取库存,有限购时取限购数和库存数中小的那个
if (this.quota > 0 && quotaLimit <= this.stock) {
// 修正负的limit
limit = quotaLimit < 0 ? 0 : quotaLimit;
this.limitType = QUOTA_LIMIT;
} else {
limit = this.stock;
}
return limit;
}
},
methods: {
setCurrentNum(num) {
this.currentNum = num;
},
handleOverLimit(action) {
this.skuEventBus.$emit('sku:overLimit', {
action,
limitType: this.limitType,
quota: this.quota,
quotaUsed: this.quotaUsed
});
}
}
};
</script>
+4
View File
@@ -0,0 +1,4 @@
export const LIMIT_TYPE = {
QUOTA_LIMIT: 0,
STOCK_LIMIT: 1
};
+302
View File
@@ -0,0 +1,302 @@
<template>
<van-popup v-model="show" position="bottom" :lock-scroll="true">
<div class="van-sku-container">
<div class="van-sku-layout">
<slot name="sku-header" :skuEventBus="skuEventBus" :selectedSku="selectedSku" :selectedSkuComb="selectedSkuComb">
<van-sku-header
:skuEventBus="skuEventBus"
:selectedSku="selectedSku"
:selectedSkuComb="selectedSkuComb"
:goods="goods"
:sku="sku">
</van-sku-header>
</slot>
<div class="van-sku-body scroller" :style="bodyStyle">
<slot name="sku-group" :selectedSku="selectedSku">
<div v-if="hasSku" class="van-sku-group-container">
<div v-for="(skutreeItem, index) in skuTree"
class="van-sku-row-group"
:key="index">
<van-sku-row
v-bind="$attrs"
:skuEventBus="skuEventBus"
:skuRow="skutreeItem"
:skuList="sku.list"
:selectedSku="selectedSku">
</van-sku-row>
</div>
</div>
</slot>
<slot name="extra-sku-group" :skuEventBus="skuEventBus"></slot>
<slot name="sku-stepper" :skuEventBus="skuEventBus" :selectedSku="selectedSku" :selectedSkuComb="selectedSkuComb" :selectedNum="selectedNum">
<van-sku-stepper
ref="skuStepper"
:skuEventBus="skuEventBus"
:selectedSku="selectedSku"
:selectedSkuComb="selectedSkuComb"
:selectedNum="selectedNum"
:stepperTitle="stepperTitle"
:sku="sku"
:quota="quota"
:quotaUsed="quotaUsed"
:hideStock="hideStock">
</van-sku-stepper>
</slot>
<slot name="sku-messages">
<van-sku-messages
ref="skuMessages"
:messages="sku.messages">
</van-sku-messages>
</slot>
<slot name="sku-actions" :skuEventBus="skuEventBus">
<van-sku-actions
:skuEventBus="skuEventBus"
:buyText="buyText"
:showAddCartBtn="showAddCartBtn">
</van-sku-actions>
</slot>
</div>
</div>
</div>
</van-popup>
</template>
<script>
import Vue from 'vue';
import Popup from '../../popup';
import Toast from '../../toast';
import SkuHeader from '../components/SkuHeader';
import SkuRow from '../components/SkuRow';
import SkuStepper from '../components/SkuStepper';
import SkuMessages from '../components/SkuMessages';
import SkuActions from '../components/SkuActions';
import { isAllSelected, getSkuComb, getSelectedSkuValues } from '../utils/skuHelper';
import { LIMIT_TYPE } from '../constants';
const { QUOTA_LIMIT } = LIMIT_TYPE;
export default {
name: 'van-sku',
components: {
[Popup.name]: Popup,
[SkuHeader.name]: SkuHeader,
[SkuRow.name]: SkuRow,
[SkuStepper.name]: SkuStepper,
[SkuMessages.name]: SkuMessages,
[SkuActions.name]: SkuActions
},
props: {
goods: Object,
initialSku: {
type: Object,
default() {
return {};
}
},
sku: Object,
quota: {
type: Number,
default: 0
},
quotaUsed: {
type: Number,
default: 0
},
hideStock: {
type: Boolean,
default: false
},
showAddCartBtn: {
type: Boolean,
default: true
},
buyText: String,
stepperTitle: {
type: String,
default: '购买数量'
},
bodyOffsetTop: {
type: Number,
default: 150
},
resetStepperOnHide: Boolean,
value: Boolean
},
data() {
return {
selectedSku: {},
selectedNum: 1,
show: this.value
};
},
watch: {
show(val) {
this.$emit('input', val);
if (!val) {
const selectedSkuValues = getSelectedSkuValues(this.sku.tree, this.selectedSku);
this.$emit('sku-close', {
selectedSkuValues,
selectedNum: this.selectedNum,
selectedSkuComb: this.selectedSkuComb
});
if (this.resetStepperOnHide) {
this.$refs.skuStepper && this.$refs.skuStepper.setCurrentNum(1);
}
}
},
value(val) {
this.show = val;
},
skuTree(val) {
this.resetSelectedSku(val);
}
},
computed: {
bodyStyle() {
const windowHeight = window.innerHeight;
// header高度大概82px
const maxHeight = windowHeight - this.bodyOffsetTop;
return {
maxHeight: maxHeight + 'px'
};
},
isSkuCombSelected() {
return isAllSelected(this.sku.tree, this.selectedSku);
},
hasSku() {
return !this.sku.none_sku;
},
selectedSkuComb() {
if (!this.hasSku) {
return {
id: this.sku.collection_id,
price: Math.round(this.sku.price * 100),
'stock_num': this.sku.stock_num
};
} else if (this.isSkuCombSelected) {
return getSkuComb(this.sku.list, this.selectedSku);
}
return null;
},
skuTree() {
return this.sku.tree || [];
}
},
created() {
var skuEventBus = new Vue();
this.skuEventBus = skuEventBus;
skuEventBus.$on('sku:close', this.handleCloseClicked);
skuEventBus.$on('sku:select', this.handleSkuSelected);
skuEventBus.$on('sku:numChange', this.handleNumChange);
skuEventBus.$on('sku:overLimit', this.handleOverLimit);
skuEventBus.$on('sku:addCart', this.handleAddCartClicked);
skuEventBus.$on('sku:buy', this.handleBuyClicked);
this.resetSelectedSku(this.skuTree);
// 组件初始化后的钩子,抛出skuEventBus
this.$emit('after-sku-create', skuEventBus);
},
methods: {
resetSelectedSku(skuTree) {
this.selectedSku = {};
skuTree.forEach((item) => {
// 只有一个sku规格值时默认选中
if (item.v.length === 1) {
this.selectedSku[item.k_s] = item.v[0].id;
} else {
this.selectedSku[item.k_s] = this.initialSku[item.k_s] || '';
}
});
},
getSkuMessages() {
return this.$refs.skuMessages
? this.$refs.skuMessages.getMessages()
: {};
},
getSkuCartMessages() {
return this.$refs.skuMessages
? this.$refs.skuMessages.getCartMessages()
: {};
},
validateSkuMessages() {
return this.$refs.skuMessages
? this.$refs.skuMessages.validateMessages()
: '';
},
validateSku() {
if (this.selectedNum === 0) {
return '商品已经无法购买啦';
}
if (this.isSkuCombSelected) {
const error = this.validateSkuMessages();
// sku留言没有错误则校验通过
return error ? error : '';
} else {
return '请选择完整的规格';
}
},
handleCloseClicked() {
this.show = false;
},
handleSkuSelected(skuValue) {
// 点击已选中的sku时则取消选中
this.selectedSku = this.selectedSku[skuValue.skuKeyStr] === skuValue.id
? { ...this.selectedSku, [skuValue.skuKeyStr]: '' }
: { ...this.selectedSku, [skuValue.skuKeyStr]: skuValue.id };
this.$emit('sku-selected', {
skuValue,
selectedSku: this.selectedSku,
selectedSkuComb: this.selectedSkuComb
});
},
handleNumChange(num) {
this.selectedNum = num;
},
handleOverLimit({ action, limitType, quota, quotaUsed }) {
if (action === 'minus') {
Toast('至少选择一件');
} else if (action === 'plus') {
if (limitType === QUOTA_LIMIT) {
let msg = `限购${quota}`;
if (quotaUsed > 0) msg += `,您已购买${quotaUsed}`;
Toast(msg);
} else {
Toast('库存不足');
}
}
},
handleAddCartClicked() {
this.handleBuyOrAddCart('add-cart');
},
handleBuyClicked() {
this.handleBuyOrAddCart('buy-clicked');
},
handleBuyOrAddCart(type) {
const error = this.validateSku();
if (error) {
Toast(error);
return;
}
this.$emit(type, {
selectedNum: this.selectedNum,
selectedSkuComb: this.selectedSkuComb,
messages: this.getSkuMessages(),
cartMessages: this.getSkuCartMessages()
});
}
}
};
</script>
+18
View File
@@ -0,0 +1,18 @@
import SkuActions from './components/SkuActions';
import SkuHeader from './components/SkuHeader';
import SkuMessages from './components/SkuMessages';
import SkuStepper from './components/SkuStepper';
import SkuRow from './components/SkuRow';
import SkuRowItem from './components/SkuRowItem';
import skuHelper from './utils/skuHelper';
import SkuContainer from './containers/SkuContainer';
SkuContainer.SkuActions = SkuActions;
SkuContainer.SkuHeader = SkuHeader;
SkuContainer.SkuMessages = SkuMessages;
SkuContainer.SkuStepper = SkuStepper;
SkuContainer.SkuRow = SkuRow;
SkuContainer.SkuRowItem = SkuRowItem;
SkuContainer.skuHelper = skuHelper;
export default SkuContainer;
+95
View File
@@ -0,0 +1,95 @@
import isArray from 'lodash/isArray';
import keys from 'lodash/keys';
import filter from 'lodash/filter';
import find from 'lodash/find';
import isObject from 'lodash/isObject';
import every from 'lodash/every';
/*
normalize sku tree
[
{
count: 2,
k: "品种", // 规格名称 skuKeyName
k_id: "1200", // skuKeyId
k_s: "s1" // skuKeyStr
v: [ // skuValues
{ // skuValue
id: "1201", // skuValueId
name: "萌" // 具体的规格值 skuValueName
}, {
id: "973",
name: "帅"
}
]
},
...
]
|
v
{
s1: [{
id: "1201",
name: "萌"
}, {
id: "973",
name: "帅"
}],
...
}
*/
export const normalizeSkuTree = (skuTree) => {
const normalizedTree = {};
skuTree.forEach(treeItem => {
normalizedTree[treeItem.k_s] = treeItem.v;
});
return normalizedTree;
};
// 判断是否所有的sku都已经选中
export const isAllSelected = (skuTree, selectedSku) => {
if (!isArray(skuTree)) {
throw new Error('skuTree must be array');
}
if (!isObject(selectedSku)) {
throw new Error('selectedSku must be object');
}
const selected = filter(keys(selectedSku), skuKeyStr => selectedSku[skuKeyStr] !== '');
return skuTree.length === selected.length;
};
// 根据已选择的sku获取skuComb
export const getSkuComb = (skuList, selectedSku) => {
const skuComb = find(skuList, (skuComb) => {
return every(keys(selectedSku), skuKeyStr => {
// 后端给的key有时数字有时字符串,需要兼容=。=
return skuComb[skuKeyStr] == selectedSku[skuKeyStr]; // eslint-disable-line
});
});
return skuComb;
};
// 获取已选择的sku名称
export const getSelectedSkuValues = (skuTree, selectedSku) => {
const normalizedTree = normalizeSkuTree(skuTree);
return keys(selectedSku).reduce((selectedValues, skuKeyStr) => {
const skuValues = normalizedTree[skuKeyStr];
const skuValueId = selectedSku[skuKeyStr];
if (skuValueId) {
const skuValue = find(skuValues, skuValue => skuValue.id === skuValueId);
skuValue && selectedValues.push(skuValue);
}
return selectedValues;
}, []);
};
const SkuHelper = {
normalizeSkuTree,
isAllSelected,
getSkuComb,
getSelectedSkuValues
};
export default SkuHelper;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "vant-css",
"version": "0.9.1",
"version": "0.9.2",
"description": "vant css.",
"main": "lib/index.css",
"style": "lib/index.css",
+1
View File
@@ -52,3 +52,4 @@
@import './coupon-list.css';
@import './goods-action.css';
@import './submit-bar.css';
@import './sku.css';
+202
View File
@@ -0,0 +1,202 @@
@import './mixins/border_retina.css';
.van-sku-container {
background: #f8f8f8;
}
.van-sku-layout {
background: #fff;
}
.van-sku-body {
max-height: 350px;
overflow-y: scroll;
&::-webkit-scrollbar {
display: none;
}
}
.van-sku-group-container {
position: relative;
padding-top: 12px;
padding-bottom: 2px;
margin-left: 15px;
&::after {
@mixin border-retina (bottom);
}
}
.van-sku-row-group {
margin-right: 15px;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
/*
sku header
*/
.van-sku-header {
position: relative;
margin-left: 15px;
&::after {
@mixin border-retina (bottom);
}
}
.van-sku-header__img-wrap {
position: relative;
float: left;
margin-top: -10px;
width: 80px;
height: 80px;
background: #fafafa;
border-radius: 2px;
.van-sku__goods-img {
position: absolute;
margin: auto;
top: 0;
left: 0;
right: 0;
bottom: 0;
max-width: 100%;
max-height: 100%;
}
}
.van-sku-header__goods-info {
padding: 10px 60px 10px 10px;
min-height: 62px;
overflow: hidden;
}
.van-sku__goods-name {
font-size: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.van-sku__price-symbol {
font-size: 14px;
vertical-align: middle;
}
.van-sku__price-num {
vertical-align: middle;
}
.van-sku__goods-price {
margin-top: 10px;
font-size: 16px;
color: #f44;
vertical-align: middle;
}
.van-sku__close-icon {
position: absolute;
top: 0;
right: 0;
width: 44px;
height: 44px;
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACwAAAAsCAMAAAApWqozAAAAUVBMVEUAAACfn5+bm5uampqZmZmoqKiampqampqZmZmampqampqbm5ubm5uZmZmqqqqampqampqZmZmampqampqampqbm5ubm5uampqdnZ2ampqZmZkjZXmqAAAAGnRSTlMAFYBC5wm+rdPv1Ckl8wzZxKWicm9eUjoN8nSyS9UAAAFVSURBVDjLvZXbrsIgEEWhtAUU6M1a3f//oSeHWKfuVhv7IC+TkJVhz4UZ9bsThq5uYmzqbgifSV04LI4r9Fs0FRHw/WhCugUz9h6IRdpmryVgzfLGWKC8brEF0E58ObVAsUIrC1y2fFwAW9GdhTfb6oyHZQ3+/C7us39VcgXE79o3sIgylayXdZdpIaJVH08rQnTE9BmeEPXsWMK1rV6m01Xz/dO1g5H3nH6yNcoZNnCPPoOXTjplemYlnR4h2wG9YppZ1WPItsOomGZWjeiyrWEU08wqgzrbBkERfWJWBTTZRiTFNLMqIWZ7x43atQYa+lA33EmG5KyB0yyDApT8/uvWFCCnTvIrtKRuLgqzQktRpNzMCi3l5kaymZXqiGS3blGX2Zl2ftGi6+avqtcsUvN/8a2++LCHRsH+kDkwvg4Mxt2Re3yY85pIsiaOLKD91faz8wcoUxux/aS9awAAAABJRU5ErkJggg==);
background-size: 22px 22px;
background-repeat: no-repeat;
background-position: 7px 10px;
}
/*
sku row
*/
.van-sku-row__title {
font-size: 14px;
padding-bottom: 10px;
}
.van-sku-row__item {
display: inline-block;
padding: 5px 9px;
margin-right: 10px;
margin-bottom: 10px;
height: 16px;
min-width: 32px;
line-height: 16px;
font-size: 12px;
color: #333;
text-align: center;
border: 1px solid #999;
border-radius: 3px;
&.van-sku-row__item--active {
border-color: #f44;
color: #fff;
background: #f44;
}
&.van-sku-row__item--disabled {
background: #efefef;
border-color: #e5e5e5;
color: #cacaca;
}
}
/*
sku stepper
*/
.van-sku-stepper-stock {
padding: 12px 0;
margin-left: 15px;
}
.van-sku-stepper-container {
height: 30px;
margin-right: 20px;
}
.van-sku__stepper-title {
float: left;
line-height: 30px;
font-size: 14px;
}
.van-sku__stepper {
float: right;
top: 7px;
left: 4px;
}
.van-sku__stock {
display: inline-block;
margin-right: 10px;
color: #999;
font-size: 12px;
}
.van-sku__quota {
display: inline-block;
color: #f44;
font-size: 12px;
}
/*
sku actions
*/
.van-sku-actions {
margin-top: 10px;
font-size: 0;
}
.van-sku__add-cart-btn {
width: 50%;
height: 50px;
padding: 0;
border: 0;
background: #f85;
color: #fff;
font-size: 16px;
line-height: 50px;
text-align: center;
& + .van-sku__buy-btn {
width: 50%;
}
}
.van-sku__buy-btn {
width: 100%;
height: 50px;
padding: 0;
border: 0;
background: #f44;
color: #fff;
font-size: 16px;
line-height: 50px;
text-align: center;
}