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 | <template> | 1 | <template> |
| 2 | - <div :class="[prefixCls]"> | 2 | + <div :class="classes" v-clickoutside="handleClose"> |
| 3 | <i-input | 3 | <i-input |
| 4 | readonly | 4 | readonly |
| 5 | :disabled="disabled" | 5 | :disabled="disabled" |
| 6 | :value.sync="displayRender" | 6 | :value.sync="displayRender" |
| 7 | :size="size" | 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 | </div> | 22 | </div> |
| 10 | </template> | 23 | </template> |
| 11 | <script> | 24 | <script> |
| 12 | import iInput from '../input/input.vue'; | 25 | import iInput from '../input/input.vue'; |
| 13 | import Dropdown from '../select/dropdown.vue'; | 26 | import Dropdown from '../select/dropdown.vue'; |
| 27 | + import Icon from '../icon/icon.vue'; | ||
| 28 | + import Caspanel from './caspanel.vue'; | ||
| 14 | import clickoutside from '../../directives/clickoutside'; | 29 | import clickoutside from '../../directives/clickoutside'; |
| 15 | - import { oneOf, MutationObserver } from '../../utils/assist'; | 30 | + import { oneOf } from '../../utils/assist'; |
| 16 | 31 | ||
| 17 | const prefixCls = 'ivu-cascader'; | 32 | const prefixCls = 'ivu-cascader'; |
| 18 | 33 | ||
| 19 | export default { | 34 | export default { |
| 35 | + components: { iInput, Dropdown, Icon, Caspanel }, | ||
| 36 | + directives: { clickoutside }, | ||
| 20 | props: { | 37 | props: { |
| 21 | data: { | 38 | data: { |
| 22 | type: Array, | 39 | type: Array, |
| @@ -33,7 +50,7 @@ | @@ -33,7 +50,7 @@ | ||
| 33 | }, | 50 | }, |
| 34 | clearable: { | 51 | clearable: { |
| 35 | type: Boolean, | 52 | type: Boolean, |
| 36 | - default: false | 53 | + default: true |
| 37 | }, | 54 | }, |
| 38 | placeholder: { | 55 | placeholder: { |
| 39 | type: String, | 56 | type: String, |
| @@ -56,18 +73,31 @@ | @@ -56,18 +73,31 @@ | ||
| 56 | }, | 73 | }, |
| 57 | renderFormat: { | 74 | renderFormat: { |
| 58 | type: Function, | 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 | data () { | 81 | data () { |
| 65 | return { | 82 | return { |
| 66 | prefixCls: prefixCls, | 83 | prefixCls: prefixCls, |
| 67 | - selected: [] | 84 | + visible: false, |
| 85 | + selected: [], | ||
| 86 | + tmpSelected: [] | ||
| 68 | } | 87 | } |
| 69 | }, | 88 | }, |
| 70 | computed: { | 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 | displayRender () { | 101 | displayRender () { |
| 72 | let label = []; | 102 | let label = []; |
| 73 | for (let i = 0; i < this.selected.length; i++) { | 103 | for (let i = 0; i < this.selected.length; i++) { |
| @@ -78,7 +108,22 @@ | @@ -78,7 +108,22 @@ | ||
| 78 | } | 108 | } |
| 79 | }, | 109 | }, |
| 80 | methods: { | 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 | </script> | 129 | </script> |
| 85 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 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 | \ No newline at end of file | 86 | \ No newline at end of file |
src/styles/components/index.less
| @@ -25,4 +25,5 @@ | @@ -25,4 +25,5 @@ | ||
| 25 | @import "tooltip"; | 25 | @import "tooltip"; |
| 26 | @import "poptip"; | 26 | @import "poptip"; |
| 27 | @import "input"; | 27 | @import "input"; |
| 28 | -@import "slider"; | ||
| 29 | \ No newline at end of file | 28 | \ No newline at end of file |
| 29 | +@import "slider"; | ||
| 30 | +@import "cascader"; | ||
| 30 | \ No newline at end of file | 31 | \ No newline at end of file |
src/styles/components/select.less
| @@ -183,50 +183,7 @@ | @@ -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 | .@{select-prefix-cls}-multiple .@{select-item-prefix-cls} { | 188 | .@{select-prefix-cls}-multiple .@{select-item-prefix-cls} { |
| 232 | &-selected{ | 189 | &-selected{ |
src/styles/mixins/index.less
| @@ -14,4 +14,5 @@ | @@ -14,4 +14,5 @@ | ||
| 14 | @import "breadcrumb"; | 14 | @import "breadcrumb"; |
| 15 | @import "mask"; | 15 | @import "mask"; |
| 16 | @import "content"; // card、modal | 16 | @import "content"; // card、modal |
| 17 | -@import "tooltip"; | ||
| 18 | \ No newline at end of file | 17 | \ No newline at end of file |
| 18 | +@import "tooltip"; | ||
| 19 | +@import "select"; | ||
| 19 | \ No newline at end of file | 20 | \ No newline at end of file |
| 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 | \ No newline at end of file | 47 | \ No newline at end of file |
test/routers/cascader.vue
| 1 | <template> | 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 | </div> | 4 | </div> |
| 5 | </template> | 5 | </template> |
| 6 | <script> | 6 | <script> |
| 7 | import { Cascader } from 'iview'; | 7 | import { Cascader } from 'iview'; |
| 8 | export default { | 8 | export default { |
| 9 | props: { | 9 | props: { |
| 10 | - | 10 | + |
| 11 | }, | 11 | }, |
| 12 | data () { | 12 | data () { |
| 13 | return { | 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 | computed: { | 47 | computed: { |
| 18 | - | 48 | + |
| 19 | }, | 49 | }, |
| 20 | methods: { | 50 | methods: { |
| 21 | - | 51 | + |
| 22 | } | 52 | } |
| 23 | } | 53 | } |
| 24 | </script> | 54 | </script> |
| 25 | \ No newline at end of file | 55 | \ No newline at end of file |