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

商家展示功能的开发

作者: 五号社会好青年 | 来源:发表于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

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

相关文章

  • 商家展示功能的开发

    具体重要功能 (1)封装一个功能函数把有多个界面要使用的函数封装起来commonCartEffects.js (2...

  • 企业如何选择合适定制商城系统开发商?

    定制商城系统开发是目前比较流行的商城开发模式,所谓定制开发是指在商家在除了基本功能之外,还有其他功能需求都可以选择...

  • 餐饮版小程序 打通餐饮线下消费关键场景

    微炫客餐饮版小程序提供菜品展示、商家详情、下单、地址添加、在线点餐、在线支付、智慧外卖等功能,帮助餐饮商家完成线上...

  • 3月第一周-jessie

    千牛——淘宝商家端APP 首页 亮点:首页不仅仅是功能入口,同时展示用户关心的数据,更容易向用户展示效果;同时支持...

  • BlaCat项目周报

    上周周报(9.3-9.9) 1. 完善演示版本未完成功能,游戏信息展示,论坛发帖功能等功能 2. 开发者后台开发,...

  • 酒店版小程序 扩大酒店业获客半径

    微炫客酒店版小程序提供酒店展示、酒店管理、房型展示、预约订房、在线支付等功能,帮助商家实现线上线下流量的互通、引流...

  • 小鹅通商家学院:如何对课程内容进行分组?

    商品分组,是一种知识商品归类展示的功能。可以帮助商家把店铺内的知识商品进行自定义分组,商品分组后可设置展示在店铺主...

  • 开源一个网络图片浏览器HooPhotoBrowser

    在公司开发项目中需要弹出展示从网络上下载的图片,并提供滑动展示功能。目前采用同事开发的图片浏览器,但还有优化的控件...

  • 和IT链小程序与时俱进

    IT链小程序是什么? IT链小程序是微信上为商家展示信息的新功能,无需下载、不用安装、一次使用、自...

  • 商城APP开发功能有哪些

    商城APP开发功能有哪些 商城APP开发概述 商城APP是专门为商业零售及服务企业开发的手机客户端系统,帮助商家快...

网友评论

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

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