Эх сурвалжийг харах

Merge branch 'next' of github.com:eleme/element-ui into next

Leopoldthecoder 9 жил өмнө
parent
commit
f301f215c6

+ 3 - 0
components.json

@@ -148,5 +148,8 @@
   ],
   "message": [
     "./packages/message/index.js"
+  ],
+  "card": [
+    "./packages/card/index.js"
   ]
 }

BIN
examples/assets/images/hamburger.png


+ 1 - 1
examples/components/table-filter.vue

@@ -23,7 +23,7 @@
       }
     },
 
-    ready() {
+    mounted() {
       console.log('popup ready');
     }
   };

+ 45 - 9
examples/docs/alert.md

@@ -1,3 +1,12 @@
+<script>
+  export default {
+    methods: {
+      hello() {
+        alert('Hello World!');
+      }
+    }
+  }
+</script>
 <style>
   .demo-box.demo-alert .el-alert {
     margin: 20px 0 0;
@@ -6,6 +15,8 @@
 
 ## 基本用法
 
+alert组件提供四种主题,由`type`属性指定,默认值为`info`,下面的示例展示了四种不同的主题。
+
 <div class="demo-box demo-alert">
   <el-alert title="成功提示的文案" type="success"></el-alert>
   <el-alert title="消息提示的文案" type="info"></el-alert>
@@ -22,16 +33,32 @@
 
 ## 自定义关闭按钮
 
+在alert组件中,你可以设置是否可关闭,关闭按钮的文本以及关闭时的回调函数。
+
+`closable`属性决定是否可关闭,接受`boolean`,默认为`true`。
+
+你可以设置`close-text`属性来代替右侧的关闭图标,注意:`close-text`必须为文本。
+
+设置`close`事件来设置关闭时的回调。
+
+下面的示例展示了上述三种情况:
+
 <div class="demo-box demo-alert">
-  <el-alert title="成功提示的文案" type="success" close-text="知道了"></el-alert>
+  <el-alert title="不可关闭的alert" type="success" :closable="false"></el-alert>
+  <el-alert title="自定义close-text" type="info" close-text="知道了"></el-alert>
+  <el-alert title="设置了回调的alert" type="warning" @close="hello"></el-alert>
 </div>
 
 ```html
-<el-alert title="成功提示的文案" type="success" close-text="知道了"></el-alert>
+<el-alert title="不可关闭" type="success" :closable="false"></el-alert>
+<el-alert title="自定义close-text" type="info" close-text="知道了"></el-alert>
+<el-alert title="设置了回调的alert" type="warning" @close="hello"></el-alert>
 ```
 
 ## 带有 icon
 
+通过设置`show-icon`属性来显示alert的icon,这能更有效的向用户展示你的显示意图。
+
 <div class="demo-box demo-alert">
   <el-alert title="成功提示的文案" type="success" show-icon></el-alert>
   <el-alert title="消息提示的文案" type="info" show-icon></el-alert>
@@ -48,20 +75,24 @@
 
 ## 带有辅助性文字介绍
 
+除了必填的`title`属性外,你可以设置`description`属性来帮助你更好的介绍,我们称之为辅助性文字。辅助性文字只能存放单行文本,会自动换行显示。
+
 <div class="demo-box demo-alert">
-  <el-alert title="成功提示的文案" type="success" description="文字说明文字说明文字说明文字说明文字说明文字说明"></el-alert>
+  <el-alert title="带辅助性文字介绍" type="success" description="这是一句绕口令:黑灰化肥会挥发发灰黑化肥挥发;灰黑化肥会挥发发黑灰化肥发挥。 黑灰化肥会挥发发灰黑化肥黑灰挥发化为灰……"></el-alert>
 </div>
 
 ```html
 <el-alert
-  title="成功提示的文案"
+  title="带辅助性文字介绍"
   type="success"
-  description="文字说明文字说明文字说明文字说明文字说明文字说明">
+  description="这是一句绕口令:黑灰化肥会挥发发灰黑化肥挥发;灰黑化肥会挥发发黑灰化肥发挥。 黑灰化肥会挥发发灰黑化肥黑灰挥发化为灰……">
 </el-alert>
 ```
 
 ## 带有 icon 和辅助性文字介绍
 
+最后,这是一个同时具有icon和辅助性文字的样例。
+
 <div class="demo-box demo-alert">
   <el-alert title="成功提示的文案" type="success" description="文字说明文字说明文字说明文字说明文字说明文字说明" show-icon></el-alert>
   <el-alert title="消息提示的文案" type="info" description="文字说明文字说明文字说明文字说明文字说明文字说明" show-icon></el-alert>
@@ -99,13 +130,18 @@
 </el-alert>
 ```
 
-## API
+## Attributes
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
 |---------- |-------------- |---------- |--------------------------------  |-------- |
-| title | 标题, 必选参数 | string | | |
+| **title** | 标题,**必选参数** | string | | |
 | type | 主题 | string | 'success', 'warning', 'info', 'error' | 'info' |
-| description | 说明文字 | string | | |
+| description | 辅助性文字 | string | | |
 | closable | 是否可关闭 | boolean | | true |
 | closeText | 关闭按钮自定义文本 | string | | |
 | showIcon | 是否显示图标 | boolean | | false |
-| onClose | 关闭时的回调函数 | function | | |
+
+
+## Events
+| 参数      | 说明          | 类型      | 可选值                           | 默认值  |
+|---------- |-------------- |---------- |--------------------------------  |-------- |
+| close | 关闭时的回调函数 | function | | |

+ 41 - 8
examples/docs/button.md

@@ -31,6 +31,8 @@
 
 ## 基础用法
 
+button组件默认提供7种主题,由`type`属性来定义,默认为`default`:
+
 <div class="demo-box demo-button">
   <el-button>Default</el-button>
   <el-button type="primary">primary</el-button>
@@ -38,10 +40,18 @@
   <el-button type="warning">warning</el-button>
   <el-button type="danger">danger</el-button>
   <el-button type="info">info</el-button>
-  <el-button :disabled="true">disabled</el-button>
   <el-button type="text">text button</el-button>
 </div>
 
+你可以使用`disabled`属性来定义按钮是否可用,它接受一个`Boolean`值:
+
+<div class="demo-box demo-button">
+  <el-button>Default</el-button>
+  <el-button :disabled="true">disabled</el-button>
+</div>
+
+下面是他们的样例代码:
+
 ```html
 <el-button>Default</el-button>
 <el-button type="primary">primary</el-button>
@@ -49,12 +59,20 @@
 <el-button type="warning">warning</el-button>
 <el-button type="danger">danger</el-button>
 <el-button type="info">info</el-button>
-<el-button :disabled="true">disabled</el-button>
 <el-button type="text">text button</el-button>
+<el-button :disabled="true">disabled</el-button>
 ```
 
 ## Plain Button
 
+默认的`Default`主题,我们称之为朴素按钮(Plain Button),朴素按钮同样设置了不同的`type`属性对应的样式(可选值同上),默认为`info`。
+
+设置`plain`属性,它接受一个`Boolean`。
+
+注意,在该情况下,`type`虽然可以为`text`,但是是没有意义的,会显示为`text button`的样式。
+
+你可以Hover在样例上进行预览:
+
 <div class="demo-box demo-button">
   <el-button :plain="true">Default</el-button>
   <el-button :plain="true" type="success">success</el-button>
@@ -73,6 +91,8 @@
 
 ## 尺寸
 
+button组件提供除了默认值以外的三种尺寸:`large`、`small`、`mini`来满足不同的需求,通过设置`size`属性来配置它们。
+
 <div class="demo-box demo-button">
   <el-button type="primary" size="large">large</el-button>
   <el-button type="primary">Default</el-button>
@@ -89,27 +109,38 @@
 
 ## Loading
 
+button组件提供的`loading`属性可以方便的让你处理 loading 状态,它接受一个`Boolean`,要设置为 loading 状态,只要设置`loading`属性为`true`即可,下面是一个样例:
+
 <div class="demo-box demo-button">
   <el-button type="primary" :loading="true">Button</el-button>
 </div>
-<p>点击后变成 loading 状态</p>
+
+点击后变成 loading 状态的样例,尝试点击它:
+
 <div class="demo-box demo-button">
-  <el-button type="primary" :loading="isLoading" @click="isLoading = true">Button</el-button>
-  <el-button type="primary" icon="search" :loading="isLoading2" @click="isLoading2 = true">Button</el-button>
+  <el-button type="primary" :loading="isLoading" @click.native="isLoading = true">Button</el-button>
+  <el-button type="primary" icon="search" :loading="isLoading2" @click.native="isLoading2 = true">Button</el-button>
 </div>
 
 ```html
 <el-button type="primary" :loading="true">Button</el-button>
 
-<el-button type="primary" :loading="isLoading" @click="isLoading = true">Button</el-button>
-<el-button type="primary" icon="search" :loading="isLoading2" @click="isLoading2 = true">Button</el-button>
+<el-button type="primary" :loading="isLoading" @click.native="isLoading = true">Button</el-button>
+<el-button type="primary" icon="search" :loading="isLoading2" @click.native="isLoading2 = true">Button</el-button>
 ```
 
 ## 图标按钮
 
+理所当然的,Element也支持图标按钮,设置`icon`属性即可,icon的列表可以参考Element的icon组件,下面是简单的预览:
+
 <div class="demo-box demo-button">
   <el-button type="primary" icon="edit"></el-button>
   <el-button type="primary" icon="search">Search</el-button>
+</div>
+
+除此以外,你也可以设置在文字右边的icon,只要使用`i`标签即可,使用图标组件来实现它:
+
+<div class="demo-box demo-button">
   <el-button type="primary">Upload<i class="el-icon-upload el-icon-right"></i></el-button>
 </div>
 
@@ -121,6 +152,8 @@
 
 ## 按钮组
 
+如果你需要多个并列的按钮,按钮组可以帮你轻松的实现它,使用`<el-button-group>`标签来嵌套你的按钮即可。
+
 <div class="demo-box demo-button">
   <el-button-group>
     <el-button>Button</el-button>
@@ -149,7 +182,7 @@
 </el-button-group>
 ```
 
-## API
+## Attributes
 | 参数      | 说明    | 类型      | 可选值       | 默认值   |
 |---------- |-------- |---------- |-------------  |-------- |
 | size     | 尺寸   | string  |   large,small,mini            |         |

+ 132 - 0
examples/docs/card.md

@@ -0,0 +1,132 @@
+<script>
+  import dateUtil from 'main/utils/date'
+  export default {
+    data() {
+      return {
+        currentDate: dateUtil.format(new Date(), 'yyyy-MM-dd HH:mm')
+      };
+    }
+  }
+</script>
+
+<style scoped>
+  .text {
+    font-size: 14px;
+  }
+
+  .time {
+    font-size: 13px;
+    color: #999;
+  }
+
+  .bottom {
+    margin-top: 13px;
+    line-height: 12px;
+  }
+
+  .item {
+    padding: 18px 0;
+  }
+
+  .button {
+    padding: 0;
+    float: right;
+  }
+
+  .image {
+    width: 100%;
+    display: block;
+  }
+
+  .clearfix {
+    @utils-clearfix;
+  }
+</style>
+
+## 基础用法
+包含标题, 内容和操作
+
+<el-row>
+  <el-col :span="12">
+    <el-card>
+      <div slot="header" class="clearfix">
+        <span style="line-height: 36px;">卡片名称</span>
+        <el-button style="float: right;" type="primary">操作按钮</el-button>
+      </div>
+      <div v-for="o in 4" class="text item">
+        {{'列表内容 ' + o }}
+      </div>
+    </el-card>
+  </el-col>
+</el-row>
+
+```html
+<el-card>
+  <div slot="header" class="clearfix">
+    <span style="line-height: 36px;">卡片名称</span>
+    <el-button style="float: right;" type="primary">操作按钮</el-button>
+  </div>
+  <div v-for="o in 4" class="text item">
+    {{'列表内容 ' + o }}
+  </div>
+</el-card>
+```
+
+## 简单卡片
+卡片可以只有内容区域
+
+<el-row>
+  <el-col :span="12">
+    <el-card>
+      <div v-for="o in 4" class="text item">
+        {{'列表内容 ' + o }}
+      </div>
+    </el-card>
+  </el-col>
+</el-row>
+
+```html
+<el-card>
+  <div v-for="o in 4" class="text item">
+    {{'列表内容 ' + o }}
+  </div>
+</el-card>
+```
+
+
+## 带图片
+可配置定义更丰富的内容展示
+
+<el-row>
+  <el-col :span="8" v-for="(o, index) in 2" :offset="index > 0 ? 2 : 0">
+    <el-card :body-style="{ padding: '0px' }">
+      <img src="~examples/assets/images/hamburger.png" class="image">
+      <div style="padding: 14px;">
+        <span>好吃的汉堡</span>
+        <div class="bottom clearfix">
+          <time class="time">{{ currentDate }}</time>
+          <el-button type="text" class="button">操作按钮</el-button>
+        </div>
+      </div>
+    </el-card>
+  </el-col>
+</el-row>
+
+```html
+<el-card :body-style="{ padding: '0px' }">
+  <img src="~examples/assets/images/hamburger.png" class="image">
+  <div style="padding: 14px;">
+    <span>好吃的汉堡</span>
+    <div class="bottom clearfix">
+      <time class="time">{{ currentDate }}</time>
+      <el-button type="text" class="button">操作按钮</el-button>
+    </div>
+  </div>
+</el-card>
+```
+
+## API
+| 参数      | 说明    | 类型      | 可选值       | 默认值   |
+|---------- |-------- |---------- |-------------  |-------- |
+| header | 设置 header,也可以通过 `slot#header` 传入 DOM | string| | |
+| body-style | 设置 body 的样式| object| | { padding: '20px' } |

+ 42 - 15
examples/docs/table.md

@@ -1,6 +1,5 @@
 <script>
   import Vue from 'vue';
-  let popup = Vue.extend(require('examples/components/table-filter.vue'));
   export default {
     data() {
       return {
@@ -81,6 +80,16 @@
       };
     },
 
+    methods: {
+      handleSelectionChange(val) {
+        this.singleSelection = val;
+      },
+
+      handleMultipleSelectionChange(val) {
+        this.multipleSelection = val;
+      }
+    },
+
     watch: {
       singleSelection(val) {
         console.log('selection: ', val);
@@ -287,7 +296,7 @@
     <el-table-column property="address" label="地址"></el-table-column>
   </el-table>
 </template>
- 
+
 <script>
   export default {
     data() {
@@ -427,16 +436,16 @@
 
 ## 单选
 
-<el-table :data="tableData" selection-mode="single" :selection.sync="singleSelection" style="width: 520px" allow-no-selection>
+<el-table :data="tableData" selection-mode="single" @selectionchange="handleSelectionChange" style="width: 520px" allow-no-selection>
   <el-table-column property="date" label="日期" width="120"></el-table-column>
   <el-table-column property="name" label="姓名" width="120"></el-table-column>
   <el-table-column property="address" label="地址"></el-table-column>
 </el-table>
-<p>{{ singleSelection | json }}</p>
+<p>{{ singleSelection }}</p>
 
 ```html
 <template>
-  <el-table :data="tableData" selection-mode="single" :selection.sync="singleSelection">
+  <el-table :data="tableData" selection-mode="single" @selectionchange="handleSelectionChange">
     <el-table-column property="date" label="日期" width="120"></el-table-column>
     <el-table-column property="name" label="姓名" width="120"></el-table-column>
     <el-table-column property="address" label="地址"></el-table-column>
@@ -466,6 +475,12 @@
         }],
         singleSelection: {}
       }
+    },
+
+    methods: {
+      handleSelectionChange(val) {
+        this.singleSelection = val;
+      }
     }
   }
 </script>
@@ -473,19 +488,23 @@
 
 ## 多选
 
-<el-table :data="tableData3" selection-mode="multiple" :selection.sync="multipleSelection" style="width: 520px">
+<el-table :data="tableData3" selection-mode="multiple" style="width: 520px" @selectionchange="handleMultipleSelectionChange">
   <el-table-column type="selection" width="50"></el-table-column>
-  <el-table-column property="date" label="日期" width="120"></el-table-column>
+  <el-table-column inline-template property="date" label="日期" width="120">
+    <div>{{ row.date }}</div>
+  </el-table-column>
   <el-table-column property="name" label="姓名" width="120"></el-table-column>
   <el-table-column property="address" label="地址"></el-table-column>
 </el-table>
-<p>{{ multipleSelection | json }}</p>
+<p>{{ multipleSelection }}</p>
 
 ```html
 <template>
-  <el-table :data="tableData3" selection-mode="multiple" :selection.sync="multipleSelection">
+  <el-table :data="tableData3" selection-mode="multiple" @selectionchange="handleSelectionChange">
     <el-table-column type="selection" width="50"></el-table-column>
-    <el-table-column property="date" label="日期" width="120"></el-table-column>
+    <el-table-column inline-template property="date" label="日期" width="120">
+      <div>{{ row.date }}</div>
+    </el-table-column>
     <el-table-column property="name" label="姓名" width="120"></el-table-column>
     <el-table-column property="address" label="地址"></el-table-column>
   </el-table>
@@ -526,6 +545,12 @@
         }],
         multipleSelection: []
       }
+    },
+
+    methods: {
+      handleSelectionChange(val) {
+        this.multipleSelection = val;
+      }
     }
   }
 </script>
@@ -584,16 +609,15 @@
 | border | 是否带有纵向边框 | boolean | | false |
 | selectionMode | 列表项选择模式 | string | 'single', 'multiple', 'none' | 'none' |
 | allowNoSelection | 单选模式是否允许选项为空 | boolean | | false |
-| selection | 多选模式下返回数组,单选模式下返回选中的元素。 | array/object | | |
 | fixedColumnCount | 固定列的个数 | number | | 0 |
 
 ## el-table 事件
 | 事件名 | 说明 | 参数 |
 | ---- | ---- | ---- |
-| selection-change | 当选择项发生变化时会触发该事件 | selected |
-| cell-mouse-enter | 当单元格 hover 进入时会触发该事件 | row, column, cell, event |
-| cell-mouse-leave | 当单元格 hover 退出时会触发该事件 | row, column, cell, event |
-| cell-click | 当某个单元格被点击时会触发该事件 | row, column, cell, event |
+| selectionchange | 当选择项发生变化时会触发该事件 | selected |
+| cellmouseenter | 当单元格 hover 进入时会触发该事件 | row, column, cell, event |
+| cellmouseleave | 当单元格 hover 退出时会触发该事件 | row, column, cell, event |
+| cellclick | 当某个单元格被点击时会触发该事件 | row, column, cell, event |
 
 ## el-table-column API
 | 参数      | 说明          | 类型      | 可选值                           | 默认值  |
@@ -604,3 +628,6 @@
 | sortable | 对应列是否可以排序 | boolean | | false |
 | type | 对应列的类型。如果设置了 `selection` 则显示多选按钮,如果设置了 `index` 则显示该行的索引(从 1 开始计算) | string | 'selection', 'index' | 0 |
 | formatter | 用来格式化内容,在 formatter 执行的时候,会传入 row 和 column | function | |  |
+| show-tooltip-when-overflow | 当过长被隐藏时显示 tooltip | Boolean | | false |
+| inline-template | 指定该属性后可以自定义 column 模板,参考多选的时间列,通过 row 获取行信息。此时不需要配置 property 属性  | | |
+

+ 6 - 0
examples/nav.config.json

@@ -92,6 +92,12 @@
         "name": "加载 (loading)",
         "title": "loading 加载",
         "description": "加载数据时显示"
+      },
+      {
+        "path": "/card",
+        "name": "卡片 (card)",
+        "title": "card 卡片",
+        "description": "将信息聚合在卡片容器中展示"
       }
     ]
   },

