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="/avatar">Avatar</router-link></li>
|
||||
<li><router-link to="/color-picker">ColorPicker</router-link></li>
|
||||
<li><router-link to="/auto-complete">AutoComplete</router-link></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<router-view></router-view>
|
||||
|
|
|
@ -188,6 +188,10 @@ const router = new VueRouter({
|
|||
{
|
||||
path: '/color-picker',
|
||||
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);
|
||||
},
|
||||
focus() {
|
||||
focus () {
|
||||
if (this.type === 'textarea') {
|
||||
this.$refs.textarea.focus();
|
||||
} else {
|
||||
this.$refs.input.focus();
|
||||
}
|
||||
},
|
||||
blur () {
|
||||
if (this.type === 'textarea') {
|
||||
this.$refs.textarea.blur();
|
||||
} else {
|
||||
this.$refs.input.blur();
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
</template>
|
||||
<script>
|
||||
import Emitter from '../../mixins/emitter';
|
||||
import { findComponentUpward } from '../../utils/assist';
|
||||
|
||||
const prefixCls = 'ivu-select-item';
|
||||
|
||||
|
@ -29,7 +30,8 @@
|
|||
index: 0, // for up and down to focus
|
||||
isFocus: false,
|
||||
hidden: false, // for search
|
||||
searchLabel: '' // the value is slot,only for search
|
||||
searchLabel: '', // the value is slot,only for search
|
||||
autoComplete: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -38,7 +40,7 @@
|
|||
`${prefixCls}`,
|
||||
{
|
||||
[`${prefixCls}-disabled`]: this.disabled,
|
||||
[`${prefixCls}-selected`]: this.selected,
|
||||
[`${prefixCls}-selected`]: this.selected && !this.autoComplete,
|
||||
[`${prefixCls}-focus`]: this.isFocus
|
||||
}
|
||||
];
|
||||
|
@ -72,6 +74,9 @@
|
|||
this.$on('on-query-change', (val) => {
|
||||
this.queryChange(val);
|
||||
});
|
||||
|
||||
const Select = findComponentUpward(this, 'iSelect');
|
||||
if (Select) this.autoComplete = Select.autoComplete;
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.dispatch('iSelect', 'remove');
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
<template>
|
||||
<div :class="classes" v-clickoutside="handleClose">
|
||||
<div
|
||||
:class="[prefixCls + '-selection']"
|
||||
:class="selectionCls"
|
||||
ref="reference"
|
||||
@click="toggleMenu">
|
||||
<slot name="input">
|
||||
<div class="ivu-tag" v-for="(item, index) in selectedMultiple">
|
||||
<span class="ivu-tag-text">{{ item.label }}</span>
|
||||
<Icon type="ios-close-empty" @click.native.stop="removeTag(index)"></Icon>
|
||||
|
@ -24,6 +25,7 @@
|
|||
ref="input">
|
||||
<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>
|
||||
<transition :name="transitionName">
|
||||
<Drop
|
||||
|
@ -33,7 +35,7 @@
|
|||
ref="dropdown"
|
||||
:data-transfer="transfer"
|
||||
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="loading" :class="[prefixCls + '-loading']">{{ localeLoadingText }}</ul>
|
||||
</Drop>
|
||||
|
@ -123,6 +125,11 @@
|
|||
transfer: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
// Use for AutoComplete
|
||||
autoComplete: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
data () {
|
||||
|
@ -161,7 +168,13 @@
|
|||
dropdownCls () {
|
||||
return {
|
||||
[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 () {
|
||||
|
@ -225,16 +238,19 @@
|
|||
let status = true;
|
||||
const options = this.$slots.default || [];
|
||||
if (!this.loading && this.remote && this.query === '' && !options.length) status = false;
|
||||
|
||||
if (this.autoComplete && !options.length) status = false;
|
||||
|
||||
return this.visible && status;
|
||||
},
|
||||
notFountShow () {
|
||||
notFoundShow () {
|
||||
const options = this.$slots.default || [];
|
||||
return (this.notFound && !this.remote) || (this.remote && !this.loading && !options.length);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
toggleMenu () {
|
||||
if (this.disabled) {
|
||||
if (this.disabled || this.autoComplete) {
|
||||
return false;
|
||||
}
|
||||
this.visible = !this.visible;
|
||||
|
@ -543,6 +559,7 @@
|
|||
},
|
||||
handleBlur () {
|
||||
setTimeout(() => {
|
||||
if (this.autoComplete) return;
|
||||
const model = this.model;
|
||||
|
||||
if (this.multiple) {
|
||||
|
@ -737,7 +754,7 @@
|
|||
if (this.multiple) {
|
||||
this.$refs.input.focus();
|
||||
} else {
|
||||
this.$refs.input.select();
|
||||
if (!this.autoComplete) this.$refs.input.select();
|
||||
}
|
||||
if (this.remote) {
|
||||
this.findChild(child => {
|
||||
|
@ -753,7 +770,7 @@
|
|||
this.broadcast('Drop', 'on-update-popper');
|
||||
} else {
|
||||
if (this.filterable) {
|
||||
this.$refs.input.blur();
|
||||
if (!this.autoComplete) this.$refs.input.blur();
|
||||
// #566 reset options visible
|
||||
setTimeout(() => {
|
||||
this.broadcastQuery('');
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'core-js/fn/array/find-index';
|
|||
|
||||
import Affix from './components/affix';
|
||||
import Alert from './components/alert';
|
||||
import AutoComplete from './components/auto-complete';
|
||||
import Avatar from './components/avatar';
|
||||
import BackTop from './components/back-top';
|
||||
import Badge from './components/badge';
|
||||
|
@ -51,6 +52,7 @@ import locale from './locale';
|
|||
const iview = {
|
||||
Affix,
|
||||
Alert,
|
||||
AutoComplete,
|
||||
Avatar,
|
||||
BackTop,
|
||||
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;
|
||||
}
|
||||
}
|
|
@ -41,3 +41,4 @@
|
|||
@import "tree";
|
||||
@import "avatar";
|
||||
@import "color-picker";
|
||||
@import "auto-complete";
|
Loading…
Add table
Reference in a new issue