2024-01-03 14:19:05 +08:00

919 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="wrap">
<view class="menu">
<scroll-view scroll-y scroll-with-animation class="tab-view menu-scroll-view" :scroll-top="scrollTop"
:scroll-into-view="itemId">
<view v-for="(item,index) in category" :key="index" class="tab-item"
:class="[current == index ? 'tab-item-active' : '']" @tap.stop="swichMenu(index)">
<text class="hm-line-1">{{item.name}}</text>
</view>
</scroll-view>
<scroll-view :scroll-top="scrollRightTop" scroll-y scroll-with-animation class="right-box"
@scroll="rightScroll">
<view class="class-item" :id="'item' + index" v-for="(item , index) in category" :key="index">
<view class="item-title">{{item.name}}</view>
<view class="item-goods" v-for="(item1, index1) in item.goods" :key="index1">
<image :src="item1.image[0].url"></image>
<div v-if="applet.other.is_stock == 1 && item1.stock < 1" class="tag">
售罄
</div>
<div v-else-if="item1.goods_status.value == 20" class="tag">
{{item1.goods_status.text}}
</div>
<view class="title hm-line-1">{{item1.goods_name}}</view>
<view class="selling">
<text v-if="item1.selling_point">{{item1.selling_point}}</text>
</view>
<!-- 选购工具 -->
<view v-if="item1.goods_status.value == 10" class="cart-but">
<block v-if="applet.other.is_stock == 0 || (applet.other.is_stock == 1 && item1.stock > 1)">
<block v-if="item1.spec_type == 10 || item1.total_num > 0">
<view class="but add" @click="addCount(index,index1)">
<view class="icon">
<u-icon name="plus" color="#fff" bold></u-icon>
</view>
</view>
<view v-if="item1.total_num>0" class="total-num">{{item1.total_num}}</view>
<view class="but minus" v-if="item1.total_num>0"
@click="minusCount(index,index1)">
<view class="icon">
<u-icon name="minus" color="#FF9900"></u-icon>
</view>
</view>
</block>
<block v-else>
<view class="spec" @click="addCount(index,index1)">选择</view>
</block>
</block>
</view>
<view class="sold">已售{{item1.goods_sales}}份</view>
<view class="price">¥{{item1.spec[0].goods_price}}</view>
</view>
</view>
</scroll-view>
<view class="hm-footer-fixed hm-bg-f hm-border-t">
<view class="bottom-cart">
<view class="cart" @click="setCartShow()">
<view class="hm-m-t-10 hm-m-l-10">
<u-icon name="shopping-cart-fill" color="#FFF" size="40"></u-icon>
</view>
<view class="count">{{order_total_num?order_total_num:''}}</view>
</view>
<view class="total">¥{{order_total_price}}</view>
<view class="cart-but">
<u-button @click="gotoCart()" type="error" :disabled="!(order_total_num>0)">
{{(order_total_num>0)?'选好了':'请选单'}}
</u-button>
</view>
</view>
</view>
</view>
<view v-if="show">
<u-popup :show="show" round="10" @close="show = false" closeable>
<view class="spec-box">
<view class="goods">
<image :src="goods_img"></image>
<view class="goods-name">{{goods.goods_name}}</view>
<view class="spec-name">{{spec_name}}</view>
<view class="hm-col-error">
¥{{goods_price}}
<text class="hm-font-zhx hm-col-tips hm-m-l-10" v-if="line_price>0">¥{{line_price}}</text>
</view>
</view>
<view v-for="(attr, attr_idx) in specData.spec_attr" :key="attr_idx">
<view class="title" :data-id="attr.group_id">{{attr.group_name}}</view>
<view class="hm-cf hm-p-b-20">
<view v-for="(item, item_idx) in attr.spec_items" :key="item_idx" class="but"
:class="item.checked?'but-ok':'but-no'" @click="specTap(attr_idx,item_idx)">
{{item.spec_value}}
</view>
</view>
</view>
<view class="hm-p-b-20">
<view class="title">购买数量</view>
<view class="selectNumber">
<view @click="down"
:class="'default left ' + (goods.spec[spec_index].sell_num>=1?'':' default-active')">-
</view>
<view class="num">{{goods.spec[spec_index].sell_num}}</view>
<view @click="up" class="default right">+</view>
<view v-if="applet.other.is_stock==1 && stock_num < 1" class="hm-dis-block-inline hm-m-l-10">
<u-badge value="库存不足" shape="horn"></u-badge>
</view>
</view>
</view>
<view class="hm-p-lr-b25 hm-m-t-20">
<u-button @click="show = false;" type="warning" text="加入购物车"></u-button>
</view>
</view>
</u-popup>
</view>
<view v-if="cart_show">
<u-popup :show="cart_show" round="10" @close="cart_show = false" closeable>
<view class="cart-list">
<view class="title">
商品列表
<view class="cler">
<u-button size="small" @click="cartCler()" text="清空" icon="trash"></u-button>
</view>
</view>
<view class="item" v-for="(item, index) in goodslist" :key="index">
<image v-if="item.spec_type == 10" :src="item.image[0].url"></image>
<image v-if="item.spec_type == 20"
:src="item.goods_sku.image.url ?item.goods_sku.image.url:item.image[0].url"></image>
<view class="cart-but">
<view class="but hm-bg-warning" @click="cartUp(index)">
<view class="hm-p-10">
<u-icon name="plus" color="#fff" bold></u-icon>
</view>
</view>
<view class="total-num">{{item.total_num}}</view>
<view class="but hm-bg-tips" @click="cartDown(index)">
<view class="hm-p-10">
<u-icon name="minus" color="#fff"></u-icon>
</view>
</view>
</view>
<view class="hm-p-t-10 hm-col-main">
{{item.goods_name}}<text class="hm-m-l-10 hm-col-tips">{{item.goods_sku.goods_attr}}</text>
</view>
<view class="hm-p-t-10 hm-col-error">{{item.goods_sku.goods_price}}</view>
</view>
</view>
</u-popup>
</view>
</view>
</template>
<script>
const App = getApp();
export default {
data() {
return {
applet:{},
table_id: '',
user_id: '',
scrollTop: 0, //tab标题的滚动条位置
oldScrollTop: 0,
current: 0, // 预设当前项的值
menuHeight: 0, // 左边菜单的高度
menuItemHeight: 0, // 左边菜单item的高度
itemId: '', // 栏目右边scroll-view用于滚动的id
arr: [],
scrollRightTop: 0, // 右边栏目scroll-view的滚动条高度
timer: null, // 定时器
//多规格商品信息
show: false, //是否显示
goods_spec_arr: [],
goods: {}, //商品详情
specData: {}, //商品规格
goods_sku_id: '',
goods_price: '',
line_price: '',
stock_num: 0,
spec_name:'',
spec_index: 0,
goods_img: '',
category: {}, //分类+商品列表
order_total_num: 0, //购物车商品总数
order_total_price: 0, //购物车商品总价
goodslist: {},
cart_show: false
}
},
onLoad(options) {
let _this = this;
_this.user_id = options.user_id || '';
_this.table_id = options.table_id;
_this.applet = App.getApplet();
},
onShow() {
let _this = this;
_this.getGoods();
},
onReady() {
this.getMenuItemTop()
},
methods: {
/**
* 递增指定的商品数量
*/
addCount: function(index, index1) {
let _this = this,
goods = _this.category[index].goods[index1]; // 后端同步更新
//判断是否为多规格
if (goods.spec_type == 20) {
_this.specShow(goods);
return true;
}
//判断单规格商品库存
if(_this.applet.other.is_stock == 1){
if(goods.total_num >= goods.spec[0].stock_num){
App.showError('库存不足');
return false;
}
}
App._post_form('cart/add', {
goods_id: goods.goods_id,
goods_num: 1,
goods_sku_id: goods.spec[0].spec_sku_id,
table_id: _this.table_id
}, () => {
_this.getGoods();
});
},
//显示底部弹出框
specShow: function(goods) {
let _this = this,
specData = null;
for (let i = 0; i < goods.specData.spec_attr.length; i++) {
for (let j = 0; j < goods.specData.spec_attr[i].spec_items.length; j++) {
if (j == 0) {
goods.specData.spec_attr[i].spec_items[0].checked = true;
this.goods_spec_arr[i] = goods.specData.spec_attr[i].spec_items[0].item_id;
} else {
goods.specData.spec_attr[i].spec_items[j].checked = false;
}
}
}
specData = goods.specData;
let spec_list = specData.spec_list[0];
_this.setData({
show: true,
goods: goods,
specData: specData,
goods_sku_id: spec_list.goods_spec_id,
spec_name: spec_list.spec_name,
goods_price: spec_list.form.goods_price,
line_price: spec_list.form.line_price,
goods_img: spec_list.form.image_url,
stock_num: spec_list.form.stock_num
});
},
/**
* 递减指定的商品数量
*/
minusCount: function(index, index1) {
let _this = this,
goods = _this.category[index].goods[index1]; // 后端同步更新
//判断是否为多规格
if (goods.spec_type == 20) {
_this.specShow(goods);
return true;
}
//判断单规格商品库存
if(_this.applet.other.is_stock == 1){
if(goods.total_num >= goods.spec[0].stock_num){
App.showError('库存不足');
return false;
}
}
if (goods.total_num > 1) {
// 后端同步更新
App._post_form('cart/sub', {
goods_id: goods.goods_id,
goods_sku_id: goods.spec[0].spec_sku_id,
table_id: _this.table_id
}, () => {
_this.getGoods();
});
}
if (goods.total_num == 1) {
// 后端同步更新
App._post_form('cart/delete', {
goods_id: goods.goods_id,
goods_sku_id: goods.spec[0].spec_sku_id,
table_id: _this.table_id
}, function(result) {
_this.getGoods();
});
}
},
/**
* 购物车 - 清空
*/
cartCler: function(goods_id, goods_sku_id) {
let _this = this;
uni.showModal({
title: "提示",
content: "确定要清空购物车?",
success: function(o) {
if (o.confirm) {
App._get('cart/clearAll', {
table_id: _this.table_id
}, function(result) {
App.showSuccess(result.msg, function() {
_this.cart_show = false;
_this.getGoods();
});
});
}
}
});
},
/**
* 购物车 - 递增
*/
cartUp: function(index) {
let _this = this,
goods = _this.goodslist[index]; // 后端同步更新
//判断单规格商品库存
if(_this.applet.other.is_stock == 1){
if(goods.total_num >= goods.goods_sku.stock_num){
App.showError('库存不足');
return false;
}
}
App._post_form('cart/add', {
goods_id: goods.goods_id,
goods_num: 1,
goods_sku_id: goods.goods_sku_id,
table_id: _this.table_id
}, () => {
_this.getGoods();
});
},
/**
* 购物车 - 递减
*/
cartDown: function(index) {
let _this = this,
goods = _this.goodslist[index]; // 后端同步更新
App._post_form('cart/sub', {
goods_id: goods.goods_id,
goods_sku_id: goods.goods_sku_id,
table_id: _this.table_id
}, () => {
_this.getGoods();
});
},
//转到订单确认页面
gotoCart: function() {
let _this = this;
if (_this.order_total_num > 0) {
App.goTo('flow/checkout?table_id=' + _this.table_id + '&user_id=' + _this.user_id);
}
return false;
},
/**
* 点击切换不同规格
*/
specTap: function(attrIdx, itemIdx) {
let specData = this.specData;
for (let i in specData.spec_attr) {
for (let j in specData.spec_attr[i].spec_items) {
if (attrIdx == i) {
specData.spec_attr[i].spec_items[j].checked = false;
if (itemIdx == j) {
specData.spec_attr[i].spec_items[itemIdx].checked = true;
this.goods_spec_arr[i] = specData.spec_attr[i].spec_items[itemIdx].item_id;
}
}
}
}
this.specData = specData; // 更新商品规格信息
this.updateSpecGoods();
},
/**
* 更新商品规格信息
*/
updateSpecGoods: function() {
let spec_sku_id = this.goods_spec_arr.join('_'),
spec_list = this.specData.spec_list,
spec_index = 0,
goods_img = '',
skuItem;
for (let i = 0; i < spec_list.length; i++) {
if (spec_list[i].spec_sku_id == spec_sku_id) {
skuItem = spec_list[i];
spec_index = i;
break;
}
}
//规格图片
if (skuItem.form.image_url == '') {
goods_img = this.goods.image[0].url;
} else {
goods_img = skuItem.form.image_url;
}
// 记录goods_sku_id
// 更新商品价格、划线价、库存
if (typeof skuItem === 'object') {
this.setData({
goods_sku_id: skuItem.spec_sku_id,
goods_price: skuItem.form.goods_price,
line_price: skuItem.form.line_price,
spec_index: spec_index,
goods_img: goods_img
});
}
},
/**
* 递增 - 多规格
*/
up: function() {
let _this = this,
spec_index = _this.spec_index,
goods = _this.goods; // 后端同步更新
//判断单规格商品库存
if(_this.applet.other.is_stock == 1){
if(goods.spec[spec_index].sell_num >= goods.spec[spec_index].stock_num){
App.showError('库存不足');
return false;
}
}
App._post_form('cart/add', {
goods_id: goods.goods_id,
goods_num: 1,
goods_sku_id: goods.spec[spec_index].spec_sku_id,
table_id: _this.table_id
}, () => {
goods.spec[spec_index].sell_num++;
_this.goods = goods;
_this.getGoods();
});
},
/**
* 递减指定的商品数量
*/
down: function() {
let _this = this,
spec_index = _this.spec_index,
goods = _this.goods;
if (goods.spec[spec_index].sell_num > 1) {
// 后端同步更新
App._post_form('cart/sub', {
goods_id: goods.goods_id,
goods_sku_id: goods.spec[spec_index].spec_sku_id,
table_id: _this.table_id
}, () => {
goods.spec[spec_index].sell_num--;
_this.goods = goods;
_this.getGoods();
});
}
if (goods.spec[spec_index].sell_num == 1) {
// 后端同步更新
App._post_form('cart/delete', {
goods_id: goods.goods_id,
goods_sku_id: goods.spec[spec_index].spec_sku_id,
table_id: _this.table_id
}, function(result) {
goods.spec[spec_index].sell_num--;
_this.goods = goods;
_this.getGoods();
});
}
},
/**
* 切换购物车列表显示状态
*/
setCartShow: function() {
let _this = this;
if (_this.order_total_num < 1) {
App.showSuccess('购物车数据为空');
return false;
}
_this.cart_show = !_this.cart_show;
},
/**
* 获取商品列表
*/
getGoods: function() {
let _this = this;
App._get('goods/food', {
table_id: _this.table_id
}, function(result) {
_this.setData(result.data);
});
},
// 点击左边的栏目切换
async swichMenu(index) {
if (this.arr.length == 0) {
await this.getMenuItemTop();
}
if (index == this.current) return;
this.scrollRightTop = this.oldScrollTop;
this.$nextTick(function() {
this.scrollRightTop = this.arr[index];
this.current = index;
this.leftMenuStatus(index);
})
},
// 获取一个目标元素的高度
getElRect(elClass, dataVal) {
new Promise((resolve, reject) => {
const query = uni.createSelectorQuery().in(this);
query.select('.' + elClass).fields({
size: true
}, res => {
// 如果节点尚未生成res值为null循环调用执行
if (!res) {
setTimeout(() => {
this.getElRect(elClass);
}, 10);
return;
}
this[dataVal] = res.height;
resolve();
}).exec();
})
},
// 观测元素相交状态
async observer() {
this.tabbar.map((val, index) => {
let observer = uni.createIntersectionObserver(this);
// 检测右边scroll-view的id为itemxx的元素与right-box的相交状态
// 如果跟.right-box底部相交就动态设置左边栏目的活动状态
observer.relativeTo('.right-box', {
top: 0
}).observe('#item' + index, res => {
if (res.intersectionRatio > 0) {
let id = res.id.substring(4);
this.leftMenuStatus(id);
}
})
})
},
// 设置左边菜单的滚动状态
async leftMenuStatus(index) {
this.current = index;
// 如果为0意味着尚未初始化
if (this.menuHeight == 0 || this.menuItemHeight == 0) {
await this.getElRect('menu-scroll-view', 'menuHeight');
await this.getElRect('tab-item', 'menuItemHeight');
}
// 将菜单活动item垂直居中
this.scrollTop = index * this.menuItemHeight + this.menuItemHeight / 2 - this.menuHeight / 2;
},
// 获取右边菜单每个item到顶部的距离
getMenuItemTop() {
new Promise(resolve => {
let selectorQuery = uni.createSelectorQuery();
selectorQuery.selectAll('.class-item').boundingClientRect((rects) => {
// 如果节点尚未生成rects值为[](因为用selectAll所以返回的是数组),循环调用执行
if (!rects.length) {
setTimeout(() => {
this.getMenuItemTop();
}, 10);
return;
}
rects.forEach((rect) => {
// 这里减去rects[0].top是因为第一项顶部可能不是贴到导航栏(比如有个搜索框的情况)
this.arr.push(rect.top - rects[0].top);
resolve();
})
}).exec()
})
},
// 右边菜单滚动
async rightScroll(e) {
this.oldScrollTop = e.detail.scrollTop;
if (this.arr.length == 0) {
await this.getMenuItemTop();
}
if (this.timer) return;
if (!this.menuHeight) {
await this.getElRect('menu-scroll-view', 'menuHeight');
}
setTimeout(() => { // 节流
this.timer = null;
// scrollHeight为右边菜单垂直中点位置
let scrollHeight = e.detail.scrollTop + this.menuHeight / 2;
for (let i = 0; i < this.arr.length; i++) {
let height1 = this.arr[i];
let height2 = this.arr[i + 1];
// 如果不存在height2意味着数据循环已经到了最后一个设置左边菜单为最后一项即可
if (!height2 || scrollHeight >= height1 && scrollHeight < height2) {
this.leftMenuStatus(i);
return;
}
}
}, 10)
},
goTo: function(url) {
App.goTo(url);
}
}
}
</script>
<style lang="scss" scoped>
.wrap {
height: calc(100vh - 100rpx);
/* #ifdef H5 */
height: calc(100vh - 100rpx - var(--window-top));
/* #endif */
display: flex;
flex-direction: column;
.menu {
display: flex;
overflow: hidden;
.tab-view {
width: 220rpx;
height: 100%;
background-color: #FFFFFF;
.tab-item {
height: 110rpx;
background: #FFFFFF;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
color: #444;
font-weight: 400;
line-height: 1;
border-bottom: 1rpx solid #f3f4f6;
}
.tab-item-active {
position: relative;
color: #000;
font-size: 30rpx;
font-weight: 600;
background: #f3f4f6;
}
.tab-item-active::before {
content: "";
position: absolute;
border-left: 4px solid #fa3534;
height: 32rpx;
left: 0;
top: 39rpx;
}
}
.right-box {
background-color: #f3f4f6;
.class-item {
padding: 16rpx 16rpx 0rpx 16rpx;
.item-title {
font-size: 28rpx;
color: #999;
background-color: #FFF;
padding: 15rpx 20rpx;
border-bottom: 1rpx solid #f3f4f6;
}
.item-goods {
padding: 15rpx;
height: 180rpx;
position:relative;
background-color: #fff;
image {
width: 180rpx;
height: 180rpx;
border-radius: 8rpx;
float: left;
margin-right: 20rpx;
}
.tag{
display: inline-block;
z-index: 1;
position: absolute;
top: 15rpx;
left: 15rpx;
width: 180rpx;
height: 180rpx;
border-radius: 8rpx;
background: rgba(0, 0, 0, 0.5);
color: #fff;
line-height: 180rpx;
text-align: center;
font-size: 26rpx;
}
.title {
padding-top: 10rpx;
font-size: 32rpx;
font-weight: 700;
color: #000;
}
.selling{
height: 30rpx;
padding-top: 5rpx;
color: #999;
font-size: 22rpx;
}
.sold {
padding-top: 10rpx;
color: #303133;
font-size: 24rpx;
}
.price {
padding-top: 5rpx;
color: #fa3534;
font-size: 28rpx;
font-weight: 900;
}
}
}
}
}
}
/*选购工具样式*/
.cart-but {
display: inline-block;
float: right;
margin-top: 10rpx;
.but {
width: 50rpx;
height: 50rpx;
text-align: center;
line-height: 50rpx;
vertical-align: middle;
float: right;
border-radius: 50%;
.icon{
margin: 10rpx 0rpx 0rpx 10rpx;
}
}
.add{
background-color: #FF9900;
}
.minus{
border: 1rpx solid #FF9900;
}
.total-num {
margin: 0 10rpx;
font-size: 25rpx;
font-weight: 700;
color: #606266;
border-bottom: 1rpx solid #f3f4f6;
float: right;
text-align: center;
height: 45rpx;
line-height: 45rpx;
margin-top: 5rpx;
width: 30rpx;
}
.spec {
height: 50rpx;
line-height: 50rpx;
background-color: #FF9900;
padding: 2rpx 25rpx;
font-size: 26rpx;
border-radius: 50rpx;
color: #fff;
}
}
.bottom-cart {
width: 100%;
.cart{
display: inline-block;
float: left;
margin-top: -20rpx;
margin-left: 50rpx;
width: 100rpx;
height: 100rpx;
text-align: center;
border-radius: 50%;
background-color: #FF9900;
.count {
position: absolute;
left: 130rpx;
top: -20rpx;
font-size: 30rpx;
line-height: 40rpx;
padding: 0 12rpx;
color: #fff;
background: #ff495e;
border-radius: 20rpx;
}
}
.total {
display: inline-block;
float: left;
color: #ff495e;
font-size: 30rpx;
font-weight: 900;
padding-left: 20rpx;
}
.cart-but{
display: inline-block;
float: right;
margin-right: 20rpx;
}
}
.cart-list {
padding: 20rpx;
.title {
padding-bottom: 20rpx;
border-bottom: 1rpx solid #f3f4f6;
.cler {
display: inline-block;
float: right;
margin-top: -8rpx;
padding-right: 100rpx;
}
}
.item {
height: 100rpx;
padding: 10rpx 0;
border-bottom: 1rpx solid #f3f4f6;
font-size: 25rpx;
image {
width: 100rpx;
height: 100rpx;
border-radius: 10rpx;
float: left;
margin-right: 20rpx;
}
.cart-but {
display: inline-block;
height: 50rpx;
line-height: 50rpx;
float: right;
margin-top: 25rpx;
.total-num {
margin: 0 10rpx;
font-size: 25rpx;
color: #999;
border-bottom: 1rpx solid #f3f4f6;
float: right;
text-align: center;
height: 40rpx;
line-height: 40rpx;
margin-top: 5rpx;
width: 30rpx;
}
.but {
width: 50rpx;
height: 50rpx;
text-align: center;
line-height: 50rpx;
vertical-align: middle;
float: right;
border-radius: 50%;
}
}
}
}
/* 多规格选项框 */
.spec-box {
padding: 20rpx;
.goods{
height: 150rpx;
padding-bottom: 20rpx;
image {
height: 150rpx;
width: 150rpx;
border-radius: 10rpx;
float: left;
margin-right: 30rpx;
box-shadow: 6rpx 6rpx 8rpx rgba(26, 26, 26, 0.2);
}
.goods-name {
font-size: 32rpx;
padding-top: 10rpx;
color: #000000;
}
.spec-name {
font-size: 28rpx;
padding: 10rpx 0rpx;
color: #999;
}
}
.title {
color: #606266;
padding-top: 20rpx;
font-size: 28rpx;
border-top: 1px dashed #eee;
}
.but {
height: 50rpx;
line-height: 50rpx;
vertical-align: middle;
text-align: center;
padding: 0 20rpx;
border-radius: 10rpx;
float: left;
margin-top: 20rpx;
margin-left: 20rpx;
font-size: 28rpx;
}
.but-ok{
background-color: #FF9900;
color: #fff;
}
.but-no{
background-color: #f3f4f6;
color: #c0c4cc;
}
}
</style>