support Select

support Select
This commit is contained in:
梁灏 2017-03-06 18:24:57 +08:00
parent 47a7f21dc6
commit 4aec6a66bb
9 changed files with 343 additions and 143 deletions

View file

@ -41,3 +41,5 @@ MenuItem 和 Submenu 的 key 改为了 name
Menu 的 activeKey 改为 activeName,openKeys 改为 openNames Menu 的 activeKey 改为 activeName,openKeys 改为 openNames
### Cascader ### Cascader
Caspanel 的 sublist 从 prop -> data Caspanel 的 sublist 从 prop -> data
### Select
model 改为 value支持 v-model

View file

@ -25,7 +25,7 @@
- [x] Checkbox - [x] Checkbox
- [x] Switch - [x] Switch
- [ ] Table - [ ] Table
- [ ] Select - [x] Select
- [x] Slider - [x] Slider
- [ ] DatePicker - [ ] DatePicker
- [ ] TimePicker - [ ] TimePicker

View file

@ -43,6 +43,7 @@ li + li { border-left: solid 1px #bbb; padding-left: 10px; margin-left: 10px; }
<li><router-link to="/menu">Menu</router-link></li> <li><router-link to="/menu">Menu</router-link></li>
<li><router-link to="/spin">Spin</router-link></li> <li><router-link to="/spin">Spin</router-link></li>
<li><router-link to="/cascader">Cascader</router-link></li> <li><router-link to="/cascader">Cascader</router-link></li>
<li><router-link to="/select">Select</router-link></li>
</ul> </ul>
</nav> </nav>
<router-view></router-view> <router-view></router-view>

View file

@ -136,6 +136,10 @@ const router = new VueRouter({
{ {
path: '/cascader', path: '/cascader',
component: require('./routers/cascader.vue') component: require('./routers/cascader.vue')
},
{
path: '/select',
component: require('./routers/select.vue')
} }
] ]
}); });

View file

