feat(editor): 集成富文本编辑器并优化响应式布局

1. 添加wangEditor富文本编辑器替换原有简易编辑器
2. 新增编辑器相关CSS样式和功能按钮
3. 优化详情页和编辑页的响应式布局
4. 调整评论区域样式结构
5. 添加移动端适配样式
This commit is contained in:
DESKTOP-RQ919RC\Pc
2025-11-25 19:12:29 +08:00
parent 73731fbbba
commit 460450c339
12 changed files with 24728 additions and 171 deletions

File diff suppressed because one or more lines are too long

View File

@@ -71,6 +71,7 @@
padding: 40px 30px 35px;
position: relative;
display: none;
max-width: calc(100vw - 10px);
}
.coins-area .coins-box .fork {

View File

@@ -1,6 +1,7 @@
#details {
width: 1200px;
max-width: 1200px;
margin: 0 auto;
min-width: 320px;
}
#details .matter {
align-items: flex-start;
@@ -338,6 +339,7 @@
}
#details .matter .matter-left .related .list .item .text {
width: 352px;
flex: 1;
}
#details .matter .sidebar-box {
position: sticky;
@@ -845,30 +847,29 @@
border-top: 1px dotted #ebebeb;
}
.answer-discuss .comments-box .comments-item .comments-header {
justify-content: space-between;
margin-bottom: 9px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left {
.answer-discuss .comments-box .comments-item .comments-header {
font-size: 13px;
position: relative;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-avatar {
.answer-discuss .comments-box .comments-item .comments-header .comments-avatar {
width: 20px;
height: 20px;
margin-right: 10px;
border-radius: 50%;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-username {
.answer-discuss .comments-box .comments-item .comments-header .comments-username {
color: #555;
margin-right: 10px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-time {
.answer-discuss .comments-box .comments-item .comments-header .comments-time {
color: #aaaaaa;
margin-right: 8px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-identity {
.answer-discuss .comments-box .comments-item .comments-header .comments-identity {
font-size: 12px;
color: #7f7f7f;
padding: 0 3px;
@@ -877,18 +878,19 @@
border: 1px solid #d7d7d7;
border-radius: 5px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box {
position: relative;
margin-left: auto;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box:hover .operate-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box:hover .operate-box {
display: block;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .menu-icon {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .menu-icon {
width: 14px;
height: 14px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box {
display: none;
flex-direction: column;
position: absolute;
@@ -903,13 +905,13 @@
background-color: #f6f6f6;
border: 1px solid #d7d7d7;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box .item {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box .item {
height: 24px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box .item:not(:last-of-type) {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box .item:not(:last-of-type) {
border-bottom: 1px solid #d7d7d7;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box::after {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box::after {
content: "";
width: 58px;
height: 36px;
@@ -918,7 +920,7 @@
right: 0;
z-index: -1;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .report-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .report-box {
display: none;
position: absolute;
top: 24px;
@@ -932,7 +934,7 @@
color: #7f7f7f;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .report-box::after {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .report-box::after {
content: "";
width: 58px;
height: 36px;
@@ -940,23 +942,23 @@
top: -14px;
right: 0;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .comment-icon {
.answer-discuss .comments-box .comments-item .comments-header .comment-icon {
width: 14px;
height: 13px;
margin-left: 40px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .like-box {
.answer-discuss .comments-box .comments-item .comments-header .like-box {
font-size: 12px;
color: #aaa;
margin-left: 40px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .like-box .like-icon {
.answer-discuss .comments-box .comments-item .comments-header .like-box .like-icon {
width: 14px;
height: 14px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .like-box .like-quantity {
.answer-discuss .comments-box .comments-item .comments-header .like-box .like-quantity {
margin-left: 6px;
}
.answer-discuss .comments-box .comments-item .comments-content {
@@ -1251,3 +1253,89 @@
z-index: 1;
background-color: rgba(0, 0, 0, 0.20392157);
}
@media screen and (max-width: 850px) {
#details {
padding: 10px 10px 0;
}
#details .head-top {
display: none;
}
#details .head-navigation {
display: none;
}
#details .matter .matter-left {
margin-right: 0 !important;
}
#details .matter .matter-left .action-bar {
margin-right: 0 !important;
justify-content: space-around;
padding: 0 !important;
}
#details .matter .matter-left .action-bar .action-bar-item {
margin-right: 0 !important;
}
#details .matter .matter-left .related .related-head {
padding-left: 14px;
}
#details .matter .matter-left .related .list {
padding: 14px;
}
#details .matter .matter-left .related .list .item {
width: 100% !important;
}
#details .matter .matter-left .related .list .item:not(:last-child) {
margin-bottom: 7px;
}
#details .matter .matter-left .related .list .item .text {
width: calc(100vw - 100px);
}
#details .matter .sidebar-box {
display: none;
}
#details .answer-discuss {
padding: 15px;
}
#details .answer-discuss .input-box .picture-box {
width: calc(100vw - 68px);
}
#details .answer-discuss .input-box .bottom .operate .item .emoji-box {
position: fixed;
bottom: 0;
left: 0;
transform: translateX(0);
width: 100vw;
height: 300px;
top: auto;
overflow: auto;
border: none;
}
#details .answer-discuss .input-box .bottom .operate .item .emoji-box::after {
display: none;
}
#details .answer-discuss .comments-box .input-box .picture-box {
width: calc(100vw - 88px);
}
#details .answer-discuss .comments-box .comments-item .comments-header {
justify-content: inherit;
}
#details .answer-discuss .comments-box .comments-item .comments-header .menu-box {
margin-left: auto;
}
#details .answer-discuss .comments-box .comments-item .child-comments .input-box .picture-box {
width: calc(100vw - 128px);
}
}
@media screen and (max-width: 500px) {
#details .answer-discuss .comments-box .comments-item .comments-header {
font-size: 12px;
}
#details .answer-discuss .comments-box .comments-item .comments-header .comments-title {
height: 14px !important;
}
}
@media screen and (max-width: 450px) {
#details .answer-discuss .comments-box .comments-item .comments-header .comment-icon,
#details .answer-discuss .comments-box .comments-item .comments-header .like-box {
margin-left: 15px;
}
}

View File

@@ -1,6 +1,8 @@
#details {
width: 1200px;
// width: 1200px;
max-width: 1200px;
margin: 0 auto;
min-width: 320px;
.matter {
align-items: flex-start;
@@ -393,6 +395,7 @@
.text {
width: 352px;
flex: 1;
}
}
}
@@ -994,16 +997,16 @@
}
.answer-discuss .comments-box .comments-item .comments-header {
justify-content: space-between;
// justify-content: space-between;
margin-bottom: 9px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left {
.answer-discuss .comments-box .comments-item .comments-header {
font-size: 13px;
position: relative;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-avatar {
.answer-discuss .comments-box .comments-item .comments-header .comments-avatar {
width: 20px;
height: 20px;
margin-right: 10px;
@@ -1011,18 +1014,18 @@
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-username {
.answer-discuss .comments-box .comments-item .comments-header .comments-username {
color: #555;
margin-right: 10px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-time {
.answer-discuss .comments-box .comments-item .comments-header .comments-time {
color: #aaaaaa;
margin-right: 8px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-left .comments-identity {
.answer-discuss .comments-box .comments-item .comments-header .comments-identity {
font-size: 12px;
color: #7f7f7f;
padding: 0 3px;
@@ -1032,21 +1035,22 @@
border-radius: 5px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box {
position: relative;
margin-left: auto;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box:hover .operate-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box:hover .operate-box {
display: block;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .menu-icon {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .menu-icon {
width: 14px;
height: 14px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box {
display: none;
flex-direction: column;
position: absolute;
@@ -1062,15 +1066,15 @@
border: 1px solid rgba(215, 215, 215, 1);
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box .item {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box .item {
height: 24px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box .item:not(:last-of-type) {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box .item:not(:last-of-type) {
border-bottom: 1px solid rgba(215, 215, 215, 1);
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .operate-box::after {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .operate-box::after {
content: "";
width: 58px;
height: 36px;
@@ -1080,7 +1084,7 @@
z-index: -1;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .report-box {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .report-box {
display: none;
position: absolute;
top: 24px;
@@ -1095,7 +1099,7 @@
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .menu-box .report-box::after {
.answer-discuss .comments-box .comments-item .comments-header .menu-box .report-box::after {
content: "";
width: 58px;
height: 36px;
@@ -1104,26 +1108,26 @@
right: 0;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .comment-icon {
.answer-discuss .comments-box .comments-item .comments-header .comment-icon {
width: 14px;
height: 13px;
margin-left: 40px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .like-box {
.answer-discuss .comments-box .comments-item .comments-header .like-box {
font-size: 12px;
color: #aaa;
margin-left: 40px;
cursor: pointer;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .like-box .like-icon {
.answer-discuss .comments-box .comments-item .comments-header .like-box .like-icon {
width: 14px;
height: 14px;
}
.answer-discuss .comments-box .comments-item .comments-header .comments-header-right .like-box .like-quantity {
.answer-discuss .comments-box .comments-item .comments-header .like-box .like-quantity {
margin-left: 6px;
}
@@ -1458,3 +1462,145 @@
z-index: 1;
background-color: rgba(0, 0, 0, 0.20392157);
}
// 媒体查询 最大宽度 850px 时
@media screen and (max-width: 850px) {
#details {
padding: 10px 10px 0;
.head-top {
display: none;
}
.head-navigation {
display: none;
}
.matter {
.matter-left {
margin-right: 0 !important;
.action-bar {
margin-right: 0 !important;
justify-content: space-around;
padding: 0 !important;
.action-bar-item {
margin-right: 0 !important;
}
}
.related {
.related-head {
padding-left: 14px;
}
.list {
padding: 14px;
.item {
width: 100% !important;
&:not(:last-child) {
margin-bottom: 7px;
}
.text {
width: calc(100vw - 100px);
}
}
}
}
}
.sidebar-box {
display: none;
}
}
.answer-discuss {
padding: 15px;
.input-box {
.picture-box {
width: calc(100vw - 68px);
}
.bottom {
.operate {
.item {
.emoji-box {
position: fixed;
bottom: 0;
left: 0;
transform: translateX(0);
width: 100vw;
height: 300px;
top: auto;
overflow: auto;
border: none;
&::after {
display: none;
}
}
}
}
}
}
.comments-box {
.input-box {
.picture-box {
width: calc(100vw - 88px);
}
}
.comments-item {
.comments-header {
justify-content: inherit;
.menu-box {
margin-left: auto;
}
}
.child-comments {
.input-box {
.picture-box {
width: calc(100vw - 128px);
}
}
}
}
}
}
}
}
@media screen and (max-width: 500px) {
#details {
.answer-discuss {
.comments-box {
.comments-item {
.comments-header {
font-size: 12px;
.comments-title {
height: 14px !important;
}
}
}
}
}
}
}
@media screen and (max-width: 450px) {
#details {
.answer-discuss {
.comments-box {
.comments-item {
.comments-header {
.comment-icon,
.like-box {
margin-left: 15px;
}
}
}
}
}
}
}

27
css/editor.css Normal file

File diff suppressed because one or more lines are too long

View File

@@ -18,7 +18,7 @@
<body>
<sign-in-box></sign-in-box>
<div class="container" id="details" v-cloak>
<div class="templateValue" ref="uniqidRef">fi88yrHXiDSj</div>
<div class="templateValue" ref="uniqidRef">qXi0yrL189WW</div>
<div class="head-top flexacenter">
<img class="logo" src="https://oss.gter.net/logo" alt="" />
@@ -211,7 +211,6 @@
<div class="comments-box" v-if="commentTotalCount != 0">
<div class="comments-item" v-for="(item, index) in commentList" :key="index">
<div class="comments-header flexacenter">
<div class="comments-header-left flexacenter">
<img class="comments-avatar" @click="openUserInfo(index)" :src="item.avatar || item.user['avatar']" />
<div class="comments-username" @click="openUserInfo(index)">{{ item.nickname || item.user["nickname"] || "匿名用户" }}</div>
<div class="comments-time">{{ item["timestamp"] }}</div>
@@ -229,8 +228,7 @@
</a>
<div class="avatar-mask" @click="closeUserInfo(index)"></div>
</div>
</div>
<div class="comments-header-right flexacenter">
<div class="menu-box flexacenter">
<img class="menu-icon" src="./img/menu-icon-gray.svg" />
<!-- <div class="report-box flexcenter">举报</div> -->
@@ -247,7 +245,6 @@
<div class="like-quantity">{{ item["likenum"] || "" }}</div>
</div>
</div>
</div>
<div class="comments-content">
<div class="comments-text" v-if="item['content']" v-html="item['content']"></div>
<div class="comments-img-box">
@@ -285,7 +282,6 @@
<div class="child-comments" v-if="item['child'].length != 0">
<div class="comments-item" v-for="(ite, i) in item['child']" :key="ite.id">
<div class="comments-header flexacenter">
<div class="comments-header-left flexacenter">
<img class="comments-avatar" @click="openUserInfo(index, i)" :src="ite.avatar || ite.user['avatar']" />
<div class="comments-username" @click="openUserInfo(index, i)">{{ ite.nickname || ite.user["nickname"] || "匿名用户" }}</div>
<div class="comments-time">{{ ite["timestamp"] }}</div>
@@ -302,8 +298,6 @@
</a>
<div class="avatar-mask" @click="closeUserInfo(index, i)"></div>
</div>
</div>
<div class="comments-header-right flexacenter">
<div class="menu-box flexacenter">
<img class="menu-icon" src="./img/menu-icon-gray.svg" />
<div class="operate-box">
@@ -319,7 +313,6 @@
<div class="like-quantity">{{ ite["likenum"] || "" }}</div>
</div>
</div>
</div>
<div class="comments-content">
<div class="comments-text" v-if="ite['content']">
<div class="comments-reply" v-if="ite?.reply?.nickname">@{{ ite["reply"]["nickname"] || "匿名用户" }}</div>

View File

@@ -7,21 +7,40 @@
<title>发布帖子 - 轻论坛</title>
<link rel="stylesheet" href="./css/public.css" />
<link rel="stylesheet" href="./css/edit.css" />
<link href="./css/editor.css" rel="stylesheet" />
<script src="./js/vue.global.js"></script>
<style>
[v-cloak] {
display: none;
}
</style>
<style>
#editorwrapper {
/* border: 1px solid #ccc; */
z-index: 100;
/* 按需定义 */
}
#toolbar-container {
/* border-bottom: 1px solid #ccc; */
}
#editor-container {
min-height: 509px;
max-height: 80vh;
}
</style>
<script src="./js/editor.js"></script>
</head>
<body>
<div class="container" id="edit" v-cloak>
<div class="edit-head flexacenter">
<div class="edit-head-container flexacenter">
<a class="" href="/" target="_blank">
<img class="icon" src="{@/img/edit-logo-icon.png}" />
</a>
<a class="" href="/" target="_blank"><img class="icon" src="{@/img/edit-logo-icon.png}" /></a>
<div class="dot"></div>
<div class="title">发帖</div>
<div class="hint">发帖奖励 3 个寄托币/篇每天最高奖励3篇</div>
@@ -32,13 +51,13 @@
<div class="edit-container">
<!-- 标题输入 -->
<div class="title-box">
<input class="title-input" type="title" placeholder="输入标题(非必填)" v-model="info.title"
:maxlength="titleLength" />
<input class="title-input" type="title" placeholder="输入标题(非必填)" v-model="info.title" :maxlength="titleLength" />
<div class="sum">{{ info?.title?.length ? titleLength - info?.title?.length : titleLength }}</div>
</div>
<div id="editor—wrapper">
<!-- 工具栏 -->
<div class="editor-toolbar flexacenter">
<div id="toolbar-container" class="editor-toolbar flexacenter">
<div class="toolbar-item flexacenter h2" :class="{'pitch': isPTitle}" @click="paragraphTitle">
<img class="icon" src="{@/img/t-icon.png}" alt="段落标题" />
<span>段落标题</span>
@@ -58,8 +77,7 @@
<span>表情</span>
<div class="emoji-box-mask" @click.stop="closeEmoji"></div>
<div class="emoji-box">
<div class="emoji-icon" v-for="emoji in optionEmoji" :key="emoji"
@click.stop="selectEmoji(emoji)">{{ emoji }}</div>
<div class="emoji-icon" v-for="emoji in optionEmoji" :key="emoji" @click.stop="selectEmoji(emoji)">{{ emoji }}</div>
</div>
</div>
<div class="toolbar-item flexacenter link" :class="{'pitch': linkState}" @click="openLink">
@@ -78,17 +96,23 @@
<div class="btn flexcenter" @click.stop="insertLink">OK</div>
</div>
</div>
<div class="toolbar-item flexacenter expression" :class="{'pitch': emojiState}" @click="overstriking">
<img class="icon" src="{@/img/overstriking-icon.png}" alt="加粗" />
<span>加粗</span>
<div class="emoji-box-mask" @click.stop="closeEmoji"></div>
<div class="emoji-box">
<div class="emoji-icon" v-for="emoji in optionEmoji" :key="emoji" @click.stop="selectEmoji(emoji)">{{ emoji }}</div>
</div>
</div>
</div>
<!-- 内容编辑区 -->
<div class="content-input" id="editor" contenteditable="true" :class="{ 'empty': isEmpty }"
placeholder="输入正文" ref="editorRef" @input="onEditorInput" @focus="onEditorFocus" @blur="onEditorBlur"
v-html="info.content" @click="handleClick"></div>
<!-- 标签选择 -->
<!-- <div class="tags-list flexacenter">
<div class="tag-item" v-for="item in tagList" :key="item.tagId" @click="insertLabel(item.tagId)">#{{ item.title }}</div>
</div> -->
<!-- <div id="toolbar-container"></div> -->
<div id="editor-container"><!-- 编辑器 --></div>
</div>
<!-- 内容编辑区 -->
<!-- <div class="content-input" id="editor" contenteditable="true" :class="{ 'empty': isEmpty }" placeholder="输入正文" ref="editorRef" @input="onEditorInput" @focus="onEditorFocus" @blur="onEditorBlur" v-html="info.content" @click="handleClick"></div> -->
<!-- 操作按钮 -->
<div class="action-buttons flexacenter">

BIN
img/between -icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 261 B

BIN
img/overstriking-icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -15,9 +15,6 @@ const appSectionIndex = createApp({
setup() {
onMounted(() => {
getUserInfoWin();
setTimeout(() => {
SignInComponent.initComponent();
}, 3000);
});
let isLogin = ref(false);

View File

@@ -1,6 +1,8 @@
// 简单版本的论坛编辑器,确保图片插入功能正常
const { createApp, ref, computed, onMounted, nextTick, onUnmounted } = Vue;
import { headTop } from "../component/head-top/head-top.js";
const { createEditor, createToolbar } = window.wangEditor;
console.log("createEditor", createEditor);
const editApp = createApp({
setup() {
@@ -12,11 +14,9 @@ const editApp = createApp({
uniqid.value = params.uniqid || "";
getUserInfoWin();
checkWConfig();
cUpload();
init();
checkWConfig();
// 添加selectionchange事件监听当鼠标选中区域内容时更新lastSelection
document.addEventListener("selectionchange", handleSelectionChange);
@@ -107,6 +107,10 @@ const editApp = createApp({
ajaxGet(`/v2/api/config/upload?type=topic`).then((res) => {
const data = res.data;
uConfigData = data;
console.log("uConfigData", uConfigData);
init();
});
};
@@ -132,15 +136,153 @@ const editApp = createApp({
info.value = infoTarget;
token.value = data.token;
nextTick(() => {
judgeIsEmpty();
});
// nextTick(() => {
// judgeIsEmpty();
// });
initEditor();
})
.catch((err) => {
console.log("err", err);
});
};
let editor = null;
const initEditor = () => {
let infoTarget = info.value || {};
console.log("infoTarget", infoTarget);
const editorConfig = {
placeholder: "Type here...",
enabledMenus: [],
MENU_CONF: {
["emotion"]: {
emotions: optionEmoji.value,
},
["uploadImage"]: {
server: uConfigData.url,
// form-data fieldName ,默认值 'wangeditor-uploaded-image'
fieldName: uConfigData.requestName,
// 单个文件的最大体积限制,默认为 2M
maxFileSize: maxSize, // 1M
// 最多可上传几个文件,默认为 100
maxNumberOfFiles: imageLength,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes: ["image/*", ".png", ".jpg", ".jpeg"],
// 自定义上传参数,例如传递验证的 token 等。参数会被添加到 formData 中,一起上传到服务端。
meta: { ...uConfigData.params },
// 将 meta 拼接到 url 参数中,默认 false
metaWithUrl: false,
// 自定义增加 http header
headers: { accept: "application/json, text/plain, */*", ...uConfigData.headers },
// 跨域是否传递 cookie ,默认为 false
withCredentials: true,
// 超时时间,默认为 10 秒
async customUpload(file, insertFn) {
try {
let config = uConfigData;
// 1. 构造 FormData包含你的接口所需字段
const formData = new FormData();
formData.append(config.requestName, file); // 文件数据
formData.append("name", file.name); // 文件名
formData.append("type", "image"); // 文件名
formData.append("data", config.params.data); // 文件名
ajax(config.url, formData).then((res) => {
const data = res.data;
insertFn(data.url); // 传入图片的可访问 URL
});
} catch (err) {
console.error("上传出错:", err);
}
},
},
},
// 4. 链接菜单:显式启用(默认启用,补充配置防止被过滤)
link: {
disabled: false, // 确保不禁用
showTarget: true, // 显示「是否新窗口打开」选项
showRel: true, // 显示「rel 属性」选项
},
// 5. 对齐菜单:显式启用(默认启用,兜底配置)
justify: {
disabled: false,
},
onChange(editor) {
const html = editor.getHtml();
console.log("editor content", html);
// 也可以同步到 <textarea>
},
};
editor = createEditor({
selector: "#editor-container",
html: "<p><br>555</p>",
config: editorConfig,
mode: "default",
});
setTimeout(() => {
console.log("editor", editor);
editor.addMark("bold", true); // 加粗
}, 1000);
// const toolbar = DomEditor.getToolbar(editor)
const toolbarConfig = {
// toolbarKeys: ["bold", "italic", "list"],
toolbarKeys: [
"headerSelect", // 标题
"bold", // 粗体
"italic", // 斜体
// "justify", // 对齐方式
{
key: "justifyCenter",
title: "对齐",
iconSvg: '<svg viewBox="0 0 1024 1024"><path d="M768 793.6v102.4H51.2v-102.4h716.8z m204.8-230.4v102.4H51.2v-102.4h921.6z m-204.8-230.4v102.4H51.2v-102.4h716.8zM972.8 102.4v102.4H51.2V102.4h921.6z"></path></svg>',
menuKeys: ["justifyLeft", "justifyRight", "justifyCenter", "justifyJustify"],
},
"emotion", // 表情
"insertLink", // 插入链接
"uploadImage", // 插入图片
"uploadVideo", // 插入视频
"undo", // 撤销
"redo", // 重做
"fullScreen", // 全屏
],
};
const toolbar = createToolbar({
editor,
selector: "#toolbar-container",
config: toolbarConfig,
mode: "default",
});
console.log("toolbar", toolbar);
// setTimeout(() => {
// const el = document.querySelector("#toolbar-container");
// if (el && el.children.length === 0) {
// createToolbar({ editor, selector: "#toolbar-container", config: toolbarConfig, mode: "default" });
// }
// }, 0);
};
const restoreHtml = (formattedText, attachments) => {
const imageList = attachments?.images || [];
@@ -196,7 +338,7 @@ const editApp = createApp({
html = html.replace(/(<span class="blue">[^<]+<\/span>)\s+/gi, '$1 <span class="fill"></span> ');
// 7. 清理多余的<br>标签
html = html.replace(/<br><br>/g, "<br>");
// html = html.replace(/<br><br>/g, "<br>");
imageList.forEach((element) => {
html += `<img src="${element.url}" data-aid="${element.aid}"><br/>`;
@@ -218,6 +360,7 @@ const editApp = createApp({
const editorRef = ref(null);
const focusLastNode = () => {
return;
const newRange = document.createRange();
const textNode = document.createTextNode("");
editorRef.value.appendChild(textNode);
@@ -408,6 +551,7 @@ const editApp = createApp({
// 处理选中文本变化的函数
const handleSelectionChange = () => {
return;
const selection = window.getSelection();
// 确保有选中内容且选中区域在编辑器内
if (selection.rangeCount > 0) {
@@ -560,7 +704,7 @@ const editApp = createApp({
html = html.replace(/<(?!(a\b|\/a\b))[^>]+>/gi, "");
// 8. 清理连续换行(最多保留两个空行,避免过多空行)
html = html.replace(/\n{3,}/g, "\n\n");
// html = html.replace(/\n{3,}/g, "\n\n");
// 去除首尾空白
html = html.trim();
@@ -745,7 +889,15 @@ const editApp = createApp({
const linkClick = () => {};
return { linkClick, insertVideo, insertLink, linkUrl, linkText, linkState, openLink, closeLink, handleClick, uniqid, userInfoWin, titleLength, submit, emojiState, openEmoji, closeEmoji, selectEmoji, optionEmoji, isPTitle, onEditorInput, onEditorFocus, onEditorBlur, paragraphTitle, info, tagList, token, cutAnonymity, editorRef, insertImage, judgeIsEmpty, isEmpty };
const overstriking = () => {
console.log("加粗");
editor.addMark("bold", true); // 加粗
editor.addMark("color", "#999"); // 文本颜色
console.log("editor", editor.addMark);
};
return { overstriking, linkClick, insertVideo, insertLink, linkUrl, linkText, linkState, openLink, closeLink, handleClick, uniqid, userInfoWin, titleLength, submit, emojiState, openEmoji, closeEmoji, selectEmoji, optionEmoji, isPTitle, onEditorInput, onEditorFocus, onEditorBlur, paragraphTitle, info, tagList, token, cutAnonymity, editorRef, insertImage, judgeIsEmpty, isEmpty };
},
});

24129
js/editor.js Normal file

File diff suppressed because one or more lines are too long