+ 1 - 1
package.json

@@ -46,7 +46,7 @@
     "q": "^1.4.1",
     "uppercamelcase": "^1.1.0",
     "vue-loader": "^9.3.2",
-    "vue": "^2.0.0-rc.1",
+    "vue": "^2.0.0-rc.2",
     "vue-markdown-loader": "^0.4.0",
     "vue-popup": "^0.2.2",
     "vue-router": "^2.0.0-beta.2"

+ 1 - 1
packages/alert/src/main.vue

@@ -57,7 +57,7 @@
     methods: {
       close() {
         this.visible = false;
-        this.$emit('onClose');
+        this.$emit('close');
       }
     },
 

+ 31 - 0
packages/card/cooking.conf.js

@@ -0,0 +1,31 @@
+var cooking = require('cooking');
+var path = require('path');
+
+cooking.set({
+  entry: {
+    index: path.join(__dirname, 'index.js')
+  },
+  dist: path.join(__dirname, 'lib'),
+  template: false,
+  format: 'umd',
+  moduleName: 'ElCard',
+  extractCSS: 'style.css',
+
+  extends: ['vue', 'saladcss']
+});
+
+cooking.add('resolve.alias', {
+  'main': path.join(__dirname, '../../src'),
+  'packages': path.join(__dirname, '../../packages')
+});
+
+cooking.add('externals', {
+  vue: {
+    root: 'Vue',
+    commonjs: 'vue',
+    commonjs2: 'vue',
+    amd: 'vue'
+  }
+});
+
+module.exports = cooking.resolve();

