add AutoComplete component
This commit is contained in:
parent
1183836a92
commit
fed3e09d15
11 changed files with 255 additions and 31 deletions
|
@ -56,6 +56,7 @@ li + li { border-left: solid 1px #bbb; padding-left: 10px; margin-left: 10px; }
|
||||||
<li><router-link to="/notice">Notice</router-link></li>
|
<li><router-link to="/notice">Notice</router-link></li>
|
||||||
<li><router-link to="/avatar">Avatar</router-link></li>
|
<li><router-link to="/avatar">Avatar</router-link></li>
|
||||||
<li><router-link to="/color-picker">ColorPicker</router-link></li>
|
<li><router-link to="/color-picker">ColorPicker</router-link></li>
|
||||||
|
<li><router-link to="/auto-complete">AutoComplete</router-link></li>
|
||||||
</ul>
|
</ul>
|
||||||
</nav>
|
</nav>
|
||||||
<router-view></router-view>
|
<router-view></router-view>
|
||||||
|
|
|
@ -188,6 +188,10 @@ const router = new VueRouter({
|
||||||
{
|
{
|
||||||
path: '/color-picker',
|
path: '/color-picker',
|
||||||
component: require('./routers/color-picker.vue')
|
component: require('./routers/color-picker.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/auto-complete',
|
||||||
|
component: require('./routers/auto-complete.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
40
examples/routers/auto-complete.vue
Normal file
40
examples/routers/auto-complete.vue
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
<template>
|
||||||
|
<div style="margin: 100px;width: 200px;">
|
||||||
|
<AutoComplete transfer v-model="value" :data="data" @on-change="hc" :filter-method="fm">
|
||||||
|
<!--<Option v-for="item in data" :value="item" :label="item" :key="item">-->
|
||||||
|
<!--<span style="color: red">{{ item }}</span>-->
|
||||||
|
<!--</Option>-->
|
||||||
|
</AutoComplete>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
props: {
|
||||||
|
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
value: '',
|
||||||
|
// data: [],
|
||||||
|
data: ['Burns Bay Road', 'Downing Street', 'Wall Street']
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {},
|
||||||
|
methods: {
|
||||||
|
handleSearch (value) {
|
||||||
|
this.data = !value ? [] : [
|
||||||
|
value + '@qq.com',
|
||||||
|
value + '@sina.com',
|
||||||
|
value + '@163.com'
|
||||||
|
]
|
||||||
|
},
|
||||||
|
hc (v) {
|
||||||
|
console.log(v)
|
||||||
|
},
|
||||||
|
fm (value, item) {
|
||||||
|
return item.toUpperCase().indexOf(value.toUpperCase()) !== -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
130
src/components/auto-complete/auto-complete.vue
Normal file
130
src/components/auto-complete/auto-complete.vue
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<template>
|
||||||
|
<i-select
|
||||||
|
ref="select"
|
||||||
|
class="ivu-auto-complete"
|
||||||
|
:label="label"
|
||||||
|
:disabled="disabled"
|
||||||
|
:clearable="clearable"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:size="size"
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
auto-complete
|
||||||
|
:remote-method="remoteMethod"
|
||||||
|
@on-change="handleChange"
|
||||||
|
:transfer="transfer">
|
||||||
|
<slot name="input">
|
||||||
|
<i-input
|
||||||
|
ref="input"
|
||||||
|
slot="input"
|
||||||
|
v-model="currentValue"
|
||||||
|
:placeholder="placeholder"
|
||||||
|
:disabled="disabled"
|
||||||
|
:size="size"
|
||||||
|
:icon="closeIcon"
|
||||||
|
@on-click="handleClear"
|
||||||
|
@on-focus="handleFocus"
|
||||||
|
@on-blur="handleBlur"></i-input>
|
||||||
|
</slot>
|
||||||
|
<slot>
|
||||||
|
<i-option v-for="item in filteredData" :value="item" :key="item">{{ item }}</i-option>
|
||||||
|
</slot>
|
||||||
|
</i-select>
|
||||||
|
</template>
|
||||||
|
<script>
|
||||||
|
import iSelect from '../select/select.vue';
|
||||||
|
import iOption from '../select/option.vue';
|
||||||
|
import iInput from '../input/input.vue';
|
||||||
|
import { oneOf } from '../../utils/assist';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'AutoComplete',
|
||||||
|
components: { iSelect, iOption, iInput },
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
clearable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
placeholder: {
|
||||||
|
type: String
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
validator (value) {
|
||||||
|
return oneOf(value, ['small', 'large', 'default']);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
filterMethod: {
|
||||||
|
type: [Function, Boolean],
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
transfer: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data () {
|
||||||
|
return {
|
||||||
|
currentValue: this.value
|
||||||
|
};
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
closeIcon () {
|
||||||
|
return this.clearable && this.currentValue ? 'ios-close' : '';
|
||||||
|
},
|
||||||
|
filteredData () {
|
||||||
|
if (this.filterMethod) {
|
||||||
|
return this.data.filter(item => this.filterMethod(this.currentValue, item));
|
||||||
|
} else {
|
||||||
|
return this.data;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value (val) {
|
||||||
|
this.currentValue = val;
|
||||||
|
},
|
||||||
|
currentValue (val) {
|
||||||
|
this.$refs.select.query = val;
|
||||||
|
this.$emit('input', val);
|
||||||
|
this.$emit('on-change', val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
remoteMethod (query) {
|
||||||
|
this.$emit('on-search', query);
|
||||||
|
},
|
||||||
|
handleChange (val) {
|
||||||
|
this.currentValue = val;
|
||||||
|
this.$refs.select.model = val;
|
||||||
|
this.$refs.input.blur();
|
||||||
|
this.$emit('on-select', val);
|
||||||
|
},
|
||||||
|
handleFocus () {
|
||||||
|
this.$refs.select.visible = true;
|
||||||
|
},
|
||||||
|
handleBlur () {
|
||||||
|
this.$refs.select.visible = false;
|
||||||
|
},
|
||||||
|
handleClear () {
|
||||||
|
this.currentValue = '';
|
||||||
|
this.$refs.select.model = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
2
src/components/auto-complete/index.js
Normal file
2
src/components/auto-complete/index.js
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
import AutoComplete from './auto-complete.vue';
|
||||||
|
export default AutoComplete;
|
|
@ -212,12 +212,19 @@
|
||||||
|
|
||||||
this.textareaStyles = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
|
this.textareaStyles = calcTextareaHeight(this.$refs.textarea, minRows, maxRows);
|
||||||
},
|
},
|
||||||
focus() {
|
focus () {
|
||||||
if (this.type === 'textarea') {
|
if (this.type === 'textarea') {
|
||||||
this.$refs.textarea.focus();
|
this.$refs.textarea.focus();
|
||||||
} else {
|
} else {
|
||||||
this.$refs.input.focus();
|
this.$refs.input.focus();
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
blur () {
|
||||||
|
if (this.type === 'textarea') {
|
||||||
|
this.$refs.textarea.blur();
|
||||||
|
} else {
|
||||||
|
this.$refs.input.blur();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
import Emitter from '../../mixins/emitter';
|
import Emitter from '../../mixins/emitter';
|
||||||
|
import { findComponentUpward } from '../../utils/assist';
|
||||||
|
|
||||||
const prefixCls = 'ivu-select-item';
|
const prefixCls = 'ivu-select-item';
|
||||||
|
|
||||||
|
@ -29,7 +30,8 @@
|
||||||
index: 0, // for up and down to focus
|
index: 0, // for up and down to focus
|
||||||
isFocus: false,
|
isFocus: false,
|
||||||
hidden: false, // for search
|
hidden: false, // for search
|
||||||
searchLabel: '' // the value is slot,only for search
|
searchLabel: '', // the value is slot,only for search
|
||||||
|
autoComplete: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -38,7 +40,7 @@
|
||||||
`${prefixCls}`,
|
`${prefixCls}`,
|
||||||
{
|
{
|
||||||
[`${prefixCls}-disabled`]: this.disabled,
|
[`${prefixCls}-disabled`]: this.disabled,
|
||||||
[`${prefixCls}-selected`]: this.selected,
|
[`${prefixCls}-selected`]: this.selected && !this.autoComplete,
|
||||||
[`${prefixCls}-focus`]: this.isFocus
|
[`${prefixCls}-focus`]: this.isFocus
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
@ -72,6 +74,9 @@
|
||||||
this.$on('on-query-change', (val) => {
|
this.$on('on-query-change', (val) => {
|
||||||
this.queryChange(val);
|
this.queryChange(val);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const Select = findComponentUpward(this, 'iSelect');
|
||||||
|
if (Select) this.autoComplete = Select.autoComplete;
|
||||||
},
|
},
|
||||||
beforeDestroy () {
|
beforeDestroy () {
|
||||||
this.dispatch('iSelect', 'remove');
|
this.dispatch('iSelect', 'remove');
|
||||||
|
|
|
@ -1,29 +1,31 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="classes" v-clickoutside="handleClose">
|
<div :class="classes" v-clickoutside="handleClose">
|
||||||
<div
|
<div
|
||||||
:class="[prefixCls + '-selection']"
|
:class="selectionCls"
|
||||||
ref="reference"
|
ref="reference"
|
||||||
@click="toggleMenu">
|
@click="toggleMenu">
|
||||||
<div class="ivu-tag" v-for="(item, index) in selectedMultiple">
|
<slot name="input">
|
||||||
<span class="ivu-tag-text">{{ item.label }}</span>
|
<div class="ivu-tag" v-for="(item, index) in selectedMultiple">
|
||||||
<Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
|
<span class="ivu-tag-text">{{ item.label }}</span>
|
||||||
</div>
|
<Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
|
||||||
<span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ localePlaceholder }}</span>
|
</div>
|
||||||
<span :class="[prefixCls + '-selected-value']" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
|
<span :class="[prefixCls + '-placeholder']" v-show="showPlaceholder && !filterable">{{ localePlaceholder }}</span>
|
||||||
<input
|
<span :class="[prefixCls + '-selected-value']" v-show="!showPlaceholder && !multiple && !filterable">{{ selectedSingle }}</span>
|
||||||
type="text"
|
<input
|
||||||
v-if="filterable"
|
type="text"
|
||||||
v-model="query"
|
v-if="filterable"
|
||||||
:disabled="disabled"
|
v-model="query"
|
||||||
:class="[prefixCls + '-input']"
|
:disabled="disabled"
|
||||||
:placeholder="showPlaceholder ? localePlaceholder : ''"
|
:class="[prefixCls + '-input']"
|
||||||
:style="inputStyle"
|
:placeholder="showPlaceholder ? localePlaceholder : ''"
|
||||||
@blur="handleBlur"
|
:style="inputStyle"
|
||||||
@keydown="resetInputState"
|
@blur="handleBlur"
|
||||||
@keydown.delete="handleInputDelete"
|
@keydown="resetInputState"
|
||||||
ref="input">
|
@keydown.delete="handleInputDelete"
|
||||||
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon>
|
ref="input">
|
||||||
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!remote"></Icon>
|
<Icon type="ios-close" :class="[prefixCls + '-arrow']" v-show="showCloseIcon" @click.native.stop="clearSingleSelect"></Icon>
|
||||||
|
<Icon type="arrow-down-b" :class="[prefixCls + '-arrow']" v-if="!remote"></Icon>
|
||||||
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
<transition :name="transitionName">
|
<transition :name="transitionName">
|
||||||
<Drop
|
<Drop
|
||||||
|
@ -33,7 +35,7 @@
|
||||||
ref="dropdown"
|
ref="dropdown"
|
||||||
:data-transfer="transfer"
|
:data-transfer="transfer"
|
||||||
v-transfer-dom>
|
v-transfer-dom>
|
||||||
<ul v-show="notFountShow" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>
|
<ul v-show="notFoundShow" :class="[prefixCls + '-not-found']"><li>{{ localeNotFoundText }}</li></ul>
|
||||||
<ul v-show="(!notFound && !remote) || (remote && !loading && !notFound)" :class="[prefixCls + '-dropdown-list']"><slot></slot></ul>
|
<ul v-show="(!notFound && !remote) || (remote && !loading && !notFound)" :class="[prefixCls + '-dropdown-list']"><slot></slot></ul>
|
||||||
<ul v-show="loading" :class="[prefixCls + '-loading']">{{ localeLoadingText }}</ul>
|
<ul v-show="loading" :class="[prefixCls + '-loading']">{{ localeLoadingText }}</ul>
|
||||||
</Drop>
|
</Drop>
|
||||||
|
@ -123,6 +125,11 @@
|
||||||
transfer: {
|
transfer: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
// Use for AutoComplete
|
||||||
|
autoComplete: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
data () {
|
data () {
|
||||||
|
@ -161,7 +168,13 @@
|
||||||
dropdownCls () {
|
dropdownCls () {
|
||||||
return {
|
return {
|
||||||
[prefixCls + '-dropdown-transfer']: this.transfer,
|
[prefixCls + '-dropdown-transfer']: this.transfer,
|
||||||
[prefixCls + '-multiple']: this.multiple && this.transfer
|
[prefixCls + '-multiple']: this.multiple && this.transfer,
|
||||||
|
['ivu-auto-complete']: this.autoComplete,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
selectionCls () {
|
||||||
|
return {
|
||||||
|
[`${prefixCls}-selection`]: !this.autoComplete
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
showPlaceholder () {
|
showPlaceholder () {
|
||||||
|
@ -225,16 +238,19 @@
|
||||||
let status = true;
|
let status = true;
|
||||||
const options = this.$slots.default || [];
|
const options = this.$slots.default || [];
|
||||||
if (!this.loading && this.remote && this.query === '' && !options.length) status = false;
|
if (!this.loading && this.remote && this.query === '' && !options.length) status = false;
|
||||||
|
|
||||||
|
if (this.autoComplete && !options.length) status = false;
|
||||||
|
|
||||||
return this.visible && status;
|
return this.visible && status;
|
||||||
},
|
},
|
||||||
notFountShow () {
|
notFoundShow () {
|
||||||
const options = this.$slots.default || [];
|
const options = this.$slots.default || [];
|
||||||
return (this.notFound && !this.remote) || (this.remote && !this.loading && !options.length);
|
return (this.notFound && !this.remote) || (this.remote && !this.loading && !options.length);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
toggleMenu () {
|
toggleMenu () {
|
||||||
if (this.disabled) {
|
if (this.disabled || this.autoComplete) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
this.visible = !this.visible;
|
this.visible = !this.visible;
|
||||||
|
@ -543,6 +559,7 @@
|
||||||
},
|
},
|
||||||
handleBlur () {
|
handleBlur () {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
if (this.autoComplete) return;
|
||||||
const model = this.model;
|
const model = this.model;
|
||||||
|
|
||||||
if (this.multiple) {
|
if (this.multiple) {
|
||||||
|
@ -737,7 +754,7 @@
|
||||||
if (this.multiple) {
|
if (this.multiple) {
|
||||||
this.$refs.input.focus();
|
this.$refs.input.focus();
|
||||||
} else {
|
} else {
|
||||||
this.$refs.input.select();
|
if (!this.autoComplete) this.$refs.input.select();
|
||||||
}
|
}
|
||||||
if (this.remote) {
|
if (this.remote) {
|
||||||
this.findChild(child => {
|
this.findChild(child => {
|
||||||
|
@ -753,7 +770,7 @@
|
||||||
this.broadcast('Drop', 'on-update-popper');
|
this.broadcast('Drop', 'on-update-popper');
|
||||||
} else {
|
} else {
|
||||||
if (this.filterable) {
|
if (this.filterable) {
|
||||||
this.$refs.input.blur();
|
if (!this.autoComplete) this.$refs.input.blur();
|
||||||
// #566 reset options visible
|
// #566 reset options visible
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.broadcastQuery('');
|
this.broadcastQuery('');
|
||||||
|
|
|
@ -3,6 +3,7 @@ import 'core-js/fn/array/find-index';
|
||||||
|
|
||||||
import Affix from './components/affix';
|
import Affix from './components/affix';
|
||||||
import Alert from './components/alert';
|
import Alert from './components/alert';
|
||||||
|
import AutoComplete from './components/auto-complete';
|
||||||
import Avatar from './components/avatar';
|
import Avatar from './components/avatar';
|
||||||
import BackTop from './components/back-top';
|
import BackTop from './components/back-top';
|
||||||
import Badge from './components/badge';
|
import Badge from './components/badge';
|
||||||
|
@ -51,6 +52,7 @@ import locale from './locale';
|
||||||
const iview = {
|
const iview = {
|
||||||
Affix,
|
Affix,
|
||||||
Alert,
|
Alert,
|
||||||
|
AutoComplete,
|
||||||
Avatar,
|
Avatar,
|
||||||
BackTop,
|
BackTop,
|
||||||
Badge,
|
Badge,
|
||||||
|
|
15
src/styles/components/auto-complete.less
Normal file
15
src/styles/components/auto-complete.less
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
@auto-complete-prefix-cls: ~"@{css-prefix}auto-complete";
|
||||||
|
|
||||||
|
.@{auto-complete-prefix-cls} {
|
||||||
|
.@{select-prefix-cls} {
|
||||||
|
&-not-found{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.@{icon-prefix-cls}-ios-close{
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
&:hover .@{icon-prefix-cls}-ios-close{
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
|
@ -40,4 +40,5 @@
|
||||||
@import "upload";
|
@import "upload";
|
||||||
@import "tree";
|
@import "tree";
|
||||||
@import "avatar";
|
@import "avatar";
|
||||||
@import "color-picker";
|
@import "color-picker";
|
||||||
|
@import "auto-complete";
|
Loading…
Add table
Reference in a new issue