Browse Source

Merge branch 'v2' of https://github.com/jdf2e/nutui into v2

lilinsen 7 years ago
parent
commit
c5fbdebb82

+ 6 - 0
CHANGELOG.md

@@ -1,6 +1,12 @@
 ## 2.0.3
 
+`2019-1-25`
+
+* :sparkles: 新增返回顶部`BackTop`组件
 * :bug: 修复`Swiper`组件 slideChangeEnd 事件的一个bug
+* :zap: `Rate`组件增加允许设置只读和间距
+* :zap: 文档MD转Vue增加文件缓存功能,提升文档预览和构建速度
+* :zap: 示例页面增加评分推荐
 
 ## 2.0.2
 

+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "@nutui/nutui",
-  "version": "2.0.2",
+  "version": "2.0.3",
   "description": "一套轻量级移动端Vue组件库",
   "typings": "dist/types/index.d.ts",
   "main": "dist/nutui.js",

+ 2 - 3
scripts/mdToVue.js

@@ -1,5 +1,5 @@
 const fs = require('fs');
-var path = require('path');
+const path = require('path');
 //hash获取工具
 let { hashElement } = require('folder-hash');
 //marked转换工具
@@ -83,7 +83,7 @@ function createdFile(output, sorce, ishasCode) {
     var bufs = `<template><div  @click="dsCode">
         <div v-if="content" class="layer">
           <pre><span class="close-box" @click="closelayer"></span><div v-html="content"></div></pre>
-        </div>`+ res + `<nut-backtop></nut-backtop></div></template><script>import root from '../root.js';
+        </div>`+ res + `<nut-backtop :right="50" :bottom="50"></nut-backtop></div></template><script>import root from '../root.js';
         export default {
             mixins:[root]
         }</script>`;
@@ -187,7 +187,6 @@ function checkIsexists (path,callback){
     let pathFileName = path.replace(/[^a-zA-Z]/g,'');
     let cacheName = './local'+pathFileName+'.cache';
     fs.exists(cacheName, res=>{
-        console.log(res)
         if(!res){
             fs.writeFile(cacheName,'','utf8',()=>{
                 callback(cacheName)

+ 1 - 1
sites/demo/app.js

@@ -69,7 +69,7 @@ OfflinePluginRuntime.install({
     console.log('PWA缓存有更新,需要刷新页面');
 
     app.$dialog({
-      title: "您正在浏览的页面有更新,请刷新",
+      title: "当前页面有新版本,请刷新",
       noCloseBtn: true,
       noOkBtn: true,
       cancelBtnTxt: "刷新页面",

+ 1 - 2
sites/demo/app.vue

@@ -51,7 +51,6 @@ h4 {
   box-sizing: border-box;
 }
 .demo {
-  padding-left: 8px;
-  padding-right: 8px;
+  padding:0 8px 20px 8px;
 }
 </style>

+ 9 - 4
sites/demo/view/index.vue

@@ -16,6 +16,7 @@
             <a :href="'./demo.html#/'+cpt.name">
               {{cpt.name}}
               <span>{{cpt.chnName}}</span>
+              <nut-rate class="cpt-rec" v-if="cpt.star" :total="5" :value="cpt.star" :size="8" :spacing="3" :readOnly="true"></nut-rate>
             </a>
           </li>
         </template>
@@ -151,12 +152,13 @@ export default {
     border-top: 1px solid #edeef1;
     border-radius: 2px;
     background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 11 30'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cpath d='M-11 0h30v30h-30z'/%3E%3Cpath d='M7.757 15C5.241 10.755 2.727 6.51.21 2.266A1.5 1.5 0 0 1 2.79.736l8 13.499c.28.472.28 1.058 0 1.53l-8 13.5a1.499 1.499 0 1 1-2.58-1.53L7.757 15z' fill='rgb(132,132,132)' /%3E%3C/g%3E%3C/svg%3E")
-      no-repeat right 15px center;
+      no-repeat right 10px center;
     background-size: 10px 10px;
     a {
-      display: block;
+      display: flex;
+      align-items:center;
       height: 100%;
-      padding: 20px;
+      padding: 20px 10px 20px 10px;
       text-decoration: none;
       color: #2e2d2d;
     }
@@ -176,7 +178,10 @@ li {
   margin: 0;
   padding: 0;
 }
-
+.cpt-rec{
+  display:inline-flex;
+  margin-left:5px;
+}
 @-webkit-keyframes swing {
   from {
     -webkit-transform: perspective(400px) rotate3d(0, 1, 0, 90deg);

+ 1 - 1
sites/doc/app.vue

@@ -9,7 +9,7 @@
           <a href="#/index" class="logo-link">
             <img src="./asset/css/i/nut.png" alt>
           </a>
-          <span class="version">{{version}}</span>
+          <span class="version">v{{version}}</span>
         </div>
         <div class="h-nav">
           <search/>

BIN
src/assets/img/loading.png


+ 33 - 10
src/config.json

@@ -26,6 +26,7 @@
       "type": "method",
       "showDemo": true,
       "desc": "模态弹窗,支持按钮交互,支持图片弹窗。",
+      "star": 5,
       "author": "Frans"
     },
     {
@@ -46,6 +47,7 @@
       "desc": "轻提示。",
       "type": "method",
       "showDemo": true,
+      "star": 4,
       "author": "Frans"
     },
     {
@@ -56,6 +58,7 @@
       "desc": "从底部弹出的动作菜单面板。",
       "type": "component",
       "showDemo": true,
+      "star": 5,
       "author": "iris"
     },
     {
@@ -66,6 +69,7 @@
       "desc": "常用于平级区域大块内容的的收纳和展现。",
       "type": "component",
       "showDemo": true,
+      "star": 3,
       "author": "甄玉磊"
     },
     {
@@ -86,6 +90,7 @@
       "desc": "用于不同模块以之间的切换",
       "type": "component",
       "showDemo": true,
+      "star": 3,
       "author": "甄玉磊"
     },
     {
@@ -96,6 +101,7 @@
       "desc": "日历",
       "type": "component",
       "showDemo": true,
+      "star": 5,
       "author": "iris"
     },
     {
@@ -106,6 +112,7 @@
       "desc": "日期选择",
       "type": "component",
       "showDemo": true,
+      "star": 5,
       "author": "iris"
     },
     {
@@ -129,6 +136,16 @@
       "author": "wangyue"
     },
     {
+      "name": "Switch",
+      "version": "1.0.0",
+      "sort": "1",
+      "chnName": "开关",
+      "type": "component",
+      "showDemo": true,
+      "desc": "滑动开关,通过点击使按钮左右滑动,同时触发对应的开关状态",
+      "author": "Frans"
+    },
+    {
       "version": "1.0.0",
       "name": "Slider",
       "sort": "1",
@@ -136,6 +153,7 @@
       "desc": "滑动输入器,用于在数值区间/自定义区间内进行选择。",
       "type": "component",
       "showDemo": true,
+      "star": 4,
       "author": "Frans"
     },
     {
@@ -146,6 +164,7 @@
       "desc": "区间选择器组件",
       "type": "component",
       "showDemo": true,
+      "star": 4,
       "author": "famanoder"
     },
     {
@@ -156,6 +175,7 @@
       "desc": "提供多个选项集合供用户选择其中一项。",
       "type": "component",
       "showDemo": true,
+      "star": 5,
       "author": "iris"
     },
     {
@@ -246,6 +266,7 @@
       "desc": "用于快速的评级操作,或对评价进行展示。",
       "type": "component",
       "showDemo": true,
+      "star": 4,
       "author": "永无止晋"
     },
     {
@@ -256,6 +277,7 @@
       "desc": "常用于一组图片或卡片轮播,当内容空间不足时,可以用走马灯的形式进行收纳,进行轮播展现。",
       "type": "component",
       "showDemo": true,
+      "star": 5,
       "author": "wangnan31"
     },
     {
@@ -276,6 +298,7 @@
       "type": "component",
       "sort": "1",
       "showDemo": true,
+      "star": 3,
       "author": "famanoder"
     },
     {
@@ -309,16 +332,6 @@
       "author": "苏子刚"
     },
     {
-      "name": "Switch",
-      "version": "1.0.0",
-      "sort": "1",
-      "chnName": "开关",
-      "type": "component",
-      "showDemo": true,
-      "desc": "滑动开关,通过点击使按钮左右滑动,同时触发对应的开关状态",
-      "author": "Frans"
-    },
-    {
       "name": "Radio",
       "version": "1.0.0",
       "sort": "1",
@@ -380,6 +393,16 @@
     },
     {
       "version": "1.0.0",
+      "name": "Scroller",
+      "chnName": "滚动",
+      "desc": "滚动组件",
+      "type": "component",
+      "sort": "1",
+      "showDemo": false,
+      "author": "iris"
+    },
+    {
+      "version": "1.0.0",
       "name": "BackTop",
       "chnName": "回到顶部",
       "desc": "用于页面内容高度过长,快捷回到顶部使用。",

+ 1 - 1
src/packages/cell/cell.vue

@@ -7,7 +7,7 @@
             </div>
             <div class="nut-cell-right">
                 <span class="nut-cell-desc"><slot name="desc">{{desc}}</slot></span>
-                <span class="nut-cell-icon"><slot name="icon" v-if="showIcon"><img src="data:image/svg+xml,%3Csvg viewBox='0 0 5 10' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.215 9.757l3.577-4.17a.931.931 0 0 0 0-1.173L1.215.244a.642.642 0 0 0-1.007 0 .929.929 0 0 0 0 1.172L3.283 5 .208 8.584a.93.93 0 0 0 0 1.173.643.643 0 0 0 1.007 0z' fill='%23c8c8cd'/%3E%3C/svg%3E" alt=""></slot></span>
+                <span class="nut-cell-icon"><slot name="icon" v-if="showIcon"><img src="data:image/svg+xml,%3Csvg viewBox='0 0 5 10' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.215 9.757l3.577-4.17a.931.931 0 0 0 0-1.173L1.215.244a.642.642 0 0 0-1.007 0 .929.929 0 0 0 0 1.172L3.283 5 .208 8.584a.93.93 0 0 0 0 1.173.643.643 0 0 0 1.007 0z' fill='rgb(200,200,205)'/%3E%3C/svg%3E" alt=""></slot></span>
             </div>
         </div>
     </a>

+ 1 - 1
src/packages/cell/demo.vue

@@ -18,7 +18,7 @@
             </nut-cell>
             <nut-cell :showIcon="true">
                 <span slot="title">通过Slot自定义右侧ICON</span>
-                <img slot="icon" src="data:image/svg+xml, %3Csvg version='1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M16 3.2c0-.7-.5-1.2-1.2-1.2-.3 0-.7.1-.9.4-2.4 2.5-6.2 6.8-7.7 8.5-.1.1-.2.2-.4 0L2.1 7.2c-.3-.3-.6-.4-.9-.4-.7 0-1.2.5-1.2 1.1 0 .3.1.6.3.8l.1.1 4.9 4.9c.8.8 1.6 0 2-.5 4.1-4.4 7.9-8.6 8.4-9.2.2-.3.3-.5.3-.8z' fill='%23737383'/%3E%3C/svg%3E" alt="">
+                <img slot="icon" src="data:image/svg+xml, %3Csvg version='1' xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M16 3.2c0-.7-.5-1.2-1.2-1.2-.3 0-.7.1-.9.4-2.4 2.5-6.2 6.8-7.7 8.5-.1.1-.2.2-.4 0L2.1 7.2c-.3-.3-.6-.4-.9-.4-.7 0-1.2.5-1.2 1.1 0 .3.1.6.3.8l.1.1 4.9 4.9c.8.8 1.6 0 2-.5 4.1-4.4 7.9-8.6 8.4-9.2.2-.3.3-.5.3-.8z' fill='rgb(115,115,131)'/%3E%3C/svg%3E" alt="">
             </nut-cell>
         </div>
     </div>

+ 2 - 2
src/packages/dialog/dialog.scss

@@ -63,7 +63,7 @@ body.dialog-open {
     font-size: 20px;
     text-align: center;
     text-decoration: none;
-    background: url("data:image/svg+xml, %3Csvg width='30' height='30' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23848484' fill-rule='evenodd'%3E%3Cpath d='M.44 2.56A1.5 1.5 0 1 1 2.56.44l27 27a1.5 1.5 0 1 1-2.12 2.12L15 17.123 2.56 29.56A1.5 1.5 0 1 1 .44 27.44L12.878 15 .44 2.56zM27.44.44a1.5 1.5 0 1 1 2.12 2.12l-9 9a1.5 1.5 0 1 1-2.12-2.12l9-9z'/%3E%3C/g%3E%3C/svg%3E") no-repeat center;
+    background: url("data:image/svg+xml, %3Csvg width='30' height='30' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='rgb(132,132,132)' fill-rule='evenodd'%3E%3Cpath d='M.44 2.56A1.5 1.5 0 1 1 2.56.44l27 27a1.5 1.5 0 1 1-2.12 2.12L15 17.123 2.56 29.56A1.5 1.5 0 1 1 .44 27.44L12.878 15 .44 2.56zM27.44.44a1.5 1.5 0 1 1 2.12 2.12l-9 9a1.5 1.5 0 1 1-2.12-2.12l9-9z'/%3E%3C/g%3E%3C/svg%3E") no-repeat center;
     background-size: 13px 13px;
     img {
         height: 13px;
@@ -91,7 +91,7 @@ body.dialog-open {
         text-decoration: none;
         border: 2px solid #FFF;
         border-radius: 50%;
-        background: url("data:image/svg+xml, %3Csvg width='30' height='30' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='%23ffffff' fill-rule='evenodd'%3E%3Cpath d='M.44 2.56A1.5 1.5 0 1 1 2.56.44l27 27a1.5 1.5 0 1 1-2.12 2.12L15 17.123 2.56 29.56A1.5 1.5 0 1 1 .44 27.44L12.878 15 .44 2.56zM27.44.44a1.5 1.5 0 1 1 2.12 2.12l-9 9a1.5 1.5 0 1 1-2.12-2.12l9-9z'/%3E%3C/g%3E%3C/svg%3E") no-repeat center;
+        background: url("data:image/svg+xml, %3Csvg width='30' height='30' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='rgb(255,255,255)' fill-rule='evenodd'%3E%3Cpath d='M.44 2.56A1.5 1.5 0 1 1 2.56.44l27 27a1.5 1.5 0 1 1-2.12 2.12L15 17.123 2.56 29.56A1.5 1.5 0 1 1 .44 27.44L12.878 15 .44 2.56zM27.44.44a1.5 1.5 0 1 1 2.12 2.12l-9 9a1.5 1.5 0 1 1-2.12-2.12l9-9z'/%3E%3C/g%3E%3C/svg%3E") no-repeat center;
         background-size: 13px 13px;
         img {
             height: 13px;

+ 2 - 1
src/packages/dialog/doc.md

@@ -88,7 +88,8 @@ this.$dialog({
 | noFooter | 是否隐藏底部按钮栏 | Boolean | false
 | noOkBtn | 是否隐藏确定按钮 | Boolean | false
 | noCancelBtn | 是否隐藏取消按钮 | Boolean | false
-| okBtnTxt | 确定按钮文案 | String | ”确定“
+| cancelBtnTxt | 取消按钮文案 | String | ”取 s消“
+| okBtnTxt | 确定按钮文案 | String | ”确 定“
 | okBtnDisabled | 禁用确定按钮 | Boolean | false
 | cancelAutoClose | 取消按钮是否默认关闭弹窗 | Boolean | true
 | textAlign | 文字对齐方向,可选值同css的text-align | String | "center"

+ 1 - 1
src/packages/navbar/navbar.scss

@@ -18,7 +18,7 @@
                 display: inline-block;
                 width: 20px;
                 height: 20px;
-                background: url("data:image/svg+xml, %3Csvg width='19' height='36' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.552 35.569a1.5 1.5 0 0 1-2.121-.017c-5.952-6.05-9.905-10.1-15.857-16.15a2 2 0 0 1 0-2.804c5.952-6.05 9.905-10.1 15.857-16.15a1.5 1.5 0 1 1 2.138 2.104C12.847 8.368 9.125 12.184 3.403 18c5.722 5.816 9.444 9.632 15.166 15.448a1.5 1.5 0 0 1-.017 2.121z' fill='%239E9E9E' fill-rule='evenodd'/%3E%3C/svg%3E") no-repeat;
+                background: url("data:image/svg+xml, %3Csvg width='19' height='36' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M18.552 35.569a1.5 1.5 0 0 1-2.121-.017c-5.952-6.05-9.905-10.1-15.857-16.15a2 2 0 0 1 0-2.804c5.952-6.05 9.905-10.1 15.857-16.15a1.5 1.5 0 1 1 2.138 2.104C12.847 8.368 9.125 12.184 3.403 18c5.722 5.816 9.444 9.632 15.166 15.448a1.5 1.5 0 0 1-.017 2.121z' fill='rgb(158,158,158)' fill-rule='evenodd'/%3E%3C/svg%3E") no-repeat;
                 background-size: contain;
                 vertical-align: middle;
             }

File diff suppressed because it is too large
+ 20 - 19
src/packages/noticebar/noticebar.scss


File diff suppressed because it is too large
+ 1 - 1
src/packages/noticebar/noticebar.vue


+ 20 - 4
src/packages/rate/demo.vue

@@ -10,13 +10,13 @@
             </nut-cell>
         </div>
 
-        <h4>事件</h4>
+        <h4>只读</h4>
         <div>
             <nut-cell>
-                <span slot="title"><nut-rate @click="onClick"></nut-rate></span>
+                <span slot="title"><nut-rate v-model="val2" :readOnly="true"></nut-rate></span>
             </nut-cell>
             <nut-cell>
-                <span slot="title">结果:{{result}}</span>
+                <span slot="title">结果:{{val2}}</span>
             </nut-cell>
         </div>
 
@@ -24,11 +24,21 @@
         <div>
             <nut-cell>
                 <span slot="title"><nut-rate
-                :size="35"
+                :size="30"
                 ></nut-rate></span>
             </nut-cell>
         </div>
 
+        <h4>事件</h4>
+        <div>
+            <nut-cell>
+                <span slot="title"><nut-rate @click="onClick"></nut-rate></span>
+            </nut-cell>
+            <nut-cell>
+                <span slot="title">结果:{{result}}</span>
+            </nut-cell>
+        </div>
+
         <h4>自定义ICON</h4>
         <div>
             <nut-cell>
@@ -38,6 +48,7 @@
                 ></nut-rate></span>
             </nut-cell>
         </div>
+        
     </div>
 </template>
 
@@ -46,7 +57,9 @@ export default {
     data(){
         return{
             val:4,
+            val2:2,
             result:'',
+            result2:'',
             icon1:`url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='rgb(255,0,0)' d='M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zM6.5 9a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm7 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm2.16 3H4.34a6 6 0 0 0 11.32 0z'/%3E%3C/svg%3E")`,
             icon2:`url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3E%3Cpath fill='rgb(255,0,0)' d='M10 20a10 10 0 1 1 0-20 10 10 0 0 1 0 20zm0-2a8 8 0 1 0 0-16 8 8 0 0 0 0 16zM6.5 9a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zm7 0a1.5 1.5 0 1 1 0-3 1.5 1.5 0 0 1 0 3zM7 13h6a1 1 0 0 1 0 2H7a1 1 0 0 1 0-2z'/%3E%3C/svg%3E")`
         }
@@ -55,6 +68,9 @@ export default {
     methods:{
         onClick(idx){
             this.result = '您点击了第'+idx+'个!';
+        },
+        onClick2(idx){
+            this.result2 = '您点击了第'+idx+'个!';
         }
     }
 }

+ 16 - 3
src/packages/rate/doc.md

@@ -5,10 +5,22 @@
 ## 基础用法
 
 ```html
-<nut-rate>
+<nut-rate 
+    v-model="val"
+>
 </nut-rate>
 ```
 
+只读
+```html
+<nut-rate 
+    v-model="val"
+    :readOnly="true"
+>
+</nut-rate>
+```
+
+
 绑定事件
 
 ```html
@@ -41,13 +53,14 @@
 | 字段 | 说明 | 类型 | 默认值
 | ----- | ----- | ----- | -----
 | total | star 总数 | Number | 5
-| value | 当前 star 数,可使用 v-model 双向绑定数据 | Number | 3
+| value | 当前 star 数,可使用 v-model 双向绑定数据 | Number | 3
 | size | star 大小 | Number | 25
 | spacing | 两个star的间距 | Number | 20
+| readOnly | 是否只读 | Boolean | false
 | uncheckedIcon | 使用图标(未选中) | String | -
 | checkedIcon | 使用图标(选中) | String | -
 
 ## Event
 | 字段 | 说明 | 回调参数 
 |----- | ----- | ----- 
-| click | 点击star触发 | star的index
+| click | 点击star触发 | star的index

+ 11 - 6
src/packages/rate/rate.vue

@@ -38,9 +38,9 @@ export default {
             type:String,
             default:null
         },
-        testProp:{
-            type:String,
-            default:null
+        readOnly:{
+            type:Boolean,
+            default: false
         },
         spacing:{
             type:[String,Number],
@@ -57,9 +57,14 @@ export default {
     },
     methods: {
         onClick($event,idx){
-            this.current = idx;
-            this.$emit('input',idx);
-            this.$emit('click',idx);
+            if(this.readOnly){
+                this.$emit('input',this.current);
+                this.$emit('click',this.current);
+            }else{
+                this.current = idx;
+                this.$emit('input',idx);
+                this.$emit('click',idx);
+            }
         }
     }
 }

+ 200 - 0
src/packages/scroller/demo.vue

@@ -0,0 +1,200 @@
+<template>
+    <div class="demo-list">
+        <nut-noticebar
+            :closeMode="true"
+            v-if="!isMobile"
+        >此 Demo 在 PC 端浏览器与移动端浏览器体验差异较大,建议在 Android 或 iOS 设备上体验。</nut-noticebar>
+        <h4>横向用法</h4>
+        <div class="hor-panel">
+            <nut-scroller :list-data="listData"  
+                :is-un-more="isUnMore" 
+                :is-loading="isLoading"
+                @loadMore="loadMoreHor"
+                @jump="jump()"
+            >
+                <div slot="list" class="nut-hor-list-item" v-for="(item, index) of listData" :key="index">{{index}}</div>
+                <slot slot="more"><div class="nut-hor-jump-more">释放查看更多</div></slot>
+            </nut-scroller>
+        </div>
+        <h4>竖向用法</h4>
+        <div class="vert-panel">
+            <nut-scroller
+                :is-un-more="isUnMore1" 
+                :is-loading="isLoading1"
+                :type="'vertical'"
+                @loadMore="loadMoreVert"
+                @pulldown="pulldown"
+            > 
+                <div slot="list" class="nut-vert-list-panel">
+                    <div class="nut-vert-list-item" v-for="(item, index) of listData1" :key="index">
+                        {{index}}
+                    </div>
+                </div>
+            </nut-scroller>
+        </div>
+        <h4>竖向不满一屏用法</h4>
+        <div class="vert-panel">
+            <nut-scroller
+                :is-un-more="isUnMore2" 
+                :is-loading="isLoading2"
+                :type="'vertical'"
+                @loadMore="loadMoreVert2"
+                @pulldown="pulldown2"
+            > 
+                <div slot="list" class="nut-vert-list-panel">
+                    <div class="nut-vert-list-item" v-for="(item, index) of listData2" :key="index">
+                        {{index}}
+                    </div>
+                </div>
+            </nut-scroller>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    data() {
+        return {
+            listData: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+            listData1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],
+            listData2: [1, 2],
+            isUnMore: false,
+            isLoading: false,
+            maxPages: 3,
+            page: 2,
+            timers: null,
+            isUnMore1: false,
+            isLoading1: false,
+            page1: 2,
+            maxPages2: 1,
+            isUnMore2: false,
+            isLoading2: false,
+            page2: 2
+        };
+    },
+
+    methods: {
+        loadMoreHor() {
+            this.isLoading = true;
+            if (this.page > this.maxPages) {
+                this.isUnMore = true;
+                this.isLoading = false;
+            } else {
+                this.timer = setTimeout(() => {
+                    this.page == ++this.page;
+                    this.listData = [...this.listData, ...[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]];
+                    console.log(this.listData);
+                    this.isLoading = false;
+                }, 300);
+            }
+        },
+
+        jump() {
+            console.log('跳转');
+            location.href = 'http://www.jd.com';
+        },
+
+        loadMoreVert() {
+            this.isLoading1 = true;
+            if (this.page1 > this.maxPages) {
+                this.isUnMore1 = true;
+                this.isLoading1 = false;
+            } else {
+                this.timer = setTimeout(() => {
+                    this.page1 = ++this.page1;
+                    this.listData1 = [...this.listData1, ...[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]];
+                    console.log(this.listData1);
+                    this.isLoading1 = false;
+                }, 2000);
+            }
+        },
+
+        pulldown() {
+            this.isLoading1 = true;
+            this.timer = setTimeout(() => {
+                this.page1 = 2;
+                this.listData1 = [11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
+                this.isLoading1 = false;
+                this.isUnMore1 = false;
+            }, 2000);
+        },
+
+
+        loadMoreVert2() {
+            this.isLoading2 = true;
+            if (this.page2 > this.maxPages2) {
+                this.isUnMore2 = true;
+                this.isLoading2 = false;
+            } else {
+                this.timer = setTimeout(() => {
+                    this.page2 = ++this.page1;
+                    this.listData2 = [...this.listData1, ...[11, 12, 13, 14, 15, 16, 17, 18, 19, 20]];
+                    console.log(this.listData1);
+                    this.isLoading2 = false;
+                }, 2000);
+            }
+        },
+
+        pulldown2() {
+            this.isLoading2 = true;
+            this.timer = setTimeout(() => {
+                this.page2 = 2;
+                this.listData2 = [11, 12];
+                this.isLoading2 = false;
+                this.isUnMore2 = false;
+            }, 2000);
+        }
+    },
+    
+    destroyed() {
+        clearTimeout(this.timer);
+    }
+};
+</script>
+
+<style lang="scss" scoped>
+.hor-panel{
+    height: 100px;
+    background-color: mix($primary-color, #FFF, 10%);
+}
+.nut-hor-list{
+    .nut-hor-list-item{
+        display: flex;
+        align-content: center;
+        justify-content: center;
+        flex-shrink: 0;
+        width: 200px;
+        height: 100px;
+        background-color: mix($primary-color, #FFF, 90%);
+        color: #FFF;
+        line-height: 100px;
+        margin-left: 10px;
+    }
+}
+.nut-hor-jump-more{
+    height: 100%;
+    width: 20px;
+    padding: 5px 10px;
+    font-size: 12px;
+    text-align: center;
+    color: $primary-color;
+}
+
+.vert-panel{
+    height: 400px; 
+    padding: 10px; 
+    background-color: mix($primary-color, #FFF, 10%);
+}
+.nut-vert-list-panel{
+    .nut-vert-list-item{
+        width: 100%;
+        height: 100px;
+        margin-bottom: 10px;
+        background-color: mix($primary-color, #FFF, 90%);
+        font-size: 12px;
+        text-align: center;
+        line-height: 100px;
+        color: #FFF;
+    }
+}
+</style>

+ 0 - 0
src/packages/scroller/doc.md


+ 173 - 0
src/packages/scroller/horizontal-scroll.vue

@@ -0,0 +1,173 @@
+<template>
+    <div class="nut-hor-scroll" rel="wrapper">
+        <div class="nut-hor-list" ref="list">
+            <slot name="list"></slot>
+            <div class="nut-hor-control" v-if="isUnMore && $slots.more && isShowLoadMore()">
+                <slot name="more"></slot>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name:'nut-hor-scroll',
+    props: {
+        listData: {
+            type: Array,
+            required: true,
+            default: () => []
+        },
+        lineSpacing: {
+            type: Number,
+            default: 210
+        },
+        stretch: {
+            type: Number,
+            default: 200
+        },
+        isUnMore: {
+            type: Boolean,
+            default: false
+        },
+        isLoading: {
+            type: Boolean,
+            default: false
+        }
+    },
+    data() {
+        return {
+            touchParams: {
+                startX: 0, 
+                endX: 0, 
+                startTime: 0, 
+                endTime: 0
+            },
+            transformX: 0,
+            scrollDistance: 0,
+            timer: null
+        }
+    },
+    methods: {
+        isShowLoadMore() {
+            this.$nextTick(() => {
+                let wrapH = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
+                let listH = this.listData.length * this.lineSpacing;
+                if (wrapH <= listH) {
+                    return true;
+                } else {
+                    return false;
+                }
+            });
+        },
+        setTransform(translateX = 0, type, time = 500) {
+            if (type === 'end') {
+                this.$refs.list.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
+            } else {
+                this.$refs.list.style.webkitTransition = '';
+            }
+            this.$refs.list.style.webkitTransform = `translate3d(${translateX}px, 0, 0)`;
+            this.scrollDistance = translateX;
+        },
+
+        setMove(move, type, time) {
+            let updateMove = move + this.transformX;
+            let w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
+            let offsetWidth = this.lineSpacing * this.listData.length;
+            if (type === 'end') {
+                if (updateMove > 0) {
+                    updateMove = 0;
+                } else if (updateMove < -offsetWidth + w) {
+                    if (-offsetWidth + w <= 0) {
+                        updateMove = -offsetWidth + w;
+                    } else {
+                        updateMove = 0;
+                    }
+                }
+                this.setTransform(updateMove, type, time)
+            } else {
+                let maxMove =  -offsetWidth + w;
+                if (updateMove > 0 && updateMove > this.stretch) {
+                    updateMove = this.stretc;
+                } else if (updateMove < maxMove - this.stretch) {
+                    if (maxMove <= 0) {
+                        updateMove  = maxMove - this.stretch;
+                    } else {
+                        updateMove =  updateMove < -this.stretch ? -this.stretch : updateMove;
+                    }
+                }
+                this.setTransform(updateMove, null, null);
+            }
+        },
+	
+	    touchStart(event) {
+            event.preventDefault();
+
+            let changedTouches = event.changedTouches[0];
+            this.touchParams.startX = changedTouches.pageX;
+            this.touchParams.startTime = event.timestamp || Date.now();
+            this.transformX = this.scrollDistance;
+        },
+
+        touchMove(event) {
+
+            let changedTouches = event.changedTouches[0];
+            this.touchParams.lastX = changedTouches.pageX;
+            this.touchParams.lastTime = event.timestamp || Date.now();
+            let move = this.touchParams.lastX - this.touchParams.startX;
+
+            this.setMove(move);
+        },
+
+        touchEnd(event) {
+            event.preventDefault();
+
+            let changedTouches = event.changedTouches[0];
+            this.touchParams.lastX = changedTouches.pageX;
+            this.touchParams.lastTime = event.timestamp || Date.now();
+            let move = this.touchParams.lastX - this.touchParams.startX;
+
+            let moveTime = this.touchParams.lastTime - this.touchParams.startTime;
+            let w = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
+            let maxMove = -this.lineSpacing * this.listData.length + w;
+
+            // 释放跳转之类
+            if (this.isUnMore && move < 0 && (move + this.transformX) < maxMove - 50) {
+                //this.$emit('jump');
+            }
+            
+            // 加载更多
+            if (!this.isLoading && !this.isUnMore && move < 0 && (move + this.transformX) < maxMove + 2 * w) {
+                this.$emit('loadMore');
+            }
+
+            if (moveTime <= 300) {
+                move = move * 2;
+                if (move < 0 && move + this.transformX < maxMove) {
+                    move = maxMove - this.transformX;
+                }
+                moveTime = moveTime + 500;
+                this.setMove(move, 'end', moveTime);
+            } else {
+                this.setMove(move, 'end');
+            }
+        }
+    },
+
+    mounted() {
+        this.$nextTick(() => {
+            // 监听
+            this.$el.addEventListener('touchstart', this.touchStart);
+            this.$el.addEventListener('touchmove', this.touchMove);
+            this.$el.addEventListener('touchend', this.touchEnd);
+        });
+    },
+    beforeDestroy() {
+        // 移除监听
+        this.$el.removeEventListener('touchstart', this.touchStart);
+        this.$el.removeEventListener('touchmove', this.touchMove);
+        this.$el.removeEventListener('touchend', this.touchEnd);
+        clearTimeout(this.timer);
+    }
+}
+</script>
+

+ 8 - 0
src/packages/scroller/index.js

@@ -0,0 +1,8 @@
+import Scroller from './scroller.vue';
+import './scroller.scss';
+
+Scroller.install = function(Vue) {
+  Vue.component(Scroller.name, Scroller);
+};
+
+export default Scroller

File diff suppressed because it is too large
+ 62 - 0
src/packages/scroller/scroller.scss


+ 81 - 0
src/packages/scroller/scroller.vue

@@ -0,0 +1,81 @@
+<template>
+    <div class="nut-scroller">
+        <template v-if="type === 'vertical'">
+            <nut-vert-scroll 
+                :stretch="stretch"
+                :is-un-more="isUnMore"
+                :is-loading="isLoading"
+                @loadMore="loadMore"
+                @pulldown="pulldown"
+            >
+                <slot name="list"  slot="list"></slot>
+            </nut-vert-scroll>
+        </template>
+        <template v-else-if="type === 'horizontal'">
+            <nut-hor-scroll :list-data="listData"
+                :line-spacing="lineSpacing"
+                :stretch="stretch"
+                :is-un-more="isUnMore"
+                :is-loading="isLoading"
+                @loadMore="loadMore"
+                @jump="jump"
+            >
+                <slot name="list"  slot="list"></slot>
+                <slot name="more"  slot="more"></slot>
+            </nut-hor-scroll>
+        </template>
+    </div>
+</template>
+<script>
+import nutVertScroll from "./vertical-scroll.vue";
+import nutHorScroll from "./horizontal-scroll.vue";
+export default {
+    name:'nut-scroller',
+    props: {
+        type: {
+            type: String,
+            default: 'horizontal' // horizontal vertical
+        },
+        listData: {
+            type: Array,
+            default: () => []
+        },
+        lineSpacing: {
+            type: Number,
+            default: 210
+        },
+        stretch: {
+            type: Number,
+            default: 200
+        },
+        isUnMore: {
+            type: Boolean,
+            default: false
+        },
+        isLoading: {
+            type: Boolean,
+            default: false
+        }
+    },
+    data() {
+        return {};
+    },
+    components: {
+        [nutVertScroll.name]: nutVertScroll,
+        [nutHorScroll.name]: nutHorScroll
+    },
+    methods: {
+        loadMore() {
+            this.$emit('loadMore');
+        },
+
+        jump() {
+            this.$emit('jump');
+        },
+
+        pulldown() {
+            this.$emit('pulldown');
+        }
+    }
+}
+</script>

+ 208 - 0
src/packages/scroller/vertical-scroll.vue

@@ -0,0 +1,208 @@
+<template>
+    <div class="nut-vert-scroll" ref="wrapper">
+        <div class="nut-vert-list" ref="list">
+            <div class="nut-vert-pulldown">
+                <div class="nut-vert-pulldown-txt" v-if="!isLoading">{{pulldownTxt}}</div>
+                <div class="nut-vert-pulldown-status" v-else>
+                    <span class="nut-vert-loading"></span>
+                    <span class="nut-vert-loading-txt">加载中...</span>
+                </div>
+            </div>
+            <slot name="list"></slot>
+            <div class="nut-vert-loadmore" v-if="!isUnMore && isShowLoadMore()">
+                <div class="nut-vert-load-txt" v-if="!isLoading">{{loadMoreTxt}}</div>
+                <div class="nut-vert-load-status" v-else>
+                    <span class="nut-vert-loading"></span>
+                    <span class="nut-vert-loading-txt">加载中...</span>
+                </div>
+            </div>
+            <div v-else-if="isUnMore" class="nut-vert-unloadmore" >
+                <div class="nut-vert-unloadmore-txt">{{unloadMoreTxt}}</div>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+export default {
+    name:'nut-vert-scroll',
+    props: {
+        stretch: {
+            type: Number,
+            default: 50
+        },
+        isUnMore: {
+            type: Boolean,
+            default: false
+        },
+        isLoading: {
+            type: Boolean,
+            default: false
+        },
+        pulldownTxt: {
+            type: String,
+            default: '下拉刷新'
+        },
+        loadMoreTxt: {
+            type: String,
+            default: '上拉加载'
+        },
+        unloadMoreTxt: {
+            type: String,
+            default: '没有更多了'
+        }
+    },
+    watch: {
+        'isLoading': function(status) {
+            if (!status && this.realMove === 0) {
+                clearTimeout(this.timer);
+                this.setTransform(this.realMove, 'end', 0);
+            }
+        }
+    },
+    data() {
+        return {
+            touchParams: {
+                startY: 0, 
+                endY: 0, 
+                startTime: 0, 
+                endTime: 0
+            },
+            translateY: 0,
+            scrollDistance: 0,
+            timer: null,
+            timerEmit: null,
+            realMove: 0
+        }
+    },
+
+    methods: {
+        isShowLoadMore() {
+            this.$nextTick(() => {
+                let wrapH = this.$refs.wrapper.offsetHeight;
+                let listH = this.$refs.list.offsetHeight;
+                if (wrapH <= listH) {
+                    return true;
+                } else {
+                    return false;
+                }
+            });
+        },
+
+        setTransform(translateY = 0, type, time = 500) {
+            if (type === 'end') {
+                this.$refs.list.style.webkitTransition = `transform ${time}ms cubic-bezier(0.19, 1, 0.22, 1)`;
+            } else {
+                this.$refs.list.style.webkitTransition = '';
+            }
+            this.$refs.list.style.webkitTransform = `translate3d(0, ${translateY}px, 0)`;
+            this.scrollDistance = translateY;
+        },
+
+        setMove(move, type, time) {
+            let updateMove = move + this.translateY;
+            let h = this.$refs.wrapper.offsetHeight;
+            let maxMove = -this.$refs.list.offsetHeight + h;
+            if (type === 'end') {
+                if (updateMove > 0) {
+                    updateMove = 50;
+                    this.realMove = 0;
+                    if (!this.isLoading) {
+                        clearTimeout(this.timerEmit);
+                        this.timerEmit = setTimeout(() => {
+                            this.$emit('pulldown');
+                        }, time / 2);
+                    }
+                } else if (updateMove < maxMove) {
+                    if (maxMove <= 0) {
+                        updateMove = maxMove;
+                    } else {
+                        updateMove = 0;
+                    }
+                    this.realMove = maxMove;
+                    if (!this.isLoading && !this.isUnMore) {
+                        clearTimeout(this.timerEmit);
+                        this.timerEmit = setTimeout(() => {
+                            this.$emit('loadMore');
+                        }, time / 2);
+                    }
+                }
+                if (updateMove == 50 && !this.isLoading) {
+                    clearTimeout(this.timer);
+                    this.timer = setTimeout(() => {
+                        this.setTransform(this.realMove, 'end', null);
+                    }, 3000);
+                }
+                this.setTransform(updateMove, type, time)
+            } else {
+                // if (updateMove > 0 && updateMove > this.stretch) {
+                //     updateMove = this.stretc;
+                // } else if (updateMove < maxMove - this.stretch) {
+                //     updateMove = maxMove - this.stretch;
+                // }
+                this.setTransform(updateMove, null, null);
+            }
+        },
+	
+	    touchStart(event) {
+            event.preventDefault();
+
+            let changedTouches = event.changedTouches[0];
+            this.touchParams.startY = changedTouches.pageY;
+            this.touchParams.startTime = event.timestamp || Date.now();
+            this.translateY = this.scrollDistance;
+        },
+
+        touchMove(event) {
+
+            let changedTouches = event.changedTouches[0];
+            this.touchParams.lastY = changedTouches.pageY;
+            this.touchParams.lastTime = event.timestamp || Date.now();
+            let move = this.touchParams.lastY - this.touchParams.startY;
+
+            this.setMove(move);
+        },
+
+        touchEnd(event) {
+            event.preventDefault();
+
+            let changedTouches = event.changedTouches[0];
+            this.touchParams.lastY = changedTouches.pageY;
+            this.touchParams.lastTime = event.timestamp || Date.now();
+            let move = this.touchParams.lastY - this.touchParams.startY;
+
+            let moveTime = this.touchParams.lastTime - this.touchParams.startTime;
+            let h = this.$refs.wrapper.offsetHeight;
+            let maxMove = -this.$refs.list.offsetHeight + h;
+
+            if (moveTime <= 300) {
+                move = move * 2;
+                if (move < 0 && move < maxMove) {
+                    move = maxMove;
+                }
+                moveTime = moveTime + 500;
+                this.setMove(move, 'end', moveTime);
+            } else {
+                this.setMove(move, 'end');
+            }
+        }
+    },
+
+    mounted() {
+        this.$nextTick(() => {
+            // 监听
+            this.$el.addEventListener('touchstart', this.touchStart);
+            this.$el.addEventListener('touchmove', this.touchMove);
+            this.$el.addEventListener('touchend', this.touchEnd);
+        });
+    },
+    beforeDestroy() {
+        // 移除监听
+        this.$el.removeEventListener('touchstart', this.touchStart);
+        this.$el.removeEventListener('touchmove', this.touchMove);
+        this.$el.removeEventListener('touchend', this.touchEnd);
+        clearTimeout(this.timer);
+        clearTimeout(this.timerEmit);
+    }
+}
+</script>
+

+ 2 - 2
src/packages/shortpassword/shortpassword.scss

@@ -97,7 +97,7 @@ ul {
             height: 19px;
             width: 19px;
             display: inline-block;
-            background: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23d2d2d2' viewBox='0 0 30 30'%3E%3Cg fill-rule='evenodd'%3E%3Cpath d='M.44 2.56A1.5 1.5 0 1 1 2.56.44l27 27a1.5 1.5 0 1 1-2.12 2.12L15 17.123 2.56 29.56A1.5 1.5 0 1 1 .44 27.44L12.878 15 .44 2.56zM27.44.44a1.5 1.5 0 1 1 2.12 2.12l-9 9a1.5 1.5 0 1 1-2.12-2.12l9-9z'/%3E%3C/g%3E%3C/svg%3E") 0 0 no-repeat;
+            background: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' fill='rgb(210,210,210)' viewBox='0 0 30 30'%3E%3Cg fill-rule='evenodd'%3E%3Cpath d='M.44 2.56A1.5 1.5 0 1 1 2.56.44l27 27a1.5 1.5 0 1 1-2.12 2.12L15 17.123 2.56 29.56A1.5 1.5 0 1 1 .44 27.44L12.878 15 .44 2.56zM27.44.44a1.5 1.5 0 1 1 2.12 2.12l-9 9a1.5 1.5 0 1 1-2.12-2.12l9-9z'/%3E%3C/g%3E%3C/svg%3E") 0 0 no-repeat;
             background-size: 100% 100%;
             position: absolute;
             top: 15px;
@@ -162,7 +162,7 @@ ul {
                 display: inline-block;
                 width: 12px;
                 height: 12px;
-                background: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill-rule='evenodd'%3E%3Cpath d='M.5 12c0 6.351 5.149 11.5 11.5 11.5S23.5 18.351 23.5 12 18.351.5 12 .5.5 5.649.5 12z' stroke='%230c0c0c' fill-opacity='0'/%3E%3Cpath  fill='%230c0c0c' d='M10.909 4.364h2.182v2.182h-2.182zM10.909 8.727h2.182v10.909h-2.182z'/%3E%3C/g%3E%3C/svg%3E") no-repeat;
+                background: url("data:image/svg+xml, %3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill-rule='evenodd'%3E%3Cpath d='M.5 12c0 6.351 5.149 11.5 11.5 11.5S23.5 18.351 23.5 12 18.351.5 12 .5.5 5.649.5 12z' stroke='rgb(12,12,12)' fill-opacity='0'/%3E%3Cpath  fill='rgb(12,12,12)' d='M10.909 4.364h2.182v2.182h-2.182zM10.909 8.727h2.182v10.909h-2.182z'/%3E%3C/g%3E%3C/svg%3E") no-repeat;
                 background-size: 100% 100%;
                 vertical-align: middle;
                 margin: -4px 5px 0 0;

+ 1 - 1
src/packages/toast/demo.vue

@@ -109,7 +109,7 @@ export default {
       this.$toast.text(msg, { duration });
     },
     sucToast(msg) {
-      this.$toast.success(msg);
+      this.$toast.success(msg, { duration:8000 });
     },
     failToast(msg) {
       this.$toast.fail(msg);

File diff suppressed because it is too large
+ 3 - 3
src/packages/toast/toast.scss


+ 2 - 0
types/nutui.d.ts

@@ -44,6 +44,8 @@ export declare class Tab extends UIComponent {}
 export declare class Tabbar extends UIComponent {}
 export declare class TabPanel extends UIComponent {}
 export declare class Toast extends UIComponent {}
+export declare class BackTop extends UIComponent {}
+export declare class Scroller extends UIComponent {}
 
 export interface InstallationOptions {
     locale?: any