vite实现商城购物车及折扣预设

该文章展示了一个基于Vite的商城应用中购物车功能和折扣预设的实现。通过Vue组件`index.vue`和`discountCalculation.vue`,实现了商品搜索、列表展示、数量增减、折扣设置等功能,并提供了折扣计算器的交互逻辑。代码中包含了商品的添加、删除以及总价和折扣价的计算方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:vue实践
🌈 若有帮助,还请关注点赞收藏,不行的话我再努努力💪💪💪

需求背景

实现商城的购物车功能,和折扣预设

解决效果

在这里插入图片描述

在这里插入图片描述

index.vue

<!--/**
 * @author: liuk
 * @date: 2023/3/30
 * @describe: 创建业务
*/-->
<template>
  <div>
    <el-row>
      <el-col :span="17" class="tombstone-main">
        <el-form ref="tombstoneRef" :model="form" :rules="rules" label-width="80px" label-position="right">
          <el-scrollbar ref="scrollbarRef" class="scrollbar-flex-content" height="600px" style="overflow-x: hidden"
          >
			<!-- 无关代码已省略 -->
            <div class="shop-search">
              <h2>商品列表</h2>
              <el-form-item label="商品名称">
                <el-input v-model="shopName" style="width: 200px">
                  <template #append>
                    <el-icon>
                      <Search/>
                    </el-icon>
                  </template>
                </el-input>
              </el-form-item>
            </div>
            <el-table :data="tableList" border>
              <el-table-column min-width="80" label="序号" align="center" prop="a"/>
              <el-table-column min-width="120" label="图片" align="center" prop="b"/>
              <el-table-column min-width="120" label="规格" align="center" prop="c"/>
              <el-table-column min-width="120" label="单位" align="center" prop="d"/>
              <el-table-column min-width="120" label="库存" align="center" prop="e"/>
              <el-table-column min-width="120" label="单价(元)" align="center" prop="price"/>
              <el-table-column fixed="right" label="操作" align="center" class-name="small-padding fixed-width"
                               min-width="210">
                <template #default="scope">
                  <el-button type="success" @click="addShop(scope.row)"> + 添加</el-button>
                </template>
              </el-table-column>
            </el-table>
          </el-scrollbar>
        </el-form>
      </el-col>
      <el-col :span="6" :push="1">
        <div v-if="!shoppCart.length">
          <el-empty description="请先选择商品"/>
        </div>
        <div class="order-wrap" v-else>
          <h1>已选购({{ shoppCart.length }})</h1>
          <ul class="order-item">
            <li v-for="item in shoppCart" :key="item">
              <div class="img">
                <img src=""/>
              </div>
              <div class="main-info">
                <p>{{ item.a }}</p>
                <div class="con-info">
                  <div class="price-wrap">
                    <del class="discount" v-if="item.remissionPrice &&item.remissionPrice != 0">{{ item.remissionPrice }}</del>
                    <p class="price">{{ discountPrice(item) }}</p>
                  </div>
                  <div>
                    <el-icon color="blue">
                      <Setting @click="setDiscount(item)"/>
                    </el-icon>
                    <el-input-number :min="1" :max='item.e' v-model="item.num" @change="numberChange($event,item)"/>
                    <el-icon @click="delShop(item)">
                      <Delete/>
                    </el-icon>
                  </div>
                </div>
              </div>
            </li>
          </ul>
          <el-divider/>
          <div class="footer">
            <div class="info">
              <p>总折扣:<span class="discount">{{ totalDiscount ? totalDiscount.toFixed(2) : 0 }}</span></p>
              <p>总应收:<span class="price">{{ totalPrice ? totalPrice.toFixed(2) : 0 }}</span></p>
            </div>
            <div class="pay">结算</div>
          </div>
        </div>
      </el-col>
    </el-row>
    <!--    折扣计算器-->
    <el-dialog title="折扣预设" v-model="discountCalculationOpen" width="450px">
      <discount-calculation :data="curDiscountVal" @submit="setRemissionPrice"
                            @close="discountCalculationOpen=false"/>
    </el-dialog>
  </div>
</template>


<script setup>
import {getCurrentInstance, onMounted, reactive, toRefs} from 'vue'
import {listUser} from "@/api/cemetery/screening"
import _ from "lodash"

// components
import DiscountCalculation from "./discountCalculation"

// @Props
const props = defineProps(['customerData', 'id', 'blessingInformation'])
// @Emits
const emit = defineEmits(['close', 'getlist'])

