magiccube-vue3
Version:
vue3-js版组件库
281 lines (266 loc) • 11.2 kB
JavaScript
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