Browse Source

test: version 2.2.2

richard1015 5 years ago
parent
commit
814ca5de25

+ 40 - 0
CHANGELOG.md

@@ -1,3 +1,43 @@
+## 2.2.2
+
+`2020-03-31`
+
+* :sparkles: feat(Video):新增数据展示-视频组件 @vickyYE
+* :sparkles: feat(Signature):新增业务类型-签名组件 @irisSong
+* :sparkles: upd(TabSelect):新增确认按钮回调事件 @yi-ge
+* :sparkles: upd(TextBox):新增v-model属性 @yi-ge
+* :sparkles: upd(Stepper):新增超出最小、最大回调事件 @richard1015
+* :sparkles: upd(ActionSheet):优化内部代码,文档优化 @irisSong
+* :sparkles: upd(Flex):优化内部代码,文档优化 @szg2008
+* :sparkles: upd(Toast):优化内部代码,文档优化 @zy19940510
+* :sparkles: upd(Progress):优化内部代码,文档优化 @layman666
+* :sparkles: upd(SearchBar、TextInput):组件优化 @yangxiaolu1993
+* :bug: upd(Range):绑定的值无法更新到组件 #227 @undo03
+* :bug: upd(Countdown):修复异步计算属性无法更新组件 #228 @undo03
+* :bug: upd(LuckDraw):抽奖组件图片链接修复 @Ymm0008
+* :bug: upd(Picker)优化自定义数据联动demo @irisSong
+* :bug: upd(Popup):按需加载icon修复 @yangkaixuan
+* :bug: upd(TabSelect):组件tabList数据改变内容不刷新问题修复 @dushoujun
+* :bug: upd(TabBar):更新数据 重新渲染的功能 @zhenyulei
+
+## 2.2.1
+
+`2020-03-09`
+
+* :sparkles: upd(BackTop):支持指定容器Id监听滚动事件,重构优化,文档优化 @richard1015
+* :sparkles: upd(Cell):重构优化内部代码,文档优化 @zhenyulei
+* :sparkles: upd(Dialog):重构优化内部代码,文档优化 @guoxiao158
+* :sparkles: doc:文档新增业务组件 @richard1015
+* :sparkles: feat(Types):新增TextBox types类型 @richard1015
+* :sparkles: feat(Scroller):vertical-scroll组件新增滚动监听事件 @richard1015
+* :sparkles: feat(LuckDraw):新增业务抽奖组件 @Ymm0008
+* :bug: fix(Stepper) 修复步进器组件触发多次change事件 #207 @richard1015
+* :bug: fix(Popup) 修复样式加载问题 @yangkaixuan
+* :bug: fix(Tab) 修复标签中数据更新不渲染页面的问题 #215 @zhenyulei
+* :bug: fix(LeftSlip) 左滑删除优化,单元测试问题修复 @vickyYE
+* :bug: fix(MoToVue) md 转换 vue 插件性能优化 @linrufeng
+* :bug: fix(checkboxgroup) 修复代码冲突 @richard1015
+
 ## 2.2.0
 
 `2020-02-12`

+ 1 - 1
package.json

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

+ 1 - 0
src/packages/avatar/avatar.scss

@@ -17,6 +17,7 @@
     .text{
         display: inline-block;
         width:100%;
+        height: 100%;
         text-align: center;
         overflow: hidden;
     }

+ 3 - 3
src/packages/avatar/avatar.vue

@@ -32,13 +32,13 @@ export default {
     computed:{
         styles(){
             return {
-                'background':this.bgColor + ' url('+this.bgImage+')'+'no-repeat',
+                'background':`${this.bgColor} url(${this.bgImage}) no-repeat`,
                 'backgroundSize':'100%'
             }
         },
         iconStyles(){
             return {
-                'backgroundImage': 'url('+this.bgIcon+')'
+                'backgroundImage': `url(${this.bgIcon})`
             }
         }
     },
@@ -47,7 +47,7 @@ export default {
     },
     methods: {
         activeAvatar(){
-            this.$emit('activeAvatar');
+            this.$emit('active-avatar');
         }
     }
 }

+ 6 - 6
src/packages/avatar/demo.vue

@@ -15,28 +15,28 @@
     </div>
     <h4>修改背景色</h4>
     <div class="white-bg">
-      <nut-avatar bgcolor="#f0250f"></nut-avatar>
+      <nut-avatar bg-color="#f0250f"></nut-avatar>
     </div>
     <h4>可以修改头像的内容</h4>
     <div class="white-bg">
-      <nut-avatar size="large" bgIcon>U</nut-avatar>
+      <nut-avatar size="large" bg-icon>U</nut-avatar>
     </div>
     <h4>修改背景图片</h4>
     <div class="white-bg">
       <nut-avatar
-        bgIcon
-        bgImage="http://img30.360buyimg.com/uba/jfs/t1/84318/29/2102/10483/5d0704c1Eb767fa74/fc456b03fdd6cbab.png"
+        bg-icon
+        bg-image="http://img30.360buyimg.com/uba/jfs/t1/84318/29/2102/10483/5d0704c1Eb767fa74/fc456b03fdd6cbab.png"
       ></nut-avatar>
     </div>
     <h4>修改头像ICON图标</h4>
     <div class="white-bg">
       <nut-avatar
-        bgIcon="http://img10.360buyimg.com/uba/jfs/t1/69001/30/2126/550/5d06f947Effd02898/95f18e668670e598.png"
+        bg-icon="http://img10.360buyimg.com/uba/jfs/t1/69001/30/2126/550/5d06f947Effd02898/95f18e668670e598.png"
       ></nut-avatar>
     </div>
     <h4>点击头像有触发事件</h4>
     <div class="white-bg">
-      <nut-avatar @activeAvatar="activeAvatar"></nut-avatar>
+      <nut-avatar @active-avatar="activeAvatar"></nut-avatar>
     </div>
   </div>
 </template>

+ 26 - 42
src/packages/avatar/doc.md

@@ -4,21 +4,6 @@
 
 ## 基本用法
 
-```html
-<nut-avatar></nut-avatar>
-```
-
-## 设置背景色
-
-```html
-<nut-avatar 
-    bgColor="#87d068"
-></nut-avatar>
-```
-
-
-## 设置大小
-
 内置 **smal** / **normal** / **large** 三种尺寸规格
 
 ```html
@@ -27,45 +12,44 @@
 <nut-avatar size="small"></nut-avatar>
 ```
 
-## 设置头像的形状类型
+## 修改形状类型
+
 ```html
-<nut-avatar 
-  shape="square"
-></nut-avatar>
+<nut-avatar size="large" shape="square"></nut-avatar>
+<nut-avatar size="normal" shape="square"></nut-avatar>
+<nut-avatar size="small" shape="square"></nut-avatar>
 ```
 
+## 修改背景色
+
+```html
+<nut-avatar bg-color="#f0250f"></nut-avatar>
+```
 
 ## 设置头像的文本内容
+
 ```html
-<nut-avatar 
-  :size="30"
-  bgIcon=""
->U</nut-avatar>
+<nut-avatar size="large" bg-icon>U</nut-avatar>
 ```
 
-## 设置头像背景图片
+## 设置背景图片
 ```html
-<nut-avatar 
-  bgIcon="" 
-  bgImage="http://img30.360buyimg.com/uba/jfs/t1/84318/29/2102/10483/5d0704c1Eb767fa74/fc456b03fdd6cbab.png"
->
-</nut-avatar>
+<nut-avatar
+  bg-icon
+  bg-image="http://img30.360buyimg.com/uba/jfs/t1/84318/29/2102/10483/5d0704c1Eb767fa74/fc456b03fdd6cbab.png"
+></nut-avatar>
 ```
 
