1- import { defineComponent , ref , renderSlot , computed , Transition } from "vue"
2- import {
3- OptionItem ,
4- editableSelectProps ,
5- EditableSelectProps ,
6- } from "./editable-select-types"
7- import "./editable-select.scss"
8- import { Icon } from "../../icon"
9- import ClickOutside from "../../shared/devui-directive/clickoutside"
10- import { className } from "./utils"
11- import { debounce } from "lodash"
1+ import { defineComponent , ref , renderSlot , computed , Transition , watch } from 'vue'
2+ import { OptionItem , editableSelectProps , EditableSelectProps } from './editable-select-types'
3+ import './editable-select.scss'
4+ import { Icon } from '../../icon'
5+ import ClickOutside from '../../shared/devui-directive/clickoutside'
6+ import { className } from './utils'
7+ import { debounce } from 'lodash'
128export default defineComponent ( {
13- name : " DEditableSelect" ,
9+ name : ' DEditableSelect' ,
1410 directives : { ClickOutside } ,
1511 props : editableSelectProps ,
16- emits : [ " update:modelValue" ] ,
12+ emits : [ ' update:modelValue' ] ,
1713 setup ( props : EditableSelectProps , ctx ) {
18- const inputCls = className (
19- "devui-form-control devui-dropdown-origin devui-dropdown-origin-open" ,
20- {
21- disabled : props . disabled ,
22- }
23- )
24-
25- const getLiCls = ( item ) => {
26- const { disabledKey } = props
27- return className ( "devui-dropdown-item" , {
28- disabled : disabledKey ? ! ! item [ disabledKey ] : false ,
29- } )
30- }
31-
14+ const dropdownRef = ref ( null )
3215 const visible = ref ( false )
33- const inputValue = ref ( "" )
16+ const inputValue = ref ( '' )
17+ const activeIndex = ref ( 0 )
3418 const query = ref ( props . modelValue )
3519
3620 const wait = computed ( ( ) => ( props . remote ? 300 : 0 ) )
3721
3822 const emptyText = computed ( ( ) => {
3923 const options = filteredOptions . value
40- if ( ! props . remote && inputValue . value && options . length === 0 ) {
41- return " 没有相关记录"
24+ if ( ! props . remote && options . length === 0 ) {
25+ return ' 没有相关记录'
4226 }
4327 if ( options . length === 0 ) {
44- return " 没有数据"
28+ return ' 没有数据'
4529 }
4630 return null
4731 } )
32+
4833 const normalizeOptions = computed ( ( ) => {
4934 let options : OptionItem
5035 const { disabledKey } = props
51- disabledKey ? disabledKey : " disabled"
36+ disabledKey ? disabledKey : ' disabled'
5237 return props . options . map ( ( item ) => {
53- if ( typeof item !== " object" ) {
38+ if ( typeof item !== ' object' ) {
5439 options = {
55- name : item ,
40+ name : item
5641 }
5742 return options
5843 }
@@ -61,9 +46,11 @@ export default defineComponent({
6146 } )
6247
6348 const filteredOptions = computed ( ( ) => {
64- const isValidOption = ( o : OptionItem ) : boolean => {
49+ const isValidOption = ( o : OptionItem ) => {
6550 const query = inputValue . value
66- const containsQueryString = query ? o . name . includes ( query ) : true
51+ const containsQueryString = query
52+ ? o . name . toLocaleLowerCase ( ) . indexOf ( query . toLocaleLowerCase ( ) ) >= 0
53+ : true
6754 return containsQueryString
6855 }
6956 return normalizeOptions . value
@@ -76,6 +63,12 @@ export default defineComponent({
7663 . filter ( ( item ) => item !== null )
7764 } )
7865
66+ const findIndex = ( o ) => {
67+ return normalizeOptions . value . findIndex ( ( item ) => {
68+ return item . name === o . name
69+ } )
70+ }
71+
7972 const handleClose = ( ) => {
8073 visible . value = false
8174 }
@@ -84,11 +77,12 @@ export default defineComponent({
8477 visible . value = ! visible . value
8578 }
8679 }
87- const onInputChange = ( ) => {
80+
81+ const onInputChange = ( val : string ) => {
8882 if ( props . filterMethod ) {
89- props . filterMethod ( inputValue . value )
83+ props . filterMethod ( val )
9084 } else if ( props . remote ) {
91- props . remoteMethod ( inputValue . value )
85+ props . remoteMethod ( val )
9286 }
9387 }
9488
@@ -99,65 +93,83 @@ export default defineComponent({
9993 inputValue . value = value
10094 query . value = value
10195 if ( props . remote ) {
102- debouncedOnInputChange ( )
96+ debouncedOnInputChange ( value )
10397 } else {
104- onInputChange ( )
98+ onInputChange ( value )
10599 }
106100 }
101+
107102 const selectOptionClick = ( e , item ) => {
108103 const { disabledKey } = props
109104 if ( disabledKey && item [ disabledKey ] ) {
110105 e . stopPropagation ( )
111106 } else {
112107 query . value = item . name
113- ctx . emit ( "update:modelValue" , item . name )
108+ activeIndex . value = findIndex ( item )
109+ inputValue . value = ''
110+ ctx . emit ( 'update:modelValue' , item . name )
114111 }
115112 }
113+
114+ const loadMore = ( ) => {
115+ if ( ! props . enableLazyLoad ) return
116+ const dropdownVal = dropdownRef . value
117+ if ( dropdownVal . clientHeight + dropdownVal . scrollTop >= dropdownVal . scrollHeight ) {
118+ props . remoteMethod ( inputValue . value )
119+ }
120+ }
121+
116122 return ( ) => {
123+ const selectCls = className ( 'devui-form-group devui-has-feedback' , {
124+ 'devui-select-open' : visible . value
125+ } )
126+ const inputCls = className (
127+ 'devui-form-control devui-dropdown-origin devui-dropdown-origin-open' ,
128+ {
129+ disabled : props . disabled
130+ }
131+ )
132+
133+ const getLiCls = ( item , index ) => {
134+ const { disabledKey } = props
135+ return className ( 'devui-dropdown-item' , {
136+ disabled : disabledKey ? ! ! item [ disabledKey ] : false ,
137+ selected : activeIndex . value === index
138+ } )
139+ }
140+
117141 return (
118- < div
119- class = "devui-form-group devui-has-feedback devui-select-open"
120- v-click-outside = { handleClose }
121- onClick = { toggleMenu }
122- >
123- < input
124- class = { inputCls }
125- type = "text"
126- onInput = { handleInput }
127- value = { query . value }
128- />
129- < span class = "devui-form-control-feedback" >
130- < span class = "devui-select-chevron-icon" >
131- < Icon name = "select-arrow" />
142+ < div class = { selectCls } v-click-outside = { handleClose } onClick = { toggleMenu } >
143+ < input class = { inputCls } type = 'text' onInput = { handleInput } value = { query . value } />
144+ < span class = 'devui-form-control-feedback' >
145+ < span class = 'devui-select-chevron-icon' >
146+ < Icon name = 'select-arrow' />
132147 </ span >
133148 </ span >
134- < div class = " devui-editable-select" >
135- < Transition name = " fade" >
136- < div class = " devui-dropdown-menu" v-show = { visible . value } >
149+ < div class = ' devui-editable-select' >
150+ < Transition name = ' fade' >
151+ < div class = ' devui-dropdown-menu' v-show = { visible . value } >
137152 < ul
138- class = "devui-list-unstyled scroll-height"
153+ class = 'devui-list-unstyled scroll-height'
154+ ref = { dropdownRef }
139155 style = { {
140- maxHeight : props . maxHeight + "px" ,
156+ maxHeight : props . maxHeight + 'px'
141157 } }
158+ onScroll = { loadMore }
142159 >
143- { filteredOptions . value . map ( ( item ) => {
160+ { filteredOptions . value . map ( ( item , index ) => {
144161 return (
145162 < li
146- class = { getLiCls ( item ) }
163+ class = { getLiCls ( item , index ) }
147164 onClick = { ( $evnet ) => selectOptionClick ( $evnet , item ) }
148165 key = { item . name }
149166 >
150- { ctx . slots . default
151- ? renderSlot ( ctx . slots , "default" , { item } )
152- : item . name }
167+ { ctx . slots . default ? renderSlot ( ctx . slots , 'default' , { item } ) : item . name }
153168 </ li >
154169 )
155170 } ) }
156- < li
157- class = "devui-no-result-template"
158- v-show = { filteredOptions . value . length === 0 }
159- >
160- < div class = "devui-no-data-tip" > { emptyText . value } </ div >
171+ < li class = 'devui-no-result-template' v-show = { filteredOptions . value . length === 0 } >
172+ < div class = 'devui-no-data-tip' > { emptyText . value } </ div >
161173 </ li >
162174 </ ul >
163175 </ div >
@@ -166,5 +178,5 @@ export default defineComponent({
166178 </ div >
167179 )
168180 }
169- } ,
181+ }
170182} )
0 commit comments