mod: rebuild editor
This commit is contained in:
parent
f944b4cc1a
commit
07ce954a2d
86
src/components/wang-editor/Title.vue
Normal file
86
src/components/wang-editor/Title.vue
Normal file
|
@ -0,0 +1,86 @@
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { onBeforeMount, ref } from 'vue'
|
||||||
|
import WangEditor from '@/components/wang-editor/WangEditor.vue'
|
||||||
|
import KUNGalgameFooter from '@/components/KUNGalgameFooter.vue'
|
||||||
|
|
||||||
|
// 导入编辑帖子的 store
|
||||||
|
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
||||||
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
|
// 导入防抖函数
|
||||||
|
import { debounce } from '@/utils/debounce'
|
||||||
|
|
||||||
|
const topicData = storeToRefs(useKUNGalgameEditStore())
|
||||||
|
|
||||||
|
// 话题标题的文字
|
||||||
|
const topicTitle = ref('')
|
||||||
|
// 标题的最大长度
|
||||||
|
const maxInputLength = 40
|
||||||
|
|
||||||
|
onBeforeMount(() => {
|
||||||
|
if (topicData.isSave.value) {
|
||||||
|
topicTitle.value = topicData.title.value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 处理用户输入
|
||||||
|
const handelInput = () => {
|
||||||
|
// 标题不能超过 40 字
|
||||||
|
if (topicTitle.value.length > maxInputLength) {
|
||||||
|
topicTitle.value = topicTitle.value.slice(0, maxInputLength)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户输入了纯空格
|
||||||
|
if (topicTitle.value.trim() === '') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建一个防抖处理函数
|
||||||
|
const debouncedInput = debounce(() => {
|
||||||
|
// 过滤 xss
|
||||||
|
topicData.title.value = topicTitle.value
|
||||||
|
}, 300)
|
||||||
|
|
||||||
|
// 调用防抖处理函数,会在延迟时间内只执行一次更新操作
|
||||||
|
debouncedInput()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 话题的标题 -->
|
||||||
|
<div class="title">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="请输入话题的标题(40字以内)"
|
||||||
|
v-model="topicTitle"
|
||||||
|
@input="handelInput"
|
||||||
|
:maxlength="maxInputLength"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/* 话题的发布标题 */
|
||||||
|
.title {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 话题标题的输入框 */
|
||||||
|
.title input {
|
||||||
|
color: var(--kungalgame-font-color-2);
|
||||||
|
/* 距离外轮廓的距离 */
|
||||||
|
padding: 7px;
|
||||||
|
/* 内边距盒子 */
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 100%;
|
||||||
|
/* 标题输入字体大小 */
|
||||||
|
font-size: 40px;
|
||||||
|
border: none;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 标题输入框 focus 之后的样式 */
|
||||||
|
.title input:focus {
|
||||||
|
box-shadow: 0px 0px 5px var(--kungalgame-blue-4);
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -2,14 +2,32 @@
|
||||||
编辑器实例共用组件
|
编辑器实例共用组件
|
||||||
-->
|
-->
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import {
|
||||||
|
defineAsyncComponent,
|
||||||
|
onBeforeMount,
|
||||||
|
onBeforeUnmount,
|
||||||
|
ref,
|
||||||
|
shallowRef,
|
||||||
|
} from 'vue'
|
||||||
|
// 编辑器本体配置
|
||||||
import '@wangeditor/editor/dist/css/style.css'
|
import '@wangeditor/editor/dist/css/style.css'
|
||||||
import '@/styles/editor/editor.scss'
|
import '@/styles/editor/editor.scss'
|
||||||
import { IDomEditor } from '@wangeditor/editor'
|
import { IDomEditor, IEditorConfig } from '@wangeditor/editor'
|
||||||
import { onBeforeMount, onBeforeUnmount, ref, shallowRef } from 'vue'
|
import { Editor } from '@wangeditor/editor-for-vue'
|
||||||
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
|
||||||
|
// 编辑器菜单配置
|
||||||
|
import { IToolbarConfig } from '@wangeditor/editor'
|
||||||
|
import { Toolbar } from '@wangeditor/editor-for-vue'
|
||||||
|
|
||||||
|
// 导入标题
|
||||||
|
const Title = defineAsyncComponent(
|
||||||
|
() => import('@/components/wang-editor/Title.vue')
|
||||||
|
)
|
||||||
|
|
||||||
// 导入编辑帖子的 store
|
// 导入编辑帖子的 store
|
||||||
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
|
||||||
// 导入过滤 xss 的工具
|
// 导入过滤 xss 的工具
|
||||||
import DOMPurify from 'dompurify'
|
import DOMPurify from 'dompurify'
|
||||||
// 导入防抖函数
|
// 导入防抖函数
|
||||||
|
@ -18,7 +36,12 @@ import { debounce } from '@/utils/debounce'
|
||||||
const topicData = storeToRefs(useKUNGalgameEditStore())
|
const topicData = storeToRefs(useKUNGalgameEditStore())
|
||||||
|
|
||||||
// 定义父组件传参
|
// 定义父组件传参
|
||||||
const props = defineProps(['height', 'isShowToolbar', 'isShowAdvance'])
|
const props = defineProps<{
|
||||||
|
height: number
|
||||||
|
isShowToolbar: boolean
|
||||||
|
isShowAdvance: boolean
|
||||||
|
isShowTitle: boolean
|
||||||
|
}>()
|
||||||
|
|
||||||
// 自定义编辑区域高度
|
// 自定义编辑区域高度
|
||||||
const editorHeight = `height: ${props.height}px`
|
const editorHeight = `height: ${props.height}px`
|
||||||
|
@ -32,7 +55,7 @@ const valueHtml = ref('')
|
||||||
const textCount = ref(0)
|
const textCount = ref(0)
|
||||||
|
|
||||||
// 编辑器相关配置
|
// 编辑器相关配置
|
||||||
const editorConfig = {
|
const editorConfig: Partial<IEditorConfig> = {
|
||||||
placeholder: 'Moe Moe Moe!',
|
placeholder: 'Moe Moe Moe!',
|
||||||
readOnly: false,
|
readOnly: false,
|
||||||
MENU_CONF: {
|
MENU_CONF: {
|
||||||
|
@ -53,7 +76,18 @@ const editorConfig = {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleCreated = (editor: IDomEditor) => {}
|
const keys: string[] = []
|
||||||
|
|
||||||
|
// 工具栏相关配置
|
||||||
|
const toolbarConfig: Partial<IToolbarConfig> = {
|
||||||
|
excludeKeys: ['uploadVideo'].concat(keys),
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCreated = (editor: IDomEditor) => {
|
||||||
|
console.log(editor.getAllMenuKeys())
|
||||||
|
|
||||||
|
editorRef.value = editor // 记录 editor 实例,重要!
|
||||||
|
}
|
||||||
|
|
||||||
// 挂载之前载入数据,如果不保存,则不载入
|
// 挂载之前载入数据,如果不保存,则不载入
|
||||||
onBeforeMount(() => {
|
onBeforeMount(() => {
|
||||||
|
@ -92,9 +126,11 @@ const handleChange = (editor: IDomEditor) => {
|
||||||
<Toolbar
|
<Toolbar
|
||||||
class="toolbar-container"
|
class="toolbar-container"
|
||||||
:editor="editorRef"
|
:editor="editorRef"
|
||||||
|
:defaultConfig="toolbarConfig"
|
||||||
:mode="$props.isShowAdvance ? 'default' : 'simple'"
|
:mode="$props.isShowAdvance ? 'default' : 'simple'"
|
||||||
v-show="props.isShowToolbar"
|
v-show="props.isShowToolbar"
|
||||||
/>
|
/>
|
||||||
|
<Title />
|
||||||
<Editor
|
<Editor
|
||||||
:style="editorHeight"
|
:style="editorHeight"
|
||||||
v-model="valueHtml"
|
v-model="valueHtml"
|
||||||
|
@ -109,16 +145,11 @@ const handleChange = (editor: IDomEditor) => {
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
/* 编辑器的样式 */
|
/* 编辑器的样式 */
|
||||||
.editor—wrapper {
|
.editor—wrapper {
|
||||||
/* 编辑器的 border */
|
|
||||||
border: 1px solid var(--kungalgame-blue-4);
|
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
/* 编辑器的宽度 */
|
/* 编辑器的宽度 */
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
z-index: 1008; /* 按需定义 */
|
z-index: 9999;
|
||||||
}
|
|
||||||
.toolbar-container {
|
|
||||||
border-bottom: 1px solid var(--kungalgame-blue-4);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
|
@ -128,7 +159,7 @@ const handleChange = (editor: IDomEditor) => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: end;
|
justify-content: end;
|
||||||
color: var(--kungalgame-font-color-0);
|
color: var(--kungalgame-font-color-0);
|
||||||
background-color: var(--kungalgame-white);
|
background-color: var(--kungalgame-white-9);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
62
src/components/wang-editor/key.js
Normal file
62
src/components/wang-editor/key.js
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
;[
|
||||||
|
'bold',
|
||||||
|
'underline',
|
||||||
|
'italic',
|
||||||
|
'through',
|
||||||
|
'code',
|
||||||
|
'sub',
|
||||||
|
'sup',
|
||||||
|
'clearStyle',
|
||||||
|
'color',
|
||||||
|
'bgColor',
|
||||||
|
'fontSize',
|
||||||
|
'fontFamily',
|
||||||
|
'indent',
|
||||||
|
'delIndent',
|
||||||
|
'justifyLeft',
|
||||||
|
'justifyRight',
|
||||||
|
'justifyCenter',
|
||||||
|
'justifyJustify',
|
||||||
|
'lineHeight',
|
||||||
|
'insertImage',
|
||||||
|
'deleteImage',
|
||||||
|
'editImage',
|
||||||
|
'viewImageLink',
|
||||||
|
'imageWidth30',
|
||||||
|
'imageWidth50',
|
||||||
|
'imageWidth100',
|
||||||
|
'divider',
|
||||||
|
'emotion',
|
||||||
|
'insertLink',
|
||||||
|
'editLink',
|
||||||
|
'unLink',
|
||||||
|
'viewLink',
|
||||||
|
'codeBlock',
|
||||||
|
'blockquote',
|
||||||
|
'headerSelect',
|
||||||
|
'header1',
|
||||||
|
'header2',
|
||||||
|
'header3',
|
||||||
|
'header4',
|
||||||
|
'header5',
|
||||||
|
'todo',
|
||||||
|
'redo',
|
||||||
|
'undo',
|
||||||
|
'fullScreen',
|
||||||
|
'enter',
|
||||||
|
'bulletedList',
|
||||||
|
'numberedList',
|
||||||
|
'insertTable',
|
||||||
|
'deleteTable',
|
||||||
|
'insertTableRow',
|
||||||
|
'deleteTableRow',
|
||||||
|
'insertTableCol',
|
||||||
|
'deleteTableCol',
|
||||||
|
'tableHeader',
|
||||||
|
'tableFullWidth',
|
||||||
|
'insertVideo',
|
||||||
|
'uploadVideo',
|
||||||
|
'editVideoSize',
|
||||||
|
'uploadImage',
|
||||||
|
'codeSelectLang',
|
||||||
|
]
|
|
@ -5,7 +5,7 @@ import 'animate.css'
|
||||||
|
|
||||||
import { currBackground } from '@/hooks/useBackgroundPicture'
|
import { currBackground } from '@/hooks/useBackgroundPicture'
|
||||||
|
|
||||||
import KUNGalgameTopBar from '@/components/TopBar/KUNGalgameTopBar.vue'
|
import KUNGalgameTopBar from '@/components/top-bar/KUNGalgameTopBar.vue'
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -29,12 +29,12 @@ u {
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
/* textarea - css vars */
|
/* textarea - css vars */
|
||||||
--w-e-textarea-bg-color: var(--kungalgame-white);
|
--w-e-textarea-bg-color: var(--kungalgame-white-9);
|
||||||
--w-e-textarea-color: var(--kungalgame-font-color-3);
|
--w-e-textarea-color: var(--kungalgame-font-color-3);
|
||||||
--w-e-textarea-border-color: var(--kungalgame-blue-1);
|
--w-e-textarea-border-color: var(--kungalgame-blue-1);
|
||||||
--w-e-textarea-slight-border-color: var(--kungalgame-blue-4);
|
--w-e-textarea-slight-border-color: var(--kungalgame-blue-4);
|
||||||
--w-e-textarea-slight-color: var(--kungalgame-font-color-0);
|
--w-e-textarea-slight-color: var(--kungalgame-font-color-0);
|
||||||
--w-e-textarea-slight-bg-color: var(--kungalgame-white);
|
--w-e-textarea-slight-bg-color: var(--kungalgame-white-9);
|
||||||
--w-e-textarea-selected-border-color: var(--kungalgame-blue-1);
|
--w-e-textarea-selected-border-color: var(--kungalgame-blue-1);
|
||||||
/* 选中的元素,如选中了分割线 */
|
/* 选中的元素,如选中了分割线 */
|
||||||
--w-e-textarea-handler-bg-color: var(--kungalgame-blue-4);
|
--w-e-textarea-handler-bg-color: var(--kungalgame-blue-4);
|
||||||
|
@ -42,9 +42,9 @@ u {
|
||||||
|
|
||||||
/* toolbar - css vars */
|
/* toolbar - css vars */
|
||||||
--w-e-toolbar-color: var(--kungalgame-font-color-1);
|
--w-e-toolbar-color: var(--kungalgame-font-color-1);
|
||||||
--w-e-toolbar-bg-color: var(--kungalgame-white);
|
--w-e-toolbar-bg-color: var(--kungalgame-trans-blue-0);
|
||||||
--w-e-toolbar-active-color: var(--kungalgame-font-color-3);
|
--w-e-toolbar-active-color: var(--kungalgame-font-color-3);
|
||||||
--w-e-toolbar-active-bg-color: var(--kungalgame-white);
|
--w-e-toolbar-active-bg-color: var(--kungalgame-trans-blue-1);
|
||||||
--w-e-toolbar-disabled-color: var(--kungalgame-red-4);
|
--w-e-toolbar-disabled-color: var(--kungalgame-red-4);
|
||||||
--w-e-toolbar-border-color: var(--kungalgame-blue-4);
|
--w-e-toolbar-border-color: var(--kungalgame-blue-4);
|
||||||
|
|
||||||
|
|
|
@ -1,80 +1,22 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onBeforeMount, ref } from 'vue'
|
import WangEditor from '@/components/wang-editor/WangEditor.vue'
|
||||||
import WangEditor from '@/components/WangEditor.vue'
|
|
||||||
import Tags from './components/Tags.vue'
|
import Tags from './components/Tags.vue'
|
||||||
import Footer from './components/Footer.vue'
|
import Footer from './components/Footer.vue'
|
||||||
import KUNGalgameFooter from '@/components/KUNGalgameFooter.vue'
|
import KUNGalgameFooter from '@/components/KUNGalgameFooter.vue'
|
||||||
|
|
||||||
// 导入编辑帖子的 store
|
|
||||||
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
|
||||||
import { storeToRefs } from 'pinia'
|
|
||||||
|
|
||||||
// 导入防抖函数
|
|
||||||
import { debounce } from '@/utils/debounce'
|
|
||||||
|
|
||||||
const topicData = storeToRefs(useKUNGalgameEditStore())
|
|
||||||
|
|
||||||
// 话题标题的文字
|
|
||||||
const topicTitle = ref('')
|
|
||||||
// 标题的最大长度
|
|
||||||
const maxInputLength = 40
|
|
||||||
|
|
||||||
onBeforeMount(() => {
|
|
||||||
if (topicData.isSave.value) {
|
|
||||||
topicTitle.value = topicData.title.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 处理用户输入
|
|
||||||
const handelInput = () => {
|
|
||||||
// 标题不能超过 40 字
|
|
||||||
if (topicTitle.value.length > maxInputLength) {
|
|
||||||
topicTitle.value = topicTitle.value.slice(0, maxInputLength)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用户输入了纯空格
|
|
||||||
if (topicTitle.value.trim() === '') {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// 创建一个防抖处理函数
|
|
||||||
const debouncedInput = debounce(() => {
|
|
||||||
// 过滤 xss
|
|
||||||
topicData.title.value = topicTitle.value
|
|
||||||
}, 300)
|
|
||||||
|
|
||||||
// 调用防抖处理函数,会在延迟时间内只执行一次更新操作
|
|
||||||
debouncedInput()
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="root">
|
<div class="root">
|
||||||
<!-- 内容区容器 -->
|
<!-- 内容区容器 -->
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- 内容区容器 -->
|
<!-- 编辑器 -->
|
||||||
<div class="content">
|
<WangEditor
|
||||||
<!-- 内容区的头部 -->
|
class="editor"
|
||||||
<div class="header">
|
:height="400"
|
||||||
<!-- 话题的标题 -->
|
:isShowToolbar="true"
|
||||||
<div class="title">
|
:isShowAdvance="true"
|
||||||
<input
|
:isShowTitle="true"
|
||||||
type="text"
|
/>
|
||||||
placeholder="请输入话题的标题(40字以内)"
|
|
||||||
v-model="topicTitle"
|
|
||||||
@input="handelInput"
|
|
||||||
:maxlength="maxInputLength"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- 编辑器 -->
|
|
||||||
<WangEditor
|
|
||||||
class="editor"
|
|
||||||
:height="400"
|
|
||||||
:isShowToolbar="true"
|
|
||||||
:isShowAdvance="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- 内容区的底部 -->
|
<!-- 内容区的底部 -->
|
||||||
<div class="content-footer">
|
<div class="content-footer">
|
||||||
|
@ -92,10 +34,6 @@ const handelInput = () => {
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.content {
|
|
||||||
margin: 0 auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.root {
|
.root {
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
min-height: 1200px;
|
min-height: 1200px;
|
||||||
|
@ -117,39 +55,6 @@ const handelInput = () => {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 容器的顶部 */
|
|
||||||
.header {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 话题的发布标题 */
|
|
||||||
.title {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 话题标题的输入框 */
|
|
||||||
.title input {
|
|
||||||
color: var(--kungalgame-font-color-2);
|
|
||||||
/* 距离外轮廓的距离 */
|
|
||||||
padding: 7px;
|
|
||||||
/* 内边距盒子 */
|
|
||||||
box-sizing: border-box;
|
|
||||||
width: 100%;
|
|
||||||
/* 标题输入字体大小 */
|
|
||||||
font-size: 40px;
|
|
||||||
border: 1px solid var(--kungalgame-blue-4);
|
|
||||||
background-color: var(--kungalgame-white);
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 标题输入框 focus 之后的样式 */
|
|
||||||
.title input:focus {
|
|
||||||
box-shadow: 0px 0px 5px var(--kungalgame-blue-4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-footer {
|
.content-footer {
|
||||||
/* 距离内容区的距离 */
|
/* 距离内容区的距离 */
|
||||||
margin-top: 17px;
|
margin-top: 17px;
|
||||||
|
|
|
@ -9,7 +9,7 @@ import 'animate.css'
|
||||||
import { defineAsyncComponent } from 'vue'
|
import { defineAsyncComponent } from 'vue'
|
||||||
|
|
||||||
// 导入编辑器
|
// 导入编辑器
|
||||||
import WangEditor from '@/components/WangEditor.vue'
|
import WangEditor from '@/components/wang-editor/WangEditor.vue'
|
||||||
|
|
||||||
// 异步导入话题标签
|
// 异步导入话题标签
|
||||||
const Tags = defineAsyncComponent(
|
const Tags = defineAsyncComponent(
|
||||||
|
|
Loading…
Reference in a new issue