-## 设置头像Icon图标
+## 设置头像ICON图标
 ```html
 <nut-avatar
-  bgIcon="http://img10.360buyimg.com/uba/jfs/t1/69001/30/2126/550/5d06f947Effd02898/95f18e668670e598.png"
->
-</nut-avatar>
+  bg-icon="http://img10.360buyimg.com/uba/jfs/t1/69001/30/2126/550/5d06f947Effd02898/95f18e668670e598.png"
+></nut-avatar>
 ```
 
 ## 点击头像有触发事件
 ```html
-<nut-avatar 
-  @activeAvatar="activeAvatar"
->
-</nut-avatar>
+<nut-avatar @active-avatar="activeAvatar"></nut-avatar>
 ```
 
 
@@ -73,15 +57,15 @@
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
-| bgColor | 设置头像背景色 | String | #eee
+| bg-color | 设置头像背景色 | String | #eee
 | size | 设置头像的大小,提供三种:large /normal/small | String | normal
-| shape | 设置头像的形状,默认是圆形,可以设置为square方形 | String | --
-| bgImage | 设置头像的背景图片 | String | --
-| bgIcon | 设置头像的icon图标 | String | --
+| shape | 设置头像的形状,默认是圆形,可以设置为square方形 | String | round
+| bg-image | 设置头像的背景图片 | String | --
+| bg-icon | 设置头像的icon图标 | String | --
 
 
 ## Event
 
 | 字段 | 说明 | 回调参数 
 |----- | ----- | ----- 
-| activeAvatar | 点击头像触发事件 | -- 
+| active-avatar | 点击头像触发事件 | -- 

+ 2 - 1
src/packages/backtop/backtop.vue

@@ -123,6 +123,7 @@ export default {
       this.isAnimation && this.duration > 0
         ? this.scrollAnimation()
         : this.scroll();
+      this.$emit("click");
     },
     scrollAnimation() {
       const self = this;
@@ -133,7 +134,7 @@ export default {
         var y = (t * -self.scrollTop) / self.duration + self.scrollTop;
         self.scroll(y);
         cid = self.requestAniFrame()(fn);
-        if (t == self.duration) {
+        if (t == self.duration || y == 0) {
           window.cancelAnimationFrame(cid);
         }
       });

+ 26 - 20
src/packages/countdown/countdown.vue

@@ -119,6 +119,9 @@ const countdownTimer = {
         this.p += (this.getTimeStamp() - this._curr);
         this.$emit('on-restart', this.restTime);
       }
+    },
+    endTime() {
+      this.initTimer();
     }
   },
   methods: {
@@ -127,29 +130,32 @@ const countdownTimer = {
       let t = timeStr;
       t = t > 0? +t: t.toString().replace(/\-/g, '/');
       return new Date(t).getTime();
+    },
+    initTimer() {
+      const delay = 1000;
+      const curr = Date.now();
+      const start = this.getTimeStamp(this.startTime || curr);
+      const end = this.getTimeStamp(this.endTime || curr);
+      const diffTime = curr - start;
+
+      this.restTime = end - (start + diffTime);
+      this.timer = setInterval(() => {
+        if(!this.paused) {
+          let restTime = end - (Date.now() - this.p + diffTime);
+          this.restTime = restTime;
+          if(restTime < delay) {
+            this.restTime = 0;
+            this.$emit('on-end');
+            clearInterval(this.timer);
+          }
+        }else{
+          // 暂停
+        }
+      }, delay);
     }
   },
   created() {
-    const delay = 1000;
-    const curr = Date.now();
-    const start = this.getTimeStamp(this.startTime || curr);
-    const end = this.getTimeStamp(this.endTime || curr);
-    const diffTime = curr - start;
-
-    this.restTime = end - (start + diffTime);
-    this.timer = setInterval(() => {
-      if(!this.paused) {
-        let restTime = end - (Date.now() - this.p + diffTime);
-        this.restTime = restTime;
-        if(restTime < delay) {
-          this.restTime = 0;
-          this.$emit('on-end');
-          clearInterval(this.timer);
-        }
-      }else{
-        // 暂停
-      }
-    }, delay);
+    this.initTimer();
   }
 }
 countdownTimer.restTime = restTime;

+ 12 - 0
src/packages/countdown/demo.vue

@@ -24,6 +24,12 @@
           <nut-countdown slot="title" showDays showPlainText :endTime="end" />
         </nut-cell>
       </div>
+      <h4>异步更新结束时间</h4>
+      <div>
+        <nut-cell>
+          <nut-countdown slot="title" showPlainText :endTime="asyncEnd" />
+        </nut-cell>
+      </div>
       <h4>控制开始和暂停的倒计时</h4>
       <div>
         <nut-cell>
@@ -50,6 +56,7 @@ export default {
     return {
       serverTime: Date.now() - 30 * 1000,
       end: Date.now() + 50 * 1000,
+      asyncEnd: 0,
       paused: false
     };
   },
@@ -66,6 +73,11 @@ export default {
     onrestart(v) {
       console.log('restart: ', v);
     }
+  },
+  mounted() {
+    setTimeout(() => {
+      this.asyncEnd = Date.now() + 30 * 1000
+    }, 3000)
   }
 };
 </script>

+ 12 - 1
src/packages/countdown/doc.md

@@ -41,6 +41,17 @@
 </nut-countdown>
 ```
 
+异步更新结束时间
+
+```html
+<nut-countdown 
+    showDays 
+    showPlainText 
+    :endTime="asyncEnd" 
+>
+</nut-countdown>
+```
+
 控制开始和暂停的倒计时
 
 ```html
@@ -65,7 +76,7 @@
 ## Event
 
 | 字段 | 说明 | 回调参数
-| ----- | ----- | ----- | -----
+| ----- | ----- | ----- 
 | on-end | 倒计时结束时 | 剩余时间戳
 | on-paused | 暂停时 | 剩余时间戳
 | on-restart | 暂停时 | 剩余时间戳

+ 10 - 7
src/packages/progress/demo.vue

@@ -1,11 +1,19 @@
 <template>
   <div class="demo-list">
     <h4>基本用法</h4>
+    <div>
+      <nut-cell>
+        <span slot="title">
+          <nut-progress percentage="30" />
+        </span>
+      </nut-cell>
+    </div>
+
     <p>线形进度条-设置颜色高度</p>
     <div>
       <nut-cell>
         <span slot="title">
-          <nut-progress percentage="30" stroke-color="#8e71c7"/>
+          <nut-progress percentage="30" stroke-color="pink"  stroke-width="20" text-color="red"/>
         </span>
       </nut-cell>
     </div>
@@ -13,7 +21,7 @@
     <div>
       <nut-cell>
         <span slot="title">
-          <nut-progress percentage="50" :show-text="false" stroke-width="24"/>
+          <nut-progress percentage="50" :show-text="false" stroke-height="24"/>
         </span>
       </nut-cell>
     </div>
@@ -107,11 +115,6 @@ export default {
       size:'small'
     };
   },
