buddhism/pages/index/index.vue
2025-11-25 16:16:54 +08:00

841 lines
20 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="page">
<!-- 加载状态指示器 -->
<view v-if="loading" class="loading-overlay">
<view class="loading-content">
<text>正在加载配置...</text>
</view>
</view>
<!-- 背景图 -->
<image
:src="pageConfig.background.img"
class="bj"
mode="aspectFill"
></image>
<!-- 音频与VR图标 -->
<view class="tubiao">
<view class="audio-controls">
<image
v-if="isAudioPlaying"
:src="pageConfig.topIcons.leftIcon.img"
mode=""
@click="toggleAudio"
></image>
<image
v-if="!isAudioPlaying"
:src="pageConfig.topIcons.rightIcon.img"
mode=""
@click="toggleAudio"
></image>
</view>
<view class="tubiao-item">
<!-- <view class="tubiao-item" @click="goToVR">-->
<!-- <image :src="pageConfig.topIcons.bottomIcon.img" mode=""></image>-->
</view>
</view>
<!-- 底部 -->
<view class="bot">
<view class="name">
<image :src="bottomSection.openingTime.decorationImg" mode=""></image>
<text>{{ bottomSection.openingTime.title }}</text>
<image :src="bottomSection.openingTime.decorationImg" mode=""></image>
</view>
<view class="time">
{{ templeData.startTime }} - {{ templeData.endTime }}
</view>
<!-- 公告 -->
<view class="announcement">
<image :src="pageConfig.announcement.icon" class="ggimg" mode="" />
<view class="marquee-wrap">
<view
:style="{ transform: `translateX(${marqueeX}rpx)` }"
class="marquee"
>
{{ templeData.bulletinContent }}
</view>
</view>
</view>
<view class="hua">
<image
mode=""
src="https://api.ccttiot.com/smartmeter/img/static/uyz1LDPTjPqeOzBMjLZ7"
></image>
</view>
<view class="list-scroll">
<view class="list">
<!-- 导航项目列表 -->
<view
v-for="(item, index) in navigationItems"
:key="index"
class="li"
@click="navigateToPage(item, index)"
>
<image :src="item.img" mode=""></image>
<view class="da">
{{ item.title }}
</view>
<view class="xiao">
{{ item.subtitle }}
</view>
</view>
<!-- 空状态提示 -->
<view
v-if="!loading && navigationItems.length === 0"
class="empty-state"
>
<text>暂无功能项目</text>
</view>
</view>
</view>
<view
v-if="
bottomSection.prayerSection &&
bottomSection.prayerSection.backgroundImg
"
class="bottom"
>
<image
:src="bottomSection.prayerSection.backgroundImg"
mode=""
@click="goToPersonalCenter"
></image>
<!-- <view class="rixing">-->
<!-- {{ bottomSection.prayerSection.title }}-->
<!-- </view>-->
<view class="qifu" @click="goToPray">
<image
:src="bottomSection.prayerSection.prayerButton.icon"
mode=""
></image>
<view class="zaixian">
<view class="da">
{{ bottomSection.prayerSection.prayerButton.title }}
</view>
<view class="xiao">
{{ bottomSection.prayerSection.prayerButton.subtitle }}
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
import { navigateToPage } from "../../utils/router.js";
import { getHomeConfig, getTempleIndex } from "../../api/index/index.js";
import { getArticleById } from "../../api/article/article.js";
export default {
data() {
return {
marqueeX: "", // 初始位置和marquee-wrap宽度一致
timer: null,
loading: true, // 默认为true等待API数据加载
// 页面配置数据完全依赖API
pageConfig: {
login: { img: "" },
background: { img: "" },
announcement: { icon: "", text: "" },
topIcons: {
leftIcon: { hidden: false, img: "" },
rightIcon: { img: "" },
bottomIcon: { img: "" },
},
},
navigationItems: [],
bottomSection: {
openingTime: {
title: "",
time: "",
decorationImg: "",
},
},
templeData: {
imgUrl: "",
bulletinContent: "",
startTime: "",
endTime: "",
audioUrl: "",
},
isAudioPlaying: false, // 改为默认开启
audioContext: null,
};
},
onLoad() {
// 页面加载时获取配置
this.loadHomeConfig();
// 获取寺庙数据
this.loadTempleData();
},
// 添加下拉刷新支持
onPullDownRefresh() {
console.log("用户触发下拉刷新");
Promise.all([this.loadHomeConfig(), this.loadTempleData()]).finally(() => {
uni.stopPullDownRefresh();
});
},
onShow() {
// 启动跑马灯(方法内部会检查是否有文本)
this.startMarquee();
// this.playAudio();
},
onHide() {
// 页面隐藏时停止音频播放,确保只在当前页面播放
this.stopAudio();
},
onUnload() {
this.stopMarquee();
// 停止音频播放
this.stopAudio();
},
methods: {
/**
* 获取首页配置
*/
async loadHomeConfig() {
console.log("开始获取首页配置...");
this.loading = true;
// 重试机制
const maxRetries = 3;
let retryCount = 0;
while (retryCount < maxRetries) {
try {
console.log(`第 ${retryCount + 1} 次尝试获取配置...`);
const response = await getHomeConfig();
console.log("API响应:", response);
// 简化验证:只检查关键字段
if (response?.code === 200 && response?.data?.[0]?.body) {
const parsedConfig = JSON.parse(response.data[0].body);
// 更新页面配置
this.updatePageConfig(parsedConfig);
// 重新启动跑马灯
this.stopMarquee();
this.startMarquee();
console.log("配置加载成功");
break;
} else {
throw new Error("API响应数据无效");
}
} catch (error) {
retryCount++;
console.error(`第 ${retryCount} 次尝试失败:`, error.message);
if (retryCount >= maxRetries) {
console.error("所有重试都失败了");
this.useDefaultConfig();
break;
} else {
await new Promise((resolve) =>
setTimeout(resolve, retryCount * 1000),
);
}
}
}
this.loading = false;
console.log("配置加载完成");
},
/**
* 处理配置加载失败
*/
useDefaultConfig() {
console.log("API获取失败无法加载配置");
// 不设置任何默认配置,保持初始化的空状态
// 数据保持为初始化时的空结构
// 显示提示信息
uni.showModal({
title: "加载失败",
content: "无法获取页面配置,请检查网络连接后重试",
showCancel: true,
cancelText: "取消",
confirmText: "重试",
success: (res) => {
if (res.confirm) {
this.loadHomeConfig();
}
},
});
},
/**
* 更新页面配置
*/
updatePageConfig(parsedConfig) {
// 直接更新配置使用默认值防止undefined
this.pageConfig = parsedConfig.pageConfig || this.pageConfig;
this.navigationItems = parsedConfig.navigationItems || [];
this.bottomSection = parsedConfig.bottomSection || this.bottomSection;
console.log("配置更新完成");
console.log("pageConfig.login.img", this.pageConfig.login.img);
uni.setStorageSync("loginImg", this.pageConfig.login.img);
},
/**
* 获取寺庙数据
*/
async loadTempleData() {
try {
console.log("开始获取寺庙数据...");
const response = await getTempleIndex();
console.log("寺庙数据API响应:", response);
if (response?.code === 200 && response?.data) {
this.templeData = {
imgUrl: response.data.imgUrl || "",
bulletinContent: response.data.bulletinContent || "",
startTime: response.data.startTime || "",
endTime: response.data.endTime || "",
audioUrl: response.data.audioUrl || "",
};
uni.setStorageSync("abbotId", response.data.abbotId);
uni.setStorageSync("templeId", response.data.id);
// 重新启动跑马灯
this.stopMarquee();
this.startMarquee();
// 自动播放音频
if (this.templeData.audioUrl) {
this.playAudio();
}
console.log("寺庙数据加载成功");
} else {
throw new Error("寺庙数据API响应无效");
}
} catch (error) {
console.error("获取寺庙数据失败:", error);
uni.showToast({
title: "获取寺庙数据失败",
icon: "none",
});
}
},
startMarquee() {
// 简单检查公告文本是否存在
const announcementText =
this.templeData?.bulletinContent || this.pageConfig?.announcement?.text;
if (!announcementText) {
return;
}
// 停止之前的定时器
this.stopMarquee();
// 估算文字宽度每个字24rpx
const textWidth = announcementText.length * 24;
this.timer = setInterval(() => {
this.marqueeX--;
if (this.marqueeX < -textWidth) {
this.marqueeX = 600;
}
}, 16);
},
stopMarquee() {
if (this.timer) {
clearInterval(this.timer);
this.timer = null;
}
},
// 兼容两种跳转方式page路由跳转 或 aid文章详情跳转
async navigateToPage(item, index) {
try {
console.log("导航项信息:", item);
console.log("导航项索引:", index);
// 优先使用page字段进行路由跳转
if (item.page) {
console.log("使用page字段进行路由跳转:", item.page);
// 导入路由工具
const { navigateToPage: routerNavigate } = await import(
"../../utils/router.js"
);
routerNavigate(item.page);
return;
}
// 如果没有page字段则使用aid进行文章详情跳转
if (item.aid) {
console.log("使用aid字段获取文章详情:", item.aid);
// 显示加载提示
uni.showLoading({
title: "加载中...",
mask: true,
});
// 调用API获取文章详情
const response = await getArticleById(item.aid);
// 隐藏加载提示
uni.hideLoading();
if (response.code === 200 && response.data) {
console.log("获取文章详情成功:", response.data);
// 将文章数据存储到本地,供目标页面使用
uni.setStorageSync("currentArticle", response.data);
// 直接跳转到文章详情页面
uni.navigateTo({
url: "/pages/article/article-detail",
fail: (err) => {
console.error("跳转到文章详情页面失败:", err);
uni.showToast({
title: "页面跳转失败",
icon: "none",
});
},
});
} else {
console.error("获取文章详情失败:", response);
uni.showToast({
title: "获取内容失败",
icon: "none",
});
}
} else {
console.error("导航项既没有page字段也没有aid字段:", item);
uni.showToast({
title: "配置错误",
icon: "none",
});
}
} catch (error) {
// 隐藏加载提示
uni.hideLoading();
console.error("导航跳转出错:", error);
uni.showToast({
title: "网络错误",
icon: "none",
});
}
},
/**
* 跳转到祈福页面
*/
goToPray() {
try {
console.log("跳转到祈福页面");
// 使用navigateToPage方法传入参数pray
navigateToPage("pray");
} catch (error) {
console.error("跳转到祈福页面失败:", error);
uni.showToast({
title: "页面跳转失败",
icon: "none",
});
}
},
goToPersonalCenter() {
try {
console.log("跳转到个人中心页面");
navigateToPage("pc");
} catch (error) {
console.error("跳转到祈福页面失败:", error);
uni.showToast({
title: "页面跳转失败",
icon: "none",
});
}
},
goToVR() {
let id = "15";
uni.navigateTo({
url: "/page_user/webViewVR/webViewVR?id=" + id,
});
},
/**
* 切换音频播放状态
*/
toggleAudio() {
if (this.isAudioPlaying) {
this.stopAudio();
} else {
this.playAudio();
}
},
/**
* 播放音频 - 实现单例模式
*/
playAudio() {
if (!this.templeData.audioUrl) {
// uni.showToast({
// title: "暂无音频资源",
// icon: "none",
// });
return;
}
try {
// 确保全局只有一个音频实例在播放(单例模式)
// 如果当前页面已有音频实例,先停止
if (this.audioContext) {
this.stopAudio();
}
// 创建新的音频上下文实例
this.audioContext = uni.createInnerAudioContext();
this.audioContext.src = this.templeData.audioUrl;
this.audioContext.loop = true; // 循环播放
// 监听播放状态
this.audioContext.onPlay(() => {
console.log("音频开始播放");
this.isAudioPlaying = true;
});
this.audioContext.onError((err) => {
console.error("音频播放错误:", err);
this.isAudioPlaying = false;
uni.showToast({
title: "音频播放失败",
icon: "none",
});
});
this.audioContext.onEnded(() => {
console.log("音频播放结束");
this.isAudioPlaying = false;
});
// 开始播放
this.audioContext.play();
} catch (error) {
console.error("音频播放失败:", error);
this.isAudioPlaying = false;
uni.showToast({
title: "音频播放失败",
icon: "none",
});
}
},
/**
* 停止音频 - 确保资源正确释放
*/
stopAudio() {
if (this.audioContext) {
try {
this.audioContext.stop();
this.audioContext.destroy();
} catch (error) {
console.error("停止音频时出错:", error);
} finally {
this.audioContext = null;
this.isAudioPlaying = false;
}
}
},
},
};
</script>
<style lang="scss">
page {
background-color: #fff;
}
.bot {
position: fixed;
left: 50%;
transform: translateX(-50%);
bottom: 56rpx;
width: 100%;
max-width: 750rpx;
/* 确保不会限制子元素的滚动 */
overflow: visible;
.bottom {
margin-top: 64rpx;
display: flex;
padding: 0 118rpx;
box-sizing: border-box;
justify-content: center;
gap: 20rpx;
image {
width: 64rpx;
height: 64rpx;
}
.rixing {
width: 206rpx;
height: 64rpx;
background: #522510;
border-radius: 45rpx 45rpx 45rpx 45rpx;
text-align: center;
line-height: 64rpx;
font-weight: 600;
font-size: 24rpx;
color: #ffffff;
border-radius: 50rpx;
margin-left: 26rpx;
margin-right: 26rpx;
}
.qifu {
width: 194rpx;
height: 64rpx;
border-radius: 41rpx 41rpx 41rpx 41rpx;
border: 1rpx solid #522510;
display: flex;
align-items: center;
justify-content: center;
image {
width: 32rpx;
height: 32rpx;
}
.zaixian {
margin-left: 12rpx;
.da {
font-size: 24rpx;
color: #522510;
font-weight: 600;
}
.xiao {
font-size: 12rpx;
color: #522510;
}
}
}
}
.list-scroll {
width: 100%;
margin-top: 20rpx;
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
/* 确保滚动容器不被父元素限制 */
position: relative;
z-index: 1;
/* 隐藏滚动条 */
scrollbar-width: none;
-ms-overflow-style: none;
&::-webkit-scrollbar {
display: none;
}
}
.list {
display: flex;
width: 1400rpx; /* 更大的宽度确保滚动 */
padding: 0 20rpx;
box-sizing: border-box;
/* 确保内容不被压缩 */
flex-shrink: 0;
.li {
width: 150rpx;
flex: 0 0 150rpx;
text-align: center;
margin-right: 20rpx;
image {
width: 56rpx;
height: 56rpx;
}
.da {
font-size: 24rpx;
color: #522510;
margin-top: 8rpx;
}
.xiao {
font-size: 12rpx;
color: #522510;
}
}
.empty-state {
width: 100%;
text-align: center;
padding: 40rpx;
color: #999;
font-size: 28rpx;
}
}
.hua {
width: 100%;
text-align: center;
//margin-top: 30rpx;
image {
width: 656rpx;
height: 108rpx;
}
}
.time {
font-weight: 600;
font-size: 32rpx;
color: #522510;
margin-top: 8rpx;
width: 100%;
text-align: center;
}
.name {
display: flex;
align-items: center;
width: 100%;
justify-content: center;
text {
font-weight: 600;
font-size: 32rpx;
color: #522510;
margin-left: 24rpx;
margin-right: 24rpx;
}
image {
width: 12rpx;
height: 12rpx;
}
}
}
.tubiao {
margin-top: 184rpx;
width: 100%;
display: flex;
padding: 0 40rpx;
box-sizing: border-box;
justify-content: space-between;
.audio-controls {
display: flex;
align-items: center;
gap: 26rpx;
}
image {
width: 82rpx;
height: 82rpx;
display: block;
margin-top: 50rpx;
transition: all 0.3s ease;
&:active {
transform: scale(0.9);
opacity: 0.8;
}
}
}
@keyframes pulse {
0% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
.announcement {
width: 100%;
//margin-top: 184rpx;
padding: 20rpx 48rpx;
box-sizing: border-box;
display: flex;
align-items: center;
white-space: nowrap;
.ggimg {
width: 32rpx;
height: 32rpx;
margin-right: 14rpx;
}
.marquee-wrap {
width: 600rpx;
overflow: hidden;
position: relative;
height: 32rpx;
display: flex;
align-items: center;
}
.marquee {
white-space: nowrap;
position: absolute;
left: 0;
top: 0;
font-size: 24rpx;
color: #522510;
}
}
.bj {
width: 100%;
height: 100vh;
position: fixed;
top: 0;
left: 0;
z-index: -1;
}
/* 加载状态指示器样式 */
.loading-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
}
.loading-content {
background-color: rgba(255, 255, 255, 0.9);
padding: 40rpx 60rpx;
border-radius: 20rpx;
text-align: center;
}
.loading-content text {
font-size: 28rpx;
color: #333;
}
</style>