ykt-wxapp/pages/message/article-detail.vue
2026-05-28 16:37:57 +08:00

248 lines
6.1 KiB
Vue

<template>
<full-page :customScroll="true" pageStyle="background:#fff">
<view v-if="loading" class="loading-container">
<uni-icons type="spinner-cycle" size="40" color="#999" />
<text class="loading-text">加载中...</text>
</view>
<view v-else-if="error" class="error-container">
<text class="error-text">{{ error }}</text>
<button class="retry-btn" @click="loadArticle">重试</button>
</view>
<view v-else class="article-content">
<view class="article-header">
<text class="article-title">{{ articleData.title }}</text>
<text class="article-date">{{ articleData.date }}</text>
</view>
<view class="article-body">
<view class="rich-text-wrapper">
<rich-text :nodes="articleData.content"></rich-text>
</view>
</view>
</view>
<template #footer>
<button-footer :showConfirm="showStar" cancelText="关闭" @cancel="back()">
<template #confirm>
<view class="flex justify-center items-center h-full border-primary rounded bg-primary" @click="star()">
<uni-icons class="flex-shrinl-0" :type="article.star ? 'star-filled' : 'star'" color="#FFD700"
size="20"></uni-icons>
<view class="text-base text-white">{{ article.star ? '已收藏' : '收藏' }}</view>
</view>
</template>
</button-footer>
</template>
</full-page>
</template>
<script setup>
import { computed } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { storeToRefs } from "pinia";
import fullPage from '@/components/full-page.vue';
import buttonFooter from '@/components/button-footer.vue';
import useAccountStore from "@/store/account.js";
import useGuard from "@/hooks/useGuard.js";
import api from "@/utils/api.js";
import { toast, loading as showLoading, hideLoading } from "@/utils/widget";
import { ref } from "vue";
const env = __VITE_ENV__;
const corpId = env.MP_CORP_ID;
const loading = ref(true);
const error = ref("");
const articleData = ref({
title: "",
content: "",
date: "",
});
let articleId = "";
const article = ref({});
const accountStore = useAccountStore();
const { doctorInfo, account } = storeToRefs(accountStore);
const userId = computed(() => doctorInfo.value?.userid || "");
const { useLoad } = useGuard()
const showStar = computed(() => {
const isMine = article.value && article.value.creatorSignature && article.value.creatorSignature.person && article.value.creatorSignature.person.userId === userId.value;
return userId.value && !isMine;
})
// 处理富文本内容,使图片自适应
const processRichTextContent = (html) => {
if (!html) return "";
// 给所有 img 标签添加样式
let processedHtml = html.replace(
/<img/gi,
'<img style="max-width:100%;height:auto;display:block;margin:10px 0;"'
);
// 移除可能存在的固定宽度样式
processedHtml = processedHtml.replace(
/style="[^"]*width:\s*\d+px[^"]*"/gi,
(match) => {
return match.replace(/width:\s*\d+px;?/gi, "max-width:100%;");
}
);
// 处理表格,添加自适应样式
processedHtml = processedHtml.replace(
/<table/gi,
'<table style="max-width:100%;overflow-x:auto;display:block;"'
);
// 给整体内容添加容器样式
processedHtml = `<div style="width:100%;overflow-x:hidden;word-wrap:break-word;word-break:break-all;">${processedHtml}</div>`;
return processedHtml;
};
// 加载文章
const loadArticle = async () => {
loading.value = true;
error.value = "";
try {
const res = await api("getArticle", { id: articleId, corpId, userId: userId.value });
if (res.success && res.data) {
// 格式化日期
let date = "";
if (res.data.createTime) {
const d = new Date(res.data.createTime);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, "0");
const day = String(d.getDate()).padStart(2, "0");
date = `${year}-${month}-${day}`;
}
article.value = res.data;
articleData.value = {
title: res.data.title || "宣教文章",
content: processRichTextContent(res.data.content || ""),
date: date,
};
} else {
error.value = res.message || "加载文章失败";
}
} catch (err) {
console.error("加载文章失败:", err);
error.value = "加载失败,请重试";
} finally {
loading.value = false;
}
};
async function star() {
showLoading();
const res = await api("starArticle", { articleId: articleId, userId: userId.value, star: !article.value.star });
hideLoading();
if (res.success) {
uni.$emit('changeArticleStar', article.value._id);
loadArticle()
} else {
toast(res.message || "操作失败");
}
}
function back() {
uni.navigateBack();
}
useLoad((options) => {
if (options.id) {
articleId = options.id;
loadArticle();
} else {
error.value = "文章信息不完整";
loading.value = false;
}
});
</script>
<style scoped lang="scss">
.ml-15 {
margin-left: 30rpx;
}
.loading-container,
.error-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100vh;
padding: 40rpx;
}
.loading-text {
margin-top: 20rpx;
font-size: 30rpx;
color: #999;
}
.error-text {
font-size: 30rpx;
color: #999;
margin-bottom: 30rpx;
text-align: center;
}
.retry-btn {
padding: 16rpx 60rpx;
background-color: #0877f1;
color: #fff;
border: none;
border-radius: 8rpx;
font-size: 30rpx;
}
.article-content {
height: 100vh;
}
.article-header {
padding: 40rpx 30rpx 20rpx;
border-bottom: 1px solid #eee;
}
.article-title {
display: block;
font-size: 38rpx;
font-weight: bold;
color: #333;
line-height: 1.6;
margin-bottom: 20rpx;
}
.article-date {
display: block;
font-size: 26rpx;
color: #999;
}
.article-body {
padding: 0;
}
.rich-text-wrapper {
padding: 30rpx;
width: 100%;
box-sizing: border-box;
overflow: hidden;
}
.rich-text-wrapper ::v-deep rich-text {
width: 100%;
}
.rich-text-wrapper ::v-deep rich-text img {
max-width: 100% !important;
height: auto !important;
display: block;
}
</style>