Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
E
Element
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
CI / CD Analytics
Repository Analytics
Value Stream Analytics
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
林焕东
Element
Commits
1a16fbb7
Commit
1a16fbb7
authored
Jan 16, 2017
by
baiyaaaaa
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
add cascader
parent
0a408949
Changes
14
Hide whitespace changes
Inline
Side-by-side
Showing
14 changed files
with
641 additions
and
27 deletions
+641
-27
components.json
components.json
+2
-1
examples/docs/en-US/cascader.md
examples/docs/en-US/cascader.md
+1
-0
examples/docs/zh-CN/cascader.md
examples/docs/zh-CN/cascader.md
+109
-0
examples/nav.config.json
examples/nav.config.json
+8
-0
packages/cascader/cooking.conf.js
packages/cascader/cooking.conf.js
+18
-0
packages/cascader/index.js
packages/cascader/index.js
+8
-0
packages/cascader/package.json
packages/cascader/package.json
+15
-0
packages/cascader/src/main.vue
packages/cascader/src/main.vue
+207
-0
packages/cascader/src/menu.vue
packages/cascader/src/menu.vue
+133
-0
packages/theme-default/src/cascader.css
packages/theme-default/src/cascader.css
+118
-24
packages/theme-default/src/index.css
packages/theme-default/src/index.css
+1
-0
src/index.js
src/index.js
+5
-2
src/utils/vue-popper.js
src/utils/vue-popper.js
+1
-0
test/unit/specs/cascader.spec.js
test/unit/specs/cascader.spec.js
+15
-0
No files found.
components.json
View file @
1a16fbb7
...
...
@@ -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"
}
examples/docs/en-US/cascader.md
0 → 100644
View file @
1a16fbb7
## cascader
examples/docs/zh-CN/cascader.md
0 → 100644
View file @
1a16fbb7
<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:
[]
,
selectedOptions2:
[
'jiangsu', 'nanjing', 'zhonghuamen'
]
,
selectedOptions3:
[]
,
selectedOptions4:
[]
,
selectedOptions5:
[]
};
},
methods: {
}
};
</script>
## 级联选择
需要从一组相关联的数据集合进行选择,例如省市区,公司层级,事物分类等。
从一个较大的数据集合中进行选择时,用多级分类进行分隔,方便选择。
### 基本使用
:::demo
```
html
<el-cascader
:options=
"options"
v-model=
"selectedOptions"
></el-cascader>
```
:::
### 默认值
:::demo
```
html
<el-cascader
:options=
"options"
v-model=
"selectedOptions2"
></el-cascader>
```
:::
### 移入展开
:::demo
```
html
<el-cascader
:options=
"options"
v-model=
"selectedOptions3"
expand-trigger=
"hover"
></el-cascader>
```
:::
### 选择即改变
:::demo
```
html
<el-cascader
:options=
"options"
v-model=
"selectedOptions4"
change-on-select
></el-cascader>
```
:::
### 可搜索
:::demo
```
html
<el-cascader
:options=
"options"
v-model=
"selectedOptions5"
show-search
></el-cascader>
```
:::
\ No newline at end of file
examples/nav.config.json
View file @
1a16fbb7
...
...
@@ -215,6 +215,10 @@
{
"path"
:
"/collapse"
,
"title"
:
"Collapse 折叠面板"
},
{
"path"
:
"/cascader"
,
"title"
:
"Cascader 级联选择"
}
]
}
...
...
@@ -437,6 +441,10 @@
{
"path"
:
"/collapse"
,
"title"
:
"Collapse"
},
{
"path"
:
"/cascader"
,
"title"
:
"Cascader"
}
]
}
...
...
packages/cascader/cooking.conf.js
0 → 100644
View file @
1a16fbb7
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
();
packages/cascader/index.js
0 → 100644
View file @
1a16fbb7
import
Cascader
from
'
./src/main
'
;
/* istanbul ignore next */
Cascader
.
install
=
function
(
Vue
)
{
Vue
.
component
(
Cascader
.
name
,
Cascader
);
};
export
default
Cascader
;
packages/cascader/package.json
0 → 100644
View file @
1a16fbb7
{
"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"
:
{}
}
packages/cascader/src/main.vue
0 → 100644
View file @
1a16fbb7
<
template
>
<span
class=
"el-cascader"
:class=
"
{
'is-opened': menuVisible
}"
@click="menuVisible = !menuVisible"
@mouseenter="inputHover = true"
@mouseleave="inputHover = false"
ref="reference"
v-clickoutside="handleClickoutside"
>
<el-input
:readonly=
"!showSearch"
:placeholder=
"placeholder"
v-model=
"inputValue"
@
change=
"handleInputChange"
>
<template
slot=
"icon"
>
<i
key=
"1"
v-if=
"inputHover && displayValue !== ''"
class=
"el-input__icon el-icon-circle-close"
@
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
'
;
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
],
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
'
},
showSearch
:
Boolean
},
data
()
{
return
{
currentValue
:
this
.
value
,
displayValue
:
this
.
value
.
join
(
'
/
'
),
menuVisible
:
false
,
inputHover
:
false
,
inputValue
:
''
,
flatOptions
:
this
.
showSearch
&&
this
.
flattenOptions
(
this
.
options
)
};
},
watch
:
{
menuVisible
(
value
)
{
value
?
this
.
showMenu
()
:
this
.
hideMenu
();
},
value
(
value
)
{
this
.
currentValue
=
value
;
},
currentValue
(
value
)
{
this
.
displayValue
=
value
.
join
(
'
/
'
);
}
},
methods
:
{
showMenu
()
{
if
(
!
this
.
menu
)
{
this
.
menu
=
new
Vue
(
ElCascaderMenu
).
$mount
(
document
.
createElement
(
'
div
'
));
this
.
menu
.
options
=
this
.
options
;
this
.
menu
.
expandTrigger
=
this
.
expandTrigger
;
this
.
menu
.
changeOnSelect
=
this
.
changeOnSelect
;
this
.
popperElm
=
this
.
menu
.
$el
;
}
this
.
menu
.
value
=
this
.
currentValue
.
slice
(
0
);
this
.
menu
.
visible
=
true
;
this
.
menu
.
$on
(
'
change
'
,
this
.
handlePick
);
this
.
updatePopper
();
},
hideMenu
()
{
this
.
menu
.
visible
=
false
;
this
.
inputValue
=
''
;
},
handlePick
(
value
,
close
=
true
)
{
this
.
currentValue
=
value
;
this
.
$emit
(
'
input
'
,
value
);
if
(
close
)
{
this
.
menuVisible
=
false
;
}
},
handleInputChange
(
value
)
{
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
)
{
this
.
menu
.
options
=
filteredFlatOptions
.
map
(
optionStack
=>
{
return
{
__IS__FLAT__OPTIONS
:
true
,
value
:
optionStack
.
map
(
item
=>
item
.
value
),
label
:
this
.
renderRenderFilteredOption
(
value
,
optionStack
)
};
});
}
else
{
return
[{
label
:
'
notFoundContent
'
,
value
:
'
ANT_CASCADER_NOT_FOUND
'
,
disabled
:
true
}];
}
},
renderRenderFilteredOption
(
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
);
}
if
(
option
.
children
)
{
flatOptions
=
flatOptions
.
concat
(
this
.
flattenOptions
(
option
.
children
,
optionsStack
));
}
});
return
flatOptions
;
},
clearValue
(
ev
)
{
ev
.
stopPropagation
();
this
.
handlePick
([],
true
);
},
handleClickoutside
()
{
this
.
menuVisible
=
false
;
}
}
};
</
script
>
packages/cascader/src/menu.vue
0 → 100644
View file @
1a16fbb7
<
script
>
export
default
{
name
:
'
ElCascaderMenu
'
,
data
()
{
return
{
options
:
[],
visible
:
false
,
activeValue
:
[],
value
:
[],
expandTrigger
:
'
click
'
,
changeOnSelect
:
false
};
},
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
;
let
options
=
this
.
options
;
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
;
};
const
result
=
loadActiveOptions
(
options
);
return
result
;
}
}
},
methods
:
{
selectItem
(
item
,
menuIndex
)
{
const
len
=
this
.
activeOptions
.
length
;
const
closeMenu
=
!
item
.
children
;
if
(
item
.
__IS__FLAT__OPTIONS
)
{
this
.
activeValue
.
splice
(
menuIndex
,
len
,
...
item
.
value
);
}
else
{
this
.
activeValue
.
splice
(
menuIndex
,
len
,
item
.
value
);
}
if
(
this
.
changeOnSelect
)
{
this
.
$emit
(
'
change
'
,
this
.
activeValue
,
closeMenu
);
}
},
expandItem
(
item
,
menuIndex
)
{
const
len
=
this
.
activeOptions
.
length
;
if
(
item
.
children
)
{
this
.
activeValue
.
splice
(
menuIndex
,
len
,
item
.
value
);
this
.
activeOptions
.
splice
(
menuIndex
+
1
,
len
,
item
.
children
);
}
},
handleItemClick
(
item
,
menuIndex
)
{
this
.
expandItem
(
item
,
menuIndex
);
this
.
selectItem
(
item
,
menuIndex
);
if
(
!
item
.
children
&&
!
this
.
changeOnSelect
)
{
this
.
$emit
(
'
change
'
,
this
.
activeValue
);
}
}
},
render
(
h
)
{
const
{
activeValue
,
activeOptions
,
visible
,
expandTrigger
}
=
this
;
const
menus
=
this
.
_l
(
activeOptions
,
(
menu
,
index
)
=>
{
const
items
=
this
.
_l
(
menu
,
item
=>
{
const
events
=
{
on
:
{}
};
if
(
expandTrigger
===
'
click
'
||
!
item
.
children
)
{
events
.
on
[
'
click
'
]
=
()
=>
{
this
.
handleItemClick
(
item
,
index
);
};
}
else
{
events
.
on
[
'
mouseenter
'
]
=
()
=>
{
this
.
expandItem
(
item
,
index
);
};
}
return
(
<
li
class
=
{{
'
el-cascader-menu__item
'
:
true
,
'
el-cascader-menu__item--extensible
'
:
item
.
children
,
'
is-active
'
:
item
.
value
===
activeValue
[
index
]
}}
{...
events
}
>
{
item
.
label
}
<
/li
>
);
});
return
<
ul
class
=
"
el-cascader-menu
"
>
{
items
}
<
/ul>
;
});
return
(
<
transition
name
=
"
el-zoom-in-top
"
>
<
div
class
=
"
el-cascader-menus
"
v
-
show
=
{
visible
}
>
{
menus
}
<
/div
>
<
/transition
>
);
}
};
</
script
>
\ No newline at end of file
packages/theme-default/src/cascader.css
View file @
1a16fbb7
...
...
@@ -3,42 +3,136 @@
@import
"./common/var.css"
;
/*@import "./core/dropdown.css";*/
@component-namespace
el
ement
{
@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
:
var
(
--input-height
);
line-height
:
@
height
;
padding
:
0
15px
0
10px
;
color
:
var
(
--input-color
);
width
:
100%
;
white-space
:
nowrap
;
z-index
:
10
;
text-overflow
:
ellipsis
;
overflow
:
hidden
;
box-sizing
:
border-box
;
cursor
:
pointer
;
}
}
@e
wrap
{
overflow
:
hidden
;
@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
menu
{
border
:
0
;
box-shadow
:
none
;
display
:
inline-block
;
margin
:
0
;
@e
item
{
font-size
:
var
(
--select-font-size
);
padding
:
8px
30px
8px
10px
;
position
:
relative
;
vertical-align
:
top
;
&::before
{
border-left
:
var
(
--cascader-menu-border
);
content
:
" "
;
height
:
var
(
--cascader-height
);
left
:
0
;
position
:
absolute
;
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
);
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
);
}
}
}
}
...
...
packages/theme-default/src/index.css
View file @
1a16fbb7
...
...
@@ -44,3 +44,4 @@
@import
"./carousel.css"
;
@import
"./carousel-item.css"
;
@import
"./collapse.css"
;
@import
"./cascader.css"
;
src/index.js
View file @
1a16fbb7
...
...
@@ -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
};
src/utils/vue-popper.js
View file @
1a16fbb7
...
...
@@ -83,6 +83,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
);
...
...
test/unit/specs/cascader.spec.js
0 → 100644
View file @
1a16fbb7
import
{
createTest
,
destroyVM
}
from
'
../util
'
;
import
Cascader
from
'
packages/cascader
'
;
describe
(
'
Cascader
'
,
()
=>
{
let
vm
;
afterEach
(()
=>
{
destroyVM
(
vm
);
});
it
(
'
create
'
,
()
=>
{
vm
=
createTest
(
Cascader
,
true
);
expect
(
vm
.
$el
).
to
.
exist
;
});
});
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment