Files
yunzerwebsiteallinone/backend/src/views/apps/cms/article/index/components/preview.vue
T
2026-08-20 16:21:07 +08:00

350 lines
7.5 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>
<el-drawer v-model="visible" title="文章预览" size="60%">
<div class="article-preview">
<div class="article-header">
<h1 class="article-title">{{ model?.title || "无标题" }}</h1>
<div class="article-meta">
<span class="meta-item">
<i class="el-icon-price-tag"></i>
<el-tag type="primary">{{ getCategoryName(model?.cate) }}</el-tag>
</span>
<span class="meta-item">
<i class="el-icon-user"></i>
作者:{{ model?.author }}
</span>
<span class="meta-item">
<i class="el-icon-view"></i>
发布人:{{ model?.publisher || "暂未发布" }}
</span>
<span class="meta-item">
<i class="el-icon-view"></i>
阅读量:{{ model?.views || 0 }}
</span>
<span class="meta-item">
<i class="el-icon-view"></i>
收藏量:{{ model?.likes || 0 }}
</span>
<span class="meta-item" v-if="model?.publish_time">
<i class="el-icon-time"></i>
{{ formatDate(model.publish_time) }}
</span>
<span class="meta-item">
<i class="el-icon-view"></i>
创建日期:{{ model?.create_time }}
</span>
</div>
</div>
<div class="article-content">
<div
v-if="model?.content"
v-html="model.content"
class="content-html"
></div>
<div v-else class="no-content">
<el-empty description="暂无内容"></el-empty>
</div>
</div>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { ref, watch, onMounted } from "vue";
import { listCategories } from "@/api/article";
import { ElMessage, ElMessageBox } from "element-plus";
const categoryOptions = ref([]);
const props = defineProps({
modelValue: {
type: Boolean,
default: false,
},
model: {
type: Object,
default: () => null,
},
});
// 获取分类列表
const fetchCategories = async () => {
try {
const res = await listCategories({ page: 1, limit: 1000 });
if (res.code === 200) {
categoryOptions.value = Array.isArray(res.data)
? res.data
: Array.isArray(res.data?.list)
? res.data.list
: [];
} else {
ElMessage.error(res?.msg || "获取分类列表失败");
}
} catch (error) {
ElMessage.error("获取分类列表失败");
}
};
// 根据分类ID获取分类名称
const getCategoryName = (cate) => {
if (!cate && cate !== 0) return "无分类";
// 确保比较时类型一致
const cateId = Number(cate);
// 传入的不是数字(已经是分类名),直接展示
if (Number.isNaN(cateId)) return String(cate);
const category = categoryOptions.value.find(
(item) => Number(item.id) === cateId,
);
return category?.name || "无分类";
};
const emit = defineEmits(["update:modelValue"]);
const visible = ref(false);
// 监听对话框显示状态
watch(
() => props.modelValue,
(newVal) => {
visible.value = newVal;
},
);
// 监听visible变化,同步给父组件
watch(visible, (newVal) => {
emit("update:modelValue", newVal);
});
onMounted(() => {
fetchCategories();
});
</script>
<style lang="less" scoped>
.article-preview {
padding: 20px;
.article-header {
border-bottom: 1px solid #ebeef5;
padding-bottom: 20px;
margin-bottom: 20px;
.article-title {
margin: 0 0 15px 0;
font-size: 24px;
font-weight: 600;
color: #303133;
line-height: 1.4;
}
.article-meta {
display: flex;
flex-wrap: wrap;
gap: 15px;
color: #909399;
font-size: 14px;
.meta-item {
display: flex;
align-items: center;
gap: 5px;
i {
font-size: 16px;
}
}
}
}
.article-content {
/* 确保article-content直接子元素的样式 */
& > div {
line-height: 1.8;
color: #606266;
padding: 20px 0;
}
/* 支持所有子元素的内联样式 */
* {
box-sizing: border-box;
}
/* 段落样式 */
p {
margin: 10px 0;
line-height: 1.8;
}
/* 标题样式 */
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 20px 0 10px 0;
font-weight: 600;
}
h1 {
font-size: 28px;
}
h2 {
font-size: 24px;
}
h3 {
font-size: 20px;
}
h4 {
font-size: 18px;
}
h5 {
font-size: 16px;
}
h6 {
font-size: 14px;
}
/* 列表样式 */
ul,
ol {
margin: 10px 0;
padding-left: 30px;
}
/* 引用样式 */
blockquote {
border-left: 4px solid #ebeef5;
padding-left: 15px;
margin: 15px 0;
color: #909399;
font-style: italic;
}
/* 代码样式 */
:deep(code) {
border-radius: 4px;
padding: 2px 6px;
font-family: "JetBrains Mono", "Fira Code", "Consolas", monospace;
font-size: 13px;
color: #f06b6b;
}
/* 代码块样式 */
:deep(pre) {
background: linear-gradient(135deg, #1e1e2e 0%, #2d2d3f 100%);
border-radius: 12px;
padding: 10px;
overflow-x: auto;
margin: 20px 0;
border: 1px solid rgba(255, 255, 255, 0.1);
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
}
/* 图片样式 */
img {
max-width: 100%;
height: auto;
border-radius: 4px;
margin: 10px 0;
}
/* 表格样式 */
table {
width: 100%;
border-collapse: collapse;
margin: 15px 0;
th,
td {
border: 1px solid #ebeef5;
padding: 8px 12px;
text-align: left;
}
th {
background-color: #fafafa;
font-weight: 600;
}
}
/* 确保内联样式能够生效 */
[style] {
/* 允许内联样式正常工作 */
all: unset;
/* 重新应用基本样式 */
line-height: 1.8;
color: #606266;
/* 允许特定内联样式覆盖 */
&[style*="text-align"] {
text-align: var(--text-align, inherit) !important;
}
&[style*="color"] {
color: var(--color, inherit) !important;
}
&[style*="font-size"] {
font-size: var(--font-size, inherit) !important;
}
&[style*="font-weight"] {
font-weight: var(--font-weight, inherit) !important;
}
&[style*="font-style"] {
font-style: var(--font-style, inherit) !important;
}
&[style*="text-decoration"] {
text-decoration: var(--text-decoration, inherit) !important;
}
&[style*="margin"] {
margin: var(--margin, inherit) !important;
}
&[style*="padding"] {
padding: var(--padding, inherit) !important;
}
}
/* 直接支持text-align属性 */
[align] {
text-align: attr(align) !important;
}
/* 确保content-html类的样式 */
.content-html {
& > * {
margin: 10px 0;
}
& > h1,
& > h2,
& > h3,
& > h4,
& > h5,
& > h6 {
margin: 20px 0 10px 0;
}
& > p {
margin: 10px 0;
}
/* 支持居中样式 */
& > p[style*="text-align: center"],
& > p[align="center"] {
text-align: center !important;
}
/* 图片居中 */
& > p[style*="text-align: center"] img,
& > p[align="center"] img {
display: inline-block;
margin: 10px auto;
}
}
.no-content {
text-align: center;
padding: 40px 0;
}
}
}
</style>