Compare commits
3 Commits
9d893b4ed4
...
e2926b0c70
| Author | SHA1 | Date | |
|---|---|---|---|
| e2926b0c70 | |||
| 1701169c9d | |||
| e97cc128ec |
@@ -1,8 +1,6 @@
|
|||||||
import {useEffect, useState} from 'react'
|
import {useEffect, useState} from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import {View, Text, Image} from '@tarojs/components'
|
import {View, Text, Image} from '@tarojs/components'
|
||||||
import useAuthStore from '@/stores/auth/useAuthStore'
|
|
||||||
import useCartStore from '@/stores/cart/useCartStore'
|
|
||||||
import IndexImage from '@/static/images/nav/index.png';
|
import IndexImage from '@/static/images/nav/index.png';
|
||||||
import IndexActiveImage from '@/static/images/nav/index_active.png';
|
import IndexActiveImage from '@/static/images/nav/index_active.png';
|
||||||
import CartImage from '@/static/images/nav/cart.png';
|
import CartImage from '@/static/images/nav/cart.png';
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { View, Text, ScrollView } from '@tarojs/components'
|
import { View, Text, ScrollView } from '@tarojs/components'
|
||||||
import CustomNavBar from '@/components/NavBar'
|
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +8,6 @@ import './index.less'
|
|||||||
export default function AgreementPage() {
|
export default function AgreementPage() {
|
||||||
return (
|
return (
|
||||||
<View className='agreement-page'>
|
<View className='agreement-page'>
|
||||||
<CustomNavBar title='用户服务协议' />
|
|
||||||
<ScrollView scrollY className='agreement-scroll'>
|
<ScrollView scrollY className='agreement-scroll'>
|
||||||
<View className='agreement-content'>
|
<View className='agreement-content'>
|
||||||
<Text className='doc-title'>用户服务协议</Text>
|
<Text className='doc-title'>用户服务协议</Text>
|
||||||
|
|||||||
+26
-29
@@ -9,16 +9,24 @@
|
|||||||
padding-bottom: 160rpx;
|
padding-bottom: 160rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===== 待支付汇总 =====
|
// 底部待支付汇总栏留出空间
|
||||||
.bill-summary {
|
&--pay {
|
||||||
|
padding-bottom: 180rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 底部待支付汇总栏 =====
|
||||||
|
.bill-paybar {
|
||||||
|
position: fixed;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
z-index: 99;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
background: linear-gradient(135deg, #ee0a24, #ff4d4f);
|
padding: 16rpx 24rpx calc(16rpx + env(safe-area-inset-bottom));
|
||||||
border-radius: 16rpx;
|
background: #fff;
|
||||||
padding: 28rpx;
|
box-shadow: 0 -2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
margin-bottom: 20rpx;
|
|
||||||
color: #fff;
|
|
||||||
|
|
||||||
&__info {
|
&__info {
|
||||||
display: flex;
|
display: flex;
|
||||||
@@ -26,35 +34,24 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
&__label {
|
&__label {
|
||||||
font-size: 24rpx;
|
font-size: 22rpx;
|
||||||
opacity: 0.85;
|
color: #969799;
|
||||||
}
|
|
||||||
|
|
||||||
&__count {
|
|
||||||
margin-top: 8rpx;
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
&__amount {
|
&__amount {
|
||||||
font-size: 40rpx;
|
margin-top: 4rpx;
|
||||||
|
font-size: 36rpx;
|
||||||
|
color: #ee0a24;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
&__right {
|
&__btn {
|
||||||
display: flex;
|
padding: 16rpx 56rpx;
|
||||||
flex-direction: column;
|
|
||||||
align-items: flex-end;
|
|
||||||
}
|
|
||||||
|
|
||||||
&__pay {
|
|
||||||
margin-top: 12rpx;
|
|
||||||
padding: 8rpx 32rpx;
|
|
||||||
background: #fff;
|
|
||||||
color: #ee0a24;
|
|
||||||
font-size: 24rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
border-radius: 999rpx;
|
border-radius: 999rpx;
|
||||||
|
background: linear-gradient(135deg, #ee0a24, #ff6034);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+16
-16
@@ -29,7 +29,7 @@ interface CategoryOption {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 账单列表页
|
* 账单列表页
|
||||||
* 采购单完成后由后台按门店生成(只读);头部 summary 为门店口径待支付汇总(含审核中,不受筛选影响)
|
* 采购单完成后由后台按门店生成(只读);底部汇总栏为门店口径待支付汇总(含审核中,不受筛选影响)
|
||||||
* 支持多选账单合并导出 Excel(可按一级分类过滤商品明细)
|
* 支持多选账单合并导出 Excel(可按一级分类过滤商品明细)
|
||||||
*/
|
*/
|
||||||
export default function BillListPage() {
|
export default function BillListPage() {
|
||||||
@@ -185,22 +185,11 @@ export default function BillListPage() {
|
|||||||
[selectedIds, toggleSelectMode],
|
[selectedIds, toggleSelectMode],
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
/** 底部待支付汇总栏是否可见(多选导出时让位给导出栏) */
|
||||||
<View className={`bill-page ${selectMode ? 'bill-page--select' : ''}`}>
|
const showPayBar = loggedIn && !selectMode && !!summary && summary.unpaid_count > 0
|
||||||
{/* ========== 待支付汇总(门店口径,含审核中) ========== */}
|
|
||||||
{loggedIn && summary && summary.unpaid_count > 0 && (
|
|
||||||
<View className='bill-summary'>
|
|
||||||
<View className='bill-summary__info'>
|
|
||||||
<Text className='bill-summary__label'>待支付账单(含审核中)</Text>
|
|
||||||
<Text className='bill-summary__count'>{summary.unpaid_count} 笔</Text>
|
|
||||||
</View>
|
|
||||||
<View className='bill-summary__right'>
|
|
||||||
<Text className='bill-summary__amount'>¥{summary.unpaid_amount}</Text>
|
|
||||||
<View className='bill-summary__pay' onClick={goPay}>去付款</View>
|
|
||||||
</View>
|
|
||||||
</View>
|
|
||||||
)}
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className={`bill-page ${selectMode ? 'bill-page--select' : ''} ${showPayBar ? 'bill-page--pay' : ''}`}>
|
||||||
{/* ========== 状态筛选 + 导出入口 ========== */}
|
{/* ========== 状态筛选 + 导出入口 ========== */}
|
||||||
<View className='bill-toolbar'>
|
<View className='bill-toolbar'>
|
||||||
<ScrollView scrollX className='status-scroll'>
|
<ScrollView scrollX className='status-scroll'>
|
||||||
@@ -278,6 +267,17 @@ export default function BillListPage() {
|
|||||||
<View className='bill-loading'><Text>没有更多了</Text></View>
|
<View className='bill-loading'><Text>没有更多了</Text></View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* ========== 底部待支付汇总栏(门店口径,含审核中) ========== */}
|
||||||
|
{showPayBar && summary && (
|
||||||
|
<View className='bill-paybar'>
|
||||||
|
<View className='bill-paybar__info'>
|
||||||
|
<Text className='bill-paybar__label'>待支付账单(含审核中){summary.unpaid_count} 笔</Text>
|
||||||
|
<Text className='bill-paybar__amount'>¥{summary.unpaid_amount}</Text>
|
||||||
|
</View>
|
||||||
|
<View className='bill-paybar__btn' onClick={goPay}>去付款</View>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* ========== 导出操作栏 ========== */}
|
{/* ========== 导出操作栏 ========== */}
|
||||||
{selectMode && (
|
{selectMode && (
|
||||||
<View className='export-bar'>
|
<View className='export-bar'>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {formatRetailPrice, formatSpec} from '@/utils/format'
|
|||||||
import type { CartItem } from '@/types/cart'
|
import type { CartItem } from '@/types/cart'
|
||||||
import type { StoreDetail } from '@/types/store'
|
import type { StoreDetail } from '@/types/store'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
import CustomTabBar from "@/components/CustomTabBar";
|
||||||
|
|
||||||
export default function CartPage() {
|
export default function CartPage() {
|
||||||
const token = useAuthStore(s => s.token)
|
const token = useAuthStore(s => s.token)
|
||||||
@@ -351,6 +352,8 @@ export default function CartPage() {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</Popup>
|
</Popup>
|
||||||
|
|
||||||
|
{process.env.TARO_ENV === 'h5' && <CustomTabBar />}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -235,6 +235,17 @@
|
|||||||
color: #969799;
|
color: #969799;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&__loading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20rpx 0 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__loading-text {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #969799;
|
||||||
|
}
|
||||||
|
|
||||||
&__login-btn {
|
&__login-btn {
|
||||||
margin-top: 24rpx;
|
margin-top: 24rpx;
|
||||||
padding: 14rpx 60rpx;
|
padding: 14rpx 60rpx;
|
||||||
|
|||||||
+125
-54
@@ -1,22 +1,27 @@
|
|||||||
import { useCallback, useMemo, useState } from 'react'
|
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||||
import Taro, { useDidShow } from '@tarojs/taro'
|
import Taro, { useDidShow, useReachBottom } from '@tarojs/taro'
|
||||||
import { View, Text, Image } from '@tarojs/components'
|
import { View, Text, Image } from '@tarojs/components'
|
||||||
import { Grid, GridItem, Icon, Search, Swiper, SwiperItem } from '@antmjs/vantui'
|
import { Grid, GridItem, Icon, Search, Swiper, SwiperItem } from '@antmjs/vantui'
|
||||||
import useCartStore from '@/stores/cart/useCartStore'
|
import useCartStore from '@/stores/cart/useCartStore'
|
||||||
import { getHomeConfigApi } from '@/services/home'
|
import { getHomeConfigApi } from '@/services/home'
|
||||||
import type { HomeConfig } from '@/services/home'
|
import type { HomeConfig } from '@/services/home'
|
||||||
import { getProductListApi } from '@/services/product'
|
import { getSpecialListApi, normalizeSpecialCart } from '@/services/special'
|
||||||
import { getProductCover } from '@/types/product'
|
import { getProductCover } from '@/types/product'
|
||||||
import type { Product, ProductCartPatch } from '@/types/product'
|
import type { Product, ProductCartPatch } from '@/types/product'
|
||||||
|
import { getToken } from '@/utils/request'
|
||||||
import CartBall from '@/components/CartBall'
|
import CartBall from '@/components/CartBall'
|
||||||
import CartStepper from '@/components/CartStepper'
|
import CartStepper from '@/components/CartStepper'
|
||||||
import {formatRetailPrice, formatSpec} from '@/utils/format'
|
import {formatRetailPrice, formatSpec} from '@/utils/format'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
import CustomTabBar from "@/components/CustomTabBar";
|
||||||
|
|
||||||
/** 首页 → 商品页的本地存储传参 key(switchTab 无法带参) */
|
/** 首页 → 商品页的本地存储传参 key(switchTab 无法带参) */
|
||||||
const PENDING_CATEGORY_KEY = 'product_category_id'
|
const PENDING_CATEGORY_KEY = 'product_category_id'
|
||||||
const PENDING_KEYWORD_KEY = 'product_keyword'
|
const PENDING_KEYWORD_KEY = 'product_keyword'
|
||||||
|
|
||||||
|
/** 特价推荐每页条数 */
|
||||||
|
const SPECIAL_PAGE_SIZE = 10
|
||||||
|
|
||||||
/** tabBar 页面路径(link 跳转需改用 switchTab) */
|
/** tabBar 页面路径(link 跳转需改用 switchTab) */
|
||||||
const TAB_PATHS = [
|
const TAB_PATHS = [
|
||||||
'pages/index/index',
|
'pages/index/index',
|
||||||
@@ -45,8 +50,17 @@ export default function IndexPage() {
|
|||||||
|
|
||||||
/** 首页配置(轮播图 / 宫格导航 / 促销卡片) */
|
/** 首页配置(轮播图 / 宫格导航 / 促销卡片) */
|
||||||
const [config, setConfig] = useState<HomeConfig>({ banners: [], navs: [], promos: [] })
|
const [config, setConfig] = useState<HomeConfig>({ banners: [], navs: [], promos: [] })
|
||||||
/** 推荐商品 */
|
/** 特价推荐商品(后台「客户端配置 → 特价推荐」标记,价格为登录门店的等级价) */
|
||||||
const [products, setProducts] = useState<Product[]>([])
|
const [specials, setSpecials] = useState<Product[]>([])
|
||||||
|
/** 特价推荐分页 */
|
||||||
|
const [specialPage, setSpecialPage] = useState(1)
|
||||||
|
const [specialHasMore, setSpecialHasMore] = useState(false)
|
||||||
|
/** 加载更多中(首屏重置不展示,避免已渲染列表下方闪烁) */
|
||||||
|
const [specialLoading, setSpecialLoading] = useState(false)
|
||||||
|
/** 特价推荐请求序号(返回 tab 重置与上拉加载并发时,仅采用最后一次响应) */
|
||||||
|
const specialSeqRef = useRef(0)
|
||||||
|
/** 是否有「加载更多」请求进行中 */
|
||||||
|
const specialLoadingRef = useRef(false)
|
||||||
/** 搜索框输入 */
|
/** 搜索框输入 */
|
||||||
const [keyword, setKeyword] = useState('')
|
const [keyword, setKeyword] = useState('')
|
||||||
|
|
||||||
@@ -54,7 +68,13 @@ export default function IndexPage() {
|
|||||||
|
|
||||||
useDidShow(() => {
|
useDidShow(() => {
|
||||||
loadHomeConfig()
|
loadHomeConfig()
|
||||||
loadRecommend()
|
loadSpecials(1, true)
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 上拉加载更多特价推荐 */
|
||||||
|
useReachBottom(() => {
|
||||||
|
if (!specialHasMore) return
|
||||||
|
loadSpecials(specialPage + 1, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 首页配置聚合数据(响应附带悬浮球汇总) */
|
/** 首页配置聚合数据(响应附带悬浮球汇总) */
|
||||||
@@ -69,15 +89,37 @@ export default function IndexPage() {
|
|||||||
}
|
}
|
||||||
}, [setSummary])
|
}, [setSummary])
|
||||||
|
|
||||||
/** 推荐商品 */
|
/**
|
||||||
const loadRecommend = useCallback(async () => {
|
* 特价推荐商品(reset 时回到第一页整体替换)。
|
||||||
try {
|
* 行结构与 /mini/product/list 一致;响应附带悬浮球汇总
|
||||||
const res = await getProductListApi({ page: 1, pageSize: 10 })
|
*/
|
||||||
setProducts(res.data.data)
|
const loadSpecials = useCallback(
|
||||||
} catch {
|
async (pageNum: number, reset: boolean) => {
|
||||||
// 错误已由 request 层 toast
|
if (!reset && specialLoadingRef.current) return
|
||||||
}
|
const seq = ++specialSeqRef.current
|
||||||
}, [])
|
specialLoadingRef.current = true
|
||||||
|
if (!reset) setSpecialLoading(true)
|
||||||
|
try {
|
||||||
|
const res = await getSpecialListApi({ page: pageNum, pageSize: SPECIAL_PAGE_SIZE })
|
||||||
|
if (seq !== specialSeqRef.current) return // 已有更新的请求,丢弃本次响应
|
||||||
|
const { data, total, cart } = res.data
|
||||||
|
setSpecials(prev => (reset ? data : [...prev, ...data]))
|
||||||
|
setSpecialPage(pageNum)
|
||||||
|
setSpecialHasMore(pageNum * SPECIAL_PAGE_SIZE < total)
|
||||||
|
// 列表响应附带悬浮球汇总(旧版本后端可能未返回)
|
||||||
|
const summary = normalizeSpecialCart(cart)
|
||||||
|
if (summary) setSummary(summary)
|
||||||
|
} catch {
|
||||||
|
// 错误已由 request 层 toast
|
||||||
|
} finally {
|
||||||
|
if (seq === specialSeqRef.current) {
|
||||||
|
specialLoadingRef.current = false
|
||||||
|
setSpecialLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[setSummary],
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 后台配置的 link 统一跳转:
|
* 后台配置的 link 统一跳转:
|
||||||
@@ -115,9 +157,9 @@ export default function IndexPage() {
|
|||||||
goProduct(keyword.trim())
|
goProduct(keyword.trim())
|
||||||
}, [goProduct, keyword])
|
}, [goProduct, keyword])
|
||||||
|
|
||||||
/** 行内加减购确认后回写推荐商品项的购物车字段 */
|
/** 行内加减购确认后回写特价商品项的购物车字段 */
|
||||||
const handleRowSync = useCallback((productId: number, patch: ProductCartPatch) => {
|
const handleRowSync = useCallback((productId: number, patch: ProductCartPatch) => {
|
||||||
setProducts(prev => prev.map(p => (p.id === productId ? { ...p, ...patch } : p)))
|
setSpecials(prev => prev.map(p => (p.id === productId ? { ...p, ...patch } : p)))
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
/** 快捷加购(用返回的购物车行回写,卡片随即展示加减器) */
|
/** 快捷加购(用返回的购物车行回写,卡片随即展示加减器) */
|
||||||
@@ -135,6 +177,16 @@ export default function IndexPage() {
|
|||||||
[addItem, handleRowSync],
|
[addItem, handleRowSync],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/** 无价格时点击:未登录引导登录,已登录但未设等级价提示原因 */
|
||||||
|
const handlePriceGuide = useCallback((e: any) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
if (getToken()) {
|
||||||
|
Taro.showToast({ title: '该商品暂未设置等级价', icon: 'none' })
|
||||||
|
} else {
|
||||||
|
Taro.navigateTo({ url: '/pages/login/index' })
|
||||||
|
}
|
||||||
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='home-page'>
|
<View className='home-page'>
|
||||||
{/* ========== 自定义顶部导航栏 ========== */}
|
{/* ========== 自定义顶部导航栏 ========== */}
|
||||||
@@ -237,62 +289,81 @@ export default function IndexPage() {
|
|||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* ========== 推荐商品 ========== */}
|
{/* ========== 特价推荐 ========== */}
|
||||||
<View className='home-recommend'>
|
<View className='home-recommend'>
|
||||||
<View className='home-recommend__header'>
|
<View className='home-recommend__header'>
|
||||||
<View className='home-recommend__title-wrap'>
|
<View className='home-recommend__title-wrap'>
|
||||||
<View className='home-recommend__title-bar' />
|
<View className='home-recommend__title-bar' />
|
||||||
<Text className='home-recommend__title'>推荐商品</Text>
|
<Text className='home-recommend__title'>特价推荐</Text>
|
||||||
</View>
|
</View>
|
||||||
<Text className='home-recommend__more' onClick={() => goProduct()}>查看更多 ›</Text>
|
<Text className='home-recommend__more' onClick={() => goProduct()}>查看更多 ›</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{ products.length === 0 ? (
|
{ specials.length === 0 ? (
|
||||||
<View className='home-recommend__empty'>
|
<View className='home-recommend__empty'>
|
||||||
<Text className='home-recommend__empty-text'>暂无商品</Text>
|
<Text className='home-recommend__empty-text'>暂无特价商品</Text>
|
||||||
</View>
|
</View>
|
||||||
) : (
|
) : (
|
||||||
<View className='product-grid'>
|
<>
|
||||||
{products.map(product => (
|
<View className='product-grid'>
|
||||||
<View key={product.id} className='product-card' onClick={() => goDetail(product.id)}>
|
{specials.map(product => (
|
||||||
<Image
|
<View key={product.id} className='product-card' onClick={() => goDetail(product.id)}>
|
||||||
className='product-card__image'
|
<Image
|
||||||
src={getProductCover(product) || 'https://img.yzcdn.cn/vant/cat.jpeg'}
|
className='product-card__image'
|
||||||
mode='aspectFill'
|
src={getProductCover(product) || 'https://img.yzcdn.cn/vant/cat.jpeg'}
|
||||||
lazyLoad
|
mode='aspectFill'
|
||||||
/>
|
lazyLoad
|
||||||
<View className='product-card__info'>
|
/>
|
||||||
<Text className='product-card__name'>{product.name}</Text>
|
<View className='product-card__info'>
|
||||||
<View className='product-card__spec'>
|
<Text className='product-card__name'>{product.name}</Text>
|
||||||
{formatSpec(product.spec, product.unit)}{' '}
|
<View className='product-card__spec'>
|
||||||
{product.price !== null && <>
|
{formatSpec(product.spec, product.unit)}{' '}
|
||||||
单价:{formatRetailPrice(product.price, product.spec)} {product.price_unit}
|
{product.price !== null && <>
|
||||||
</>}
|
单价:{formatRetailPrice(product.price, product.spec)} {product.price_unit}
|
||||||
</View>
|
</>}
|
||||||
<View className='product-card__bottom'>
|
</View>
|
||||||
{product.price !== null ? (
|
<View className='product-card__bottom'>
|
||||||
<Text className='product-card__price'>¥{product.price}</Text>
|
{product.price !== null ? (
|
||||||
) : (
|
<Text className='product-card__price'>¥{product.price}</Text>
|
||||||
<Text className='product-card__price product-card__price--none'>登陆后查看价格</Text>
|
) : (
|
||||||
)}
|
<Text
|
||||||
{/* 已加购展示行内加减器,否则展示快捷加购按钮 */}
|
className='product-card__price product-card__price--none'
|
||||||
{Number(product.cart_quantity ?? 0) > 0 ? (
|
onClick={handlePriceGuide}
|
||||||
<CartStepper product={product} onSync={handleRowSync} />
|
>
|
||||||
) : (
|
登录后查看价格
|
||||||
<View className='product-card__add' onClick={e => handleQuickAdd(product, e)}>
|
</Text>
|
||||||
<Text className='product-card__add-icon'>+</Text>
|
)}
|
||||||
</View>
|
{/* 已加购展示行内加减器,否则展示快捷加购按钮 */}
|
||||||
)}
|
{Number(product.cart_quantity ?? 0) > 0 ? (
|
||||||
|
<CartStepper product={product} onSync={handleRowSync} />
|
||||||
|
) : (
|
||||||
|
<View className='product-card__add' onClick={e => handleQuickAdd(product, e)}>
|
||||||
|
<Text className='product-card__add-icon'>+</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
{/* 加载更多状态 */}
|
||||||
|
{specialLoading && (
|
||||||
|
<View className='home-recommend__loading'>
|
||||||
|
<Text className='home-recommend__loading-text'>加载中…</Text>
|
||||||
</View>
|
</View>
|
||||||
))}
|
)}
|
||||||
</View>
|
{!specialHasMore && specialPage > 1 && (
|
||||||
|
<View className='home-recommend__loading'>
|
||||||
|
<Text className='home-recommend__loading-text'>已加载全部特价商品</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
{/* ========== 购物车悬浮球 ========== */}
|
{/* ========== 购物车悬浮球 ========== */}
|
||||||
<CartBall />
|
<CartBall />
|
||||||
|
{process.env.TARO_ENV === 'h5' && <CustomTabBar />}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -132,6 +132,8 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
font-size: 30px;
|
font-size: 30px;
|
||||||
color: #323233;
|
color: #323233;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form-input-placeholder {
|
.form-input-placeholder {
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { useCallback, useState } from 'react'
|
import { useCallback, useState } from 'react'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import { View, Text, Button, Input } from '@tarojs/components'
|
import { View, Text, Button, Input } from '@tarojs/components'
|
||||||
import CustomNavBar from '@/components/NavBar'
|
|
||||||
import useAuthStore from '@/stores/auth/useAuthStore'
|
import useAuthStore from '@/stores/auth/useAuthStore'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
|
||||||
@@ -76,8 +75,6 @@ export default function LoginPage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<View className='login-page'>
|
<View className='login-page'>
|
||||||
{/* ========== 导航栏 ========== */}
|
|
||||||
<CustomNavBar title='登录' />
|
|
||||||
|
|
||||||
{/* ========== 内容区域 ========== */}
|
{/* ========== 内容区域 ========== */}
|
||||||
<View className='login-content'>
|
<View className='login-content'>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import { formatTime } from '@/utils/format'
|
|||||||
import { NOTICE_TYPE_MAP } from '@/types/notice'
|
import { NOTICE_TYPE_MAP } from '@/types/notice'
|
||||||
import type { Notice, NoticeType } from '@/types/notice'
|
import type { Notice, NoticeType } from '@/types/notice'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
import CustomTabBar from "@/components/CustomTabBar";
|
||||||
|
|
||||||
const PAGE_SIZE = 10
|
const PAGE_SIZE = 10
|
||||||
|
|
||||||
@@ -123,6 +124,8 @@ export default function MessagePage() {
|
|||||||
{loggedIn && finished && notices.length > 0 && (
|
{loggedIn && finished && notices.length > 0 && (
|
||||||
<View className='message-loading'><Text>没有更多了</Text></View>
|
<View className='message-loading'><Text>没有更多了</Text></View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{process.env.TARO_ENV === 'h5' && <CustomTabBar />}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import { View, Text, ScrollView } from '@tarojs/components'
|
import { View, Text, ScrollView } from '@tarojs/components'
|
||||||
import CustomNavBar from '@/components/NavBar'
|
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -9,7 +8,6 @@ import './index.less'
|
|||||||
export default function PrivacyPage() {
|
export default function PrivacyPage() {
|
||||||
return (
|
return (
|
||||||
<View className='privacy-page'>
|
<View className='privacy-page'>
|
||||||
<CustomNavBar title='隐私政策' />
|
|
||||||
<ScrollView scrollY className='privacy-scroll'>
|
<ScrollView scrollY className='privacy-scroll'>
|
||||||
<View className='privacy-content'>
|
<View className='privacy-content'>
|
||||||
<Text className='doc-title'>隐私政策</Text>
|
<Text className='doc-title'>隐私政策</Text>
|
||||||
|
|||||||
@@ -3,9 +3,6 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
// 底部预留给自定义 tabBar(110rpx + 安全区),避免列表被遮挡
|
|
||||||
padding-bottom: calc(110rpx + env(safe-area-inset-bottom));
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.product-search {
|
.product-search {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import CartBall from '@/components/CartBall'
|
|||||||
import CartStepper from '@/components/CartStepper'
|
import CartStepper from '@/components/CartStepper'
|
||||||
import {formatRetailPrice, formatSpec} from '@/utils/format'
|
import {formatRetailPrice, formatSpec} from '@/utils/format'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
import CustomTabBar from "@/components/CustomTabBar";
|
||||||
|
|
||||||
const PAGE_SIZE = 10
|
const PAGE_SIZE = 10
|
||||||
/** 存储 key:首页点击分类/搜索跳转时经本地存储传参(switchTab 无法带参) */
|
/** 存储 key:首页点击分类/搜索跳转时经本地存储传参(switchTab 无法带参) */
|
||||||
@@ -331,6 +332,7 @@ export default function ProductPage() {
|
|||||||
|
|
||||||
{/* ========== 购物车悬浮球 ========== */}
|
{/* ========== 购物车悬浮球 ========== */}
|
||||||
<CartBall />
|
<CartBall />
|
||||||
|
{process.env.TARO_ENV === 'h5' && <CustomTabBar />}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import type { BillSummary } from '@/services/bill'
|
|||||||
import { ORDER_NAV_ITEMS } from '@/types/order'
|
import { ORDER_NAV_ITEMS } from '@/types/order'
|
||||||
import { resolveAvatarUrl } from '@/utils/format'
|
import { resolveAvatarUrl } from '@/utils/format'
|
||||||
import './index.less'
|
import './index.less'
|
||||||
|
import CustomTabBar from "@/components/CustomTabBar";
|
||||||
|
|
||||||
/** 菜单项(订单/账单入口已由上方专区承载,后续单独页面开发时在此追加) */
|
/** 菜单项(订单/账单入口已由上方专区承载,后续单独页面开发时在此追加) */
|
||||||
const MENU_ITEMS = [
|
const MENU_ITEMS = [
|
||||||
@@ -229,6 +230,8 @@ export default function ProfilePage() {
|
|||||||
<Text>退出登录</Text>
|
<Text>退出登录</Text>
|
||||||
</View>
|
</View>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{process.env.TARO_ENV === 'h5' && <CustomTabBar />}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { get } from '@/utils/request'
|
||||||
|
import type { PaginatedData } from '@/types/api'
|
||||||
|
import type { CartSummary } from '@/types/cart'
|
||||||
|
import type { Product } from '@/types/product'
|
||||||
|
|
||||||
|
/** 特价推荐列表参数 */
|
||||||
|
export interface SpecialListParams {
|
||||||
|
page?: number
|
||||||
|
pageSize?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 特价推荐列表响应(分页 + 购物车悬浮球汇总)。
|
||||||
|
* 行结构与 /mini/product/list 一致:商品基础字段 + price + cart_id/cart_quantity
|
||||||
|
*/
|
||||||
|
export interface SpecialListData extends PaginatedData<Product> {
|
||||||
|
/**
|
||||||
|
* 购物车悬浮球汇总(未登录返回零值结构;旧版本后端可能未返回,调用方判空)。
|
||||||
|
* 接口文档示例为 count/quantity/amount 简写,与 /mini/home 等接口的 total_* 命名不一致,
|
||||||
|
* 统一经 normalizeSpecialCart 转换后再写入 store
|
||||||
|
*/
|
||||||
|
cart?: CartSummary | { count: number; quantity: string; amount: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 特价推荐商品列表(免登录;携带门店 token 时返回等级价与购物车字段):GET /mini/special/list */
|
||||||
|
export function getSpecialListApi(params: SpecialListParams = {}) {
|
||||||
|
return get<SpecialListData>('/mini/special/list', { data: params })
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 特价推荐响应附带的悬浮球汇总归一化(兼容 count/quantity/amount 与 total_* 两种命名) */
|
||||||
|
export function normalizeSpecialCart(cart: SpecialListData['cart']): CartSummary | null {
|
||||||
|
if (!cart) return null
|
||||||
|
const raw = cart as Record<string, unknown>
|
||||||
|
const count = raw.total_count ?? raw.count
|
||||||
|
const quantity = raw.total_quantity ?? raw.quantity
|
||||||
|
const amount = raw.total_amount ?? raw.amount
|
||||||
|
if (count == null || quantity == null || amount == null) return null
|
||||||
|
return {
|
||||||
|
total_count: Number(count),
|
||||||
|
total_quantity: String(quantity),
|
||||||
|
total_amount: String(amount),
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user