init: 整合项目(dashboard + www + docs)

This commit is contained in:
eafonyang
2026-07-30 11:23:50 +08:00
commit 99ab6cc4f4
435 changed files with 61037 additions and 0 deletions
+24
View File
@@ -0,0 +1,24 @@
# Dependencies
node_modules/
# Build output
dist/
.output/
# Nuxt generated
.nuxt/
# Env local files
.env.local
.env.*.local
# Logs
*.log
# Editor
.vscode/*
!.vscode/extensions.json
.idea/
# OS
.DS_Store
+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
+22
View File
@@ -0,0 +1,22 @@
{
"app": {
"name": "Luxsin",
"slogan": "Luxsin Official Website"
},
"nav": {
"home": "Home",
"products": "Products",
"news": "News",
"about": "About Us",
"contact": "Contact"
},
"footer": {
"copyright": "© {year} Luxsin. All rights reserved."
},
"common": {
"learnMore": "Learn More",
"backHome": "Back to Home",
"notFound": "Page Not Found",
"notFoundDesc": "Sorry, the page you visited does not exist or has been removed."
}
}
+22
View File
@@ -0,0 +1,22 @@
{
"app": {
"name": "乐笙",
"slogan": "乐笙产品官网"
},
"nav": {
"home": "首页",
"products": "产品",
"news": "新闻",
"about": "关于我们",
"contact": "联系我们"
},
"footer": {
"copyright": "© {year} 乐笙 Luxsin. 保留所有权利。"
},
"common": {
"learnMore": "了解更多",
"backHome": "返回首页",
"notFound": "页面不存在",
"notFoundDesc": "抱歉,您访问的页面不存在或已被移除。"
}
}
+37
View File
@@ -0,0 +1,37 @@
export default defineNuxtConfig({
// SPA 模式渲染,预留 SSR/SSG 升级能力
ssr: false,
modules: ['@unocss/nuxt', '@nuxtjs/i18n', '@pinia/nuxt', '@vueuse/nuxt'],
// 国际化:中文默认无前缀,英文 /en 前缀
// v10 自动检测 i18n/locales/ 目录,无需 langDir / lazy
i18n: {
locales: [
{ code: 'zh', language: 'zh-CN', name: '中文' },
{ code: 'en', language: 'en-US', name: 'English' }
],
defaultLocale: 'zh',
strategy: 'prefix_except_default',
detectBrowserLanguage: {
useCookie: true,
cookieKey: 'i18n_redirected',
redirectOn: 'root'
}
},
app: {
head: {
title: '乐笙 Luxsin',
meta: [{ name: 'description', content: '乐笙(Luxsin)产品官网' }]
}
},
devServer: {
port: 3100
},
telemetry: false,
compatibilityDate: '2026-07-29'
});
+28
View File
@@ -0,0 +1,28 @@
{
"name": "web",
"private": true,
"type": "module",
"scripts": {
"dev": "nuxt dev",
"build": "nuxt build",
"generate": "nuxt generate",
"preview": "nuxt preview",
"postinstall": "nuxt prepare",
"typecheck": "nuxt typecheck"
},
"dependencies": {
"@nuxtjs/i18n": "^10.5.0",
"@pinia/nuxt": "^1.0.1",
"@vueuse/nuxt": "^14.4.0",
"nuxt": "^4.5.1",
"pinia": "^4.0.2",
"vue": "^3.5.0",
"vue-router": "^5.2.0"
},
"devDependencies": {
"@unocss/nuxt": "^66.0.0",
"typescript": "^5.8.0",
"unocss": "^66.0.0",
"vue-tsc": "^3.0.0"
}
}
+9825
View File
File diff suppressed because it is too large Load Diff
+7
View File
@@ -0,0 +1,7 @@
allowBuilds:
esbuild: true
minimumReleaseAgeExclude:
- '@vueuse/core@14.4.0'
- '@vueuse/metadata@14.4.0'
- '@vueuse/nuxt@14.4.0'
- '@vueuse/shared@14.4.0'
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 1.1 MiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 11 KiB

View File
+3
View File
@@ -0,0 +1,3 @@
{
"extends": "./.nuxt/tsconfig.json"
}
+28
View File
@@ -0,0 +1,28 @@
import { defineConfig, presetWind3, transformerDirectives, transformerVariantGroup } from 'unocss';
export default defineConfig({
content: {
pipeline: {
exclude: ['node_modules', 'dist', '.nuxt']
}
},
theme: {
colors: {
primary: '#2563eb'
},
fontSize: {
'icon-xs': '0.875rem',
'icon-small': '1rem',
icon: '1.125rem',
'icon-large': '1.5rem',
'icon-xl': '2rem'
}
},
shortcuts: {
'flex-center': 'flex justify-center items-center',
'flex-col-center': 'flex flex-col justify-center items-center',
'section-container': 'mx-auto max-w-1200px px-16px'
},
transformers: [transformerDirectives(), transformerVariantGroup()],
presets: [presetWind3({ dark: 'class' })]
});