feat(miniprogram): 访客首页轮播图接入公开 API + 文章列表替换核心功能区域

This commit is contained in:
iven
2026-05-10 16:23:17 +08:00
parent 6bf8cc53f8
commit b2c6d9c8c8
2 changed files with 122 additions and 25 deletions

View File

@@ -339,6 +339,19 @@
&--1 {
background: linear-gradient(135deg, $pri-d 0%, $pri 60%, $pri-l 100%);
}
&--2 {
background: linear-gradient(135deg, $acc 0%, #3D5A40 60%, $acc-l 100%);
}
&--3 {
background: linear-gradient(135deg, #8B6F4E 0%, $wrn 60%, $wrn-l 100%);
}
}
.guest-slide-image {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
}
.guest-slide:nth-child(2) .guest-slide-bg {
@@ -397,14 +410,27 @@
.guest-article-card {
background: $card;
border-radius: $r;
padding: 16px 18px;
overflow: hidden;
box-shadow: $shadow-sm;
display: flex;
&:active {
opacity: 0.85;
}
}
.guest-article-cover {
width: 100px;
height: 80px;
flex-shrink: 0;
}
.guest-article-body {
padding: 12px 14px;
flex: 1;
min-width: 0;
}
.guest-article-title {
font-size: var(--tk-font-body-sm);
font-weight: 600;

View File

@@ -1,5 +1,5 @@
import { View, Text, Swiper, SwiperItem } from '@tarojs/components';
import { useState, useCallback } from 'react';
import { View, Text, Swiper, SwiperItem, Image } from '@tarojs/components';
import { useState } from 'react';
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
import { useAuthStore } from '../../stores/auth';
import { useUIStore } from '../../stores/ui';
@@ -12,6 +12,8 @@ import * as appointmentApi from '@/services/appointment';
import * as followupApi from '@/services/followup';
import { listPendingSuggestions, type AiSuggestionItem } from '@/services/ai-analysis';
import { notificationService } from '@/services/notification';
import { api } from '@/services/request';
import type { Article } from '@/services/article';
import './index.scss';
interface ReminderItem {
@@ -21,15 +23,62 @@ interface ReminderItem {
tag: string;
}
interface PublicBanner {
id: string;
title?: string;
subtitle?: string;
image_url?: string;
link_type?: string;
link_target?: string;
}
// ─── 访客首页 ───
const CAROUSEL_SLIDES = [
{ id: 'slide-1', title: '专业血透中心', desc: '三甲级医护团队全程守护' },
{ id: 'slide-2', title: '智慧健康管理', desc: 'AI 驱动个性化健康方案' },
{ id: 'slide-3', title: '温馨就医环境', desc: '舒适安全的治疗体验' },
const FALLBACK_SLIDES = [
{ id: 'slide-1', title: '专业血透中心', desc: '三甲级医护团队全程守护', image_url: '' },
{ id: 'slide-2', title: '智慧健康管理', desc: 'AI 驱动个性化健康方案', image_url: '' },
{ id: 'slide-3', title: '温馨就医环境', desc: '舒适安全的治疗体验', image_url: '' },
];
function GuestHome({ modeClass }: { modeClass: string }) {
const [banners, setBanners] = useState<PublicBanner[]>([]);
const [articles, setArticles] = useState<Article[]>([]);
useDidShow(() => {
loadPublicData();
});
const loadPublicData = async () => {
const tenantId = Taro.getStorageSync('tenant_id');
if (!tenantId) {
setBanners(FALLBACK_SLIDES);
return;
}
try {
const [bannerData, articleData] = await Promise.allSettled([
api.get<PublicBanner[]>('/public/banners', { tenant_id: tenantId }, 300_000),
api.get<{ data: Article[]; total: number }>('/health/articles', {
status: 'published',
page_size: 4,
}, 300_000),
]);
if (bannerData.status === 'fulfilled' && bannerData.value?.length > 0) {
setBanners(bannerData.value);
} else {
setBanners(FALLBACK_SLIDES);
}
if (articleData.status === 'fulfilled' && articleData.value?.data?.length > 0) {
setArticles(articleData.value.data);
}
} catch {
setBanners(FALLBACK_SLIDES);
}
};
const slides = banners.length > 0 ? banners : FALLBACK_SLIDES;
return (
<View className={`guest-page ${modeClass}`}>
{/* 轮播图 */}
@@ -43,36 +92,58 @@ function GuestHome({ modeClass }: { modeClass: string }) {
interval={4000}
duration={500}
>
{CAROUSEL_SLIDES.map((slide) => (
<SwiperItem key={slide.id}>
{slides.map((slide, idx) => (
<SwiperItem key={slide.id || idx}>
<View className='guest-slide'>
<View className='guest-slide-bg guest-slide-bg--1' />
{slide.image_url ? (
<Image className='guest-slide-image' src={slide.image_url} mode='aspectFill' />
) : (
<View className={`guest-slide-bg guest-slide-bg--${(idx % 3) + 1}`} />
)}
<View className='guest-slide-content'>
<Text className='guest-slide-title'>{slide.title}</Text>
<Text className='guest-slide-desc'>{slide.desc}</Text>
<Text className='guest-slide-desc'>{slide.subtitle || slide.desc}</Text>
</View>
</View>
</SwiperItem>
))}
</Swiper>
{/* 功能亮点 */}
{/* 推荐文章(替换原来的"核心功能"区域) */}
<View className='guest-section'>
<Text className='guest-section-title'></Text>
<View className='guest-articles'>
<View className='guest-article-card'>
<Text className='guest-article-title'></Text>
<Text className='guest-article-summary'></Text>
<Text className='guest-section-title'></Text>
{articles.length > 0 ? (
<View className='guest-articles'>
{articles.map((article) => (
<View className='guest-article-card' key={article.id}>
{article.cover_image && (
<Image className='guest-article-cover' src={article.cover_image} mode='aspectFill' />
)}
<View className='guest-article-body'>
<Text className='guest-article-title'>{article.title}</Text>
<Text className='guest-article-summary'>
{article.summary || '点击查看详情'}
</Text>
</View>
</View>
))}
</View>
<View className='guest-article-card'>
<Text className='guest-article-title'></Text>
<Text className='guest-article-summary'>线</Text>
) : (
<View className='guest-articles'>
<View className='guest-article-card'>
<Text className='guest-article-title'></Text>
<Text className='guest-article-summary'></Text>
</View>
<View className='guest-article-card'>
<Text className='guest-article-title'></Text>
<Text className='guest-article-summary'>线</Text>
</View>
<View className='guest-article-card'>
<Text className='guest-article-title'>AI </Text>
<Text className='guest-article-summary'></Text>
</View>
</View>
<View className='guest-article-card'>
<Text className='guest-article-title'>AI </Text>
<Text className='guest-article-summary'></Text>
</View>
</View>
)}
</View>
{/* 底部登录引导 */}