web-上传组件、富文本组件
This commit is contained in:
@@ -15,43 +15,48 @@
|
||||
<!-- 分类和标签 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="文章分类" prop="category">
|
||||
<el-select v-model="articleForm.category" placeholder="请选择分类" style="width: 100%">
|
||||
<el-option label="新闻资讯" value="news" />
|
||||
<el-option label="技术文章" value="tech" />
|
||||
<el-option label="学习资料" value="study" />
|
||||
<el-option label="通知公告" value="notice" />
|
||||
<el-form-item label="文章分类" prop="categoryID">
|
||||
<el-select v-model="articleForm.categoryID" placeholder="请选择分类" style="width: 100%" :loading="categoryLoading">
|
||||
<el-option
|
||||
v-for="category in categoryList"
|
||||
:key="category.categoryID || category.id"
|
||||
:label="category.name"
|
||||
:value="category.categoryID || category.id || ''"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="标签" prop="tags">
|
||||
<el-select v-model="articleForm.tags" multiple placeholder="请选择标签" style="width: 100%">
|
||||
<el-option label="重要" value="important" />
|
||||
<el-option label="推荐" value="recommend" />
|
||||
<el-option label="热门" value="hot" />
|
||||
<el-option label="原创" value="original" />
|
||||
<el-select v-model="articleForm.tags" multiple placeholder="请选择标签" style="width: 100%" :loading="tagLoading">
|
||||
<el-option
|
||||
v-for="tag in tagList"
|
||||
:key="tag.id || tag.tagID"
|
||||
:label="tag.name"
|
||||
:value="tag.id || tag.tagID || ''"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 摘要 -->
|
||||
<el-form-item label="文章摘要" prop="summary">
|
||||
<el-input v-model="articleForm.summary" type="textarea" :rows="3" placeholder="请输入文章摘要(选填)"
|
||||
maxlength="200" show-word-limit />
|
||||
</el-form-item>
|
||||
|
||||
<!-- 封面图 -->
|
||||
<el-form-item label="封面图片">
|
||||
<el-upload class="cover-uploader" :show-file-list="false" :on-success="handleCoverSuccess"
|
||||
:before-upload="beforeCoverUpload" action="#">
|
||||
<img v-if="articleForm.cover" :src="articleForm.cover" class="cover" />
|
||||
<el-icon v-else class="cover-uploader-icon">
|
||||
<Plus />
|
||||
</el-icon>
|
||||
</el-upload>
|
||||
<div class="upload-tip">建议尺寸:800x450px,支持jpg、png格式</div>
|
||||
<!-- 上传区域 - 只在没有封面图片时显示 -->
|
||||
<FileUpload
|
||||
v-if="!articleForm.coverImage"
|
||||
:as-dialog="false"
|
||||
accept="image/*"
|
||||
:max-size="2"
|
||||
:multiple="false"
|
||||
tip="建议尺寸:800x450px,支持jpg、png格式"
|
||||
@success="handleCoverUploadSuccess"
|
||||
/>
|
||||
<!-- 封面预览 - 只在有封面图片时显示 -->
|
||||
<div v-if="articleForm.coverImage" class="cover-preview">
|
||||
<img :src="articleForm.coverImage" class="cover" />
|
||||
<el-button type="danger" size="small" @click="removeCover">删除封面</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
|
||||
<!-- 文章内容 -->
|
||||
@@ -93,28 +98,22 @@
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 预览对话框 -->
|
||||
<el-dialog v-model="previewVisible" title="文章预览" width="900px" :close-on-click-modal="false">
|
||||
<div class="article-preview">
|
||||
<h1 class="preview-title">{{ articleForm.title }}</h1>
|
||||
<div class="preview-meta">
|
||||
<span>分类:{{ getCategoryLabel(articleForm.category) }}</span>
|
||||
<span v-if="articleForm.tags.length">
|
||||
标签:{{ articleForm.tags.join(', ') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="preview-summary" v-if="articleForm.summary">
|
||||
{{ articleForm.summary }}
|
||||
</div>
|
||||
<img v-if="articleForm.cover" :src="articleForm.cover" class="preview-cover" />
|
||||
<div class="preview-content ql-editor" v-html="articleForm.content"></div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<!-- 文章预览组件 -->
|
||||
<ArticleShowView
|
||||
v-model="previewVisible"
|
||||
:as-dialog="true"
|
||||
title="文章预览"
|
||||
width="900px"
|
||||
:article-data="articleForm"
|
||||
:category-list="categoryList"
|
||||
:show-edit-button="false"
|
||||
@close="previewVisible = false"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive, onMounted } from 'vue';
|
||||
import { ref, onMounted } from 'vue';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import {
|
||||
ElForm,
|
||||
@@ -126,13 +125,14 @@ import {
|
||||
ElRow,
|
||||
ElCol,
|
||||
ElCheckbox,
|
||||
ElUpload,
|
||||
ElIcon,
|
||||
ElMessage,
|
||||
ElDialog
|
||||
ElMessage
|
||||
} from 'element-plus';
|
||||
import { ArrowLeft, Plus } from '@element-plus/icons-vue';
|
||||
import { ArrowLeft } from '@element-plus/icons-vue';
|
||||
import { RichTextComponent } from '@/components/text';
|
||||
import { FileUpload } from '@/components/file';
|
||||
import { ArticleShowView } from './index';
|
||||
import { resourceCategoryApi, resourceTagApi, resourceApi } from '@/apis/resource';
|
||||
import { Resource, ResourceCategory, Tag } from '@/types/resource';
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
@@ -146,17 +146,25 @@ const previewVisible = ref(false);
|
||||
// 是否编辑模式
|
||||
const isEdit = ref(false);
|
||||
|
||||
// 数据状态
|
||||
const categoryList = ref<ResourceCategory[]>([]);
|
||||
const tagList = ref<Tag[]>([]);
|
||||
const categoryLoading = ref(false);
|
||||
const tagLoading = ref(false);
|
||||
|
||||
// 表单数据
|
||||
const articleForm = reactive({
|
||||
const articleForm = ref<Resource>({
|
||||
title: '',
|
||||
category: '',
|
||||
tags: [] as string[],
|
||||
summary: '',
|
||||
cover: '',
|
||||
content: '',
|
||||
categoryID: '',
|
||||
author: '',
|
||||
source: '',
|
||||
sourceUrl: '',
|
||||
viewCount: 0,
|
||||
coverImage: '',
|
||||
tags: [] as Tag[],
|
||||
allowComment: true,
|
||||
isTop: false,
|
||||
isRecommend: false
|
||||
});
|
||||
|
||||
// 表单验证规则
|
||||
@@ -165,7 +173,7 @@ const rules = {
|
||||
{ required: true, message: '请输入文章标题', trigger: 'blur' },
|
||||
{ min: 5, max: 100, message: '标题长度在 5 到 100 个字符', trigger: 'blur' }
|
||||
],
|
||||
category: [
|
||||
categoryID: [
|
||||
{ required: true, message: '请选择文章分类', trigger: 'change' }
|
||||
],
|
||||
content: [
|
||||
@@ -173,19 +181,43 @@ const rules = {
|
||||
]
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 检查是否是编辑模式
|
||||
const id = route.query.id;
|
||||
if (id) {
|
||||
isEdit.value = true;
|
||||
loadArticle(id as string);
|
||||
}
|
||||
});
|
||||
|
||||
// 加载文章数据(编辑模式)
|
||||
function loadArticle(id: string) {
|
||||
// TODO: 调用API加载文章数据
|
||||
console.log('加载文章:', id);
|
||||
// 加载分类列表
|
||||
async function loadCategoryList() {
|
||||
try {
|
||||
categoryLoading.value = true;
|
||||
const result = await resourceCategoryApi.getCategoryList();
|
||||
if (result.success) {
|
||||
// 数组数据从 dataList 获取
|
||||
categoryList.value = result.dataList || [];
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载分类失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载分类失败:', error);
|
||||
ElMessage.error('加载分类失败');
|
||||
} finally {
|
||||
categoryLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载标签列表
|
||||
async function loadTagList() {
|
||||
try {
|
||||
tagLoading.value = true;
|
||||
const result = await resourceTagApi.getTagList();
|
||||
if (result.success) {
|
||||
// 数组数据从 dataList 获取
|
||||
tagList.value = result.dataList || [];
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载标签失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载标签失败:', error);
|
||||
ElMessage.error('加载标签失败');
|
||||
} finally {
|
||||
tagLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 返回
|
||||
@@ -235,8 +267,8 @@ async function handleSaveDraft() {
|
||||
|
||||
// 预览
|
||||
function handlePreview() {
|
||||
console.log(articleForm.content);
|
||||
if (!articleForm.title) {
|
||||
console.log(articleForm.value.content);
|
||||
if (!articleForm.value.title) {
|
||||
ElMessage.warning('请先输入文章标题');
|
||||
return;
|
||||
}
|
||||
@@ -244,35 +276,46 @@ function handlePreview() {
|
||||
}
|
||||
|
||||
// 封面上传成功
|
||||
function handleCoverSuccess(response: any) {
|
||||
// TODO: 处理上传成功的响应
|
||||
articleForm.cover = response.url;
|
||||
}
|
||||
|
||||
// 上传前验证
|
||||
function beforeCoverUpload(file: File) {
|
||||
const isImage = file.type.startsWith('image/');
|
||||
const isLt2M = file.size / 1024 / 1024 < 2;
|
||||
|
||||
if (!isImage) {
|
||||
ElMessage.error('只能上传图片文件!');
|
||||
function handleCoverUploadSuccess(files: any[]) {
|
||||
if (files && files.length > 0) {
|
||||
const file = files[0];
|
||||
// 使用文件下载URL构建完整路径
|
||||
import('@/config').then(config => {
|
||||
articleForm.value.coverImage = config.FILE_DOWNLOAD_URL + file.id;
|
||||
});
|
||||
}
|
||||
if (!isLt2M) {
|
||||
ElMessage.error('图片大小不能超过 2MB!');
|
||||
}
|
||||
return isImage && isLt2M;
|
||||
}
|
||||
|
||||
// 获取分类标签
|
||||
function getCategoryLabel(value: string): string {
|
||||
const map: Record<string, string> = {
|
||||
news: '新闻资讯',
|
||||
tech: '技术文章',
|
||||
study: '学习资料',
|
||||
notice: '通知公告'
|
||||
};
|
||||
return map[value] || value;
|
||||
// 删除封面
|
||||
function removeCover() {
|
||||
articleForm.value.coverImage = '';
|
||||
}
|
||||
|
||||
|
||||
onMounted(async () => {
|
||||
// 并行加载分类和标签数据
|
||||
await Promise.all([
|
||||
loadCategoryList(),
|
||||
loadTagList()
|
||||
]);
|
||||
|
||||
// 如果是编辑模式,加载文章数据
|
||||
const id = route.query.id;
|
||||
if (id) {
|
||||
try {
|
||||
isEdit.value = true;
|
||||
const result = await resourceApi.getResourceById(id as string);
|
||||
if (result.success && result.data) {
|
||||
articleForm.value = result.data;
|
||||
} else {
|
||||
ElMessage.error(result.message || '加载文章失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载文章失败:', error);
|
||||
ElMessage.error('加载文章失败');
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -341,82 +384,16 @@ function getCategoryLabel(value: string): string {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.article-preview {
|
||||
padding: 20px;
|
||||
|
||||
:deep(.ql-code-block-container) {
|
||||
margin: 12px 0; // 上下间距
|
||||
}
|
||||
|
||||
:deep(.ql-code-block) {
|
||||
background: #282c34; // 代码块背景色(类似深色主题)
|
||||
color: #abb2bf; // 代码文字颜色
|
||||
padding: 12px; // 内边距
|
||||
border-radius: 4px; // 圆角
|
||||
overflow-x: auto; // 横向滚动
|
||||
font-family: 'Courier New', monospace; // 等宽字体
|
||||
white-space: pre; // 保留空格和换行
|
||||
}
|
||||
|
||||
.preview-title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin: 0 0 16px 0;
|
||||
}
|
||||
|
||||
.preview-meta {
|
||||
display: flex;
|
||||
gap: 24px;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.preview-summary {
|
||||
background: #f5f7fa;
|
||||
padding: 16px;
|
||||
.cover-preview {
|
||||
margin-top: 16px;
|
||||
position: relative;
|
||||
|
||||
.cover {
|
||||
width: 200px;
|
||||
height: auto;
|
||||
border-radius: 4px;
|
||||
color: #606266;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.preview-cover {
|
||||
width: 100%;
|
||||
max-height: 400px;
|
||||
object-fit: cover;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.preview-content {
|
||||
// ql-editor 类会自动应用 Quill 的默认样式
|
||||
// 这里只添加必要的自定义样式覆盖
|
||||
|
||||
// 图片和视频样式(保留用户设置的尺寸)
|
||||
:deep(img[width]),
|
||||
:deep(video[width]),
|
||||
:deep(img[style*="width"]),
|
||||
:deep(video[style*="width"]) {
|
||||
// 如果有 width 属性或 style 中包含 width,使用用户设置的尺寸
|
||||
max-width: 100%;
|
||||
// 不强制设置 height: auto,保留用户设置的固定尺寸
|
||||
display: block;
|
||||
margin: 12px auto;
|
||||
}
|
||||
|
||||
// 没有 width 属性的图片和视频使用默认样式
|
||||
:deep(img:not([width]):not([style*="width"])),
|
||||
:deep(video:not([width]):not([style*="width"])),
|
||||
:deep(iframe) {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
display: block;
|
||||
margin: 12px auto;
|
||||
}
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
382
schoolNewsWeb/src/views/article/ArticleShowView.vue
Normal file
382
schoolNewsWeb/src/views/article/ArticleShowView.vue
Normal file
@@ -0,0 +1,382 @@
|
||||
<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">
|
||||
<span v-if="articleData.category" class="meta-item">
|
||||
分类:{{ getCategoryLabel(articleData.category) }}
|
||||
</span>
|
||||
<span v-if="articleData.tags && articleData.tags.length" class="meta-item">
|
||||
标签:{{ getTagsString(articleData.tags) }}
|
||||
</span>
|
||||
<span v-if="articleData.author" class="meta-item">
|
||||
作者:{{ articleData.author }}
|
||||
</span>
|
||||
<span v-if="articleData.createTime" class="meta-item">
|
||||
发布时间:{{ formatDate(articleData.createTime) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 封面图片 -->
|
||||
<div v-if="articleData.coverImage" class="article-cover">
|
||||
<img :src="articleData.coverImage" class="cover-image" />
|
||||
</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">
|
||||
<span v-if="articleData.category" class="meta-item">
|
||||
分类:{{ getCategoryLabel(articleData.category) }}
|
||||
</span>
|
||||
<span v-if="articleData.tags && articleData.tags.length" class="meta-item">
|
||||
标签:{{ getTagsString(articleData.tags) }}
|
||||
</span>
|
||||
<span v-if="articleData.author" class="meta-item">
|
||||
作者:{{ articleData.author }}
|
||||
</span>
|
||||
<span v-if="articleData.createTime" class="meta-item">
|
||||
发布时间:{{ formatDate(articleData.createTime) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 封面图片 -->
|
||||
<div v-if="articleData.coverImage" class="article-cover">
|
||||
<img :src="articleData.coverImage" class="cover-image" />
|
||||
</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';
|
||||
|
||||
interface ArticleData {
|
||||
title?: string;
|
||||
content?: string;
|
||||
coverImage?: string;
|
||||
category?: string;
|
||||
tags?: Array<{ name?: string }> | string[]; // 支持对象数组或字符串数组
|
||||
author?: string;
|
||||
createTime?: string | Date;
|
||||
}
|
||||
|
||||
interface Props {
|
||||
modelValue?: boolean; // Dialog 模式下的显示状态
|
||||
asDialog?: boolean; // 是否作为 Dialog 使用
|
||||
title?: string; // Dialog 标题
|
||||
width?: string; // Dialog 宽度
|
||||
articleData?: ArticleData; // 文章数据
|
||||
categoryList?: Array<{ id?: string; categoryID?: string; name?: string }>; // 分类列表
|
||||
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
|
||||
});
|
||||
|
||||
// 获取标签字符串
|
||||
function getTagsString(tags: Array<{ name?: string }> | string[]): string {
|
||||
if (!tags || tags.length === 0) return '';
|
||||
|
||||
if (typeof tags[0] === 'string') {
|
||||
return (tags as string[]).join(', ');
|
||||
} else {
|
||||
return (tags as Array<{ name?: string }>)
|
||||
.map(tag => tag.name || '')
|
||||
.filter(name => name)
|
||||
.join(', ');
|
||||
}
|
||||
}
|
||||
|
||||
// 获取分类标签
|
||||
function getCategoryLabel(categoryId: string): string {
|
||||
if (!props.categoryList || !categoryId) return '';
|
||||
|
||||
const category = props.categoryList.find(cat =>
|
||||
cat.id === categoryId || cat.categoryID === categoryId
|
||||
);
|
||||
|
||||
return category?.name || categoryId;
|
||||
}
|
||||
|
||||
// 格式化日期
|
||||
function formatDate(date: string | Date): string {
|
||||
if (!date) return '';
|
||||
|
||||
const d = new Date(date);
|
||||
return d.toLocaleDateString('zh-CN', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
// 关闭处理
|
||||
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;
|
||||
padding: 20px;
|
||||
background: #fff;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.article-header {
|
||||
margin-bottom: 24px;
|
||||
padding-bottom: 16px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.article-title {
|
||||
font-size: 28px;
|
||||
font-weight: bold;
|
||||
color: #303133;
|
||||
margin: 0 0 16px 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.article-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16px;
|
||||
color: #909399;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background: #909399;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.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 {
|
||||
line-height: 1.8;
|
||||
color: #303133;
|
||||
font-size: 16px;
|
||||
|
||||
// 继承富文本编辑器的样式
|
||||
: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>
|
||||
2
schoolNewsWeb/src/views/article/index.ts
Normal file
2
schoolNewsWeb/src/views/article/index.ts
Normal file
@@ -0,0 +1,2 @@
|
||||
export { default as ArticleAddView } from './ArticleAddView.vue';
|
||||
export { default as ArticleShowView } from './ArticleShowView.vue';
|
||||
Reference in New Issue
Block a user