完善居家服务商品详情页
This commit is contained in:
@@ -72,9 +72,118 @@
|
||||
@select-product="navigateToProduct"
|
||||
></HomeMallContent>
|
||||
|
||||
<HomeServiceContent
|
||||
v-else
|
||||
></HomeServiceContent>
|
||||
<view v-else class="service-home-section">
|
||||
<view class="service-hero-banner">
|
||||
<view class="service-hero-content">
|
||||
<text class="service-hero-tag">康养到家</text>
|
||||
<text class="service-hero-title">专业康养服务到家</text>
|
||||
<text class="service-hero-subtitle">护理照护|康复指导|陪诊陪护</text>
|
||||
<view class="service-hero-tags">
|
||||
<text class="service-hero-chip">平台认证</text>
|
||||
<text class="service-hero-chip">上门服务</text>
|
||||
<text class="service-hero-chip">服务可追溯</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="service-hero-visual-wrap">
|
||||
<view class="service-hero-visual service-hero-visual-primary">
|
||||
<text class="service-hero-visual-text">护</text>
|
||||
</view>
|
||||
<view class="service-hero-visual service-hero-visual-secondary">
|
||||
<text class="service-hero-visual-subtext">康</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="service-category-card">
|
||||
<view
|
||||
v-for="(item, index) in serviceCategories"
|
||||
:key="buildListItemKey('service-category', item.id, index)"
|
||||
class="service-category-item"
|
||||
@tap="handleServiceCategoryClick(item)"
|
||||
>
|
||||
<view
|
||||
:class="['service-category-icon', selectedServiceCategory == item.id ? 'service-category-icon-active' : '']"
|
||||
:style="{ backgroundColor: item.color }"
|
||||
>
|
||||
<text class="service-category-icon-text">{{ item.iconText }}</text>
|
||||
</view>
|
||||
<text :class="['service-category-name', selectedServiceCategory == item.id ? 'service-category-name-active' : '']">{{ item.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="service-shortcut-row">
|
||||
<view
|
||||
v-for="(item, index) in serviceShortcuts"
|
||||
:key="buildListItemKey('service-shortcut', item.id, index)"
|
||||
class="service-shortcut-card"
|
||||
@tap="handleServiceShortcut(item.id)"
|
||||
>
|
||||
<text class="service-shortcut-icon">{{ item.iconText }}</text>
|
||||
<view class="service-shortcut-body">
|
||||
<text class="service-shortcut-title">{{ item.title }}</text>
|
||||
<text class="service-shortcut-desc">{{ item.desc }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="service-products-section">
|
||||
<view class="service-section-title-row">
|
||||
<view>
|
||||
<text class="service-section-title">推荐服务</text>
|
||||
<text class="service-section-subtitle">{{ serviceSelectedCategoryLabel }}</text>
|
||||
</view>
|
||||
<text class="service-section-more" @tap="goServiceHall">更多</text>
|
||||
</view>
|
||||
|
||||
<view v-if="serviceLoading" class="service-state-card">
|
||||
<text class="service-state-title">正在加载服务...</text>
|
||||
<text class="service-state-desc">请稍候,正在整理适合居家康养的服务内容。</text>
|
||||
</view>
|
||||
|
||||
<view v-else-if="serviceProducts.length > 0" class="service-products-grid">
|
||||
<view
|
||||
v-for="(item, index) in serviceProducts"
|
||||
:key="buildListItemKey('service-product', item.id, index)"
|
||||
class="service-product-card"
|
||||
@tap="goServiceDetail(item)"
|
||||
>
|
||||
<view class="service-product-cover" :style="{ background: item.coverGradient }">
|
||||
<text class="service-product-cover-badge">到家服务</text>
|
||||
<text class="service-product-cover-text">{{ item.imageText }}</text>
|
||||
</view>
|
||||
<view class="service-product-body">
|
||||
<text class="service-product-title">{{ item.title }}</text>
|
||||
<text class="service-product-subtitle">{{ item.subtitle }}</text>
|
||||
<view class="service-product-tags">
|
||||
<text
|
||||
v-for="(tag, tagIndex) in item.tags"
|
||||
:key="buildListItemKey(item.id + '-tag', tag, tagIndex)"
|
||||
class="service-product-tag"
|
||||
>
|
||||
{{ tag }}
|
||||
</text>
|
||||
</view>
|
||||
<view class="service-product-price-row">
|
||||
<text class="service-product-price-symbol">¥</text>
|
||||
<text class="service-product-price">{{ item.price }}</text>
|
||||
<text class="service-product-unit">起 / {{ item.unit }}</text>
|
||||
</view>
|
||||
<text class="service-product-sales">{{ item.salesText }}</text>
|
||||
<view class="service-product-action-row">
|
||||
<view class="service-product-secondary-btn" @tap.stop="showServicePreview(item)">查看详情</view>
|
||||
<view class="service-product-primary-btn" @tap.stop="goServiceDetail(item)">立即预约</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-else class="service-state-card">
|
||||
<text class="service-state-title">该分类服务正在完善</text>
|
||||
<text class="service-state-desc">可先查看全部服务,或进入服务大厅了解更多上门服务。</text>
|
||||
<view class="service-state-action" @tap="goServiceHall">进入服务大厅</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="safe-area" :style="{ height: bottomSafeArea + 88 + 'px' }"></view>
|
||||
</scroll-view>
|
||||
@@ -115,8 +224,9 @@
|
||||
import { ref, reactive, onMounted, onUnmounted, computed } from 'vue'
|
||||
import { onShow, onLoad, onHide } from '@dcloudio/uni-app'
|
||||
import HomeMallContent from '@/components/home/HomeMallContent.uvue'
|
||||
import HomeServiceContent from '@/components/home/HomeServiceContent.uvue'
|
||||
import JdLikeHomeHeader from '@/components/home/JdLikeHomeHeader.uvue'
|
||||
import { fetchHomeServiceCatalog } from '@/services/homeServiceService.uts'
|
||||
import type { HomeServiceCatalogType } from '@/types/home-service.uts'
|
||||
import supabaseService from '@/utils/supabaseService.uts'
|
||||
import type { Product, Category, Brand, PaginatedResponse } from '@/utils/supabaseService.uts'
|
||||
import { getRecommendMarketingChannels } from '@/utils/mockChannelData.uts'
|
||||
@@ -178,6 +288,314 @@ const headerSearchPlaceholder = computed((): string => {
|
||||
return currentPlaceholderKeyword.value != '' ? currentPlaceholderKeyword.value : '感冒药 / 康复护理 / 居家护理 / 血压计'
|
||||
})
|
||||
|
||||
type HomeCareCategoryType = {
|
||||
id: string
|
||||
name: string
|
||||
iconText: string
|
||||
color: string
|
||||
}
|
||||
|
||||
type HomeCareServiceProductType = {
|
||||
id: string
|
||||
title: string
|
||||
subtitle: string
|
||||
categoryId: string
|
||||
price: number
|
||||
unit: string
|
||||
tags: Array<string>
|
||||
salesText: string
|
||||
imageText: string
|
||||
coverGradient: string
|
||||
detailPath: string
|
||||
bookingPath: string
|
||||
}
|
||||
|
||||
type ServiceShortcutType = {
|
||||
id: string
|
||||
title: string
|
||||
desc: string
|
||||
iconText: string
|
||||
}
|
||||
|
||||
const serviceCategories: Array<HomeCareCategoryType> = [
|
||||
{ id: 'basic_care', name: '基础照护', iconText: '护', color: '#EAFBF7' },
|
||||
{ id: 'rehab', name: '康复指导', iconText: '康', color: '#EFF6FF' },
|
||||
{ id: 'escort', name: '陪诊服务', iconText: '陪', color: '#FFF7ED' },
|
||||
{ id: 'nursing', name: '上门护理', iconText: '医', color: '#F0FDFA' },
|
||||
{ id: 'chronic', name: '慢病随访', iconText: '访', color: '#F5F3FF' },
|
||||
{ id: 'assessment', name: '健康评估', iconText: '评', color: '#ECFEFF' },
|
||||
{ id: 'elderly', name: '适老改造', iconText: '老', color: '#FEF3C7' },
|
||||
{ id: 'cleaning', name: '居家保洁', iconText: '洁', color: '#F1F5F9' },
|
||||
{ id: 'medicine', name: '用药提醒', iconText: '药', color: '#FCE7F3' },
|
||||
{ id: 'all', name: '全部服务', iconText: '全', color: '#F3F4F6' }
|
||||
]
|
||||
|
||||
const serviceShortcuts: Array<ServiceShortcutType> = [
|
||||
{ id: 'guarantee', title: '服务保障', desc: '认证机构与过程留痕', iconText: '保' },
|
||||
{ id: 'hall', title: '服务大厅', desc: '查看全部居家服务', iconText: '厅' },
|
||||
{ id: 'tracking', title: '服务单跟踪', desc: '预约进度随时可查', iconText: '单' }
|
||||
]
|
||||
|
||||
const serviceLoading = ref(false)
|
||||
const selectedServiceCategory = ref('all')
|
||||
const allServiceProducts = ref<Array<HomeCareServiceProductType>>([])
|
||||
|
||||
const serviceProducts = computed((): Array<HomeCareServiceProductType> => {
|
||||
if (selectedServiceCategory.value == 'all') {
|
||||
return allServiceProducts.value
|
||||
}
|
||||
const result: Array<HomeCareServiceProductType> = []
|
||||
for (let i = 0; i < allServiceProducts.value.length; i++) {
|
||||
const item = allServiceProducts.value[i]
|
||||
if (item.categoryId == selectedServiceCategory.value) {
|
||||
result.push(item)
|
||||
}
|
||||
}
|
||||
return result
|
||||
})
|
||||
|
||||
const serviceSelectedCategoryLabel = computed((): string => {
|
||||
for (let i = 0; i < serviceCategories.length; i++) {
|
||||
if (serviceCategories[i].id == selectedServiceCategory.value) {
|
||||
return serviceCategories[i].name
|
||||
}
|
||||
}
|
||||
return '全部服务'
|
||||
})
|
||||
|
||||
function getServiceGradient(categoryId: string): string {
|
||||
if (categoryId == 'basic_care') {
|
||||
return 'linear-gradient(135deg, #e0f7f5 0%, #f0fdfb 100%)'
|
||||
}
|
||||
if (categoryId == 'rehab') {
|
||||
return 'linear-gradient(135deg, #e8f1ff 0%, #f5f9ff 100%)'
|
||||
}
|
||||
if (categoryId == 'escort') {
|
||||
return 'linear-gradient(135deg, #fff1e4 0%, #fffaf5 100%)'
|
||||
}
|
||||
if (categoryId == 'nursing') {
|
||||
return 'linear-gradient(135deg, #def7f3 0%, #eefcf9 100%)'
|
||||
}
|
||||
if (categoryId == 'chronic') {
|
||||
return 'linear-gradient(135deg, #efe9ff 0%, #f8f5ff 100%)'
|
||||
}
|
||||
if (categoryId == 'assessment') {
|
||||
return 'linear-gradient(135deg, #ebfbff 0%, #f6fdff 100%)'
|
||||
}
|
||||
return 'linear-gradient(135deg, #eef4f8 0%, #f7fafc 100%)'
|
||||
}
|
||||
|
||||
function mapServiceCatalogCategory(category: string): string {
|
||||
if (category == '日常照护') {
|
||||
return 'basic_care'
|
||||
}
|
||||
if (category == '康复支持') {
|
||||
return 'rehab'
|
||||
}
|
||||
if (category == '健康管理') {
|
||||
return 'chronic'
|
||||
}
|
||||
return 'all'
|
||||
}
|
||||
|
||||
function buildServiceSalesText(serviceId: string): string {
|
||||
if (serviceId == 'svc-001') {
|
||||
return '已服务230+'
|
||||
}
|
||||
if (serviceId == 'svc-002') {
|
||||
return '已服务180+'
|
||||
}
|
||||
if (serviceId == 'svc-003') {
|
||||
return '已服务150+'
|
||||
}
|
||||
return '已服务99+'
|
||||
}
|
||||
|
||||
function buildServiceImageText(categoryId: string): string {
|
||||
if (categoryId == 'basic_care') {
|
||||
return '护'
|
||||
}
|
||||
if (categoryId == 'rehab') {
|
||||
return '康'
|
||||
}
|
||||
if (categoryId == 'escort') {
|
||||
return '陪'
|
||||
}
|
||||
if (categoryId == 'nursing') {
|
||||
return '医'
|
||||
}
|
||||
if (categoryId == 'chronic') {
|
||||
return '访'
|
||||
}
|
||||
if (categoryId == 'assessment') {
|
||||
return '评'
|
||||
}
|
||||
return '服'
|
||||
}
|
||||
|
||||
function buildMockServiceProducts(): Array<HomeCareServiceProductType> {
|
||||
// TODO: 后续替换为服务首页专用接口,当前仅在真实服务目录为空时兜底。
|
||||
return [
|
||||
{
|
||||
id: 'svc-001',
|
||||
title: '基础上门照护',
|
||||
subtitle: '协助起居、日常陪护、健康观察',
|
||||
categoryId: 'basic_care',
|
||||
price: 99,
|
||||
unit: '次',
|
||||
tags: ['平台认证', '可预约'],
|
||||
salesText: '已服务230+',
|
||||
imageText: '护',
|
||||
coverGradient: getServiceGradient('basic_care'),
|
||||
detailPath: '/pages/mall/consumer/home-service/service-detail?id=svc-001',
|
||||
bookingPath: '/pages/mall/consumer/home-service/service-detail?id=svc-001&mode=booking'
|
||||
},
|
||||
{
|
||||
id: 'svc-002',
|
||||
title: '居家康复指导',
|
||||
subtitle: '术后恢复、动作训练、康复评估',
|
||||
categoryId: 'rehab',
|
||||
price: 129,
|
||||
unit: '次',
|
||||
tags: ['康复指导', '上门服务'],
|
||||
salesText: '已服务180+',
|
||||
imageText: '康',
|
||||
coverGradient: getServiceGradient('rehab'),
|
||||
detailPath: '/pages/mall/consumer/home-service/service-detail?id=svc-002',
|
||||
bookingPath: '/pages/mall/consumer/home-service/service-detail?id=svc-002&mode=booking'
|
||||
},
|
||||
{
|
||||
id: 'svc-mock-escort',
|
||||
title: '陪诊陪护服务',
|
||||
subtitle: '挂号陪同、检查陪同、取药协助',
|
||||
categoryId: 'escort',
|
||||
price: 168,
|
||||
unit: '次',
|
||||
tags: ['陪诊服务', '安心陪护'],
|
||||
salesText: '已服务320+',
|
||||
imageText: '陪',
|
||||
coverGradient: getServiceGradient('escort'),
|
||||
detailPath: '',
|
||||
bookingPath: ''
|
||||
},
|
||||
{
|
||||
id: 'svc-003',
|
||||
title: '慢病随访服务',
|
||||
subtitle: '血压血糖记录、健康建议、定期回访',
|
||||
categoryId: 'chronic',
|
||||
price: 79,
|
||||
unit: '次',
|
||||
tags: ['慢病管理', '健康随访'],
|
||||
salesText: '已服务150+',
|
||||
imageText: '访',
|
||||
coverGradient: getServiceGradient('chronic'),
|
||||
detailPath: '/pages/mall/consumer/home-service/service-detail?id=svc-003',
|
||||
bookingPath: '/pages/mall/consumer/home-service/service-detail?id=svc-003&mode=booking'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function buildServiceProductsFromCatalog(catalog: Array<HomeServiceCatalogType>): Array<HomeCareServiceProductType> {
|
||||
const result: Array<HomeCareServiceProductType> = []
|
||||
for (let i = 0; i < catalog.length; i++) {
|
||||
const item = catalog[i]
|
||||
const categoryId = mapServiceCatalogCategory(item.category)
|
||||
result.push({
|
||||
id: item.id,
|
||||
title: item.name,
|
||||
subtitle: item.summary,
|
||||
categoryId,
|
||||
price: item.price,
|
||||
unit: '次',
|
||||
tags: item.tags.length > 0 ? item.tags.slice(0, 2) : ['平台认证', '可预约'],
|
||||
salesText: buildServiceSalesText(item.id),
|
||||
imageText: buildServiceImageText(categoryId),
|
||||
coverGradient: getServiceGradient(categoryId),
|
||||
detailPath: '/pages/mall/consumer/home-service/service-detail?id=' + encodeURIComponent(item.id),
|
||||
bookingPath: '/pages/mall/consumer/home-service/service-detail?id=' + encodeURIComponent(item.id) + '&mode=booking'
|
||||
} as HomeCareServiceProductType)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
async function loadServiceHomeData(): Promise<void> {
|
||||
serviceLoading.value = true
|
||||
try {
|
||||
const catalog = await fetchHomeServiceCatalog()
|
||||
if (catalog.length > 0) {
|
||||
allServiceProducts.value = buildServiceProductsFromCatalog(catalog)
|
||||
} else {
|
||||
allServiceProducts.value = buildMockServiceProducts()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载服务首页数据失败', error)
|
||||
allServiceProducts.value = buildMockServiceProducts()
|
||||
} finally {
|
||||
serviceLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleServiceCategoryClick(item: HomeCareCategoryType): void {
|
||||
selectedServiceCategory.value = item.id
|
||||
if (item.id == 'all') {
|
||||
return
|
||||
}
|
||||
let hasMatched = false
|
||||
for (let i = 0; i < allServiceProducts.value.length; i++) {
|
||||
if (allServiceProducts.value[i].categoryId == item.id) {
|
||||
hasMatched = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if (!hasMatched) {
|
||||
uni.showToast({
|
||||
title: '该类服务正在完善',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function handleServiceShortcut(shortcutId: string): void {
|
||||
if (shortcutId == 'hall') {
|
||||
goServiceHall()
|
||||
return
|
||||
}
|
||||
if (shortcutId == 'tracking') {
|
||||
uni.navigateTo({ url: '/pages/mall/consumer/home-service/index' })
|
||||
return
|
||||
}
|
||||
uni.showToast({
|
||||
title: '服务保障体系建设中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
function showServicePreview(item: HomeCareServiceProductType): void {
|
||||
if (item.detailPath != '') {
|
||||
uni.navigateTo({ url: item.detailPath })
|
||||
return
|
||||
}
|
||||
uni.showToast({
|
||||
title: '服务详情建设中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
function goServiceDetail(item: HomeCareServiceProductType): void {
|
||||
if (item.bookingPath != '') {
|
||||
uni.navigateTo({ url: item.bookingPath })
|
||||
return
|
||||
}
|
||||
uni.showToast({
|
||||
title: '服务详情建设中',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
|
||||
function goServiceHall(): void {
|
||||
uni.navigateTo({ url: '/pages/mall/consumer/home-service/index' })
|
||||
}
|
||||
|
||||
// 分类标签栏相关
|
||||
type CategoryItem = {
|
||||
id: string
|
||||
@@ -343,6 +761,9 @@ function handleHeaderSearch(keyword: string) {
|
||||
function handleTopModuleChange(moduleKey: string) {
|
||||
activeTopModule.value = moduleKey
|
||||
showCategoryPanel.value = false
|
||||
if (moduleKey == 'service' && allServiceProducts.value.length == 0 && !serviceLoading.value) {
|
||||
void loadServiceHomeData()
|
||||
}
|
||||
}
|
||||
|
||||
function handleMainScrollToLower() {
|
||||
@@ -483,9 +904,22 @@ function buildSimpleCategoryChannels(categoryId: string): SimpleCategoryChannel[
|
||||
return []
|
||||
}
|
||||
|
||||
function buildVisibleRecommendChannels(): MarketingChannel[] {
|
||||
const source = getRecommendMarketingChannels()
|
||||
const visible: MarketingChannel[] = []
|
||||
for (let i = 0; i < source.length; i++) {
|
||||
const channel = source[i]
|
||||
if (channel.title == '排行榜' || channel.title == '品质优选') {
|
||||
continue
|
||||
}
|
||||
visible.push(channel)
|
||||
}
|
||||
return visible
|
||||
}
|
||||
|
||||
function applyChannelDisplay(categoryId: string): void {
|
||||
if (categoryId === 'recommend') {
|
||||
marketingChannels.value = getRecommendMarketingChannels()
|
||||
marketingChannels.value = buildVisibleRecommendChannels()
|
||||
categorySimpleChannels.value = []
|
||||
return
|
||||
}
|
||||
@@ -1161,6 +1595,7 @@ const initData = async () => {
|
||||
await loadCategories()
|
||||
await loadBrands()
|
||||
await loadHotKeywords()
|
||||
await loadServiceHomeData()
|
||||
if (await consumeSelectedCategoryFromStorage()) {
|
||||
await loadRecommendedProducts(defaultLoadLimit)
|
||||
return
|
||||
@@ -1687,6 +2122,462 @@ const navigateToReminders = () => uni.navigateTo({ url: '/pages/user/reminders'
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.service-home-section {
|
||||
background-color: #f4f8fb;
|
||||
padding: 16rpx 16rpx 32rpx;
|
||||
box-sizing: border-box;
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.service-hero-banner {
|
||||
min-height: 240rpx;
|
||||
border-radius: 28rpx;
|
||||
padding: 28rpx;
|
||||
background: linear-gradient(135deg, #e0f7f5 0%, #eff6ff 55%, #fff7ed 100%);
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
overflow: hidden;
|
||||
margin-bottom: 18rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-hero-content {
|
||||
flex: 1;
|
||||
flex-direction: column;
|
||||
padding-right: 12rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-hero-tag {
|
||||
width: 128rpx;
|
||||
height: 38rpx;
|
||||
line-height: 38rpx;
|
||||
text-align: center;
|
||||
border-radius: 999rpx;
|
||||
background-color: #ffffff;
|
||||
color: #0f766e;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.service-hero-title {
|
||||
font-size: 40rpx;
|
||||
font-weight: 700;
|
||||
color: #16324f;
|
||||
margin-bottom: 10rpx;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.service-hero-subtitle {
|
||||
font-size: 24rpx;
|
||||
color: #64748b;
|
||||
margin-bottom: 16rpx;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
|
||||
.service-hero-tags {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-right: -8rpx;
|
||||
margin-bottom: -8rpx;
|
||||
}
|
||||
|
||||
.service-hero-chip {
|
||||
font-size: 22rpx;
|
||||
color: #0f766e;
|
||||
background-color: rgba(255, 255, 255, 0.86);
|
||||
border-radius: 999rpx;
|
||||
padding: 6rpx 14rpx;
|
||||
margin-right: 8rpx;
|
||||
margin-bottom: 8rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-hero-visual-wrap {
|
||||
width: 156rpx;
|
||||
height: 156rpx;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.service-hero-visual {
|
||||
position: absolute;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 999rpx;
|
||||
background-color: rgba(255, 255, 255, 0.72);
|
||||
box-shadow: 0 12rpx 24rpx rgba(14, 165, 164, 0.12);
|
||||
}
|
||||
|
||||
.service-hero-visual-primary {
|
||||
width: 128rpx;
|
||||
height: 128rpx;
|
||||
right: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.service-hero-visual-secondary {
|
||||
width: 74rpx;
|
||||
height: 74rpx;
|
||||
left: 0;
|
||||
bottom: 6rpx;
|
||||
background-color: rgba(15, 118, 110, 0.12);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.service-hero-visual-text {
|
||||
font-size: 56rpx;
|
||||
font-weight: 700;
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.service-hero-visual-subtext {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.service-category-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 28rpx;
|
||||
padding: 22rpx 12rpx 10rpx;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 18rpx;
|
||||
box-shadow: 0 10rpx 24rpx rgba(15, 23, 42, 0.04);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-category-item {
|
||||
width: 20%;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-category-icon {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
border-radius: 24rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 10rpx;
|
||||
border-width: 2rpx;
|
||||
border-style: solid;
|
||||
border-color: transparent;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-category-icon-active {
|
||||
border-color: #14b8a6;
|
||||
box-shadow: 0 8rpx 18rpx rgba(20, 184, 166, 0.14);
|
||||
}
|
||||
|
||||
.service-category-icon-text {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.service-category-name {
|
||||
font-size: 23rpx;
|
||||
color: #334155;
|
||||
text-align: center;
|
||||
line-height: 30rpx;
|
||||
padding: 0 4rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-category-name-active {
|
||||
color: #0f766e;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.service-shortcut-row {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 18rpx;
|
||||
}
|
||||
|
||||
.service-shortcut-card {
|
||||
width: 32%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 22rpx;
|
||||
padding: 18rpx 16rpx;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.04);
|
||||
min-height: 116rpx;
|
||||
}
|
||||
|
||||
.service-shortcut-icon {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
line-height: 52rpx;
|
||||
text-align: center;
|
||||
border-radius: 18rpx;
|
||||
background-color: #e8f7f6;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: #0f766e;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.service-shortcut-body {
|
||||
flex: 1;
|
||||
margin-left: 12rpx;
|
||||
min-width: 0;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.service-shortcut-title {
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
color: #16324f;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.service-shortcut-desc {
|
||||
margin-top: 6rpx;
|
||||
font-size: 20rpx;
|
||||
color: #64748b;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
|
||||
.service-products-section {
|
||||
background-color: #f4f8fb;
|
||||
}
|
||||
|
||||
.service-section-title-row {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 14rpx;
|
||||
}
|
||||
|
||||
.service-section-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #16324f;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.service-section-subtitle {
|
||||
margin-top: 6rpx;
|
||||
font-size: 22rpx;
|
||||
color: #94a3b8;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.service-section-more {
|
||||
font-size: 24rpx;
|
||||
color: #94a3b8;
|
||||
padding: 8rpx 0 8rpx 12rpx;
|
||||
}
|
||||
|
||||
.service-products-grid {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.service-product-card {
|
||||
width: 49%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 22rpx;
|
||||
overflow: hidden;
|
||||
margin-bottom: 14rpx;
|
||||
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.05);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-product-cover {
|
||||
height: 220rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
padding-top: 28rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-product-cover-badge {
|
||||
position: absolute;
|
||||
top: 14rpx;
|
||||
left: 14rpx;
|
||||
font-size: 20rpx;
|
||||
color: #0f766e;
|
||||
background-color: rgba(255, 255, 255, 0.82);
|
||||
border-radius: 999rpx;
|
||||
padding: 4rpx 10rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-product-cover-text {
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
color: #0f766e;
|
||||
}
|
||||
|
||||
.service-product-body {
|
||||
padding: 18rpx;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-product-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #1f2937;
|
||||
line-height: 36rpx;
|
||||
min-height: 72rpx;
|
||||
max-height: 72rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.service-product-subtitle {
|
||||
margin-top: 8rpx;
|
||||
font-size: 22rpx;
|
||||
color: #64748b;
|
||||
line-height: 32rpx;
|
||||
min-height: 64rpx;
|
||||
max-height: 64rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.service-product-tags {
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-top: 10rpx;
|
||||
margin-right: -6rpx;
|
||||
margin-bottom: -6rpx;
|
||||
}
|
||||
|
||||
.service-product-tag {
|
||||
font-size: 20rpx;
|
||||
color: #0f766e;
|
||||
background-color: #e0f2f1;
|
||||
border-radius: 8rpx;
|
||||
padding: 4rpx 8rpx;
|
||||
margin-right: 6rpx;
|
||||
margin-bottom: 6rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-product-price-row {
|
||||
margin-top: 12rpx;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.service-product-price-symbol {
|
||||
font-size: 22rpx;
|
||||
color: #e1251b;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
margin-bottom: 6rpx;
|
||||
}
|
||||
|
||||
.service-product-price {
|
||||
font-size: 36rpx;
|
||||
color: #e1251b;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.service-product-unit {
|
||||
font-size: 22rpx;
|
||||
color: #64748b;
|
||||
margin-left: 4rpx;
|
||||
margin-bottom: 4rpx;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.service-product-sales {
|
||||
margin-top: 6rpx;
|
||||
font-size: 21rpx;
|
||||
color: #94a3b8;
|
||||
line-height: 30rpx;
|
||||
}
|
||||
|
||||
.service-product-action-row {
|
||||
margin-top: 14rpx;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.service-product-secondary-btn,
|
||||
.service-product-primary-btn {
|
||||
height: 58rpx;
|
||||
border-radius: 999rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 22rpx;
|
||||
font-weight: 700;
|
||||
padding: 0 16rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-product-secondary-btn {
|
||||
width: 47%;
|
||||
border-width: 1rpx;
|
||||
border-style: solid;
|
||||
border-color: #cbd5e1;
|
||||
color: #476072;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.service-product-primary-btn {
|
||||
width: 49%;
|
||||
background-color: #16a085;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.service-state-card {
|
||||
background-color: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
padding: 28rpx 24rpx;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 8rpx 20rpx rgba(15, 23, 42, 0.04);
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.service-state-title {
|
||||
font-size: 28rpx;
|
||||
font-weight: 700;
|
||||
color: #16324f;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.service-state-desc {
|
||||
margin-top: 10rpx;
|
||||
font-size: 22rpx;
|
||||
color: #64748b;
|
||||
line-height: 32rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.service-state-action {
|
||||
margin-top: 18rpx;
|
||||
height: 68rpx;
|
||||
line-height: 68rpx;
|
||||
padding: 0 28rpx;
|
||||
border-radius: 999rpx;
|
||||
background-color: #16a085;
|
||||
color: #ffffff;
|
||||
font-size: 24rpx;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.pdd-home-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user