UNPKG

magiccube-vue3

Version:

vue3-js版组件库

281 lines (266 loc) 11.2 kB
import { ref, watch, toRaw, inject } from 'vue' import ICON_REMOVE from '../../img/icon_close.svg' const Cascade = { props: { // selected数据类型是对象,包含code、simpleName等 selected: [Array, Number, String], keyword: String, options: { type: Array, required: true }, max: { type: Number, default: 1 }, dropdownHeight: Number, enabledSelectSecondLevel: Boolean }, emits: ['change'], setup(props, { emit, expose }){ const activeFirst = ref('') const activeSecond = ref('') const model = ref([]) const secondColumn = ref([]) const thirdColumn = ref([]) // 外部数据变化,催动组件内交互变化 watch(() => props.selected, (newVal, oldVal) => { newVal !== oldVal && (model.value = newVal) }) const init = () => { if(props.selected?.length){ model.value = props.selected const target = props.selected[0] const firstItem = props.options.find(n => target.code.indexOf(n.code) === 0) const secondItem = firstItem.sub.find(n => target.code.indexOf(n.code) === 0) activeFirst.value = firstItem.code activeSecond.value = secondItem.code secondColumn.value = firstItem.sub thirdColumn.value = secondItem.sub } else { // 默认所有列第一个值为锚中状态,并设置子列 const firstItem = props.options[0] const secondItem = firstItem.sub[0] activeFirst.value = firstItem.code activeSecond.value = secondItem.code secondColumn.value = firstItem.sub thirdColumn.value = secondItem.sub } } const handleClear = () => { model.value = [] emit('change', toRaw(model.value)) } const handleRemove = (data, evt) => { evt.stopPropagation() model.value = model.value.filter(item => item.code!== data.code) emit('change', toRaw(model.value)) } const handleClick = (level, data, event = {}) => { event.stopPropagation() switch(level){ case 'first': { activeFirst.value = data.code // 默认锚中子集第一个 activeSecond.value = data.sub[0].code // 设置二级和三级列表 secondColumn.value = data.sub thirdColumn.value = data.sub[0].sub break } case 'second': { if(data.sub.length) { activeSecond.value = data.code // 设置三级列表 thirdColumn.value = data.sub } else { // 如果没有子级就设置二级的值为结果 setValue(data) } break } case 'third': { setValue(data) break } } } // 获取父级"codeToData"函数 const getDataByCode = inject('getDataByCode', () => {}) // 去重 const filterDuplicateData = (list) => { const keySet = new Set() const resultSet = new Set() list.forEach(item => { if(!keySet.has(item.code)){ keySet.add(item.code) resultSet.add(item) } }) return [...resultSet] } const setValue = (data) => { const isExist = model.value.find(n => n.code === data.code) if(isExist) return false // !如果选择的是“全部”,值不是对象而是code字符,在这里需要进行转换 const _data = typeof data === 'string'? getDataByCode(data) : data if(props.max === 1){ /** * 单选时,选择全部就是传递2级的值,选择3级就是传递3级的值 */ model.value = [_data] } else if(props.max === model.value.length){ return false } else { /** * 多选时,如果有子级,则选中所有子级。如果没有子级,就添加到结果中 */ if(_data.sub.length) { model.value = filterDuplicateData([ ...model.value, ..._data.sub, ]) } else { model.value.push(_data) } } emit('change', toRaw(model.value)) } const getBadge = code => { let count = 0 model.value.forEach(n => { if(n?.code?.indexOf(code) === 0) count++ }) return count } expose({ init }) return () => ( <div class={[ 'mc-cascader' ]}> <div class={['mc-cascader__tip']}> <span> { props.max < 999? `最多选择 ${props.max} 项` : '可以选择多项' } </span> </div> {/* 一级 */} <ul class={[ 'mc-cascader__column', 'mc-cascader__column--first', ]} style={{ height: props.dropdownHeight + 'px' }}> { props.options.map((p, idx) => ( <li class={[ 'mc-cascader__column--item', 'view-full', { active: activeFirst.value === p.code } ]} key={idx} v-ellipsis={p.simpleName} onClick={(e) => handleClick('first', p, e)}> <span>{p.simpleName}</span> { getBadge(p.code)? <span class="mc-cascader__badge">{getBadge(p.code)}</span> : '' } </li> )) } </ul> {/* 二级 */} <ul class={[ 'mc-cascader__column', 'mc-cascader__column--second', ]} style={{ height: props.dropdownHeight + 'px' }}> { secondColumn.value.map((c, idx) => ( <li class={[ 'mc-cascader__column--item', 'view-full', { active: activeSecond.value === c.code } ]} key={idx} onClick={(e) => handleClick('second', c, e)}> <span>{c.simpleName}</span> { getBadge(c.code)? <span class="mc-cascader__badge">{getBadge(c.code)}</span> : '' } </li> )) } </ul> {/* 三级 */} <ul class={[ 'mc-cascader__column', 'mc-cascader__column--third', ]} style={{ height: props.dropdownHeight + 'px' }}> { // 如果没有第三级 就不显示全部按钮 props.enabledSelectSecondLevel && !props.keyword && thirdColumn.value?.length? ( <li class={[ 'mc-cascader__column--tag', ]}> <span onClick={e => handleClick('third', activeSecond.value, e)}>全部</span> </li> ) : '' } { thirdColumn.value.map((d, idx) => ( <li class={[ 'mc-cascader__column--tag', { active: model.value.some(n => n.code === d.code) } ]} key={idx}> <span onClick={(e) => handleClick('third', 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={handleClear}> <img src={require('../../img/icon_trash.svg')} /> </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">{n.simpleName}</span> <i class="mc-cascader__selected--list__item--tag--close" onClick={(e) => handleRemove(n, e)}> <img src={ICON_REMOVE} /> </i> </div> </div> </li> )) } </ul> </div> ) : '' } </div> ) } } export default Cascade