feat: advance toolbar
This commit is contained in:
parent
50ced43d20
commit
352f20ec93
|
@ -1,25 +1,23 @@
|
|||
<script setup lang="ts">
|
||||
import { ref } from 'vue'
|
||||
import { defineAsyncComponent, ref } from 'vue'
|
||||
// 引入图标字体
|
||||
import { Icon } from '@iconify/vue'
|
||||
// 导入模式切换按钮
|
||||
import SwitchButton from './SwitchButton.vue'
|
||||
|
||||
// 异步引入编辑器设置面板
|
||||
const EditorSettingsMenu = defineAsyncComponent(
|
||||
() => import('./EditorSettingsMenu.vue')
|
||||
)
|
||||
|
||||
// 引入 css 动画
|
||||
import 'animate.css'
|
||||
|
||||
// 导入编辑帖子的 store
|
||||
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
const { editorHeight } = storeToRefs(useKUNGalgameEditStore())
|
||||
|
||||
// 接受传入的编辑器计数
|
||||
defineProps<{
|
||||
textCount: number
|
||||
}>()
|
||||
|
||||
// 是否显示编辑器设置面板
|
||||
const isShowSettingsMenu = ref(true)
|
||||
const isShowSettingsMenu = ref(false)
|
||||
|
||||
// 点击设置按钮
|
||||
const handelClickSettings = () => {
|
||||
|
@ -43,37 +41,8 @@ const handelClickSettings = () => {
|
|||
<!-- 文字计数 -->
|
||||
<span class="count">{{ textCount + ` ${$tm('edit.word')}` }}</span>
|
||||
|
||||
<Transition
|
||||
enter-active-class="animate__animated animate__jackInTheBox animate__faster"
|
||||
leave-active-class="animate__animated animate__rollOut animate__faster"
|
||||
>
|
||||
<!-- 设置菜单 -->
|
||||
<div v-if="isShowSettingsMenu" class="settings-menu">
|
||||
<!-- 编辑器高度设置 -->
|
||||
<div class="editor-height-title">
|
||||
<span> 编辑器高度 </span>
|
||||
<span>{{ editorHeight }} px</span>
|
||||
</div>
|
||||
<div class="editor-height">
|
||||
<span>300 px</span>
|
||||
<input
|
||||
type="range"
|
||||
min="300"
|
||||
max="500"
|
||||
step="1"
|
||||
v-model="editorHeight"
|
||||
/>
|
||||
<span>500 px</span>
|
||||
</div>
|
||||
|
||||
<!-- 是否显示编辑器高级选项 -->
|
||||
<div class="editor-advance-title">
|
||||
<span> 高级选项 </span>
|
||||
<!-- 切换按钮 -->
|
||||
<SwitchButton />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
<!-- 设置面板 -->
|
||||
<EditorSettingsMenu :isShowSettingsMenu="isShowSettingsMenu" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -101,37 +70,6 @@ const handelClickSettings = () => {
|
|||
}
|
||||
}
|
||||
|
||||
.settings-menu {
|
||||
padding: 10px;
|
||||
z-index: 1009;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
background-color: var(--kungalgame-white);
|
||||
border: 1px solid var(--kungalgame-blue-1);
|
||||
box-shadow: var(--shadow);
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.editor-height-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.editor-advance-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.editor-height {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.count {
|
||||
color: var(--kungalgame-font-color-0);
|
||||
background-color: var(--kungalgame-trans-white-9);
|
||||
|
|
146
src/components/wang-editor/EditorSettingsMenu.vue
Normal file
146
src/components/wang-editor/EditorSettingsMenu.vue
Normal file
|
@ -0,0 +1,146 @@
|
|||
<script setup lang="ts">
|
||||
import { ref, watch } from 'vue'
|
||||
// 引入图标字体
|
||||
import { Icon } from '@iconify/vue'
|
||||
// 导入模式切换按钮
|
||||
import SwitchButton from './SwitchButton.vue'
|
||||
|
||||
// 引入 css 动画
|
||||
import 'animate.css'
|
||||
|
||||
// 导入编辑帖子的 store
|
||||
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
// 使用编辑界面的 store
|
||||
const settingsStore = useKUNGalgameEditStore()
|
||||
const { isShowAdvance } = storeToRefs(settingsStore)
|
||||
|
||||
const { editorHeight } = storeToRefs(useKUNGalgameEditStore())
|
||||
|
||||
defineProps<{
|
||||
isShowSettingsMenu: boolean
|
||||
}>()
|
||||
|
||||
// 是否显示刷新页面
|
||||
const isRefreshPage = ref(false)
|
||||
|
||||
// 点击高级选项时提醒用户刷新页面
|
||||
watch(isShowAdvance, () => {
|
||||
isRefreshPage.value = !isRefreshPage.value
|
||||
})
|
||||
|
||||
const handleRefreshPage = () => location.reload()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition
|
||||
enter-active-class="animate__animated animate__jackInTheBox animate__faster"
|
||||
leave-active-class="animate__animated animate__rollOut animate__faster"
|
||||
>
|
||||
<!-- 设置菜单 -->
|
||||
<div v-if="isShowSettingsMenu" class="settings-menu">
|
||||
<!-- 编辑器高度设置 -->
|
||||
<div class="editor-height-title">
|
||||
<span> 编辑器高度 </span>
|
||||
<span>{{ editorHeight }} px</span>
|
||||
</div>
|
||||
<div class="editor-height">
|
||||
<span>300 px</span>
|
||||
<input
|
||||
type="range"
|
||||
min="300"
|
||||
max="500"
|
||||
step="1"
|
||||
v-model="editorHeight"
|
||||
/>
|
||||
<span>500 px</span>
|
||||
</div>
|
||||
|
||||
<!-- 是否显示编辑器高级选项 -->
|
||||
<div class="editor-advance-title">
|
||||
<div class="editor-advance">
|
||||
<Transition mode="out-in" name="slide-up">
|
||||
<span v-if="!isRefreshPage"> 高级编辑模式 </span>
|
||||
<span
|
||||
@click="handleRefreshPage"
|
||||
class="refresh"
|
||||
v-else-if="isRefreshPage"
|
||||
>
|
||||
<span>刷新页面生效</span>
|
||||
<Icon icon="line-md:rotate-270" />
|
||||
</span>
|
||||
</Transition>
|
||||
</div>
|
||||
<!-- 切换按钮 -->
|
||||
<SwitchButton />
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.settings-menu {
|
||||
padding: 10px;
|
||||
z-index: 1009;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
background-color: var(--kungalgame-white);
|
||||
border: 1px solid var(--kungalgame-blue-1);
|
||||
box-shadow: var(--shadow);
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.editor-height-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.editor-advance-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
|
||||
.editor-height {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.editor-advance {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.refresh {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 23px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: var(--kungalgame-blue-4);
|
||||
}
|
||||
span {
|
||||
margin-right: 10px;
|
||||
font-size: 17px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.slide-up-enter-active,
|
||||
.slide-up-leave-active {
|
||||
transition: all 0.25s ease-out;
|
||||
}
|
||||
|
||||
.slide-up-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateY(30px);
|
||||
}
|
||||
|
||||
.slide-up-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(-30px);
|
||||
}
|
||||
</style>
|
|
@ -1,6 +1,7 @@
|
|||
<!-- KUNGalgame 通用切换按钮 -->
|
||||
|
||||
<script setup lang="ts">
|
||||
import { watch } from 'vue'
|
||||
// 导入编辑界面 store
|
||||
import { useKUNGalgameEditStore } from '@/store/modules/edit'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
@ -9,15 +10,14 @@ import { storeToRefs } from 'pinia'
|
|||
const settingsStore = useKUNGalgameEditStore()
|
||||
const { isShowAdvance } = storeToRefs(settingsStore)
|
||||
|
||||
// 用户点击切换编辑器 toolbar 工具
|
||||
const toggleEditorMode = () => {
|
||||
isShowAdvance.value = !isShowAdvance.value
|
||||
}
|
||||
// 监听 store 中的状态变化,保持按钮状态与 store 同步
|
||||
watch(isShowAdvance, (newValue) => {
|
||||
isShowAdvance.value = newValue
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input type="checkbox" id="switch" /><label
|
||||
@click="toggleEditorMode"
|
||||
<input type="checkbox" id="switch" v-model="isShowAdvance" /><label
|
||||
for="switch"
|
||||
>Toggle</label
|
||||
>
|
||||
|
|
|
@ -78,7 +78,5 @@ const handelInput = () => {
|
|||
/* 标题输入字体大小 */
|
||||
font-size: 40px;
|
||||
border: none;
|
||||
/* 光标样式 */
|
||||
caret-shape: block;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -20,6 +20,7 @@ import { Editor } from '@wangeditor/editor-for-vue'
|
|||
// 编辑器菜单配置
|
||||
import { IToolbarConfig } from '@wangeditor/editor'
|
||||
import { Toolbar } from '@wangeditor/editor-for-vue'
|
||||
import { keysToExclude } from './keysToExclude'
|
||||
|
||||
// 导入标题
|
||||
const Title = defineAsyncComponent(
|
||||
|
@ -38,7 +39,9 @@ import DOMPurify from 'dompurify'
|
|||
// 导入防抖函数
|
||||
import { debounce } from '@/utils/debounce'
|
||||
|
||||
const topicData = storeToRefs(useKUNGalgameEditStore())
|
||||
const { editorHeight, isShowAdvance, isSave, content } = storeToRefs(
|
||||
useKUNGalgameEditStore()
|
||||
)
|
||||
|
||||
// 定义父组件传参
|
||||
const props = defineProps<{
|
||||
|
@ -54,7 +57,7 @@ const props = defineProps<{
|
|||
const editorRef = shallowRef<IDomEditor | undefined>(undefined)
|
||||
|
||||
// 编辑器的高度
|
||||
const editorHeight = computed(() => `height: ${topicData.editorHeight.value}px`)
|
||||
const editorHeightStyle = computed(() => `height: ${editorHeight.value}px`)
|
||||
// 编辑器内的内容
|
||||
const valueHtml = ref('')
|
||||
// 编辑器文字计数
|
||||
|
@ -82,23 +85,23 @@ const editorConfig: Partial<IEditorConfig> = {
|
|||
},
|
||||
}
|
||||
|
||||
const keys: string[] = []
|
||||
|
||||
// 工具栏相关配置
|
||||
const toolbarConfig: Partial<IToolbarConfig> = {
|
||||
excludeKeys: ['uploadVideo'].concat(keys),
|
||||
// 是否显示高级模式,显示则不排除 keys
|
||||
excludeKeys: isShowAdvance.value ? ['uploadVideo'] : keysToExclude,
|
||||
}
|
||||
|
||||
const handleCreated = (editor: IDomEditor) => {
|
||||
console.log(editor.getAllMenuKeys())
|
||||
|
||||
editorRef.value = editor // 记录 editor 实例,重要!
|
||||
editor.on('modalOrPanelShow', (modalOrPanel) => {
|
||||
console.log(modalOrPanel)
|
||||
})
|
||||
}
|
||||
|
||||
// 挂载之前载入数据,如果不保存,则不载入
|
||||
onBeforeMount(() => {
|
||||
if (topicData.isSave.value) {
|
||||
valueHtml.value = topicData.content.value
|
||||
if (isSave.value) {
|
||||
valueHtml.value = content.value
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -115,7 +118,7 @@ const handleChange = (editor: IDomEditor) => {
|
|||
// 创建一个防抖处理函数
|
||||
const debouncedUpdateContent = debounce(() => {
|
||||
// 过滤 xss
|
||||
topicData.content.value = DOMPurify.sanitize(editor.getHtml())
|
||||
content.value = DOMPurify.sanitize(editor.getHtml())
|
||||
}, 1007)
|
||||
|
||||
// 调用防抖处理函数,会在延迟时间内只执行一次更新操作
|
||||
|
@ -149,7 +152,7 @@ const handleChange = (editor: IDomEditor) => {
|
|||
<!-- 编辑器本体 -->
|
||||
<Editor
|
||||
class="wang-editor"
|
||||
:style="editorHeight"
|
||||
:style="editorHeightStyle"
|
||||
v-model="valueHtml"
|
||||
:defaultConfig="editorConfig"
|
||||
@onCreated="handleCreated"
|
||||
|
@ -162,10 +165,7 @@ const handleChange = (editor: IDomEditor) => {
|
|||
</div>
|
||||
|
||||
<!-- 编辑器 footer -->
|
||||
<EditorFooter
|
||||
:textCount="textCount"
|
||||
:editorHeight="topicData.editorHeight.value"
|
||||
/>
|
||||
<EditorFooter :textCount="textCount" :editorHeight="editorHeight" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
|
@ -177,8 +177,7 @@ const handleChange = (editor: IDomEditor) => {
|
|||
width: 100%;
|
||||
margin: 0 auto;
|
||||
z-index: 1008;
|
||||
background-color: var(--kungalgame-trans-white-5);
|
||||
backdrop-filter: blur(5px);
|
||||
background-color: var(--kungalgame-trans-white-2);
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
;[
|
||||
'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',
|
||||
]
|
34
src/components/wang-editor/keysToExclude.ts
Normal file
34
src/components/wang-editor/keysToExclude.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
// 编辑器 toolbar 要排除的项目
|
||||
export const keysToExclude: string[] = [
|
||||
// 角标
|
||||
'sub',
|
||||
'sup',
|
||||
// 文字精确大小,字体,行高
|
||||
'fontSize',
|
||||
'fontFamily',
|
||||
'lineHeight',
|
||||
// 文字背景色
|
||||
'bgColor',
|
||||
// todo, undo, redo
|
||||
'todo',
|
||||
'redo',
|
||||
'undo',
|
||||
// 缩进的组
|
||||
'group-indent',
|
||||
// 插入视频的组
|
||||
'group-video',
|
||||
// 表格
|
||||
'insertTable',
|
||||
'deleteTable',
|
||||
'deleteTableRow',
|
||||
'insertTableCol',
|
||||
'deleteTableCol',
|
||||
'tableHeader',
|
||||
'tableFullWidth',
|
||||
// 右键菜单已有的功能
|
||||
'headerSelect',
|
||||
'bold',
|
||||
'bulletedList',
|
||||
'insertLink',
|
||||
'group-more-style',
|
||||
]
|
|
@ -27,14 +27,27 @@ u {
|
|||
text-shadow: none;
|
||||
}
|
||||
|
||||
/* 不显示分割线 */
|
||||
.w-e-bar-divider {
|
||||
display: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: var(--kungalgame-blue-5);
|
||||
border-bottom: 1.5px solid var(--kungalgame-trans-white-9);
|
||||
&:hover {
|
||||
border-bottom: 1.5px solid var(--kungalgame-blue-5);
|
||||
}
|
||||
}
|
||||
|
||||
:root {
|
||||
/* textarea - css vars */
|
||||
--w-e-textarea-bg-color: var(--kungalgame-white-9);
|
||||
--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-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-bg-color: var(--kungalgame-white-9);
|
||||
--w-e-textarea-slight-bg-color: var(--kungalgame-trans-blue-0);
|
||||
--w-e-textarea-selected-border-color: var(--kungalgame-blue-1);
|
||||
/* 选中的元素,如选中了分割线 */
|
||||
--w-e-textarea-handler-bg-color: var(--kungalgame-blue-4);
|
||||
|
|
|
@ -28,7 +28,8 @@ onMounted(() => {
|
|||
<style lang="scss" scoped>
|
||||
/* 内容区右侧的话题展示区 */
|
||||
.w-e-text-container {
|
||||
width: 82%;
|
||||
/** 100 + 20 + 20 + 1 = 141px */
|
||||
width: calc(100% - 141px);
|
||||
font-size: 15px;
|
||||
padding: 17px;
|
||||
border-left: 1px solid var(--kungalgame-blue-1);
|
||||
|
|
Loading…
Reference in a new issue