结构目录优化

This commit is contained in:
eafonyang
2026-07-30 11:32:45 +08:00
parent 99ab6cc4f4
commit 707d889514
29 changed files with 10 additions and 95 deletions
+32
View File
@@ -0,0 +1,32 @@
<script setup lang="ts">
import zhCN from '~~/i18n/locales/zh.json';
import enUS from '~~/i18n/locales/en.json';
const { locale } = useI18n();
useHead({
htmlAttrs: {
lang: () => (locale.value === 'zh' ? 'zh-CN' : 'en-US')
}
});
// 根据当前语言动态设置页面标题
const head = useLocaleHead({
addDirAttribute: true,
identifierAttribute: 'id',
addSeoAttributes: true
});
useHead({
title: () => (locale.value === 'zh' ? zhCN.app.name : enUS.app.name),
htmlAttrs: head.value.htmlAttrs,
link: [...(head.value.link || [])],
meta: [...(head.value.meta || [])]
});
</script>
<template>
<NuxtLayout>
<NuxtPage />
</NuxtLayout>
</template>
+20
View File
@@ -0,0 +1,20 @@
/* 全局基础样式 */
html {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body {
margin: 0;
font-family:
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
'PingFang SC',
'Hiragino Sans GB',
'Microsoft YaHei',
sans-serif;
}
+14
View File
@@ -0,0 +1,14 @@
<script setup lang="ts">
const { t } = useI18n();
const currentYear = new Date().getFullYear();
</script>
<template>
<footer class="border-t border-gray-100 bg-gray-50 dark:bg-dark-900 dark:border-dark-700">
<div class="section-container py-32px flex flex-col items-center gap-12px">
<p class="text-sm text-gray-500 dark:text-gray-400">
{{ t('footer.copyright', { year: currentYear }) }}
</p>
</div>
</footer>
</template>
+47
View File
@@ -0,0 +1,47 @@
<script setup lang="ts">
const { t, locale, locales } = useI18n();
const switchLocalePath = useSwitchLocalePath();
const navItems = computed(() => [
{ label: t('nav.home'), path: '/' },
{ label: t('nav.products'), path: '/products' },
{ label: t('nav.news'), path: '/news' },
{ label: t('nav.about'), path: '/about' }
]);
</script>
<template>
<header class="sticky top-0 z-50 bg-white/80 backdrop-blur-md border-b border-gray-100 dark:bg-dark-900/80 dark:border-dark-700">
<div class="section-container h-64px flex items-center justify-between">
<!-- Logo -->
<NuxtLink :to="switchLocalePath(locale)" class="flex items-center gap-8px text-xl font-bold text-primary">
{{ t('app.name') }}
</NuxtLink>
<!-- 导航 -->
<nav class="hidden md:flex items-center gap-24px">
<NuxtLink
v-for="item in navItems"
:key="item.path"
:to="switchLocalePath(locale) === '/' ? item.path : switchLocalePath(locale) + item.path"
class="text-sm text-gray-600 hover:text-primary transition-colors dark:text-gray-300"
>
{{ item.label }}
</NuxtLink>
</nav>
<!-- 语言切换 -->
<div class="flex items-center gap-12px">
<NuxtLink
v-for="loc in locales"
:key="loc.code"
:to="switchLocalePath(loc.code)"
class="px-8px py-4px rounded text-sm transition-colors"
:class="locale === loc.code ? 'bg-primary text-white' : 'text-gray-500 hover:text-primary'"
>
{{ loc.code === 'zh' ? '中' : 'EN' }}
</NuxtLink>
</div>
</div>
</header>
</template>
+38
View File
@@ -0,0 +1,38 @@
/**
* 统一请求封装(基于 Nuxt 内置 ofetch
* 响应格式约定:{ code, data, message },成功码 '0000'
*/
interface ApiResponse<T = unknown> {
code: string;
data: T;
message: string;
}
export function useRequest() {
const config = useRuntimeConfig();
const baseURL = config.public.apiBaseUrl as string;
async function request<T = unknown>(url: string, options?: Parameters<typeof $fetch>[1]): Promise<T> {
const res = await $fetch<ApiResponse<T>>(url, {
baseURL,
...options
});
if (res.code !== '0000') {
throw new Error(res.message || 'Request failed');
}
return res.data;
}
function get<T = unknown>(url: string, params?: Record<string, unknown>) {
return request<T>(url, { method: 'GET', params });
}
function post<T = unknown>(url: string, body?: Record<string, unknown>) {
return request<T>(url, { method: 'POST', body });
}
return { request, get, post };
}
+8
View File
@@ -0,0 +1,8 @@
<script setup lang="ts">
</script>
<template>
<div class="min-h-screen">
<slot />
</div>
</template>
+12
View File
@@ -0,0 +1,12 @@
<script setup lang="ts">
</script>
<template>
<div class="min-h-screen flex flex-col">
<AppHeader />
<main class="flex-1">
<slot />
</main>
<AppFooter />
</div>
</template>
View File
+30
View File
@@ -0,0 +1,30 @@
<script setup lang="ts">
const { t, locale } = useI18n();
const switchLocalePath = useSwitchLocalePath();
definePageMeta({
layout: 'blank'
});
useHead({
title: () => `404 - ${t('common.notFound')}`
});
</script>
<template>
<div class="min-h-screen flex-col-center">
<h1 class="text-7xl font-bold text-gray-200 dark:text-gray-700">404</h1>
<h2 class="mt-16px text-xl font-medium text-gray-700 dark:text-gray-200">
{{ t('common.notFound') }}
</h2>
<p class="mt-8px text-sm text-gray-400">
{{ t('common.notFoundDesc') }}
</p>
<NuxtLink
:to="switchLocalePath(locale)"
class="mt-32px px-24px py-10px rounded-6px bg-primary text-white text-sm hover:opacity-90 transition-opacity"
>
{{ t('common.backHome') }}
</NuxtLink>
</div>
</template>
+23
View File
@@ -0,0 +1,23 @@
<script setup lang="ts">
const { t } = useI18n();
useHead({
title: () => `${t('app.name')} - ${t('nav.home')}`
});
</script>
<template>
<div>
<!-- Hero 区块 -->
<section class="section-container py-96px flex-col-center text-center">
<h1 class="text-4xl md:text-5xl font-bold text-gray-900 dark:text-white">
{{ t('app.name') }}
</h1>
<p class="mt-16px text-lg text-gray-500 dark:text-gray-400">
{{ t('app.slogan') }}
</p>
</section>
<!-- 页面内容由后台 CMS 配置的区块Section组合渲染 -->
</div>
</template>
View File
+18
View File
@@ -0,0 +1,18 @@
import { defineStore } from 'pinia';
/**
* 全局应用状态
*/
export const useAppStore = defineStore('app', () => {
/** 移动端菜单展开状态 */
const mobileMenuOpen = ref(false);
function toggleMobileMenu(open?: boolean) {
mobileMenuOpen.value = open ?? !mobileMenuOpen.value;
}
return {
mobileMenuOpen,
toggleMobileMenu
};
});
View File
View File