frontend/src/views/news-management/EditNews.vue

487 lines
12 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.

<script setup>
import {onMounted, ref} from 'vue';
import {ElButton, ElForm, ElFormItem, ElInput, ElMessage, ElOption, ElSelect, ElUpload} from 'element-plus';
import {
AccessibilityHelp,
Alignment,
Autoformat,
AutoLink,
Autosave,
BalloonToolbar,
BlockQuote,
Bold,
ClassicEditor,
CodeBlock,
Essentials,
FindAndReplace,
GeneralHtmlSupport,
Heading,
HorizontalLine,
Indent,
IndentBlock,
Italic,
Link,
Paragraph,
SelectAll,
Style,
Table,
TableCaption,
TableCellProperties,
TableColumnResize,
TableProperties,
TableToolbar,
TextTransformation,
Undo
} from 'ckeditor5';
import translations from 'ckeditor5/translations/zh-cn.js';
import 'ckeditor5/ckeditor5.css';
import {Delete, Plus, Refresh, ZoomIn} from "@element-plus/icons-vue";
import {useRoute, useRouter} from "vue-router";
import {useStore} from "vuex";
import axios from "axios";
const route = useRoute();
const router = useRouter();
const store = useStore();
const form = ref({
token: '',
title: '',
author: '',
content: '',
summary: '',
tenant: '',
imagePath: ''
});
const createMode = ref(false);
const modeTitle = ref('');
const fileList = ref([]); // 用于存储上传文件的列表
const basePath = '/api/news'
const uploadUrl = basePath + '/uploadPic'; // 上传的后端地址
const dialogImageUrl = ref('');
const dialogVisible = ref(false); // 用于图片放大预览
const beforeUpload = (file) => {
// 检查文件类型是否为图片png, jpg, jpeg
const isImage = file.type === 'image/png' || file.type === 'image/jpg' || file.type === 'image/jpeg';
if (!isImage) {
ElMessage.error('只能上传 png/jpg/jpeg 格式的图片文件!');
return false;
}
// 检查文件大小是否超过 5MB
const isLt5M = file.size / 1024 / 1024 < 5;
if (!isLt5M) {
ElMessage.error('上传文件大小不能超过 5MB');
return false;
}
return true;
};
const handleSuccess = (response, file, fileList) => {
file.url = response.url;
};
const handleError = (error, file, fileList) => {
ElMessage.error('图片上传失败,请重试!');
};
const handleChange = (file, fileList) => {
// 处理文件选择变化时的逻辑,确保只选择最新上传的文件
if (fileList.length > 1) {
fileList.splice(0, fileList.length - 1)
}
};
const handlePictureCardPreview = (file) => {
dialogImageUrl.value = file.url
dialogVisible.value = true
}
const handleRemove = (file) => {
fileList.value = fileList.value.filter((item) => item.uid !== file.uid);
console.log(file)
}
const handleCommit = async () => {
form.value.token = store.getters['authentication/token'];
if (createMode.value) {
try {
form.value.imagePath = fileList.value[0].url;
await axios.post(basePath, form.value);
ElMessage.success('添加成功!');
router.push('/news');
} catch (e) {
ElMessage.error('添加失败,请重试!');
}
} else {
try {
const res = await axios.put(basePath, form.value);
ElMessage.success('修改成功!');
router.push('/news');
} catch (e) {
ElMessage.error('修改失败,请重试!');
}
}
};
const isLayoutReady = ref(false);
const config = ref(null); // CKEditor needs the DOM tree before calculating the configuration.
const editor = ClassicEditor;
onMounted(() => {
if (route.query.mode === 'create') {
createMode.value = true;
modeTitle.value = '添加资讯';
}
if (route.query.mode === 'edit') {
createMode.value = false;
modeTitle.value = '修改资讯';
fetchNewsDetail();
}
config.value = {
toolbar: {
items: [
'undo',
'redo',
'|',
'heading',
'style',
'|',
'bold',
'italic',
'|',
'link',
'insertTable',
'blockQuote',
'codeBlock',
'|',
'alignment',
'|',
'indent',
'outdent'
],
shouldNotGroupWhenFull: false
},
plugins: [
AccessibilityHelp,
Alignment,
Autoformat,
AutoLink,
Autosave,
BalloonToolbar,
BlockQuote,
Bold,
CodeBlock,
Essentials,
FindAndReplace,
GeneralHtmlSupport,
Heading,
HorizontalLine,
Indent,
IndentBlock,
Italic,
Link,
Paragraph,
SelectAll,
Style,
Table,
TableCaption,
TableCellProperties,
TableColumnResize,
TableProperties,
TableToolbar,
TextTransformation,
Undo
],
balloonToolbar: ['bold', 'italic', '|', 'link'],
heading: {
options: [
{
model: 'paragraph',
title: 'Paragraph',
class: 'ck-heading_paragraph'
},
{
model: 'heading1',
view: 'h1',
title: 'Heading 1',
class: 'ck-heading_heading1'
},
{
model: 'heading2',
view: 'h2',
title: 'Heading 2',
class: 'ck-heading_heading2'
},
{
model: 'heading3',
view: 'h3',
title: 'Heading 3',
class: 'ck-heading_heading3'
},
{
model: 'heading4',
view: 'h4',
title: 'Heading 4',
class: 'ck-heading_heading4'
},
{
model: 'heading5',
view: 'h5',
title: 'Heading 5',
class: 'ck-heading_heading5'
},
{
model: 'heading6',
view: 'h6',
title: 'Heading 6',
class: 'ck-heading_heading6'
}
]
},
htmlSupport: {
allow: [
{
name: /^.*$/,
styles: true,
attributes: true,
classes: true
}
]
},
initialData: '',
language: 'zh-cn',
link: {
addTargetToExternalLinks: true,
defaultProtocol: 'https://',
decorators: {
toggleDownloadable: {
mode: 'manual',
label: 'Downloadable',
attributes: {
download: 'file'
}
}
}
},
menuBar: {
isVisible: true
},
placeholder: '请在此处输入你的文章',
style: {
definitions: [
{
name: 'Article category',
element: 'h3',
classes: ['category']
},
{
name: 'Title',
element: 'h2',
classes: ['document-title']
},
{
name: 'Subtitle',
element: 'h3',
classes: ['document-subtitle']
},
{
name: 'Info box',
element: 'p',
classes: ['info-box']
},
{
name: 'Side quote',
element: 'blockquote',
classes: ['side-quote']
},
{
name: 'Marker',
element: 'span',
classes: ['marker']
},
{
name: 'Spoiler',
element: 'span',
classes: ['spoiler']
},
{
name: 'Code (dark)',
element: 'pre',
classes: ['fancy-code', 'fancy-code-dark']
},
{
name: 'Code (bright)',
element: 'pre',
classes: ['fancy-code', 'fancy-code-bright']
}
]
},
table: {
contentToolbar: ['tableColumn', 'tableRow', 'mergeTableCells', 'tableProperties', 'tableCellProperties']
},
translations: [translations]
};
isLayoutReady.value = true;
});
</script>
<template>
<div class="form-container">
<el-form :model="form" label-width="120px">
<h2>{{ modeTitle }}</h2>
<el-form-item label="新闻标题">
<el-input v-model="form.title" placeholder="请输入新闻标题"></el-input>
</el-form-item>
<el-form-item label="新闻图片路径">
<el-upload
:action="uploadUrl"
:limit="2"
:before-upload="beforeUpload"
:on-success="handleSuccess"
:on-error="handleError"
:on-change="handleChange"
:file-list="fileList"
list-type="picture-card"
auto-upload
v-model:file-list="fileList"
>
<template #file="{ file }">
<div>
<img class="el-upload-list__item-thumbnail" :src="file.url" alt=""/>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<el-icon><zoom-in/></el-icon>
</span>
<span
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<el-icon><Delete/></el-icon>
</span>
</span>
</div>
</template>
<el-icon v-if="fileList.length === 0">
<Plus/>
</el-icon>
<el-icon v-else>
<Refresh/>
</el-icon>
</el-upload>
<div class="tip">
请上传大小不超过 <span style="color: red;">5MB</span> 格式为 <span style="color: red;">png/jpg/jpeg</span> 的文件
</div>
</el-form-item>
<el-form-item label="新闻内容">
<div class="editor-container editor-container_classic-editor editor-container_include-style"
ref="editorContainerElement">
<div class="editor-container__editor">
<div ref="editorElement">
<ckeditor v-if="isLayoutReady" v-model="form.content" :editor="editor" :config="config"/>
</div>
</div>
</div>
</el-form-item>
<el-form-item label="作者">
<el-input v-model="form.author" placeholder="请输入作者"></el-input>
</el-form-item>
<el-form-item label="新闻简介">
<el-input v-model="form.summary" placeholder="请输入新闻简介"></el-input>
</el-form-item>
<el-form-item label="选择租户">
<el-select v-model="form.tenant" placeholder="请选择" class="dynamic-width-select">
<el-option label="Option 1" value="option1"></el-option>
<el-option label="Option 2" value="option2"></el-option>
<el-option label="Option 3" value="option3"></el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleCommit">确定</el-button>
<el-button>取消</el-button>
</el-form-item>
</el-form>
</div>
<el-dialog v-model="dialogVisible" class="image-preview">
<img w-full :src="dialogImageUrl" alt="Preview Image"/>
</el-dialog>
</template>
<style scoped>
.form-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
background: white;
overflow-y: auto; /* 使容器在内容溢出时出现滚动条 */
}
.image-preview {
display: flex;
justify-content: center;
align-items: center;
}
.image-preview img {
max-width: 100%;
height: auto;
}
.tip {
margin-top: 8px;
color: #727272;
font-size: 12px;
}
.el-form {
background: white;
padding: 2rem;
border-radius: 8px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
width: 55%;
max-height: 90vh; /* 确保表单不会超过视口高度 */
overflow-y: auto; /* 使表单在内容溢出时出现滚动条 */
}
.el-form-item {
margin-bottom: 1rem;
text-align: left;
}
.el-form-item label {
display: block;
margin-bottom: 0.5rem;
}
.el-input,
.el-select {
width: 100%;
}
.el-button--primary {
background-color: #007bff;
color: white;
}
.el-button--primary:hover {
background-color: #0056b3;
}
.dynamic-width-select {
min-width: 30%;
max-width: 100%;
width: auto; /* 使宽度根据内容调整 */
}
</style>