167 lines
5.1 KiB
Vue
167 lines
5.1 KiB
Vue
<template>
|
|
<!-- 区域号码选择 -->
|
|
<up-popup :show="isShow" round="20" closeable @close="close">
|
|
<view class="container flex flex-col px-[20px]">
|
|
<view class="title flex items-center justify-center mx-[15px]">
|
|
<text class="txt text-[16px] py-[15px] font-bold">{{ t('areaCode') }}</text>
|
|
</view>
|
|
<view class="search-box my-[10px]">
|
|
<up-search v-model="keyword" :showAction="false" :placeholder="t('areaCodePlace')" @change="handleKeywordChange"></up-search>
|
|
</view>
|
|
<!-- 主列表 -->
|
|
<view class="index-list relative overflow-hidden">
|
|
<scroll-view scroll-y :scroll-into-view="scrollIntoId" class="main-list h-[70vh] overflow-hidden" @scroll="handleScroll">
|
|
<template v-if="isEmpty(keyword)">
|
|
<view v-for="(group, index) in list" :key="index" :id="group.groupKey" class="group-container relative">
|
|
<!-- 吸顶标题 -->
|
|
<view class="group-title text-[14px] text-main py-[5px] sticky top-0 bg-white z-[999] sticky-title">
|
|
{{ group.name }}
|
|
</view>
|
|
<view v-for="(item, idx) in group.data" :key="idx" class="list-item text-main text-[14px] py-[10px]" @click="handleSelect(item)">
|
|
{{ `${item.name} (+${item.code})` }}
|
|
</view>
|
|
</view>
|
|
</template>
|
|
<view v-else v-for="(item, idx) in filterList" :key="idx" class="list-item text-main text-[14px] py-[10px]" @click="handleSelect(item)">
|
|
{{ `${item.name} (+${item.code})` }}
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<!-- 右侧索引 -->
|
|
<view class="index-bar absolute top-0 right-0 py-[10px] gap-[10px] z-[9999]">
|
|
<view
|
|
v-for="(group, index) in list"
|
|
:key="index"
|
|
class="index-item text-center text-[12px] "
|
|
:class="currentIntoId === group.groupKey ? 'bg-[#e61e30] rounded-full text-white' : 'text-[#e61e30]'"
|
|
@click="handleIndexclick(group.groupKey)"
|
|
>
|
|
{{ group.groupKey }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</up-popup>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { nextTick, ref, watch } from 'vue';
|
|
import { onLoad, onReady } from '@dcloudio/uni-app';
|
|
import { useSystemInfo } from '@/hooks/useSystemInfo.js';
|
|
import { useI18n } from 'vue-i18n';
|
|
import { isEmpty } from '@/common/common.js';
|
|
import { apiWholeAreaCode } from '@/api/common.js';
|
|
const { t, locale } = useI18n();
|
|
|
|
const { computedDomHeight } = useSystemInfo();
|
|
|
|
const props = defineProps({
|
|
show: {
|
|
type: Boolean,
|
|
default: false,
|
|
},
|
|
});
|
|
|
|
// 获取区域码数据
|
|
const getAreaCodeList = async () => {
|
|
apiWholeAreaCode({ type: '' }).then((res) => {
|
|
if (res.success) {
|
|
list.value = res.data.codeGroups;
|
|
nextTick(() => {
|
|
calculateGroupPositions();
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
const isShow = ref(false);
|
|
watch(
|
|
() => props.show,
|
|
(newVal) => {
|
|
isShow.value = newVal;
|
|
if (newVal) getAreaCodeList();
|
|
},
|
|
{
|
|
immediate: true,
|
|
}
|
|
);
|
|
const emits = defineEmits(['close', 'select']);
|
|
const close = () => emits('close');
|
|
|
|
const keyword = ref('');
|
|
const filterList = ref([]);
|
|
const handleKeywordChange = (e) => {
|
|
if (!e) return (filterList.value = []);
|
|
const result = [];
|
|
for (let group of list.value) {
|
|
for (let item of group.data) {
|
|
const target = item.search?.replace(/\s+/g, '');
|
|
if (target.includes(e)) {
|
|
result.push(item);
|
|
}
|
|
}
|
|
}
|
|
filterList.value = uniqueByCode(result, 'code');
|
|
};
|
|
|
|
// 数组对象去重
|
|
const uniqueByCode = (arr, field) => {
|
|
const map = new Map(); // 用于存储已经出现过的 field
|
|
return arr.filter((item) => {
|
|
if (!map.has(item[field])) {
|
|
map.set(item[field], true); // 将 field 添加到 Map 中
|
|
return true; // 保留当前对象
|
|
}
|
|
return false; // 过滤掉重复对象
|
|
});
|
|
};
|
|
|
|
const handleSelect = (e) => {
|
|
emits('select', e);
|
|
};
|
|
|
|
// 数据
|
|
const list = ref([]);
|
|
|
|
const scrollIntoId = ref(''); // 当前滚动到的锚点ID
|
|
const currentIntoId = ref(''); // 当前的索引
|
|
const groupPositions = ref([]); // 存储每个组的顶部位置
|
|
|
|
// 点击索引跳转
|
|
const handleIndexclick = (title) => {
|
|
keyword.value = '';
|
|
scrollIntoId.value = title;
|
|
currentIntoId.value = title;
|
|
};
|
|
|
|
// 处理滚动事件
|
|
const handleScroll = (event) => {
|
|
const scrollTop = event.detail.scrollTop;
|
|
// 根据滚动位置判断当前吸顶的标题
|
|
for (let i = groupPositions.value.length - 1; i >= 0; i--) {
|
|
if (scrollTop > groupPositions.value[i].top) {
|
|
currentIntoId.value = groupPositions.value[i].title;
|
|
break;
|
|
}
|
|
}
|
|
};
|
|
|
|
// 计算每个组的顶部位置
|
|
const calculateGroupPositions = async () => {
|
|
let heightList = [];
|
|
for (let i = 0; i < list.value.length; i++) {
|
|
const group = list.value[i];
|
|
const data = await computedDomHeight(`#${group.groupKey}`);
|
|
if (data) {
|
|
heightList[i] = data.height || 0;
|
|
groupPositions.value.push({
|
|
title: group.groupKey,
|
|
top: i > 0 ? heightList.slice(0, i).reduce((pre, cur) => cur + pre, 0) : 0,
|
|
});
|
|
}
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<style scoped lang="scss"></style>
|