数据结构优化

This commit is contained in:
李琦
2026-01-20 19:59:50 +08:00
parent cf8b89f731
commit ec4f5a9856
6 changed files with 216 additions and 105 deletions

View File

@@ -16,10 +16,10 @@
<div id="toast-container" class="toast-container relative z-[70]"></div> <div id="toast-container" class="toast-container relative z-[70]"></div>
<!-- Header (Hide on Admin & Login) --> <!-- Header (Hide on Admin & Login) -->
<Header v-if="!isAdminOrLogin" class="relative z-50" /> <Header v-if="!isAdminOrLogin" class="z-50" />
<!-- Mobile Menu (Hide on Admin & Login) --> <!-- Mobile Menu (Hide on Admin & Login) -->
<MobileMenu v-if="!isAdminOrLogin" class="relative z-50" /> <MobileMenu v-if="!isAdminOrLogin" class="z-50" />
<!-- Main Content --> <!-- Main Content -->
<!-- Conditional classes: Apply max-w-7xl only for frontend pages --> <!-- Conditional classes: Apply max-w-7xl only for frontend pages -->

View File

@@ -1,88 +1,189 @@
<template> <template>
<header class="fixed top-0 w-full z-50 glass-nav transition-all duration-300" id="main-header"> <header class="fixed top-0 w-full z-50 glass-nav transition-all duration-300" id="main-header">
<div class="max-w-7xl mx-auto px-6 h-20 flex items-center justify-between"> <!-- 优化点1: 移动端高度减小 (h-16), 桌面端保持 h-20 -->
<div class="cursor-pointer group" @click="goTo('/')"> <div class="max-w-7xl mx-auto px-4 md:px-6 h-16 md:h-20 flex items-center justify-between">
<span class="font-serif text-2xl italic tracking-wider text-white group-hover:text-art-accent transition-colors">{{ siteTitle || '年糕崽崽' }}</span> <!-- Logo -->
<span class="font-serif text-2xl italic tracking-wider text-art-accent group-hover:text-white transition-colors">.Blog</span> <div class="cursor-pointer group flex items-center gap-1" @click="goTo('/')">
<span class="font-serif text-xl md:text-2xl italic tracking-wider text-white group-hover:text-art-accent transition-colors">{{ siteTitle || '年糕崽崽' }}</span>
<span class="font-serif text-xl md:text-2xl italic tracking-wider text-art-accent group-hover:text-white transition-colors">.Blog</span>
</div> </div>
<nav class="hidden md:flex items-center gap-10">
<button <!-- Desktop Nav -->
v-if="visibleMenus.includes('home')" <nav class="hidden md:flex items-center gap-8 lg:gap-10">
@click="goTo('/')" <button
class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" v-if="visibleMenus.includes('home')"
:class="{ 'text-white after:w-full': activeNav === 'home' }" @click="goTo('/')"
data-target="home" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline"
:class="{ 'text-white after:w-full': activeNav === 'home' }"
> >
首页 首页
</button> </button>
<button <button
v-if="visibleMenus.includes('blog')" v-if="visibleMenus.includes('blog')"
@click="goTo('/blog')" @click="goTo('/blog')"
class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline"
:class="{ 'text-white after:w-full': activeNav === 'blog' }" :class="{ 'text-white after:w-full': activeNav === 'blog' }"
data-target="blog"
> >
思考 思考
</button> </button>
<button <button
v-if="visibleMenus.includes('columns')" v-if="visibleMenus.includes('columns')"
@click="goTo('/columns')" @click="goTo('/columns')"
class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline"
:class="{ 'text-white after:w-full': activeNav === 'columns' }" :class="{ 'text-white after:w-full': activeNav === 'columns' }"
data-target="columns"
> >
专栏 专栏
</button> </button>
<button <button
v-if="visibleMenus.includes('works')" v-if="visibleMenus.includes('works')"
@click="goTo('/works')" @click="goTo('/works')"
class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline"
:class="{ 'text-white after:w-full': activeNav === 'works' }" :class="{ 'text-white after:w-full': activeNav === 'works' }"
data-target="works"
> >
作品 作品
</button> </button>
<button <button
v-if="visibleMenus.includes('snippets')" v-if="visibleMenus.includes('snippets')"
@click="goTo('/snippets')" @click="goTo('/snippets')"
class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline"
:class="{ 'text-white after:w-full': activeNav === 'snippets' }" :class="{ 'text-white after:w-full': activeNav === 'snippets' }"
data-target="snippets"
> >
代码 代码
</button> </button>
<button <button
v-if="visibleMenus.includes('about')" v-if="visibleMenus.includes('about')"
@click="goTo('/about')" @click="goTo('/about')"
class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline" class="nav-item text-sm font-medium tracking-wide text-art-muted hover:text-white transition-colors link-underline"
:class="{ 'text-white after:w-full': activeNav === 'about' }" :class="{ 'text-white after:w-full': activeNav === 'about' }"
data-target="about"
> >
关于 关于
</button> </button>
</nav> </nav>
<!-- Desktop Actions -->
<div class="hidden md:flex items-center gap-4"> <div class="hidden md:flex items-center gap-4">
<a href="#" class="text-art-muted hover:text-white transition-colors"><Icon name="github" :size="20" /></a> <a href="#" class="text-art-muted hover:text-white transition-colors"><Icon name="github" :size="20" /></a>
<a href="/admin" target="_blank" class="text-art-muted hover:text-white transition-colors" title="后台管理"> <a href="/admin" target="_blank" class="text-art-muted hover:text-white transition-colors" title="后台管理">
<Icon name="layout-dashboard" :size="20" /> <Icon name="layout-dashboard" :size="20" />
</a> </a>
<button <button
v-if="visibleMenus.includes('services')" v-if="visibleMenus.includes('services')"
@click="goTo('/services')" @click="goTo('/services')"
class="px-5 py-2 text-xs font-bold tracking-widest uppercase border border-white/20 hover:border-art-accent hover:text-art-accent transition-all rounded-full" class="px-5 py-2 text-xs font-bold tracking-widest uppercase border border-white/20 hover:border-art-accent hover:text-art-accent transition-all rounded-full"
> >
合作 合作
</button> </button>
</div> </div>
<button class="md:hidden text-white" @click="toggleMobileMenu">
<!-- Mobile Menu Button -->
<button class="md:hidden text-white p-2 -mr-2 active:scale-95 transition-transform" @click="isMobileMenuOpen = true">
<Icon name="menu" :size="24" /> <Icon name="menu" :size="24" />
</button> </button>
</div> </div>
<!-- Mobile Drawer Menu (抽屉式) -->
<!-- 优化点3: 使用 Teleport 传送到 body确保不被父级遮挡并实现抽屉动画 -->
<Teleport to="body">
<Transition name="fade">
<div v-if="isMobileMenuOpen" class="fixed inset-0 z-[100] md:hidden" @click="closeMobileMenu">
<!-- Backdrop -->
<div class="absolute inset-0 bg-black/60 backdrop-blur-sm"></div>
</div>
</Transition>
<Transition name="slide-right">
<div v-if="isMobileMenuOpen" class="fixed top-0 right-0 bottom-0 w-64 bg-[#1a1a1a] z-[101] shadow-2xl border-l border-white/10 flex flex-col md:hidden">
<!-- Drawer Header -->
<div class="h-16 px-6 flex items-center justify-between border-b border-white/5">
<span class="font-serif text-lg italic tracking-wider text-white">
Menu
</span>
<button class="text-white/70 hover:text-white p-2 -mr-2" @click="closeMobileMenu">
<Icon name="x" :size="24" />
</button>
</div>
<!-- Drawer Content -->
<div class="flex-1 overflow-y-auto py-6 px-6 space-y-6">
<nav class="flex flex-col space-y-4">
<button
v-if="visibleMenus.includes('home')"
@click="goTo('/')"
class="text-left text-base font-medium py-2 border-b border-white/5 transition-colors"
:class="activeNav === 'home' ? 'text-art-accent' : 'text-art-muted hover:text-white'"
>
首页
</button>
<button
v-if="visibleMenus.includes('blog')"
@click="goTo('/blog')"
class="text-left text-base font-medium py-2 border-b border-white/5 transition-colors"
:class="activeNav === 'blog' ? 'text-art-accent' : 'text-art-muted hover:text-white'"
>
思考
</button>
<button
v-if="visibleMenus.includes('columns')"
@click="goTo('/columns')"
class="text-left text-base font-medium py-2 border-b border-white/5 transition-colors"
:class="activeNav === 'columns' ? 'text-art-accent' : 'text-art-muted hover:text-white'"
>
专栏
</button>
<button
v-if="visibleMenus.includes('works')"
@click="goTo('/works')"
class="text-left text-base font-medium py-2 border-b border-white/5 transition-colors"
:class="activeNav === 'works' ? 'text-art-accent' : 'text-art-muted hover:text-white'"
>
作品
</button>
<button
v-if="visibleMenus.includes('snippets')"
@click="goTo('/snippets')"
class="text-left text-base font-medium py-2 border-b border-white/5 transition-colors"
:class="activeNav === 'snippets' ? 'text-art-accent' : 'text-art-muted hover:text-white'"
>
代码
</button>
<button
v-if="visibleMenus.includes('about')"
@click="goTo('/about')"
class="text-left text-base font-medium py-2 border-b border-white/5 transition-colors"
:class="activeNav === 'about' ? 'text-art-accent' : 'text-art-muted hover:text-white'"
>
关于
</button>
</nav>
<div class="pt-6 border-t border-white/10 space-y-4">
<div class="flex items-center gap-4">
<a href="#" class="flex items-center gap-2 text-art-muted hover:text-white transition-colors text-sm">
<Icon name="github" :size="18" />
<span>GitHub</span>
</a>
<a href="/admin" target="_blank" class="flex items-center gap-2 text-art-muted hover:text-white transition-colors text-sm">
<Icon name="layout-dashboard" :size="18" />
<span>管理后台</span>
</a>
</div>
<button
v-if="visibleMenus.includes('services')"
@click="goTo('/services')"
class="w-full py-3 text-xs font-bold tracking-widest uppercase border border-white/20 hover:bg-white/5 hover:border-art-accent hover:text-art-accent transition-all rounded-lg text-center text-white"
>
商业合作
</button>
</div>
</div>
</div>
</Transition>
</Teleport>
</header> </header>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, onUpdated, watch, nextTick } from 'vue' import { ref, onMounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router' import { useRouter, useRoute } from 'vue-router'
import { getPublicSettings } from '../services/api' import { getPublicSettings } from '../services/api'
import Icon from './Icon.vue' import Icon from './Icon.vue'
@@ -91,26 +192,13 @@ const router = useRouter()
const route = useRoute() const route = useRoute()
const activeNav = ref('home') const activeNav = ref('home')
const siteTitle = ref<string>('') const siteTitle = ref<string>('')
const visibleMenus = ref<string[]>(['home', 'blog', 'columns', 'works', 'snippets', 'about', 'services']) // 默认显示所有菜单 const visibleMenus = ref<string[]>(['home', 'blog', 'columns', 'works', 'snippets', 'about', 'services'])
const isMobileMenuOpen = ref(false)
const toggleMobileMenu = () => { const closeMobileMenu = () => {
const menu = document.getElementById('mobile-menu') isMobileMenuOpen.value = false
if (menu) {
if (menu.classList.contains('menu-closed')) {
menu.classList.remove('menu-closed')
menu.classList.add('menu-open')
document.body.style.overflow = 'hidden'
} else {
menu.classList.remove('menu-open')
menu.classList.add('menu-closed')
document.body.style.overflow = ''
}
}
} }
// navigate function is used in template via @click handlers
// navigate function removed - not used in template
const updateActiveNav = () => { const updateActiveNav = () => {
const path = route.path const path = route.path
if (path === '/') activeNav.value = 'home' if (path === '/') activeNav.value = 'home'
@@ -123,32 +211,25 @@ const updateActiveNav = () => {
else if (path === '/about') activeNav.value = 'about' else if (path === '/about') activeNav.value = 'about'
} }
// Icons are now handled by Vue components
// 获取网站配置
const loadSiteSettings = async () => { const loadSiteSettings = async () => {
try { try {
const settings = await getPublicSettings() const settings = await getPublicSettings()
if (settings.site_title) { if (settings.site_title) {
siteTitle.value = settings.site_title siteTitle.value = settings.site_title
} }
// 获取菜单显示配置
if (settings.visible_menus) { if (settings.visible_menus) {
try { try {
// 尝试解析JSON格式
const parsed = JSON.parse(settings.visible_menus) const parsed = JSON.parse(settings.visible_menus)
if (Array.isArray(parsed)) { if (Array.isArray(parsed)) {
visibleMenus.value = parsed visibleMenus.value = parsed
} }
} catch { } catch {
// 如果不是JSON按逗号分隔
visibleMenus.value = settings.visible_menus.split(',').map((m: string) => m.trim()).filter((m: string) => m) visibleMenus.value = settings.visible_menus.split(',').map((m: string) => m.trim()).filter((m: string) => m)
} }
} }
} catch (error) { } catch (error) {
console.error('Failed to load site settings:', error) console.error('Failed to load site settings:', error)
// 使用默认值,不抛出错误
} }
} }
@@ -158,17 +239,47 @@ onMounted(() => {
}) })
watch( watch(
() => route.path, () => route.path,
() => { () => {
updateActiveNav() updateActiveNav()
} }
) )
const goTo = (target: string) => { const goTo = (target: string) => {
router.push(target) router.push(target)
closeMobileMenu() // 跳转时关闭菜单
} }
// 监听菜单状态,控制 body 滚动,防止抽屉打开时背景还能滚动
watch(isMobileMenuOpen, (isOpen) => {
if (isOpen) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
})
</script> </script>
<style scoped> <style scoped>
/* 组件特定样式可以在这里添加 */ /* Fade Animation */
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
/* Slide Right Animation */
.slide-right-enter-active,
.slide-right-leave-active {
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
}
.slide-right-enter-from,
.slide-right-leave-to {
transform: translateX(100%);
}
</style> </style>

