Commit c463ab87579ca750bc91fb6873f8f0730295bc5d
1 parent
6ff31952
add Cascader
add Cascader
Showing
9 changed files
with
345 additions
and
60 deletions
Show diff stats
src/components/cascader/cascader.vue
| 1 | 1 | <template> |
| 2 | - <div :class="[prefixCls]"> | |
| 2 | + <div :class="classes" v-clickoutside="handleClose"> | |
| 3 | 3 | <i-input |
| 4 | 4 | readonly |
| 5 | 5 | :disabled="disabled" |
| 6 | 6 | :value.sync="displayRender" |
| 7 | 7 | :size="size" |
| 8 | - :placeholder="placeholder"></i-input> | |
| 8 | + :placeholder="placeholder" | |
| 9 | + @on-focus="onFocus"></i-input> | |
| 10 | + <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.stop="clearSelect"></Icon> | |
| 11 | + <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']"></Icon> | |
| 12 | + <Dropdown v-show="visible" transition="slide-up"> | |
| 13 | + <div> | |
| 14 | + <Caspanel | |
| 15 | + :prefix-cls="prefixCls" | |
| 16 | + :data.sync="data" | |
| 17 | + :disabled="disabled" | |
| 18 | + :trigger="trigger" | |
| 19 | + @on-update-result="updateResult"></Caspanel> | |
| 20 | + </div> | |
| 21 | + </Dropdown> | |
| 9 | 22 | </div> |
| 10 | 23 | </template> |
| 11 | 24 | <script> |
| 12 | 25 | import iInput from '../input/input.vue'; |
| 13 | 26 | import Dropdown from '../select/dropdown.vue'; |
| 27 | + import Icon from '../icon/icon.vue'; | |
| 28 | + import Caspanel from './caspanel.vue'; | |
| 14 | 29 | import clickoutside from '../../directives/clickoutside'; |
| 15 | - import { oneOf, MutationObserver } from '../../utils/assist'; | |
| 30 | + import { oneOf } from '../../utils/assist'; | |
| 16 | 31 | |
| 17 | 32 | const prefixCls = 'ivu-cascader'; |
| 18 | 33 | |
| 19 | 34 | export default { |
| 35 | + components: { iInput, Dropdown, Icon, Caspanel }, | |
| 36 | + directives: { clickoutside }, | |
| 20 | 37 | props: { |
| 21 | 38 | data: { |
| 22 | 39 | type: Array, |
| ... | ... | @@ -33,7 +50,7 @@ |
| 33 | 50 | }, |
| 34 | 51 | clearable: { |
| 35 | 52 | type: Boolean, |
| 36 | - default: false | |
| 53 | + default: true | |
| 37 | 54 | }, |
| 38 | 55 | placeholder: { |
| 39 | 56 | type: String, |
| ... | ... | @@ -56,18 +73,31 @@ |
| 56 | 73 | }, |
| 57 | 74 | renderFormat: { |
| 58 | 75 | type: Function, |
| 59 | - default: (label, selectedData) => { | |
| 60 | - label.join('/'); | |
| 76 | + default (label, selectedData) { | |
| 77 | + return label.join('/'); | |
| 61 | 78 | } |
| 62 | 79 | } |
| 63 | 80 | }, |
| 64 | 81 | data () { |
| 65 | 82 | return { |
| 66 | 83 | prefixCls: prefixCls, |
| 67 | - selected: [] | |
| 84 | + visible: false, | |
| 85 | + selected: [], | |
| 86 | + tmpSelected: [] | |
| 68 | 87 | } |
| 69 | 88 | }, |
| 70 | 89 | computed: { |
| 90 | + classes () { | |
| 91 | + return [ | |
| 92 | + `${prefixCls}`, | |
| 93 | + { | |
| 94 | + [`${prefixCls}-show-clear`]: this.showCloseIcon | |
| 95 | + } | |
| 96 | + ] | |
| 97 | + }, | |
| 98 | + showCloseIcon () { | |
| 99 | + return this.value && this.value.length && this.clearable; | |
| 100 | + }, | |
| 71 | 101 | displayRender () { |
| 72 | 102 | let label = []; |
| 73 | 103 | for (let i = 0; i < this.selected.length; i++) { |
| ... | ... | @@ -78,7 +108,22 @@ |
| 78 | 108 | } |
| 79 | 109 | }, |
| 80 | 110 | methods: { |
| 81 | - | |
| 111 | + clearSelect () { | |
| 112 | + | |
| 113 | + }, | |
| 114 | + handleClose () { | |
| 115 | + this.visible = false; | |
| 116 | + }, | |
| 117 | + onFocus () { | |
| 118 | + this.visible = true; | |
| 119 | + }, | |
| 120 | + updateResult (result) { | |
| 121 | + console.log(JSON.stringify(result)) | |
| 122 | + this.selected = result; | |
| 123 | + } | |
| 124 | + }, | |
| 125 | + ready () { | |
| 126 | + | |
| 82 | 127 | } |
| 83 | 128 | } |
| 84 | 129 | </script> |
| 85 | 130 | \ No newline at end of file | ... | ... |
| 1 | +<template> | |
| 2 | + <li :class="classes">{{ data.label }}<i v-if="data.children && data.children.length" class="ivu-icon ivu-icon-ios-arrow-right"></i></li> | |
| 3 | +</template> | |
| 4 | +<script> | |
| 5 | + export default { | |
| 6 | + props: { | |
| 7 | + data: Object, | |
| 8 | + prefixCls: String, | |
| 9 | + tmpItem: Object | |
| 10 | + }, | |
| 11 | + computed: { | |
| 12 | + classes () { | |
| 13 | + return [ | |
| 14 | + `${this.prefixCls}-menu-item`, | |
| 15 | + { | |
| 16 | + [`${this.prefixCls}-menu-item-active`]: this.tmpItem.value === this.data.value | |
| 17 | + } | |
| 18 | + ] | |
| 19 | + } | |
| 20 | + }, | |
| 21 | + ready () { | |
| 22 | + | |
| 23 | + } | |
| 24 | + } | |
| 25 | +</script> | |
| 0 | 26 | \ No newline at end of file | ... | ... |
| 1 | +<template> | |
| 2 | + <ul v-if="data && data.length" :class="[prefixCls + '-menu']"> | |
| 3 | + <Casitem | |
| 4 | + v-for="item in data" | |
| 5 | + :prefix-cls="prefixCls" | |
| 6 | + :data.sync="item" | |
| 7 | + :tmp-item="tmpItem" | |
| 8 | + @click.stop="handleClickItem(item)" | |
| 9 | + @mouseenter.stop="handleHoverItem(item)"></Casitem> | |
| 10 | + </ul><Caspanel v-if="sublist && sublist.length" :prefix-cls="prefixCls" :data.sync="sublist" :disabled="disabled" :trigger="trigger" @on-update-result="updateResult"></Caspanel> | |
| 11 | +</template> | |
| 12 | +<script> | |
| 13 | + import Casitem from './casitem.vue'; | |
| 14 | + import { oneOf } from '../../utils/assist'; | |
| 15 | + | |
| 16 | + export default { | |
| 17 | + name: 'Caspanel', | |
| 18 | + components: { Casitem }, | |
| 19 | + props: { | |
| 20 | + data: { | |
| 21 | + type: Array, | |
| 22 | + default () { | |
| 23 | + return [] | |
| 24 | + } | |
| 25 | + }, | |
| 26 | + sublist: { | |
| 27 | + type: Array, | |
| 28 | + default () { | |
| 29 | + return [] | |
| 30 | + } | |
| 31 | + }, | |
| 32 | + disabled: Boolean, | |
| 33 | + changeOnSelect: Boolean, | |
| 34 | + trigger: { | |
| 35 | + validator (value) { | |
| 36 | + return oneOf(value, ['click', 'hover']); | |
| 37 | + } | |
| 38 | + }, | |
| 39 | + prefixCls: String | |
| 40 | + }, | |
| 41 | + data () { | |
| 42 | + return { | |
| 43 | + tmpItem: {}, | |
| 44 | + result: [] | |
| 45 | + } | |
| 46 | + }, | |
| 47 | + methods: { | |
| 48 | + handleClickItem (item) { | |
| 49 | + if (this.trigger !== 'click') return; | |
| 50 | + this.handleTriggerItem(item); | |
| 51 | + }, | |
| 52 | + handleHoverItem (item) { | |
| 53 | + if (this.trigger !== 'hover') return; | |
| 54 | + this.handleTriggerItem(item); | |
| 55 | + }, | |
| 56 | + handleTriggerItem (item) { | |
| 57 | + if (item.disabled) return; | |
| 58 | + | |
| 59 | + if (item.children && item.children.length){ | |
| 60 | + this.sublist = item.children; | |
| 61 | + // todo 实时选择 | |
| 62 | + } else { | |
| 63 | + this.sublist = []; | |
| 64 | + // todo 选择 | |
| 65 | + } | |
| 66 | + | |
| 67 | + // return value back | |
| 68 | + const backItem = this.getBaseItem(item); | |
| 69 | + | |
| 70 | + this.tmpItem = backItem; | |
| 71 | + this.$emit('on-update-result', [backItem]); | |
| 72 | + }, | |
| 73 | + updateResult (item) { | |
| 74 | + this.result = [this.tmpItem].concat(item); | |
| 75 | + this.$emit('on-update-result', this.result); | |
| 76 | + }, | |
| 77 | + getBaseItem (item) { | |
| 78 | + let backItem = Object.assign({}, item); | |
| 79 | + if (backItem.children) { | |
| 80 | + delete backItem.children; | |
| 81 | + } | |
| 82 | + | |
| 83 | + return backItem; | |
| 84 | + } | |
| 85 | + }, | |
| 86 | + watch: { | |
| 87 | + data () { | |
| 88 | + this.sublist = []; | |
| 89 | + } | |
| 90 | + }, | |
| 91 | + ready () { | |
| 92 | + // todo 初始化时,判断预设的值 | |
| 93 | + } | |
| 94 | + } | |
| 95 | +</script> | |
| 0 | 96 | \ No newline at end of file | ... | ... |
| 1 | +@cascader-prefix-cls: ~"@{css-prefix}cascader"; | |
| 2 | +@cascader-item-prefix-cls: ~"@{css-prefix}cascader-menu-item"; | |
| 3 | + | |
| 4 | +.@{cascader-prefix-cls} { | |
| 5 | + position: relative; | |
| 6 | + | |
| 7 | + .@{css-prefix}input{ | |
| 8 | + display: block; | |
| 9 | + cursor: pointer; | |
| 10 | + } | |
| 11 | + | |
| 12 | + .@{cascader-prefix-cls}-arrow:nth-of-type(1) { | |
| 13 | + display: none; | |
| 14 | + cursor: pointer; | |
| 15 | + } | |
| 16 | + | |
| 17 | + &:hover { | |
| 18 | + .@{cascader-prefix-cls}-arrow:nth-of-type(1) { | |
| 19 | + display: inline-block; | |
| 20 | + } | |
| 21 | + } | |
| 22 | + &-show-clear:hover .@{cascader-prefix-cls}-arrow:nth-of-type(2){ | |
| 23 | + display: none; | |
| 24 | + } | |
| 25 | + | |
| 26 | + &-arrow { | |
| 27 | + position: absolute; | |
| 28 | + top: 50%; | |
| 29 | + right: 8px; | |
| 30 | + line-height: 1; | |
| 31 | + margin-top: -6px; | |
| 32 | + font-size: @font-size-base; | |
| 33 | + color: @subsidiary-color; | |
| 34 | + .transition(all @transition-time @ease-in-out); | |
| 35 | + } | |
| 36 | + | |
| 37 | + .@{select-dropdown-prefix-cls} { | |
| 38 | + padding: 0; | |
| 39 | + white-space: nowrap; | |
| 40 | + } | |
| 41 | + | |
| 42 | + .select-item(@cascader-prefix-cls, @cascader-item-prefix-cls); | |
| 43 | + | |
| 44 | + &-menu{ | |
| 45 | + display: inline-block; | |
| 46 | + min-width: 100px; | |
| 47 | + height: 180px; | |
| 48 | + margin: 0; | |
| 49 | + padding: 5px 0; | |
| 50 | + vertical-align: top; | |
| 51 | + list-style: none; | |
| 52 | + border-right: 1px solid @border-color-split; | |
| 53 | + overflow: auto; | |
| 54 | + | |
| 55 | + &:first-child { | |
| 56 | + | |
| 57 | + } | |
| 58 | + &:last-child { | |
| 59 | + border-right-color: transparent; | |
| 60 | + margin-right: -1px; | |
| 61 | + } | |
| 62 | + &:only-child { | |
| 63 | + | |
| 64 | + } | |
| 65 | + | |
| 66 | + & &-item{ | |
| 67 | + position: relative; | |
| 68 | + padding-right: 24px; | |
| 69 | + .transition(all @transition-time @ease-in-out); | |
| 70 | + | |
| 71 | + i{ | |
| 72 | + font-size: @font-size-small; | |
| 73 | + position: absolute; | |
| 74 | + right: 15px; | |
| 75 | + top: 50%; | |
| 76 | + margin-top: -6px; | |
| 77 | + } | |
| 78 | + | |
| 79 | + &-active{ | |
| 80 | + background-color: @background-color-select-hover; | |
| 81 | + font-weight: bold; | |
| 82 | + } | |
| 83 | + } | |
| 84 | + } | |
| 85 | +} | |
| 0 | 86 | \ No newline at end of file | ... | ... |
src/styles/components/index.less
src/styles/components/select.less
| ... | ... | @@ -183,50 +183,7 @@ |
| 183 | 183 | } |
| 184 | 184 | } |
| 185 | 185 | |
| 186 | -.@{select-item-prefix-cls} { | |
| 187 | - margin: 0; | |
| 188 | - padding: 7px 16px; | |
| 189 | - clear: both; | |
| 190 | - color: @text-color; | |
| 191 | - font-size: @font-size-small !important; | |
| 192 | - //border-radius: @btn-border-radius-small; | |
| 193 | - white-space: nowrap; | |
| 194 | - cursor: pointer; | |
| 195 | - .transition(background @transition-time @ease-in-out); | |
| 196 | - | |
| 197 | - &:hover{ | |
| 198 | - background: @background-color-select-hover; | |
| 199 | - } | |
| 200 | - | |
| 201 | - &-focus { | |
| 202 | - background: @background-color-select-hover; | |
| 203 | - } | |
| 204 | - | |
| 205 | - &-disabled { | |
| 206 | - color: @btn-disable-color; | |
| 207 | - cursor: @cursor-disabled; | |
| 208 | - | |
| 209 | - &:hover { | |
| 210 | - color: @btn-disable-color; | |
| 211 | - background-color: #fff; | |
| 212 | - cursor: @cursor-disabled; | |
| 213 | - } | |
| 214 | - } | |
| 215 | - | |
| 216 | - &-selected ,&-selected:hover{ | |
| 217 | - color: #fff; | |
| 218 | - background: @selected-color; | |
| 219 | - } | |
| 220 | - | |
| 221 | - &-selected&-focus { | |
| 222 | - background: shade(@selected-color, 10%); | |
| 223 | - } | |
| 224 | -} | |
| 225 | - | |
| 226 | -.@{select-prefix-cls}-large .@{select-item-prefix-cls}{ | |
| 227 | - padding: 7px 16px 8px; | |
| 228 | - font-size: @font-size-base !important; | |
| 229 | -} | |
| 186 | +.select-item(@select-prefix-cls, @select-item-prefix-cls); | |
| 230 | 187 | |
| 231 | 188 | .@{select-prefix-cls}-multiple .@{select-item-prefix-cls} { |
| 232 | 189 | &-selected{ | ... | ... |
src/styles/mixins/index.less
| 1 | +.select-item(@size-class, @item-class) { | |
| 2 | + .@{item-class} { | |
| 3 | + margin: 0; | |
| 4 | + padding: 7px 16px; | |
| 5 | + clear: both; | |
| 6 | + color: @text-color; | |
| 7 | + font-size: @font-size-small !important; | |
| 8 | + white-space: nowrap; | |
| 9 | + list-style: none; | |
| 10 | + cursor: pointer; | |
| 11 | + .transition(background @transition-time @ease-in-out); | |
| 12 | + | |
| 13 | + &:hover{ | |
| 14 | + background: @background-color-select-hover; | |
| 15 | + } | |
| 16 | + | |
| 17 | + &-focus { | |
| 18 | + background: @background-color-select-hover; | |
| 19 | + } | |
| 20 | + | |
| 21 | + &-disabled { | |
| 22 | + color: @btn-disable-color; | |
| 23 | + cursor: @cursor-disabled; | |
| 24 | + | |
| 25 | + &:hover { | |
| 26 | + color: @btn-disable-color; | |
| 27 | + background-color: #fff; | |
| 28 | + cursor: @cursor-disabled; | |
| 29 | + } | |
| 30 | + } | |
| 31 | + | |
| 32 | + &-selected ,&-selected:hover{ | |
| 33 | + color: #fff; | |
| 34 | + background: @selected-color; | |
| 35 | + } | |
| 36 | + | |
| 37 | + &-selected&-focus { | |
| 38 | + background: shade(@selected-color, 10%); | |
| 39 | + } | |
| 40 | + } | |
| 41 | + | |
| 42 | + .@{size-class}-large .@{item-class} { | |
| 43 | + padding: 7px 16px 8px; | |
| 44 | + font-size: @font-size-base !important; | |
| 45 | + } | |
| 46 | +} | |
| 0 | 47 | \ No newline at end of file | ... | ... |
test/routers/cascader.vue
| 1 | 1 | <template> |
| 2 | - <div style="margin: 150px;width:300px"> | |
| 3 | - <Cascader></Cascader> | |
| 2 | + <div style="margin: 50px;width:300px"> | |
| 3 | + <Cascader :data="data" :value="value"></Cascader> | |
| 4 | 4 | </div> |
| 5 | 5 | </template> |
| 6 | 6 | <script> |
| 7 | 7 | import { Cascader } from 'iview'; |
| 8 | 8 | export default { |
| 9 | 9 | props: { |
| 10 | - | |
| 10 | + | |
| 11 | 11 | }, |
| 12 | 12 | data () { |
| 13 | 13 | return { |
| 14 | - | |
| 14 | + value: [], | |
| 15 | + data: [{ | |
| 16 | + value: 'zhejiang', | |
| 17 | + label: 'Zhejiang', | |
| 18 | + children: [{ | |
| 19 | + value: 'hangzhou', | |
| 20 | + label: 'Hangzhou' | |
| 21 | + }], | |
| 22 | + }, { | |
| 23 | + value: 'jiangsu', | |
| 24 | + label: 'Jiangsu', | |
| 25 | + children: [{ | |
| 26 | + value: 'nanjing', | |
| 27 | + label: 'Nanjing', | |
| 28 | + children: [{ | |
| 29 | + value: 'zhonghuamen', | |
| 30 | + label: 'Zhong Hua Men', | |
| 31 | + children: [{ | |
| 32 | + value: 'abc', | |
| 33 | + label: 'ABC' | |
| 34 | + }] | |
| 35 | + }] | |
| 36 | + }, { | |
| 37 | + value: 'hhh', | |
| 38 | + label: 'HHH', | |
| 39 | + children: [{ | |
| 40 | + value: 'ddd', | |
| 41 | + label: 'DDD' | |
| 42 | + }] | |
| 43 | + }] | |
| 44 | + }] | |
| 15 | 45 | } |
| 16 | 46 | }, |
| 17 | 47 | computed: { |
| 18 | - | |
| 48 | + | |
| 19 | 49 | }, |
| 20 | 50 | methods: { |
| 21 | - | |
| 51 | + | |
| 22 | 52 | } |
| 23 | 53 | } |
| 24 | 54 | </script> |
| 25 | 55 | \ No newline at end of file | ... | ... |