-  mounted(){
-    setTimeout(() => {
-      this.size='large';
-    }, 3000);
-  },
   methods: {
     setAddVal() {
       if (this.val >= 100) {

+ 42 - 22
src/packages/progress/doc.md

@@ -15,8 +15,8 @@
 ```html
 <nut-progress
     percentage="30"
-    stroke-color="#8e71c7" 
-    stroke-width="12"
+    stroke-color="pink" 
+    stroke-width="20"
 >
 </nut-progress>
 ```
@@ -29,6 +29,16 @@
 >
 </nut-progress>
 ```
+设置百分比外显
+
+```html
+<nut-progress 
+    percentage="60" 
+    :text-inside="false" 
+    stroke-height="24"
+></nut-progress>
+```
+
 设置百分比内显
 
 ```html
@@ -38,56 +48,66 @@
     stroke-width="24"
 ></nut-progress>
 ```
-设置状态显示
 
+## 自定义尺寸
+
+内置 **small**,**base**,**large** 三种规格供使用。
 ```html
 <nut-progress 
+    size="small"
     percentage="30" 
-    stroke-color="#f30" 
-    status="active"
+    text-inside="true" 
 >
 </nut-progress>
-<nut-progress 
-    percentage="50"
-    stroke-color="#f30"
-    stroke-width="15"
-    status="wrong"
+<nut-progress
+    size="base"
+    percentage="50" 
+    text-inside="true" 
 >
 </nut-progress>
 <nut-progress 
-    percentage="100" 
-    :stroke-width="15" 
-    status="success"
+    size="large"
+    percentage="70" 
+    text-inside="true" 
 >
 </nut-progress>
 ```
-## 自定义尺寸
+设置状态显示
 
-内置 **small**,**base**,**large** 三种规格供使用。
 ```html
 <nut-progress 
-    size="small"
+    percentage="30" 
+    stroke-color="#1890ff" 
+    status="active"
 >
 </nut-progress>
-<nut-progress
-    size="base"
+<nut-progress 
+    percentage="50"
+    stroke-color="#f30"
+    stroke-width="15"
+    status="wrong"
 >
 </nut-progress>
 <nut-progress 
-    size="large"
+    percentage="100" 
+    stroke-color="#1890ff" 
+    :stroke-width="15" 
+    status="success"
 >
 </nut-progress>
 ```
 
 
+
 ## Prop
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | -----
 | percentage | 百分比 | Number | 0
-| stroke-color | 进度条背景色 | String | #1890ff
-| stroke-width | 进度条度 | String | ''
+| stroke-color | 进度条背景色 | String | #f30
+| stroke-width | 进度条度 | String | ''
 | size | 进度条及文字尺寸,可选值small/base/large | String | -
 | show-text | 是否显示进度条文字内容 | Boolean | true
-| text-inside | 进度条文字显示位置 | Boolean | false
+| text-inside | 进度条文字显示位置(false:外显,true:内显) | Boolean | false
+| text-color | 进度条文字颜色设置 | String | #333
 | status | 进度条当前状态,可选值text/active/wrong/success | String | text

+ 3 - 4
src/packages/progress/progress.scss

@@ -78,17 +78,16 @@
         font-size: 12px;
         line-height: 1;
     }
-    .nut-icon-success {
+    .nut-icon-success,.nut-icon-fail {
         width: 10px;
         height: 10px;
         display: inline-block;
+    }
+    .nut-icon-success {
         background: url('#{$assetsPath}/img/success.png') no-repeat;
         background-size: cover;
     }
     .nut-icon-fail {
-        width: 10px;
-        height: 10px;
-        display: inline-block;
         background: url('#{$assetsPath}/img/wrong.png') no-repeat;
         background-size: cover;
     }

+ 19 - 12
src/packages/progress/progress.vue

@@ -1,14 +1,14 @@
 <template>
     <div class="nut-progress">
-        <div class="nut-progress-outer" :class="[showText && !textInside ? 'nut-progress-outer-part' :'','nut-progress-'+size]" :style="{height: strokeWidth + 'px'}">
+        <div class="nut-progress-outer" :class="[showText && !textInside ? 'nut-progress-outer-part' :'','nut-progress-'+size]" :style="{height: height}">
             <div :class="['nut-progress-inner',status=='active' ? 'nut-active' : '']" :style="bgStyle">
-                <div class="nut-progress-text" :style="{lineHeight:strokeWidth + 'px'}" v-if="showText && textInside"> {{percentage}}%</div>
+                <div class="nut-progress-text" :style="{lineHeight:height}" v-if="showText && textInside">  <span :style="textStyle">{{percentage}}%</span></div>
             </div>
         </div>
-        <div class="nut-progress-text" :style="{lineHeight:strokeWidth + 'px'}" v-if="showText && !textInside">
+        <div class="nut-progress-text" :style="{lineHeight:height}" v-if="showText && !textInside">
             <slot>
                 <template v-if="status=='text' || status=='active'" >
-                     {{percentage}}%
+                     <span :style="textStyle">{{percentage}}%</span>
                 </template>
                 <template v-else-if="status=='success' || 'wrong'">
                     <i :class="statusIcon"></i>
@@ -50,25 +50,32 @@ export default {
         strokeColor: {
             type: String,
             default: ''
+        },
+        textColor: {
+            tyep: String,
+            default: ''
         }
     },
     data() {
         return {
-            currentStatus: this.status,
-        };
+            height:this.strokeWidth+'px'
+        }
     },
     computed:{
         bgStyle () {
-            const style = {};
-            style.width = this.percentage + '%';
-            style.backgroundColor = this.strokeColor || '';
-            return style;
+            return {
+                width:this.percentage + '%',
+                backgroundColor:this.strokeColor || ''
+            }
+        },
+        textStyle() {
+            return {
+                color:this.textColor || ''
+            }
         },
         statusIcon () {
             return  this.status==='success' ? 'nut-icon-success' :  this.status==='wrong' ? 'nut-icon-fail' : '';
         }
     },
-    methods: {
-    }
 }
 </script>

+ 10 - 2
src/packages/range/range.vue

@@ -104,6 +104,11 @@ export default {
 			ani: false
     };
   },
+  watch: {
+    range() {
+      this.init();
+    }
+  },
   computed: {
     total() {
       return this.range[1] - this.range[0];
@@ -122,6 +127,10 @@ export default {
 		}
   },
   methods: {
+    init() {
+      this.box = this.$el.querySelector(".nut-range-box");
+      this.propInit();
+    },
 		updateRangeValues() {
 			let rangeValues = this.currentLeft > this.currentRight? [this.currentRight, this.currentLeft]: [this.currentLeft, this.currentRight];
 			this.$emit("update:rangeValues", rangeValues);
@@ -180,8 +189,7 @@ export default {
   },
   mounted() {
     this.$nextTick(() => {
-      this.box = this.$el.querySelector(".nut-range-box");
-      this.propInit();
+      this.init();
     });
   }
 };

+ 13 - 21
src/packages/tab/__test__/tab.spec.js

@@ -6,12 +6,12 @@ import Vue from 'vue';
 describe('Tab.vue', () => {
     const wrapper = mount(Tab);
     
-    it('页签类型为based', () => {
-        wrapper.setProps({ type: 'based' });
-        return Vue.nextTick().then(function () {
-            expect(wrapper.contains('.based')).toBe(true);
-        })
-    });
+    // it('页签类型为based', () => {
+    //     wrapper.setProps({ type: 'based' });
+    //     return Vue.nextTick().then(function () {
+    //         expect(wrapper.contains('.based')).toBe(true);
+    //     })
+    // });
 
     it('当前tab的位置', () => {
         wrapper.setProps({ positionNav: 'left' });
@@ -21,13 +21,13 @@ describe('Tab.vue', () => {
         })
     });
 
-    it('是否显示内容区域', () => {
-        wrapper.setProps({ contentShow: true });
-        return Vue.nextTick().then(function () {
-            expect(wrapper.contains('.nut-tab-item')).toBe(true);
+    // it('是否显示内容区域', () => {
+    //     wrapper.setProps({ contentShow: true });
+    //     return Vue.nextTick().then(function () {
+    //         expect(wrapper.contains('.nut-tab-item')).toBe(true);
             
-        })
-    });
+    //     })
+    // });
 
     it('禁止选择第一个标签', () => {
         wrapper.setData({ tabTitleList: [
@@ -61,13 +61,6 @@ describe('Tab.vue', () => {
               expect(wrapper.findAll('.nut-title-nav-leftnav').at(0).is('.nut-tab-disable')).toBe(true)
           }) 
     });
-    it('是否显示关闭按钮', () => {
-        wrapper.setProps({ closable: true });
-        return Vue.nextTick().then(function () {
-            expect(wrapper.contains('.close-btn')).toBe(true);
-            
-        })
-    });
     it('当前默认选中的tab', () => {
         wrapper.setProps({ positionNav: 'top' });
         return Vue.nextTick().then(function () {
@@ -76,7 +69,7 @@ describe('Tab.vue', () => {
     });
     it('tab标签标题', () => {
       return Vue.nextTick().then(function () {
-         expect(wrapper.findAll('.nut-tab-link').at(0).text()).toBe('衣物');
+         expect(wrapper.findAll('.nut-title-nav').at(0).text()).toBe('衣物');
       })
     });
     it('点击tab标签', () => {
@@ -84,7 +77,6 @@ describe('Tab.vue', () => {
             wrapper.findAll('.nut-title-nav-list').at(1).trigger('click');
             expect(wrapper.findAll('.nut-title-nav-list').at(1).is('.nut-tab-active')).toBe(true)
       })
-        
     });
 });
 

+ 18 - 18
src/packages/tab/demo.vue

@@ -3,44 +3,44 @@
     <!-- DEMO区域 -->
     <h4>默认用法</h4>
     <nut-tab @tab-switch="tabSwitch">
-        <nut-tab-panel tabTitle="页签1">页签1</nut-tab-panel>
-        <nut-tab-panel tabTitle="页签2">页签2</nut-tab-panel>
-        <nut-tab-panel tabTitle="页签3">页签3</nut-tab-panel>
+        <nut-tab-panel tab-title="页签1">页签1</nut-tab-panel>
+        <nut-tab-panel tab-title="页签2">页签2</nut-tab-panel>
+        <nut-tab-panel tab-title="页签3">页签3</nut-tab-panel>
     </nut-tab>
     <h4>支持导航条在上下左右位置</h4>
     <nut-tab @tab-switch="tabSwitch">
       <nut-tab-panel
         v-for="value in editableTabs"
         v-bind:key="value.tabTitle"
-        :tabTitle="value.tabTitle"
-        :iconUrl="value.iconUrl"
+        :tab-title="value.tabTitle"
+        :icon-url="value.iconUrl"
         v-html="value.content"
       ></nut-tab-panel>
     </nut-tab>
-    <nut-tab @tab-switch="tabSwitch" positionNav="left">
+    <nut-tab @tab-switch="tabSwitch" position-nav="left">
       <nut-tab-panel
         v-for="value in editableTabs"
         v-bind:key="value.tabTitle"
-        :tabTitle="value.tabTitle"
-        :iconUrl="value.iconUrl"
+        :tab-title="value.tabTitle"
+        :icon-url="value.iconUrl"
         v-html="value.content"
       ></nut-tab-panel>
     </nut-tab>
-    <nut-tab @tab-switch="tabSwitch" positionNav="right">
+    <nut-tab @tab-switch="tabSwitch" position-nav="right">
       <nut-tab-panel
         v-for="value in editableTabs"
         v-bind:key="value.tabTitle"
-        :tabTitle="value.tabTitle"
+        :tab-title="value.tabTitle"
         :iconUrl="value.iconUrl"
         v-html="value.content"
       ></nut-tab-panel>
     </nut-tab>
-    <nut-tab @tab-switch="tabSwitch" positionNav="bottom">
+    <nut-tab @tab-switch="tabSwitch" position-nav="bottom">
       <nut-tab-panel
         v-for="value in editableTabs"
         v-bind:key="value.tabTitle"
-        :tabTitle="value.tabTitle"
-        :iconUrl="value.iconUrl"
+        :tab-title="value.tabTitle"
+        :icon-url="value.iconUrl"
         v-html="value.content"
       ></nut-tab-panel>
     </nut-tab>
@@ -48,11 +48,11 @@
     <h4>禁止选中,默认选中某个标签</h4>
     <h4>如需要更新页面,请将监听变化的数据传入init-data</h4>
 
-    <nut-tab :defIndex="defIndex" class="customer-css" @tab-switch="tabSwitch" :contentShow="true" :init-data="disableTabs">
+    <nut-tab :def-index="defIndex" class="customer-css" @tab-switch="tabSwitch" :contentShow="true" :init-data="disableTabs">
       <nut-tab-panel
         v-for="value in disableTabs"
         v-bind:key="value.tabTitle"
-        :tabTitle="value.tabTitle"
+        :tab-title="value.tabTitle"
         :disable="value.disable"
         v-html="value.content"
       ></nut-tab-panel>
@@ -206,9 +206,9 @@ export default {
   .nut-title-nav-list {
     background: #fff;
     border-left: 1px solid #e4e7ed;
-  }
-  .nut-title-nav-list:first-child {
-    border-left: 0;
+    &:first-child{
+      border-left: 0;
+    }
   }
   .nut-tab-active {
     background: $primary-color;

+ 16 - 16
src/packages/tab/doc.md

@@ -6,9 +6,9 @@
 
 ```html
 <nut-tab @tab-switch="tabSwitch">
-    <nut-tab-panel tabTitle="页签1">页签1</nut-tab-panel>
-    <nut-tab-panel tabTitle="页签2">页签2</nut-tab-panel>
-    <nut-tab-panel tabTitle="页签3">页签3</nut-tab-panel>
+    <nut-tab-panel tab-title="页签1">页签1</nut-tab-panel>
+    <nut-tab-panel tab-title="页签2">页签2</nut-tab-panel>
+    <nut-tab-panel tab-title="页签3">页签3</nut-tab-panel>
 </nut-tab>
 ```
 ```javascript
@@ -21,15 +21,15 @@ export default {
 };
 ```
 
-使用数据渲染,支持上/下/左/右四个样式;
+## 使用数据渲染,支持上/下/左/右四个样式;
 
 ```html
-<nut-tab @tab-switch="tabSwitch"  positionNav="bottom">
+<nut-tab @tab-switch="tabSwitch"  position-nav="bottom">
     <nut-tab-panel 
       v-for="value in editableTabs" 
       v-bind:key="value.tabTitle" 
-      :tabTitle="value.tabTitle" 
-      :iconUrl="value.tabUrl" 
+      :tab-title="value.tabTitle" 
+      :icon-url="value.tabUrl" 
       v-html="value.content"
     >
     </nut-tab-panel>
@@ -72,14 +72,14 @@ export default {
 };
 ```
 
-禁止选中,默认选中某个标签,如需更新数组后,重新渲染Tab页面,请将更新数组传入init-data
+## 禁止选中,默认选中某个标签,如需更新数组后,重新渲染Tab页面,请将更新数组传入init-data
 
 ```html
-<nut-tab :defIndex="1" class="customer-css" @tab-switch="tabSwitch" :contentShow="true" :init-data="disableTabs">
+<nut-tab :def-index="1" class="customer-css" @tab-switch="tabSwitch" :contentShow="true" :init-data="disableTabs">
     <nut-tab-panel
     v-for="value in disableTabs"
     v-bind:key="value.tabTitle"
-    :tabTitle="value.tabTitle"
+    :tab-title="value.tabTitle"
     :disable="value.disable"
     v-html="value.content"
   ></nut-tab-panel>
@@ -195,9 +195,8 @@ export default {
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
-| positionNav | 页签栏的分布,可选值 top/bottom/left/right | String | top
-| defIndex | 默认选中的页签栏 | String | 1
-| contentShow | 是否显示tab内容 | Boolean | true
+| position-nav | 页签栏的分布,可选值 top/bottom/left/right | String | top
+| def-index | 默认选中的页签栏 | String | 1
 | init-data | 监听数据变化,渲染更新页面 | Array | []
 
 
@@ -206,13 +205,14 @@ export default {
 
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | ----- 
-| tabTitle | 页签的标题 | String | ''
-| iconUrl | 页签的图标地址 | String | ''
+| tab-title | 页签的标题 | String | ''
+| icon-url | 页签的图标地址 | String | ''
 | content | 页签的自定义内容 | String | ''
+| disable | 是否禁用页签 |Boolean|false|
 
 ### Event
 
 | 事件名称 | 说明 | 回调参数 
 |----- | ----- | ----- 
-| tabSwitch | 切换页签时触发事件 | 点击的索引值和触发元素
+| tab-switch | 切换页签时触发事件 | 点击的索引值和触发元素
 

+ 12 - 18
src/packages/tab/tab.scss

@@ -5,7 +5,7 @@
     font-size: 12px;
     background:#eee;
 }
-.nut-tab-leftnav{
+.nut-tab-horizontal{
     display: flex;
     flex-direction: row;
     position: relative;
@@ -25,7 +25,7 @@
     top: 0px;
     border-radius: 50%;
 }
-.nut-tab-leftnav .close-btn{
+.nut-tab-horizontal .close-btn{
     position: absolute;
     width: 17px;
     height: 17px;
@@ -97,6 +97,15 @@
         transition: all 0.4s ease-in-out;
     }
 }
+.nut-tab-link{
+    color: #333;
+    display: flex;
+    align-items:center;
+    justify-content:center;
+    font-size:12px;
+    text-decoration:none;
+    line-height: 1;
+}
 .nut-tab-title-bottomnav{
     border:1px solid #fff;
     border-top:1px solid #EDEDED;
@@ -118,13 +127,13 @@
 
 .nut-title-nav-list{
     flex: 1;
-    cursor: pointer;
     position: relative;
     flex-direction: row;
     align-items:center;
     justify-content:center;
     display: flex;
     background: #fff;
+    box-sizing: border-box;
 }
 .nut-title-nav-leftnav{
     flex:1;
@@ -142,15 +151,6 @@
     align-items: center;
     position: relative;
 }
-.nut-tab-link{
-    color: #333;
-    display: flex;
-    align-items:center;
-    justify-content:center;
-    font-size:12px;
-    text-decoration:none;
-    line-height: 1;
-}
 .nut-tab-icon{
     display: inline-block;
     margin-right: 5px;
@@ -162,12 +162,6 @@
 .nut-tab-active{
     background: #fff;
     border: 0;
-    .nut-tab-link{
-        font-size: 14px;
-        font-weight: bold;
-        border: 0;
-        color: #333;
-    }
 }
 .nut-tab-item {  
     height: 200px;

+ 73 - 117
src/packages/tab/tab.vue

@@ -1,76 +1,71 @@
 <template>
     <div v-if='showTabs'>
-        <div class="nut-tab" :class="{'nut-tab-leftnav' : positionNavCss}">
+        <div class="nut-tab" :class="{'nut-tab-horizontal' : positionNavCss}">
             <template v-if="positionNav=='top'">
-                <div :class="['nut-tab-title',tabType]">
+                <div class="nut-tab-title">
                     <b class="nav-bar" :style="[{transform:'translateX('+initX+'px)'},{width:navWidth+'px'}]"></b>
                     <span v-for="(value,index) in tabTitleList"  
-                    v-bind:key="index"
-                    v-on:click="switchTab(index,$event,value.disable)" class="nut-title-nav-list" 
+                    :key="index"
+                    @click="switchTab(index,$event,value.disable)" class="nut-title-nav-list" 
                     :class="['nut-title-nav',{'nut-tab-disable':value.disable},{'nut-tab-active' : activeIndex == index}]"
                     >
-                    <b v-if='closable' class="close-btn" v-on:click="closeItem(value)">x</b>
                     <a :href="value.href" :clstag="value.clstag" class="nut-tab-link" v-on:click="switchTabLink(index,$event,value.disable)">
                     <i class="nut-tab-icon" :style="{backgroundImage: 'url('+value.iconUrl+')'}" v-if="value.iconUrl"></i>
                     {{value.tabTitle}}
                     </a>
                     </span>
                 </div>
-                <div class="nut-tab-item"  v-show="contentShow">
+                <div class="nut-tab-item">
                     <slot></slot>
                 </div>  
             </template>
             <template v-else-if="positionNav=='left'">
-                    <div :class="['nut-tab-title-leftnav',tabType]">
-                        <b class="nav-bar-left" :style="[{transform:'translateY('+initX+'px)'},{height:navWidth+'px'}]"></b>
-                        <span v-for="(value,index) in tabTitleList" 
-                        v-bind:key="index"
-                        v-on:click="switchTab(index,$event,value.disable)" class="nut-title-nav-leftnav" 
-                        :class="['nut-title-nav',{'nut-tab-disable':value.disable},{'nut-tab-active' : activeIndex == index}]"
-                        >
-                        <b v-if='closable' class="close-btn" v-on:click="closeItem(value)">x</b>
-                        <a :href="value.href" :clstag="value.clstag" class="nut-tab-link" v-on:click="switchTabLink(index,$event,value.disable)">
-                        <i class="nut-tab-icon" :style="{backgroundImage: 'url('+value.iconUrl+')'}" v-if="value.iconUrl"></i>
-                        {{value.tabTitle}}
-                        </a>
-                        </span>
-                    </div>
-                    <div class="nut-tab-item"  v-show="contentShow">
-                        <slot></slot>
-                    </div>  
+                <div class="nut-tab-title-leftnav">
+                    <b class="nav-bar-left" :style="[{transform:'translateY('+initX+'px)'},{height:navWidth+'px'}]"></b>
+                    <span v-for="(value,index) in tabTitleList" 
+                    :key="index"
+                    @click="switchTab(index,$event,value.disable)" class="nut-title-nav-leftnav" 
+                    :class="['nut-title-nav',{'nut-tab-disable':value.disable},{'nut-tab-active' : activeIndex == index}]"
+                    >
+                    <a :href="value.href" :clstag="value.clstag" class="nut-tab-link" v-on:click="switchTabLink(index,$event,value.disable)">
+                    <i class="nut-tab-icon" :style="{backgroundImage: 'url('+value.iconUrl+')'}" v-if="value.iconUrl"></i>
+                    {{value.tabTitle}}
+                    </a>
+                    </span>
+                </div>
+                <div class="nut-tab-item">
+                    <slot></slot>
+                </div>  
             </template>
             <template v-else-if="positionNav=='right'">
-                    <div class="nut-tab-item"  v-show="contentShow">
-                        <slot></slot>
-                    </div>
-                    <div :class="['nut-tab-title-rightnav',tabType]">
-                        <b class="nav-bar-right" :style="[{transform:'translateY('+initX+'px)'},{height:navWidth+'px'}]"></b>
-                        <span v-for="(value,index) in tabTitleList" 
-                        v-bind:key="index"
-                        v-on:click="switchTab(index,$event,value.disable)" class="nut-title-nav-rightnav" 
-                        :class="['nut-title-nav',{'nut-tab-disable':value.disable},{'nut-tab-active' : activeIndex == index}]"
-                        >
-                        <b v-if='closable' class="close-btn" v-on:click="closeItem(value)">x</b>
-                        <a :href="value.href" :clstag="value.clstag" class="nut-tab-link" v-on:click="switchTabLink(index,$event,value.disable)">
-                        
-                        {{value.tabTitle}}
-                        <i class="nut-tab-icon" :style="{backgroundImage: 'url('+value.iconUrl+')'}" v-if="value.iconUrl"></i>
-                        </a>
-                        </span>
+                <div class="nut-tab-item">
+                    <slot></slot>
+                </div>
+                <div class="nut-tab-title-rightnav">
+                    <b class="nav-bar-right" :style="[{transform:'translateY('+initX+'px)'},{height:navWidth+'px'}]"></b>
+                    <span v-for="(value,index) in tabTitleList" 
+                    :key="index"
+                    @click="switchTab(index,$event,value.disable)" class="nut-title-nav-rightnav" 
+                    :class="['nut-title-nav',{'nut-tab-disable':value.disable},{'nut-tab-active' : activeIndex == index}]"
+                    >
+                    <a :href="value.href" :clstag="value.clstag" class="nut-tab-link" v-on:click="switchTabLink(index,$event,value.disable)">
+                    {{value.tabTitle}}
+                    <i class="nut-tab-icon" :style="{backgroundImage: 'url('+value.iconUrl+')'}" v-if="value.iconUrl"></i>
+                    </a>
+                    </span>
                 </div>
             </template>
             <template v-else-if="positionNav=='bottom'">
-                <div class="nut-tab-item"  v-show="contentShow">
+                <div class="nut-tab-item">
                     <slot></slot>
                 </div> 
-                    <div :class="['nut-tab-title-bottomnav',tabType]">
+                    <div class="nut-tab-title-bottomnav">
                     <b class="nav-bar-bottom" :style="[{transform:'translateX('+initX+'px)'},{width:navWidth+'px'}]"></b>
                     <span v-for="(value,index) in tabTitleList" 
-                    v-bind:key="index"
-                    v-on:click="switchTab(index,$event,value.disable)" class="nut-title-nav-list" 
+                    :key="index"
+                    @click="switchTab(index,$event,value.disable)" class="nut-title-nav-list" 
                     :class="['nut-title-nav',{'nut-tab-disable':value.disable},{'nut-tab-active' : activeIndex == index}]"
                     >
-                    <b v-if='closable' class="close-btn" v-on:click="closeItem(value)">x</b>
                     <a :href="value.href" :clstag="value.clstag" class="nut-tab-link" v-on:click="switchTabLink(index,$event,value.disable)">
                     <i class="nut-tab-icon" :style="{backgroundImage: 'url('+value.iconUrl+')'}" v-if="value.iconUrl"></i>
                     {{value.tabTitle}}
@@ -85,26 +80,14 @@
 export default {
     name:'nut-tab',
     props: {
-        'type':{
-            type:String,
-            default:'based',
-        },
         'defIndex':{
             type:Number,
             default:0,
         },
-        'contentShow':{
-            type:Boolean,
-            default:true,
-        },
         'positionNav':{
             type:String,
             default:'top',
         },
-        'closable':{
-            type:Boolean,
-            default:false,
-        },
         'initData':{
             type:Array,
             default:function(){
@@ -116,7 +99,6 @@ export default {
         return {
             tabTitleList:[],
             activeIndex:this.defIndex,
-            initIndex:0,
             showTabs:true,
             initX:'0px',
             navWidth:0,
@@ -134,71 +116,46 @@ export default {
        }
     },
     computed:{
-        tabType:function(){
-            return this.type;
-        },
         positionNavCss:function(){
-            if(this.positionNav=='top' || this.positionNav=='bottom'){
-                return false;
-            }else{
-                return true;
-            }
+            if(this.positionNav==='left' || this.positionNav==='right') return true;
         },
     },
     mounted() {
         this.$nextTick(()=>{
-            this.$slots.default && this.initTab(this.$slots.default); 
+            this.$slots.default && this.updeteTab(this.$slots.default); 
         })     
     },
     methods: {
         updeteTab:function(){
-            setTimeout(()=>{
-                let slot = [...this.$slots.default];
+            setTimeout(()=>{;
                 this.tabTitleList = [];
                 this.activeIndex = this.defIndex;
-                this.initTab(slot);  
+                this.initTab([...this.$slots.default]);  
             },100);  
         },
-        closeItem:function(value){
-            this.$emit('tab-remove',value); 
-            setTimeout(()=>{
-                this.tabTitleList=[];
-                if(this.$slots.default){
-                    let slot = [...this.$slots.default];
-                    this.initTab(slot);
-                }else{
-                    this.showTabs = false;
-                }
-            },10);
-        },
-        initTab:function(params){
-            let slot = params;
+        initTab:function(slot){
             for(let i = 0; i < slot.length; i++) {
-                let aa = slot[i].tag;
-                if(typeof(aa)=='string'){
-                    if(slot[i].tag.indexOf('nut-tab-panel') != -1) {
-                        let item ={
-                        'tabTitle':slot[i].data.attrs.tabTitle,
-                        'disable':slot[i].data.attrs.disable==''?true:false,
-                        'iconUrl':slot[i].data.attrs.iconUrl,
-                        'clstag': slot[i].data.attrs.clstag,
-                        'href':slot[i].data.attrs.tabLink?slot[i].data.attrs.tabLink:'javascript:void(0)',
-                       }
-                       this.tabTitleList.push(item);
-                       let slotElm = slot[i].elm;
-                       if(slotElm){
-                            this.addClass(slotElm,'hide');
-                            if(this.activeIndex == i) {
-                                this.removeClass(slotElm,'hide')
-                            }
-                       }                
+                let slotTag = slot[i].tag;
+                if(typeof(slotTag)=='string' && slotTag.indexOf('nut-tab-panel') != -1) {
+                    let attrs = slot[i].data.attrs;
+                    let item ={
+                        'tabTitle':attrs['tab-title'] || attrs['tabTitle'],
+                        'disable':attrs.disable===false,
+                        'iconUrl':attrs['iconUrl'] || attrs['icon-url'],
                     }
-                }  
+                    this.tabTitleList.push(item);
+                    let slotElm = slot[i].elm;
+                    if(slotElm){
+                        this.addClass(slotElm,'hide');
+                        if(this.activeIndex == i) {
+                            this.removeClass(slotElm,'hide');
+                        }
+                    }                
+                }
             }
             setTimeout(()=>{
                 this.getTabWidth();
             },0);
-            
         },
         getStyle:function(obj,styleName){
             if(!obj){
@@ -214,19 +171,18 @@ export default {
             if(this.positionNav=='top' || this.positionNav=='bottom'){
                 let tabTitle = document.querySelector('.nut-tab-title');
                 let tabWidth = this.getStyle(tabTitle,'width');
-                let tabWidthNum = tabWidth.substring(0,tabWidth.length-2);
-                let navBarWidth = (tabWidthNum/this.tabTitleList.length);
-                this.navWidth = navBarWidth;
-                this.initX= parseInt(this.navWidth * this.defIndex);
+                this.setInitX(tabWidth);
             }else{
-                let tabTitle = document.querySelector('.nut-tab-title-leftnav')||document.querySelector('.nut-tab-title-rightnav');
+                let tabTitle = document.querySelector('.nut-tab-title-leftnav')|| document.querySelector('.nut-tab-title-rightnav');
                 let tabWidth = this.getStyle(tabTitle,'height');
-                let tabWidthNum = tabWidth.substring(0,tabWidth.length-2);
-                let navBarWidth = (tabWidthNum/this.tabTitleList.length);
-                this.navWidth = navBarWidth;
-                this.initX= parseInt(this.navWidth * this.defIndex);
+                this.setInitX(tabWidth);
             }
-            
+        },
+        setInitX(tabWidth){
+            let tabWidthNum = tabWidth.substring(0,tabWidth.length-2);
+            let navBarWidth = (tabWidthNum/this.tabTitleList.length);
+            this.navWidth = navBarWidth;
+            this.initX= parseInt(this.navWidth * this.defIndex);
         },
         hasClass:function( elements,cName ){ 
             return !!elements.className.match( new RegExp( "(\\s|^)" + cName + "(\\s|$)") ); // ( \\s|^ ) 判断前面是否有空格 (\\s | $ )判断后面是否有空格 两个感叹号为转换为布尔值 以方便做判断 
@@ -238,7 +194,7 @@ export default {
         },
         removeClass:function ( elements,cName ){ 
             if( this.hasClass( elements,cName ) ){ 
-            elements.className = elements.className.replace( new RegExp( "(\\s|^)" + cName + "(\\s|$)" )," " ); // replace方法是替换 
+                elements.className = elements.className.replace( new RegExp( "(\\s|^)" + cName + "(\\s|$)" )," " ); // replace方法是替换 
             }; 
         }, 
         switchTabLink:function(index,event,disable){
@@ -251,16 +207,16 @@ export default {
                 this.activeIndex=index;
                 this.initX= parseInt(this.navWidth * index);
                 let nutTab = event.target.parentNode.parentNode;
-                let items =  this.positionNav=='bottom' || this.positionNav=='right' ?nutTab.children[0].children : nutTab.children[1].children;
+                let items =  this.positionNav=='bottom' || this.positionNav=='right' ? nutTab.children[0].children : nutTab.children[1].children;
                 for(let i=0;i<items.length;i++){
                     if(i==index){
                         this.removeClass(items[i],'hide');
-                        
                     }else{
                         this.addClass(items[i],'hide');
                     }
                 }
                 this.$emit('tab-switch',index,event); 
+                this.$emit('tabSwitch',index,event); //兼容以前驼峰法命名
             }
         }
     },

+ 47 - 28
src/packages/tabbar/__test__/tabbar.spec.js

@@ -4,7 +4,46 @@ import Vue from 'vue';
 
 
 describe('Tabbar.vue', () => {
-    const wrapper = shallowMount(Tabbar);
+    const wrapper = mount(Tabbar,{
+		propsData:{
+			tabList: [
+				{
+					'tabTitle':'百度',
+					'curr':false,
+					'icon':'',
+					'num':5,
+				},
+				{
+					'tabTitle':'京东',
+					'curr':true,
+					'icon':''
+				},
+				{
+					'tabTitle':'知乎',
+					'curr':false,
+					'icon':'',
+				}
+			]
+		}
+	});
+	wrapper.setData({ tabList: [
+		{
+			'tabTitle':'百度',
+			'curr':false,
+			'icon':'',
+			'num':5,
+		},
+		{
+			'tabTitle':'京东',
+			'curr':true,
+			'icon':''
+		},
+		{
+			'tabTitle':'知乎',
+			'curr':false,
+			'icon':'',
+		}
+	]});
     it('页签类型为based', () => {
         wrapper.setProps({ type: 'based' });
         return Vue.nextTick().then(function () {
@@ -20,33 +59,13 @@ describe('Tabbar.vue', () => {
         })
     });
 
-    
-    it('点击tab标签', () => {
-    	wrapper.setData({ tabList: [
-    	        {
-    	          'tabTitle':'百度',
-    	          'curr':false,
-    	          'icon':'',
-    	          'num':5,
-    	        },
-    	        {
-    	          'tabTitle':'京东',
-    	          'curr':true,
-    	          'icon':''
-    	        },
-    	        {
-    	          'tabTitle':'知乎',
-    	          'curr':false,
-    	          'icon':'',
-    	        }
-    	      ]});
-    	return Vue.nextTick().then(function () {
-		    wrapper.findAll('.tabbar-nav').at(1).trigger('click');
-            expect(wrapper.findAll('.tabbar-nav').at(1).is('.curr')).toBe(true)
-        })
-        
-	});
-	it('tab标签标题', () => {
+    // it('点击tab标签', () => {
+    // 	return Vue.nextTick().then(function () {
+	// 		wrapper.findAll('.tabbar-nav').at(1).trigger('click');
+	// 		expect(wrapper.findAll('.tabbar-nav').at(1).contains('.curr')).toBe(true)
+	// 	})
+	// });
+	it('设置tab标签标题', () => {
 		return Vue.nextTick().then(function () {
 		   expect(wrapper.findAll('.tabbar-nav-word').at(0).text()).toBe('百度');
 		})

+ 3 - 28
src/packages/tabbar/demo.vue

@@ -94,38 +94,13 @@ export default {
   },
   methods: {
       tabSwitch1:function(value,index){
-        /*let arr = [...this.tabList1];
-        arr.map((item,idx)=>{
-          if(idx==index){
-              item.curr = true;
-          }else{
-              item.curr = false;
-          }
-        })
-        this.tabList1 = [...arr];*/
-        console.log(index);
+        console.log(value,index);
       },
       tabSwitch2:function(value,index){
-        /*let arr = [...this.tabList2];
-        arr.map((item,idx)=>{
-          if(idx==index){
-              item.curr = true;
-          }else{
-              item.curr = false;
-          }
-        })
-        this.tabList2 = [...arr];*/
+        console.log(value,index);
       },
       tabSwitch3:function(value,index){
-        /*let arr = [...this.tabList3];
-        arr.map((item,idx)=>{
-          if(idx==index){
-              item.curr = true;
-          }else{
-              item.curr = false;
-          }
-        })
-        this.tabList3 = [...arr];*/
+        console.log(value,index);
       }
   }
 };

+ 6 - 2
src/packages/tabbar/tabbar.scss

@@ -1,12 +1,16 @@
 .nut-tabbar{
 	border:0px;
 	border-bottom: 1px solid #eee;
+	border-top: 1px solid #eee;
     width: 100%;
     display: flex;
     height: 70px;
     box-sizing: border-box;
     background: #fff;
-    margin-top: 20px;
+	margin-top: 20px;
+	&:last-child{
+		border-right: 0;
+	}
 }
 .bottom{
 	position: fixed;
@@ -24,7 +28,7 @@
     justify-content: center;
     align-items: center;
 }
-.bor-right{
+.card{
 	border-right:1px solid #eee;
 }
 .curr{

+ 12 - 14
src/packages/tabbar/tabbar.vue

@@ -2,8 +2,8 @@
     <div class="nut-tabbar" :class="{'bottom':bottom}">
         <a class="tabbar-nav" 
         v-for="(value,index) in tabList" 
-        :class="[{'curr':value.curr},type=='card'?'bor-right':null]"
-        v-bind:key="value.tabTitle"
+        :class="[{'curr':value.curr},type]"
+        :key="value.tabTitle"
         v-on:click="switchTabs(value,index)"
         :href="value.href"
         >   
@@ -11,9 +11,8 @@
             <b class="tips" v-if="value.num">{{value.num}}</b>
             <template v-if="value.icon">
                 <div class="icon" v-if="value.curr" :style="{backgroundImage: 'url('+value.activeIcon+')'}"></div>
-                <div class="icon" v-if="!value.curr" :style="{backgroundImage: 'url('+value.icon+')'}"></div>
+                <div class="icon" v-else :style="{backgroundImage: 'url('+value.icon+')'}"></div>
             </template>
-            
             <span :class="['tabbar-nav-word',{'big-word':!value.icon}]">{{value.tabTitle}}</span>
         </span>
         </a>
@@ -43,19 +42,17 @@ export default {
           tabList:this.tabbarList
         };
     },
-    computed:{
-        
-    },
-    mounted() {
-        
+    watch:{
+       tabbarList:{
+           handler(value){
+            this.tabList = value;
+           },
+           deep:true
+       }
     },
     methods: {
-        closeItem:function(value,e){
-            this.$emit('delete-tabs',value); 
-        },
         switchTabs:function(value,index){
-            let newArr = [...this.tabList];
-            newArr.forEach((item,idx)=>{
+            let newArr = this.tabList.map((item,idx)=>{
                 if(index == idx){
                    item.curr = true;
                 }else{
@@ -64,6 +61,7 @@ export default {
             })
             this.tabList =newArr;
             this.$emit('tab-switch',value,index); 
+            this.$emit('tabSwitch',value,index); //兼容以前驼峰法
         }
     },
 }

+ 1 - 1
src/packages/tabselect/__test__/tabselect.spec.js

@@ -164,7 +164,7 @@ describe("TabSelect.vue", () => {
     return Vue.nextTick().then(function() {
       expect(
         wrapper
-          .findAll(".nut-tab-link")
+          .findAll(".nut-title-nav")
           .at(0)
           .text()
       ).toBe("京东快递测试");

+ 2 - 2
src/packages/textbox/demo.vue

@@ -2,7 +2,7 @@
     <div>
         <h5>示例</h5>
         <h6>默认用法</h6>
-        <nut-textbox > </nut-textbox>
+         <nut-textbox v-model="val"></nut-textbox>
         <h6>自定义高度:100px</h6>
         <nut-textbox :txtAreaH="100" :maxNum="300"></nut-textbox>
 
@@ -37,7 +37,7 @@
 export default {
     data(){
         return{
-           
+            val: ''
         }
     },
     methods:{

+ 49 - 42
src/packages/textbox/doc.md

@@ -3,88 +3,95 @@
 多行文本输入框,支持字数提示、字数限制等功能。
 
 ## 基本用法
+
 ```html
- <nut-textbox><nut-textbox/>
+<nut-textbox v-model="val"></nut-textbox>
 ```
+
 ## 自定义高度
+
 ```html
- <nut-textbox 
-    :txtAreaH="5" 
-    :maxNum="300">
-</nut-textbox>
+<nut-textbox :txtAreaH="5" :maxNum="300"> </nut-textbox>
 ```
+
 ## 自定义提示语
+
 ```html
-<nut-textbox 
-    :placeText="'请填写详细情况请填写详细情况'">
-</nut-textbox>
+<nut-textbox :placeText="'请填写详细情况请填写详细情况'"> </nut-textbox>
 ```
+
 ## 自定义字数限制
+
 ```html
-<nut-textbox 
-    :maxNum="100">
-</nut-textbox>
+<nut-textbox :maxNum="100"> </nut-textbox>
 ```
+
 ## 限制字数不可超出
+
 ```html
- <nut-textbox 
-    :switchMax="true" 
-    :maxNum="10" 
-    :txtAreaH="2" 
-    textBgColor="#efefef">
+<nut-textbox :switchMax="true" :maxNum="10" :txtAreaH="2" textBgColor="#efefef">
 </nut-textbox>
 ```
+
 ## 字数超出报错
+
 ```html
- <nut-textbox 
-    :maxNum="10" 
-    :txtAreaH="2" 
-     @errorFunc="overLength" >
-</nut-textbox>
+<nut-textbox :maxNum="10" :txtAreaH="2" @errorFunc="overLength"> </nut-textbox>
 ```
+
 ## 自定义文本框背景色
+
 ```html
- <nut-textbox :switchMax="true" :maxNum="10" :txtAreaH="2" textBgColor="#feefef"></nut-textbox>
+<nut-textbox
+  :switchMax="true"
+  :maxNum="10"
+  :txtAreaH="2"
+  textBgColor="#feefef"
+></nut-textbox>
 ```
 
 ## 不显示字数限制
+
 ```html
-<nut-textbox :limitShow="false" :maxNum="10" ></nut-textbox>
+<nut-textbox :limitShow="false" :maxNum="10"></nut-textbox>
 ```
 
 ## 输入回调返回文字
+
 ```html
- <nut-textbox  :maxNum="10" txtAreaH="2" @inputFunc="inputText" ></nut-textbox>
+<nut-textbox :maxNum="10" txtAreaH="2" @inputFunc="inputText"></nut-textbox>
 ```
+
 ```javascript
 export default {
   data() {
     return {
-      val: ""
-    };
+      val: ''
+    }
   },
   methods: {
-     inputText(val){
-           alert(val);
-        }
+    inputText(val) {
+      alert(val)
+    }
   }
-};
+}
 ```
 
 ## Prop
 
-| 字段 | 说明 | 类型 | 默认值
-|----- | ----- | ----- | ----- 
-| txtAreaH | 文本框高度 | Number | 1rem
-| placeText | 自定义placeholder文案提示 | String | 请您在此输
-| maxNum | 最大字数 | Number | 50
-| switchMax | 控制字数超出是否不可输入,注意:最大字数限制,请设置maxNum | Boolean | false
-| textBgColor | 设置输入框背景色 | String | #fff
-| limitShow | 不显示字数限制 | Boolean | true
+| 字段        | 说明                                                        | 类型    | 默认值     |
+| ----------- | ----------------------------------------------------------- | ------- | ---------- |
+| value       | 当前 input 值,可使用 v-model 双向绑定数据                  | String  | ''         |
+| txtAreaH    | 文本框高度                                                  | Number  | 1rem       |
+| placeText   | 自定义 placeholder 文案提示                                 | String  | 请您在此输 |
+| maxNum      | 最大字数                                                    | Number  | 50         |
+| switchMax   | 控制字数超出是否不可输入,注意:最大字数限制,请设置 maxNum | Boolean | false      |
+| textBgColor | 设置输入框背景色                                            | String  | #fff       |
+| limitShow   | 不显示字数限制                                              | Boolean | true       |
 
 ## Event
 
-| 字段 | 说明 | 回调参数 
-|----- | ----- | ----- 
-| errorFunc | 输入字数超过限定字数时触发事件 | -- 
-| inputText | 文字输入事件回调,默认传回输入文本 | --
+| 字段      | 说明                               | 回调参数 |
+| --------- | ---------------------------------- | -------- |
+| errorFunc | 输入字数超过限定字数时触发事件     | --       |
+| inputText | 文字输入事件回调,默认传回输入文本 | --       |

+ 18 - 8
src/packages/textbox/textbox.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="nut-textbox">
         <div class="txt-area" :class="{'error':errorState,'num-none':limitShow == false}" :style="{background:textBgColor}">
-            <textarea :placeholder="placeText" :style="{height:txtAreaHeight+'px'}" v-model="textInfo" @input="txtIptLength" :switchMax="switchMax" :maxlength="iptMaxlength"></textarea>
+            <textarea :placeholder="placeText" :style="{height:txtAreaHeight+'px'}" v-model="text" @input="txtIptLength" :switchMax="switchMax" :maxlength="iptMaxlength"></textarea>
             <span v-show="limitShow">{{txtNum}}/{{maxNum}}</span>
         </div>
     </div>
@@ -10,6 +10,10 @@
 export default {
     name:'nut-textbox',
     props: {
+        value: {
+            type: String,
+            default: ""
+        },
     	maxNum:{
     		type:[String,Number],
     		default:50,
@@ -38,7 +42,7 @@ export default {
     },
     data() {
         return {
-        	textInfo:'',
+            text: '',
             errorState:false,
             txtNum:0,
         };
@@ -58,18 +62,24 @@ export default {
         	return maxlength
         },
     },
+    mounted() {
+        this.text = this.value
+    },
     methods: {
-        txtIptLength(){
-            let txtVal = this.textInfo.length;
-            this.txtNum = txtVal;  
-            if(txtVal > this.maxNum) {
+        txtIptLength(event){
+            const data = event.target.value
+            console.log(data)
+            const txtLength = data.length;
+            this.txtNum = txtLength;
+            if(txtLength > this.maxNum) {
                 this.errorState = true;
                 this.$emit('errorFunc'); 
            	}else{
                 this.errorState = false;
            	}
-            this.$emit('inputFunc',this.textInfo);
+            this.$emit('inputFunc',data);
+            this.$emit('input',data);
         }
     }
 }
-</script>
+</script>