Binary file not shown.
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 2.4 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 207 B After Width: | Height: | Size: 16 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 224 B After Width: | Height: | Size: 3.0 KiB |
@@ -1,19 +1,20 @@
|
||||
import { Text, View } from '@tarojs/components';
|
||||
import BenefitFigure from './BenefitFigure';
|
||||
|
||||
type CouponBadgeProps = {
|
||||
amount: number | string;
|
||||
label?: string;
|
||||
};
|
||||
|
||||
/** Taro 友好版权益角标(对齐 shared-ui CouponBadge) */
|
||||
/** 首页商品角标:纯文案「享{amount}好客权益」(无门店图标) */
|
||||
export default function CouponBadge({ amount, label = '好客权益' }: CouponBadgeProps) {
|
||||
const n = Number(amount);
|
||||
const display = Number.isFinite(n) ? (Number.isInteger(n) ? String(n) : n.toFixed(0)) : String(amount);
|
||||
return (
|
||||
<View className="coupon-badge">
|
||||
<Text>享</Text>
|
||||
<BenefitFigure value={`${display} ${label}`} size="sm" />
|
||||
<Text>
|
||||
享{display}
|
||||
{label}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
export type CheckoutContext = {
|
||||
productId?: string;
|
||||
skuId?: string;
|
||||
qty?: string;
|
||||
addressId?: string;
|
||||
cross?: boolean;
|
||||
@@ -9,6 +10,7 @@ export type CheckoutContext = {
|
||||
export function buildQuery(ctx: CheckoutContext): string {
|
||||
const parts: string[] = [];
|
||||
if (ctx.productId) parts.push(`productId=${encodeURIComponent(ctx.productId)}`);
|
||||
if (ctx.skuId) parts.push(`skuId=${encodeURIComponent(ctx.skuId)}`);
|
||||
if (ctx.qty) parts.push(`qty=${encodeURIComponent(ctx.qty)}`);
|
||||
if (ctx.addressId) parts.push(`addressId=${encodeURIComponent(ctx.addressId)}`);
|
||||
if (ctx.cross) parts.push('cross=1');
|
||||
@@ -36,12 +38,14 @@ export function buildAddressEditUrl(id: string | undefined, ctx: CheckoutContext
|
||||
export function buildPayUrl(params: {
|
||||
orderId: string;
|
||||
productId?: string;
|
||||
skuId?: string;
|
||||
qty?: string;
|
||||
addressId?: string;
|
||||
cross?: boolean;
|
||||
}): string {
|
||||
const parts = [`orderId=${encodeURIComponent(params.orderId)}`];
|
||||
if (params.productId) parts.push(`productId=${encodeURIComponent(params.productId)}`);
|
||||
if (params.skuId) parts.push(`skuId=${encodeURIComponent(params.skuId)}`);
|
||||
if (params.qty) parts.push(`qty=${encodeURIComponent(params.qty)}`);
|
||||
if (params.addressId) parts.push(`addressId=${encodeURIComponent(params.addressId)}`);
|
||||
if (params.cross) parts.push('cross=1');
|
||||
@@ -51,6 +55,7 @@ export function buildPayUrl(params: {
|
||||
export function readCheckoutContext(params: Record<string, string | undefined>): CheckoutContext {
|
||||
return {
|
||||
productId: params.productId,
|
||||
skuId: params.skuId,
|
||||
qty: params.qty,
|
||||
addressId: params.addressId,
|
||||
cross: params.cross === '1',
|
||||
|
||||
@@ -32,12 +32,14 @@ type OrderPreview = {
|
||||
quantityOk?: boolean;
|
||||
quantityMessage?: string | null;
|
||||
minQty?: number;
|
||||
saleUnit?: 'BOTTLE' | 'BOX';
|
||||
};
|
||||
|
||||
export default function OrderConfirmPickupPage() {
|
||||
const router = useRouter();
|
||||
const productId = router.params.productId ?? '';
|
||||
const [quantity, setQuantity] = useState(Math.max(2, Number(router.params.qty || 2)));
|
||||
const skuId = router.params.skuId ?? '';
|
||||
const [quantity, setQuantity] = useState(Math.max(1, Number(router.params.qty || 2)));
|
||||
const [preview, setPreview] = useState<OrderPreview | null>(null);
|
||||
const [previewLoading, setPreviewLoading] = useState(false);
|
||||
const [loading, setLoading] = useState(false);
|
||||
@@ -50,7 +52,7 @@ export default function OrderConfirmPickupPage() {
|
||||
setPreviewLoading(true);
|
||||
request<OrderPreview>('/trade/orders/preview', {
|
||||
method: 'POST',
|
||||
data: { productId, quantity, onSitePickup: true },
|
||||
data: { productId, quantity, onSitePickup: true, ...(skuId ? { skuId } : {}) },
|
||||
})
|
||||
.then((data) => {
|
||||
if (!cancelled) {
|
||||
@@ -70,15 +72,16 @@ export default function OrderConfirmPickupPage() {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [productId, quantity]);
|
||||
}, [productId, skuId, quantity]);
|
||||
|
||||
const unitLabel = preview?.saleUnit === 'BOX' ? '箱' : '瓶';
|
||||
const minQty = preview?.minQty ?? 2;
|
||||
const quantityOk = preview ? preview.quantityOk !== false && quantity >= minQty : false;
|
||||
const canSubmit = !!preview && quantityOk && !loading && !previewLoading;
|
||||
|
||||
function updateQuantity(next: number) {
|
||||
if (next < minQty) {
|
||||
const tip = `现场提货至少购买 ${minQty} 瓶`;
|
||||
const tip = `现场提货至少购买 ${minQty}${unitLabel}`;
|
||||
toast(tip);
|
||||
setMsg(tip);
|
||||
if (next < 1) return;
|
||||
@@ -91,12 +94,13 @@ export default function OrderConfirmPickupPage() {
|
||||
async function doSubmit() {
|
||||
const order = await request<{ id: string }>('/trade/orders', {
|
||||
method: 'POST',
|
||||
data: { productId, quantity, onSitePickup: true },
|
||||
data: { productId, quantity, onSitePickup: true, ...(skuId ? { skuId } : {}) },
|
||||
});
|
||||
Taro.redirectTo({
|
||||
url: buildPayUrl({
|
||||
orderId: order.id,
|
||||
productId,
|
||||
skuId: skuId || undefined,
|
||||
qty: String(quantity),
|
||||
}),
|
||||
});
|
||||
|
||||
@@ -54,6 +54,9 @@ type OrderPreview = {
|
||||
minQty?: number;
|
||||
allowCrossCityDelivery?: boolean;
|
||||
allowOnlinePurchase?: boolean;
|
||||
skuId?: string;
|
||||
saleUnit?: 'BOTTLE' | 'BOX';
|
||||
bottlesPerUnit?: number;
|
||||
};
|
||||
|
||||
const CROSS_CITY_BLOCK_MSG = '该商品不支持跨城配送,请更换为开城城市内的收货地址';
|
||||
@@ -66,6 +69,7 @@ export default function OrderConfirmPage() {
|
||||
const router = useRouter();
|
||||
const checkoutCtx = readCheckoutContext(router.params);
|
||||
const productId = checkoutCtx.productId ?? '';
|
||||
const skuId = checkoutCtx.skuId ?? '';
|
||||
const forceCross = checkoutCtx.cross === true;
|
||||
const [quantity, setQuantity] = useState(Math.max(1, Number(checkoutCtx.qty || 2)));
|
||||
const [addresses, setAddresses] = useState<Address[]>([]);
|
||||
@@ -96,11 +100,12 @@ export default function OrderConfirmPage() {
|
||||
if (!productId) return;
|
||||
let cancelled = false;
|
||||
setPreviewLoading(true);
|
||||
const body: { productId: string; quantity: number; addressId?: string } = {
|
||||
const body: { productId: string; quantity: number; addressId?: string; skuId?: string } = {
|
||||
productId,
|
||||
quantity,
|
||||
};
|
||||
if (addressId) body.addressId = addressId;
|
||||
if (skuId) body.skuId = skuId;
|
||||
|
||||
request<OrderPreview>('/trade/orders/preview', { method: 'POST', data: body })
|
||||
.then((data) => {
|
||||
@@ -139,7 +144,7 @@ export default function OrderConfirmPage() {
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [productId, quantity, addressId]);
|
||||
}, [productId, skuId, quantity, addressId]);
|
||||
|
||||
const selectedAddress = useMemo(
|
||||
() => addresses.find((a) => String(a.id) === addressId),
|
||||
@@ -157,6 +162,7 @@ export default function OrderConfirmPage() {
|
||||
forceCross || preview?.deliveryType === 'CROSS_CITY' || localCross;
|
||||
const crossBlocked = isCross && !allowCross;
|
||||
const addressOk = preview ? preview.addressOk !== false && !crossBlocked : !crossBlocked;
|
||||
const unitLabel = preview?.saleUnit === 'BOX' ? '箱' : '瓶';
|
||||
const minQty =
|
||||
preview?.minQty ??
|
||||
(isCross ? (preview?.city?.crossMinQty ?? 6) : (preview?.city?.localMinQty ?? 2));
|
||||
@@ -171,8 +177,8 @@ export default function OrderConfirmPage() {
|
||||
function updateQuantity(next: number) {
|
||||
if (next < minQty) {
|
||||
const tip = isCross
|
||||
? `跨城配送至少购买 ${minQty} 瓶(1箱)`
|
||||
: `同城配送至少购买 ${minQty} 瓶`;
|
||||
? `跨城配送至少购买 ${minQty}${unitLabel}`
|
||||
: `同城配送至少购买 ${minQty}${unitLabel}`;
|
||||
toast(tip);
|
||||
setMsg(tip);
|
||||
if (next < 1) return;
|
||||
@@ -195,6 +201,7 @@ export default function OrderConfirmPage() {
|
||||
productId,
|
||||
quantity,
|
||||
addressId,
|
||||
...(skuId ? { skuId } : {}),
|
||||
...(clientLocation ? { clientLocation } : {}),
|
||||
},
|
||||
});
|
||||
@@ -202,6 +209,7 @@ export default function OrderConfirmPage() {
|
||||
url: buildPayUrl({
|
||||
orderId: order.id,
|
||||
productId,
|
||||
skuId: skuId || undefined,
|
||||
qty: String(quantity),
|
||||
addressId,
|
||||
cross: forceCross,
|
||||
@@ -223,15 +231,15 @@ export default function OrderConfirmPage() {
|
||||
}
|
||||
if (!quantityOk) {
|
||||
const tip = isCross
|
||||
? `跨城配送至少购买 ${minQty} 瓶(1箱)`
|
||||
: `同城配送至少购买 ${minQty} 瓶`;
|
||||
? `跨城配送至少购买 ${minQty}${unitLabel}`
|
||||
: `同城配送至少购买 ${minQty}${unitLabel}`;
|
||||
setMsg(tip);
|
||||
toast(tip);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const returnPath = `/pages/order-confirm/index?productId=${productId}&qty=${quantity}&addressId=${addressId}${forceCross ? '&cross=1' : ''}`;
|
||||
const returnPath = `/pages/order-confirm/index?productId=${productId}&qty=${quantity}&addressId=${addressId}${skuId ? `&skuId=${skuId}` : ''}${forceCross ? '&cross=1' : ''}`;
|
||||
|
||||
if (!phonePromptSkipped.current) {
|
||||
try {
|
||||
@@ -371,8 +379,8 @@ export default function OrderConfirmPage() {
|
||||
{!quantityOk ? (
|
||||
<Text className="order-qty-hint">
|
||||
{isCross
|
||||
? `跨城配送至少购买 ${minQty} 瓶(1箱),请调整数量`
|
||||
: `同城配送至少购买 ${minQty} 瓶,请调整数量`}
|
||||
? `跨城配送至少购买 ${minQty}${unitLabel},请调整数量`
|
||||
: `同城配送至少购买 ${minQty}${unitLabel},请调整数量`}
|
||||
</Text>
|
||||
) : null}
|
||||
</View>
|
||||
|
||||
@@ -8,7 +8,7 @@ import Taro, {
|
||||
useShareAppMessage,
|
||||
useShareTimeline,
|
||||
} from '@tarojs/taro';
|
||||
import type { ProductDetailContentDto } from '@dukang/shared-types';
|
||||
import type { ProductDetailContentDto, ProductSkuDto, ProductSpecAttrDto } from '@dukang/shared-types';
|
||||
import PageShell from '../../components/PageShell';
|
||||
import PageNavBar from '../../components/PageNavBar';
|
||||
import ProductCarousel from '../../components/ProductCarousel';
|
||||
@@ -47,8 +47,39 @@ type Product = ProductImageSource & {
|
||||
allowOnSitePickup?: boolean;
|
||||
allowOnlinePurchase?: boolean;
|
||||
allowCrossCityDelivery?: boolean;
|
||||
specEnabled?: boolean;
|
||||
specAttrs?: ProductSpecAttrDto[];
|
||||
skus?: ProductSkuDto[];
|
||||
defaultSkuId?: string;
|
||||
saleUnit?: 'BOTTLE' | 'BOX';
|
||||
};
|
||||
|
||||
function findSku(
|
||||
skus: ProductSkuDto[],
|
||||
selected: Record<string, string>,
|
||||
attrs: ProductSpecAttrDto[],
|
||||
): ProductSkuDto | undefined {
|
||||
const valueIds = attrs.map((a) => selected[a.id]).filter(Boolean);
|
||||
if (valueIds.length !== attrs.length) return undefined;
|
||||
const key = [...valueIds].sort().join('_');
|
||||
return skus.find((s) => [...s.specValueIds].sort().join('_') === key);
|
||||
}
|
||||
|
||||
function isValueAvailable(
|
||||
skus: ProductSkuDto[],
|
||||
attrs: ProductSpecAttrDto[],
|
||||
selected: Record<string, string>,
|
||||
attrId: string,
|
||||
valueId: string,
|
||||
): boolean {
|
||||
const trial = { ...selected, [attrId]: valueId };
|
||||
const partialIds = attrs.map((a) => trial[a.id]).filter(Boolean);
|
||||
return skus.some((s) => {
|
||||
if (s.status !== 'ON_SALE') return false;
|
||||
return partialIds.every((id) => s.specValueIds.includes(id));
|
||||
});
|
||||
}
|
||||
|
||||
export default function ProductDetailPage() {
|
||||
const router = useRouter();
|
||||
const productId = router.params.id ?? '';
|
||||
@@ -58,6 +89,7 @@ export default function ProductDetailPage() {
|
||||
);
|
||||
const [product, setProduct] = useState<Product | null>(null);
|
||||
const [headerSolid, setHeaderSolid] = useState(false);
|
||||
const [selected, setSelected] = useState<Record<string, string>>({});
|
||||
|
||||
usePageScroll(({ scrollTop }) => {
|
||||
setHeaderSolid(scrollTop > 100);
|
||||
@@ -72,7 +104,25 @@ export default function ProductDetailPage() {
|
||||
toast('商品不存在或暂未开放');
|
||||
return;
|
||||
}
|
||||
setProduct(normalizeFulfillmentFlags(p));
|
||||
const normalized = normalizeFulfillmentFlags(p);
|
||||
setProduct(normalized);
|
||||
const attrs = p.specAttrs ?? [];
|
||||
const skus = p.skus ?? [];
|
||||
const def =
|
||||
skus.find((s) => s.id === p.defaultSkuId) ||
|
||||
skus.find((s) => s.isDefault && s.status === 'ON_SALE') ||
|
||||
skus.find((s) => s.status === 'ON_SALE') ||
|
||||
skus[0];
|
||||
if (def && attrs.length) {
|
||||
const next: Record<string, string> = {};
|
||||
for (const attr of attrs) {
|
||||
const hit = attr.values.find((v) => def.specValueIds.includes(v.id));
|
||||
if (hit) next[attr.id] = hit.id;
|
||||
}
|
||||
setSelected(next);
|
||||
} else {
|
||||
setSelected({});
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
setProduct(null);
|
||||
@@ -84,11 +134,38 @@ export default function ProductDetailPage() {
|
||||
loadProduct();
|
||||
}, [loadProduct]);
|
||||
|
||||
// 登录后返回详情须带 token 重拉,否则白名单商品会一直空白
|
||||
useDidShow(() => {
|
||||
loadProduct();
|
||||
});
|
||||
|
||||
const attrs = product?.specAttrs ?? [];
|
||||
const skus = product?.skus ?? [];
|
||||
const specEnabled = !!(product?.specEnabled && attrs.length > 0);
|
||||
const activeSku = useMemo(() => {
|
||||
if (!product) return undefined;
|
||||
if (!specEnabled) {
|
||||
return (
|
||||
skus.find((s) => s.id === product.defaultSkuId) ||
|
||||
skus.find((s) => s.isDefault) ||
|
||||
skus.find((s) => s.status === 'ON_SALE') ||
|
||||
skus[0]
|
||||
);
|
||||
}
|
||||
return findSku(skus, selected, attrs);
|
||||
}, [product, specEnabled, skus, selected, attrs]);
|
||||
|
||||
const displayPrice = activeSku ? Number(activeSku.price) : Number(product?.price ?? 0);
|
||||
const displayBenefit = activeSku
|
||||
? Number(activeSku.benefitAmount)
|
||||
: Number(product?.benefitDisplay ?? product?.benefitAmount ?? product?.price ?? 0);
|
||||
const fulfillment = activeSku
|
||||
? {
|
||||
allowOnlinePurchase: activeSku.allowOnlinePurchase,
|
||||
allowCrossCityDelivery: activeSku.allowCrossCityDelivery,
|
||||
allowOnSitePickup: activeSku.allowOnSitePickup,
|
||||
}
|
||||
: product;
|
||||
|
||||
const sharePayload = useMemo(
|
||||
() =>
|
||||
buildSceneSharePayload('productDetail', {
|
||||
@@ -118,9 +195,23 @@ export default function ProductDetailPage() {
|
||||
Taro.switchTab({ url: '/pages/home/index' });
|
||||
}
|
||||
|
||||
function ensureSkuSelected(): string | null {
|
||||
if (!specEnabled) return activeSku?.id ?? product?.defaultSkuId ?? null;
|
||||
if (!activeSku || activeSku.status !== 'ON_SALE') {
|
||||
toast('请选择完整规格');
|
||||
return null;
|
||||
}
|
||||
return activeSku.id;
|
||||
}
|
||||
|
||||
async function goBuy() {
|
||||
if (!productId) return;
|
||||
const returnPath = `/pages/order-confirm/index?productId=${productId}&qty=2`;
|
||||
const skuId = ensureSkuSelected();
|
||||
if (specEnabled && !skuId) return;
|
||||
const qty = activeSku?.saleUnit === 'BOX' ? 1 : 2;
|
||||
const qs = [`productId=${productId}`, `qty=${qty}`];
|
||||
if (skuId) qs.push(`skuId=${skuId}`);
|
||||
const returnPath = `/pages/order-confirm/index?${qs.join('&')}`;
|
||||
if (!isLoggedIn()) {
|
||||
goLogin(returnPath);
|
||||
return;
|
||||
@@ -132,7 +223,12 @@ export default function ProductDetailPage() {
|
||||
|
||||
async function goOnSitePickup() {
|
||||
if (!productId) return;
|
||||
const returnPath = `/pages/order-confirm-pickup/index?productId=${productId}&qty=1`;
|
||||
const skuId = ensureSkuSelected();
|
||||
if (specEnabled && !skuId) return;
|
||||
const qty = activeSku?.saleUnit === 'BOX' ? 1 : 1;
|
||||
const qs = [`productId=${productId}`, `qty=${qty}`];
|
||||
if (skuId) qs.push(`skuId=${skuId}`);
|
||||
const returnPath = `/pages/order-confirm-pickup/index?${qs.join('&')}`;
|
||||
if (!isLoggedIn()) {
|
||||
goLogin(returnPath);
|
||||
return;
|
||||
@@ -151,9 +247,8 @@ export default function ProductDetailPage() {
|
||||
);
|
||||
}
|
||||
|
||||
const allowOnline = canBuyOnline(product);
|
||||
const allowOnSite = canPickupOnSite(product);
|
||||
const benefit = Number(product.benefitDisplay ?? product.benefitAmount ?? product.price);
|
||||
const allowOnline = canBuyOnline(fulfillment ?? {});
|
||||
const allowOnSite = canPickupOnSite(fulfillment ?? {});
|
||||
const carouselImages = getProductCarouselImages(product);
|
||||
const detailImages = getProductDetailImages(product);
|
||||
const detail = product.detailContent ?? {};
|
||||
@@ -177,12 +272,45 @@ export default function ProductDetailPage() {
|
||||
<View className="product-detail-info">
|
||||
<View className="product-detail-price">
|
||||
<Text className="product-detail-price-symbol">¥</Text>
|
||||
<Text className="product-detail-price-value">{Number(product.price).toFixed(2)}</Text>
|
||||
<Text className="product-detail-price-value">{displayPrice.toFixed(2)}</Text>
|
||||
</View>
|
||||
<Text className="product-detail-name">{product.name}</Text>
|
||||
{product.subtitle ? (
|
||||
<Text className="product-detail-subtitle">{product.subtitle}</Text>
|
||||
) : null}
|
||||
{activeSku?.specText ? (
|
||||
<Text className="product-detail-subtitle">{activeSku.specText}</Text>
|
||||
) : null}
|
||||
|
||||
{specEnabled ? (
|
||||
<View className="product-detail-specs">
|
||||
{attrs.map((attr) => (
|
||||
<View key={attr.id} className="product-detail-spec-row">
|
||||
<Text className="product-detail-spec-label">{attr.name}</Text>
|
||||
<View className="product-detail-spec-chips">
|
||||
{attr.values.map((val) => {
|
||||
const active = selected[attr.id] === val.id;
|
||||
const available = isValueAvailable(skus, attrs, selected, attr.id, val.id);
|
||||
return (
|
||||
<View
|
||||
key={val.id}
|
||||
className={`product-detail-spec-chip${active ? ' is-active' : ''}${
|
||||
available ? '' : ' is-disabled'
|
||||
}`}
|
||||
onClick={() => {
|
||||
if (!available) return;
|
||||
setSelected((prev) => ({ ...prev, [attr.id]: val.id }));
|
||||
}}
|
||||
>
|
||||
<Text className="product-detail-spec-chip-text">{val.name}</Text>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
<View className="product-detail-promo">
|
||||
<View className="product-detail-promo-glow" />
|
||||
@@ -192,7 +320,7 @@ export default function ProductDetailPage() {
|
||||
</View>
|
||||
<View className="product-detail-promo-title">
|
||||
<Text>买杜康美酒 · 享全城好客礼遇</Text>
|
||||
<BenefitFigure value={String(benefit)} size="sm" className="product-detail-promo-amount" />
|
||||
<BenefitFigure value={String(displayBenefit)} size="sm" className="product-detail-promo-amount" />
|
||||
</View>
|
||||
</View>
|
||||
<Text className="product-detail-promo-desc">
|
||||
|
||||
@@ -453,7 +453,7 @@ export default function StoreDetailPage() {
|
||||
|
||||
{benefitRule ? (
|
||||
<View className="store-detail-section">
|
||||
<Text className="store-detail-section-title">好客权益券使用规则</Text>
|
||||
<Text className="store-detail-section-title">好客权益使用规则</Text>
|
||||
<Text className="store-detail-intro">{benefitRule}</Text>
|
||||
</View>
|
||||
) : null}
|
||||
|
||||
@@ -375,3 +375,53 @@
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.product-detail-specs {
|
||||
margin: 12px 0 4px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.product-detail-spec-row {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.product-detail-spec-label {
|
||||
font-size: 13px;
|
||||
color: var(--color-on-surface-variant, #666);
|
||||
}
|
||||
|
||||
.product-detail-spec-chips {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.product-detail-spec-chip {
|
||||
padding: 6px 14px;
|
||||
border-radius: 8px;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.product-detail-spec-chip.is-active {
|
||||
background: rgba(166, 29, 36, 0.08);
|
||||
border-color: var(--color-heritage-red);
|
||||
}
|
||||
|
||||
.product-detail-spec-chip.is-disabled {
|
||||
opacity: 0.35;
|
||||
}
|
||||
|
||||
.product-detail-spec-chip-text {
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.product-detail-spec-chip.is-active .product-detail-spec-chip-text {
|
||||
color: var(--color-heritage-red);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user