Merge branch 'dev' into 1.1

pull/2036/head
杨奕 2016-12-28 15:23:51 +08:00 committed by GitHub
commit d2f02b828f
65 changed files with 1791 additions and 770 deletions

View File

@ -1,5 +1,22 @@
## Changelog
### 1.0.9
*2016-12-27*
- Fixed DatePicker incorrectly triggering input event, #1834
- Fixed Tree reporting `event is undefined` error in Firefox, #1945
- Added `change` event for DatePicker, whose parameter is the formatted value, #1841
- Added `header-align` attribute for Table, #1424
- Fixed single select Table's highlight style not removing when data is removed, #1890
- Fixed filterable Select lagging issue with more options, #1933
- Fixed multiple disabled Select not disabling removing selected options issue, #2001
- Fixed Col style not working in `xs`, #2011
- Added `value` attribute for Tab, #2008
- Fixed InputNumber `change` event incorrectly firing multiple times in some conditions, #1999
- Added `clearable` attribute for DatePicker, #1994
- Fixed Form always passing validation in async mode, #1936
### 1.0.8
*2016-12-20*

View File

@ -1,5 +1,21 @@
## 更新日志
### 1.0.9
*2016-12-27*
- 修复 DatePicker 不能正确触发 input 事件的问题,现在只有当日期改变时才触发,#1834
- 修复 Tree 在 Firefox 下会提示 event is undefined 的问题,#1945
- 新增 DatePicker 的 `change` 事件,返回和输入框一致的格式化后的值,#1841
- 新增 Table 的 `header-align` 属性,#1424
- 修复单选的 Table 在数据移除时,高亮状态仍然存在的问题,#1890
- 修复可搜索的 Select 在选项较多时的卡顿问题,#1933
- 修复多选的 Select 在禁用状态下仍然能够手动删除选中项的问题,#2001
- 修复 Col `xs` 分辨率下样式无效的问题,#2011
- 新增 Tab 组件的 `value` 属性并支持 `v-model` 用法,#2008
- 修复 Input Number 在某些条件下 change 事件被触发多次的问题,#1999
- 新增 DatePicker 的 `clearable` 属性,#1994
- 修复 Form 异步验证时某些条件下总是验证通过的问题,#1936
### 1.0.8
*2016-12-20*

View File

@ -1,6 +1,7 @@
var cooking = require('cooking');
var config = require('./config');
var md = require('markdown-it')();
var CopyWebpackPlugin = require('copy-webpack-plugin');
var striptags = require('./strip-tags');
var slugify = require('transliteration').slugify;
var isProd = process.env.NODE_ENV === 'production';
@ -113,5 +114,8 @@ if (isProd) {
cooking.add('externals.vue-router', 'VueRouter');
}
cooking.add('plugin.CopyWebpackPlugin', new CopyWebpackPlugin([
{ from: 'examples/versions.json' }
]));
cooking.add('vue.preserveWhitespace', false);
module.exports = cooking.resolve();

View File

