part1
part2
part3
part4
part5
part6(本页)
7.1 用户地址簿相关功能
7.2 菜品展示
7.3 购物车
7.4 下单
<div class="divContent">
<div class="divItem" v-for="(item,index) in addressList" :key="index" @click.capture="itemClick(item)">
<div class="divAddress">
<span :class="{spanCompany:item.label === '公司',spanHome:item.label === '家',spanSchool:item.label === '学校'}">{{item.label}}span>
{{item.detail}}
div>
<div class="divUserPhone">
<span>{{item.consignee}}span>
<span>{{item.sex === '0' ? '女士' : '先生'}}span>
<span>{{item.phone}}span>
div>
<img src="./../images/edit.png" @click.stop.prevent="toAddressEditPage(item)"/>
<div class="divSplit">div>
<div class="divDefault" >
<img src="./../images/checked_true.png" v-if="item.isDefault === 1">
<img src="./../images/checked_false.png" @click.stop.prevent="setDefaultAddress(item)" v-else>设为默认地址
div>
div>
div>
<div class="divBottom" @click="toAddressCreatePage">+ 添加收货地址div>
div>
页面效果如下:
点击添加收获地址之后触发函数发送跳转,类似的编辑也是这样
toAddressCreatePage(){
window.requestAnimationFrame(()=>{
window.location.href= '/front/page/address-edit.html'
})
},
async initData(){
/*
window.location.search 是 JavaScript 中的一个属性,
它表示当前页面的 URL 中的查询字符串部分。
例如,如果当前页面的 URL 是 "https://www.example.com/search?q=javascript&page=2",
那么 window.location.search 的值就是 "?q=javascript&page=2"。
* */
const params = parseUrl(window.location.search)
this.id = params.id
if(params.id){
this.title = '编辑收货地址'
const res = await addressFindOneApi(params.id)
if(res.code === 1){
this.form = res.data
}else{
this.$notify({ type:'warning', message:res.msg});
}
}
}
增加和删除的前端代码,还是很好理解的.
async saveAddress(){
const form = this.form
if(!form.consignee){
this.$notify({ type:'warning', message:'请输入联系人'});
return
}
if(!form.phone){
this.$notify({ type:'warning', message:'请输入手机号'});
return
}
if(!form.detail){
this.$notify({ type:'warning', message:'请输入收货地址'});
return
}
const reg = /^1[3|4|5|7|8][0-9]{9}$/
if(!reg.test(form.phone)){
this.$notify({ type:'warning', message:'手机号码不合法'});
return
}
let res= {}
if(this.id){
res = await updateAddressApi(this.form)
}else{
res = await addAddressApi(this.form)
}
if(res.code === 1){
window.requestAnimationFrame(()=>{
window.location.replace('/front/page/address.html')
})
}else{
this.$notify({ type:'warning', message:res.msg});
}
},
deleteAddress(){
this.$dialog.confirm({
title: '确认删除',
message: '确认要删除当前地址吗?',
})
.then( async () => {
const res = await deleteAddressApi({ids:this.id })
if(res.code === 1){
window.requestAnimationFrame(()=>{
window.location.replace('/front/page/address.html')
})
}else{
this.$notify({ type:'warning', message:res.msg});
}
})
.catch(() => {
});
},
其中三个关键的axios请求:
//新增地址
function addAddressApi(data){
return $axios({
'url': '/addressBook',
'method': 'post',
data
})
}
//修改地址
function updateAddressApi(data){
return $axios({
'url': '/addressBook',
'method': 'put',
data
})
}
//删除地址
function deleteAddressApi(params) {
return $axios({
'url': '/addressBook',
'method': 'delete',
params
})
}
这部分比较简单,就是接收前端的数据,从前面看到,提交的都是大多json格式的数据,需要使用@RequestBody 获取。
前端请求是这样的
function addressFindOneApi(id) {
return $axios({
'url': `/addressBook/${id}`,
'method': 'get',
})
}
controller:
@GetMapping("/{id}")
public RetObj get(@PathVariable Long id) {
AddressBook addressBook = addressBookService.getById(id);
if (addressBook != null) {
return RetObj.success(addressBook);
} else {
return RetObj.error("没有找到该对象");
}
}
async initData(){
const res = await addressListApi()
if(res.code === 1){
this.addressList = res.data
}else{
this.$message.error(res.msg)
}
},
后端:
@GetMapping("/list")
public RetObj<List> getAddressById(){
Long userId = BaseContext.getThreadLocal();
LambdaQueryWrapper<AddressBook> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(AddressBook::getUserId,userId)
.orderByDesc(AddressBook::getUpdateTime);
//SQL:select * from address_book where user_id = ? order by update_time desc
List<AddressBook> addressBooks = addressBookService.list(lambdaQueryWrapper);
return RetObj.success(addressBooks);
}
@PostMapping("")
public RetObj<String> addAddressController(@RequestBody AddressBook addressBook){
//注意需要 将当前操作插入的用户注入,使用ThreadLocal
addressBook.setUserId(BaseContext.getThreadLocal());
boolean res = addressBookService.save(addressBook);
if (res){
return RetObj.success("成功新增地址");
}else {
return RetObj.error("地址添加失败!");
}
}
async setDefaultAddress(item){
if(item.id){
const res = await setDefaultAddressApi({id:item.id})
if(res.code === 1){
this.initData()
}else{
this.$message.error(res.msg)
}
}
},
后端,根据id 设置默认地址,有很多注意的点,比如先得把表中所有地址设置为非默认,否则就有两个默认地址
/**
* 设置默认的地址,需要把其他地址都先设置为非默认的!!(而且是当前user对应的地址)
* @param addressBook
* @return
*/
@PutMapping("/default")
public RetObj setDefaultAddress(@RequestBody AddressBook addressBook){
log.info("addressBook:{}", addressBook);
LambdaUpdateWrapper<AddressBook> lambdaUpdateWrapper = new LambdaUpdateWrapper<>();
lambdaUpdateWrapper.eq(addressBook != null,AddressBook::getUserId
,BaseContext.getThreadLocal())
.set(AddressBook::getIsDefault,0);
boolean res = addressBookService.update(lambdaUpdateWrapper);
//上面先把所有的地址都设置为非默认
//现在把指定的地址设置为默认
addressBookService.updateById(addressBook);//会根据非null的字段进行更新!!
return RetObj.success("成功设置为默认地址");
}
/**
* 查询默认地址,因为可能没有查到地址等情况,所以返回值需要判别一下
* SQL:select * from address_book where user_id = ? and is_default = 1
*/
@GetMapping("default")
public RetObj<AddressBook> getDefault() {
LambdaQueryWrapper<AddressBook> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(AddressBook::getUserId, BaseContext.getThreadLocal());
queryWrapper.eq(AddressBook::getIsDefault, 1);
//SQL:select * from address_book where user_id = ? and is_default = 1
AddressBook addressBook = addressBookService.getOne(queryWrapper);
if (null == addressBook) {
return RetObj.error("没有找到该对象");
} else {
return RetObj.success(addressBook);
}
}
List
,现在不仅仅需要Dish,还需要Dish对应的口味信息,后端那边要对这个进行改造(使用DishDto)!<div class="divType">
<ul>
<li v-for="(item,index) in categoryList" :key="index" @click="categoryClick(index,item.id,item.type)" :class="{active:activeType === index}">{{item.name}}</li>
</ul>
</div>
显示出套餐下的菜品,调用getDishList()
//分类点击
categoryClick(index,id,type){
this.activeType = index
this.categoryId = id
if(type === 1){//菜品
this.getDishList()
}else{
this.getSetmealData()
}
},
getDishList()具体方法如下,获取了后端分装好的data数据,保存在dishList中进行数据的双向绑定。
//获取菜品数据
async getDishList(){
if(!this.categoryId){
return
}
const res = await dishListApi({categoryId:this.categoryId,status:1})
if(res.code === 1){
let dishList = res.data
const cartData = this.cartData
if(dishList.length > 0 && cartData.length > 0){
dishList.forEach(dish=>{ //循环为DishList插入cart值
cartData.forEach(cart=>{
if(dish.id === cart.dishId){
dish.number = cart.number
})
}
this.dishList = dishList
}else{
this.$notify({ type:'warning', message:res.msg});
}
},
具体发送的axios请求。这里的后端代码也是复用了之前的,之前在添加套餐的时候要选择菜品,下拉框中就是这些菜品。
function dishListApi(data) {
return $axios({
'url': '/dish/list',
'method': 'get',
params:{...data}
})
}
<div class="divMenu">
<div>
<div class="divItem" v-for="(item,index) in dishList" :key="index" @click="dishDetails(item)">
<el-image :src="imgPathConvert(item.image)" >
<div slot="error" class="image-slot">
<img src="./images/noImg.png"/>
</div>
</el-image>
<div>
<div class="divName">{{item.name}}</div>
<div class="divDesc">{{item.description}}</div>
<div class="divDesc">{{'月销' + (item.saleNum ? item.saleNum : 0) }}</div>
<div class="divBottom"><span>¥</span><span>{{item.price/100}}</span></div>
<div class="divNum">
<div class="divSubtract" v-if="item.number > 0">
<img src="./images/subtract.png" @click.prevent.stop="subtractCart(item)"/>
</div>
<div class="divDishNum">{{item.number}}</div>
<div class="divTypes" v-if="item.flavors && item.flavors.length > 0 && !item.number " @click.prevent.stop="chooseFlavorClick(item)">选择规格</div>
<div class="divAdd" v-else>
<img src="./images/add.png" @click.prevent.stop="addCart(item)"/>
</div>
</div>
</div>
</div>
</div>
进行了后端交互,调用setMealDishDetailsApi()
async dishDetails(item){
//先清除对象数据,如果不行的话dialog使用v-if
this.detailsDialog.item = {}
this.setMealDialog.item = {}
if(Array.isArray(item.flavors)){
this.detailsDialog.item = item
this.detailsDialog.show = true
}else{
//显示套餐的数据
const res = await setMealDishDetailsApi(item.id)
if(res.code === 1){
this.setMealDialog.item = {...item,list:res.data}
this.setMealDialog.show = true
}else{
this.$notify({ type:'warning', message:res.msg});
}
}
}
axios交互:
//获取套餐的全部菜品
function setMealDishDetailsApi(id) {
return $axios({
'url': `/setmeal/dish/${id}`,
'method': 'get',
})
}
mounted(){
this.initData()
},
methods:{
//初始化数据
initData(){
Promise.all([categoryListApi(),cartListApi({})]).then(res=>{
//获取分类数据
if(res[0].code === 1){
this.categoryList = res[0].data
if(Array.isArray(res[0].data) && res[0].data.length > 0){
this.categoryId = res[0].data[0].id
if(res[0].data[0].type === 1){
this.getDishList()
}else{
this.getSetmealData()
}
}
}else{
this.$notify({ type:'warning', message:res[0].msg});
}
//获取菜品数据
if(res[1].code === 1){
this.cartData = res[1].data
}else
this.$notify({ type:'warning', message:res[1].msg});
}
})
},
//获取菜品数据
async getDishList(){
if(!this.categoryId){
return
}
const res = await dishListApi({categoryId:this.categoryId,status:1})
if(res.code === 1){
let dishList = res.data
const cartData = this.cartData
if(dishList.length > 0 && cartData.length > 0){
dishList.forEach(dish=>{ //为每一条dishList插入cart值
cartData.forEach(cart=>{
if(dish.id === cart.dishId){
dish.number = cart.number
}
})
})
}
this.dishList = dishList
}else{
this.$notify({ type:'warning', message:res.msg});
}
},
对应api:
function dishListApi(data) {
return $axios({
'url': '/backend/page/food/list/getDishByCategoryId.do',
'method': 'get',
params:{...data}
})
}
//获取套餐数据setmealId
async getSetmealData(){
if(!this.categoryId){
return
}
const res = await setmealListApi({categoryId:this.categoryId,status:1})
if(res.code === 1){
let dishList = res.data
const cartData = this.cartData
if(dishList.length > 0 && cartData.length > 0){
dishList.forEach(dish=>{
cartData.forEach(cart=>{
if(dish.id === cart.setmealId){
dish.number = cart.number
}
})
})
}
this.dishList = dishList
}else{
this.$notify({ type:'warning', message:res.msg});
}
},
setmealListApi如下
function setmealListApi(data) {
return $axios({
'url': '/setmeal/list',
'method': 'get',
params:{...data}
})
}
async dishDetails(item){
//先清除对象数据,如果不行的话dialog使用v-if
this.detailsDialog.item = {}
this.setMealDialog.item = {}
if(Array.isArray(item.flavors)){
this.detailsDialog.item = item
this.detailsDialog.show = true
}else{
//显示套餐的数据
const res = await setMealDishDetailsApi(item.id)
if(res.code === 1){
this.setMealDialog.item = {...item,list:res.data}
this.setMealDialog.show = true
}else{
this.$notify({ type:'warning', message:res.msg});
}
}
}
@GetMapping("/food/list/getCategory.do")
public RetObj getCategoryList(Category category){
LambdaQueryWrapper<Category> lambdaQueryWrapper = new LambdaQueryWrapper<>();
//category.getType() != null 加上了这个,因为在移动端front中会直接查询,不带type,把菜品和套餐全部查出来
lambdaQueryWrapper.eq(category.getType() != null,Category::getType,category.getType())
.orderByAsc(Category::getSort)
.orderByDesc(Category::getUpdateTime);
List<Category> categoryList = categoryService.list(lambdaQueryWrapper);
//log.info("查询出菜品:{}",categoryList);
return RetObj.success(categoryList);
}
/**
* 根据菜品分类查菜品比如: 川菜这个选项一点击,就通过这个controller返回一个list(元素就是各种川菜dish)
* 这里要注意的是,返回值应该不仅包含菜品,还需要保护这些菜品对应的口味,在添加到购物车的时候就可以把口味也添加到其中。
* 主要的思路就是使用dishDto
* @param dish 参数只有一个categoryId,
* @return
*/
@GetMapping("list/getDishByCategoryId.do")
public RetObj getDishByCategoryId(Dish dish){
LambdaQueryWrapper<Dish> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(dish != null,Dish::getCategoryId,dish.getCategoryId())
.orderByDesc(Dish::getSort);
//先将所有的dish查询出来
List<Dish> dishList = dishService.list(lambdaQueryWrapper);
//循环重构每一个元素。
List<DishDto> dishDtoList = dishList.stream().map(item -> {
DishDto dishDto = new DishDto();
BeanUtils.copyProperties(item, dishDto);
LambdaQueryWrapper<DishFlavor> dishFlavorLambdaQueryWrapper = new LambdaQueryWrapper<>();
//注意Id对应,Dish元素的id对应DishFlavor中的DishId的id
dishFlavorLambdaQueryWrapper.eq(DishFlavor::getDishId, item.getId());
List<DishFlavor> dishFlavors = dishFlavorService.list(dishFlavorLambdaQueryWrapper);
dishDto.setFlavors(dishFlavors);
return dishDto;
}).collect(Collectors.toList());
return RetObj.success(dishDtoList);
}
/**
* 查出套餐分类下有哪些套餐:如二逼套餐中有二逼套餐A计划、B计划等(二逼套餐A计划中有二逼(二逼这个具体的菜又属于二逼菜这个菜品分类)
* 注意,
* @param 主要就是categoryID,还有state是1,没被禁用的查出
* @return
*/
@GetMapping("list/getSetMealByCategoryId.do")
public RetObj getSetMealByCategoryId(Long categoryId,Integer status){
LambdaQueryWrapper<Setmeal> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(Setmeal::getCategoryId,categoryId)
.eq(Setmeal::getStatus,status)
.orderByDesc(Setmeal::getUpdateTime);
List<Setmeal> setmealList = setmealService.list(lambdaQueryWrapper);
return RetObj.success(setmealList);
}
做完后效果如下
4. 以上是完成左边分类栏目的显示,以及点击分类栏目在右边示出该分类具体的项目,现在点击具体的项目,要显示出详情。比如点击上面的二逼套餐A计划,要能看到这个套餐中有哪些菜品。
/**
* 获取套餐的全部菜品
* @param id 注意这个id是二逼套餐A计划套餐的id,查表setmeal_dish中的setmeal_id这个字段对应起来
* @return
*/
@GetMapping("setmeal/setMealDishDetails.do/{id}")
public RetObj setMealDishDetails(@PathVariable Long id){
LambdaQueryWrapper<SetmealDish> dishLambdaQueryWrapper = new LambdaQueryWrapper<>();
dishLambdaQueryWrapper.eq(SetmealDish::getSetmealId,id)
.orderByDesc(SetmealDish::getUpdateTime);
List<SetmealDish> setmealDishes = setmealDishService.list(dishLambdaQueryWrapper);
return RetObj.success(setmealDishes);
}
//获取购物车数据
async getCartData(){
// async 是异步的意思,而 await 是等待的意思,await 用于等待一个异步任务执行完成的结果。
const res = await cartListApi({})
if(res.code === 1){
this.cartData = res.data
}else{
this.$notify({ type:'warning', message:res.msg});
}
},
对应的ajax请求
//获取购物车内商品的集合
function cartListApi(data) {
return $axios({
'url': '/shoppingCart/list',
'method': 'get',
params:{...data}
})
}
async addCart(item){
let params = {
amount:item.price/100,//金额
dishFlavor:item.dishFlavor,//口味 如果没有传undefined
dishId:undefined,//菜品id
setmealId:undefined,//套餐id
name:item.name,
image:item.image
}
//先将菜品id和套餐id置为0,之后再判断到底是菜品还是套餐,进行赋值
if(Array.isArray(item.flavors)){//表示是菜品
params.dishId = item.id
}else{//表示套餐 套餐没有口味
params.setmealId = item.id
}
const res = await addCartApi(params)
if(res.code === 1){
this.dishList.forEach(dish=>{
if(dish.id === item.id){
dish.number = res.data.number
}
})
if(this.setMealDialog.show){
item.number = res.data.number
}
this.getCartData()
}else{
this.$notify({ type:'warning', message:res.msg});
}
},
调用后端把购物车中的数据存起来,目的就是防止用户离开后,购物车数据消失。
function addCartApi(data){
return $axios({
'url': '/shoppingCart/add',
'method': 'post',
data
})
}
async subtractCart(item){
let params = {
dishId:item.id,
}
if(!Array.isArray(item.flavors)){
params = {
setmealId:item.id,
}
}
const res = await updateCartApi(params)
if(res.code === 1){
this.dishList.forEach(dish=>{
if(dish.id === item.id){
dish.number = (res.data.number === 0 ? undefined : res.data.number)
}
})
if(this.setMealDialog.show){
item.number = (res.data.number === 0 ? undefined : res.data.number)
}
this.getCartData()
}else{
this.$notify({ type:'warning', message:res.msg});
}
},
发送的axios
function updateCartApi(data){
return $axios({
'url': '/shoppingCart/sub',
'method': 'post',
data
})
}
@GetMapping("/list")
public RetObj listShoppingCart(){
LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();
shoppingCartLambdaQueryWrapper.orderByDesc(ShoppingCart::getCreateTime)
.eq(ShoppingCart::getUserId, BaseContext.getThreadLocal());
List<ShoppingCart> shoppingCartList = shoppingCartService.list(shoppingCartLambdaQueryWrapper);
return RetObj.success(shoppingCartList);
}
let params = {amount:item.amount,//金额 dishFlavor:item.dishFlavor,//口味 如果没有传undefined dishId:item.dishId,//菜品id setmealId:item.setmealId,//套餐id name:item.name, image:item.image }
注意,前端在添加菜品的时候,添加多次同一个菜品,应该在原来购物车的基础上,添加该商品的个数,而不是再重复在表中加一个记录。
/**
* 添加菜品或套餐到购物车
* 1.前端传过来的是json的格式,去研究一下如何看前端的数据到底是json还是?的形式
* 2.注意前端传过来的数据,肯定没有userID,这个是必填字段,从ThreadLocal中获取,或者session
* 3.需要先查询数据库,看数据库里面有没有这条数据,如果有,再添加东西的时候,只有将这条记录的number字段+1,而不是在数据库里面再加一条数据
* 具体操作哪个?不应该是userId,也不应该是id本身,因为id本身是自动生成的,每个都不一样,考虑使用菜品、套餐的名称
* @param shoppingCart 根据前端的意思,把对应的参数接收到这个对象中
* @return
*/
@PostMapping("/add")
public RetObj addShoppingCart(@RequestBody ShoppingCart shoppingCart){
//填充其对应是哪个用户的购物车
Long userId = BaseContext.threadLocal.get();
shoppingCart.setUserId(userId);
//先查出有没有该条菜品、套餐的记录
LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();
shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getName,shoppingCart.getName());
ShoppingCart shoppingCartDish = shoppingCartService.getOne(shoppingCartLambdaQueryWrapper);
if (shoppingCartDish != null){
//如果本来就有记录,记录条数加1就可以了。
shoppingCartDish.setNumber(shoppingCartDish.getNumber()+1);
//shoppingCartDish.setAmount(shoppingCartDish.getAmount());
shoppingCartService.updateById(shoppingCartDish);
return RetObj.success("成功加添");
}else { //第一次加入
shoppingCart.setNumber(1);
shoppingCart.setCreateTime(LocalDateTime.now());
boolean save = shoppingCartService.save(shoppingCart);
if (save) return RetObj.success("成功加入购物车!");
else return RetObj.error("加入购物车失败!");
}
}
/**
* 减少购物车中的商品
* 1. 注意前端 let params = {dishId:item.id,或者套餐的id} 直接把这个param穿过来,所以肯定是json的格式
* 2. 前端也是根据有没有口味来判断到底要删除的是菜品,还是套餐,从而判断传过来的param到底是dishId,还是setmealId
*
* 3. 前端只传了id过来,那现在应该根据id查出最重要的number,进行减1
* 4.要注意,如果已经是1,应该直接删除这条数据(删除购物车的东西),而不是将number设置为0
* 5. 忘了最基本的一点:查询本用户的购物车,不是整个表的!!
* @param shoppingCart
* @return
*/
@PostMapping("/sub")
public RetObj subUpdateShoppingCart(@RequestBody ShoppingCart shoppingCart){
LambdaQueryWrapper<ShoppingCart> shoppingCartLambdaQueryWrapper = new LambdaQueryWrapper<>();
shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getUserId, BaseContext.getThreadLocal());
/* //可能是菜品,也可能是套餐.
if (shoppingCart.getDishId() == null){ //是套餐,queryWrapper应该追加条件
shoppingCartLambdaQueryWrapper.eq(ShoppingCart::getDIshId,shoppingCart.getDishId())
}else ...*/
//优化
shoppingCartLambdaQueryWrapper.eq(shoppingCart.getDishId() != null, ShoppingCart::getDishId,shoppingCart.getDishId())
.eq(shoppingCart.getSetmealId() != null, ShoppingCart::getSetmealId,shoppingCart.getSetmealId())
.eq(ShoppingCart::getUserId,BaseContext.getThreadLocal());//基本的别忘了,不然可能会把别人购物车的东西删除了!
ShoppingCart shoppingCart1 = shoppingCartService.getOne(shoppingCartLambdaQueryWrapper);
if (shoppingCart1.getNumber() == 1){ //如果只有一条,直接删除
shoppingCartService.removeById(shoppingCart1);//是从数据库查出来的,所以有id
}else{ //如果多份商品,num-1
shoppingCart1.setNumber(shoppingCart1.getNumber()-1);
shoppingCartService.updateById(shoppingCart1);
}
return RetObj.success("删除成功!");
}
/**
* 无参Api,直接清空整个购物车
* 注意,不是删除整个表!!而是删除该用户下的所有记录条数(删除自己的)
*/
@DeleteMapping("/clean")
public RetObj cleanShoppingCart(){
//SQL:delete from shopping_cart where user_id = ?
LambdaQueryWrapper<ShoppingCart> lambdaQueryWrapper = new LambdaQueryWrapper<>();
lambdaQueryWrapper.eq(ShoppingCart::getUserId,BaseContext.getThreadLocal());
boolean remove = shoppingCartService.remove(lambdaQueryWrapper);
if (remove) {
return RetObj.success("成功清除购物车");
}else {
return RetObj.error("清除失败!");
}
}
该部分前端较为简单,主要就是点击提交订单,进行页面跳转,并进行ajax请求把对应的数据存在数据库就好了
下面为add-order的页面