960 lines
24 KiB
Markdown
960 lines
24 KiB
Markdown
# 创高家具官网 - 前端项目架构设计
|
||
|
||
## 一、技术栈选型
|
||
|
||
| 层级 | 技术 | 版本 | 用途 |
|
||
|------|------|------|------|
|
||
| 框架 | Vue 3 | ^3.4.0 | 渐进式前端框架 |
|
||
| 构建 | Vite | ^5.0.0 | 快速构建工具 |
|
||
| 路由 | Vue Router | ^4.2.0 | 单页应用路由 |
|
||
| 状态 | Pinia | ^2.1.0 | 状态管理 |
|
||
| 国际化 | Vue I18n | ^9.0.0 | 中英文切换 |
|
||
| UI组件 | Element Plus | ^2.5.0 | 基础组件库 |
|
||
| 动画 | GSAP | ^3.12.0 | 高级动画效果 |
|
||
| 图标 | Element Plus Icons | - | 图标库 |
|
||
| CSS | SCSS | - | 预处理器 |
|
||
|
||
---
|
||
|
||
## 二、项目目录结构
|
||
|
||
```
|
||
client/
|
||
├── index.html # 入口HTML
|
||
├── package.json # 依赖配置
|
||
├── vite.config.ts # Vite配置
|
||
├── tsconfig.json # TypeScript配置
|
||
├── .env # 环境变量(开发)
|
||
├── .env.production # 环境变量(生产)
|
||
│
|
||
├── public/ # 静态资源(不处理)
|
||
│ ├── favicon.ico
|
||
│ └── logo.png
|
||
│
|
||
├── src/
|
||
│ ├── main.ts # 应用入口
|
||
│ ├── App.vue # 根组件
|
||
│ │
|
||
│ ├── api/ # API接口层
|
||
│ │ ├── request.ts # axios封装
|
||
│ │ ├── portal/ # 前台接口
|
||
│ │ │ ├── company.ts
|
||
│ │ │ ├── home.ts
|
||
│ │ │ ├── product.ts
|
||
│ │ │ ├── case.ts
|
||
│ │ │ ├── news.ts
|
||
│ │ │ └── contact.ts
|
||
│ │ └── admin/ # 后台接口
|
||
│ │ ├── auth.ts
|
||
│ │ ├── upload.ts
|
||
│ │ ├── product.ts
|
||
│ │ ├── case.ts
|
||
│ │ ├── news.ts
|
||
│ │ ├── pageBlock.ts
|
||
│ │ └── media.ts
|
||
│ │
|
||
│ ├── assets/ # 资源文件
|
||
│ │ ├── styles/ # 全局样式
|
||
│ │ │ ├── variables.scss # SCSS变量
|
||
│ │ │ ├── mixins.scss # SCSS混入
|
||
│ │ │ ├── global.scss # 全局样式
|
||
│ │ │ └── element-plus.scss # Element Plus主题覆盖
|
||
│ │ ├── images/ # 静态图片
|
||
│ │ └── fonts/ # 字体文件
|
||
│ │
|
||
│ ├── components/ # 公共组件
|
||
│ │ ├── common/ # 通用组件
|
||
│ │ │ ├── LangSwitch.vue # 语言切换
|
||
│ │ │ ├── PageLoading.vue # 页面加载
|
||
│ │ │ ├── ImageLazy.vue # 懒加载图片
|
||
│ │ │ └── Breadcrumb.vue # 面包屑
|
||
│ │ ├── layout/ # 布局组件
|
||
│ │ │ ├── AppHeader.vue # 顶部导航
|
||
│ │ │ ├── AppFooter.vue # 底部导航
|
||
│ │ │ ├── MobileMenu.vue # 移动端菜单
|
||
│ │ │ └── BackToTop.vue # 返回顶部
|
||
│ │ ├── home/ # 首页专用组件
|
||
│ │ │ ├── HeroBanner.vue # 首屏轮播
|
||
│ │ │ ├── ServiceSection.vue # 服务板块
|
||
│ │ │ ├── ProductSection.vue # 产品板块
|
||
│ │ │ ├── CaseSection.vue # 案例板块
|
||
│ │ │ └── StrengthSection.vue # 实力板块
|
||
│ │ └── product/ # 产品页组件
|
||
│ │ ├── ProductCard.vue
|
||
│ │ ├── ProductFilter.vue
|
||
│ │ └── ProductGallery.vue
|
||
│ │
|
||
│ ├── composables/ # 组合式函数
|
||
│ │ ├── useLocale.ts # 语言相关
|
||
│ │ ├── useScroll.ts # 滚动监听
|
||
│ │ ├── useDevice.ts # 设备检测
|
||
│ │ └── useSEO.ts # SEO设置
|
||
│ │
|
||
│ ├── directives/ # 自定义指令
|
||
│ │ ├── lazyLoad.ts # 图片懒加载
|
||
│ │ └── parallax.ts # 视差滚动
|
||
│ │
|
||
│ ├── locales/ # 国际化文件
|
||
│ │ ├── index.ts # 配置入口
|
||
│ │ ├── zh-CN.ts # 中文
|
||
│ │ └── en-US.ts # 英文
|
||
│ │
|
||
│ ├── router/ # 路由配置
|
||
│ │ ├── index.ts # 路由入口
|
||
│ │ ├── routes.ts # 路由定义
|
||
│ │ ├── portal.ts # 前台路由
|
||
│ │ └── admin.ts # 后台路由
|
||
│ │
|
||
│ ├── stores/ # Pinia状态管理
|
||
│ │ ├── index.ts
|
||
│ │ ├── app.ts # 应用状态
|
||
│ │ ├── user.ts # 用户状态
|
||
│ │ └── setting.ts # 设置状态
|
||
│ │
|
||
│ ├── types/ # TypeScript类型
|
||
│ │ ├── api.d.ts # API响应类型
|
||
│ │ ├── model.d.ts # 数据模型类型
|
||
│ │ └── global.d.ts # 全局类型
|
||
│ │
|
||
│ ├── utils/ # 工具函数
|
||
│ │ ├── storage.ts # 本地存储封装
|
||
│ │ ├── format.ts # 格式化
|
||
│ │ ├── validate.ts # 验证
|
||
│ │ └── constants.ts # 常量
|
||
│ │
|
||
│ └── views/ # 页面视图
|
||
│ ├── portal/ # 前台页面
|
||
│ │ ├── HomeView.vue
|
||
│ │ ├── ProductView.vue
|
||
│ │ ├── ProductDetailView.vue
|
||
│ │ ├── CaseView.vue
|
||
│ │ ├── CaseDetailView.vue
|
||
│ │ ├── AboutView.vue
|
||
│ │ ├── NewsView.vue
|
||
│ │ ├── NewsDetailView.vue
|
||
│ │ └── ContactView.vue
|
||
│ └── admin/ # 后台管理页面
|
||
│ ├── LoginView.vue
|
||
│ ├── DashboardView.vue
|
||
│ ├── Layout.vue
|
||
│ ├── company/
|
||
│ │ └── InfoManage.vue
|
||
│ ├── content/
|
||
│ │ ├── CarouselManage.vue
|
||
│ │ ├── PageBlockManage.vue
|
||
│ │ └── MediaLibrary.vue
|
||
│ ├── product/
|
||
│ │ ├── CategoryManage.vue
|
||
│ │ └── ProductManage.vue
|
||
│ ├── case/
|
||
│ │ ├── CaseCategoryManage.vue
|
||
│ │ └── CaseManage.vue
|
||
│ ├── news/
|
||
│ │ ├── NewsCategoryManage.vue
|
||
│ │ └── NewsManage.vue
|
||
│ ├── message/
|
||
│ │ └── MessageManage.vue
|
||
│ └── system/
|
||
│ ├── UserManage.vue
|
||
│ └── LogManage.vue
|
||
```
|
||
|
||
---
|
||
|
||
## 三、核心配置文件
|
||
|
||
### 3.1 vite.config.ts
|
||
|
||
```typescript
|
||
import { defineConfig } from 'vite'
|
||
import vue from '@vitejs/plugin-vue'
|
||
import { resolve } from 'path'
|
||
|
||
export default defineConfig({
|
||
plugins: [vue()],
|
||
resolve: {
|
||
alias: {
|
||
'@': resolve(__dirname, 'src'),
|
||
},
|
||
},
|
||
css: {
|
||
preprocessorOptions: {
|
||
scss: {
|
||
additionalData: `
|
||
@use "@/assets/styles/variables.scss" as *;
|
||
@use "@/assets/styles/mixins.scss" as *;
|
||
`,
|
||
},
|
||
},
|
||
},
|
||
server: {
|
||
port: 3000,
|
||
proxy: {
|
||
'/api': {
|
||
target: 'http://localhost:8080',
|
||
changeOrigin: true,
|
||
},
|
||
},
|
||
},
|
||
build: {
|
||
outDir: 'dist',
|
||
assetsDir: 'assets',
|
||
rollupOptions: {
|
||
output: {
|
||
chunkFileNames: 'js/[name]-[hash].js',
|
||
entryFileNames: 'js/[name]-[hash].js',
|
||
assetFileNames: (assetInfo) => {
|
||
const info = assetInfo.name.split('.')
|
||
const ext = info[info.length - 1]
|
||
if (/\.(png|jpe?g|gif|svg|webp|ico)$/.test(assetInfo.name)) {
|
||
return 'images/[name]-[hash][extname]'
|
||
}
|
||
if (/\.(woff2?|eot|ttf|otf)$/.test(assetInfo.name)) {
|
||
return 'fonts/[name]-[hash][extname]'
|
||
}
|
||
return '[ext]/[name]-[hash][extname]'
|
||
},
|
||
},
|
||
},
|
||
},
|
||
})
|
||
```
|
||
|
||
### 3.2 .env.production
|
||
|
||
```
|
||
# 站点代码
|
||
VITE_SITE_CODE=cg
|
||
|
||
# API地址
|
||
VITE_API_BASE_URL=/api
|
||
|
||
# 多语言默认
|
||
VITE_DEFAULT_LANG=zh-CN
|
||
|
||
# MinIO配置
|
||
VITE_MINIO_ENDPOINT=http://localhost:9000
|
||
VITE_MINIO_BUCKET=chuanggao-images
|
||
VITE_MINIO_PUBLIC_URL=http://localhost:9000/chuanggao-images
|
||
```
|
||
|
||
### 3.3 tsconfig.json
|
||
|
||
```json
|
||
{
|
||
"compilerOptions": {
|
||
"target": "ESNext",
|
||
"useDefineForClassFields": true,
|
||
"module": "ESNext",
|
||
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
||
"skipLibCheck": true,
|
||
"moduleResolution": "bundler",
|
||
"allowImportingTsExtensions": true,
|
||
"resolveJsonModule": true,
|
||
"isolatedModules": true,
|
||
"noEmit": true,
|
||
"jsx": "preserve",
|
||
"strict": true,
|
||
"noUnusedLocals": true,
|
||
"noUnusedParameters": true,
|
||
"noFallthroughCasesInSwitch": true,
|
||
"baseUrl": ".",
|
||
"paths": {
|
||
"@/*": ["src/*"]
|
||
}
|
||
},
|
||
"include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"],
|
||
"references": [{ "path": "./tsconfig.node.json" }]
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 四、国际化配置
|
||
|
||
### 4.1 locales/index.ts
|
||
|
||
```typescript
|
||
import { createI18n } from 'vue-i18n'
|
||
import zhCN from './zh-CN'
|
||
import enUS from './en-US'
|
||
|
||
const messages = {
|
||
'zh-CN': zhCN,
|
||
'en-US': enUS,
|
||
}
|
||
|
||
const i18n = createI18n({
|
||
legacy: false,
|
||
locale: localStorage.getItem('lang') || import.meta.env.VITE_DEFAULT_LANG || 'zh-CN',
|
||
fallbackLocale: 'zh-CN',
|
||
messages,
|
||
})
|
||
|
||
export default i18n
|
||
```
|
||
|
||
### 4.2 locales/zh-CN.ts(节选)
|
||
|
||
```typescript
|
||
export default {
|
||
// 通用
|
||
common: {
|
||
home: '首页',
|
||
products: '产品中心',
|
||
cases: '案例展示',
|
||
about: '关于创高',
|
||
contact: '联系我们',
|
||
franchise: '招商加盟',
|
||
more: '了解更多',
|
||
viewAll: '查看全部',
|
||
submit: '提交',
|
||
cancel: '取消',
|
||
confirm: '确认',
|
||
loading: '加载中...',
|
||
noData: '暂无数据',
|
||
search: '搜索',
|
||
hotline: '服务热线',
|
||
},
|
||
|
||
// 首页
|
||
home: {
|
||
hero: {
|
||
title: '全屋定制 匠心之作',
|
||
subtitle: '专注高端家具定制,为品质生活而生',
|
||
btnContact: '立即咨询',
|
||
btnProducts: '查看产品',
|
||
},
|
||
services: {
|
||
title: '核心业务',
|
||
subtitle: '三大业务板块,全方位满足您的需求',
|
||
custom: {
|
||
title: '全屋定制',
|
||
desc: '从客厅到卧室,从厨房到书房,一站式全屋定制解决方案',
|
||
},
|
||
furniture: {
|
||
title: '成品家具',
|
||
desc: '精选全球优质原材料,打造舒适耐用的高端家具',
|
||
},
|
||
project: {
|
||
title: '工程项目',
|
||
desc: '酒店、办公、精装房等专业工程家具配套服务',
|
||
},
|
||
},
|
||
products: {
|
||
title: '热门产品',
|
||
subtitle: '精选系列,品质之选',
|
||
},
|
||
cases: {
|
||
title: '经典案例',
|
||
subtitle: '实景呈现,见证品质',
|
||
},
|
||
strength: {
|
||
title: '品牌实力',
|
||
subtitle: '二十年匠心积淀,铸就行业标杆',
|
||
years: '年行业经验',
|
||
factory: '㎡生产基地',
|
||
clients: '家服务客户',
|
||
patents: '项国家专利',
|
||
},
|
||
},
|
||
|
||
// 产品中心
|
||
product: {
|
||
title: '产品中心',
|
||
filter: {
|
||
all: '全部',
|
||
category: '分类',
|
||
style: '风格',
|
||
scene: '场景',
|
||
},
|
||
params: '产品参数',
|
||
scene: '场景展示',
|
||
inquiry: '立即咨询',
|
||
similar: '相关产品',
|
||
},
|
||
|
||
// 案例展示
|
||
case: {
|
||
title: '案例展示',
|
||
filter: '场景筛选',
|
||
area: '项目面积',
|
||
location: '项目地点',
|
||
date: '完工时间',
|
||
products: '使用产品',
|
||
},
|
||
|
||
// 关于创高
|
||
about: {
|
||
title: '关于创高',
|
||
story: '品牌故事',
|
||
factory: '工厂实力',
|
||
honor: '荣誉资质',
|
||
news: '新闻资讯',
|
||
},
|
||
|
||
// 联系我们
|
||
contact: {
|
||
title: '联系我们',
|
||
form: {
|
||
name: '您的姓名',
|
||
phone: '联系电话',
|
||
email: '电子邮箱',
|
||
company: '公司名称',
|
||
type: '咨询类型',
|
||
message: '留言内容',
|
||
placeholder: '请输入您的咨询内容...',
|
||
},
|
||
types: {
|
||
join: '加盟咨询',
|
||
cooperate: '工程合作',
|
||
feedback: '客户反馈',
|
||
other: '其他',
|
||
},
|
||
success: '提交成功,我们会尽快联系您!',
|
||
},
|
||
|
||
// 招商加盟
|
||
franchise: {
|
||
title: '招商加盟',
|
||
subtitle: '携手创高,共创未来',
|
||
policy: '加盟政策',
|
||
process: '加盟流程',
|
||
support: '六大支持',
|
||
contact: '立即咨询',
|
||
},
|
||
|
||
// 页脚
|
||
footer: {
|
||
navigation: '快速导航',
|
||
products: '产品中心',
|
||
service: '客户服务',
|
||
contact: '联系方式',
|
||
follow: '关注我们',
|
||
copyright: '© {year} 创高家具 版权所有',
|
||
icp: '粤ICP备xxxxxxxx号',
|
||
},
|
||
}
|
||
```
|
||
|
||
### 4.3 locales/en-US.ts(结构同上,英文翻译)
|
||
|
||
---
|
||
|
||
## 五、路由设计
|
||
|
||
### 5.1 router/portal.ts(前台路由)
|
||
|
||
```typescript
|
||
import type { RouteRecordRaw } from 'vue-router'
|
||
|
||
const portalRoutes: RouteRecordRaw[] = [
|
||
{
|
||
path: '/',
|
||
name: 'Home',
|
||
component: () => import('@/views/portal/HomeView.vue'),
|
||
meta: {
|
||
title: '首页',
|
||
titleEn: 'Home',
|
||
},
|
||
},
|
||
{
|
||
path: '/products',
|
||
name: 'Products',
|
||
component: () => import('@/views/portal/ProductView.vue'),
|
||
meta: {
|
||
title: '产品中心',
|
||
titleEn: 'Products',
|
||
},
|
||
},
|
||
{
|
||
path: '/products/:id',
|
||
name: 'ProductDetail',
|
||
component: () => import('@/views/portal/ProductDetailView.vue'),
|
||
meta: {
|
||
title: '产品详情',
|
||
titleEn: 'Product Detail',
|
||
},
|
||
},
|
||
{
|
||
path: '/cases',
|
||
name: 'Cases',
|
||
component: () => import('@/views/portal/CaseView.vue'),
|
||
meta: {
|
||
title: '案例展示',
|
||
titleEn: 'Cases',
|
||
},
|
||
},
|
||
{
|
||
path: '/cases/:id',
|
||
name: 'CaseDetail',
|
||
component: () => import('@/views/portal/CaseDetailView.vue'),
|
||
meta: {
|
||
title: '案例详情',
|
||
titleEn: 'Case Detail',
|
||
},
|
||
},
|
||
{
|
||
path: '/about',
|
||
name: 'About',
|
||
component: () => import('@/views/portal/AboutView.vue'),
|
||
meta: {
|
||
title: '关于创高',
|
||
titleEn: 'About Us',
|
||
},
|
||
},
|
||
{
|
||
path: '/news',
|
||
name: 'News',
|
||
component: () => import('@/views/portal/NewsView.vue'),
|
||
meta: {
|
||
title: '新闻资讯',
|
||
titleEn: 'News',
|
||
},
|
||
},
|
||
{
|
||
path: '/news/:id',
|
||
name: 'NewsDetail',
|
||
component: () => import('@/views/portal/NewsDetailView.vue'),
|
||
meta: {
|
||
title: '新闻详情',
|
||
titleEn: 'News Detail',
|
||
},
|
||
},
|
||
{
|
||
path: '/contact',
|
||
name: 'Contact',
|
||
component: () => import('@/views/portal/ContactView.vue'),
|
||
meta: {
|
||
title: '联系我们',
|
||
titleEn: 'Contact Us',
|
||
},
|
||
},
|
||
]
|
||
|
||
export default portalRoutes
|
||
```
|
||
|
||
### 5.2 router/admin.ts(后台路由)
|
||
|
||
```typescript
|
||
import type { RouteRecordRaw } from 'vue-router'
|
||
|
||
const adminRoutes: RouteRecordRaw[] = [
|
||
{
|
||
path: '/admin/login',
|
||
name: 'AdminLogin',
|
||
component: () => import('@/views/admin/LoginView.vue'),
|
||
meta: { public: true },
|
||
},
|
||
{
|
||
path: '/admin',
|
||
name: 'AdminLayout',
|
||
component: () => import('@/views/admin/Layout.vue'),
|
||
redirect: '/admin/dashboard',
|
||
children: [
|
||
{
|
||
path: 'dashboard',
|
||
name: 'AdminDashboard',
|
||
component: () => import('@/views/admin/DashboardView.vue'),
|
||
},
|
||
{
|
||
path: 'company/info',
|
||
name: 'CompanyInfo',
|
||
component: () => import('@/views/admin/company/InfoManage.vue'),
|
||
},
|
||
{
|
||
path: 'content/carousel',
|
||
name: 'CarouselManage',
|
||
component: () => import('@/views/admin/content/CarouselManage.vue'),
|
||
},
|
||
{
|
||
path: 'content/blocks',
|
||
name: 'PageBlockManage',
|
||
component: () => import('@/views/admin/content/PageBlockManage.vue'),
|
||
},
|
||
{
|
||
path: 'content/media',
|
||
name: 'MediaLibrary',
|
||
component: () => import('@/views/admin/content/MediaLibrary.vue'),
|
||
},
|
||
{
|
||
path: 'product/category',
|
||
name: 'ProductCategory',
|
||
component: () => import('@/views/admin/product/CategoryManage.vue'),
|
||
},
|
||
{
|
||
path: 'product/list',
|
||
name: 'ProductList',
|
||
component: () => import('@/views/admin/product/ProductManage.vue'),
|
||
},
|
||
{
|
||
path: 'case/category',
|
||
name: 'CaseCategory',
|
||
component: () => import('@/views/admin/case/CaseCategoryManage.vue'),
|
||
},
|
||
{
|
||
path: 'case/list',
|
||
name: 'CaseList',
|
||
component: () => import('@/views/admin/case/CaseManage.vue'),
|
||
},
|
||
{
|
||
path: 'news/category',
|
||
name: 'NewsCategory',
|
||
component: () => import('@/views/admin/news/NewsCategoryManage.vue'),
|
||
},
|
||
{
|
||
path: 'news/list',
|
||
name: 'NewsList',
|
||
component: () => import('@/views/admin/news/NewsManage.vue'),
|
||
},
|
||
{
|
||
path: 'message/list',
|
||
name: 'MessageList',
|
||
component: () => import('@/views/admin/message/MessageManage.vue'),
|
||
},
|
||
{
|
||
path: 'system/users',
|
||
name: 'UserManage',
|
||
component: () => import('@/views/admin/system/UserManage.vue'),
|
||
},
|
||
],
|
||
},
|
||
]
|
||
|
||
export default adminRoutes
|
||
```
|
||
|
||
---
|
||
|
||
## 六、核心组件设计
|
||
|
||
### 6.1 AppHeader.vue(顶部导航)
|
||
|
||
```vue
|
||
<template>
|
||
<header :class="['app-header', { 'is-scrolled': isScrolled, 'is-mobile': isMobile }]">
|
||
<div class="header-container">
|
||
<!-- Logo -->
|
||
<router-link to="/" class="logo">
|
||
<img :src="companyInfo.logo" :alt="companyInfo.brandName">
|
||
<span class="brand-name">{{ companyInfo.brandName }}</span>
|
||
</router-link>
|
||
|
||
<!-- 桌面导航 -->
|
||
<nav v-if="!isMobile" class="main-nav">
|
||
<router-link
|
||
v-for="item in navItems"
|
||
:key="item.path"
|
||
:to="item.path"
|
||
:class="['nav-item', { active: isActive(item.path) }]"
|
||
>
|
||
{{ $t(item.labelKey) }}
|
||
</router-link>
|
||
</nav>
|
||
|
||
<!-- 右侧工具 -->
|
||
<div class="header-tools">
|
||
<!-- 语言切换 -->
|
||
<LangSwitch />
|
||
|
||
<!-- 热线 -->
|
||
<a v-if="!isMobile" :href="`tel:${companyInfo.contactPhone}`" class="hotline">
|
||
<el-icon><Phone /></el-icon>
|
||
<span>{{ companyInfo.contactPhone }}</span>
|
||
</a>
|
||
|
||
<!-- 移动端菜单按钮 -->
|
||
<button v-if="isMobile" class="menu-toggle" @click="toggleMobileMenu">
|
||
<el-icon><Menu /></el-icon>
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 移动端菜单 -->
|
||
<MobileMenu v-model="mobileMenuVisible" :nav-items="navItems" />
|
||
</header>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, computed } from 'vue'
|
||
import { useRoute } from 'vue-router'
|
||
import { useI18n } from 'vue-i18n'
|
||
import { useScroll } from '@/composables/useScroll'
|
||
import { useDevice } from '@/composables/useDevice'
|
||
import LangSwitch from '@/components/common/LangSwitch.vue'
|
||
import MobileMenu from '@/components/layout/MobileMenu.vue'
|
||
|
||
const { t } = useI18n()
|
||
const route = useRoute()
|
||
const { isScrolled } = useScroll()
|
||
const { isMobile } = useDevice()
|
||
|
||
const mobileMenuVisible = ref(false)
|
||
|
||
const navItems = [
|
||
{ path: '/', labelKey: 'common.home' },
|
||
{ path: '/products', labelKey: 'common.products' },
|
||
{ path: '/cases', labelKey: 'common.cases' },
|
||
{ path: '/about', labelKey: 'common.about' },
|
||
{ path: '/contact', labelKey: 'common.contact' },
|
||
]
|
||
|
||
const isActive = (path: string) => {
|
||
if (path === '/') {
|
||
return route.path === '/'
|
||
}
|
||
return route.path.startsWith(path)
|
||
}
|
||
|
||
const toggleMobileMenu = () => {
|
||
mobileMenuVisible.value = !mobileMenuVisible.value
|
||
}
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.app-header {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
z-index: 1000;
|
||
height: 80px;
|
||
background: transparent;
|
||
transition: all 0.3s ease;
|
||
|
||
&.is-scrolled {
|
||
background: rgba(255, 255, 255, 0.95);
|
||
backdrop-filter: blur(10px);
|
||
box-shadow: 0 2px 20px rgba(0, 0, 0, 0.05);
|
||
height: 64px;
|
||
}
|
||
|
||
// 深色导航样式(首页首屏)
|
||
&:not(.is-scrolled) {
|
||
.nav-item {
|
||
color: #fff;
|
||
}
|
||
.brand-name {
|
||
color: #fff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.header-container {
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 0 24px;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.logo {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
text-decoration: none;
|
||
|
||
img {
|
||
height: 40px;
|
||
width: auto;
|
||
}
|
||
|
||
.brand-name {
|
||
font-size: 20px;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
}
|
||
}
|
||
|
||
.main-nav {
|
||
display: flex;
|
||
gap: 40px;
|
||
|
||
.nav-item {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
transition: color 0.3s;
|
||
position: relative;
|
||
|
||
&::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: -4px;
|
||
left: 0;
|
||
width: 0;
|
||
height: 2px;
|
||
background: var(--primary-color);
|
||
transition: width 0.3s;
|
||
}
|
||
|
||
&:hover,
|
||
&.active {
|
||
color: var(--primary-color);
|
||
|
||
&::after {
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.header-tools {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 24px;
|
||
}
|
||
|
||
.hotline {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
color: var(--text-primary);
|
||
text-decoration: none;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
|
||
.el-icon {
|
||
font-size: 16px;
|
||
color: var(--primary-color);
|
||
}
|
||
}
|
||
</style>
|
||
```
|
||
|
||
---
|
||
|
||
## 七、样式系统设计
|
||
|
||
### 7.1 variables.scss(CSS变量)
|
||
|
||
```scss
|
||
:root {
|
||
// 品牌色 - 深色底+金属质感
|
||
--primary-color: #C9A962; // 金色(金属质感)
|
||
--primary-light: #E8D5A3; // 浅金色
|
||
--primary-dark: #9A7B3D; // 深金色
|
||
|
||
// 背景色
|
||
--bg-dark: #1A1A1A; // 深色背景
|
||
--bg-dark-secondary: #242424; // 次级深色
|
||
--bg-light: #FFFFFF; // 浅色背景
|
||
--bg-gray: #F5F5F5; // 灰色背景
|
||
|
||
// 文字色
|
||
--text-primary: #1A1A1A; // 主文字(深色)
|
||
--text-secondary: #666666; // 次级文字
|
||
--text-light: #999999; // 辅助文字
|
||
--text-white: #FFFFFF; // 白色文字
|
||
|
||
// 边框
|
||
--border-color: #E8E8E8;
|
||
--border-dark: #333333;
|
||
|
||
// 阴影
|
||
--shadow-sm: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||
--shadow-md: 0 4px 16px rgba(0, 0, 0, 0.12);
|
||
--shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.16);
|
||
|
||
// 间距
|
||
--spacing-xs: 4px;
|
||
--spacing-sm: 8px;
|
||
--spacing-md: 16px;
|
||
--spacing-lg: 24px;
|
||
--spacing-xl: 32px;
|
||
--spacing-xxl: 48px;
|
||
|
||
// 圆角
|
||
--radius-sm: 4px;
|
||
--radius-md: 8px;
|
||
--radius-lg: 16px;
|
||
|
||
// 过渡
|
||
--transition-fast: 0.2s ease;
|
||
--transition-normal: 0.3s ease;
|
||
--transition-slow: 0.5s ease;
|
||
|
||
// 布局
|
||
--header-height: 80px;
|
||
--header-height-mobile: 64px;
|
||
--container-max-width: 1400px;
|
||
--container-padding: 24px;
|
||
}
|
||
|
||
// 响应式断点
|
||
$breakpoints: (
|
||
'xs': 576px,
|
||
'sm': 768px,
|
||
'md': 992px,
|
||
'lg': 1200px,
|
||
'xl': 1400px,
|
||
);
|
||
|
||
@mixin respond-to($breakpoint) {
|
||
$value: map-get($breakpoints, $breakpoint);
|
||
@media (max-width: $value) {
|
||
@content;
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 八、响应式断点策略
|
||
|
||
| 断点 | 宽度 | 布局调整 |
|
||
|------|------|----------|
|
||
| xl | ≥1400px | 最大容器宽度,完整导航 |
|
||
| lg | 1200-1399px | 略窄容器 |
|
||
| md | 992-1199px | 平板横屏,简化导航 |
|
||
| sm | 768-991px | 平板竖屏,移动菜单 |
|
||
| xs | <768px | 手机,单列布局 |
|
||
|
||
---
|
||
|
||
## 九、性能优化策略
|
||
|
||
### 9.1 图片处理
|
||
- 使用 `loading="lazy"` 懒加载
|
||
- 多尺寸图片:pc端 / mobile端分离
|
||
- WebP 格式优先,JPEG 降级
|
||
- 响应式图片 `srcset`
|
||
|
||
### 9.2 代码分割
|
||
- 路由级别懒加载
|
||
- 组件异步加载
|
||
- vendors chunk 分离
|
||
|
||
### 9.3 缓存策略
|
||
- 静态资源长期缓存(1年)
|
||
- API 响应 Cache-Control
|
||
- 图片 CDN 加速
|
||
|
||
---
|
||
|
||
## 十、开发命令
|
||
|
||
```bash
|
||
# 安装依赖
|
||
npm install
|
||
|
||
# 开发环境
|
||
npm run dev
|
||
|
||
# 生产构建
|
||
npm run build
|
||
|
||
# 预览生产构建
|
||
npm run preview
|
||
|
||
# 类型检查
|
||
npm run type-check
|
||
|
||
# 代码检查
|
||
npm run lint
|
||
```
|