+ 7 - 0
packages/card/index.js

@@ -0,0 +1,7 @@
+const Card = require('./src/main');
+
+Card.install = function(Vue) {
+  Vue.component(Card.name, Card);
+};
+
+module.exports = Card;

+ 15 - 0
packages/card/package.json

@@ -0,0 +1,15 @@
+{
+  "name": "el-card",
+  "version": "0.0.0",
+  "description": "A card component for Vue.js.",
+  "keywords": [
+    "element",
+    "vue",
+    "component"
+  ],
+  "main": "./lib/index.js",
+  "repository": "https://github.com/element-component/element/tree/master/packages/card",
+  "author": "elemefe",
+  "license": "MIT",
+  "dependencies": {}
+}

+ 18 - 0
packages/card/src/main.vue

@@ -0,0 +1,18 @@
+<template>
+  <div class="el-card">
+    <div class="el-card__header" v-if="$slots.header">
+      <slot name="header">{{ header }}</slot>
+    </div>
+    <div class="el-card__body" :style="bodyStyle">
+      <slot></slot>
+    </div>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: 'el-card',
+
+    props: ['header', 'bodyStyle']
+  };
+</script>

+ 1 - 1
packages/date-picker/src/util/index.js

@@ -1,4 +1,4 @@
-import dateUtil from './fecha';
+import dateUtil from 'main/utils/date';
 
 const newArray = function(start, end) {
   let result = [];

+ 155 - 0
packages/table/src/table-body.js

@@ -0,0 +1,155 @@
+const getColumnById = function(grid, columnId) {
+  let column = null;
+  grid.columns.forEach(function(item) {
+    if (item.id === columnId) {
+      column = item;
+    }
+  });
+  return column;
+};
+
+const getColumnByCell = function(grid, cell) {
+  const matches = (cell.className || '').match(/grid_[^\s]+/gm);
+  if (matches) {
+    return getColumnById(grid, matches[0]);
+  }
+  return null;
+};
+
+import { getValueByPath, getCell, orderBy, getChild } from './util';
+
+export default {
+  props: {
+    columns: {},
+    data: {},
+    fixed: {},
+    selection: {
+      default() {
+        return [];
+      }
+    }
+  },
+
+  render(h) {
+    return (
+      <table
+        class="el-table__body"
+        cellspacing="0"
+        cellpadding="0"
+        border="0">
+        <tbody>
+          {
+            this._l(this.data, (row, $index) =>
+              <tr
+                on-click={ ($event) => this.handleClick($event, row) }
+                on-mouseenter={ _ => this.handleMouseEnter($index) }
+                class={{
+                  'current-row': row === this.$parent.$parent.selected,
+                  'hover': this.$parent.$parent.hoverRowIndex === $index,
+                  'positive-row': row.$positive,
+                  'info-row': row.$info,
+                  'warning-row': row.$warning,
+                  'negative-row': row.$negative
+                }}>
+                {
+                  this._l(this.columns, (column) =>
+                    <td
+                      class={ column.id }
+                      on-mouseenter={ ($event) => this.handleCellMouseEnter($event, row) }
+                      on-mouseleave={ this.handleCellMouseLeave }>
+                      {
+                        column.template
+                          ? column.template.call(this._renderProxy, h, { row, column, $index })
+                          : <div class="cell">{ this.$getPropertyText(row, column.property, column.id) }</div>
+                      }
+                    </td>
+                  ).concat(this.fixed ? <td class="gutter" /> : '')
+                }
+              </tr>
+            )
+          }
+        </tbody>
+      </table>
+    );
+  },
+
+  data() {
+    return {
+      tooltipDisabled: true
+    };
+  },
+
+  filters: {
+    orderBy
+  },
+
+  methods: {
+    handleCellMouseEnter(event, row) {
+      let grid = this.$parent;
+      const cell = getCell(event);
+
+      if (cell) {
+        const column = getColumnByCell(grid, cell);
+        const hoverState = grid.hoverState = { cell: cell, column: column, row: row };
+        grid.$emit('cellmouseenter', hoverState.row, hoverState.column, hoverState.cell, event);
+      }
+
+      // 判断是否text-overflow, 如果是就显示tooltip
+      const cellChild = getChild(event);
+
+      if (cellChild.scrollWidth > cellChild.offsetWidth) {
+        this.tooltipDisabled = false;
+      } else {
+        this.tooltipDisabled = true;
+      }
+    },
+
+    handleCellMouseLeave(event) {
+      let grid = this.$parent;
+      const cell = getCell(event);
+
+      if (cell) {
+        const oldHoverState = grid.hoverState;
+        grid.$emit('cellmouseleave', oldHoverState.row, oldHoverState.column, oldHoverState.cell, event);
+      }
+    },
+
+    handleMouseEnter(index) {
+      this.$parent.hoverRowIndex = index;
+    },
+
+    handleClick(event, row) {
+      let grid = this.$parent;
+      const cell = getCell(event);
+
+      if (cell) {
+        const column = getColumnByCell(grid, cell);
+        if (column) {
+          grid.$emit('cellclick', row, column, cell, event);
+        }
+      }
+
+      if (grid.selectionMode === 'single') {
+        grid.selected = row;
+        grid.$emit('selectionchange', row);
+      }
+
+      grid.$emit('rowclick', row, event);
+    },
+
+    handleCreate(vm) {
+      document.body.appendChild(vm.$refs.popper);
+      vm.updatePopper();
+    },
+
+    $getPropertyText(row, property, columnId) {
+      let grid = this.$parent;
+      const column = getColumnById(grid, columnId);
+      if (column && column.formatter) {
+        return column.formatter(row, column);
+      }
+
+      return getValueByPath(row, property);
+    }
+  }
+};

+ 0 - 142
packages/table/src/table-body.vue

@@ -1,142 +0,0 @@
-<template>
-  <table class="el-table__body" cellspacing="0" cellpadding="0" border="0">
-    <tbody>
-    <tr v-for="row in data"
-      @click="handleClick($event, row)" @mouseenter="handleMouseEnter($index)"
-      :class="{
-        'current-row': row === $parent.$parent.selected,
-        hover: $parent.$parent.hoverRowIndex === $index,
-        'positive-row': row.$positive,
-        'info-row': row.$info,
-        'warning-row': row.$warning,
-        'negative-row': row.$negative
-      }">
-      <td v-for="column in columns" :class="column.id"
-          @mouseenter="handleCellMouseEnter($event, row)"
-          @mouseleave="handleCellMouseLeave($event)">
-        <partial v-if="column.template" :name="'template:' + column.id"></partial>
-        <partial v-else name="template:default"></partial>
-      </td>
-      <td class="gutter" v-if="!fixed"></td>
-    </tr>
-    </tbody>
-  </table>
-</template>
-
-<script type="text/babel">
-  const getColumnById = function(grid, columnId) {
-    let column = null;
-    grid.columns.forEach(function(item) {
-      if (item.id === columnId) {
-        column = item;
-      }
-    });
-    return column;
-  };
-
-  const getColumnByCell = function(grid, cell) {
-    const matches = (cell.className || '').match(/grid_[^\s]+/gm);
-    if (matches) {
-      return getColumnById(grid, matches[0]);
-    }
-    return null;
-  };
-
-  import { getValueByPath, getCell, orderBy, getChild } from './util';
-
-  export default {
-    props: {
-      columns: {},
-      data: {},
-      fixed: {},
-      selection: {
-        default() {
-          return [];
-        }
-      }
-    },
-
-    data() {
-      return {
-        tooltipDisabled: true
-      };
-    },
-
-    filters: {
-      orderBy
-    },
-
-    partials: {
-      'template:default': '<div class="cell">{{ $getPropertyText(row, column.property, column.id) }}</div>'
-    },
-
-    methods: {
-      handleCellMouseEnter(event, row) {
-        let grid = this.$parent;
-        const cell = getCell(event);
-
-        if (cell) {
-          const column = getColumnByCell(grid, cell);
-          const hoverState = grid.hoverState = { cell: cell, column: column, row: row };
-          grid.$emit('cell-mouse-enter', hoverState.row, hoverState.column, hoverState.cell, event);
-        }
-
-        // 判断是否text-overflow, 如果是就显示tooltip
-        const cellChild = getChild(event);
-        if (cellChild.scrollWidth > cellChild.offsetWidth) {
-          this.tooltipDisabled = false;
-        } else {
-          this.tooltipDisabled = true;
-        }
-      },
-
-      handleCellMouseLeave(event) {
-        let grid = this.$parent;
-        const cell = getCell(event);
-
-        if (cell) {
-          const oldHoverState = grid.hoverState;
-          grid.$emit('cell-mouse-leave', oldHoverState.row, oldHoverState.column, oldHoverState.cell, event);
-        }
-      },
-
-      handleMouseEnter(index) {
-        this.$parent.hoverRowIndex = index;
-      },
-
-      handleClick(event, row) {
-        let grid = this.$parent;
-        const cell = getCell(event);
-
-        if (cell) {
-          const column = getColumnByCell(grid, cell);
-          if (column) {
-            grid.$emit('cell-click', row, column, cell, event);
-          }
-        }
-
-        if (grid.selectionMode === 'single') {
-          grid.selected = row;
-          grid.$emit('selection-change', row);
-        }
-
-        grid.$emit('row-click', row, event);
-      },
-
-      handleCreate(vm) {
-        document.body.appendChild(vm.popper);
-        vm.updatePopper();
-      },
-
-      $getPropertyText(row, property, columnId) {
-        let grid = this.$parent;
-        const column = getColumnById(grid, columnId);
-        if (column && column.formatter) {
-          return column.formatter(row, column);
-        }
-
-        return getValueByPath(row, property);
-      }
-    }
-  };
-</script>

