Files
schoolNews/schoolNewsWeb/src/views/article/ArticleShowView.vue
2025-10-20 18:28:38 +08:00

340 lines
7.4 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>
<!-- Dialog 模式 -->
<el-dialog
v-if="asDialog"
v-model="visible"
:title="title"
:width="width"
:close-on-click-modal="false"
@close="handleClose"
>
<div class="article-show-container">
<!-- 文章头部信息 -->
<div class="article-header">
<h1 class="article-title">{{ articleData.title }}</h1>
<div class="article-meta-info">
<div class="meta-item" v-if="articleData.publishTime || articleData.createTime">
发布时间{{ formatDateSimple(articleData.publishTime || articleData.createTime || '') }}
</div>
<div class="meta-item" v-if="articleData.viewCount !== undefined">
浏览次数{{ articleData.viewCount }}
</div>
<div class="meta-item" v-if="articleData.source">
来源{{ articleData.source }}
</div>
</div>
</div>
<!-- 分隔线 -->
<div class="separator"></div>
<!-- 文章内容 -->
<div class="article-content ql-editor" v-html="articleData.content"></div>
</div>
<template #footer>
<el-button @click="handleClose">关闭</el-button>
<el-button v-if="showEditButton" type="primary" @click="handleEdit">编辑</el-button>
</template>
</el-dialog>
<!-- Dialog 模式 -->
<div v-else class="article-show-container">
<!-- 文章头部信息 -->
<div class="article-header">
<h1 class="article-title">{{ articleData.title }}</h1>
<div class="article-meta-info">
<div class="meta-item" v-if="articleData.publishTime || articleData.createTime">
发布时间{{ formatDateSimple(articleData.publishTime || articleData.createTime || '') }}
</div>
<div class="meta-item" v-if="articleData.viewCount !== undefined">
浏览次数{{ articleData.viewCount }}
</div>
<div class="meta-item" v-if="articleData.source">
来源{{ articleData.source }}
</div>
</div>
</div>
<!-- 分隔线 -->
<div class="separator"></div>
<!-- 文章内容 -->
<div class="article-content ql-editor" v-html="articleData.content"></div>
</div>
</template>
<script setup lang="ts">
import { computed } from 'vue';
import { ElDialog, ElButton } from 'element-plus';
import { ResourceCategory, Resource, Tag } from '@/types';
interface Props {
modelValue?: boolean; // Dialog 模式下的显示状态
asDialog?: boolean; // 是否作为 Dialog 使用
title?: string; // Dialog 标题
width?: string; // Dialog 宽度
articleData?: Resource; // 文章数据
categoryList?: Array<ResourceCategory>; // 分类列表
showEditButton?: boolean; // 是否显示编辑按钮
}
const props = withDefaults(defineProps<Props>(), {
modelValue: false,
asDialog: true,
title: '文章预览',
width: '900px',
articleData: () => ({}),
categoryList: () => [],
showEditButton: false
});
const emit = defineEmits<{
'update:modelValue': [value: boolean];
'close': [];
'edit': [];
}>();
// Dialog 显示状态
const visible = computed({
get: () => props.asDialog ? props.modelValue : false,
set: (val) => props.asDialog ? emit('update:modelValue', val) : undefined
});
// 格式化日期简单格式YYYY-MM-DD
function formatDateSimple(date: string | Date): string {
if (!date) return '';
const d = new Date(date);
const year = d.getFullYear();
const month = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
}
// 关闭处理
function handleClose() {
if (props.asDialog) {
visible.value = false;
}
emit('close');
}
// 编辑处理
function handleEdit() {
emit('edit');
}
// 暴露方法
defineExpose({
open: () => {
if (props.asDialog) {
visible.value = true;
}
},
close: handleClose
});
</script>
<style lang="scss" scoped>
.article-show-container {
max-width: 100%;
margin: 0 auto;
background: #fff;
}
.article-header {
margin-bottom: 36px;
}
.article-title {
font-family: 'PingFang SC';
font-weight: 600;
font-size: 28px;
line-height: 28px;
color: #141F38;
margin: 0 0 30px 0;
text-align: center;
}
.article-meta-info {
display: flex;
align-items: center;
gap: 48px;
margin-top: 30px;
justify-content: center;
}
.meta-item {
font-family: 'PingFang SC';
font-weight: 400;
font-size: 14px;
line-height: 28px;
color: #979797;
}
.separator {
width: 100%;
height: 1px;
background: #E9E9E9;
margin-bottom: 40px;
}
.article-cover {
margin-bottom: 24px;
text-align: center;
}
.cover-image {
max-width: 100%;
max-height: 400px;
border-radius: 8px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
}
.article-content {
font-family: 'PingFang SC';
font-weight: 400;
font-size: 16px;
line-height: 30px;
color: #334155;
// 继承富文本编辑器的样式
:deep(img) {
max-width: 100%;
height: auto;
display: inline-block;
vertical-align: bottom;
}
:deep(video),
:deep(iframe) {
max-width: 100%;
height: auto;
display: inline-block;
vertical-align: bottom;
}
// 对齐方式样式 - 图片和视频分别处理
:deep(.ql-align-center) {
text-align: center !important;
// 视频始终居中显示
video, .custom-video {
display: block !important;
margin-left: auto !important;
margin-right: auto !important;
}
// 图片跟随文字对齐
img, .custom-image {
display: inline-block !important;
vertical-align: bottom !important;
}
}
:deep(.ql-align-right) {
text-align: right !important;
// 视频始终居中显示
video, .custom-video {
display: block !important;
margin-left: auto !important;
margin-right: auto !important;
}
// 图片跟随文字对齐
img, .custom-image {
display: inline-block !important;
vertical-align: bottom !important;
}
}
:deep(.ql-align-left) {
text-align: left !important;
// 视频始终居中显示
video, .custom-video {
display: block !important;
margin-left: auto !important;
margin-right: auto !important;
}
// 图片跟随文字对齐
img, .custom-image {
display: inline-block !important;
vertical-align: bottom !important;
}
}
// 其他富文本样式
:deep(h1), :deep(h2), :deep(h3), :deep(h4), :deep(h5), :deep(h6) {
margin: 24px 0 16px 0;
font-weight: bold;
color: #303133;
}
:deep(p) {
margin: 16px 0;
}
:deep(blockquote) {
margin: 16px 0;
padding: 16px;
background: #f5f7fa;
border-left: 4px solid #409eff;
border-radius: 4px;
}
:deep(code) {
background: #f5f7fa;
padding: 2px 6px;
border-radius: 3px;
font-family: 'Courier New', monospace;
font-size: 14px;
}
:deep(pre) {
background: #f5f7fa;
padding: 16px;
border-radius: 4px;
overflow-x: auto;
margin: 16px 0;
code {
background: none;
padding: 0;
}
}
:deep(ul), :deep(ol) {
margin: 16px 0;
padding-left: 24px;
}
:deep(li) {
margin: 8px 0;
}
:deep(table) {
width: 100%;
border-collapse: collapse;
margin: 16px 0;
}
:deep(th), :deep(td) {
border: 1px solid #ebeef5;
padding: 8px 12px;
text-align: left;
}
:deep(th) {
background: #f5f7fa;
font-weight: bold;
}
}
</style>