mirror of
https://gitee.com/ElemeFE/element.git
synced 2024-12-02 04:08:10 +08:00
commit
5baa8774cb
@ -29,6 +29,7 @@
|
||||
- [awesome-element](https://github.com/ElementUI/awesome-element)
|
||||
- [FAQ](./FAQ.md)
|
||||
- [Customize Theme](http://element.eleme.io/#/en-US/component/custom-theme)
|
||||
- [Preview and generate theme online](https://elementui.github.io/theme-preview)
|
||||
- Starter Kit
|
||||
- [element-starter](https://github.com/ElementUI/element-starter)
|
||||
- [element-cooking-starter](https://github.com/ElementUI/element-cooking-starter)
|
||||
|
35
build/bin/build-locale.js
Normal file
35
build/bin/build-locale.js
Normal file
@ -0,0 +1,35 @@
|
||||
var fs = require('fs');
|
||||
var save = require('file-save');
|
||||
var resolve = require('path').resolve;
|
||||
var basename = require('path').basename;
|
||||
var localePath = resolve(__dirname, '../../src/locale/lang');
|
||||
var fileList = fs.readdirSync(localePath);
|
||||
|
||||
var transform = function(filename, name, cb) {
|
||||
require('babel-core').transformFile(resolve(localePath, filename), {
|
||||
plugins: [
|
||||
'add-module-exports',
|
||||
['transform-es2015-modules-umd', {loose: true}]
|
||||
],
|
||||
moduleId: name
|
||||
}, cb);
|
||||
};
|
||||
|
||||
fileList.forEach(function(file) {
|
||||
var name = basename(file, '.js');
|
||||
|
||||
transform(file, name, function(err, result) {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
var code = result.code;
|
||||
|
||||
code = code
|
||||
.replace('define(\'', 'define(\'element/locale/')
|
||||
.replace('global.', 'global.ELEMENT.lang = global.ELEMENT.lang || {}; \n global.ELEMENT.lang.');
|
||||
save(resolve(__dirname, '../../lib/umd/locale', file)).write(code);
|
||||
|
||||
console.log(file);
|
||||
}
|
||||
});
|
||||
});
|
@ -58,5 +58,6 @@
|
||||
"scrollbar": "./packages/scrollbar/index.js",
|
||||
"carousel-item": "./packages/carousel-item/index.js",
|
||||
"collapse": "./packages/collapse/index.js",
|
||||
"collapse-item": "./packages/collapse-item/index.js"
|
||||
"collapse-item": "./packages/collapse-item/index.js",
|
||||
"cascader": "./packages/cascader/index.js"
|
||||
}
|
||||
|
487
examples/docs/en-US/cascader.md
Normal file
487
examples/docs/en-US/cascader.md
Normal file
@ -0,0 +1,487 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
optionsWithDisabled: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
disabled: true,
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
selectedOptions: [],
|
||||
selectedOptions2: ['jiangsu', 'nanjing', 'zhonghuamen']
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.demo-cascader {
|
||||
.el-cascader {
|
||||
width: 222px;
|
||||
}
|
||||
}
|
||||
.demo-cascader-size {
|
||||
.el-cascader {
|
||||
vertical-align: top;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
## Cascader
|
||||
|
||||
It's used to select from a set of associated data set. Such as province/city/district, company level, and categories.
|
||||
|
||||
### Basic usage
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
v-model="selectedOptions"
|
||||
@change="handleChange"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Disabled option
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="optionsWithDisabled"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
optionsWithDisabled: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
disabled: true,
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Default Value
|
||||
|
||||
:::demo default value is assigned by an array type value.
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
v-model="selectedOptions2"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
selectedOptions2: ['jiangsu', 'nanjing', 'zhonghuamen']
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Size
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<div class="demo-cascader-size">
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
size="large"
|
||||
></el-cascader>
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
></el-cascader>
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
size="small"
|
||||
></el-cascader>
|
||||
</div>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Hover to expand
|
||||
|
||||
Hover to expand the next level options, click to select option.
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
expand-trigger="hover"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Change on select
|
||||
|
||||
Allow only select parent options.
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
change-on-select
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Search
|
||||
|
||||
Search and select options directly.
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="Please select"
|
||||
:options="options"
|
||||
filterable
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Attributes
|
||||
| Attribute | Description | Type | Options | Default|
|
||||
|---------- |-------------------- |---------|------------- |-------- |
|
||||
| options | data source of the options | array | — | — |
|
||||
| value | selected value | array | — | — |
|
||||
| popper-class | className of popup overlay | string | — | — |
|
||||
| placeholder | input placeholder | string | — | — |
|
||||
| disabled | 是否禁用 | boolean | — | false |
|
||||
| clearable | whether allow clear | boolean | — | false |
|
||||
| expand-trigger | trigger mode of expandind the current item | string | click / hover | 'click' |
|
||||
| filterable | whether the options can be searched | boolean | — | — |
|
||||
| size | size | string | large / small / mini | — |
|
||||
|
||||
### Events
|
||||
| Event Name | Description | Parameters |
|
||||
|---------- |-------- |---------- |
|
||||
| change | triggers when the binding value changes | value |
|
@ -1,5 +1,15 @@
|
||||
## Custom theme
|
||||
Element uses BEM-styled CSS so that you can override styles easily. But if you need to replace styles at a large scale, e.g. change the theme color from blue to orange or green, maybe overriding them one by one is not a good idea, and this is where our theme customization tool kicks in.
|
||||
Element uses BEM-styled CSS so that you can override styles easily. But if you need to replace styles at a large scale, e.g. change the theme color from blue to orange or green, maybe overriding them one by one is not a good idea, and this is where our theme customization tools kick in.
|
||||
|
||||
## Changing theme color
|
||||
If you just want to change the theme color of Element, the [theme preview website](https://elementui.github.io/theme-preview/#/en-US) is recommended.
|
||||
|
||||
The main color of Element is bright and friendly blue. By changing it, you can make Element more visually connected to specific projects.
|
||||
|
||||
The above website enables you to preview theme of a new theme color in real-time, and it can generate a complete style package based on the new theme color for you to download directly (to import new style files in your project, please refer to the 'Import custom theme' or 'Import component theme on demand' part of this section).
|
||||
|
||||
## More customizations
|
||||
If you need more customization than just changing the theme color, please follow these steps:
|
||||
|
||||
### Install related tool
|
||||
First install the theme generator globally or locally. Local install is recommended because in this way, when others clone your project, npm will automatically install it for them.
|
||||
|
@ -164,6 +164,7 @@ The content of Dialog can be anything, even a table or a form. This example show
|
||||
| size | size of Dialog | string | tiny/small/large/full | small |
|
||||
| top | value for `top` of Dialog CSS, works when `size` is not `full` | string | — | 15% |
|
||||
| modal | whether a mask is displayed | boolean | — | true |
|
||||
| modal-append-to-body | whether to append modal to body element. If false, the modal will be appended to Dialog's parent element | boolean | — | true |
|
||||
| lock-scroll | whether scroll of body is disabled while Dialog is displayed | boolean | — | true |
|
||||
| custom-class | custom class names for Dialog | string | — | — |
|
||||
| close-on-click-modal | whether the Dialog can be closed by clicking the mask | boolean | — | true |
|
||||
|
@ -72,6 +72,33 @@ Vue.use(Element, {
|
||||
})
|
||||
```
|
||||
|
||||
## Import via CDN
|
||||
|
||||
```html
|
||||
<script src="//unpkg.com/vue"></script>
|
||||
<script src="//unpkg.com/element-ui"></script>
|
||||
<script src="//unpkg.com/element-ui/lib/umd/locale/en.js"></script>
|
||||
|
||||
<script>
|
||||
ELEMENT.locale(ELEMENT.lang.en)
|
||||
</script>
|
||||
```
|
||||
|
||||
Compatible with `vue-i18n`
|
||||
|
||||
```html
|
||||
<script src="//unpkg.com/vue"></script>
|
||||
<script src="//unpkg.com/vue-i18n/dist/vue-i18n.js"></script>
|
||||
<script src="//unpkg.com/element-ui"></script>
|
||||
<script src="//unpkg.com/element-ui/lib/umd/locale/zh-CN.js"></script>
|
||||
<script src="//unpkg.com/element-ui/lib/umd/locale/en.js"></script>
|
||||
|
||||
<script>
|
||||
Vue.locale('en', ELEMENT.lang.en)
|
||||
Vue.locale('zh-cn', ELEMENT.lang.zhCN)
|
||||
</script>
|
||||
```
|
||||
|
||||
Currently Element ships with the following languages:
|
||||
<ul class="language-list">
|
||||
<li>Simplified Chinese (zh-CN)</li>
|
||||
|
@ -120,7 +120,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-autocomplete__suggestions.my-autocomplete {
|
||||
.el-autocomplete-suggestion.my-autocomplete {
|
||||
li {
|
||||
line-height: normal;
|
||||
padding: 7px *;
|
||||
@ -133,7 +133,6 @@
|
||||
font-size: 12px;
|
||||
color: #b4b4b4;
|
||||
}
|
||||
|
||||
.highlighted .addr {
|
||||
color: #ddd;
|
||||
}
|
||||
|
@ -65,7 +65,7 @@ Radio should not have too many options. Otherwise, use the Select component inst
|
||||
|
||||
### Radio button group
|
||||
|
||||
Suitable for choosing from some mutually exclusive options.
|
||||
Suitable for choosing from some mutually exclusive options.
|
||||
|
||||
:::demo Combine `<el-radio-group>` with `<el-radio>` to display a radio group. Bind a variable with `v-model` of `<el-radio-group>` element and set label value in `<el-radio>`. It also provides `change` event with the current value as its parameter.
|
||||
|
||||
@ -131,15 +131,15 @@ Radio with button styles.
|
||||
|
||||
### Radio Attributes
|
||||
|
||||
Attribute | Description | Type | Accepted Values | Default
|
||||
Attribute | Description | Type | Accepted Values | Default
|
||||
---- | ---- | ---- | ---- | ----
|
||||
label | the value of radio | string/number | — | —
|
||||
label | the value of radio | string/number/boolean | — | —
|
||||
disabled | whether radio is disabled | boolean | — | false
|
||||
name | native 'name' attribute | string | — | —
|
||||
name | native 'name' attribute | string | — | —
|
||||
|
||||
### Radio-group Attributes
|
||||
|
||||
Attribute | Description | Type | Accepted Values | Default
|
||||
Attribute | Description | Type | Accepted Values | Default
|
||||
---- | ---- | ---- | ---- | ----
|
||||
size | the size of radio buttons | string | large/small | —
|
||||
fill | border and background color when button is active | string | — | #20a0ff |
|
||||
@ -153,7 +153,7 @@ change | triggers when the bound value changes | the label value of the chosen r
|
||||
|
||||
### Radio-button Attributes
|
||||
|
||||
Attribute | Description | Type | Accepted Values | Default
|
||||
Attribute | Description | Type | Accepted Values | Default
|
||||
---- | ---- | ---- | ---- | ----
|
||||
label | the value of radio | string/number | — | —
|
||||
disabled | whether radio is disabled | boolean | — | false
|
||||
|
@ -114,7 +114,7 @@ Vertical step bars.
|
||||
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| space | the spacing of each step, will be responsive if omitted | Number | — | — |
|
||||
| space | the spacing of each step, will be responsive if omitted. Support percentage. | Number,String | — | — |
|
||||
| direction | display direction | string | vertical/horizontal | horizontal |
|
||||
| active | current activation step | number | — | 0 |
|
||||
| process-status | status of current step | string | wait/process/finish/error/success | process |
|
||||
|
@ -2,9 +2,20 @@
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
activeName: 'first',
|
||||
activeName: 'second',
|
||||
activeName2: 'first',
|
||||
tabs: [{
|
||||
editableTabsValue: '2',
|
||||
editableTabsValue2: '2',
|
||||
editableTabs: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
editableTabs2: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
@ -17,11 +28,62 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRemove(tab) {
|
||||
console.log(tab);
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
console.log(tab, event);
|
||||
},
|
||||
handleTabsEdit(targetName, action) {
|
||||
if (action === 'add') {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue = newTabName;
|
||||
}
|
||||
if (action === 'remove') {
|
||||
let tabs = this.editableTabs;
|
||||
let activeName = this.editableTabsValue;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue = activeName;
|
||||
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
},
|
||||
addTab(targetName) {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs2.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue2 = newTabName;
|
||||
},
|
||||
removeTab(targetName) {
|
||||
let tabs = this.editableTabs2;
|
||||
let activeName = this.editableTabsValue2;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue2 = activeName;
|
||||
this.editableTabs2 = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,37 +157,6 @@ Tabs styled as cards.
|
||||
```
|
||||
:::
|
||||
|
||||
### Closable
|
||||
|
||||
Closable tabs.
|
||||
|
||||
:::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>
|
||||
<el-tabs type="card" :closable="true" @tab-click="handleClick" @tab-remove="handleRemove">
|
||||
<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>
|
||||
<el-tab-pane label="Task">Task</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
handleRemove(tab) {
|
||||
console.log(tab);
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
console.log(tab, event);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
|
||||
:::
|
||||
|
||||
### Border card
|
||||
|
||||
Border card tabs.
|
||||
@ -161,11 +192,151 @@ You can use named slot to customize the tab label content.
|
||||
```
|
||||
:::
|
||||
|
||||
### Add & close tab
|
||||
|
||||
Only card type Tabs support addable & closeable.
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-tabs v-model="editableTabsValue" type="card" editable @edit="handleTabsEdit">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in editableTabs"
|
||||
:label="item.title"
|
||||
:name="item.name"
|
||||
>
|
||||
{{item.content}}
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
editableTabsValue: '2',
|
||||
editableTabs: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
tabIndex: 2
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTabsEdit(targetName, action) {
|
||||
if (action === 'add') {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue = newTabName;
|
||||
}
|
||||
if (action === 'remove') {
|
||||
let tabs = this.editableTabs;
|
||||
let activeName = this.editableTabsValue;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue = activeName;
|
||||
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Customized trigger button of new tab
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<div style="margin-bottom: 20px;">
|
||||
<el-button
|
||||
size="small"
|
||||
@click="addTab(editableTabsValue2)"
|
||||
>
|
||||
add tab
|
||||
</el-button>
|
||||
</div>
|
||||
<el-tabs v-model="editableTabsValue2" type="card" closable @tab-remove="removeTab">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in editableTabs2"
|
||||
:label="item.title"
|
||||
:name="item.name"
|
||||
>
|
||||
{{item.content}}
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
editableTabsValue2: '2',
|
||||
editableTabs2: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
tabIndex: 2
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addTab(targetName) {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs2.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue2 = newTabName;
|
||||
},
|
||||
removeTab(targetName) {
|
||||
let tabs = this.editableTabs2;
|
||||
let activeName = this.editableTabsValue2;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue2 = activeName;
|
||||
this.editableTabs2 = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Tabs Attributes
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| type | type of Tab | string | card/border-card | — |
|
||||
| closable | whether Tab is closable | boolean | — | false |
|
||||
| addable | whether Tab is addable | boolean | — | false |
|
||||
| editable | whether Tab is addable and closable | boolean | — | false |
|
||||
| active-name(deprecated) | name of the selected tab | string | — | name of first tab |
|
||||
| value | name of the selected tab | string | — | name of first tab |
|
||||
|
||||
@ -173,7 +344,9 @@ You can use named slot to customize the tab label content.
|
||||
| Event Name | Description | Parameters |
|
||||
|---------- |-------- |---------- |
|
||||
| tab-click | triggers when a tab is clicked | clicked tab |
|
||||
| tab-remove | triggers when a tab is removed | removed tab |
|
||||
| tab-remove | triggers when tab-remove button is clicked | name of the removed tab |
|
||||
| tab-add | triggers when tab-add button is clicked | — |
|
||||
| edit | triggers when tab-add button or tab-remove is clicked | (targetName, action) |
|
||||
|
||||
### Tab-pane Attributes
|
||||
| Attribute | Description | Type | Accepted Values | Default |
|
||||
|
@ -22,6 +22,9 @@
|
||||
|
||||
showInput() {
|
||||
this.inputVisible = true;
|
||||
this.$nextTick(_ => {
|
||||
this.$refs.saveTagInput.$refs.input.focus();
|
||||
});
|
||||
},
|
||||
|
||||
handleInputConfirm() {
|
||||
@ -150,6 +153,9 @@ You can use the `close` event to add and remove tag dynamically.
|
||||
|
||||
showInput() {
|
||||
this.inputVisible = true;
|
||||
this.$nextTick(_ => {
|
||||
this.$refs.saveTagInput.$refs.input.focus();
|
||||
});
|
||||
},
|
||||
|
||||
handleInputConfirm() {
|
||||
|
491
examples/docs/zh-CN/cascader.md
Normal file
491
examples/docs/zh-CN/cascader.md
Normal file
@ -0,0 +1,491 @@
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
optionsWithDisabled: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
disabled: true,
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: [],
|
||||
selectedOptions2: ['jiangsu', 'nanjing', 'zhonghuamen']
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.demo-cascader {
|
||||
.el-cascader {
|
||||
width: 222px;
|
||||
}
|
||||
}
|
||||
.demo-cascader-size {
|
||||
.el-cascader {
|
||||
vertical-align: top;
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
## 级联选择
|
||||
|
||||
需要从一组相关联的数据集合进行选择,例如省市区,公司层级,事物分类等。
|
||||
|
||||
从一个较大的数据集合中进行选择时,用多级分类进行分隔,方便选择。
|
||||
|
||||
### 基本使用
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
v-model="selectedOptions"
|
||||
@change="handleChange"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleChange(value) {
|
||||
console.log(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### 禁用选项
|
||||
|
||||
通过在数据源中设置 `disabled` 字段来声明该选项时禁用的
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="optionsWithDisabled"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
optionsWithDisabled: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
disabled: true,
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### 默认值
|
||||
|
||||
:::demo 默认值通过数组的方式指定。
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
v-model="selectedOptions2"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}],
|
||||
selectedOptions2: ['jiangsu', 'nanjing', 'zhonghuamen']
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### 尺寸
|
||||
|
||||
:::demo 提供三种尺寸的级联选择器
|
||||
```html
|
||||
<div class="demo-cascader-size">
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
size="large"
|
||||
></el-cascader>
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
></el-cascader>
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
size="small"
|
||||
></el-cascader>
|
||||
</div>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### 移入展开
|
||||
|
||||
在鼠标移入时就展开下级菜单,完成选择仍需要进行点击。
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
expand-trigger="hover"
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### 选择即改变
|
||||
|
||||
该模式下允许只选中父级选项。
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
change-on-select
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### 可搜索
|
||||
|
||||
可以直接搜索选项并选择。
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-cascader
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
filterable
|
||||
></el-cascader>
|
||||
<script>
|
||||
module.exports = {
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake',
|
||||
}],
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei',
|
||||
}],
|
||||
}],
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men',
|
||||
}],
|
||||
}],
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### Attributes
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| options | 可选项数据源 | array | — | — |
|
||||
| value | 指定选中项 | array | — | — |
|
||||
| popper-class | 自定义浮层类名 | string | — | — |
|
||||
| placeholder | 输入框占位文本 | string | — | — |
|
||||
| disabled | 是否禁用 | boolean | — | false |
|
||||
| clearable | 是否支持清除 | boolean | — | false |
|
||||
| expand-trigger | 次级菜单的展开方式 | string | click / hover | 'click' |
|
||||
| filterable | 是否支持搜索选项 | boolean | — | — |
|
||||
| size | 尺寸 | string | large / small / mini | — |
|
||||
|
||||
### Events
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
|---------- |-------- |---------- |
|
||||
| change | 当绑定值变化时触发的事件 | 当前值 |
|
@ -1,5 +1,17 @@
|
||||
## 自定义主题
|
||||
Element 默认提供一套主题,CSS 命名采用 BEM 的风格方便使用者覆盖样式。如果你想完全替换主题色或者部分样式,可以使用下面方法。
|
||||
Element 默认提供一套主题,CSS 命名采用 BEM 的风格方便使用者覆盖样式。如果你想完全替换主题色或者部分样式,可以使用下面的方法。
|
||||
|
||||
## 仅替换主题色
|
||||
如果仅希望更换 Element 的主题色,推荐使用[在线主题生成工具](https://elementui.github.io/theme-preview)。
|
||||
|
||||
Element 默认的主题色是鲜艳、友好的蓝色。通过替换主题色,能够让 Element 的视觉更加符合具体项目的定位。
|
||||
|
||||
使用上述工具,可以很方便地实时预览主题色改变之后的视觉,同时它还可以基于新的主题色生成完整的样式文件包,供直接下载使用(关于如何使用下载的主题包,请参考本节「引入自定义主题」和「搭配插件按需引入组件主题」部分)。
|
||||
|
||||
如果希望进行除主题色之外的样式定制,请继续阅读。
|
||||
|
||||
## 深层次的定制
|
||||
如果仅仅改变主题色不能满足你的需求,请按以下步骤进行更深层次的主题定制:
|
||||
|
||||
### 安装工具
|
||||
首先安装「主题生成工具」,可以全局安装或者安装在当前项目下,推荐安装在项目里,方便别人 clone 项目时能直接安装依赖并启动,这里以全局安装做演示。
|
||||
|
@ -183,6 +183,7 @@ Dialog 组件的内容可以是任意的,甚至可以是表格或表单,下
|
||||
| size | Dialog 的大小 | string | tiny/small/large/full | small |
|
||||
| top | Dialog CSS 中的 top 值(仅在 size 不为 full 时有效) | string | — | 15% |
|
||||
| modal | 是否需要遮罩层 | boolean | — | true |
|
||||
| modal-append-to-body | 遮罩层是否插入至 body 元素上,若为 false,则遮罩层会插入至 Dialog 的父元素上 | boolean | — | true |
|
||||
| lock-scroll | 是否在 Dialog 出现时将 body 滚动锁定 | boolean | — | true |
|
||||
| custom-class | Dialog 的自定义类名 | string | — | — |
|
||||
| close-on-click-modal | 是否可以通过点击 modal 关闭 Dialog | boolean | — | true |
|
||||
|
@ -84,6 +84,33 @@ Vue.use(Element, {
|
||||
})
|
||||
```
|
||||
|
||||
## 通过 CDN 的方式加载语言文件
|
||||
|
||||
```html
|
||||
<script src="//unpkg.com/vue"></script>
|
||||
<script src="//unpkg.com/element-ui"></script>
|
||||
<script src="//unpkg.com/element-ui/lib/umd/locale/en.js"></script>
|
||||
|
||||
<script>
|
||||
ELEMENT.locale(ELEMENT.lang.en)
|
||||
</script>
|
||||
```
|
||||
|
||||
搭配 `vue-i18n` 使用
|
||||
|
||||
```html
|
||||
<script src="//unpkg.com/vue"></script>
|
||||
<script src="//unpkg.com/vue-i18n/dist/vue-i18n.js"></script>
|
||||
<script src="//unpkg.com/element-ui"></script>
|
||||
<script src="//unpkg.com/element-ui/lib/umd/locale/zh-CN.js"></script>
|
||||
<script src="//unpkg.com/element-ui/lib/umd/locale/en.js"></script>
|
||||
|
||||
<script>
|
||||
Vue.locale('en', ELEMENT.lang.en)
|
||||
Vue.locale('zh-cn', ELEMENT.lang.zhCN)
|
||||
</script>
|
||||
```
|
||||
|
||||
目前 Element 内置了以下语言:
|
||||
<ul class="language-list">
|
||||
<li>简体中文(zh-CN)</li>
|
||||
|
@ -161,7 +161,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.el-autocomplete__suggestions.my-autocomplete {
|
||||
.el-autocomplete-suggestion.my-autocomplete {
|
||||
li {
|
||||
line-height: normal;
|
||||
padding: 7px *;
|
||||
@ -174,7 +174,6 @@
|
||||
font-size: 12px;
|
||||
color: #b4b4b4;
|
||||
}
|
||||
|
||||
.highlighted .addr {
|
||||
color: #ddd;
|
||||
}
|
||||
|
@ -137,7 +137,7 @@
|
||||
### Radio Attributes
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| label | Radio 的 value | string,number | — | — |
|
||||
| label | Radio 的 value | string,number,boolean | — | — |
|
||||
| disabled | 是否禁用 | boolean | — | false |
|
||||
| name | 原生 name 属性 | string | — | — |
|
||||
|
||||
|
@ -108,7 +108,7 @@
|
||||
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| space | 每个 step 的间距,不填写将自适应间距 | Number | — | — |
|
||||
| space | 每个 step 的间距,不填写将自适应间距。支持百分比。 | Number,String | — | — |
|
||||
| direction | 显示方向 | string | vertical/horizontal | horizontal |
|
||||
| active | 设置当前激活步骤 | number | — | 0 |
|
||||
| process-status | 设置当前步骤的状态 | string | wait/process/finish/error/success | process |
|
||||
|
@ -4,7 +4,18 @@
|
||||
return {
|
||||
activeName: 'second',
|
||||
activeName2: 'first',
|
||||
tabs: [{
|
||||
editableTabsValue: '2',
|
||||
editableTabsValue2: '2',
|
||||
editableTabs: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
editableTabs2: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
@ -17,11 +28,62 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleRemove(tab) {
|
||||
console.log(tab);
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
console.log(tab, event);
|
||||
},
|
||||
handleTabsEdit(targetName, action) {
|
||||
if (action === 'add') {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue = newTabName;
|
||||
}
|
||||
if (action === 'remove') {
|
||||
let tabs = this.editableTabs;
|
||||
let activeName = this.editableTabsValue;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue = activeName;
|
||||
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
},
|
||||
addTab(targetName) {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs2.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue2 = newTabName;
|
||||
},
|
||||
removeTab(targetName) {
|
||||
let tabs = this.editableTabs2;
|
||||
let activeName = this.editableTabsValue2;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue2 = activeName;
|
||||
this.editableTabs2 = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -95,36 +157,6 @@
|
||||
```
|
||||
:::
|
||||
|
||||
### 可关闭
|
||||
|
||||
可以关闭标签页。
|
||||
|
||||
:::demo 通过设置 `closable` 属性来打开 `Tabs` 的可关闭标签效果, `closable` 也可以设置在 `Tab Panel` 中实现部分标签页的可关闭效果。
|
||||
|
||||
```html
|
||||
<template>
|
||||
<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>
|
||||
<el-tab-pane label="定时任务补偿">定时任务补偿</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
handleRemove(tab) {
|
||||
console.log(tab);
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
console.log(tab, event);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
### 卡片化
|
||||
|
||||
卡片化的标签页。
|
||||
@ -158,18 +190,141 @@
|
||||
```
|
||||
:::
|
||||
|
||||
### 动态增加标签页
|
||||
### 动态增减标签页
|
||||
|
||||
展示如何通过触发器来动态增加标签页
|
||||
增减标签页按钮只能在选项卡样式的标签页下使用
|
||||
|
||||
:::demo
|
||||
```html
|
||||
<el-tabs v-model="editableTabsValue" type="card" editable @edit="handleTabsEdit">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in editableTabs"
|
||||
:label="item.title"
|
||||
:name="item.name"
|
||||
>
|
||||
{{item.content}}
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
editableTabsValue: '2',
|
||||
editableTabs: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
tabIndex: 2
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleTabsEdit(targetName, action) {
|
||||
if (action === 'add') {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue = newTabName;
|
||||
}
|
||||
if (action === 'remove') {
|
||||
let tabs = this.editableTabs;
|
||||
let activeName = this.editableTabsValue;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue = activeName;
|
||||
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
||||
<el-button
|
||||
size="small"
|
||||
@click="addTab(editableTabsValue2)"
|
||||
>
|
||||
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 v-model="editableTabsValue2" type="card" closable @tab-remove="removeTab">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in editableTabs2"
|
||||
:label="item.title"
|
||||
:name="item.name"
|
||||
>
|
||||
{{item.content}}
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<script>
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
editableTabsValue2: '2',
|
||||
editableTabs2: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
tabIndex: 2
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
addTab(targetName) {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs2.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue2 = newTabName;
|
||||
},
|
||||
removeTab(targetName) {
|
||||
let tabs = this.editableTabs2;
|
||||
let activeName = this.editableTabsValue2;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
this.editableTabsValue2 = activeName;
|
||||
this.editableTabs2 = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
:::
|
||||
|
||||
@ -178,14 +333,18 @@
|
||||
|---------- |-------- |---------- |------------- |-------- |
|
||||
| type | 风格类型 | string | card/border-card | — |
|
||||
| closable | 标签是否可关闭 | boolean | — | false |
|
||||
| addable | 标签是否可增加 | boolean | — | false |
|
||||
| editable | 标签是否同时可增加和关闭 | boolean | — | false |
|
||||
| active-name(deprecated) | 选中选项卡的 name | string | — | 第一个选项卡的 name |
|
||||
| value | 绑定值,选中选项卡的 name | string | — | 第一个选项卡的 name |
|
||||
|
||||
### Tabs Events
|
||||
| 事件名称 | 说明 | 回调参数 |
|
||||
|---------- |-------- |---------- |
|
||||
| tab-click | tab 被选中的钩子 | 被选中的标签 tab 实例 |
|
||||
| tab-remove | tab 被删除的钩子 | 被删除的标签 tab 实例 |
|
||||
| tab-click | tab 被选中时触发 | 被选中的标签 tab 实例 |
|
||||
| tab-remove | 点击 tab 移除按钮后触发 | 被删除的标签的 name |
|
||||
| tab-add | 点击 tabs 的新增按钮后触发 | — |
|
||||
| edit | 点击 tabs 的新增按钮或 tab 被关闭后触发 | (targetName, action) |
|
||||
|
||||
### Tab-pane Attributes
|
||||
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|
||||
|
@ -22,6 +22,9 @@
|
||||
|
||||
showInput() {
|
||||
this.inputVisible = true;
|
||||
this.$nextTick(_ => {
|
||||
this.$refs.saveTagInput.$refs.input.focus();
|
||||
});
|
||||
},
|
||||
|
||||
handleInputConfirm() {
|
||||
@ -150,6 +153,9 @@
|
||||
|
||||
showInput() {
|
||||
this.inputVisible = true;
|
||||
this.$nextTick(_ => {
|
||||
this.$refs.saveTagInput.$refs.input.focus();
|
||||
});
|
||||
},
|
||||
|
||||
handleInputConfirm() {
|
||||
|
@ -76,6 +76,10 @@
|
||||
"path": "/select",
|
||||
"title": "Select 选择器"
|
||||
},
|
||||
{
|
||||
"path": "/cascader",
|
||||
"title": "Cascader 级联选择"
|
||||
},
|
||||
{
|
||||
"path": "/switch",
|
||||
"title": "Switch 开关"
|
||||
@ -298,6 +302,10 @@
|
||||
"path": "/select",
|
||||
"title": "Select"
|
||||
},
|
||||
{
|
||||
"path": "/cascader",
|
||||
"title": "Cascader"
|
||||
},
|
||||
{
|
||||
"path": "/switch",
|
||||
"title": "Switch"
|
||||
|
@ -13,13 +13,14 @@
|
||||
"build:file": "node build/bin/iconInit.js & node build/bin/build-entry.js & node build/bin/i18n.js & node build/bin/version.js",
|
||||
"build:theme": "node build/bin/gen-cssfile && gulp build --gulpfile packages/theme-default/gulpfile.js && cp-cli packages/theme-default/lib lib/theme-default",
|
||||
"build:utils": "cross-env BABEL_ENV=utils babel src --out-dir lib --ignore src/index.js",
|
||||
"build:umd": "node build/bin/build-locale.js",
|
||||
"clean": "rimraf lib && rimraf packages/*/lib && rimraf test/**/coverage && lerna clean --yes",
|
||||
"deploy": "npm run deploy:build && gh-pages -d examples/element-ui --remote eleme && del examples/element-ui",
|
||||
"deploy:build": "npm run build:file && cooking build -c build/cooking.demo.js -p && echo element.eleme.io>>examples/element-ui/CNAME",
|
||||
"dev": "npm run bootstrap && npm run build:file && cooking watch -c build/cooking.demo.js -p",
|
||||
"dev:play": "npm run build:file && cross-env PLAY_ENV=true cooking watch -c build/cooking.demo.js -p",
|
||||
"dist": "npm run clean && npm run build:file && npm run lint && cooking build -c build/cooking.conf.js,build/cooking.common.js,build/cooking.component.js -p && npm run build:utils && npm run build:theme",
|
||||
"dist:all": "node build/bin/build-all.js",
|
||||
"dist": "npm run clean && npm run build:file && npm run lint && cooking build -c build/cooking.conf.js,build/cooking.common.js,build/cooking.component.js -p && npm run build:utils && npm run build:umd && npm run build:theme",
|
||||
"dist:all": "node build/bin/build-all.js && npm run build:theme",
|
||||
"i18n": "node build/bin/i18n.js",
|
||||
"lint": "eslint src/**/* test/**/* packages/**/*.{js,vue} build/**/* --quiet",
|
||||
"pub": "npm run bootstrap && sh build/git-release.sh && sh build/release.sh",
|
||||
@ -56,6 +57,7 @@
|
||||
"babel-cli": "^6.14.0",
|
||||
"babel-core": "^6.14.0",
|
||||
"babel-loader": "^6.2.5",
|
||||
"babel-plugin-add-module-exports": "^0.2.1",
|
||||
"babel-plugin-module-resolver": "^2.2.0",
|
||||
"babel-plugin-syntax-jsx": "^6.8.0",
|
||||
"babel-plugin-transform-vue-jsx": "^3.3.0",
|
||||
@ -113,7 +115,7 @@
|
||||
"uppercamelcase": "^1.1.0",
|
||||
"url-loader": "^0.5.7",
|
||||
"vue": "^2.1.8",
|
||||
"vue-loader": "^10.0.2",
|
||||
"vue-loader": "^10.3.0",
|
||||
"vue-markdown-loader": "^0.5.1",
|
||||
"vue-router": "^2.0.0",
|
||||
"vue-template-compiler": "^2.1.8",
|
||||
|
@ -1,30 +1,36 @@
|
||||
<template>
|
||||
<transition name="el-zoom-in-top" @after-leave="doDestroy">
|
||||
<ul
|
||||
<div
|
||||
v-show="showPopper"
|
||||
class="el-autocomplete__suggestions"
|
||||
class="el-autocomplete-suggestion"
|
||||
:class="{ 'is-loading': parent.loading }"
|
||||
:style="{ width: dropdownWidth }"
|
||||
>
|
||||
<li v-if="parent.loading"><i class="el-icon-loading"></i></li>
|
||||
<template v-for="(item, index) in suggestions" v-else>
|
||||
<li
|
||||
v-if="!parent.customItem"
|
||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||
@click="select(item)"
|
||||
>
|
||||
{{item.value}}
|
||||
</li>
|
||||
<component
|
||||
v-else
|
||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||
@click="select(item)"
|
||||
:is="parent.customItem"
|
||||
:item="item"
|
||||
:index="index">
|
||||
</component>
|
||||
</template>
|
||||
</ul>
|
||||
<el-scrollbar
|
||||
tag="ul"
|
||||
wrap-class="el-autocomplete-suggestion__wrap"
|
||||
view-class="el-autocomplete-suggestion__list"
|
||||
>
|
||||
<li v-if="parent.loading"><i class="el-icon-loading"></i></li>
|
||||
<template v-for="(item, index) in suggestions" v-else>
|
||||
<li
|
||||
v-if="!parent.customItem"
|
||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||
@click="select(item)"
|
||||
>
|
||||
{{item.value}}
|
||||
</li>
|
||||
<component
|
||||
v-else
|
||||
:class="{'highlighted': parent.highlightedIndex === index}"
|
||||
@click="select(item)"
|
||||
:is="parent.customItem"
|
||||
:item="item"
|
||||
:index="index">
|
||||
</component>
|
||||
</template>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
<script>
|
||||
|
@ -98,6 +98,10 @@
|
||||
},
|
||||
handleChange(value) {
|
||||
this.$emit('input', value);
|
||||
if (!this.triggerOnFocus && !value) {
|
||||
this.suggestions = [];
|
||||
return;
|
||||
}
|
||||
this.getData(value);
|
||||
},
|
||||
handleFocus() {
|
||||
@ -129,22 +133,22 @@
|
||||
},
|
||||
highlight(index) {
|
||||
if (!this.suggestionVisible || this.loading) { return; }
|
||||
if (index < 0) {
|
||||
index = 0;
|
||||
} else if (index >= this.suggestions.length) {
|
||||
if (index < 0) index = 0;
|
||||
if (index >= this.suggestions.length) {
|
||||
index = this.suggestions.length - 1;
|
||||
}
|
||||
var elSuggestions = this.$refs.suggestions.$el;
|
||||
const suggestion = this.$refs.suggestions.$el.querySelector('.el-autocomplete-suggestion__wrap');
|
||||
const suggestionList = suggestion.querySelectorAll('.el-autocomplete-suggestion__list li');
|
||||
|
||||
var elSelect = elSuggestions.children[index];
|
||||
var scrollTop = elSuggestions.scrollTop;
|
||||
var offsetTop = elSelect.offsetTop;
|
||||
let highlightItem = suggestionList[index];
|
||||
let scrollTop = suggestion.scrollTop;
|
||||
let offsetTop = highlightItem.offsetTop;
|
||||
|
||||
if (offsetTop + elSelect.scrollHeight > (scrollTop + elSuggestions.clientHeight)) {
|
||||
elSuggestions.scrollTop += elSelect.scrollHeight;
|
||||
if (offsetTop + highlightItem.scrollHeight > (scrollTop + suggestion.clientHeight)) {
|
||||
suggestion.scrollTop += highlightItem.scrollHeight;
|
||||
}
|
||||
if (offsetTop < scrollTop) {
|
||||
elSuggestions.scrollTop -= elSelect.scrollHeight;
|
||||
suggestion.scrollTop -= highlightItem.scrollHeight;
|
||||
}
|
||||
|
||||
this.highlightedIndex = index;
|
||||
|
18
packages/cascader/cooking.conf.js
Normal file
18
packages/cascader/cooking.conf.js
Normal file
@ -0,0 +1,18 @@
|
||||
var cooking = require('cooking');
|
||||
var path = require('path');
|
||||
var config = require('../../build/config');
|
||||
|
||||
cooking.set({
|
||||
entry: {
|
||||
index: path.join(__dirname, 'index.js')
|
||||
},
|
||||
dist: path.join(__dirname, 'lib'),
|
||||
template: false,
|
||||
format: 'umd',
|
||||
moduleName: 'ElCascader',
|
||||
extends: ['vue2'],
|
||||
alias: config.alias,
|
||||
externals: { vue: config.vue }
|
||||
});
|
||||
|
||||
module.exports = cooking.resolve();
|
8
packages/cascader/index.js
Normal file
8
packages/cascader/index.js
Normal file
@ -0,0 +1,8 @@
|
||||
import Cascader from './src/main';
|
||||
|
||||
/* istanbul ignore next */
|
||||
Cascader.install = function(Vue) {
|
||||
Vue.component(Cascader.name, Cascader);
|
||||
};
|
||||
|
||||
export default Cascader;
|
15
packages/cascader/package.json
Normal file
15
packages/cascader/package.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "element-cascader",
|
||||
"version": "0.0.0",
|
||||
"description": "A cascader component for Vue.js.",
|
||||
"keywords": [
|
||||
"element",
|
||||
"vue",
|
||||
"component"
|
||||
],
|
||||
"main": "./lib/index.js",
|
||||
"repository": "https://github.com/ElemeFE/element/tree/master/packages/cascader",
|
||||
"author": "elemefe",
|
||||
"license": "MIT",
|
||||
"dependencies": {}
|
||||
}
|
243
packages/cascader/src/main.vue
Normal file
243
packages/cascader/src/main.vue
Normal file
@ -0,0 +1,243 @@
|
||||
<template>
|
||||
<span
|
||||
class="el-cascader"
|
||||
:class="[
|
||||
{
|
||||
'is-opened': menuVisible,
|
||||
'is-disabled': disabled
|
||||
},
|
||||
size ? 'el-cascader--' + size : ''
|
||||
]"
|
||||
@click="handleClick"
|
||||
@mouseenter="inputHover = true"
|
||||
@mouseleave="inputHover = false"
|
||||
ref="reference"
|
||||
v-clickoutside="handleClickoutside"
|
||||
>
|
||||
<el-input
|
||||
ref="input"
|
||||
:readonly="!filterable"
|
||||
:placeholder="displayValue ? undefined : placeholder"
|
||||
v-model="inputValue"
|
||||
@change="handleInputChange"
|
||||
:validate-event="false"
|
||||
:size="size"
|
||||
:disabled="disabled"
|
||||
>
|
||||
<template slot="icon">
|
||||
<i
|
||||
key="1"
|
||||
v-if="inputHover && displayValue !== ''"
|
||||
class="el-input__icon el-icon-circle-close el-cascader__clearIcon"
|
||||
@click="clearValue"
|
||||
></i>
|
||||
<i
|
||||
key="2"
|
||||
v-else
|
||||
class="el-input__icon el-icon-caret-bottom"
|
||||
:class="{ 'is-reverse': menuVisible }"
|
||||
></i>
|
||||
</template>
|
||||
</el-input>
|
||||
<span class="el-cascader__label" v-show="inputValue === ''">{{displayValue}}</span>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Vue from 'vue';
|
||||
import ElCascaderMenu from './menu';
|
||||
import ElInput from 'element-ui/packages/input';
|
||||
import Popper from 'element-ui/src/utils/vue-popper';
|
||||
import Clickoutside from 'element-ui/src/utils/clickoutside';
|
||||
import emitter from 'element-ui/src/mixins/emitter';
|
||||
import Locale from 'element-ui/src/mixins/locale';
|
||||
|
||||
const popperMixin = {
|
||||
props: {
|
||||
placement: {
|
||||
type: String,
|
||||
default: 'bottom-start'
|
||||
},
|
||||
appendToBody: Popper.props.appendToBody,
|
||||
offset: Popper.props.offset,
|
||||
boundariesPadding: Popper.props.boundariesPadding,
|
||||
popperOptions: Popper.props.popperOptions
|
||||
},
|
||||
methods: Popper.methods,
|
||||
data: Popper.data,
|
||||
beforeDestroy: Popper.beforeDestroy
|
||||
};
|
||||
|
||||
export default {
|
||||
name: 'ElCascader',
|
||||
|
||||
directives: { Clickoutside },
|
||||
|
||||
mixins: [popperMixin, emitter, Locale],
|
||||
|
||||
components: {
|
||||
ElInput
|
||||
},
|
||||
|
||||
props: {
|
||||
options: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: Array,
|
||||
default() {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
placeholder: String,
|
||||
disabled: Boolean,
|
||||
clearable: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
changeOnSelect: Boolean,
|
||||
popperClass: String,
|
||||
expandTrigger: {
|
||||
type: String,
|
||||
default: 'click'
|
||||
},
|
||||
filterable: Boolean,
|
||||
size: String
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
currentValue: this.value,
|
||||
displayValue: this.value.join('/'),
|
||||
menuVisible: false,
|
||||
inputHover: false,
|
||||
inputValue: '',
|
||||
flatOptions: this.filterable && this.flattenOptions(this.options)
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
menuVisible(value) {
|
||||
value ? this.showMenu() : this.hideMenu();
|
||||
},
|
||||
value(value) {
|
||||
this.currentValue = value;
|
||||
},
|
||||
currentValue(value) {
|
||||
this.displayValue = value.join('/');
|
||||
this.dispatch('ElFormItem', 'el.form.change', [value]);
|
||||
},
|
||||
options(value) {
|
||||
this.menu.options = value;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
showMenu() {
|
||||
if (!this.menu) {
|
||||
this.menu = new Vue(ElCascaderMenu).$mount();
|
||||
this.menu.options = this.options;
|
||||
this.menu.expandTrigger = this.expandTrigger;
|
||||
this.menu.changeOnSelect = this.changeOnSelect;
|
||||
this.menu.popperClass = this.popperClass;
|
||||
this.popperElm = this.menu.$el;
|
||||
}
|
||||
|
||||
this.menu.value = this.currentValue.slice(0);
|
||||
this.menu.visible = true;
|
||||
this.menu.options = this.options;
|
||||
this.menu.$on('pick', this.handlePick);
|
||||
this.updatePopper();
|
||||
this.$nextTick(_ => {
|
||||
this.menu.inputWidth = this.$refs.input.$el.offsetWidth - 2;
|
||||
});
|
||||
},
|
||||
hideMenu() {
|
||||
this.inputValue = '';
|
||||
this.menu.visible = false;
|
||||
},
|
||||
handlePick(value, close = true) {
|
||||
this.currentValue = value;
|
||||
this.$emit('input', value);
|
||||
this.$emit('change', value);
|
||||
|
||||
if (close) {
|
||||
this.menuVisible = false;
|
||||
}
|
||||
},
|
||||
handleInputChange(value) {
|
||||
if (!this.menuVisible) return;
|
||||
const flatOptions = this.flatOptions;
|
||||
|
||||
if (!value) {
|
||||
this.menu.options = this.options;
|
||||
return;
|
||||
}
|
||||
|
||||
let filteredFlatOptions = flatOptions.filter(optionsStack => {
|
||||
return optionsStack.some(option => option.label.indexOf(value) > -1);
|
||||
});
|
||||
|
||||
if (filteredFlatOptions.length > 0) {
|
||||
filteredFlatOptions = filteredFlatOptions.map(optionStack => {
|
||||
return {
|
||||
__IS__FLAT__OPTIONS: true,
|
||||
value: optionStack.map(item => item.value),
|
||||
label: this.renderFilteredOptionLabel(value, optionStack)
|
||||
};
|
||||
});
|
||||
} else {
|
||||
filteredFlatOptions = [{
|
||||
__IS__FLAT__OPTIONS: true,
|
||||
label: this.t('el.cascader.noMatch'),
|
||||
value: '',
|
||||
disabled: true
|
||||
}];
|
||||
}
|
||||
this.menu.options = filteredFlatOptions;
|
||||
},
|
||||
renderFilteredOptionLabel(inputValue, optionsStack) {
|
||||
return optionsStack.map(({ label }, index) => {
|
||||
const node = label.indexOf(inputValue) > -1 ? this.highlightKeyword(label, inputValue) : label;
|
||||
return index === 0 ? node : [' / ', node];
|
||||
});
|
||||
},
|
||||
highlightKeyword(label, keyword) {
|
||||
const h = this._c;
|
||||
return label.split(keyword)
|
||||
.map((node, index) => index === 0 ? node : [
|
||||
h('span', { class: { 'el-cascader-menu__item__keyword': true }}, [this._v(keyword)]),
|
||||
node
|
||||
]);
|
||||
},
|
||||
flattenOptions(options, ancestor = []) {
|
||||
let flatOptions = [];
|
||||
options.forEach((option) => {
|
||||
const optionsStack = ancestor.concat(option);
|
||||
if (!option.children) {
|
||||
flatOptions.push(optionsStack);
|
||||
} else {
|
||||
flatOptions = flatOptions.concat(this.flattenOptions(option.children, optionsStack));
|
||||
}
|
||||
});
|
||||
return flatOptions;
|
||||
},
|
||||
clearValue(ev) {
|
||||
ev.stopPropagation();
|
||||
this.handlePick([], true);
|
||||
},
|
||||
handleClickoutside() {
|
||||
this.menuVisible = false;
|
||||
},
|
||||
handleClick() {
|
||||
if (this.disabled) return;
|
||||
if (this.filterable) {
|
||||
this.menuVisible = true;
|
||||
return;
|
||||
}
|
||||
this.menuVisible = !this.menuVisible;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
148
packages/cascader/src/menu.vue
Normal file
148
packages/cascader/src/menu.vue
Normal file
@ -0,0 +1,148 @@
|
||||
<script>
|
||||
export default {
|
||||
name: 'ElCascaderMenu',
|
||||
|
||||
data() {
|
||||
return {
|
||||
inputWidth: 0,
|
||||
options: [],
|
||||
visible: false,
|
||||
activeValue: [],
|
||||
value: [],
|
||||
expandTrigger: 'click',
|
||||
changeOnSelect: false,
|
||||
popperClass: ''
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
visible(value) {
|
||||
if (value) {
|
||||
this.activeValue = this.value;
|
||||
}
|
||||
},
|
||||
value: {
|
||||
immediate: true,
|
||||
handler(value) {
|
||||
this.activeValue = value;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
activeOptions: {
|
||||
cache: false,
|
||||
get() {
|
||||
const activeValue = this.activeValue;
|
||||
|
||||
const loadActiveOptions = (options, activeOptions = []) => {
|
||||
const level = activeOptions.length;
|
||||
activeOptions[level] = options;
|
||||
let active = activeValue[level];
|
||||
if (active) {
|
||||
options = options.filter(option => option.value === active)[0];
|
||||
if (options && options.children) {
|
||||
loadActiveOptions(options.children, activeOptions);
|
||||
}
|
||||
}
|
||||
return activeOptions;
|
||||
};
|
||||
|
||||
return loadActiveOptions(this.options);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
select(item, menuIndex) {
|
||||
if (item.__IS__FLAT__OPTIONS) {
|
||||
this.activeValue = item.value;
|
||||
} else {
|
||||
this.activeValue.splice(menuIndex, 1, item.value);
|
||||
}
|
||||
this.$emit('pick', this.activeValue);
|
||||
},
|
||||
activeItem(item, menuIndex) {
|
||||
const len = this.activeOptions.length;
|
||||
this.activeValue.splice(menuIndex, len, item.value);
|
||||
this.activeOptions.splice(menuIndex + 1, len, item.children);
|
||||
if (this.changeOnSelect) this.$emit('pick', this.activeValue, false);
|
||||
}
|
||||
},
|
||||
|
||||
render(h) {
|
||||
const {
|
||||
activeValue,
|
||||
activeOptions,
|
||||
visible,
|
||||
expandTrigger,
|
||||
popperClass
|
||||
} = this;
|
||||
|
||||
const menus = this._l(activeOptions, (menu, menuIndex) => {
|
||||
let isFlat = false;
|
||||
const items = this._l(menu, item => {
|
||||
const events = {
|
||||
on: {}
|
||||
};
|
||||
|
||||
if (item.__IS__FLAT__OPTIONS) isFlat = true;
|
||||
|
||||
if (!item.disabled) {
|
||||
if (item.children) {
|
||||
let triggerEvent = {
|
||||
click: 'click',
|
||||
hover: 'mouseenter'
|
||||
}[expandTrigger];
|
||||
events.on[triggerEvent] = () => { this.activeItem(item, menuIndex); };
|
||||
} else {
|
||||
events.on.click = () => { this.select(item, menuIndex); };
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<li
|
||||
class={{
|
||||
'el-cascader-menu__item': true,
|
||||
'el-cascader-menu__item--extensible': item.children,
|
||||
'is-active': item.value === activeValue[menuIndex],
|
||||
'is-disabled': item.disabled
|
||||
}}
|
||||
{...events}
|
||||
>
|
||||
{item.label}
|
||||
</li>
|
||||
);
|
||||
});
|
||||
let menuStyle = {};
|
||||
if (isFlat) {
|
||||
menuStyle.width = this.inputWidth + 'px';
|
||||
}
|
||||
|
||||
return (
|
||||
<ul
|
||||
class={{
|
||||
'el-cascader-menu': true,
|
||||
'el-cascader-menu--flexible': isFlat
|
||||
}}
|
||||
style={menuStyle}>
|
||||
{items}
|
||||
</ul>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<transition name="el-zoom-in-top">
|
||||
<div
|
||||
v-show={visible}
|
||||
class={[
|
||||
'el-cascader-menus',
|
||||
popperClass
|
||||
]}
|
||||
>
|
||||
{menus}
|
||||
</div>
|
||||
</transition>
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
@ -329,7 +329,7 @@ export default {
|
||||
created() {
|
||||
RANGE_SEPARATOR = this.rangeSeparator;
|
||||
// vue-popper
|
||||
this.options = {
|
||||
this.popperOptions = {
|
||||
boundariesPadding: 0,
|
||||
gpuAcceleration: false
|
||||
};
|
||||
|
@ -41,6 +41,11 @@
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
modalAppendToBody: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
|
||||
lockScroll: {
|
||||
type: Boolean,
|
||||
|
@ -1,5 +1,16 @@
|
||||
<template>
|
||||
<li class="el-menu-item-group">
|
||||
<div class="el-menu-item-group__title" :style="{paddingLeft: levelPadding + 'px'}">
|
||||
<template v-if="!$slots.title">{{title}}</template>
|
||||
<slot v-else name="title"></slot>
|
||||
</div>
|
||||
<ul>
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
<script>
|
||||
module.exports = {
|
||||
export default {
|
||||
name: 'ElMenuItemGroup',
|
||||
|
||||
componentName: 'ElMenuItemGroup',
|
||||
@ -31,14 +42,3 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<li class="el-menu-item-group">
|
||||
<div class="el-menu-item-group__title" :style="{paddingLeft: levelPadding + 'px'}">
|
||||
<template v-if="!$slots.title">{{title}}</template>
|
||||
<slot v-else name="title"></slot>
|
||||
</div>
|
||||
<ul>
|
||||
<slot></slot>
|
||||
</ul>
|
||||
</li>
|
||||
</template>
|
||||
|
@ -13,7 +13,7 @@
|
||||
import Menu from './menu-mixin';
|
||||
import Emitter from 'element-ui/src/mixins/emitter';
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
name: 'ElMenuItem',
|
||||
|
||||
componentName: 'ElMenuItem',
|
||||
|
@ -1,4 +1,4 @@
|
||||
module.exports = {
|
||||
export default {
|
||||
computed: {
|
||||
indexPath() {
|
||||
var path = [this.index];
|
||||
|
@ -30,7 +30,7 @@
|
||||
import Emitter from 'element-ui/src/mixins/emitter';
|
||||
import CollapseTransition from 'element-ui/src/transitions/collapse-transition';
|
||||
|
||||
module.exports = {
|
||||
export default {
|
||||
name: 'ElSubmenu',
|
||||
|
||||
componentName: 'ElSubmenu',
|
||||
|
@ -14,7 +14,7 @@
|
||||
mixins: [Emitter],
|
||||
|
||||
props: {
|
||||
value: [String, Number],
|
||||
value: {},
|
||||
size: String,
|
||||
fill: String,
|
||||
textColor: String,
|
||||
|
@ -26,7 +26,7 @@
|
||||
default: 0
|
||||
},
|
||||
|
||||
options: {
|
||||
popperOptions: {
|
||||
default() {
|
||||
return {
|
||||
forceAbsolute: true,
|
||||
|
@ -69,7 +69,7 @@ export default {
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
beforeCreate() {
|
||||
this.$parent.steps.push(this);
|
||||
},
|
||||
|
||||
@ -118,9 +118,11 @@ export default {
|
||||
const isCenter = parent.center;
|
||||
const len = parent.steps.length;
|
||||
const isLast = this.isLast = parent.steps[parent.steps.length - 1] === this;
|
||||
const space = parent.space
|
||||
const space = typeof parent.space === 'number'
|
||||
? parent.space + 'px'
|
||||
: 100 / (isCenter ? len - 1 : len) + '%';
|
||||
: parent.space
|
||||
? parent.space
|
||||
: 100 / (isCenter ? len - 1 : len) + '%';
|
||||
|
||||
if (parent.direction === 'horizontal') {
|
||||
this.style = { width: space };
|
||||
|
@ -11,7 +11,7 @@ export default {
|
||||
name: 'ElSteps',
|
||||
|
||||
props: {
|
||||
space: Number,
|
||||
space: [Number, String],
|
||||
active: Number,
|
||||
direction: {
|
||||
type: String,
|
||||
@ -39,13 +39,13 @@ export default {
|
||||
watch: {
|
||||
active(newVal, oldVal) {
|
||||
this.$emit('change', newVal, oldVal);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.steps.forEach((child, index) => {
|
||||
child.index = index;
|
||||
});
|
||||
steps(steps) {
|
||||
steps.forEach((child, index) => {
|
||||
child.index = index;
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -182,8 +182,7 @@ export default {
|
||||
created() {
|
||||
this.customRender = this.$options.render;
|
||||
this.$options.render = h => h('div', this.$slots.default);
|
||||
|
||||
let columnId = this.columnId = this.columnKey || ((this.$parent.tableId || (this.$parent.columnId + '_')) + 'column_' + columnIdSeed++);
|
||||
this.columnId = (this.$parent.tableId || (this.$parent.columnId + '_')) + 'column_' + columnIdSeed++;
|
||||
|
||||
let parent = this.$parent;
|
||||
let owner = this.owner;
|
||||
@ -210,7 +209,8 @@ export default {
|
||||
let isColumnGroup = false;
|
||||
|
||||
let column = getDefaultColumn(type, {
|
||||
id: columnId,
|
||||
id: this.columnId,
|
||||
columnKey: this.columnKey,
|
||||
label: this.label,
|
||||
className: this.className,
|
||||
property: this.prop || this.property,
|
||||
|
@ -87,14 +87,14 @@ 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
|
||||
});
|
||||
}
|
||||
});
|
||||
// states.data.forEach((item) => {
|
||||
// if (!item.$extra) {
|
||||
// Object.defineProperty(item, '$extra', {
|
||||
// value: {},
|
||||
// enumerable: false
|
||||
// });
|
||||
// }
|
||||
// });
|
||||
|
||||
this.updateCurrentRow();
|
||||
|
||||
@ -152,15 +152,17 @@ TableStore.prototype.mutations = {
|
||||
}
|
||||
|
||||
const prop = column.property;
|
||||
const filters = [];
|
||||
|
||||
if (prop) {
|
||||
states.filters[column.id] = values;
|
||||
filters[column.columnKey || column.id] = values;
|
||||
}
|
||||
|
||||
let data = states._data;
|
||||
const filters = states.filters;
|
||||
|
||||
Object.keys(filters).forEach((columnId) => {
|
||||
const values = filters[columnId];
|
||||
Object.keys(states.filters).forEach((columnId) => {
|
||||
const values = states.filters[columnId];
|
||||
if (!values || values.length === 0) return;
|
||||
const column = getColumnById(this.states, columnId);
|
||||
if (column && column.filterMethod) {
|
||||
|
@ -3,9 +3,12 @@
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'TabBar',
|
||||
|
||||
props: {
|
||||
tabs: Array
|
||||
},
|
||||
|
||||
computed: {
|
||||
barStyle: {
|
||||
cache: false,
|
||||
|
174
packages/tabs/src/tab-nav.vue
Normal file
174
packages/tabs/src/tab-nav.vue
Normal file
@ -0,0 +1,174 @@
|
||||
<script>
|
||||
import TabBar from './tab-bar';
|
||||
|
||||
function noop() {}
|
||||
|
||||
export default {
|
||||
name: 'TabNav',
|
||||
|
||||
components: {
|
||||
TabBar
|
||||
},
|
||||
|
||||
props: {
|
||||
panes: Array,
|
||||
currentName: String,
|
||||
editable: Boolean,
|
||||
onTabClick: {
|
||||
type: Function,
|
||||
default: noop
|
||||
},
|
||||
onTabRemove: {
|
||||
type: Function,
|
||||
default: noop
|
||||
},
|
||||
type: String
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
scrollable: false,
|
||||
navStyle: {
|
||||
transform: ''
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
methods: {
|
||||
scrollPrev() {
|
||||
const containerWidth = this.$refs.navScroll.offsetWidth;
|
||||
const currentOffset = this.getCurrentScrollOffset();
|
||||
|
||||
if (!currentOffset) return;
|
||||
|
||||
let newOffset = currentOffset > containerWidth
|
||||
? currentOffset - containerWidth
|
||||
: 0;
|
||||
|
||||
this.setOffset(newOffset);
|
||||
},
|
||||
scrollNext() {
|
||||
const navWidth = this.$refs.nav.offsetWidth;
|
||||
const containerWidth = this.$refs.navScroll.offsetWidth;
|
||||
const currentOffset = this.getCurrentScrollOffset();
|
||||
|
||||
if (navWidth - currentOffset <= containerWidth) return;
|
||||
|
||||
let newOffset = navWidth - currentOffset > containerWidth * 2
|
||||
? currentOffset + containerWidth
|
||||
: (navWidth - containerWidth);
|
||||
|
||||
this.setOffset(newOffset);
|
||||
},
|
||||
scrollToActiveTab() {
|
||||
if (!this.scrollable) return;
|
||||
const nav = this.$refs.nav;
|
||||
const activeTab = this.$el.querySelector('.is-active');
|
||||
const navScroll = this.$refs.navScroll;
|
||||
const activeTabBounding = activeTab.getBoundingClientRect();
|
||||
const navScrollBounding = navScroll.getBoundingClientRect();
|
||||
const navBounding = nav.getBoundingClientRect();
|
||||
const currentOffset = this.getCurrentScrollOffset();
|
||||
let newOffset = currentOffset;
|
||||
|
||||
if (activeTabBounding.left < navScrollBounding.left) {
|
||||
newOffset = currentOffset - (navScrollBounding.left - activeTabBounding.left);
|
||||
}
|
||||
if (activeTabBounding.right > navScrollBounding.right) {
|
||||
newOffset = currentOffset + activeTabBounding.right - navScrollBounding.right;
|
||||
}
|
||||
if (navBounding.right < navScrollBounding.right) {
|
||||
newOffset = nav.offsetWidth - navScrollBounding.width;
|
||||
}
|
||||
this.setOffset(Math.max(newOffset, 0));
|
||||
},
|
||||
getCurrentScrollOffset() {
|
||||
const { navStyle } = this;
|
||||
return navStyle.transform
|
||||
? Number(navStyle.transform.match(/translateX\(-(\d+(\.\d+)*)px\)/)[1])
|
||||
: 0;
|
||||
},
|
||||
setOffset(value) {
|
||||
this.navStyle.transform = `translateX(-${value}px)`;
|
||||
}
|
||||
},
|
||||
|
||||
updated() {
|
||||
const navWidth = this.$refs.nav.offsetWidth;
|
||||
const containerWidth = this.$refs.navScroll.offsetWidth;
|
||||
const currentOffset = this.getCurrentScrollOffset();
|
||||
|
||||
if (containerWidth < navWidth) {
|
||||
const currentOffset = this.getCurrentScrollOffset();
|
||||
this.scrollable = this.scrollable || {};
|
||||
this.scrollable.prev = currentOffset;
|
||||
this.scrollable.next = currentOffset + containerWidth < navWidth;
|
||||
if (navWidth - currentOffset < containerWidth) {
|
||||
this.setOffset(navWidth - containerWidth);
|
||||
}
|
||||
} else if (currentOffset > 0) {
|
||||
this.setOffset(0);
|
||||
}
|
||||
},
|
||||
|
||||
render(h) {
|
||||
const {
|
||||
type,
|
||||
panes,
|
||||
editable,
|
||||
onTabClick,
|
||||
onTabRemove,
|
||||
navStyle,
|
||||
scrollable,
|
||||
scrollNext,
|
||||
scrollPrev
|
||||
} = this;
|
||||
|
||||
const scrollBtn = scrollable
|
||||
? [
|
||||
<span class={['el-tabs__nav-prev', scrollable.prev ? '' : 'is-disabled']} on-click={scrollPrev}><i class="el-icon-arrow-left"></i></span>,
|
||||
<span class={['el-tabs__nav-next', scrollable.next ? '' : 'is-disabled']} on-click={scrollNext}><i class="el-icon-arrow-right"></i></span>
|
||||
] : null;
|
||||
|
||||
const tabs = this._l(panes, (pane, index) => {
|
||||
let tabName = pane.name || pane.index || index;
|
||||
const closable = pane.isClosable || editable;
|
||||
|
||||
pane.index = `${index}`;
|
||||
|
||||
const btnClose = closable
|
||||
? <span class="el-icon-close" on-click={(ev) => { onTabRemove(pane, ev); }}></span>
|
||||
: null;
|
||||
|
||||
const tabLabelContent = pane.$slots.label || pane.label;
|
||||
return (
|
||||
<div
|
||||
class={{
|
||||
'el-tabs__item': true,
|
||||
'is-active': pane.active,
|
||||
'is-disabled': pane.disabled,
|
||||
'is-closable': closable
|
||||
}}
|
||||
ref="tabs"
|
||||
refInFor
|
||||
on-click={(ev) => { onTabClick(pane, tabName, ev); }}
|
||||
>
|
||||
{tabLabelContent}
|
||||
{btnClose}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
return (
|
||||
<div class={['el-tabs__nav-wrap', scrollable ? 'is-scrollable' : '']}>
|
||||
{scrollBtn}
|
||||
<div class={['el-tabs__nav-scroll']} ref="navScroll">
|
||||
<div class="el-tabs__nav" ref="nav" style={navStyle}>
|
||||
{!type ? <tab-bar tabs={panes}></tab-bar> : null}
|
||||
{tabs}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
};
|
||||
</script>
|
@ -4,7 +4,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
module.exports = {
|
||||
export default {
|
||||
name: 'ElTabPane',
|
||||
|
||||
componentName: 'ElTabPane',
|
||||
|
@ -1,25 +1,24 @@
|
||||
<script>
|
||||
import TabBar from './tab-bar';
|
||||
module.exports = {
|
||||
import TabNav from './tab-nav';
|
||||
|
||||
export default {
|
||||
name: 'ElTabs',
|
||||
|
||||
components: {
|
||||
TabBar
|
||||
TabNav
|
||||
},
|
||||
|
||||
props: {
|
||||
type: String,
|
||||
activeName: String,
|
||||
closable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
value: {}
|
||||
closable: Boolean,
|
||||
addable: Boolean,
|
||||
value: {},
|
||||
editable: Boolean
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
children: null,
|
||||
currentName: this.value || this.activeName,
|
||||
panes: []
|
||||
};
|
||||
@ -31,57 +30,31 @@
|
||||
},
|
||||
value(value) {
|
||||
this.setCurrentName(value);
|
||||
}
|
||||
},
|
||||
|
||||
computed: {
|
||||
currentTab() {
|
||||
let result;
|
||||
this.panes.forEach(tab => {
|
||||
if (this.currentName === (tab.name || tab.index)) {
|
||||
result = tab;
|
||||
}
|
||||
});
|
||||
return result;
|
||||
},
|
||||
currentName(value) {
|
||||
if (this.$refs.nav) {
|
||||
this.$nextTick(_ => {
|
||||
this.$refs.nav.scrollToActiveTab();
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleTabRemove(pane, event) {
|
||||
event.stopPropagation();
|
||||
const panes = this.panes;
|
||||
const currentTab = this.currentTab;
|
||||
|
||||
let index = panes.indexOf(pane);
|
||||
|
||||
if (index === -1) return;
|
||||
|
||||
panes.splice(index, 1);
|
||||
pane.$destroy();
|
||||
|
||||
this.$emit('tab-remove', pane);
|
||||
|
||||
this.$nextTick(_ => {
|
||||
if (pane.active) {
|
||||
const panes = this.panes;
|
||||
let nextChild = panes[index];
|
||||
let prevChild = panes[index - 1];
|
||||
let nextActiveTab = nextChild || prevChild || null;
|
||||
|
||||
if (nextActiveTab) {
|
||||
this.setCurrentName(nextActiveTab.name || nextActiveTab.index);
|
||||
}
|
||||
return;
|
||||
} else {
|
||||
this.setCurrentName(currentTab.name || currentTab.index);
|
||||
}
|
||||
});
|
||||
},
|
||||
handleTabClick(tab, tabName, event) {
|
||||
if (tab.disabled) return;
|
||||
this.setCurrentName(tabName);
|
||||
this.$emit('tab-click', tab, event);
|
||||
},
|
||||
handleTabRemove(pane, ev) {
|
||||
ev.stopPropagation();
|
||||
this.$emit('edit', pane.name, 'remove');
|
||||
this.$emit('tab-remove', pane.name);
|
||||
},
|
||||
handleTabAdd() {
|
||||
this.$emit('edit', null, 'add');
|
||||
this.$emit('tab-add');
|
||||
},
|
||||
setCurrentName(value) {
|
||||
this.currentName = value;
|
||||
this.$emit('input', value);
|
||||
@ -100,42 +73,37 @@
|
||||
render(h) {
|
||||
let {
|
||||
type,
|
||||
handleTabRemove,
|
||||
handleTabClick,
|
||||
handleTabRemove,
|
||||
handleTabAdd,
|
||||
currentName,
|
||||
panes
|
||||
panes,
|
||||
editable,
|
||||
addable
|
||||
} = this;
|
||||
|
||||
const tabs = this._l(panes, (pane, index) => {
|
||||
let tabName = pane.name || pane.index || index;
|
||||
if (currentName === undefined && index === 0) {
|
||||
this.setCurrentName(tabName);
|
||||
}
|
||||
const newButton = editable || addable
|
||||
? (
|
||||
<span
|
||||
class="el-tabs__new-tab"
|
||||
on-click={ handleTabAdd }
|
||||
>
|
||||
<i class="el-icon-plus"></i>
|
||||
</span>
|
||||
)
|
||||
: null;
|
||||
|
||||
pane.index = index;
|
||||
|
||||
const btnClose = pane.isClosable
|
||||
? <span class="el-icon-close" on-click={(ev) => { handleTabRemove(pane, ev); }}></span>
|
||||
: null;
|
||||
|
||||
const tabLabelContent = pane.$slots.label || pane.label;
|
||||
return (
|
||||
<div
|
||||
class={{
|
||||
'el-tabs__item': true,
|
||||
'is-active': pane.active,
|
||||
'is-disabled': pane.disabled,
|
||||
'is-closable': pane.isClosable
|
||||
}}
|
||||
ref="tabs"
|
||||
refInFor
|
||||
on-click={(ev) => { handleTabClick(pane, tabName, ev); }}
|
||||
>
|
||||
{tabLabelContent}
|
||||
{btnClose}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
const navData = {
|
||||
props: {
|
||||
currentName,
|
||||
onTabClick: handleTabClick,
|
||||
onTabRemove: handleTabRemove,
|
||||
editable,
|
||||
type,
|
||||
panes
|
||||
},
|
||||
ref: 'nav'
|
||||
};
|
||||
|
||||
return (
|
||||
<div class={{
|
||||
@ -144,14 +112,19 @@
|
||||
'el-tabs--border-card': type === 'border-card'
|
||||
}}>
|
||||
<div class="el-tabs__header">
|
||||
{!type ? <tab-bar tabs={panes}></tab-bar> : null}
|
||||
{tabs}
|
||||
{newButton}
|
||||
<tab-nav { ...navData }></tab-nav>
|
||||
</div>
|
||||
<div class="el-tabs__content">
|
||||
{this.$slots.default}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
},
|
||||
created() {
|
||||
if (!this.currentName) {
|
||||
this.setCurrentName('0');
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
@ -6,71 +6,73 @@
|
||||
@b autocomplete {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
@b autocomplete-suggestion {
|
||||
margin: 5px 0 0;
|
||||
box-shadow: 0 0 6px 0 rgba(0,0,0,0.04), 0 2px 4px 0 rgba(0,0,0,0.12);
|
||||
|
||||
@e suggestions {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 110%;
|
||||
margin: 5px 0 0;
|
||||
@e wrap {
|
||||
max-height: 280px;
|
||||
overflow: auto;
|
||||
background-color: var(--color-white);
|
||||
border: 1px solid var(--color-base-gray);
|
||||
width: 100%;
|
||||
padding: 6px 0;
|
||||
z-index: 10;
|
||||
border-radius: 2px;
|
||||
max-height: 280px;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
box-shadow: 0 0 6px 0 rgba(0,0,0,0.04), 0 2px 4px 0 rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
& li {
|
||||
list-style: none;
|
||||
line-height: 36px;
|
||||
padding: 0 10px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
color: var(--color-extra-light-black);
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@e list {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
& li {
|
||||
list-style: none;
|
||||
line-height: 36px;
|
||||
padding: 0 10px;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
color: var(--color-extra-light-black);
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--select-option-hover-background);
|
||||
}
|
||||
&.highlighted {
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-white);
|
||||
}
|
||||
&:active {
|
||||
background-color: darken(var(--color-primary), 0.2);
|
||||
}
|
||||
&.divider {
|
||||
margin-top: 6px;
|
||||
border-top: 1px solid var(--color-base-gray);
|
||||
}
|
||||
&.divider:last-child {
|
||||
margin-bottom: -6px;
|
||||
}
|
||||
}
|
||||
|
||||
@when loading {
|
||||
li {
|
||||
text-align: center;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
@utils-vertical-center;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--select-option-hover-background);
|
||||
}
|
||||
&.highlighted {
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-white);
|
||||
}
|
||||
&:active {
|
||||
background-color: darken(var(--color-primary), 0.2);
|
||||
}
|
||||
&.divider {
|
||||
margin-top: 6px;
|
||||
border-top: 1px solid var(--color-base-gray);
|
||||
}
|
||||
&.divider:last-child {
|
||||
margin-bottom: -6px;
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
@when loading {
|
||||
li {
|
||||
text-align: center;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 20px;
|
||||
color: #999;
|
||||
@utils-vertical-center;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
& .el-icon-loading {
|
||||
vertical-align: middle;
|
||||
}
|
||||
& .el-icon-loading {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,44 +1,163 @@
|
||||
@charset "UTF-8";
|
||||
@import "./input.css";
|
||||
@import "./common/var.css";
|
||||
/*@import "./core/dropdown.css";*/
|
||||
|
||||
@component-namespace element {
|
||||
@component-namespace el {
|
||||
|
||||
@b cascader {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
background-color: #fff;
|
||||
|
||||
@e dropdown {
|
||||
background-color: var(--cascader-menu-fill);
|
||||
border: var(--cascader-menu-border);
|
||||
border-radius: var(--cascader-menu-radius);
|
||||
box-shadow: var(--cascader-menu-submenu-shadow);
|
||||
margin-top: 5px;
|
||||
max-height: var(--cascader-height);
|
||||
.el-input,
|
||||
.el-input__inner {
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.el-input__icon {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
.el-icon-caret-bottom {
|
||||
transition: transform .3s;
|
||||
|
||||
@when reverse {
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
}
|
||||
|
||||
@e label {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
line-height: 34px;
|
||||
padding: 0 15px 0 10px;
|
||||
color: var(--input-color);
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
@e wrap {
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@e menu {
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
display: inline-block;
|
||||
margin: 0;
|
||||
position: relative;
|
||||
vertical-align: top;
|
||||
@m large {
|
||||
font-size: var(--input-large-font-size);
|
||||
|
||||
&::before {
|
||||
border-left: var(--cascader-menu-border);
|
||||
content: " ";
|
||||
height: var(--cascader-height);
|
||||
left: 0;
|
||||
position: absolute;
|
||||
.el-cascader__label {
|
||||
line-height: calc(var(--input-large-height) - 2);
|
||||
}
|
||||
}
|
||||
@m small {
|
||||
font-size: var(--input-small-font-size);
|
||||
|
||||
.el-cascader__label {
|
||||
line-height: calc(var(--input-small-height) - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@b cascader-menus {
|
||||
white-space: nowrap;
|
||||
background: #fff;
|
||||
position: absolute;
|
||||
margin: 5px 0;
|
||||
z-index: 1001;
|
||||
border: var(--select-dropdown-border);
|
||||
border-radius: var(--border-radius-small);
|
||||
overflow: hidden;
|
||||
box-shadow: var(--select-dropdown-shadow);
|
||||
}
|
||||
|
||||
@b cascader-menu {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
height: 180px;
|
||||
overflow: auto;
|
||||
border-right: var(--select-dropdown-border);
|
||||
background-color: var(--select-dropdown-background);
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
min-width: 110px;
|
||||
|
||||
&:last-child {
|
||||
border-right: 0;
|
||||
}
|
||||
|
||||
@e item {
|
||||
font-size: var(--select-font-size);
|
||||
padding: 8px 30px 8px 10px;
|
||||
position: relative;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--select-option-color);
|
||||
height: var(--select-option-height);
|
||||
line-height: 1.5;
|
||||
box-sizing: border-box;
|
||||
cursor: pointer;
|
||||
|
||||
@e keyword {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
@m extensible {
|
||||
&:after {
|
||||
font-family: 'element-icons';
|
||||
content: "\e602";
|
||||
font-size: 12px;
|
||||
transform: scale(0.8);
|
||||
color: rgb(191, 203, 217);
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
margin-top: 1px;
|
||||
}
|
||||
}
|
||||
|
||||
@when disabled {
|
||||
color: var(--select-option-disabled-color);
|
||||
background-color: var(--select-option-disabled-background);
|
||||
cursor: not-allowed;
|
||||
|
||||
&:hover {
|
||||
background-color: var(--color-white);
|
||||
}
|
||||
}
|
||||
|
||||
@when active {
|
||||
color: var(--color-white);
|
||||
background-color: var(--select-option-selected);
|
||||
|
||||
&.hover {
|
||||
background-color: var(--select-option-selected-hover);
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: var(--select-option-hover-background);
|
||||
}
|
||||
|
||||
&.selected {
|
||||
color: var(--color-white);
|
||||
background-color: var(--select-option-selected);
|
||||
|
||||
&.hover {
|
||||
background-color: var(--select-option-selected-hover);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@m flexible {
|
||||
height: auto;
|
||||
max-height: 180px;
|
||||
overflow: auto;
|
||||
|
||||
.el-cascader-menu__item {
|
||||
overflow: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,10 @@
|
||||
}
|
||||
|
||||
@when active {
|
||||
.el-collapse-item__header__arrow {
|
||||
transform: rotate(90deg);
|
||||
> .el-collapse-item__header {
|
||||
.el-collapse-item__header__arrow {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -161,6 +161,7 @@
|
||||
|
||||
--select-option-color: var(--link-color);
|
||||
--select-option-disabled-color: var(--color-extra-light-silver);
|
||||
--select-option-disabled-background: var(--color-white);
|
||||
--select-option-height: 36px;
|
||||
--select-option-hover-background: var(--color-light-gray);
|
||||
--select-option-selected: var(--color-primary);
|
||||
@ -458,8 +459,8 @@
|
||||
--tag-gray-border: var(--color-light-gray);
|
||||
--tag-gray-color: var(--link-color);
|
||||
|
||||
--tag-primary-fill: rgba(32,159,255,0.10);
|
||||
--tag-primary-border: rgba(32,159,255,0.20);
|
||||
--tag-primary-fill: rgba(var(--color-primary),0.10);
|
||||
--tag-primary-border: rgba(var(--color-primary),0.20);
|
||||
--tag-primary-color: var(--color-primary);
|
||||
|
||||
--tag-success-fill: rgba(18,206,102,0.10);
|
||||
|
@ -44,3 +44,4 @@
|
||||
@import "./carousel.css";
|
||||
@import "./carousel-item.css";
|
||||
@import "./collapse.css";
|
||||
@import "./cascader.css";
|
||||
|
@ -8,7 +8,10 @@
|
||||
overflow: hidden;
|
||||
width: 180px;
|
||||
position: relative;
|
||||
|
||||
|
||||
& .el-input {
|
||||
display: block;
|
||||
}
|
||||
& .el-input__inner {
|
||||
appearance: none;
|
||||
padding-right: calc(var(--input-height) * 2 + 10);
|
||||
|
@ -127,7 +127,6 @@
|
||||
}
|
||||
@b radio-button {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
display: inline-block;
|
||||
|
||||
@e inner {
|
||||
@ -138,6 +137,7 @@
|
||||
cursor: pointer;
|
||||
background: var(--button-default-fill);
|
||||
border: var(--border-base);
|
||||
border-left: 0;
|
||||
color: var(--button-default-color);
|
||||
-webkit-appearance: none;
|
||||
text-align: center;
|
||||
@ -157,7 +157,7 @@
|
||||
|
||||
& [class*="el-icon-"] {
|
||||
line-height: 0.9;
|
||||
|
||||
|
||||
& + span {
|
||||
margin-left: 5px;
|
||||
}
|
||||
@ -176,6 +176,7 @@
|
||||
color: var(--radio-button-checked-color);
|
||||
background-color: var(--radio-button-checked-fill);
|
||||
border-color: var(--radio-button-checked-border-color);
|
||||
box-shadow: -1px 0 0 0 var(--radio-button-checked-border-color);
|
||||
}
|
||||
}
|
||||
|
||||
@ -190,12 +191,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
margin-right: -1px;
|
||||
}
|
||||
&:first-child {
|
||||
.el-radio-button__inner {
|
||||
border-left: var(--border-base);
|
||||
border-radius: var(--border-radius-base) 0 0 var(--border-radius-base);
|
||||
box-shadow: none !important;
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
|
@ -80,23 +80,11 @@
|
||||
background-color: transparent;
|
||||
text-align: center;
|
||||
user-select: none;
|
||||
@utils-vertical-center;
|
||||
|
||||
.el-tooltip {
|
||||
line-height: 1;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.el-tooltip::after {
|
||||
content: '';
|
||||
width: 0;
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.el-tooltip__rel {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
&:hover,
|
||||
|
@ -38,16 +38,8 @@
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.el-tooltip {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.el-tooltip__rel {
|
||||
display: block;
|
||||
|
||||
.cell {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.el-tooltip.cell {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@e empty-block {
|
||||
|
@ -8,7 +8,6 @@
|
||||
padding: 0;
|
||||
position: relative;
|
||||
margin: 0 0 15px;
|
||||
@utils-clearfix;
|
||||
}
|
||||
@e active-bar {
|
||||
position: absolute;
|
||||
@ -20,16 +19,68 @@
|
||||
transition: transform .3s cubic-bezier(.645,.045,.355,1);
|
||||
list-style: none;
|
||||
}
|
||||
@e new-tab {
|
||||
float: right;
|
||||
border: 1px solid #d3dce6;
|
||||
height: 18px;
|
||||
width: @height;
|
||||
line-height: @height;
|
||||
margin: 12px 0 9px 10px;
|
||||
border-radius: 3px;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #d3dce6;
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
|
||||
.el-icon-plus {
|
||||
transform: scale(0.8, 0.8);
|
||||
}
|
||||
|
||||
&:hover {
|
||||
color: var(--color-primary);
|
||||
}
|
||||
}
|
||||
@e nav-wrap {
|
||||
overflow: hidden;
|
||||
margin-bottom: -1px;
|
||||
position: relative;
|
||||
|
||||
@when scrollable {
|
||||
padding: 0 15px;
|
||||
}
|
||||
}
|
||||
@e nav-scroll {
|
||||
overflow: hidden;
|
||||
}
|
||||
@e nav-next, nav-prev {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
line-height: 44px;
|
||||
font-size: 12px;
|
||||
color: var(--color-base-silver);
|
||||
}
|
||||
@e nav-next {
|
||||
right: 0;
|
||||
}
|
||||
@e nav-prev {
|
||||
left: 0;
|
||||
}
|
||||
@e nav {
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
float: left;
|
||||
transition: transform .3s;
|
||||
}
|
||||
@e item {
|
||||
padding: 0 16px;
|
||||
height: 42px;
|
||||
box-sizing: border-box;
|
||||
line-height: @height;
|
||||
float: left;
|
||||
display: inline-block;
|
||||
list-style: none;
|
||||
font-size: 14px;
|
||||
color: var(--color-base-silver);
|
||||
margin-bottom: -1px;
|
||||
position: relative;
|
||||
|
||||
& .el-icon-close {
|
||||
@ -67,10 +118,10 @@
|
||||
position: relative;
|
||||
}
|
||||
@m card {
|
||||
&>.el-tabs__header>.el-tabs__active-bar {
|
||||
.el-tabs__nav .el-tabs__active-bar {
|
||||
display: none;
|
||||
}
|
||||
&>.el-tabs__header>.el-tabs__item .el-icon-close {
|
||||
.el-tabs__nav .el-tabs__item .el-icon-close {
|
||||
position: relative;
|
||||
font-size: 12px;
|
||||
width: 0;
|
||||
@ -82,7 +133,7 @@
|
||||
right: -2px;
|
||||
transform-origin: 100% 50%;
|
||||
}
|
||||
&>.el-tabs__header>.el-tabs__item {
|
||||
.el-tabs__nav .el-tabs__item {
|
||||
border: 1px solid transparent;
|
||||
transition: all .3s cubic-bezier(.645,.045,.355,1);
|
||||
|
||||
|
@ -3,13 +3,6 @@
|
||||
|
||||
@component-namespace el {
|
||||
@b tooltip {
|
||||
display: inline-block;
|
||||
|
||||
@e rel {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
@e popper {
|
||||
position: absolute;
|
||||
border-radius: 4px;
|
||||
|
113
packages/tooltip/src/main.js
Normal file
113
packages/tooltip/src/main.js
Normal file
@ -0,0 +1,113 @@
|
||||
import Popper from 'element-ui/src/utils/vue-popper';
|
||||
import debounce from 'throttle-debounce/debounce';
|
||||
import Vue from 'vue';
|
||||
|
||||
export default {
|
||||
name: 'ElTooltip',
|
||||
|
||||
mixins: [Popper],
|
||||
|
||||
props: {
|
||||
openDelay: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
disabled: Boolean,
|
||||
manual: Boolean,
|
||||
effect: {
|
||||
type: String,
|
||||
default: 'dark'
|
||||
},
|
||||
popperClass: String,
|
||||
content: String,
|
||||
visibleArrow: {
|
||||
default: true
|
||||
},
|
||||
transition: {
|
||||
type: String,
|
||||
default: 'fade-in-linear'
|
||||
},
|
||||
popperOptions: {
|
||||
default() {
|
||||
return {
|
||||
boundariesPadding: 10,
|
||||
gpuAcceleration: false
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
beforeCreate() {
|
||||
this.popperVM = new Vue({
|
||||
data: { node: '' },
|
||||
render(h) {
|
||||
return this.node;
|
||||
}
|
||||
}).$mount();
|
||||
|
||||
this.debounceClose = debounce(200, () => this.handleClosePopper());
|
||||
},
|
||||
|
||||
render(h) {
|
||||
this.popperVM.node = (
|
||||
<transition
|
||||
name={ this.transition }
|
||||
onAfterLeave={ this.doDestroy }>
|
||||
<div
|
||||
onMouseleave={ () => { this.debounceClose(); this.togglePreventClose(); } }
|
||||
onMouseenter= { this.togglePreventClose }
|
||||
ref="popper"
|
||||
v-show={!this.disabled && this.showPopper}
|
||||
class={
|
||||
['el-tooltip__popper', 'is-' + this.effect, this.popperClass]
|
||||
}>
|
||||
{ this.$slots.content || this.content }
|
||||
</div>
|
||||
</transition>);
|
||||
|
||||
if (!this.$slots.default) return this.$slots.default;
|
||||
|
||||
const vnode = this.$slots.default[0];
|
||||
const data = vnode.data = vnode.data || {};
|
||||
const on = vnode.data.on = vnode.data.on || {};
|
||||
|
||||
on.mouseenter = this.addEventHandle(on.mouseenter, this.handleShowPopper);
|
||||
on.mouseleave = this.addEventHandle(on.mouseleave, this.debounceClose);
|
||||
data.staticClass = this.concatClass(data.staticClass, 'el-tooltip');
|
||||
|
||||
return vnode;
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.referenceElm = this.$el;
|
||||
},
|
||||
|
||||
methods: {
|
||||
addEventHandle(old, fn) {
|
||||
return old ? Array.isArray(old) ? old.concat(fn) : [old, fn] : fn;
|
||||
},
|
||||
|
||||
concatClass(a, b) {
|
||||
if (a && a.indexOf(b) > -1) return a;
|
||||
return a ? b ? (a + ' ' + b) : a : (b || '');
|
||||
},
|
||||
|
||||
handleShowPopper() {
|
||||
if (this.manual) return;
|
||||
clearTimeout(this.timeout);
|
||||
this.timeout = setTimeout(() => {
|
||||
this.showPopper = true;
|
||||
}, this.openDelay);
|
||||
},
|
||||
|
||||
handleClosePopper() {
|
||||
if (this.preventClose || this.manual) return;
|
||||
clearTimeout(this.timeout);
|
||||
this.showPopper = false;
|
||||
},
|
||||
|
||||
togglePreventClose() {
|
||||
this.preventClose = !this.preventClose;
|
||||
}
|
||||
}
|
||||
};
|
@ -1,75 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="el-tooltip"
|
||||
@mouseenter="handleShowPopper"
|
||||
@mouseleave="handleClosePopper">
|
||||
<div class="el-tooltip__rel" ref="reference">
|
||||
<slot></slot>
|
||||
</div>
|
||||
|
||||
<transition :name="transition" @after-leave="doDestroy">
|
||||
<div
|
||||
class="el-tooltip__popper"
|
||||
:class="['is-' + effect, popperClass]"
|
||||
ref="popper"
|
||||
v-show="!disabled && showPopper">
|
||||
<slot name="content"><div v-text="content"></div></slot>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Popper from 'element-ui/src/utils/vue-popper';
|
||||
|
||||
export default {
|
||||
name: 'ElTooltip',
|
||||
|
||||
mixins: [Popper],
|
||||
|
||||
props: {
|
||||
openDelay: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
disabled: Boolean,
|
||||
manual: Boolean,
|
||||
effect: {
|
||||
type: String,
|
||||
default: 'dark'
|
||||
},
|
||||
popperClass: String,
|
||||
content: String,
|
||||
visibleArrow: {
|
||||
default: true
|
||||
},
|
||||
transition: {
|
||||
type: String,
|
||||
default: 'fade-in-linear'
|
||||
},
|
||||
options: {
|
||||
default() {
|
||||
return {
|
||||
boundariesPadding: 10,
|
||||
gpuAcceleration: false
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
handleShowPopper() {
|
||||
if (this.manual) return;
|
||||
this.timeout = setTimeout(() => {
|
||||
this.showPopper = true;
|
||||
}, this.openDelay);
|
||||
},
|
||||
|
||||
handleClosePopper() {
|
||||
if (this.manual) return;
|
||||
clearTimeout(this.timeout);
|
||||
this.showPopper = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
@ -60,6 +60,7 @@ import Scrollbar from '../packages/scrollbar';
|
||||
import CarouselItem from '../packages/carousel-item';
|
||||
import Collapse from '../packages/collapse';
|
||||
import CollapseItem from '../packages/collapse-item';
|
||||
import Cascader from '../packages/cascader';
|
||||
import locale from 'element-ui/src/locale';
|
||||
|
||||
const components = [
|
||||
@ -118,7 +119,8 @@ const components = [
|
||||
Scrollbar,
|
||||
CarouselItem,
|
||||
Collapse,
|
||||
CollapseItem
|
||||
CollapseItem,
|
||||
Cascader
|
||||
];
|
||||
|
||||
const install = function(Vue, opts = {}) {
|
||||
@ -211,5 +213,6 @@ module.exports = {
|
||||
Scrollbar,
|
||||
CarouselItem,
|
||||
Collapse,
|
||||
CollapseItem
|
||||
CollapseItem,
|
||||
Cascader
|
||||
};
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Няма данни',
|
||||
placeholder: 'Избери'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Няма намерени',
|
||||
placeholder: 'Избери'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Иди на',
|
||||
pagesize: '/страница',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Ingen data',
|
||||
placeholder: 'Vælg'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Ingen matchende data',
|
||||
placeholder: 'Vælg'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Gå til',
|
||||
pagesize: '/side',
|
||||
|
@ -58,6 +58,10 @@ export default {
|
||||
noData: 'Keine Datei',
|
||||
placeholder: 'Datei wählen'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Nichts gefunden.',
|
||||
placeholder: 'Datei wählen'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Gehe zu',
|
||||
pagesize: 'pro Seite',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'No data',
|
||||
placeholder: 'Select'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'No matching data',
|
||||
placeholder: 'Select'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Go to',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Sin datos',
|
||||
placeholder: 'Seleccionar'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'No hay datos que coincidan',
|
||||
placeholder: 'Seleccionar'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Ir a',
|
||||
pagesize: '/pagina',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'اطلاعاتی وجود ندارد',
|
||||
placeholder: 'انتخاب کنید'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'هیچ دادهای پیدا نشد',
|
||||
placeholder: 'انتخاب کنید'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'برو به',
|
||||
pagesize: '/صفحه',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Aucun résultat',
|
||||
placeholder: 'Choisir'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Aucune correspondance',
|
||||
placeholder: 'Choisir'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Aller à',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Tidak ada data',
|
||||
placeholder: 'Pilih'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Tidak ada data yang cocok',
|
||||
placeholder: 'Pilih'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Pergi ke',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Nessun risultato',
|
||||
placeholder: 'Seleziona'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Nessuna corrispondenza',
|
||||
placeholder: 'Seleziona'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Vai a',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'データなし',
|
||||
placeholder: '選択してください'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'データなし',
|
||||
placeholder: '選択してください'
|
||||
},
|
||||
pagination: {
|
||||
goto: '',
|
||||
pagesize: '件/ページ',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: '데이터 없음',
|
||||
placeholder: '선택'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: '맞는 데이터가 없습니다',
|
||||
placeholder: '선택'
|
||||
},
|
||||
pagination: {
|
||||
goto: '이동',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Ingen data',
|
||||
placeholder: 'Velg'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Ingen samsvarende data',
|
||||
placeholder: 'Velg'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Gå til',
|
||||
pagesize: '/side',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Geen data',
|
||||
placeholder: 'Selecteer'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Geen overeenkomende resultaten',
|
||||
placeholder: 'Selecteer'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Ga naar',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Brak danych',
|
||||
placeholder: 'Wybierz'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Brak dopasowań',
|
||||
placeholder: 'Wybierz'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Idź do',
|
||||
pagesize: '/strona',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Sem dados',
|
||||
placeholder: 'Selecione'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Sem resultados',
|
||||
placeholder: 'Selecione'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Ir para',
|
||||
pagesize: '/pagina',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Sem dados',
|
||||
placeholder: 'Selecione'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Sem correspondência',
|
||||
placeholder: 'Selecione'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Ir para',
|
||||
pagesize: '/pagina',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Нет данных',
|
||||
placeholder: 'Выбрать'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Совпадений не найдено',
|
||||
placeholder: 'Выбрать'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Перейти',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'ไม่พบข้อมูล',
|
||||
placeholder: 'เลือก'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'ไม่พบข้อมูลที่ตรงกัน',
|
||||
placeholder: 'เลือก'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'ไปที่',
|
||||
pagesize: '/หน้า',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Veri yok',
|
||||
placeholder: 'Seç'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Eşleşen veri bulunamadı',
|
||||
placeholder: 'Seç'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Git',
|
||||
pagesize: '/page',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: 'Không tìm thấy dữ liệu',
|
||||
placeholder: 'Chọn'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: 'Dữ liệu không phù hợp',
|
||||
placeholder: 'Chọn'
|
||||
},
|
||||
pagination: {
|
||||
goto: 'Nhảy tới',
|
||||
pagesize: '/trang',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: '无数据',
|
||||
placeholder: '请选择'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: '无匹配数据',
|
||||
placeholder: '请选择'
|
||||
},
|
||||
pagination: {
|
||||
goto: '前往',
|
||||
pagesize: '条/页',
|
||||
|
@ -56,6 +56,10 @@ export default {
|
||||
noData: '無資料',
|
||||
placeholder: '請選擇'
|
||||
},
|
||||
cascader: {
|
||||
noMatch: '無匹配資料',
|
||||
placeholder: '請選擇'
|
||||
},
|
||||
pagination: {
|
||||
goto: '前往',
|
||||
pagesize: '項/頁',
|
||||
|
@ -70,6 +70,10 @@ export default {
|
||||
default: true
|
||||
},
|
||||
modalClass: {},
|
||||
modalAppendToBody: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
lockScroll: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
@ -185,7 +189,7 @@ export default {
|
||||
PopupManager.closeModal(this._popupId);
|
||||
this._closing = false;
|
||||
}
|
||||
PopupManager.openModal(this._popupId, PopupManager.nextZIndex(), dom, props.modalClass, props.modalFade);
|
||||
PopupManager.openModal(this._popupId, PopupManager.nextZIndex(), this.modalAppendToBody ? undefined : dom, props.modalClass, props.modalFade);
|
||||
if (props.lockScroll) {
|
||||
if (!this.bodyOverflow) {
|
||||
this.bodyPaddingRight = document.body.style.paddingRight;
|
||||
|
@ -36,7 +36,7 @@ export default {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
options: {
|
||||
popperOptions: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
@ -76,7 +76,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
|
||||
const options = this.options;
|
||||
const options = this.popperOptions;
|
||||
const popper = this.popperElm = this.popperElm || this.popper || this.$refs.popper;
|
||||
let reference = this.referenceElm = this.referenceElm || this.reference || this.$refs.reference;
|
||||
|
||||
@ -85,6 +85,7 @@ export default {
|
||||
this.$slots.reference[0]) {
|
||||
reference = this.referenceElm = this.$slots.reference[0].elm;
|
||||
}
|
||||
|
||||
if (!popper || !reference) return;
|
||||
if (this.visibleArrow) this.appendArrow(popper);
|
||||
if (this.appendToBody) document.body.appendChild(this.popperElm);
|
||||
|
@ -57,7 +57,7 @@ describe('Autocomplete', () => {
|
||||
setTimeout(_ => {
|
||||
const suggestions = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
expect(suggestions.style.display).to.not.equal('none');
|
||||
expect(suggestions.children.length).to.be.equal(4);
|
||||
expect(suggestions.querySelectorAll('.el-autocomplete-suggestion__list li').length).to.be.equal(4);
|
||||
|
||||
document.body.click();
|
||||
setTimeout(_ => {
|
||||
@ -74,14 +74,12 @@ describe('Autocomplete', () => {
|
||||
ref="autocomplete"
|
||||
:fetch-suggestions="querySearch"
|
||||
placeholder="请输入内容autocomplete2"
|
||||
@select="handleSelect"
|
||||
></el-autocomplete>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
restaurants: [],
|
||||
state: '',
|
||||
onceSelected: false
|
||||
state: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -102,27 +100,29 @@ describe('Autocomplete', () => {
|
||||
{ 'value': '新旺角茶餐厅', 'address': '上海市普陀区真北路988号创邑金沙谷6号楼113' },
|
||||
{ 'value': '泷千家(天山西路店)', 'address': '天山西路438号' }
|
||||
];
|
||||
},
|
||||
handleSelect() {
|
||||
this.onceSelected = true;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.restaurants = this.loadAll();
|
||||
}
|
||||
}, true);
|
||||
let elm = vm.$el;
|
||||
let inputElm = elm.querySelector('input');
|
||||
const autocomplete = vm.$refs.autocomplete;
|
||||
const elm = vm.$el;
|
||||
const inputElm = elm.querySelector('input');
|
||||
const spy = sinon.spy();
|
||||
|
||||
autocomplete.$on('select', spy);
|
||||
inputElm.focus();
|
||||
|
||||
setTimeout(_ => {
|
||||
let suggestionsList = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
suggestionsList.children[1].click();
|
||||
const suggestions = autocomplete.$refs.suggestions.$el;
|
||||
const suggestionList = suggestions.querySelectorAll('.el-autocomplete-suggestion__list li');
|
||||
suggestionList[1].click();
|
||||
setTimeout(_ => {
|
||||
expect(inputElm.value).to.be.equal('Hot honey 首尔炸鸡(仙霞路)');
|
||||
expect(vm.state).to.be.equal('Hot honey 首尔炸鸡(仙霞路)');
|
||||
expect(vm.onceSelected).to.be.true;
|
||||
expect(elm.querySelector('.el-autocomplete__suggestions')).to.not.exist;
|
||||
expect(spy.withArgs().calledOnce).to.be.true;
|
||||
expect(suggestions.style.display).to.be.equal('none');
|
||||
done();
|
||||
}, 500);
|
||||
}, 500);
|
||||
@ -135,14 +135,12 @@ describe('Autocomplete', () => {
|
||||
v-model="state"
|
||||
:fetch-suggestions="querySearch"
|
||||
placeholder="请输入内容autocomplete3"
|
||||
@select="handleSelect"
|
||||
></el-autocomplete>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
restaurants: [],
|
||||
state: '',
|
||||
onceSelected: false
|
||||
state: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -181,28 +179,25 @@ describe('Autocomplete', () => {
|
||||
{ 'value': '纵食', 'address': '元丰天山花园(东门) 双流路267号' },
|
||||
{ 'value': '钱记', 'address': '上海市长宁区天山西路' }
|
||||
];
|
||||
},
|
||||
handleSelect() {
|
||||
this.onceSelected = true;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.restaurants = this.loadAll();
|
||||
}
|
||||
}, true);
|
||||
let elm = vm.$el;
|
||||
let inputElm = elm.querySelector('input');
|
||||
const autocomplete = vm.$refs.autocomplete;
|
||||
const inputElm = autocomplete.$el.querySelector('input');
|
||||
|
||||
inputElm.focus();
|
||||
|
||||
setTimeout(_ => {
|
||||
vm.$refs.autocomplete.highlight(8);
|
||||
autocomplete.highlight(8);
|
||||
vm.$nextTick(_ => {
|
||||
let suggestionsList = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
let highlightedItem = suggestionsList.children[8];
|
||||
expect(highlightedItem.className).to.be.equal('highlighted');
|
||||
expect(suggestionsList.scrollTop === highlightedItem.scrollHeight).to.be.true;
|
||||
|
||||
vm.$refs.autocomplete.highlight(8);
|
||||
const suggestions = autocomplete.$refs.suggestions.$el.querySelector('.el-autocomplete-suggestion__wrap');
|
||||
let suggestionsList = suggestions.querySelectorAll('.el-autocomplete-suggestion__list li');
|
||||
let highlightedItem = suggestionsList[8];
|
||||
expect(highlightedItem.classList.contains('highlighted')).to.be.true;
|
||||
expect(suggestions.scrollTop === highlightedItem.scrollHeight).to.be.true;
|
||||
done();
|
||||
});
|
||||
}, 500);
|
||||
@ -215,14 +210,12 @@ describe('Autocomplete', () => {
|
||||
v-model="state"
|
||||
:fetch-suggestions="querySearch"
|
||||
placeholder="请输入内容autocomplete3"
|
||||
@select="handleSelect"
|
||||
></el-autocomplete>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
restaurants: [],
|
||||
state: '',
|
||||
onceSelected: false
|
||||
state: ''
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -251,29 +244,27 @@ describe('Autocomplete', () => {
|
||||
{ 'value': '阿姨奶茶/豪大大', 'address': '嘉定区曹安路1611号' },
|
||||
{ 'value': '新麦甜四季甜品炸鸡', 'address': '嘉定区曹安公路2383弄55号' }
|
||||
];
|
||||
},
|
||||
handleSelect() {
|
||||
this.onceSelected = true;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.restaurants = this.loadAll();
|
||||
}
|
||||
}, true);
|
||||
let elm = vm.$el;
|
||||
let inputElm = elm.querySelector('input');
|
||||
const autocomplete = vm.$refs.autocomplete;
|
||||
let inputElm = vm.$el.querySelector('input');
|
||||
inputElm.focus();
|
||||
|
||||
setTimeout(_ => {
|
||||
vm.$refs.autocomplete.highlight(15);
|
||||
autocomplete.highlight(15);
|
||||
vm.$nextTick(_ => {
|
||||
let suggestionsList = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
let highlightedItem = suggestionsList.children[11];
|
||||
const suggestions = autocomplete.$refs.suggestions.$el;
|
||||
const suggestionsList = suggestions.querySelectorAll('.el-autocomplete-suggestion__list li');
|
||||
let highlightedItem = suggestionsList[11];
|
||||
expect(highlightedItem.className).to.be.equal('highlighted');
|
||||
|
||||
vm.$refs.autocomplete.highlight(-5);
|
||||
autocomplete.highlight(-5);
|
||||
vm.$nextTick(_ => {
|
||||
let highlightedItem = suggestionsList.children[0];
|
||||
let highlightedItem = suggestionsList[0];
|
||||
expect(highlightedItem.className).to.be.equal('highlighted');
|
||||
});
|
||||
done();
|
||||
@ -321,13 +312,12 @@ describe('Autocomplete', () => {
|
||||
this.restaurants = this.loadAll();
|
||||
}
|
||||
}, true);
|
||||
let elm = vm.$el;
|
||||
let inputElm = elm.querySelector('input');
|
||||
let inputElm = vm.$el.querySelector('input');
|
||||
inputElm.focus();
|
||||
|
||||
setTimeout(_ => {
|
||||
let suggestionsList = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
expect(suggestionsList.style.display).to.be.equal('none');
|
||||
let suggestions = vm.$refs.autocomplete.$refs.suggestions.$el;
|
||||
expect(suggestions.style.display).to.be.equal('none');
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
|
524
test/unit/specs/cascader.spec.js
Normal file
524
test/unit/specs/cascader.spec.js
Normal file
@ -0,0 +1,524 @@
|
||||
import { createVue, destroyVM, triggerEvent } from '../util';
|
||||
|
||||
describe('Cascader', () => {
|
||||
let vm;
|
||||
afterEach(() => {
|
||||
destroyVM(vm);
|
||||
});
|
||||
|
||||
it('create', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-cascader
|
||||
ref="cascader"
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
v-model="selectedOptions"
|
||||
></el-cascader>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
expect(vm.$el).to.be.exist;
|
||||
vm.$el.click();
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus')).to.be.exist;
|
||||
|
||||
const menu = vm.$refs.cascader.menu;
|
||||
const menuElm = menu.$el;
|
||||
const item1 = menuElm.querySelector('.el-cascader-menu__item');
|
||||
|
||||
item1.click();
|
||||
menu.$nextTick(_ => {
|
||||
expect(menuElm.children.length).to.be.equal(2);
|
||||
expect(item1.classList.contains('is-active')).to.be.true;
|
||||
|
||||
const item2 = menuElm.children[1].querySelector('.el-cascader-menu__item');
|
||||
item2.click();
|
||||
|
||||
menu.$nextTick(_ => {
|
||||
expect(menuElm.children.length).to.be.equal(3);
|
||||
expect(item2.classList.contains('is-active')).to.be.true;
|
||||
|
||||
const item3 = menuElm.children[2].querySelector('.el-cascader-menu__item');
|
||||
item3.click();
|
||||
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus').style.display).to.be.equal('none');
|
||||
expect(vm.selectedOptions[0]).to.be.equal('zhejiang');
|
||||
expect(vm.selectedOptions[1]).to.be.equal('hangzhou');
|
||||
expect(vm.selectedOptions[2]).to.be.equal('xihu');
|
||||
|
||||
triggerEvent(vm.$refs.cascader.$el, 'mouseenter');
|
||||
vm.$nextTick(_ => {
|
||||
vm.$refs.cascader.$el.querySelector('.el-cascader__clearIcon').click();
|
||||
vm.$nextTick(_ => {
|
||||
expect(vm.selectedOptions.length).to.be.equal(0);
|
||||
done();
|
||||
});
|
||||
});
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
it('not allow clearable', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-cascader
|
||||
ref="cascader"
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
:clearable="false"
|
||||
v-model="selectedOptions"
|
||||
></el-cascader>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
expect(vm.$el).to.be.exist;
|
||||
triggerEvent(vm.$refs.cascader.$el, 'mouseenter');
|
||||
vm.$nextTick(_ => {
|
||||
expect(vm.$refs.cascader.$el.querySelector('.el-cascader__clearIcon')).to.not.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
it('disabled options', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-cascader
|
||||
ref="cascader"
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
v-model="selectedOptions"
|
||||
></el-cascader>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
disabled: true,
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
expect(vm.$el).to.be.exist;
|
||||
vm.$el.click();
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus')).to.be.exist;
|
||||
|
||||
const menu = vm.$refs.cascader.menu;
|
||||
const menuElm = menu.$el;
|
||||
const item1 = menuElm.querySelector('.el-cascader-menu__item');
|
||||
|
||||
item1.click();
|
||||
menu.$nextTick(_ => {
|
||||
expect(menuElm.children.length).to.be.equal(1);
|
||||
expect(item1.classList.contains('is-active')).to.be.false;
|
||||
done();
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
it('default value', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-cascader
|
||||
ref="cascader"
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
v-model="selectedOptions"
|
||||
></el-cascader>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: ['zhejiang', 'hangzhou', 'xihu']
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
expect(vm.$el).to.be.exist;
|
||||
vm.$el.click();
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus')).to.be.exist;
|
||||
|
||||
const menu = vm.$refs.cascader.menu;
|
||||
const menuElm = menu.$el;
|
||||
const item1 = menuElm.children[0].querySelector('.el-cascader-menu__item');
|
||||
const item2 = menuElm.children[1].querySelector('.el-cascader-menu__item');
|
||||
const item3 = menuElm.children[2].querySelector('.el-cascader-menu__item');
|
||||
|
||||
expect(menuElm.children.length).to.be.equal(3);
|
||||
expect(item1.classList.contains('is-active')).to.be.true;
|
||||
expect(item2.classList.contains('is-active')).to.be.true;
|
||||
expect(item3.classList.contains('is-active')).to.be.true;
|
||||
|
||||
document.body.click();
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus').style.display).to.be.equal('none');
|
||||
done();
|
||||
}, 500);
|
||||
}, 300);
|
||||
});
|
||||
it('expand by hover', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-cascader
|
||||
ref="cascader"
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
expand-trigger="hover"
|
||||
v-model="selectedOptions"
|
||||
></el-cascader>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
expect(vm.$el).to.be.exist;
|
||||
vm.$el.click();
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus')).to.be.exist;
|
||||
|
||||
const menu = vm.$refs.cascader.menu;
|
||||
const menuElm = menu.$el;
|
||||
const item1 = menuElm.querySelector('.el-cascader-menu__item');
|
||||
|
||||
triggerEvent(item1, 'mouseenter');
|
||||
menu.$nextTick(_ => {
|
||||
expect(menuElm.children.length).to.be.equal(2);
|
||||
expect(item1.classList.contains('is-active')).to.be.true;
|
||||
|
||||
const item2 = menuElm.children[1].querySelector('.el-cascader-menu__item');
|
||||
triggerEvent(item2, 'mouseenter');
|
||||
|
||||
menu.$nextTick(_ => {
|
||||
expect(menuElm.children.length).to.be.equal(3);
|
||||
expect(item2.classList.contains('is-active')).to.be.true;
|
||||
|
||||
const item3 = menuElm.children[2].querySelector('.el-cascader-menu__item');
|
||||
item3.click();
|
||||
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus').style.display).to.be.equal('none');
|
||||
expect(vm.selectedOptions[0]).to.be.equal('zhejiang');
|
||||
expect(vm.selectedOptions[1]).to.be.equal('hangzhou');
|
||||
expect(vm.selectedOptions[2]).to.be.equal('xihu');
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
it('change on select', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-cascader
|
||||
ref="cascader"
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
change-on-select
|
||||
v-model="selectedOptions"
|
||||
></el-cascader>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
expect(vm.$el).to.be.exist;
|
||||
vm.$el.click();
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus')).to.be.exist;
|
||||
|
||||
const menu = vm.$refs.cascader.menu;
|
||||
const menuElm = menu.$el;
|
||||
const item1 = menuElm.querySelector('.el-cascader-menu__item');
|
||||
|
||||
item1.click();
|
||||
menu.$nextTick(_ => {
|
||||
expect(menuElm.children.length).to.be.equal(2);
|
||||
expect(item1.classList.contains('is-active')).to.be.true;
|
||||
expect(vm.selectedOptions[0]).to.be.equal('zhejiang');
|
||||
|
||||
const item2 = menuElm.children[1].querySelector('.el-cascader-menu__item');
|
||||
item2.click();
|
||||
|
||||
menu.$nextTick(_ => {
|
||||
expect(menuElm.children.length).to.be.equal(3);
|
||||
expect(item2.classList.contains('is-active')).to.be.true;
|
||||
expect(vm.selectedOptions[1]).to.be.equal('hangzhou');
|
||||
|
||||
const item3 = menuElm.children[2].querySelector('.el-cascader-menu__item');
|
||||
item3.click();
|
||||
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus').style.display).to.be.equal('none');
|
||||
expect(vm.selectedOptions[0]).to.be.equal('zhejiang');
|
||||
expect(vm.selectedOptions[1]).to.be.equal('hangzhou');
|
||||
expect(vm.selectedOptions[2]).to.be.equal('xihu');
|
||||
done();
|
||||
}, 500);
|
||||
});
|
||||
});
|
||||
}, 300);
|
||||
});
|
||||
it('filterable', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-cascader
|
||||
ref="cascader"
|
||||
placeholder="请选择"
|
||||
:options="options"
|
||||
filterable
|
||||
v-model="selectedOptions"
|
||||
></el-cascader>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
options: [{
|
||||
value: 'zhejiang',
|
||||
label: 'Zhejiang',
|
||||
children: [{
|
||||
value: 'hangzhou',
|
||||
label: 'Hangzhou',
|
||||
children: [{
|
||||
value: 'xihu',
|
||||
label: 'West Lake'
|
||||
}]
|
||||
}, {
|
||||
value: 'ningbo',
|
||||
label: 'NingBo',
|
||||
children: [{
|
||||
value: 'jiangbei',
|
||||
label: 'Jiang Bei'
|
||||
}]
|
||||
}]
|
||||
}, {
|
||||
value: 'jiangsu',
|
||||
label: 'Jiangsu',
|
||||
children: [{
|
||||
value: 'nanjing',
|
||||
label: 'Nanjing',
|
||||
children: [{
|
||||
value: 'zhonghuamen',
|
||||
label: 'Zhong Hua Men'
|
||||
}]
|
||||
}]
|
||||
}],
|
||||
selectedOptions: []
|
||||
};
|
||||
}
|
||||
}, true);
|
||||
expect(vm.$el).to.be.exist;
|
||||
vm.$refs.cascader.inputValue = 'z';
|
||||
vm.$el.click();
|
||||
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus')).to.be.exist;
|
||||
|
||||
const menu = vm.$refs.cascader.menu;
|
||||
const menuElm = menu.$el;
|
||||
const item1 = menuElm.querySelector('.el-cascader-menu__item');
|
||||
|
||||
expect(menuElm.children.length).to.be.equal(1);
|
||||
expect(menuElm.children[0].children.length).to.be.equal(1);
|
||||
done();
|
||||
|
||||
item1.click();
|
||||
|
||||
setTimeout(_ => {
|
||||
expect(document.body.querySelector('.el-cascader-menus').style.display).to.be.equal('none');
|
||||
expect(vm.selectedOptions[0]).to.be.equal('zhejiang');
|
||||
expect(vm.selectedOptions[1]).to.be.equal('hangzhou');
|
||||
expect(vm.selectedOptions[2]).to.be.equal('xihu');
|
||||
done();
|
||||
}, 500);
|
||||
}, 300);
|
||||
});
|
||||
});
|
@ -24,7 +24,7 @@ describe('Tabs', () => {
|
||||
vm.$refs.tabs.$on('tab-click', spy);
|
||||
|
||||
setTimeout(_ => {
|
||||
const tabList = vm.$refs.tabs.$refs.tabs;
|
||||
const tabList = vm.$refs.tabs.$refs.nav.$refs.tabs;
|
||||
expect(tabList[0].classList.contains('is-active')).to.be.true;
|
||||
expect(paneList[0].style.display).to.not.ok;
|
||||
|
||||
@ -60,7 +60,7 @@ describe('Tabs', () => {
|
||||
}, true);
|
||||
setTimeout(_ => {
|
||||
const paneList = vm.$el.querySelector('.el-tabs__content').children;
|
||||
const tabList = vm.$refs.tabs.$refs.tabs;
|
||||
const tabList = vm.$refs.tabs.$refs.nav.$refs.tabs;
|
||||
|
||||
expect(tabList[1].classList.contains('is-active')).to.be.true;
|
||||
expect(paneList[1].style.display).to.not.ok;
|
||||
@ -140,33 +140,172 @@ describe('Tabs', () => {
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
it('closable', done => {
|
||||
it('editable', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<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>
|
||||
<el-tab-pane label="定时任务补偿">D</el-tab-pane>
|
||||
<el-tabs ref="tabs" v-model="editableTabsValue" type="card" editable @edit="handleTabsEdit">
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in editableTabs"
|
||||
:label="item.title"
|
||||
:name="item.name"
|
||||
>
|
||||
{{item.content}}
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
`
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
editableTabsValue: '2',
|
||||
editableTabs: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}, {
|
||||
title: 'Tab 3',
|
||||
name: '3',
|
||||
content: 'Tab 3 content'
|
||||
}],
|
||||
tabIndex: 3
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleTabsEdit(targetName, action) {
|
||||
if (action === 'add') {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue = newTabName;
|
||||
}
|
||||
if (action === 'remove') {
|
||||
let tabs = this.editableTabs;
|
||||
let activeName = this.editableTabsValue;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.editableTabsValue = activeName;
|
||||
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
let spy = sinon.spy();
|
||||
vm.$refs.tabs.$on('tab-remove', spy);
|
||||
|
||||
setTimeout(_ => {
|
||||
const tabList = vm.$refs.tabs.$refs.tabs;
|
||||
const tabList = vm.$refs.tabs.$refs.nav.$refs.tabs;
|
||||
const paneList = vm.$el.querySelector('.el-tabs__content').children;
|
||||
|
||||
tabList[1].querySelector('.el-icon-close').click();
|
||||
vm.$nextTick(_ => {
|
||||
expect(tabList.length).to.be.equal(2);
|
||||
expect(paneList.length).to.be.equal(2);
|
||||
expect(tabList[1].classList.contains('is-active')).to.be.true;
|
||||
|
||||
vm.$refs.tabs.$el.querySelector('.el-tabs__new-tab').click();
|
||||
vm.$nextTick(_ => {
|
||||
expect(tabList.length).to.be.equal(3);
|
||||
expect(paneList.length).to.be.equal(3);
|
||||
expect(tabList[2].classList.contains('is-active')).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
it('addable & closable', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<el-tabs
|
||||
ref="tabs"
|
||||
v-model="editableTabsValue"
|
||||
type="card"
|
||||
addable
|
||||
closable
|
||||
@tab-add="addTab"
|
||||
@tab-remove="removeTab"
|
||||
>
|
||||
<el-tab-pane
|
||||
v-for="(item, index) in editableTabs"
|
||||
:label="item.title"
|
||||
:name="item.name"
|
||||
>
|
||||
{{item.content}}
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
`,
|
||||
data() {
|
||||
return {
|
||||
editableTabsValue: '2',
|
||||
editableTabs: [{
|
||||
title: 'Tab 1',
|
||||
name: '1',
|
||||
content: 'Tab 1 content'
|
||||
}, {
|
||||
title: 'Tab 2',
|
||||
name: '2',
|
||||
content: 'Tab 2 content'
|
||||
}],
|
||||
tabIndex: 2
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
addTab(targetName) {
|
||||
let newTabName = ++this.tabIndex + '';
|
||||
this.editableTabs.push({
|
||||
title: 'New Tab',
|
||||
name: newTabName,
|
||||
content: 'New Tab content'
|
||||
});
|
||||
this.editableTabsValue = newTabName;
|
||||
},
|
||||
removeTab(targetName) {
|
||||
let tabs = this.editableTabs;
|
||||
let activeName = this.editableTabsValue;
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
let nextTab = tabs[index + 1] || tabs[index - 1];
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
this.editableTabsValue = activeName;
|
||||
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
setTimeout(_ => {
|
||||
const tabList = vm.$refs.tabs.$refs.nav.$refs.tabs;
|
||||
const paneList = vm.$el.querySelector('.el-tabs__content').children;
|
||||
|
||||
vm.$refs.tabs.$el.querySelector('.el-tabs__new-tab').click();
|
||||
|
||||
vm.$nextTick(_ => {
|
||||
expect(tabList.length).to.be.equal(3);
|
||||
expect(paneList.length).to.be.equal(3);
|
||||
expect(spy.calledOnce).to.true;
|
||||
expect(tabList[1].innerText.trim()).to.be.equal('角色管理');
|
||||
expect(paneList[0].innerText.trim()).to.be.equal('A');
|
||||
done();
|
||||
expect(tabList[2].classList.contains('is-active')).to.be.true;
|
||||
|
||||
tabList[2].querySelector('.el-icon-close').click();
|
||||
vm.$nextTick(_ => {
|
||||
expect(tabList.length).to.be.equal(2);
|
||||
expect(paneList.length).to.be.equal(2);
|
||||
expect(tabList[1].classList.contains('is-active')).to.be.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
}, 100);
|
||||
});
|
||||
@ -187,42 +326,6 @@ describe('Tabs', () => {
|
||||
done();
|
||||
}, 100);
|
||||
});
|
||||
it('closable edge', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
<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>
|
||||
<el-tab-pane label="定时任务补偿">D</el-tab-pane>
|
||||
</el-tabs>
|
||||
`
|
||||
}, true);
|
||||
|
||||
vm.$nextTick(_ => {
|
||||
const paneList = vm.$el.querySelector('.el-tabs__content').children;
|
||||
const tabList = vm.$refs.tabs.$refs.tabs;
|
||||
|
||||
tabList[0].querySelector('.el-icon-close').click();
|
||||
vm.$nextTick(_ => {
|
||||
expect(tabList.length).to.be.equal(3);
|
||||
expect(paneList.length).to.be.equal(3);
|
||||
expect(tabList[0].innerText.trim()).to.be.equal('配置管理');
|
||||
expect(paneList[0].innerText.trim()).to.be.equal('B');
|
||||
|
||||
tabList[2].click();
|
||||
tabList[2].querySelector('.el-icon-close').click();
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('disabled', done => {
|
||||
vm = createVue({
|
||||
template: `
|
||||
@ -236,7 +339,7 @@ describe('Tabs', () => {
|
||||
}, true);
|
||||
|
||||
vm.$nextTick(_ => {
|
||||
const tabList = vm.$refs.tabs.$refs.tabs;
|
||||
const tabList = vm.$refs.tabs.$refs.nav.$refs.tabs;
|
||||
|
||||
tabList[1].click();
|
||||
vm.$nextTick(_ => {
|
||||
|
@ -6,22 +6,27 @@ describe('Tooltip', () => {
|
||||
destroyVM(vm);
|
||||
});
|
||||
|
||||
it('create', () => {
|
||||
it('create', done => {
|
||||
vm = createVue(`
|
||||
<el-tooltip content="提示文字">
|
||||
<el-tooltip ref="tooltip" content="提示文字">
|
||||
<button>click</button>
|
||||
</el-tooltip>`);
|
||||
|
||||
expect(vm.$el.querySelector('.el-tooltip__popper')).to.have.property('textContent', '提示文字');
|
||||
vm.$nextTick(_ => {
|
||||
expect(vm.$refs.tooltip.popperVM.$el).to.have.property('textContent', '提示文字');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('custom popper class', () => {
|
||||
it('custom popper class', done => {
|
||||
vm = createVue(`
|
||||
<el-tooltip content="提示文字" popper-class="custom-popper">
|
||||
<el-tooltip ref="tooltip" content="提示文字" popper-class="custom-popper">
|
||||
<button>click</button>
|
||||
</el-tooltip>`);
|
||||
|
||||
expect(vm.$el.querySelector('.el-tooltip__popper').classList.contains('custom-popper')).to.true;
|
||||
vm.$nextTick(_ => {
|
||||
expect(vm.$refs.tooltip.popperVM.$el.classList.contains('custom-popper')).to.true;
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('manual', () => {
|
||||
@ -73,18 +78,24 @@ describe('Tooltip', () => {
|
||||
triggerEvent(tooltip.$el, 'mouseenter');
|
||||
it('popperElm is exist', () => expect(tooltip.popperElm).to.exist);
|
||||
it('showPopper is true', () => expect(tooltip.showPopper).to.true);
|
||||
it('close popper', () => {
|
||||
it('close popper', done => {
|
||||
triggerEvent(tooltip.$el, 'mouseleave');
|
||||
expect(tooltip.showPopper).to.false;
|
||||
setTimeout(() => {
|
||||
expect(tooltip.showPopper).to.false;
|
||||
done();
|
||||
}, 300);
|
||||
});
|
||||
});
|
||||
|
||||
it('light mode', () => {
|
||||
it('light mode', done => {
|
||||
vm = createVue(`
|
||||
<el-tooltip content="abc" effect="light">
|
||||
<el-tooltip ref="tooltip" content="abc" effect="light">
|
||||
<button>abc</button>
|
||||
</el-tooltip>
|
||||
`);
|
||||
expect(vm.$el.querySelector('.is-light')).to.exist;
|
||||
vm.$nextTick(_ => {
|
||||
expect(vm.$refs.tooltip.popperVM.$el.classList.contains('is-light')).to.exist;
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
18
yarn.lock
18
yarn.lock
@ -437,6 +437,10 @@ babel-messages@^6.8.0:
|
||||
dependencies:
|
||||
babel-runtime "^6.0.0"
|
||||
|
||||
babel-plugin-add-module-exports@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz#9ae9a1f4a8dc67f0cdec4f4aeda1e43a5ff65e25"
|
||||
|
||||
babel-plugin-check-es2015-constants@^6.3.13:
|
||||
version "6.8.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.8.0.tgz#dbf024c32ed37bfda8dee1e76da02386a8d26fe7"
|
||||
@ -6905,20 +6909,19 @@ vue-hot-reload-api@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/vue-hot-reload-api/-/vue-hot-reload-api-2.0.6.tgz#817d4bfb30f55428aa1012d029499e07f3147d21"
|
||||
|
||||
vue-loader@>=9.4.2, vue-loader@^10.0.2:
|
||||
version "10.0.2"
|
||||
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-10.0.2.tgz#6fe7bcefb32c5439bd0338464aa22673ad62299c"
|
||||
version "10.3.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-loader/-/vue-loader-10.3.0.tgz#436421736e9ad0f1c481330327c376963db86a19"
|
||||
dependencies:
|
||||
consolidate "^0.14.0"
|
||||
hash-sum "^1.0.2"
|
||||
js-beautify "^1.6.3"
|
||||
loader-utils "^0.2.10"
|
||||
lru-cache "^4.0.1"
|
||||
object-assign "^4.0.0"
|
||||
postcss "^5.0.10"
|
||||
postcss-selector-parser "^2.0.0"
|
||||
source-map "^0.5.6"
|
||||
vue-hot-reload-api "^2.0.1"
|
||||
vue-style-loader "^1.0.0"
|
||||
vue-style-loader "^2.0.0"
|
||||
vue-template-es2015-compiler "^1.2.2"
|
||||
|
||||
vue-markdown-loader@^0.5.1:
|
||||
@ -6941,6 +6944,13 @@ vue-style-loader@^1.0.0:
|
||||
dependencies:
|
||||
loader-utils "^0.2.7"
|
||||
|
||||
vue-style-loader@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/vue-style-loader/-/vue-style-loader-2.0.0.tgz#1a3bb55239ac541ee3af0301d66f16fc86786543"
|
||||
dependencies:
|
||||
hash-sum "^1.0.2"
|
||||
loader-utils "^0.2.7"
|
||||
|
||||
vue-template-compiler@*, vue-template-compiler@^2.1.8:
|
||||
version "2.1.8"
|
||||
resolved "https://registry.yarnpkg.com/vue-template-compiler/-/vue-template-compiler-2.1.8.tgz#12dd1cc63793f59be580c694a61610cb9369d629"
|
||||
|
Loading…
Reference in New Issue
Block a user