+ 255 - 0
packages/table/src/table-column.js

@@ -0,0 +1,255 @@
+import ElCheckbox from 'packages/checkbox/index.js';
+import ElTag from 'packages/tag/index.js';
+import objectAssign from 'object-assign';
+
+let columnIdSeed = 1;
+
+const defaults = {
+  default: {
+    direction: ''
+  },
+  selection: {
+    width: 48,
+    minWidth: 48,
+    realWidth: 48,
+    direction: ''
+  },
+  index: {
+    width: 48,
+    minWidth: 48,
+    realWidth: 48,
+    direction: ''
+  },
+  filter: {
+    headerTemplate: function(h) { return <span>filter header</span>; },
+    direction: ''
+  }
+};
+
+const forced = {
+  selection: {
+    headerTemplate: function(h) { return <div><el-checkbox nativeOn-click={ this.toggleAllSelection } domProps-value={ this.allSelected } on-input={ ($event) => this.$emit('allselectedchange', $event) } /></div>; },
+    template: function(h, { row }) { return <el-checkbox domProps-value={ row.$selected } on-input={ ($event) => {row.$selected = $event;} } />; },
+    sortable: false,
+    resizable: false
+  },
+  index: {
+    headerTemplate: function(h) { return <div>#</div>; },
+    template: function(h, { row, $index }) { return <div>{ $index + 1 }</div>; },
+    sortable: false
+  },
+  filter: {
+    headerTemplate: function(h) { return <div>#</div>; },
+    template: function(h, { row, column }) { return <el-tag type="primary" style="height: 16px; line-height: 16px; min-width: 40px; text-align: center">{ row[column.property] }</el-tag>; },
+    resizable: false
+  }
+};
+
+const getDefaultColumn = function(type, options) {
+  const column = {};
+
+  objectAssign(column, defaults[type || 'default']);
+
+  for (let name in options) {
+    if (options.hasOwnProperty(name)) {
+      const value = options[name];
+      if (typeof value !== 'undefined') {
+        column[name] = value;
+      }
+    }
+  }
+
+  return column;
+};
+
+export default {
+  name: 'el-table-column',
+
+  props: {
+    type: {
+      type: String,
+      default: 'default'
+    },
+    label: String,
+    property: String,
+    width: {},
+    minWidth: {},
+    template: String,
+    sortable: {
+      type: Boolean,
+      default: false
+    },
+    resizable: {
+      type: Boolean,
+      default: true
+    },
+    showTooltipWhenOverflow: Boolean,
+    formatter: Function
+  },
+
+  render(h) {
+    return <div />;
+  },
+
+  data() {
+    return {
+      isChildColumn: false,
+      columns: [],
+      row: {}
+    };
+  },
+
+  components: {
+    ElCheckbox,
+    ElTag
+  },
+
+  created() {
+    let columnId = this.columnId = (this.$parent.gridId || (this.$parent.columnId + '_')) + 'column_' + columnIdSeed++;
+
+    let parent = this.$parent;
+    if (!parent.gridId) {
+      this.isChildColumn = true;
+    }
+
+    let type = this.type;
+
+    let width = this.width;
+    if (width !== undefined) {
+      width = parseInt(width, 10);
+      if (isNaN(width)) {
+        width = null;
+      }
+    }
+
+    let minWidth = this.minWidth;
+    if (minWidth !== undefined) {
+      minWidth = parseInt(minWidth, 10);
+      if (isNaN(minWidth)) {
+        minWidth = 80;
+      }
+    } else {
+      minWidth = 80;
+    }
+
+    let isColumnGroup = false;
+    let template;
+
+    let property = this.property;
+    if (property) {
+      template = function(h, { row }) {
+        return <span>{ this.$getPropertyText(row, property, columnId) }</span>;
+      };
+    }
+
+    let column = getDefaultColumn(type, {
+      id: columnId,
+      label: this.label,
+      property: this.property,
+      type,
+      template,
+      minWidth,
+      width,
+      isColumnGroup,
+      realWidth: width || minWidth,
+      sortable: this.sortable,
+      resizable: this.resizable,
+      formatter: this.formatter
+    });
+
+    objectAssign(column, forced[type] || {});
+
+    let renderColumn = column.template;
+    let _self = this;
+    column.template = function(h, data) {
+      if (_self.$vnode.data.inlineTemplate) {
+        let costomRender = _self.$options.render;
+
+        renderColumn = function(_h) {
+          return costomRender.call(data, _h);
+        };
+      };
+
+      return _self.showTooltipWhenOverflow
+        ? <el-tooltip
+            on-created={ this.handleCreate }
+            effect={ this.effect }
+            placement="top"
+            disabled={ this.tooltipDisabled }>
+            <div class="cell">{ renderColumn.call(this._renderProxy, h, data) }</div>
+            <span slot="content">{ renderColumn.call(this._renderProxy, h, data) }</span>
+          </el-tooltip>
+        : <div class="cell">{ renderColumn.call(this._renderProxy, h, data) }</div>;
+    };
+
+    this.columnConfig = column;
+  },
+
+  destroyed() {
+    if (!this.$parent) {
+      return;
+    }
+    let columns = this.$parent.columns;
+    if (columns) {
+      let columnId = this.columnId;
+      for (let i = 0, j = columns.length; i < j; i++) {
+        let column = columns[i];
+
+        if (column.id === columnId) {
+          columns.splice(i, 1);
+          break;
+        }
+      }
+    }
+
+    if (this.isChildColumn) {
+      if (this.$parent.$parent.$ready) {
+        this.$parent.$parent.debouncedReRender();
+      }
+    } else {
+      if (this.$parent.$ready) {
+        this.$parent.debouncedReRender();
+      }
+    }
+  },
+
+  watch: {
+    label(newVal) {
+      if (this.columnConfig) {
+        this.columnConfig.label = newVal;
+      }
+    },
+
+    property(newVal) {
+      if (this.columnConfig) {
+        this.columnConfig.property = newVal;
+      }
+    }
+  },
+
+  mounted() {
+    let parent = this.$parent;
+    let columnConfig = this.columnConfig;
+    let columnIndex;
+
+    if (!this.isChildColumn) {
+      columnIndex = [].indexOf.call(parent.$refs.hiddenColumns.children, this.$el);
+    } else {
+      columnIndex = [].indexOf.call(parent.$el.children, this.$el);
+    }
+
+    parent.columns.splice(columnIndex, 0, columnConfig);
+
+    if (this.isChildColumn) {
+      parent.columnConfig.columns = parent.columns;
+
+      if (parent.$parent.$ready) {
+        parent.$parent.debouncedReRender();
+      }
+    } else {
+      if (parent.$ready) {
+        parent.debouncedReRender();
+      }
+    }
+  }
+};

+ 0 - 254
packages/table/src/table-column.vue

@@ -1,254 +0,0 @@
-<template>
-  <div><slot></slot></div>
-</template>
-
-<script type="text/babel">
-  import ElCheckbox from 'packages/checkbox/index.js';
-  import ElTag from 'packages/tag/index.js';
-
-  let columnIdSeed = 1;
-
-  const defaults = {
-    default: {
-      direction: ''
-    },
-    selection: {
-      width: 48,
-      minWidth: 48,
-      realWidth: 48,
-      direction: ''
-    },
-    index: {
-      width: 48,
-      minWidth: 48,
-      realWidth: 48,
-      direction: ''
-    },
-    filter: {
-      headerTemplate: 'filter header',
-      direction: ''
-    }
-  };
-
-  const forced = {
-    selection: {
-      // TODO :value.sync="$parent.$parent.selection"
-      headerTemplate: '<div><el-checkbox @click="toggleAllSelection($event)" :value.sync="allSelected"></el-checkbox></div>',
-      template: '<el-checkbox :value.sync="row.$selected"></el-checkbox>',
-      sortable: false,
-      resizable: false
-    },
-    index: {
-      headerTemplate: '<div>#</div>',
-      template: '{{ $parent.$index + 1 }}',
-      sortable: false
-    },
-    filter: {
-      headerTemplate: '<div>#</div>',
-      template: '<el-tag type="primary" style="height: 16px; line-height: 16px; min-width: 40px; text-align: center">{{ row[column.property] }}</el-tag>',
-      resizable: false
-    }
-  };
-
-  const getDefaultColumn = function(type, options) {
-    const column = {};
-
-    Object.assign(column, defaults[type || 'default']);
-
-    for (let name in options) {
-      if (options.hasOwnProperty(name)) {
-        const value = options[name];
-        if (typeof value !== 'undefined') {
-          column[name] = value;
-        }
-      }
-    }
-
-    return column;
-  };
-
-  import Vue from 'vue';
-
-  export default {
-    name: 'el-table-column',
-
-    props: {
-      type: {
-        type: String,
-        default: 'default'
-      },
-      label: String,
-      property: String,
-      width: {},
-      minWidth: {},
-      template: String,
-      sortable: {
-        type: Boolean,
-        default: false
-      },
-      resizable: {
-        type: Boolean,
-        default: true
-      },
-      formatter: Function
-    },
-
-    data() {
-      return {
-        isChildColumn: false,
-        columns: []
-      };
-    },
-
-    components: {
-      ElCheckbox,
-      ElTag
-    },
-
-    beforeCompile() {
-      let columnId = this.columnId = (this.$parent.gridId || (this.$parent.columnId + '_')) + 'column_' + columnIdSeed++;
-
-      let parent = this.$parent;
-      if (!parent.gridId) {
-        this.isChildColumn = true;
-      }
-
-      let type = this.type;
-
-      let width = this.width;
-      if (width !== undefined) {
-        width = parseInt(width, 10);
-        if (isNaN(width)) {
-          width = null;
-        }
-      }
-
-      let minWidth = this.minWidth;
-      if (minWidth !== undefined) {
-        minWidth = parseInt(minWidth, 10);
-        if (isNaN(minWidth)) {
-          minWidth = 80;
-        }
-      } else {
-        minWidth = 80;
-      }
-
-      let options = this.$options;
-      let tagName = options.el.tagName.toLowerCase();
-      let isColumnGroup = false;
-      let template = this.template;
-
-      if (options._content) {
-        let content = options._content.innerHTML;
-        if (content.indexOf(`</${tagName}>`) === -1) {
-          options._content = null;
-          template = content;
-        } else {
-          template = null;
-          isColumnGroup = true;
-        }
-      }
-
-      let property = this.property;
-      if ((!template || /^\s*$/.test(template)) && property) {
-        template = `{{ $getPropertyText(row, '${property}', '${columnId}') }}`;
-      }
-
-      let column = getDefaultColumn(type, {
-        id: columnId,
-        label: this.label,
-        property: this.property,
-        type,
-        template,
-        minWidth,
-        width,
-        isColumnGroup,
-        realWidth: width || minWidth,
-        sortable: this.sortable,
-        resizable: this.resizable,
-        formatter: this.formatter
-      });
-
-      Object.assign(column, forced[type] || {});
-
-      if (column.headerTemplate) {
-        Vue.partial('headerTemplate:' + column.id, column.headerTemplate);
-      }
-
-      if (column.template) {
-//        Vue.partial('template:' + column.id, `<div class="cell">${column.template}</div>`);
-        Vue.partial('template:' + column.id, `<el-tooltip @created="handleCreate" :effect="effect" placement="top" :disabled="tooltipDisabled"><div class="cell">${column.template}</div><span slot="content">${column.template}</span></el-tooltip>`);
-      }
-
-      this.columnConfig = column;
-    },
-
-    detached() {
-      if (!this.$parent) {
-        return;
-      }
-      let columns = this.$parent.columns;
-      if (columns) {
-        let columnId = this.columnId;
-        for (let i = 0, j = columns.length; i < j; i++) {
-          let column = columns[i];
-
-          if (column.id === columnId) {
-            columns.splice(i, 1);
-            break;
-          }
-        }
-      }
-
-      if (this.isChildColumn) {
-        if (this.$parent.$parent.$ready) {
-          this.$parent.$parent.debouncedReRender();
-        }
-      } else {
-        if (this.$parent.$ready) {
-          this.$parent.debouncedReRender();
-        }
-      }
-    },
-
-    watch: {
-      label(newVal) {
-        if (this.columnConfig) {
-          this.columnConfig.label = newVal;
-        }
-      },
-
-      property(newVal) {
-        if (this.columnConfig) {
-          this.columnConfig.property = newVal;
-        }
-      }
-    },
-
-    ready() {
-      let parent = this.$parent;
-      let columnConfig = this.columnConfig;
-      let columnIndex;
-
-      if (!this.isChildColumn) {
-        columnIndex = [].indexOf.call(parent.$els.hiddenColumns.children, this.$el);
-      } else {
-        columnIndex = [].indexOf.call(parent.$el.children, this.$el);
-      }
-
-      parent.columns.splice(columnIndex, 0, columnConfig);
-
-      if (this.isChildColumn) {
-        parent.columnConfig.columns = parent.columns;
-
-        if (parent.$parent.$ready) {
-          parent.$parent.debouncedReRender();
-        }
-      } else {
-        if (parent.$ready) {
-          parent.debouncedReRender();
-        }
-      }
-    }
-  };
-</script>

+ 232 - 0
packages/table/src/table-header.js

@@ -0,0 +1,232 @@
+import ElCheckbox from 'packages/checkbox/index.js';
+import ElTag from 'packages/tag/index.js';
+
+export default {
+  name: 'el-table-header',
+
+  render(h) {
+    return (
+      <table
+        class="el-table__header"
+        cellspacing="0"
+        cellpadding="0"
+        border="0">
+        {
+          this._l(this.columns, column =>
+            <colgroup
+              name={ column.id }
+              width={ column.realWidth || column.width }
+            />).concat(
+              <thead>
+                <tr>
+                  {
+                    this._l(this.columns, column =>
+                      <th
+                        on-mousemove={ ($event) => this.handleMouseMove($event, column) }
+                        on-mouseout={ this.handleMouseOut }
+                        on-mousedown={ ($event) => this.handleMouseDown($event, column) }
+                        on-click={ ($event) => this.handleHeaderClick($event, column) }
+                        class={ [column.id, column.direction] }>
+                        {
+                          [
+                            column.headerTemplate
+                              ? column.headerTemplate.call(this._renderProxy, h)
+                              : <div>{ column.label }</div>,
+                            column.sortable
+                              ? <div class="caret-wrapper">
+                                  <i class="sort-caret ascending"></i>
+                                  <i class="sort-caret descending"></i>
+                                </div>
+                              : ''
+                          ]
+                        }
+                      </th>
+                    ).concat(<th
+                              class="gutter"
+                              style={{
+                                width: (this.$parent.showVScrollBar
+                                          ? this.$parent.gutterWidth
+                                          : 0
+                                        ) + 'px'
+                              }}>&nbsp;</th>)
+                  }
+                </tr>
+              </thead>
+            )
+        }
+      </table>
+    );
+  },
+
+  props: {
+    columns: {},
+    fixed: Boolean,
+    allSelected: {
+      default: Boolean
+    },
+    border: Boolean
+  },
+
+  components: {
+    ElCheckbox,
+    ElTag
+  },
+
+  methods: {
+    toggleAllSelection($event) {
+      this.$parent.toggleAllSelection($event);
+    },
+
+    handleMouseDown(event, column) {
+      if (this.draggingColumn && this.border) {
+        this.dragging = true;
+
+        this.$parent.resizeProxyVisible = true;
+
+        const gridEl = this.$parent.$el;
+        const gridLeft = gridEl.getBoundingClientRect().left;
+        const columnEl = this.$el.querySelector(`th.${column.id}`);
+        const columnRect = columnEl.getBoundingClientRect();
+        const minLeft = columnRect.left - gridLeft + 30;
+
+        columnEl.classList.add('noclick');
+
+        this.dragState = {
+          startMouseLeft: event.clientX,
+          startLeft: columnRect.right - gridLeft,
+          startColumnLeft: columnRect.left - gridLeft,
+          gridLeft: gridLeft
+        };
+
+        const resizeProxy = this.$parent.$refs.resizeProxy;
+        resizeProxy.style.left = this.dragState.startLeft + 'px';
+
+        document.onselectstart = function() { return false; };
+        document.ondragstart = function() { return false; };
+
+        const mousemove = (event) => {
+          const deltaLeft = event.clientX - this.dragState.startMouseLeft;
+          const proxyLeft = this.dragState.startLeft + deltaLeft;
+
+          resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px';
+        };
+
+        const mouseup = () => {
+          if (this.dragging) {
+            const finalLeft = parseInt(resizeProxy.style.left, 10);
+            const columnWidth = finalLeft - this.dragState.startColumnLeft;
+            column.width = column.realWidth = columnWidth;
+
+            this.$nextTick(() => {
+              this.$parent.$calcColumns();
+            });
+
+            document.body.style.cursor = '';
+            this.dragging = false;
+            this.draggingColumn = null;
+            this.dragState = {};
+
+            this.$parent.resizeProxyVisible = false;
+          }
+
+          document.removeEventListener('mousemove', mousemove);
+          document.removeEventListener('mouseup', mouseup);
+          document.onselectstart = null;
+          document.ondragstart = null;
+
+          setTimeout(function() {
+            columnEl.classList.remove('noclick');
+          }, 0);
+        };
+
+        document.addEventListener('mousemove', mousemove);
+        document.addEventListener('mouseup', mouseup);
+      }
+    },
+
+    handleMouseMove(event, column) {
+      const target = event.target;
+
+      if (!column || !column.resizable) return;
+
+      if (!this.dragging && this.border) {
+        let rect = target.getBoundingClientRect();
+
+        if (rect.width > 12 && rect.right - event.pageX < 8) {
+          document.body.style.cursor = 'col-resize';
+          this.draggingColumn = column;
+        } else if (!this.dragging) {
+          document.body.style.cursor = '';
+          this.draggingColumn = null;
+          if (column.sortable) document.body.style.cursor = 'pointer';
+        }
+      }
+    },
+
+    handleMouseOut() {
+      document.body.style.cursor = '';
+    },
+
+    handleHeaderClick(event, column) {
+      let target = event.target;
+      while (target && target.tagName !== 'TH') {
+        target = target.parentNode;
+      }
+
+      if (target && target.tagName === 'TH') {
+        if (target.classList.contains('noclick')) {
+          target.classList.remove('noclick');
+          return;
+        }
+      }
+
+      if (!column.sortable) return;
+
+      const grid = this.$parent;
+
+      if (grid.sortingColumn !== column) {
+        if (grid.sortingColumn) {
+          grid.sortingColumn.direction = '';
+        }
+        grid.sortingColumn = column;
+        grid.sortingProperty = column.property;
+      }
+
+      if (!column.direction) {
+        column.direction = 'ascending';
+      } else if (column.direction === 'ascending') {
+        column.direction = 'descending';
+      } else {
+        column.direction = '';
+        grid.sortingColumn = null;
+        grid.sortingProperty = null;
+      }
+
+      grid.sortingDirection = column.direction === 'descending' ? -1 : 1;
+    },
+
+    $setVisibleFilter(property) {
+      if (this.visibleFilter) {
+        this.visibleFilter = null;
+      } else {
+        this.visibleFilter = property;
+      }
+    }
+  },
+
+  watch: {
+    visibleFilter(val) {
+      this.$parent.visibleFilter = val;
+    }
+  },
+
+  data() {
+    return {
+      draggingColumn: null,
+      dragging: false,
+      dragState: {},
+      columnsMap: null,
+      visibleFilter: null
+    };
+  }
+};

+ 0 - 198
packages/table/src/table-header.vue

@@ -1,198 +0,0 @@
-<template>
-  <table class="el-table__header" cellspacing="0" cellpadding="0" border="0">
-    <colgroup v-for="column in columns" :name="column.id" :width="column.realWidth || column.width"></colgroup>
-    <thead>
-      <tr>
-        <th v-for="column in columns"
-          @mousemove="handleMouseMove($event, column)"
-          @mouseout="handleMouseOut"
-          @mousedown="handleMouseDown($event, column)"
-          @click="handleHeaderClick($event, column)"
-          class="{{ column.id }} {{column.direction}}">
-          <partial v-if="column.headerTemplate" :name="'headerTemplate:' + column.id"></partial>
-          <partial v-else name="default"></partial><div class="caret-wrapper" v-if="column.sortable"><i class="sort-caret ascending"></i><i class="sort-caret descending"></i></div>
-        </th>
-        <th class="gutter" :style="{ width: ($parent.showVScrollBar ? $parent.gutterWidth : 0) + 'px' }">&nbsp;</th>
-      </tr>
-    </thead>
-  </table>
-</template>
-
-<script type="text/babel">
-  import Vue from 'vue';
-
-  export default {
-    name: 'el-table-header',
-
-    props: {
-      columns: {},
-      fixed: Boolean,
-      allSelected: {
-        default: Boolean
-      },
-      border: Boolean
-    },
-
-    partials: {
-      default: '<div>{{column.label}}</div>'
-    },
-
-    methods: {
-      toggleAllSelection($event) {
-        this.$parent.toggleAllSelection($event);
-      },
-
-      handleMouseDown(event, column) {
-        if (this.draggingColumn && this.border) {
-          this.dragging = true;
-
-          this.$parent.resizeProxyVisible = true;
-
-          const gridEl = this.$parent.$el;
-          const gridLeft = gridEl.getBoundingClientRect().left;
-          const columnEl = this.$el.querySelector(`th.${column.id}`);
-          const columnRect = columnEl.getBoundingClientRect();
-          const minLeft = columnRect.left - gridLeft + 30;
-
-          columnEl.classList.add('noclick');
-
-          this.dragState = {
-            startMouseLeft: event.clientX,
-            startLeft: columnRect.right - gridLeft,
-            startColumnLeft: columnRect.left - gridLeft,
-            gridLeft: gridLeft
-          };
-
-          const resizeProxy = this.$parent.$els.resizeProxy;
-          resizeProxy.style.left = this.dragState.startLeft + 'px';
-
-          document.onselectstart = function() { return false; };
-          document.ondragstart = function() { return false; };
-
-          const mousemove = (event) => {
-            const deltaLeft = event.clientX - this.dragState.startMouseLeft;
-            const proxyLeft = this.dragState.startLeft + deltaLeft;
-
-            resizeProxy.style.left = Math.max(minLeft, proxyLeft) + 'px';
-          };
-
-          const mouseup = () => {
-            if (this.dragging) {
-              const finalLeft = parseInt(resizeProxy.style.left, 10);
-              const columnWidth = finalLeft - this.dragState.startColumnLeft;
-              column.width = column.realWidth = columnWidth;
-
-              Vue.nextTick(() => {
-                this.$parent.$calcColumns();
-              });
-
-              document.body.style.cursor = '';
-              this.dragging = false;
-              this.draggingColumn = null;
-              this.dragState = {};
-
-              this.$parent.resizeProxyVisible = false;
-            }
-
-            document.removeEventListener('mousemove', mousemove);
-            document.removeEventListener('mouseup', mouseup);
-            document.onselectstart = null;
-            document.ondragstart = null;
-
-            setTimeout(function() {
-              columnEl.classList.remove('noclick');
-            }, 0);
-          };
-
-          document.addEventListener('mousemove', mousemove);
-          document.addEventListener('mouseup', mouseup);
-        }
-      },
-
-      handleMouseMove(event, column) {
-        const target = event.target;
-
-        if (!column || !column.resizable) return;
-
-        if (!this.dragging && this.border) {
-          let rect = target.getBoundingClientRect();
-
-          if (rect.width > 12 && rect.right - event.pageX < 8) {
-            document.body.style.cursor = 'col-resize';
-            this.draggingColumn = column;
-          } else if (!this.dragging) {
-            document.body.style.cursor = '';
-            this.draggingColumn = null;
-            if (column.sortable) document.body.style.cursor = 'pointer';
-          }
-        }
-      },
-
-      handleMouseOut() {
-        document.body.style.cursor = '';
-      },
-
-      handleHeaderClick(event, column) {
-        let target = event.target;
-        while (target && target.tagName !== 'TH') {
-          target = target.parentNode;
-        }
-
-        if (target && target.tagName === 'TH') {
-          if (target.classList.contains('noclick')) {
-            target.classList.remove('noclick');
-            return;
-          }
-        }
-
-        if (!column.sortable) return;
-
-        const grid = this.$parent;
-
-        if (grid.sortingColumn !== column) {
-          if (grid.sortingColumn) {
-            grid.sortingColumn.direction = '';
-          }
-          grid.sortingColumn = column;
-          grid.sortingProperty = column.property;
-        }
-
-        if (!column.direction) {
-          column.direction = 'ascending';
-        } else if (column.direction === 'ascending') {
-          column.direction = 'descending';
-        } else {
-          column.direction = '';
-          grid.sortingColumn = null;
-          grid.sortingProperty = null;
-        }
-
-        grid.sortingDirection = column.direction === 'descending' ? -1 : 1;
-      },
-
-      $setVisibleFilter(property) {
-        if (this.visibleFilter) {
-          this.visibleFilter = null;
-        } else {
-          this.visibleFilter = property;
-        }
-      }
-    },
-
-    watch: {
-      visibleFilter(val) {
-        this.$parent.visibleFilter = val;
-      }
-    },
-
-    data() {
-      return {
-        draggingColumn: null,
-        dragging: false,
-        dragState: {},
-        columnsMap: null,
-        visibleFilter: null
-      };
-    }
-  };
-</script>

+ 49 - 55
packages/table/src/table.vue

@@ -1,36 +1,36 @@
 <template>
   <div class="el-table" :class="{ 'el-table--fit': fit, 'el-table--striped': stripe, 'el-table--border': border }" @mouseleave="handleMouseLeave($event)">
-    <div class="hidden-columns" v-el:hidden-columns><slot></slot></div>
+    <div class="hidden-columns" ref="hiddenColumns"><slot></slot></div>
     <div class="el-table__header-wrapper">
-      <table-header :columns="columns" :all-selected.sync="allSelected" :selection.sync="selection" :style="{ width: bodyWidth ? bodyWidth + 'px' : '' }" :border="border"></table-header>
+      <table-header :columns="columns" :all-selected="allSelected" @allselectedchange="handleAllSelectedChange" :selection="selection" :style="{ width: bodyWidth ? bodyWidth + 'px' : '' }" :border="border"></table-header>
     </div>
     <div class="el-table__body-wrapper">
-      <table-body :columns="columns" :selection.sync="selection" :data="data | orderBy sortingProperty sortingDirection" :style="{ width: bodyWidth ? bodyWidth - (showVScrollBar ? gutterWidth : 0 ) + 'px' : '' }"></table-body>
+      <table-body :columns="columns" :selection="selection" :data="filterData" :style="{ width: bodyWidth ? bodyWidth - (showVScrollBar ? gutterWidth : 0 ) + 'px' : '' }"></table-body>
     </div>
-    <div class="el-table__fixed" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }" v-el:fixed>
+    <div class="el-table__fixed" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }" ref="fixed">
       <div class="el-table__fixed-header-wrapper" v-if="fixedColumnCount > 0">
