Browse Source

Merge branch 'v3-dev' of https://github.com/jdf2e/nutui into v3-dev

yushuang24 5 years ago
parent
commit
dad682e419

+ 1 - 1
package.json

@@ -18,7 +18,7 @@
     "clean": "nutui-cli clean",
     "add": "nutui-cli add",
     "lint": "nutui-cli lint",
-    "test": "cross-env NODE_ENV=test nyc mocha-webpack --webpack-config node_modules/@nutui/cli/dist_cli/webpack/test.config.js  --require node_modules/@nutui/cli/dist_cli/test/setup.js src/packages/*/__test__/**.spec.js",
+    "test": "cross-env NODE_ENV=test nyc mocha-webpack --webpack-config node_modules/@nutui/cli/dist_cli/webpack/test.config.js  --require node_modules/@nutui/cli/dist_cli/test/setup.js src/packages/tab/__test__/**.spec.js",
     "coveralls": "cat ./coverage/lcov.info | coveralls",
     "test:watch": "npm run test --watch",
     "prettier:check": "prettier -l src/**/*.{ts,js,vue,scss}",

+ 10 - 32
src/config.json

@@ -169,16 +169,6 @@
     },
     {
       "version": "1.0.0",
-      "name": "Badge",
-      "sort": "0",
-      "chnName": "徽标",
-      "desc": "出现在图标或文字右上角的红色圆点、数字或者文字,表示有新内容或者待处理的信息",
-      "type": "component",
-      "showDemo": true,
-      "author": "杨磊"
-    },
-    {
-      "version": "1.0.0",
       "name": "Rate",
       "sort": "1",
       "chnName": "评分",
@@ -261,28 +251,6 @@
     },
     {
       "version": "1.0.0",
-      "name": "Scroller",
-      "chnName": "滚动",
-      "desc": "滚动组件",
-      "type": "component",
-      "sort": "0",
-      "star": 5,
-      "showDemo": true,
-      "author": "iris"
-    },
-    {
-      "version": "1.0.0",
-      "name": "InfiniteLoading",
-      "chnName": "无限加载",
-      "desc": "无限加载",
-      "type": "component",
-      "sort": "0",
-      "star": 4,
-      "showDemo": true,
-      "author": "iris"
-    },
-    {
-      "version": "1.0.0",
       "name": "Uploader",
       "chnName": "上传",
       "desc": "文件上传组件",
@@ -390,6 +358,16 @@
       "sort": "6",
       "showDemo": true,
       "author": "yangxiaolu"
+    },
+    {
+      "version": "1.0.0",
+      "name": "Tag",
+      "chnName": "标签",
+      "desc": "Tag 标签组件",
+      "type": "component",
+      "sort": "0",
+      "showDemo": true,
+      "author": "ivanwancy"
     }
   ]
 }

+ 4 - 10
src/nutui.js

@@ -37,8 +37,6 @@ import Steps from './packages/steps/index.js';
 import './packages/steps/steps.scss';
 import Button from './packages/button/index.js';
 import './packages/button/button.scss';
-import Badge from './packages/badge/index.js';
-import './packages/badge/badge.scss';
 import Rate from './packages/rate/index.js';
 import './packages/rate/rate.scss';
 import Stepper from './packages/stepper/index.js';
@@ -55,10 +53,6 @@ import CheckBox from './packages/checkbox/index.js';
 import './packages/checkbox/checkbox.scss';
 import Skeleton from './packages/skeleton/index.js';
 import './packages/skeleton/skeleton.scss';
-import Scroller from './packages/scroller/index.js';
-import './packages/scroller/scroller.scss';
-import InfiniteLoading from './packages/infiniteloading/index.js';
-import './packages/infiniteloading/infiniteloading.scss';
 import Uploader from './packages/uploader/index.js';
 import './packages/uploader/uploader.scss';
 import TextInput from './packages/textinput/index.js';
@@ -80,6 +74,8 @@ import SideNavBarItem from './packages/sidenavbaritem/index.js';
 import './packages/sidenavbaritem/sidenavbaritem.scss';
 import Address from './packages/address/index.js';
 import './packages/address/address.scss';
