[架构之美]手动搭建Vue3 前端项目框架
我们将手动创建一个完整的Vue前端项目,包含基础结构、路由、状态管理和UI组件。下面是实现方案:
一. 项目结构设计
1.1 项目架构设计
my-vue-project/
├── public/
│ ├── index.html
│ └── favicon.ico
├── src/
│ ├── assets/
│ │ └── logo.png
│ │ ├── main.css
│ ├── components/
│ │ ├── NavBar.vue
│ │ ├── AppFooter.vue
│ │ └── ProductCard.vue
│ ├── views/
│ │ ├── HomeView.vue
│ │ ├── AboutView.vue
│ │ ├── ProductsView.vue
│ │ └── ContactView.vue
│ ├── store/
│ │ └── index.js
│ ├── router/
│ │ └── index.js
│ ├── App.vue
│ └── main.js
├── package.json
└── README.md
1.2 环境准备
1.确保安装Node.js和npm
node -v
npm -v
本机之前已安装过,所以不再演示安装Node.js。
2.安装Vue CLL
npm install -g @vue/cli
3.创建Vue项目
使用Vue CLI创建一个新项目。在终端中运行
vue create my-vue-project
其中my-vue-project
是项目名称。
接下来,CLI会提示选择预设。可以选择默认的Vue 2或Vue 3,也可以手动选择特性。对于这个示例,我们选择Vue 3。
4.进入项目启动开发服务器
cd my-vue-project
npm run serve
#生产构建
npm run build
5.访问界面
项目启动后,可以在浏览器中访问http://localhost:8080
来查看Vue应用
6.安装其它工具(可选)
#安装Axios进行HTTP请求:
npm install axios
#安装Element Plus(UI库)
npm install element-plus
#配置ESLint和Prettier(可选)
npm install --save-dev eslint prettier eslint-plugin-vue eslint-config-prettier
二. 完整实现代码
以上是简单的将框架搭建起来,下面填充具体业务内容:
2.1 首先安装必要的依赖
npm install vue vue-router vuex
2.2 public/index.html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue 项目</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
</head>
<body>
<div id="app"></div>
</body>
</html>
2.3 src/main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
// 全局样式
import './assets/main.css'
createApp(App)
.use(store)
.use(router)
.mount('#app')
2.4 src/assets/main.css
:root {
--primary: #3498db;
--secondary: #2ecc71;
--dark: #2c3e50;
--light: #ecf0f1;
--danger: #e74c3c;
--warning: #f39c12;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f8f9fa;
color: #333;
}
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
}
.btn {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: 600;
transition: all 0.3s ease;
}
.btn-primary {
background-color: var(--primary);
color: white;
}
.btn-primary:hover {
background-color: #2980b9;
}
.btn-outline {
background: transparent;
border: 2px solid var(--primary);
color: var(--primary);
}
.btn-outline:hover {
background-color: var(--primary);
color: white;
}
.section {
padding: 80px 0;
}
.section-title {
text-align: center;
margin-bottom: 50px;
font-size: 2.5rem;
color: var(--dark);
position: relative;
}
.section-title::after {
content: '';
position: absolute;
bottom: -15px;
left: 50%;
transform: translateX(-50%);
width: 80px;
height: 4px;
background: var(--primary);
border-radius: 2px;
}
2.5 src/App.vue
<template>
<div id="app">
<NavBar />
<div class="main-content">
<router-view />
</div>
<Footer />
</div>
</template>
<script>
import NavBar from './components/NavBar.vue'
import Footer from './components/Footer.vue'
export default {
name: 'App',
components: {
NavBar,
Footer
}
}
</script>
<style scoped>
#app {
display: flex;
flex-direction: column;
min-height: 100vh;
}
.main-content {
flex: 1;
padding: 30px 0;
}
</style>
2.6 src/components/NavBar.vue
<template>
<nav class="navbar">
<div class="container">
<router-link to="/" class="logo">
<i class="fas fa-cube"></i>
<span>VueProject</span>
</router-link>
<div class="nav-links">
<router-link to="/">首页</router-link>
<router-link to="/about">关于</router-link>
<router-link to="/products">产品</router-link>
<router-link to="/contact">联系我们</router-link>
</div>
<div class="nav-actions">
<button class="btn btn-outline">
<i class="fas fa-user"></i> 登录
</button>
</div>
</div>
</nav>
</template>
<script>
export default {
name: 'NavBar'
}
</script>
<style scoped>
.navbar {
background-color: white;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
padding: 15px 0;
position: sticky;
top: 0;
z-index: 100;
}
.navbar .container {
display: flex;
justify-content: space-between;
align-items: center;
}
.logo {
display: flex;
align-items: center;
text-decoration: none;
font-size: 1.5rem;
font-weight: 700;
color: var(--dark);
}
.logo i {
margin-right: 10px;
color: var(--primary);
}
.nav-links {
display: flex;
gap: 30px;
}
.nav-links a {
text-decoration: none;
color: var(--dark);
font-weight: 500;
position: relative;
padding: 5px 0;
}
.nav-links a.router-link-exact-active {
color: var(--primary);
}
.nav-links a.router-link-exact-active::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 100%;
height: 3px;
background-color: var(--primary);
border-radius: 3px;
}
.nav-actions .btn {
padding: 8px 15px;
}
</style>
2.7 src/components/ AppFooter.vue
<template>
<footer class="footer">
<div class="container">
<div class="footer-content">
<div class="footer-section">
<h3>VueProject</h3>
<p>基于Vue.js构建的现代前端项目</p>
<div class="social-links">
<a href="#"><i class="fab fa-github"></i></a>
<a href="#"><i class="fab fa-twitter"></i></a>
<a href="#"><i class="fab fa-linkedin"></i></a>
<a href="#"><i class="fab fa-facebook"></i></a>
</div>
</div>
<div class="footer-section">
<h4>导航</h4>
<ul>
<li><router-link to="/">首页</router-link></li>
<li><router-link to="/about">关于我们</router-link></li>
<li><router-link to="/products">产品</router-link></li>
<li><router-link to="/contact">联系我们</router-link></li>
</ul>
</div>
<div class="footer-section">
<h4>联系方式</h4>
<ul>
<li><i class="fas fa-map-marker-alt"></i> 北京市朝阳区科技园</li>
<li><i class="fas fa-phone"></i> +86 123 4567 8910</li>
<li><i class="fas fa-envelope"></i> contact@vueproject.com</li>
</ul>
</div>
</div>
<div class="footer-bottom">
<p>© 2023 VueProject. 保留所有权利。</p>
</div>
</div>
</footer>
</template>
<script>
export default {
name: 'AppFooter'
}
</script>
<style scoped>
.footer {
background-color: var(--dark);
color: white;
padding: 60px 0 0;
}
.footer-content {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 40px;
margin-bottom: 40px;
}
.footer-section h3 {
font-size: 1.8rem;
margin-bottom: 20px;
}
.footer-section h4 {
font-size: 1.2rem;
margin-bottom: 20px;
position: relative;
padding-bottom: 10px;
}
.footer-section h4::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 50px;
height: 3px;
background: var(--primary);
}
.footer-section p {
margin-bottom: 20px;
line-height: 1.6;
}
.footer-section ul {
list-style: none;
}
.footer-section ul li {
margin-bottom: 15px;
display: flex;
align-items: center;
}
.footer-section ul li i {
margin-right: 10px;
color: var(--primary);
}
.footer-section a {
color: #ddd;
text-decoration: none;
transition: color 0.3s;
}
.footer-section a:hover {
color: var(--primary);
}
.social-links {
display: flex;
gap: 15px;
}
.social-links a {
display: flex;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
background: rgba(255,255,255,0.1);
border-radius: 50%;
transition: all 0.3s;
}
.social-links a:hover {
background: var(--primary);
transform: translateY(-5px);
}
.footer-bottom {
border-top: 1px solid rgba(255,255,255,0.1);
padding: 20px 0;
text-align: center;
font-size: 0.9rem;
color: #aaa;
}
</style>
2.8 src/router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import Products from '../views/Products.vue'
import Contact from '../views/Contact.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: About
},
{
path: '/products',
name: 'Products',
component: Products
},
{
path: '/contact',
name: 'Contact',
component: Contact
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
scrollBehavior() {
return { top: 0, behavior: 'smooth' }
}
})
export default router
2.9 src/store/index.js
import { createStore } from 'vuex'
export default createStore({
state: {
products: [
{ id: 1, name: 'Vue基础课程', price: 299, image: 'https://via.placeholder.com/300' },
{ id: 2, name: 'Vue高级实战', price: 499, image: 'https://via.placeholder.com/300' },
{ id: 3, name: 'Vue状态管理', price: 399, image: 'https://via.placeholder.com/300' },
{ id: 4, name: 'Vue组件开发', price: 349, image: 'https://via.placeholder.com/300' },
{ id: 5, name: 'Vue路由管理', price: 299, image: 'https://via.placeholder.com/300' },
{ id: 6, name: 'Vue性能优化', price: 449, image: 'https://via.placeholder.com/300' }
],
cart: []
},
mutations: {
addToCart(state, product) {
const existingItem = state.cart.find(item => item.id === product.id)
if (existingItem) {
existingItem.quantity++
} else {
state.cart.push({ ...product, quantity: 1 })
}
},
removeFromCart(state, productId) {
state.cart = state.cart.filter(item => item.id !== productId)
}
},
actions: {
addProductToCart({ commit }, product) {
commit('addToCart', product)
},
removeProductFromCart({ commit }, productId) {
commit('removeFromCart', productId)
}
},
getters: {
cartTotal(state) {
return state.cart.reduce((total, item) => total + (item.price * item.quantity), 0)
},
cartItemCount(state) {
return state.cart.reduce((count, item) => count + item.quantity, 0)
}
}
})
2.10 src/views/ HomeView.vue
<template>
<div class="home">
<!-- Hero Section -->
<section class="hero">
<div class="container">
<div class="hero-content">
<h1>构建现代化的 Vue 应用</h1>
<p>使用 Vue.js 创建响应式、高性能的单页面应用程序</p>
<div class="hero-buttons">
<router-link to="/products" class="btn btn-primary">浏览产品</router-link>
<router-link to="/about" class="btn btn-outline">了解更多</router-link>
</div>
</div>
</div>
</section>
<!-- Features Section -->
<section class="section features">
<div class="container">
<h2 class="section-title">为什么选择 Vue.js?</h2>
<div class="features-grid">
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-bolt"></i>
</div>
<h3>高性能</h3>
<p>虚拟DOM和高效的渲染机制确保应用流畅运行</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-layer-group"></i>
</div>
<h3>组件化</h3>
<p>可复用组件让开发更高效,维护更简单</p>
</div>
<div class="feature-card">
<div class="feature-icon">
<i class="fas fa-code"></i>
</div>
<h3>渐进式框架</h3>
<p>可根据项目需要逐步采用,灵活适配各种场景</p>
</div>
</div>
</div>
</section>
<!-- Products Preview -->
<section class="section products-preview">
<div class="container">
<h2 class="section-title">热门产品</h2>
<div class="products-grid">
<ProductCard
v-for="product in featuredProducts"
:key="product.id"
:product="product"
@add-to-cart="addToCart"
/>
</div>
<div class="text-center" style="margin-top: 30px;">
<router-link to="/products" class="btn btn-primary">查看所有产品</router-link>
</div>
</div>
</section>
</div>
</template>
<script>
import ProductCard from '@/components/ProductCard.vue'
import { mapState, mapActions } from 'vuex'
export default {
name: 'HomeView',
components: {
ProductCard
},
computed: {
...mapState(['products']),
featuredProducts() {
return this.products.slice(0, 3)
}
},
methods: {
...mapActions(['addProductToCart']),
addToCart(product) {
this.addProductToCart(product)
this.$notify({
title: '已添加到购物车',
text: `${product.name} 已添加到您的购物车`,
type: 'success'
})
}
}
}
</script>
<style scoped>
.hero {
background: linear-gradient(135deg, #3498db, #8e44ad);
color: white;
padding: 100px 0;
text-align: center;
}
.hero-content {
max-width: 800px;
margin: 0 auto;
}
.hero h1 {
font-size: 3.5rem;
margin-bottom: 20px;
text-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.hero p {
font-size: 1.5rem;
margin-bottom: 40px;
opacity: 0.9;
}
.hero-buttons {
display: flex;
justify-content: center;
gap: 20px;
}
.features-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
gap: 30px;
}
.feature-card {
background: white;
border-radius: 10px;
padding: 30px;
text-align: center;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
transition: transform 0.3s ease;
}
.feature-card:hover {
transform: translateY(-10px);
}
.feature-icon {
width: 80px;
height: 80px;
background: rgba(52, 152, 219, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin: 0 auto 20px;
color: var(--primary);
font-size: 2rem;
}
.feature-card h3 {
margin-bottom: 15px;
color: var(--dark);
}
.feature-card p {
color: #666;
line-height: 1.6;
}
.products-preview {
background-color: #f8f9fa;
}
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 30px;
}
</style>
2.11 src/components/ProductCard.vue
<template>
<div class="product-card">
<div class="product-image">
<img :src="product.image" :alt="product.name">
</div>
<div class="product-details">
<h3>{{ product.name }}</h3>
<p class="price">¥{{ product.price }}</p>
<p class="description">高质量Vue.js学习资源,提升你的前端开发技能</p>
<button class="btn btn-primary" @click="$emit('add-to-cart', product)">
<i class="fas fa-shopping-cart"></i> 加入购物车
</button>
</div>
</div>
</template>
<script>
export default {
name: 'ProductCard',
props: {
product: {
type: Object,
required: true
}
}
}
</script>
<style scoped>
.product-card {
background: white;
border-radius: 10px;
overflow: hidden;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
transition: all 0.3s ease;
}
.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
}
.product-image {
height: 200px;
overflow: hidden;
}
.product-image img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.product-card:hover .product-image img {
transform: scale(1.05);
}
.product-details {
padding: 20px;
}
.product-details h3 {
margin-bottom: 10px;
color: var(--dark);
}
.price {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
margin-bottom: 15px;
}
.description {
color: #666;
margin-bottom: 20px;
min-height: 60px;
}
</style>
2.12 src/views/ AboutView.vue
<template>
<div class="about">
<section class="hero">
<div class="container">
<h1>关于我们</h1>
<p>致力于提供高质量的Vue.js学习资源和开发工具</p>
</div>
</section>
<section class="section">
<div class="container">
<div class="about-content">
<div class="about-text">
<h2 class="section-title">我们的故事</h2>
<p>VueProject成立于2018年,由一群热爱前端技术的开发者共同创立。我们见证了Vue.js从一个小众框架发展成为今天的主流前端框架之一。</p>
<p>我们的使命是帮助开发者更好地掌握Vue.js技术栈,提高开发效率,构建更优秀的Web应用。</p>
<p>通过精心设计的课程、实用的工具和活跃的社区,我们为全球超过10万名开发者提供了学习资源和技术支持。</p>
<div class="stats">
<div class="stat">
<h3>10,000+</h3>
<p>活跃用户</p>
</div>
<div class="stat">
<h3>50+</h3>
<p>专业课程</p>
</div>
<div class="stat">
<h3>100+</h3>
<p>技术文章</p>
</div>
</div>
</div>
<div class="about-image">
<img src="https://via.placeholder.com/500" alt="团队照片">
</div>
</div>
</div>
</section>
<section class="section team">
<div class="container">
<h2 class="section-title">核心团队</h2>
<div class="team-grid">
<div class="team-member">
<img src="https://via.placeholder.com/200" alt="团队成员">
<h3>张明</h3>
<p>创始人 & 技术总监</p>
<p>10年前端开发经验,Vue.js官方文档贡献者</p>
</div>
<div class="team-member">
<img src="https://via.placeholder.com/200" alt="团队成员">
<h3>李华</h3>
<p>课程总监</p>
<p>资深前端讲师,开发多门Vue.js热门课程</p>
</div>
<div class="team-member">
<img src="https://via.placeholder.com/200" alt="团队成员">
<h3>王芳</h3>
<p>产品经理</p>
<p>负责产品规划和用户体验优化</p>
</div>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
name: 'About'
}
</script>
<style scoped>
.hero {
background: linear-gradient(135deg, #2ecc71, #3498db);
color: white;
padding: 100px 0;
text-align: center;
}
.hero h1 {
font-size: 3rem;
margin-bottom: 20px;
}
.about-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 50px;
align-items: center;
}
.about-image img {
width: 100%;
border-radius: 10px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
}
.stats {
display: flex;
gap: 30px;
margin-top: 30px;
}
.stat {
text-align: center;
}
.stat h3 {
font-size: 2rem;
color: var(--primary);
margin-bottom: 5px;
}
.team {
background-color: #f8f9fa;
}
.team-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 30px;
}
.team-member {
background: white;
border-radius: 10px;
padding: 30px;
text-align: center;
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
}
.team-member img {
width: 150px;
height: 150px;
border-radius: 50%;
object-fit: cover;
margin: 0 auto 20px;
}
.team-member h3 {
margin-bottom: 10px;
color: var(--dark);
}
.team-member p {
margin-bottom: 8px;
color: #666;
}
@media (max-width: 768px) {
.about-content {
grid-template-columns: 1fr;
}
.stats {
flex-direction: column;
gap: 20px;
}
}
</style>
2.13 src/views/ ProductsView.vue
<template>
<div class="products">
<section class="hero">
<div class="container">
<h1>产品中心</h1>
<p>探索我们的Vue.js学习资源和开发工具</p>
</div>
</section>
<section class="section">
<div class="container">
<div class="products-header">
<h2 class="section-title">所有产品</h2>
<div class="search-filter">
<input type="text" placeholder="搜索产品..." v-model="searchQuery">
<select v-model="sortOption">
<option value="default">默认排序</option>
<option value="price-asc">价格: 低到高</option>
<option value="price-desc">价格: 高到低</option>
</select>
</div>
</div>
<div class="products-grid">
<ProductCard
v-for="product in filteredProducts"
:key="product.id"
:product="product"
@add-to-cart="addToCart"
/>
</div>
</div>
</section>
</div>
</template>
<script>
import ProductCard from '@/components/ProductCard.vue'
import { mapState, mapActions } from 'vuex'
export default {
name: 'Products',
components: {
ProductCard
},
data() {
return {
searchQuery: '',
sortOption: 'default'
}
},
computed: {
...mapState(['products']),
filteredProducts() {
let result = [...this.products]
// 搜索过滤
if (this.searchQuery) {
const query = this.searchQuery.toLowerCase()
result = result.filter(product =>
product.name.toLowerCase().includes(query)
}
// 排序
if (this.sortOption === 'price-asc') {
result.sort((a, b) => a.price - b.price)
} else if (this.sortOption === 'price-desc') {
result.sort((a, b) => b.price - a.price)
}
return result
}
},
methods: {
...mapActions(['addProductToCart']),
addToCart(product) {
this.addProductToCart(product)
this.$notify({
title: '已添加到购物车',
text: `${product.name} 已添加到您的购物车`,
type: 'success'
})
}
}
}
</script>
<style scoped>
.hero {
background: linear-gradient(135deg, #f39c12, #e74c3c);
color: white;
padding: 100px 0;
text-align: center;
}
.products-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 40px;
flex-wrap: wrap;
}
.search-filter {
display: flex;
gap: 15px;
}
.search-filter input {
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 4px;
min-width: 250px;
}
.search-filter select {
padding: 10px 15px;
border: 1px solid #ddd;
border-radius: 4px;
background: white;
}
.products-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 30px;
}
@media (max-width: 768px) {
.products-header {
flex-direction: column;
align-items: flex-start;
gap: 20px;
}
.search-filter {
width: 100%;
flex-direction: column;
}
.search-filter input {
min-width: 100%;
}
}
</style>
2.14 src/views/ ContactView.vue
<template>
<div class="contact">
<section class="hero">
<div class="container">
<h1>联系我们</h1>
<p>我们很乐意听取您的反馈和建议</p>
</div>
</section>
<section class="section">
<div class="container">
<div class="contact-content">
<div class="contact-info">
<h2 class="section-title">联系方式</h2>
<p>如果您对我们的产品有任何疑问或建议,请随时联系我们。我们的团队将尽快回复您。</p>
<div class="info-item">
<i class="fas fa-map-marker-alt"></i>
<div>
<h3>地址</h3>
<p>北京市朝阳区科技园区88号</p>
</div>
</div>
<div class="info-item">
<i class="fas fa-phone"></i>
<div>
<h3>电话</h3>
<p>+86 123 4567 8910</p>
</div>
</div>
<div class="info-item">
<i class="fas fa-envelope"></i>
<div>
<h3>邮箱</h3>
<p>contact@vueproject.com</p>
</div>
</div>
<div class="info-item">
<i class="fas fa-clock"></i>
<div>
<h3>工作时间</h3>
<p>周一至周五: 9:00 - 18:00</p>
<p>周六至周日: 休息</p>
</div>
</div>
</div>
<div class="contact-form">
<h2 class="section-title">发送消息</h2>
<form @submit.prevent="submitForm">
<div class="form-group">
<input type="text" placeholder="您的姓名" v-model="form.name" required>
</div>
<div class="form-group">
<input type="email" placeholder="您的邮箱" v-model="form.email" required>
</div>
<div class="form-group">
<input type="text" placeholder="主题" v-model="form.subject" required>
</div>
<div class="form-group">
<textarea placeholder="您的消息" v-model="form.message" required></textarea>
</div>
<button type="submit" class="btn btn-primary">发送消息</button>
</form>
</div>
</div>
</div>
</section>
<!-- Map Section -->
<section class="map-section">
<div class="map-container">
<div class="map-placeholder">
<i class="fas fa-map-marked-alt"></i>
<p>北京市朝阳区科技园区88号</p>
</div>
</div>
</section>
</div>
</template>
<script>
export default {
name: 'Contact',
data() {
return {
form: {
name: '',
email: '',
subject: '',
message: ''
}
}
},
methods: {
submitForm() {
// 在实际应用中,这里会发送到后端API
this.$notify({
title: '消息已发送',
text: '感谢您的留言,我们会尽快回复您!',
type: 'success'
})
// 重置表单
this.form = {
name: '',
email: '',
subject: '',
message: ''
}
}
}
}
</script>
<style scoped>
.hero {
background: linear-gradient(135deg, #9b59b6, #3498db);
color: white;
padding: 100px 0;
text-align: center;
}
.contact-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 50px;
}
.contact-info .section-title,
.contact-form .section-title {
text-align: left;
margin-bottom: 30px;
}
.contact-info p {
margin-bottom: 30px;
line-height: 1.6;
}
.info-item {
display: flex;
gap: 20px;
margin-bottom: 25px;
}
.info-item i {
font-size: 1.5rem;
color: var(--primary);
margin-top: 5px;
}
.info-item h3 {
margin-bottom: 5px;
color: var(--dark);
}
.form-group {
margin-bottom: 20px;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 12px 15px;
border: 1px solid #ddd;
border-radius: 4px;
font-size: 1rem;
}
.form-group textarea {
min-height: 150px;
resize: vertical;
}
.map-section {
margin-top: 50px;
}
.map-container {
height: 400px;
background: #f8f9fa;
display: flex;
align-items: center;
justify-content: center;
}
.map-placeholder {
text-align: center;
color: var(--dark);
}
.map-placeholder i {
font-size: 4rem;
color: var(--primary);
margin-bottom: 20px;
}
@media (max-width: 768px) {
.contact-content {
grid-template-columns: 1fr;
}
}
</style>
三. 项目运行说明
3.1 安装依赖
npm install
3.2 启动开发服务器
npm run serve
3.3 构建生产版本
npm run build
3.4 项目框架界面
这个项目提供了完整的Vue前端应用基础,包括导航、页面路由、状态管理和响应式设计,可以轻松扩展为电子商务、博客或企业网站等各种类型的应用。
希望本教程对您有帮助,请点赞❤️收藏⭐关注支持!欢迎在评论区留言交流技术细节!