-        <table-header :columns="fixedColumns" :all-selected.sync="allSelected" :selection.sync="selection" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }" :border="border"></table-header>
+        <table-header :columns="fixedColumns" :all-selected="allSelected" @allselectedchange="handleAllSelectedChange" :selection="selection" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }" :border="border"></table-header>
       </div>
       <div class="el-table__fixed-body-wrapper" v-if="fixedColumnCount > 0" :style="{ top: headerHeight + 'px' }">
-        <table-body :columns="fixedColumns" fixed :selection.sync="selection" :data="data | orderBy sortingProperty sortingDirection" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }"></table-body>
+        <table-body :columns="fixedColumns" fixed :selection="selection" :data="filterData" :style="{ width: fixedBodyWidth ? fixedBodyWidth + 'px' : '' }"></table-body>
       </div>
     </div>
-    <div class="el-table__column-resize-proxy" v-el:resize-proxy v-show="resizeProxyVisible"></div>
+    <div class="el-table__column-resize-proxy" ref="resizeProxy" v-show="resizeProxyVisible"></div>
     <slot name="bottom"></slot>
   </div>
 </template>
 
 <script type="text/babel">
-  import Vue from 'vue';
   import throttle from 'throttle-debounce/throttle';
   import debounce from 'throttle-debounce/debounce';
