Files
----/前端源码/uni-app/components/screens/Home.vue

1704 lines
38 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="home-screen">
<!-- 状态栏占位 -->
<view class="status-bar-placeholder" :style="{ height: statusBarHeight }"></view>
<!-- Header -->
<view class="home-header" @click="onNavigateToCalendar && onNavigateToCalendar()">
<view class="home-header-bg">{{ dateInfo.zodiac }}</view>
<view class="home-header-content">
<view class="home-header-left">
<view class="home-header-title-row">
<text class="home-header-title">{{ dateInfo.lunar.split(" ")[1] }}</text>
<text class="home-header-subtitle writing-vertical-rl">{{ dateInfo.lunar.split(" ")[0] }}</text>
</view>
<view class="home-header-date-row">
<text class="home-header-year">{{ dateInfo.year }}</text>
<text class="home-header-solar">{{ dateInfo.solar }}</text>
</view>
</view>
<view class="home-header-right">
<view class="home-header-card">
<view class="home-header-card-icon">
<text class="home-header-card-icon-text"></text>
</view>
<view class="home-header-card-content">
<text class="home-header-card-text" v-for="(item, idx) in dateInfo.yi.slice(0, 2)" :key="idx">{{ item
}}</text>
</view>
</view>
<view class="home-header-card home-header-card-ji">
<view class="home-header-card-icon home-header-card-icon-ji">
<text class="home-header-card-icon-text home-header-card-icon-text-ji"></text>
</view>
<view class="home-header-card-content home-header-card-content-ji">
<text class="home-header-card-text home-header-card-text-ji"
v-for="(item, idx) in dateInfo.ji.slice(0, 2)" :key="idx">{{ item }}</text>
</view>
</view>
</view>
</view>
</view>
<!-- Notification -->
<view class="home-notification-wrapper" v-if="notifications.length > 0">
<view class="home-notification">
<view class="home-notification-icon">
<view class="home-notification-dot"></view>
</view>
<view class="home-notification-content">
<view class="home-notification-scroll">
<text v-for="(note, idx) in notifications" :key="idx" class="home-notification-text">
{{ note }}
</text>
</view>
</view>
</view>
</view>
<!-- Main actions -->
<view class="home-actions">
<view class="home-action-card home-action-card-primary" @click="onNavigateToNaming && onNavigateToNaming()">
<view class="home-action-card-bg"></view>
<view class="home-action-card-icon">
<view class="home-action-card-icon-inner">
<text class="home-action-card-icon-text"></text>
</view>
</view>
<view class="home-action-card-content">
<text class="home-action-card-title">年度财运</text>
<text class="home-action-card-subtitle">流年大运 · 财富密码</text>
</view>
<view class="home-action-card-tags">
<text class="home-action-card-tag home-action-card-tag-primary">宝宝起名</text>
<text class="home-action-card-tag">公司起名</text>
</view>
</view>
<view class="home-action-card home-action-card-secondary" @click="onNavigateToTest && onNavigateToTest()">
<view class="home-action-card-bg-secondary"></view>
<view class="home-action-card-icon-secondary">
<view class="home-action-card-icon-inner-secondary">
<text class="home-action-card-icon-text-secondary"></text>
</view>
</view>
<view class="home-action-card-content">
<view class="home-action-card-title-secondary">姓名测试</view>
<view class="home-action-card-subtitle-secondary">五格三才 · 吉凶分析</view>
</view>
<view class="home-action-card-tags">
<text class="home-action-card-tag home-action-card-tag-secondary">个人测名</text>
<text class="home-action-card-tag home-action-card-tag-secondary-alt">改名</text>
</view>
</view>
</view>
<!-- 缘分合盘模块 -->
<view class="home-fate-section">
<view class="home-fate-card" @click="props.onNavigateToAffinity && props.onNavigateToAffinity()">
<view class="home-fate-deco-left"></view>
<view class="home-fate-deco-right"></view>
<view class="home-fate-content">
<view class="home-fate-header">
<view class="home-fate-icon-wrap">
<text class="home-fate-icon">💕</text>
</view>
<text class="home-fate-title">缘分合盘</text>
</view>
<text class="home-fate-subtitle">
深度解析双方 <text class="home-fate-highlight">性格</text> · <text class="home-fate-highlight">情感</text> · <text
class="home-fate-highlight">未来</text>
</text>
<view class="home-fate-tags">
<text class="home-fate-tag home-fate-tag-primary">上上婚</text>
<text class="home-fate-tag home-fate-tag-secondary">前世羁绊</text>
</view>
</view>
<view class="home-fate-decoration">
<view class="home-fate-decoration-outer">
<view class="home-fate-decoration-middle">
<view class="home-fate-decoration-inner">
<text class="home-fate-decoration-text"></text>
</view>
</view>
</view>
<view class="home-fate-decoration-pulse"></view>
</view>
</view>
</view>
<view class="home-sections">
<!-- Video block -->
<section class="home-section">
<view class="home-section-header">
<view class="home-section-title-row">
<view class="home-section-title-bar"></view>
<text class="home-section-title">大师影像</text>
</view>
<text class="home-section-subtitle">国风起名</text>
</view>
<view class="home-video-card" @click="playVideo">
<!-- 封面图片 - 使用背景图方式 -->
<view v-if="aboutVideo.cover_url" class="home-video-cover"
:style="{ backgroundImage: `url(${aboutVideo.cover_url})` }"></view>
<!-- 默认背景 -->
<view v-else class="home-video-bg"></view>
<!-- 渐变遮罩 -->
<view class="home-video-overlay"></view>
<!-- 播放按钮 -->
<view class="home-video-play">
<text class="home-video-play-icon"></text>
</view>
<!-- 视频信息 -->
<view v-if="aboutVideo.title" class="home-video-info-overlay">
<text class="home-video-title-overlay">{{ aboutVideo.title }}</text>
<text v-if="aboutVideo.description" class="home-video-desc-overlay">{{ aboutVideo.description }}</text>
</view>
</view>
</section>
<!-- Best names -->
<!-- <section class="home-section">
<view class="home-section-header-simple">
<view class="home-section-title-row">
<view class="home-section-title-bar home-section-title-bar-red"></view>
<text class="home-section-title">佳名赏析</text>
</view>
</view>
<scroll-view scroll-x="true" enable-flex="true" class="home-names-scroll">
<view class="home-names-scroll-content">
<view v-for="(item, idx) in bestNames" :key="idx" class="home-name-card"
@click="handleNameCardClick(item.id)">
<view class="home-name-card-divider"></view>
<view class="home-name-card-content">
<text class="home-name-card-name writing-vertical-rl">{{ item.name }}</text>
<text class="home-name-card-source writing-vertical-rl">{{ item.source }}</text>
</view>
<view class="home-name-card-footer">
<text class="home-name-card-desc">
{{ item.desc }}
</text>
</view>
</view>
</view>
</scroll-view>
</section> -->
<!-- Story - Book Style -->
<section class="home-book">
<view class="home-book-spine"></view>
<view class="home-book-page home-book-page-left">
<view class="home-book-header">
<text class="home-book-quote">"</text>
<text class="home-book-title">{{ aboutUs.title }}</text>
</view>
<view class="home-book-text">
<text class="home-book-drop">{{ contentParts.left.charAt(0) }}</text>
<text>{{ contentParts.left.slice(1) }}</text>
</view>
</view>
<view class="home-book-page home-book-page-right">
<view class="home-book-text">
<text>{{ contentParts.right }}</text>
</view>
<view class="home-book-footer">
<text class="home-book-date">乙巳年冬</text>
<view class="home-book-seal">
<text>壹梵</text>
</view>
</view>
</view>
</section>
</view>
<!-- 视频播放器弹窗 -->
<view v-if="showVideoModal" class="video-modal" @click="closeVideoModal">
<view class="video-modal-content" @click.stop>
<view class="video-modal-header">
<text class="video-modal-title">{{ aboutVideo.title || '大师影像' }}</text>
<view class="video-modal-close" @click="closeVideoModal">
<text class="video-modal-close-icon">✕</text>
</view>
</view>
<view class="video-modal-body">
<video :src="aboutVideo.video_url" :poster="aboutVideo.cover_url" controls class="video-player"
object-fit="contain" show-center-play-btn enable-play-gesture />
</view>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { watch, ref, onMounted, computed } from 'vue';
import type { CalendarInfo, AboutUsInfo, AboutVideoInfo, RecommendedSolution, NoticeItem } from '../../api/types';
import { homePageApi } from '../../api/home';
const CALENDAR_CACHE_KEY = 'calendar_today_cache';
const ABOUT_US_CACHE_KEY = 'about_us_cache';
const ABOUT_VIDEO_CACHE_KEY = 'about_video_cache';
const RECOMMENDED_CACHE_KEY = 'recommended_solutions_cache';
const NOTICE_CACHE_KEY = 'notice_list_cache';
const props = defineProps<{
active?: boolean;
onNavigateToCalendar?: () => void;
onNavigateToNaming?: () => void;
onNavigateToTest?: () => void;
onNavigateToAffinity?: () => void;
onShowSolutionDetail?: (id: number) => void;
}>();
// 状态栏高度H5 环境下设置为 0
const statusBarHeight = ref('0px');
const notifications = ref<string[]>([]);
const noticeList = ref<NoticeItem[]>([]);
// 日历数据
const calendarInfo = ref<CalendarInfo | null>(null);
// 格式化后的日期信息(用于模板显示)
const dateInfo = computed(() => {
if (calendarInfo.value) {
const c = calendarInfo.value;
return {
lunar: `${c.lunar_month} ${c.lunar_day}`,
year: c.lunar_year,
solar: c.solar_date,
yi: c.yi || [],
ji: c.ji || [],
zodiac: c.zodiac,
weekday: c.weekday,
};
}
// 默认值
return {
lunar: '正月 初一',
year: '乙巳年',
solar: '2025.01.29',
yi: ['祈福', '开市'],
ji: ['动土', '安葬'],
zodiac: '蛇',
weekday: '星期三',
};
});
// 获取今天的日期字符串 YYYY-MM-DD
const getTodayStr = () => {
const now = new Date();
const y = now.getFullYear();
const m = String(now.getMonth() + 1).padStart(2, '0');
const d = String(now.getDate()).padStart(2, '0');
return `${y}-${m}-${d}`;
};
// 加载万年历(带缓存)
const loadCalendar = async () => {
const todayStr = getTodayStr();
// 尝试读取缓存
try {
const cached = localStorage.getItem(CALENDAR_CACHE_KEY);
if (cached) {
const parsedCache = JSON.parse(cached);
if (parsedCache.date === todayStr && parsedCache.data) {
calendarInfo.value = parsedCache.data;
return; // 缓存有效,直接返回
}
}
} catch (e) {
console.warn('读取万年历缓存失败', e);
}
// 缓存无效或过期,请求接口
try {
const result = await homePageApi.getCalendarToday();
if (result) {
calendarInfo.value = result;
// 存入缓存
localStorage.setItem(CALENDAR_CACHE_KEY, JSON.stringify({
date: todayStr,
data: result,
}));
}
} catch (e) {
console.warn('加载万年历失败(使用默认数据)', e);
}
};
// 加载首页数据
const loadData = async () => {
try {
// 加载消息轮播
await loadNoticeList();
// 加载万年历(带缓存)
await loadCalendar();
// 加载文字介绍
await loadAboutUs();
// 加载视频介绍
await loadAboutVideo();
// 加载佳名赏析
await loadRecommendedSolutions();
} catch (e) {
console.error('加载首页数据失败', e);
}
};
// 加载消息轮播
const loadNoticeList = async () => {
// 尝试读取缓存
try {
const cached = localStorage.getItem(NOTICE_CACHE_KEY);
if (cached) {
const parsedCache = JSON.parse(cached);
if (parsedCache.data && parsedCache.data.length > 0) {
noticeList.value = parsedCache.data;
notifications.value = parsedCache.data.map((item: NoticeItem) => item.notice_content);
}
}
} catch (e) {
console.warn('读取消息轮播缓存失败', e);
// 清除损坏的缓存
try {
localStorage.removeItem(NOTICE_CACHE_KEY);
} catch (err) {
console.error('清除缓存失败', err);
}
}
// 请求接口获取最新数据
try {
const result = await homePageApi.getNoticeList(1, 20);
if (result && result.items && result.items.length > 0) {
noticeList.value = result.items;
// 提取消息内容用于显示
notifications.value = result.items.map((item: NoticeItem) => item.notice_content);
// 如果只有一条消息,复制一份用于滚动动画
if (notifications.value.length === 1) {
notifications.value = [...notifications.value, ...notifications.value];
}
// 存入缓存
try {
localStorage.setItem(NOTICE_CACHE_KEY, JSON.stringify({
data: result.items,
timestamp: Date.now(),
}));
} catch (cacheError) {
console.warn('保存消息轮播缓存失败', cacheError);
}
} else {
// 接口返回空数据
console.warn('消息轮播接口返回空数据');
if (notifications.value.length === 0) {
notifications.value = ['欢迎使用壹梵起名,祝您好运连连!'];
}
}
} catch (e: any) {
// 静默处理错误,不显示给用户
console.warn('加载消息轮播失败(使用默认消息)', {
error: e.message,
msg: e.msg,
url: '/notice/mini/list'
});
// 如果加载失败且没有缓存数据,使用默认消息
if (notifications.value.length === 0) {
notifications.value = ['欢迎使用壹梵起名,祝您好运连连!'];
}
}
};
// 佳名赏析数据
const recommendedSolutions = ref<RecommendedSolution[]>([]);
// 格式化后的佳名数据(用于模板显示)
const bestNames = computed(() => {
return recommendedSolutions.value.map((item: RecommendedSolution) => ({
id: item.id,
name: item.name,
source: item.wuxing || '',
desc: item.name_meaning || '',
}));
});
// 加载佳名赏析
const loadRecommendedSolutions = async () => {
// 尝试读取缓存
try {
const cached = localStorage.getItem(RECOMMENDED_CACHE_KEY);
if (cached) {
const parsedCache = JSON.parse(cached);
if (parsedCache.data && parsedCache.data.length > 0) {
recommendedSolutions.value = parsedCache.data;
}
}
} catch (e) {
console.warn('读取佳名赏析缓存失败', e);
}
// 请求接口获取最新数据
try {
const result = await homePageApi.getRecommendedSolutions(1, 10);
if (result && result.items && result.items.length > 0) {
recommendedSolutions.value = result.items;
// 存入缓存
localStorage.setItem(RECOMMENDED_CACHE_KEY, JSON.stringify({
data: result.items,
timestamp: Date.now(),
}));
}
} catch (e) {
console.warn('加载佳名赏析失败(使用默认数据)', e);
}
};
// 文字介绍数据
const aboutUs = ref<AboutUsInfo>({
id: 0,
title: '壹梵缘起',
content: '古人云:"赐子千金不如教子一艺教子一艺不如赐子好名"壹梵起名,立足于中华五千年传统文化,深耕周易数理与汉字音形义。\n\n我们不只是简单的文字组合而是探寻生命磁场与文字能量的共鸣。在这里每一笔一划都蕴含着对未来的期许。',
});
// 解析内容,分成左右两部分
const contentParts = computed(() => {
const parts = aboutUs.value.content.split('\n\n');
return {
left: parts[0] || '',
right: parts[1] || '',
};
});
// 加载文字介绍(带缓存)
const loadAboutUs = async () => {
// 尝试读取缓存
try {
const cached = localStorage.getItem(ABOUT_US_CACHE_KEY);
if (cached) {
const parsedCache = JSON.parse(cached);
if (parsedCache.data) {
aboutUs.value = parsedCache.data;
}
}
} catch (e) {
console.warn('读取文字介绍缓存失败', e);
}
// 请求接口获取最新数据
try {
const result = await homePageApi.getAboutUs();
if (result) {
aboutUs.value = result;
// 存入缓存
localStorage.setItem(ABOUT_US_CACHE_KEY, JSON.stringify({
data: result,
timestamp: Date.now(),
}));
}
} catch (e) {
console.warn('加载文字介绍失败(使用默认数据)', e);
}
};
// 视频介绍数据
const aboutVideo = ref<AboutVideoInfo>({
id: 0,
title: '',
description: '',
video_url: '',
cover_url: '',
});
// 加载视频介绍(带缓存)
const loadAboutVideo = async () => {
// 尝试读取缓存
try {
const cached = localStorage.getItem(ABOUT_VIDEO_CACHE_KEY);
if (cached) {
const parsedCache = JSON.parse(cached);
if (parsedCache.data) {
aboutVideo.value = parsedCache.data;
}
}
} catch (e) {
console.warn('读取视频介绍缓存失败', e);
}
// 请求接口获取最新数据
try {
const result = await homePageApi.getAboutVideo();
if (result) {
aboutVideo.value = result;
// 存入缓存
localStorage.setItem(ABOUT_VIDEO_CACHE_KEY, JSON.stringify({
data: result,
timestamp: Date.now(),
}));
}
} catch (e) {
console.warn('加载视频介绍失败(使用默认数据)', e);
}
};
// 播放视频
const showVideoModal = ref(false);
const playVideo = () => {
if (aboutVideo.value.video_url) {
showVideoModal.value = true;
} else {
uni.showToast({ title: '暂无视频', icon: 'none' });
}
};
const closeVideoModal = () => {
showVideoModal.value = false;
};
// 点击佳名卡片
const handleNameCardClick = (id: number) => {
if (props.onShowSolutionDetail) {
props.onShowSolutionDetail(id);
}
};
// 监听 active 变化,进入页面时加载数据
watch(
() => props.active,
(isActive) => {
if (isActive) {
loadData();
}
}
);
// 组件挂载时加载数据
onMounted(() => {
loadData();
});
</script>
<style scoped>
.home-screen {
height: 100%;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
background: #fdfbf7;
padding-bottom: 64rpx;
overflow-y: auto;
}
/* 状态栏占位 */
.status-bar-placeholder {
height: var(--status-bar-height, 0);
width: 100%;
flex-shrink: 0;
}
/* Header */
.home-header {
padding: 48rpx 48rpx 32rpx;
border-bottom: 1px solid #eaddcf;
position: relative;
overflow: hidden;
}
.home-header-bg {
position: absolute;
right: -32rpx;
top: -32rpx;
font-size: 120px;
font-weight: bold;
color: #8b2323;
opacity: 0.03;
user-select: none;
pointer-events: none;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
line-height: 1;
}
.home-header-content {
display: flex;
justify-content: space-between;
align-items: stretch;
}
.home-header-left {
display: flex;
flex-direction: column;
justify-content: space-between;
}
.home-header-title-row {
display: flex;
align-items: baseline;
gap: 16rpx;
}
.home-header-title {
font-size: 40px;
font-weight: bold;
color: #2c2c2c;
letter-spacing: -0.02em;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
}
.home-header-subtitle {
font-size: 24rpx;
color: #5a5a5a;
letter-spacing: 0.1em;
border-left: 1px solid #8b2323;
padding-left: 16rpx;
display: flex;
align-items: center;
height: 45px;
}
.home-header-date-row {
font-size: 12px;
color: #8a8a8a;
margin-top: 16rpx;
letter-spacing: 0.2em;
text-transform: uppercase;
display: flex;
align-items: center;
gap: 16rpx;
}
.home-header-year {
background: #2c2c2c;
color: #fdfbf7;
padding: 4rpx 8rpx;
}
.home-header-solar {
color: #8a8a8a;
}
.home-header-right {
display: flex;
gap: 16rpx;
}
.home-header-card {
display: flex;
flex-direction: column;
align-items: center;
padding: 12rpx 16rpx;
background: #fffdf9;
border: 1px solid #eaddcf;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
min-width: 60rpx;
}
.home-header-card-icon {
width: 32rpx;
height: 32rpx;
border-radius: 50%;
border: 1px solid #2c2c2c;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 8rpx;
}
.home-header-card-icon-text {
font-size: 18rpx;
font-weight: bold;
color: #2c2c2c;
}
.home-header-card-icon-ji {
border-color: #8b2323;
}
.home-header-card-icon-text-ji {
color: #8b2323;
}
.home-header-card-content {
display: flex;
flex-direction: column;
align-items: center;
gap: 4rpx;
border-top: 1px solid #eaddcf;
padding-top: 8rpx;
}
.home-header-card-text {
font-size: 20rpx;
color: #5a5a5a;
line-height: 1.4;
}
.home-header-card-text-ji {
color: #8b2323;
}
.home-header-card-content-ji {
border-top-color: rgba(139, 35, 35, 0.2);
}
/* Notification */
.home-notification-wrapper {
padding: 0 20px;
margin-top: 20px;
}
.home-notification {
background: #f4f1ea;
padding: 12px 16px;
display: flex;
align-items: center;
border-radius: 4px;
border-left: 2px solid #8b2323;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.home-notification-icon {
color: #8b2323;
margin-right: 16px;
opacity: 0.8;
flex-shrink: 0;
}
.home-notification-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: rgba(139, 35, 35, 0.6);
display: block;
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 0.6;
transform: scale(1);
}
50% {
opacity: 1;
transform: scale(1.1);
}
}
.home-notification-content {
flex: 1;
overflow: hidden;
height: 24px;
position: relative;
}
.home-notification-scroll {
display: flex;
flex-direction: column;
animation: scroll-vertical 10s linear infinite;
}
@keyframes scroll-vertical {
0% {
transform: translateY(0);
}
100% {
transform: translateY(-100%);
}
}
.home-notification-text {
font-size: 14px;
color: #5a5a5a;
letter-spacing: 0.05em;
white-space: nowrap;
font-weight: 500;
height: 24px;
line-height: 24px;
display: block;
overflow: hidden;
text-overflow: ellipsis;
padding: 0 10px 0 0;
}
/* Actions */
.home-actions {
padding: 0 32rpx;
margin-top: 48rpx;
display: grid;
grid-template-columns: 1fr 1fr;
gap: 32rpx;
}
.home-action-card {
border-radius: 24rpx;
padding: 4vw;
position: relative;
overflow: hidden;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
max-height: 256rpx;
aspect-ratio: 1.2;
display: flex;
flex-direction: column;
justify-content: space-between;
}
@media (max-width: 750rpx) {
.home-action-card {
padding: 32rpx;
}
}
@media (min-width: 750rpx) {
.home-action-card {
padding: 40rpx;
}
}
.home-action-card-primary {
background: #2c2c2c;
}
.home-action-card-bg {
position: absolute;
right: 0;
top: 0;
width: 24vw;
min-width: 192rpx;
max-width: 240rpx;
aspect-ratio: 1;
background: #3a3a3a;
border-radius: 50%;
transform: translate(10vw, -10vw);
}
.home-action-card-icon {
position: absolute;
right: 16rpx;
top: 16rpx;
opacity: 0.2;
}
.home-action-card-icon-inner {
width: 12vw;
min-width: 96rpx;
max-width: 120rpx;
aspect-ratio: 1;
border: 2px solid #fff;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.home-action-card-icon-text {
font-size: 24rpx;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
color: white;
}
.home-action-card-content {
position: relative;
z-index: 10;
}
.home-action-card-title {
font-size: 20rpx;
font-weight: bold;
color: #fdfbf7;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
letter-spacing: 0.1em;
margin-bottom: 8rpx;
display: block;
}
.home-action-card-subtitle {
font-size: 12px;
color: #a0a0a0;
display: block;
}
.home-action-card-tags {
position: relative;
z-index: 10;
display: flex;
align-items: center;
gap: 8rpx;
}
.home-action-card-tag {
font-size: 11px;
padding: 4rpx 12rpx;
border-radius: 4rpx;
border: 1px solid rgba(160, 160, 160, 0.3);
color: #a0a0a0;
}
.home-action-card-tag-primary {
border-color: rgba(212, 175, 55, 0.3);
color: #d4af37;
}
.home-action-card-secondary {
background: #fffdf9;
border: 1px solid #eaddcf;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
.home-action-card-bg-secondary {
position: absolute;
right: 0;
top: 0;
width: 24vw;
min-width: 192rpx;
max-width: 240rpx;
aspect-ratio: 1;
background: #8b2323;
border-radius: 50%;
transform: translate(10vw, -10vw);
opacity: 0.05;
}
.home-action-card-icon-secondary {
position: absolute;
right: 16rpx;
top: 16rpx;
opacity: 0.1;
color: #8b2323;
}
.home-action-card-icon-inner-secondary {
width: 12vw;
min-width: 96rpx;
max-width: 120rpx;
aspect-ratio: 1;
border: 2px solid #8b2323;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.home-action-card-icon-text-secondary {
font-size: 24rpx;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
color: #8b2323;
}
.home-action-card-title-secondary {
color: #2c2c2c;
font-size: 13px;
}
.home-action-card-subtitle-secondary {
color: #5a5a5a;
font-size: 12px;
margin-top: 3px;
}
.home-action-card-tag-secondary {
border-color: rgba(139, 35, 35, 0.3);
color: #8b2323;
}
.home-action-card-tag-secondary-alt {
border-color: #eaddcf;
color: #5a5a5a;
}
/* 缘分合盘模块 */
.home-fate-section {
padding: 0 32rpx;
margin-top: 48rpx;
}
.home-fate-card {
background: linear-gradient(to right, #ffe4e6, #fff1f2);
border: 2rpx solid #fecdd3;
border-radius: 24rpx;
padding: 40rpx;
position: relative;
overflow: hidden;
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.1);
display: flex;
align-items: center;
justify-content: space-between;
}
/* 装饰元素 */
.home-fate-deco-left {
position: absolute;
left: 0;
bottom: 0;
width: 256rpx;
height: 256rpx;
background: #fda4af;
border-radius: 50%;
filter: blur(64rpx);
opacity: 0.2;
transform: translate(-80rpx, 80rpx);
}
.home-fate-deco-right {
position: absolute;
right: 0;
top: 0;
width: 256rpx;
height: 256rpx;
background: #d4af37;
border-radius: 50%;
filter: blur(96rpx);
opacity: 0.1;
transform: translate(80rpx, -80rpx);
}
.home-fate-content {
flex: 1;
position: relative;
z-index: 10;
}
.home-fate-header {
display: flex;
align-items: center;
gap: 16rpx;
margin-bottom: 8rpx;
}
.home-fate-icon-wrap {
width: 48rpx;
height: 48rpx;
background: rgba(244, 63, 94, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.home-fate-icon {
font-size: 32rpx;
}
.home-fate-title {
font-size: 40rpx;
font-weight: bold;
color: #2c2c2c;
letter-spacing: 0.3em;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
}
.home-fate-subtitle {
font-size: 22rpx;
color: #5a5a5a;
padding-left: 8rpx;
display: block;
white-space: nowrap;
line-height: 1.2;
}
.home-fate-highlight {
color: #db2777;
font-weight: bold;
}
.home-fate-tags {
display: flex;
gap: 16rpx;
margin-top: 24rpx;
}
.home-fate-tag {
font-size: 20rpx;
padding: 4rpx 16rpx;
border-radius: 999rpx;
}
.home-fate-tag-primary {
color: #be185d;
background: rgba(251, 207, 232, 0.5);
}
.home-fate-tag-secondary {
color: #8a8a8a;
background: rgba(255, 255, 255, 0.5);
border: 1rpx solid #fecdd3;
}
.home-fate-decoration {
position: relative;
width: 120rpx;
height: 120rpx;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
z-index: 10;
}
.home-fate-decoration-outer {
width: 96rpx;
height: 96rpx;
border-radius: 50%;
border: 3rpx solid #fecdd3;
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.3);
backdrop-filter: blur(8rpx);
}
.home-fate-decoration-middle {
width: 72rpx;
height: 72rpx;
border-radius: 50%;
border: 2rpx solid #fbcfe8;
background: #fce7f3;
display: flex;
align-items: center;
justify-content: center;
}
.home-fate-decoration-inner {
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.home-fate-decoration-text {
font-size: 36rpx;
color: #ec4899;
font-weight: bold;
}
.home-fate-decoration-pulse {
position: absolute;
inset: 0;
border-radius: 50%;
border: 2rpx solid #f9a8d4;
opacity: 0.2;
animation: fate-pulse 2s ease-in-out infinite;
}
@keyframes fate-pulse {
0%,
100% {
transform: scale(1);
opacity: 0.5;
}
50% {
transform: scale(1.2);
opacity: 0;
}
}
/* Sections */
.home-sections {
padding: 32rpx;
display: flex;
flex-direction: column;
gap: 80rpx;
}
.home-section-header {
display: flex;
align-items: flex-end;
justify-content: space-between;
margin-bottom: 24rpx;
padding: 0 8rpx;
}
.home-section-title-row {
display: flex;
align-items: center;
gap: 16rpx;
}
.home-section-title-bar {
width: 2vw;
min-width: 12rpx;
max-width: 16rpx;
height: 4vh;
max-height: 48rpx;
background: #2c2c2c;
}
.home-section-title-bar-red {
background: #8b2323;
}
.home-section-title {
color: #2c2c2c;
font-weight: bold;
letter-spacing: 0.2em;
font-size: 30rpx;
}
.home-section-subtitle {
font-size: 12px;
color: #8a8a8a;
letter-spacing: 0.1em;
}
.home-section-header-simple {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 8rpx;
}
/* Video */
.home-video-card {
position: relative;
width: 100%;
aspect-ratio: 16 / 9;
background: #2c2c2c;
overflow: hidden;
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
border: 2px solid #fff;
outline: 1px solid #dcd3c9;
}
.home-video-cover {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
z-index: 1;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}
.home-video-bg {
position: absolute;
inset: 0;
background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
z-index: 1;
}
.home-video-overlay {
position: absolute;
inset: 0;
background: linear-gradient(to top, rgba(0, 0, 0, 0.7), transparent 50%, transparent);
z-index: 2;
pointer-events: none;
}
.home-video-info-overlay {
position: absolute;
left: 24rpx;
bottom: 24rpx;
right: 140rpx;
z-index: 10;
}
.home-video-title-overlay {
display: block;
font-size: 28rpx;
font-weight: bold;
color: #fff;
letter-spacing: 0.1em;
margin-bottom: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.home-video-desc-overlay {
display: block;
font-size: 22rpx;
color: rgba(255, 255, 255, 0.7);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.home-video-info-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
padding: 0 8rpx;
}
.home-video-info {
flex: 1;
}
.home-video-badge {
display: inline-block;
padding: 4rpx 16rpx;
background: #8b2323;
color: #fdfbf7;
font-size: 11px;
margin-bottom: 16rpx;
letter-spacing: 0.1em;
}
.home-video-title {
color: white;
font-weight: bold;
letter-spacing: 0.1em;
font-size: 18rpx;
margin-bottom: 8rpx;
display: block;
}
.home-video-desc {
color: rgba(255, 255, 255, 0.6);
font-size: 13px;
font-weight: 300;
letter-spacing: 0.05em;
display: block;
}
.home-video-info-spacer {
width: 32rpx;
height: 32rpx;
}
.home-video-play {
width: 12vw;
min-width: 96rpx;
max-width: 120rpx;
aspect-ratio: 1;
border-radius: 50%;
border: 1px solid rgba(255, 255, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(8px);
position: absolute;
right: 24rpx;
bottom: 24rpx;
z-index: 10;
}
.home-video-play-icon {
color: white;
margin-left: 4rpx;
}
/* Names */
.home-names-scroll {
width: 100%;
white-space: nowrap;
}
.home-names-scroll-content {
display: flex;
flex-direction: row;
padding-left: 8rpx;
white-space: nowrap;
width: max-content;
min-width: 100%;
}
.home-name-card {
width: 200rpx;
flex-shrink: 0;
aspect-ratio: 0.64;
background: #fffdf9;
border: 1px solid #eaddcf;
padding: 4vw;
position: relative;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
align-items: center;
justify-content: space-between;
}
@media (max-width: 750rpx) {
.home-name-card {
padding: 24rpx;
}
}
@media (min-width: 750rpx) {
.home-name-card {
padding: 32rpx;
}
}
.home-name-card-divider {
width: 100%;
height: 2rpx;
background: #eaddcf;
margin-bottom: 16rpx;
}
.home-name-card-content {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
padding: 2vh 0;
min-width: 0;
}
.home-name-card-name {
font-size: 40rpx;
font-weight: bold;
color: #2c2c2c;
font-family: SimSun, "Songti SC", "Songti TC", "Noto Serif SC", STSong, serif;
letter-spacing: 0.3em;
border-right: 1px solid rgba(139, 35, 35, 0.2);
padding-right: 24rpx;
margin-right: 24rpx;
}
.home-name-card-source {
font-size: 12px;
color: #8a8a8a;
letter-spacing: 0.1em;
flex: 1;
display: flex;
justify-content: center;
position: relative;
bottom: 20px;
}
.home-name-card-footer {
width: 100%;
text-align: center;
border-top: 1px solid #eaddcf;
padding-top: 24rpx;
overflow: hidden;
}
.home-name-card-desc {
font-size: 11px;
color: #5a5a5a;
line-height: 1.6;
opacity: 0.8;
display: -webkit-box;
-webkit-box-orient: vertical;
line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
white-space: normal;
}
/* Book Style */
.home-book {
display: flex;
margin-top: -48rpx;
background: #f5f0e6;
border-radius: 8rpx;
box-shadow:
0 2px 4px rgba(0, 0, 0, 0.1),
0 8px 16px rgba(0, 0, 0, 0.1),
inset 0 0 30px rgba(0, 0, 0, 0.03);
position: relative;
overflow: hidden;
}
.home-book-spine {
position: absolute;
left: 50%;
top: 0;
bottom: 0;
width: 8rpx;
transform: translateX(-50%);
background: linear-gradient(90deg,
rgba(0, 0, 0, 0.08) 0%,
rgba(0, 0, 0, 0.15) 50%,
rgba(0, 0, 0, 0.08) 100%);
z-index: 10;
}
.home-book-page {
flex: 1;
padding: 48rpx 32rpx;
background: linear-gradient(to right, #fffdf9, #faf7f0);
position: relative;
}
.home-book-page-left {
border-right: none;
background: linear-gradient(to left, #f8f5ed, #fffdf9);
box-shadow: inset -8px 0 15px -10px rgba(0, 0, 0, 0.1);
}
.home-book-page-right {
background: linear-gradient(to right, #f8f5ed, #fffdf9);
box-shadow: inset 8px 0 15px -10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
justify-content: space-between;
}
.home-book-header {
text-align: center;
margin-bottom: 32rpx;
}
.home-book-quote {
display: block;
font-size: 48rpx;
color: #8b2323;
opacity: 0.6;
line-height: 1;
margin-bottom: 16rpx;
}
.home-book-title {
font-size: 28rpx;
font-weight: bold;
color: #2c2c2c;
letter-spacing: 0.4em;
}
.home-book-text {
font-size: 13px;
color: #5a5a5a;
line-height: 2;
text-align: justify;
}
.home-book-drop {
float: left;
font-size: 36rpx;
font-weight: bold;
color: #8b2323;
line-height: 1;
margin-right: 8rpx;
margin-top: 4rpx;
}
.home-book-footer {
display: flex;
justify-content: flex-end;
align-items: center;
gap: 16rpx;
margin-top: 32rpx;
}
.home-book-date {
font-size: 11px;
color: #8a8a8a;
letter-spacing: 0.1em;
}
.home-book-seal {
width: 64rpx;
height: 64rpx;
background: #8b2323;
color: #fdfbf7;
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: bold;
border-radius: 4rpx;
}
.fade-slide {
animation: fadeSlide 8s ease;
}
@keyframes fadeSlide {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 视频播放器弹窗 */
.video-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.9);
z-index: 9999;
display: flex;
align-items: center;
justify-content: center;
padding: 32rpx;
}
.video-modal-content {
width: 100%;
max-width: 1200rpx;
background: #1a1a2e;
border-radius: 24rpx;
overflow: hidden;
border: 2rpx solid rgba(212, 175, 55, 0.3);
}
.video-modal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32rpx;
border-bottom: 2rpx solid rgba(255, 255, 255, 0.1);
}
.video-modal-title {
font-size: 32rpx;
font-weight: bold;
color: #d4af37;
font-family: SimSun, "Songti SC", serif;
letter-spacing: 0.2em;
}
.video-modal-close {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: rgba(255, 255, 255, 0.1);
cursor: pointer;
transition: all 0.3s;
}
.video-modal-close:hover {
background: rgba(255, 255, 255, 0.2);
}
.video-modal-close-icon {
font-size: 32rpx;
color: #e2e2e2;
}
.video-modal-body {
padding: 32rpx;
background: #000;
}
.video-player {
width: 100%;
height: 600rpx;
background: #000;
}
</style>