View File

@@ -3,7 +3,7 @@
:is="iconComponent" :is="iconComponent"
:class="className" :class="className"
:style="style" :style="style"
:size="size" :size="iconSize"
/> />
</template> </template>
@@ -35,4 +35,13 @@ const iconComponent = computed(() => {
const iconName = toPascalCase(props.name) as keyof typeof LucideIcons const iconName = toPascalCase(props.name) as keyof typeof LucideIcons
return LucideIcons[iconName] || LucideIcons.AlertCircle return LucideIcons[iconName] || LucideIcons.AlertCircle
}) })
// 确保 size 是 number 类型
const iconSize = computed(() => {
if (typeof props.size === 'string') {
const parsed = parseInt(props.size, 10)
return isNaN(parsed) ? 24 : parsed
}
return props.size ?? 24
})
</script> </script>

View File

@@ -198,7 +198,7 @@ const updateAndDrawParticles = () => {
} }
} }
const createMeteor = (force = false) => { const createMeteor = () => {
const startX = Math.random() * width const startX = Math.random() * width
const angle = Math.PI / 4 // 45度 const angle = Math.PI / 4 // 45度
meteors.push({ meteors.push({

View File

@@ -381,8 +381,8 @@ md.renderer.rules.fence = function (tokens, idx) {
const lines = token.content.trimEnd().split('\n') const lines = token.content.trimEnd().split('\n')
const lineNumbers = lines.map((_, i) => i + 1).join('\n') const lineNumbers = lines.map((_, i) => i + 1).join('\n')
// SVG Icon 手动嵌入,不使用 Icon 组件 // 修复1: 显式定义的 SVG Icon颜色更亮 (text-white/70),去除复杂 opacity 类
const copyIconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>` const copyIconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"/><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"/></svg>`
return ` return `
<div class="ios-code-container my-6 md:my-10 rounded-lg md:rounded-xl overflow-hidden shadow-2xl bg-[#1e1e1e] border border-white/5 relative group/code text-sm"> <div class="ios-code-container my-6 md:my-10 rounded-lg md:rounded-xl overflow-hidden shadow-2xl bg-[#1e1e1e] border border-white/5 relative group/code text-sm">
@@ -396,10 +396,10 @@ md.renderer.rules.fence = function (tokens, idx) {
<div class="text-[10px] md:text-xs text-white/30 font-mono select-none uppercase tracking-wider">${langName || 'TEXT'}</div> <div class="text-[10px] md:text-xs text-white/30 font-mono select-none uppercase tracking-wider">${langName || 'TEXT'}</div>
</div> </div>
<!-- 修复: 在移动端始终显示(opacity-100),桌面端 Hover 显示 --> <!-- 修复2: 复制按钮始终可见,增加点击热区,颜色加深 -->
<button class="copy-btn flex items-center gap-1.5 px-2 py-1 rounded hover:bg-white/10 transition-all cursor-pointer opacity-100 md:opacity-0 md:group-hover/code:opacity-100" aria-label="Copy code"> <button class="copy-btn flex items-center gap-1.5 px-2 py-1 rounded hover:bg-white/10 transition-all cursor-pointer opacity-100" aria-label="Copy code">
<span class="text-white/40">${copyIconSvg}</span> <span class="text-white/70">${copyIconSvg}</span>
<span class="text-[10px] md:text-xs text-white/40 font-medium">复制</span> <span class="text-[10px] md:text-xs text-white/70 font-medium">复制</span>
</button> </button>
</div> </div>
@@ -408,9 +408,9 @@ md.renderer.rules.fence = function (tokens, idx) {
<pre class="font-mono text-xs md:text-sm leading-relaxed" style="margin:0;">${lineNumbers}</pre> <pre class="font-mono text-xs md:text-sm leading-relaxed" style="margin:0;">${lineNumbers}</pre>
</div> </div>
<!-- 修复: 添加 whitespace-pre 到 code 标签,确保不换行 --> <!-- 修复3: 强制添加 style="white-space: pre; overflow-x: auto" 确保滑动 -->
<div class="code-block-wrapper flex-1 overflow-x-auto py-3 md:py-4 px-3 md:px-4 bg-[#1e1e1e] min-w-0 w-full"> <div class="code-block-wrapper flex-1 py-3 md:py-4 px-3 md:px-4 bg-[#1e1e1e] min-w-0 w-full" style="overflow-x: auto; -webkit-overflow-scrolling: touch;">
<pre class="hljs !bg-transparent !m-0 !p-0 !rounded-none !overflow-visible"><code class="!font-mono text-xs md:text-sm leading-relaxed block !whitespace-pre">${highlighted}</code></pre> <pre class="hljs !bg-transparent !m-0 !p-0 !rounded-none !overflow-visible"><code class="!font-mono text-xs md:text-sm leading-relaxed block" style="white-space: pre;">${highlighted}</code></pre>
</div> </div>
</div> </div>
</div> </div>
@@ -506,7 +506,7 @@ const addCopyListeners = () => {
const text = codeElement.innerText const text = codeElement.innerText
await navigator.clipboard.writeText(text) await navigator.clipboard.writeText(text)
const originalContent = newBtn.innerHTML const originalContent = newBtn.innerHTML
const checkIconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-green-400"><polyline points="20 6 9 17 4 12"/></svg>` const checkIconSvg = `<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="text-green-400" style="display: block;"><polyline points="20 6 9 17 4 12"/></svg>`
newBtn.innerHTML = `<span>${checkIconSvg}</span><span class="text-green-400 text-[10px] md:text-xs font-medium">已复制</span>` newBtn.innerHTML = `<span>${checkIconSvg}</span><span class="text-green-400 text-[10px] md:text-xs font-medium">已复制</span>`
setTimeout(() => { setTimeout(() => {
newBtn.innerHTML = originalContent newBtn.innerHTML = originalContent
@@ -603,24 +603,22 @@ onBeforeUnmount(() => {
border-radius: 0 !important; border-radius: 0 !important;
box-shadow: none !important; box-shadow: none !important;
border: none !important; border: none !important;
white-space: pre !important; /* 关键:强制不自动换行 */ /* 确保这里也强制 pre */
white-space: pre !important;
} }
:deep(.prose code) { :deep(.prose code) {
font-family: 'SF Mono', Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; font-family: 'SF Mono', Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
white-space: pre !important; /* 关键:强制不自动换行 */
} }
/* 覆盖 highlight.js 或其他样式可能带来的换行 */
:deep(.code-block-wrapper pre code) { :deep(.code-block-wrapper pre code) {
white-space: pre !important; white-space: pre !important;
word-wrap: normal !important; word-wrap: normal !important;
overflow-wrap: normal !important; overflow-wrap: normal !important;
} }
/* 允许容器横向滚动 */
:deep(.code-block-wrapper) { :deep(.code-block-wrapper) {
-webkit-overflow-scrolling: touch; /* iOS 惯性滚动 */ -webkit-overflow-scrolling: touch;
} }
:deep(.prose :not(pre) > code) { :deep(.prose :not(pre) > code) {
@@ -629,7 +627,8 @@ onBeforeUnmount(() => {
padding: 0.2em 0.4em; padding: 0.2em 0.4em;
border-radius: 0.3em; border-radius: 0.3em;
font-weight: 500; font-weight: 500;
word-break: break-word; /* 内联代码允许换行,防止撑开 */ /* 内联代码依然需要换行 */
word-break: break-word;
} }
:deep(.prose h1), :deep(.prose h2), :deep(.prose h3) { :deep(.prose h1), :deep(.prose h2), :deep(.prose h3) {

View File

@@ -299,14 +299,6 @@ const uvTrendOption = computed(() => ({
}] }]
})) }))
// 所有省份列表(用于设置默认值)
const allProvinces = [
'北京', '天津', '河北', '山西', '内蒙古', '辽宁', '吉林', '黑龙江',
'上海', '江苏', '浙江', '安徽', '福建', '江西', '山东', '河南',
'湖北', '湖南', '广东', '广西', '海南', '重庆', '四川', '贵州',
'云南', '西藏', '陕西', '甘肃', '青海', '宁夏', '新疆', '台湾', '香港', '澳门'
]
// 所有省份完整名称列表(用于匹配地图数据) // 所有省份完整名称列表(用于匹配地图数据)
const allProvincesFullNames = [ const allProvincesFullNames = [
'北京市', '天津市', '河北省', '山西省', '内蒙古自治区', '辽宁省', '吉林省', '黑龙江省', '北京市', '天津市', '河北省', '山西省', '内蒙古自治区', '辽宁省', '吉林省', '黑龙江省',