@ -1,70 +1,246 @@
<!--<template>-->
<!--<div>-->
<!--<i-select v-model="model1" style="width:200px" clearable>-->
<!--<i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option>-->
<!--</i-select>-->
<!--{{ model1 }}-->
<!--<div @click="c">change</div>-->
<!--</div>-->
<!--</template>-->
<!--<script>-->
<!--export default {-->
<!--data () {-->
<!--return {-->
<!--cityList: [-->
<!--{-->
<!--value: 'beijing',-->
<!--label: '北京市'-->
<!--},-->
<!--{-->
<!--value: 'shanghai',-->
<!--label: '上海市'-->
<!--},-->
<!--{-->
<!--value: 'shenzhen',-->
<!--label: '深圳市'-->
<!--},-->
<!--{-->
<!--value: 'hangzhou',-->
<!--label: '杭州市'-->
<!--},-->
<!--{-->
<!--value: 'nanjing',-->
<!--label: '南京市'-->
<!--},-->
<!--{-->
<!--value: 'chongqing',-->
<!--label: '重庆市'-->
<!--}-->
<!--],-->
<!--model1: ''-->
<!--}-->
<!--},-->
<!--methods: {-->
<!--c () {-->
<!--this.model1 = 'hangzhou'-->
<!--}-->
<!--}-->
<!--}-->
<!--</script>-->
<!--<template>-->
<!--<div>-->
<!--<i-select v-model="model5" disabled style="width:200px">-->
<!--<i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option>-->
<!--</i-select>-->
<!--<i-select v-model="model6" style="width:200px">-->
<!--<i-option value="beijing">北京市</i-option>-->
<!--<i-option value="shanghai" disabled>上海市</i-option>-->
<!--<i-option value="shenzhen">深圳市</i-option>-->
<!--</i-select>-->
<!--</div>-->
<!--</template>-->
<!--<script>-->
<!--export default {-->
<!--data () {-->
<!--return {-->
<!--cityList: [-->
<!--{-->
<!--value: 'beijing',-->
<!--label: '北京市'-->
<!--},-->
<!--{-->
<!--value: 'shanghai',-->
<!--label: '上海市'-->
<!--},-->
<!--{-->
<!--value: 'shenzhen',-->
<!--label: '深圳市'-->
<!--},-->
<!--{-->
<!--value: 'hangzhou',-->
<!--label: '杭州市'-->
<!--},-->
<!--{-->
<!--value: 'nanjing',-->
<!--label: '南京市'-->
<!--},-->
<!--{-->
<!--value: 'chongqing',-->
<!--label: '重庆市'-->
<!--}-->
<!--],-->
<!--model5: '',-->
<!--model6: ''-->
<!--}-->
<!--}-->
<!--}-->
<!--</script>-->
<!--<template>-->
<!--<div>-->
<!--<i-select v-model="model7" style="width:200px">-->
<!--<Option-group label="热门城市">-->
<!--<i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option>-->
<!--</Option-group>-->
<!--<Option-group label="其它城市">-->
<!--<i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option>-->
<!--</Option-group>-->
<!--</i-select>-->
<!--</div>-->
<!--</template>-->
<!--<script>-->
<!--export default {-->
<!--data () {-->
<!--return {-->
<!--cityList: [-->
<!--{-->
<!--value: 'beijing',-->
<!--label: '北京市'-->
<!--},-->
<!--{-->
<!--value: 'shanghai',-->
<!--label: '上海市'-->
<!--},-->
<!--{-->
<!--value: 'shenzhen',-->
<!--label: '深圳市'-->
<!--},-->
<!--{-->
<!--value: 'hangzhou',-->
<!--label: '杭州市'-->
<!--},-->
<!--{-->
<!--value: 'nanjing',-->
<!--label: '南京市'-->
<!--},-->
<!--{-->
<!--value: 'chongqing',-->
<!--label: '重庆市'-->
<!--}-->
<!--],-->
<!--model7: ''-->
<!--}-->
<!--}-->
<!--}-->
<!--</script>-->
<!--<template>-->
<!--<div>-->
<!--<i-select v-model="model10" multiple style="width:260px">-->
<!--<i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option>-->
<!--</i-select>-->
<!--</div>-->
<!--</template>-->
<!--<script>-->
<!--export default {-->
<!--data () {-->
<!--return {-->
<!--cityList: [-->
<!--{-->
<!--value: 'beijing',-->
<!--label: '北京市'-->
<!--},-->
<!--{-->
<!--value: 'shanghai',-->
<!--label: '上海市'-->
<!--},-->
<!--{-->
<!--value: 'shenzhen',-->
<!--label: '深圳市'-->
<!--},-->
<!--{-->
<!--value: 'hangzhou',-->
<!--label: '杭州市'-->
<!--},-->
<!--{-->
<!--value: 'nanjing',-->
<!--label: '南京市'-->
<!--},-->
<!--{-->
<!--value: 'chongqing',-->
<!--label: '重庆市'-->
<!--}-->
<!--],-->
<!--model10: []-->
<!--}-->
<!--}-->
<!--}-->
<!--</script>-->
<template> <template>
<Row> <div>
<i-col span="12" style="padding-right:10px"> <Row>
<i-select :model.sync="model111" filterable> <i-col span="12" style="padding-right:10px">
<i-option v-for="item in cityList1" :value="item.value">{{ item.label }}</i-option> <i-select v-model="model11" filterable>
</i-select> <i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option>
</i-col> </i-select>
</Row> </i-col>
<Row> <i-col span="12">
<i-col span="12" style="padding-right:10px"> <i-select v-model="model12" filterable multiple>
<i-select :model.sync="model112" filterable> <i-option v-for="item in cityList" :value="item.value">{{ item.label }}</i-option>
<i-option v-for="item in cityList2" :value="item.value">{{ item.label }}</i-option> </i-select>
</i-select> </i-col>
</i-col> </Row>
</Row> <div @click="model11 = 'shanghai'">change</div>
<Row> </div>
<i-col span="12">
<i-select :model.sync="model12" filterable multiple>
<i-option v-for="item in cityList1" :value="item.value">{{ item.label }}</i-option>
</i-select>
</i-col>
</Row>
</template> </template>
<script> <script>
const cityList = [
{
value: 'beijing',
label: '北京市'
},
{
value: 'shanghai',
label: '上海市'
},
{
value: 'shenzhen',
label: '深圳市'
},
{
value: 'hangzhou',
label: '杭州市'
},
{
value: 'nanjing',
label: '南京市'
},
{
value: 'chongqing',
label: '重庆市'
}
]
export default { export default {
data () { data () {
return { return {
cityList1: cityList, cityList: [
model111: '', {
value: 'beijing',
cityList2: [], label: '北京市'
model112: 'beijing', },
{
value: 'shanghai',
label: '上海市'
},
{
value: 'shenzhen',
label: '深圳市'
},
{
value: 'hangzhou',
label: '杭州市'
},
{
value: 'nanjing',
label: '南京市'
},
{
value: 'chongqing',
label: '重庆市'
}
],
model11: '',
model12: [] model12: []
} }
},
ready() {
this.model111 = 'hangzhou'
setTimeout(()=>{
this.cityList2 = cityList
}, 500)
} }
} }
</script> </script>

