baodeng_xcx/page_user/diandan/index.vue

1001 lines
24 KiB
Vue
Raw Normal View History

2025-06-19 17:41:39 +08:00
<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>