const {proxy} = getCurrentInstance()
const model = reactive({
  form: {
    installationTime: proxy.parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')
  },// 表单数据
  users: [],//使用人列表
  isView: true,//编辑模式
  tableList: [//列表数据
    {id: 1, a: 1, b: "", num: 0, d: '个', e: 4, price: 6},
    {id: 2, a: 2, b: "", num: 0, d: '个', e: 4, price: 6},
    {id: 3, a: 3, b: "", num: 0, d: '个', e: 4, price: 6},
    {id: 4, a: 4, b: "", num: 0, d: '个', e: 4, price: 6}
  ],
  shopName: "",//商品名称
  shoppCart: [],//购物车
  discountCalculationOpen: false,//折扣计算器
  curDiscountVal: 0,//当前修设置折扣的价钱
  curShop: {},//当前打折商品
})
const {form, users, isView, tableList, shopName, shoppCart, discountCalculationOpen, curDiscountVal} = toRefs(model)

// 总折扣
const totalDiscount = computed(() => {
  return model.shoppCart.reduce((sum, item) => {
    const discount = item.discountCount || 1
    return sum * 1 + item.num * item.price * (1 - discount)
  }, 0) || 0
})

// 总应收
const totalPrice = computed(() => {
  return model.shoppCart.reduce((sum, item) => {
    const discount = item.discountCount || 1
    return sum + item.num * item.price * discount
  }, 0) || 0
})

// 商品折后价格
const discountPrice = (item) =>{
  const discount = item.discountCount || 1
  return (item.num * item.price * discount).toFixed(2)
}


// 商品数量变化
const numberChange = (num, item) => {
  if (item.discountCount) {
    item.remissionPrice = (num * item.price * (1 - item.discountCount)).toFixed(2)
  }
}

// 获取数据
const getlist = () => {
  listUser(['sale']).then(res => {
    model.users = res.data.map((item) => ({
      label: item.nickName,
      value: item.userId
    }))
  })
}

// 设置折扣弹窗
const setDiscount = (item) => {
  model.discountCalculationOpen = true
  model.curDiscountVal =item.price * item.num
  model.curShop = item
}

// 设置实际折扣
const setRemissionPrice = (item) => {
  const {discountCount, remissionPrice} = item
  model.curShop.remissionPrice = remissionPrice;
  model.curShop.discountCount = discountCount;
  model.discountCalculationOpen = false
  item.remissionPrice = (num * item.price * (1 - item.discountCount)).toFixed(2)
}
// 添加商品
const addShop = (item) => {
  if (_.indexOf(model.shoppCart, item) === -1) {
    model.shoppCart.push(item)
  } else {
    item.num++
    numberChange(item.num, item)
  }
}

// 删除商品
const delShop = (item) => {
  let index = _.findIndex(model.shoppCart, (val) => val.id === item.id)
  item.num = 0
  item.remissionPrice = 0
  item.discountCount = 0
  model.shoppCart.splice(index, 1)
}

// 获取商品
const getShop = () => {
  let params = {
    putShelf: 1,
    hide: 0,
  }
  listGoods(params).then(res => {
    console.log(res.data, 222111)
  })
}

onMounted(() => {
  getlist()
})

const rules = {}
</script>

<style lang="scss" scoped>
.tombstone-main {
  //background: #eee;
}

.shop-search {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0 15px;
  background: #eeeff3;

  h2 {
    margin: 0;
  }

  .el-form-item--default {
    margin: 10px 0;
  }


}

:deep(.el-table__inner-wrapper) {
  height: 440px !important;
}

.order-wrap {
  height: calc(100% - 190px);

  .order-item {
    padding: 0;
    list-style: none;
    height: 100%;

    li {
      display: flex;
      justify-content: space-between;
      margin: 5px 0;

      .img {
        width: 70px;
        height: 70px;

        img {
          display: block;
          width: 100%;
          height: 100%;
        }
      }

      .main-info {
        flex: 1;
        padding: 5px;
        box-sizing: border-box;

        p {
          width: 270px;
          margin: 0 0 5px;
          font-size: 20px;
          overflow: hidden;
          text-overflow: ellipsis;
          white-space: nowrap;
        }

        .con-info {
          display: flex;
          justify-content: space-between;

          .price-wrap {
            width: 80px;

            .discount {
              margin: 0;
              font-size: 12px;
              font-weight: bold;
              color: red;
            }

            .price {
              margin: 0;
              font-size: 16px;
              font-weight: bold;
              color: #70cb4f;
            }
          }

        }
      }
    }
  }

  .footer {
    display: flex;

    .info {
      flex: 1;
      font-size: 16px;
      font-weight: bold;

      .discount {
        color: red;
      }

      .price {
        color: rgb(112, 203, 79);;
      }
    }

    .pay {
      width: 100px;
      height: 60px;
      margin: auto;
      line-height: 60px;
      text-align: center;
      font-size: 20px;
      background: green;
      color: #fff;
    }
  }
}
</style>

