347 lines
8.4 KiB
Vue
347 lines
8.4 KiB
Vue
<!-- 通知公告详情页 -->
|
||
<template>
|
||
<s-layout title="通知公告" :bgStyle="{ backgroundColor:'#F8EDE8' }" navbar="inner" color="#333333">
|
||
<!-- 页面主体:flex布局,头部固定+内容滚动 -->
|
||
<view class="notice-detail-page">
|
||
<!-- 固定头部区域 -->
|
||
<view class="detail-header">
|
||
<!-- 文章标题 -->
|
||
<view class="article-title">
|
||
<text class="title-text">{{ noticeInfo.title }}</text>
|
||
</view>
|
||
|
||
<!-- 发布信息 -->
|
||
<view class="publish-info">
|
||
<view class="publisher">
|
||
<image class="publisher-avatar" src="/static/img/login_img.png" mode="aspectFill" />
|
||
<text class="publisher-name">{{ noticeInfo.publisher }}</text>
|
||
</view>
|
||
<text class="publish-date">{{ noticeInfo.publishDate }}</text>
|
||
</view>
|
||
|
||
<!-- 分割线 -->
|
||
<view class="divider"></view>
|
||
</view>
|
||
|
||
<!-- 富文本内容 - 独立滚动区域 -->
|
||
<scroll-view class="article-scroll" scroll-y :style="{ height: contentScrollHeight + 'px' }">
|
||
<view class="article-content">
|
||
<mp-html :content="noticeInfo.content"></mp-html>
|
||
</view>
|
||
<!-- 占位,防止内容被底部附件遮挡 -->
|
||
<view class="bottom-placeholder" v-if="noticeInfo.attachments && noticeInfo.attachments.length > 0"></view>
|
||
</scroll-view>
|
||
|
||
</view>
|
||
|
||
<!-- 附件列表 - 固定在底部 -->
|
||
<view class="attachment-section" v-if="noticeInfo.attachments && noticeInfo.attachments.length > 0">
|
||
<view
|
||
class="attachment-item"
|
||
v-for="(item, index) in noticeInfo.attachments"
|
||
:key="index"
|
||
@tap="downloadAttachment(item)"
|
||
>
|
||
<view class="attachment-left">
|
||
<image class="attachment-icon" :src="getAttachmentIcon(item.type)" mode="aspectFit" />
|
||
<text class="attachment-name">{{ item.name }}</text>
|
||
</view>
|
||
<image class="download-icon" src="/static/img/right-icon.png" mode="aspectFit" />
|
||
</view>
|
||
</view>
|
||
</s-layout>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, onMounted, nextTick, getCurrentInstance } from 'vue';
|
||
import { onLoad } from '@dcloudio/uni-app';
|
||
import sheep from '@/sheep';
|
||
|
||
// 富文本滚动区域高度(单位:px)
|
||
const contentScrollHeight = ref(0);
|
||
|
||
// 公告信息
|
||
const noticeInfo = ref({
|
||
title: '',
|
||
publisher: '',
|
||
publishDate: '',
|
||
content: '',
|
||
attachments: []
|
||
});
|
||
|
||
// 页面加载
|
||
onLoad((options) => {
|
||
if (options.id) {
|
||
loadNoticeDetail(options.id);
|
||
}
|
||
});
|
||
|
||
// 渲染完成后计算scroll-view高度(头部固定,剩余空间给内容滚动)
|
||
onMounted(() => {
|
||
const instance = getCurrentInstance();
|
||
nextTick(() => {
|
||
const sysInfo = uni.getSystemInfoSync();
|
||
uni.createSelectorQuery()
|
||
.in(instance)
|
||
.select('.detail-header')
|
||
.boundingClientRect((rect) => {
|
||
if (rect) {
|
||
// scroll-view高度 = 屏幕可用高度 - 头部高度 - 头部距离顶部距离
|
||
contentScrollHeight.value = sysInfo.windowHeight - rect.height - rect.top;
|
||
}
|
||
})
|
||
.exec();
|
||
});
|
||
});
|
||
|
||
// 加载公告详情
|
||
async function loadNoticeDetail(id) {
|
||
// TODO: 调用API获取公告详情
|
||
// const { code, data } = await NoticeApi.getNoticeDetail(id);
|
||
// if (code === 0) {
|
||
// noticeInfo.value = data;
|
||
// }
|
||
|
||
// 模拟数据
|
||
noticeInfo.value = {
|
||
title: '关于召开业主大会讨论物业费价格调整的通知',
|
||
publisher: 'x小区物业',
|
||
publishDate: '2026-01-20',
|
||
content: `<p>各位业主:</p>
|
||
<p>为进一步提升本小区物业服务质量,保障小区公共设施设备的正常运维、环境卫生整治、安保服务升级等工作有序开展,切实维护全体业主的共同利益,根据《物业管理条例》《业主大会和业主委员会指导规则》及本小区《管理规约》相关规定,经业主委员会研究决定,召开业主大会,专门讨论本小区物业费价格调整相关事宜。现将具体事项通知如下:</p>
|
||
<p>一、会议基本信息</p>
|
||
<p>会议时间:2026年2月15日(周六)上午9:00</p>
|
||
<p>会议地点:小区活动中心一楼会议室</p>
|
||
<p>参会人员:全体业主或业主代表</p>`,
|
||
attachments: [
|
||
{ name: 'IMG-2309.PNG', type: 'image', url: '' },
|
||
{ name: 'EXCEL-2309.XLXS', type: 'excel', url: '' }
|
||
]
|
||
};
|
||
}
|
||
|
||
// 获取附件图标
|
||
function getAttachmentIcon(type) {
|
||
const iconMap = {
|
||
image: '/static/img/eli-icon1.png',
|
||
excel: '/static/img/eli-icon2.png',
|
||
pdf: '/static/img/eli-icon2.png',
|
||
word: '/static/img/eli-icon2.png'
|
||
};
|
||
return iconMap[type] || '/static/img/eli-icon2.png';
|
||
}
|
||
|
||
// 下载附件
|
||
function downloadAttachment(item) {
|
||
if (!item.url) {
|
||
uni.showToast({
|
||
title: '附件链接不存在',
|
||
icon: 'none'
|
||
});
|
||
return;
|
||
}
|
||
|
||
uni.showLoading({
|
||
title: '下载中...'
|
||
});
|
||
|
||
uni.downloadFile({
|
||
url: item.url,
|
||
success: (res) => {
|
||
if (res.statusCode === 200) {
|
||
// 保存到本地
|
||
uni.saveFile({
|
||
tempFilePath: res.tempFilePath,
|
||
success: (saveRes) => {
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: '下载成功',
|
||
icon: 'success'
|
||
});
|
||
|
||
// 打开文件
|
||
uni.openDocument({
|
||
filePath: saveRes.savedFilePath,
|
||
showMenu: true,
|
||
success: () => {
|
||
console.log('打开文档成功');
|
||
},
|
||
fail: () => {
|
||
uni.showToast({
|
||
title: '无法打开该文件',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
},
|
||
fail: () => {
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: '保存失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
} else {
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: '下载失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
fail: () => {
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: '下载失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
/* 页面容器 - 伪元素渐变向上覆盖到导航栏 */
|
||
.notice-detail-page {
|
||
position: relative;
|
||
display: flex;
|
||
flex-direction: column;
|
||
padding: 0 32rpx;
|
||
|
||
&::before {
|
||
background: linear-gradient(180deg, #F8EDE8 0%, #FFFFFF 30%);
|
||
}
|
||
}
|
||
|
||
/* 固定头部区域(标题+发布信息) */
|
||
.detail-header {
|
||
flex-shrink: 0; /* 不被压缩 */
|
||
}
|
||
|
||
/* 底部占位 */
|
||
.bottom-placeholder {
|
||
height: 300rpx;
|
||
}
|
||
|
||
/* 文章标题 */
|
||
.article-title {
|
||
padding: 32rpx 0 24rpx;
|
||
|
||
.title-text {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
line-height: 1.5;
|
||
}
|
||
}
|
||
|
||
/* 发布信息 */
|
||
.publish-info {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding-bottom: 24rpx;
|
||
|
||
.publisher {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.publisher-avatar {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.publisher-name {
|
||
font-size: 28rpx;
|
||
color: #666666;
|
||
}
|
||
}
|
||
|
||
.publish-date {
|
||
font-size: 26rpx;
|
||
color: #999999;
|
||
}
|
||
}
|
||
|
||
/* 分割线 */
|
||
.divider {
|
||
height: 1rpx;
|
||
background-color: #E5E5E5;
|
||
margin-bottom: 32rpx;
|
||
}
|
||
|
||
/* 富文本滚动区域 - 高度由JS动态绑定 */
|
||
.article-scroll {
|
||
flex: 1; /* 占据剩余空间 */
|
||
}
|
||
|
||
/* 富文本内容 */
|
||
.article-content {
|
||
border-radius: 24rpx;
|
||
padding: 32rpx;
|
||
|
||
:deep(p) {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
line-height: 1.8;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
:deep(img) {
|
||
width: 100%;
|
||
border-radius: 16rpx;
|
||
margin: 24rpx 0;
|
||
}
|
||
}
|
||
|
||
/* 附件区域 - 固定在底部 */
|
||
.attachment-section {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
width: 750rpx;
|
||
height: 300rpx;
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx -8rpx 64rpx 0rpx rgba(0, 0, 0, 0.16);
|
||
border-radius: 0;
|
||
padding: 24rpx 32rpx;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.attachment-item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 24rpx 0;
|
||
|
||
&:not(:last-child) {
|
||
border-bottom: 1rpx solid #F0F0F0;
|
||
}
|
||
|
||
.attachment-left {
|
||
display: flex;
|
||
align-items: center;
|
||
flex: 1;
|
||
|
||
.attachment-icon {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.attachment-name {
|
||
font-size: 28rpx;
|
||
color: #333333;
|
||
}
|
||
}
|
||
|
||
.download-icon {
|
||
width: 32rpx;
|
||
height: 32rpx;
|
||
}
|
||
}
|
||
</style>
|