+import Tag from './packages/tag/index.js';
+import './packages/tag/tag.scss';
 
 const packages = {
   Cell,
@@ -101,7 +97,6 @@ const packages = {
   Row,
   Steps,
   Button,
-  Badge,
   Rate,
   Stepper,
   SearchBar,
@@ -110,8 +105,6 @@ const packages = {
   RadioGroup,
   CheckBox,
   Skeleton,
-  Scroller,
-  InfiniteLoading,
   Uploader,
   TextInput,
   TextBox,
@@ -122,7 +115,8 @@ const packages = {
   SideNavBar: SideNavBar,
   SubSideNavBar: SubSideNavBar,
   SideNavBarItem: SideNavBarItem,
-  Address: Address
+  Address: Address,
+  Tag: Tag
 };
 
 const components = {};

+ 0 - 38
src/packages/badge/__test__/badge.spec.js

@@ -1,38 +0,0 @@
-import { shallowMount, mount } from '@vue/test-utils'
-import Badge from '../badge.vue';
-import Vue from 'vue';
-
-describe('Badge.vue', () => {
-    const wrapper = shallowMount(Badge, {});
-    it('创建结构', () => {
-        wrapper.setProps({ value: '9'});
-        return Vue.nextTick().then(function () {
-            expect(wrapper.contains('sup')).toBe(true);            
-        })
-    });
-    it('字数设置', () => {
-        wrapper.setProps({ value: '9'});
-        return Vue.nextTick().then(function () {
-            expect(wrapper.find('.nut-badge__content').text()).toBe('9');            
-        })
-    });
-
-    it('最大值设置', () => {
-        wrapper.setProps({ value: 200, max: 99 });
-        return Vue.nextTick().then(function () {
-            expect(wrapper.find('.nut-badge__content').text()).toBe('99+');
-        })
-    });
-    it('文字设置',() => {
-        wrapper.setProps({value: 'new'});
-        return Vue.nextTick().then(function() {
-            expect(wrapper.find('.nut-badge__content').text()).toBe('new');
-        })
-    })
-    it('设置为点操作',() => {
-        wrapper.setProps({value: 'new', isDot: true});
-        return Vue.nextTick().then(function() {
-            expect(wrapper.find('.nut-badge__content').text()).toBe('');
-        })
-    })
-});

+ 0 - 29
src/packages/badge/badge.scss

@@ -1,29 +0,0 @@
-.nut-badge {
-  position: relative;
-  display: inline-block;
-  sup {
-    position: absolute;
-    height: 18px;
-    min-width: 8px;
-    line-height: 18px;
-    padding: 0 5px;
-    background-color: #fff;
-    text-align: center;
-    border: 1px solid $primary-color;
-    color: $primary-color;
-    font-size: 10px;
-    border-radius: 10px;
-    z-index: $zindex-mask;
-  }
-  .nut-badge__content {
-    transform: translateY(-50%) translateX(100%);
-  }
-  .is-dot {
-    width: 10px;
-    height: 10px;
-    padding: 0;
-    right: 5px;
-    border-radius: 10px;
-    background: $primary-color;
-  }
-}

+ 0 - 60
src/packages/badge/badge.vue

@@ -1,60 +0,0 @@
-<template>
-  <div class="nut-badge">
-    <slot></slot>
-    <sup v-show="!hidden && (content || isDot)" v-text="content" class="nut-badge__content" :class="{ 'is-dot': isDot }" :style="stl"> </sup>
-  </div>
-</template>
-<script>
-export default {
-  name: 'nut-badge',
-  props: {
-    value: {
-      type: [String, Number]
-    },
-    max: {
-      type: Number,
-      default: 10000
-    },
-    isDot: {
-      type: Boolean,
-      default: false
-    },
-    hidden: {
-      type: Boolean,
-      default: false
-    },
-    top: {
-      type: String,
-      default: '0'
-    },
-    right: {
-      type: String,
-      default: '0'
-    },
-    zIndex: {
-      type: Number,
-      default: 10
-    }
-  },
-  data() {
-    return {
-      stl: {
-        top: this.top,
-        right: this.right,
-        zIndex: this.zIndex
-      }
-    };
-  },
-  computed: {
-    content() {
-      if (this.isDot) return;
-      const value = this.value;
-      const max = this.max;
-      if (typeof value === 'number' && typeof max === 'number') {
-        return max < value ? `${max}+` : value;
-      }
-      return value;
-    }
-  }
-};
-</script>

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

@@ -1,63 +0,0 @@
-<template>
-  <div class="container">
-    <h4>默认用法</h4>
-    <div class="demo-w">
-      <nut-badge :value="9" class="item"><div class="demo-svg"></div></nut-badge>
-      <nut-badge :value="9" class="item">购物车</nut-badge>
-      <nut-badge :value="9" class="item"><nut-button>购物车</nut-button></nut-badge>
-    </div>
-
-    <h4>Max用法</h4>
-    <div class="demo-w">
-      <nut-badge :value="200" :max="99" class="item"><div class="demo-svg"></div></nut-badge>
-      <nut-badge :value="200" :max="99" class="item">购物车</nut-badge>
-      <nut-badge :value="200" :max="99" class="item"><nut-button>购物车</nut-button></nut-badge>
-    </div>
-
-    <h4>文字用法</h4>
-    <div class="demo-w">
-      <nut-badge value="new" class="item"><div class="demo-svg"></div></nut-badge>
-      <nut-badge value="new" class="item">购物车</nut-badge>
-      <nut-badge value="new" :max="99" class="item"><nut-button>购物车</nut-button></nut-badge>
-    </div>
-
-    <h4>小圆点</h4>
-    <div class="demo-w">
-      <nut-badge :isDot="true" class="item"><div class="demo-svg"></div></nut-badge>
-      <nut-badge :isDot="true" class="item">购物车</nut-badge>
-      <nut-badge :isDot="true" :max="99" class="item"><nut-button>购物车</nut-button></nut-badge>
-    </div>
-
-    <h4>自定义位置</h4>
-    <div class="demo-w">
-      <nut-badge :value="200" top="5px" right="10px" class="item"><div class="demo-svg"></div></nut-badge>
-    </div>
-  </div>
-</template>
-
-<script>
-export default {
-  data() {
-    return {};
-  },
-  created() {},
-  methods: {}
-};
-</script>
-
-<style lang="scss" scoped>
-.item {
-  margin: 10px 20px;
-}
-.demo-w {
-  margin: 20px 0;
-}
-.demo-svg {
-  display: inline-block;
-  height: 30px;
-  width: 35px;
-  background-size: 100% 100%;
-  background-image: url('#{$assetsPath}/img/gift.png');
-  background-repeat: no-repeat;
-}
-</style>

+ 0 - 143
src/packages/badge/doc.md

@@ -1,143 +0,0 @@
-# Badge 徽标
-
-出现在图标或文字右上角的红色圆点、数字或者文字,表示有新内容或者待处理的信息。
-
-## 基本用法
-
-```html
-<nut-badge 
-    :value="9" 
-    class="item"
->
-    <div class="demo-svg"></div>
-</nut-badge>
-
-<nut-badge 
-    :value="9" 
-    class="item"
->
-    购物车
-</nut-badge>
-
-<nut-badge 
-    :value="9" 
-    class="item"
->
-    <nut-button>
-        购物车
-    </nut-button>
-</nut-badge>
-```
-
-## Max用法
-
-```html
-<nut-badge 
-    :value="200" 
-    :max="99" 
-    class="item"
->
-    <div class="demo-svg"></div>
-</nut-badge>
-
-<nut-badge 
-    :value="200" 
-    :max="99" 
-    class="item"
->
-    购物车
-</nut-badge>
-
-<nut-badge 
-    :value="200" 
-    :max="99" 
-    class="item"
->
-    <nut-button>
-        购物车
-    </nut-button>
-</nut-badge>
-```
-
-## 文字用法
-
-```html
-<nut-badge 
-    value="new" 
-    class="item"
->
-    <div class="demo-svg"></div>
-</nut-badge>
-
-<nut-badge 
-    value="new" 
-    class="item"
->
-    购物车
-</nut-badge>
-
-<nut-badge 
-    value="new" 
-    :max="99" 
-    class="item"
->
-    <nut-button>
-        购物车
-    </nut-button>
-</nut-badge>
-```
-
-## 小圆点
-
-```html
-<nut-badge 
-    :isDot="true" 
-    class="item"
->
-    <div class="demo-svg"></div>
-</nut-badge>
-
-<nut-badge 
-    :isDot="true" 
-    class="item"
->
-    文字内容
-</nut-badge>
-
-<nut-badge 
-    :isDot="true" 
-    :max="99" 
-    class="item"
->
-    <nut-button>
-        购物车
-    </nut-button>
-</nut-badge>
-```
-
-## 自定义位置
-
-```html
-<nut-badge 
-    :value="200" 
-    top="5px" 
-    right="10px" 
-    class="item"
->
-    <div class="demo-svg">
-    </div>
-</nut-badge>
-```
-
-
-## Prop
-
-| 字段 | 说明 | 类型 | 默认值
-|----- | ----- | ----- | ----- 
-| value | 显示的内容 | String | -
-| max | value为数值时,最大值 | Number | 10000
-| zIndex | 徽标的z-index值 | Number | 10
-| isDot | 是否为小点 | Boolean | false
-| hidden | 是否隐藏 | Boolean | false
-| top   | 上下偏移量,支持单位设置,可设置为:5px、5rem等 | String | 0
-| left  | 左右偏移量,支持单位设置,可设置为:5px、5rem等 | String | 0

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

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

+ 0 - 8
src/packages/switch/__test__/switch.spec.js

@@ -56,14 +56,6 @@ describe('Switch.vue', () => {
         })
     });
 
-    it('尺寸设置为large', () => {
-        wrapper.setProps({ size: 'large' });
-
-        return Vue.nextTick().then(function () {
-            expect(wrapper.contains('.nut-switch-large')).toBe(true);
-        })
-    });
-
     it('打开状态点击关闭', () => {
         wrapper.setProps({ active: true });
         wrapper.trigger('click');

+ 27 - 46
src/packages/switch/demo.vue

@@ -1,29 +1,36 @@
 <template>
   <div class="demo-list">
-    <h4>基本用法</h4>
+    <h4>基础样式</h4>
     <div>
-      <nut-cell>
-        <span slot="title"><nut-switch :active.sync="swActive"></nut-switch></span>
-        <div slot="desc">{{ swActive }}</div>
-      </nut-cell>
+        <nut-cell>
+            <div slot="title">{{ swActive ? '开' : '关' }}</div>
+            <span slot="desc"><nut-switch :active.sync="swActive"></nut-switch></span>
+        </nut-cell>
+        <nut-cell>
+            <div slot="title">开启禁用</div>
+            <span slot="desc"><nut-switch :disabled="true"></nut-switch></span>
+        </nut-cell>
+        <nut-cell>
+            <div slot="title">关闭禁用</div>
+            <span slot="desc"><nut-switch @change="onChange" :active="true" :disabled="true"></nut-switch></span>
+        </nut-cell>
     </div>
 
-    <h4>自定义尺寸</h4>
+    <h4>迷你开关</h4>
     <div>
-      <nut-cell>
-        <span slot="title"><nut-switch :active="true" size="small"></nut-switch></span>
-        <div slot="desc">small</div>
-      </nut-cell>
-      <nut-cell>
-        <span slot="title"><nut-switch :active="true" size="base"></nut-switch></span>
-        <div slot="desc">base</div>
-      </nut-cell>
-      <nut-cell>
-        <span slot="title"><nut-switch :active="true" size="large"></nut-switch></span>
-        <div slot="desc">large</div>
-      </nut-cell>
+        <nut-cell>
+            <div slot="title">{{ swActive2 ? '开' : '关' }}</div>
+            <span slot="desc"><nut-switch :active.sync="swActive2" size="small"></nut-switch></span>
+        </nut-cell>
+        <nut-cell>
+            <div slot="title">开启禁用</div>
+            <span slot="desc"><nut-switch :disabled="true" size="small"></nut-switch></span>
+        </nut-cell>
+        <nut-cell>
+            <div slot="title">关闭禁用</div>
+            <span slot="desc"><nut-switch @change="onChange" :active="true" :disabled="true" size="small"></nut-switch></span>
+        </nut-cell>
     </div>
-    <p>内置"small","base","large"三种规格</p>
 
     <h4>change事件</h4>
     <div>
@@ -31,25 +38,6 @@
         <span slot="title"><nut-switch @change="onChange"></nut-switch></span>
       </nut-cell>
     </div>
-
-    <h4>禁用状态</h4>
-    <div>
-      <nut-cell>
-        <span slot="title"><nut-switch :disabled="true"></nut-switch></span>
-      </nut-cell>
-      <nut-cell>
-        <span slot="title"><nut-switch @change="onChange" :active="true" :disabled="true"></nut-switch></span>
-      </nut-cell>
-    </div>
-    <p>禁用状态下,change事件参数永远为初始值</p>
-
-    <h4>循环场景</h4>
-    <div v-for="(item, index) of list" :key="index">
-      <nut-cell>
-        <span slot="title"> {{ item.name }}</span>
-        <span slot="desc"><nut-switch @change="onChangeLabel($event, index)" :active="true"></nut-switch></span>
-      </nut-cell>
-    </div>
     <h4>自定义Class</h4>
     <div>
       <nut-cell>
@@ -64,19 +52,12 @@ export default {
   data() {
     return {
       swActive: true,
-      list: [
-        { id: '1', name: 'a' },
-        { id: '2', name: 'b' },
-        { id: '3', name: 'c' }
-      ]
+      swActive2: true
     };
   },
   methods: {
     onChange(status) {
       alert(status);
-    },
-    onChangeLabel(status, index) {
-      alert('status:' + status + ',selected:' + index);
     }
   }
 };

+ 3 - 7
src/packages/switch/doc.md

@@ -2,7 +2,7 @@
 
 用来打开或关闭选项。
 
-## 基本用法
+## 基础样式
 
 ```html
 <nut-switch 
@@ -30,7 +30,7 @@ export default {
 
 ## 自定义尺寸
 
-内置 **small**,**base**,**large** 三种规格供使用。
+内置 **small**,**base**种规格供使用。
 ```html
 <nut-switch 
   :active="true" 
@@ -40,10 +40,6 @@ export default {
   :active="true" 
   size="base"
 >
-<nut-switch 
-  :active="true" 
-  size="large"
->
 ```
 
 ## change事件
@@ -87,5 +83,5 @@ export default {
 | 字段 | 说明 | 类型 | 默认值
 |----- | ----- | ----- | -----
 | active | 开关状态 | Boolean | false
-| size | 尺寸,可选值small/base/large | String | base
+| size | 尺寸,可选值small/base | String | base
 | disabled | 是否禁用 | Boolean | false

+ 18 - 32
src/packages/switch/switch.scss

@@ -1,70 +1,56 @@
 .nut-switch {
   position: relative;
   display: inline-block;
-  background: #fff;
-  border-radius: 1000px;
+  background: #eee;
+  border-radius: 20px;
   vertical-align: bottom;
   box-sizing: content-box;
-  border: 2px $border-style-base $border-color-base;
+  border: 0;
   transition: all $transition-duration $animation-timing-fun;
   .nut-switch-btn {
     position: absolute;
     left: 0;
-    background: $border-color-base;
-    border: 2px solid #fff;
+    background: #fff;
+    border: 0;
     border-radius: 50%;
     box-sizing: border-box;
     transition: all $transition-duration $animation-timing-fun;
   }
   &.nut-switch-active {
-    border-color: $border-color-active;
-    .nut-switch-btn {
-      background-color: $primary-color;
-    }
+    background-color: $primary-color-jd-red;
   }
   &.nut-switch-disabled {
     opacity: 0.6;
   }
 }
 
-.nut-switch-small {
-  height: 14px;
-  width: 32px;
+.nut-switch-base {
+  height: 28px;
+  width: 54px;
   .nut-switch-btn {
-    height: 14px;
-    width: 14px;
+    height: 28px;
+    width: 28px;
   }
   &.nut-switch-active {
     .nut-switch-btn {
-      left: 18px;
+      left: 28px;
     }
   }
 }
 
-.nut-switch-base {
+.nut-switch-small {
   height: 20px;
-  width: 46px;
+  width: 38px;
+  &.nut-switch{
+      border-radius: 11px;
+  }
   .nut-switch-btn {
     height: 20px;
     width: 20px;
   }
   &.nut-switch-active {
     .nut-switch-btn {
-      left: 26px;
-    }
-  }
-}
-
-.nut-switch-large {
-  height: 28px;
-  width: 58px;
-  .nut-switch-btn {
-    height: 28px;
-    width: 28px;
-  }
-  &.nut-switch-active {
-    .nut-switch-btn {
-      left: 30px;
+      left: 19px;
     }
   }
 }

+ 51 - 61
src/packages/tab/__test__/tab.spec.js

@@ -5,14 +5,24 @@ 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);
-    //     })
-    // });
-
+    wrapper.setData({ tabTitleList: [
+      {
+        tabTitle: "衣物",
+        content: "<p>衣物内容</p>"
+      },
+      {
+        tabTitle: "日用品",
+        content: "<p>日用品内容</p>"
+      },
+      {
+        tabTitle: "运动器材",
+        content: "<p>运动器材内容</p>"
+      },
+      {
+        tabTitle: "电影票",
+        content: "<p>电影票内容</p>"
+      }
+    ]});
     it('当前tab的位置', () => {
         wrapper.setProps({ positionNav: 'left' });
         return Vue.nextTick().then(function () {
@@ -20,64 +30,44 @@ describe('Tab.vue', () => {
             
         })
     });
-
-    // it('是否显示内容区域', () => {
-    //     wrapper.setProps({ contentShow: true });
-    //     return Vue.nextTick().then(function () {
-    //         expect(wrapper.contains('.nut-tab-item')).toBe(true);
-            
-    //     })
-    // });
-
-    it('禁止选择第一个标签', () => {
-        wrapper.setData({ tabTitleList: [
-            {
-              tabTitle: "衣物",
-              disable: true,
-              iconUrl:
-                "http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg",
-              content: "<p>衣物内容</p>"
-            },
-            {
-              tabTitle: "日用品",
-              iconUrl:
-                "http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg",
-              content: "<p>日用品内容</p>"
-            },
-            {
-              tabTitle: "运动器材",
-              iconUrl:
-                "http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg",
-              content: "<p>运动器材内容</p>"
-            },
-            {
-              tabTitle: "电影票",
-              iconUrl:
-                "http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg",
-              content: "<p>电影票内容</p>"
-            }
-          ] });
-          return Vue.nextTick().then(function () {
-              expect(wrapper.findAll('.nut-title-nav-leftnav').at(0).is('.nut-tab-disable')).toBe(true)
-          }) 
+    it('当前tab是否可以滑动', () => {
+      wrapper.setProps({ isScroll: 'true' ,positionNav:'top'});
+      return Vue.nextTick().then(function () {
+          expect(wrapper.contains('.nut-title-nav-scroll')).toBe(true);
+      })
     });
-    it('当前默认选中的tab', () => {
-        wrapper.setProps({ positionNav: 'top' });
-        return Vue.nextTick().then(function () {
-            expect(wrapper.findAll('.nut-title-nav-list').at(0).is('.nut-tab-active')).toBe(true)
-        })
+    it('当前tab包含滑动的底部导航条', () => {
+      wrapper.setProps({ scrollLine: 'true' ,positionNav:'top'});
+      return Vue.nextTick().then(function () {
+          expect(wrapper.contains('.nav-bar-top')).toBe(true);
+      })
+    });
+    it('当前tab包含点击式的底部导航条', () => {
+      wrapper.setProps({ tabLine: 'true' ,positionNav:'top'});
+      return Vue.nextTick().then(function () {
+          expect(wrapper.contains('.tab-line')).toBe(true);
+      })
+    });
+    it('当前tab默认活动的页签是否正确', () => {
+      wrapper.setProps({ tabLine: 'true', positionNav:'top'});
+      wrapper.setData({ activeIndex: 1});
+      return Vue.nextTick().then(function () {
+        expect(wrapper.findAll('.nut-title-nav').at(1).contains('.nut-tab-active')).toBe(true);
+      })
+    });
+    it('当前tab设置高度是否生效', () => {
+      wrapper.setProps({wrapperHeight:'250', positionNav:'left'});
+      return Vue.nextTick().then(function () {
+        expect(wrapper.findAll('.nut-tab-item').at(0).hasStyle('height','250px')).toBe(true);
+      })
     });
-    it('tab标签标题', () => {
+    it('点击tab事件', () => {
+      wrapper.setProps({positionNav:'top'});
       return Vue.nextTick().then(function () {
-         expect(wrapper.findAll('.nut-title-nav').at(0).text()).toBe('衣物');
+        wrapper.findAll('.nut-title-nav').at(2).trigger('click');
+        expect(wrapper.findAll('.nut-title-nav').at(1).contains('.nut-tab-active')).toBe(true);
       })
     });
-    // it('点击tab标签', () => {
-    // 	return Vue.nextTick().then(function () {
-    //         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)
-    //   })
-    // });
 });
 
 

+ 120 - 105
src/packages/tab/demo.vue

@@ -1,25 +1,24 @@
 <template>
   <div>
     <!-- DEMO区域 -->
-    <h4>默认用法</h4>
+    <h4>基础样式</h4>
     <nut-tab @tab-switch="tabSwitch">
-      <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-panel tab-title="页签4">页签4</nut-tab-panel>
+        <nut-tab-panel tab-title="页签一">这里是页签1内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签二">这里是页签2内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签三">这里是页签3内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签四">这里是页签4内容</nut-tab-panel>
     </nut-tab>
 
-    <h4>支持导航条在上下左右位置</h4>
+    <h4>禁用样式</h4>
     <nut-tab @tab-switch="tabSwitch">
-      <nut-tab-panel
-        v-for="value in editableTabs"
-        v-bind:key="value.tabTitle"
-        :tab-title="value.tabTitle"
-        :icon-url="value.iconUrl"
-        v-html="value.content"
-      ></nut-tab-panel>
+        <nut-tab-panel tab-title="页签一">这里是页签1内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签二">这里是页签2内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签三" :disable="true">这里是页签3内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签四">这里是页签4内容</nut-tab-panel>
     </nut-tab>
-    <nut-tab @tab-switch="tabSwitch" position-nav="left">
+
+    <h4>超出界面宽度</h4>
+    <nut-tab @tab-switch="tabSwitch" :is-scroll="true">
       <nut-tab-panel
         v-for="value in editableTabs"
         v-bind:key="value.tabTitle"
@@ -28,17 +27,20 @@
         v-html="value.content"
       ></nut-tab-panel>
     </nut-tab>
-    <nut-tab @tab-switch="tabSwitch" position-nav="right">
+
+    <h4>纵向tab切换</h4>
+    <nut-tab @tab-switch="tabSwitch" position-nav="left" :wrapper-height="350">
       <nut-tab-panel
         v-for="value in editableTabs"
         v-bind:key="value.tabTitle"
         :tab-title="value.tabTitle"
-        :iconUrl="value.iconUrl"
+        :icon-url="value.iconUrl"
         v-html="value.content"
       ></nut-tab-panel>
     </nut-tab>
 
-    <nut-tab @tab-switch="tabSwitch" position-nav="bottom">
+    <h4>纵向tab切换超出界面高度,设置tab区域高度</h4>
+    <nut-tab @tab-switch="tabSwitch" position-nav="left" :is-scroll="true" :wrapper-height="200">
       <nut-tab-panel
         v-for="value in editableTabs"
         v-bind:key="value.tabTitle"
@@ -48,36 +50,21 @@
       ></nut-tab-panel>
     </nut-tab>
 
-    <h4>支持滑动选择多个页签</h4>
-    <nut-tab @tab-switch="tabSwitch" :is-scroll="true">
-      <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-panel tab-title="页签4">页签4</nut-tab-panel>
-      <nut-tab-panel tab-title="页签5">页签5</nut-tab-panel>
-      <nut-tab-panel tab-title="页签6">页签6</nut-tab-panel>
-      <nut-tab-panel tab-title="页签7">页签7</nut-tab-panel>
+    <h4>设置可以滑动的线段</h4>
+    <nut-tab @tab-switch="tabSwitch" :tab-line="false" :scroll-line="true">
+        <nut-tab-panel tab-title="页签一">这里是页签1内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签二">这里是页签2内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签三">这里是页签3内容</nut-tab-panel>
+        <nut-tab-panel tab-title="页签四">这里是页签4内容</nut-tab-panel>
     </nut-tab>
-
-    <h4>支持滑动选择多个页签</h4>
-    <nut-tab @tab-switch="tabSwitch" :is-scroll="true" position-nav="left">
-      <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-panel tab-title="页签4">页签4</nut-tab-panel>
-      <nut-tab-panel tab-title="页签5">页签5</nut-tab-panel>
-      <nut-tab-panel tab-title="页签6">页签6</nut-tab-panel>
-      <nut-tab-panel tab-title="页签7">页签7</nut-tab-panel>
-    </nut-tab>
-
-    <h4>禁止选中,默认选中某个标签</h4>
-    <h4>如需要更新页面,请将监听变化的数据传入init-data</h4>
-
-    <nut-tab :def-index="defIndex" class="customer-css" @tab-switch="tabSwitch" :contentShow="true" :init-data="disableTabs" :is-show-line="false">
+    
+    <h4>若更新页面,将监听变化的数据传入init-data</h4>
+    <nut-tab :def-index="defIndex" class="customer-css" :tab-line="false" @tab-switch="tabSwitch" :init-data="disableTabs"  :scroll-line="true">
       <nut-tab-panel
         v-for="value in disableTabs"
         v-bind:key="value.tabTitle"
         :tab-title="value.tabTitle"
+        :icon-url="value.iconUrl"
         :disable="value.disable"
         v-html="value.content"
       ></nut-tab-panel>
@@ -91,112 +78,140 @@
 
 <script>
 export default {
-  components: {},
+  components:{
+  },
   data() {
     return {
-      defIndex: 1,
-      positionNavCurr: 'top',
+      defIndex:1,
+      positionNavCurr: "top",
       editableTabs: [
         {
-          tabTitle: '衣物',
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg',
-          content: '<p>衣物内容</p>'
+          tabTitle: "推荐分类",
+          iconUrl: "",
+          content: "<p>这里是推荐分类内容</p>"
+        },
+        {
+          tabTitle: "家电清洗",
+          iconUrl: "",
+          content: "<p>这里是家电清洗内容</p>"
+        },
+        {
+          tabTitle: "箱包养护",
+          iconUrl: "",
+          content: "<p>这里是箱包养护内容</p>"
         },
         {
-          tabTitle: '日用品',
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg',
-          content: '<p>日用品内容</p>'
+          tabTitle: "屏幕换新",
+          iconUrl: "",
+          content: "<p>这里是屏幕换新内容</p>"
         },
         {
-          tabTitle: '器材',
-          iconUrl: 'http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg',
-          content: '<p>运动器材内容</p>'
+          tabTitle: "电池换新",
+          iconUrl: "",
+          content: "<p>这里是电池换新内容</p>"
         },
         {
-          tabTitle: '电影票',
-          iconUrl: 'http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg',
-          content: '<p>电影票内容</p>'
+          tabTitle: "内存升级",
+          iconUrl: "",
+          content: "<p>这里是内存升级内容</p>"
+        },
+        {
+          tabTitle: "家电服务",
+          iconUrl: "",
+          content: "<p>这里是家电服务内容</p>"
         }
       ],
       disableTabs: [
         {
-          tabTitle: '衣物',
-          disable: false,
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg',
-          content: '<p>衣物内容</p>'
+          tabTitle: "衣物",
+          disable:true,
+          iconUrl:
+            "http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg",
+          content: "<p>衣物内容</p>"
         },
         {
-          tabTitle: '日用品',
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg',
-          content: '<p>日用品内容</p>'
+          tabTitle: "日用品",
+          iconUrl:
+            "http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg",
+          content: "<p>日用品内容</p>"
         },
         {
-          tabTitle: '运动器材',
-          iconUrl: 'http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg',
-          content: '<p>运动器材内容</p>'
+          tabTitle: "运动器材",
+          iconUrl:
+            "http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg",
+          content: "<p>运动器材内容</p>"
         },
         {
-          tabTitle: '电影票',
-          iconUrl: 'http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg',
-          content: '<p>电影票内容</p>'
+          tabTitle: "电影票",
+          iconUrl:
+            "http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg",
+          content: "<p>电影票内容</p>"
         }
       ]
     };
   },
   methods: {
     tabSwitch: function(index, event) {
-      console.log(index + '--' + event);
+      console.log(index + "--" + event);
       //this.defIndex = index;
     },
-    clickHandler: function() {
+    clickHandler:function(){
       let newEditableTabs = [
         {
-          tabTitle: '衣物2',
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg',
-          content: '<p>改变衣物内容</p>'
+          tabTitle: "衣物2",
+          iconUrl:
+            "http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg",
+          content: "<p>改变衣物内容</p>"
         },
         {
-          tabTitle: '日用品2',
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg',
-          content: '<p>改变日用品内容</p>'
+          tabTitle: "日用品2",
+          iconUrl:
+            "http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg",
+          content: "<p>改变日用品内容</p>"
         },
         {
-          tabTitle: '器材2',
-          iconUrl: 'http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg',
-          content: '<p>改变运动器材内容</p>'
+          tabTitle: "器材2",
+          iconUrl:
+            "http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg",
+          content: "<p>改变运动器材内容</p>"
         },
         {
-          tabTitle: '电影票2',
-          iconUrl: 'http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg',
-          content: '<p>改变电影票内容</p>'
+          tabTitle: "电影票2",
+          iconUrl:
+            "http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg",
+          content: "<p>改变电影票内容</p>"
         }
-      ];
+      ]
       this.disableTabs = newEditableTabs;
     },
-    resetHandler: function() {
-      let newEditableTabs = [
-        {
-          tabTitle: '衣物',
-          disable: false,
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg',
-          content: '<p>衣物内容</p>'
+    resetHandler:function(){
+        let newEditableTabs = [
+        {
+          tabTitle: "衣物",
+          disable: true,
+          iconUrl:
+            "http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg",
+          content: "<p>衣物内容</p>"
         },
         {
-          tabTitle: '日用品',
-          iconUrl: 'http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg',
-          content: '<p>日用品内容</p>'
+          tabTitle: "日用品",
+          iconUrl:
+            "http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg",
+          content: "<p>日用品内容</p>"
         },
         {
-          tabTitle: '运动器材',
-          iconUrl: 'http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg',
-          content: '<p>运动器材内容</p>'
+          tabTitle: "运动器材",
+          iconUrl:
+            "http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg",
+          content: "<p>运动器材内容</p>"
         },
         {
-          tabTitle: '电影票',
-          iconUrl: 'http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg',
-          content: '<p>电影票内容</p>'
+          tabTitle: "电影票",
+          iconUrl:
+            "http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg",
+          content: "<p>电影票内容</p>"
         }
-      ];
+      ]
       this.disableTabs = newEditableTabs;
     }
   }
@@ -206,17 +221,16 @@ export default {
 <style lang="scss">
 .customer-css {
   .nut-tab-active .nut-tab-link {
-    color: #fff;
+    color: #E1251B;
   }
   .nut-title-nav-list {
     background: #fff;
     border-left: 1px solid #e4e7ed;
-    &:first-child {
+    &:first-child{
       border-left: 0;
     }
   }
   .nut-tab-active {
-    background: $primary-color;
     border: 0;
     transition: all 0.3s ease-in-out;
   }
@@ -228,3 +242,4 @@ export default {
   }
 }
 </style>
+

+ 114 - 72
src/packages/tab/doc.md

@@ -1,18 +1,19 @@
 # Tab 选项卡
 
-常用于平级区域大块内容的的收纳和展现。
+常用于平级区域大块内容的的收纳和展现,支持内嵌标签形式和渲染循环数据形式
 
 > 注意,使用该组件需要在引入 nut-tab-panel 组件
 
 `import { TabPanel } from "@nutui/nutui";`
 
-## 基本用法
+## 基础样式
 
 ```html
 <nut-tab @tab-switch="tabSwitch">
-    <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-panel tab-title="页签一">这里是页签1内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签二">这里是页签2内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签三">这里是页签3内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签四">这里是页签4内容</nut-tab-panel>
 </nut-tab>
 ```
 ```javascript
@@ -25,97 +26,135 @@ export default {
 };
 ```
 
-## 使用数据渲染,支持上/下/左/右四个样式;
+## 禁用样式:
 
 ```html
-<nut-tab @tab-switch="tabSwitch"  position-nav="bottom">
-    <nut-tab-panel 
-      v-for="value in editableTabs" 
-      v-bind:key="value.tabTitle" 
-      :tab-title="value.tabTitle" 
-      :icon-url="value.tabUrl" 
-      v-html="value.content"
-    >
-    </nut-tab-panel>
+<nut-tab @tab-switch="tabSwitch">
+    <nut-tab-panel tab-title="页签一">这里是页签1内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签二">这里是页签2内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签三" :disable="true">这里是页签3内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签四">这里是页签4内容</nut-tab-panel>
+</nut-tab>
+```
+
+## 超出界面宽度
+```html
+<nut-tab @tab-switch="tabSwitch" :is-scroll="true">
+  <nut-tab-panel
+    v-for="value in editableTabs"
+    v-bind:key="value.tabTitle"
+    :tab-title="value.tabTitle"
+    :icon-url="value.iconUrl"
+    v-html="value.content"
+  ></nut-tab-panel>
 </nut-tab>
 ```
 ```javascript
 export default {
   data() {
-    return {
-      positionNavCurr:'top',
-      editableTabs:[
-        {
-          'tabTitle':'衣物',
-          'tabUrl':'http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg',
-          'content':'<p>衣物内容</p>'
-        },
-        {
-          'tabTitle':'日用品',
-          'tabUrl':'http://img13.360buyimg.com/uba/jfs/t30331/209/562746340/2190/6619973d/5bf763aaN6ff02099.jpg',
-          'content':'<p>日用品内容</p>'
-        },
-        {
-          'tabTitle':'器材',
-          'tabUrl':'http://img20.360buyimg.com/uba/jfs/t30346/262/553689202/2257/5dfa3983/5bf76407N72deabf4.jpg',
-          'content':'<p>运动器材内容</p>'
-        },
-        {
-          'tabTitle':'电影票',
-          'tabUrl':'http://img10.360buyimg.com/uba/jfs/t26779/215/2118525153/2413/470d1613/5bf767b2N075957b7.jpg',
-          'content':'<p>电影票内容</p>'
-        }
-      ]
-    };
-  },
-  methods: {
-      tabSwitch:function(index,event){
-        console.log(index+'--'+event);
+      return {
+          editableTabs: [
+          {
+            tabTitle: "推荐分类",
+            iconUrl: "",
+            content: "<p>这里是推荐分类内容</p>"
+          },
+          {
+            tabTitle: "家电清洗",
+            iconUrl: "",
+            content: "<p>这里是家电清洗内容</p>"
+          },
+          {
+            tabTitle: "箱包养护",
+            iconUrl: "",
+            content: "<p>这里是箱包养护内容</p>"
+          },
+          {
+            tabTitle: "屏幕换新",
+            iconUrl: "",
+            content: "<p>这里是屏幕换新内容</p>"
+          },
+          {
+            tabTitle: "电池换新",
+            iconUrl: "",
+            content: "<p>这里是电池换新内容</p>"
+          },
+          {
+            tabTitle: "内存升级",
+            iconUrl: "",
+            content: "<p>这里是内存升级内容</p>"
+          },
+          {
+            tabTitle: "家电服务",
+            iconUrl: "",
+            content: "<p>这里是家电服务内容</p>"
+          }
+        ]
       }
   }
 };
 ```
-## 支持滑动选择多个页签
+
+## 纵向tab切换
 
 ```html
-  <nut-tab @tab-switch="tabSwitch" :is-scroll="true">
-      <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-panel tab-title="页签4">页签4</nut-tab-panel>
-      <nut-tab-panel tab-title="页签5">页签5</nut-tab-panel>
-      <nut-tab-panel tab-title="页签6">页签6</nut-tab-panel>
-      <nut-tab-panel tab-title="页签7">页签7</nut-tab-panel>
-  </nut-tab>
+<nut-tab @tab-switch="tabSwitch" position-nav="left" :wrapper-height="350">
+  <nut-tab-panel
+    v-for="value in editableTabs"
+    v-bind:key="value.tabTitle"
+    :tab-title="value.tabTitle"
+    :icon-url="value.iconUrl"
+    v-html="value.content"
+  ></nut-tab-panel>
+</nut-tab>
 ```
 
+## 纵向tab切换超出界面高度,设置tab区域高度
+
 ```html
-  <nut-tab @tab-switch="tabSwitch" :is-scroll="true" position-nav="left">
-      <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-panel tab-title="页签4">页签4</nut-tab-panel>
-      <nut-tab-panel tab-title="页签5">页签5</nut-tab-panel>
-      <nut-tab-panel tab-title="页签6">页签6</nut-tab-panel>
-      <nut-tab-panel tab-title="页签7">页签7</nut-tab-panel>
-  </nut-tab>
+<nut-tab @tab-switch="tabSwitch" position-nav="left" :is-scroll="true" :wrapper-height="200">
+  <nut-tab-panel
+    v-for="value in editableTabs"
+    v-bind:key="value.tabTitle"
+    :tab-title="value.tabTitle"
+    :icon-url="value.iconUrl"
+    v-html="value.content"
+  ></nut-tab-panel>
+</nut-tab>
 ```
 
-## 禁止选中,默认选中某个标签,如需更新数组后,重新渲染Tab页面,请将更新数组传入init-data
+## 设置可以滑动的线段
+
+```html
+<nut-tab @tab-switch="tabSwitch" :tab-line="false" :scroll-line="true">
+    <nut-tab-panel tab-title="页签一">这里是页签1内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签二">这里是页签2内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签三">这里是页签3内容</nut-tab-panel>
+    <nut-tab-panel tab-title="页签四">这里是页签4内容</nut-tab-panel>
+</nut-tab>
+```
 
+## 若更新页面,将监听变化的数据传入init-data,且可以自定义class类名,样式覆盖
 ```html
-<nut-tab :def-index="1" class="customer-css" @tab-switch="tabSwitch" :contentShow="true" :init-data="disableTabs">
-    <nut-tab-panel
+<nut-tab 
+  :def-index="defIndex" 
+  class="customer-css" 
+  :tab-line="false" 
+  @tab-switch="tabSwitch" 
+  :init-data="disableTabs"  
+  :scroll-line="true">
+  <nut-tab-panel
     v-for="value in disableTabs"
     v-bind:key="value.tabTitle"
     :tab-title="value.tabTitle"
+    :icon-url="value.iconUrl"
     :disable="value.disable"
     v-html="value.content"
   ></nut-tab-panel>
 </nut-tab>
 <div style="width:100%;height=50px;text-align:center">
-  <Button @click="resetHandler" type="light">重置Tab页面</Button>
-  <Button @click="clickHandler">更新Tab页面</Button>
+  <nut-button @click="resetHandler" type="light">重置Tab页面</nut-button>
+  <nut-button @click="clickHandler">更新Tab页面</nut-button>
 </div>
 ```
 
@@ -128,7 +167,7 @@ export default {
       disableTabs:[
         {
           'tabTitle':'衣物',
-          'disable':false,
+          'disable':true,
           'tabUrl':'http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg',
           'content':'<p>衣物内容</p>'
         },
@@ -187,7 +226,7 @@ export default {
           let newEditableTabs = [
           {
             tabTitle: "衣物",
-            disable: false,
+            disable: true,
             iconUrl:
               "http://img13.360buyimg.com/uba/jfs/t27280/289/2061314663/2392/872e32ff/5bf76318Ndc80c1d8.jpg",
             content: "<p>衣物内容</p>"
@@ -227,8 +266,11 @@ export default {
 | position-nav | 页签栏的分布,可选值 top/bottom/left/right | String | top
 | def-index | 默认选中的页签栏 | String | 1
 | init-data | 监听数据变化,渲染更新页面 | Array | []
-| is-show-line|是否显示tab切换时的红条|Boolean|true|
-| is-scroll|是否支持滑动选择多个页签|Boolean|false|
+| scroll-line|是否显示tab切换时,滑动效果的红条|Boolean|false|
+| tab-line|是否显示tab切换时,无滑动效果的active红条|Boolean|true|
+| is-scroll|是否支持超出范围后,滑动选择多个页签|Boolean|false|
+| wrapper-height |设置tab区域的高度,只有在positionNav=left或者right 的情况下有效|Number/String|200|
+
 
 ### nut-tab-panel
 

+ 120 - 45
src/packages/tab/tab.scss

@@ -1,9 +1,5 @@
-.nut-tab {
-  position: relative;
-  border: 1px solid #eee;
-  padding: 10px;
-  font-size: 12px;
-  background: #eee;
+.nut-tab-part{
+  margin-bottom: 20px;
 }
 .nut-tab-horizontal {
   display: flex;
@@ -39,22 +35,45 @@
   top: 0px;
   border-radius: 50%;
 }
-.nut-tab-title {
+.nut-tab-title-topnav {
   //border:1px solid #fff;
   border-bottom: 1px solid #ededed;
+  scroll-behavior: smooth;
   width: 100%;
   display: flex;
-  height: 50px;
+  height: 48px;
   line-height: 48px;
   box-sizing: border-box;
   position: relative;
   overflow-x: auto;
   overflow-y: hidden;
   &::-webkit-scrollbar {
-    display: none;
+      display: none;
+  }
+  .nut-tab-active {
+    background: #fff;
+    position: relative;
+    border: 0;
+    .nut-tab-link{
+        color: #E1251B;
+        font-weight: bold;
+    }
+    .tab-line{
+      &:after{
+        content: "";
+        position: absolute;
+        width: 30%;
+        transform: translateX(-50%);
+        height: 2px;
+        background: #E1251B;
+        bottom: 0px;
+        left: 50%;
+        z-index: 20;
+      }
+    }
   }
 }
-.nav-bar {
+.nav-bar-top {
   position: absolute;
   height: 2px;
   bottom: 0px;
@@ -64,17 +83,17 @@
   transition: all 0.3s ease-in-out;
 }
 .nut-tab-title-leftnav {
-  border-right: 1px solid #ededed;
-  width: 100px;
+  border-right: 1px solid #fff;
+  scroll-behavior: smooth;
+  width: 90px;
   display: flex;
-  background: #fff;
   flex-direction: column;
   position: relative;
-  height: 200px;
+  // height: 200px;
   overflow-y: auto;
   overflow-x: hidden;
   &::-webkit-scrollbar {
-    display: none;
+      display: none;
   }
   .nav-bar-left {
     position: absolute;
@@ -87,42 +106,76 @@
   }
   .nut-title-nav {
     border: 0;
+    background: #F7F7F7;
     border-left: 1px solid #f5f7fa;
   }
   .nut-tab-active {
-    background: #fff;
-    //   a{
-    //     color: red;
-    //   }
+    .nut-tab-link{
+      position: relative;
+      background: #fff;
+      color: #E1251B;
+      font-weight: bold;
+    }
+    .tab-line{
+      &:after{
+        content: "";
+        position: absolute;
+        height: 100%;
+        width: 3px;
+        background: #E1251B;
+        left: 0px;
+        top: 0;
+        z-index: 2;
+      }
+    }
   }
 }
 .nut-tab-title-rightnav {
-  background: #fff;
+  background: #F7F7F7;
   // border: 1px solid #fff;
-  border-left: 1px solid #ededed;
+  scroll-behavior: smooth;
+  border-left: 1px solid #fff;
   width: 100px;
   display: flex;
   flex-direction: column;
   position: relative;
   overflow-y: auto;
   overflow-x: hidden;
-  height: 200px;
   box-sizing: border-box;
   &::-webkit-scrollbar {
-    display: none;
+      display: none;
   }
   .nav-bar-right {
     position: absolute;
     width: 2px;
     left: 0px;
     top: 0px;
-    z-index: 2;
-    background: $primary-color;
+    background: #E1251B;
+    z-index:2;
     transition: all 0.3s ease-in-out;
   }
+  .nut-tab-active {
+    .nut-tab-link{
+      background: #fff;
+      color: #E1251B;
+      font-weight: bold;
+    }
+    .tab-line{
+      &:after{
+        content: "";
+        position: absolute;
+        height: 100%;
+        width: 3px;
+        background: #E1251B;
+        right: 0px;
+        top: 0;
+        z-index: 2;
+      }
+    }
+  }
 }
 .nut-tab-link {
-  color: #333;
+  color: #969696;
   display: flex;
   align-items: center;
   justify-content: center;
@@ -130,21 +183,23 @@
   text-decoration: none;
   line-height: 1;
   width: 100%;
-  height: 100%;
+  height:100%;
+  font-size: 13px;
 }
 .nut-tab-title-bottomnav {
   border: 1px solid #fff;
+  scroll-behavior: smooth;
   border-top: 1px solid #ededed;
   width: 100%;
   display: flex;
-  height: 50px;
-  line-height: 49px;
+  height: 48px;
+  line-height: 48px;
   box-sizing: border-box;
   position: relative;
   overflow-x: auto;
   overflow-y: hidden;
   &::-webkit-scrollbar {
-    display: none;
+      display: none;
   }
   .nav-bar-bottom {
     position: absolute;
@@ -155,9 +210,31 @@
     background: $primary-color;
     transition: all 0.3s ease-in-out;
   }
+  .nut-tab-active {
+    background: #fff;
+    position: relative;
+    border: 0;
+    .nut-tab-link{
+        color: #E1251B;
+        font-weight: bold;
+    }
+    .tab-line{
+      &:after{
+        content: "";
+        position: absolute;
+        width: 30%;
+        transform: translateX(-50%);
+        height: 2px;
+        background: #E1251B;
+        top: 0px;
+        left: 50%;
+        z-index: 20;
+      }
+    }
+  }
 }
 
-.nut-title-nav-list {
+.nut-title-nav-topnav,.nut-title-nav-bottomnav{
   flex: 1;
   position: relative;
   flex-direction: row;
@@ -183,7 +260,7 @@
   align-items: center;
   position: relative;
 }
-.nut-title-nav-scroll {
+.nut-title-nav-scroll{
   min-width: 100px;
   position: relative;
   flex-direction: row;
@@ -193,14 +270,13 @@
   background: #fff;
   box-sizing: border-box;
 }
-.nut-title-vertical-scroll {
-  min-height: 55px;
+.nut-title-vertical-scroll{
+  min-height: 50px;
   position: relative;
   flex-direction: row;
   align-items: center;
   justify-content: center;
   display: flex;
-  background: #fff;
   box-sizing: border-box;
 }
 .nut-tab-icon {
@@ -211,26 +287,25 @@
   background-repeat: no-repeat;
   background-size: 100% 100%;
 }
-.nut-tab-active {
-  background: #fff;
-  border: 0;
-  // a{
-  //     color: red;
-  // }
-}
+
 .nut-tab-item {
-  height: 200px;
+  height: 88px;
   border: 1px solid #fff;
   background: #fff;
   width: 100%;
-  padding: 10px;
+  padding: 20px;
   box-sizing: border-box;
+  font-size: 14px;
+  color: #646464;
   .hide {
     display: none;
   }
 }
 .nut-tab-disable {
-  background: #e1e1e1 !important;
+  .nut-tab-link{
+    color: #C8C8C8 !important;
+    font-size: 15px;
+  }
 }
 .tabbar-nav-word {
   font-size: $font-size-small;

+ 62 - 72
src/packages/tab/tab.vue

@@ -1,23 +1,23 @@
 <template>
   <div class="nut-tab-part">
     <div class="nut-tab" :class="{ 'nut-tab-horizontal': positionNavCss }">
-      <div v-if="positionNav == 'right' || positionNav == 'bottom'" class="nut-tab-item" ref="items">
+      <div v-if="positionNav == 'right' || positionNav == 'bottom'" class="nut-tab-item" ref="items" :style="customHeight">
         <slot></slot>
       </div>
-      <div :class="titleClass" ref="navlist">
-        <b v-if="isShowLine" :class="navBarClass" :style="navBarStyle"></b>
-        <span
-          v-for="(value, index) in tabTitleList"
-          :key="index"
-          :class="[titleNavList, 'nut-title-nav', { 'nut-tab-disable': value.disable }, { 'nut-tab-active': activeIndex == index }]"
-        >
-          <a class="nut-tab-link" v-on:click="switchTab(index, $event, value.disable)">
-            <i class="nut-tab-icon" :style="{ backgroundImage: 'url(' + value.iconUrl + ')' }" v-if="value.iconUrl"></i>
-            {{ value.tabTitle }}
-          </a>
-        </span>
+      <div :class="titleClass"  ref="navlist" :style="customHeight">
+          <b v-if="scrollLine" :class="navBarClass" :style="navBarStyle"></b>
+          <span
+            v-for="(value, index) in tabTitleList"
+            :key="index"
+            :class="[titleNavList, 'nut-title-nav', { 'nut-tab-disable': value.disable }, { 'nut-tab-active': activeIndex == index }]"
+          >
+            <span class="nut-tab-link" v-on:click="switchTab(index, $event, value.disable)" :class="{'tab-line':tabLine}">
+              <i  v-if="value.iconUrl" class="nut-tab-icon" :style="{ backgroundImage: 'url(' + value.iconUrl + ')' }"></i>
+              {{ value.tabTitle }}
+            </span>
+          </span>
       </div>
-      <div v-if="positionNav == 'top' || positionNav == 'left'" class="nut-tab-item" ref="items">
+      <div v-if="positionNav == 'top' || positionNav == 'left'" class="nut-tab-item" ref="items" :style="customHeight">
         <slot></slot>
       </div>
     </div>
@@ -27,11 +27,15 @@
 export default {
   name: 'nut-tab',
   props: {
-    isScroll: {
+    isScroll:{
+        type:Boolean,
+        default:false
+    },
+    scrollLine: {
       type: Boolean,
       default: false
     },
-    isShowLine: {
+    tabLine:{
       type: Boolean,
       default: true
     },
@@ -48,14 +52,19 @@ export default {
       default: function() {
         return [];
       }
+    },
+    wrapperHeight:{
+      type: [String,Number],
+      default: '200'
     }
   },
   data() {
     return {
       tabTitleList: [],
       activeIndex: this.defIndex,
-      initX: '0',
-      navWidth: 0
+      initX: 0,
+      navWidth: 0,
+      tapWidth:0
     };
   },
   watch: {
@@ -70,52 +79,47 @@ export default {
     }
   },
   computed: {
-    //下面有些样式名称是为了兼容之前的版本
     positionNavCss: function() {
       if (this.positionNav === 'left' || this.positionNav === 'right') return true;
     },
     titleClass: function() {
-      if (this.positionNav == 'top') {
-        return 'nut-tab-title';
-      }
       return 'nut-tab-title-' + this.positionNav + 'nav';
     },
     navBarClass: function() {
-      if (this.positionNav == 'top') {
-        return 'nav-bar';
-      }
       return 'nav-bar-' + this.positionNav;
     },
     titleNavList: function() {
       if (this.positionNav == 'top' || this.positionNav == 'bottom') {
-        if (this.isScroll) {
-          return 'nut-title-nav-scroll';
-        }
-        return 'nut-title-nav-list';
-      } else {
-        if (this.isScroll) {
-          return 'nut-title-vertical-scroll';
-        }
-        return 'nut-title-nav-' + this.positionNav + 'nav';
+        if(this.isScroll) return 'nut-title-nav-scroll';
+      }else{
+        if(this.isScroll) return 'nut-title-vertical-scroll';
       }
+      return 'nut-title-nav-' + this.positionNav + 'nav';
     },
     navBarStyle: function() {
       if (this.positionNav === 'top' || this.positionNav === 'bottom') {
-        return {
-          transform: `translateX(${this.initX}px)`,
-          width: this.navWidth + 'px'
-        };
+          return {
+            transform: `translateX(${this.initX}px)`,
+            width: this.navWidth + 'px'
+          };
       }
       return {
-        transform: `translateY(${this.initX}px)`,
-        height: this.navWidth + 'px'
+          transform: `translateY(${this.initX}px)`,
+          height: this.navWidth + 'px'
       };
+    },
+    customHeight:function(){
+      if(this.positionNav === 'left' || this.positionNav === 'right'){
+        return {
+          height:this.wrapperHeight+'px'
+        }
+      }else{
+        return null;
+      }
     }
   },
   mounted() {
-    this.$nextTick(() => {
-      this.$slots.default && this.updeteTab(this.$slots.default);
-    });
+    this.$slots.default && this.updeteTab(this.$slots.default);
   },
   methods: {
     updeteTab: function() {
@@ -130,9 +134,10 @@ export default {
         let slotTag = slot[i].tag;
         if (typeof slotTag == 'string' && slotTag.indexOf('nut-tab-panel') != -1) {
           let attrs = slot[i].data.attrs;
+          console.log(attrs.disable);
           let item = {
             tabTitle: attrs['tab-title'] || attrs['tabTitle'],
-            disable: attrs.disable === false,
+            disable: attrs.disable === true,
             iconUrl: attrs['iconUrl'] || attrs['icon-url']
           };
           this.tabTitleList.push(item);
@@ -146,41 +151,27 @@ export default {
         }
       }
       this.$nextTick(() => {
+        let tapWidth;
         if (this.positionNav == 'top' || this.positionNav == 'bottom') {
-          this.navWidth = this.$refs.navlist.querySelector('.nut-title-nav').offsetWidth;
-        } else {
-          this.navWidth = this.$refs.navlist.querySelector('.nut-title-nav').offsetHeight;
+            this.navWidth = this.$refs.navlist.querySelector('.nut-title-nav').offsetWidth;
+            tapWidth = this.$refs.navlist.offsetWidth;
+        }else{
+            this.navWidth = this.$refs.navlist.querySelector('.nut-title-nav').offsetHeight;
+            tapWidth = this.$refs.navlist.offsetHeight;
         }
         this.initX = parseInt(this.navWidth * this.defIndex);
+        this.tapWidth = tapWidth/2-this.navWidth/2;
       });
     },
-    getStyle: function(obj, styleName) {
-      if (!obj) {
-        return '';
-      }
-      if (obj.currentStyle) {
-        return obj.currentStyle[styleName];
-      } else {
-        return getComputedStyle(obj, null)[styleName];
-      }
-    },
-    findParent(event, myclass) {
-      let parentCpt = event.target;
-      let flag = 0; //避免死循环
-      while (parentCpt && flag < 10) {
-        flag++;
-        if (parentCpt.className && parentCpt.className === myclass) {
-          break;
-        }
-        parentCpt = parentCpt.parentNode;
-      }
-      return parentCpt;
-    },
     switchTab: function(index, event, disable) {
       if (!disable) {
         this.activeIndex = index;
-        this.initX = parseInt(this.navWidth * index);
-        let nutTab = this.findParent(event, 'nut-tab-part');
+        this.initX = parseInt(this.navWidth * index); 
+        if(this.positionNav == 'top' || this.positionNav == 'bottom'){
+            this.$refs.navlist.scroll(this.initX-this.tapWidth,0);
+        }else{
+            this.$refs.navlist.scroll(0, this.initX-this.tapWidth);
+        }   
         let items = this.$refs.items.children;
         for (let i = 0; i < items.length; i++) {
           if (i == index) {
@@ -190,7 +181,6 @@ export default {
           }
         }
         this.$emit('tab-switch', index, event);
-        this.$emit('tabSwitch', index, event); //兼容以前驼峰法命名
       }
     }
   }

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

@@ -3,7 +3,7 @@
     <a
       class="tabbar-nav"
       v-for="(value, index) in tabList"
-      :class="[{ curr: index == currIndex }, type]"
+      :class="[{ 'curr': index == currIndex }, type]"
       :key="value.tabTitle"
       v-on:click="switchTabs(value, index)"
       :href="value.href"
@@ -44,15 +44,26 @@ export default {
       tabList: this.tabbarList
     };
   },
+  mounted(){
+    this.initBar();
+  },
   watch: {
     tabbarList: {
       handler(value) {
         this.tabList = value;
+        this.initBar();
       },
       deep: true
     }
   },
   methods: {
+    initBar(){
+      this.tabList.forEach((item,index)=>{
+        if(item.curr){
+            this.currIndex = index;
+        }
+      })
+    },
     switchTabs: function(value, index) {
       this.currIndex = index;
       this.$emit('tab-switch', value, index);

+ 93 - 0
src/packages/tag/__test__/tag.spec.js

@@ -0,0 +1,93 @@
+import { shallowMount, mount } from '@vue/test-utils'
+import Tag from '../tag.vue';
+import Vue from 'vue';
+
+describe('Tag.vue', () => {
+    let wrapper = shallowMount(Tag, {
+        slots: {
+            default: '标签'
+        }
+    });
+    it('设置 color 基础样式: blue', () => {
+        wrapper.setProps({
+            color: 'blue'
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.attributes('class')).toContain('blue');
+        })
+    });
+    it('设置 color 基础样式: green', () => {
+        wrapper.setProps({
+            color: 'green'
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.attributes('class')).toContain('green');
+        })
+    });
+    it('设置 color 基础样式: jc-blue', () => {
+        wrapper.setProps({
+            color: 'jc-blue'
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.attributes('class')).toContain('jc-blue');
+        })
+    });
+    it('设置 color 基础样式: orange', () => {
+        wrapper.setProps({
+            color: 'orange'
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.attributes('class')).toContain('orange');
+        })
+    });
+    it('设置slot', () => {
+        return Vue.nextTick().then(function () {
+            expect(wrapper.text()).toBe('标签');
+        })
+    });
+    it('设置disabled', () => {
+        wrapper.setProps({
+            disabled: true
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.text()).toBe('标签');
+            expect(wrapper.attributes('class')).toContain('disabled');
+        })
+    });
+    it('设置circle', () => {
+        wrapper.setProps({
+            circle: true
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.text()).toBe('标签');
+            expect(wrapper.attributes('class')).toContain('circle');
+        })
+    });
+    it('设置hollow', () => {
+        wrapper.setProps({
+            hollow: true
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.text()).toBe('标签');
+            expect(wrapper.attributes('class')).toContain('hollow');
+        })
+    });
+    it('设置 size: big', () => {
+        wrapper.setProps({
+            size: 'big'
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.text()).toBe('标签');
+            expect(wrapper.attributes('class')).toContain('big');
+        })
+    });
+    it('设置 size: middle', () => {
+        wrapper.setProps({
+            size: 'middle'
+        })
+        return Vue.nextTick().then(function () {
+            expect(wrapper.text()).toBe('标签');
+            expect(wrapper.attributes('class')).toContain('middle');
+        })
+    });
+});

+ 83 - 0
src/packages/tag/demo.vue

@@ -0,0 +1,83 @@
+<template>
+  <div>
+    <h4>基础样式</h4>
+    <div class="white-bg">
+      <!-- green, orange, yellow, blue, purple -->
+      <nut-tag>标签</nut-tag>
+      <nut-tag color="jc-blue">标签</nut-tag>
+      <nut-tag color="orange">标签</nut-tag>
+      <nut-tag color="yellow">标签</nut-tag>
+      <nut-tag color="green">标签</nut-tag>
+      <nut-tag color="blue">标签</nut-tag>
+      <nut-tag color="purple">标签</nut-tag>
+      <nut-tag disabled>标签</nut-tag>
+    </div>
+    <h4>圆角样式</h4>
+    <div class="white-bg">
+      <!-- green, orange, yellow, blue, purple -->
+      <nut-tag circle>标签</nut-tag>
+      <nut-tag circle color="jc-blue">标签</nut-tag>
+      <nut-tag circle color="orange">标签</nut-tag>
+      <nut-tag circle color="yellow">标签</nut-tag>
+      <nut-tag circle color="green">标签</nut-tag>
+      <nut-tag circle color="blue">标签</nut-tag>
+      <nut-tag circle color="purple">标签</nut-tag>
+      <nut-tag circle disabled>标签</nut-tag>
+    </div>
+    <h4>自定义颜色</h4>
+    <div class="white-bg">
+      <nut-tag :style="{background: '#FCE9E8', color: '#E1251B'}">标签</nut-tag>
+      <nut-tag :style="{background: '#EBF0FD', color: '#3C6EF0'}">标签</nut-tag>
+      <nut-tag :style="{background: '#FFF0ED', color: '#FF8B70'}">标签</nut-tag>
+      <nut-tag :style="{background: '#FFF8E7', color: '#FFBA12'}">标签</nut-tag>
+      <nut-tag :style="{background: '#E9F6F0', color: '#26A872'}">标签</nut-tag>
+      <nut-tag :style="{background: '#E9F6FC', color: '#2CA6E1'}">标签</nut-tag>
+      <nut-tag :style="{background: '#EFEAFF', color: '#6236FF'}">标签</nut-tag>
+      <nut-tag :style="{background: '#F7F7F7', color: '#7C7A8A'}">标签</nut-tag>
+    </div>
+    <h4>空心样式</h4>
+    <div class="white-bg">
+      <nut-tag hollow>标签</nut-tag>
+      <nut-tag hollow color="jc-blue">标签</nut-tag>
+      <nut-tag hollow color="orange">标签</nut-tag>
+      <nut-tag hollow color="yellow">标签</nut-tag>
+      <nut-tag hollow color="green">标签</nut-tag>
+      <nut-tag hollow color="blue">标签</nut-tag>
+      <nut-tag hollow color="purple">标签</nut-tag>
+      <nut-tag hollow disabled>标签</nut-tag>
+    </div>
+    <h4>标签大小</h4>
+    <div class="white-bg">
+      <nut-tag>标签</nut-tag>
+      <nut-tag size="middle">标签</nut-tag>
+      <nut-tag @click="clickHandler" size="big">标签</nut-tag>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  data() {
+    return {
+      disabled: false
+    };
+  },
+  methods: {
+    clickHandler(e) {
+      console.log(e, '我点击了标签');
+      this.disabled = true;
+      setTimeout(() => {
+        this.disabled = false;
+      }, 2000);
+    }
+  }
+};
+</script>
+
+<style lang="scss" scoped>
+.white-bg {
+  padding: 10px;
+  background: #fff;
+  line-height: 1.6;
+}
+</style>

+ 98 - 0
src/packages/tag/doc.md

@@ -0,0 +1,98 @@
+# Tag 标签
+
+## 基本用法 
+
+常规标签
+
+```html
+<nut-tag 
+  @click="clickHandler"
+  :disabled='disabled'
+>
+  标签
+</nut-tag>
+
+<nut-tag 
+  disabled
+>
+  标签
+</nut-tag>
+```
+
+```javascript
+export default {
+    data() {
+        return {
+            disabled: false
+        };
+    },
+    methods: {
+        clickHandler(e) {
+            console.log(e, "我点击了标签");
+            this.disabled = true;
+            setTimeout(() => {
+                this.disabled = false;
+            }, 2000);
+        }
+    }
+};
+```
+
+常规标签
+
+```html
+<nut-tag>
+  标签
+</nut-tag>
+
+<nut-tag 
+  disabled
+>
+  标签
+</nut-tag>
+```
+
+标签-颜色(枚举)
+
+默认京东红,可选颜色:jc-blue,orange,yellow,green,blue,purple,禁用状态 disabled
+
+```html
+<nut-tag circle>标签</nut-tag>
+<nut-tag circle color="jc-blue">标签</nut-tag>
+<nut-tag circle color="orange">标签</nut-tag>
+<nut-tag circle color="yellow">标签</nut-tag>
+<nut-tag circle color="green">标签</nut-tag>
+<nut-tag circle color="blue">标签</nut-tag>
+<nut-tag circle color="purple">标签</nut-tag>
+<nut-tag circle disabled>标签</nut-tag>
+```
+
+标签-圆角
+
+```html
+<nut-tag circle> 登录 </nut-tag>
+```
+
+标签-空心
+
+```html
+<nut-tag hollow> 登录 </nut-tag>
+```
+
+标签型号-大小
+
+```html
+<nut-tag>标签</nut-tag>
+<nut-tag size="middle">标签</nut-tag>
+<nut-tag @click="clickHandler" size="big">标签</nut-tag>
+```
+
+## Prop
+
+| 字段  | 说明                                                                                      | 类型    | 默认值 |
+| ----- | ----------------------------------------------------------------------------------------- | ------- | ------ |
+| color  | 按钮类型,可选类型包含:[red(默认), jc-blue, green, orange, yellow, blue, purple] | String  | red     |
+| disabled | 是否禁用状态                                                                                | Boolean | false  |
+| circle | 是否为圆角标签                                                                            | Boolean | false  |
+| hollow | 是否为空心标签                                                          | Boolean  | false      |
+| size  | 按钮型号,可选型号 small 默认,middle, big                                                            | String  | small     |

+ 9 - 0
src/packages/tag/index.js

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

+ 70 - 0
src/packages/tag/tag.scss

@@ -0,0 +1,70 @@
+
+@mixin tagColor($color, $hollow) {
+  @if ($hollow == true) {
+    border: $border-width-base solid $color;
+    color: $color;
+    background-color: transparent;
+    padding: 1px 8px;
+  } @else {
+    background-color: $color;
+    color: #fff;
+  }
+}
+
+$colors: 'orange' 'yellow' 'green' 'blue' 'purple' 'jc-blue';
+$colorlist: $assist-color-orange $assist-color-yellow $assist-color-green $assist-color-blue $assist-color-purple $primary-color-jc-blue;
+
+.nut-tag {
+  cursor: pointer;
+  min-height: $tag-height-small;
+  padding: 2px 9px;
+  font-size: $font-size-small;
+  border: none;
+  outline: none;
+  border-radius: $tag-border-radius;
+  box-sizing: border-box;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  min-width: 42px;
+  max-width: 100%;
+
+  // 默认 京东红
+  @include tagColor($primary-color-jd-red, false);
+
+  &.hollow {
+    @include tagColor($primary-color-jd-red, true);
+  }
+  
+  // 京牛蓝 jc-blue 其它颜色枚举:orange yellow green blue purple
+  @for $i from 1 to length($colors) + 1 {
+    &.#{nth($colors, $i)} {
+      @include tagColor(nth($colorlist, $i), false);
+      &.hollow {
+        @include tagColor(nth($colorlist, $i), true);
+      }
+    }
+  }
+
+  // disabled 状态颜色
+  &.disabled {
+    @include tagColor($assist-color-light-gray, false);
+
+    &.hollow {
+      @include tagColor($assist-color-gray, true);
+    }
+  }
+
+  // size-中号
+  &.middle {
+    height: $tag-height-middle;
+  }
+  // size-大号
+  &.big {
+    height: $tag-height-big;
+  }
+  // circle-圆角
+  &.circle {
+    border-radius: $tag-border-radius-circle;
+  }
+}

+ 50 - 0
src/packages/tag/tag.vue

@@ -0,0 +1,50 @@
+<template>
+  <span :class="clsStyle" @click="clickHandler">
+    <slot></slot>
+  </span>
+</template>
+<script>
+export default {
+  name: 'nut-tag',
+  props: {
+    size: {
+      type: String,
+      default: 'small' // [small, middle, big]
+    },
+    disabled: {
+      type: Boolean,
+      default: false
+    },
+    circle: {
+      type: Boolean,
+      default: false
+    },
+    hollow: {
+      type: Boolean,
+      default: false
+    },
+    color: {
+      type: String,
+      default: 'red' // [red, jc-blue, green, orange, yellow, blue, purple]
+    }
+  },
+  computed: {
+    clsStyle() {
+      let cls = `nut-tag ${this.color} ${this.size}
+            ${this.circle ? ' circle' : ''}
+            ${this.disabled ? ' disabled' : ''}
+            ${this.hollow ? ' hollow' : ''}`;
+      return cls;
+    }
+  },
+  methods: {
+    clickHandler(event) {
+      // 如果是 disabled 就阻止点击
+      if (this.disabled) {
+        return;
+      }
+      this.$emit('click', event);
+    }
+  }
+};
+</script>

+ 43 - 1
src/styles/variable.scss

@@ -1,4 +1,4 @@
-// ---- color ----
+// ---- color ---- 这是之前在用的颜色,后续开发中使用新的色值
 $primary-color: #f0250f !default;
 $normal-color: #848484 !default;
 $link-color: $primary-color !default;
@@ -7,6 +7,40 @@ $title-color: #2d2d2d !default;
 $text-color: #848484 !default;
 $light-color: #f6f6f6 !default;
 $dark-color: #dadada !default;
+
+// v3.0 主要色值
+// ---- 主色调color ----
+$primary-color-jd-red: #E1251B !default; // 京东红,主要用于对外C端产品。
+$primary-color-jc-blue: #3C6EF0 !default; // 京牛蓝,主要用于对内B端产品。
+// ---- 辅助color ----
+$assist-color-green: #26A872 !default; // 常用于成功信息/成功icon,或者卡片标签
+$assist-color-orange: #FF6E4C !default; // 常用于警告信息/警告icon,或者卡片标签
+$assist-color-yellow: #FFBA12 !default; // 常用于提示信息/提示icon,或者卡片标签
+$assist-color-blue: #2CA6E1 !default; // 冷链蓝,主要用于京东冷链相关产品,也可以用于卡片标签或页面其他元素
+$assist-color-purple: #6236FF !default; 
+$assist-color-gray: #7C7A8A !default; // 常用于卡片标签或页面其他元素
+$assist-color-light-gray: #D9D9D9 !default; // 常用于弱标签背景色
+// ---- 文本color ----
+$text-black-1: #323232 !default; // 重要文字颜色,用于Narbar等大标题
+$text-black-2: #646464 !default; // 普通文字颜色,用于段落、表单标题、列表内容等
+$text-black-3: #969696 !default; // 辅助文字,用于次要信息
+$text-black-4: #C8C8C8 !default; // 一般文字,用于提示性文字
+$text-jd-red: #E1251B !default; // 用于报错文字颜色
+// ---- 背景/分割线color ----
+$body-background-color: #F7F7F7 !default; // 用于页面整体背景
+$split-line-color: #E6E6E6 !default; // 用于分割线,按钮描边,1px
+$disabled-bg-color: #DCDCDC !default; // 用于按钮置灰
+// ---- v3.0 常用字号 ----
+$font-size-title-large: 20px; // 用于标题类文字
+$font-size-title-normal: 18px; // 用于全局操作类文字
+$font-size-body-large: 17px; // 常用字号,用于需要强调的文字信息
+$font-size-body-normal: 16px; // 最常用的正文字号
+$font-size-body-small: 14px; // 常用字号,用于较弱的文字信息
+$font-size-display-large: 14px; // 常用于辅助说明文字
+$font-size-display-normal: 13px; // 常用字号,用于解释信息
+$font-size-display-small: 12px; // 常用于标签信息展示
+
+
 // ---- base ----
 $body-background: #f6f6f6 !default;
 $mask-bg: rgba(0, 0, 0, 0.5) !default;
@@ -83,3 +117,11 @@ $assetsPath: '../../assets' !default;
 $stepper-color: #333 !default;
 $stepperbar-width: 11px !default;
 $stepperbar-color: #333 !default;
+
+// ---- Tag ----
+$tag-height-small: 20px !default;
+$tag-height-middle: 22px !default;
+$tag-height-big: 24px !default;
+$tag-border-radius: 2px !default;
+$tag-border-radius-circle: 10px !default;
+