consumer-app/pages/detail/richTextDetail.vue

349 lines
8.9 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<view class="rich-text-detail-page">
<!-- 顶部导航栏 -->
<NavHeader title="详情" />
<!-- 内容区域 -->
<scroll-view class="content-scroll" scroll-y="true">
<!-- 加载中 -->
<view class="loading-state" v-if="loading">
<text class="loading-text">加载中...</text>
</view>
<!-- 富文本内容 - 混合渲染 -->
<view
class="content-wrapper"
v-else-if="parsedContent && parsedContent.length > 0"
>
<block v-for="(item, index) in parsedContent" :key="index">
<!-- 使 image -->
<view v-if="item.type === 'image'" class="rich-text-image-wrapper">
<image
class="rich-text-image"
:src="item.src"
mode="widthFix"
:lazy-load="true"
></image>
</view>
<!-- 如果是文本内容,使用 rich-text -->
<rich-text
v-else-if="item.type === 'text'"
:nodes="item.html"
class="rich-text-content"
></rich-text>
</block>
</view>
<!-- 空数据提示 -->
<view class="empty-state" v-else>
<image
class="empty-icon"
src="/static/home/entry_icon.png"
mode="aspectFit"
></image>
<text class="empty-text"></text>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<DetailActionBar
v-if="detailData"
:liked="isLiked"
:collected="isCollected"
:id="detailId"
:zanType="noticeType"
:type="noticeType"
/>
</view>
</template>
<script>
import { getGuildDetail } from "@/api/home.js";
import NavHeader from "@/components/NavHeader/NavHeader.vue";
import DetailActionBar from "@/components/DetailActionBar/DetailActionBar.vue";
export default {
components: {
NavHeader,
DetailActionBar,
},
data() {
return {
detailId: null, // 详情ID用于通过接口获取数据
title: "",
content: "",
parsedContent: [], // 解析后的内容数组
loading: false,
isLiked: false, // 是否已点赞
isCollected: false, // 是否已收藏
detailData: null, // 详情数据(通过接口获取时使用)
noticeType: null, // 内容类型
};
},
onLoad(options) {
// 支持两种模式:
// 1. 通过 id 调用接口获取详情(原 activitiesDetail 的用法)
// 2. 直接传入 title 和 content原 richTextDetail 的用法)
if (options.id) {
this.detailId = options.id;
this.loadDetailById();
} else if (options.content) {
// 直接传入内容模式
if (options.title) {
this.title = decodeURIComponent(options.title);
}
let content = decodeURIComponent(options.content);
// 解析 HTML分离图片和文本
this.parsedContent = this.parseHtmlContent(content);
if (options.info) {
// info 从上个页面传过来是 encodeURIComponent(JSON.stringify(obj)),这里要 decode + JSON.parse
try {
const parsed = JSON.parse(decodeURIComponent(options.info));
this.detailData = parsed;
this.detailId = parsed.id || this.detailId;
// 兼容字段noticeType/messageType/type
this.noticeType = parsed.noticeType ?? parsed.messageType ?? parsed.type ?? this.noticeType;
// 如果没传 title则用 info 里的 title
if (!this.title && parsed.title) {
this.title = parsed.title;
}
} catch (e) {
console.error('解析 info 失败:', e, options.info);
}
}
}
},
// 分享给朋友(微信小程序)
onShareAppMessage() {
if (!this.detailData) {
return {
title: this.title || "详情",
path: this.detailId
? `/pages/detail/richTextDetail?id=${this.detailId}`
: "",
};
}
return {
title: this.detailData.title || this.title || "详情",
path: `/pages/detail/richTextDetail?id=${this.detailId}`,
imageUrl: this.detailData.coverUrl || "",
};
},
// 分享到朋友圈(微信小程序)
// #ifdef MP-WEIXIN
onShareTimeline() {
if (!this.detailData) {
return {
title: this.title || "详情",
query: this.detailId ? `id=${this.detailId}` : "",
};
}
return {
title: this.detailData.title || this.title || "详情",
query: `id=${this.detailId}`,
imageUrl: this.detailData.coverUrl || "",
};
},
// #endif
methods: {
// 通过 ID 加载详情(原 activitiesDetail 的功能)
async loadDetailById() {
if (!this.detailId) {
return;
}
try {
this.loading = true;
const res = await getGuildDetail(this.detailId);
if (res) {
this.detailData = res;
this.noticeType = res.noticeType;
this.title = res.title || "详情";
// 解析 HTML分离图片和文本
if (res.content) {
this.parsedContent = this.parseHtmlContent(res.content);
}
// 设置点赞和收藏状态(如果接口返回了这些字段)
if (res.isLiked !== undefined) {
this.isLiked = res.isLiked;
}
if (res.isCollected !== undefined) {
this.isCollected = res.isCollected;
}
}
} catch (error) {
console.error("加载详情失败:", error);
uni.showToast({
title: "加载失败,请重试",
icon: "none",
});
} finally {
this.loading = false;
}
},
// 解析 HTML 内容,将图片和文本分离
parseHtmlContent(html) {
if (!html) return [];
const result = [];
let currentIndex = 0;
// 匹配所有 img 标签
const imgRegex = /<img([^>]*)>/gi;
let match;
let lastIndex = 0;
while ((match = imgRegex.exec(html)) !== null) {
// 添加图片之前的文本内容
if (match.index > lastIndex) {
const textContent = html.substring(lastIndex, match.index);
if (textContent.trim()) {
result.push({
type: "text",
html: textContent,
});
}
}
// 提取图片 src
const imgAttrs = match[1];
const srcMatch = imgAttrs.match(/src=["']([^"']+)["']/i);
if (srcMatch && srcMatch[1]) {
result.push({
type: "image",
src: srcMatch[1],
});
}
lastIndex = match.index + match[0].length;
}
// 添加最后剩余的文本内容
if (lastIndex < html.length) {
const textContent = html.substring(lastIndex);
if (textContent.trim()) {
result.push({
type: "text",
html: textContent,
});
}
}
// 如果没有匹配到图片,直接返回整个内容作为文本
if (result.length === 0 && html.trim()) {
result.push({
type: "text",
html: html,
});
}
return result;
},
},
};
</script>
<style lang="scss" scoped>
.rich-text-detail-page {
height: 100vh;
background-color: #e2e8f1;
display: flex;
flex-direction: column;
overflow: hidden;
padding-bottom: 120rpx; // 为底部操作栏留出空间
}
/* 内容区域 */
.content-scroll {
flex: 1;
height: 0; // 配合 flex: 1 使用,让 scroll-view 可以滚动
}
.content-wrapper {
padding: 30rpx 20rpx;
background-color: #ffffff;
margin: 20rpx;
border-radius: 20rpx;
min-height: 200rpx;
box-sizing: border-box;
overflow-x: hidden;
overflow-y: visible;
word-wrap: break-word;
word-break: break-all;
// 图片容器样式
.rich-text-image-wrapper {
width: 100%;
margin: 20rpx 0;
display: flex;
justify-content: center;
align-items: center;
box-sizing: border-box;
.rich-text-image {
width: 100%;
height: auto;
display: block;
max-width: 100%;
}
}
// 文本内容样式
.rich-text-content {
width: 100%;
display: block;
font-family: PingFang-SC, PingFang-SC;
font-size: 28rpx;
line-height: 1.8;
color: #333333;
word-wrap: break-word;
word-break: break-all;
box-sizing: border-box;
}
}
/* 加载状态 */
.loading-state {
display: flex;
justify-content: center;
align-items: center;
padding: 200rpx 0;
min-height: 500rpx;
.loading-text {
font-family: PingFang-SC, PingFang-SC;
font-weight: 500;
font-size: 28rpx;
color: #999999;
}
}
/* 空数据提示 */
.empty-state {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 200rpx 0;
min-height: 500rpx;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: 40rpx;
opacity: 0.5;
}
.empty-text {
font-family: PingFang-SC, PingFang-SC;
font-weight: 500;
font-size: 28rpx;
color: #999999;
}
}
</style>