商家展示功能的開發(fā)

具體重要功能

(1)封裝一個(gè)功能函數(shù)
把有多個(gè)界面要使用的函數(shù)封裝起來(lái)
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中的數(shù)據(jù)
store文件夾下的index.js代碼

import { createStore } from 'vuex'

//實(shí)現(xiàn)本地存儲(chǔ)
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;}
      // 等價(jià)于
      // (pyload.num > 0) && (product.check = true);
    
      if(product.count < 0) {product.count = 0;}
      // 等價(jià)于
      // (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)路由注冊(cè)功能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

商家詳情頁(yè)總體代碼ShopV.vue

<template>
<div class="wrapper">
    <div class="search">
        <div
         class="search__back iconfont"
         @click="handleBackClick"
         >&#xe6db;</div>
        <div class="search__content">
            <span class="search__content__icon iconfont">&#xe615;</span>
            <input
             class="search__content__input"
             placeholder="請(qǐng)輸入商品名稱"
            />
        </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'

//獲取當(dāng)前商鋪信息
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}
}
//點(diǎn)擊回退邏輯
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">基礎(chǔ)運(yùn)費(fèi): {{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>

詳情內(nèi)容部分代碼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">&yen;{{ item.price }}</span>
                        <span class="product__item__oringin">&yen;{{ item.oldPrice }}</span>
                    </p>
                </div>
                <div class="product__number">
                    <span
                        class="product__number__minus iconfont"
                        @click="changeCartItem(shopId, item._id, item, -1,shopName)"
                    >&#xe66d;</span>
                    {{getProductCartCount(shopId,item._id) }}
                    <span
                        class="product__number__plus iconfont"
                        @click="changeCartItem(shopId, item._id, item, 1,shopName)"
                    >&#xe781;</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切換相關(guān)的邏輯
const useTabEffect = () => {
    const currentTab = ref(categories[0].tab);
    const handleTabClick = (tab) => {
        currentTab.value = tab;
    }
    return { currentTab, handleTabClick }
}

//列表內(nèi)容相關(guān)的東西
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 }
}

//購(gòu)物車相關(guān)邏輯
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>

購(gòu)物車部分代碼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 ?'&#xe652;':'&#xe619;'"
                ></span>
                全選</div>
            <div  class="product__header__clear">   
                <span 
                    class="product__header__clear__btn"
                    @click="() => cleanCartProducts(shopId)"
                >清空購(gòu)物車</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 ? '&#xe652;':'&#xe619;'"
                @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">&yen;{{item.price}}</span>
                    <span class="product__item__oringin">&yen;{{item.oldPrice}}</span>
                </p>
            </div>
            <div class="product__number">
                <span 
                    class="product__number__minus iconfont"
                    @click="()=>{changeCartItemInfo(shopId,item._id,item,-1)}"
                >&#xe66d;</span>
                {{item.count || 0}}
                <span 
                    class="product__number__plus iconfont"
                    @click="()=>{changeCartItemInfo(shopId,item._id,item,1)}"
                >&#xe781;</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">
            總計(jì):<span class="check__info__price">&yen; {{calculations.price}}</span>
        </div>
        <div class="check__button" v-show="calculations.total>0">
            <router-link :to="{path:`/orderConfirmation/${shopId}`}">
                去結(jié)算
            </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'

//獲取購(gòu)物車信息邏輯
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
    }
}


    //展示隱藏購(gòu)物車邏輯
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;//做縮小的時(shí)候,以左側(cè)中心的位置為中心
        }
    }
    &__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>

具體實(shí)現(xiàn)界面

詳情1.png
詳情2.png
詳情3.png
詳情4.png
詳情5.png

(2)點(diǎn)擊清空購(gòu)物車可清空購(gòu)物車內(nèi)容

點(diǎn)擊詳情5圖片中去結(jié)算可跳轉(zhuǎn)到新的頁(yè)面,確認(rèn)訂單界面

確認(rèn)訂單界面開發(fā)

詳細(xì)代碼

(1)確認(rèn)訂單總體代碼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)頂部區(qū)域代碼TopArea.vue
<template>
<div class="top">
    <div class="top__header">
        <div 
            class="iconfont top__header__back"
            @click="handleBackClick"
        >&#xe6db;</div>
        確認(rèn)訂單
    </div>
    <div class="top__receiver">
        <div class="top__receiver__title">收貨地址</div>
        <div class="top__receiver__address">南華大學(xué)雨母校區(qū)三省園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">&#xe6db;</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">實(shí)付金額 ¥<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">確認(rèn)離開收銀臺(tái)</h3>
            <p class="mask__content__desc">請(qǐng)盡快完成支付,否則30分鐘后自動(dòng)取消</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)"
                >確認(rèn)支付</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'

