You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
493 lines
12 KiB
493 lines
12 KiB
<script setup>
|
|
import { onLoad, onShow } from "@dcloudio/uni-app";
|
|
import { nextTick, ref } from "vue";
|
|
import customTabBar from "@/components/custom-tab-bar/my-tab-bar.vue";
|
|
import navv from "@/components/nav/nav.vue";
|
|
import {
|
|
getCartInfoApi,
|
|
getMenuProductsApi,
|
|
getMenusTypesApi,
|
|
getUserInfoApi,
|
|
newMenusToCartApi,
|
|
previewApi,
|
|
} from "@/libs/api";
|
|
import { sleep, useHeight, useRect } from "@/libs/utils";
|
|
import useStore from "@/store";
|
|
|
|
const store = useStore();
|
|
/**
|
|
* 页面容器高度
|
|
*/
|
|
const pageContainerHeight = ref(0);
|
|
/**
|
|
* 导航数据 菜品类
|
|
*/
|
|
const navData = ref([]);
|
|
// 当前tab标签
|
|
const currentTab = ref(0);
|
|
// 当前子类标签
|
|
const currentType = ref(0);
|
|
const navScrollLeft = ref(0);
|
|
// 子类型数据
|
|
const typeData = ref([]);
|
|
// 商品数据
|
|
const products = ref([]);
|
|
// const sum = ref(0);
|
|
// const normText = ref("选规格");
|
|
// const hiddenModal = ref(true);
|
|
// const modalValue = ref("");
|
|
// const modalId = ref("");
|
|
// const scrollHeight = ref("300");
|
|
// const windowWidth = ref("");
|
|
const pageIndex = ref(1);
|
|
const pageSize = ref(20);
|
|
const pageCount = ref("");
|
|
// 滚动加载数据时当前商品类型id
|
|
const scrollId = ref("");
|
|
// 判断商品列表有无数据
|
|
const showList = ref(true);
|
|
// 判断商品列表滚动到底部是否还有数据
|
|
const unLoading = ref(false);
|
|
// checkbox选择状态
|
|
// const checked = ref(true);
|
|
const totalPrice = ref(0);
|
|
// 服务费
|
|
const serviceFee = ref(0);
|
|
// 配送费
|
|
const shippingFee = ref(0);
|
|
// const defaultAddressId = ref("");
|
|
// const isSearch = ref(false);
|
|
// const showClear = ref(false);
|
|
const keyword = ref("");
|
|
// const facus = ref(false);
|
|
// const searchChildTypeId = ref("");
|
|
// const inputValue = ref("");
|
|
const showBall = ref(false);
|
|
const actionTime = ref(true);
|
|
// 购物车小球的动画
|
|
const animationY = ref({});
|
|
// 购物车小球的X轴动画
|
|
const animationX = ref({});
|
|
// 购物车小球的Y轴坐标
|
|
const testY = ref(0);
|
|
// 购物车小球的X轴坐标
|
|
const testX = ref(0);
|
|
// const touchDotX = ref("");
|
|
// const touchDotY = ref("");
|
|
// const touchStar = ref(false);
|
|
const showCart = ref(false);
|
|
// const cartList = ref([]);
|
|
// const cartListH = ref("");
|
|
// const navHeight = ref(app.appHead.navHeight);
|
|
// const capsuleTop = ref(app.appHead.capsuleTop);
|
|
// const tabbarHeight = ref(89);
|
|
// const barHeight = ref(42); // 胶囊高度
|
|
// const statusBarHeight = ref(86);
|
|
// const screenHeight = ref(0); // 页面总高度
|
|
// const firstTypeHeight = ref(55);
|
|
const isCommy = ref(false);
|
|
const isLogin = ref(false);
|
|
|
|
/**
|
|
* 获取商品分类
|
|
* @param id 商品分类id
|
|
*/
|
|
async function getProductTypes(id) {
|
|
const res = await getMenusTypesApi({
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
parentId: "",
|
|
});
|
|
if (res.code !== "0") {
|
|
return;
|
|
}
|
|
navData.value = res.data;
|
|
|
|
if (id) {
|
|
getLeftTypes();
|
|
currentTab.value = res.data.findIndex(item => item.id == id);
|
|
}
|
|
else {
|
|
getLeftTypes(navData.value[0].id);
|
|
}
|
|
// 这里写获取商品分类的逻辑
|
|
// firstTypeHeight.value = (await useRect(".second")).height;
|
|
}
|
|
|
|
/**
|
|
* 获取左侧子类别
|
|
* @param parentId 父类别id
|
|
*/
|
|
async function getLeftTypes(parentId) {
|
|
const res = await getMenusTypesApi({
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
parentId,
|
|
});
|
|
if (res.code !== "0") {
|
|
return;
|
|
};
|
|
typeData.value = res.data;
|
|
|
|
if (res.total > 0) {
|
|
scrollId.value = res.data[0].id;
|
|
getProducts(scrollId.value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 获取商品信息
|
|
* @param parentId 商品分类id
|
|
*/
|
|
async function getProducts(parentId) {
|
|
showList.value = true;
|
|
const res = await getMenuProductsApi({
|
|
mealName: keyword.value,
|
|
current: pageIndex.value,
|
|
pageSize: pageSize.value,
|
|
groupId: parentId,
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
});
|
|
if (res.code !== "0") {
|
|
return;
|
|
};
|
|
pageCount.value = res.pageCount;
|
|
if (res.tota <= 0) {
|
|
showList.value = false;
|
|
return;
|
|
}
|
|
products.value = pageIndex.value == 1 ? res.data : [...products.value, ...res.data];
|
|
}
|
|
|
|
/**
|
|
* 获取用户信息
|
|
*/
|
|
async function getUserInfo() {
|
|
// 这里写获取用户信息的逻辑
|
|
const res = await getUserInfoApi();
|
|
if (res.code === "0") {
|
|
isLogin.value = true;
|
|
isCommy.value = !!res.data.company;
|
|
}
|
|
if (res.code === "4") {
|
|
isLogin.value = false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 切换导航
|
|
* @param index 导航索引
|
|
* @param id 导航id
|
|
*/
|
|
function switchNav(index, id) {
|
|
navScrollLeft.value = `${(index - 2) * 212}rpx`;
|
|
if (currentTab.value === index)
|
|
return;
|
|
currentTab.value = index;
|
|
currentType.value = 0;
|
|
products.value = [];
|
|
unLoading.value = false;
|
|
showList.value = true;
|
|
pageIndex.value = 1;
|
|
getLeftTypes(id);
|
|
}
|
|
/**
|
|
* 切换子类别
|
|
* @param index 子类别索引
|
|
* @param id 子类别id
|
|
*/
|
|
function switchType(index, id) {
|
|
if (currentType.value == index) {
|
|
return false;
|
|
}
|
|
else {
|
|
currentType.value = index;
|
|
products.value = [];
|
|
unLoading.value = false;
|
|
showList.value = true;
|
|
pageIndex.value = 1;
|
|
scrollId.value = id;
|
|
this.getProducts(scrollId.value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 翻页数据
|
|
*/
|
|
function loadList() {
|
|
if (++pageIndex.value <= pageCount.value) {
|
|
unLoading.value = false;
|
|
getProducts(scrollId.value);
|
|
}
|
|
else {
|
|
unLoading.value = true;
|
|
}
|
|
}
|
|
/**
|
|
* 点击添加商品
|
|
* @param item 商品信息
|
|
*/
|
|
async function onAdd(item, e) {
|
|
if (!isLogin.value) {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "请先登录",
|
|
complete: (res) => {
|
|
if (res.confirm) {
|
|
uni.navigateTo({
|
|
url: "/pages/login/login",
|
|
});
|
|
}
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
if (!isCommy.value) {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "请先添加企业",
|
|
complete: (res) => {
|
|
if (res.confirm) {
|
|
uni.navigateTo({
|
|
// url: "../../enterprise/pages/joinEnterprise/joinEnterprise",
|
|
});
|
|
}
|
|
},
|
|
});
|
|
return;
|
|
}
|
|
if (!actionTime.value) {
|
|
return;
|
|
}
|
|
|
|
createAnimation(e.detail.x, e.detail.y);
|
|
|
|
const res = await newMenusToCartApi({
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
addrId: uni.getStorageSync("addressId"),
|
|
mealIds: item.id,
|
|
});
|
|
|
|
if (res.code !== "0") {
|
|
return;
|
|
};
|
|
|
|
uni.showToast({
|
|
title: "添加成功",
|
|
duration: 3000,
|
|
});
|
|
await initCartInfo();
|
|
}
|
|
|
|
async function initCartInfo() {
|
|
const data = {
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
addrId: uni.getStorageSync("addressId"),
|
|
};
|
|
const res = await getCartInfoApi(data);
|
|
if (res.code !== "0") {
|
|
return;
|
|
}
|
|
|
|
// cartCount.value = res.total;
|
|
// cartList.value = res.data;
|
|
totalPrice.value = res.data.reduce((acc, cur) => acc + cur.amount, 0).toFixed(2);
|
|
// const itemIds = res.data.map(item => item.id);
|
|
|
|
// store.changeCartList([]);
|
|
// await sleep(500);
|
|
store.changeCartList(res.data);
|
|
// cartCount.value = store.getCartTotalQuantity();
|
|
// store.cartList = res.data;
|
|
if (store.cartList.length > 0) {
|
|
preview(store.cartList.map(item => item.id));
|
|
}
|
|
else if (showCart.value) {
|
|
showCart.value = false;
|
|
}
|
|
}
|
|
|
|
async function preview(itemIds) {
|
|
const res = await previewApi({
|
|
addrId: uni.getStorageSync("addressId"),
|
|
itemIds: itemIds.join(","),
|
|
});
|
|
if (res.code !== "0") {
|
|
return;
|
|
}
|
|
serviceFee.value = res.data.serviceFee;
|
|
shippingFee.value = res.data.shippingFee;
|
|
totalPrice.value = res.data.itemAmount;
|
|
}
|
|
|
|
/**
|
|
* 创建购物车小球的动画
|
|
*/
|
|
async function createAnimation(eX, eY) {
|
|
actionTime.value = false;
|
|
testX.value = eX;
|
|
testY.value = eY;
|
|
|
|
const bottomX = (store.tabbarItemWidth || 83) * 2 - 30;
|
|
const bottomY = uni.getWindowInfo().windowHeight;
|
|
|
|
const stepX = flyX(bottomX, eX);
|
|
const stepY = flyY(bottomY, eY);
|
|
showBall.value = true;
|
|
animationX.value = stepX.export(); // 创建小球水平动画
|
|
animationY.value = stepY.export(); // 创建小球垂直动画
|
|
|
|
await sleep(400);
|
|
actionTime.value = true;
|
|
showBall.value = false;
|
|
|
|
animationX.value = flyX(0, 0).export();
|
|
animationY.value = flyY(0, 0).export();
|
|
|
|
// await sleep(800);
|
|
}
|
|
/**
|
|
* 购物车小球水平移动动画
|
|
*/
|
|
function flyX(bottomX, ballX, duration = 400) {
|
|
const animation = uni.createAnimation({
|
|
duration,
|
|
timingFunction: "linear",
|
|
});
|
|
animation.translateX(-bottomX).step();
|
|
return animation;
|
|
}
|
|
/**
|
|
* 购物车小球垂直移动动画
|
|
*/
|
|
function flyY(bottomY, ballY, duration = 400) {
|
|
const animation = uni.createAnimation({
|
|
duration,
|
|
timingFunction: "ease-in",
|
|
});
|
|
animation.translateY(bottomY - ballY).step();
|
|
return animation;
|
|
}
|
|
|
|
onLoad(() => {
|
|
// #ifdef APP
|
|
uni.hideTabBar();
|
|
// #endif
|
|
|
|
products.value = [];
|
|
showCart.value = false;
|
|
// cartList.value = [];
|
|
store.cartList = [];
|
|
currentTab.value = 0;
|
|
currentType.value = 0;
|
|
getProductTypes("");
|
|
getUserInfo();
|
|
});
|
|
|
|
onShow(() => {
|
|
initCartInfo();
|
|
|
|
nextTick(async () => {
|
|
await sleep(500);
|
|
const boxHeight = (await useRect(".second"))?.height || 0;
|
|
|
|
const { content, tabbar } = useHeight();
|
|
pageContainerHeight.value = content - boxHeight - tabbar;
|
|
});
|
|
},
|
|
);
|
|
</script>
|
|
|
|
<template>
|
|
<navv>
|
|
<template #default="{ menu, status }">
|
|
<view
|
|
class="header-box"
|
|
:style="{
|
|
paddingTop: `${status}px`,
|
|
height: `${menu || 45}px`,
|
|
}"
|
|
>
|
|
<text class="iconfont icon-search1" />
|
|
<text>菜谱下单</text>
|
|
</view>
|
|
|
|
<scroll-view class="second" :scroll-x="true" :scroll-with-animation="true">
|
|
<view
|
|
v-for="(navItem, idx) in navData"
|
|
:key="idx"
|
|
class="second-list"
|
|
:class="[currentTab === idx ? 'second-list-active' : '']"
|
|
@tap="() => switchNav(idx, navItem.id)"
|
|
>
|
|
{{ navItem.groupName }}
|
|
</view>
|
|
</scroll-view>
|
|
|
|
<view
|
|
class="page-container"
|
|
:style="{ height: `calc(${pageContainerHeight}px - 35rpx)` }"
|
|
>
|
|
<view class="left">
|
|
<view
|
|
v-for="(typeItem, typex) in typeData"
|
|
:key="typex"
|
|
class="text"
|
|
>
|
|
<text
|
|
class="name"
|
|
:class="[currentType == typex ? 'text-active' : '']"
|
|
@tap="() => switchType(typex, typeItem.id)"
|
|
>
|
|
{{ typeItem.groupName }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
<scroll-view class="right" :scroll-y="true" @scrolltolower="loadList">
|
|
<view
|
|
v-for="(item, index) in products"
|
|
:key="index"
|
|
class="right-list"
|
|
>
|
|
<view class="info">
|
|
<view class="top">
|
|
{{ item.mealName }}
|
|
</view>
|
|
<view class="under">
|
|
<text
|
|
v-for="detail in item.details"
|
|
:key="detail.id"
|
|
class="name"
|
|
>
|
|
{{ detail.productName }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
<view
|
|
class="add"
|
|
@tap.stop="(e) => onAdd(item, e)"
|
|
>
|
|
<image class="icon" src="/static/home/add.png" mode="" />
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
|
|
<view
|
|
v-if="showBall"
|
|
:animation="animationY"
|
|
:style="{ position: 'fixed', top: `${testY}px` }"
|
|
>
|
|
<view
|
|
class="round"
|
|
:animation="animationX"
|
|
:style="{ position: 'fixed', left: `${testX}px` }"
|
|
/>
|
|
</view>
|
|
|
|
<customTabBar tab-index="2" />
|
|
</template>
|
|
</navv>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
@import './recipeOrder.scss';
|
|
</style>
|