From d2d2f5b173c4ece7a09a3eeaef1737cd7726c238 Mon Sep 17 00:00:00 2001
From: tangjinzhou <tangjinzhou@yidian-inc.com>
Date: Fri, 1 Dec 2017 18:48:16 +0800
Subject: [PATCH] add demo

---
 components/tabs/InkTabBar.vue               |   2 +-
 components/tabs/ScrollableInkTabBar.vue     |   2 +-
 components/tabs/ScrollableTabBar.vue        |   2 +-
 components/tabs/ScrollableTabBarMixin.js    |  26 ++--
 components/tabs/TabBarMixin.js              |  82 ++++++++----
 components/tabs/TabPane.vue                 |   6 +-
 components/tabs/Tabs.vue                    |  31 ++---
 components/tabs/demo/card-top.vue           |  69 ++++++++++
 components/tabs/demo/card.vue               |  25 ++++
 components/tabs/demo/custom-add-trigger.vue |  69 ++++++++++
 components/tabs/demo/editable-card.vue      |  61 +++++++++
 components/tabs/demo/extra.vue              |  21 +++
 components/tabs/demo/index.vue              |  54 ++++++++
 components/tabs/demo/position.vue           |  38 ++++++
 components/tabs/demo/size.vue               |  16 +++
 components/tabs/demo/slide.vue              |   7 +-
 components/tabs/index.vue                   | 141 ++++++++------------
 components/tabs/index.zh-CN                 |  52 ++++++++
 18 files changed, 555 insertions(+), 149 deletions(-)
 create mode 100644 components/tabs/demo/card-top.vue
 create mode 100644 components/tabs/demo/card.vue
 create mode 100644 components/tabs/demo/custom-add-trigger.vue
 create mode 100644 components/tabs/demo/editable-card.vue
 create mode 100644 components/tabs/demo/extra.vue
 create mode 100644 components/tabs/demo/index.vue
 create mode 100644 components/tabs/demo/position.vue
 create mode 100644 components/tabs/demo/size.vue
 create mode 100644 components/tabs/index.zh-CN

diff --git a/components/tabs/InkTabBar.vue b/components/tabs/InkTabBar.vue
index 48dafa1d2..bab261e8e 100644
--- a/components/tabs/InkTabBar.vue
+++ b/components/tabs/InkTabBar.vue
@@ -8,7 +8,7 @@ export default {
   render (h) {
     const inkBarNode = this.getInkBarNode()
     const tabs = this.getTabs(h)
-    return this.getRootNode([inkBarNode, tabs])
+    return this.getRootNode([inkBarNode, tabs], h)
   },
 }
 </script>
diff --git a/components/tabs/ScrollableInkTabBar.vue b/components/tabs/ScrollableInkTabBar.vue
index 292633176..f04450b9f 100644
--- a/components/tabs/ScrollableInkTabBar.vue
+++ b/components/tabs/ScrollableInkTabBar.vue
@@ -10,7 +10,7 @@ export default {
     const inkBarNode = this.getInkBarNode()
     const tabs = this.getTabs(h)
     const scrollbarNode = this.getScrollBarNode([inkBarNode, tabs])
-    return this.getRootNode(scrollbarNode)
+    return this.getRootNode(scrollbarNode, h)
   },
 }
 </script>
diff --git a/components/tabs/ScrollableTabBar.vue b/components/tabs/ScrollableTabBar.vue
index 3a1e83071..7abc2d748 100644
--- a/components/tabs/ScrollableTabBar.vue
+++ b/components/tabs/ScrollableTabBar.vue
@@ -9,7 +9,7 @@ export default {
     const inkBarNode = this.getInkBarNode()
     const tabs = this.getTabs(h)
     const scrollbarNode = this.getScrollBarNode([inkBarNode, tabs])
-    return this.getRootNode(scrollbarNode)
+    return this.getRootNode(scrollbarNode, h)
   },
 }
 </script>
diff --git a/components/tabs/ScrollableTabBarMixin.js b/components/tabs/ScrollableTabBarMixin.js
index 3f91dd48a..6df86318b 100644
--- a/components/tabs/ScrollableTabBarMixin.js
+++ b/components/tabs/ScrollableTabBarMixin.js
@@ -27,20 +27,8 @@ export default {
     this.resizeEvent = addDOMEventListener(window, 'resize', debouncedResize)
   },
 
