Browse Source
refactor(components): 移除classification页面并重构导航组件
refactor(components): 移除classification页面并重构导航组件
重构导航组件使用uni.getWindowInfo替代uni.getSystemInfoSync 新增topTitle和nav组件 更新utils中的useHeight方法并添加useRect工具函数 添加allDish页面的样式文件master
8 changed files with 1332 additions and 996 deletions
-
55components/nav/nav.vue
-
2components/navigation/navigation.vue
-
52components/topTitle/topTitle.vue
-
44libs/utils/index.js
-
7pages/allDish/allDish.scss
-
1184pages/allDish/allDish.vue
-
5pages/classification/classification.md
-
979pages/classification/classification.vue
@ -0,0 +1,55 @@ |
|||
<script setup> |
|||
import { onLoad } from "@dcloudio/uni-app"; |
|||
import { ref } from "vue"; |
|||
import { useHeight } from "@/libs/utils"; |
|||
// import useStore from "@/store"; |
|||
|
|||
// const store = useStore(); |
|||
|
|||
const safeHeight = ref({ |
|||
status: 0, |
|||
menu: 0, |
|||
screen: 0, |
|||
content: 0, |
|||
bottom: 0, |
|||
}); |
|||
|
|||
onLoad(() => { |
|||
const height = useHeight(); |
|||
// console.log(height); |
|||
|
|||
safeHeight.value.status = height.status; |
|||
safeHeight.value.menu = height.menu; |
|||
safeHeight.value.screen = height.screen; |
|||
safeHeight.value.content = height.content; |
|||
safeHeight.value.bottom = height.bottom; |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<view> |
|||
<slot |
|||
:status="safeHeight.status" |
|||
:menu="safeHeight.menu" |
|||
:screen="safeHeight.screen" |
|||
:bottom="safeHeight.bottom" |
|||
:tabbar="safeHeight.tabbar" |
|||
:fix-style=" { |
|||
height: safeHeight.menu ? `${safeHeight.menu}px` : '45px', |
|||
paddingTop: `${safeHeight.status}px`, |
|||
backgroundColor: '#f8f8f8', |
|||
width: '100%', |
|||
position: 'fixed', |
|||
top: 0, |
|||
zIndex: 99, |
|||
left: 0, |
|||
display: 'flex', |
|||
alignItems: 'center', |
|||
} " |
|||
:pad-style="{ |
|||
paddingTop: `${safeHeight.status + (safeHeight.menu || 45) + 5}px`, |
|||
height: `${safeHeight.menu || 45}px`, |
|||
}" |
|||
/> |
|||
</view> |
|||
</template> |
|||
@ -0,0 +1,52 @@ |
|||
<script setup> |
|||
import { defineProps } from "vue"; |
|||
import { gotoBack } from "@/libs/utils"; |
|||
|
|||
const props = defineProps( |
|||
{ |
|||
title: { |
|||
type: String, |
|||
default() { |
|||
return ""; |
|||
}, |
|||
}, |
|||
}, |
|||
); |
|||
</script> |
|||
|
|||
<template> |
|||
<view class="left"> |
|||
<text |
|||
class="iconfont icon-back" |
|||
:style="{ color: '#333' }" |
|||
@click="gotoBack" |
|||
/> |
|||
<text |
|||
class="title" |
|||
:style="{ color: '#333' }" |
|||
> |
|||
{{ props.title }} |
|||
</text> |
|||
</view> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
.left { |
|||
display: flex; |
|||
align-items: center; |
|||
padding-left: 29rpx; |
|||
|
|||
.icon-back { |
|||
font-size: 42rpx; |
|||
font-weight: bold; |
|||
color: black; |
|||
} |
|||
|
|||
.title { |
|||
font-weight: bold; |
|||
font-size: 33rpx; |
|||
color: black; |
|||
// margin-left: 5rpx; |
|||
} |
|||
} |
|||
</style> |
|||
1184
pages/allDish/allDish.vue
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
@ -1,5 +0,0 @@ |
|||
add |
|||
item specs[0] |
|||
exa sum |
|||
quantity 1 |
|||
id specs[0].id |
|||
@ -1,979 +0,0 @@ |
|||
<script setup> |
|||
import { onShow } from "@dcloudio/uni-app"; |
|||
import { nextTick, ref } from "vue"; |
|||
import customTabBar from "@/components/custom-tab-bar/my-tab-bar.vue"; |
|||
import navigation from "@/components/navigation/navigation.vue"; |
|||
import { |
|||
addCartApi, |
|||
getCartInfoApi, |
|||
getProductsApi, |
|||
getProductTypesApi, |
|||
previewApi, |
|||
} from "@/libs/api"; |
|||
import { getHeight, useHeight, 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({}); |
|||
/** |
|||
* 当前标签 |
|||
*/ |
|||
const currentTab = ref(0); |
|||
/** |
|||
* 当前子类标签 |
|||
*/ |
|||
const currentType = ref(0); |
|||
/** |
|||
* 右侧商品列表的高度 |
|||
*/ |
|||
const bottomHeight = ref(0); |
|||
/** |
|||
* 右侧商品列表的滚动高度 |
|||
*/ |
|||
const scrollTop = 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 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 pageContainerHeight = ref(0); |
|||
/** |
|||
* 标记是否已获取过购物车(首次加载后设为true) |
|||
*/ |
|||
const hasFetchedCart = ref(true); |
|||
/** |
|||
* 上次点击加入购物车的时间 |
|||
*/ |
|||
// const actionTime = ref(true); |
|||
/** |
|||
* 输入购买数量弹框的值 |
|||
*/ |
|||
const inputValue = ref(""); |
|||
/** |
|||
* 隐藏购买数量弹框 |
|||
*/ |
|||
// const hiddenModal = ref(true); |
|||
|
|||
function onSearch() {} |
|||
|
|||
function onChangeKeyword() {} |
|||
/** |
|||
* 展开顶部类型数据 |
|||
*/ |
|||
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); |
|||
} |
|||
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") { |
|||
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; |
|||
const total = cartList.reduce((cur, acc) => acc + cur.quantity, 0); |
|||
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; |
|||
} |
|||
|
|||
res.data.forEach((item) => { |
|||
// 初始化购物车映射表(格式:specId_price -> quantity) |
|||
const cartMap = cartList.reduce((acc, cur) => { |
|||
return { ...acc, [`${cur.specId}_${cur.price}`]: cur.quantity }; |
|||
}, {}); |
|||
|
|||
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; |
|||
}); |
|||
|
|||
products.value = pageIndex.value === 1 |
|||
? res.data |
|||
: [...products.value, ...(res.data || [])]; |
|||
|
|||
rightHeight.value = (await getHeight("#scroll-page")) + 1; |
|||
if (tips === 1) { |
|||
currentType.value -= 1; |
|||
} |
|||
else if (tips === 2) { |
|||
currentType.value += 1; |
|||
scrollTop.value = 0; |
|||
bottomHeight.value = 0; |
|||
} |
|||
uni.hideNavigationBarLoading(); // 停止下拉刷新 |
|||
uni.stopPullDownRefresh(); |
|||
} |
|||
/** |
|||
* 滚动事件 |
|||
* @param {Event} e - 滚动事件参数 |
|||
*/ |
|||
function onScroll(e) { |
|||
isBottom.value = (e.detail.scrollHeight - e.detail.scrollTop) <= rightHeight.value; |
|||
} |
|||
/** |
|||
* 翻页数据 |
|||
*/ |
|||
function onLoadList() { |
|||
const pageIdx = pageIndex.value + 1; |
|||
if (pageIdx <= pageCount.value) { |
|||
pageIndex.value = pageIdx; |
|||
unLoading.value = false; |
|||
getProducts(scrollId.value); |
|||
} |
|||
else { |
|||
unLoading.value = true; |
|||
} |
|||
} |
|||
/** |
|||
* 下拉刷新 |
|||
*/ |
|||
function onRefresherrefresh() { |
|||
triggered.value = false; |
|||
if (currentType.value !== 0) { |
|||
getProducts(typeData[currentType.value - 1].id, 1); |
|||
} |
|||
} |
|||
/** |
|||
* 触摸开始事件 |
|||
* @param e 触摸事件参数 |
|||
*/ |
|||
function onTouchStart(e) { |
|||
touchDotX.value = e.touches[0].pageX; |
|||
touchDotY.value = e.touches[0].pageY; |
|||
touchStar.value = false; |
|||
} |
|||
/** |
|||
* 触摸移动事件 |
|||
* @param e 触摸事件参数 |
|||
*/ |
|||
function onTouchMove(e) { |
|||
const pageX = e.touches[0].pageX; |
|||
const pageY = e.touches[0].pageY; |
|||
if (!touchStar.value) { |
|||
return; |
|||
} |
|||
const moveY = Math.abs(pageY - touchDotY.value); |
|||
/** |
|||
* 左滑手势:横向滑动距离 ≥ 40 且纵向偏移 < 10,且当前不是最后一个一级分类时,切换到下一个一级分类 |
|||
*/ |
|||
const isLeft = pageX - touchDotX.value <= -40 |
|||
&& moveY < 10 |
|||
&& (currentTab.value < navData.value.length - 1); |
|||
|
|||
/** |
|||
* 右滑手势:横向滑动距离 ≥ 40 且纵向偏移 < 10,且当前不是第一个一级分类时,切换到上一个一级分类 |
|||
*/ |
|||
const isRight = pageX - 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); |
|||
} |
|||
|
|||
bottomHeight.value = touchDotY.value > pageY |
|||
? bottomHeight.value + 0.2 // 向上 |
|||
: bottomHeight.value - 0.2; // 往下 |
|||
} |
|||
/** |
|||
* 触摸结束事件 |
|||
*/ |
|||
function onTouchEnd() { |
|||
if ( |
|||
// 当底部手势滑动距离 ≥ 4 且当前二级分类索引未越界时,触发加载下一二级分类 |
|||
bottomHeight.value >= 4 |
|||
&& currentType.value <= typeData.value.length - 1 |
|||
) { |
|||
bottomHeight.value = 0; |
|||
if (!isSearching.value) { |
|||
getProducts(typeData.value[currentType.value + 1].id, 2); |
|||
} |
|||
} |
|||
if ( |
|||
// 当底部手势滑动距离大于 0 且已滚动到底部时,重置手势滑动距离 |
|||
bottomHeight.value > 0 |
|||
&& bottomHeight.value >= 0 |
|||
&& isBottom.value |
|||
) { |
|||
bottomHeight.value = 0; |
|||
} |
|||
} |
|||
|
|||
function onGotoNext() { |
|||
if (currentType.value === typeData.value.length - 1) { |
|||
return; |
|||
} |
|||
currentType.value += 1; |
|||
scrollTop.value = 0; |
|||
bottomHeight.value = 0; |
|||
} |
|||
|
|||
function onGotoDetail(item) { |
|||
uni.navigateTo({ |
|||
url: `/pages/detail/detail?id=${item.id}`, |
|||
}); |
|||
} |
|||
/** |
|||
* 初始化购物车信息 |
|||
*/ |
|||
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.goodsCheckedItems = res.data.map(item => item.id); ; |
|||
totalPrice.value = res.data.reduce((acc, item) => acc + item.amount, 0).toFixed(2); |
|||
if (store.goodsCheckedItems > 0) { |
|||
preview(store.goodsCheckedItems); |
|||
} |
|||
else if (showCart.value) { |
|||
showList.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) { |
|||
// item.specs[0].sum = 9; |
|||
// if (item.sum === "") { |
|||
// item.sum = inputValue.value; |
|||
// } |
|||
const isPass = validates([ |
|||
() => item.stock == item.sum && "采购数量不能大于库存数量", |
|||
() => item.stock == 0 && "库存数量为0无法添加", |
|||
]); |
|||
|
|||
if (!isPass) { |
|||
return; |
|||
} |
|||
|
|||
// 判空为 0, 否则转换为数字 |
|||
item.sum = Number(item.sum) || 0; |
|||
// 起订量(item.minNum), 否则每次+1 |
|||
item.sum += item.sum === 0 ? (item.minNum || 1) : 1; |
|||
// 保持输入框与购物车数量同步 |
|||
inputValue.value = item.sum; |
|||
|
|||
toCart(item); |
|||
} |
|||
|
|||
/** |
|||
* 更新购物车 |
|||
* @param item 商品项 |
|||
*/ |
|||
async function toCart(item) { |
|||
const data = { |
|||
quantity: item.sum, |
|||
specId: item.id, |
|||
Chuxiao: item.chuxiao, |
|||
warehouseId: uni.getStorageSync("warehousId"), |
|||
addrId: uni.getStorageSync("addressId"), |
|||
}; |
|||
|
|||
if (!data.specId) { |
|||
uni.showModal({ |
|||
title: "提示", |
|||
content: "当前商品规格错误,请稍候再试", |
|||
showCancel: false, |
|||
confirmText: "确定", |
|||
}); |
|||
} |
|||
|
|||
if (data.Chuxiao === "" || data.Chuxiao === undefined) { |
|||
data.Chuxiao = false; |
|||
} |
|||
|
|||
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") { |
|||
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; |
|||
// 达到起订量(item.minNum)直接清空, 否则每次-1 |
|||
item.sum -= item.sum === (item.minNum || 1) ? (item.minNum || 1) : 1; |
|||
// 保持输入框与购物车数量同步 |
|||
inputValue.value = item.sum; |
|||
|
|||
toCart(item); |
|||
} |
|||
|
|||
function onChooseNorm(item) { |
|||
item.showChild = !item.showChild; |
|||
} |
|||
|
|||
// function onModelConfirm() { } |
|||
|
|||
// function onModelCancel() { } |
|||
|
|||
function onShowKeyboard() { } |
|||
|
|||
onShow(() => { |
|||
getProductTypes(); |
|||
safeHeight.value = useHeight(); |
|||
|
|||
nextTick(async () => { |
|||
const boxHeight = await getHeight(".first-box"); |
|||
const { screen, status, menu, bottom } = safeHeight.value; |
|||
pageContainerHeight.value = screen - status - menu - bottom - boxHeight - 60; |
|||
// searchHeight.value = await getHeight(".header-box"); |
|||
}); |
|||
}); |
|||
</script> |
|||
|
|||
<template> |
|||
<!-- 微信的胶囊以及搜索框 --> |
|||
<navigation background="#fff" class="header-box"> |
|||
<div |
|||
class="box" |
|||
:style="{ |
|||
height: `${safeHeight.menu || 45}px`, |
|||
width: safeHeight.menu ? '70%' : '100%', |
|||
}" |
|||
> |
|||
<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" @click="onOpenRecord" /> |
|||
</div> |
|||
</navigation> |
|||
|
|||
<!-- 横向的分类 一级分类 --> |
|||
<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" |
|||
@click="() => 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" @click="onShowAllTypes"> |
|||
<text class="text"> |
|||
展 |
|||
</text> |
|||
<text class="text"> |
|||
开 |
|||
</text> |
|||
<text class="iconfont icon-down" /> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 遮罩层 --> |
|||
<view v-show="isShowAll" class="first-type-mask" @click="onShowAllTypes" /> |
|||
|
|||
<!-- 展开全部分类 --> |
|||
<view |
|||
class="first-big-type" |
|||
:style="{ |
|||
top: isShowAll |
|||
? `${safeHeight.status + (safeHeight.menu || 45) + 5}px` |
|||
: '-1600rpx', |
|||
}" |
|||
> |
|||
<view class="title"> |
|||
全部分类 |
|||
</view> |
|||
<view class="list-box"> |
|||
<view |
|||
v-for="(navItem, idx) in navData" |
|||
:key="idx" |
|||
class="listx" |
|||
@click="() => 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" |
|||
@click="onShowAllTypes" |
|||
> |
|||
<view class="left"> |
|||
点击收起 |
|||
</view> |
|||
<text class="iconfont icon-up1" /> |
|||
</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" |
|||
@click="() => 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" |
|||
:throttle="false" |
|||
:style="{ |
|||
width: isSearching ? '100%' : '80%', |
|||
height: '100%', |
|||
marginTop: `${-(bottomHeight)}%`, |
|||
transition: '.01s all', |
|||
}" |
|||
@scrolltolower="onLoadList" |
|||
@scroll="onScroll" |
|||
@refresherrefresh="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" |
|||
@click="() => 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" @click="() => 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" @click="() => onGotoDetail(item)"> |
|||
<view class="price"> |
|||
<text>¥{{ item.specs[0].price }}</text> |
|||
</view> |
|||
<text v-if="item.specs[0].chuxiao" class="original"> |
|||
正价:¥{{ 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" @click.stop="() => onMinus(item)"> |
|||
<image class="icon" src="/static/home/minus.png" mode="" /> |
|||
</view> |
|||
<text class="input" @click="() => onShowKeyboard(item)"> |
|||
{{ item.specs[0].sum }} |
|||
</text> |
|||
</block> |
|||
<view class="add" @click.stop="() => onAdd(item)"> |
|||
<image class="icon" src="/static/home/add.png" mode="" /> |
|||
</view> |
|||
</view> |
|||
<view |
|||
v-if="item.showChoose" |
|||
class="specifications" |
|||
@click.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>¥{{ 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" |
|||
@click="onSetSum" |
|||
> |
|||
<image class="icon" src="/static/home/minus.png" mode="" /> |
|||
</view> |
|||
<text |
|||
class="input" |
|||
@click="() => showKeyboard()" |
|||
> |
|||
{{ 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" |
|||
@click="() => setSum(childs)" |
|||
> |
|||
<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" |
|||
@click="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> |
|||
|
|||
<!-- 输入购买数量弹框 --> |
|||
<!-- <modal |
|||
v-show="!hiddenModal" |
|||
title="修改购买数量" |
|||
confirm-text="确定" |
|||
cancel-text="取消" |
|||
@confirm="onModelConfirm" |
|||
@cancel="onModelCancel" |
|||
> |
|||
<input |
|||
placeholder="输入所需数量" |
|||
focus="{{focus}}" |
|||
:value="inputValue" |
|||
type="number" |
|||
@input="input" |
|||
> |
|||
</modal> --> |
|||
|
|||
<customTabBar tab-index="1" /> |
|||
</template> |
|||
|
|||
<style lang="scss" scoped> |
|||
@import './classification.scss'; |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue