美文网首页
商家展示功能的开发

商家展示功能的开发

作者: 五号社会好青年 | 来源:发表于2022-04-05 13:07 被阅读0次

    具体重要功能

    (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"
             >&#xe6db;</div>
            <div class="search__content">
                <span class="search__content__icon iconfont">&#xe615;</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">&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切换相关的逻辑
    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 ?'&#xe652;':'&#xe619;'"
                    ></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 ? '&#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">
                总计:<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}`}">
                    去结算
                </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"
            >&#xe6db;</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">&#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">实付金额 ¥<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">&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>
    

    下单界面具体实现界面

    下单1.png 下单2.png

    **点击确认支付和取消订单均跳转到首页,因为没有做支付界面

    相关文章

      网友评论

          本文标题:商家展示功能的开发

          本文链接:https://www.haomeiwen.com/subject/lojksrtx.html