-  updated (prevProps) {
-    const props = this.$props
-    if (prevProps && prevProps.tabBarPosition !== props.tabBarPosition) {
-      this.setOffset(0)
-      return
-    }
-    const nextPrev = this.setNextPrev()
-    // wait next, prev show hide
-    if (this.isNextPrevShown(this) !== this.isNextPrevShown(nextPrev)) {
-      Object.assign(this, this.scrollToActiveTab)
-    } else if (!prevProps || props.activeKey !== prevProps.activeKey) {
-      // can not use props.activeKey
-      this.scrollToActiveTab()
-    }
+  updated () {
+    this.updatedCal()
   },
 
   beforeDestroy () {
@@ -48,9 +36,17 @@ export default {
       this.resizeEvent.remove()
     }
   },
+  watch: {
+    tabBarPosition (val) {
+      this.setOffset(0)
+    },
+  },
   methods: {
     updatedCal () {
-
+      this.setNextPrev()
+      this.$nextTick(() => {
+        this.scrollToActiveTab()
+      })
     },
     setNextPrev () {
       const navNode = this.$refs.nav
diff --git a/components/tabs/TabBarMixin.js b/components/tabs/TabBarMixin.js
index f70010914..7f148de68 100644
--- a/components/tabs/TabBarMixin.js
+++ b/components/tabs/TabBarMixin.js
@@ -1,3 +1,4 @@
+import Icon from '../icon'
 export default {
   props: {
     prefixCls: {
@@ -19,6 +20,16 @@ export default {
     },
     activeKey: String,
     panels: Array,
+    extraContent: [String, Number, Function],
+    hideAdd: Boolean,
+    removeTab: {
+      default: () => {},
+      type: Function,
+    },
+    createNewTab: {
+      default: () => {},
+      type: Function,
+    },
   },
   methods: {
     getTabs (h) {
@@ -28,33 +39,46 @@ export default {
         if (!child) {
           return
         }
+        let { disabled, closable } = child
+        const { tabKey, tab } = child
         // componentOptions.propsData中获取的值disabled没有根据类型初始化, 会出现空字符串
-        child.disabled = child.disabled === '' || child.disabled
-        const key = child.tabKey
-        let cls = activeKey === key ? `${prefixCls}-tab-active` : ''
+        disabled = disabled === '' || disabled
+        let cls = activeKey === tabKey ? `${prefixCls}-tab-active` : ''
         cls += ` ${prefixCls}-tab`
-        if (child.disabled) {
+        if (disabled) {
           cls += ` ${prefixCls}-tab-disabled`
         } else {
         }
         const onClick = () => {
-          !child.disabled && this.onTabClick(key)
+          !disabled && this.onTabClick(tabKey)
         }
-        // const ref = {}
-        // if (activeKey === key) {
-        //   ref.ref = this.saveRef('activeTab')
-        // }
+
+        let tabC = typeof tab === 'function' ? child.tab(h, tabKey) : tab
+        if (this.$parent.type === 'editable-card') {
+          closable = closable === undefined ? true : closable === '' || closable
+          const closeIcon = closable ? (
+            <Icon
+              type='close'
+              onClick={e => this.removeTab(tabKey, e)}
+            />
+          ) : null
+          tabC = <div class={closable ? undefined : `${prefixCls}-tab-unclosable`}>
+            {tabC}
+            {closeIcon}
+          </div>
+        }
+
         rst.push(
           <div
             role='tab'
-            aria-disabled={child.disabled ? 'true' : 'false'}
-            aria-selected={activeKey === key ? 'true' : 'false'}
+            aria-disabled={disabled ? 'true' : 'false'}
+            aria-selected={activeKey === tabKey ? 'true' : 'false'}
             class={cls}
-            key={key}
+            key={tabKey}
             onClick={onClick}
-            ref={activeKey === key ? 'activeTab' : undefined}
+            ref={activeKey === tabKey ? 'activeTab' : undefined}
           >
-            {typeof child.tab === 'function' ? child.tab(h, key) : child.tab}
+            {tabC}
           </div>
         )
       })
@@ -63,23 +87,35 @@ export default {
     },
     getRootNode (contents, createElement) {
       const {
-        prefixCls, onKeyDown, tabBarPosition, $slots,
+        prefixCls, onKeyDown, tabBarPosition, hideAdd,
       } = this
+      let extraContent = this.extraContent
+      const tabsType = this.$parent.type
       const cls = {
         [`${prefixCls}-bar`]: true,
       }
       const topOrBottom = (tabBarPosition === 'top' || tabBarPosition === 'bottom')
       const tabBarExtraContentStyle = topOrBottom ? { float: 'right' } : {}
       let children = contents
-      if ($slots.default) {
-        children = [
-          <div key='extra' class={`${prefixCls}-extra-content`} style={tabBarExtraContentStyle}>
-            {$slots.default}
-          </div>,
-          contents,
-        ]
-        children = topOrBottom ? children : children.reverse()
+      extraContent = typeof extraContent === 'function' ? extraContent(createElement) : extraContent
+
+      if (tabsType === 'editable-card' && !hideAdd) {
+        extraContent = (
+          <span>
+            <Icon type='plus' class={`${prefixCls}-new-tab`} onClick={this.createNewTab} />
+            {extraContent}
+          </span>
+        )
       }
+
+      children = [
+        <div key='extra' class={`${prefixCls}-extra-content`} style={tabBarExtraContentStyle}>
+          {extraContent}
+        </div>,
+        contents,
+      ]
+      children = topOrBottom ? children : children.reverse()
+
       return (
         <div
           role='tablist'
diff --git a/components/tabs/TabPane.vue b/components/tabs/TabPane.vue
index 9375f89a6..5a66ec07a 100644
--- a/components/tabs/TabPane.vue
+++ b/components/tabs/TabPane.vue
@@ -4,7 +4,7 @@
     :aria-hidden="active ? 'false' : 'true'"
     :class="classes"
   >
-    <slot v-if="isRender || forceRender">
+    <slot v-if="isRender">
     </slot>
   </div>
 </template>
@@ -14,12 +14,10 @@ export default {
   props: {
     tabKey: [String, Number],
     tab: [String, Number, Function],
-    forceRender: Boolean,
     disabled: Boolean,
-    // placeholder: [Function, String, Number],
+    closable: Boolean,
   },
   data () {
-    console.log(this.disabled)
     return {
     }
   },
diff --git a/components/tabs/Tabs.vue b/components/tabs/Tabs.vue
index 94b61f883..e99854e27 100644
--- a/components/tabs/Tabs.vue
+++ b/components/tabs/Tabs.vue
@@ -45,6 +45,13 @@ export default {
     destroyInactiveTabPane: Boolean,
     activeKey: String,
     defaultActiveKey: String,
+    type: {
+      validator (value) {
+        return ['line', 'card', 'editable-card'].includes(value)
+      },
+    },
+    onChange: { type: Function, default: () => {} },
+    onTabClick: { type: Function, default: () => {} },
   },
   data () {
     return {
@@ -79,11 +86,8 @@ export default {
       }
       return activeKey
     },
-    onTabClick (activeKey) {
-      console.log('onTabClick', activeKey)
-      // if (this.tabBar.props.onTabClick) {
-      //   this.tabBar.props.onTabClick(activeKey)
-      // }
+    handleTabClick (activeKey) {
+      this.onTabClick(activeKey)
       this.setActiveKey(activeKey)
     },
 
@@ -92,11 +96,11 @@ export default {
       if (eventKeyCode === KeyCode.RIGHT || eventKeyCode === KeyCode.DOWN) {
         e.preventDefault()
         const nextKey = this.getNextActiveKey(true)
-        this.onTabClick(nextKey)
+        this.handleTabClick(nextKey)
       } else if (eventKeyCode === KeyCode.LEFT || eventKeyCode === KeyCode.UP) {
         e.preventDefault()
         const previousKey = this.getNextActiveKey(false)
-        this.onTabClick(previousKey)
+        this.handleTabClick(previousKey)
       }
     },
 
@@ -105,8 +109,7 @@ export default {
         if (!this.activeKey) {
           this.stateActiveKey = activeKey
         }
-        // this.stateActiveKey = activeKey
-        this.$emit('change', activeKey)
+        this.onChange(activeKey)
       }
     },
 
@@ -115,7 +118,7 @@ export default {
       const children = []
       this.$slots.default.forEach(({ componentOptions = {}}) => {
         const c = componentOptions.propsData
-        if (c && !c.disabled) {
+        if (c && !c.disabled && c.disabled !== '') {
           if (next) {
             children.push(c)
           } else {
@@ -145,7 +148,7 @@ export default {
       tabBarPosition,
       destroyInactiveTabPane,
       onNavKeyDown,
-      onTabClick,
+      handleTabClick,
       stateActiveKey,
       classes,
       setActiveKey,
@@ -176,16 +179,14 @@ export default {
         prefixCls: prefixCls,
         onKeyDown: onNavKeyDown,
         tabBarPosition: tabBarPosition,
-        onTabClick: onTabClick,
+        onTabClick: handleTabClick,
         activeKey: stateActiveKey,
         key: 'tabBar',
       },
       style: this.tabBarProps.style || {},
     }
     const contents = [
-      <ScrollableInkTabBar {...tabBarProps}>
-        {this.$slots.tabBarExtraContent}
-      </ScrollableInkTabBar>,
+      <ScrollableInkTabBar {...tabBarProps} />,
       <TabContent {...tabContentProps}>
         {$slots.default}
       </TabContent>,
diff --git a/components/tabs/demo/card-top.vue b/components/tabs/demo/card-top.vue
new file mode 100644
index 000000000..444ab42f8
--- /dev/null
+++ b/components/tabs/demo/card-top.vue
@@ -0,0 +1,69 @@
+<template>
+  <div class="card-container">
+    <Tabs type="card">
+      <TabPane tab="Tab Title 1" tabKey="1">
+        <p>Content of Tab Pane 1</p>
+        <p>Content of Tab Pane 1</p>
+        <p>Content of Tab Pane 1</p>
+      </TabPane>
+      <TabPane tab="Tab Title 2" tabKey="2">
+        <p>Content of Tab Pane 2</p>
+        <p>Content of Tab Pane 2</p>
+        <p>Content of Tab Pane 2</p>
+      </TabPane>
+      <TabPane tab="Tab Title 3" tabKey="3">
+        <p>Content of Tab Pane 3</p>
+        <p>Content of Tab Pane 3</p>
+        <p>Content of Tab Pane 3</p>
+      </TabPane>
+    </Tabs>
+  </div>
+</template>
+<script>
+import { Tabs } from 'antd'
+export default {
+  data () {
+    return {
+    }
+  },
+  methods: {
+    callback (key) {
+      console.log(key)
+    },
+  },
+  components: {
+    Tabs,
+    TabPane: Tabs.TabPane,
+  },
+}
+</script>
+<style>
+.card-container {
+  background: #F5F5F5;
+  overflow: hidden;
+  padding: 24px;
+}
+.card-container > .ant-tabs-card > .ant-tabs-content {
+  height: 120px;
+  margin-top: -16px;
+}
+
+.card-container > .ant-tabs-card > .ant-tabs-content > .ant-tabs-tabpane {
+  background: #fff;
+  padding: 16px;
+}
+
+.card-container > .ant-tabs-card > .ant-tabs-bar {
+  border-color: #fff;
+}
+
+.card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab {
+  border-color: transparent;
+  background: transparent;
+}
+
+.card-container > .ant-tabs-card > .ant-tabs-bar .ant-tabs-tab-active {
+  border-color: #fff;
+  background: #fff;
+}
+</style>
diff --git a/components/tabs/demo/card.vue b/components/tabs/demo/card.vue
new file mode 100644
index 000000000..916b8acdc
--- /dev/null
+++ b/components/tabs/demo/card.vue
@@ -0,0 +1,25 @@
+<template>
+  <Tabs @change="callback" type="card">
+    <TabPane tab="Tab 1" tabKey="1">Content of Tab Pane 1</TabPane>
+    <TabPane tab="Tab 2" tabKey="2">Content of Tab Pane 2</TabPane>
+    <TabPane tab="Tab 3" tabKey="3">Content of Tab Pane 3</TabPane>
+  </Tabs>
+</template>
+<script>
+import { Tabs } from 'antd'
+export default {
+  data () {
+    return {
+    }
+  },
+  methods: {
+    callback (key) {
+      console.log(key)
+    },
+  },
+  components: {
+    Tabs,
+    TabPane: Tabs.TabPane,
+  },
+}
+</script>
diff --git a/components/tabs/demo/custom-add-trigger.vue b/components/tabs/demo/custom-add-trigger.vue
new file mode 100644
index 000000000..bfb7317aa
--- /dev/null
+++ b/components/tabs/demo/custom-add-trigger.vue
@@ -0,0 +1,69 @@
+<template>
+<div>
+    <div :style="{ marginBottom: '16px' }">
+      <AntButton @click="add">ADD</AntButton>
+    </div>
+    <Tabs
+      hideAdd
+      v-model="activeKey"
+      type="editable-card"
+      @edit="onEdit"
+    >
+    <TabPane v-for="pane in panes" :tab="pane.title" :key="pane.key" :tabKey="pane.key" :closable="pane.closable">
+      {{pane.content}}
+    </TabPane>
+  </Tabs>
+      </div>
+
+</template>
+<script>
+import { Tabs, Button } from 'antd'
+export default {
+  data () {
+    const panes = [
+      { title: 'Tab 1', content: 'Content of Tab 1', key: '1' },
+      { title: 'Tab 2', content: 'Content of Tab 2', key: '2' },
+    ]
+    return {
+      activeKey: panes[0].key,
+      panes,
+      newTabIndex: 0,
+    }
+  },
+  methods: {
+    callback (key) {
+      console.log(key)
+    },
+    onEdit (targetKey, action) {
+      this[action](targetKey)
+    },
+    add () {
+      const panes = this.panes
+      const activeKey = `newTab${this.newTabIndex++}`
+      panes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey })
+      this.panes = panes
+      this.activeKey = activeKey
+    },
+    remove (targetKey) {
+      let activeKey = this.activeKey
+      let lastIndex
+      this.panes.forEach((pane, i) => {
+        if (pane.key === targetKey) {
+          lastIndex = i - 1
+        }
+      })
+      const panes = this.panes.filter(pane => pane.key !== targetKey)
+      if (lastIndex >= 0 && activeKey === targetKey) {
+        activeKey = panes[lastIndex].key
+      }
+      this.panes = panes
+      this.activeKey = activeKey
+    },
+  },
+  components: {
+    Tabs,
+    TabPane: Tabs.TabPane,
+    AntButton: Button,
+  },
+}
+</script>
diff --git a/components/tabs/demo/editable-card.vue b/components/tabs/demo/editable-card.vue
new file mode 100644
index 000000000..ecf56dfb9
--- /dev/null
+++ b/components/tabs/demo/editable-card.vue
@@ -0,0 +1,61 @@
+<template>
+  <Tabs
+    v-model="activeKey"
+    type="editable-card"
+    @edit="onEdit"
+  >
+    <TabPane v-for="pane in panes" :tab="pane.title" :key="pane.key" :tabKey="pane.key" :closable="pane.closable">
+      {{pane.content}}
+    </TabPane>
+  </Tabs>
+</template>
+<script>
+import { Tabs } from 'antd'
+export default {
+  data () {
+    const panes = [
+      { title: 'Tab 1', content: 'Content of Tab 1', key: '1', closable: false },
+      { title: 'Tab 2', content: 'Content of Tab 2', key: '2' },
+    ]
+    return {
+      activeKey: panes[0].key,
+      panes,
+      newTabIndex: 0,
+    }
+  },
+  methods: {
+    callback (key) {
+      console.log(key)
+    },
+    onEdit (targetKey, action) {
+      this[action](targetKey)
+    },
+    add () {
+      const panes = this.panes
+      const activeKey = `newTab${this.newTabIndex++}`
+      panes.push({ title: 'New Tab', content: 'Content of new Tab', key: activeKey })
+      this.panes = panes
+      this.activeKey = activeKey
+    },
+    remove (targetKey) {
+      let activeKey = this.activeKey
+      let lastIndex
+      this.panes.forEach((pane, i) => {
+        if (pane.key === targetKey) {
+          lastIndex = i - 1
+        }
+      })
+      const panes = this.panes.filter(pane => pane.key !== targetKey)
+      if (lastIndex >= 0 && activeKey === targetKey) {
+        activeKey = panes[lastIndex].key
+      }
+      this.panes = panes
+      this.activeKey = activeKey
+    },
+  },
+  components: {
+    Tabs,
+    TabPane: Tabs.TabPane,
+  },
+}
+</script>
diff --git a/components/tabs/demo/extra.vue b/components/tabs/demo/extra.vue
new file mode 100644
index 000000000..a0f206665
--- /dev/null
+++ b/components/tabs/demo/extra.vue
@@ -0,0 +1,21 @@
+<template>
+  <Tabs :tabBarExtraContent="operations">
+    <TabPane tab="Tab 1" tabKey="1">Content of tab 1</TabPane>
+    <TabPane tab="Tab 2" tabKey="2">Content of tab 2</TabPane>
+    <TabPane tab="Tab 3" tabKey="3">Content of tab 3</TabPane>
+  </Tabs>
+</template>
+<script>
+import { Tabs, Button } from 'antd'
+export default {
+  methods: {
+    operations (h) {
+      return h('span', [<Button>Extra Action</Button>])
+    },
+  },
+  components: {
+    Tabs,
+    TabPane: Tabs.TabPane,
+  },
+}
+</script>
diff --git a/components/tabs/demo/index.vue b/components/tabs/demo/index.vue
new file mode 100644
index 000000000..192a15f49
--- /dev/null
+++ b/components/tabs/demo/index.vue
@@ -0,0 +1,54 @@
+<template>
+  <div>
+    <h1>Basic</h1>
+    <Basic />
+    <h1>CardTop</h1>
+    <CardTop />
+    <h1>Card</h1>
+    <Card />
+    <h1>CustomAddTrigger</h1>
+    <CustomAddTrigger />
+    <h1>Disabled</h1>
+    <Disabled />
+    <h1>EditableCard</h1>
+    <EditableCard />
+    <h1>Extra</h1>
+    <Extra />
+    <h1>Icon</h1>
+    <Icon />
+    <h1>Position</h1>
+    <Position />
+    <h1>Size</h1>
+    <Size />
+    <h1>Slide</h1>
+    <Slide/>
+  </div>
+</template>
+<script>
+import Basic from './basic'
+import CardTop from './card-top'
+import Card from './card'
+import CustomAddTrigger from './custom-add-trigger'
+import Disabled from './disabled'
+import EditableCard from './editable-card'
+import Extra from './extra'
+import Icon from './icon'
+import Position from './position'
+import Size from './size'
+import Slide from './slide'
+export default {
+  components: {
+    Basic,
+    CardTop,
+    Card,
+    CustomAddTrigger,
+    Disabled,
+    EditableCard,
+    Extra,
+    Icon,
+    Position,
+    Size,
+    Slide,
+  },
+}
+</script>
diff --git a/components/tabs/demo/position.vue b/components/tabs/demo/position.vue
new file mode 100644
index 000000000..e79faf24c
--- /dev/null
+++ b/components/tabs/demo/position.vue
@@ -0,0 +1,38 @@
+<template>
+  <div style="width: 500px">
+    <RadioGroup v-model="tabPosition" style="margin:8px">
+      <RadioButton value="top">top</RadioButton>
+      <RadioButton value="bottom">bottom</RadioButton>
+      <RadioButton value="left">left</RadioButton>
+      <RadioButton value="right">right</RadioButton>
+    </RadioGroup>
+    <Tabs defaultActiveKey="1" :tabPosition="tabPosition">
+      <TabPane tab="Tab 1" tabKey="1">Content of Tab 1</TabPane>
+      <TabPane tab="Tab 2" tabKey="2">Content of Tab 2</TabPane>
+      <TabPane tab="Tab 3" tabKey="3">Content of Tab 3</TabPane>
+    </Tabs>
+  </div>
+
+</template>
+<script>
+import { Tabs, Radio } from 'antd'
+export default {
+  data () {
+    return {
+      tabPosition: 'top',
+    }
+  },
+  methods: {
+    callback (val) {
+      console.log(val)
+    },
+  },
+  components: {
+    Tabs,
+    TabPane: Tabs.TabPane,
+    Radio,
+    RadioGroup: Radio.Group,
+    RadioButton: Radio.Button,
+  },
+}
+</script>
diff --git a/components/tabs/demo/size.vue b/components/tabs/demo/size.vue
new file mode 100644
index 000000000..7ccecdee5
--- /dev/null
+++ b/components/tabs/demo/size.vue
@@ -0,0 +1,16 @@
+<template>
+  <Tabs defaultActiveKey="2" size="small">
+    <TabPane tab="Tab 1" tabKey="1">Content of tab 1</TabPane>
+    <TabPane tab="Tab 2" tabKey="2">Content of tab 2</TabPane>
+    <TabPane tab="Tab 3" tabKey="3">Content of tab 3</TabPane>
+  </Tabs>
+</template>
+<script>
+import { Tabs } from 'antd'
+export default {
+  components: {
+    Tabs,
+    TabPane: Tabs.TabPane,
+  },
+}
+</script>
diff --git a/components/tabs/demo/slide.vue b/components/tabs/demo/slide.vue
index 514e7b1af..8b9158181 100644
--- a/components/tabs/demo/slide.vue
+++ b/components/tabs/demo/slide.vue
@@ -1,10 +1,10 @@
 <template>
   <div style="width: 500px">
-    <RadioGroup v-model="mode" :style="{ marginBottom: 8 }">
+    <RadioGroup v-model="mode" :style="{ marginBottom: '8px' }">
       <RadioButton value="top">Horizontal</RadioButton>
       <RadioButton value="left">Vertical</RadioButton>
     </RadioGroup>
-    <Tabs defaultActiveKey="1" :tabPosition="mode" :style="{ height: '200px'}">
+    <Tabs defaultActiveKey="1" :tabPosition="mode" :style="{ height: '200px'}" @prevClick="callback" @nextClick="callback">
       <TabPane tab="Tab 1" tabKey="1">Content of tab 1</TabPane>
       <TabPane tab="Tab 2" tabKey="2">Content of tab 2</TabPane>
       <TabPane tab="Tab 3" tabKey="3">Content of tab 3</TabPane>
@@ -29,6 +29,9 @@ export default {
     }
   },
   methods: {
+    callback (val) {
+      console.log(val)
+    },
   },
   components: {
     Tabs,
diff --git a/components/tabs/index.vue b/components/tabs/index.vue
index e4875a79c..48ca53ccd 100644
--- a/components/tabs/index.vue
+++ b/components/tabs/index.vue
@@ -1,10 +1,5 @@
 <script>
 import Tabs from './Tabs'
-// import TabPane from './TabPane'
-import ScrollableInkTabBar from './ScrollableInkTabBar'
-import TabBar from './TabBar'
-import TabContent from './TabContent'
-import Icon from '../icon'
 import isFlexSupported from '../_util/isFlexSupported'
 export default {
   props: {
@@ -12,11 +7,9 @@ export default {
     activeKey: String,
     defaultActiveKey: String,
     hideAdd: { type: Boolean, default: false },
-    onChange: { type: Function, default: () => {} },
-    onTabClick: { type: Function, default: () => {} },
-    onPrevClick: { type: Function, default: () => {} },
-    onNextClick: { type: Function, default: () => {} },
     tabBarStyle: Object,
+    tabBarExtraContent: [String, Number, Function],
+    destroyInactiveTabPane: { type: Boolean, default: false },
     type: {
       validator (value) {
         return ['line', 'card', 'editable-card'].includes(value)
@@ -27,20 +20,20 @@ export default {
         return ['top', 'right', 'bottom', 'left'].includes(value)
       },
     },
-    onEdit: { type: Function, default: () => {} },
     size: {
       validator (value) {
         return ['default', 'small'].includes(value)
       },
     },
-    animated: Boolean | Object,
+    animated: { type: [Boolean, Object], default: undefined },
+  },
+  model: {
+    prop: 'activeKey',
+    event: 'change',
   },
   methods: {
     createNewTab (targetKey) {
-      const onEdit = this.$props.onEdit
-      if (onEdit) {
-        onEdit(targetKey, 'add')
-      }
+      this.$emit('edit', targetKey, 'add')
     },
 
     removeTab (targetKey, e) {
@@ -48,20 +41,21 @@ export default {
       if (!targetKey) {
         return
       }
-
-      const onEdit = this.$props.onEdit
-      if (onEdit) {
-        onEdit(targetKey, 'remove')
-      }
+      this.$emit('edit', targetKey, 'remove')
     },
 
     handleChange (activeKey) {
-      // const onChange = this.$props.onChange
-      // if (onChange) {
-      //   onChange(activeKey)
-      // }
       this.$emit('change', activeKey)
     },
+    onTabClick (val) {
+      this.$emit('tabClick', val)
+    },
+    onPrevClick (val) {
+      this.$emit('prevClick', val)
+    },
+    onNextClick (val) {
+      this.$emit('nextClick', val)
+    },
   },
 
   mounted () {
@@ -72,29 +66,32 @@ export default {
     }
   },
 
-  render () {
+  render (createElement) {
     const {
       prefixCls,
       size,
       type = 'line',
       tabPosition,
       tabBarStyle,
-      // hideAdd,
+      hideAdd,
       onTabClick,
       onPrevClick,
       onNextClick,
-      animated = true,
-    } = this.$props
-    let { tabBarExtraContent } = this.$props
+      animated,
+      destroyInactiveTabPane = false,
+      activeKey,
+      defaultActiveKey,
+    } = this
+    const { tabBarExtraContent } = this.$props
     let { inkBarAnimated, tabPaneAnimated } = typeof animated === 'object' ? { // eslint-disable-line
-      inkBarAnimated: animated.inkBar, tabPaneAnimated: animated.tabPane,
+      inkBarAnimated: !!animated.inkBar, tabPaneAnimated: !!animated.tabPane,
     } : {
-      inkBarAnimated: animated, tabPaneAnimated: animated,
+      inkBarAnimated: animated === undefined || animated, tabPaneAnimated: animated === undefined || animated,
     }
 
     // card tabs should not have animation
     if (type !== 'line') {
-      tabPaneAnimated = 'animated' in this.$props ? tabPaneAnimated : false
+      tabPaneAnimated = animated === undefined ? false : tabPaneAnimated
     }
     const cls = {
       [`${prefixCls}-mini`]: size === 'small' || size,
@@ -103,56 +100,7 @@ export default {
       [`${prefixCls}-${type}`]: true,
       [`${prefixCls}-no-animation`]: !tabPaneAnimated,
     }
-    // only card type tabs can be added and closed
-    let childrenWithClose
-    // if (type === 'editable-card') {
-    //   childrenWithClose = []
-    //   React.Children.forEach(children, (child, index) => {
-    //     let closable = child.props.closable
-    //     closable = typeof closable === 'undefined' ? true : closable
-    //     const closeIcon = closable ? (
-    //       <Icon
-    //         type='close'
-    //         onClick={e => this.removeTab(child.key, e)}
-    //       />
-    //     ) : null
-    //     childrenWithClose.push(cloneElement(child, {
-    //       tab: (
-    //         <div className={closable ? undefined : `${prefixCls}-tab-unclosable`}>
-    //           {child.props.tab}
-    //           {closeIcon}
-    //         </div>
-    //       ),
-    //       key: child.key || index,
-    //     }))
-    //   })
-    //   // Add new tab handler
-    //   if (!hideAdd) {
-    //     tabBarExtraContent = (
-    //       <span>
-    //         <Icon type='plus' className={`${prefixCls}-new-tab`} onClick={this.createNewTab} />
-    //         {tabBarExtraContent}
-    //       </span>
-    //     )
-    //   }
-    // }
 
-    tabBarExtraContent = tabBarExtraContent ? (
-      <div class={`${prefixCls}-extra-content`}>
-        {tabBarExtraContent}
-      </div>
-    ) : null
-
-    // const renderTabBar = () => (
-    //   <ScrollableInkTabBar
-    //     inkBarAnimated={inkBarAnimated}
-    //     extraContent={tabBarExtraContent}
-    //     onTabClick={onTabClick}
-    //     onPrevClick={onPrevClick}
-    //     onNextClick={onNextClick}
-    //     style={tabBarStyle}
-    //   />
-    // )
     const tabBarProps = {
       inkBarAnimated,
       extraContent: tabBarExtraContent,
@@ -160,21 +108,40 @@ export default {
       onPrevClick,
       onNextClick,
       style: tabBarStyle,
+      hideAdd,
+      removeTab: this.removeTab,
+      createNewTab: this.createNewTab,
     }
     const tabContentProps = {
       animated: tabPaneAnimated,
       animatedWithMargin: true,
     }
+    const self = this
+    const tabsProps = {
+      props: {
+        prefixCls,
+        tabBarPosition: tabPosition,
+        onChange: this.handleChange,
+        tabBarProps: tabBarProps,
+        tabContentProps: tabContentProps,
+        destroyInactiveTabPane,
+        activeKey,
+        defaultActiveKey,
+        type,
+        onTabClick: this.onTabClick,
+      },
+      on: {
+        change (val) {
+          self.handleChange(val)
+        },
+      },
+    }
     return (
       <Tabs
-        {...this.$props}
         class={cls}
-        tabBarPosition={tabPosition}
-        onChange={this.handleChange}
-        tabBarProps={tabBarProps}
-        tabContentProps={tabContentProps}
+        {...tabsProps}
       >
-        {childrenWithClose || this.$slots.default}
+        {this.$slots.default}
       </Tabs>
     )
   },
diff --git a/components/tabs/index.zh-CN b/components/tabs/index.zh-CN
new file mode 100644
index 000000000..fb06b446c
--- /dev/null
+++ b/components/tabs/index.zh-CN
@@ -0,0 +1,52 @@
+---
+category: Components
+subtitle: 标签页
+type: Data Display
+title: Tabs
+cols: 1
+---
+
+选项卡切换组件。
+
+## 何时使用
+
+提供平级的区域将大块内容进行收纳和展现,保持界面整洁。
+
+Ant Design 依次提供了三级选项卡,分别用于不同的场景。
+
+- 卡片式的页签,提供可关闭的样式,常用于容器顶部。
+- 标准线条式页签,用于容器内部的主功能切换,这是最常用的 Tabs。
+- [RadioButton](/components/radio/#components-radio-demo-radiobutton) 可作为更次级的页签来使用。
+
+## API
+
+### Tabs
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| activeKey | 当前激活 tab 面板的 key | string | 无 |
+| animated | 是否使用动画切换 Tabs,在 `tabPosition=top|bottom` 时有效 | boolean \| {inkBar:boolean, tabPane:boolean} | true, 当 type="card" 时为 false |
+| defaultActiveKey | 初始化选中面板的 key,如果没有设置 activeKey | string | 第一个面板 |
+| hideAdd | 是否隐藏加号图标,在 `type="editable-card"` 时有效 | boolean | false |
+| size | 大小,提供 `default` 和 `small` 两种大小,仅当 `type="line"` 时生效。 | string | 'default' |
+| tabBarExtraContent | tab bar 上额外的元素 | string\|number\|Function | 无 |
+| tabBarStyle | tab bar 的样式对象 | object | - |
+| tabPosition | 页签位置,可选值有 `top` `right` `bottom` `left` | string | 'top' |
+| type | 页签的基本样式,可选 `line`、`card` `editable-card` 类型 | string | 'line' |
+
+
+### 事件
+| 参数 | 说明 | 类型 | 默认值 |
+| change | 切换面板的回调 | Function | 无 |
+| edit | 新增和删除页签的回调,在 `type="editable-card"` 时有效 | (targetKey, action): void | 无 |
+| nextClick | next 按钮被点击的回调 | Function | 无 |
+| prevClick | prev 按钮被点击的回调 | Function | 无 |
+| tabClick | tab 被点击的回调 | Function | 无 |
+
+### Tabs.TabPane
+
+| 参数 | 说明 | 类型 | 默认值 |
+| --- | --- | --- | --- |
+| forceRender | 被隐藏时是否渲染 DOM 结构 | boolean | false |
+| tabKey | 对应 activeKey | string | 无 |
+| tab | 选项卡头显示文字 | string\|Function | 无 |