Commit c463ab87579ca750bc91fb6873f8f0730295bc5d

Authored by 梁灏
1 parent 6ff31952

add Cascader

add Cascader
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
... ...
src/components/cascader/casitem.vue 0 → 100644
  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
... ...
src/components/cascader/caspanel.vue 0 → 100644
  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
... ...
src/styles/components/cascader.less 0 → 100644
  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
... ... @@ -25,4 +25,5 @@
25 25 @import "tooltip";
26 26 @import "poptip";
27 27 @import "input";
28   -@import "slider";
29 28 \ No newline at end of file
  29 +@import "slider";
  30 +@import "cascader";
30 31 \ No newline at end of file
... ...
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
... ... @@ -14,4 +14,5 @@
14 14 @import "breadcrumb";
15 15 @import "mask";
16 16 @import "content"; // card、modal
17   -@import "tooltip";
18 17 \ No newline at end of file
  18 +@import "tooltip";
  19 +@import "select";
19 20 \ No newline at end of file
... ...
src/styles/mixins/select.less 0 → 100644
  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
... ...