Merge branch 'master' into hostfix/uploader

This commit is contained in:
tsxuehu
2017-04-13 10:11:50 +08:00
146 changed files with 4208 additions and 1404 deletions

View File

@@ -56,6 +56,7 @@ export default {
title: String,
cancelText: String,
overlay: {
type: Boolean,
default: true
},
closeOnClickOverlay: {
@@ -89,7 +90,9 @@ export default {
methods: {
handleItemClick(item) {
if (item.callback && typeof item.callback === 'function') {
item.callback(item);
}
}
}
};

View File

@@ -14,6 +14,12 @@
type: [Number, String],
default: 0
}
},
data() {
return {
badges: []
};
}
};
</script>

View File

@@ -1,5 +1,7 @@
<template>
<a class="zan-badge" :class="classNames" :href="url" @click="handleClick">
<a class="zan-badge" :href="url" @click="handleClick" :class="{
'zan-badge--select': isSelect
}">
<div class="zan-badge__active"></div>
<div v-if="info" class="zan-badge__info">{{info}}</div>
{{title}}
@@ -9,11 +11,8 @@
<script>
export default {
name: 'zan-badge',
props: {
mark: {
type: [Number, String],
required: true
},
title: {
type: String,
required: true
@@ -26,22 +25,26 @@ export default {
type: String
}
},
beforeCreate() {
this.$parent.badges.push(this);
},
computed: {
isSelect() {
const parent = this.$parent;
return parent.badges.indexOf(this) === parent.activeKey;
}
},
methods: {
handleClick(e) {
this.$emit('click', e, {
mark: this.mark,
title: this.title,
url: this.url,
info: this.info
});
}
},
computed: {
classNames() {
return {
'is-select': this.mark === this.$parent.activeKey
};
}
}
};
</script>

View File

@@ -11,16 +11,23 @@
* <zan-button size="large" type="primary">按钮</zan-button>
*/
const allowedSize = ['mini', 'small', 'normal', 'large'];
const allowedType = ['default', 'danger', 'primary'];
import ZanLoading from 'packages/loading';
const ALLOWED_SIZE = ['mini', 'small', 'normal', 'large'];
const ALLOWED_TYPE = ['default', 'danger', 'primary'];
export default {
name: 'zan-button',
components: {
'zan-loading': ZanLoading
},
props: {
disabled: Boolean,
loading: Boolean,
block: Boolean,
bottomAction: Boolean,
tag: {
type: String,
default: 'button'
@@ -30,27 +37,28 @@ export default {
type: String,
default: 'default',
validator(value) {
return allowedType.indexOf(value) > -1;
return ALLOWED_TYPE.indexOf(value) > -1;
}
},
size: {
type: String,
default: 'normal',
validator(value) {
return allowedSize.indexOf(value) > -1;
return ALLOWED_SIZE.indexOf(value) > -1;
}
}
},
methods: {
handleClick(e) {
if (this.loading || this.disabled) return;
this.$emit('click', e);
}
},
render(h) {
let { type, nativeType, size, disabled, loading, block } = this;
let Tag = this.tag;
const { type, nativeType, size, disabled, loading, block, bottomAction } = this;
const Tag = this.tag;
return (
<Tag
@@ -63,19 +71,20 @@ export default {
{
'zan-button--disabled': disabled,
'zan-button--loading': loading,
'zan-button--block': block
'zan-button--block': block,
'zan-button--bottom-action': bottomAction
}
]}
onClick={this.handleClick}
>
{
loading ?
<zan-loading
class="zan-button__icon-loading"
type="circle"
color={type === 'default' ? 'black' : 'white'}>
</zan-loading> :
null
loading
? <zan-loading
class="zan-button__icon-loading"
type="circle"
color={type === 'default' ? 'black' : 'white'}>
</zan-loading>
: null
}
<span class="zan-button__text">{this.$slots.default}</span>
</Tag>

View File

@@ -1,7 +1,7 @@
<template>
<div class="zan-card">
<img :src="thumb" alt="" class="zan-card__img">
<div class="zan-card__content" :class="{'is-center': !this.$slots.footer}">
<div class="zan-card__content" :class="{'zan-card__content--center': !this.$slots.footer}">
<div class="zan-card__info">
<slot name="title">
<h4 v-text="title" class="zan-card__title"></h4>

View File

@@ -1,6 +1,9 @@
<template>
<a class="zan-cell" :href="url" @click="handleClick">
<div :class="{ 'zan-cell__title': true, 'zan-cell__required': required }" v-if="this.$slots.title || title || label">
<a :class="['zan-cell', { 'zan-cell--required': required }]" :href="url" @click="handleClick">
<div
class="zan-cell__title"
v-if="this.$slots.title || title"
>
<slot name="icon">
<i v-if="icon" class="zan-icon" :class="'zan-icon-' + icon"></i>
</slot>
@@ -9,10 +12,14 @@
<span class="zan-cell__label" v-if="label" v-text="label"></span>
</slot>
</div>
<div class="zan-cell__value" :class="{
'is-link': isLink,
'is-alone': !this.$slots.title && !title && !label
}">
<div
class="zan-cell__value"
v-if="value || this.$slots.default"
:class="{
'zan-cell__value--link': isLink,
'zan-cell__value--alone': !this.$slots.title && !title && !label
}"
>
<slot>
<span v-text="value"></span>
</slot>

View File

@@ -1,5 +1,6 @@
<template>
<zan-picker
ref="picker"
:columns="columns"
:visible-item-count="visibleItemCount"
@change="handlePickerChange"
@@ -28,6 +29,10 @@ export default {
return allowedType.indexOf(value) > -1;
}
},
format: {
type: String,
default: 'yyyy.mm.dd hh时 mm分'
},
visibleItemCount: {
type: Number,
default: 5
@@ -56,31 +61,45 @@ export default {
},
data() {
let value = this.value;
if (!value) {
if (this.type.indexOf('date') > -1) {
value = this.minDate;
} else {
const minHour = this.minHour;
value = `${minHour > 10 ? minHour : '0' + minHour}:00`;
}
} else {
value = this.correctValue(value);
}
return {
innerValue: this.val
innerValue: value
};
},
watch: {
value(val) {
this.innerValue = val;
val = this.correctValue(val);
const isEqual = this.type === 'time' ? val === this.innerValue : val.valueOf() === this.innerValue.valueOf();
if (!isEqual) this.innerValue = val;
},
innerValue(val) {
console.log(val + '!!!');
this.updateColumnValue(val);
this.$emit('input', val);
}
},
computed: {
ranges() {
console.log(this.innerValue + '!!');
// return this.innerValue + '!!';
if (this.type === 'time') {
return [
[this.minHour, this.maxHour],
[0, 59]
];
}
const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = this.getBoundary('max', this.innerValue);
const { minYear, minDate, minMonth, minHour, minMinute } = this.getBoundary('min', this.innerValue);
@@ -96,7 +115,7 @@ export default {
return result;
},
columns() {
return this.ranges.map(range => {
const results = this.ranges.map(range => {
const values = this.times(range[1] - range[0] + 1, index => {
const value = range[0] + index;
return value < 10 ? `0${value}` : `${value}`;
@@ -106,10 +125,31 @@ export default {
values
};
});
return results;
}
},
methods: {
correctValue(value) {
// 仅时间
if (this.type === 'time') {
const [hour, minute] = value.split(':');
let correctedHour = Math.max(hour, this.minHour);
correctedHour = Math.min(correctedHour, this.maxHour);
return `${correctedHour}:${minute}`;
}
// 含有日期的情况
const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = this.getBoundary('max', value);
const { minYear, minDate, minMonth, minHour, minMinute } = this.getBoundary('min', value);
const minDay = new Date(minYear, minMonth - 1, minDate, minHour, minMinute);
const maxDay = new Date(maxYear, maxMonth - 1, maxDate, maxHour, maxMinute);
value = Math.max(value, minDay);
value = Math.min(value, maxDay);
return new Date(value);
},
times(n, iteratee) {
let index = -1;
const result = Array(n);
@@ -137,11 +177,11 @@ export default {
if (value.getFullYear() === year) {
month = boundary.getMonth() + 1;
if (value.getMonth() + 1 === month) {
date = value.getDate();
date = boundary.getDate();
if (value.getDate() === date) {
hour = value.getHours();
hour = boundary.getHours();
if (value.getHours() === hour) {
minute = value.getMinutes();
minute = boundary.getMinutes();
}
}
}
@@ -180,8 +220,9 @@ export default {
handlePickerConfirm(values) {
this.$emit('confirm', this.innerValue);
},
handlePickerChange(picker, values, index) {
console.log(this.innerValue);
handlePickerChange(picker) {
const values = picker.$children.filter(child => child.currentValue !== undefined).map(child => child.currentValue);
console.log(values);
let value;
if (this.type === 'time') {
@@ -200,21 +241,50 @@ export default {
}
value = new Date(year, month - 1, date, hour, minute);
}
value = this.correctValue(value);
this.innerValue = value;
console.log(value, this.innerValue);
// this.$emit('input', value);
}
},
created() {
this.innerValue = this.value;
if (!this.innerValue) {
if (this.type.indexOf('date') > -1) {
this.innerValue = this.minDate;
},
updateColumnValue(value) {
let values = [];
if (this.type === 'time') {
const currentValue = value.split(':');
values = [
currentValue[0],
currentValue[1]
];
} else {
const minHour = this.minHour;
this.innerValue = `${minHour > 10 ? minHour : '0' + minHour}:00`;
values = [
`${value.getFullYear()}`,
`0${value.getMonth() + 1}`.slice(-2),
`0${value.getDate()}`.slice(-2)
];
if (this.type === 'datetime') {
values.push(
`0${value.getHours()}`.slice(-2),
`0${value.getMinutes()}`.slice(-2)
);
}
}
this.$nextTick(() => {
this.setColumnByValues(values);
});
},
setColumnByValues(values) {
const setColumnValue = this.$refs.picker.setColumnValue;
if (this.type === 'time') {
setColumnValue(0, values[0]);
setColumnValue(1, values[1]);
} else {
setColumnValue(0, values[0]);
setColumnValue(1, values[1]);
setColumnValue(2, values[2]);
if (this.type === 'datetime') {
setColumnValue(3, values[3]);
setColumnValue(4, values[4]);
}
}
[].forEach.call(this.$refs.picker.$children, child => child.doOnValueChange());
}
}
};

View File

@@ -10,7 +10,7 @@ let dialogQueue = [];
const defaultCallback = action => {
if (currentDialog) {
let callback = currentDialog.callback;
const callback = currentDialog.callback;
if (typeof callback === 'function') {
callback(action);
@@ -40,9 +40,9 @@ const showNextDialog = () => {
if (!instance.value && dialogQueue.length > 0) {
currentDialog = dialogQueue.shift();
let options = currentDialog.options;
const options = currentDialog.options;
for (let prop in options) {
for (const prop in options) {
if (options.hasOwnProperty(prop)) {
instance[prop] = options[prop];
}

View File

@@ -68,7 +68,7 @@ export default {
if (this.lockOnScroll) {
setTimeout(() => {
if (this.modal && this.bodyOverflow !== 'hidden') {
if (this.overlay && this.bodyOverflow !== 'hidden') {
document.body.style.overflow = this.bodyOverflow;
}
this.bodyOverflow = null;

View File

@@ -53,7 +53,7 @@ export default {
type: {
type: String,
default: 'text',
validate(value) {
validator(value) {
return VALID_TYPES.indexOf(value) > -1;
}
},
@@ -71,7 +71,7 @@ export default {
autosize: {
type: Boolean,
default: false,
validate(value) {
validator(value) {
if (value && this.type !== 'textarea') return false;
}
}

View File

@@ -7,7 +7,10 @@
name: 'zan-icon',
props: {
name: String
name: {
type: String,
required: true
}
},
methods: {

View File

@@ -1,6 +1,5 @@
import Vue from 'vue';
import ImagePreview from './image-preview.vue';
import merge from 'src/utils/merge';
let instance;

View File

@@ -3,9 +3,7 @@
<div class="zan-image-preview" ref="previewContainer" v-show="value" @click="handlePreviewClick">
<zan-swipe>
<zan-swipe-item v-for="item in images">
<img class="zan-image-preview__image" :src="item" alt="" :class="{
'zan-image-preview__image--center': true
}">
<img class="zan-image-preview__image" @load="handleLoad" :src="item" alt="">
</zan-swipe-item>
</zan-swipe>
</div>
@@ -53,6 +51,22 @@ export default {
this.value = false;
},
handleLoad(event) {
const containerSize = this.$refs.previewContainer.getBoundingClientRect();
const ratio = containerSize.width / containerSize.height;
const target = event.currentTarget;
const targetRatio = target.width / target.height;
const centerClass = 'zan-image-preview__image--center';
const bigClass = 'zan-image-preview__image--big';
if (targetRatio > ratio) {
target.className += (' ' + centerClass);
} else {
target.className += (' ' + bigClass);
}
},
close() {
if (this.closing) return;

View File

@@ -1,79 +1,3 @@
export default {
install: function(Vue, options) {
options = options || { fade: false, nohori: false };
// scroll结束的时候触发scrollend事件
var timer = null;
var topValue = 0;
var bodyEle = document.body;
var scrollEnd = document.createEvent('HTMLEvents');
scrollEnd.initEvent('scrollEnd', true, false);
function enterFrame() {
if (bodyEle.scrollTop === topValue) {
window.cancelAnimationFrame(timer);
window.dispatchEvent(scrollEnd);
} else {
topValue = bodyEle.scrollTop;
}
window.requestAnimationFrame(enterFrame);
}
document.addEventListener('scroll', function() {
if (!timer) {
timer = window.requestAnimationFrame(enterFrame);
}
}, true);
// vue指令
function update(value) {
if (!value) {
return;
}
var isFadeIn = this.modifiers.fade || options.fade;
var isNoHori = this.modifiers.nohori || options.nohori;
// 用css3来控制过渡效果
if (isFadeIn) {
this.el.style.opacity = 0;
this.el.style.transition = 'opacity .3s';
this.el.style.webkitTransition = 'opacity .3s';
}
var compute = function() {
if (this.el === null) {
return;
}
var rect = this.el.getBoundingClientRect();
var vpWidth = document.head.parentNode.clientWidth;
var vpHeight = document.head.parentNode.clientHeight;
var loadImg = function() {
this.el.src = value;
this.el.addEventListener('load', onloadEnd);
window.removeEventListener('scrollEnd', compute, true);
window.removeEventListener('resize', compute, true);
}.bind(this);
if (this.el.src === value) return;
if (isNoHori) {
if (rect.bottom >= 0 && rect.top <= vpHeight) {
loadImg();
}
} else if (rect.bottom >= 0 && rect.top <= vpHeight && rect.right >= 0 && rect.left <= vpWidth) {
loadImg();
}
}.bind(this);
var onload = function() {
compute();
this.el && this.el.removeEventListener('load', onload);
window.addEventListener('scrollEnd', compute, true);
window.addEventListener('resize', compute, true);
}.bind(this);
var onloadEnd = function() {
if (this.el === null) {
return;
}
if (isFadeIn) {
this.el.style.opacity = 1;
}
this.el.removeEventListener('load', onloadEnd);
}.bind(this);
// 元素load触发事件
this.el.addEventListener('load', onload);
}
Vue.directive('lazyload', update);
}
};
import Lazyload from 'vue-lazyload';
export default Lazyload;

View File

@@ -14,14 +14,14 @@ export default {
type: {
type: String,
default: 'gradient-circle',
validate(value) {
validator(value) {
return VALID_TYPES.indexOf(value) > -1;
}
},
color: {
type: String,
default: 'black',
validate(value) {
validator(value) {
return VALID_COLORS.indexOf(value) > -1;
}
}

View File

@@ -65,23 +65,19 @@ export default {
},
watch: {
value(val) {
this.currentValue = val;
},
values(val) {
this.currentValues = val;
},
currentValues(val) {
if (this.valueIndex === -1) {
this.currentValue = (val || [])[0];
}
},
currentValue(val) {
this.doOnValueChange();
this.$emit('change', this);
this.$emit('input', val);
this.$emit('columnChange', this);
}
},
@@ -236,8 +232,6 @@ export default {
const value = this.currentValue;
const wrapper = this.$refs.wrapper;
this.$emit('input', this.currentValue);
translateUtil.translateElement(wrapper, null, this.value2Translate(value));
}
}

View File

@@ -15,7 +15,7 @@
:itemHeight="itemHeight"
:visible-item-count="visibleItemCount"
:value-key="valueKey"
@change="columnValueChange(index)">
@columnChange="columnValueChange(index)">
</picker-column>
<div class="zan-picker-center-highlight" :style="{ height: itemHeight + 'px', marginTop: -itemHeight / 2 + 'px' }"></div>
</div>

View File

@@ -2,7 +2,7 @@
<div class="zan-progress">
<div class="zan-progress__bar">
<span class="zan-progress__bar__finished-portion" :style="{backgroundColor: componentColor, width: percentage + '%'}"></span>
<span class="zan-progress__bar__pivot" :style="pivotStyle">{{currentPivotText}}</span>
<span class="zan-progress__bar__pivot" :style="pivotStyle">{{ pivotText }}</span>
</div>
</div>
</template>
@@ -21,6 +21,11 @@
* @example
* <zan-switch checked="true" disabled="false"></zan-switch>
*/
const DEFAULT_COLOR = '#38f';
const DEFAULT_TEXT_COLOR = '#fff';
const INACTIVE_COLOR = '#cacaca';
export default {
name: 'zan-progress',
@@ -28,36 +33,30 @@ export default {
percentage: {
type: Number,
required: true,
validate(value) {
validator(value) {
return value <= 100 && value >= 0;
}
},
inactive: {
type: Boolean,
default: false
},
inactive: Boolean,
pivotText: {
type: String,
default: function() {
return this.percentage.toString() + '%';
return this.percentage + '%';
}
},
color: {
type: String,
default: '#38f'
default: DEFAULT_COLOR
},
textColor: {
type: String,
default: '#fff'
default: DEFAULT_TEXT_COLOR
}
},
computed: {
currentPivotText() {
return this.pivotText ? this.pivotText : this.this.percentage.toString() + '%';
},
componentColor() {
return this.inactive ? '#cacaca' : this.color;
return this.inactive ? INACTIVE_COLOR : this.color;
},
pivotStyle() {
const pivotStyle = {

View File

@@ -7,7 +7,12 @@
'zan-quantity__minus--disabled': isMinusDisabled
}">
</button>
<input type="text" class="zan-quantity__input" :value="currentValue" @input="handleInputChange" :disabled="disabled">
<input
type="text"
class="zan-quantity__input"
:value="currentValue"
@input="handleInputChange"
:disabled="disabled">
<button
@click="handleChange('plus')"
class="zan-quantity__stepper zan-quantity__plus"
@@ -44,8 +49,14 @@ export default {
},
data() {
let value = this.value ? +this.value : +this.defaultValue;
const correctedValue = this.correctValue(value);
if (value !== correctedValue) {
value = correctedValue;
this.$emit('input', value);
}
return {
currentValue: this.value ? +this.value : +this.defaultValue
currentValue: value
};
},
@@ -66,38 +77,38 @@ export default {
watch: {
currentValue(val) {
this.$emit('input', +val);
this.$emit('input', val);
this.$emit('change', val);
},
value(val) {
if (val) {
this.currentValue = +val;
val = this.correctValue(+val);
if (val !== this.currentValue) {
this.currentValue = val;
}
}
},
methods: {
// 纠正value值
correctValue(value) {
value = Math.max(this.min, value);
value = Math.min(this.max, value);
return value;
},
handleInputChange(event) {
let val = +event.target.value;
const max = +this.max;
const min = +this.min;
const val = +event.target.value;
if (val > max) {
val = max;
} else if (val < min) {
val = min;
}
this.currentValue = val;
this.currentValue = this.correctValue(val);
},
handleChange(type) {
if ((this.isMinusDisabled && type === 'minus') || (this.isPlusDisabled && type === 'plus')) {
this.$emit('overlimit', type);
return;
}
const step = +this.step;
const currentValue = +this.currentValue;
this.currentValue = type === 'minus' ? (currentValue - step) : (currentValue + step);
this.$emit('change', this.currentValue);
}
}
};

View File

@@ -2,10 +2,17 @@
<div class="zan-search" :class="{ 'zan-search--focus' : isFocus }">
<div class="zan-search__input-wrap">
<zan-icon name="search"></zan-icon>
<input type="text" :placeholder="placeholder" v-model="value" v-refocus="focusStatus" @focus="handleFocus" @keyup.enter="handleSearch">
<zan-icon name="clear" @click="handleClean"></zan-icon>
<input
type="text"
:placeholder="placeholder"
class="zan-search__input"
v-model="value"
v-refocus="focusStatus"
@focus="handleFocus"
@keyup.enter="handleSearch">
<zan-icon name="clear" @click="handleClean" v-show="isFocus"></zan-icon>
</div>
<div class="zan-search__cancel" :class="{ 'zan-search__cancel--focus' : isFocus }" @click="handleBack">取消</div>
<div class="zan-search__cancel" v-show="isFocus" @click="handleBack">取消</div>
</div>
</template>
@@ -14,14 +21,21 @@
export default {
name: 'zan-search',
components: {
ZanIcon
},
props: {
placeholder: {
type: String
placeholder: String
},
watch: {
value(val) {
this.$emit('change', val);
}
},
data() {
return {
value: '',
@@ -29,31 +43,47 @@
isFocus: false
};
},
directives: {
refocus: {
update: function(el, state) {
if (state.value) { el.focus(); }
if (state.value) {
el.focus();
}
}
}
},
methods: {
/**
* 进入input焦点出现close和取消
*/
handleFocus() {
// 进入input焦点出现close和取消
this.isFocus = true;
},
/**
* 点击close后清空vlaue后再聚焦input框
*/
handleClean() {
// 点击close后清空vlaue后再聚焦input框
this.value = '';
this.focusStatus = true;
},
/**
* 点击取消后,清空所有回复最初状态
*/
handleBack() {
// 点击取消后,清空所有回复最初状态
this.value = '';
this.focusStatus = false;
this.isFocus = false;
this.$emit('cancel');
},
/**
* input输入回车后发送回调
*/
handleSearch() {
// input输入回车后发送回调
this.$emit('search', this.value);
}
}

View File

@@ -1,7 +1,11 @@
<template>
<div class="zan-steps" :class="`zan-steps--${steps.length}`">
<div class="zan-steps__status" v-if="icon">
<i class="zan-icon zan-steps__icon" :class="computedIconClass"></i>
<div class="zan-steps__status" v-if="title || description">
<div class="zan-steps__icon" v-if="icon || $slot.icon">
<slot name="icon">
<zan-icon :name="icon" :class="iconClass"></zan-icon>
</slot>
</div>
<div class="zan-steps__message">
<div class="zan-steps__message-wrapper">
<h4 class="zan-steps__title" v-text="title"></h4>
@@ -11,16 +15,24 @@
<slot name="message-extra">
</slot>
</div>
<div class="zan-steps__items">
<div class="zan-steps__items" :class="{
'zan-steps__items--alone': !title && !description
}">
<slot></slot>
</div>
</div>
</template>
<script>
import Icon from 'packages/icon';
export default {
name: 'zan-steps',
components: {
'zan-icon': Icon
},
props: {
active: Number,
icon: String,
@@ -32,16 +44,6 @@ export default {
description: String
},
computed: {
computedIconClass() {
const iconName = `zan-icon-${this.icon}`;
const result = this.iconClass.split(' ');
result.push(iconName);
return result;
}
},
data() {
return {
steps: []

View File

@@ -24,7 +24,6 @@ Input.prototype = Object.create(new EventEmitter());
extend(Input.prototype, {
bind: function(host) {
bindEvents(host, 'touchstart mousedown', this.onTouchStart);
if (this.options.listenMoving) {
bindEvents(window, 'touchmove mousemove', this.onTouchMove);
@@ -74,11 +73,11 @@ extend(Input.prototype, {
this.orgDirection = Math.abs(distX) > Math.abs(distY);
}
this.emit('move', {x: distX, y: distY}, isEnd, e, {orgDirection: this.orgDirection});
this.emit('move', { x: distX, y: distY }, isEnd, e, { orgDirection: this.orgDirection });
},
pointerEventToXY: function(e) {
var out = {x: 0, y: 0};
var out = { x: 0, y: 0 };
var type = e.type;
if (e.originalEvent) {
e = e.originalEvent;

View File

@@ -1,10 +1,10 @@
import { EventEmitter, extend } from './utils'
import { EventEmitter, extend } from './utils';
const setElementsStyles = (elems, styles) => {
Array.prototype.forEach.call(elems, item => {
extend(item.style, styles)
})
}
extend(item.style, styles);
});
};
function Scroll(wrapElem, options) {
EventEmitter.apply(this, arguments);
@@ -33,14 +33,14 @@ extend(Scroll.prototype, {
},
update: function() {
const oldPages = this.pages
const oldPages = this.pages;
this.pages = this.wrapElem.querySelectorAll('.zan-swipe-item');
if (oldPages && oldPages.length === this.pages.length) {
const isSame = Array.prototype.every.call(this.pages, (elem, index) => {
return this.pages[index] === oldPages[index]
})
return this.pages[index] === oldPages[index];
});
if (isSame) {
return
return;
}
}
var defaultStyle = {

View File

@@ -1,4 +1,4 @@
import { requestAnimationFrame, cancelAnimationFrame, EventEmitter, extend } from './utils'
import { requestAnimationFrame, cancelAnimationFrame, EventEmitter, extend } from './utils';
function SpringDummy(scroll, input, options) {
var wrapElem = scroll.wrapElem;
@@ -31,7 +31,6 @@ function SpringDummy(scroll, input, options) {
}).on('bounceStart', function() {
self.input.deaf();
});
}
SpringDummy.prototype = Object.create(new EventEmitter());
@@ -85,7 +84,6 @@ extend(SpringDummy.prototype, {
addition = w * (tempOffsetPage - offsetPage + this.scroll.pages.length - 1);
}
}
}
this.initTween(addition - dist, 150, 'bounce');
@@ -105,12 +103,12 @@ extend(SpringDummy.prototype, {
function round() {
elapse = new Date() - startTime;
if (elapse > duration) {
self.emit(eventName, {x: dist}, true);
self.emit(eventName, { x: dist }, true);
self.emit(eventName + 'End');
return;
}
self.emit(eventName, {x: dist / duration * elapse}, false);
self.emit(eventName, { x: dist / duration * elapse }, false);
self.tweenRid = requestAnimationFrame(round);
}
round();

View File

@@ -6,6 +6,10 @@
<script>
export default {
name: 'zan-swipe-item'
name: 'zan-swipe-item',
beforeCreate() {
this.$parent.swipes.push(this);
}
};
</script>

View File

@@ -1,6 +1,14 @@
<template>
<div class="zan-swipe">
<slot></slot>
<div class="zan-swipe__items">
<slot></slot>
</div>
<div class="zan-swipe__indicators" v-if="showIndicators">
<span class="zan-swipe__indicator" v-for="i in swipes.length" :class="{
'zan-swipe__indicator--active': currIndex === i -1
}">
</span>
</div>
</div>
</template>
@@ -13,16 +21,20 @@ export default {
name: 'zan-swipe',
props: {
autoPlay: {
autoPlay: Boolean,
showIndicators: {
type: Boolean,
default: false
},
onPageChangeEnd: {
type: Function,
default: () => {}
default: true
}
},
data() {
return {
currIndex: 0,
swipes: []
};
},
mounted() {
this.input = new Input(this.$el, {
listenMoving: true
@@ -53,6 +65,13 @@ export default {
updated() {
this.scroll.update();
},
methods: {
onPageChangeEnd(page, currIndex) {
this.currIndex = +currIndex;
this.$emit('pagechange:end', page, currIndex);
}
}
};
</script>

View File

@@ -18,11 +18,11 @@ EventEmitter.prototype = {
if (arr) {
arr.forEach(function(cb) {
cb.apply(self, argus);
})
});
}
},
removeListener: function(name, fn) {
if (this.__events[name] == undefined) {
if (!this.__events[name]) {
return;
}
let index;
@@ -50,14 +50,14 @@ const cancelAnimationFrame = window.cancelAnimationFrame || window.mozCancelAnim
};
const bindEvents = (elem, eventNames, fn) => {
eventNames = eventNames.split(/\s+/)
eventNames.forEach(eventName => elem.addEventListener(eventName, fn))
}
eventNames = eventNames.split(/\s+/);
eventNames.forEach(eventName => elem.addEventListener(eventName, fn));
};
const removeEvents = (elem, eventNames, fn) => {
eventNames = eventNames.split(/\s+/)
eventNames.forEach(eventName => elem.removeEventListener(eventName, fn))
}
eventNames = eventNames.split(/\s+/);
eventNames.forEach(eventName => elem.removeEventListener(eventName, fn));
};
export {
extend,

View File

@@ -13,12 +13,12 @@ import ZanLoading from 'packages/loading';
* zan-switch
* @module components/switch
* @desc 开关
* @param {boolean} [checked=false] - 开关状态
* @param {boolean} [value=false] - 开关状态
* @param {boolean} [disabled=false] - 禁用
* @param {boolean} [loading=false] - loading状态
*
* @example
* <zan-switch checked="true" disabled="false"></zan-switch>
* <zan-switch :checked="true" :disabled="false"></zan-switch>
*/
export default {
name: 'zan-switch',
@@ -26,23 +26,32 @@ export default {
'zan-loading': ZanLoading
},
props: {
checked: {
type: Boolean,
default: false
value: Boolean,
disabled: Boolean,
loading: Boolean,
onChange: Function
},
data() {
return {
checked: this.value
};
},
watch: {
checked(val) {
this.$emit('input', val);
},
disabled: {
type: Boolean,
default: false
},
loading: {
type: Boolean,
default: false
value(val) {
this.checked = val;
}
},
computed: {
switchStates: function() {
const switchStates = ['zan-switch--' + (this.checked ? 'on' : 'off'),
'zan-switch--' + (this.disabled ? 'disabled' : '')];
const switchStates = ['zan-switch--' + (this.checked ? 'on' : 'off')];
if (this.disabled) {
switchStates.push('zan-switch--disabled');
}
return switchStates;
}
@@ -53,7 +62,11 @@ export default {
*/
toggleState: function() {
if (this.disabled || this.loading) return;
this.$emit('change', !this.checked);
if (this.onChange) {
this.onChange(!this.checked);
} else {
this.checked = !this.checked;
}
}
}
};

View File

@@ -13,15 +13,15 @@
type: String,
required: true
},
disable: Boolean
disabled: Boolean
},
beforeCreate() {
this.$parent.tabs.push(this);
},
computed: {
classNames() {
return { 'zan-tab__pane--select': this.$parent.tabs.indexOf(this) === this.$parent.switchActiveTabKey };
return { 'zan-tab__pane--select': this.$parent.tabs.indexOf(this) === this.$parent.curActive };
}
},
created() {
this.$parent.tabs.push(this);
}
};
</script>

View File

@@ -1,18 +1,23 @@
<template>
<div class="zan-tabs">
<div class="zan-tabs__nav" :class="classNames">
<div class="zan-tabs__nav-bar" :style="navBarStyle"></div>
<div class="zan-tabs" :class="[`zan-tabs--${type}`]">
<div
class="zan-tabs__nav"
:class="[`zan-tabs__nav--${this.type}`, `zan-tabs--col-${this.tabs.length}`]"
>
<div class="zan-tabs__nav-bar" :style="navBarStyle" v-if="type === 'line'"></div>
<div
v-for="(tab, index) in tabs"
class="zan-tab"
:class="{'zan-tab--active': index == switchActiveTabKey}"
:class="{'zan-tab--active': index === curActive}"
ref="tabkey"
@click="handleTabClick(index,tab)"
@click="handleTabClick(index, tab)"
>
{{ tab.title }}
</div>
</div>
<div class="zan-tabs__content"><slot></slot></div>
<div class="zan-tabs__content">
<slot></slot>
</div>
</div>
</template>
@@ -30,11 +35,6 @@
type: {
type: String,
default: 'line'
},
// nav的wrap的样式
navclass: {
type: String,
default: ''
}
},
@@ -42,27 +42,28 @@
return {
tabs: [],
isReady: false,
switchActiveTabKey: +this.active
curActive: +this.active
};
},
watch: {
active(val) {
this.switchActiveTabKey = +val;
this.curActive = +val;
}
},
computed: {
classNames() {
return [`zan-tabs__nav--${this.type}`, `zan-tabs--col-${this.tabs.length}`, this.navclass];
},
/**
* `type`为`line`时tab下方的横线的样式
*/
navBarStyle() {
if (!this.isReady) return;
const tabKey = this.switchActiveTabKey;
if (!this.isReady || this.type !== 'line') return;
const tabKey = this.curActive;
const elem = this.$refs.tabkey[tabKey];
const offsetWidth = `${elem.offsetWidth || 0}px`;
const offsetLeft = `${elem.offsetLeft || 0}px`;
return {
width: offsetWidth,
transform: `translate3d(${offsetLeft}, 0px, 0px)`
@@ -71,12 +72,20 @@
},
methods: {
/**
* tab点击事件
*
* @param {number} index tab在tabs中的索引
* @param {Object} el tab的vue实例
*/
handleTabClick(index, el) {
if (el.disable) {
el.$emit('disable');
if (el.disabled) {
el.$emit('disabled', index);
return;
}
this.switchActiveTabKey = index;
this.$emit('click', index);
this.curActive = index;
}
},

View File

@@ -2,23 +2,15 @@ import Vue from 'vue';
import merge from 'src/utils/merge';
const ToastConstructor = Vue.extend(require('./toast.vue'));
let toastQueue = [];
let instance;
const getInstance = () => {
if (toastQueue.length > 0) {
const instance = toastQueue[0];
toastQueue.splice(0, 1);
return instance;
}
return new ToastConstructor({
if (instance) instance.clear();
instance = new ToastConstructor({
el: document.createElement('div')
});
};
const returnInstance = instance => {
if (instance) {
toastQueue.push(instance);
}
return instance;
};
const removeDom = event => {
@@ -30,8 +22,7 @@ const removeDom = event => {
var Toast = (options = {}) => {
const duration = options.duration || 3000;
let instance = getInstance();
returnInstance(instance);
const instance = getInstance();
instance.closed = false;
clearTimeout(instance.timer);
instance.type = options.type ? options.type : 'text';
@@ -77,4 +68,8 @@ Toast.fail = (options) => {
}, options));
};
Toast.clear = () => {
if (instance) instance.clear();
};
export default Toast;

View File

@@ -1,5 +1,5 @@
<template>
<transition name="zan-toast">
<transition name="zan-toast-fade">
<div class="zan-toast-wrapper" v-show="visible">
<div class="zan-toast" :class="['zan-toast--' + displayStyle]">
<!-- 只显示文字 -->
@@ -50,14 +50,14 @@ export default {
type: {
type: String,
default: 'text',
validate(value) {
validator(value) {
return TOAST_TYPES.indexOf(value) > -1;
}
},
message: {
type: String,
default: '',
validate(value) {
validator(value) {
if (this.type === 'success' || this.type === 'fail') {
return value.length <= 16;
}

View File

@@ -104,12 +104,13 @@ export default function(type) {
},
update(el) {
el[CONTEXT].scrollEventListener();
const context = el[CONTEXT];
context.scrollEventListener && context.scrollEventListener();
},
unbind(el) {
const context = el[CONTEXT];
context.scrollEventTarget.removeEventListener('scroll', context.scrollEventListener);
context.scrollEventTarget && context.scrollEventTarget.removeEventListener('scroll', context.scrollEventListener);
}
};
};

View File

@@ -1,6 +1,6 @@
{
"name": "@youzan/zanui-css",
"version": "0.0.42",
"version": "0.1.0",
"description": "zanui css.",
"main": "lib/index.css",
"style": "lib/index.css",

View File

@@ -14,7 +14,7 @@ command_exists () {
fontname() {
if command_exists superman ; then
echo "//b.yzcdn.cn$server_prefix/$(basename $basepath/../build/font/zanui-icon-*.$1)"
echo "https://b.yzcdn.cn$server_prefix/$(basename $basepath/../build/font/zanui-icon-*.$1)"
else
echo "$(abspath $basepath/../build/font/zanui-icon-*.$1)"
fi

View File

@@ -66,7 +66,8 @@ module.exports = {
{
keywords: ['chat'],
src: '客服.svg',
css: 'chat'
css: 'chat',
'correct_contour_direction': true
},
{
keywords: ['shop'],
@@ -142,6 +143,12 @@ module.exports = {
keywords: ['fail'],
src: '失败.svg',
css: 'fail'
},
{
keywords: ['contact'],
src: '联系人.svg',
css: 'contact',
'correct_contour_direction': true
}
]
};

View File

@@ -2,70 +2,76 @@
@import './mixins/border_retina.css';
@component-namespace zan {
@b badge-group {
position: relative;
width: 85px;
&::after {
@mixin border-retina (top);
}
@b badge-group {
position: relative;
width: 85px;
&::after {
@mixin border-retina (top);
}
@b badge {
}
@b badge {
display: block;
overflow: hidden;
position: relative;
padding: 20px 12px;
box-sizing: border-box;
line-height: 1.4;
background-color: $c-background;
color: $c-gray-darker;
font-size: 14px;
text-decoration: none;
word-break: break-all;
@m select {
font-weight: bold;
color: $c-black;
background-color: $c-white;
.zan-badge__active {
display: block;
overflow: hidden;
position: relative;
padding: 20px 12px;
box-sizing: border-box;
line-height: 1.4;
background-color: $c-background;
color: $c-gray-darker;
font-size: 14px;
text-decoration: none;
word-break: break-all;
@e active {
display: none;
position: absolute;
left: 0;
top: 0;
width: 3px;
height: 100%;
background-color: #FF4444;
}
@e info {
position: absolute;
top: 2px;
right: 2px;
font-size: 10px;
transform:scale(0.8);
text-align: center;
box-sizing: border-box;
padding: 0 6px;
min-width: 18px;
height: 18px;
line-height: 18px;
border-radius: 9px;
background-color: #FF4444;
color: $c-white;
}
@when select {
font-weight: bold;
color: $c-black;
background-color: $c-white;
.zan-badge__active {
display: block;
}
&::after {
@mixin border-retina (top);
@mixin border-retina (right);
@mixin border-retina (left);
}
}
&::after {
@mixin border-retina (bottom);
}
&:last-child {
&::after {
border-bottom: 0;
}
}
}
&::after {
@mixin border-retina (top);
@mixin border-retina (right);
@mixin border-retina (left);
}
}
@e active {
display: none;
position: absolute;
left: 0;
top: 0;
width: 3px;
height: 100%;
background-color: #FF4444;
}
@e info {
position: absolute;
top: 2px;
right: 2px;
font-size: 10px;
transform:scale(0.8);
text-align: center;
box-sizing: border-box;
padding: 0 6px;
min-width: 18px;
height: 18px;
line-height: 18px;
border-radius: 9px;
background-color: #FF4444;
color: $c-white;
}
&::after {
@mixin border-retina (bottom);
}
&:last-child {
&::after {
border-bottom: 0;
}
}
}
}

View File

@@ -9,7 +9,6 @@
height: 45px;
line-height: 43px;
border-radius: 4px;
border: 0;
box-sizing: border-box;
font-size: 16px;
text-align: center;
@@ -28,12 +27,11 @@
opacity: .3;
}
& + .zan-button {
margin-left: 10px;
}
& + .zan-button--block {
margin-left: 0;
@e icon-loading {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: middle;
}
@m default {
@@ -62,7 +60,7 @@
}
@m normal {
padding: 0 10px;
padding: 0 15px;
font-size: 14px;
}
@@ -105,11 +103,20 @@
}
}
@e icon-loading {
display: inline-block;
width: 16px;
height: 16px;
vertical-align: middle;
@m bottom-action {
width: 100%;
height: 50px;
line-height: 50px;
border: 0;
border-radius: 0;
background-color: $bottom-action-button-default-background-color;
color: $bottom-action-button-default-color;
font-size: 16px;
&.zan-button--primary {
background-color: $bottom-action-button-primary-background-color;
color: $bottom-action-button-primary-color;
}
}
}
}

View File

@@ -1,33 +0,0 @@
@define-mixin button-wrap {
display: inline-block;
box-sizing: border-box;
padding-right: 10px;
vertical-align: middle;
&:last-child {
padding-right: 0;
}
.zan-button {
width: 100%;
}
}
@component-namespace zan {
@b button-group {
font-size: 0;
}
@b button-1 {
@mixin button-wrap;
padding-right: 0;
width: 100%;
}
@b button-2 {
@mixin button-wrap;
width: 50%;
}
@b button-3 {
@mixin button-wrap;
width: 33.33%;
}
}

View File

@@ -15,7 +15,7 @@
@e img {
width: 90px;
height: 90px;
height: auto;
border: 0;
position: absolute;
top: 5px;
@@ -26,7 +26,7 @@
display: table;
width: 100%;
@when center {
@m center {
display: table;
height: 90px;

View File

@@ -1,5 +1,6 @@
@import './common/var.css';
@import './mixins/border_retina.css';
@import './icon.css';
@component-namespace zan {
@b cell-group {
@@ -36,16 +37,6 @@
@e title {
float: left;
&.zan-cell__required {
&::before {
content: '*';
position: absolute;
left: -7px;
font-size: 14px;
color: #f44;
}
}
}
@e label {
@@ -59,15 +50,32 @@
float: right;
overflow: hidden;
@when link {
@m link {
margin-right: 20px;
}
@when alone {
@m alone {
float: none;
}
}
@m required {
overflow: visible;
&::before {
content: '*';
position: absolute;
left: -7px;
font-size: 14px;
color: #f44;
}
.zan-cell__title {
float: none;
display: inline-block;
}
}
.zan-icon-arrow {
position: absolute;
top: 50%;

View File

@@ -32,3 +32,9 @@ $button-danger-border-color: #e33;
$button-disabled-color: $c-gray-dark;
$button-disabled-background-color: $c-gray-light;
$button-disabled-border-color: #cacaca;
$bottom-action-button-default-color: $c-white;
$bottom-action-button-default-background-color: #f85;
$bottom-action-button-primary-color: $c-white;
$bottom-action-button-primary-background-color: #f44;

View File

@@ -2,11 +2,11 @@
@font-face {
font-family: 'zan-icon';
src: url('//b.yzcdn.cn/zanui/icon/zanui-icon-d64cb2f719.eot');
src: url('//b.yzcdn.cn/zanui/icon/zanui-icon-d64cb2f719.eot?#iefix') format('embedded-opentype'),
url('//b.yzcdn.cn/zanui/icon/zanui-icon-d64cb2f719.woff2') format('woff2'),
url('//b.yzcdn.cn/zanui/icon/zanui-icon-d64cb2f719.woff') format('woff'),
url('//b.yzcdn.cn/zanui/icon/zanui-icon-d64cb2f719.ttf') format('truetype')
src: url('https://b.yzcdn.cn/zanui/icon/zanui-icon-55de83a2f0.eot');
src: url('https://b.yzcdn.cn/zanui/icon/zanui-icon-55de83a2f0.eot?#iefix') format('embedded-opentype'),
url('https://b.yzcdn.cn/zanui/icon/zanui-icon-55de83a2f0.woff2') format('woff2'),
url('https://b.yzcdn.cn/zanui/icon/zanui-icon-55de83a2f0.woff') format('woff'),
url('https://b.yzcdn.cn/zanui/icon/zanui-icon-55de83a2f0.ttf') format('truetype')
}
.zan-icon {
@@ -72,4 +72,5 @@
.zan-icon-search:before { content: '\e816'; } /* '' */
.zan-icon-clear:before { content: '\e817'; } /* '' */
.zan-icon-success:before { content: '\e818'; } /* '' */
.zan-icon-fail:before { content: '\e819'; } /* '' */
.zan-icon-fail:before { content: '\e819'; } /* '' */
.zan-icon-contact:before { content: '\e81a'; } /* '' */

View File

@@ -1,3 +1,5 @@
@import './swipe.css';
@component-namespace zan {
@b image-preview {
position: fixed;
@@ -9,18 +11,24 @@
@e image {
display: block;
width: 100%;
height: auto;
transition: .2s ease-out;
position: absolute;
left: 0;
@m center {
width: 100%;
height: auto;
top: 50%;
transform: translate3d(0, -50%, 0);
}
}
.zan-image-preview__image--big {
height: 100%;
width: auto;
left: 50%;
transform: translate3d(-50%, 0, 0);
}
.zan-swipe {
height: 100%;
}

View File

@@ -1,69 +1,61 @@
@import './common/var.css';
@import './icon.css';
@component-namespace zan {
@b search {
display: flex;
flex-direction: row;
justify-content: space-around;
align-items: center;
position: relative;
box-sizing: border-box;
padding: 4px 15px;
background-color: #F2F2F2;
@b search {
position: relative;
box-sizing: border-box;
padding: 4px 15px;
background-color: #F2F2F2;
@m focus {
.zan-search__input-wrap {
width: 82%;
}
.zan-icon-clear {
display: inline-block;
}
}
@e input-wrap {
position: relative;
width: 90%;
padding: 8px 24px 8px 35px;
border: 1px solid $c-gray-light;
border-radius: 4px;
background-color: $c-white;
input {
width: 100%;
height: 14px;
font-size: 14px;
color: $c-gray-dark;
border: none;
outline: none;
}
}
@e cancel {
display: none;
color: #44BB00;
font-size: 14px;
white-space: nowrap;
margin-left: 5px;
@m focus {
display: block;
}
}
.zan-icon-search {
color: $c-gray-darker;
position: absolute;
top: 9px;
left: 10px;
font-size: 16px;
}
.zan-icon-clear {
display: none;
position: absolute;
right: 5px;
top: 8px;
color: #888;
}
@m focus {
padding-right: 50px;
}
@e input-wrap {
position: relative;
padding: 8px 24px 8px 35px;
border: 1px solid $c-gray-light;
border-radius: 4px;
background-color: $c-white;
}
@e input {
display: block;
width: 100%;
height: 14px;
font-size: 14px;
color: $c-gray-dark;
border: none;
outline: none;
}
@e cancel {
position: absolute;
line-height: 34px;
padding: 4px 0;
top: 0;
right: 10px;
font-size: 14px;
color: $c-green;
}
.zan-icon-search {
color: $c-gray-darker;
position: absolute;
top: 8px;
left: 10px;
font-size: 16px;
line-height: 1;
}
.zan-icon-clear {
font-size: 14px;
line-height: 1;
position: absolute;
right: 5px;
top: 9px;
color: #888;
}
}
}

View File

@@ -1,5 +1,6 @@
@import './common/var.css';
@import './mixins/ellipsis.css';
@import './icon.css';
@component-namespace zan {
@b steps {
@@ -20,12 +21,15 @@
}
@e icon {
font-size: 40px;
line-height: 1;
float: left;
margin-right: 10px;
}
.zan-icon {
font-size: 40px;
line-height: 1;
}
@e message {
display: table;
height: 40px;
@@ -53,8 +57,12 @@
@e items {
margin: 0 0 10px;
overflow: hidden;
padding-bottom: 20px;
position: relative;
padding-bottom: 20px;
@m alone {
padding-top: 10px;
}
}
}

View File

@@ -1,24 +1,56 @@
@import './common/var.css';
@component-namespace zan {
@b swipe {
position: relative;
overflow: hidden;
width: 100%;
@b swipe {
position: relative;
overflow: hidden;
width: 100%;
height: 100%;
@e indicators {
position: absolute;
bottom: 10px;
left: 50%;
transform: translateX(-50%);
}
@b swipe-item {
display: none;
width: 100%;
height: 100%;
overflow: hidden;
text-align: center;
@e indicator {
width: 5px;
height: 5px;
display: inline-block;
border-radius: 100%;
background: #999;
opacity: .8;
margin: 0 3px;
z-index: 1;
img {
width: 100%;
height: auto;
}
&:first-child {
display: block;
}
@m active {
background: $c-orange;
opacity: 1;
}
}
@e items {
position: relative;
overflow: hidden;
position: relative;
height: 100%;
}
}
@b swipe-item {
display: none;
height: 100%;
width: 100%;
text-align: center;
img {
width: 100%;
height: auto;
}
&:first-child {
display: block;
}
}
}

View File

@@ -53,9 +53,7 @@
border-radius: 2px;
border: 1px solid #666666;
overflow: hidden;
.zan-tabs__nav-bar {
display: none;
}
.zan-tab {
color: #666666;
line-height: 28px;

View File

@@ -23,6 +23,7 @@
&::after {
border-color: $c-green;
}
@when plain {
color: $c-green;
}
@@ -34,6 +35,7 @@
&::after {
border-color: $button-danger-background-color;
}
@when plain {
color: $button-danger-background-color;
}
@@ -45,6 +47,7 @@
&::after {
border-color: $c-blue;
}
@when plain {
color: $c-blue;
}

View File

@@ -44,9 +44,13 @@
}
.zan-toast__text {
padding-bottom: 20px;
padding: 15px 0 20px;
font-size: 14px;
}
}
}
}
.zan-toast-fade-enter, .zan-toast-fade-leave-active {
opacity: 0;
}