-  import { getScrollBarWidth } from './util';
+  import { getScrollBarWidth, orderBy } from './util';
+  import objectAssign from 'object-assign';
 
   let gridIdSeed = 1;
   let GUTTER_WIDTH;
 
-  import TableBody from './table-body.vue';
-  import TableHeader from './table-header.vue';
+  import TableBody from './table-body';
+  import TableHeader from './table-header';
 
   export default {
     name: 'el-table',
@@ -84,24 +84,16 @@
       }
     },
 
-    events: {
-      onresize() {
-        Vue.nextTick(() => {
-          this.$calcColumns();
-        });
-      }
-    },
-
-    partials: {
-      default: '<div>{{column.label}}</div>'
-    },
-
     components: {
       TableHeader,
       TableBody
     },
 
     methods: {
+      handleAllSelectedChange(val) {
+        this.allSelected = val;
+      },
+
       doOnDataChange(data) {
         data = data || [];
 
@@ -111,7 +103,7 @@
             if (!this.allowNoSelection) {
               this.selected = data[0];
               if (this.selected !== oldSelection) {
-                this.$emit('selection-change', this.selected);
+                this.$emit('selectionchange', this.selected);
               }
             }
           } else if (data.indexOf(oldSelection) === -1) {
@@ -121,7 +113,7 @@
               this.selected = null;
             }
             if (this.selected !== oldSelection) {
-              this.$emit('selection-change', this.selected);
+              this.$emit('selectionchange', this.selected);
             }
           }
         }
