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.
1264 lines
32 KiB
1264 lines
32 KiB
<script setup>
|
|
import { onHide, onShow } from "@dcloudio/uni-app";
|
|
import { nextTick, ref } from "vue";
|
|
import customTabBar from "@/components/custom-tab-bar/my-tab-bar.vue";
|
|
import keyboard from "@/components/keyboard/keyboard.vue";
|
|
// import navigation from "@/components/navigation/navigation.vue";
|
|
import navv from "@/components/nav/nav.vue";
|
|
import {
|
|
addCartApi,
|
|
getCartInfoApi,
|
|
getProductsApi,
|
|
getProductTypesApi,
|
|
previewApi,
|
|
} from "@/libs/api";
|
|
import { sleep, useHeight, useRect, validates } from "@/libs/utils";
|
|
import useStore from "@/store";
|
|
|
|
const store = useStore();
|
|
/**
|
|
* 购物车商品id列表
|
|
*/
|
|
// const keys = [];
|
|
|
|
// const plugin = requirePlugin("WechatSI")
|
|
// const manager = plugin.getRecordRecognitionManager()
|
|
|
|
/**
|
|
* 搜索关键字
|
|
*/
|
|
const keyword = ref("");
|
|
/**
|
|
* 搜索框的高度
|
|
*/
|
|
// const searchHeight = ref(0);
|
|
/**
|
|
* 状态栏高度
|
|
*/
|
|
// const statusBarHeight = ref(0);
|
|
/**
|
|
* 屏幕的高度
|
|
*/
|
|
// const screenHeight = ref(0);
|
|
/**
|
|
* 各种距离
|
|
*/
|
|
// const safeHeight = ref({
|
|
// screen: 0,
|
|
// status: 0,
|
|
// menu: 0,
|
|
// content: 0,
|
|
// });
|
|
/**
|
|
* 当前标签
|
|
*/
|
|
const currentTab = ref(0);
|
|
/**
|
|
* 当前子类标签
|
|
*/
|
|
const currentType = ref(0);
|
|
/**
|
|
* 右侧商品列表的高度
|
|
*/
|
|
const bottomHeight = ref(0);
|
|
/**
|
|
* 右侧商品列表的滚动高度 FIXME: 无法生效
|
|
*/
|
|
const scrollTop = ref(0);
|
|
/**
|
|
* 是否显示购物车小球
|
|
*/
|
|
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 navScrollLeft = ref(0);
|
|
/**
|
|
* 顶部类型数据
|
|
*/
|
|
const navData = ref([]);
|
|
/**
|
|
* 展开顶部类型数据
|
|
*/
|
|
const isShowAll = ref(false);
|
|
/**
|
|
* 子类型数据
|
|
*/
|
|
const typeData = ref([]);
|
|
/**
|
|
* 商品数据
|
|
*/
|
|
const products = ref([]);
|
|
/**
|
|
* 购物车商品数量
|
|
*/
|
|
// const cartCount = ref(0);
|
|
/**
|
|
* 购物车商品列表
|
|
*/
|
|
// const cartList = ref([]);
|
|
/**
|
|
* 购物车商品总价
|
|
*/
|
|
const totalPrice = ref(0);
|
|
/**
|
|
* 服务费用
|
|
*/
|
|
const serviceFee = ref(0);
|
|
/**
|
|
* 配送费
|
|
*/
|
|
const shippingFee = ref(0);
|
|
/**
|
|
* 是否显示购物车
|
|
*/
|
|
const showCart = ref(false);
|
|
/**
|
|
* 是否显示弹窗
|
|
*/
|
|
const showModalStatus = ref(false);
|
|
/**
|
|
* 判断商品列表滚动到底部是否还有数据
|
|
*/
|
|
const unLoading = ref(false);
|
|
/**
|
|
* 判断商品列表有无数据
|
|
*/
|
|
const showList = ref(true);
|
|
/**
|
|
* 当前页码
|
|
*/
|
|
const pageIndex = ref(1);
|
|
/**
|
|
* 每页显示数量
|
|
*/
|
|
const pageSize = ref(20);
|
|
/**
|
|
* 总页数
|
|
*/
|
|
const pageCount = ref(0);
|
|
/**
|
|
* 判断是否滚动到了底部
|
|
*/
|
|
const isBottom = ref(false);
|
|
/**
|
|
* 下拉刷新状态
|
|
*/
|
|
const triggered = ref(false);
|
|
/**
|
|
* 触摸开始时的X坐标
|
|
*/
|
|
const touchDotX = ref(0);
|
|
/**
|
|
* 触摸开始时的Y坐标
|
|
*/
|
|
const touchDotY = ref(0);
|
|
/**
|
|
* 是否触摸开始
|
|
*/
|
|
const touchStar = ref(false);
|
|
/**
|
|
* 右侧商品列表的高度
|
|
*/
|
|
const rightHeight = ref(0);
|
|
/**
|
|
* 滚动加载数据时当前商品类型id
|
|
*/
|
|
const scrollId = ref("");
|
|
/**
|
|
* 是否在搜索中
|
|
*/
|
|
const isSearching = ref(false);
|
|
/**
|
|
* 是否显示清除按钮
|
|
*/
|
|
const showClear = ref(false);
|
|
/**
|
|
* 滚动页面的高度
|
|
*/
|
|
const pageContainerHeight = ref(0);
|
|
/**
|
|
* 标记是否已获取过购物车(首次加载后设为true)
|
|
*/
|
|
const hasFetchedCart = ref(false);
|
|
/**
|
|
* 输入购买数量弹框的值
|
|
*/
|
|
const inputValue = ref("");
|
|
/**
|
|
* 是否显示购买数量弹框
|
|
*/
|
|
const isShowkeyboard = ref(false);
|
|
/**
|
|
* 搜索商品
|
|
*/
|
|
function onSearch() {
|
|
pageIndex.value = 1;
|
|
isSearching.value = true;
|
|
isShowAll.value = false;
|
|
scrollTop.value = 0;
|
|
getProducts("");
|
|
}
|
|
/**
|
|
* 取消搜索
|
|
*/
|
|
function onCancel() {
|
|
isSearching.value = false;
|
|
keyword.value = "";
|
|
showClear.value = false;
|
|
pageIndex.value = 1;
|
|
isShowAll.value = false;
|
|
scrollTop.value = 0;
|
|
getProducts(scrollId.value);
|
|
}
|
|
|
|
function onChangeKeyword(e) {
|
|
const value = e.detail.value.replace(/\s+/g, "");
|
|
showClear.value = value != "";
|
|
if (value != "") {
|
|
keyword.value = value;
|
|
onSearch();
|
|
}
|
|
else {
|
|
onCancel();
|
|
}
|
|
}
|
|
/**
|
|
* 展开顶部类型数据
|
|
*/
|
|
function onShowAllTypes() {
|
|
isShowAll.value = !isShowAll.value;
|
|
}
|
|
/**
|
|
* 点击一级分类,并修改二级分类数据
|
|
*/
|
|
function onSwitchNav(item, index) {
|
|
navScrollLeft.value = (index - 2) * 50;
|
|
if (currentTab.value === index)
|
|
return;
|
|
|
|
currentTab.value = index;
|
|
currentType.value = 0;
|
|
products.value = [];
|
|
unLoading.value = false;
|
|
showList.value = true;
|
|
pageIndex.value = 1;
|
|
isShowAll.value = false;
|
|
keyword.value = "";
|
|
isSearching.value = false;
|
|
getLeftTypes(item.id);
|
|
}
|
|
/**
|
|
* TODO: 打开语音搜索
|
|
*/
|
|
function onOpenRecord() {}
|
|
/**
|
|
* 点击了二级分类,并修改列表
|
|
*/
|
|
function onSwitchType(item, index) {
|
|
if (currentType.value === index)
|
|
return;
|
|
currentType.value = index;
|
|
products.value = [];
|
|
unLoading.value = false;
|
|
showList.value = true;
|
|
pageIndex.value = 1;
|
|
scrollId.value = item.id;
|
|
getProducts(scrollId.value);
|
|
}
|
|
/**
|
|
* 获取顶部tab标签列表,对应左侧类别
|
|
*/
|
|
async function getProductTypes(id, isSearch) {
|
|
const res = await getProductTypesApi();
|
|
if (res.code === "0") {
|
|
navData.value = res.data;
|
|
if (id) {
|
|
getLeftTypes(id);
|
|
res.data.forEach((item, index) => {
|
|
if (item.id === id) {
|
|
currentTab.value = index;
|
|
navScrollLeft.value = (index - 2) * 50;
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
const idx = isSearch ? 0 : currentTab.value;
|
|
getLeftTypes(navData.value[idx].id, isSearch ? true : undefined);
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* 获取左侧子类别
|
|
* @param parentId
|
|
* @param isSearch
|
|
*/
|
|
async function getLeftTypes(parentId, isSearch) {
|
|
const res = await getProductTypesApi({ parentId });
|
|
if (res.code !== "0") {
|
|
return;
|
|
}
|
|
|
|
typeData.value = res.data;
|
|
if (res.total > 0) {
|
|
scrollId.value = typeData.value[currentType.value].id;
|
|
getProducts(isSearch ? "" : scrollId.value);
|
|
}
|
|
}
|
|
/**
|
|
* 获取商品信息
|
|
*/
|
|
async function getProducts(parentId, tips) {
|
|
if (!hasFetchedCart.value) {
|
|
const params = {
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
addrId: uni.getStorageSync("addressId"),
|
|
};
|
|
const res = await getCartInfoApi(params);
|
|
store.changeCartList(res.code === "0" ? res.data : []);
|
|
// app.globalData.cartList = res.code === "0" ? res.data : [];
|
|
hasFetchedCart.value = res.code === "0";
|
|
}
|
|
|
|
loadProductList(parentId, tips);
|
|
}
|
|
/**
|
|
* 菜品列表 全局只加载一次
|
|
*/
|
|
async function loadProductList(parentId, tips) {
|
|
showList.value = true;
|
|
// const params = {
|
|
// warehouseId: wx.getStorageSync("warehousId"),
|
|
// addrId: wx.getStorageSync("addressId"),
|
|
// };
|
|
|
|
// const cartList = store.cartList;
|
|
// console.log(store.cartList, 3333);
|
|
|
|
const total = store.getCartTotalQuantity();
|
|
|
|
store.increment(total);
|
|
|
|
const params = {
|
|
typeId: parentId,
|
|
promotion: false,
|
|
orderByField: "name",
|
|
ase: true,
|
|
search: keyword.value,
|
|
pageNum: pageIndex.value,
|
|
pageSize: pageSize.value,
|
|
warehouseid: uni.getStorageSync("warehousId"),
|
|
};
|
|
|
|
const res = await getProductsApi(params);
|
|
if (res.code !== "0")
|
|
return;
|
|
|
|
pageCount.value = res.pageCount;
|
|
|
|
if (!(res.total > 0)) {
|
|
showList.value = false;
|
|
return;
|
|
}
|
|
|
|
// 初始化购物车映射表(格式:specId_price -> quantity)
|
|
const cartMap = store.getCartMap();
|
|
res.data.forEach((item) => {
|
|
item.specs.forEach((specsItem) => {
|
|
// 为每个规格项生成相同的组合键
|
|
const specKey = `${specsItem.id}_${specsItem.price}`;
|
|
// 从映射表中获取数量(不存在则设为0)
|
|
specsItem.sum = cartMap[specKey] || 0;
|
|
});
|
|
|
|
// 判断是否显示“选规格”按钮
|
|
item.showChoose = item.specs.length > 1 ? 1 : 0;
|
|
item.showChild = item.specs.length > 1 ? false : item.showChild;
|
|
});
|
|
|
|
const temp = products.value;
|
|
products.value = [];
|
|
await sleep(100); // 等待更新数据 scrollTop.value 无效的
|
|
|
|
products.value = pageIndex.value === 1
|
|
? res.data
|
|
: [...temp, ...(res.data || [])];
|
|
|
|
rightHeight.value = ((await useRect("#scroll-page"))?.height || 1) + 1;
|
|
if (tips === 1) {
|
|
currentType.value -= 1;
|
|
}
|
|
else if (tips === 2) {
|
|
currentType.value += 1;
|
|
scrollTop.value = 0;
|
|
bottomHeight.value = 0;
|
|
}
|
|
scrollTop.value = 0;
|
|
uni.hideNavigationBarLoading(); // 停止下拉刷新
|
|
uni.stopPullDownRefresh();
|
|
}
|
|
/**
|
|
* 滚动事件 判断触底否
|
|
* @param {Event} e - 滚动事件参数
|
|
*/
|
|
function onScroll(e) {
|
|
if ((e.detail.scrollHeight - e.detail.scrollTop) <= rightHeight.value) {
|
|
isBottom.value = true;
|
|
}
|
|
else {
|
|
if (isBottom.value) {
|
|
isBottom.value = false;
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* 翻页数据
|
|
*/
|
|
// function onLoadList() {
|
|
// console.log("翻页数据");
|
|
|
|
// const pageIdx = pageIndex.value + 1;
|
|
|
|
// if (pageIdx <= pageCount.value) {
|
|
// pageIndex.value = pageIdx;
|
|
// unLoading.value = false;
|
|
// getProducts(scrollId.value);
|
|
// }
|
|
// else {
|
|
// unLoading.value = true;
|
|
// }
|
|
// }
|
|
/**
|
|
* 下拉刷新
|
|
*/
|
|
// function onRefresherRefresh() {
|
|
// // console.log("下拉刷新");
|
|
// triggered.value = false;
|
|
// if (currentType.value != 0) {
|
|
// getProducts(typeData.value[currentType.value - 1].id, 1);
|
|
// }
|
|
// }
|
|
/**
|
|
* 触摸开始事件
|
|
* @param e 触摸事件参数
|
|
*/
|
|
function onTouchStart(e) {
|
|
touchDotX.value = e.touches[0].pageX;
|
|
touchDotY.value = e.touches[0].pageY;
|
|
touchStar.value = true;
|
|
}
|
|
/**
|
|
* 触摸移动事件
|
|
* @param e 触摸事件参数
|
|
*/
|
|
function onTouchMove(e) {
|
|
const touchMoveX = e.touches[0].pageX;
|
|
const touchMoveY = e.touches[0].pageY;
|
|
if (!touchStar.value) {
|
|
return;
|
|
}
|
|
const moveY = Math.abs(touchMoveY - touchDotY.value);
|
|
/**
|
|
* 左滑手势:横向滑动距离 ≥ 40 且纵向偏移 < 10,且当前不是最后一个一级分类时,切换到下一个一级分类
|
|
*/
|
|
const isLeft = touchMoveX - touchDotX.value <= -40
|
|
&& moveY < 10
|
|
&& (currentTab.value < navData.value.length - 1);
|
|
|
|
/**
|
|
* 右滑手势:横向滑动距离 ≥ 40 且纵向偏移 < 10,且当前不是第一个一级分类时,切换到上一个一级分类
|
|
*/
|
|
const isRight = touchMoveX - touchDotX.value >= 40
|
|
&& moveY < 10
|
|
&& (currentTab.value !== 0);
|
|
|
|
// 左右滑动切换一级分类时,重置当前二级分类为第一个
|
|
if (isLeft || isRight) {
|
|
currentType.value = 0;
|
|
products.value = [];
|
|
unLoading.value = false;
|
|
showList.value = true;
|
|
pageIndex.value = 1;
|
|
touchStar.value = false;
|
|
|
|
currentTab.value = isLeft ? currentTab.value + 1 : currentTab.value - 1;
|
|
const navId = navData.value[currentTab.value]?.id;
|
|
navScrollLeft.value = (currentTab.value - 2) * 50;
|
|
getLeftTypes(navId);
|
|
}
|
|
|
|
if (isBottom.value) {
|
|
bottomHeight.value = touchDotY.value > touchMoveY
|
|
? bottomHeight.value + 0.2 // 向上
|
|
: bottomHeight.value - 0.2; // 往下
|
|
}
|
|
}
|
|
/**
|
|
* 触摸结束事件
|
|
*/
|
|
async function onTouchEnd() {
|
|
if (
|
|
// 当底部手势滑动距离 ≥ 4 且当前二级分类索引未越界时,触发加载下一二级分类
|
|
bottomHeight.value >= 4
|
|
&& currentType.value <= typeData.value.length - 1
|
|
) {
|
|
bottomHeight.value = 0;
|
|
if (!isSearching.value) {
|
|
await getProducts(typeData.value[currentType.value + 1].id, 2);
|
|
}
|
|
}
|
|
|
|
if (
|
|
// 当底部手势滑动距离大于 0 且已滚动到底部时,重置手势滑动距离
|
|
bottomHeight.value >= 0 && isBottom.value
|
|
) {
|
|
bottomHeight.value = 0;
|
|
}
|
|
}
|
|
|
|
async function onGotoNext() {
|
|
await getProducts(typeData.value[currentType.value + 1].id, 2);
|
|
scrollTop.value = 0;
|
|
// if (currentType.value === typeData.value.length - 1) {
|
|
// return;
|
|
// }
|
|
// currentType.value += 1;
|
|
// scrollTop.value = 0;
|
|
// bottomHeight.value = 0;
|
|
}
|
|
|
|
function onGotoDetail(item) {
|
|
uni.navigateTo({
|
|
url: `/pages/allDish/dishDetail?id=${item.id}`,
|
|
fail(err) {
|
|
console.log(err, 333);
|
|
},
|
|
success(res) {
|
|
console.log(res, 222);
|
|
},
|
|
complete() {
|
|
console.log("跳转111");
|
|
},
|
|
});
|
|
}
|
|
/**
|
|
* 创建购物车小球的动画
|
|
*/
|
|
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;
|
|
}
|
|
/**
|
|
* 初始化购物车信息
|
|
*/
|
|
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 || [];
|
|
|
|
store.changeCartList(res.data);
|
|
totalPrice.value = res.data.reduce((acc, item) => acc + item.amount, 0).toFixed(2);
|
|
if (store.cartList.length > 0) {
|
|
preview(store.cartList.map(item => item.id));
|
|
}
|
|
else if (showCart.value) {
|
|
showCart.value = false;
|
|
}
|
|
}
|
|
/**
|
|
* 订单预览
|
|
* @param itemIds 商品id列表
|
|
*/
|
|
async function preview(itemIds) {
|
|
const data = {
|
|
itemIds: itemIds.join(","),
|
|
addrId: uni.getStorageSync("addressId"),
|
|
};
|
|
|
|
const res = await previewApi(data);
|
|
serviceFee.value = res.data.serviceFee;
|
|
shippingFee.value = res.data.shippingFee;
|
|
totalPrice.value = res.data.itemAmount;
|
|
}
|
|
/**
|
|
* 加入购物车
|
|
* @param item 商品项
|
|
*/
|
|
async function onAdd(item, e) {
|
|
// item.specs[0].sum = 9;
|
|
// if (item.sum === "") {
|
|
// item.sum = inputValue.value;
|
|
// }
|
|
|
|
if (!actionTime.value) {
|
|
return;
|
|
}
|
|
|
|
createAnimation(e.detail.x, e.detail.y);
|
|
|
|
const isPass = validates([
|
|
() => item.stock == item.sum && "采购数量不能大于库存数量",
|
|
() => item.stock == 0 && "库存数量为0无法添加",
|
|
]);
|
|
|
|
if (!isPass) {
|
|
return;
|
|
}
|
|
|
|
// 判空为 0, 否则转换为数字
|
|
item.sum = Number(item.sum) || 0;
|
|
// 起订量(item.minNum), 否则每次+1
|
|
const diff = item.sum === 0 ? (item.minNum || 1) : 1;
|
|
item.sum += diff;
|
|
// 保持输入框与购物车数量同步
|
|
inputValue.value = item.sum;
|
|
|
|
toCart(item, diff);
|
|
}
|
|
|
|
/**
|
|
* 更新购物车
|
|
* @param item 商品项
|
|
* @param diff 变化量
|
|
*/
|
|
async function toCart(item, diff) {
|
|
const data = {
|
|
quantity: diff,
|
|
specId: item.id,
|
|
Chuxiao: item.chuxiao,
|
|
isChuxiao: false,
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
addrId: uni.getStorageSync("addressId"),
|
|
// isChuxiao: item.chuxiao,
|
|
};
|
|
|
|
if (!data.specId) {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "当前商品规格错误,请稍候再试",
|
|
showCancel: false,
|
|
confirmText: "确定",
|
|
});
|
|
}
|
|
|
|
if (!data.warehouseId || !data.addrId) {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "请先选择收货地址,再添加商品",
|
|
showCancel: false,
|
|
confirmText: "确定",
|
|
});
|
|
}
|
|
|
|
// keys.push(item.id);
|
|
const res = await addCartApi(data);
|
|
// keys.splice(item.id, 1);
|
|
if (res.code !== "0") {
|
|
return;
|
|
}
|
|
store.changeCartList(res.data.items);
|
|
// initCartInfo();
|
|
}
|
|
|
|
/**
|
|
* 减少购买数量
|
|
* @param item 商品项
|
|
*/
|
|
function onMinus(item) {
|
|
// if (item.sum === "") {
|
|
// item.sum = inputValue.value;
|
|
// }
|
|
const isPass = validates([
|
|
() => item.stock == item.sum && "采购数量不能大于库存数量",
|
|
() => item.stock == 0 && "库存数量为0无法添加",
|
|
() => item.sum < 0 && "显示错误,采购数量不能小于0",
|
|
]);
|
|
|
|
if (!isPass) {
|
|
return;
|
|
}
|
|
|
|
// 判空为 0, 否则转换为数字
|
|
item.sum = Number(item.sum) || 0;
|
|
const minNum = item.minNum || 1;
|
|
// 达到起订量(item.minNum)直接清空, 否则每次-1
|
|
const diff = -(item.sum === minNum ? minNum : 1);
|
|
item.sum += diff;
|
|
|
|
// 保持输入框与购物车数量同步
|
|
inputValue.value = item.sum;
|
|
|
|
toCart(item, diff);
|
|
}
|
|
|
|
function onChooseNorm(item) {
|
|
item.showChild = !item.showChild;
|
|
}
|
|
|
|
// function onModelConfirm() { }
|
|
|
|
// function onModelCancel() { }
|
|
|
|
function openKeyBoard(item) {
|
|
isShowkeyboard.value = true;
|
|
store.cache.keyboardConfirm = (value) => {
|
|
determine(value, item);
|
|
};
|
|
}
|
|
/**
|
|
* 确定购买数量
|
|
* @param value 购买数量
|
|
* @param item 商品项
|
|
*/
|
|
async function determine(value, item) {
|
|
const isPass = validates([
|
|
() => item.stock != -1 && value > item.stock && "采购数量不能大于库存数量",
|
|
() => item.stock == 0 && "库存数量为0无法添加",
|
|
]);
|
|
|
|
if (!isPass) {
|
|
return;
|
|
}
|
|
|
|
item.sum = value;
|
|
isShowkeyboard.value = false;
|
|
|
|
const data = {
|
|
quantity: value,
|
|
specId: item.id,
|
|
Chuxiao: item.chuxiao,
|
|
warehouseId: uni.getStorageSync("warehousId"),
|
|
addrId: uni.getStorageSync("addressId"),
|
|
isUpdate: 1,
|
|
isChuxiao: false,
|
|
};
|
|
if (!data.specId) {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "当前商品规格错误,请稍候再试",
|
|
showCancel: false,
|
|
confirmText: "确定",
|
|
});
|
|
}
|
|
if (!data.warehouseId || !data.addrId) {
|
|
uni.showModal({
|
|
title: "提示",
|
|
content: "请先选择收货地址,再添加商品",
|
|
showCancel: false,
|
|
confirmText: "确定",
|
|
});
|
|
}
|
|
|
|
const res = await addCartApi(data);
|
|
if (res.code !== "0") {
|
|
return;
|
|
}
|
|
// store.changeCartList(res.data);
|
|
await initCartInfo();
|
|
}
|
|
|
|
onShow(() => {
|
|
if (store.productTypeId) {
|
|
currentType.value = 0;
|
|
}
|
|
|
|
showModalStatus.value = false;
|
|
showCart.value = false;
|
|
// cartList.value = [];
|
|
|
|
if (store.searchValue) {
|
|
onChangeKeyword({ detail: { value: store.searchValue } });
|
|
store.searchValue = "";
|
|
getProductTypes(store.productTypeId, true);
|
|
}
|
|
else {
|
|
getProductTypes(store.productTypeId);
|
|
}
|
|
|
|
if (store.showVoice) {
|
|
onOpenRecord();
|
|
store.showVoice = false;
|
|
}
|
|
|
|
nextTick(async () => {
|
|
const boxHeight = (await useRect(".first-box"))?.height || 0;
|
|
const { content, tabbar } = useHeight();
|
|
pageContainerHeight.value = content - boxHeight - tabbar;
|
|
});
|
|
});
|
|
|
|
// onLoad(async () => {
|
|
// const windowInfo = uni.getWindowInfo();
|
|
// tabbarHeight.value = 55 + (windowInfo.screenHeight - windowInfo.safeArea.bottom);
|
|
// screenHeight.value = windowInfo.screenHeight;
|
|
// tabbarItemWidth.value = store.tabbarItemWidth;
|
|
// const res = await useRect(".first-box");
|
|
// firstTypeHeight.value = res.height;
|
|
// console.log(res, "333");
|
|
// });
|
|
|
|
onHide(() => {
|
|
store.productTypeId = "";
|
|
isSearching.value = false;
|
|
keyword.value = "";
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<navv>
|
|
<template #default="{ menu, status }">
|
|
<view
|
|
class="header-box"
|
|
:style="{ paddingTop: `${status}px` }"
|
|
>
|
|
<view
|
|
class="box"
|
|
:style="{
|
|
height: `${menu || 45}px`,
|
|
width: menu ? '70%' : 'auto',
|
|
}"
|
|
>
|
|
<text class="iconfont icon-search1" />
|
|
<input
|
|
class="input"
|
|
type="text"
|
|
placeholder="请输入商品名称"
|
|
placeholder-style="color: #999999"
|
|
:value="keyword"
|
|
@onConfirm="onSearch"
|
|
@input="onChangeKeyword"
|
|
>
|
|
<text class="iconfont icon-yuyin" @tap="onOpenRecord" />
|
|
</view>
|
|
<!-- 横向的分类 一级分类 -->
|
|
<view class="first-box">
|
|
<view class="left">
|
|
<scroll-view
|
|
class="scroll-view"
|
|
scroll-x
|
|
scroll-with-animation
|
|
:scroll-left="navScrollLeft"
|
|
>
|
|
<view
|
|
v-for="(navItem, idx) in navData"
|
|
:key="idx"
|
|
class="list-box"
|
|
@tap="() => onSwitchNav(navItem, idx)"
|
|
>
|
|
<view class="type-list">
|
|
<image class="image" :src="navItem.imageUrl" mode="" />
|
|
<text
|
|
class="name" :class="[currentTab === idx ? 'name-active' : ''] "
|
|
>
|
|
{{ navItem.name }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
|
|
<view class="right" @tap="onShowAllTypes">
|
|
<text class="text">
|
|
展
|
|
</text>
|
|
<text class="text">
|
|
开
|
|
</text>
|
|
<text class="iconfont icon-down" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 内容 可滚动 -->
|
|
<view
|
|
class="page-container"
|
|
:style="{ height: `${pageContainerHeight}px` }"
|
|
>
|
|
<view v-if="!isSearching" class="left">
|
|
<view
|
|
v-for="(typeItem, typex) in typeData"
|
|
:key="typex"
|
|
class="text"
|
|
@tap="() => onSwitchType(typeItem, typex)"
|
|
>
|
|
<text
|
|
class="name"
|
|
:class="[currentType == typex ? 'text-active' : '']"
|
|
>
|
|
{{ typeItem.name }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
<!--
|
|
:scroll-top="scrollTop" 设置滚动条位置
|
|
scroll-y 允许纵向滚动
|
|
scroll-with-animation 滚动时带动画
|
|
show-scrollbar 显示滚动条
|
|
enhanced 启用增强特性
|
|
enable-passive 启用 passive 滚动,提升性能
|
|
refresher-enabled 启用下拉刷新
|
|
refresher-default-style="white" 下拉刷新默认样式为白色
|
|
:refresher-threshold="20" 下拉刷新触发阈值 20px
|
|
:refresher-triggered="triggered" 控制下拉刷新状态
|
|
:throttle="false" 关闭滚动节流
|
|
@scrolltolower="onLoadList" 滚动到底部时触发加载下一页
|
|
@scroll="onScroll" 滚动过程中实时监听滚动位置
|
|
@refresherrefresh="onRefresherrefresh" 下拉刷新时触发重新加载当前分类数据
|
|
-->
|
|
<scroll-view
|
|
id="scroll-page"
|
|
class="right"
|
|
:scroll-top="scrollTop"
|
|
scroll-y
|
|
scroll-with-animation
|
|
show-scrollbar
|
|
enhanced
|
|
enable-passive
|
|
refresher-enabled
|
|
refresher-default-style="white"
|
|
:refresher-threshold="20"
|
|
:refresher-triggered="triggered"
|
|
:refresherrestore="triggered"
|
|
:throttle="false"
|
|
:style="{
|
|
width: isSearching ? '100%' : '80%',
|
|
height: '100%',
|
|
marginTop: `${-(bottomHeight)}%`,
|
|
transition: '.01s all',
|
|
}"
|
|
@scrolltolower="() => {
|
|
// FIXME: onLoadList 原版如何触发
|
|
// onLoadList()
|
|
}"
|
|
@scroll="onScroll"
|
|
@refresherrefresh="() => {
|
|
// FIXME: onRefresherRefresh 原版如何触发
|
|
// onRefresherRefresh()
|
|
}"
|
|
>
|
|
<view
|
|
v-if="showList"
|
|
class="content-right"
|
|
@touchstart="onTouchStart"
|
|
@touchmove="onTouchMove"
|
|
@touchend="onTouchEnd"
|
|
>
|
|
<view
|
|
v-for="(item, idx) in products"
|
|
:key="idx"
|
|
class="right-list"
|
|
>
|
|
<view v-if="!item.showChoose && item.specs[0].stock == 0" class="of-stock" />
|
|
<view class="right-list-top">
|
|
<view
|
|
class="right-list-left"
|
|
@tap="() => onGotoDetail(item)"
|
|
>
|
|
<image class="image" :src="item.imageUrl" mode="" />
|
|
<view
|
|
v-if="!item.showChoose && item.specs[0].stock == 0"
|
|
class="of-stock-text"
|
|
>
|
|
补货中
|
|
</view>
|
|
</view>
|
|
<view class="right-list-right">
|
|
<view class="top" @tap="() => onGotoDetail(item)">
|
|
<view class="name">
|
|
{{ item.name }}
|
|
</view>
|
|
<view class="inventory">
|
|
{{
|
|
// 仅当商品只有1个规格且库存不为“无限库存”(-1)时才显示库存数量
|
|
item.specs.length == 1 && !(item.specs[0].stock == -1)
|
|
? `库存:${item.specs[0].stock}`
|
|
: ''
|
|
}}
|
|
</view>
|
|
</view>
|
|
<view class="under">
|
|
<view class="price-box" @tap="() => onGotoDetail(item)">
|
|
<view class="price">
|
|
<text>
|
|
<text class="moneySymbol">
|
|
¥
|
|
</text>{{ item.specs[0].price }}
|
|
</text>
|
|
</view>
|
|
<text v-if="item.specs[0].chuxiao" class="original">
|
|
正价:<text class="moneySymbol">
|
|
¥
|
|
</text>{{ item.specs[0].oldPrice }}/{{ item.specs[0].unit }}
|
|
</text>
|
|
<text v-else class="no-original">
|
|
/{{ item.specs[0].unit }}
|
|
</text>
|
|
</view>
|
|
<view v-if="!item.showChoose && item.specs[0].stock != 0" class="choose-box">
|
|
<block v-if="item.specs[0].sum !== 0">
|
|
<view class="line-one" />
|
|
<view class="line-two" />
|
|
<view class="minus" @tap.stop="() => onMinus(item.specs[0])">
|
|
<image class="icon" src="/static/home/minus.png" mode="" />
|
|
</view>
|
|
<text
|
|
class="input"
|
|
@tap="() => openKeyBoard(item.specs[0])"
|
|
>
|
|
{{ item.specs[0].sum }}
|
|
</text>
|
|
</block>
|
|
<view class="add" @tap.stop="(e) => onAdd(item.specs[0], e)">
|
|
<image class="icon" src="/static/home/add.png" mode="" />
|
|
</view>
|
|
</view>
|
|
<view
|
|
v-if="item.showChoose"
|
|
class="specifications"
|
|
@tap.stop="() => onChooseNorm(item)"
|
|
>
|
|
{{ item.showChild ? '收起' : '选规格' }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view v-if="item.showChild" class="right-list-under">
|
|
<view
|
|
v-for="(childs) in item.specs"
|
|
:key="childs.id"
|
|
class="goods-under-list"
|
|
>
|
|
<view class="price-box">
|
|
<view class="price">
|
|
<text class="moneySymbol">
|
|
¥
|
|
</text>
|
|
<text>{{ childs.price }}</text>
|
|
</view>
|
|
<text v-if="item.specs[0].chuxiao" class="original">
|
|
/{{ childs.unit }}
|
|
</text>
|
|
<text v-else class="no-original">
|
|
/{{ childs.unit }}
|
|
</text>
|
|
<view class="no-original">
|
|
{{ childs.stock == -1 ? '' : `库存:${childs.stock}` }}
|
|
</view>
|
|
</view>
|
|
<view class="choose-box">
|
|
<block v-if="childs.sum !== 0 || childs.sum != ''">
|
|
<view class="line-one" />
|
|
<view class="line-two" />
|
|
<view
|
|
class="minus"
|
|
data-quantity="-1"
|
|
data-exa="sum"
|
|
@tap="() => onMinus(childs)"
|
|
>
|
|
<image class="icon" src="/static/home/minus.png" mode="" />
|
|
</view>
|
|
<!-- <text
|
|
class="input"
|
|
@tap="() => showKeyboard()"
|
|
>
|
|
{{ childs.sum }}
|
|
</text> -->
|
|
<text
|
|
class="input"
|
|
@tap="() => openKeyBoard(childs)"
|
|
>
|
|
{{ childs.sum }}
|
|
</text>
|
|
</block>
|
|
<view
|
|
v-if="childs.stock != 0"
|
|
class="add"
|
|
data-quantity="1"
|
|
data-id="{{childs.id}}"
|
|
data-chuxiao="{{childs.chuxiao}}"
|
|
data-exa="sum"
|
|
@tap="(e) => onAdd(childs, e)"
|
|
>
|
|
<image class="icon" src="/static/home/add.png" mode="" />
|
|
</view>
|
|
<text v-if="childs.stock == 0" class="no-stock">
|
|
补货中
|
|
</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view
|
|
v-if="!isSearching
|
|
&& products.length !== 0
|
|
&& currentType != typeData.length - 1"
|
|
class="next"
|
|
@tap="onGotoNext"
|
|
>
|
|
<text>上划或点击进入</text>
|
|
<text style="color: #3aa24b;">
|
|
{{ typeData[currentType + 1].name }}
|
|
</text>
|
|
</view>
|
|
</view>
|
|
<view
|
|
v-if="products.length === 0"
|
|
:style="{
|
|
fontSize: '29rpx',
|
|
color: '#666666',
|
|
textAlign: 'center',
|
|
marginTop: '30rpx',
|
|
}"
|
|
>
|
|
暂无商品~
|
|
</view>
|
|
</scroll-view>
|
|
</view>
|
|
|
|
<!-- 遮罩层 -->
|
|
<view v-show="isShowAll" class="first-type-mask" @tap="onShowAllTypes" />
|
|
|
|
<!-- 展开全部分类 -->
|
|
<view>
|
|
<view
|
|
class="first-big-type"
|
|
:style="{
|
|
top: isShowAll
|
|
? `${status + (menu || 45) + 5}px`
|
|
: '-1600rpx',
|
|
}"
|
|
>
|
|
<view class="title">
|
|
全部分类
|
|
</view>
|
|
<view class="list-box">
|
|
<view
|
|
v-for="(navItem, idx) in navData"
|
|
:key="idx"
|
|
class="listx"
|
|
@tap="() => onSwitchNav(navItem, idx)"
|
|
>
|
|
<image
|
|
class="img"
|
|
:class="[currentTab == idx ? 'nav-item-img-active' : '']"
|
|
:src="navItem.imageUrl"
|
|
/>
|
|
<view
|
|
class="name"
|
|
:class="[currentTab == idx ? 'active' : '']"
|
|
>
|
|
{{ navItem.name }}
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<view
|
|
class="retract"
|
|
@tap="onShowAllTypes"
|
|
>
|
|
<view class="left">
|
|
点击收起
|
|
</view>
|
|
<text class="iconfont icon-up1" />
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- display: showBall ? '' : 'none' -->
|
|
<view
|
|
v-if="showBall"
|
|
:animation="animationY"
|
|
:style="{ position: 'fixed', top: `${testY}px` }"
|
|
>
|
|
<view
|
|
class="round"
|
|
:animation="animationX"
|
|
:style="{ position: 'fixed', left: `${testX}px` }"
|
|
/>
|
|
</view>
|
|
|
|
<keyboard
|
|
v-if="isShowkeyboard"
|
|
@cancel="() => isShowkeyboard = false"
|
|
/>
|
|
<!-- @confirm="(val) => determine(val)" -->
|
|
|
|
<customTabBar tab-index="1" />
|
|
</template>
|
|
</navv>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.moneySymbol {
|
|
font-size: $text-xs;
|
|
}
|
|
|
|
@import './allDish.scss';
|
|
</style>
|