Merge branch 'master' of gitlab.qima-inc.com:fe/zanui-vue

This commit is contained in:
taiyong 2017-03-21 14:14:29 +08:00
commit 5120356351
81 changed files with 2061 additions and 461 deletions

View File

@ -23,7 +23,8 @@ module.exports = {
document: false,
navigator: false,
window: false,
require: true
require: true,
FileReader: true
},
rules: {

View File

@ -31,6 +31,9 @@
"actionsheet": "./packages/actionsheet/index.js",
"quantity": "./packages/quantity/index.js",
"progress": "./packages/progress/index.js",
"toast": "./packages/toast/index.js",
"uploader": "./packages/uploader/index.js",
"swipe": "./packages/swipe/index.js",
"swipe-item": "./packages/swipe-item/index.js"
"swipe-item": "./packages/swipe-item/index.js",
"datetime-picker": "./packages/datetime-picker/index.js"
}

View File

@ -4,6 +4,9 @@
<i class="zan-icon zan-icon-arrow"></i>
</router-link>
<router-view></router-view>
<div class="footer">
<img src="https://b.yzcdn.cn/v2/image/wap/zanui-logo.png" alt="logo" class="zanui-logo">
</div>
</div>
</template>
@ -43,8 +46,16 @@ export default {
text-decoration: none;
}
body, html {
height: 100%;
}
.examples-container {
padding-bottom: 30px;
padding-bottom: 10px;
height: 100%;
overflow: auto;
background: #f9fafb;
position: relative;
}
.page-back {
@ -76,4 +87,17 @@ export default {
font-size: 16px;
padding: 10px 15px;
}
.footer {
margin-top: 30px;
width: 100%;
padding-bottom: 15px;
}
.zanui-logo {
display: block;
margin: 0 auto;
width: 150px;
height: auto;
}
</style>

2
docs/build/0.js vendored

File diff suppressed because one or more lines are too long

2
docs/build/1.js vendored
View File

@ -1 +1 @@
webpackJsonp([1],{250:function(t,e,a){a(455);var i=a(0)(a(264),a(401),null,null);t.exports=i.exports},264:function(t,e,a){"use strict";function i(t){return t&&t.__esModule?t:{default:t}}Object.defineProperty(e,"__esModule",{value:!0});var n=a(28),s=i(n);e.default={data:function(){return{highlights:[],navState:[],data:s.default["zh-CN"],base:"/component"}}}},309:function(t,e,a){e=t.exports=a(16)(),e.push([t.i,".side-nav{width:100%;box-sizing:border-box;padding:40px 20px;background:#f9fafb}.side-nav li{list-style:none}.side-nav ul{padding:0;margin:0;overflow:hidden}.side-nav .nav-item a{font-size:16px;color:#5e6d82;line-height:40px;height:40px;margin:0;padding:0;text-decoration:none;display:block;position:relative;-webkit-transition:all .3s;transition:all .3s}.side-nav .nav-item a.active{color:#20a0ff}.side-nav .nav-item .nav-item a{display:block;height:40px;line-height:40px;font-size:13px;padding-left:24px}.side-nav .nav-item .nav-item a:hover{color:#20a0ff}.side-nav .nav-group__title{font-size:12px;color:#99a9bf;padding-left:8px;line-height:26px;margin-top:10px}",""])},401:function(t,e){t.exports={render:function(){var t=this,e=t.$createElement,a=t._self._c||e;return a("div",{staticClass:"side-nav"},[a("ul",t._l(t.data,function(e){return a("li",{staticClass:"nav-item"},[e.path?a("router-link",{attrs:{"active-class":"active",to:t.base+e.path,exact:""},domProps:{textContent:t._s(e.title||e.name)}}):a("a",[t._v(t._s(e.name))]),t._v(" "),e.children?a("ul",{staticClass:"pure-menu-list sub-nav"},t._l(e.children,function(e){return a("li",{staticClass:"nav-item"},[a("router-link",{attrs:{"active-class":"active",to:t.base+e.path},domProps:{textContent:t._s(e.title||e.name)}})],1)})):t._e(),t._v(" "),e.groups?t._l(e.groups,function(e){return a("div",{staticClass:"nav-group"},[a("div",{staticClass:"nav-group__title"},[t._v(t._s(e.groupName))]),t._v(" "),a("ul",{staticClass:"pure-menu-list"},[t._l(e.list,function(e){return[e.disabled?t._e():a("li",{staticClass:"nav-item"},[a("router-link",{attrs:{"active-class":"active",to:t.base+e.path},domProps:{textContent:t._s(e.title)}})],1)]})],2)])}):t._e()],2)}))])},staticRenderFns:[]}},455:function(t,e,a){var i=a(309);"string"==typeof i&&(i=[[t.i,i,""]]),i.locals&&(t.exports=i.locals);a(29)("1517d9c0",i,!0)}});
webpackJsonp([1],{259:function(e,t,n){n(490);var i=n(0)(n(273),n(431),null,null);e.exports=i.exports},273:function(e,t,n){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}Object.defineProperty(t,"__esModule",{value:!0});var o=n(30),a=i(o),s=n(372),r=i(s);t.default={data:function(){return{highlights:[],navState:[],data:a.default["zh-CN"],base:"/component"}},components:{MobileNav:r.default}}},275:function(e,t,n){"use strict";Object.defineProperty(t,"__esModule",{value:!0}),t.default={props:{group:{type:Object,default:function(){return[]}},base:String},data:function(){return{isOpen:!1}}}},325:function(e,t,n){t=e.exports=n(18)(),t.push([e.i,".side-nav{width:100%;box-sizing:border-box;padding:90px 15px 20px;position:relative;z-index:1}.side-nav .zanui-desc,.side-nav .zanui-title{text-align:center;font-weight:400}.side-nav .zanui-title{font-size:26px;color:#333}.side-nav .zanui-desc{font-size:14px;color:#666;margin-bottom:50px}",""])},341:function(e,t,n){t=e.exports=n(18)(),t.push([e.i,".mobile-nav-group{border-radius:2px;margin-bottom:15px;padding-left:20px;background-color:#fff;box-shadow:0 1px 1px 0 rgba(0,0,0,.1)}.mobile-nav-group li{list-style:none}.mobile-nav-group ul{padding:0;margin:0;overflow:hidden}.mobile-nav-group__title{font-size:16px;color:#333;line-height:56px;position:relative}.mobile-nav-group__title a{color:#333;display:block;border-top:1px solid #e5e5e5}.mobile-nav-group__title .zan-icon-arrow{position:absolute;font-size:12px;line-height:1;top:24px;right:20px}.mobile-nav-group__title--open{color:#999}",""])},372:function(e,t,n){n(506);var i=n(0)(n(275),n(450),null,null);e.exports=i.exports},431:function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"side-nav"},[n("h1",{staticClass:"zanui-title"},[e._v("Zan UI Wap")]),e._v(" "),n("h2",{staticClass:"zanui-desc"},[e._v("有赞移动wap端组件库")]),e._v(" "),n("div",{staticClass:"mobile-navs"},[e._l(e.data,function(t){return[t.showInMobile?n("div",{staticClass:"mobile-nav-item"},e._l(t.groups,function(t){return n("mobile-nav",{attrs:{group:t,base:e.base}})})):e._e()]})],2)])},staticRenderFns:[]}},450:function(e,t){e.exports={render:function(){var e=this,t=e.$createElement,n=e._self._c||t;return n("div",{staticClass:"mobile-nav-group"},[n("div",{staticClass:"mobile-nav-group__title",class:{"mobile-nav-group__title--open":e.isOpen},on:{click:function(t){e.isOpen=!e.isOpen}}},[e._v("\n "+e._s(e.group.groupName)+"\n ")]),e._v(" "),n("ul",{directives:[{name:"show",rawName:"v-show",value:e.isOpen,expression:"isOpen"}],staticClass:"pure-menu-list"},[e._l(e.group.list,function(t){return[t.disabled?e._e():n("li",{staticClass:"mobile-nav-group__title"},[n("router-link",{attrs:{"active-class":"active",to:e.base+t.path},domProps:{textContent:e._s(t.title)}}),e._v(" "),n("zan-icon",{attrs:{name:"arrow"}})],1)]})],2)])},staticRenderFns:[]}},490:function(e,t,n){var i=n(325);"string"==typeof i&&(i=[[e.i,i,""]]),i.locals&&(e.exports=i.locals);n(31)("1517d9c0",i,!0)},506:function(e,t,n){var i=n(341);"string"==typeof i&&(i=[[e.i,i,""]]),i.locals&&(e.exports=i.locals);n(31)("8bcdd7d6",i,!0)}});

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,49 +1,20 @@
<template>
<div class="side-nav">
<ul>
<li class="nav-item" v-for="item in data">
<a v-if="!item.path">{{item.name}}</a>
<router-link
v-else
active-class="active"
:to="base + item.path"
exact
v-text="item.title || item.name">
</router-link>
<ul class="pure-menu-list sub-nav" v-if="item.children">
<li class="nav-item" v-for="navItem in item.children">
<router-link
active-class="active"
:to="base + navItem.path"
v-text="navItem.title || navItem.name">
</router-link>
</li>
</ul>
<template v-if="item.groups">
<div class="nav-group" v-for="group in item.groups">
<div class="nav-group__title">{{group.groupName}}</div>
<ul class="pure-menu-list">
<template v-for="navItem in group.list">
<li
class="nav-item"
v-if="!navItem.disabled">
<router-link
active-class="active"
:to="base + navItem.path"
v-text="navItem.title">
</router-link>
</li>
</template>
</ul>
</div>
</template>
</li>
</ul>
<h1 class="zanui-title">Zan UI Wap</h1>
<h2 class="zanui-desc">有赞移动wap端组件库</h2>
<div class="mobile-navs">
<template v-for="item in data">
<div class="mobile-nav-item" v-if="item.showInMobile">
<mobile-nav v-for="group in item.groups" :group="group" :base="base"></mobile-nav>
</div>
</template>
</div>
</div>
</template>
<script>
import navConfig from '../nav.config.json';
import MobileNav from './mobile-nav';
export default {
data() {
@ -53,6 +24,10 @@ export default {
data: navConfig['zh-CN'],
base: '/component'
};
},
components: {
MobileNav
}
};
</script>
@ -61,56 +36,25 @@ export default {
.side-nav {
width: 100%;
box-sizing: border-box;
padding: 40px 20px;
background: #f9fafb;
padding: 90px 15px 20px;
position: relative;
z-index: 1;
li {
list-style: none;
}
ul {
padding: 0;
margin: 0;
overflow: hidden;
.zanui-title,
.zanui-desc {
text-align: center;
font-weight: normal;
}
.nav-item {
a {
font-size: 16px;
color: #5e6d82;
line-height: 40px;
height: 40px;
margin: 0;
padding: 0;
text-decoration: none;
display: block;
position: relative;
transition: all .3s;
&.active {
color: #20a0ff;
}
}
.nav-item {
a {
display: block;
height: 40px;
line-height: 40px;
font-size: 13px;
padding-left: 24px;
&:hover {
color: #20a0ff;
}
}
}
.zanui-title {
font-size: 26px;
color: #333;
}
.nav-group__title {
font-size: 12px;
color: #99a9bf;
padding-left: 8px;
line-height: 26px;
margin-top: 10px;
.zanui-desc {
font-size: 14px;
color: #666;
margin-bottom: 50px;
}
}
</style>

View File

@ -0,0 +1,94 @@
<template>
<div class="mobile-nav-group">
<div
class="mobile-nav-group__title"
:class="{
'mobile-nav-group__title--open': isOpen
}"
@click="isOpen = !isOpen">
{{group.groupName}}
</div>
<ul class="pure-menu-list" v-show="isOpen">
<template v-for="navItem in group.list">
<li
class="mobile-nav-group__title"
v-if="!navItem.disabled">
<router-link
active-class="active"
:to="base + navItem.path"
v-text="navItem.title">
</router-link>
<zan-icon name="arrow"></zan-icon>
</li>
</template>
</ul>
</div>
</template>
<script>
export default {
props: {
group: {
type: Object,
default: () => {
return [];
}
},
base: String
},
data() {
return {
isOpen: false
};
}
};
</script>
<style>
@component-namespace mobile {
@b nav-group {
border-radius: 2px;
margin-bottom: 15px;
padding-left: 20px;
background-color: #fff;
box-shadow: 0 1px 1px 0 rgba(0,0,0,0.10);
@e title {
font-size: 16px;
color: #333;
line-height: 56px;
position: relative;
@m open {
color: #999;
}
a {
color: #333;
display: block;
border-top: 1px solid #e5e5e5;
}
.zan-icon-arrow {
position: absolute;
font-size: 12px;
line-height: 1;
top: 24px;
right: 20px;
}
}
li {
list-style: none;
}
ul {
padding: 0;
margin: 0;
overflow: hidden;
}
}
}
</style>

View File

@ -33,13 +33,19 @@ export default {
<style>
.mobile-popup {
width: 380px;
height: 500px;
border: 5px solid #e5e5e5;
width: 375px;
height: 650px;
background: url(https://b.yzcdn.cn/v2/image/wap/zanui-mobile-container.png) no-repeat;
}
.mobile-popup-iframe {
width: 100%;
height: 500px;
box-sizing: border-box;
border-left: 1px solid #e5e5e5;
border-right: 1px solid #e5e5e5;
border-bottom: 1px solid #e5e5e5;
position: relative;
top: 64px;
height: 586px;
}
</style>

View File

@ -1,21 +1,30 @@
<template><section class="demo-icon"><h1 class="demo-title">icon</h1><example-block title="所有Icon">
<zan-icon name="album"></zan-icon>
<zan-icon name="arrow"></zan-icon>
<zan-icon name="camera"></zan-icon>
<zan-icon name="certificate"></zan-icon>
<zan-icon name="qr-invalid"></zan-icon>
<zan-icon name="qr"></zan-icon>
<zan-icon name="exchange"></zan-icon>
<zan-icon name="close"></zan-icon>
<zan-icon name="location"></zan-icon>
<zan-icon name="upgrade"></zan-icon>
<zan-icon name="check"></zan-icon>
<zan-icon name="checked"></zan-icon>
<zan-icon name="close"></zan-icon>
<zan-icon name="like-o"></zan-icon>
<zan-icon name="like"></zan-icon>
<zan-icon name="chat"></zan-icon>
<zan-icon name="shop"></zan-icon>
<zan-icon name="photograph"></zan-icon>
<zan-icon name="add"></zan-icon>
<zan-icon name="add2"></zan-icon>
<zan-icon name="photo"></zan-icon>
<zan-icon name="logistics"></zan-icon>
<zan-icon name="edit"></zan-icon>
<zan-icon name="passed"></zan-icon>
<zan-icon name="cart"></zan-icon>
<zan-icon name="arrow"></zan-icon>
<zan-icon name="gift"></zan-icon>
<zan-icon name="home"></zan-icon>
<zan-icon name="location"></zan-icon>
<zan-icon name="message"></zan-icon>
<zan-icon name="send"></zan-icon>
<zan-icon name="shopping-cart"></zan-icon>
<zan-icon name="sign"></zan-icon>
<zan-icon name="store"></zan-icon>
<zan-icon name="topay"></zan-icon>
<zan-icon name="tosend"></zan-icon>
<zan-icon name="search"></zan-icon>
<zan-icon name="clear"></zan-icon>
<zan-icon name="success"></zan-icon>
<zan-icon name="fail"></zan-icon>
</example-block></section></template>
<style>

View File

@ -1,4 +1,30 @@
<template><section class="demo-image-preview"><h1 class="demo-title">image-preview</h1></section></template>
<template><section class="demo-image-preview"><h1 class="demo-title">image-preview</h1><example-block title="">
<zan-button @click="handleImagePreview">预览图片</zan-button>
</example-block></section></template>
<style>
@component-namespace demo {
@b image-preview {
.zan-button {
margin-left: 15px;
}
}
}
</style>
<script>
import Vue from "vue";import ExampleBlock from "../components/example-block";Vue.component("example-block", ExampleBlock);</script>
import Vue from "vue";import ExampleBlock from "../components/example-block";Vue.component("example-block", ExampleBlock);
import { ImagePreview } from 'src/index';
export default {
methods: {
handleImagePreview() {
ImagePreview([
'https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp',
'https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg?imageView2/2/w/980/h/980/q/75/format/webp'
]);
}
}
};
</script>

View File

@ -7,7 +7,7 @@
</zan-popup>
<div class="zan-row">
<zan-button @click="popupShow2 = true">从上方弹出popup</zan-button>
<zan-button @click="popupShow2 = true">从上方弹出popup</zan-button>
</div>
<zan-popup v-model="popupShow2" position="top" class="zan-popup-2" :overlay="false">
更新成功
@ -55,7 +55,10 @@
.zan-popup-4 {
width: 60%;
height: 200px;
box-sizing: border-box;
padding: 20px;
border-radius: 5px;
text-align: center;
}
.zan-button {

View File

@ -3,9 +3,19 @@
<zan-swipe-item>
<img src="https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
<zan-swipe-item>
<img src="https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
</zan-swipe>
</example-block><example-block title="自动轮播">
<zan-swipe :auto-play="true">
<zan-swipe-item>
<img src="https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
<zan-swipe-item>
<img src="https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
</zan-swipe>
</example-block></section></template>
@ -14,6 +24,10 @@
@b swipe {
.zan-swipe {
height: 200px;
img {
width: 100%;
}
}
}
}

View File

@ -1,6 +1,6 @@
<template><section class="demo-switch"><h1 class="demo-title">switch</h1><example-block title="基础用法">
<div class="demo-switch__wrapper">
<zan-switch class="some-customized-class" :checked="switchState" :on-change="updateState"></zan-switch>
<zan-switch class="some-customized-class" :checked="switchState" @change="updateState"></zan-switch>
<div class="demo-switch__text">{{switchStateText}}</div>
</div>
<div class="demo-switch__wrapper">
@ -41,4 +41,22 @@
}
</style>
<script>
import Vue from "vue";import ExampleBlock from "../components/example-block";Vue.component("example-block", ExampleBlock);</script>
import Vue from "vue";import ExampleBlock from "../components/example-block";Vue.component("example-block", ExampleBlock);
export default {
data() {
return {
switchState: true
};
},
computed: {
switchStateText() {
return this.switchState ? ' ON' : 'OFF';
}
},
methods: {
updateState(newState) {
this.switchState = newState;
}
}
};
</script>

View File

@ -38,6 +38,15 @@
</example-block></section></template>
<style>
@component-namespace demo {
@b tab {
.zan-tabs-pane {
background-color: #fff;
padding: 20px;
}
}
}
</style><style>
.page-tab {
padding: 0 15px;
}

View File

@ -0,0 +1,56 @@
<template><section class="demo-toast"><h1 class="demo-title">toast</h1><example-block title="基础用法">
<zan-button @click="showSimpleToast">普通文字提示</zan-button>
<zan-button @click="showLoadingToast">加载Toast</zan-button>
<zan-button @click="showSuccessToast">成功</zan-button>
<zan-button @click="showFailToast">失败</zan-button>
<zan-button @click="showCustomizedToast(5000)">倒数5秒</zan-button>
</example-block></section></template>
<style>
@component-namespace demo {
@b toast {
.zan-button {
margin: 15px;
}
}
}
</style>
<script>
import Vue from "vue";import ExampleBlock from "../components/example-block";Vue.component("example-block", ExampleBlock);
import { Toast } from 'src/index';
export default {
methods: {
showSimpleToast() {
Toast('我是提示文案,建议不超过十五字~');
},
showLoadingToast() {
Toast.loading();
},
showSuccessToast() {
Toast.success('成功文案');
},
showFailToast() {
Toast.fail('失败文案');
},
showCustomizedToast(duration) {
let leftSec = duration / 1000;
let toast = Toast({
duration: duration + 1000,
type: 'success',
message: leftSec.toString()
});
window.setInterval(() => {
if (leftSec <= 1) {
window.clearInterval();
toast.message = '跳转中...'
return;
}
toast.message = (--leftSec).toString();
}, 1000);
}
}
};
</script>

View File

@ -0,0 +1,29 @@
<template><section class="demo-uploader"><h1 class="demo-title">uploader</h1><example-block title="基础用法">
<div class="uploader-container">
<zan-uploader :before-read="logContent" @file-readed="logContent">
</zan-uploader>
</div>
</example-block><example-block title="自定义上传图标">
<div class="uploader-container">
<zan-uploader @file-readed="logContent">
<zan-icon name="photograph"></zan-icon>
</zan-uploader>
</div>
</example-block></section></template>
<style>
.uploader-container {
padding: 5px 15px;
}
</style>
<script>
import Vue from "vue";import ExampleBlock from "../components/example-block";Vue.component("example-block", ExampleBlock);
export default {
methods: {
logContent(file) {
console.log(file)
}
}
};
</script>

View File

@ -29,6 +29,18 @@ export default {
```
:::
### 带*号,标明必填
传入`required`属性
:::demo 带*号,标明必填
```html
<zan-cell-group>
<zan-cell title="单元格1" required></zan-cell>
</zan-cell-group>
```
:::
### 标题带描述信息
传入`label`属性,属性值为描述信息的值。

View File

@ -0,0 +1,94 @@
<script>
export default {
data() {
return {
minHour: 10,
maxHour: 20,
minDate: new Date()
};
},
methods: {
handlePickerChange(picker, values) {
// picker.setColumnValues(1, citys[values[0]]);
console.log(values);
},
handlePickerCancel() {
alert('picker cancel');
},
handlePickerConfirm() {
alert('picker confirm');
}
}
};
</script>
## Picker组件
模仿iOS中的`UIPickerView`
### 基础用法
:::demo 基础用法
```html
<zan-datetime-picker
type="time"
:min-hour="minHour"
:max-hour="maxHour"
:min-date="minDate"
@change="handlePickerChange">
</zan-datetime-picker>
<script>
export default {
data() {
return {
minHour: 10,
maxHour: 20,
minDate: new Date()
};
},
methods: {
handlePickerChange(picker, values) {
picker.setColumnValues(1, citys[values[0]]);
}
}
};
</script>
```
:::
### API
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| visibileColumnCount | 每一列可见备选元素的个数 | Number | 5 | |
| itemHeight | 选中元素区高度 | Number | 44 | |
| columns | 对象数组,配置每一列显示的数据 | Array | | |
| showToolbar | 是否在组件顶部显示一个toolbar | Boolean | true | |
### columns
`API`中的`columns`为一个对象数组,数组中的每一个对象配置每一列,每一列有以下`key`
| key | 说明 |
|-----------|-----------|
| values | 列中对应的备选值 |
| defaultIndex | 初始选中值的索引默认为0 |
| className | 为对应列添加特殊的`class` |
### change事件
`change`事件中,可以获取到`picker`实例,对`picker`进行相应的更新等操作:
| 函数 | 说明 |
|-----------|-----------|
| getColumnValue(index) | 获取对应列中选中的值 |
| setColumnValue(index, value) | 设置对应列中选中的值 |
| getColumnValues(index) | 获取对应列中所有的备选值 |
| setColumnValues(index, values) | 设置对应列中所有的备选值 |
| getValues() | 获取所有列中被选中的值,返回一个数组 |
| setValues(values) | `values`为一个数组,设置所有列中被选中的值 |

View File

@ -29,9 +29,9 @@ export default {
:::demo 基础用法
```html
<zan-cell-group>
<zan-field type="text" label="用户名:" placeholder="请输入用户名" v-model="username"></zan-field>
<zan-field type="password" label="密码:" placeholder="请输入密码"></zan-field>
<zan-field type="textarea" label="个人介绍:" placeholder="请输入个人介绍"></zan-field>
<zan-field type="text" label="用户名:" placeholder="请输入用户名" v-model="username" required></zan-field>
<zan-field type="password" label="密码:" placeholder="请输入密码" required></zan-field>
<zan-field type="textarea" label="个人介绍:" placeholder="请输入个人介绍" required></zan-field>
</zan-cell-group>
```
:::
@ -84,6 +84,19 @@ export default {
```
:::
### Autosize的输入框(仅支持textarea)
传入`autosize`属性, 且将`rows`设为1。
:::demo 错误的输入框
```html
<zan-cell-group>
<zan-field label="留言:" type="textarea" placeholder="请输入留言" rows="1" autosize></zan-field>
</zan-cell-group>
```
:::
### API
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
@ -96,4 +109,7 @@ export default {
| error | 输入框是否有错误 | boolean | false | |
| readonly | 输入框是否只读 | boolean | false | |
| maxlength | 输入框maxlength | [String, Number] | '' | |
| rows | textarea rows | [String, Number] | '' | |
| cols | textarea cols | [String, Number] | '' | |
| autosize | 自动调整高度(仅支持textarea) | Boolean | false | true, false |

View File

@ -16,23 +16,32 @@
:::demo 所有Icon
```html
<zan-icon name="album"></zan-icon>
<zan-icon name="arrow"></zan-icon>
<zan-icon name="camera"></zan-icon>
<zan-icon name="certificate"></zan-icon>
<zan-icon name="qr-invalid"></zan-icon>
<zan-icon name="qr"></zan-icon>
<zan-icon name="exchange"></zan-icon>
<zan-icon name="close"></zan-icon>
<zan-icon name="location"></zan-icon>
<zan-icon name="upgrade"></zan-icon>
<zan-icon name="check"></zan-icon>
<zan-icon name="checked"></zan-icon>
<zan-icon name="close"></zan-icon>
<zan-icon name="like-o"></zan-icon>
<zan-icon name="like"></zan-icon>
<zan-icon name="chat"></zan-icon>
<zan-icon name="shop"></zan-icon>
<zan-icon name="photograph"></zan-icon>
<zan-icon name="add"></zan-icon>
<zan-icon name="add2"></zan-icon>
<zan-icon name="photo"></zan-icon>
<zan-icon name="logistics"></zan-icon>
<zan-icon name="edit"></zan-icon>
<zan-icon name="passed"></zan-icon>
<zan-icon name="cart"></zan-icon>
<zan-icon name="arrow"></zan-icon>
<zan-icon name="gift"></zan-icon>
<zan-icon name="home"></zan-icon>
<zan-icon name="location"></zan-icon>
<zan-icon name="message"></zan-icon>
<zan-icon name="send"></zan-icon>
<zan-icon name="shopping-cart"></zan-icon>
<zan-icon name="sign"></zan-icon>
<zan-icon name="store"></zan-icon>
<zan-icon name="topay"></zan-icon>
<zan-icon name="tosend"></zan-icon>
<zan-icon name="search"></zan-icon>
<zan-icon name="clear"></zan-icon>
<zan-icon name="success"></zan-icon>
<zan-icon name="fail"></zan-icon>
```
:::

View File

@ -1,3 +1,50 @@
<style>
@component-namespace demo {
@b image-preview {
.zan-button {
margin-left: 15px;
}
}
}
</style>
<script>
import { ImagePreview } from 'src/index';
export default {
methods: {
handleImagePreview() {
ImagePreview([
'https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp',
'https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg?imageView2/2/w/980/h/980/q/75/format/webp'
]);
}
}
};
</script>
## ImagePreview 图片预览
### 基础用法
:::demo
```html
<zan-button @click="handleImagePreview">预览图片</zan-button>
<script>
import { ImagePreview } from 'src/index';
export default {
methods: {
handleImagePreview() {
ImagePreview([
'https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp',
'https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg?imageView2/2/w/980/h/980/q/75/format/webp'
]);
}
}
};
</script>
```
:::

View File

@ -22,7 +22,10 @@
.zan-popup-4 {
width: 60%;
height: 200px;
box-sizing: border-box;
padding: 20px;
border-radius: 5px;
text-align: center;
}
.zan-button {
@ -87,7 +90,7 @@ export default {
</zan-popup>
<div class="zan-row">
<zan-button @click="popupShow2 = true">从上方弹出popup</zan-button>
<zan-button @click="popupShow2 = true">从上方弹出popup</zan-button>
</div>
<zan-popup v-model="popupShow2" position="top" class="zan-popup-2" :overlay="false">
更新成功

View File

@ -3,6 +3,10 @@
@b swipe {
.zan-swipe {
height: 200px;
img {
width: 100%;
}
}
}
}
@ -19,7 +23,22 @@
<img src="https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
<zan-swipe-item>
<img src="https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
<img src="https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
</zan-swipe>
```
:::
### 自动轮播
:::demo 自动轮播
```html
<zan-swipe :auto-play="true">
<zan-swipe-item>
<img src="https://img.yzcdn.cn/upload_files/2017/03/14/FmTPs0SeyQaAOSK1rRe1sL8RcwSY.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
<zan-swipe-item>
<img src="https://img.yzcdn.cn/upload_files/2017/03/15/FvexrWlG_WxtCE9Omo5l27n_mAG_.jpeg?imageView2/2/w/980/h/980/q/75/format/webp" alt="">
</zan-swipe-item>
</zan-swipe>
```

View File

@ -16,6 +16,25 @@
}
</style>
<script>
export default {
data() {
return {
switchState: true
};
},
computed: {
switchStateText() {
return this.switchState ? ' ON' : 'OFF';
}
},
methods: {
updateState(newState) {
this.switchState = newState;
}
}
};
</script>
## Switch组件
@ -24,7 +43,7 @@
:::demo 基础用法
```html
<div class="demo-switch__wrapper">
<zan-switch class="some-customized-class" :checked="switchState" :on-change="updateState"></zan-switch>
<zan-switch class="some-customized-class" :checked="switchState" @change="updateState"></zan-switch>
<div class="demo-switch__text">{{switchStateText}}</div>
</div>
<div class="demo-switch__wrapper">
@ -73,4 +92,3 @@ export default {
| checked | 开关状态 | boolean | false | true, false |
| loading | loading状态 | boolean | false | true, false |
| disabled | 禁用状态 | boolean | false | true, false |
| onChange | 回调 | function | function{} | - |

View File

@ -1,3 +1,14 @@
<style>
@component-namespace demo {
@b tab {
.zan-tabs-pane {
background-color: #fff;
padding: 20px;
}
}
}
</style>
## Tab 组件
### 基础用法

104
docs/examples-docs/toast.md Normal file
View File

@ -0,0 +1,104 @@
<style>
@component-namespace demo {
@b toast {
.zan-button {
margin: 15px;
}
}
}
</style>
<script>
import { Toast } from 'src/index';
export default {
methods: {
showSimpleToast() {
Toast('我是提示文案,建议不超过十五字~');
},
showLoadingToast() {
Toast.loading();
},
showSuccessToast() {
Toast.success('成功文案');
},
showFailToast() {
Toast.fail('失败文案');
},
showCustomizedToast(duration) {
let leftSec = duration / 1000;
let toast = Toast({
duration: duration + 1000,
type: 'success',
message: leftSec.toString()
});
window.setInterval(() => {
if (leftSec <= 1) {
window.clearInterval();
toast.message = '跳转中...'
return;
}
toast.message = (--leftSec).toString();
}, 1000);
}
}
};
</script>
## Toast
### 基础用法
:::demo 基础用法
```html
<zan-button @click="showSimpleToast">普通文字提示</zan-button>
<zan-button @click="showLoadingToast">加载Toast</zan-button>
<zan-button @click="showSuccessToast">成功</zan-button>
<zan-button @click="showFailToast">失败</zan-button>
<zan-button @click="showCustomizedToast(5000)">倒数5秒</zan-button>
<script>
import { Toast } from 'src/index';
export default {
methods: {
showSimpleToast() {
Toast('我是提示文案,建议不超过十五字~');
},
showLoadingToast() {
Toast.loading();
},
showSuccessToast() {
Toast.success('成功文案');
},
showFailToast() {
Toast.fail('失败文案');
},
showCustomizedToast(duration) {
let leftSec = duration / 1000;
let toast = Toast({
duration: duration + 1000,
type: 'success',
message: leftSec.toString()
});
window.setInterval(() => {
if (leftSec <= 1) {
window.clearInterval();
toast.message = '跳转中...'
return;
}
toast.message = (--leftSec).toString();
}, 1000);
}
}
};
</script>
```
:::
### API
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| type | 类型 | String | 'text' | 'text', 'loading', 'success', 'failure' |
| message | 内容 | String | '' | - |

View File

@ -0,0 +1,54 @@
<style>
.uploader-container {
padding: 5px 15px;
}
</style>
<script>
export default {
methods: {
logContent(file) {
console.log(file)
}
}
};
</script>
## Uploader 组件
### 基础用法
:::demo 基础用法
```html
<div class="uploader-container">
<zan-uploader
:before-read="logContent"
@file-readed="logContent">
</zan-uploader>
</div>
```
:::
### 自定义上传图标
:::demo 自定义上传图标
```html
<div class="uploader-container">
<zan-uploader @file-readed="logContent">
<zan-icon name="photograph"></zan-icon>
</zan-uploader>
</div>
```
:::
### API
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| result-type | 读取文件的方式以base64的方式读取以文本的方式读取 | String | 'dataUrl' | 'dataUrl','text' |
| disable | 是否禁用上传,在图片上传期间设置为true禁止用户点击此组件上传图片 | boolean | false | |
| before-read | 读文件之前的钩子,参数为选择的文件,若返回 false 则停止读取文件。 | Function | | |
| file-readed | 文件读完之后出发此事件,参数为{name:'文件名',type:'文件类型',size:'文件大小',content:'读的内容'} | Function | | |
### Slot
| name | 描述 |
|-----------|-----------|
| - | 自定义上传显示图标 |

View File

@ -19,9 +19,10 @@
},
{
"name": "ZanUI组件",
"showInMobile": true,
"groups": [
{
"groupName": "CSS组件",
"groupName": "基础组件",
"list": [
{
"path": "/button",
@ -63,14 +64,42 @@
"path": "/badge",
"title": "Badge"
},
{
"path": "/tab",
"title": "Tab"
},
{
"path": "/lazyload",
"title": "Lazyload"
},
{
"path": "/popup",
"title": "Popup"
},
{
"path": "/swipe",
"title": "Swipe"
},
{
"path": "/search",
"title": "Search"
},
{
"path": "/quantity",
"title": "Quantity"
},
{
"path": "/waterfall",
"title": "Waterfall"
},
{
"path": "/image-preview",
"title": "ImagePreview"
}
]
},
{
"groupName": "Form",
"groupName": "表单",
"list": [
{
"path": "/switch",
@ -87,28 +116,24 @@
{
"path": "/checkbox",
"title": "Checkbox"
},
{
"path": "/uploader",
"title": "Uploader"
}
]
},
{
"groupName": "JS组件",
"groupName": "操作反馈",
"list": [
{
"path": "/actionsheet",
"title": "ActionSheet"
},
{
"path": "/tab",
"title": "Tab"
},
{
"path": "/toast",
"title": "Toast"
},
{
"path": "/img-uploader",
"title": "Img Uploader"
},
{
"path": "/picker",
"title": "Picker"
@ -117,33 +142,9 @@
"path": "/datetime-picker",
"title": "Datetime Picker"
},
{
"path": "/lazyload",
"title": "Lazyload"
},
{
"path": "/popup",
"title": "Popup"
},
{
"path": "/dialog",
"title": "Dialog"
},
{
"path": "/swipe",
"title": "Swipe"
},
{
"path": "/quantity",
"title": "Quantity"
},
{
"path": "/waterfall",
"title": "Waterfall"
},
{
"path": "/image-preview",
"title": "ImagePreview"
}
]
}

View File

@ -1,6 +1,6 @@
{
"name": "@youzan/zanui-vue",
"version": "0.0.31",
"version": "0.0.38",
"description": "有赞vue wap组件库",
"main": "lib/zanui.js",
"style": "lib/zanui-css/index.css",
@ -47,6 +47,7 @@
"devDependencies": {
"2webpack2": "^1.2.1",
"autoprefixer": "^6.7.5",
"avoriaz": "^1.9.1",
"babel-cli": "^6.14.0",
"babel-core": "^6.17.0",
"babel-eslint": "^6.1.2",

View File

@ -1,6 +1,6 @@
<template>
<a class="zan-cell" :href="url" @click="handleClick">
<div class="zan-cell__title">
<div :class="{ 'zan-cell__title': true, 'zan-cell__required': required }">
<slot name="icon">
<i v-if="icon" class="zan-icon" :class="'zan-icon-' + icon"></i>
</slot>
@ -31,7 +31,8 @@ export default {
value: [String, Number],
url: String,
label: String,
isLink: Boolean
isLink: Boolean,
required: Boolean
},
methods: {

View File

@ -0,0 +1,8 @@
## 0.0.2 (2017-01-20)
* 改了bug A
* 加了功能B
## 0.0.1 (2017-01-10)
* 第一版

View File

@ -0,0 +1,26 @@
# @youzan/<%= name %>
!!! 请在此处填写你的文档最简单描述 !!!
[![version][version-image]][download-url]
[![download][download-image]][download-url]
[version-image]: http://npm.qima-inc.com/badge/v/@youzan/<%= name %>.svg?style=flat-square
[download-image]: http://npm.qima-inc.com/badge/d/@youzan/<%= name %>.svg?style=flat-square
[download-url]: http://npm.qima-inc.com/package/@youzan/<%= name %>
## Demo
## Usage
## API
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| className | 自定义额外类名 | string | '' | '' |
## License
[MIT](https://opensource.org/licenses/MIT)

View File

@ -0,0 +1,3 @@
import DateTimePicker from './src/datetime-picker';
export default DateTimePicker;

View File

@ -0,0 +1,10 @@
{
"name": "@youzan/zan-datetime-picker",
"version": "0.0.1",
"description": "datetime picker component",
"main": "./index.js",
"author": "niunai <niunai@youzan.com>",
"license": "MIT",
"devDependencies": {},
"dependencies": {}
}

View File

@ -0,0 +1,222 @@
<template>
<zan-picker
:columns="columns"
:visible-item-count="visibleItemCount"
@change="handlePickerChange"
@confirm="handlePickerConfirm"
showToolbar>
</zan-picker>
</template>
<script>
import Picker from 'packages/picker';
const allowedType = ['time', 'date', 'datetime'];
export default {
name: 'zan-datetime-picker',
components: {
Picker
},
props: {
type: {
type: String,
default: 'datetime',
validator(value) {
return allowedType.indexOf(value) > -1;
}
},
visibleItemCount: {
type: Number,
default: 5
},
minDate: {
type: Date,
default() {
return new Date(new Date().getFullYear() - 10, 0, 1);
}
},
maxDate: {
type: Date,
default() {
return new Date(new Date().getFullYear() + 10, 11, 31);
}
},
minHour: {
type: Number,
default: 0
},
maxHour: {
type: Number,
default: 23
},
value: null
},
data() {
return {
innerValue: this.val
};
},
watch: {
value(val) {
this.innerValue = val;
},
innerValue(val) {
console.log(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]
];
}
debugger
const { maxYear, maxDate, maxMonth, maxHour, maxMinute } = this.getBoundary('max', this.innerValue);
const { minYear, minDate, minMonth, minHour, minMinute } = this.getBoundary('min', this.innerValue);
const result = [
[minYear, maxYear],
[minMonth, maxMonth],
[minDate, maxDate],
[minHour, maxHour],
[minMinute, maxMinute]
];
if (this.type === 'date') result.splice(3, 2);
return result;
},
columns() {
return 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}`;
});
return {
values
};
});
}
},
methods: {
times(n, iteratee) {
let index = -1;
const result = Array(n);
while (++index < n) {
result[index] = iteratee(index);
}
return result;
},
getBoundary(type, value) {
const boundary = this[`${type}Date`];
const year = boundary.getFullYear();
let month = 1;
let date = 1;
let hour = 0;
let minute = 0;
if (type === 'max') {
month = 12;
date = this.getMonthEndDay(value.getFullYear(), value.getMonth() + 1);
hour = 23;
minute = 59;
}
if (value.getFullYear() === year) {
month = boundary.getMonth() + 1;
if (value.getMonth() + 1 === month) {
date = value.getDate();
if (value.getDate() === date) {
hour = value.getHours();
if (value.getHours() === hour) {
minute = value.getMinutes();
}
}
}
}
return {
[`${type}Year`]: year,
[`${type}Month`]: month,
[`${type}Date`]: date,
[`${type}Hour`]: hour,
[`${type}Minute`]: minute
};
},
getTrueValue(formattedValue) {
if (!formattedValue) return;
while (isNaN(parseInt(formattedValue, 10))) {
formattedValue = formattedValue.slice(1);
}
return parseInt(formattedValue, 10);
},
getMonthEndDay(year, month) {
if (this.isShortMonth(month)) {
return 30;
} else if (month === 2) {
return this.isLeapYear(year) ? 29 : 28;
} else {
return 31;
}
},
isLeapYear(year) {
return (year % 400 === 0) || (year % 100 !== 0 && year % 4 === 0);
},
isShortMonth(month) {
return [4, 6, 9, 11].indexOf(month) > -1;
},
handlePickerConfirm(values) {
this.$emit('confirm', this.innerValue);
},
handlePickerChange(picker, values, index) {
console.log(this.innerValue);
let value;
if (this.type === 'time') {
value = values.join(':');
} else {
const year = this.getTrueValue(values[0]);
const month = this.getTrueValue(values[1]);
const maxDate = this.getMonthEndDay(year, month);
let date = this.getTrueValue(values[2]);
date = date > maxDate ? maxDate : date;
let hour = 0;
let minute = 0;
if (this.type === 'datetime') {
hour = this.getTrueValue(values[3]);
minute = this.getTrueValue(values[4]);
}
value = new Date(year, month - 1, date, hour, minute);
}
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;
} else {
const minHour = this.minHour;
this.innerValue = `${minHour > 10 ? minHour : '0' + minHour}:00`;
}
}
}
};
</script>

View File

@ -2,21 +2,26 @@
<zan-cell
class="zan-field"
:title="label"
:required="required"
:class="{
'zan-field--hastextarea': type === 'textarea',
'zan-field--nolabel': !label,
'zan-field--disabled': disabled,
'zan-field--error': error,
'zan-field--border': border
'zan-field--border': border,
'zan-field--autosize': autosize
}">
<textarea
v-if="type === 'textarea'"
ref="textareaElement"
class="zan-field__control"
v-model="currentValue"
:placeholder="placeholder"
:maxlength="maxlength"
:disabled="disabled"
:readonly="readonly">
:readonly="readonly"
:rows="rows"
:cols="cols">
</textarea>
<input
v-else
@ -32,6 +37,7 @@
</template>
<script>
const VALID_TYPES = ['text', 'number', 'email', 'url', 'tel', 'date', 'datetime', 'password', 'textarea'];
import zanCell from 'packages/cell';
export default {
@ -44,7 +50,10 @@ export default {
props: {
type: {
type: String,
default: 'text'
default: 'text',
validate(value) {
return VALID_TYPES.indexOf(value) > -1;
}
},
placeholder: String,
value: {},
@ -52,8 +61,18 @@ export default {
disabled: Boolean,
error: Boolean,
readonly: Boolean,
required: Boolean,
maxlength: [String, Number],
border: Boolean
border: Boolean,
rows: [String, Number],
cols: [String, Number],
autosize: {
type: Boolean,
default: false,
validate(value) {
if (value && this.type !== 'textarea') return false;
}
}
},
data() {
@ -68,6 +87,7 @@ export default {
},
currentValue(val) {
if (this.autosize && this.type === 'textarea') this.sizeAdjust();
this.$emit('input', val);
}
},
@ -75,6 +95,15 @@ export default {
methods: {
handleInput(event) {
this.currentValue = event.target.value;
},
sizeAdjust() {
const textareaElement = this.$refs.textareaElement;
const textAreaDiff = (parseInt(textareaElement.style.paddingBottom, 10) +
parseInt(textareaElement.style.paddingTop, 10)) || 0;
// 0 scrollHeight
textareaElement.style.height = 0 + 'px';
textareaElement.style.height = (textareaElement.scrollHeight - textAreaDiff) + 'px';
}
}
};

View File

@ -12,13 +12,13 @@ const initInstance = () => {
});
};
var ImagePreviewBox = image => {
var ImagePreviewBox = images => {
if (!instance) {
initInstance();
}
if (!instance.value) {
instance.image = image;
instance.images = images;
document.body.appendChild(instance.$el);

View File

@ -1,22 +1,32 @@
<template>
<transition name="image-fade">
<div class="zan-image-preview" ref="previewContainer" v-show="value" @click="handlePreviewClick">
<img class="zan-image-preview__image" :src="image" alt="" :class="{
'zan-image-preview__image--center': true,
'zan-image-preview__image--top': imageIsLargeView
}">
<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
}">
</zan-swipe-item>
</zan-swipe>
</div>
</transition>
</template>
<script>
import Popup from 'src/mixins/popup';
import ZanSwipe from 'packages/swipe';
import ZanSwipeItem from 'packages/swipe-item';
export default {
name: 'zan-image-preview',
mixins: [Popup],
components: {
ZanSwipe,
ZanSwipeItem
},
props: {
overlay: {
default: true
@ -33,7 +43,7 @@ export default {
data() {
return {
image: '',
images: [],
viewportSize: null
};
},
@ -42,36 +52,6 @@ export default {
handlePreviewClick() {
this.value = false;
},
/**
* 图片样式计算
* 根据屏幕自适应显示最长边
*/
computeImageStyle() {
// const previewSize = this.$refs.previewContainer.getBoundingClientRect();
// const img = new Image();
// const _this = this;
// img.onload = function() {
// const imgRatio = parseFloat(this.width / this.height);
// const previewRatio = parseFloat(previewSize.width / previewSize.height);
// if (previewRatio <= imgRatio) {
// const top = (previewSize.height - parseInt(previewSize.width / imgRatio, 10)) / 2;
// _this.imageStyle = {
// width: '100%',
// height: 'auto',
// top: top + 'px'
// };
// } else if (previewRatio > imgRatio) {
// _this.imageStyle = {
// width: 'auto',
// height: '100%'
// };
// }
// };
// img.src = this.image;
},
close() {
if (this.closing) return;

View File

@ -1,3 +1,3 @@
import Loading from './src/loading';
import ZanLoading from './src/loading';
export default Loading;
export default ZanLoading;

View File

@ -1,16 +1,22 @@
<template>
<div class="zan-search" :class="{ 'is-focus' : isFocus }">
<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">
<span class="zan-icon zan-icon-close" @click="handleClean"></span>
<zan-icon name="clear" @click="handleClean"></zan-icon>
</div>
<div class="zan-search__cancel" :class="{ 'is-focus' : isFocus }" @click="handleBack">取消</div>
<div class="zan-search__cancel" :class="{ 'zan-search__cancel--focus' : isFocus }" @click="handleBack">取消</div>
</div>
</template>
<script>
import ZanIcon from 'packages/icon';
export default {
name: 'zan-search',
components: {
ZanIcon
},
props: {
placeholder: {
type: String

View File

@ -34,7 +34,7 @@ extend(Scroll.prototype, {
update: function() {
const oldPages = this.pages
this.pages = this.wrapElem.querySelectorAll('.swp-page');
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]
@ -49,8 +49,8 @@ extend(Scroll.prototype, {
left: 0,
width: '100%',
height: '100%',
display: 'block',
'-webkit-transform': 'translate3d(-9999px, 0, 0)'
'-webkit-transform': 'translate3d(-9999px, 0, 0)',
'pointer-events': 'none'
};
setElementsStyles(this.pages, defaultStyle);
this.mCache = {
@ -74,6 +74,7 @@ extend(Scroll.prototype, {
page = this.getCurrentPage();
if (page) {
page.style['-webkit-transform'] = 'translate3d(' + offset + 'px, 0, 0)';
page.style['display'] = 'block';
}
leftPage = this.pages[this.mapLoopPage(currentOffsetPage - 1)];

View File

@ -8,6 +8,7 @@
</template>
<script>
import ZanLoading from 'packages/loading';
/**
* zan-switch
* @module components/switch
@ -15,13 +16,15 @@
* @param {boolean} [checked=false] - 开关状态
* @param {boolean} [disabled=false] - 禁用
* @param {boolean} [loading=false] - loading状态
* @param {callback} [onChange] - 开关状态改变回调函数
*
* @example
* <zan-switch checked="true" disabled="false"></zan-switch>
*/
export default {
name: 'zan-switch',
components: {
'zan-loading': ZanLoading
},
props: {
checked: {
type: Boolean,
@ -34,10 +37,6 @@ export default {
loading: {
type: Boolean,
default: false
},
onChange: {
type: Function,
default: function() {}
}
},
computed: {
@ -54,7 +53,7 @@ export default {
*/
toggleState: function() {
if (this.disabled || this.loading) return;
this.onChange(!this.checked);
this.$emit('change', !this.checked);
}
}
};

26
packages/toast/README.md Normal file
View File

@ -0,0 +1,26 @@
# @youzan/<%= name %>
!!! 请在此处填写你的文档最简单描述 !!!
[![version][version-image]][download-url]
[![download][download-image]][download-url]
[version-image]: http://npm.qima-inc.com/badge/v/@youzan/<%= name %>.svg?style=flat-square
[download-image]: http://npm.qima-inc.com/badge/d/@youzan/<%= name %>.svg?style=flat-square
[download-url]: http://npm.qima-inc.com/package/@youzan/<%= name %>
## Demo
## Usage
## API
| 参数 | 说明 | 类型 | 默认值 | 可选值 |
|-----------|-----------|-----------|-------------|-------------|
| className | 自定义额外类名 | string | '' | '' |
## License
[MIT](https://opensource.org/licenses/MIT)

3
packages/toast/index.js Normal file
View File

@ -0,0 +1,3 @@
import Toast from './src/toast';
export default Toast;

View File

@ -0,0 +1,10 @@
{
"name": "@youzan/zan-toast",
"version": "0.0.1",
"description": "toast component",
"main": "./index.js",
"author": "jiangruowei",
"license": "MIT",
"devDependencies": {},
"dependencies": {}
}

View File

@ -0,0 +1,76 @@
import Vue from 'vue';
import merge from 'src/utils/merge';
const ToastConstructor = Vue.extend(require('./toast.vue'));
let toastQueue = [];
const getInstance = () => {
if (toastQueue.length > 0) {
const instance = toastQueue[0];
toastQueue.splice(0, 1);
return instance;
}
return new ToastConstructor({
el: document.createElement('div')
});
};
const returnInstance = instance => {
if (instance) {
toastQueue.push(instance);
}
};
const removeDom = event => {
if (event.target.parentNode) {
event.target.parentNode.removeChild(event.target);
}
};
var Toast = (options = {}) => {
const duration = options.duration || 3000;
let instance = getInstance();
returnInstance(instance);
instance.closed = false;
clearTimeout(instance.timer);
instance.type = options.type ? options.type : 'text';
instance.message = typeof options === 'string' ? options : options.message;
document.body.appendChild(instance.$el);
Vue.nextTick(function() {
instance.visible = true;
instance.$el.removeEventListener('transitionend', removeDom);
instance.timer = setTimeout(function() {
if (instance.closed) return;
instance.visible = false;
instance.$el.addEventListener('transitionend', removeDom);
instance.closed = true;
}, duration);
});
return instance;
};
Toast.loading = (options) => {
return new Toast(merge({
type: 'loading'
}, options));
};
Toast.success = (options) => {
const message = typeof options === 'string' ? options : options.message;
return new Toast(merge({
type: 'success',
message: message
}, options));
};
Toast.fail = (options) => {
const message = typeof options === 'string' ? options : options.message;
return new Toast(merge({
type: 'fail',
message: message
}, options));
};
export default Toast;

View File

@ -0,0 +1,81 @@
<template>
<transition name="zan-toast">
<div class="zan-toast" :class="['zan-toast--' + displayStyle]" v-show="visible">
<!-- 只显示文字 -->
<template v-if="displayStyle === 'text'" >
<div class="zan-toast__text">{{message}}</div>
</template>
<!-- 加载中 -->
<template v-if="displayStyle === 'loading'">
<zan-loading v-if="type === 'loading'" type="gradient-circle" color="white"></zan-loading>
</template>
<!-- 图案加文字 -->
<template v-if="displayStyle === 'default'">
<zan-icon class="zan-toast__icon" name="check"></zan-icon>
<div class="zan-toast__text">{{message}}</div>
</template>
</div>
</transition>
</template>
<script>
import zanLoading from 'packages/loading';
import zanIcon from 'packages/icon';
const TOAST_TYPES = ['text', 'loading', 'success', 'fail'];
/**
* zan-toast
* @module components/toast
* @desc toast
* @param {string} [type=false] - 类型
* @param {string} [message] - 信息
*
*/
export default {
name: 'zan-toast',
components: {
'zan-loading': zanLoading,
'zan-icon': zanIcon
},
props: {
type: {
type: String,
default: 'text',
validate(value) {
return TOAST_TYPES.indexOf(value) > -1;
}
},
message: {
type: String,
default: '',
validate(value) {
if (this.type === 'success' || this.type === 'fail') {
return value.length <= 16;
}
}
}
},
data() {
return {
visible: false
};
},
computed: {
displayStyle() {
switch (this.type) {
case 'text':
return 'text';
case 'loading':
return 'loading';
default:
return 'default';
}
},
iconName() {
// TODO: icon
return 'check';
}
}
};
</script>

View File

@ -0,0 +1,3 @@
import Uploader from './src/main';
export default Uploader;

View File

@ -0,0 +1,57 @@
<template>
<div class="zan-uploader">
<slot>
<div>
<zan-button block>上传文件</zan-button>
</div>
</slot>
<input type="file" @change="onValueChange" class="zan-uploader__input" ref="input" />
</div>
</template>
<script>
export default {
name: 'zan-uploader',
props: {
disabled: {
type: Boolean,
default: false
},
beforeRead: Function,
resultType: {
type: String,
default: 'dataUrl',
validator(value) {
return value === 'dataUrl' || value === 'text';
}
}
},
methods: {
onValueChange(event) {
if (this.disabled) {
return;
}
var files = event.target.files;
var file = files[0];
if (!file) return;
if (this.beforeRead && !this.beforeRead(file)) return;
var reader = new FileReader();
reader.onload = (e) => {
this.$emit('file-readed',
{
name: file.name,
type: file.type,
size: file.size,
content: e.target.result
});
this.$refs.input.value = '';
};
if (this.resultType === 'dataUrl') {
reader.readAsDataURL(file);
} else if (this.resultType === 'text') {
reader.readAsText(file);
}
}
}
};
</script>

View File

@ -30,21 +30,21 @@ function doBindEvent() {
// 处理滚动函数
function handleScrollEvent() {
let element = this.el;
let scrollEventTarget = this.scrollEventTarget;
const element = this.el;
const scrollEventTarget = this.scrollEventTarget;
// 已被禁止的滚动处理
if (this.disabled) return;
let targetScrollTop = Utils.getScrollTop(scrollEventTarget);
let targetBottom = targetScrollTop + Utils.getVisibleHeight(scrollEventTarget);
const targetScrollTop = Utils.getScrollTop(scrollEventTarget);
const targetBottom = targetScrollTop + Utils.getVisibleHeight(scrollEventTarget);
// 判断是否到了底
let needLoadMoreToLower = false;
if (element === scrollEventTarget) {
needLoadMoreToLower = scrollEventTarget.scollHeight - targetBottom < this.offset;
} else {
let elementBottom = Utils.getElementTop(element) - Utils.getElementTop(scrollEventTarget) + Utils.getVisibleHeight(element);
const elementBottom = Utils.getElementTop(element) - Utils.getElementTop(scrollEventTarget) + Utils.getVisibleHeight(element);
needLoadMoreToLower = elementBottom - Utils.getVisibleHeight(scrollEventTarget) < this.offset;
}
if (needLoadMoreToLower) {
@ -56,7 +56,7 @@ function handleScrollEvent() {
if (element === scrollEventTarget) {
needLoadMoreToUpper = targetScrollTop < this.offset;
} else {
let elementTop = Utils.getElementTop(element) - Utils.getElementTop(scrollEventTarget);
const elementTop = Utils.getElementTop(element) - Utils.getElementTop(scrollEventTarget);
needLoadMoreToUpper = elementTop + this.offset > 0;
}
if (needLoadMoreToUpper) {
@ -64,6 +64,30 @@ function handleScrollEvent() {
}
}
// 绑定事件
function startBind(el) {
const context = el[CONTEXT];
context.vm.$nextTick(function() {
if (Utils.isAttached(el)) {
doBindEvent.call(el[CONTEXT]);
}
});
}
// 确认何时绑事件监听函数
function doCheckStartBind(el) {
const context = el[CONTEXT];
if (context.vm._isMounted) {
startBind(el);
} else {
context.vm.$on('hook:mounted', function() {
startBind(el);
});
}
}
export default function(type) {
return {
bind(el, binding, vnode) {
@ -76,11 +100,7 @@ export default function(type) {
}
el[CONTEXT].cb[type] = binding.value;
vnode.context.$on('hook:mounted', function() {
if (Utils.isAttached(el)) {
doBindEvent.call(el[CONTEXT]);
}
});
doCheckStartBind(el);
},
update(el) {

Binary file not shown.

View File

@ -1,6 +1,6 @@
{
"name": "@youzan/zanui-css",
"version": "0.0.31",
"version": "0.0.38",
"description": "zanui css.",
"main": "lib/index.css",
"style": "lib/index.css",
@ -9,7 +9,8 @@
"src"
],
"scripts": {
"build": "gulp build"
"build": "gulp build",
"build:icons": "sh scripts/build.sh"
},
"license": "MIT",
"devDependencies": {

View File

@ -0,0 +1,83 @@
#!/bin/bash
basepath=$(dirname $0)
server_prefix=/zanui/icon
# convert relative path to absolute path
function abspath() {
pushd . > /dev/null; if [ -d "$1" ]; then cd "$1"; dirs -l +0; else cd "`dirname \"$1\"`"; cur_dir=`dirs -l +0`; if [ "$cur_dir" == "/" ]; then echo "$cur_dir`basename \"$1\"`"; else echo "$cur_dir/`basename \"$1\"`"; fi; fi; popd > /dev/null;
}
command_exists () {
type "$1" >/dev/null 2>&1
}
fontname() {
if command_exists superman ; then
echo "//b.yzcdn.cn$server_prefix/$(basename $basepath/../build/font/zanui-icon-*.$1)"
else
echo "$(abspath $basepath/../build/font/zanui-icon-*.$1)"
fi
}
# generate font files from sketch file
$basepath/extract-icons.sh
$basepath/generate-font.sh
if command_exists superman ; then
# upload to cdn
superman cdn $server_prefix $basepath/../build/font/zanui-icon-*
fi
# generate fontface style
eot=$(fontname eot)
cat > $basepath/../src/icon.css <<EOF
/* DO NOT EDIT! Generated by fount */
@font-face {
font-family: 'zan-icon';
src: url('$eot');
src: url('$eot?#iefix') format('embedded-opentype'),
url('$(fontname woff2)') format('woff2'),
url('$(fontname woff)') format('woff'),
url('$(fontname ttf)') format('truetype')
}
.zan-icon {
display: inline-block;
}
.zan-icon::before {
font-family: "zan-icon" !important;
font-style: normal;
font-weight: normal;
speak: none;
display: inline-block;
text-decoration: inherit;
width: 1em;
text-align: center;
/* For safety - reset parent styles, that can break glyph codes*/
font-variant: normal;
text-transform: none;
/* fix buttons height, for twitter bootstrap */
line-height: 1em;
/* Animation center compensation - margins should be symmetric */
/* remove if not needed */
/* margin-left: .2em; */
/* you can be more comfortable with increased icons size */
/* font-size: 120%; */
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
EOF
cat $basepath/../build/css/zanui-icon-codes.css >> $basepath/../src/icon.css

View File

@ -0,0 +1,6 @@
#!/bin/sh
basepath=$(dirname $0)
rm -rf $basepath/../icons
sketchtool export slices --formats=svg --overwriting=YES --save-for-web=YES --output=$basepath/../icons $basepath/../assets/icons.sketch

View File

@ -0,0 +1,147 @@
module.exports = {
name: 'zanui-icon',
output: '../build',
meta: {
author: 'houzi, zhangmin',
license: 'MIT',
license_url: 'https://opensource.org/licenses/MIT',
homepage: 'http://github.com/youzan',
css_prefix_text: 'zan-icon-',
filename_hash: true
},
hinting: true,
glyphs_dir: '../icons',
glyphs: [
{
keywords: ['qr', 'invalid'],
src: '二维码失效.svg',
css: 'qr-invalid'
},
{
keywords: ['qr'],
src: '二维码.svg',
css: 'qr'
},
{
keywords: ['exchange'],
src: '兑换.svg',
css: 'exchange',
'correct_contour_direction': true
},
{
keywords: ['close'],
src: '关闭.svg',
css: 'close'
},
{
keywords: ['location'],
src: '其他分店.svg',
css: 'location'
},
{
keywords: ['upgrade'],
src: '升级地址.svg',
css: 'upgrade'
},
{
keywords: ['check'],
src: '单选.svg',
css: 'check'
},
{
keywords: ['checked'],
src: '选中.svg',
css: 'checked'
},
{
keywords: ['like', 'outline'],
src: '喜欢.svg',
css: 'like-o'
},
{
keywords: ['like', 'filled'],
src: '喜欢2.svg',
css: 'like'
},
{
keywords: ['chat'],
src: '客服.svg',
css: 'chat'
},
{
keywords: ['shop'],
src: '店铺.svg',
css: 'shop'
},
{
keywords: ['photograph'],
src: '拍照.svg',
css: 'photograph'
},
{
keywords: ['add'],
src: '新增地址.svg',
css: 'add'
},
{
keywords: ['add2'],
src: '添加.svg',
css: 'add2'
},
{
keywords: ['photo'],
src: '照片.svg',
css: 'photo'
},
{
keywords: ['logistics'],
src: '物流.svg',
css: 'logistics'
},
{
keywords: ['edit'],
src: '编辑地址.svg',
css: 'edit'
},
{
keywords: ['passed'],
src: '认证通过.svg',
css: 'passed'
},
{
keywords: ['cart'],
src: '购物车.svg',
css: 'cart'
},
{
keywords: ['arrow'],
src: '进入箭头.svg',
css: 'arrow'
},
{
keywords: ['gift'],
src: '送礼.svg',
css: 'gift'
},
{
keywords: ['search'],
src: '搜索.svg',
css: 'search'
},
{
keywords: ['clear'],
src: '清除搜索.svg',
css: 'clear'
},
{
keywords: ['success'],
src: '成功.svg',
css: 'success'
},
{
keywords: ['fail'],
src: '失败.svg',
css: 'fail'
}
]
};

View File

@ -0,0 +1,5 @@
#!/bin/sh
basepath=$(dirname $0)
iconfount --config $basepath/fount-config.js

View File

@ -32,6 +32,10 @@
margin-left: 10px;
}
& + .zan-button--block {
margin-left: 0;
}
@m default {
color: $button-default-color;
background-color: $button-default-background-color;

View File

@ -3,7 +3,7 @@
@component-namespace zan {
@b cell-group {
padding-left: 10px;
padding-left: 15px;
position: relative;
background-color: #fff;
@ -14,9 +14,8 @@
@b cell {
display: block;
overflow: hidden;
position: relative;
padding: 10px 10px 10px 0;
padding: 10px 15px 10px 0;
box-sizing: border-box;
line-height: 22px;
background-color: $c-white;
@ -35,8 +34,19 @@
}
@e title {
float: left;
overflow: hidden;
display: inline-block;
/* 清除空白字符对高度的影响 */
vertical-align: bottom;
&.zan-cell__required {
&::before {
content: '*';
position: absolute;
left: -7px;
font-size: 14px;
color: #f44;
}
}
}
@e label {
@ -62,7 +72,7 @@
.zan-icon-arrow {
position: absolute;
top: 50%;
right: 10px;
right: 15px;
transform: translateY(-50%);
color: $c-gray-dark;
font-size: 12px;

View File

@ -5,7 +5,6 @@
@component-namespace zan {
@b field {
width: 100%;
overflow: hidden;
@m hastextarea {
.zan-field__control {
@ -31,8 +30,7 @@
}
@m error {
.zan-field__control,
.zan-cell__title {
.zan-field__control {
color: $c-red;
}
}
@ -52,6 +50,12 @@
}
}
@m autosize {
.zan-field__control {
min-height: 0px;
}
}
.zan-cell__title,
.zan-cell__value {
float: none;

View File

@ -1,19 +1,19 @@
/* DO NOT EDIT! Generated by fount */
@font-face {
font-family: 'zuiicon';
src: url('https://b.yzcdn.cn/zui/font/zuiicon-b37948cf5d.eot');
src: url('https://b.yzcdn.cn/zui/font/zuiicon-b37948cf5d.eot?#iefix') format('embedded-opentype'),
url('https://b.yzcdn.cn/zui/font/zuiicon-b37948cf5d.woff2') format('woff2'),
url('https://b.yzcdn.cn/zui/font/zuiicon-b37948cf5d.woff') format('woff'),
url('https://b.yzcdn.cn/zui/font/zuiicon-b37948cf5d.ttf') format('truetype')
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')
}
.zan-icon {
display: inline-block;
}
.zan-icon::before {
font-family: "zuiicon" !important;
font-family: "zan-icon" !important;
font-style: normal;
font-weight: normal;
speak: none;
@ -39,28 +39,37 @@
/* Font smoothing. That was taken from TWBS */
-webkit-font-smoothing: antialiased;
-mozan-osx-font-smoothing: grayscale;
-moz-osx-font-smoothing: grayscale;
/* Uncomment for 3D effect */
/* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}
/* DO NOT EDIT! Generated by fount */
/* DO NOT EDIT! Generated by iconfount */
.zan-icon-album:before { content: '\e800'; } /* '' */
.zan-icon-arrow:before { content: '\e801'; } /* '' */
.zan-icon-camera:before { content: '\e802'; } /* '' */
.zan-icon-certificate:before { content: '\e803'; } /* '' */
.zan-icon-check:before { content: '\e804'; } /* '' */
.zan-icon-checked:before { content: '\e805'; } /* '' */
.zan-icon-close:before { content: '\e806'; } /* '' */
.zan-icon-gift:before { content: '\e807'; } /* '' */
.zan-icon-home:before { content: '\e808'; } /* '' */
.zan-icon-location:before { content: '\e809'; } /* '' */
.zan-icon-message:before { content: '\e80a'; } /* '' */
.zan-icon-send:before { content: '\e80b'; } /* '' */
.zan-icon-shopping-cart:before { content: '\e80c'; } /* '' */
.zan-icon-sign:before { content: '\e80d'; } /* '' */
.zan-icon-store:before { content: '\e80e'; } /* '' */
.zan-icon-topay:before { content: '\e80f'; } /* '' */
.zan-icon-tosend:before { content: '\e810'; } /* '' */
.zan-icon-qr-invalid:before { content: '\e800'; } /* '' */
.zan-icon-qr:before { content: '\e801'; } /* '' */
.zan-icon-exchange:before { content: '\e802'; } /* '' */
.zan-icon-close:before { content: '\e803'; } /* '' */
.zan-icon-location:before { content: '\e804'; } /* '' */
.zan-icon-upgrade:before { content: '\e805'; } /* '' */
.zan-icon-check:before { content: '\e806'; } /* '' */
.zan-icon-checked:before { content: '\e807'; } /* '' */
.zan-icon-like-o:before { content: '\e808'; } /* '' */
.zan-icon-like:before { content: '\e809'; } /* '' */
.zan-icon-chat:before { content: '\e80a'; } /* '' */
.zan-icon-shop:before { content: '\e80b'; } /* '' */
.zan-icon-photograph:before { content: '\e80c'; } /* '' */
.zan-icon-add:before { content: '\e80d'; } /* '' */
.zan-icon-add2:before { content: '\e80e'; } /* '' */
.zan-icon-photo:before { content: '\e80f'; } /* '' */
.zan-icon-logistics:before { content: '\e810'; } /* '' */
.zan-icon-edit:before { content: '\e811'; } /* '' */
.zan-icon-passed:before { content: '\e812'; } /* '' */
.zan-icon-cart:before { content: '\e813'; } /* '' */
.zan-icon-arrow:before { content: '\e814'; } /* '' */
.zan-icon-gift:before { content: '\e815'; } /* '' */
.zan-icon-search:before { content: '\e816'; } /* '' */
.zan-icon-clear:before { content: '\e817'; } /* '' */
.zan-icon-success:before { content: '\e818'; } /* '' */
.zan-icon-fail:before { content: '\e819'; } /* '' */

View File

@ -5,13 +5,13 @@
left: 0;
width: 100%;
height: 100%;
overflow: scroll;
overflow: auto;
@e image {
display: block;
width: 100%;
height: auto;
transition: .2s;
transition: .2s ease-out;
position: absolute;
left: 0;
@ -19,11 +19,10 @@
top: 50%;
transform: translate3d(0, -50%, 0);
}
}
@m top {
top: 0;
transform: none;
}
.zan-swipe {
height: 100%;
}
}
}

View File

@ -25,4 +25,6 @@
@import './actionsheet.css';
@import './quantity.css';
@import './progress.css';
@import './toast.css';
@import './uploader.css';
@import './swipe.css';

View File

@ -0,0 +1,7 @@
@define-mixin clearfix {
&::after {
content: '';
display: table;
clear: both;
}
}

View File

@ -3,6 +3,7 @@
@component-namespace zan {
@b picker {
overflow: hidden;
background-color: #fff;
@e toolbar {
height: 40px;
@ -47,6 +48,18 @@
width: 33.333%;
}
}
@m 4 {
.zan-picker-column {
width: 25%;
}
}
@m 5 {
.zan-picker-column {
width: 20%;
}
}
}
}

View File

@ -66,12 +66,14 @@
padding: 1px;
border: 1px solid $c-gray-dark;
border-width: 1px 0;
border-radius: 0;
box-sizing: content-box;
color: $c-gray-darker;
font-size: 14px;
outline: 0;
vertical-align: middle;
text-align: center;
-webkit-appearance: none;
}
}
}

View File

@ -10,14 +10,16 @@
box-sizing: border-box;
padding: 4px 15px;
background-color: #F2F2F2;
@when focus {
@m focus {
.zan-search__input-wrap {
width: 82%;
}
span {
.zan-icon-clear {
display: inline-block;
}
}
@e input-wrap {
position: relative;
width: 90%;
@ -25,12 +27,7 @@
border: 1px solid $c-gray-light;
border-radius: 4px;
background-color: $c-white;
span {
display: none;
position: absolute;
right: 5px;
top: 8px;
}
input {
width: 100%;
height: 14px;
@ -40,14 +37,33 @@
outline: none;
}
}
@e cancel {
display: none;
color: #44BB00;
font-size: 14px;
white-space: nowrap;
@when focus {
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;
}
}
}

View File

@ -13,8 +13,8 @@
text-align: center;
img {
max-width: 100%;
max-height: 100%;
width: 100%;
height: auto;
}
&:first-child {

View File

@ -0,0 +1,38 @@
@import './common/var.css';
@component-namespace zan {
@b toast {
position: fixed;
z-index: 3000;
border-radius: 5px;
background-color: #272727;
opacity: .7;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
font-size: 12px;
color: $c-white;
text-align: center;
@m loading {
padding: 45px;
}
@m text {
padding: 12px;
min-width: 200px;
}
@m default {
width: 120px;
height: 120px;
.zan-toast__icon {
padding: 20px;
font-size: 36px;
}
.zan-toast__text {
padding-bottom: 20px;
}
}
}
}

View File

@ -0,0 +1,24 @@
@component-namespace zan {
@b uploader {
position: relative;
display: inline-block;
@e input {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
display: block;
width: 100%;
height: 100%;
opacity: 0;
cursor:pointer;
}
input[type="file" i]::-webkit-file-upload-button {
cursor:pointer;
}
}
}

View File

@ -30,8 +30,11 @@ import Row from '../packages/row/index.js';
import Actionsheet from '../packages/actionsheet/index.js';
import Quantity from '../packages/quantity/index.js';
import Progress from '../packages/progress/index.js';
import Toast from '../packages/toast/index.js';
import Uploader from '../packages/uploader/index.js';
import Swipe from '../packages/swipe/index.js';
import SwipeItem from '../packages/swipe-item/index.js';
import DatetimePicker from '../packages/datetime-picker/index.js';
const install = function(Vue) {
if (install.installed) return;
@ -64,8 +67,10 @@ const install = function(Vue) {
Vue.component(Actionsheet.name, Actionsheet);
Vue.component(Quantity.name, Quantity);
Vue.component(Progress.name, Progress);
Vue.component(Uploader.name, Uploader);
Vue.component(Swipe.name, Swipe);
Vue.component(SwipeItem.name, SwipeItem);
Vue.component(DatetimePicker.name, DatetimePicker);
};
// auto install
@ -75,7 +80,7 @@ if (typeof window !== 'undefined' && window.Vue) {
module.exports = {
install,
version: '0.0.31',
version: '0.0.38',
Button,
Switch,
Field,
@ -108,6 +113,9 @@ module.exports = {
Actionsheet,
Quantity,
Progress,
Toast,
Uploader,
Swipe,
SwipeItem
SwipeItem,
DatetimePicker
};

View File

@ -2,13 +2,15 @@ import Vue from 'vue';
import { addClass } from 'src/utils/dom';
const getModal = function() {
let modalDom = PopupManager.modalDom;
let modalDom = window.popupContext && window.popupContext.modalDom;
if (modalDom) {
PopupManager.popupContext.hasModal = true;
window.popupContext.hasModal = true;
} else {
PopupManager.popupContext.hasModal = false;
window.popupContext.hasModal = false;
modalDom = document.createElement('div');
PopupManager.modalDom = modalDom;
window.popupContext.modalDom = modalDom;
modalDom.addEventListener('touchmove', function(event) {
event.preventDefault();
@ -75,7 +77,11 @@ const PopupManager = {
addClass(modalDom, 'zan-modal');
document.body.appendChild(modalDom);
if (dom && dom.parentNode && dom.parentNode.nodeType !== 11) {
dom.parentNode.appendChild(modalDom);
} else {
document.body.appendChild(modalDom);
}
if (zIndex) {
modalDom.style.zIndex = zIndex;

View File

@ -1,57 +0,0 @@
import Vue from 'vue';
let id = 0;
class Creater {
constructor(Compo, propsData) {
let Ctor = Vue.extend(Compo);
this.vue = new Ctor({ propsData });
this.el = null;
}
mount() {
const elem = exports.createElm();
this.vue.$mount(elem);
this.el = this.vue.$el;
}
triggerEvent(name, ...opts) {
let eventName;
let elem = this.el;
if (/^mouse|click/.test(name)) {
eventName = 'MouseEvents';
} else if (/^key/.test(name)) {
eventName = 'KeyboardEvent';
} else {
eventName = 'HTMLEvents';
}
const evt = document.createEvent(eventName);
evt.initEvent(name, ...opts);
elem.dispatchEvent
? elem.dispatchEvent(evt)
: elem.fireEvent('on' + name, evt);
return elem;
}
destroy() {
this.el &&
this.el.parentNode &&
this.el.parentNode.removeChild(this.el);
}
}
exports.createElm = function() {
const elm = document.createElement('div');
elm.id = 'app' + ++id;
document.body.appendChild(elm);
return elm;
};
exports.createVue = function(Compo, propsData = {}) {
return new Creater(Compo, propsData);
};

View File

@ -1,16 +1,17 @@
import { createVue } from '../creater';
import ActionSheet from 'packages/actionsheet';
import { mount } from 'avoriaz';
describe('ActionSheet', () => {
let vm;
let wrapper;
afterEach(() => {
vm && vm.destroy();
wrapper && wrapper.destroy();
});
it('create', () => {
vm = createVue(ActionSheet);
vm.mount();
wrapper = mount(ActionSheet, {
propsData: {}
});
expect(vm.el.classList.contains('zan-actionsheet')).to.true;
expect(wrapper.hasClass('zan-actionsheet')).to.be.true;
});
});

View File

@ -1,20 +1,19 @@
import { createVue } from '../creater';
import Card from 'packages/card';
import { mount } from 'avoriaz';
describe('Card', () => {
let vm;
let wrapper;
afterEach(() => {
vm && vm.destroy();
wrapper && wrapper.destroy();
});
it('create', () => {
vm = createVue(Card, {
title: 'card',
desc: 'card',
thumb: 'https://img.yzcdn.cn/upload_files/2017/02/17/FnDwvwHmU-OiqsbjAO5X7wh1KWrR.jpg!100x100.jpg'
wrapper = mount(Card, {
propsData: {
thumb: 'thumb'
}
});
vm.mount();
expect(vm.el.classList.contains('zan-card')).to.true;
expect(wrapper.hasClass('zan-card')).to.be.true;
});
});

View File

@ -1,16 +1,17 @@
import { createVue } from '../creater';
import CellGroup from 'packages/cell-group';
import { mount } from 'avoriaz';
describe('Cell', () => {
let vm;
describe('CellGroup', () => {
let wrapper;
afterEach(() => {
vm && vm.destroy();
wrapper && wrapper.destroy();
});
it('cell group create', () => {
vm = createVue(CellGroup);
vm.mount();
it('create', () => {
wrapper = mount(CellGroup, {
propsData: {}
});
expect(vm.el.classList.contains('zan-cell-group')).to.true;
expect(wrapper.hasClass('zan-cell-group')).to.be.true;
});
});

View File

@ -1,16 +1,17 @@
import { createVue } from '../creater';
import Checkbox from 'packages/checkbox';
import { mount } from 'avoriaz';
describe('Checkbox', () => {
let vm;
let wrapper;
afterEach(() => {
vm && vm.destroy();
wrapper && wrapper.destroy();
});
it('create', () => {
vm = createVue(Checkbox);
vm.mount();
wrapper = mount(Checkbox, {
propsData: {}
});
expect(vm.el.classList.contains('zan-checkbox')).to.true;
expect(wrapper.hasClass('zan-checkbox')).to.be.true;
});
});

View File

@ -1,16 +1,17 @@
import { createVue } from '../creater';
import Field from 'packages/field';
import { mount } from 'avoriaz';
describe('Field', () => {
let vm;
let wrapper;
afterEach(() => {
vm && vm.destroy();
wrapper && wrapper.destroy();
});
it('create', () => {
vm = createVue(Field);
vm.mount();
wrapper = mount(Field, {
propsData: {}
});
expect(vm.el.classList.contains('zan-field')).to.true;
expect(wrapper.hasClass('zan-field')).to.be.true;
});
});

View File

@ -0,0 +1,67 @@
import Loading from 'packages/loading';
import { mount } from 'avoriaz';
describe('Loading', () => {
let wrapper;
afterEach(() => {
wrapper && wrapper.destroy();
});
it('create default', () => {
wrapper = mount(Loading);
expect(wrapper.hasClass('zan-loading')).to.be.true;
});
it('create gradient-circle black', () => {
wrapper = mount(Loading, {
propsData: {
type: 'gradient-circle',
color: 'black'
}
});
const spinner = wrapper.find('.zan-loading__spinner')[0];
expect(spinner.hasClass('zan-loading__spinner--gradient-circle')).to.be.true;
expect(spinner.hasClass('zan-loading__spinner--black')).to.be.true;
});
it('create gradient-circle white', () => {
wrapper = mount(Loading, {
propsData: {
type: 'gradient-circle',
color: 'white'
}
});
const spinner = wrapper.find('.zan-loading__spinner')[0];
expect(spinner.hasClass('zan-loading__spinner--gradient-circle')).to.be.true;
expect(spinner.hasClass('zan-loading__spinner--white')).to.be.true;
});
it('create circle black', () => {
wrapper = mount(Loading, {
propsData: {
type: 'circle',
color: 'black'
}
});
const spinner = wrapper.find('.zan-loading__spinner')[0];
expect(spinner.hasClass('zan-loading__spinner--circle')).to.be.true;
expect(spinner.hasClass('zan-loading__spinner--black')).to.be.true;
});
it('create circle white', () => {
wrapper = mount(Loading, {
propsData: {
type: 'circle',
color: 'white'
}
});
const spinner = wrapper.find('.zan-loading__spinner')[0];
expect(spinner.hasClass('zan-loading__spinner--circle')).to.be.true;
expect(spinner.hasClass('zan-loading__spinner--white')).to.be.true;
});
});

View File

@ -1,83 +1,100 @@
import Switch from 'packages/switch';
import { createVue } from '../creater';
import ZanLoading from 'packages/loading';
import { mount } from 'avoriaz';
// import { stub } from 'sinon';
describe('Switch', () => {
let vm;
let wrapper;
afterEach(() => {
vm && vm.destroy();
wrapper && wrapper.destroy();
});
it('create', () => {
vm = createVue(Switch, {
checked: true
it('create on switch', () => {
wrapper = mount(Switch, {
propsData: {
checked: true
}
});
vm.mount();
expect(vm.el.classList.contains('zan-switch')).to.true;
expect(vm.el.classList.contains('zan-switch--on')).to.true;
expect(wrapper.hasClass('zan-switch')).to.be.true;
expect(wrapper.hasClass('zan-switch--on')).to.be.true;
});
it('create off switch', () => {
vm = createVue(Switch, {
checked: false
});
vm.mount();
expect(vm.el.classList.contains('zan-switch')).to.true;
});
it('switch click default', done => {
vm = createVue({
data() {
return {
checked: false
};
},
components: {
'zan-switch': Switch
},
template: `
<zan-switch :checked="checked"></zan-switch>
`
});
vm.mount();
expect(vm.el.classList.contains('zan-switch')).to.true;
expect(vm.el.classList.contains('zan-switch--off')).to.true;
vm.el.click();
setTimeout(() => {
expect(vm.el.classList.contains('zan-switch--off')).to.true;
done();
});
});
it('switch click', done => {
vm = createVue({
data() {
return {
checked: false
};
},
components: {
'zan-switch': Switch
},
template: `
<zan-switch :checked="checked" :onChange="handleClick"></zan-switch>
`,
methods: {
handleClick(e) {
this.checked = !this.checked;
}
wrapper = mount(Switch, {
propsData: {
checked: false
}
});
vm.mount();
expect(vm.el.classList.contains('zan-switch')).to.true;
expect(vm.el.classList.contains('zan-switch--off')).to.true;
vm.el.click();
setTimeout(() => {
expect(vm.el.classList.contains('zan-switch--on')).to.true;
done();
expect(wrapper.hasClass('zan-switch')).to.be.true;
expect(wrapper.hasClass('zan-switch--off')).to.be.true;
});
it('create loading switch', () => {
wrapper = mount(Switch, {
propsData: {
loading: true
}
});
const loading = wrapper.find(ZanLoading)[0];
expect(wrapper.hasClass('zan-switch')).to.be.true;
expect(loading.isVueComponent).to.be.true;
});
it('loading switch should be unclickable', () => {
wrapper = mount(Switch, {
propsData: {
loading: true,
checked: true
}
});
expect(wrapper.hasClass('zan-switch--on')).to.be.true;
wrapper.simulate('click');
expect(wrapper.hasClass('zan-switch--on')).to.be.true;
});
it('create disabled switch', () => {
wrapper = mount(Switch, {
propsData: {
disabled: true
}
});
expect(wrapper.hasClass('zan-switch')).to.be.true;
expect(wrapper.hasClass('zan-switch--disabled')).to.be.true;
});
it('disabled switch should be unclickable', () => {
wrapper = mount(Switch, {
propsData: {
disabled: true,
checked: false
}
});
expect(wrapper.hasClass('zan-switch--off')).to.be.true;
wrapper.simulate('click');
expect(wrapper.hasClass('zan-switch--off')).to.be.true;
});
it('click event should fire change event', () => {
wrapper = mount(Switch, {
propsData: {
checked: false
},
methods: {
}
});
const eventStub = sinon.stub(wrapper.vm, '$emit');
expect(wrapper.hasClass('zan-switch--off')).to.be.true;
wrapper.simulate('click');
expect(eventStub.calledOnce).to.be.true;
expect(eventStub.calledWith('change')).to.be.true;
});
});