帖子编辑页,代码高亮显示和图片插入有bug

main
abab2320 1 week ago
parent 943cee36ec
commit f4a16c5ed1

@ -552,7 +552,7 @@ export default defineComponent({
height: 150px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
cursor: pointer;
}
.avatar-preview {

@ -0,0 +1,291 @@
<template>
<div class = "flow-container">
<div class="post-editor">
<!-- 顶部工具栏 -->
<div class="toolbar">
<el-button :icon="ArrowLeft" circle></el-button>
<el-select v-model="selectedCategory" placeholder="选择分区" class="category-select" size="large">
<el-option
v-for="item in categories"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<div class="icon-group">
<el-button :icon="Document " circle size="large"></el-button>
<el-button :icon="Picture" circle size="large" @click="handlePicture"></el-button>
<el-button :icon="ArrowLeft" circle size="large"></el-button>
<el-button :icon="ArrowRight" circle size="large"></el-button>
</div>
<div class="action-buttons">
<button class = "btn-primary btn">保存草稿</button>
<button class = "btn-secondary btn">定时发布</button>
<button class = "btn-primary btn">发布帖子</button>
</div>
</div>
</div>
<!-- 图片上传对话框 -->
<el-dialog v-model="uploadPictureDialog" title="上传图片" :show-close="false">
<el-upload
:auto-upload="false"
:show-file-list="false"
:on-change="handleImageUpload"
accept="image/*"
class="upload-dialog"
>
<img v-if="previewUrl" :src="previewUrl" class="avatar-preview"/>
<el-icon size = 30px v-else><Plus/></el-icon>
</el-upload>
<template #footer>
<button class = "btn btn-primary" @click="uploadPictureDialog = false,previewUrl = ''">取消</button>
<button class = "btn btn-primary" @click="uploadPictureDialog = false ,previewUrl = ''">确定</button>
</template>
</el-dialog>
<!-- 编辑器主体区域 -->
<div class="editor-body">
<!-- 左侧Markdown编辑框 -->
<div class="editor-pane card">
<el-input
v-model="title"
placeholder="请输入文章标题"
class="editor-title"
/>
<el-input
id="markdown-editor"
v-model="markdownText"
type="textarea"
:autosize="{ minRows: 20 }"
placeholder="正文"
class="markdown-input"
ref="elInputRef"
@click="saveCursor"
@keyup="saveCursor"
/>
</div>
<!-- 右侧Markdown预览框 -->
<div class="preview-pane card">
<div class="editor-title">{{ title || '请输入文章标题' }}</div>
<div
class="markdown-preview"
v-html="DOMPurify.sanitize(compiledMarkdown)"
></div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { marked } from 'marked'
import hljs from 'highlight.js'
import type { UploadFile } from 'element-plus'
import { markedHighlight } from 'marked-highlight'
import 'highlight.js/styles/github.css'
import { ArrowLeft, Document, Picture, ArrowRight } from '@element-plus/icons-vue'
import DOMPurify from 'dompurify'
const title = ref('')
const markdownText = ref('')
const elInputRef = ref();
const compiledMarkdown = ref('')
const selectedCategory = ref(null)
const categories = [
{ label: '分区1', value: 1 },
{ label: '分区2', value: 2 },
{ label: '分区3', value: 3 },
{ label: '分区4', value: 4 },
{ label: '分区5', value: 5 },
{ label: '分区6', value: 6 },
]
marked.use(
markedHighlight({
langPrefix: 'hljs language-',
highlight: (code: string, lang: string) => {
return hljs.highlightAuto(code, [lang]).value
},
})
)
watch(markdownText, async () => {
compiledMarkdown.value = await marked.parse(markdownText.value)
})
//
const uploadPictureDialog = ref(false)
const handlePicture = () => {
uploadPictureDialog.value = true
}
const previewUrl = ref('');
function handleImageUpload(rawFile: UploadFile) {
const file = rawFile.raw; // File
if (!file) return;
const reader = new FileReader();
reader.onload = () => {
const base64 = reader.result as string;
insertAtCursor(`\n![image](${base64})\n`);
previewUrl.value = base64;
};
reader.readAsDataURL(file);
}
//Markdown
let cursorPos = 0;
//
function saveCursor() {
const textarea = elInputRef.value?.$el.querySelector('textarea');
if (textarea) {
cursorPos = textarea.selectionStart;
}
}
//Markdown
function insertAtCursor(text: string) {
const textarea = elInputRef.value?.$el.querySelector('textarea');
if (!textarea) return;
const current = markdownText.value;
markdownText.value =
current.slice(0, cursorPos) + text + current.slice(cursorPos);
}
</script>
<style scoped lang="scss">
.post-editor {
position:fixed;
top:0;
left:0;
right:0;
height:60px;
padding-right:50px;
padding-left: 16px;
padding-bottom: 16px;
padding-top:16px;
background: #ead1fb;
z-index: 1000;
.toolbar {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
.el-button{
height: 55px;
width:55px;
::v-deep(.el-icon)
{
font-size:24px;
}
}
.category-select {
width: 300px;
}
.icon-group {
display: flex;
gap: 6px;
margin-left: 10px;
}
.title-input {
flex-grow: 1;
max-width: 400px;
}
.title-length {
color: #999;
margin-left: 8px;
}
.action-buttons {
margin-left: auto;
display: flex;
gap: 6px;
}
}
}
/* 图片上传窗口样式 */
.upload-dialog {
min-width: 95%;
min-height:300px;
margin:10px;
margin-left:20px;
border: 1px dashed #d9d9d9;
border-radius: 6px;
display: flex;
justify-content: center;
align-items: center;
align-self: center;
.el-dialog__header {
background-color: #f5f5f5;
border-bottom: 1px solid #e0e0e0;
}
.el-dialog__body {
border: 1px dashed #d9d9d9;
border-radius: 6px;
padding: 20px;
}
.avatar-preview {
width: 900px;
height: 500px;
object-fit: cover;
}
}
.editor-body {
display: flex;
flex-direction:row;
justify-content: space-between;
gap: 16px;
margin-top:20px;
min-height: 100%;
.editor-pane,
.preview-pane {
width:50%;
height:auto;
padding: 16px;
background: #fff;
border-radius: 8px;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.05);
}
.editor-title {
height:50px;
font-weight: bold;
margin-bottom: 16px;
font-size: 25px;
}
.markdown-input {
font-size:20px;
width: 100%;
}
.markdown-preview {
padding-top: 10px;
img {
max-width: 50%;
height: auto;
display: block;
margin: 0.5rem 0;
}
}
}
</style>
Loading…
Cancel
Save