浏览代码

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

yewenwen 6 年之前
父节点
当前提交
3a4befaaa8

文件差异内容过多而无法显示
+ 557 - 539
src/config.json


+ 4 - 1
src/nutui.js

@@ -104,6 +104,8 @@ import LuckDraw from "./packages/luckdraw/index.js";
 import "./packages/luckdraw/luckdraw.scss";
 import Video from "./packages/video/index.js";
 import "./packages/video/video.scss";
+import Signature from "./packages/signature/index.js";
+import "./packages/signature/signature.scss";
 
 const packages = {
   Cell,
@@ -156,7 +158,8 @@ const packages = {
   LeftSlip,
   TabSelect: TabSelect,
   LuckDraw: LuckDraw,
-  Video: Video
+  Video: Video,
+  Signature: Signature
 };
 
 const components = {};

+ 7 - 7
src/packages/luckdraw/doc.md

@@ -36,7 +36,7 @@ export default {
         pointerStyle: {
           width: '80px',
           height: '80px',
-          backgroundImage: 'url("https://img11.360buyimg.com/imagetools/jfs/t1/106989/15/11126/137350/5e265414E8ee514bc/3456bd0d3a0454da.png")',
+          backgroundImage: 'url("https://img11.360buyimg.com/imagetools/jfs/t1/89512/11/15244/137408/5e6f15edEf57fa3ff/cb57747119b3bf89.png")',
           backgroundSize: 'contain',
           backgroundRepeat: 'no-repeat',
         },
@@ -45,32 +45,32 @@ export default {
           {
             id: 'xiaomi',
             prizeName: '小米手机',
-            prizeImg: 'https://m.360buyimg.com/mobilecms/s843x843_jfs/t1/96788/40/337/73706/5dabd0e2E1f166028/7120ca2b421cb0a0.jpg!q70.dpg.webp',
+            prizeImg: 'https://img14.360buyimg.com/imagetools/jfs/t1/104165/34/15186/96522/5e6f1435E46bc0cb0/d4e878a15bfd9362.png',
           },
           {
             id: 'blue',
             prizeName: '蓝牙耳机',
-            prizeImg: 'https://m.360buyimg.com/mobilecms/s843x843_jfs/t1/65070/13/4325/183551/5d26e23fE09ab2010/a94eaff8242e6c63.jpg!q70.dpg.webp',
+            prizeImg: 'https://img13.360buyimg.com/imagetools/jfs/t1/91864/11/15108/139003/5e6f146dE1c7b511d/1ddc5aa6e502060a.jpg',
           },
           {
             id: 'apple',
             prizeName: 'apple watch',
-            prizeImg: 'https://m.360buyimg.com/mobilecms/s843x843_jfs/t1/105083/3/4010/126031/5de4aa51E1c7fefc6/0288f4cf3016e061.jpg!q70.dpg.webp',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/105385/19/15140/111093/5e6f1506E48bd0dfb/829a98a8cdb4c27f.png',
           },
           {
             id: 'fruit',
             prizeName: '迪士尼苹果',
-            prizeImg: 'https://m.360buyimg.com/mobilecms/s750x750_jfs/t1/47486/35/13399/356858/5da3cde2E9b3ec40f/3b3a56d54d5db565.jpg!q80.dpg.webp',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/108308/11/8890/237603/5e6f157eE489cccf1/26e0437cfd93b9c8.png',
           },
           {
             id: 'fish',
             prizeName: '海鲜套餐',
-            prizeImg: 'https://m.360buyimg.com/mobilecms/s843x843_jfs/t1/109529/24/1330/283533/5dfc836fE33d8ce6b/372adb638802710a.jpg!q70.dpg.webp',
+            prizeImg: 'https://img14.360buyimg.com/imagetools/jfs/t1/90507/38/15165/448364/5e6f15b4E5df0c718/4bd4c3d375eec312.png',
           },
           {
             id: 'thanks',
             prizeName: '谢谢参与',
-            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/104502/28/10892/5123/5e265414Ec167392c/2831c6155895f33d.png',
+            prizeImg: 'https://img11.360buyimg.com/imagetools/jfs/t1/96116/38/15085/5181/5e6f15d1E48e31d30/71353b61dff705d4.png',
           }
         ],
         turnsNumber: 5, // 转动圈数

+ 87 - 36
src/packages/picker/demo.vue

@@ -6,39 +6,73 @@
     >此 Demo 在 PC 端浏览器与移动端浏览器体验差异较大,建议在 Android 或 iOS 设备上体验。</nut-noticebar>
     <h4>基本用法</h4>
     <div>
-      <nut-cell :showIcon="true" :isLink="true" @click.native="switchPicker('isVisible0')">
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click.native="switchPicker('isVisible0')"
+      >
         <span slot="title">
           <label>年月选择</label>
         </span>
         <span slot="sub-title">不联动多列~~~</span>
-        <div slot="desc" class="selected-option">{{date ? date : '请选择'}}</div>
+        <div
+          slot="desc"
+          class="selected-option"
+        >{{date ? date : '请选择'}}</div>
       </nut-cell>
-      <nut-cell :showIcon="true" :isLink="true" @click.native="switchPicker('isVisible')">
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click.native="switchPicker('isVisible')"
+      >
         <span slot="title">
           <label>城市选择</label>
         </span>
         <span slot="sub-title">联动~~~</span>
-        <div slot="desc" class="selected-option">
-          <span class="btn" @click.stop.prevent="modifyCity">修改为指定的城市</span>
+        <div
+          slot="desc"
+          class="selected-option"
+        >
+          <span
+            class="btn"
+            @click.stop.prevent="modifyCity"
+          >修改为指定的城市</span>
           <span class="show-value">{{city ? city : '请选择'}}</span>
         </div>
       </nut-cell>
-      <nut-cell :showIcon="true" :isLink="true" @click.native="switchPicker('isVisible1')">
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click.native="switchPicker('isVisible1')"
+      >
         <span slot="title">
           <label>年选择</label>
         </span>
         <span slot="sub-title">单列~~~</span>
-        <div slot="desc" class="selected-option">
-          <span class="btn" @click.stop.prevent="modifyYear">修改为指定的年份</span>
+        <div
+          slot="desc"
+          class="selected-option"
+        >
+          <span
+            class="btn"
+            @click.stop.prevent="modifyYear"
+          >修改为指定的年份</span>
           <span class="show-value">{{year ? year : '请选择'}}</span>
         </div>
       </nut-cell>
-      <nut-cell :showIcon="true" :isLink="true" @click.native="switchPicker('isVisible2')">
+      <nut-cell
+        :showIcon="true"
+        :isLink="true"
+        @click.native="switchPicker('isVisible2')"
+      >
         <span slot="title">
           <label>城市选择自定义数据</label>
         </span>
         <span slot="sub-title">联动~~~</span>
-        <div slot="desc" class="selected-option">
+        <div
+          slot="desc"
+          class="selected-option"
+        >
           <span class="show-value">{{cityCustmer ? cityCustmer : '请选择'}}</span>
         </div>
       </nut-cell>
@@ -195,9 +229,7 @@ export default {
       defaultValueData: null,
       year: null,
       isVisible1: false,
-      listData1: [
-        ["2010", "2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018"]
-      ],
+      listData1: [["2018", "2019","2010"]],
       custmerCityData: [
         [
           {
@@ -231,16 +263,26 @@ export default {
       this.date = `${chooseData[0]}年${chooseData[1]}月`;
     },
 
+    setYearValue(chooseData) {
+      this.year = `${chooseData[0]}年`;
+    },
+
+    modifyCity() {
+      this.updateLinkage("", "重庆", 1, "重庆");
+      this.defaultValueData = ["重庆", "重庆"];
+    },
+
+    modifyYear() {
+      this.defaultValueData1 = ["2018"];
+    },
+
+    // demo 城市选择(联动) start
     setChooseValue(chooseData) {
       this.city = `${chooseData[0]}-${chooseData[1]}${
         chooseData[2] ? "-" + chooseData[2] : ""
       }`;
     },
 
-    setYearValue(chooseData) {
-      this.year = `${chooseData[0]}年`;
-    },
-
     updateLinkage(self, value, index, chooseValue, cacheValueData) {
       if (!value) {
         return false;
@@ -274,32 +316,41 @@ export default {
     closeUpdateChooseValue(self, chooseData) {
       this.updateLinkage(self, chooseData[0], 1, chooseData[1], chooseData);
     },
-
+    // demo 城市选择(联动) end
     setChooseValueCustmer(chooseData) {
       var str = chooseData.map(item => item.value).join("-");
       this.cityCustmer = str;
     },
+
     closeUpdateChooseValueCustmer(self, chooseData) {
-      this.setChooseValueCustmer(chooseData);
-    },
-    updateChooseValueCustmer(self, index, resValue, cacheValueData) {
-      let { label, value } = resValue;
-      //此处模拟查询API
-      setTimeout(() => {
-        var resItems = APIData.find(item => item.label == label);
-        if (resItems && resItems.array.length) {
-          this.$set(this.custmerCityData, index + 1, resItems.array);
-          self.updateChooseValue(self, index + 1, resItems.array[0]);
-        }
-      }, 100);
-    },
-    modifyCity() {
-      this.updateLinkage("", "重庆", 1, "重庆");
-      this.defaultValueData = ["重庆", "重庆"];
+        //此处模拟查询API,如果数据缓存了不需要再重新请求
+        setTimeout(() => {
+          let { label, value } = chooseData[0];
+          var resItems = APIData.find(item => item.label == label);
+          if (resItems && resItems.array.length) {
+            this.$set(this.custmerCityData, 1, resItems.array);
+            // 复原位置
+            self.updateChooseValue(self, 0, chooseData[0]);
+            self.updateChooseValue(self, 1, chooseData[1]);
+          }
+        }, 100);
     },
 
-    modifyYear() {
-      this.defaultValueData1 = ["2018"];
+    updateChooseValueCustmer(self, index, resValue, cacheValueData) {
+      // 本demo为二级联动,所以限制只有首列变动的时候触发事件
+      if (index === 0) {
+        //此处模拟查询API,如果数据缓存了不需要再重新请求
+        let { label, value } = resValue;
+        setTimeout(() => {
+          var resItems = APIData.find(item => item.label == label);
+          if (resItems && resItems.array.length) {
+            this.$set(this.custmerCityData, 1, resItems.array);
+            // 更新第二列位置
+            let idx = this.custmerCityData[0].indexOf(resValue);
+            self.updateChooseValue(self, index + 1, this.custmerCityData[idx][0]);
+          }
+        }, 100);
+      }
     }
   }
 };

+ 1 - 0
src/packages/picker/picker-slot.vue

@@ -14,6 +14,7 @@
             <div class="nut-picker-item" v-for="(item,index) in listData"
                 :key="item.label ? item.label : index">{{item.value ? item.value : item}}
             </div>
+            <div class="nut-picker-placeholder" v-if="listData && listData.length === 1"></div>
         </div>
     </div>
     <div class="nut-picker-mask"></div>

+ 3 - 0
src/packages/picker/picker.scss

@@ -96,4 +96,7 @@
             opacity: 0;
         }
     }
+}
+.nut-picker-placeholder{
+    height: 1px;
 }

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

@@ -74,7 +74,7 @@ export default {
         updateChooseValue(self, index, value) {
             self.cacheValueData.splice(index, 1, value);
             let ref = `picer-slot-${index}`;
-            self.$refs[ref][0].updateTransform(value);
+            self.$refs[ref] && self.$refs[ref][0].updateTransform(value);
         },
 
         closeActionSheet() {

+ 7 - 3
src/packages/popup/popup.vue

@@ -12,7 +12,7 @@
       @click="$emit('click', this)"
     >
       <slot></slot>
-      <nut-icon
+      <icon
         v-if="closeable"
         @click.native="$emit('input', false)"
         :type="closeIcon"
@@ -20,16 +20,20 @@
         class="nutui-popup__close-icon"
         :class="'nutui-popup__close-icon--' + closeIconPosition"
       >
-      </nut-icon>
+      </icon>
     </div>
   </transition>
 </template>
 <script>
 import Vue from "vue";
 import overlay from "./overlay.vue";
-
+import Icon from '../icon/icon.vue';
+import '../icon/icon.scss';
 export default {
   name: "nut-popup",
+  components:{
+      "icon":Icon
+  },
   props: {
     value: Boolean,
     position: {

+ 26 - 1
src/packages/searchbar/demo.vue

@@ -30,10 +30,30 @@
 		        @submit="submitFun"
 	        ></nut-searchbar>
         </div>
+
+		<h4>获取焦点与失去焦点</h4>
+        <div class="demo1">
+            <nut-searchbar
+		        placeText="请输入自定义文案"
+		        @submit="search"
+				ref="myInput"
+	        ></nut-searchbar>
+        </div>
     </div>
 </template>
 <script>
 	export default {
+		mounted(){
+
+			const th = this
+			this.$nextTick(function() {
+				setTimeout(function() {
+					th.$refs.myInput.focus()
+				}, 2000)
+				
+			})
+			
+		},
 	    methods:{
 	    	focusFun() {
 	    		console.log('获取焦点操作!');
@@ -49,7 +69,12 @@
             submitFun(value) {
             	console.log(value);
                 console.log('默认提交操作!');
-            }
+			},
+			
+			search(value) {
+				this.$refs.myInput.blur()
+				console.log('搜索')
+			}
 	    }
 	}
 </script>

+ 24 - 0
src/packages/searchbar/doc.md

@@ -42,6 +42,19 @@
     @submit="submitFun"
 ></nut-searchbar>
 ```
+
+## 获取焦点与失去焦点
+
+#### 注:由于移动设备的不同,第一次自动获取焦点并不一定能吊起键盘,需要手动吊起来一次,当再次进入时则正常吊起键盘
+
+```html
+<nut-searchbar
+    placeText="请输入自定义文案"
+    @submit="search"
+    ref="myInput"
+></nut-searchbar>
+```
+
 > 输入、失去焦点、提交事件都会返回当前输入值
 
 ```javascript
@@ -50,6 +63,12 @@ export default {
         return {
         }
     },
+    mounted(){
+        //设置获取焦点
+        this.$nextTick(function() {
+            this.$refs.myInput.focus()
+        })
+    },
     methods:{
         focusFun() {
             console.log('获取焦点操作!');
@@ -65,6 +84,11 @@ export default {
         submitFun(value) {
             console.log(value);
             console.log('默认提交操作!');
+        },
+        search(value) {
+            //点击键盘中的’搜索‘时,失去焦点
+            this.$refs.myInput.blur()
+            console.log('搜索')
         }
     }
 }

+ 5 - 1
src/packages/searchbar/searchbar.scss

@@ -30,7 +30,11 @@
 		    margin-left: 10px;
 		    background-color: $light-color;
 		    border-color: transparent;
-		    outline: none;
+			outline: none;
+			
+			&::-webkit-search-cancel-button{
+				display: none;
+			}
 	    }
 	    &.focus {
 	    	box-shadow: 0px 0px 5px 0px $light-color;

+ 21 - 9
src/packages/searchbar/searchbar.vue

@@ -1,14 +1,16 @@
 <template>
     <div :class="['nut-searchbar',customClass ? customClass : '']">
     	<div class="search-input" :class="[animation ? 'nut-search-ani':'',inputFocusAnimation?'focus':'']">
-	    	<form action="" id="input-form" @submit="submitFun">
+	    	<form action="javascript:return true" id="input-form">
 	    		<nut-icon type="search" v-if="hasIcon" :size="searchIconSize" :color="searchIconColor"></nut-icon>
-	    		<input type="text"
-	    		v-model="value"
-	    		:placeholder="placeText || nutTranslate('lang.searchbar.placeText')"
-	    		@focus="focusFun"
-	    		@input="inputFun"
-	    		@blur="blurFun"
+	    		<input type="search"
+					v-model="value"
+					:placeholder="placeText || nutTranslate('lang.searchbar.placeText')"
+				
+					@keyup.enter="submitFun"
+					@input="inputFun"
+					@blur="blurFun"
+					ref="searchInput"
 	    		>
 	    		<span class="close-icon" :class="hasCloseIcon ? 'show':''"
 	    		@click="clearInput">
@@ -108,12 +110,22 @@ export default {
     		this.$emit('input', this.value);
     	},
     	blurFun() {
-    		this.inputFocusAnimation = false;
+			this.inputFocusAnimation = false;
     		this.$emit('blur', this.value);
     	},
         submitFun() {
             this.$emit('submit', this.value);
-        }
+		},
+		// 失去焦点
+		blur() {
+			this.$refs.searchInput.blur()
+		},
+		//js控制获取焦点
+		focus() {
+			this.$nextTick(function() {
+				this.$refs.searchInput.focus()
+			})
+		}
     }
 }
 </script>

+ 29 - 0
src/packages/signature/__test__/signature.spec.js

@@ -0,0 +1,29 @@
+import { shallowMount, mount } from '@vue/test-utils'
+import Signature from '../signature.vue';
+import Vue from 'vue';
+
+HTMLCanvasElement.prototype.getContext = () => { 
+    // return whatever getContext has to return
+};
+
+describe('Signature.vue', () => {
+    const wrapper = mount(Signature, {
+        propsData: { 
+            customClass: 'signature-wrap'
+        }
+    });
+    it('设置自定义class', () => {
+        return Vue.nextTick().then(function() {
+            expect(wrapper.find('.nut-signature').classes()).toContain('signature-wrap');
+        });
+    });
+
+    /* it('自定义不支持Canvas情况下的展示文案', () => {
+        wrapper.setProps({
+            unSupportTpl: '当前不可使用' 
+        });
+        return Vue.nextTick().then(function () {
+            expect(wrapper.find('nut-signature-unsopport').text()).toBe('当前不可使用');
+        })
+    }); */
+});

+ 63 - 0
src/packages/signature/demo.vue

@@ -0,0 +1,63 @@
+<template>
+    <div>
+        <h4>基本用法</h4>
+        <p>默认</p>
+        <p><nut-signature  @confirm="confirm" @clear="clear"></nut-signature></p>
+        <p class="demo-tips demo1">Tips: 点击确认按钮,下方显示签名图片</p>
+        <p class="margin-top">修改签字颜色和画笔粗细</p>
+        <p><nut-signature  @confirm="confirm1" @clear="clear1" :lineWidth="lineWidth" :strokeStyle="strokeStyle"></nut-signature></p>
+        <p class="demo-tips demo2">Tips: 点击确认按钮,下方显示签名图片</p>
+    </div>
+</template>
+
+<script>
+export default {
+    data(){
+        return{
+            lineWidth: 4,
+            strokeStyle: 'green'
+        }
+    },
+    computed:{
+
+    },
+    methods:{
+        confirm(canvas, data) {
+            let img = document.createElement('img');
+            img.src = data;
+            document.querySelector('.demo1').appendChild(img);
+        },
+
+        clear() {
+            let img = document.querySelector('.demo1 img'); 
+            if (img) {
+                img.remove();
+            }
+        },
+
+        confirm1(canvas, data) {
+            let img = document.createElement('img');
+            img.src = data;
+            document.querySelector('.demo2').appendChild(img);
+        },
+
+        clear1() {
+            let img = document.querySelector('.demo2 img'); 
+            if (img) {
+                img.remove();
+            }
+        },
+    }
+}
+</script>
+
+<style lang="scss">
+.demo-tips{
+    padding: 0.22rem 0;
+    font-size: 0.24rem;
+}
+.margin-top{
+  margin-top: 30px;
+}
+</style>
+

+ 89 - 0
src/packages/signature/doc.md

@@ -0,0 +1,89 @@
+# Signature 签名
+
+基于Canvas的签名组件。
+
+## 基本用法
+
+默认
+```html
+<nut-signature  
+    @confirm="confirm" 
+    @clear="clear"
+></nut-signature>
+<p class="demo-tips demo">Tips: 点击确认按钮,下方显示签名图片</p>
+```
+```javascript
+export default {
+    data(){
+        return{
+        }
+    },
+    methods:{
+        confirm(canvas, data) {
+            let img = document.createElement('img');
+            img.src = data;
+            document.querySelector('.demo').appendChild(img);
+        },
+
+        clear() {
+            let img = document.querySelector('.demo img'); 
+            if (img) {
+                img.remove();
+            }
+        }
+    }
+}
+```
+
+修改签字颜色和画笔粗细
+
+```html
+<nut-signature  
+    @confirm="confirm1" 
+    @clear="clear1" 
+    :lineWidth="lineWidth" 
+    :strokeStyle="strokeStyle"
+></nut-signature>
+<p class="demo-tips demo">Tips: 点击确认按钮,下方显示签名图片</p>
+```
+```javascript
+export default {
+    data(){
+        return{
+            lineWidth: 4,
+            strokeStyle: 'green'
+        }
+    },
+    methods:{
+        confirm(canvas, data) {
+            let img = document.createElement('img');
+            img.src = data;
+            document.querySelector('.demo').appendChild(img);
+        },
+
+        clear() {
+            let img = document.querySelector('.demo img'); 
+            if (img) {
+                img.remove();
+            }
+        }
+    }
+}
+```
+
+## Prop
+
+| 字段 | 说明 | 类型 | 默认值
+|----- | ----- | ----- | ----- 
+| custom-class | 自定义class | String | -
+| line-width | 线条的宽度 | Number | 3
+| stroke-style | 绘图笔触颜色 | String | '#000'
+| type | 图片格式 | String | 'png'
+| un-support-tpl | 不支持Canvas情况下的展示文案 | String | '对不起,当前浏览器不支持Canvas,无法使用本控件!'
+
+## Event
+
+| 字段 | 说明 | 回调参数 
+|----- | ----- | ----- 
+| confirm | 点击确认按钮触发事件回调函数 | canvas和签名图片展示的 data URI
+| clear | 点击重签按钮触发事件回调函数 | 无

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

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

+ 13 - 0
src/packages/signature/signature.scss

@@ -0,0 +1,13 @@
+.nut-signature{
+    .nut-signature-inner{
+        height: 10rem;
+        margin-bottom: 1rem;
+        border: 1px solid $border-color-base;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+    }
+    .nut-signature-unsopport{
+        font-size: $font-size-base;
+    }
+}

+ 136 - 0
src/packages/signature/signature.vue

@@ -0,0 +1,136 @@
+<template>
+    <div class="nut-signature" :class="customClass">
+        <div class="nut-signature-inner" ref="wrap">
+            <canvas ref="canvas" :height="canvasHeight" :width="canvasWidth" v-if="isCanvasSupported"></canvas>
+            <p class="nut-signature-unsopport" v-else>{{unSupportTpl}}</p>
+        </div>
+        <slot></slot>
+        <nut-button type="red" shape="circle" small @click="clear()">重签</nut-button>
+        <nut-button shape="circle" small @click="confirm()">确认</nut-button>
+    </div>
+</template>
+<script>
+import NutButton from "../button/button.vue";
+import "../button/button.scss";
+export default {
+    name:'nut-signature',
+    props: {
+        customClass:  {
+            type: String,
+            default: ''
+        },
+	    lineWidth:  {
+            type: Number,
+            default: 2
+        },
+	    strokeStyle:   {
+            type: String,
+            default: '#000'
+        },
+	    type: {
+            type: String,
+            default: 'png'
+        },
+	    unSupportTpl: {
+            type: String,
+            default: '对不起,当前浏览器不支持Canvas,无法使用本控件!'
+        }
+    },
+    data() {
+        return {
+        	canvasHeight: 0,
+            canvasWidth: 0,
+            ctx: null,
+            isSupportTouch : ('ontouchstart' in window),
+            events: ('ontouchstart' in window) ? ['touchstart', 'touchmove', 'touchend'] : ['mousedown', 'mousemove', 'mouseup']
+        };
+    },
+    components: {
+        'nut-button': NutButton
+    },
+    computed: {
+		isCanvasSupported() {
+	        let elem = document.createElement('canvas');
+	        return !!(elem.getContext && elem.getContext('2d'));
+	    },
+    },
+
+    methods: {
+	    addEvent() {
+	        this.startEventHandler =  this.startEventHandler.bind(this),
+	        this.$refs.canvas.addEventListener(this.events[0], this.startEventHandler, false);
+	    },
+
+	    startEventHandler(event) {
+            event.preventDefault();
+            
+	        this.ctx.beginPath();
+	        this.ctx.lineWidth = this.lineWidth;
+	        this.ctx.strokeStyle = this.strokeStyle;
+	        this.moveEventHandler=  this.moveEventHandler.bind(this),
+	        this.endEventHandler= this.endEventHandler.bind(this)
+	        this.$refs.canvas.addEventListener(this.events[1], this.moveEventHandler, false);
+	        this.$refs.canvas.addEventListener(this.events[2], this.endEventHandler, false);
+
+	    },
+
+	    moveEventHandler(event) {
+            event.preventDefault();
+            
+	        let evt = this.isSupportTouch ? event.touches[0] : event;
+	        let coverPos = this.$refs.canvas.getBoundingClientRect();
+	        let mouseX = evt.clientX  - coverPos.left;
+	        let mouseY = evt.clientY  - coverPos.top;
+
+	        this.ctx.lineTo(
+	            mouseX,
+	            mouseY
+	        );
+	        this.ctx.stroke();
+	    },
+
+	    endEventHandler(event) {
+	        event.preventDefault();
+
+	        this.$refs.canvas.removeEventListener(this.events[1], this.moveEventHandler, false);
+	        this.$refs.canvas.removeEventListener(this.events[2], this.endEventHandler, false);
+	    },
+
+	    clear(isUnEmit) {
+            this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight);
+            this.ctx.closePath();
+            if (!isUnEmit) {
+                this.$emit('clear');
+            }
+        },
+
+	    confirm() {
+	        this.onSave(this.$refs.canvas);
+	    },
+
+	    onSave(canvas) {
+	        let dataurl;
+	        switch(this.type) {
+	            case 'png':
+	                dataurl = canvas.toDataURL('image/png');
+	                break;
+	            case 'jpg':
+	                dataurl = canvas.toDataURL('image/jpeg', 0.8);
+	                break;
+            }
+            this.clear(true);
+	        this.$emit('confirm', canvas, dataurl);
+    	}
+    },
+
+
+    mounted(){
+    	if(this.isCanvasSupported) {
+    		this.ctx = this.$refs.canvas.getContext('2d');
+	        this.canvasWidth = this.$refs.wrap.offsetWidth;
+	        this.canvasHeight= this.$refs.wrap.offsetHeight,
+			this.addEvent();
+    	}
+    }
+}
+</script>

+ 2 - 2
src/packages/swiper/doc.md

@@ -14,7 +14,7 @@
     ref="demo1"
 >
     <div  v-for="(item,index) in dataItem" :key="index"  class="nut-swiper-slide">
-        <span>page{{item.name}}</span>
+        <span>page{{item.name} }</span>
     </div>
 </nut-swiper>
 ```
@@ -30,7 +30,7 @@
     ref="demo2"
 >
     <div  v-for="(item,index) in dataItem" :key="index"  class="nut-swiper-slide">
-        <span>page{{item.name}}</span>
+        <span>page{{item.name} }</span>
     </div>
 
 </nut-swiper>

+ 17 - 9
src/packages/tab/tab.vue

@@ -123,16 +123,13 @@ export default {
         };
     },
     watch:{
+        defIndex(){
+            this.updeteTab();
+        },
        initData:{
            handler(){
-            setTimeout(()=>{
-                let slot = [...this.$slots.default];
-                this.tabTitleList = [];
-                this.activeIndex = this.defIndex;
-                this.initTab(slot);  
-            },100);  
+            this.updeteTab();
            },
-           immediate:true,
            deep:true
        }
     },
@@ -154,6 +151,14 @@ export default {
         })     
     },
     methods: {
+        updeteTab:function(){
+            setTimeout(()=>{
+                let slot = [...this.$slots.default];
+                this.tabTitleList = [];
+                this.activeIndex = this.defIndex;
+                this.initTab(slot);  
+            },100);  
+        },
         closeItem:function(value){
             this.$emit('tab-remove',value); 
             setTimeout(()=>{
@@ -196,10 +201,13 @@ export default {
             
         },
         getStyle:function(obj,styleName){
+            if(!obj){
+                return ''
+            }
             if(obj.currentStyle){
-            return obj.currentStyle[styleName];
+                return obj.currentStyle[styleName];
             }else{
-            return getComputedStyle(obj,null)[styleName];
+                return getComputedStyle(obj,null)[styleName];
             }
         },
         getTabWidth:function(){

+ 153 - 88
src/packages/tabselect/__test__/tabselect.spec.js

@@ -3,125 +3,190 @@ import TabSelect from "../tabselect.vue";
 import Vue from "vue";
 
 describe("TabSelect.vue", () => {
-  const wrapper = mount(TabSelect);
+  const wrapper = mount(TabSelect, {
+    propsData: {
+      mainTitle: "配送测试",
+      subTitle: "送达时间测试",
+      defaultContent: [
+        "9:00——10:00",
+        "10:00——11:00",
+        "11:00——12:00",
+        "12:00——13:00",
+        "13:00——15:00",
+        "15:00——17:00",
+        "17:00——19:00"
+      ],
+      tabList: [
+        {
+          tabTitle: "京东快递测试",
+          children: [
+            {
+              tabTitle: "1月13日 (星期一)",
+              content: [
+                "11:00——12:00",
+                "12:00——13:00",
+                "13:00——15:00",
+                "15:00——17:00",
+                "17:00——19:00"
+              ]
+            },
+            {
+              tabTitle: "1月14日 (星期二)"
+            },
+            {
+              tabTitle: "1月15日 (星期三)"
+            },
+            {
+              tabTitle: "1月16日 (星期四)"
+            },
+            {
+              tabTitle: "1月17日 (星期五)"
+            },
+            {
+              tabTitle: "1月18日 (星期六)"
+            },
+            {
+              tabTitle: "1月19日 (星期天)"
+            }
+          ]
+        },
+        {
+          tabTitle: "上门自提",
+          children: [
+            {
+              tabTitle: "2月13日 (星期一)",
+              content: ["13:00——15:00", "15:00——17:00", "17:00——19:00"]
+            },
+            {
+              tabTitle: "2月14日 (星期二)"
+            },
+            {
+              tabTitle: "2月15日 (星期三)"
+            },
+            {
+              tabTitle: "2月16日 (星期四)"
+            },
+            {
+              tabTitle: "2月17日 (星期五)"
+            },
+            {
+              tabTitle: "2月18日 (星期六)"
+            },
+            {
+              tabTitle: "2月19日 (星期天)"
+            }
+          ]
+        }
+      ],
+      show: true,
+      multiple: false,
+      isDefaultSelected: false,
+      max: 2
+    }
+  });
 
   it("mainTitle标题", () => {
-    wrapper.setProps({ mainTitle: "配送" });
     return Vue.nextTick().then(function() {
       expect(
         wrapper
           .findAll(".nut-tabselect-main-title")
           .at(0)
           .text()
-      ).toBe("配送");
+      ).toBe("配送测试");
     });
   });
 
   it("subTitle标题", () => {
-    wrapper.setProps({ subTitle: "送达时间" });
     return Vue.nextTick().then(function() {
-      setTimeout(() => {
-        expect(
-          wrapper
-            .findAll(".nut-tabselect-sub-title")
-            .at(0)
-            .text()
-        ).toBe("送达时间");
-      }, 200);
+      expect(
+        wrapper
+          .findAll(".nut-tabselect-sub-title")
+          .at(0)
+          .text()
+      ).toBe("送达时间测试");
+      //   expect(wrapper.html()).toContain(
+      //     '<div class="nut-tabselect-main-title">送达时间测试</div>'
+      //   );
     });
   });
 
-  it("是否支持多选", () => {
-    wrapper.setProps({ multiple: true });
+  it("defaultContent默认内容", () => {
     return Vue.nextTick().then(function() {
-      setTimeout(() => {
-        wrapper
-          .findAll(".nut-tab-panel-list")
-          .at(1)
-          .trigger("click");
+      expect(
         wrapper
           .findAll(".nut-tab-panel-list")
-          .at(2)
-          .trigger("click");
-        expect(
-          wrapper
-            .findAll(".nut-tab-panel-list")
-            .at(1)
-            .is(".nut-tab-panel-list-active")
-        ).toBe(true);
-        expect(
-          wrapper
-            .findAll(".nut-tab-panel-list")
-            .at(2)
-            .is(".nut-tab-panel-list-active")
-        ).toBe(true);
-      }, 200);
+          .at(0)
+          .text()
+      ).toBe("11:00——12:00");
     });
   });
 
   it("是否支持单选", () => {
-    wrapper.setProps({ multiple: false });
+    wrapper
+      .findAll(".nut-tab-panel-list")
+      .at(1)
+      .trigger("click");
+    wrapper
+      .findAll(".nut-tab-panel-list")
+      .at(2)
+      .trigger("click");
     return Vue.nextTick().then(function() {
-      setTimeout(() => {
-        wrapper
-          .findAll(".nut-tab-panel-list")
-          .at(1)
-          .trigger("click");
-        wrapper
-          .findAll(".nut-tab-panel-list")
-          .at(2)
-          .trigger("click");
-        expect(
-          wrapper
-            .findAll(".nut-tab-panel-list")
-            .at(1)
-            .is(".nut-tab-panel-list-active")
-        ).toBe(false);
-        expect(
-          wrapper
-            .findAll(".nut-tab-panel-list")
-            .at(2)
-            .is(".nut-tab-panel-list-active")
-        ).toBe(true);
-      }, 200);
+      expect(wrapper.findAll(".nut-tab-panel-list-active").length).toBe(1);
+    });
+  });
+
+  it("是否支持多选", () => {
+    wrapper.setProps({ multiple: true });
+    wrapper
+      .findAll(".nut-tab-panel-list")
+      .at(1)
+      .trigger("click");
+    return Vue.nextTick().then(function() {
+      expect(wrapper.findAll(".nut-tab-panel-list-active").length).toBe(2);
     });
   });
 
   it("设置max", () => {
-    wrapper.setProps({ max: 2, multiple: true });
+    wrapper
+      .findAll(".nut-tab-panel-list")
+      .at(3)
+      .trigger("click");
+    wrapper
+      .findAll(".nut-tab-panel-list")
+      .at(4)
+      .trigger("click");
     return Vue.nextTick().then(function() {
-      setTimeout(() => {
-        wrapper
-          .findAll(".nut-tab-panel-list")
-          .at(1)
-          .trigger("click");
+      expect(wrapper.findAll(".nut-tab-panel-list-active").length).toBe(2);
+    });
+  });
+
+  it("tabList整体数据", () => {
+    return Vue.nextTick().then(function() {
+      expect(
         wrapper
-          .findAll(".nut-tab-panel-list")
-          .at(2)
-          .trigger("click");
+          .findAll(".nut-tab-link")
+          .at(0)
+          .text()
+      ).toBe("京东快递测试");
+    });
+  });
+
+  it("设置show", () => {
+    wrapper.setProps({ show: true });
+    return Vue.nextTick().then(function() {
+      expect(
         wrapper
-          .findAll(".nut-tab-panel-list")
-          .at(3)
-          .trigger("click");
-        expect(
-          wrapper
-            .findAll(".nut-tab-panel-list")
-            .at(1)
-            .is(".nut-tab-panel-list-active")
-        ).toBe(true);
-        expect(
-          wrapper
-            .findAll(".nut-tab-panel-list")
-            .at(2)
-            .is(".nut-tab-panel-list-active")
-        ).toBe(true);
-        expect(
-          wrapper
-            .findAll(".nut-tab-panel-list")
-            .at(3)
-            .is(".nut-tab-panel-list-active")
-        ).toBe(false);
-      }, 200);
+          .findAll(".popup-box")
+          .at(0)
+          .attributes("display")
+      ).toBe(undefined);
+    });
+  });
+
+  it("设置isDefaultSelected", () => {
+    wrapper.setProps({ isDefaultSelected: true });
+    return Vue.nextTick().then(function() {
+      expect(wrapper.contains(".nut-tab-panel-list-active")).toBe(true);
     });
   });
 });

+ 11 - 0
src/packages/tabselect/demo.vue

@@ -15,7 +15,9 @@
       :show="show"
       @close="show = false"
       @choose="choose"
+      @onOkBtn="onOkBtn"
       :multiple="false"
+      :isDefaultSelected="true"
     ></nut-tabselect>
 
     <nut-cell
@@ -33,6 +35,7 @@
       :show="showMore"
       @close="showMore = false"
       @choose="choose"
+      @onOkBtn="onOkBtn"
       :multiple="true"
       :max="3"
     ></nut-tabselect>
@@ -121,9 +124,17 @@ export default {
       showMore: false
     };
   },
+  //   mounted() {
+  //     setTimeout(() => {
+  //       this.tabList[0].tabTitle = "测试";
+  //     }, 10000);
+  //   },
   methods: {
     choose(title, item) {
       console.log(title, item);
+    },
+    onOkBtn(event) {
+      console.log(event)
     }
   }
 };

+ 51 - 43
src/packages/tabselect/doc.md

@@ -11,7 +11,9 @@
   :show="show"
   @close="show = false"
   @choose="choose"
+  @onOkBtn="onOkBtn"
   :multiple="false"
+  :isDefaultSelected="true"
 ></nut-tabselect>
 ```
 
@@ -26,6 +28,7 @@
   :show="show"
   @close="show = false"
   @choose="choose"
+  @onOkBtn="onOkBtn"
   :multiple="true"
   :max="3"
 ></nut-tabselect>
@@ -36,107 +39,112 @@ export default {
   components: {},
   data() {
     return {
-      mainTitle: "配送",
-      subTitle: "送达时间",
+      mainTitle: '配送',
+      subTitle: '送达时间',
       defaultContent: [
-        "9:00——10:00",
-        "10:00——11:00",
-        "11:00——12:00",
-        "12:00——13:00",
-        "13:00——15:00",
-        "15:00——17:00",
-        "17:00——19:00"
+        '9:00——10:00',
+        '10:00——11:00',
+        '11:00——12:00',
+        '12:00——13:00',
+        '13:00——15:00',
+        '15:00——17:00',
+        '17:00——19:00'
       ],
       tabList: [
         {
-          tabTitle: "京东快递", // 一级tab标题
+          tabTitle: '京东快递', // 一级tab标题
           children: [
             // 一级tab内容
             {
-              tabTitle: "1月13日 (星期一)", // 二级tab标题
+              tabTitle: '1月13日 (星期一)', // 二级tab标题
               content: [
                 // 二级tab内容,不传默认使用defaultContent字段
-                "11:00——12:00",
-                "12:00——13:00",
-                "13:00——15:00",
-                "15:00——17:00",
-                "17:00——19:00"
+                '11:00——12:00',
+                '12:00——13:00',
+                '13:00——15:00',
+                '15:00——17:00',
+                '17:00——19:00'
               ]
             },
             {
-              tabTitle: "1月14日 (星期二)"
+              tabTitle: '1月14日 (星期二)'
             },
             {
-              tabTitle: "1月15日 (星期三)"
+              tabTitle: '1月15日 (星期三)'
             },
             {
-              tabTitle: "1月16日 (星期四)"
+              tabTitle: '1月16日 (星期四)'
             },
             {
-              tabTitle: "1月17日 (星期五)"
+              tabTitle: '1月17日 (星期五)'
             },
             {
-              tabTitle: "1月18日 (星期六)"
+              tabTitle: '1月18日 (星期六)'
             },
             {
-              tabTitle: "1月19日 (星期天)"
+              tabTitle: '1月19日 (星期天)'
             }
           ]
         },
         {
-          tabTitle: "上门自提",
+          tabTitle: '上门自提',
           children: [
             {
-              tabTitle: "2月13日 (星期一)",
-              content: ["13:00——15:00", "15:00——17:00", "17:00——19:00"]
+              tabTitle: '2月13日 (星期一)',
+              content: ['13:00——15:00', '15:00——17:00', '17:00——19:00']
             },
             {
-              tabTitle: "2月14日 (星期二)"
+              tabTitle: '2月14日 (星期二)'
             },
             {
-              tabTitle: "2月15日 (星期三)"
+              tabTitle: '2月15日 (星期三)'
             },
             {
-              tabTitle: "2月16日 (星期四)"
+              tabTitle: '2月16日 (星期四)'
             },
             {
-              tabTitle: "2月17日 (星期五)"
+              tabTitle: '2月17日 (星期五)'
             },
             {
-              tabTitle: "2月18日 (星期六)"
+              tabTitle: '2月18日 (星期六)'
             },
             {
-              tabTitle: "2月19日 (星期天)"
+              tabTitle: '2月19日 (星期天)'
             }
           ]
         }
       ],
       show: false
-    };
+    }
   },
   methods: {
     choose(title, item) {
-      console.log(title, item);
+      console.log(title, item)
+    },
+    onOkBtn(event) {
+      console.log(event)
     }
   }
-};
+}
 ```
 
 ### Prop
 
-| 字段           | 说明                        | 类型    | 默认值   |
-| -------------- | --------------------------- | ------- | -------- |
-| mainTitle      | 一级 tab 标题               | String  | ''       |
-| subTitle       | 二级 tab 标题               | String  | ''       |
-| defaultContent | 二级 tab 下内容完全一致时传 | Array   | null     |
-| multiple       | 是否允许多选                | Boolean | false    |
-| tabList        | 整体数据                    | Array   | null     |
-| show           | 是否显示                    | Boolean | false    |
-| max            | 多选时最多可选个数          | Number  | Infinity |
+| 字段              | 说明                                       | 类型    | 默认值   |
+| ----------------- | ------------------------------------------ | ------- | -------- |
+| mainTitle         | 一级 tab 标题                              | String  | ''       |
+| subTitle          | 二级 tab 标题                              | String  | ''       |
+| defaultContent    | 二级 tab 下内容完全一致时传                | Array   | null     |
+| multiple          | 是否允许多选                               | Boolean | false    |
+| tabList           | 整体数据                                   | Array   | null     |
+| show              | 是否显示                                   | Boolean | false    |
+| max               | 多选时最多可选个数                         | Number  | Infinity |
+| isDefaultSelected | 单选时是否默认选中第一项(多选默认不选中) | Boolean | false    |
 
 ### Event
 
 | 事件名称 | 说明                 | 回调参数                               |
 | -------- | -------------------- | -------------------------------------- |
 | choose   | 切换页签或选中某一项 | 点击的一级 tab ,二级 tab ,选中项内容 |
+| onOkBtn  | 确定按钮回调         | Event                                  |
 | close    | 组件隐藏时           | --                                     |

+ 47 - 21
src/packages/tabselect/tabselect.vue

@@ -20,6 +20,7 @@
             positionNav="left"
             class="nut-tab-inner"
             :init-data="value.children"
+            :defIndex="defIndex"
           >
             <nut-tab-panel
               v-for="(item, index) in value.children"
@@ -59,7 +60,7 @@
         </nut-tab-panel>
       </nut-tab>
       <div class="nut-tabselect-btn">
-        <a href="javascript:;" @click="isShow = false">确定</a>
+        <a href="javascript:;" @click="clickHandler">确定</a>
       </div>
     </nut-popup>
   </div>
@@ -99,6 +100,10 @@ export default {
     max: {
       type: Number,
       default: Infinity
+    },
+    isDefaultSelected: {
+      type: Boolean,
+      default: false
     }
   },
   data() {
@@ -106,9 +111,10 @@ export default {
       isShow: false,
       level0: 0,
       level1: new Set([0]),
-      level2: new Set(["0-0"]),
-      allChoose: new Set([this.getText(0, 0, 0)]),
-      list: []
+      level2: this.isDefaultSelected ? new Set(["0-0"]) : new Set(),
+      allChoose: this.getText(0, 0, this.isDefaultSelected ? 0 : null),
+      list: [],
+      defIndex: 0
     };
   },
   components: {
@@ -124,18 +130,21 @@ export default {
         this.$emit("close");
       }
     },
-    tabList(val) {
-      this.list = val;
-      this.level0 = 0;
-      this.level1 = new Set([0]);
-      this.level2 = new Set(["0-0"]);
-      this.allChoose = new Set([this.getText(0, 0, 0)]);
-      this.emit();
+    tabList: {
+      handler(val) {
+        this.list = val;
+        this.level0 = 0;
+        this.level1 = new Set([0]);
+        this.level2 = this.isDefaultSelected ? new Set(["0-0"]) : new Set();
+        this.allChoose = this.getText(0, 0, this.isDefaultSelected ? 0 : null);
+        this.emit();
+      },
+      deep: true
     }
   },
   mounted() {
     this.list = this.tabList;
-    this.allChoose = new Set([this.getText(0, 0, 0)]);
+    this.allChoose = this.getText(0, 0, this.isDefaultSelected ? 0 : null);
     this.emit();
   },
   methods: {
@@ -146,28 +155,34 @@ export default {
           this.list[this.level0] &&
           this.list[this.level0].tabTitle) ||
           "",
-        [...this.allChoose]
+        (this.allChoose && [...this.allChoose]) || []
       );
     },
     getText(idx, index, sIndex) {
+      if (sIndex === null) {
+        return null;
+      }
       const tab =
         (this.list && this.list[idx] && this.list[idx].children[index]) || {};
       const subTit = tab.tabTitle;
       const content =
         (tab.content && tab.content[sIndex]) || this.defaultContent[sIndex];
-      return subTit + " " + content;
+      return new Set([{ subTit, content }]);
     },
     tabSwitchOuter: function(index, event) {
-      this.list.map(item => {
-        item.children = item.children.concat();
-      });
+      this.defIndex = 0;
       this.level0 = index;
       this.level1 = new Set([0]);
-      this.level2 = new Set(["0-0"]);
-      this.allChoose = new Set([this.getText(index, 0, 0)]);
+      this.level2 = this.isDefaultSelected ? new Set(["0-0"]) : new Set();
+      this.allChoose = this.getText(
+        index,
+        0,
+        this.isDefaultSelected ? 0 : null
+      );
       this.emit();
     },
     tabSwitchInner: function(index, event) {
+      this.defIndex = index;
       if (!this.multiple) {
         this.level1 = new Set([index]);
       } else {
@@ -187,16 +202,27 @@ export default {
       }
       if (!this.multiple) {
         this.level2 = new Set([index + "-" + sIndex]);
-        this.allChoose = new Set([this.getText(idx, index, sIndex)]);
+        this.allChoose = this.getText(idx, index, sIndex);
       } else {
         if (this.max !== Infinity && this.max === this.level2.size) {
           return;
         }
         this.level2 = new Set([...this.level2.add(index + "-" + sIndex)]);
-        this.allChoose.add(this.getText(idx, index, sIndex));
+        if (this.allChoose) {
+          this.allChoose.add(...this.getText(idx, index, sIndex));
+        } else {
+          this.allChoose = this.getText(idx, index, sIndex);
+        }
       }
       this.emit();
     },
+    clickHandler (event) {
+      this.$emit(
+        "onOkBtn",
+        event
+      )
+      this.isShow = false
+    },
     isActive(idx, index, sIndex) {
       if (
         idx === this.level0 &&

+ 39 - 0
src/packages/textinput/demo.vue

@@ -1,5 +1,6 @@
 <template>
   <div class="textinput-demo">
+
     <h4>基本用法</h4>
     <div>
       <nut-cell>
@@ -53,6 +54,7 @@
         </span>
       </nut-cell>
     </div>
+    
     <h4>使用input原生事件</h4>
     <div>
       <nut-cell>
@@ -79,6 +81,13 @@
         </span>
       </nut-cell>
     </div>
+    
+    <h4>自动聚焦</h4>
+    <div class="autoFucus">
+      <nut-textinput  class="my-input" type="search" v-model="val8"  placeholder="请输入搜索内容" ref="myInput" @keyup.enter="submit" />
+
+      <div class="searchBtn" @click="submit">搜索</div>
+    </div>
   </div>
 </template>
 
@@ -93,15 +102,30 @@ export default {
       val5: "我使用了readonly原生属性",
       val6: "",
       val7: "",
+      val8: "",
       result: "尚未触发"
     };
   },
+
+  mounted() {
+    const th = this
+			this.$nextTick(function() {
+				setTimeout(function() {
+					th.$refs.myInput.focus()
+				}, 2000)
+				
+			})
+  },
   methods: {
     onFocus() {
       this.result = "focus事件触发!";
     },
     onBlur() {
       this.result = "blur事件触发!";
+    },
+
+    submit() {
+        this.$refs.myInput.blur()
     }
   }
 };
@@ -116,6 +140,21 @@ export default {
   padding: 0 10px;
 }
 
+.autoFucus{
+  background: #fff;
+  padding: 10px;
+
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+
+  .my-input{
+    flex: 1;
+  }
+  .searchBtn{
+    margin-left: 10px;
+  }
+}
 .nut-textinput {
   &.my-input {
     input {

+ 31 - 2
src/packages/textinput/doc.md

@@ -62,6 +62,19 @@
     v-model="val"
 />
 ```
+
+自动聚焦
+
+注:由于移动设备的不同,第一次自动获取焦点并不一定能吊起键盘,需要手动吊起来一次,当再次进入时则正常吊起键盘
+
+```html
+<div class="autoFucus">
+  <nut-textinput  class="my-input" type="search" v-model="val8"  placeholder="请输入搜索内容" ref="myInput" @keyup.enter="submit" />
+
+  <div class="searchBtn" @click="submit">搜索</div>
+</div>
+```
+
 ```javascript
 export default {
   data() {
@@ -69,12 +82,22 @@ export default {
       val: ""
     };
   },
+  mounted() {
+    //设置获取焦点
+    this.$nextTick(function() {
+				this.$refs.myInput.focus()
+		})
+  },
   methods: {
     onFocus() {
       console.log("focus事件触发!");
     },
     onBlur() {
       console.log("blur事件触发!");
+    },
+    submit() {
+      //失去焦点
+        this.$refs.myInput.blur()
     }
   }
 };
@@ -86,7 +109,13 @@ export default {
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
 | value | 当前input值,可使用 v-model 双向绑定数据 | String | ''
+| type | input输入框的类型 | String | 'text'
+| placeholder | 占位文本 | String | ''
 | label | 文本框前面的标签 | String | ''
 | disabled | 是否禁用 | Boolean | false
-| clearBtn | 是否需要清空按钮 | Boolean | true
-| hasBorder | 是否需要边框 | Boolean | true
+| clear-btn | 是否需要清空按钮 | Boolean | true
+| has-border | 是否需要边框 | Boolean | true
+
+## 特殊说明 
+
+设置input框自动聚焦时,由于ios和安卓设备的不同,在ios中,引入的父组件第一次加载,键盘不会弹起,退出再次进入会自动弹起。

+ 8 - 0
src/packages/textinput/textinput.scss

@@ -18,6 +18,10 @@
             color: #C1C4CB;
             font-style: normal;
         }
+
+        &::-webkit-search-cancel-button{
+            display: none;
+        }
     }
 
     .nut-textinput-clear {
@@ -44,5 +48,9 @@
         &::-webkit-input-placeholder {
             color: #D1D3D9;
         }
+
+        &::-webkit-search-cancel-button{
+            display: none;
+        }
     }
 }

+ 39 - 18
src/packages/textinput/textinput.vue

@@ -1,22 +1,26 @@
 <template>
-  <label :class="['nut-textinput',{'nut-textinput-disabled':disabled}]">
-    <span class="nut-textinput-label" v-if="label">{{label}}</span>
-    <input
-      :type="type"
-      :value="value"
-      :disabled="disabled"
-      :style="{'borderWidth':hasBorder?'':0,'outline':outline?'':'none','padding-right':clearBtn?'':'10px'}"
-      v-bind="$attrs"
-      v-on="inputListeners"
-    >
-    <span class="nut-textinput-clear" v-if="clearBtn" v-show="clearBtnShow" @click="clear">
-      <svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
-        <path
-          d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm2.8 9.7c.3.3.3.8 0 1.1s-.8.3-1.1 0L8 9.1l-1.7 1.7c-.3.3-.8.3-1.1 0-.3-.3-.3-.8 0-1.1L6.9 8 5.2 6.3c-.3-.3-.3-.8 0-1.1.3-.3.8-.3 1.1 0L8 6.9l1.7-1.7c.3-.3.8-.3 1.1 0 .3.3.3.8 0 1.1L9.1 8l1.7 1.7z"
-        ></path>
-      </svg>
-    </span>
-  </label>
+  <form action="javascript:return true" :class="['nut-textinput',{'nut-textinput-disabled':disabled}]">
+    
+      <span class="nut-textinput-label" v-if="label">{{label}}</span>
+      <input
+        :type="type"
+        :placeholder="placeholder"
+        :value="value"
+        :disabled="disabled"
+        ref='nutUiInput'
+        :style="{'borderWidth':hasBorder?'':0,'outline':outline?'':'none','padding-right':clearBtn?'':'10px'}"
+        v-bind="$attrs"
+        v-on="inputListeners"
+        
+      />
+      <span class="nut-textinput-clear" v-if="clearBtn" v-show="clearBtnShow" @click="clear">
+        <svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
+          <path
+            d="M8 0C3.6 0 0 3.6 0 8s3.6 8 8 8 8-3.6 8-8-3.6-8-8-8zm2.8 9.7c.3.3.3.8 0 1.1s-.8.3-1.1 0L8 9.1l-1.7 1.7c-.3.3-.8.3-1.1 0-.3-.3-.3-.8 0-1.1L6.9 8 5.2 6.3c-.3-.3-.3-.8 0-1.1.3-.3.8-.3 1.1 0L8 6.9l1.7-1.7c.3-.3.8-.3 1.1 0 .3.3.3.8 0 1.1L9.1 8l1.7 1.7z"
+          ></path>
+        </svg>
+      </span>
+  </form>
 </template>
 <script>
 export default {
@@ -34,6 +38,11 @@ export default {
       type: String,
       default: ""
     },
+    placeholder:{
+      type: String,
+      default: ""
+    },
+    
     disabled: {
       type: Boolean,
       default: false
@@ -68,14 +77,26 @@ export default {
       });
     }
   },
+
   methods: {
     clear() {
       this.$emit("input", "");
       this.clearBtnShow = false;
+    },
+
+    focus() {
+      this.$nextTick(function() {
+        this.$refs.nutUiInput.focus()
+      })
+    },
+
+    blur() {
+      this.$refs.nutUiInput.blur()
     }
   },
   mounted() {
     this.clearBtnShow = !!this.value;
+
   }
 };
 </script>

+ 1 - 0
types/nutui.d.ts

@@ -71,3 +71,4 @@ export declare class Popup extends UIComponent {}
 
 export declare class LuckDraw extends UIComponent {}
 export declare class Video extends UIComponent {}
+export declare class Signature extends UIComponent {}