UNPKG

magiccube-vue3

Version:

vue3-js版组件库

335 lines (315 loc) 14.6 kB
import { ref, computed, toRaw } from 'vue' import ICON_REMOVE from '../../img/icon_close.svg' import ICON_DELETE from '../../img/icon_trash.svg' const HOT_CITY = ['11', '12', '31', '50', '3301', '4021', '4401', '4403', '5101'] const SPECIAL_CITY = ['11', '12', '31', '50', '71', '81', '82'] const Cascade = { props: { selected: [Array, Number, String], keyword: String, options: { type: Array, default: () => [] }, max: { type: Number, default: 1 }, dropdownHeight: Number, // 是否可以直接选择省级 enableSelectProvince: Boolean, // 是否可以直接选择市级 enableSelectCity: Boolean, }, emits: ['change'], setup(props, { emit, expose }){ const CITY_DICT = computed(() => { if(props.keyword){ const arr = props.options.filter(n => n.defaultName.indexOf(props.keyword) > -1 || n.simpleName.indexOf(props.keyword) > -1) const firstCodeList = [], secondCodeList = [], thirdCodeList = [] arr.forEach((n, i) => { switch(n.propertyName){ case 'province': { firstCodeList.push(n.propertyCode) secondCodeList.push(n.propertyCode) break } case 'city': { firstCodeList.push(n.firstCode) HOT_CITY.indexOf(n.firstCode) > -1? secondCodeList.push(n.firstCode) : n.secondCode !== '0'? secondCodeList.push(n.secondCode) : '' thirdCodeList.push(n.propertyCode) break } case 'district': { firstCodeList.push(n.firstCode) secondCodeList.push(n.secondCode) thirdCodeList.push(n.propertyCode) break } } }) activeFirst.value = firstCodeList[0] || '' activeSecond.value = secondCodeList[0] || '' return props.options.filter(n => firstCodeList.includes(n.propertyCode) || secondCodeList.includes(n.propertyCode) || thirdCodeList.includes(n.propertyCode)) } else { return props.options } }) const DEFAULT_OPTION = [{simpleName: '热门城市', propertyCode: '9999'}] const activeFirst = ref('') const activeSecond = ref('') const model = ref([]) const init = () => { if(props.selected?.length){ const _def = props.selected[0] activeFirst.value = _def.firstCode === '0'? _def.propertyCode : _def.firstCode activeSecond.value = _def.secondCode === '0'? _def.propertyCode : _def.secondCode } else { activeFirst.value = '9999' activeSecond.value = HOT_CITY[0] } model.value = props.selected } const firstColumn = computed(() => props.keyword? getData('province') : [...DEFAULT_OPTION, ...getData('province')]) const secondColumn = computed(() => getData('city')) const thirdColumn = computed(() => getData('district')) const getData = (level) => { switch(level){ case 'province': { return CITY_DICT.value.filter(n => n.propertyName === 'province') } case 'city': { if(activeFirst.value === '9999'){ return CITY_DICT.value.filter(n => HOT_CITY.indexOf(n.propertyCode) > -1) } else if(SPECIAL_CITY.indexOf(activeFirst.value) > -1){ return [CITY_DICT.value.find(n => activeFirst.value === n.propertyCode)] } else { return CITY_DICT.value.filter(n => n.propertyName === 'city' && activeFirst.value === n.firstCode) } } case 'district': { if(SPECIAL_CITY.indexOf(activeFirst.value) > -1 || SPECIAL_CITY.indexOf(activeSecond.value) > -1){ return CITY_DICT.value.filter(n => (n.propertyName === 'city' && (activeSecond.value === n.firstCode || activeFirst.value === n.firstCode)) || (n.propertyName === 'district' && (activeSecond.value === n.secondCode || activeSecond.value + '00' === n.secondCode))) } else { return CITY_DICT.value.filter(n => n.propertyName === 'district' && (activeSecond.value === n.secondCode || activeFirst.value === n.secondCode)) } } } } const handleClear = (event) => { event.stopPropagation() model.value = [] emit('change', toRaw(model.value)) } const handleClick = (level, data, event = {}) => { event.stopPropagation() switch(level){ case 1: { activeFirst.value = data.propertyCode if(SPECIAL_CITY.indexOf(data.propertyCode) > -1) { activeSecond.value = data.propertyCode } else { const _c = getData('city') || [] activeSecond.value = _c[0]?.propertyCode || data.propertyCode } break } case 2: { activeSecond.value = data.propertyCode break } case 3: { let _selected = model.value const existIDX = model.value.findIndex(n => n.propertyCode === data.propertyCode) if(existIDX > -1){ _selected.splice(existIDX, 1) } else if(props.max === 1){ _selected = [data] } else if(props.max === _selected.length){ return false } else { _selected.push(data) } model.value = _selected emit('change', toRaw(model.value)) break } // 选择city级别 case 4: { const item = secondColumn.value.find(n => n.propertyCode === activeSecond.value || n.propertyCode === activeFirst.value) let _selected = model.value const existIDX = model.value.findIndex(n => n.propertyCode === item.propertyCode) if(!item) return if(existIDX > -1){ _selected.splice(existIDX, 1) } else if(props.max === 1){ _selected = [item] } else if(props.max === _selected.length){ return false } else { _selected.push(item) } model.value = _selected emit('change', toRaw(model.value)) break } // 选择province级别 case 5: { const item = firstColumn.value.find(n => n.propertyCode === activeFirst.value) if(!item) return const existIDX = model.value.findIndex(n => n.propertyCode === item.propertyCode) let _selected = model.value if(existIDX > -1){ _selected.splice(existIDX, 1) } else if(props.max === 1){ _selected = [item] } else if(props.max === _selected.length){ return false } else { _selected.push(item) } model.value = _selected emit('change', toRaw(model.value)) break } } } const getBadge = code => { let count = 0 if(code === '9999'){ model.value.forEach(n => { if(HOT_CITY.indexOf(n.firstCode) > -1 || HOT_CITY.indexOf(n.secondCode) > -1) count++ }) } else { model.value.forEach(n => { if(n.propertyCode.indexOf(code) === 0) count++ }) } return count } expose({ init }) return () => ( <div class={[ 'mc-cascader' ]}> <ul class={[ 'mc-cascader__column', 'mc-cascader__column--first', ]} style={{ height: props.dropdownHeight + 'px' }}> { firstColumn.value.map((p, idx) => ( <li class={[ 'mc-cascader__column--item', { active: activeFirst.value === p.propertyCode } ]} key={idx} v-ellipsis={p.simpleName} onClick={(e) => handleClick(1, p, e)}> <span>{p.simpleName}</span> { getBadge(p.propertyCode)? <span class="mc-cascader__badge">{getBadge(p.propertyCode)}</span> : '' } </li> )) } </ul> <ul class={[ 'mc-cascader__column', 'mc-cascader__column--second', ]} style={{ height: props.dropdownHeight + 'px' }}> { activeFirst.value !== '9999' && props.enableSelectProvince? ( <li class={[ 'mc-cascader__column--item', ]} onClick={(e) => handleClick(5, {}, e)}> <span>全部</span> </li> ) : '' } { secondColumn.value.map((c, idx) => ( <li class={[ 'mc-cascader__column--item', { active: activeSecond.value === c.propertyCode } ]} key={idx} onClick={(e) => handleClick(2, c, e)}> <span>{c.simpleName}</span> { getBadge(c.propertyCode)? <span class="mc-cascader__badge">{getBadge(c.propertyCode)}</span> : '' } </li> )) } </ul> <ul class={[ 'mc-cascader__column', 'mc-cascader__column--third', ]} style={{ height: props.dropdownHeight + 'px' }}> { props.enableSelectCity && secondColumn.value?.length ? ( <li class={[ 'mc-cascader__column--tag', ]}> <span onClick={(e) => handleClick(4, {}, e)}>全部</span> </li> ) : '' } { thirdColumn.value?.length? thirdColumn.value.map((d, idx) => ( <li class={[ 'mc-cascader__column--tag', { active: model.value.some(n => n.propertyCode === d.propertyCode) } ]} key={idx}> <span onClick={(e) => handleClick(3, d, e)}>{d.simpleName}</span> </li> )) : '' } </ul> { model.value.length? ( <div class={[ 'mc-cascader__selected' ]} style={{ height: props.dropdownHeight + 'px' }}> <div class="mc-cascader__selected--title"> <span>已选</span> <span style="flex:1;margin-left:16px;">{model.value.length || 0}个</span> <i onClick={e => handleClear(e)}> <img src={ICON_DELETE} /> </i> </div> <ul class="mc-cascader__selected--list"> { model.value.map(n => ( <li class="mc-cascader__selected--list__item"> <div class="mc-cascader__selected--list__item--tag"> <div class="mc-cascader__selected--list__item--tag__inner"> <span class="mc-cascader__selected--list__item--tag--name" v-ellipsis={n.defaultName}>{n.defaultName}</span> <i class="mc-cascader__selected--list__item--tag--close" onClick={(e) => handleClick(3, n, e)}> <img src={ICON_REMOVE} /> </i> </div> </div> </li> )) } </ul> </div> ) : '' } </div> ) } } export default Cascade