View file

@ -2,7 +2,7 @@
<li :class="[prefixCls + '-wrap']" v-show="!hidden"> <li :class="[prefixCls + '-wrap']" v-show="!hidden">
<div :class="[prefixCls + '-title']">{{ label }}</div> <div :class="[prefixCls + '-title']">{{ label }}</div>
<ul> <ul>
<li :class="[prefixCls]" v-el:options><slot></slot></li> <li :class="[prefixCls]" ref="options"><slot></slot></li>
</ul> </ul>
</li> </li>
</template> </template>
@ -10,6 +10,7 @@
const prefixCls = 'ivu-select-group'; const prefixCls = 'ivu-select-group';
export default { export default {
name: 'OptionGroup',
props: { props: {
label: { label: {
type: String, type: String,
@ -25,7 +26,7 @@
methods: { methods: {
queryChange () { queryChange () {
this.$nextTick(() => { this.$nextTick(() => {
const options = this.$els.options.querySelectorAll('.ivu-select-item'); const options = this.$refs.options.querySelectorAll('.ivu-select-item');
let hasVisibleOption = false; let hasVisibleOption = false;
for (let i = 0; i < options.length; i++) { for (let i = 0; i < options.length; i++) {
if (options[i].style.display !== 'none') { if (options[i].style.display !== 'none') {
@ -37,11 +38,11 @@
}); });
} }
}, },
events: { mounted () {
'on-query-change' () { this.$on('on-query-change', () => {
this.queryChange(); this.queryChange();
return true; return true;
} });
} }
}; };
</script> </script>

View file

@ -2,9 +2,14 @@
<li :class="classes" @click.stop="select" @mouseout.stop="blur" v-show="!hidden"><slot>{{ showLabel }}</slot></li> <li :class="classes" @click.stop="select" @mouseout.stop="blur" v-show="!hidden"><slot>{{ showLabel }}</slot></li>
</template> </template>
<script> <script>
import Emitter from '../../mixins/emitter';
const prefixCls = 'ivu-select-item'; const prefixCls = 'ivu-select-item';
export default { export default {
name: 'iOption',
componentName: 'select-item',
mixins: [ Emitter ],
props: { props: {
value: { value: {
type: [String, Number], type: [String, Number],
@ -18,7 +23,6 @@
default: false default: false
} }
}, },
componentName: 'select-item',
data () { data () {
return { return {
selected: false, selected: false,
@ -49,7 +53,7 @@
return false; return false;
} }
this.$dispatch('on-select-selected', this.value); this.dispatch('iSelect', 'on-select-selected', this.value);
}, },
blur () { blur () {
this.isFocus = false; this.isFocus = false;
@ -59,16 +63,14 @@
this.hidden = !new RegExp(parsedQuery, 'i').test(this.searchLabel); this.hidden = !new RegExp(parsedQuery, 'i').test(this.searchLabel);
} }
}, },
compiled () { mounted () {
this.searchLabel = this.$el.innerHTML; this.searchLabel = this.$el.innerHTML;
}, this.$on('on-select-close', () => {
events: {
'on-select-close' () {
this.isFocus = false; this.isFocus = false;
}, });
'on-query-change' (val) { this.$on('on-query-change', (val) => {
this.queryChange(val); this.queryChange(val);
} });
} }
}; };
</script> </script>

View file

@ -2,11 +2,11 @@
<div :class="classes" v-clickoutside="handleClose"> <div :class="classes" v-clickoutside="handleClose">
<div <div
:class="[prefixCls + '-selection']" :class="[prefixCls + '-selection']"
v-el:reference ref="reference"
@click="toggleMenu"> @click="toggleMenu">
<div class="ivu-tag" v-for="item in selectedMultiple"> <div class="ivu-tag" v-for="(item, index) in selectedMultiple">
<span class="ivu-tag-text">{{ item.label }}</span> <span class="ivu-tag-text">{{ item.label }}</span>
<Icon type="ios-close-empty" @click.stop="removeTag($index)"></Icon> <Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
</div> </div>
<span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ placeholder }}</span> <span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ placeholder }}</span>
<span :class="[prefixCls + '-selected-value']" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span> <span :class="[prefixCls + '-selected-value']" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
@ -20,31 +20,35 @@
@blur="handleBlur" @blur="handleBlur"
@keydown="resetInputState" @keydown="resetInputState"
@keydown.delete="handleInputDelete" @keydown.delete="handleInputDelete"
v-el:input> ref="input">
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.stop="clearSingleSelect"></Icon> <Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon>
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']"></Icon> <Icon type="arrow-down-b" :class="[prefixCls + '-arrow']"></Icon>
</div> </div>
<Dropdown v-show="visible" transition="slide-up" v-ref:dropdown> <transition name="slide-up">
<ul v-show="notFound" :class="[prefixCls + '-not-found']"><li>{{ notFoundText }}</li></ul> <Drop v-show="visible" ref="dropdown">
<ul v-else :class="[prefixCls + '-dropdown-list']" v-el:options><slot></slot></ul> <ul v-show="notFound" :class="[prefixCls + '-not-found']"><li>{{ notFoundText }}</li></ul>
</Dropdown> <ul v-show="!notFound" :class="[prefixCls + '-dropdown-list']" ref="options"><slot></slot></ul>
</Drop>
</transition>
</div> </div>
</template> </template>
<script> <script>
import Icon from '../icon'; import Icon from '../icon';
import Dropdown from './dropdown.vue'; import Drop from './dropdown.vue';
import clickoutside from '../../directives/clickoutside'; import clickoutside from '../../directives/clickoutside';
import { oneOf, MutationObserver } from '../../utils/assist'; import { oneOf, MutationObserver } from '../../utils/assist';
import { t } from '../../locale'; import { t } from '../../locale';
import Emitter from '../../mixins/emitter';
const prefixCls = 'ivu-select'; const prefixCls = 'ivu-select';
export default { export default {
name: 'iSelect', name: 'iSelect',
components: { Icon, Dropdown }, mixins: [ Emitter ],
components: { Icon, Drop },
directives: { clickoutside }, directives: { clickoutside },
props: { props: {
model: { value: {
type: [String, Number, Array], type: [String, Number, Array],
default: '' default: ''
}, },
@ -101,7 +105,8 @@
query: '', query: '',
inputLength: 20, inputLength: 20,
notFound: false, notFound: false,
slotChangeDuration: false // if slot change duration and in multiple, set true and after slot change, set false slotChangeDuration: false, // if slot change duration and in multiple, set true and after slot change, set false
model: this.value
}; };
}, },
computed: { computed: {
@ -161,7 +166,7 @@
hideMenu () { hideMenu () {
this.visible = false; this.visible = false;
this.focusIndex = 0; this.focusIndex = 0;
this.$broadcast('on-select-close'); this.broadcast('iOption', 'on-select-close');
}, },
// find option component // find option component
findChild (cb) { findChild (cb) {
@ -289,10 +294,10 @@
this.model.splice(index, 1); this.model.splice(index, 1);
if (this.filterable && this.visible) { if (this.filterable && this.visible) {
this.$els.input.focus(); this.$refs.input.focus();
} }
this.$broadcast('on-update-popper'); this.broadcast('Drop', 'on-update-popper');
}, },
// to select option for single // to select option for single
toggleSingleSelected (value, init = false) { toggleSingleSelected (value, init = false) {
@ -316,13 +321,15 @@
value: value, value: value,
label: label label: label
}); });
this.$dispatch('on-form-change', { // todo
value: value, // this.$dispatch('on-form-change', {
label: label // value: value,
}); // label: label
// });
} else { } else {
this.$emit('on-change', value); this.$emit('on-change', value);
this.$dispatch('on-form-change', value); // todo
// this.$dispatch('on-form-change', value);
} }
} }
} }
@ -351,10 +358,12 @@
if (!init) { if (!init) {
if (this.labelInValue) { if (this.labelInValue) {
this.$emit('on-change', hybridValue); this.$emit('on-change', hybridValue);
this.$dispatch('on-form-change', hybridValue); // todo
} else { // this.$dispatch('on-form-change', hybridValue);
// } else {
this.$emit('on-change', value); this.$emit('on-change', value);
this.$dispatch('on-form-change', value); // todo
// this.$dispatch('on-form-change', value);
} }
} }
} }
@ -463,7 +472,7 @@
}, 300); }, 300);
}, },
resetInputState () { resetInputState () {
this.inputLength = this.$els.input.value.length * 12 + 20; this.inputLength = this.$refs.input.value.length * 12 + 20;
}, },
handleInputDelete () { handleInputDelete () {
if (this.multiple && this.model.length && this.query === '') { if (this.multiple && this.model.length && this.query === '') {
@ -495,7 +504,7 @@
} }
} }
}, },
compiled () { mounted () {
this.modelToQuery(); this.modelToQuery();
this.updateOptions(true); this.updateOptions(true);
@ -509,13 +518,44 @@
this.updateOptions(true, true); this.updateOptions(true, true);
}); });
this.observer.observe(this.$els.options, { this.observer.observe(this.$refs.options, {
// attributes: true, // attributes: true,
childList: true, childList: true,
characterData: true, characterData: true,
subtree: true subtree: true
}); });
} }
this.$on('on-select-selected', (value) => {
if (this.model === value) {
this.hideMenu();
} else {
if (this.multiple) {
const index = this.model.indexOf(value);
if (index >= 0) {
this.removeTag(index);
} else {
this.model.push(value);
this.broadcast('Drop', 'on-update-popper');
}
if (this.filterable) {
this.query = '';
this.$refs.input.focus();
}
} else {
this.model = value;
if (this.filterable) {
this.findChild((child) => {
if (child.value === value) {
this.query = child.label === undefined ? child.searchLabel : child.label;
}
});
}
}
}
});
}, },
beforeDestroy () { beforeDestroy () {
document.removeEventListener('keydown', this.handleKeydown); document.removeEventListener('keydown', this.handleKeydown);
@ -524,7 +564,11 @@
} }
}, },
watch: { watch: {
value (val) {
this.model = val;
},
model () { model () {
this.$emit('input', this.model);
this.modelToQuery(); this.modelToQuery();
if (this.multiple) { if (this.multiple) {
if (this.slotChangeDuration) { if (this.slotChangeDuration) {
@ -539,18 +583,20 @@
visible (val) { visible (val) {
if (val) { if (val) {
if (this.multiple && this.filterable) { if (this.multiple && this.filterable) {
this.$els.input.focus(); this.$refs.input.focus();
} }
this.$broadcast('on-update-popper'); this.broadcast('Drop', 'on-update-popper');
} else { } else {
if (this.filterable) { if (this.filterable) {
this.$els.input.blur(); this.$refs.input.blur();
} }
this.$broadcast('on-destroy-popper'); this.broadcast('Drop', 'on-destroy-popper');
} }
}, },
query (val) { query (val) {
this.$broadcast('on-query-change', val); // todo
this.broadcast('OptionGroup', 'on-query-change', val);
this.broadcast('iOption', 'on-query-change', val);
let is_hidden = true; let is_hidden = true;
this.$nextTick(() => { this.$nextTick(() => {
@ -561,39 +607,7 @@
}); });
this.notFound = is_hidden; this.notFound = is_hidden;
}); });
this.$broadcast('on-update-popper'); this.broadcast('Drop', 'on-update-popper');
}
},
events: {
'on-select-selected' (value) {
if (this.model === value) {
this.hideMenu();
} else {
if (this.multiple) {
const index = this.model.indexOf(value);
if (index >= 0) {
this.removeTag(index);
} else {
this.model.push(value);
this.$broadcast('on-update-popper');
}
if (this.filterable) {
this.query = '';
this.$els.input.focus();
}
} else {
this.model = value;
if (this.filterable) {
this.findChild((child) => {
if (child.value === value) {
this.query = child.label === undefined ? child.searchLabel : child.label;
}
});
}
}
}
} }
} }
}; };

View file

@ -43,7 +43,7 @@ import Tooltip from './components/tooltip';
import Tree from './components/tree'; import Tree from './components/tree';
import Upload from './components/upload'; import Upload from './components/upload';
import { Row, Col } from './components/grid'; import { Row, Col } from './components/grid';
// import { Select, Option, OptionGroup } from './components/select'; import { Select, Option, OptionGroup } from './components/select';
import locale from './locale'; import locale from './locale';
const iview = { const iview = {
@ -83,8 +83,8 @@ const iview = {
// Message, // Message,
// Modal, // Modal,
// Notice, // Notice,
// iOption: Option, iOption: Option,
// OptionGroup, OptionGroup,
// Page, // Page,
Panel: Collapse.Panel, Panel: Collapse.Panel,
Poptip, Poptip,
@ -93,7 +93,7 @@ const iview = {
RadioGroup: Radio.Group, RadioGroup: Radio.Group,
Rate, Rate,
Row, Row,
// iSelect: Select, iSelect: Select,
Slider, Slider,
Spin, Spin,
Step: Steps.Step, Step: Steps.Step,