//下單相關(guān)邏輯
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("請(qǐng)求失敗")
        }         
    }
    return {handleConfirmOrder}
}

//蒙層展示相關(guān)邏輯
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">&yen;</span>
                                {{item.price}} × {{item.count}}
                            </span>
                            <span class="products__item__total">
                                <span class="products__item__yen">&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>

下單界面具體實(shí)現(xiàn)界面

下單1.png
下單2.png

**點(diǎn)擊確認(rèn)支付和取消訂單均跳轉(zhuǎn)到首頁(yè)果港,因?yàn)闆]有做支付界面

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市萨西,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖呆贿,帶你破解...
    沈念sama閱讀 219,270評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異微服,居然都是意外死亡劳曹,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,489評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門蔬螟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)此迅,“玉大人,你說我怎么就攤上這事∷市颍” “怎么了忍些?”我有些...
    開封第一講書人閱讀 165,630評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)坎怪。 經(jīng)常有香客問我罢坝,道長(zhǎng),這世上最難降的妖魔是什么搅窿? 我笑而不...
    開封第一講書人閱讀 58,906評(píng)論 1 295
  • 正文 為了忘掉前任嘁酿,我火速辦了婚禮,結(jié)果婚禮上男应,老公的妹妹穿的比我還像新娘闹司。我一直安慰自己,他們只是感情好沐飘,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,928評(píng)論 6 392
  • 文/花漫 我一把揭開白布游桩。 她就那樣靜靜地躺著,像睡著了一般耐朴。 火紅的嫁衣襯著肌膚如雪借卧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,718評(píng)論 1 305
  • 那天筛峭,我揣著相機(jī)與錄音铐刘,去河邊找鬼。 笑死影晓,一個(gè)胖子當(dāng)著我的面吹牛镰吵,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播俯艰,決...
    沈念sama閱讀 40,442評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼捡遍,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了竹握?” 一聲冷哼從身側(cè)響起画株,我...
    開封第一講書人閱讀 39,345評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啦辐,沒想到半個(gè)月后谓传,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,802評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡芹关,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,984評(píng)論 3 337
  • 正文 我和宋清朗相戀三年续挟,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片侥衬。...
    茶點(diǎn)故事閱讀 40,117評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诗祸,死狀恐怖跑芳,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情直颅,我是刑警寧澤博个,帶...
    沈念sama閱讀 35,810評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站功偿,受9級(jí)特大地震影響盆佣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜械荷,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,462評(píng)論 3 331
  • 文/蒙蒙 一共耍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吨瞎,春花似錦痹兜、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,011評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)庸娱。三九已至着绊,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間熟尉,已是汗流浹背归露。 一陣腳步聲響...
    開封第一講書人閱讀 33,139評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留斤儿,地道東北人剧包。 一個(gè)月前我還...
    沈念sama閱讀 48,377評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像往果,于是被迫代替她去往敵國(guó)和親疆液。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,060評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • boku88.com 本套課程ThinkPHP5 查看更多關(guān)于 ThinkPHP5 的文章 (tp5)第四季:實(shí)戰(zhàn)...
    博庫(kù)吧閱讀 1,249評(píng)論 0 2
  • 36 APP常用功能設(shè)計(jì) 36.1啟動(dòng)頁(yè)面設(shè)計(jì) 啟動(dòng)頁(yè)面的圖片設(shè)計(jì)成動(dòng)態(tài)配置的陕贮,可以顯示不同的廣告堕油。 當(dāng)服務(wù)器更新...
    xjbclz閱讀 1,190評(píng)論 0 1
  • 前言 這里筑夢(mèng)師,是一名正在努力學(xué)習(xí)的iOS開發(fā)工程師,目前致力于全棧方向的學(xué)習(xí),希望可以和大家一起交流技術(shù),共同...
    筑夢(mèng)師Winston閱讀 26,012評(píng)論 80 514
  • 0、雜記 0.1肮之、在實(shí)際的開發(fā)中掉缺,圖片資源不會(huì)存儲(chǔ)在小程序的目錄中,因?yàn)樾〕绦虻拇笮〔荒艹^1MB(現(xiàn)在改為2M)...
    it筱竹閱讀 5,340評(píng)論 0 10
  • 前沿 置身世外只為暗中觀察8昵堋?裘鳌!Hello大家好筐高,我是魔王哪吒搜囱!重學(xué)鞏固你的Vuejs知識(shí)體系丑瞧,如果有哪些知識(shí)點(diǎn)遺...
    lessonSam閱讀 1,180評(píng)論 0 13