├── .gitignore ├── README.md ├── babel.config.js ├── package-lock.json ├── package.json ├── public ├── favicon.ico └── index.html ├── src ├── App.vue ├── assets │ ├── css │ │ ├── base.css │ │ ├── loginPage.css │ │ ├── normalize.css │ │ └── theme │ │ │ ├── base.css │ │ │ ├── display.css │ │ │ ├── el-affix.css │ │ │ ├── el-alert.css │ │ │ ├── el-aside.css │ │ │ ├── el-autocomplete.css │ │ │ ├── el-avatar.css │ │ │ ├── el-backtop.css │ │ │ ├── el-badge.css │ │ │ ├── el-breadcrumb-item.css │ │ │ ├── el-breadcrumb.css │ │ │ ├── el-button-group.css │ │ │ ├── el-button.css │ │ │ ├── el-calendar.css │ │ │ ├── el-card.css │ │ │ ├── el-carousel-item.css │ │ │ ├── el-carousel.css │ │ │ ├── el-cascader-panel.css │ │ │ ├── el-cascader.css │ │ │ ├── el-check-tag.css │ │ │ ├── el-checkbox-button.css │ │ │ ├── el-checkbox-group.css │ │ │ ├── el-checkbox.css │ │ │ ├── el-col.css │ │ │ ├── el-collapse-item.css │ │ │ ├── el-collapse-transition.css │ │ │ ├── el-collapse.css │ │ │ ├── el-color-picker.css │ │ │ ├── el-container.css │ │ │ ├── el-date-picker.css │ │ │ ├── el-descriptions-item.css │ │ │ ├── el-descriptions.css │ │ │ ├── el-dialog.css │ │ │ ├── el-divider.css │ │ │ ├── el-drawer.css │ │ │ ├── el-dropdown-item.css │ │ │ ├── el-dropdown-menu.css │ │ │ ├── el-dropdown.css │ │ │ ├── el-empty.css │ │ │ ├── el-footer.css │ │ │ ├── el-form-item.css │ │ │ ├── el-form.css │ │ │ ├── el-header.css │ │ │ ├── el-icon.css │ │ │ ├── el-image-viewer.css │ │ │ ├── el-image.css │ │ │ ├── el-infinite-scroll.css │ │ │ ├── el-infiniteScroll.css │ │ │ ├── el-input-number.css │ │ │ ├── el-input.css │ │ │ ├── el-link.css │ │ │ ├── el-loading.css │ │ │ ├── el-main.css │ │ │ ├── el-menu-item-group.css │ │ │ ├── el-menu-item.css │ │ │ ├── el-menu.css │ │ │ ├── el-message-box.css │ │ │ ├── el-message.css │ │ │ ├── el-notification.css │ │ │ ├── el-option-group.css │ │ │ ├── el-option.css │ │ │ ├── el-overlay.css │ │ │ ├── el-page-header.css │ │ │ ├── el-pagination.css │ │ │ ├── el-popconfirm.css │ │ │ ├── el-popover.css │ │ │ ├── el-popper.css │ │ │ ├── el-progress.css │ │ │ ├── el-radio-button.css │ │ │ ├── el-radio-group.css │ │ │ ├── el-radio.css │ │ │ ├── el-rate.css │ │ │ ├── el-reset.css │ │ │ ├── el-result.css │ │ │ ├── el-row.css │ │ │ ├── el-scrollbar.css │ │ │ ├── el-select-dropdown.css │ │ │ ├── el-select.css │ │ │ ├── el-skeleton-item.css │ │ │ ├── el-skeleton.css │ │ │ ├── el-slider.css │ │ │ ├── el-space.css │ │ │ ├── el-spinner.css │ │ │ ├── el-step.css │ │ │ ├── el-steps.css │ │ │ ├── el-submenu.css │ │ │ ├── el-switch.css │ │ │ ├── el-tab-pane.css │ │ │ ├── el-table-column.css │ │ │ ├── el-table.css │ │ │ ├── el-tabs.css │ │ │ ├── el-tag.css │ │ │ ├── el-time-picker.css │ │ │ ├── el-time-select.css │ │ │ ├── el-timeline-item.css │ │ │ ├── el-timeline.css │ │ │ ├── el-tooltip.css │ │ │ ├── el-transfer.css │ │ │ ├── el-tree.css │ │ │ ├── el-upload.css │ │ │ ├── el-virtual-list.css │ │ │ ├── fonts │ │ │ ├── element-icons.ttf │ │ │ └── element-icons.woff │ │ │ └── index.css │ ├── fonts │ │ ├── iconfont.css │ │ ├── iconfont.json │ │ ├── iconfont.ttf │ │ ├── iconfont.woff │ │ └── iconfont.woff2 │ └── images │ │ ├── banner (1).svg │ │ ├── banner (2).svg │ │ ├── banner (3).svg │ │ ├── banner (4).svg │ │ └── logo.png ├── components │ └── content │ │ ├── Captcha.vue │ │ ├── MyFooter.vue │ │ ├── MyHeader.vue │ │ ├── Order.vue │ │ ├── OrderList.vue │ │ └── OrderPage.vue ├── main.js ├── network │ ├── header.js │ ├── home.js │ ├── order.js │ ├── oss.js │ ├── request.js │ └── user.js ├── plugins │ └── element.js ├── router │ └── index.js ├── store │ ├── action.js │ ├── getters.js │ ├── index.js │ └── mutations.js ├── utils │ └── formRules.js └── views │ ├── Detail.vue │ ├── PublishOrder.vue │ ├── SearchResult.vue │ ├── UpdateOrder.vue │ ├── home │ ├── Home.vue │ └── HomeRecommend.vue │ ├── login │ ├── LoginForm.vue │ ├── LoginPage.vue │ └── RegisterForm.vue │ └── user-center │ ├── Complain.vue │ ├── MyHistory.vue │ ├── MyPublished.vue │ ├── MyReceived.vue │ ├── Security.vue │ ├── UserCenter.vue │ └── UserInfo.vue └── vue.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [toc] 2 | 3 | # 项目背景 4 | 5 | 大二下学期的小组项目作业,我们小组做的项目是校园跑腿系统,系统分为前台(面向用户)和后台(面向管理员)两个部分。前台系统是所有用户的系统入口(包括管理员),主要提供给普通用户发布、浏览和接受跑腿订单;后台系统则是面向管理员,用于帮助其掌握系统运营信息的。 6 | 7 | 由于我们组采用的是前后端分离的开发模式,且我负责的是系统前台部分的前端开发工作,所以本项目只涵盖**校园跑腿系统前台部分(面向用户)的前端代码**。 8 | 9 | 这是我第一次用Vue3做项目,可以说这个项目有很多不完美的地方需要改进,但是Vue3和Vue2相比少了很多可参考的经验,加上我们的时间比较紧迫,所以做到这个份上我也能原谅我自己。 10 | 11 | **复盘从这个项目中学到了什么,自己总结出一些方法论,再从不足中确定自己需要学习的方向**,这才是我认为重要的地方,也是写下这篇文档的初衷。 12 | 13 | 本文档作为复盘,只会进行一些总结,具体的项目细节在源码中含有大量注释,请结合食用。前端新手捡破烂,希望有大佬指点迷津~ 14 | 15 | ## 技术栈 16 | 17 | - 编程语言:JavaScript 18 | - 前端框架:Vue3.x 19 | - 路由工具:Vue Router 4.x 20 | - 状态管理:Vuex 4.x 21 | - UI组件库:Element Plus 22 | - HTTP库:axios 23 | 24 | ## 项目展示 25 | 26 | 由于我们组的部署工作只完成了前端部分,所以不能线上预览系统,项目展示部分只能之后再更新。 27 | 28 | 在此简要概述一下前台各页面功能: 29 | 30 | - 首页:展示随机六条订单(提供换一组功能)、搜索订单、个人中心入口、发布订单 31 | - 个人中心页:修改个人信息、修改密码、提交投诉反馈、查看我发布的/我接受的/历史浏览订单 32 | - 订单详情页:接单、修改订单状态、修改订单信息(根据订单和用户的状态展示不同信息) 33 | 34 | 未实现:我的投诉、线上支付。 35 | 36 | # 如何规划项目结构 37 | 38 | ``` 39 | |- /assets 资源文件夹 40 | |- /css 41 | |- /fonts 字体图标 42 | |- /images 存放网页结构相关图片 43 | |- /components 可复用组件文件夹 44 | |- /common 存放可被其他项目复用的组件 45 | |- /content 存放本项目多处复用的组件 46 | |- /router 路由配置 47 | |- index.js 48 | |- /plugins 插件 49 | |- /utils 工具文件夹 50 | |- /store vuex配置,项目简单所以没有分模块配置 51 | |- index.js 52 | |- getter.js 53 | |- mutations.js 54 | |- actions.js 55 | |- /views 页面组件文件夹,内部二级文件夹以页面来划分,再存放子组件 56 | |- /network 封装网络请求 57 | |- request.js 通用网络请求配置,目录下子文件都要导入 58 | |- home.js/cart.js ... 按照模块划分网络请求配置文件 59 | ``` 60 | 61 | # 准备工作 62 | 63 | ## 基础样式 64 | 65 | 1. 引入[normalize.css](https://github.com/necolas/normalize.css),让样式兼容各个不同的浏览器 66 | 2. 完成`base.css`,定义一些基础共用样式并引入进`main.js`或`App.vue` 67 | 3. 如果有的话,引入组件库的主题样式 68 | 69 | ## 相关配置 70 | 71 | ### Webpack配置 72 | 73 | 1. 目前只接触了别名配置:可以在Webpack配置中指定文件夹的别名,方便修改路径。 74 | 75 | ### axios配置 76 | 77 | 1. 通过`axios.create({...})`来新建一个axios实例并配置`baseURL`等属性,通过实例来封装网络请求 78 | 2. 通过`instance.interceptors.request/response.use(config =>{...}`来配置请求/响应拦截器 79 | - 请求拦截器可以判断用户的登录权限并进行处理,有登陆权限就让请求头携带token 80 | - 响应拦截器可以先判断响应的`status`(200则调用接口成功),再通过`code`去判断一层业务的逻辑是否合法,在此处可以统一对后端返回的错误信息进行提示 81 | 82 | ### 路由配置 83 | 84 | 1. 定义全局前置守卫,可以判断用户是否有跳转路由的权限(没登陆就跳去登录页) 85 | 86 | > 注意区分此处的判断用户登录权限和axios中的,一个是页面跳转的权限一个是发送网络请求的权限 87 | 88 | 2. 定义全局后置守卫,可以利用后置守卫修改document的title,注意后置守卫没有`then`方法 89 | 90 | ### 定义Vuex全局状态管理数据 91 | 92 | 1. 存放一些全局公用数据,比如订单种类、订单状态对象等等 93 | 2. 注意存放用户信息时,刷新会导致Vuex里的信息丢失 94 | 95 | ### 跨域代理 96 | 97 | 1. 在`vue.config.js`中利用代理对象,把请求交给后端代理来解决跨域问题 98 | 99 | # 如何划分页面逻辑 100 | 101 | > 刚开始做项目时,打开一个页面总会有不知道从哪里开始下手的感觉,做完以后就有了一点自己的小心得,在此把心得进行总结。 102 | 103 | 1. **第一步,在`