From 5ce0d76154bf4c1d70543476cdfad9a30195b0f8 Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期六, 10 十月 2026 16:44:38 +0800
Subject: [PATCH] fix(miniapp): 调整首页圆角布局并隐藏滚动条
---
rencaika/pages/product-detail/product-detail.uvue | 92 +++++++++++++++++++++------------------------
1 files changed, 43 insertions(+), 49 deletions(-)
diff --git a/rencaika/pages/product-detail/product-detail.uvue b/rencaika/pages/product-detail/product-detail.uvue
index bda4c40..7ccf641 100644
--- a/rencaika/pages/product-detail/product-detail.uvue
+++ b/rencaika/pages/product-detail/product-detail.uvue
@@ -10,23 +10,21 @@
</view>
</view>
- <scroll-view class="content" scroll-y @scroll="handleScroll">
+ <scroll-view :show-scrollbar="false" class="content" scroll-y @scroll="handleScroll">
<view class="hero">
<image class="hero-image" :src="productImage" mode="aspectFill" @error="replaceProductImage"></image>
<view class="hero-shade"></view>
</view>
-
<view class="product-summary">
- <view class="price-line"><text class="discount-label">折扣价</text><text class="price-symbol">¥</text><text class="price">{{productPrice}}</text><text class="old-price">¥{{productOriginalPrice}}</text><view class="summary-tags"><text class="coupon-tag">券</text><text class="discount-tag">{{productDiscount}}</text></view></view>
+ <view class="price-line"><text class="discount-label">折扣价</text><text class="price-symbol">¥</text><text class="price">{{productPrice}}</text><text class="old-price">¥{{productOriginalPrice}}</text><view class="summary-tags"><text v-if="productHasCoupon" class="coupon-tag">券</text><text v-if="productDiscount != ''" class="discount-tag">{{productDiscount}}</text></view></view>
<text class="product-name">{{productName}}</text>
<text v-if="!productAvailable" class="product-status">商品已下架</text>
<text v-else-if="productSoldOut" class="product-status">已售罄</text>
</view>
- <view v-if="detailImages.length > 0 || detailText.length > 0" class="detail-card">
+ <view v-if="detailText.length > 0" class="detail-card">
<text class="card-title">商品详情</text>
- <view v-if="detailImages.length > 0" class="detail-images"><image v-for="(imageUrl, index) in detailImages" :key="index" :src="imageUrl" mode="widthFix" @error="replaceDetailImage(index)"></image></view>
- <text v-if="detailText.length > 0" class="detail-description">{{detailText}}</text>
+ <rich-text class="detail-description" :nodes="detailText"></rich-text>
</view>
<view class="notice-card">
@@ -49,7 +47,9 @@
<script setup lang="uts">
import { isApiConfigured } from '../../config/api.uts'
import { getProductDetail } from '../../services/portal.uts'
+ import { sanitizeRichText } from '../../services/business.uts'
import { resolveApiUrl } from '../../services/http.uts'
+ import { getSession } from '../../services/session.uts'
import { safeBack } from '../../utils/navigation.uts'
const navTop = ref(28)
@@ -59,18 +59,19 @@
const isSolidNav = ref(false)
const merchantId = ref('')
const productId = ref('')
+ const talentId = ref('')
const productName = ref('【川味盛宴】川菜12人餐')
- const productImage = ref('/static/food-assets/food-1.jpg')
- const fallbackImage = '/static/common/image-fallback.svg'
- const replaceProductImage = () => { productImage.value = fallbackImage }
+ const productImage = ref('')
+ const fallbackImage = ''
+ const replaceProductImage = () => { productImage.value = '' }
const productPrice = ref('138')
const productOriginalPrice = ref('266')
const productDiscount = ref('6折')
+ const productHasCoupon = ref(false)
const productAvailable = ref(true)
const productSoldOut = ref(false)
- // 后台分别返回图片数组与文字结构,不使用富文本 HTML。
- const detailImages = ref<any[]>([])
+ // Sanitize and render backend-configured product details as rich text.
const detailText = ref('')
// 购买须知固定只有四项,后台只返回四项各自的内容文本。
@@ -83,13 +84,13 @@
const purchaseNotices = ref<any[]>(defaultPurchaseNotices)
const loadProduct = (id: string) => {
- if (id == 'food-set-002') { productName.value = '【川香四溢】川菜4人餐'; productImage.value = '/static/food-assets/food-2.jpg'; productPrice.value = '118'; productOriginalPrice.value = '198'; productDiscount.value = '6折'; return }
- if (id == 'food-set-003') { productName.value = '【欢聚时光】招牌菜6人餐'; productImage.value = '/static/food-assets/food-1.jpg'; productPrice.value = '168'; productOriginalPrice.value = '248'; productDiscount.value = '6.8折'; return }
- if (id == 'food-set-004') { productName.value = '【暖心小聚】特色菜双人餐'; productImage.value = '/static/food-assets/food-2.jpg'; productPrice.value = '88'; productOriginalPrice.value = '126'; productDiscount.value = '7折'; return }
- if (id == 'food-set-005') { productName.value = '【家庭聚餐】经典川菜8人餐'; productImage.value = '/static/food-assets/food-1.jpg'; productPrice.value = '238'; productOriginalPrice.value = '366'; productDiscount.value = '6.5折'; return }
- if (id == 'food-set-006') { productName.value = '【人气必点】招牌菜4人餐'; productImage.value = '/static/food-assets/food-2.jpg'; productPrice.value = '128'; productOriginalPrice.value = '188'; productDiscount.value = '6.8折'; return }
- if (id == 'food-set-007') { productName.value = '【周末欢聚】特色菜10人餐'; productImage.value = '/static/food-assets/food-1.jpg'; productPrice.value = '298'; productOriginalPrice.value = '498'; productDiscount.value = '6折'; return }
- if (id == 'food-set-008') { productName.value = '【一人食】暖心工作餐'; productImage.value = '/static/food-assets/food-2.jpg'; productPrice.value = '39'; productOriginalPrice.value = '49'; productDiscount.value = '8折'; return }
+ if (id == 'food-set-002') { productName.value = '【川香四溢】川菜4人餐'; productImage.value = '/static/common/image-placeholder.png'; productPrice.value = '118'; productOriginalPrice.value = '198'; productDiscount.value = '6折'; return }
+ if (id == 'food-set-003') { productName.value = '【欢聚时光】招牌菜6人餐'; productImage.value = '/static/common/image-placeholder.png'; productPrice.value = '168'; productOriginalPrice.value = '248'; productDiscount.value = '6.8折'; return }
+ if (id == 'food-set-004') { productName.value = '【暖心小聚】特色菜双人餐'; productImage.value = '/static/common/image-placeholder.png'; productPrice.value = '88'; productOriginalPrice.value = '126'; productDiscount.value = '7折'; return }
+ if (id == 'food-set-005') { productName.value = '【家庭聚餐】经典川菜8人餐'; productImage.value = '/static/common/image-placeholder.png'; productPrice.value = '238'; productOriginalPrice.value = '366'; productDiscount.value = '6.5折'; return }
+ if (id == 'food-set-006') { productName.value = '【人气必点】招牌菜4人餐'; productImage.value = '/static/common/image-placeholder.png'; productPrice.value = '128'; productOriginalPrice.value = '188'; productDiscount.value = '6.8折'; return }
+ if (id == 'food-set-007') { productName.value = '【周末欢聚】特色菜10人餐'; productImage.value = '/static/common/image-placeholder.png'; productPrice.value = '298'; productOriginalPrice.value = '498'; productDiscount.value = '6折'; return }
+ if (id == 'food-set-008') { productName.value = '【一人食】暖心工作餐'; productImage.value = '/static/common/image-placeholder.png'; productPrice.value = '39'; productOriginalPrice.value = '49'; productDiscount.value = '8折'; return }
}
onLoad((options: any) => {
@@ -103,6 +104,8 @@
// #endif
if (options != null && options['merchantId'] != null) merchantId.value = options['merchantId'] as string
if (options != null && options['productId'] != null) productId.value = options['productId'] as string
+ const session = getSession()
+ if (session != null && session.talentId != null) talentId.value = '' + session.talentId
loadProduct(productId.value)
if (options != null && options['name'] != null) productName.value = decodeURIComponent(options['name'] as string)
if (options != null && options['image'] != null) productImage.value = decodeURIComponent(options['image'] as string)
@@ -113,18 +116,22 @@
})
const loadRemoteProduct = () => {
if (!isApiConfigured() || !/^\d+$/.test(productId.value)) return
- getProductDetail(productId.value, (data: any) => {
+ getProductDetail(productId.value, talentId.value, (data: any) => {
productName.value = data != null && data.productName != null ? data.productName as string : productName.value
productImage.value = data != null && data.coverUrl != null && data.coverUrl != '' ? resolveApiUrl(data.coverUrl as string) : productImage.value
- const sale = data != null && data.salePrice != null ? Number(data.salePrice) : 0
- const original = data != null && data.originalPrice != null ? Number(data.originalPrice) : 0
- if (sale > 0) productPrice.value = '' + sale
- if (original > 0) productOriginalPrice.value = '' + original
- if (sale > 0 && original > sale) productDiscount.value = '' + Math.round(sale / original * 100) / 10 + '折'
+ const hasSale = data != null && data.salePrice != null
+ const hasOriginal = data != null && data.originalPrice != null
+ const sale = hasSale ? Number(data.salePrice) : 0
+ const original = hasOriginal ? Number(data.originalPrice) : 0
+ if (hasSale && !isNaN(sale)) productPrice.value = sale.toFixed(2).replace(/\.00$/, '')
+ if (hasOriginal && !isNaN(original) && original > 0) productOriginalPrice.value = original.toFixed(2).replace(/\.00$/, '')
+ productHasCoupon.value = data != null && (data.couponAvailable === true || Number(data.couponAvailable) == 1)
+ if (data != null && data.discountText != null) productDiscount.value = data.discountText as string
+ else if (original > 0 && sale < original) productDiscount.value = '' + Math.round(sale / original * 100) / 10 + '折'
productAvailable.value = data == null || data.available !== false
productSoldOut.value = data != null && (data.soldOut === true || Number(data.stock) == 0)
- detailText.value = data != null && data.detailText != null ? data.detailText as string : ''
- detailImages.value = getProductImages(data != null ? data.productImages : null)
+ const rawDetailText = data != null && data.detailText != null ? data.detailText as string : ''
+ detailText.value = sanitizeRichText(rawDetailText)
purchaseNotices.value = buildPurchaseNotices(data != null ? data.purchaseNotice : null)
}, () => {})
}
@@ -140,31 +147,19 @@
const next: any[] = []
for (let index = 0; index < defaultPurchaseNotices.length; index++) {
const item = defaultPurchaseNotices[index]
- const content = notice != null && notice[item.type] != null && notice[item.type] != '' ? '' + notice[item.type] : '以商户说明为准'
+ let configured: any = null
+ if (notice != null) {
+ // The admin group-buy form stores these three fields under its own
+ // form names. Keep the old keys as a fallback for existing products.
+ if (item.type == 'validity') configured = notice.validPeriod != null ? notice.validPeriod : notice.validity
+ else if (item.type == 'usage') configured = notice.useMethod != null ? notice.useMethod : notice.usage
+ else if (item.type == 'otherRules') configured = notice.otherRule != null ? notice.otherRule : notice.otherRules
+ else configured = notice.availableTime
+ }
+ const content = configured != null && ('' + configured).trim() != '' ? '' + configured : '以商户说明为准'
next.push({ type: item.type, title: item.title, icon: item.icon, content: content })
}
return next
- }
-
- const getProductImages = (value: any): string[] => {
- let images: any = value
- if (typeof images == 'string') {
- try {
- images = JSON.parse(images)
- } catch (_) {
- return []
- }
- }
- if (!(images instanceof Array)) return []
- const result: string[] = []
- for (let index = 0; index < images.length; index++) {
- const image = images[index]
- if (typeof image == 'string' && image.length > 0) result.push(resolveApiUrl(image))
- }
- return result
- }
- const replaceDetailImage = (index: number) => {
- if (index >= 0 && index < detailImages.value.length) detailImages.value[index] = fallbackImage
}
const goBack = () => safeBack('/pages/benefits/benefits')
@@ -226,7 +221,6 @@
.product-status { display: block; margin-top: 11rpx; color: #E45D3C; font-size: 24rpx; }
.detail-card, .notice-card { margin: 16rpx 25rpx 0; padding: 27rpx 27rpx 30rpx; border-radius: 15rpx; background-color: #FFFFFF; }
.card-title { display: block; margin-bottom: 23rpx; color: #55575B; font-size: 30rpx; font-weight: 700; }
- .detail-images image { display: block; width: 100%; margin-bottom: 24rpx; border-radius: 12rpx; }
.detail-description { display: block; color: #626870; font-size: 25rpx; line-height: 43rpx; white-space: pre-wrap; }
.notice-item { display: flex; min-height: 105rpx; padding: 19rpx 0; border-bottom-width: 1rpx; border-bottom-style: solid; border-bottom-color: #EEEEEE; flex-direction: row; align-items: flex-start; }
.notice-item:last-child { border-bottom-width: 0; }
--
Gitblit v1.7.1