菜大王uniapp开发
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

<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>