<script setup> import {ref, computed, watch} from 'vue'; import pinyin from 'pinyin'; import countryCode from './data/index.js'; import { useI18n } from 'vue-i18n' import { useRouter } from 'vue-router' definePageMeta({ title: '国家地区', i18n: 'countryRegion.title', }) const router = useRouter() const { t, locale } = useI18n() const value = ref(''); const alphabet = [ '#', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]; // 常用国家的代码列表 const frequentCountryCodes = ['CN', 'TW', 'JP', 'US']; function groupByPinyinInitial(data) { const grouped = {}; // 先处理常用国家 grouped['#'] = []; data.forEach(country => { if (frequentCountryCodes.includes(country.code)) { const countryName = locale.value === 'zh-CN' ? country.cn : locale.value === 'zh-TW' ? country.tw : locale.value === 'ja-JP' ? country.ja : country.en; grouped['#'].push({ ...country, displayName: countryName }); } }); // 处理其他国家 data.forEach(country => { if (!frequentCountryCodes.includes(country.code)) { const countryName = locale.value === 'zh-CN' ? country.cn : locale.value === 'zh-TW' ? country.tw : locale.value === 'ja-JP' ? country.ja : country.en; const initial = locale.value === 'ja-JP' ? '' : locale.value === 'zh-CN' || locale.value === 'zh-TW' ? pinyin(countryName, {style: pinyin.STYLE_FIRST_LETTER})[0][0].toUpperCase() : countryName.charAt(0).toUpperCase(); if (!grouped[initial]) { grouped[initial] = []; } grouped[initial].push({ ...country, displayName: countryName }); } }); if (locale.value === 'ja-JP') { // 日文环境下按照片假名排序 grouped[''] = grouped[''].sort((a, b) => a.displayName.localeCompare(b.displayName, 'ja-JP')); } return grouped; } const groupedCountries = ref([]) const initData = () => { groupedCountries.value = groupByPinyinInitial(countryCode); } const searchCountry = computed(() => { if (!value.value) { return groupedCountries.value; } return Object.keys(groupedCountries.value).reduce((filtered, initial) => { const countries = groupedCountries.value[initial].filter(country => country.displayName.toLowerCase().includes(value.value.toLowerCase()) ); if (countries.length > 0) { filtered[initial] = countries; } return filtered; }, {}); }); const showIndexBar = computed(() => locale.value !== 'ja-JP') const handleCountrySelect = (country) => { router.push({ path: '/login', query: { zone: country.zone, countryName: country.displayName } }) } initData() // 监听语言变化,重新初始化数据 watch(locale, () => { initData() }) </script> <template> <div> <van-sticky> <van-search v-model="value" :placeholder="t('countryRegion.searchPlaceholder')"/> </van-sticky> <van-index-bar v-if="showIndexBar" sticky :sticky-offset-top="55" :index-list="alphabet" > <!-- 常用国家分类 --> <van-index-anchor index="#">{{ t('countryRegion.frequentCountry') }}</van-index-anchor> <van-cell v-for="country in searchCountry['#']" :key="country.code" :title="country.displayName" @click="handleCountrySelect(country)" clickable > <div class="pr-[25px]"> +{{ country.zone }}</div> </van-cell> <!-- 其他国家按字母分类 --> <template v-for="(countries, index) in searchCountry" :key="index"> <template v-if="index !== '#'"> <van-index-anchor :index="index" ></van-index-anchor> <van-cell v-for="country in countries" :key="country.code" :title="country.displayName" @click="handleCountrySelect(country)" clickable > <div class="pr-[25px]"> +{{ country.zone }}</div> </van-cell> </template> </template> </van-index-bar> <div v-else> <div class="mb-4"> <div class="px-4 py-2 text-gray-600">{{ t('countryRegion.frequentCountry') }}</div> <van-cell v-for="country in searchCountry['#']" :key="country.code" :title="country.displayName" @click="handleCountrySelect(country)" clickable > <div class="pr-[25px]"> +{{ country.zone }}</div> </van-cell> </div> <van-cell v-for="country in Object.values(searchCountry).flat().filter(c => !frequentCountryCodes.includes(c.code))" :key="country.code" :title="country.displayName" @click="handleCountrySelect(country)" clickable > <div class="pr-[25px]"> +{{ country.zone }}</div> </van-cell> </div> <van-back-top v-if="showIndexBar" right="15vw" bottom="10vh"/> </div> </template> <style scoped> </style>