discountCalculation.vue

<!--/**
 * @author: liuk
 * @date: 2023/3/30
 * @describe: 折扣计算器
*/-->
<!--/**
 * @author: liuk
 * @date: 2023/3/30
 * @describe: 折扣计算器
*/-->
<template>
  <div>
    <div class="discount-wrap">
      <div :class="discountCount==0.9?'select':''" @click="discountCount=0.9">9</div>
      <div :class="discountCount==0.8?'select':''" @click="discountCount=0.8">8</div>
      <div :class="discountCount==0.7?'select':''" @click="discountCount=0.7">7</div>
      <div :class="discountCount==0.6?'select':''" @click="discountCount=0.6">6</div>
      <div :class="discountCount==0.5?'select':''" @click="discountCount=0.5">5</div>
      <div :class="discountCount==0.4?'select':''" @click="discountCount=0.4">4</div>
      <div :class="discountCount==0.3?'select':''" @click="discountCount=0.3">3</div>
      <div :class="discountCount==0.2?'select':''" @click="discountCount=0.2">2</div>
      <el-input-number v-model="discountCount" :step="0.01" :min="0.1" :max="1"/>
    </div>
    <div class="disabled-price">
      <span> 优惠金额:</span>
      <el-input type="number" style="width: 290px" v-model="remissionPrice" @change="discountChange"
                :min="0" :max="actualPrice*0.9"/>
    </div>
    <div class="disabled-count">
      <del>{{ actualPrice.toFixed(2) }}</del>
      <p>{{ FinallyPrice }}</p>
    </div>
    <el-row justify="end">
      <el-button @click="emit('close')">取消</el-button>
      <el-button type="primary" @click="submitForm">确认</el-button>
    </el-row>
  </div>
</template>

<script setup>

// @Props
import {computed, toRefs, watch} from "vue";

const props = defineProps(['data'])
// @Emits
const emit = defineEmits(['submit', 'close'])

const model = reactive({
  actualPrice: 0,// 需折扣金额
  discountCount: 1,// 折扣率
  remissionPrice: 0,//优惠金额
})
const {actualPrice, discountCount, remissionPrice} = toRefs(model)

// 最终金额
const FinallyPrice = computed(() => (model.actualPrice - model.remissionPrice)?.toFixed(2))

watch(
    () => props.data,
    (val) => {
      model.actualPrice = JSON.parse(JSON.stringify(val)) * 1
      model.remissionPrice = 0
    },
    {immediate: true}
)

watch(
    () => discountCount.value,
    (val) => {
      model.remissionPrice = (model.actualPrice * (1 - model.discountCount))?.toFixed(2)
    }
)


// 折扣金额变化
const discountChange = (val) => {
  if (val > model.actualPrice * 0.9) {
    model.remissionPrice = model.actualPrice * 0.9
  }
  model.discountCount = (1 - (model.remissionPrice / model.actualPrice))?.toFixed(2)
}

// 提交
const submitForm = () => {
  emit('submit', {discountCount: model.discountCount, remissionPrice: model.remissionPrice},)
}

</script>

<style lang="scss" scoped>
.discount-wrap {
  display: flex;
  flex-wrap: wrap;

  > div {
    margin: 0 5px 10px;
    padding: 5px 20px;
    border: 1px solid #ccc;
    border-radius: 10px;
    cursor: default;
    user-select: none;
  }

  .select {
    border: 1px solid #439BEB;
    background: #E8F4FF;
  }

  .el-input-number {
    width: 140px;
    padding: 0;
    border: 0;
  }
}

.disabled-price {
  margin-top: 20px;

  span {
    font-size: 16px;
  }
}

.disabled-count {
  display: flex;
  margin-top: 20px;
  font-size: 30px;

  del {
    flex: 1;
    color: #bbb;
  }

  p {
    flex: 1;
    margin: 0;
    color: red;
  }
}
</style>

视频效果

vite实现商城购物车及折扣预设

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柳晓黑胡椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值