# vue3.0小兔鲜电商 **Repository Path**: yuefengzhao/rabbit-vue3-ts ## Basic Information - **Project Name**: vue3.0小兔鲜电商 - **Description**: - 电商发展十余年,是个成熟的模式,小兔鲜儿是B2C电商平台,综合品类平台。参考网易严选 - 平台理念:(品质)新鲜、(价格)亲民、(物流)快捷。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 9 - **Forks**: 1 - **Created**: 2022-07-05 - **Last Updated**: 2025-05-12 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 项目介绍 ## 项目介绍 > 了解下项目的基本功能。 - 电商发展十余年,是个成熟的模式,小兔鲜儿是B2C电商平台,综合品类平台。参考[网易严选](http://you.163.com/) - 平台理念:(品质)新鲜、(价格)亲民、(物流)快捷。 > 功能模块 - 首页模块:顶部通栏,吸顶导航,网站头部,左侧分类,轮播图,新鲜好物,人气推荐,热门品牌,分类商品推荐,专题推荐,网站底部。 - 一级分类:面包屑,轮播图,全部二级分类,二级分类推荐商品。 - 二级分类:筛选区域,排序功能,商品列表,无限加载。 - 商品详情:商品图片展示,基本信息展示,配送城市选择,SKU选择,库存选择,商品详情展示,商品评价展示,24小时热销,相关专题,加入购物车。 - 购物车 - 头部购物车:展示商品数量和列表,删除商品,跳转购物车页面。 - 购物车页面:购物车商品展示,选择商品,修改数量,修改商品规格,价格计算,跳转下单 - 登录模块:表单校验,账户密码登录,手机号登录,第三方登录,绑定手机,完善信息 - 填写订单:订单商品展示,收货地址选择,收货地址修改,支付方式选择,生成订单。 - 进行支付:订单信息展示,跳转支付网关,提示正在支付,等待支付结果,跳转支付成功页面。 - 个人中心 - 中心首页:展示个人信息,近期收藏商品,近期足迹,猜你喜欢 - 订单管理:全部订单,待付款,待发货,待收货,待评价,已完成,已取消。立即付款,取消订单,确认收货,删除订单,查看物流。 - 订单详情:订单状态,订单进度,详细信息。 总结:完成电商支付闭环。 学完小兔鲜 => 之前负责模块登录/注册?(没啥含金量) , 现在, 商品管理 购物车 支付 订单管理 ## 配套资源 > 目的:了解真实企业开发都会有哪些配套资源。 开发配套: - [原型稿](https://app.mockplus.cn/run/prototype/QO7BCWlUKB/IWlj1dabSw/c-f4gj1smb0?ha=1&ps=1) - [接口文档](./api.html) - [参照案例](http://erabbit.itheima.net/#/) ``` 账号:jfjbwb4477@sandbox.com 密码:111111 ``` 总结: - 在学习开发阶段使用本地API文档与vue3.0版本样例。 ## 使用技术 > 目的:概述下项目会运用到的技术点。 项目基于vue技术来实现,大概会使用以下技术: - vue3.0 (使用组合api的方式来开发) - vite开发 - axios (请求接口) - vue-router (单页路由) - pinia (状态管理) - normalize.css (初始化样式) - @vueuse/core (组合api常用工具库) - 算法 [Power Set](https://github.com/zhousg/javascript-algorithms/blob/master/src/algorithms/sets/power-set) - dayjs (日期处理) - vee-validate (表单校验) **重点:** 电商常见业务和解决方案,掌握基于vue3.0的组合api开发模式。 **说明:** 由于前台项目,没有合适的UI组件库(没有适用vue3.0的ui库),不会使用组件库。 - 轮播图组件 - 面包屑组件 - 查看更多组件 - 骨架屏组件 - 复选框组件 - 单选框组件 - 对话框组件 - 消息提示组件 函数调用 - 消息确认组件 函数调用 - 分页组件 - 步骤条组件 - 时间线组件 - 标签页组件 - 城市选择组件 总结:基于vue3.0的技术栈,大量的组件封装。 # 项目起步 ## 创建项目 **目标**:使用 vite 初始化小兔鲜项目 **核心步骤:** - 使用 vite 初始化项目 ```bash yarn create vite rabbit-vue3-ts --template vue-ts ``` - 安装依赖包, 需要进入到`rabbit-vue3-ts`项目中 ``` yarn ``` - 启动项目 ``` yarn dev ``` ## 目录调整 **目标:**能够调整项目的目录结构,规范开发环境。 **修改文件** - `main.ts`不需要修改 - `App.vue`删除无用的内容 ```vue ``` **删除文件** - `src/components/HelloWorld.vue` HelloWorld 组件 - `src/assets/logo.png` vue 默认的 logo **新增文件夹** - `utils` 用于存放工具相关 - `assets/images` 用于存放图片相关 - `assets/styles` 用于存放样式相关 - `router` 用于存放路由相关 - `store`用于存放数据相关 - `views`用于存放页面级别的组件 - `types`用于存放 ts 的公共类型 ## 使用 git 管理项目 **目标:**能够使用 git 管理项目,并且能够将代码上传到码云 **核心步骤:** - 初始化项目:`git init` - 将代码添加到暂存区: `git add .` - 提交代码 `git commit -m '初始化提交'` - 在[码云](https://gitee.com/)上创建项目`rabbit-vue3-ts`,选择开源 - 设置仓库别名 ```bash git remote add origin https://gitee.com/jepsonpp/rabbit-pc-ts-93.git ``` - 推送到远程仓库 ```bash git push -u origin master ``` ## axios 封装 **目的:**基于 axios 封装一个请求工具,调用接口时使用。 - 安装 axios ```bash yarn add axios ``` - 新建 `src/utils/request.ts` 模块,代码如下 ```js import axios from 'axios' // 备用接口地址: http://pcapi-xiaotuxian-front-devtest.itheima.net/ const instance = axios.create({ baseURL: 'http://pcapi-xiaotuxian-front.itheima.net/', timeout: 5000 }) // 添加请求拦截器 instance.interceptors.request.use( function (config) { // 在发送请求之前做些什么 return config }, function (error) { // 对请求错误做些什么 return Promise.reject(error) } ) // 添加响应拦截器 instance.interceptors.response.use( function (response) { return response }, function (error) { // 对响应错误做点什么 return Promise.reject(error) } ) export default instance ``` - 在`App.vue`文件中进行测试 ```vue ``` ## 配置路径别名 **目标:**能够配置@路径别名,方便导入模块 **核心步骤:** - 在`vite.config.ts`中增加配置 ```js import { defineConfig } from 'vite' import vue from '@vitejs/plugin-vue' const path = require('path') // https://vitejs.dev/config/ export default defineConfig({ plugins: [vue()], resolve: { alias: { '@': path.resolve(__dirname, './src') } } }) ``` 需要安装node的类型声明文件 ``` yarn add @types/node -D ``` - 修改`tsconfig.json`,增加如下配置 ```json { "compilerOptions": { // ... "baseUrl": ".", "paths": { "@/*": ["src/*"] } } } ``` **注意:修改完 vite.config.ts 文件需要重启** ## 样式处理-less 变量与 mixins **目标:** 能够使用 less 变量定义项目中常用的颜色,使用 mixins 定义项目中常用的样式 **核心步骤** - 变量 `src/assets/styles/variables.less` ```less // 主题 @xtxColor: #27ba9b; // 辅助 @helpColor: #e26237; // 成功 @sucColor: #1dc779; // 警告 @warnColor: #ffb302; // 价格 @priceColor: #cf4444; ``` - 混入 `src/assets/styles/mixins.less` ```less // 鼠标经过上移阴影动画 .hoverShadow () { transition: all 0.5s; &:hover { transform: translate3d(0, -3px, 0); box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2); } } ``` - 在 app.vue 中测试 ```less ``` - 需要安装less的依赖包 ``` yarn add less -D ``` - 问题:`variables.less`和`mixins.less`两个 less 文件是需要在多个组件中去使用的。但是如果组件中不导入,直接使用会报错。 ## 样式处理-自动导入 **目标:** 能够使用`style-resoures-loader`自动导入项目中的`less变量`和`mixins` **参考文档:**https://cn.vitejs.dev/config/#css-preprocessoroptions **核心步骤:** - 修改`vite.config.ts文件`,增加内容 ```js export default defineConfig({ css: { preprocessorOptions: { less: { additionalData: ` @import "@/assets/styles/variables.less"; @import "@/assets/styles/mixins.less"; ` } } } }) ``` - 修改 app.vue 中的代码 ```less ``` - 重启服务,查看效果 ## 样式处理-重置样式 **目标:**能够使用功能[normalize.css](https://github.com/necolas/normalize.css)重置项目中的样式,normalize.css 是 CSS 重置的现代替代方法 **核心步骤** - 安装 normalize.css 样式重置的库, 将浏览器中的一些默认样式, 进行重置统一 ```js yarn add normalize.css ``` - 使用 normalize.css `main.js` 导入 `normalize.css` 即可。 ```diff import { createApp } from 'vue' import App from './App.vue' + import 'normalize.css' createApp(App).mount('#app') ``` - 在 app.vue 中测试 ```jsx ``` **通过效果会发现,h1 ul 等样式还是保留的,但是不一致的样式已经被重置了** ## 样式处理-公共样式 **目标:**能够给项目设置通用的样式。虽然有了重置样式,但是项目中依旧需要通用样式, **核心步骤** - 新建文件 `src/assets/styles/common.less` ```less // 按照网站自己的需求,提供公用的样式 * { box-sizing: border-box; } html { height: 100%; font-size: 14px; } body { height: 100%; color: #333; min-width: 1240px; font: 1em/1.4 'Microsoft Yahei', 'PingFang SC', 'Avenir', 'Segoe UI', 'Hiragino Sans GB', 'STHeiti', 'Microsoft Sans Serif', 'WenQuanYi Micro Hei', sans-serif; } ul, h1, h3, h4, p, dl, dd { padding: 0; margin: 0; } a { text-decoration: none; color: #333; outline: none; } i { font-style: normal; } input[type='text'], input[type='search'], input[type='password'], input[type='checkbox'] { padding: 0; outline: none; border: none; -webkit-appearance: none; &::placeholder { color: #ccc; } } img { max-width: 100%; max-height: 100%; vertical-align: middle; // background: #ebebeb; } ul { list-style: none; } #app { background: #f5f5f5; // 不能选中文字 user-select: none; } .container { width: 1240px; margin: 0 auto; position: relative; } // 一行省略 .ellipsis { white-space: nowrap; text-overflow: ellipsis; overflow: hidden; } // 二行省略 .ellipsis-2 { word-break: break-all; text-overflow: ellipsis; display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; } .fl { float: left; } .fr { float: right; } .clearfix:after { content: '.'; display: block; visibility: hidden; height: 0; line-height: 0; clear: both; } // 闪动画 .shan { &::after { content: ''; position: absolute; animation: shan 1.5s ease 0s infinite; top: 0; width: 30%; height: 100%; background: linear-gradient( to left, rgba(255, 255, 255, 0) 0, rgba(255, 255, 255, 0.3) 50%, rgba(255, 255, 255, 0) 100% ); transform: skewX(-45deg); } } @keyframes shan { 0% { left: -100%; } 100% { left: 120%; } } // 离开淡出动画 .fade { &-leave { &-active { position: absolute; width: 100%; transition: opacity 0.5s 0.2s; z-index: 1; } &-to { opacity: 0; } } } // 1. 离开,透明度 1---->0 位移 0---->30 // 2. 进入,透明度 0---->1 位移 30---->0 // 执行顺序,先离开再进入 .pop { &-leave { &-from { opacity: 1; transform: none; } &-active { transition: all 0.5s; } &-to { opacity: 0; transform: translateX(20px); } } &-enter { &-from { opacity: 0; transform: translateX(20px); } &-active { transition: all 0.5s; } &-to { opacity: 1; transform: none; } } } // 表单 .xtx-form { padding: 50px 0; &-item { display: flex; align-items: center; width: 700px; margin: 0 auto; padding-bottom: 25px; .label { width: 180px; padding-right: 10px; text-align: right; color: #999; ~ .field { margin-left: 0; } } .field { width: 320px; height: 50px; position: relative; margin-left: 190px; .icon { position: absolute; left: 0; top: 0; width: 40px; height: 50px; text-align: center; line-height: 50px; color: #999; ~ .input { padding-left: 40px; } } .input { border: 1px solid #e4e4e4; width: 320px; height: 50px; line-height: 50px; padding: 0 10px; &.err { border-color: @priceColor; } &:focus, &:active { border-color: @xtxColor; } } } .error { width: 180px; padding-left: 10px; color: @priceColor; } } .submit { width: 320px; height: 50px; border-radius: 4px; background: @xtxColor; height: 50px; line-height: 50px; text-align: center; font-size: 16px; color: #fff; display: block; margin: 0 auto; } } ``` - 在 `main.js` 导入即可。 ```diff import { createApp } from 'vue' import App from './App.vue' import 'normalize.css' + import '@/assets/styles/common.less' createApp(App).mount('#app') ```