显示格式优化
This commit is contained in:
@@ -0,0 +1,21 @@
|
||||
// ===== 零售价标注:小字置灰 =====
|
||||
.price-text__retail {
|
||||
font-size: 20rpx;
|
||||
color: #969799;
|
||||
font-weight: 400;
|
||||
line-height: 1.4;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
// inline 模式:与大价格同行,左间距分隔
|
||||
.price-text__retail--inline {
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
|
||||
// block 模式:大价格 / 零售价上下两行(窄卡片布局)
|
||||
.price-text {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
line-height: 1.3;
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Text, View } from '@tarojs/components'
|
||||
import { formatRetailPrice } from '@/utils/format'
|
||||
import './index.less'
|
||||
|
||||
interface PriceTextProps {
|
||||
/** 售价(展示为 ¥price) */
|
||||
price: string | number
|
||||
/** 包规(用于计算零售价;无法计算时不展示零售价) */
|
||||
spec?: string | number | null
|
||||
/** 大价格样式类(字号 / 颜色由调用方控制) */
|
||||
className?: string
|
||||
/**
|
||||
* 零售价布局:
|
||||
* - inline 跟随大价格同行(宽裕区域:商品详情、各类弹层行)
|
||||
* - block 独占一行(窄卡片:首页推荐、商品列表、购物车)
|
||||
*/
|
||||
mode?: 'inline' | 'block'
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品价格:售价 + 零售价标注(小字)
|
||||
* price=30、spec=15 → ¥30 零售价:¥2
|
||||
*/
|
||||
export default function PriceText({ price, spec, className, mode = 'inline' }: PriceTextProps) {
|
||||
const retail = formatRetailPrice(price, spec)
|
||||
|
||||
// 窄卡片:大价格 / 零售价上下两行,避免与右侧按钮(+/步进器)挤压换行
|
||||
if (mode === 'block') {
|
||||
return (
|
||||
<View className={`price-text ${className ?? ''}`}>
|
||||
<Text className='price-text__main'>¥{price}</Text>
|
||||
{retail !== null && (
|
||||
<Text className='price-text__retail'>零售价:¥{retail}</Text>
|
||||
)}
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Text className={className}>
|
||||
¥{price}
|
||||
{retail !== null && (
|
||||
<Text className='price-text__retail price-text__retail--inline'>零售价:¥{retail}</Text>
|
||||
)}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
@@ -5,7 +5,8 @@ import { Empty, Popup } from '@antmjs/vantui'
|
||||
import { getBillDetailApi } from '@/services/bill'
|
||||
import { getOrderDetailApi } from '@/services/order'
|
||||
import { ORDER_STATUS_TEXT } from '@/types/order'
|
||||
import { resolveFileUrl } from '@/utils/format'
|
||||
import { formatSpec, resolveFileUrl } from '@/utils/format'
|
||||
import PriceText from '@/components/PriceText'
|
||||
import type { OrderDetail } from '@/types/order'
|
||||
import type { BillDetail } from '@/services/bill'
|
||||
import './index.less'
|
||||
@@ -161,7 +162,8 @@ export default function BillDetailPage() {
|
||||
<View className='bill-goods__main'>
|
||||
<Text className='bill-goods__name'>{item.product_name}</Text>
|
||||
<Text className='bill-goods__spec'>
|
||||
{item.product_spec ? `${item.product_spec} ` : ''}¥{item.price}/{item.unit}
|
||||
{formatSpec(item.product_spec, item.unit)}{' '}
|
||||
<PriceText price={item.price} spec={item.product_spec} />
|
||||
</Text>
|
||||
</View>
|
||||
<View className='bill-goods__side'>
|
||||
@@ -214,7 +216,8 @@ export default function BillDetailPage() {
|
||||
<View className='order-popup__item-info'>
|
||||
<Text className='order-popup__item-name'>{item.product_name}</Text>
|
||||
<Text className='order-popup__item-spec'>
|
||||
{item.product_spec ? `${item.product_spec} ` : ''}¥{item.price}/{item.unit} × {item.quantity}
|
||||
{formatSpec(item.product_spec, item.unit)}{' '}
|
||||
<PriceText price={item.price} spec={item.product_spec} /> × {item.quantity}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className='order-popup__item-amount'>¥{item.amount}</Text>
|
||||
|
||||
@@ -5,6 +5,8 @@ import { Button, Empty, Icon, Popup, Stepper } from '@antmjs/vantui'
|
||||
import useCartStore from '@/stores/cart/useCartStore'
|
||||
import { createOrderApi } from '@/services/order'
|
||||
import { getStoreInfoApi } from '@/services/store'
|
||||
import PriceText from '@/components/PriceText'
|
||||
import { formatSpec } from '@/utils/format'
|
||||
import type { CartItem } from '@/types/cart'
|
||||
import type { StoreDetail } from '@/types/store'
|
||||
import './index.less'
|
||||
@@ -205,10 +207,10 @@ export default function CartPage() {
|
||||
<Text className='cart-item__name'>{item.name}</Text>
|
||||
{item.status === 0 && <Text className='cart-item__invalid-tag'>已失效</Text>}
|
||||
</View>
|
||||
<Text className='cart-item__spec'>{item.spec} / {item.unit}</Text>
|
||||
<Text className='cart-item__spec'>{formatSpec(item.spec, item.unit)}</Text>
|
||||
<View className='cart-item__bottom'>
|
||||
{item.price !== null ? (
|
||||
<Text className='cart-item__price'>¥{item.price}</Text>
|
||||
<PriceText className='cart-item__price' price={item.price} spec={item.spec} mode='block' />
|
||||
) : (
|
||||
<Text className='cart-item__price cart-item__price--none'>价格待定</Text>
|
||||
)}
|
||||
@@ -302,7 +304,7 @@ export default function CartPage() {
|
||||
<Image className='order-popup__item-image' src={item.image} mode='aspectFill' lazyLoad />
|
||||
<View className='order-popup__item-title'>
|
||||
<Text className='order-popup__item-name'>{item.name}</Text>
|
||||
<Text className='order-popup__item-spec'>{item.spec} / {item.unit}</Text>
|
||||
<Text className='order-popup__item-spec'>{formatSpec(item.spec, item.unit)}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='order-popup__item-right'>
|
||||
|
||||
@@ -9,6 +9,8 @@ import type { HomeConfig } from '@/services/home'
|
||||
import { getProductListApi } from '@/services/product'
|
||||
import { getProductCover } from '@/types/product'
|
||||
import type { Product } from '@/types/product'
|
||||
import PriceText from '@/components/PriceText'
|
||||
import { formatSpec } from '@/utils/format'
|
||||
import './index.less'
|
||||
|
||||
/** 首页 → 商品页的本地存储传参 key(switchTab 无法带参) */
|
||||
@@ -247,10 +249,10 @@ export default function IndexPage() {
|
||||
/>
|
||||
<View className='product-card__info'>
|
||||
<Text className='product-card__name'>{product.name}</Text>
|
||||
<Text className='product-card__spec'>{product.spec} / {product.unit}</Text>
|
||||
<Text className='product-card__spec'>{formatSpec(product.spec, product.unit)}</Text>
|
||||
<View className='product-card__bottom'>
|
||||
{product.price !== null ? (
|
||||
<Text className='product-card__price'>¥{product.price}</Text>
|
||||
<PriceText className='product-card__price' price={product.price} spec={product.spec} mode='block' />
|
||||
) : (
|
||||
<Text className='product-card__price product-card__price--none'>登陆后查看价格</Text>
|
||||
)}
|
||||
|
||||
@@ -5,7 +5,8 @@ import { Empty, Popup } from '@antmjs/vantui'
|
||||
import useAuthStore from '@/stores/auth/useAuthStore'
|
||||
import { cancelOrderApi, getOrderDetailApi, getOrderListApi } from '@/services/order'
|
||||
import { ORDER_STATUS_FILTERS, ORDER_STATUS_TEXT } from '@/types/order'
|
||||
import { resolveFileUrl } from '@/utils/format'
|
||||
import { formatSpec, resolveFileUrl } from '@/utils/format'
|
||||
import PriceText from '@/components/PriceText'
|
||||
import type { OrderDetail, OrderListItem, OrderStatus } from '@/types/order'
|
||||
import './index.less'
|
||||
|
||||
@@ -184,8 +185,8 @@ export default function OrderListPage() {
|
||||
)}
|
||||
<View className='order-item__goods-info'>
|
||||
<Text className='order-item__goods-name'>{i.product_name}</Text>
|
||||
{!!i.product_spec && (
|
||||
<Text className='order-item__goods-spec'>{i.product_spec} {i.unit}</Text>
|
||||
{!!formatSpec(i.product_spec, i.unit) && (
|
||||
<Text className='order-item__goods-spec'>{formatSpec(i.product_spec, i.unit)}</Text>
|
||||
)}
|
||||
</View>
|
||||
<Text className='order-item__goods-qty'>×{i.quantity} </Text>
|
||||
@@ -262,7 +263,8 @@ export default function OrderListPage() {
|
||||
<View className='detail-popup__item-info'>
|
||||
<Text className='detail-popup__item-name'>{item.product_name}</Text>
|
||||
<Text className='detail-popup__item-spec'>
|
||||
{item.product_spec ? `${item.product_spec}${item.unit} ` : ''}¥{item.price} × {item.quantity}
|
||||
{formatSpec(item.product_spec, item.unit)}{' '}
|
||||
<PriceText price={item.price} spec={item.product_spec} /> × {item.quantity}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className='detail-popup__item-amount'>¥{item.amount}</Text>
|
||||
|
||||
@@ -61,12 +61,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
&__unit {
|
||||
margin-left: 8rpx;
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
&__name {
|
||||
display: block;
|
||||
margin-top: 16rpx;
|
||||
|
||||
@@ -5,7 +5,8 @@ import { Empty, Stepper, Swiper, SwiperItem } from '@antmjs/vantui'
|
||||
import useAuthStore from '@/stores/auth/useAuthStore'
|
||||
import useCartStore from '@/stores/cart/useCartStore'
|
||||
import { getProductDetailApi } from '@/services/product'
|
||||
import { resolveFileUrl } from '@/utils/format'
|
||||
import { formatSpec, resolveFileUrl } from '@/utils/format'
|
||||
import PriceText from '@/components/PriceText'
|
||||
import type { Product } from '@/types/product'
|
||||
import './index.less'
|
||||
|
||||
@@ -115,16 +116,15 @@ export default function ProductDetailPage() {
|
||||
<View className='goods-card'>
|
||||
<View className='goods-card__price-row'>
|
||||
{product.price !== null ? (
|
||||
<Text className='goods-card__price'>¥{product.price}</Text>
|
||||
<PriceText className='goods-card__price' price={product.price} spec={product.spec} />
|
||||
) : (
|
||||
<Text className='goods-card__price goods-card__price--none'>
|
||||
{loggedIn ? '价格待定' : '登录后查看价格'}
|
||||
</Text>
|
||||
)}
|
||||
<Text className='goods-card__unit'>/{product.unit}</Text>
|
||||
</View>
|
||||
<Text className='goods-card__name'>{product.name}</Text>
|
||||
<Text className='goods-card__spec'>{product.spec}</Text>
|
||||
<Text className='goods-card__spec'>{formatSpec(product.spec, product.unit)}</Text>
|
||||
<View className='goods-card__meta'>
|
||||
{!!product.shelf_life && product.shelf_life > 0 && (
|
||||
<Text className='goods-card__tag'>保质期 {product.shelf_life} 天</Text>
|
||||
|
||||
@@ -7,6 +7,8 @@ import { getCategoriesApi, getProductListApi } from '@/services/product'
|
||||
import type { ProductListParams } from '@/services/product'
|
||||
import { getProductCover } from '@/types/product'
|
||||
import type { Category, Product } from '@/types/product'
|
||||
import PriceText from '@/components/PriceText'
|
||||
import { formatSpec } from '@/utils/format'
|
||||
import './index.less'
|
||||
|
||||
const PAGE_SIZE = 10
|
||||
@@ -288,10 +290,10 @@ export default function ProductPage() {
|
||||
/>
|
||||
<View className='product-item__info'>
|
||||
<Text className='product-item__name'>{product.name}</Text>
|
||||
<Text className='product-item__spec'>{product.spec} / {product.unit}</Text>
|
||||
<Text className='product-item__spec'>{formatSpec(product.spec, product.unit)}</Text>
|
||||
<View className='product-item__bottom'>
|
||||
{product.price !== null ? (
|
||||
<Text className='product-item__price'>¥{product.price}</Text>
|
||||
<PriceText className='product-item__price' price={product.price} spec={product.spec} mode='block' />
|
||||
) : (
|
||||
<Text className='product-item__price product-item__price--none'>价格待定</Text>
|
||||
)}
|
||||
@@ -339,9 +341,9 @@ export default function ProductPage() {
|
||||
/>
|
||||
<View className='add-popup__info'>
|
||||
<Text className='add-popup__name'>{current.name}</Text>
|
||||
<Text className='add-popup__spec'>{current.spec} / {current.unit}</Text>
|
||||
<Text className='add-popup__spec'>{formatSpec(current.spec, current.unit)}</Text>
|
||||
{current.price !== null ? (
|
||||
<Text className='add-popup__price'>¥{current.price}</Text>
|
||||
<PriceText className='add-popup__price' price={current.price} spec={current.spec} />
|
||||
) : (
|
||||
<Text className='add-popup__price add-popup__price--none'>价格待定</Text>
|
||||
)}
|
||||
|
||||
@@ -55,3 +55,26 @@ export function resolveFileUrl(url?: string): string {
|
||||
export function resolveAvatarUrl(avatar?: string): string {
|
||||
return resolveFileUrl(avatar)
|
||||
}
|
||||
|
||||
/**
|
||||
* 商品规格展示:包规与单位直接拼接
|
||||
* spec=20、unit=斤/箱 → 20斤/箱
|
||||
*/
|
||||
export function formatSpec(spec?: string | number | null, unit?: string | null): string {
|
||||
const s = spec === null || spec === undefined ? '' : String(spec).trim()
|
||||
const u = (unit ?? '').trim()
|
||||
return `${s}${u}`
|
||||
}
|
||||
|
||||
/**
|
||||
* 零售价 = 售价 ÷ 包规(如 30¥/箱 ÷ 20斤/箱 = 2¥/斤)
|
||||
* 保留两位小数并去掉尾零(2 → "2",2.50 → "2.5")
|
||||
* 售价为空、包规非数字或 ≤0 时返回 null(不展示零售价)
|
||||
*/
|
||||
export function formatRetailPrice(price?: string | number | null, spec?: string | number | null): string | null {
|
||||
if (price === null || price === undefined || price === '') return null
|
||||
const p = Number(price)
|
||||
const s = Number(spec)
|
||||
if (!Number.isFinite(p) || !Number.isFinite(s) || s <= 0) return null
|
||||
return String(Math.round((p / s) * 100) / 100)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user