一、项目介绍
本项目源码下载https://download.csdn.net/download/lucky_fang/93540985
项目使用 Vue3 + Vite + Element Plus + Pinia + Vue Router + Axios 等搭建,每个页面通过 Pinia 实现数据同步与页面动态交互,同时也支持通过真实后端 API 请求获取真实数据,在后面会详细介绍如何使用。
项目包含登录页、注册页、首页、购物车、订单管理、商品详情、订单确认和个人中心等页面。
项目同时采用组件来布局整个框架,顶部菜单栏 + 具体页面功能展示容器 + 底部,每个页面是独立的,菜单布局作为共用的结构来设计。
二、项目概述
1.登录页
项目模拟登录,账密 admin/123456。
登录通过 Pinia 实现登录、退出逻辑。
import { defineStore } from 'pinia' import { ref, computed } from 'vue' import { useRouter } from 'vue-router' import { ElMessage } from 'element-plus' export interface User { id: number username: string email: string avatar?: string token: string } export const useUserStore = defineStore('user', () => { const user = ref<User | null>(null) const token = ref<string>('') const router = useRouter() // 是否已登录 const isLoggedIn = computed(() => !!token.value) // 模拟登录 async function login(username: string, password: string) { // 模拟API请求延迟 await new Promise(resolve => setTimeout(resolve, 1000)) // 简单模拟验证 if (username === 'admin' && password === '123456') { const mockUser: User = { id: 1, username: 'Admin', email: 'admin@vuemall.com', avatar: 'https://picsum.photos/100/100?random=user1', token: 'mock-jwt-token-' + Date.now() } user.value = mockUser token.value = mockUser.token localStorage.setItem('token', mockUser.token) localStorage.setItem('user', JSON.stringify(mockUser)) ElMessage.success('登录成功') return true } else { ElMessage.error('用户名或密码错误 (提示: admin/123456)') return false } } // 模拟注册 async function register(username: string, email: string, password: string) { await new Promise(resolve => setTimeout(resolve, 1000)) // 模拟注册成功 ElMessage.success('注册成功,请登录') return true } // 退出登录 function logout() { user.value = null token.value = '' localStorage.removeItem('token') localStorage.removeItem('user') router.push('/login') ElMessage.info('已退出登录') } // 初始化加载本地存储 function initAuth() { const localToken = localStorage.getItem('token') const localUser = localStorage.getItem('user') if (localToken && localUser) { token.value = localToken user.value = JSON.parse(localUser) } } return { user, token, isLoggedIn, login, register, logout, initAuth } })这段代码展示了登录流程的核心逻辑,下面结合代码说明 Pinia store、token 存储和路由守卫是如何配合实现登录拦截的。
1. Pinia store 的作用
在useUserStore中,通过ref定义了user和token两个响应式状态,并用computed派生isLoggedIn判断是否已登录。登录成功后,login方法会把用户信息和 token 写入这两个状态,实现全局数据同步;退出时logout方法会清空状态并跳转到登录页。这样任何组件都能通过 store 读取登录状态,无需层层传递 props。
2. token 的存储方式
登录成功后,代码通过localStorage.setItem('token', mockUser.token)和localStorage.setItem('user', JSON.stringify(mockUser))将 token 和用户信息持久化到浏览器本地存储。刷新页面后,initAuth方法会从localStorage中读取并恢复登录状态,保证用户刷新后仍保持登录。退出登录时则通过localStorage.removeItem清除这些数据。
3. 路由守卫如何配合实现登录拦截
在路由配置中,需要登录才能访问的页面(如/checkout、/orders、/profile)通过meta: { requiresAuth: true }标记。全局前置守卫router.beforeEach中,先获取useUserStore实例,再判断目标路由是否设置了requiresAuth且当前未登录:若未登录则重定向到/login并携带redirect参数,登录成功后跳回原页面;若已登录访问登录页或注册页,则直接跳转到首页。这样 Pinia 的响应式状态与路由守卫配合,实现了完整的登录拦截闭环。
2.注册页
3.首页
登录成功后跳转首页,首页包含轮播图、商品列表,可把商品添加到购物车。
添加商品到购物车:
可点击加载更多,模拟请求后端动态加载更多数据:
3.商品详情
商品详情页可放大镜查看商品图片,可添加购物车和下单,包含商品详情介绍、规格参数和评论列表。
4.购物车
可在购物车管理商品,修改商品数量、删除商品和对商品进行结算。
5.确认订单
可新增、编辑、删除收货地址、选择收货地址,设置默认收货地址等;
6.订单管理
提交订单后跳转到个人中心-我的订单,可搜索订单、切换类型和查看订单详情。
7.个人中心
基本信息管理
账户安全
收货地址
三、项目源码
项目采用 Vue3 + Vite 构建,整体目录结构清晰,各模块职责分明。下面以src目录为核心,展示项目的主要目录组织方式。
src ├── views # 页面组件,按业务模块划分 │ ├── Home.vue # 首页(轮播图、商品列表) │ ├── Login.vue # 登录页 │ ├── Register.vue # 注册页 │ ├── ProductDetail.vue # 商品详情页 │ ├── Cart.vue # 购物车页 │ ├── Checkout.vue # 确认订单页 │ ├── Orders.vue # 订单管理页 │ └── ProfilePc.vue # 个人中心页 ├── stores # Pinia 状态管理 │ └── user.ts # 用户登录态、token 等全局状态 ├── router # Vue Router 路由配置 │ └── index.ts # 路由表与全局前置守卫 ├── components # 通用组件(顶部菜单栏、底部等共用结构) ├── api # 接口请求封装(Axios 请求层) ├── assets # 静态资源(图片、样式等) ├── utils # 工具函数 ├── App.vue # 根组件 └── main.ts # 应用入口文件各目录职责说明:
- views:存放页面级组件,每个页面独立成文件,对应路由中的一个页面,负责页面布局与业务交互。
- stores:基于 Pinia 管理全局状态,如用户登录信息、token 等,实现跨页面数据同步。
- router:集中配置路由表,并通过
meta.requiresAuth与全局守卫实现登录拦截。 - components:存放可复用的通用组件,如顶部菜单栏、底部等,作为整体框架的共用结构。
- api:封装 Axios 请求,统一管理后端接口调用,便于维护与复用。
- assets:存放图片、全局样式等静态资源。
- utils:存放通用工具函数,供各模块调用。
1.vscode开发工具
2.项目依赖
package.json
{ "name": "my-shop-vue", "private": true, "version": "0.0.0", "type": "module", "scripts": { "dev": "vite", "build": "vue-tsc -b && vite build", "preview": "vite preview" }, "dependencies": { "@element-plus/icons-vue": "^2.3.2", "element-plus": "^2.14.6", "vue": "^3.5.42", "axios": "^1.6.0", "pinia": "^2.1.0", "vue-router": "^4.3.0" }, "devDependencies": { "@types/node": "^24.13.3", "@vitejs/plugin-vue": "^6.0.8", "@vue/tsconfig": "^0.9.1", "typescript": "~6.0.2", "unplugin-auto-import": "^21.1.0", "unplugin-vue-components": "^32.1.0", "vite": "^8.3.0", "vue-tsc": "^3.3.11", "sass": "^1.70.0" } }3.项目命令
安装依赖(初次成功执行一次即可):npm install
启动项目:npm run dev
4.项目路由配置
可通过requiresAuth属性指定页面是否免登录访问。
import { createRouter, createWebHistory } from 'vue-router' import Home from '../views/Home.vue' import ProductDetail from '../views/ProductDetail.vue' import Cart from '../views/Cart.vue' import Checkout from '../views/Checkout.vue' import Orders from '../views/Orders.vue' import Login from '../views/Login.vue' import Register from '../views/Register.vue' import Profile from '../views/ProfilePc.vue' import { useUserStore } from '../stores/user' const router = createRouter({ history: createWebHistory(), routes: [ { path: '/', name: 'Home', component: Home }, { path: '/product/:id', name: 'ProductDetail', component: ProductDetail, props: true }, { path: '/cart', name: 'Cart', component: Cart }, { path: '/checkout', name: 'Checkout', component: Checkout, meta: { requiresAuth: true } }, { path: '/orders', name: 'Orders', component: Orders, meta: { requiresAuth: true } }, { path: '/profile/:activeMenu', name: 'Profile', component: Profile, meta: { requiresAuth: true }, props: true }, { path: '/login', name: 'Login', component: Login }, { path: '/register', name: 'Register', component: Register } ] }) router.beforeEach((to, from, next) => { const userStore = useUserStore() if (to.meta.requiresAuth && !userStore.isLoggedIn) { next({ path: '/login', query: { redirect: to.fullPath } }) } else { if ((to.path === '/login' || to.path === '/register') && userStore.isLoggedIn) { next('/') } else { next() } } }) export default router四、总结
项目页面完整,后续可能将不断升级,完善其他页面。
关注作者,及时了解更多好项目!
更多优质项目请看作者主页!
获取源码或如需帮助,可通过博客后面名片+作者即可!
本项目源码下载https://download.csdn.net/download/lucky_fang/93540985