具体重要功能
(1)封装一个功能函数
把有多个界面要使用的函数封装起来
commonCartEffects.js
import { useStore } from "vuex"
import { computed } from "vue";
export const useCommonCartEffect = (shopId) => {
const store = useStore();
const cartList = store.state.cartList;
const changeCartItemInfo = (shopId, productId, productInfo, num) => {
// console.log(shopId,productId,productInfo,num)
store.commit('changeCartItemInfo', {
shopId, productId, productInfo, num
});
};
const productList = computed(()=>{
const productList = cartList[shopId]?.productList || [];
return productList
})
return { changeCartItemInfo,cartList,productList}
}
(2)要用到vuex中的数据
store文件夹下的index.js代码
import { createStore } from 'vuex'
//实现本地存储
const setLocalCartList = (state)=>{
const { cartList } = state;
const cartListString = JSON.stringify(cartList);
localStorage.cartList = cartListString;
}
const getLocalCartList = ()=>{
try{
return JSON.parse(localStorage.cartList) || {}
}catch(e){
return {}
}
}
export default createStore({
state: {
// cartList:{
// shopId:{
// shopNme:'沃尔玛',
// productList:{
// productId:{
// _id: "1",
// name: "番茄 250g / 份",
// imgUrl: "http://www.dell-lee.com/imgs/vue3/tomato.png",
// sales: 10,
// price: 33.6,
// oldPrice: 39.6,
// count:2
// }
// }
// }
// cartList:{}
cartList:getLocalCartList()
},
getters: {
},
mutations: {
changeCartItemInfo(state,pyload){
const {shopId,productId,productInfo} = pyload;
let shopInfo = state.cartList[shopId] || {
shopName:'',
productList:{}
};
// if(!shopInfo) {shopInfo={}}
let product = shopInfo.productList[productId];
if(!product) {
productInfo.count = 0;
product=productInfo;
// product.count = 0;
}
product.count = product.count +pyload.num;
if(pyload.num > 0){product.check = true;}
// 等价于
// (pyload.num > 0) && (product.check = true);
if(product.count < 0) {product.count = 0;}
// 等价于
// (product.count < 0) && (product.count = 0);
shopInfo.productList[productId] = product;
state.cartList[shopId] = shopInfo;
setLocalCartList(state);
},
changeShopName(state,pyload){
const {shopId,shopName} = pyload;
const shopInfo = state.cartList[shopId] || {
shopName:'',
productList:{}
}
shopInfo.shopName=shopName;
state.cartList[shopId] = shopInfo
setLocalCartList(state);
},
changeCartItemChecked(state,pyload){
const {shopId,productId} = pyload;
const product = state.cartList[shopId].productList[productId];
//console.log(product)
product.check = !product.check;
setLocalCartList(state);
},
cleanCartProducts(state,pyload){
const {shopId} = pyload;
state.cartList[shopId].productList={};
},
setCartItemsChecked(state,pyload){
const {shopId} = pyload;
const products = state.cartList[shopId].productList;
if(products){
for(let i in products){
const product =products[i];
product.check = true;
}
}
setLocalCartList(state);
},
clearCartData(state,pyload){
const {shopId} = pyload
state.cartList[shopId].productList=[];
}
},
})
(3)路由注册功能router文件夹下的index.js
import { createRouter, createWebHashHistory } from 'vue-router'
const routes = [
{
path: '/',
name: 'Home',
component: () => import(/* webpackChunkName: "home" */ '../views/home/HomeV')
},
{
path: '/cartList',
name: 'CartList',
component: () => import(/* webpackChunkName: "cartList" */ '../views/cartList/CartList')
},
{
path: '/orderConfirmation/:id',
name: 'OrderConfirmation',
component: () => import(/* webpackChunkName: "orderConfirmation" */ '../views/orderConfirmation/OrderConfirmation')
},
{
path: '/orderList',
name: 'OrderList',
component: () => import(/* webpackChunkName: "orderList" */ '../views/orderList/OrderList')
},
{
path: '/shop/:id',
name: 'Shop',
component: () => import(/* webpackChunkName: "shop" */ '../views/shop/ShopV')
},
{
path: '/login',
name: 'Login',
component: () => import(/* webpackChunkName: "login" */ '../views/login/LoginV'),
beforeEnter(to,from,next){
const {isLogin} =localStorage;
isLogin ? next({name:"Home"}) : next();
},
},
{
path: '/register',
name: 'Register',
component: () => import(/* webpackChunkName: "register" */ '../views/register/RegisterV'),
beforeEnter(to,from,next){
const {isLogin} =localStorage;
isLogin ? next({name:"Home"}) : next();
},
},
// {
// path: '/about',
// name: 'about',
// // route level code-splitting
// // this generates a separate chunk (about.[hash].js) for this route
// // which is lazy-loaded when the route is visited.
// component: () => import(/* webpackChunkName: "about" */ '../views/AboutView.vue')
// }
]
const router = createRouter({
history: createWebHashHistory(),
routes
})
router.beforeEach((to,from,next) => {
const {isLogin }= localStorage;
const {name} = to;
const isLoginOrRegister = (name === "Login" || name === "Register");
(isLogin || isLoginOrRegister) ? next() : next({name:'Login'});
})
export default router
商家详情页总体代码ShopV.vue
<template>
<div class="wrapper">
<div class="search">
<div
class="search__back iconfont"
@click="handleBackClick"
></div>
<div class="search__content">
<span class="search__content__icon iconfont"></span>
<input
class="search__content__input"
placeholder="请输入商品名称"
/>
</div>
</div>
<ShopInfo
:item="item"
:hindeBoder="true"
v-show="item.imgUrl"
/>
<Content :shopName="item.name" />
<CartV />
</div>
</template>
<script>
import {reactive, toRefs} from 'vue'
import {useRouter,useRoute} from "vue-router"
import {get} from '../../utils/request'
import ShopInfo from '../../components/ShopInfo.vue'
import Content from './Content.vue'
import CartV from './CartV.vue'
//获取当前商铺信息
const useShopInfoEffect = ()=>{
const route = useRoute();
const data = reactive({ item:{} });
const getItemData = async()=>{
const result = await get(`api/shop/${route.params.id}`);
if(result.errno===0 && result?.data){
data.item = result.data
}
}
const {item} = toRefs(data);
return {item,getItemData}
}
//点击回退逻辑
const useBackRoterEffect = ()=>{
const router = useRouter();
const handleBackClick = ()=>{
router.back();
}
return {handleBackClick}
}
export default {
name:'ShopV',
components:{ ShopInfo,Content,CartV },
setup() {
const {item,getItemData} = useShopInfoEffect();
const {handleBackClick} = useBackRoterEffect();
getItemData();
return { item,handleBackClick }
},
}
</script>
<style lang="scss" scoped>
@import '../../style/viriables.scss';
.wrapper {
padding: 0 .18rem;
}
.search{
margin: .14rem 0 .04rem 0;
display: flex;
line-height: .32rem;
&__back{
font-size: .24rem;
height: .3rem;
color: #b6b6b6;
}
&__content{
display: flex;
flex: 1;
background: $search-bgColor;
border-radius: .16rem;
&__icon{
width: .44rem;
text-align: center;
color: $search-fontColor;
}
&__input{
display: block;
width: 100%;
padding-right: .2rem;
border: none;
outline: none;
background: none;
height: .32rem;
font-size: .14rem;
color: $content-fontcolor;
&::placeholder{
color: $content-fontcolor;
}
}
}
}
</style>
商品信息代码ShopInfo.vue
<template>
<div class="shop">
<img :src="item.imgUrl" class="shop__img" />
<div
:class="{shop__content:true,'shop__content--bordered':hindeBoder?false:true}"
>
<div class="shop__content__title">{{item.name}}</div>
<div class="shop__content__tags">
<span class="shop__content__tag">月售: {{item.sales}}</span>
<span class="shop__content__tag">起送: {{item.expressLimit}}</span>
<span class="shop__content__tag">基础运费: {{item.expressPrice}}</span>
</div>
<p class="shop__content__highlight">{{item.slogan}}</p>
</div>
</div>
</template>
<script>
export default {
name:'ShopInfo',
props:['item','hindeBoder'],
setup() {
},
}
</script>
<style lang="scss" scoped>
@import '../style/viriables.scss';
@import '../style/mixins.scss';
.shop{
display: flex;
padding-top: .12rem;
&__img{
margin-right: .16rem;
width: .56rem;
height: .56rem;
}
&__content{
flex: 1;
padding-bottom: .12rem;
&--bordered{
border-bottom:.01rem solid $content-fontcolor ;
}
&__title{
line-height: .22rem;
font-size: .16rem;
color: $content-fontcolor;
}
&__tags{
margin-top: .08rem;
line-height: .18rem;
font-size: .13rem;
color: $content-fontcolor;
}
&__tag{
margin-right: .16rem;
}
&__highlight{
margin: .08rem 0 0 0;
line-height: .18rem;
font-size: .13rem;
color: $highlight-fontColor;
}
}
}
</style>
详情内容部分代码Content.vue
<template>
<div class="content">
<div class="category">
<div
:class="{ 'category__item': true, 'category__item--active': currentTab === item.tab }"
v-for="item in categories"
:key="item.name"
@click="() => handleTabClick(item.tab)"
>{{ item.name }}</div>
</div>
<div class="product">
<div class="product__item" v-for="item in list" :key="item._id">
<img class="product__item__img" :src="item.imgUrl" />
<div class="product__item__detail">
<h4 class="product__item__title">{{ item.name }}</h4>
<p class="product__item__sales">月售 {{ item.sales }} 件</p>
<p class="product__item__price">
<span class="product__item__yen">¥{{ item.price }}</span>
<span class="product__item__oringin">¥{{ item.oldPrice }}</span>
</p>
</div>
<div class="product__number">
<span
class="product__number__minus iconfont"
@click="changeCartItem(shopId, item._id, item, -1,shopName)"
></span>
{{getProductCartCount(shopId,item._id) }}
<span
class="product__number__plus iconfont"
@click="changeCartItem(shopId, item._id, item, 1,shopName)"
></span>
</div>
</div>
</div>
</div>
</template>
<script>
import { reactive, toRefs, ref, watchEffect } from 'vue'
import { useRoute } from "vue-router"
import { useStore } from 'vuex'
import { get } from '../../utils/request'
import { useCommonCartEffect } from '../../effects/cartEffects'
const categories = [
{ name: '全部商品', tab: 'all' },
{ name: '秒杀', tab: 'seckill' },
{ name: '新鲜水果', tab: 'fruit' }
]
//和tab切换相关的逻辑
const useTabEffect = () => {
const currentTab = ref(categories[0].tab);
const handleTabClick = (tab) => {
currentTab.value = tab;
}
return { currentTab, handleTabClick }
}
//列表内容相关的东西
const useCurrentListEffect = (currentTab, shopId) => {
const content = reactive({ list: [] });
const getContentData = async () => {
const result = await get(`/api/shop/${shopId}/products`, {
tab: currentTab.value
});
if (result?.errno === 0 && result?.data?.length) {
content.list = result.data;
}
}
watchEffect(() => { getContentData(); })
const { list } = toRefs(content);
return { list }
}
//购物车相关逻辑
const useCartEffect = ()=>{
const store = useStore();
const { changeCartItemInfo,cartList } = useCommonCartEffect();
const changeShopName = (shopId,shopName)=>{
store.commit('changeShopName',{shopId,shopName})
}
const changeCartItem = (shopId, productId, item, num,shopName)=>{
changeCartItemInfo(shopId, productId, item, num);
changeShopName(shopId,shopName)
}
const getProductCartCount =(shopId,productId)=>{
return cartList?.[shopId]?.productList?.[productId]?.count || 0
}
return {cartList,changeCartItem,getProductCartCount}
}
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: 'Content',
props:['shopName'],
setup() {
const route = useRoute();
const shopId = route.params.id;
const { currentTab, handleTabClick } = useTabEffect();
const { list } = useCurrentListEffect(currentTab, shopId);
const {cartList,changeCartItem,getProductCartCount} = useCartEffect();
return {
list, currentTab, categories, handleTabClick,
shopId, changeCartItem,cartList,getProductCartCount
}
},
}
</script>
<style lang="scss" scoped>
@import "../../style/mixins.scss";
@import "../../style/viriables.scss";
.content {
position: absolute;
display: flex;
left: 0;
right: 0;
top: 1.5rem;
bottom: 0.5rem;
}
.category {
overflow-y: scroll;
height: 100%;
width: 0.76rem;
background: $search-bgColor;
&__item {
line-height: 0.4rem;
text-align: center;
font-size: 0.14rem;
color: #333;
&--active {
background: $bgColor;
}
}
}
.product {
overflow-y: scroll;
flex: 1;
&__item {
position: relative;
display: flex;
padding: 0.12rem 0;
margin: 0 0.16rem;
border-bottom: 0.01rem solid $content-bgcolor;
&__detail {
overflow: hidden;
}
&__img {
width: 0.68rem;
height: 0.68rem;
margin-right: 0.16rem;
}
&__title {
margin: 0;
line-height: 0.2rem;
font-size: 0.14rem;
color: $content-fontcolor;
@include ellipsis;
}
&__sales {
margin: 0.06rem 0;
line-height: 0.16rem;
font-size: 0.12rem;
color: $content-fontcolor;
}
&__price {
margin: 0;
line-height: 0.2rem;
font-size: 0.14rem;
color: $highlight-fontColor;
}
&__yen {
font-size: 0.12rem;
}
&__oringin {
margin-left: 0.06rem;
line-height: 0.2rem;
font-size: 0.12rem;
color: $light-fontColor;
text-decoration: line-through;
}
.product__number {
position: absolute;
right: 0;
bottom: 0.12rem;
line-height: .18rem;
&__minus {
position: relative;
top: .02rem;
color: $medium-fontColor;
margin-right: 0.05rem;
}
&__plus {
position: relative;
top: .02rem;
color: #0091ff;
margin-left: 0.05rem;
}
}
}
}
</style>
购物车部分代码CartV.vue
<template>
<div
class="mask"
v-if="showCart && calculations.total>0"
@click="handleCartShowChange"
/>
<div class="cart">
<div class="product" v-if="showCart && calculations.total>0">
<div class="product__header">
<div
class="product__header__all"
@click="() => setCartItemsChecked(shopId)"
>
<span
class="product__header__icon iconfont"
v-html="calculations.allChecked ?'':''"
></span>
全选</div>
<div class="product__header__clear">
<span
class="product__header__clear__btn"
@click="() => cleanCartProducts(shopId)"
>清空购物车</span>
</div>
</div>
<div
class="product__item"
v-for="item in productList"
:key="item._id"
>
<div
class="product__item__checked iconfont"
v-html="item.check ? '':''"
@click="()=>{changeCartItemChecked(shopId,item._id)}"
/>
<img
class="product__item__img"
:src="item.imgUrl"
/>
<div class="product__item__detail">
<h4 class="product__item__title">{{item.name}}</h4>
<p class="product__item__price">
<span class="product__item__yen">¥{{item.price}}</span>
<span class="product__item__oringin">¥{{item.oldPrice}}</span>
</p>
</div>
<div class="product__number">
<span
class="product__number__minus iconfont"
@click="()=>{changeCartItemInfo(shopId,item._id,item,-1)}"
></span>
{{item.count || 0}}
<span
class="product__number__plus iconfont"
@click="()=>{changeCartItemInfo(shopId,item._id,item,1)}"
></span>
</div>
</div>
</div>
<div class="check">
<div class="check__icon">
<img
src="http://www.dell-lee.com/imgs/vue3/basket.png"
class="check__icon__img"
@click="handleCartShowChange"
/>
<div class="check__icon__tag">{{calculations.total}}</div>
</div>
<div class="check__info">
总计:<span class="check__info__price">¥ {{calculations.price}}</span>
</div>
<div class="check__button" v-show="calculations.total>0">
<router-link :to="{path:`/orderConfirmation/${shopId}`}">
去结算
</router-link>
</div>
</div>
</div>
</template>
<script>
import { ref} from 'vue'
import {useRoute} from 'vue-router'
import {useStore} from "vuex"
import { useCommonCartEffect } from '../../effects/cartEffects'
//获取购物车信息逻辑
const useCartEffect = (shopId)=>{
const store = useStore();
const {calculations,changeCartItemInfo,productList} = useCommonCartEffect(shopId);
const changeCartItemChecked = (shopId,productId)=>{
store.commit('changeCartItemChecked', {
shopId, productId
})
}
const cleanCartProducts = (shopId)=>{
store.commit('cleanCartProducts',{ shopId })
}
const setCartItemsChecked = (shopId)=>{
store.commit('setCartItemsChecked',{shopId})
}
return {
calculations,productList,changeCartItemInfo,
changeCartItemChecked,cleanCartProducts,
setCartItemsChecked
}
}
//展示隐藏购物车逻辑
const toggleCartEffect = ()=>{
const showCart = ref(false);
const handleCartShowChange = ()=>{
showCart.value=!showCart.value;
}
return {showCart,handleCartShowChange}
}
export default {
name:'CartV',
setup() {
const route = useRoute();
const shopId = route.params.id;
const {
calculations,productList,changeCartItemInfo,
changeCartItemChecked,cleanCartProducts,
setCartItemsChecked
} = useCartEffect(shopId);
const {showCart,handleCartShowChange} = toggleCartEffect();
return {
calculations,productList,changeCartItemInfo,
changeCartItemChecked,shopId,cleanCartProducts,
setCartItemsChecked,showCart,handleCartShowChange
}
},
}
</script>
<style lang="scss" scoped>
@import '../../style/viriables.scss';
@import '../../style/mixins.scss';
.mask{
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
background: rgba(0,0,0,.5);
z-index: 1;
}
.cart{
position: absolute;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
background: $bgColor;
}
.product{
overflow-y: scroll;
flex: 1;
background:$bgColor;
&__header{
display: flex;
line-height: .52rem;
border-bottom: .01rem solid $content-bgcolor;
font-size: .14rem;
color: $content-fontcolor;
&__all{
width: .64rem;
margin-left:.18rem;
}
&__icon{
display: inline-block;
vertical-align: top;
color: $btn-bgColor;
font-size: .2rem;
}
&__clear{
flex: 1;
margin-right: .16rem;
text-align: right;
&__btn{
display: inline-block;
}
}
}
&__item{
position: relative;
display: flex;
padding: .12rem 0;
margin: 0 .16rem;
border-bottom: .01rem solid $content-bgcolor;
&__detail{
overflow: hidden;
}
&__checked{
line-height: .5rem;
margin-right: .2rem;
color:$btn-bgColor;
font-size: .2rem;
}
&__img{
width: .46rem;
height: .46rem;
margin-right: .16rem;
}
&__title{
margin: 0;
line-height: .2rem;
font-size: .14rem;
color: $content-fontcolor;
@include ellipsis;
}
&__price{
margin: .06rem 0 0 0 ;
line-height: .2rem;
font-size: .14rem;
color: $highlight-fontColor;
}
&__yen{
font-size: .12rem;
}
&__oringin{
margin-left: .06rem;
line-height: .2rem;
font-size: .12rem;
color: $light-fontColor;
text-decoration: line-through;
}
.product__number{
position: absolute;
right: 0;
bottom: .26rem;
&__minus{
position: relative;
top: .02rem;
color: $medium-fontColor;
margin-right:.05rem ;
}
&__plus{
position: relative;
top: .02rem;
color: #0091ff;
margin-left: .05rem;
}
}
}
}
.check{
display: flex;
line-height: .49rem;
height: .49rem;
border-top: .01rem solid $content-bgcolor;
&__icon{
position: relative;
width: .84rem;
&__img{
display: block;
margin: .12rem auto;
width: .28rem;
height: .26rem;
}
&__tag{
position: absolute;
left: .46rem;
top: .04rem;
padding: 0 .04rem;
min-width: .2rem;
height: .2rem;
line-height: .2rem;
background-color: $highlight-fontColor;
border-radius: .1rem;
font-size: .12rem;
text-align: center;
color: $bgColor;
transform: scale(.5);
transform-origin: left centet;//做缩小的时候,以左侧中心的位置为中心
}
}
&__info{
flex: 1;
color: $content-fontcolor;
font-size: .12rem;
&__price{
line-height: .49rem;
color: $highlight-fontColor;
font-size: .18rem;
}
}
&__button{
width: .98rem;
background-color: #4fb0f9;
color: $content-bgcolor;
font-size: .14rem;
text-align: center;
a{
color: $bgColor;
text-decoration: none;
}
}
}
</style>
具体实现界面
详情1.png 详情2.png 详情3.png 详情4.png 详情5.png(2)点击清空购物车可清空购物车内容
点击详情5图片中去结算可跳转到新的页面,确认订单界面
确认订单界面开发
详细代码
(1)确认订单总体代码OrderConformation.vue
<template>
<div class="wrapper">
<TopArea />
<ProductList />
<OrderV />
</div>
</template>
<script>
import TopArea from './TopArea.vue';
import ProductList from './ProductList.vue'
import OrderV from './OrderV.vue'
export default {
name:'OrderConfirmation',
components:{TopArea,ProductList,OrderV},
}
</script>
<style lang="scss" scoped>
.wrapper{
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background-color: #eee;
overflow-y: scroll;
}
</style>
(2)顶部区域代码TopArea.vue
<template>
<div class="top">
<div class="top__header">
<div
class="iconfont top__header__back"
@click="handleBackClick"
></div>
确认订单
</div>
<div class="top__receiver">
<div class="top__receiver__title">收货地址</div>
<div class="top__receiver__address">南华大学雨母校区三省园5栋</div>
<div class="top__receiver__info">
<span class="top__receiver__info__name">彭美女</span>
<span class="top__receiver__info__name">15080846751</span>
</div>
<div class="top__receiver__icon iconfont"></div>
</div>
</div>
</template>
<script>
import { useRouter } from 'vue-router';
export default {
name:'TopArea',
setup() {
const router = useRouter()
const handleBackClick = ()=>{router.back()}
return {handleBackClick}
},
}
</script>
<style lang="scss" scoped>
@import "../../style/viriables.scss";
.top{
height: 1.96rem;
background-size: 100% 1.59rem ;
background-image: linear-gradient(0deg,rgba(0,145,255,0.00) 4%,#0091ff 50%);
background-repeat: no-repeat;
&__header{
position: relative;
padding-top: .2rem;
line-height: .24rem;
color: $bgColor;
text-align: center;
font-size: .16rem;
&__back{
position: absolute;
left: .18rem;
font-size: .22rem;
}
}
&__receiver{
position: absolute;
left: .18rem;
right: .18rem;
top: .66rem;
height: 1.11rem;
background: $bgColor;
border-radius: .04rem;
&__title{
line-height: .22rem;
padding: .16rem 0 .14rem .16rem;
font-size: .16rem;
color: $content-fontcolor;
}
&__address{
line-height: .2rem;
padding: 0 .4rem 0 .16rem;
font-size: .14rem;
color: $content-fontcolor;
}
&__info{
padding: .06rem 0 0 .16rem;
&__name{
margin-right: .06rem;
line-height: .18rem;
font-size: .12rem;
color: $medium-fontColor;
}
}
&__icon{
transform: rotate(180deg);
position: absolute;
right: .16rem;
top: .5rem;
color: $medium-fontColor;
font-size: .2rem;
}
}
}
</style>
(3)下单部分代码OrderV.vue
<template>
<div class="order">
<div class="order__price">实付金额 ¥<b>{{calculations.price}}</b></div>
<div
class="order__btn"
@click="()=>handleShowConfirmChange(true)"
>提交订单</div>
</div>
<div
class="mask"
v-show="showConfirm"
@click="()=>handleShowConfirmChange(false)"
>
<div class="mask__content" @click.stop>
<h3 class="mask__content__title">确认离开收银台</h3>
<p class="mask__content__desc">请尽快完成支付,否则30分钟后自动取消</p>
<div class="mask__content__btns">
<button
class="mask__content__btn mask__content__btn--first"
@click="()=>handleConfirmOrder(true)"
>取消订单</button>
<button
class="mask__content__btn mask__content__btn--second"
@click="()=>handleConfirmOrder(false)"
>确认支付</button>
</div>
</div>
</div>
</template>
<script>
import { ref } from '@vue/reactivity';
import { useRoute,useRouter } from 'vue-router';
import { useStore } from 'vuex';
import { useCommonCartEffect } from '../../effects/cartEffects'
import {post} from '../../utils/request'
//下单相关逻辑
const useMakeOrderEffect = (shopName,productList,shopId)=>{
const store = useStore()
const router =useRouter()
const handleConfirmOrder= async (isCancled)=>{
const products = []
for(let i in productList.value){
const product = productList.value[i]
products.push({id:parseInt(product._id,10),num:product.count})
}
try{
const result = await post('/api/order',{
addressId:1,
shopId,
shopName:shopName.value,
isCancled,
products
})
if(result?.errno === 0){
store.commit('clearCartData',{shopId})
router.push({name:'OrderList'});
}
}catch(e){
//提示下单失败
//showToast("请求失败")
}
}
return {handleConfirmOrder}
}
//蒙层展示相关逻辑
const useShowMaskEffect = ()=>{
const showConfirm = ref(false)
const handleShowConfirmChange = (status)=>{
showConfirm.value=status
}
return {showConfirm,handleShowConfirmChange}
}
export default {
name:'OrderV',
setup() {
const route = useRoute()
const shopId = parseInt(route.params.id,10)
const {calculations,shopName,productList} = useCommonCartEffect(shopId)
const {handleConfirmOrder} = useMakeOrderEffect(shopName,productList,shopId);
const {showConfirm,handleShowConfirmChange} = useShowMaskEffect()
return {showConfirm,calculations,
handleConfirmOrder,handleShowConfirmChange
}
},
}
</script>
<style lang="scss" scoped>
@import "../../style/viriables.scss";
.order{
position: absolute;
left: 0;
right: 0;
bottom: 0;
display: flex;
height: .49rem;
line-height: .49rem;
background: $bgColor;
&__price{
flex: 1;
text-indent: .24rem;
font-size: .14rem;
color: $content-fontcolor;
}
&__btn{
width: .98rem;
background: #4fb0f9;
color: $bgColor;
text-align: center;
font-size: .14rem;
}
}
.mask{
z-index: 1;
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
background: rgba(0,0,0,0.50);
&__content{
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 3rem;
height: 1.56rem;
background: #fff;
border-radius: .04rem;
text-align: center;
&__title{
margin: .24rem 0 0 0;
line-height: .26rem;
font-size: .18rem;
color: #333;
}
&__desc{
margin: .08rem 0 0 0 ;
font-size: .14rem;
color: #666;
}
&__btns{
display: flex;
margin: .24rem .58rem ;
}
&__btn{
flex: 1;
width: .8rem;
line-height: .32rem;
border-radius: .16rem;
font-size: .14rem;
&--first{
margin-right: .12rem;
border: .01rem solid #4fb0f9;
color: #4fb0f9;
}
&--second{
margin-left: .12rem;
background: #4fb0f9;
color: #fff;
}
}
}
}
</style>
(4)下单界面商品显示下单商品ProductList.vue
<template>
<div class="products">
<div class="products__title">
{{shopName}}
</div>
<div class="products__wrapper">
<div class="products__list">
<div
class="products__item"
v-for="item in productList"
:key="item._id"
>
<img class="products__item__img" :src="item.imgUrl" />
<div class="products__item__detail">
<h4 class="products__item__title">{{item.name}}</h4>
<p class="products__item__price">
<span>
<span class="products__item__yen">¥</span>
{{item.price}} × {{item.count}}
</span>
<span class="products__item__total">
<span class="products__item__yen">¥</span>
{{(item.price*item.count).toFixed(2)}}
</span>
</p>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import { useRoute } from 'vue-router';
import { useCommonCartEffect } from '../../effects/cartEffects'
export default {
name:'ProductList',
setup() {
const route = useRoute()
const shopId = route.params.id
const {productList,shopName} = useCommonCartEffect(shopId)
return {productList,shopName}
},
}
</script>
<style lang="scss" scoped>
@import "../../style/mixins.scss";
@import "../../style/viriables.scss";
.products{
margin: .16rem .18rem .1rem .18rem;
background: $bgColor;
&__title{
padding: .16rem;
font-size: .16rem;
color: $content-fontcolor;
}
&__wrapper{
overflow-y: scroll;
margin: 0 .18rem;
position: absolute;
left: 0;
right: 0;
bottom: .6rem;
top: 2.6rem;
}
&__list{
background: $bgColor;
}
&__item {
position: relative;
display: flex;
padding: 0 .16rem .16rem .16rem;
&__img {
width: 0.46rem;
height: 0.46rem;
margin-right: 0.16rem;
}
&__detail {
flex: 1;
}
&__title {
margin: 0;
line-height: 0.2rem;
font-size: 0.14rem;
color: $content-fontcolor;
@include ellipsis;
}
&__price {
display: flex;
margin: .06rem 0 0 0;
line-height: 0.2rem;
font-size: 0.14rem;
color: $highlight-fontColor;
}
&__total{
text-align: right;
flex: 1;
color: $dark-fontColor;
}
&__yen {
font-size: 0.12rem;
}
}
}
</style>
下单界面具体实现界面
下单1.png 下单2.png**点击确认支付和取消订单均跳转到首页,因为没有做支付界面
网友评论