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