@ -2,7 +2,7 @@
<footer class="footer">
<div class="container">
<div class="footer-main">
<p class="footer-main-title">Element 1.0 Hydrogen</p>
<p class="footer-main-title">Element {{ version }} Hydrogen</p>
<a href="https://github.com/ElemeFE/element/issues" class="footer-main-link" target="_blank">{{ langConfig.feedback }}</a>
<a href="https://github.com/ElemeFE/element/blob/master/.github/CONTRIBUTING.md" class="footer-main-link" target="_blank">{{ langConfig.contribution }}</a>
</div>
@ -134,8 +134,15 @@
<script type="text/babel">
import compoLang from '../i18n/component.json';
import { version } from 'main/index.js';
export default {
data() {
return {
version
};
},
computed: {
lang() {
return this.$route.path.split('/')[1];

View File

@ -12,6 +12,41 @@
margin: 0;
overflow: hidden;
}
.nav-dropdown {
margin-bottom: 6px;
width: 100%;
span {
display: block;
width: 100%;
font-size: 16px;
color: #5e6d82;
line-height: 40px;
transition: .2s;
border-bottom: 1px solid #eaeefb;
&:hover {
cursor: pointer;
}
}
i {
transition: .2s;
font-size: 12px;
color: #d3dce6;
}
@when active {
span, i {
color: #20a0ff;
}
i {
transform: rotateZ(180deg) translateY(2px);
}
}
&:hover {
span, i {
color: #20a0ff;
}
}
}
.nav-item {
a {
@ -53,9 +88,37 @@
margin-top: 10px;
}
}
.nav-dropdown-list {
width: 120px;
margin-top: -8px;
li {
font-size: 14px;
}
}
</style>
<template>
<div class="side-nav" :style="navStyle">
<el-dropdown
v-show="isComponentPage"
trigger="click"
class="nav-dropdown"
:class="{ 'is-active': dropdownVisible }">
<span>
{{ langConfig.dropdown }}{{ version }}
<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu
slot="dropdown"
:offset="-80"
class="nav-dropdown-list"
@input="handleDropdownToggle">
<el-dropdown-item
v-for="item in Object.keys(versions)"
@click.native="switchVersion(item)">
{{ item }}
</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<ul>
<li class="nav-item" v-for="item in data">
<a v-if="!item.path" @click="expandMenu">{{item.name}}</a>
@ -99,6 +162,9 @@
</div>
</template>
<script>
import compoLang from '../i18n/component.json';
import { version } from 'main/index.js';
export default {
props: {
data: Array,
@ -111,7 +177,10 @@
return {
highlights: [],
navState: [],
isSmallScreen: false
isSmallScreen: false,
versions: [],
version,
dropdownVisible: false
};
},
watch: {
@ -122,9 +191,19 @@
computed: {
navStyle() {
return this.isSmallScreen ? { 'padding-bottom': '60px' } : {};
},
isComponentPage() {
return /^component-/.test(this.$route.name);
},
langConfig() {
return compoLang.filter(config => config.lang === this.$route.meta.lang)[0]['nav'];
}
},
methods: {
switchVersion(version) {
if (version === this.version) return;
location.href = `${ location.origin }/${ this.versions[version] }/${ location.hash } `;
},
handleResize() {
this.isSmallScreen = document.documentElement.clientWidth < 768;
this.handlePathChange();
@ -160,8 +239,21 @@
if (!target.nextElementSibling || target.nextElementSibling.tagName !== 'UL') return;
this.hideAllMenu();
event.currentTarget.nextElementSibling.style.height = 'auto';
},
handleDropdownToggle(visible) {
this.dropdownVisible = visible;
}
},
created() {
const xhr = new XMLHttpRequest();
xhr.onreadystatechange = _ => {
if (xhr.readyState === 4 && xhr.status === 200) {
this.versions = JSON.parse(xhr.responseText);
}
};
xhr.open('GET', '/versions.json');
xhr.send();
},
mounted() {
this.handleResize();
window.addEventListener('resize', this.handleResize);

View File

@ -51,7 +51,7 @@ Just edit `element-variables.css`, e.g. changing the theme color to red:
```
### Build theme
After saving the variable file, use `et` to build your theme. You can activate `watch` mode by adding a parameter `-w`:
After saving the variable file, use `et` to build your theme. You can activate `watch` mode by adding a parameter `-w`. And if you customized the variable file's output, you need to add a parameter `-c` and variable file's name:
```shell
et

View File

@ -256,6 +256,7 @@ Picking a date range is supported.
| disabled | whether DatePicker is disabled | boolean | - | false |
|size | size of Input | string | large/small/mini | — |
| editable | whether the input is editable | boolean | - | true |
| clearable | Whether to show clear button | boolean | - | true |
| placeholder | placeholder | string | — | — |
| type | type of the picker | string | year/month/date/datetime/ week/datetimerange/daterange | date |
| format | format of the picker | string | year `yyyy` month `MM` day `dd`, hour `HH`, minute `mm`, second `ss` | yyyy-MM-dd |
@ -274,4 +275,10 @@ Picking a date range is supported.
| Attribute | Description | Type | Accepted Values | Default |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| text | title of the shortcut | string | — | — |
| onClick | callback function, triggers when the shortcut is clicked, with the `vm` as its parameter. You can change the picker value by emitting the `pick` event. Example: `vm.$emit('pick', new Date())`| function | — | — |
| onClick | callback function, triggers when the shortcut is clicked, with the `vm` as its parameter. You can change the picker value by emitting the `pick` event. Example: `vm.$emit('pick', new Date())`| function | — | — |
### Events
| Event Name | Description | Parameters |
|---------|--------|---------|
| change | triggers when input value changes | formatted value |

View File

@ -210,6 +210,7 @@ Select date and time in one picker.
| readonly | whether DatePicker is read only | boolean | — | false |
| disabled | whether DatePicker is disabled | boolean | - | false |
| editable | whether the input is editable | boolean | - | true |
| clearable | Whether to show clear button | boolean | - | true |
|size | size of Input | string | large/small/mini | — |
| placeholder | placeholder | string | — | — |
| type | type of the picker | string | year/month/date/datetime/ week/datetimerange/daterange | date |
@ -229,3 +230,10 @@ Select date and time in one picker.
|---------- |-------------- |---------- |-------------------------------- |-------- |
| text | title of the shortcut | string | — | — |
| onClick | callback function, triggers when the shortcut is clicked, with the `vm` as its parameter. You can change the picker value by emitting the `pick` event. Example: `vm.$emit('pick', new Date())`| function | — | — |
### Events
| Event Name | Description | Parameters |
|---------|--------|---------|
| change | triggers when input value changes | formatted value |

View File

@ -3,6 +3,9 @@
methods: {
handleClick() {
alert('button click');
},
handleCommand(command) {
this.$message('click on item ' + command);
}
}
}
@ -126,6 +129,36 @@ Use `hide-on-click` to define if menu closes on clicking.
```
:::
### Command event
Clicking each dropdown item fires an event whose parameter is assigned by each item.
:::demo
```html
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
Dropdown List<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="a">Action 1</el-dropdown-item>
<el-dropdown-item command="b">Action 2</el-dropdown-item>
<el-dropdown-item command="c">Action 3</el-dropdown-item>
<el-dropdown-item command="d" disabled>Action 4</el-dropdown-item>
<el-dropdown-item command="e" divided>Action 5</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<script>
export default {
methods: {
handleCommand(command) {
this.$message('click on item ' + command);
}
}
}
</script>
```
:::
### Dropdown Attributes
| Attribute | Description | Type | Accepted Values | Default |

View File

@ -591,7 +591,10 @@ Search data from server-side.
| Event Name | Description | Parameters |
|----| ----| ----|
|click | triggers when the icon inside Input is clicked | event object |
|click | triggers when the icon inside Input is clicked | (event: Event) |
| blur | triggers when the icon inside Input is blur | (event: Event) |
| focus | triggers when the icon inside Input is focus | (event: Event) |
| change | triggers when the icon inside Input value change | (value: string \| number) |
### Autocomplete Attributes

View File

@ -1346,6 +1346,94 @@ Customize table column so it can be integrated with other components.
```
:::
### Expandable row
When the row content is too long and you do not want to display the horizontal scroll bar, you can use the expandable row feature.
:::demo Activate expandable row by adding type="expand" and `inline-template` attributeThe template for `el-table-column` will be rendered as the contents of the expanded row, you can access the same attributes as the` inline-template`。
```html
<template>
<el-table
:data="tableData3"
style="width: 100%">
<el-table-column type="expand" inline-template>
<div>
<p>State: {{ row.state }}</p>
<p>City: {{ row.city }}</p>
<p>Address: {{ row.address }}</p>
<p>Zip: {{ row.zip }}</p>
</div>
</el-table-column>
<el-table-column
label="Date"
prop="date">
</el-table-column>
<el-table-column
label="Name"
prop="name">
</el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData3: [{
date: '2016-05-03',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036'
}, {
date: '2016-05-02',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036'
}, {
date: '2016-05-04',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036'
}, {
date: '2016-05-01',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036'
}, {
date: '2016-05-08',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036'
}, {
date: '2016-05-06',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036'
}, {
date: '2016-05-07',
name: 'Tom',
state: 'California',
city: 'Los Angeles',
address: 'No. 189, Grove St, Los Angeles',
zip: 'CA 90036'
}]
}
}
</script>
```
:::
### Table Attributes
| Attribute | Description | Type | Accepted Values | Default |
|---------- |-------------- |---------- |-------------------------------- |-------- |
@ -1361,7 +1449,9 @@ Customize table column so it can be integrated with other components.
| row-style | function that returns custom style for a row, or a string assigning custom style for every row | Function(row, index)/Object | — | — |
| row-key | key of row data, used for optimizing rendering. Required if `reserve-selection` is on | Function(row)/String | — | — |
| context | context of Table, e.g. `_self` refers to the current context, `$parent` parent context, `$root` root context, can be overridden by `context` in `el-table-column` | Object | - | current context where Table lies |
| empty-text | Displayed text when data is empty. You can customize this area with `slot="empty"` | String | | - | No Data |
| empty-text | Displayed text when data is empty. You can customize this area with `slot="empty"` | String | - | No Data |
| default-expand-all | whether expand all rows by default, only works when the table has a column type="expand" | Boolean | - | false |
| expand-row-keys | set expanded rows by this prop, prop's value is the keys of expand rows, you should set row-key before using this prop | Array | - | |
| virtual-scrollbar | Enable virtual scrollbar | Boolean | - | false |
### Table Events
@ -1380,6 +1470,7 @@ Customize table column so it can be integrated with other components.
| sort-change | triggers when Table's sorting changes | { column, prop, order } |
| filter-change | column's key. If you need to use the filter-change event, this attribute is mandatory to identify which column is being filtered | filters |
| current-change | triggers when current row changes | currentRow, oldCurrentRow |
| expand | triggers when user expands or collapses a row | row, expanded |
### Table Methods
| Method | Description | Parameters |
@ -1390,7 +1481,7 @@ Customize table column so it can be integrated with other components.
### Table-column Attributes
| Attribute | Description | Type | Accepted Values | Default |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| type | type of the column. If set to `selection`, the column will display checkbox. If set to `index`, the column will display index of the row (staring from 1) | string | selection/index | — |
| type | type of the column. If set to `selection`, the column will display checkbox. If set to `index`, the column will display index of the row (staring from 1). If set to `expand`, the column will display expand icon. | string | selection/index/expand | — |
| label | column label | string | — | — |
| column-key | column's key. If you need to use the filter-change event, you need this attribute to identify which column is being filtered | string | string | - | - |
| prop | field name. You can also use its alias: `property` | string | — | — |
@ -1404,6 +1495,7 @@ Customize table column so it can be integrated with other components.
| formatter | function that formats content | Function(row, column) | — | — |
| show-overflow-tooltip | whether to hide extra content and show them in a tooltip when hovering on the cell | boolean | — | false |
| align | alignment | string | left/center/right | left |
| header-align | alignment of the table header. If omitted, the value of the above `align` attribute will be applied | String | left/center/right | — |
| class-name | class name of cells in the column | string | — | — |
| selectable | function that determines if a certain row can be selected, works when `type` is 'selection' | Function(row, index) | — | — |
| reserve-selection | whether to reserve selection after data refreshing, works when `type` is 'selection' | boolean | — | false |

View File

@ -2,7 +2,18 @@
export default {
data() {
return {
activeName: 'first'
activeName: 'first',
activeName2: 'first',
tabs: [{
title: 'Tab 1',
name: '1',
content: 'Tab 1 content'
}, {
title: 'Tab 2',
name: '2',
content: 'Tab 2 content'
}],
tabIndex: 2
}
},
methods: {
@ -11,6 +22,9 @@
},
handleClick(tab, event) {
console.log(tab, event);
},
renderTab(h, tab) {
return <span><i class="el-icon-date"></i> {tab.label}</span>;
}
}
}
@ -24,11 +38,11 @@ Divide data collections which are related yet belong to different types.
Basic and concise tabs.
:::demo Tabs provide a selective card functionality and it can be achieved by just using `el-tabs` and child element `el-tab-pane`. In these two elements, we provide a list of attributes. The `label` in `el-tab-pane` determines the label of selective cards, and you can write content in the label. In this example, we add a `active-name` attribute indicating the active card in `el-tabs`, which can take a `String` value. In the `el-tab-pane` you can set corresponding `name` attribute, and if there is no `name`, the default sequence is `1`/`2`/`3`/`4`. In this example, the selected card is card 2. If `name` is omitted, setting `active-name` to `2` can reach the same goal.
:::demo Tabs provide a selective card functionality. By default the first tab is selected as active, and you can activate any tab by setting the `value` attribute.
```html
<template>
<el-tabs :active-name="activeName">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="User" name="first">User</el-tab-pane>
<el-tab-pane label="Config" name="second">Config</el-tab-pane>
<el-tab-pane label="Role" name="third">Role</el-tab-pane>
@ -41,6 +55,11 @@ Basic and concise tabs.
return {
activeName: 'first'
};
},
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
};
</script>
@ -55,7 +74,7 @@ Tabs styled as cards.
```html
<template>
<el-tabs type="card" @tab-click="handleClick" @tab-remove="handleRemove">
<el-tabs type="card" @tab-click="handleClick">
<el-tab-pane label="User">User</el-tab-pane>
<el-tab-pane label="Config">Config</el-tab-pane>
<el-tab-pane label="Role">Role</el-tab-pane>
@ -64,10 +83,12 @@ Tabs styled as cards.
</template>
<script>
export default {
data() {
return {
activeName: 'first'
};
},
methods: {
handleRemove(tab) {
console.log(tab);
},
handleClick(tab, event) {
console.log(tab, event);
}
@ -81,7 +102,7 @@ Tabs styled as cards.
Closable tabs.
:::demo You can set `closable` attribute in `el-tabs`. It accept `Boolean` and Tab will be closable when the boolean is `true`.
:::demo You can set the closable attribute in el-tabs to make all tabs closable. Also, closable can be set in a tab panel to make that specific tab closable.
```html
<template>
@ -125,12 +146,37 @@ Border card tabs.
:::
### Custom Tab
You can use `label-content` property to customize the tab
:::demo `label-content` is a render function,which return the vnode of the tab.
```html
<el-tabs type="border-card">
<el-tab-pane label="Route" :label-content="renderTab">Route</el-tab-pane>
<el-tab-pane label="Config">Config</el-tab-pane>
<el-tab-pane label="Role">Role</el-tab-pane>
<el-tab-pane label="Task">Task</el-tab-pane>
</el-tabs>
<script>
export default {
methods: {
renderTab(h, tab) {
return <span><i class="el-icon-date"></i> {tab.label}</span>;
}
}
}
</script>
```
:::
### Tabs Attributes
| Attribute | Description | Type | Accepted Values | Default |
|---------- |-------- |---------- |------------- |-------- |
| type | type of Tab | string | card/border-card | — |
| closable | whether Tab is closable | boolean | — | false |
| active-name | name of the selected tab | string | — | name of first tab |
| active-name(deprecated) | name of the selected tab | string | — | name of first tab |
| value | name of the selected tab | string | — | name of first tab |
### Tabs Events
| Event Name | Description | Parameters |
@ -145,3 +191,4 @@ Border card tabs.
| label-content | render function for tab title | Function(h, tab:vueInstance) | - | - |
| disabled | whether Tab is disabled | boolean | - | false |
| name | identifier corresponding to the activeName of Tabs, representing the alias of the tab-pane | string | — | ordinal number of the tab-pane in the sequence, i.e. the first tab-pane is '1' |
| closable | whether Tab is closable | boolean | — | false |

View File

@ -146,7 +146,8 @@ Can pick an arbitrary time range.
| readonly | whether DatePicker is read only | boolean | — | false |
| disabled | whether DatePicker is disabled | boolean | - | false |
| editable | whether the input is editable | boolean | - | true |
|size | size of Input | string | large/small/mini | — |
| clearable | Whether to show clear button | boolean | - | true |
| size | size of Input | string | large/small/mini | — |
| placeholder | placeholder | string | — | — |
| format | format of the picker | string | hour `HH`, minute `mm`, second `ss` | HH:mm:ss |
| value | value of the picker | date for Time Picker, and string for Time Select | hour `HH`, minute `mm`, second `ss` | HH:mm:ss |
@ -167,3 +168,10 @@ Can pick an arbitrary time range.
| Attribute | Description | Type | Accepted Values | Default |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| selectableRange | available time range, e.g.`'18:30:00 - 20:30:00'`or`['09:30:00 - 12:00:00', '14:30:00 - 18:30:00']` | string/array | — | — |
### Events
| Event Name | Description | Parameters |
|---------|--------|---------|
| change | triggers when input value changes | formatted value |

View File

@ -51,7 +51,7 @@ et -i [可以自定义变量文件]
```
### 编译主题
保存文件后,到命令行里执行 `et` 编译主题,如果你想启用 `watch` 模式,实时编译主题,增加 `-w` 参数
保存文件后,到命令行里执行 `et` 编译主题,如果你想启用 `watch` 模式,实时编译主题,增加 `-w` 参数;如果你在初始化时指定了自定义变量文件,则需要增加 `-c` 参数,并带上你的变量文件名
```shell
et

View File

@ -289,6 +289,7 @@
| readonly | 完全只读 | boolean | — | false |
| disabled | 禁用 | boolean | - | false |
| editable | 文本框可输入 | boolean | - | true |
| clearable | 是否显示清除按钮 | boolean | - | true |
| size | 输入框尺寸 | string | large, small, mini | — |
| placeholder | 占位内容 | string | — | — |
| type | 显示类型 | string | year/month/date/week/ datetime/datetimerange/daterange | date |
@ -309,3 +310,9 @@
|---------- |-------------- |---------- |-------------------------------- |-------- |
| text | 标题文本 | string | — | — |
| onClick | 选中后的回调函数,参数是 vm可通过触发 'pick' 事件设置选择器的值。例如 vm.$emit('pick', new Date()) | function | — | — |
### Events
| Event Name | Description | Parameters |
|---------|--------|---------|
| change | 当 input 的值改变时触发,返回值和文本框一致 | formatted value |

View File

@ -233,6 +233,7 @@
| readonly | 完全只读 | boolean | — | false |
| disabled | 禁用 | boolean | - | false |
| editable | 文本框可输入 | boolean | - | true |
| clearable | 是否显示清除按钮 | boolean | - | true |
| size | 输入框尺寸 | string | large, small, mini | — |
| placeholder | 占位内容 | string | — | — |
| type | 显示类型 | string | year/month/date/week/ datetime/datetimerange/daterange | date |
@ -254,3 +255,9 @@
| onClick | 选中后的回调函数,参数是 vm可通过触发 'pick' 事件设置选择器的值。例如 vm.$emit('pick', new Date()) | function | — | — |
### Events
| Event Name | Description | Parameters |
|---------|--------|---------|
| change | 当 input 的值改变时触发,返回值和文本框一致 | formatted value |

View File

@ -43,6 +43,9 @@
methods: {
handleClick() {
alert('button click');
},
handleCommand(command) {
this.$message('click on item ' + command);
}
}
}
@ -168,6 +171,35 @@
```
:::
### 指令事件
点击菜单项后会触发事件,用户可以通过相应的菜单项 key 进行不同的操作
:::demo
```html
<el-dropdown @command="handleCommand">
<span class="el-dropdown-link">
下拉菜单<i class="el-icon-caret-bottom el-icon--right"></i>
</span>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item command="a">黄金糕</el-dropdown-item>
<el-dropdown-item command="b">狮子头</el-dropdown-item>
<el-dropdown-item command="c">螺蛳粉</el-dropdown-item>
<el-dropdown-item command="d" disabled>双皮奶</el-dropdown-item>
<el-dropdown-item command="e" divided>蚵仔煎</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<script>
export default {
methods: {
handleCommand(command) {
this.$message('click on item ' + command);
}
}
}
</script>
```
:::
### Dropdown Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |

View File

@ -803,7 +803,7 @@
|---------- |-------------- |
| validate(cb) | 对整个表单进行校验的方法 |
| validateField(prop, cb) | 对部分表单字段进行校验的方法 |
| resetFields | 对整个表单进行重置,将所有字段值重置为并移除校验结果 |
| resetFields | 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果 |
### Form-Item Attributes

View File

@ -203,9 +203,7 @@
::: demo
```html
<el-input
placeholder="请输入内容"
v-model="input">
<el-input v-model="input" placeholder="请输入内容">
</el-input>
<script>
@ -759,9 +757,10 @@ export default {
### Input Events
| 事件名称 | 说明 | 回调参数 |
|---------|--------|---------|
| click | 点击 Input 内的图标时触发 | event |
| blur | 在 Input 失去焦点时触发 | event |
| focus | 在 Input 或得焦点时触发 | event |
| click | 点击 Input 内的图标时触发 | (event: Event) |
| blur | 在 Input 失去焦点时触发 | (event: Event) |
| focus | 在 Input 或得焦点时触发 | (event: Event) |
| change | 在 Input 值改变时触发 | (value: string \| number) |
### Autocomplete Attributes

View File

@ -60,6 +60,7 @@
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-02',
@ -67,6 +68,7 @@
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-04',
@ -81,6 +83,7 @@
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-08',
@ -88,6 +91,7 @@
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-06',
@ -95,6 +99,7 @@
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-07',
@ -102,6 +107,7 @@
province: '上海',
city: '普陀区',
address: '上海市普陀区金沙江路 1518 弄',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}],
tableData4: [{
@ -1354,6 +1360,74 @@
```
:::
### 展开行
当行内容过多并且不想显示横向滚动条时,可以使用 Table 展开行功能。
:::demo 通过设置 type="expand" 和 `inline-template` 属性可以开启展开行功能,`el-table-column` 的模板会被渲染成为展开行的内容,展开行可访问的属性与使用 `inline-template` 的时候相同。
```html
<template>
<el-table
:data="tableData3"
style="width: 100%">
<el-table-column type="expand" inline-template>
<div>
<p>省: {{ row.province }}</p>
<p>市: {{ row.city }}</p>
<p>住址: {{ row.detailAddress }}</p>
<p>邮编: {{ row.zip }}</p>
</div>
</el-table-column>
<el-table-column
label="日期"
prop="date">
</el-table-column>
<el-table-column
label="姓名"
prop="name">
</el-table-column>
</el-table>
</template>
<script>
export default {
data() {
return {
tableData3: [{
date: '2016-05-02',
name: '王小虎',
province: '上海',
city: '普陀区',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-04',
name: '王小虎',
province: '上海',
city: '普陀区',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-01',
name: '王小虎',
province: '上海',
city: '普陀区',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}, {
date: '2016-05-03',
name: '王小虎',
province: '上海',
city: '普陀区',
detailAddress: '金沙江路 1518 弄',
zip: 200333
}]
}
}
}
</script>
```
:::
### Table Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------------- |---------- |-------------------------------- |-------- |
@ -1368,9 +1442,12 @@
| row-style | 行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。 | Function(row, index)/Object | — | — |
| row-key | 行数据的 Key用来优化 Table 的渲染;在使用 reserve-selection 功能的情况下,该属性是必填的 | Function(row)/String | — | — |
| context | 设置上下文环境,例如设置当前上下文就是 `_self`,父级就是 `$parent`,根组件 `$root`。优先读取 column 的 context 属性。 | Object | - | Table 所处上下文 |
| empty-text | 空数据时显示的文本内容,也可以通过 `slot="empty"` 设置 | String | | - | 暂无数据 |
| empty-text | 空数据时显示的文本内容,也可以通过 `slot="empty"` 设置 | String | - | 暂无数据 |
| default-expand-all | 是否默认展开所有行,当 Table 中存在 type="expand" 的 Column 的时候有效 | Boolean | - | false |
| expand-row-keys | 可以通过该属性设置 Table 目前的展开行,需要设置 row-key 属性才能使用,该属性为展开行的 keys 数组。| Array | - | |
| virtual-scrollbar | 启用虚拟滚动条 | Boolean | - | false |
### Table Events
| 事件名 | 说明 | 参数 |
| ---- | ---- | ---- |
@ -1387,6 +1464,7 @@
| sort-change | 当表格的排序条件发生变化的时候会触发该事件 | { column, prop, order } |
| filter-change | 当表格的筛选条件发生变化的时候会触发该事件,参数的值是一个对象,对象的 key 是 column 的 columnKey对应的 value 为用户选择的筛选条件的数组。 | filters |
| current-change | 当表格的当前行发生变化的时候会触发该事件,如果要高亮当前行,请打开表格的 highlight-current-row 属性 | currentRow, oldCurrentRow |
| expand | 当用户对某一行展开或者关闭的上会触发该事件 | row, expanded |
### Table Methods
| 方法名 | 说明 | 参数 |
@ -1397,7 +1475,7 @@
### Table-column Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------------- |---------- |-------------------------------- |-------- |
| type | 对应列的类型。如果设置了 `selection` 则显示多选框如果设置了 `index` 则显示该行的索引(从 1 开始计算) | string | selection/index | — |
| type | 对应列的类型。如果设置了 `selection` 则显示多选框如果设置了 `index` 则显示该行的索引(从 1 开始计算);如果设置了 expand 则显示为一个可展开的按钮 | string | selection/index/expand | — |
| column-key | column 的 key如果需要使用 filter-change 事件,则需要此属性标识是哪个 column 的筛选条件 | string | - | - |
| label | 显示的标题 | string | — | — |
| prop | 对应列内容的字段名,也可以使用 property 属性 | string | — | — |
@ -1410,7 +1488,8 @@
| resizable | 对应列是否可以通过拖动改变宽度(需要在 el-table 上设置 border 属性为真) | boolean | — | true |
| formatter | 用来格式化内容 | Function(row, column) | — | — |
| show-overflow-tooltip | 当内容过长被隐藏时显示 tooltip | Boolean | — | false |
| align | 对齐方式 | String | left, center, right | left |
| align | 对齐方式 | String | left/center/right | left |
| header-align | 表头对齐方式,若不设置该项,则使用表格的对齐方式 | String | left/center/right | — |
| class-name | 列的 className | string | — | — |
| selectable | 仅对 type=selection 的列有效,类型为 FunctionFunction 的返回值用来决定这一行的 CheckBox 是否可以勾选 | Function(row, index) | — | — |
| reserve-selection | 仅对 type=selection 的列有效,类型为 Boolean为 true 则代表会保留之前数据的选项,需要配合 Table 的 clearSelection 方法使用。 | Boolean | — | false |

View File

@ -2,7 +2,18 @@
export default {
data() {
return {
activeName: 'first'
activeName: 'first',
activeName2: 'first',
tabs: [{
title: 'Tab 1',
name: '1',
content: 'Tab 1 content'
}, {
title: 'Tab 2',
name: '2',
content: 'Tab 2 content'
}],
tabIndex: 2
}
},
methods: {
@ -11,22 +22,27 @@
},
handleClick(tab, event) {
console.log(tab, event);
},
renderTab(h, tab) {
return <span><i class="el-icon-date"></i> {tab.label}</span>;
}
}
}
</script>
## Tabs 标签页
分隔内容上有关联但属于不同类别的数据集合。
### 基础用法
基础的、简洁的标签页。
:::demo Tabs 组件提供了选项卡功能,只需要使用`el-tabs`和子元素`el-tab-pane`即可,在两个元素中,我们分别提供了一系列的属性来方便使用,`el-tab-pane`中`label`决定了选项卡标题,标签内部写入内容即可。在下例中我们在`el-tabs`中设置了`active-name`属性,接受一个`String`值,表明选中的选项卡,在`el-tab-pane`中可以设置对应的`name`属性,如果没有设置`name`,则默认值为顺序的`1`/`2`/`3`/`4`。例子选中选项卡2如果不设置`name`,将`active-name`设为`2`,可以达成相同效果
:::demo Tabs 组件提供了选项卡功能,默认选中第一个标签页,你也可以通过 `value` 属性来指定当前选中的标签页
```html
<template>
<el-tabs :active-name="activeName">
<el-tabs v-model="activeName" @tab-click="handleClick">
<el-tab-pane label="用户管理" name="first">用户管理</el-tab-pane>
<el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
<el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
@ -39,6 +55,11 @@
return {
activeName: 'first'
};
},
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
};
</script>
@ -49,23 +70,25 @@
选项卡样式的标签页。
:::demo 只需要设置`type`属性即可,如果需要标签风格,将其设置为`card`。
:::demo 只需要设置 `type` 属性为 `card` 就可以使选项卡改变为标签风格
```html
<template>
<el-tabs type="card" @tab-click="handleClick" @tab-remove="handleRemove">
<el-tab-pane label="用户管理">用户管理</el-tab-pane>
<el-tab-pane label="配置管理">配置管理</el-tab-pane>
<el-tab-pane label="角色管理">角色管理</el-tab-pane>
<el-tab-pane label="定时任务补偿">定时任务补偿</el-tab-pane>
<el-tabs v-model="activeName2" type="card" @tab-click="handleClick">
<el-tab-pane label="用户管理" name="first">用户管理</el-tab-pane>
<el-tab-pane label="配置管理" name="second">配置管理</el-tab-pane>
<el-tab-pane label="角色管理" name="third">角色管理</el-tab-pane>
<el-tab-pane label="定时任务补偿" name="fourth">定时任务补偿</el-tab-pane>
</el-tabs>
</template>
<script>
export default {
data() {
return {
activeName: 'first'
};
},
methods: {
handleRemove(tab) {
console.log(tab);
},
handleClick(tab, event) {
console.log(tab, event);
}
@ -79,11 +102,11 @@
可以关闭标签页。
:::demo 在`el-tabs`中设置`closable`属性,接受一个`Boolean`,设置为`true`时为可关闭
:::demo 通过设置 `closable` 属性来打开 `Tabs` 的可关闭标签效果, `closable` 也可以设置在 `Tab Panel` 中实现部分标签页的可关闭效果
```html
<template>
<el-tabs type="card" :closable="true" @tab-click="handleClick" @tab-remove="handleRemove">
<el-tabs type="card" closable @tab-click="handleClick" @tab-remove="handleRemove">
<el-tab-pane label="用户管理">用户管理</el-tab-pane>
<el-tab-pane label="配置管理">配置管理</el-tab-pane>
<el-tab-pane label="角色管理">角色管理</el-tab-pane>
@ -120,12 +143,52 @@
```
:::
### 自定义标签页
可以通过 `label-content` 属性来实现自定义标签页的内容
:::demo `label-content` 是一个 render function在这个方法里返回的 vnode 会被渲染到标签页中。
```html
<el-tabs type="border-card">
<el-tab-pane label="我的行程" :label-content="renderTab">我的行程</el-tab-pane>
<el-tab-pane label="消息中心">消息中心</el-tab-pane>
<el-tab-pane label="角色管理">角色管理</el-tab-pane>
<el-tab-pane label="定时任务补偿">定时任务补偿</el-tab-pane>
</el-tabs>
<script>
export default {
methods: {
renderTab(h, tab) {
return <span><i class="el-icon-date"></i> {tab.label}</span>;
}
}
}
</script>
```
:::
### 动态增加标签页
展示如何通过触发器来动态增加标签页
:::demo
```html
<div style="margin-bottom: 20px;">
<el-button size="small" @click="tabs.push({ name: 'Tab ' + ++tabIndex, title: 'new Tab', content: 'new Tab content' })">add tab</el-button>
</div>
<el-tabs type="card" closable>
<el-tab-pane v-for="(item, index) in tabs" :label="item.title" :name="item.name">{{item.content}}</el-tab-pane>
</el-tabs>
```
:::
### Tabs Attributes
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---------- |-------- |---------- |------------- |-------- |
| type | 风格类型 | string | card/border-card | — |
| closable | 标签是否可关闭 | boolean | — | false |
| active-name | 选中选项卡的 name | string | — | 第一个选项卡的 name |
| active-name(deprecated) | 选中选项卡的 name | string | — | 第一个选项卡的 name |
| value | 绑定值,选中选项卡的 name | string | — | 第一个选项卡的 name |
### Tabs Events
| 事件名称 | 说明 | 回调参数 |
@ -140,3 +203,4 @@
| label-content | 选项卡的标题的渲染 Function | Function(h, tab:vueInstance) | - | - |
| disabled | 是否禁用 | boolean | - | false |
| name | 与选项卡 activeName 对应的标识符,表示选项卡别名 | string | — | 该选项卡在选项卡列表中的顺序值,如第一个选项卡则为'1' |
| closable | 标签是否可关闭 | boolean | — | false |

View File

@ -153,6 +153,7 @@
| readonly | 完全只读 | boolean | — | false |
| disabled | 禁用 | boolean | - | false |
| editable | 文本框可输入 | boolean | - | true |
| clearable | 是否显示清除按钮 | boolean | - | true |
| size | 输入框尺寸 | string | large, small, mini | — |
| placeholder | 占位内容 | string | — | — |
| format | 时间格式化(TimePicker) | string | 小时:`HH`,分:`mm`,秒:`ss` | 'HH:mm:ss' |
@ -175,3 +176,11 @@
|---------- |-------------- |---------- |-------------------------------- |-------- |
| selectableRange | 可选时间段,例如`'18:30:00 - 20:30:00'`或者传入数组`['09:30:00 - 12:00:00', '14:30:00 - 18:30:00']` | string/array | — | — |
### Events
| Event Name | Description | Parameters |
|---------|--------|---------|
| change | 当 input 的值改变时触发,返回值和文本框一致 | formatted value |

View File

@ -16,6 +16,9 @@
"guide": "指南",
"components": "组件",
"resource": "资源"
},
"nav": {
"dropdown": "版本:"
}
},
{
@ -35,6 +38,9 @@
"guide": "Guide",
"components": "Component",
"resource": "Resource"
},
"nav": {
"dropdown": "Version: "
}
}
]

3
examples/versions.json Normal file
View File

@ -0,0 +1,3 @@
{
"1.0.9": "1.0"
}

View File

@ -1,6 +1,6 @@
{
"name": "element-ui",
"version": "1.0.8",
"version": "1.0.9",
"description": "A Component Library for Vue.js.",
"main": "lib/element-ui.common.js",
"files": [
@ -58,13 +58,14 @@
"babel-loader": "^6.2.5",
"babel-plugin-module-resolver": "^2.2.0",
"babel-plugin-syntax-jsx": "^6.8.0",
"babel-plugin-transform-vue-jsx": "^3.1.0",
"babel-plugin-transform-vue-jsx": "^3.3.0",
"babel-preset-es2015": "^6.14.0",
"chai": "^3.5.0",
"cheerio": "^0.18.0",
"cooking": "^1.2.0",
"cooking-lint": "^0.1.3",
"cooking-vue2": "^0.3.0",
"copy-webpack-plugin": "^4.0.1",
"coveralls": "^2.11.14",
"cp-cli": "^1.0.2",
"cross-env": "^3.1.3",

View File

@ -1,14 +1,14 @@
<template>
<label class="el-checkbox">
<span class="el-checkbox__input">
<span class="el-checkbox__inner"
:class="{
'is-disabled': disabled,
'is-checked': isChecked,
'is-indeterminate': indeterminate,
'is-focus': focus
}"
></span>
<span class="el-checkbox__input"
:class="{
'is-disabled': disabled,
'is-checked': isChecked,
'is-indeterminate': indeterminate,
'is-focus': focus
}"
>
<span class="el-checkbox__inner"></span>
<input
v-if="trueLabel || falseLabel"
class="el-checkbox__original"

View File

@ -293,7 +293,7 @@
handleClear() {
this.minDate = null;
this.maxDate = null;
this.handleConfirm();
this.handleConfirm(false);
},
handleDateInput(event, type) {
@ -376,10 +376,8 @@
this.maxDate = val.maxDate;
this.minDate = val.minDate;
if (!close) return;
if (!this.showTime) {
this.$emit('pick', [this.minDate, this.maxDate]);
}
if (!close || this.showTime) return;
this.handleConfirm();
},
changeToToday() {
@ -456,7 +454,7 @@
this.resetDate();
},
handleConfirm(visible) {
handleConfirm(visible = false) {
this.$emit('pick', [this.minDate, this.maxDate], visible);
},

View File

@ -190,7 +190,7 @@
methods: {
handleClear() {
this.date = new Date();
this.$emit('pick', '');
this.$emit('pick');
},
resetDate() {

View File

@ -92,7 +92,7 @@
},
handleClear() {
this.$emit('pick', '');
this.$emit('pick');
}
},

View File

@ -104,7 +104,7 @@
methods: {
handleClear() {
this.$emit('pick', '');
this.$emit('pick');
},
handleCancel() {

View File

@ -26,7 +26,7 @@
<script>
import Vue from 'vue';
import Clickoutside from 'element-ui/src/utils/clickoutside';
import { formatDate, parseDate, getWeekNumber } from './util';
import { formatDate, parseDate, getWeekNumber, equalDate } from './util';
import Popper from 'element-ui/src/utils/vue-popper';
import Emitter from 'element-ui/src/mixins/emitter';
import ElInput from 'element-ui/packages/input';
@ -187,6 +187,10 @@ export default {
readonly: Boolean,
placeholder: String,
disabled: Boolean,
clearable: {
type: Boolean,
default: true
},
popperClass: String,
editable: {
type: Boolean,
@ -305,9 +309,10 @@ export default {
if (parsedValue && this.picker) {
this.picker.value = parsedValue;
}
return;
} else {
this.picker.value = value;
}
this.picker.value = value;
this.$forceUpdate();
}
}
},
@ -324,21 +329,36 @@ export default {
methods: {
handleMouseEnterIcon() {
if (this.readonly || this.disabled) return;
if (!this.valueIsEmpty) {
if (!this.valueIsEmpty && this.clearable) {
this.showClose = true;
}
},
handleClickIcon() {
if (this.readonly || this.disabled) return;
if (this.valueIsEmpty) {
this.pickerVisible = !this.pickerVisible;
} else {
if (this.showClose) {
this.internalValue = '';
this.$emit('input', '');
} else {
this.pickerVisible = !this.pickerVisible;
}
},
dateIsUpdated(date) {
let updated = true;
if (Array.isArray(date)) {
if (equalDate(this.cacheDateMin, date[0]) &&
equalDate(this.cacheDateMax, date[1])) updated = false;
this.cacheDateMin = date[0];
this.cacheDateMax = date[1];
} else {
if (equalDate(this.cacheDate, date)) updated = false;
this.cacheDate = date;
}
return updated;
},
handleClose() {
this.pickerVisible = false;
},
@ -419,7 +439,9 @@ export default {
this.picker.$on('dodestroy', this.doDestroy);
this.picker.$on('pick', (date, visible = false) => {
this.$emit('input', date);
if (this.dateIsUpdated(date)) this.$emit('input', date);
this.$nextTick(() => this.$emit('change', this.visualValue));
this.pickerVisible = this.picker.visible = visible;
this.picker.resetView && this.picker.resetView();
});

View File

@ -8,6 +8,10 @@ const newArray = function(start, end) {
return result;
};
export const equalDate = function(dateA, dateB) {
return new Date(dateA).getTime() === new Date(dateB).getTime();
};
export const toDate = function(date) {
date = new Date(date);
if (isNaN(date.getTime())) return null;

View File

@ -45,7 +45,7 @@ Vue.component('el-form-item', ElForm)
|---------- |-------------- |
| validate(cb) | 对整个表单进行校验的方法 |
| validateField(prop, cb) | 对部分表单字段进行校验的方法 |
| resetFields | 对整个表单进行重置,将所有字段值重置为并移除校验结果 |
| resetFields | 对整个表单进行重置,将所有字段值重置为初始值并移除校验结果 |
### Form-Item Attributes

View File

@ -54,12 +54,13 @@
},
validate(callback) {
let valid = true;
let count = 0;
this.fields.forEach((field, index) => {
field.validate('', errors => {
if (errors) {
valid = false;
}
if (typeof callback === 'function' && index === this.fields.length - 1) {
if (typeof callback === 'function' && ++count === this.fields.length) {
callback(valid);
}
});

View File

@ -6,31 +6,11 @@
{ 'is-without-controls': !controls}
]"
>
<el-input
:value="currentValue"
@keydown.up.native="increase"
@keydown.down.native="decrease"
@blur="handleBlur"
@input="handleInput"
:disabled="disabled"
:size="size"
:class="{
'is-active': inputActive
}">
<template slot="prepend" v-if="$slots.prepend">
<slot name="prepend"></slot>
</template>
<template slot="append" v-if="$slots.append">
<slot name="append"></slot>
</template>
</el-input>
<span
v-if="controls"
class="el-input-number__decrease el-icon-minus"
:class="{'is-disabled': minDisabled}"
v-repeat-click="decrease"
@mouseenter="activeInput(minDisabled)"
@mouseleave="inactiveInput(minDisabled)"
>
</span>
<span
@ -38,10 +18,24 @@
class="el-input-number__increase el-icon-plus"
:class="{'is-disabled': maxDisabled}"
v-repeat-click="increase"
@mouseenter="activeInput(maxDisabled)"
@mouseleave="inactiveInput(maxDisabled)"
>
</span>
<el-input
v-model.number="currentValue"
@keydown.up.native="increase"
@keydown.down.native="decrease"
@blur="handleBlur"
:disabled="disabled"
:size="size"
ref="input"
>
<template slot="prepend" v-if="$slots.prepend">
<slot name="prepend"></slot>
</template>
<template slot="append" v-if="$slots.append">
<slot name="append"></slot>
</template>
</el-input>
</div>
</template>
<script>
@ -50,29 +44,6 @@
export default {
name: 'ElInputNumber',
props: {
step: {
type: Number,
default: 1
},
max: {
type: Number,
default: Infinity
},
min: {
type: Number,
default: 0
},
value: {
default: 0
},
disabled: Boolean,
size: String,
controls: {
type: Boolean,
default: true
}
},
directives: {
repeatClick: {
bind(el, binding, vnode) {
@ -99,6 +70,29 @@
components: {
ElInput
},
props: {
step: {
type: Number,
default: 1
},
max: {
type: Number,
default: Infinity
},
min: {
type: Number,
default: 0
},
value: {
default: 0
},
disabled: Boolean,
size: String,
controls: {
type: Boolean,
default: true
}
},
data() {
// correct the init value
let value = this.value;
@ -111,8 +105,7 @@
value = this.max;
}
return {
currentValue: value,
inputActive: false
currentValue: value
};
},
watch: {
@ -121,19 +114,18 @@
},
currentValue(newVal, oldVal) {
let value = Number(newVal);
if (value <= this.max && value >= this.min) {
this.$emit('change', value, oldVal);
this.$emit('input', value);
if (newVal <= this.max && newVal >= this.min) {
this.$emit('change', newVal, oldVal);
this.$emit('input', newVal);
}
}
},
computed: {
minDisabled() {
return this.value - this.step < this.min;
return this.accSub(this.value, this.step) < this.min;
},
maxDisabled() {
return this.value + this.step > this.max;
return this.accAdd(this.value, this.step) > this.max;
}
},
methods: {
@ -183,41 +175,19 @@
return (arg1 + arg2) / m;
},
increase() {
if (this.maxDisabled) return;
const value = this.value || 0;
if (value + this.step > this.max || this.disabled) return;
this.currentValue = this.accAdd(this.step, value);
if (this.maxDisabled) {
this.inputActive = false;
}
if (this.accAdd(value, this.step) > this.max || this.disabled) return;
this.currentValue = this.accAdd(value, this.step);
},
decrease() {
if (this.minDisabled) return;
const value = this.value || 0;
if (value - this.step < this.min || this.disabled) return;
if (this.accSub(value, this.step) < this.min || this.disabled) return;
this.currentValue = this.accSub(value, this.step);
if (this.minDisabled) {
this.inputActive = false;
}
},
activeInput(disabled) {
if (!this.disabled && !disabled) {
this.inputActive = true;
}
},
inactiveInput(disabled) {
if (!this.disabled && !disabled) {
this.inputActive = false;
}
},
handleBlur(event) {
let value = Number(this.currentValue);
if (isNaN(value) || value > this.max || value < this.min) {
this.currentValue = this.value;
} else {
this.currentValue = value;
}
},
handleInput(value) {
this.currentValue = value;
handleBlur() {
this.$refs.input.setCurrentValue(this.currentValue);
}
}
};

View File

@ -33,7 +33,7 @@
:min="min"
:max="max"
:form="form"
:value="value"
:value="currentValue"
ref="input"
@input="handleInput"
@focus="handleFocus"
@ -48,7 +48,8 @@
<textarea
v-else
class="el-textarea__inner"
v-model="currentValue"
:value="currentValue"
@input="handleInput"
ref="textarea"
:name="name"
:placeholder="placeholder"
@ -76,6 +77,13 @@
mixins: [emitter],
data() {
return {
currentValue: this.value,
textareaStyle: {}
};
},
props: {
value: [String, Number],
placeholder: String,
@ -108,6 +116,18 @@
min: {}
},
computed: {
validating() {
return this.$parent.validateState === 'validating';
}
},
watch: {
'value'(val, oldValue) {
this.setCurrentValue(val);
}
},
methods: {
handleBlur(event) {
this.$emit('blur', event);
@ -129,46 +149,29 @@
this.$emit('focus', event);
},
handleInput(event) {
this.currentValue = event.target.value;
this.setCurrentValue(event.target.value);
},
handleIconClick(event) {
this.$emit('click', event);
},
setCurrentValue(value) {
if (value === this.currentValue) return;
this.$nextTick(_ => {
this.resizeTextarea();
});
this.currentValue = value;
this.$emit('input', value);
this.$emit('change', value);
this.dispatch('ElFormItem', 'el.form.change', [value]);
}
},
data() {
return {
currentValue: this.value,
textareaStyle: {}
};
},
created() {
this.$on('inputSelect', this.inputSelect);
},
mounted() {
this.resizeTextarea();
},
computed: {
validating() {
return this.$parent.validateState === 'validating';
}
},
watch: {
'value'(val, oldValue) {
this.currentValue = val;
},
'currentValue'(val) {
this.$nextTick(_ => {
this.resizeTextarea();
});
this.$emit('input', val);
this.$emit('change', val);
this.dispatch('ElFormItem', 'el.form.change', [val]);
}
}
};
</script>

View File

@ -107,6 +107,14 @@
},
openActiveItemMenus() {
let index = this.activeIndex;
// menu
if (this.router) {
const userSpecifiedIndexs = Object
.keys(this.menuItems)
.filter(k => this.menuItems[k].route)
.filter(k => this.menuItems[k].route.path === this.$route.path);
userSpecifiedIndexs.length && (index = this.activeIndex = userSpecifiedIndexs[0]);
}
if (!this.menuItems[index]) return;
if (index && this.mode === 'vertical') {
let indexPath = this.menuItems[index].indexPath;

View File

@ -100,6 +100,7 @@ export default {
render(h) {
return (
<button
type="button"
class={['btn-prev', { disabled: this.$parent.internalCurrentPage <= 1 }]}
on-click={ this.$parent.prev }>
<i class="el-icon el-icon-arrow-left"></i>
@ -112,6 +113,7 @@ export default {
render(h) {
return (
<button
type="button"
class={[
'btn-next',
{ disabled: this.$parent.internalCurrentPage === this.$parent.internalPageCount || this.$parent.internalPageCount === 0 }

View File

@ -1,12 +1,13 @@
<template>
<label class="el-radio">
<span class="el-radio__input">
<span class="el-radio__inner"
:class="{
<span class="el-radio__input"
:class="{
'is-disabled': disabled,
'is-checked': model === label,
'is-focus': focus
}"></span>
}"
>
<span class="el-radio__inner"></span>
<input
class="el-radio__original"
:value="label"

View File

@ -92,11 +92,6 @@
},
value() {
this.dispatch('ElSelect', 'setSelected');
},
visible() {
this.$nextTick(() => {
this.dispatch('ElSelectDropdown', 'updatePopper');
});
}
},

View File

@ -233,7 +233,9 @@
},
query(val) {
this.broadcast('ElSelectDropdown', 'updatePopper');
this.$nextTick(() => {
this.broadcast('ElSelectDropdown', 'updatePopper');
});
this.hoverIndex = -1;
if (this.multiple && this.filterable) {
this.resetInputHeight();
@ -557,7 +559,7 @@
deleteTag(event, tag) {
let index = this.selected.indexOf(tag);
if (index > -1) {
if (index > -1 && !this.disabled) {
this.value.splice(index, 1);
}
event.stopPropagation();

View File

@ -38,7 +38,7 @@ export default {
<tbody>
{
this._l(this.data, (row, $index) =>
<tr
[<tr
style={ this.rowStyle ? this.getRowStyle(row, $index) : null }
key={ this.table.rowKey ? this.getKeyOfRow(row, $index) : $index }
on-dblclick={ ($event) => this.handleDoubleClick($event, row) }
@ -46,7 +46,7 @@ export default {
on-contextmenu={ ($event) => this.handleContextMenu($event, row) }
on-mouseenter={ _ => this.handleMouseEnter($index) }
on-mouseleave={ _ => this.handleMouseLeave() }
class={ this.getRowClass(row, $index) }>
class={ [this.getRowClass(row, $index)] }>
{
this._l(this.columns, (column, cellIndex) =>
<td
@ -62,7 +62,15 @@ export default {
{
!this.fixed && this.layout.scrollY && this.layout.gutterWidth ? <td class="gutter" /> : ''
}
</tr>
</tr>,
this.store.states.expandRows.indexOf(row) > -1
? (<tr>
<td colspan={ this.columns.length } class="el-table__expanded-cell">
{ this.$parent.renderExpanded ? this.$parent.renderExpanded.call(this._renderProxy, h, { row, $index, store: this.store, _self: this.$parent.$vnode.context }) : ''}
</td>
</tr>)
: ''
]
)
}
</tbody>
@ -95,6 +103,8 @@ export default {
const newRow = rows[data.indexOf(newVal)];
if (oldRow) {
oldRow.classList.remove('current-row');
} else if (rows) {
[].forEach.call(rows, row => row.classList.remove('current-row'));
}
if (newRow) {
newRow.classList.add('current-row');
@ -180,7 +190,7 @@ export default {
if (cell) {
const column = getColumnByCell(table, cell);
const hoverState = table.hoverState = { cell, column, row };
const hoverState = table.hoverState = {cell, column, row};
table.$emit('cell-mouse-enter', hoverState.row, hoverState.column, hoverState.cell, event);
}
@ -230,6 +240,10 @@ export default {
this.store.commit('setCurrentRow', row);
table.$emit('row-click', row, event, column);
},
handleExpandClick(row) {
this.store.commit('toggleRowExpanded', row);
}
}
};

View File

@ -16,6 +16,12 @@ const defaults = {
order: '',
className: 'el-table-column--selection'
},
expand: {
width: 48,
minWidth: 48,
realWidth: 48,
order: ''
},
index: {
width: 48,
minWidth: 48,
@ -48,6 +54,21 @@ const forced = {
return <div>{ $index + 1 }</div>;
},
sortable: false
},
expand: {
renderHeader: function(h, {}) {
return '';
},
renderCell: function(h, { row, store }, proxy) {
const expanded = store.states.expandRows.indexOf(row) > -1;
return <div class={ 'el-table__expand-icon ' + (expanded ? 'el-table__expand-icon--expanded' : '') }
on-click={ () => proxy.handleExpandClick(row) }>
<i class='el-icon el-icon-arrow-right'></i>
</div>;
},
sortable: false,
resizable: false,
className: 'el-table__expand-column'
}
};
@ -114,6 +135,7 @@ export default {
context: {},
columnKey: String,
align: String,
headerAlign: String,
showTooltipWhenOverflow: Boolean,
showOverflowTooltip: Boolean,
fixed: [Boolean, String],
@ -199,6 +221,7 @@ export default {
isColumnGroup,
context: this.context,
align: this.align ? 'is-' + this.align : null,
headerAlign: this.headerAlign ? 'is-' + this.headerAlign : (this.align ? 'is-' + this.align : null),
sortable: this.sortable,
sortMethod: this.sortMethod,
resizable: this.resizable,
@ -217,9 +240,35 @@ export default {
objectAssign(column, forced[type] || {});
this.columnConfig = column;
let renderCell = column.renderCell;
let _self = this;
if (type === 'expand') {
owner.renderExpanded = function(h, data) {
if (_self.$vnode.data.inlineTemplate) {
data._self = _self.context || data._self;
if (Object.prototype.toString.call(data._self) === '[object Object]') {
for (let prop in data._self) {
if (!data.hasOwnProperty(prop)) {
data[prop] = data._self[prop];
}
}
}
data._staticTrees = _self._staticTrees;
data.$options.staticRenderFns = _self.$options.staticRenderFns;
return _self.customRender.call(data);
}
};
column.renderCell = function(h, data) {
return <div class="cell">{ renderCell(h, data, this._renderProxy) }</div>;
};
return;
}
column.renderCell = function(h, data) {
if (_self.$vnode.data.inlineTemplate) {
renderCell = function() {
@ -254,8 +303,6 @@ export default {
</el-tooltip>
: <div class="cell">{ renderCell(h, data) }</div>;
};
this.columnConfig = column;
},
destroyed() {
@ -300,6 +347,12 @@ export default {
}
},
headerAlign(newVal) {
if (this.columnConfig) {
this.columnConfig.headerAlign = newVal ? 'is-' + newVal : this.align;
}
},
width(newVal) {
if (this.columnConfig) {
this.columnConfig.width = newVal;

View File

@ -103,7 +103,7 @@ export default {
on-mouseout={ this.handleMouseOut }
on-mousedown={ ($event) => this.handleMouseDown($event, column) }
on-click={ ($event) => this.handleClick($event, column) }
class={ [column.id, column.order, column.align, column.className || '', rowIndex === 0 && this.isCellHidden(cellIndex) ? 'is-hidden' : '', !column.children ? 'is-leaf' : ''] }>
class={ [column.id, column.order, column.headerAlign, column.className || '', rowIndex === 0 && this.isCellHidden(cellIndex) ? 'is-hidden' : '', !column.children ? 'is-leaf' : ''] }>
<div class={ ['cell', column.filteredValue && column.filteredValue.length > 0 ? 'highlight' : ''] }>
{
column.renderHeader
@ -112,9 +112,9 @@ export default {
}
{
column.sortable
? <span class="caret-wrapper" on-click={ ($event) => this.handleHeaderClick($event, column) }>
<i class="sort-caret ascending"></i>
<i class="sort-caret descending"></i>
? <span class="caret-wrapper">
<i class="sort-caret ascending" on-click={ ($event) => this.handleHeaderClick($event, column, 'ascending')}></i>
<i class="sort-caret descending" on-click={ ($event) => this.handleHeaderClick($event, column, 'descending')}></i>
</span>
: ''
}
@ -334,7 +334,7 @@ export default {
document.body.style.cursor = '';
},
handleHeaderClick(event, column) {
handleHeaderClick(event, column, order) {
let target = event.target;
while (target && target.tagName !== 'TH') {
target = target.parentNode;
@ -362,15 +362,14 @@ export default {
sortProp = column.property;
}
if (!column.order) {
sortOrder = column.order = 'ascending';
} else if (column.order === 'ascending') {
sortOrder = column.order = 'descending';
} else {
if (column.order === order) {
sortOrder = column.order = null;
states.sortingColumn = null;
sortProp = null;
} else {
sortOrder = column.order = order;
}
states.sortProp = sortProp;
states.sortOrder = sortOrder;

View File

@ -69,7 +69,9 @@ const TableStore = function(table, initialState = {}) {
selectable: null,
currentRow: null,
hoverRow: null,
filters: {}
filters: {},
expandRows: [],
defaultExpandAll: false
};
for (let prop in initialState) {
@ -85,6 +87,15 @@ TableStore.prototype.mutations = {
states._data = data;
states.data = sortData((data || []), states);
states.data.forEach((item) => {
if (!item.$extra) {
Object.defineProperty(item, '$extra', {
value: {},
enumerable: false
});
}
});
this.updateCurrentRow();
if (!states.reserveSelection) {
@ -114,6 +125,11 @@ TableStore.prototype.mutations = {
}
}
const defaultExpandAll = states.defaultExpandAll;
if (defaultExpandAll) {
this.states.expandRows = (states.data || []).slice(0);
}
Vue.nextTick(() => this.table.updateScrollY());
},
@ -218,6 +234,26 @@ TableStore.prototype.mutations = {
this.updateAllSelected();
},
toggleRowExpanded: function(states, row, expanded) {
const expandRows = states.expandRows;
if (typeof expanded !== 'undefined') {
const index = expandRows.indexOf(row);
if (expanded) {
if (index === -1) expandRows.push(row);
} else {
if (index !== -1) expandRows.splice(index, 1);
}
} else {
const index = expandRows.indexOf(row);
if (index === -1) {
expandRows.push(row);
} else {
expandRows.splice(index, 1);
}
}
this.table.$emit('expand', row, expandRows.indexOf(row) !== -1);
},
toggleAllSelection: debounce(10, function(states) {
const data = states.data || [];
const value = !states.isAllSelected;
@ -286,6 +322,22 @@ TableStore.prototype.clearSelection = function() {
}
};
TableStore.prototype.setExpandRowKeys = function(rowKeys) {
const expandRows = [];
const data = this.states.data;
const rowKey = this.states.rowKey;
if (!rowKey) throw new Error('[Table] prop row-key should not be empty.');
const keysMap = getKeysMap(data, rowKey);
rowKeys.forEach((key) => {
const info = keysMap[key];
if (info) {
expandRows.push(info.row);
}
});
this.states.expandRows = expandRows;
};
TableStore.prototype.toggleRowSelection = function(row, selected) {
const changed = toggleRowSelection(this.states, row, selected);
if (changed) {
@ -395,6 +447,8 @@ TableStore.prototype.commit = function(name, ...args) {
const mutations = this.mutations;
if (mutations[name]) {
mutations[name].apply(this, [this.states].concat(args));
} else {
throw new Error(`Action not found: ${name}`);
}
};

View File

@ -164,7 +164,11 @@
highlightCurrentRow: Boolean,
emptyText: String
emptyText: String,
expandRowKeys: Array,
defaultExpandAll: Boolean
},
components: {
@ -356,6 +360,10 @@
handler(val) {
this.store.commit('setData', val);
}
},
expandRowKeys(newVal) {
this.store.setExpandRowKeys(newVal);
}
},
@ -372,7 +380,8 @@
data() {
const store = new TableStore(this, {
rowKey: this.rowKey
rowKey: this.rowKey,
defaultExpandAll: this.defaultExpandAll
});
const layout = new TableLayout({
store,
@ -383,6 +392,7 @@
return {
store,
layout,
renderExpanded: null,
resizeProxyVisible: false
};
}

View File

@ -1,3 +1,10 @@
<template>
<div class="el-tab-pane">
<div class="el-tab-pane__content" v-show="active">
<slot></slot>
</div>
</div>
</template>
<script>
module.exports = {
name: 'el-tab-pane',
@ -12,73 +19,33 @@
data() {
return {
counter: 0,
transition: '',
paneStyle: {
position: 'relative'
},
isClosable: null,
index: ''
index: null
};
},
created() {
const propsData = this.$options.propsData;
if (propsData && typeof propsData.closable !== 'undefined') {
this.isClosable = propsData.closable === '' || propsData.closable;
} else {
this.isClosable = this.$parent.closable;
}
if (!this.index) {
this.index = this.$parent.$children.indexOf(this) + 1 + '';
}
if (this.$parent.panes) {
this.$parent.panes.push(this);
computed: {
isClosable() {
return this.closable || this.$parent.closable;
},
active() {
return this.$parent.currentName === (this.name || this.index);
}
},
computed: {
show() {
return this.$parent.currentName === this.index;
}
created() {
this.$parent.$forceUpdate();
},
destroyed() {
if (this.$el && this.$el.parentNode) {
this.$el.parentNode.removeChild(this.$el);
}
const panes = this.$parent.panes;
if (panes) {
panes.splice(this, panes.indexOf(this));
}
},
watch: {
name: {
immediate: true,
handler(val) {
this.index = val;
}
},
closable(val) {
this.isClosable = val;
},
'$parent.currentName'(newValue, oldValue) {
if (this.index === newValue) {
this.transition = newValue > oldValue ? 'slideInRight' : 'slideInLeft';
}
if (this.index === oldValue) {
this.transition = oldValue > newValue ? 'slideInRight' : 'slideInLeft';
}
},
label() {
this.$parent.$forceUpdate();
}
}
};
</script>
<template>
<div class="el-tab-pane" v-show="show && $slots.default">
<slot></slot>
</div>
</template>

View File

@ -4,69 +4,84 @@
props: {
type: String,
tabPosition: String,
activeName: String,
closable: false,
tabWidth: 0
closable: {
type: Boolean,
default: false
},
value: {}
},
data() {
return {
children: null,
activeTab: null,
currentName: 0,
panes: []
currentName: this.value || this.activeName
};
},
watch: {
activeName: {
handler(val) {
this.currentName = val;
}
activeName(val) {
this.currentName = val;
},
value(val) {
this.currentName = val;
},
currentName(val) {
this.$emit('input', val);
}
},
methods: {
handleTabRemove(tab, event) {
event.stopPropagation();
let tabs = this.$children;
const tabs = this.$children;
var index = tabs.indexOf(tab);
tab.$destroy(true);
let index = tabs.indexOf(tab);
tab.$destroy();
if (tab.index === this.currentName) {
let nextChild = tabs[index];
let prevChild = tabs[index - 1];
while (prevChild && prevChild.disabled) {
prevChild = tabs[tabs.indexOf(prevChild) - 1];
}
this.currentName = nextChild
? nextChild.index
: prevChild
? prevChild.index
: '-1';
}
this.$emit('tab-remove', tab);
this.$forceUpdate();
},
handleTabClick(tab, event) {
if (tab.disabled) return;
this.currentName = tab.index;
this.$emit('tab-click', tab, event);
},
calcBarStyle() {
if (this.type || !this.$refs.tabs) return {};
var style = {};
var offset = 0;
var tabWidth = 0;
this.$children.every((panel, index) => {
this.$nextTick(_ => {
if (tab.active) {
let nextChild = tabs[index];
let prevChild = tabs[index - 1];
let nextActiveTab = nextChild || prevChild || null;
if (nextActiveTab) {
this.currentName = nextActiveTab.name || nextActiveTab.index;
}
}
});
},
handleTabClick(tab, tabName, event) {
if (tab.disabled) return;
this.currentName = tabName;
this.$emit('tab-click', tab, event);
}
},
mounted() {
this.$forceUpdate();
},
render(h) {
let {
type,
handleTabRemove,
handleTabClick,
currentName
} = this;
const getBarStyle = () => {
if (this.type || !this.$refs.tabs) return {};
let style = {};
let offset = 0;
let tabWidth = 0;
this.$children.every((tab, index) => {
let $el = this.$refs.tabs[index];
if (!$el) { return false; }
if (panel.index !== this.currentName) {
if (!tab.active) {
offset += $el.clientWidth;
return true;
} else {
@ -79,51 +94,45 @@
style.transform = `translateX(${offset}px)`;
return style;
}
},
mounted() {
this.currentName = this.activeName || this.$children[0] && this.$children[0].index || '1';
this.$nextTick(() => {
this.$forceUpdate();
});
},
render(h) {
let {
type,
panes, // eslint-disable-line
handleTabRemove,
handleTabClick,
currentName
} = this;
const barStyle = this.calcBarStyle();
const activeBar = !type
? <div class="el-tabs__active-bar" style={barStyle}></div>
: null;
};
const tabs = this.$children.map((tab, index) => {
let btnClose = h('span', {
class: {
'el-icon-close': true
},
on: { click: (ev) => { handleTabRemove(tab, ev); } }
});
const _tab = h('div', {
class: {
'el-tabs__item': true,
'is-active': currentName === tab.index,
'is-disabled': tab.disabled,
'is-closable': tab.isClosable
},
ref: 'tabs',
refInFor: true,
on: { click: (ev) => { handleTabClick(tab, ev); } }
}, [
tab.labelContent ? tab.labelContent.call(this._renderProxy, h, tab) : tab.label,
tab.isClosable ? btnClose : null,
index === 0 ? activeBar : null
]);
return _tab;
let tabName = tab.name || tab.index || index;
if (currentName === undefined && index === 0) {
this.currentName = tabName;
}
tab.index = index;
const activeBar = !type && index === 0
? <div class="el-tabs__active-bar" style={getBarStyle()}></div>
: null;
const btnClose = tab.isClosable
? <span class="el-icon-close" on-click={(ev) => { handleTabRemove(tab, ev); }}></span>
: null;
const tabLabelContent = tab.labelContent
? tab.labelContent.call(this._renderProxy, h, tab)
: tab.label;
return (
<div
class={{
'el-tabs__item': true,
'is-active': tab.active,
'is-disabled': tab.disabled,
'is-closable': tab.isClosable
}}
ref="tabs"
refInFor
on-click={(ev) => { handleTabClick(tab, tabName, ev); }}
>
{tabLabelContent}
{btnClose}
{activeBar}
</div>
);
});
return (
<div class={{

View File

@ -1,6 +1,6 @@
{
"name": "element-theme-default",
"version": "1.0.8",
"version": "1.0.9",
"description": "Element component default theme.",
"main": "lib/index.css",
"style": "lib/index.css",

View File

@ -19,6 +19,83 @@
line-height: 1;
position: relative;
vertical-align: middle;
@when disabled {
.el-checkbox__inner {
background-color: var(--checkbox-disabled-input-fill);
border-color: var(--checkbox-disabled-input-border-color);
cursor: not-allowed;
&::after {
cursor: not-allowed;
border-color: var(--checkbox-disabled-icon-color);
}
& + .el-checkbox__label {
cursor: not-allowed;
}
}
&.is-checked {
.el-checkbox__inner {
background-color: var(--checkbox-disabled-checked-input-fill);
border-color: var(--checkbox-disabled-checked-input-border-color);
&::after {
border-color: var(--checkbox-disabled-checked-icon-color);
}
}
}
&.is-indeterminate {
.el-checkbox__inner {
background-color: var(--checkbox-disabled-checked-input-fill);
border-color: var(--checkbox-disabled-checked-input-border-color);
&::before {
border-color: var(--checkbox-disabled-checked-icon-color);
}
}
}
& + .el-checkbox__label {
color: var(--disabled-color-base);
cursor: not-allowed;
}
}
@when checked {
.el-checkbox__inner {
background-color: var(--checkbox-checked-input-fill);
border-color: var(--checkbox-checked-input-border-color);
&::after {
transform: rotate(45deg) scaleY(1);
}
}
}
@when focus {
.el-checkbox__inner {
border-color: var(--checkbox-input-border-color-hover);
}
}
@when indeterminate {
.el-checkbox__inner {
background-color: var(--checkbox-checked-input-fill);
border-color: var(--checkbox-checked-input-border-color);
&::before {
content: '';
position: absolute;
display: block;
border: 1px solid var(--checkbox-checked-icon-color);
margin-top: -1px;
left: 3px;
right: 3px;
top: 50%;
}
&::after {
display: none;
}
}
}
}
@e inner {
display: inline-block;
@ -31,7 +108,7 @@
transition: border-color .25s cubic-bezier(.71,-.46,.29,1.46),
background-color .25s cubic-bezier(.71,-.46,.29,1.46);
&:not(.is-disabled):hover {
&:hover {
border-color: var(--checkbox-input-border-color-hover);
}
@ -50,72 +127,6 @@
transition: transform .15s cubic-bezier(.71,-.46,.88,.6) .05s;
transform-origin: center;
}
@when disabled {
background-color: var(--checkbox-disabled-input-fill);
border-color: var(--checkbox-disabled-input-border-color);
cursor: not-allowed;
&::after {
cursor: not-allowed;
border-color: var(--checkbox-disabled-icon-color);
}
& + .el-checkbox__label {
cursor: not-allowed;
}
}
@when checked {
background-color: var(--checkbox-checked-input-fill);
border-color: var(--checkbox-checked-input-border-color);
&::after {
transform: rotate(45deg) scaleY(1);
}
}
@when focus {
border-color: var(--checkbox-input-border-color-hover);
}
&.is-disabled.is-checked {
background-color: var(--checkbox-disabled-checked-input-fill);
border-color: var(--checkbox-disabled-checked-input-border-color);
&::after {
border-color: var(--checkbox-disabled-checked-icon-color);
}
}
@when indeterminate {
background-color: var(--checkbox-checked-input-fill);
border-color: var(--checkbox-checked-input-border-color);
&::before {
content: '';
position: absolute;
display: block;
border: 1px solid var(--checkbox-checked-icon-color);
margin-top: -1px;
left: 3px;
right: 3px;
top: 50%;
}
&::after {
display: none;
}
}
&.is-disabled.is-indeterminate {
background-color: var(--checkbox-disabled-checked-input-fill);
border-color: var(--checkbox-disabled-checked-input-border-color);
&::before {
border-color: var(--checkbox-disabled-checked-icon-color);
}
}
}
@e original {

View File

@ -7,26 +7,41 @@
}
@for $i from 1 to 24 {
.el-col-$i,
.el-col-xs-$i {
.el-col-$i {
width: calc(1 / 24 * $(i) * 100)%;
}
.el-col-offset-$i,
.el-col-xs-offset-$i {
.el-col-offset-$i {
margin-left: calc(1 / 24 * $(i) * 100)%;
}
.el-col-pull-$i,
.el-col-xs-pull-$i {
.el-col-pull-$i {
position: relative;
right: calc(1 / 24 * $(i) * 100)%;
}
.el-col-push-$i,
.el-col-xs-push-$i {
.el-col-push-$i {
position: relative;
left: calc(1 / 24 * $(i) * 100)%;
}
}
@media (max-width: 768px) {
@for $i from 1 to 24 {
.el-col-xs-$i {
width: calc(1 / 24 * $(i) * 100)%;
}
.el-col-xs-offset-$i {
margin-left: calc(1 / 24 * $(i) * 100)%;
}
.el-col-xs-pull-$i {
position: relative;
right: calc(1 / 24 * $(i) * 100)%;
}
.el-col-xs-push-$i {
position: relative;
left: calc(1 / 24 * $(i) * 100)%;
}
}
}
@media (min-width: 768px) {
@for $i from 1 to 24 {
.el-col-sm-$i {

View File

@ -23,9 +23,14 @@
color: #99A9BF;
cursor: pointer;
position: absolute;
z-index: 1;
&:hover {
color: var(--color-primary);
&:not(.is-disabled) ~ .el-input .el-input__inner:not(.is-disabled) {
border-color: var(--input-focus-border);
}
}
@when disabled {

View File

@ -7,6 +7,7 @@
white-space: nowrap;
padding: 2px 5px;
color: var(--pagination-color);
@utils-clearfix;
span,
button {

View File

@ -24,6 +24,54 @@
line-height: 1;
position: relative;
vertical-align: middle;
@when disabled {
.el-radio__inner {
background-color: var(--radio-disabled-input-fill);
border-color: var(--radio-disabled-input-border-color);
cursor: not-allowed;
&::after {
cursor: not-allowed;
background-color: var(--radio-disabled-icon-color);
}
& + .el-radio__label {
cursor: not-allowed;
}
}
&.is-checked {
.el-radio__inner {
background-color: var(--radio-disabled-checked-input-fill);
border-color: var(--radio-disabled-checked-input-border-color);
&::after {
background-color: var(--radio-disabled-checked-icon-color);
}
}
}
& + .el-radio__label {
color: var(--disabled-color-base);
cursor: not-allowed;
}
}
@when checked {
.el-radio__inner {
border-color: var(--radio-checked-input-border-color);
background: var(--radio-checked-icon-color);
&::after {
transform: translate(-50%, -50%) scale(1);
}
}
}
@when focus {
.el-radio__inner {
border-color: var(--radio-input-border-color-hover);
}
}
}
@e inner {
border: var(--radio-input-border);
@ -34,7 +82,7 @@
display: inline-block;
box-sizing: border-box;
&:not(.is-disabled):hover {
&:hover {
border-color: var(--radio-input-border-color-hover);
}
@ -47,43 +95,6 @@
transform: translate(-50%, -50%) scale(0);
transition: transform .15s cubic-bezier(.71,-.46,.88,.6);
}
@when disabled {
background-color: var(--radio-disabled-input-fill);
border-color: var(--radio-disabled-input-border-color);
cursor: not-allowed;
&::after {
cursor: not-allowed;
background-color: var(--radio-disabled-icon-color);
}
& + .el-radio__label {
cursor: not-allowed;
}
}
@when checked {
border-color: var(--radio-checked-input-border-color);
background: var(--radio-checked-icon-color);
&::after {
transform: translate(-50%, -50%) scale(1);
}
}
@when focus {
border-color: var(--radio-input-border-color-hover);
}
&.is-disabled.is-checked {
background-color: var(--radio-disabled-checked-input-fill);
border-color: var(--radio-disabled-checked-input-border-color);
&::after {
background-color: var(--radio-disabled-checked-icon-color);
}
}
}
@e original {

View File

@ -66,6 +66,44 @@
color: #5e6d82;
}
@e expand-column {
.cell {
padding: 0;
text-align: center;
}
}
@e expand-icon {
position: relative;
cursor: pointer;
color: #666;
font-size: 12px;
transition: transform 0.2s ease-in-out;
height: 40px;
@m expanded {
transform: rotate(90deg);
}
> .el-icon {
position: absolute;
left: 50%;
top: 50%;
margin-left: -5px;
margin-top: -5px;
}
}
@e expanded-cell {
padding: 20px 50px;
background-color: #f9fafc;
box-shadow: inset 0 2px 0 #f4f4f4;
&:hover {
background-color: #f9fafc !important;
}
}
@modifier fit {
border-right: 0;
border-bottom: 0;

View File

@ -147,10 +147,6 @@
}
}
}
@b tab-pane {
width: 100%;
display: inline-block;
}
}
.slideInRight-transition,

View File

@ -133,12 +133,12 @@
this.tree.$emit('current-change', store.currentNode ? store.currentNode.data : null, store.currentNode);
this.tree.currentNode = this;
if (this.tree.expandOnClickNode) {
this.handleExpandIconClick(event);
this.handleExpandIconClick();
}
this.tree.$emit('node-click', this.node.data, this.node, this);
},
handleExpandIconClick(event) {
handleExpandIconClick() {
if (this.expanded) {
this.node.collapse();
} else {

View File

@ -141,7 +141,7 @@ if (typeof window !== 'undefined' && window.Vue) {
};
module.exports = {
version: '1.0.8',
version: '1.0.9',
locale: locale.use,
install,
Loading,

View File

@ -91,6 +91,31 @@ describe('DatePicker', () => {
}, DELAY);
});
it('disabled clear value', done => {
vm = createVue({
template: `
<el-date-picker v-model="value" ref="compo" :clearable="false"></el-date-picker>
`,
data() {
return { value: '' };
}
}, true);
const input = vm.$el.querySelector('input');
input.focus();
setTimeout(_ => {
const $el = vm.$refs.compo.picker.$el;
$el.querySelector('td.available').click();
vm.$nextTick(_ => {
vm.$el.querySelector('.el-input__icon').click();
setTimeout(_ => {
expect(vm.value).to.be.exist;
done();
}, DELAY);
});
}, DELAY);
});
it('reset', done => {
vm = createVue({
template: `
@ -120,6 +145,46 @@ describe('DatePicker', () => {
}, DELAY);
});
it('change event', done => {
let inputValue;
vm = createVue({
template: `
<el-date-picker
ref="compo"
v-model="value"
format="yyyy-MM"
@change="handleChange" />`,
methods: {
handleChange(val) {
inputValue = val;
}
},
data() {
return { value: '' };
}
}, true);
const input = vm.$el.querySelector('input');
input.blur();
input.focus();
setTimeout(_ => {
const picker = vm.$refs.compo.picker;
picker.$el.querySelector('td.available').click();
vm.$nextTick(_ => {
const date = picker.date;
expect(inputValue).to.equal(`${date.getFullYear()}-${date.getMonth() + 1 }`);
done();
});
}, DELAY);
});
describe('keydown', () => {
let input;
let keyDown = function(el, keyCode) {

View File

@ -38,22 +38,14 @@ describe('InputNumber', () => {
let input = vm.$el.querySelector('input');
let btnDecrease = vm.$el.querySelector('.el-input-number__decrease');
triggerEvent(btnDecrease, 'mouseenter');
triggerEvent(btnDecrease, 'mousedown');
triggerEvent(document, 'mouseup');
setTimeout(_ => {
expect(vm.$el.querySelector('.el-input.is-active')).to.exist;
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(4);
expect(input.value).to.be.equal('4');
triggerEvent(btnDecrease, 'mouseleave');
vm.$nextTick(_ => {
expect(vm.$el.querySelector('.el-input.is-active')).to.not.exist;
done();
});
}, 300);
done();
});
});
it('increase', done => {
vm = createVue({
@ -74,11 +66,11 @@ describe('InputNumber', () => {
triggerEvent(btnIncrease, 'mousedown');
triggerEvent(document, 'mouseup');
setTimeout(_ => {
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(2.5);
expect(input.value).to.be.equal('2.5');
done();
}, 100);
});
});
it('disabled', done => {
vm = createVue({
@ -100,19 +92,19 @@ describe('InputNumber', () => {
triggerEvent(btnDecrease, 'mousedown');
triggerEvent(document, 'mouseup');
triggerEvent(btnIncrease, 'mousedown');
triggerEvent(document, 'mouseup');
setTimeout(_ => {
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(2);
expect(input.value).to.be.equal('2');
setTimeout(_ => {
triggerEvent(btnIncrease, 'mousedown');
triggerEvent(document, 'mouseup');
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(2);
expect(input.value).to.be.equal('2');
done();
}, 100);
}, 100);
});
});
});
it('step', done => {
vm = createVue({
@ -134,19 +126,19 @@ describe('InputNumber', () => {
triggerEvent(btnIncrease, 'mousedown');
triggerEvent(document, 'mouseup');
setTimeout(_ => {
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(8.2);
expect(input.value).to.be.equal('8.2');
triggerEvent(btnDecrease, 'mousedown');
triggerEvent(document, 'mouseup');
setTimeout(_ => {
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(5);
expect(input.value).to.be.equal('5');
done();
}, 100);
}, 100);
});
});
});
it('min', done => {
vm = createVue({
@ -181,11 +173,11 @@ describe('InputNumber', () => {
triggerEvent(btnDecrease, 'mousedown');
triggerEvent(document, 'mouseup');
setTimeout(_ => {
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(6);
expect(input.value).to.be.equal('6');
done();
}, 100);
});
});
it('max', done => {
vm = createVue({
@ -220,11 +212,11 @@ describe('InputNumber', () => {
triggerEvent(btnIncrease, 'mousedown');
triggerEvent(document, 'mouseup');
setTimeout(_ => {
vm.$nextTick(_ => {
expect(vm.value).to.be.equal(8);
expect(input.value).to.be.equal('8');
done();
}, 100);
});
});
it('controls', () => {
vm = createVue({
@ -242,4 +234,30 @@ describe('InputNumber', () => {
expect(vm.$el.querySelector('.el-input-number__decrease')).to.not.exist;
expect(vm.$el.querySelector('.el-input-number__increase')).to.not.exist;
});
it('event:change', done => {
vm = createVue({
template: `
<el-input-number v-model="value" ref="input">
</el-input-number>
`,
data() {
return {
value: 1.5
};
}
}, true);
let btnIncrease = vm.$el.querySelector('.el-input-number__increase');
const spy = sinon.spy();
vm.$refs.input.$on('change', spy);
triggerEvent(btnIncrease, 'mousedown');
triggerEvent(document, 'mouseup');
vm.$nextTick(_ => {
expect(spy.withArgs(2.5, 1.5).calledOnce).to.be.true;
done();
});
});
});

View File

@ -104,6 +104,7 @@ describe('Input', () => {
}, true);
expect(vm.$el.querySelector('.el-textarea__inner').getAttribute('rows')).to.be.equal('3');
});
it('autosize', done => {
vm = createVue({
template: `
@ -143,4 +144,57 @@ describe('Input', () => {
done();
}, 200);
});
describe('Input Events', () => {
it('event:focus & blur', done => {
vm = createVue({
template: `
<el-input
ref="input"
placeholder="请输入内容"
value="input">
</el-input>
`
}, true);
const spyFocus = sinon.spy();
const spyBlur = sinon.spy();
vm.$refs.input.$on('focus', spyFocus);
vm.$refs.input.$on('blur', spyBlur);
vm.$el.querySelector('input').focus();
vm.$el.querySelector('input').blur();
vm.$nextTick(_ => {
expect(spyFocus.calledOnce).to.be.true;
expect(spyBlur.calledOnce).to.be.true;
done();
});
});
it('event:change', done => {
vm = createVue({
template: `
<el-input
ref="input"
placeholder="请输入内容"
:value="input">
</el-input>
`,
data() {
return {
input: 'a'
};
}
}, true);
const spy = sinon.spy();
vm.$refs.input.$on('change', spy);
vm.input = 'b';
vm.$nextTick(_ => {
expect(spy.withArgs('b').calledOnce).to.be.true;
done();
});
});
});
});

View File

@ -40,6 +40,18 @@ describe('Pagination', () => {
expect(elm.querySelector('.el-pagination__total')).to.not.exist;
});
it('layout: all in right, need clear float', () => {
vm = createTest(Pagination, {
layout: '->, prev, pager, next',
total: 100
}, true);
const elm = vm.$el;
let right_div = elm.querySelector('.el-pagination__rightwrapper');
expect(elm.clientHeight > 0 && right_div.clientHeight > 0).to.equal(true);
// elm padding , 使 >=
expect(elm.clientHeight >= right_div.clientHeight).to.equal(true);
});
it('custom slot', () => {
vm = createVue({
template: `

View File

@ -8,11 +8,11 @@ const toArray = function(obj) {
const getTestData = function() {
return [
{ name: 'Toy Story', release: '1995-11-22', director: 'John Lasseter', runtime: 80 },
{ name: 'A Bug\'s Life', release: '1998-11-25', director: 'John Lasseter', runtime: 95 },
{ name: 'Toy Story 2', release: '1999-11-24', director: 'John Lasseter', runtime: 92 },
{ name: 'Monsters, Inc.', release: '2001-11-2', director: 'Peter Docter', runtime: 92 },
{ name: 'Finding Nemo', release: '2003-5-30', director: 'Andrew Stanton', runtime: 100 }
{ id: 1, name: 'Toy Story', release: '1995-11-22', director: 'John Lasseter', runtime: 80 },
{ id: 2, name: 'A Bug\'s Life', release: '1998-11-25', director: 'John Lasseter', runtime: 95 },
{ id: 3, name: 'Toy Story 2', release: '1999-11-24', director: 'John Lasseter', runtime: 92 },
{ id: 4, name: 'Monsters, Inc.', release: '2001-11-2', director: 'Peter Docter', runtime: 92 },
{ id: 5, name: 'Finding Nemo', release: '2003-5-30', director: 'Andrew Stanton', runtime: 100 }
];
};
@ -27,6 +27,7 @@ describe('Table', () => {
const vm = createVue({
template: `
<el-table :data="testData">
<el-table-column prop="id" />
<el-table-column prop="name" label="片名" />
<el-table-column prop="release" label="发行日期" />
<el-table-column prop="director" label="导演" />
@ -843,12 +844,12 @@ describe('Table', () => {
vm.$el.querySelectorAll('.el-checkbox')[1].click();
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-checkbox__inner.is-checked')).to.length(1);
expect(vm.$el.querySelectorAll('.el-checkbox__input.is-checked')).to.length(1);
// go to second page
vm.testData = getData(1);
setTimeout(_ => {
// expect no checked
expect(vm.$el.querySelectorAll('.el-checkbox__inner.is-checked')).to.length(0);
expect(vm.$el.querySelectorAll('.el-checkbox__input.is-checked')).to.length(0);
// click first checkbox
vm.$el.querySelectorAll('.el-checkbox')[1].click();
vm.$el.querySelectorAll('.el-checkbox')[2].click();
@ -856,11 +857,11 @@ describe('Table', () => {
// back first page
vm.testData = getData();
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-checkbox__inner.is-checked')).to.length(1);
expect(vm.$el.querySelectorAll('.el-checkbox__input.is-checked')).to.length(1);
// clear
vm.$refs.table.clearSelection();
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-checkbox__inner.is-checked')).to.length(0);
expect(vm.$el.querySelectorAll('.el-checkbox__input.is-checked')).to.length(0);
destroyVM(vm);
done();
}, DELAY);
@ -957,6 +958,90 @@ describe('Table', () => {
}, DELAY);
});
});
describe('= expand', () => {
const createInstance = function(extra) {
extra = extra || '';
return createVue({
template: `
<el-table row-key="id" :data="testData" @expand="handleExpand" ${extra}>
<el-table-column type="expand" inline-template>
<div>{{row.name}}</div>
</el-table-column>
<el-table-column prop="release" label="release" />
<el-table-column prop="director" label="director" />
<el-table-column prop="runtime" label="runtime" />
</el-table>
`,
created() {
this.testData = getTestData();
},
data() {
return { expandCount: 0, expandRowKeys: [] };
},
methods: {
handleExpand() {
this.expandCount++;
}
}
}, true);
};
it('works', done => {
const vm = createInstance();
setTimeout(_ => {
expect(vm.$el.querySelectorAll('td.el-table__expand-column').length).to.equal(5);
destroyVM(vm);
done();
}, DELAY);
});
it('should expand when click icon', done => {
const vm = createInstance();
setTimeout(_ => {
vm.$el.querySelector('td.el-table__expand-column .el-table__expand-icon').click();
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(1);
expect(vm.expandCount).to.equal(1);
vm.$el.querySelector('td.el-table__expand-column .el-table__expand-icon').click();
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(0);
expect(vm.expandCount).to.equal(2);
destroyVM(vm);
done();
}, DELAY);
}, DELAY);
}, DELAY);
});
it('should set expanded rows using expandRowKeys', done => {
const vm = createInstance(':expand-row-keys="expandRowKeys"');
setTimeout(_ => {
vm.expandRowKeys = [1, 3];
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(2);
vm.expandRowKeys = [2];
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(1);
destroyVM(vm);
done();
}, DELAY);
}, DELAY);
}, DELAY);
});
it('should default-expand-all when default-expand-all is true', done => {
const vm = createInstance('default-expand-all');
setTimeout(_ => {
expect(vm.$el.querySelectorAll('.el-table__expanded-cell').length).to.equal(5);
destroyVM(vm);
done();
}, DELAY);
});
});
});
describe('sortable', () => {
@ -981,7 +1066,7 @@ describe('Table', () => {
});
setTimeout(_ => {
const elm = vm.$el.querySelector('.caret-wrapper');
const elm = vm.$el.querySelector('.caret-wrapper > .ascending');
elm.click();
setTimeout(_ => {
@ -1003,7 +1088,7 @@ describe('Table', () => {
}
}, '@sort-change="sortChange"');
setTimeout(_ => {
const elm = vm.$el.querySelector('.caret-wrapper');
const elm = vm.$el.querySelector('.caret-wrapper > .ascending');
elm.click();
setTimeout(_ => {
@ -1019,7 +1104,7 @@ describe('Table', () => {
const vm = createTable('', '', '', 'sortable');
it('ascending', done => {
const elm = vm.$el.querySelector('.caret-wrapper');
const elm = vm.$el.querySelector('.caret-wrapper > .ascending');
elm.click();
setTimeout(_ => {
@ -1031,7 +1116,7 @@ describe('Table', () => {
});
it('descending', done => {
const elm = vm.$el.querySelector('.caret-wrapper');
const elm = vm.$el.querySelector('.caret-wrapper > .descending');
elm.click();
setTimeout(_ => {
@ -1215,6 +1300,43 @@ describe('Table', () => {
}, DELAY);
});
it('header-align', (done) => {
const vm = createVue({
template: `
<el-table :data="testData">
<el-table-column prop="name" :align="align" :header-align="headerAlign"/>
</el-table>
`,
data() {
return {
align: 'left',
headerAlign: null
};
},
created() {
this.testData = getTestData();
}
}, true);
setTimeout(() => {
expect(vm.$el.querySelectorAll('.el-table__header th.is-left').length > 0).to.be.true;
expect(vm.$el.querySelectorAll('.el-table__header td.is-center').length === 0).to.be.true;
vm.align = 'right';
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.el-table__header th.is-right').length > 0).to.be.true;
expect(vm.$el.querySelectorAll('.el-table__header td.is-center').length === 0).to.be.true;
vm.headerAlign = 'center';
vm.$nextTick(() => {
expect(vm.$el.querySelectorAll('.el-table__header th.is-right').length === 0).to.be.true;
expect(vm.$el.querySelectorAll('.el-table__header td.is-center').length > 0).to.be.true;
});
});
done();
}, DELAY);
});
it('width', (done) => {
const vm = createVue({
template: `

View File

@ -188,7 +188,7 @@ describe('Tabs', () => {
it('closable edge', done => {
vm = createVue({
template: `
<el-tabs type="card" :closable="true">
<el-tabs type="card" :closable="true" ref="tabs">
<el-tab-pane label="用户管理">A</el-tab-pane>
<el-tab-pane label="配置管理">B</el-tab-pane>
<el-tab-pane label="角色管理">C</el-tab-pane>
@ -199,7 +199,7 @@ describe('Tabs', () => {
let tabList = vm.$el.querySelector('.el-tabs__header').children;
let paneList = vm.$el.querySelector('.el-tabs__content').children;
setTimeout(_ => {
vm.$nextTick(_ => {
tabList[0].querySelector('.el-icon-close').click();
vm.$nextTick(_ => {
expect(tabList.length).to.be.equal(3);
@ -209,16 +209,16 @@ describe('Tabs', () => {
tabList[2].click();
tabList[2].querySelector('.el-icon-close').click();
vm.$nextTick(_ => {
setTimeout(_ => {
expect(tabList.length).to.be.equal(2);
expect(paneList.length).to.be.equal(2);
expect(tabList[1].classList.contains('is-active')).to.be.true;
expect(tabList[1].innerText.trim()).to.be.equal('角色管理');
expect(paneList[1].innerText.trim()).to.be.equal('C');
done();
});
}, 100);
});
}, 100);
});
});
it('tab title render function', done => {
vm = createVue({

530
yarn.lock

File diff suppressed because it is too large Load Diff