<template>
<view class="detail-container">
<view class="detail-title">{{ detail.title }}</view>
<view class="detail-date">发布日期:{{ formatDate(detail.publishDate) }}</view>
<!-- 使用处理后的富文本内容 -->
<rich-text
class="detail-content"
:nodes="parseContent(detail.content)"
/>
<!-- 返回顶部按钮 -->
<view
class="back-to-top"
:class="{ show: showBackTop }"
@click="scrollToTop"
>
<uni-icons type="arrow-up" size="24" color="#fff"></uni-icons>
</view>
</view>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { onLoad } from '@dcloudio/uni-app';
const detail = ref({
title: '',
publishDate: '',
content: ''
});
// 控制返回顶部按钮显示
const showBackTop = ref(false);
onLoad((options) => {
if (options && options.articleId) {
fetchDetail(options.articleId);
} else {
uni.showToast({ title: '缺少文章ID', icon: 'error' });
}
});
// 添加页面滚动监听
onMounted(() => {
window.addEventListener('scroll', handleScroll);
});
// 移除滚动监听
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll);
});
// 处理滚动事件 - 控制返回顶部按钮显示
const handleScroll = () => {
// 获取滚动位置
const scrollTop = window.pageYOffset ||
document.documentElement.scrollTop ||
document.body.scrollTop;
// 当滚动超过300px时显示按钮
showBackTop.value = scrollTop > 300;
};
// 平滑滚动到顶部
const scrollToTop = () => {
uni.pageScrollTo({
scrollTop: 0,
duration: 300
});
};
// 格式化日期
const formatDate = (dateStr) => {
if (!dateStr) return '';
const date = new Date(dateStr);
return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`;
};
// 优化的富文本处理函数 - 图片大小自适应优化
const parseContent = (str) => {
if (!str) return '';
// 1. 处理双重转义
const handleDoubleEscapes = (input) => {
return input
.replace(/\\\\u([0-9a-fA-F]{4})/g, (match, hex) => {
return String.fromCharCode(parseInt(hex, 16));
})
.replace(/\\\\/g, '\\')
.replace(/\\"/g, '"')
.replace(/\\\//g, '/');
};
// 2. 解码标准Unicode转义序列
const decodeUnicode = (input) => {
return input.replace(/\\u([0-9a-fA-F]{4})/g,
(match, grp) => String.fromCharCode(parseInt(grp, 16))
);
};
// 3. 执行双重转义处理
let processedHtml = handleDoubleEscapes(str);
// 4. 解码标准Unicode
processedHtml = decodeUnicode(processedHtml);
// 5. 处理空格转义
processedHtml = processedHtml.replace(/\\u00a0|\\u0020/g, ' ');
// 6. 优化图片处理 - 添加自适应大小和居中
processedHtml = processedHtml.replace(
/<img[^>]+src="([^"]+)"[^>]*>/gi,
(match, src) => {
// 处理URL中的转义字符
const cleanSrc = src
.replace(/\\/g, '')
.replace(/\s+/g, '%20');
// 添加图片自适应和居中样式
return `
<img
src="${cleanSrc}"
class="adaptive-img"
style="max-width:100%!important;height:auto!important;display:block!important;margin:20rpx auto;border-radius:16rpx;object-fit:contain;"
/>
`;
}
);
// 7. 移除所有内联的width和height属性
processedHtml = processedHtml.replace(/(<img[^>]*)width="[^"]*"/gi, '$1');
processedHtml = processedHtml.replace(/(<img[^>]*)height="[^"]*"/gi, '$1');
// 8. 处理其他HTML实体
processedHtml = processedHtml
.replace(/ /g, ' ')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, "'");
return processedHtml;
};
// 获取详情数据
const fetchDetail = async (id) => {
try {
uni.showLoading({ title: '加载中...', mask: true });
const res = await uni.request({
url: `/api/incorruptFront/frontArticleDet/${id}`,
method: 'GET',
header: {
'Content-Type': 'application/json; charset=utf-8'
}
});
// 兼容不同平台响应结构
const response = res[1] || res;
if (response.data && response.data.code === 200) {
detail.value = response.data.data;
} else {
throw new Error(response.data?.msg || '数据加载失败');
}
} catch (error) {
uni.showToast({
title: error.message || '网络请求失败',
icon: 'none',
duration: 3000
});
console.error('API请求错误:', error);
} finally {
uni.hideLoading();
}
};
</script>
<style scoped>
.detail-container {
padding: 30rpx;
background-color: #fff;
line-height: 1.8;
min-height: 100vh;
position: relative;
max-width: 100%;
overflow-x: hidden;
}
.detail-title {
font-size: 40rpx;
font-weight: bold;
margin-bottom: 20rpx;
color: #333;
text-align: center;
line-height: 1.5;
padding: 0 20rpx;
}
.detail-date {
font-size: 26rpx;
color: #999;
margin-bottom: 40rpx;
text-align: center;
}
/* 富文本内容样式增强 */
.detail-content {
font-size: 30rpx;
color: #555;
text-align: left;
padding: 0 20rpx;
max-width: 100%;
overflow-x: hidden;
/* 全局段落样式 */
:deep(p) {
margin: 25rpx 0;
line-height: 1.8;
text-align: justify;
text-indent: 2em; /* 添加首行缩进 */
max-width: 100%;
overflow-x: hidden;
}
/* 图片样式 - 优化自适应和居中 */
:deep(.adaptive-img) {
max-width: 100% !important;
width: auto !important;
height: auto !important;
display: block !important;
margin: 30rpx auto !important;
border-radius: 16rpx;
box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.1);
background-color: #f8f8f8;
transition: transform 0.3s ease, opacity 0.3s ease;
opacity: 0.95;
object-fit: contain;
}
/* 修复图片内联样式可能被覆盖的问题 */
:deep(img) {
max-width: 100% !important;
height: auto !important;
display: block !important;
margin: 30rpx auto !important;
}
/* 图片加载动画 */
:deep(.adaptive-img:not([src])) {
background: linear-gradient(90deg, #f0f0f0 25%, #e0e0e0 50%, #f0f0f0 75%);
background-size: 200% 100%;
animation: loading 1.5s infinite;
}
/* 图片悬停效果 */
:deep(.adaptive-img:hover) {
transform: scale(1.02);
opacity: 1;
box-shadow: 0 12rpx 30rpx rgba(0,0,0,0.15);
}
/* 标题样式 */
:deep(h1) {
font-size: 40rpx;
font-weight: bold;
margin: 40rpx 0 20rpx;
text-indent: 0;
max-width: 100%;
overflow-x: hidden;
}
/* 响应式图片处理 */
@media (max-width: 600rpx) {
:deep(.adaptive-img) {
max-height: 400rpx !important;
border-radius: 12rpx;
margin: 20rpx auto;
}
}
}
/* 特殊设备适配 */
@media (max-height: 700px) {
:deep(.adaptive-img) {
max-height: 300rpx !important;
}
}
/* 返回顶部按钮样式 */
.back-to-top {
position: fixed;
right: 40rpx;
bottom: 40rpx;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
background-color: #007AFF;
display: flex;
justify-content: center;
align-items: center;
box-shadow: 0 6rpx 16rpx rgba(0, 122, 255, 0.3);
opacity: 0;
transform: translateY(120rpx);
transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275);
z-index: 999;
}
.back-to-top.show {
opacity: 0.9;
transform: translateY(0);
}
.back-to-top:active {
transform: scale(0.95) translateY(0);
background-color: #0062cc;
}
/* 添加悬停效果 */
@media (hover: hover) {
.back-to-top:hover {
opacity: 1;
transform: translateY(-10rpx);
}
}
/* 图片加载动画 */
@keyframes loading {
0% {
background-position: 200% 0;
}
100% {
background-position: -200% 0;
}
}
/* 按钮动画 */
@keyframes bounce {
0%, 20%, 50%, 80%, 100% {transform: translateY(0);}
40% {transform: translateY(-20rpx);}
60% {transform: translateY(-10rpx);}
}
/* 添加按钮动画效果 */
.back-to-top.show {
animation: bounce 1.5s ease infinite;
}
</style>
这个是uniapp+vue3的移动端页面代码,我先要固定宽高富文本里的图片使得其单独占一行如果是手机打开就宽度就是手机宽度最大高度固定适用于手机屏幕,浏览器打开最大宽高固定
最新发布