🚀 个人简介:某大型测绘遥感企业资深Webgis开发工程师,软件设计师(中级)、优快云优质创作者
💟 作 者:柳晓黑胡椒❣️
📝 专 栏:vue实践
🌈 若有帮助,还请关注 ➕ 点赞➕收藏,不行的话我再努努力💪💪💪
vite实现商城购物车及折扣预设
需求背景
实现商城的购物车功能,和折扣预设
解决效果
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实现商城购物车及折扣预设