baodeng_xcx/page_user/diandan/index.vue

1001 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="">
<u-navbar title="点单" :border-bottom="false" :background="bgc" back-icon-color="#fff" title-color='#fff'
title-size='36' height='36' id="navbar">
</u-navbar>
<view class="seat-selection" @click="chooseSeat">
<image class="qian" src="https://api.ccttiot.com/smartmeter/img/static/uGlQHVe71tVCmnjW8Tsu" mode=""></image> <text>{{kazuoid}}</text>
<image class="hou" src="https://api.ccttiot.com/smartmeter/img/static/uBrspupsx9JJF7xv7oDQ" mode="aspectFit"></image>
</view>
<view class="container">
<!-- 左侧商品分类导航 -->
<view class="category-nav">
<view class="category-item" v-for="(category, index) in categories" :key="index"
@click="selectCategory(category,index)">
<text :class="{ active: currentCategoryIndex == index }">{{ category.name }}</text>
</view>
</view>
<!-- 右侧商品列表 -->
<view class="product-list">
<view class="product-item" v-for="(product, index) in filteredProducts" :key="index">
<image class="product-image-placeholder" :src="product.image" mode="aspectFill"></image>
<view class="product-info">
<view class="product-name">{{ product.name }}</view>
<view class="product-status">已售 {{product.sales}} </view>
<view class="product-price">
价格: ¥{{ product.minPrice }}
</view>
</view>
<view class="product-actions">
<text>{{ getCartItemQuantity(product.id) }}</text>
<button @click="increaseQuantity(product,0)">+</button>
</view>
</view>
<view class="" style="width: 100%;text-align: center;margin-top: 50rpx;">
当前没有更多商品咯...
</view>
</view>
</view>
<view class="jiesuan">
<view class="bot">
<view class="" @click="btngw">
<image src="https://api.ccttiot.com/smartmeter/img/static/uMcQnAhQkEMQKThZM0tJ" mode="aspectFill"></image>
共<text>{{totalQuantity}}</text>件 共<text>¥{{totalPrice}}</text>元
</view>
<view class="anniu" @click="btnxuanhao">
我已选好
</view>
</view>
</view>
<view class="gouwu" v-if="gouwuflag">
<view class="top">
已选商品
<text @click="gouwuflag = false">×</text>
</view>
<view class="qingkong" @click="btnqk">
<image src="https://api.ccttiot.com/smartmeter/img/static/ulcfcW1isbjq2dVnQsUk" mode="aspectFill"></image> 清空购物车
</view>
<view class="product-list" style="height: 37vh;overflow: scroll;">
<view class="product-item" v-for="(product, index) in selectedItems" :key="index">
<image class="product-image-placeholder" :src="product.image" mode="aspectFill"></image>
<view class="product-info">
<view class="product-name">{{ product.goodsName }}</view>
<view class="" style="font-size: 24rpx;color: #ccc;margin-bottom: 10rpx;">
{{ product.description }}
</view>
<view class="product-price">
价格: ¥{{ product.price }}
</view>
</view>
<view class="product-actions">
<button @click="decreaseQuantity(product,1)" :disabled="product.quantity <= 0">-</button>
<text>{{ product.quantity }}</text>
<button @click="increaseQuantity(product,1)">+</button>
</view>
</view>
<view class="" style="text-align: center;width: 100%;margin-top: 50rpx;color: #ccc;">
没有更多选购商品咯...
</view>
</view>
</view>
<view class="mask" v-if="gouwuflag"></view>
<!-- 位置选择弹窗 -->
<view class="seat-selector" :class="{ 'show': addmenflag }" v-show="addmenflag">
<view class="selector-header">
<view class="title">选择卡座位置</view>
<view class="floor-tabs">
<view v-for="floor in floors" :key="floor.id"
:class="['floor-tab', currentFloor === floor.id ? 'active' : '']"
@click="selectFloor(floor.id)">
{{floor.name}}
</view>
</view>
</view>
<view class="selector-content">
<view class="area-list">
<view v-for="area in areas" :key="area.id"
:class="['area-item', currentArea === area.id ? 'active' : '']" @click="selectArea(area.id)">
{{area.name}}
</view>
</view>
<view class="seat-grid">
<view v-for="seat in seats" :key="seat.id"
:class="['seat-item', currentSeat === seat.id ? 'active' : '']" @click="selectSeat(seat)">
{{seat.name}}
</view>
</view>
</view>
<view class="selector-footer">
<view class="btn cancel" @click="addmenflag = false">取消</view>
<view class="btn confirm" @click="confirmSeatSelection">确认</view>
</view>
</view>
<view class="mask" v-if="addmenflag"></view>
<!-- 特殊要求弹窗 -->
<view class="teshu" v-if="teshuflag">
<view class="top">
{{shopobj.name}}
</view>
<view style="max-height: 46vh; overflow: scroll;">
<view v-for="(specGroup, index) in shopobj.specVO" :key="specGroup.id">
<!-- 规格组名称 -->
<view class="ts">{{specGroup.name}}</view>
<!-- 规格选项 -->
<view class="yqlist">
<view
v-for="option in specGroup.specValueVO"
:key="option.id"
:class="selectedSpecs[specGroup.id] === option.id ? 'ggactive' : ''"
@click="selectSpec(specGroup.id, option.id)">
{{option.value}}
</view>
</view>
</view>
</view>
<view class="anniu">
<view class="qx" @click="teshuflag = false">
取消
</view>
<view class="qd" @click="btnadd">
(¥{{price}})确定
</view>
</view>
</view>
<view class="mask" style="z-index: 2;" v-if="teshuflag"></view>
</view>
</template>
<script>
import { number } from 'echarts'
export default {
data() {
return {
categories: [],
currentCategoryIndex: 0,
categoryId:0,
products: [],
bgc: {
backgroundColor: "#111111",
},
teshuflag:false,
gouwuflag:false,
addmenflag: false,
currentFloor: '',
currentArea: '',
currentSeat: '',
selectedPosition: '',
floors: [],
areas: [],
seats: [],
kazuoid: '请选择卡座',
partId:'',
fenquid:'',
zuoweiid:'',
selectedItems:[],
shopobj:{},
qdshop:{},
num:'',
waiindex:'',
neiindex:'',
selectedSpecs: {}, // 格式: { 规格组ID: 选中的规格值ID }
price: 0,
matchedSku: null,
totalQuantity: 0,
totalPrice: 0,
boothId:'',
skuList:[],
xiadanflag:true
}
},
computed: {
filteredProducts() {
// 这里可以根据currentCategoryIndex进行分类过滤示例中暂不区分
return this.products
}
},
onLoad() {
this.getlouceng()
this.getlist()
},
onShow() {
this.xiadanflag = true
},
methods: {
// 获取购物车中商品的数量
getCartItemQuantity(productId) {
// 找到所有相同商品ID的购物车项
const cartItems = this.selectedItems.filter(item => item.goodsId == productId);
// 计算所有规格的数量总和
return cartItems.reduce((total, item) => total + item.quantity, 0);
},
// 计算总数量和总价格
calculateTotal() {
this.totalQuantity = this.selectedItems.reduce((sum, item) => sum + item.quantity, 0);
this.totalPrice = this.selectedItems.reduce((sum, item) => sum + (item.price * item.quantity), 0).toFixed(2);
},
// 点击清空购物车
btnqk(){
let that = this
uni.showModal({
title: '提示',
content: '您确定要清空当前购物车吗?',
success: function (res) {
if (res.confirm) {
that.selectedItems = []
that.calculateTotal()
} else if (res.cancel) {
console.log('用户点击取消');
}
}
})
},
// 请求商品分类
getlist(){
this.$u.get(`/app/goodsCategory/list?pageNum=1&pageSize=999`).then(res =>{
if(res.code == 200){
this.categories = res.data
this.categoryId = res.data[0].id
this.getshoplist()
}
})
},
// 请求商品列表
getshoplist(){
this.$u.get(`/app/goods/list?pageNum=1&pageSize=999&categoryId=${this.categoryId}`).then(res =>{
if(res.code == 200){
this.products = res.data
}
})
},
// 点击我已选好
btnxuanhao(){
if(this.xiadanflag == true){
this.xiadanflag = false
if(this.kazuoid == '请选择卡座'){
uni.showToast({
title: '请选择卡座信息',
icon: 'none',
duration:3000
})
this.xiadanflag = true
}else if(this.totalQuantity > 0){
this.skuList = this.selectedItems.map(item => ({
skuId: item.id,
price: item.price,
number: item.quantity
}))
let data = {
payAmount:this.totalPrice,
boothId:this.boothId,
skuList:this.skuList
}
this.$u.post(`/app/goodsOrder`,data).then(res =>{
if(res.code == 200){
uni.navigateTo({
url:'/page_user/diandan/xiaofei?no=' + res.msg + '&totalQuantity=' + this.totalQuantity
})
}else{
uni.showToast({
title: res.msg,
icon: 'none',
duration:3000
})
this.xiadanflag = true
}
})
}else{
uni.showToast({
title: '请选择需要的商品',
icon: 'none',
duration:3000
})
this.xiadanflag = true
}
}
},
// 点击选择位置
confirmSeatSelection() {
const floor = this.floors.find(f => f.id === this.currentFloor)
const area = this.areas.find(a => a.id === this.currentArea)
const seat = this.seats.find(s => s.id === this.currentSeat)
if (floor && area && seat) { //判断有楼层,分区,卡座
this.selectedPosition = `${floor.name}-${area.name}-${seat.name}`
this.kazuoid = `${seat.name}`
this.boothId = `${seat.id}`
this.addmenflag = false
} else if(floor && seat){ //判断只有楼层和卡座
this.selectedPosition = `${floor.name}-${seat.name}`
this.kazuoid = `${seat.name}`
this.boothId = `${seat.id}`
this.addmenflag = false
} else {
uni.showToast({
title: '请选择完整的卡座信息',
icon: 'none'
})
}
},
// 查询楼层列表
getlouceng() {
this.$u.get(`/bst/part/list?storeId=${this.$store.state.storeId}&pageNum=1&pageSize=99&isFloor=true`).then(res => {
if (res.code === 200) {
this.floors = []
this.partId = res.data.partId
res.data.forEach(item => {
if (item.parentId == null) {
this.floors.push({
id: item.partId,
name: item.partName
})
}
})
if (this.floors.length > 0) {
this.fenquid = this.floors[0].id
this.currentFloor = this.floors[0].id
this.getfenqu()
}
}
})
},
// 查询分区
getfenqu() {
if (this.floors.length > 0) {
this.$u.get(`/bst/part/list?parentId=${this.fenquid}&pageNum=1&pageSize=99`).then(res => {
if (res.code === 200) {
this.areas = []
if(res.data){
res.data.forEach(item => {
this.areas.push({
id: item.partId,
name: item.partName
})
})
}
if (this.areas.length > 0) { //如果有分区则进行查下去没有分区则直接拿楼层id查卡座
this.zuoweiid = this.areas[0].id
this.currentArea = this.areas[0].id
this.getkazuo()
}else{
this.getkazuo()
}
}
})
}
},
// 查询卡座
getkazuo() {
this.$u.get(`/bst/booth/list?partId=${this.zuoweiid == undefined ? this.partId : this.zuoweiid }&pageNum=1&pageSize=99`).then(res => {
if (res.code === 200) {
this.seats = []
res.rows.forEach(item => {
this.seats.push({
id: item.boothId,
name: item.boothName
})
})
}
})
},
// 切换楼层
selectFloor(floorId) {
this.currentFloor = floorId
this.fenquid = floorId
this.getfenqu()
},
//切换分区
selectArea(areaId) {
this.currentArea = areaId
this.zuoweiid = areaId
this.getkazuo()
},
// 切换卡座
selectSeat(seat) {
this.currentSeat = seat.id
console.log(seat,this.currentSeat);
},
// 点击显示选择卡座位置
chooseSeat() {
this.addmenflag = true
},
// 点击分类请求商品列表
selectCategory(item,index) {
this.categoryId = item.id
this.currentCategoryIndex = index
this.getshoplist()
},
// 选择规格
selectSpec(groupId, optionId) {
this.$set(this.selectedSpecs, groupId, optionId)
this.updateMatchedSku()
},
// 更新匹配的SKU
updateMatchedSku() {
const selectedOptions = Object.values(this.selectedSpecs)
// 调试:打印关键数据
// console.log('当前选中选项ID:', selectedOptions)
console.log('所有SKU:', this.shopobj.skuVO.map(sku => ({
id: sku.id,
specs: sku.specs,
price: sku.price,
})))
// 匹配逻辑
this.matchedSku = this.shopobj.skuVO.find(sku => {
const isMatch = selectedOptions.every(optionId =>
sku.specs.includes(optionId))
// console.log(`检查SKU ${sku.id}:`, sku.specs, '匹配结果:', isMatch)
return isMatch
})
this.price = this.matchedSku?.price || 0
console.log('最终匹配:', this.matchedSku || '无匹配')
},
// 确定加入购物车
btnadd() {
// 检查是否所有规格都已选择
const selectedSpecsCount = Object.keys(this.selectedSpecs).length
const totalSpecsCount = this.shopobj.specVO.length
if (selectedSpecsCount !== totalSpecsCount) {
uni.showToast({
title: '请选择规格',
icon: 'none',
duration: 3000
})
return
}
// 1. 检查是否有匹配的SKU
if (!this.matchedSku) {
uni.showToast({
title: '请先选择完整规格',
icon: 'none',
duration:3000
})
return
}
// 2. 准备要添加的商品数据
const productToAdd = {
...this.matchedSku, // 复制SKU所有属性
quantity: 1, // 初始数量设为1
selectedSpecs: { ...this.selectedSpecs } // 保存当前选择的规格
}
// 3. 检查是否已存在相同规格的商品
const existingIndex = this.selectedItems.findIndex(item =>
item.id === this.matchedSku.id &&
this.isSameSpecs(item.selectedSpecs, this.selectedSpecs)
)
if (existingIndex >= 0) {
// 已存在则增加数量
this.selectedItems[existingIndex].quantity++
// 更新商品列表中的数量
const productIndex = this.products.findIndex(p => p.id === this.matchedSku.id)
if (productIndex >= 0) {
this.products[productIndex].quantity = this.selectedItems[existingIndex].quantity
}
uni.showToast({
title: '商品数量已增加',
icon: 'success',
duration:3000
})
} else {
// 不存在则添加新项
this.selectedItems.push(productToAdd)
// 更新商品列表中的数量
const productIndex = this.products.findIndex(p => p.id === this.matchedSku.id)
if (productIndex >= 0) {
this.products[productIndex].quantity = 1
}
uni.showToast({
title: '已加入购物车',
icon: 'success',
duration:3000
})
}
// 4. 关闭规格选择弹窗
this.teshuflag = false
this.calculateTotal()
// 5. 打印调试信息
console.log('当前购物车:', this.selectedItems)
},
// 辅助方法:比较两个规格是否相同
isSameSpecs(specs1, specs2) {
const keys1 = Object.keys(specs1).sort()
const keys2 = Object.keys(specs2).sort()
if (keys1.length !== keys2.length) return false
return keys1.every(key => specs1[key] === specs2[key])
},
// 根据id获取商品详情
getxq(id){
this.$u.get(`/app/goods/${id}`).then(res => {
if (res.code == 200) {
this.shopobj = res.data
this.teshuflag = true
}
})
},
// 点击商品加入购物车
increaseQuantity(product, num) {
if(num == 0){
this.selectedSpecs = {}
this.price = 0
this.qdshop = product
this.num = num
this.getxq(product.id) //点击商品的时候获取商品信息规格
} else {
const existingItem = this.selectedItems.find(item =>
item.id === product.id &&
this.isSameSpecs(item.selectedSpecs, product.selectedSpecs)
)
if (existingItem) {
existingItem.quantity++
} else {
this.selectedItems.push({
...product,
quantity: 1
})
}
this.calculateTotal()
}
},
// 点击商品从购物车列表删除商品
decreaseQuantity(product, num) {
const existingItem = this.selectedItems.find(item =>
item.id === product.id &&
this.isSameSpecs(item.selectedSpecs, product.selectedSpecs)
)
if (existingItem) {
if (existingItem.quantity > 1) {
existingItem.quantity--
} else {
const index = this.selectedItems.indexOf(existingItem)
this.selectedItems.splice(index, 1)
}
this.calculateTotal()
}
},
// 点击显示购物车列表商品
btngw(){
this.gouwuflag = true
}
}
}
</script>
<style scoped lang="less">
.container {
display: flex;
height: 87vh;
background-color: #1a1a2e;
color: white;
}
.ggactive{
background-color: #FF8998 !important;
color: #fff !important;
}
page {
background-color: #111111;
}
.teshu{
width: 658rpx;
max-height: 999rpx;
background: #1C1C1E;
border-radius: 16rpx 16rpx 16rpx 16rpx;
padding: 34rpx 38rpx;
box-sizing: border-box;
position: fixed;
top: 20%;
left: 50%;
transform: translateX(-50%);
z-index: 3;
.top{
font-size: 32rpx;
color: #FFFFFF;
width: 100%;
text-align: center;
}
.ts{
font-size: 32rpx;
color: #FFFFFF;
margin-top: 36rpx;
}
.yqlist{
width: 100%;
display: flex;
flex-wrap: wrap;
view{
max-width: 884rpx;
padding: 4rpx 30rpx;
box-sizing: border-box;
height: 64rpx;
background: #2F2F2F;
border-radius: 6rpx 6rpx 6rpx 6rpx;
font-size: 24rpx;
color: #FFFFFF;
text-align: center;
line-height: 60rpx;
margin-right: 10rpx;
margin-top: 20rpx;
}
}
.anniu{
display: flex;
justify-content: space-between;
margin-top: 60rpx;
.qx{
background-color: transparent;
border: 1px solid #FF8998;
color: #FF8998;
}
view{
width: 262rpx;
height: 76rpx;
background: #FF8998;
border-radius: 6rpx 6rpx 6rpx 6rpx;
text-align: center;
line-height: 76rpx;
font-size: 32rpx;
color: #3D3D3D;
font-weight: 600;
color: #fff;
}
}
}
.mask{
width: 100%;
height: 100vh;
background: #010000;
border-radius: 0rpx 0rpx 0rpx 0rpx;
opacity: 0.5;
position: fixed;
top: 0;
left: 0;
}
.seat-selector {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
height: 58vh;
background-color: #0B0B0B;
border-radius: 40rpx 40rpx 0 0;
z-index: 999;
padding-bottom: 100rpx;
box-sizing: border-box;
transform: translateY(100%);
transition: transform 0.3s ease-out;
&.show {
transform: translateY(0);
}
.selector-header {
padding: 40rpx 30rpx;
.title {
font-size: 32rpx;
color: #fff;
margin-bottom: 30rpx;
}
.floor-tabs {
display: flex;
gap: 20rpx;
.floor-tab {
padding: 10rpx 40rpx;
background: #1A1A1A;
color: #fff;
border-radius: 10rpx;
font-size: 28rpx;
&.active {
background: #FF8998;
}
}
}
}
.selector-content {
display: flex;
height: calc(100% - 300rpx);
.area-list {
width: 200rpx;
background: #1A1A1A;
// padding: 20rpx 0;
.area-item {
height: 80rpx;
line-height: 80rpx;
text-align: center;
color: #fff;
font-size: 28rpx;
&.active {
background: #FF8998;
}
}
}
.seat-grid {
flex: 1;
padding: 20rpx;
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 20rpx;
overflow-y: auto;
.seat-item {
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: #1A1A1A;
color: #fff;
border-radius: 10rpx;
font-size: 28rpx;
&.active {
background: #FF8998;
}
}
}
}
.selector-footer {
position: absolute;
bottom: 44rpx;
left: 0;
width: 100%;
height: 120rpx;
display: flex;
justify-content: space-between;
padding: 20rpx 30rpx;
box-sizing: border-box;
background: #0B0B0B;
.btn {
width: 45%;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 10rpx;
font-size: 28rpx;
&.cancel {
background: #1A1A1A;
color: #fff;
}
&.confirm {
background: #FF8998;
color: #fff;
}
}
}
}
.gouwu{
width: 750rpx;
height: 786rpx;
background: #010000;
border-radius: 0rpx 0rpx 0rpx 0rpx;
position: fixed;
bottom: 160rpx;
left: 0;
z-index: 1;
.product-list{
background-color: #010000;
.product-item{
background-color: #010000;
font-weight: 600;
font-size: 32rpx;
color: #FFFFFF;
.product-info{
.product-price{
color: #FF8998;
font-size: 32rpx;
font-weight: 400;
}
.product-status{
width: 320rpx;
height: 46rpx;
background: #1A1A1A;
border-radius: 4rpx 4rpx 4rpx 4rpx;
padding-left: 12rpx;
line-height: 46rpx;
font-size: 24rpx;
font-weight: 400;
color: #FF8998;
}
}
}
}
.qingkong{
width: 100%;
height: 90rpx;
line-height: 90rpx;
text-align: right;
background: #1A1A1A;
border-radius: 0rpx 0rpx 0rpx 0rpx;
padding-right: 30rpx;
box-sizing: border-box;
color: #fff;
image{
width: 32rpx;
height: 32rpx;
vertical-align: text-top;
margin-right: 14rpx;
}
}
.top{
width: 100%;
height: 98rpx;
line-height: 98rpx;
text-align: center;
position: relative;
color: #fff;
text{
font-size: 40rpx;
color: #fff;
position: absolute;
top: 0;
right: 36rpx;
}
}
}
.jiesuan{
width: 750rpx;
height: 160rpx;
line-height: 160rpx;
background: #111111;
box-shadow: 0rpx -8rpx 6rpx 0rpx rgba(0,0,0,0.2);
border-radius: 0rpx 0rpx 0rpx 0rpx;
position: fixed;
left: 0;
bottom: 0;
z-index: 1;
.bot{
display: flex;
justify-content: space-between;
align-items: center;
padding-left: 22rpx;
box-sizing: border-box;
.anniu{
width: 176rpx;
height: 80rpx;
background: #FF8998;
border-radius: 6rpx 6rpx 6rpx 6rpx;
display: flex;
justify-content: center;
}
view{
display: flex;
align-items: center;
font-size: 28rpx;
color: #FFFFFF;
text{
color: #FF8998;
}
image{
width: 40rpx;
height: 40rpx;
margin-right: 24rpx;
}
}
}
}
.seat-selection {
display: flex;
align-items: center;
width: 100%;
color: #fff;
background-color: #111111;
padding: 15rpx;
z-index: 1;
.qian{
width: 36rpx;
height: 36rpx;
margin-right: 14rpx;
}
.hou{
width: 32rpx;
height: 32rpx;
}
}
.seat-selection text {
font-size: 28rpx;
}
.seat-selection image {
width: 20rpx;
height: 20rpx;
}
.category-nav {
width: 200rpx;
background-color: #111111;
padding: 20rpx 0;
text-align: center;
}
.category-item {
padding: 15rpx 20rpx;
cursor: pointer;
}
.category-item text {
font-size: 28rpx;
color: #ccc;
}
.category-item text.active {
color: #FF8998;
}
.product-list {
flex: 1;
padding: 20rpx;
overflow-y: auto;
background-color: #1A1A1A;
}
.product-item {
display: flex;
align-items: center;
background-color: #111111;
padding: 20rpx;
border-radius: 10rpx;
margin-bottom: 20rpx;
}
.product-image-placeholder {
width: 160rpx;
height: 160rpx;
background-color: #ccc;
margin-right: 20rpx;
border-radius: 5rpx;
}
.product-info {
flex: 1;
}
.product-name {
font-size: 32rpx;
margin-bottom: 10rpx;
}
.product-status {
font-size: 24rpx;
color: #ccc;
margin-bottom: 10rpx;
}
.product-price {
font-size: 28rpx;
}
.member-price {
color: #e94b64;
margin-left: 10rpx;
}
.product-actions {
display: flex;
align-items: center;
}
.product-actions button {
width: 40rpx;
height: 40rpx;
line-height: 40rpx;
font-size: 28rpx;
background-color: #e94b64;
color: white;
border: none;
border-radius: 50%;
padding: 0;
margin: 0 10rpx;
}
.product-actions button:disabled {
opacity: 0.5;
}
</style>