UNPKG

magiccube-vue3

Version:

vue3-js版组件库

348 lines (310 loc) 12 kB
import { ref, computed } from 'vue' import * as utils from '../../utils/common' import CITY_DATA from '../../data/city' const hotCityCode = ['11', '12', '31', '50', '3301', '4021', '4401', '4403', '5101'] const specialCity = ['11', '12', '31', '50'] function limitLength(string, max = 0) { const _max = parseInt(max) return string.length > _max ? string.substring(0, _max) + '...' : string } function getChildrenList(a) { return b => b.value !== a.value && b.value.indexOf(a.value) === 0 && b.level !== a.level && ((a.level === 'province' && b.level === 'city') || (a.level === 'city' && b.level === 'district')) } function equal(item, value) { if (typeof item === 'object') { return item.value.indexOf(value) === 0 && item.value.length > value.length } else { return item === value } } function isSpecialCity(code) { return specialCity.includes(code.substring(0, 2)) } function isHotCity(code) { return hotCityCode.includes(code.substring(0, 4)) } const Tag = { props: { data: Object, type: String, idx: Number, max: { type: [Number, String], default: 99 }, active: Boolean, include: Boolean, }, emits: ['change', 'click'], setup(props, { emit }) { const handleClick = () => { if (props.type === 'review') return emit('click', props.data) } const handleRemove = (e) => { e.preventDefault() e.stopPropagation() emit('change', props.data) } return () => ( <div class={{ 'mc-city-tag': true, review: props.type === 'review', active: props.active, include: props.include }}> <div class="mc-city-tag__wrap" onClick={handleClick}> <span class="mc-city-tag__text"> { props.data.name && props.data.name.length > parseInt(props.max) ? ( // <McTip position="top" // content={props.data.name}>{limitLength(props.data.name, props.max)}</McTip> <>{limitLength(props.data.name, props.max)}</> ) : ( <>{props.data.name}</> ) } </span> { props.type === 'review' ? ( <span class="mc-city-tag__close" onClick={handleRemove}></span> ) : '' } </div> </div> ) } } const City = { name: 'McCity', props: { modelValue: Array, show: Boolean, title: { type: String, default: '选择城市信息' }, tips: { type: String, default: '可选择多项' }, offsetLeft: { type: [Number, String], default: 0 }, offsetTop: { type: [Number, String], default: 0 }, max: { type: [String, Number], default: 1 }, enabledDistrict: { type: Boolean, default: true }, }, emits: ['update:modelValue', 'change', 'close'], setup(props, { emit, expose }) { const selected = ref([]) const level = ref([{ name: '全部', value: '' }]) const keyword = ref('') const model = computed({ get() { return props.modelValue }, set(value) { emit('update:modelValue', value) } }) const handleConfirm = () => { model.value = utils.deepCopy(selected.value) emit('change', selected.value) } const reset = () => { selected.value = [] level.value = level.value.slice(0, 1) } const set = () => { selected.value = utils.deepCopy(model.value) } const setInvisible = () => { emit('close') reset() } const handleRemove = (tag) => { selected.value.splice(selected.value.findIndex(item => item.value === tag.value), 1) } const isSelected = (value) => { return selected.value.some(item => item.value === value) } const isInclude = (value) => { return selected.value.some(item => equal(item, value)) } const handleSelect = (city) => { const lastLevel = level.value[level.value.length - 1] if (isSelected(city.value)) { if ((city.level === 'province' || city.level === 'city') && lastLevel.value === city.value) return if (isSpecialCity(city.value) && level.value.length === 2) return if (city.level === 'district') return } if (city.level === 'province' && city.value !== lastLevel.value) { level.value.push(city) } else if (city.level === 'city' && isSpecialCity(city.value)) { // 直辖市 区级别选择 setResult(city) } else if (city.level === 'city' && city.value !== lastLevel.value && props.enabledDistrict) { level.value.push(city) } else { setResult(city) } } const handleClickLevel = (item) => { const idx = level.value.findIndex(n => n.value === item.value) level.value = level.value.slice(0, idx + 1) } const setResult = (item) => { if (parseInt(props.max) === 1) { selected.value = [item] handleConfirm() } else if (selected.value.length >= props.max) { console.error(`最多可选择${props.max}个`) } else { selected.value.push(item) } } const unlimitedItem = () => { return level.value[level.value.length - 1] } const handleChange = (item) => { const cityItem = item?.data?.value ? CITY_DATA.find(c => c.value === item.data.value) : null if (cityItem) setResult(cityItem) } const handleSelectSearchCity = (city) => { handleSelect(city) keyword.value = '' } const hotCityList = computed(() => { return CITY_DATA.filter(c => c.level === 'province' && hotCityCode.includes(c.value)) }) const otherCityList = computed(() => { return CITY_DATA.filter(c => c.level === 'province' && !hotCityCode.includes(c.value)) }) const childrenCityList = computed(() => { const item = level.value[level.value.length - 1] return CITY_DATA.filter(getChildrenList(item)) }) const cityOptions = computed(() => { return keyword.value? CITY_DATA.filter(n => n.name.indexOf(keyword.value) > -1) : [] }) const headerNode = () => ( <> <div class="mc-city__header--left"> <span class="mc-city__header--title">{props.title}</span> <span class="mc-city__header--tips">{props.tips}</span> </div> <div class="mc-city__header--right"> <McButton type="cancel" width="88" onClick={setInvisible}>取消</McButton> <McButton width="88" onClick={handleConfirm}>确定</McButton> </div> </> ) const reviewNode = () => ( <> { selected.value.map((tag, i) => ( <Tag key={i} idx={i} data={tag} type="review" onChange={() => handleRemove(tag)} /> )) } </> ) const toolbarNode = () => ( <> { level.value.map((item, idx) => ( <div class={{ 'mc-city__body--toolbar__tag': true, active: idx === level.value.length - 1 }} key={idx} onClick={() => handleClickLevel(item)}> <span>{item.name}</span> <span class="mc-city__arrow"></span> </div> )) } <div class="mc-city__body--toolbar__search"> <McInput v-model={keyword.value} clear /> <ul class="mc-city__body--toolbar__dropdown"> {cityOptions.value?.length? cityOptions.value.map(n => ( <li class={{ selected: isSelected(n.value) }} onClick={() => handleSelectSearchCity(n)}> {n.name} - {n.fullName} </li> )) : ''} </ul> </div> </> ) const tagListNode = (data, idx) => ( <div class="mc-city__body--list__wrap" key={idx}> <Tag data={data} max={3} active={isSelected(data.value)} include={isInclude(data.value)} onClick={() => handleSelect(data)} /> </div> ) expose({ set, reset }) return () => ( <div class="mc-city" style={{ 'margin-left': `-${340 - Number(props.offsetLeft)}px`, 'margin-top': `${props.offsetTop}px` }}> <div class="mc-city__header">{headerNode()}</div> <div class="mc-city__review">{reviewNode()}</div> <div class="mc-city__body"> <div class="mc-city__body--toolbar">{toolbarNode()}</div> <div class="mc-city__body--content"> { level.value.length === 1 ? ( <> <div class="mc-city__body--panel"> <div class="mc-city__body--title">热门城市</div> <div class="mc-city__body--list">{hotCityList.value.map((city, idx) => tagListNode(city, idx))}</div> </div> <div class="mc-city__body--panel"> <div class="mc-city__body--title">其他省市</div> <div class="mc-city__body--list">{otherCityList.value.map((city, idx) => tagListNode(city, idx))}</div> </div> </> ) : ( <div class="mc-city__body--panel"> <div class="mc-city__body--list"> <div>{tagListNode(unlimitedItem(), 0)}</div> {childrenCityList.value.map((city, idx) => tagListNode(city, idx))} </div> </div> ) } </div> </div> </div> ) } } City.install = (app) => { app.component(City.name, City) } const McCity = City export { McCity, McCity as default }