@@ -129,7 +121,7 @@
 
       toggleAllSelection() {
         setTimeout(() => {
-          this.data.forEach(item => {
+          this.tableData.forEach(item => {
             item.$selected = this.allSelected;
           });
         }, 0);
@@ -241,7 +233,7 @@
           this.fixedBodyWidth = fixedBodyWidth;
         }
 
-        Vue.nextTick(() => {
+        this.$nextTick(() => {
           this.headerHeight = this.$el.querySelector('.el-table__header-wrapper').offsetHeight;
         });
       },
@@ -258,7 +250,7 @@
           gridWrapper.style.height = bodyHeight + 'px';
 
           this.$el.style.height = height + 'px';
-          this.$els.fixed.style.height = height + 'px';
+          this.$refs.fixed.style.height = height + 'px';
 
           const fixedBodyWrapper = this.$el.querySelector('.el-table__fixed-body-wrapper');
           if (fixedBodyWrapper) {
@@ -276,7 +268,7 @@
       },
 
       updateScrollInfo() {
-        Vue.nextTick(() => {
+        this.$nextTick(() => {
           if (this.$el) {
             let gridBodyWrapper = this.$el.querySelector('.el-table__body-wrapper');
             let gridBody = this.$el.querySelector('.el-table__body-wrapper .el-table__body');
@@ -310,7 +302,7 @@
           window.addEventListener('resize', this.windowResizeListener);
         }
 
-        Vue.nextTick(() => {
+        this.$nextTick(() => {
           if (this.height) {
             this.$calcHeight(this.height);
           }
@@ -319,6 +311,7 @@
     },
 
     created() {
+      this.tableData = this.data;
       this.gridId = 'grid_' + gridIdSeed + '_';
 
       if (GUTTER_WIDTH === undefined) {
@@ -334,10 +327,8 @@
     computed: {
       selection() {
         if (this.selectionMode === 'multiple') {
-          const data = this.data || [];
-          return data.filter(function(item) {
-            return item.$selected === true;
-          });
+          const data = this.tableData || [];
+          return data.filter(item => item.$selected === true);
         } else if (this.selectionMode === 'single') {
           return this.selected;
         } else {
@@ -351,6 +342,10 @@
         return columns.filter(function(item, index) {
           return index < fixedColumnCount;
         });
+      },
+
+      filterData() {
+        return orderBy(this.tableData, this.sortingProperty, this.sortingDirection);
       }
     },
 
@@ -360,9 +355,9 @@
       },
 
       selection(val) {
-        this.$emit('selection-change', val);
+        this.$emit('selectionchange', val);
         if (this.selectionMode === 'multiple') {
-          this.allSelected = val.length === this.data.length;
+          this.allSelected = val.length === this.tableData.length;
         }
       },
 
@@ -374,26 +369,12 @@
         this.$calcHeight(value);
       },
 
-      data(newVal) {
+      tableData(newVal) {
         this.doOnDataChange(newVal);
         this.updateScrollInfo();
       }
     },
 
-    beforeCompile() {
-      const styleNode = document.createElement('style');
-      styleNode.type = 'text/css';
-      styleNode.rel = 'stylesheet';
-      styleNode.title = 'Grid Column Style';
-      document.getElementsByTagName('head')[0].appendChild(styleNode);
-
-      this.styleNode = styleNode;
-
-      if (this.data && this.selectionMode === 'multiple') {
-        this.data = this.data.map(item => Object.assign({ '$selected': false }, item));
-      }
-    },
-
     destroyed() {
       if (this.styleNode) {
         this.styleNode.parentNode.removeChild(this.styleNode);
@@ -404,23 +385,36 @@
       }
     },
 
-    ready() {
+    mounted() {
+      const styleNode = document.createElement('style');
+      styleNode.type = 'text/css';
+      styleNode.rel = 'stylesheet';
+      styleNode.title = 'Grid Column Style';
+      document.getElementsByTagName('head')[0].appendChild(styleNode);
+
+      this.styleNode = styleNode;
+
+      if (this.tableData && this.selectionMode === 'multiple') {
+        this.tableData = this.tableData.map(item => objectAssign({ '$selected': false }, item));
+      }
+
       this.doRender();
 
       this.$ready = true;
-      if (this.data) {
-        this.doOnDataChange(this.data);
+      if (this.tableData) {
+        this.doOnDataChange(this.tableData);
       }
       this.updateScrollInfo();
       if (this.fixedColumnCount > 0) {
         this.$nextTick(() => {
-          this.$els.fixed.style.height = this.$el.clientHeight + 'px';
+          this.$refs.fixed.style.height = this.$el.clientHeight + 'px';
         });
       }
     },
 
     data() {
       return {
+        tableData: [],
         showHScrollBar: false,
         showVScrollBar: false,
         hoverRowIndex: null,

+ 1 - 7
packages/table/src/util.js

@@ -77,11 +77,5 @@ export const orderBy = function(array, sortKey, reverse) {
 };
 
 export const getChild = function(event) {
-  let cell = event.target;
-
-  while (cell.children.length) {
-    cell = cell.children[0];
-  }
-
-  return cell;
+  return event.target.querySelector('.cell');
 };

+ 23 - 0
packages/theme-default/src/card.css

@@ -0,0 +1,23 @@
+@charset "UTF-8";
+@import "./common/var.css";
+
+@component-namespace el {
+  @b card {
+    border: 1px solid var(--card-border-color);
+    border-radius: var(--card-border-radius);
+    background-color: #fff;
+    overflow: hidden;
+    box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, .12),
+                0px 0px 6px 0px rgba(0, 0, 0, .04);
+
+    @e header {
+      padding: calc(var(--card-padding) - 2) var(--card-padding);
+      border-bottom: 1px solid var(--card-border-color);
+      box-sizing: border-box;
+    }
+
+    @e body {
+      padding: var(--card-padding);
+    }
+  }
+}

+ 6 - 0
packages/theme-default/src/common/var.css

@@ -353,4 +353,10 @@
     --dropdown-menu-box-shadow: 0 0 6px 0 rgba(0,0,0,.12), 0 2px 4px 0 rgba(0,0,0,.12);
     --dropdown-menuItem-hover-fill: #e5e9f2;
     --dropdown-menuItem-hover-color: #475669;
+
+    /*Card
+    --------------------------*/
+    --card-border-color: var(--disabled-border-base);
+    --card-border-radius: 4px;
+    --card-padding: 20px;
 }

+ 1 - 0
packages/theme-default/src/index.css

@@ -32,3 +32,4 @@
 @import "./row.css";
 @import "./col.css";
 @import "./spinner.css";
+@import "./card.css";

+ 0 - 0
packages/date-picker/src/util/fecha.js → src/utils/date.js