fix(mini-user): 门店套餐图片自适应完整显示,不再裁剪
CI / verify (push) Has been cancelled

- ProductCarousel 新增 imageFit="adaptive"(widthFix + 动态高度)
- 门店套餐详情按图片真实比例自适应高度,完整显示不裁剪
- 补充 v3.4.18 文档 F 项

Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
This commit is contained in:
2026-08-17 21:48:06 +08:00
parent f7c94b4f16
commit 18147e157f
4 changed files with 86 additions and 5 deletions
@@ -1,4 +1,4 @@
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { View, Image, Swiper, SwiperItem } from '@tarojs/components';
import Taro from '@tarojs/taro';
@@ -7,8 +7,12 @@ type ProductCarouselProps = {
alt: string;
variant?: 'home' | 'detail' | 'store';
previewable?: boolean;
/** cover=aspectFill 裁剪铺满;contain=aspectFit 缩放完整显示(门店门头固定区) */
imageFit?: 'cover' | 'contain';
/**
* cover=aspectFill 裁剪铺满(固定高度,可能裁切)
* contain=aspectFit 完整显示(固定高度,可能留白)
* adaptive=widthFix 按图片真实比例自适应高度,完整显示不裁剪(门店套餐详情用)
*/
imageFit?: 'cover' | 'contain' | 'adaptive';
};
/** 商品/门店轮播(对齐 h5 ProductCarousel 三变体) */
@@ -21,10 +25,55 @@ export default function ProductCarousel({
}: ProductCarouselProps) {
const slides = images.length > 0 ? images : [''];
const [activeIndex, setActiveIndex] = useState(0);
// adaptive 模式:实测容器宽度与各图渲染高度
const [wrapWidth, setWrapWidth] = useState(0);
const [imgHeights, setImgHeights] = useState<number[]>([]);
const prefix =
variant === 'store' ? 'store-detail-carousel' : variant === 'home' ? 'home-carousel' : 'detail-carousel';
const isContain = imageFit === 'contain';
const wrapClass = `${prefix}-wrap${isContain ? ` ${prefix}-wrap--contain` : ''}`;
const isAdaptive = imageFit === 'adaptive';
const wrapClass = `${prefix}-wrap${isContain ? ` ${prefix}-wrap--contain` : ''}${isAdaptive ? ` ${prefix}-wrap--adaptive` : ''}`;
function getWindowWidth(): number {
try {
const info = (Taro as any).getWindowInfo?.() ?? Taro.getSystemInfoSync();
return info?.windowWidth || 375;
} catch {
return 375;
}
}
useEffect(() => {
if (!isAdaptive) return;
const query = Taro.createSelectorQuery();
query
.select(`.${prefix}-wrap--adaptive`)
.boundingClientRect((rect: { width?: number }) => {
if (rect && rect.width) setWrapWidth(rect.width);
else setWrapWidth(getWindowWidth());
})
.exec();
// 仅挂载时测量一次容器宽度(页面宽度稳定)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isAdaptive, prefix]);
function handleImageLoad(index: number, e: any) {
const detail = e?.detail;
const naturalW = detail?.width;
const naturalH = detail?.height;
if (!naturalW || !naturalH) return;
const w = wrapWidth || getWindowWidth();
const h = Math.round((naturalH / naturalW) * w);
setImgHeights((prev) => {
const next = [...prev];
next[index] = h;
return next;
});
}
// adaptive:随当前幻灯片高度变化;未加载时给一个 3:4 兜底高度
const adaptiveHeight = imgHeights[activeIndex] || Math.round((wrapWidth || getWindowWidth()) * 0.75);
const swiperStyle = isAdaptive ? { height: `${adaptiveHeight}px` } : undefined;
function previewAt(index: number) {
const urls = slides.filter(Boolean);
@@ -37,6 +86,7 @@ export default function ProductCarousel({
<View className={wrapClass}>
<Swiper
className={prefix}
style={swiperStyle}
circular={slides.length > 1}
{...(variant === 'detail' && slides.length > 1 ? { autoplay: true, interval: 3500 } : {})}
onChange={(e) => setActiveIndex(e.detail.current)}
@@ -47,8 +97,9 @@ export default function ProductCarousel({
<Image
className={`${prefix}-image`}
src={src}
mode={isContain ? 'aspectFit' : 'aspectFill'}
mode={isAdaptive ? 'widthFix' : isContain ? 'aspectFit' : 'aspectFill'}
alt={alt}
onLoad={isAdaptive ? (e) => handleImageLoad(index, e) : undefined}
onClick={previewable ? () => previewAt(index) : undefined}
/>
) : (
@@ -143,6 +143,7 @@ export default function StorePackageDetailPage() {
alt={pkg.name}
variant="detail"
previewable
imageFit="adaptive"
/>
</View>
) : null}
@@ -18,6 +18,21 @@
overflow: hidden;
}
/* adaptive 模式:去掉固定 1:1 比例,按图片真实比例自适应高度,完整显示不裁剪 */
.detail-carousel-wrap--adaptive {
aspect-ratio: auto;
overflow: visible;
}
.detail-carousel-wrap--adaptive .detail-carousel {
height: auto;
transition: height 0.2s ease;
}
.detail-carousel-wrap--adaptive .detail-carousel-image {
height: auto;
}
.detail-carousel {
width: 100%;
height: 100%;