diff --git a/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue b/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue index 7dc4020b6b..1b9fd9f72a 100644 --- a/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue +++ b/core/core-frontend/src/custom-component/rich-text/DeRichTextView.vue @@ -10,9 +10,10 @@ @@ -46,6 +47,7 @@ import 'tinymce/plugins/contextmenu' // contextmenu import 'tinymce/plugins/directionality' import 'tinymce/plugins/nonbreaking' import 'tinymce/plugins/pagebreak' +import './plugins' //自定义插件 import { computed, nextTick, reactive, ref, toRefs, watch, onMounted, PropType } from 'vue' import { snapshotStoreWithOut } from '@/store/modules/data-visualization/snapshot' import eventBus from '@/utils/eventBus' @@ -119,11 +121,11 @@ const init = ref({ skin_url: formatDataEaseBi('./tinymce-dataease-private/skins/ui/oxide'), // 皮肤 content_css: formatDataEaseBi('./tinymce-dataease-private/skins/content/default/content.css'), plugins: - 'advlist autolink link image lists charmap media wordcount table contextmenu directionality pagebreak', // 插件 + 'vertical-content advlist autolink link image lists charmap media wordcount table contextmenu directionality pagebreak', // 插件 // 工具栏 toolbar: 'undo redo |fontselect fontsizeselect |forecolor backcolor bold italic |underline strikethrough link| formatselect |' + - 'alignleft aligncenter alignright | bullist numlist |' + + 'top-align center-align bottom-align | alignleft aligncenter alignright | bullist numlist |' + ' blockquote subscript superscript removeformat | table image | fullscreen ' + '| bdmap indent2em lineheight formatpainter axupimgs', toolbar_location: '/', @@ -134,7 +136,9 @@ const init = ref({ placeholder: '', outer_placeholder: '双击输入文字', inline: true, // 开启内联模式 - branding: false + branding: false, + icons: 'vertical-content', + vertical_align: element.value.propValue.verticalAlign }) const editStatus = computed(() => { @@ -169,6 +173,36 @@ watch( } } ) +const ALIGN_MAP = { + 'top-align': { + display: 'flex', + 'flex-direction': 'column', + 'justify-content': 'flex-start' + }, + 'center-align': { + display: 'flex', + 'flex-direction': 'column', + 'justify-content': 'center' + }, + 'bottom-align': { + display: 'flex', + 'flex-direction': 'column', + 'justify-content': 'flex-end' + } +} +const wrapperStyle = computed(() => { + const align = element.value.propValue.verticalAlign + if (!align) { + return {} + } + return ALIGN_MAP[align] +}) +useEmitt({ + name: 'vertical-change-' + tinymceId, + callback: align => { + element.value.propValue.verticalAlign = align + } +}) const viewInit = () => { useEmitt({ diff --git a/core/core-frontend/src/custom-component/rich-text/plugins/index.ts b/core/core-frontend/src/custom-component/rich-text/plugins/index.ts new file mode 100644 index 0000000000..3e6cacaa84 --- /dev/null +++ b/core/core-frontend/src/custom-component/rich-text/plugins/index.ts @@ -0,0 +1,11 @@ +import tinymce from 'tinymce/tinymce' + +const plugins = import.meta.glob(['./*.ts', '!./index.ts'], { eager: true }) +for (const pluginName in plugins) { + const plugin = plugins[pluginName]['default'] + const exist = tinymce.PluginManager.get(plugin.name) + if (exist) { + continue + } + tinymce.PluginManager.add(plugin.name, plugin.plugin) +} diff --git a/core/core-frontend/src/custom-component/rich-text/plugins/vertical-content.ts b/core/core-frontend/src/custom-component/rich-text/plugins/vertical-content.ts new file mode 100644 index 0000000000..83a1dabf0b --- /dev/null +++ b/core/core-frontend/src/custom-component/rich-text/plugins/vertical-content.ts @@ -0,0 +1,87 @@ +import { type Editor } from 'tinymce' +import tinymce from 'tinymce/tinymce' +import { useEmitt } from '@/hooks/web/useEmitt' +const { emitter } = useEmitt() +const TOP_ALIGN_BTN = + '' + + '' + + '' + + '' +const CENTER_ALIGN_BTN = + '' + + '' + + '' + + '' + + '' +const BOTTOM_ALIGN_BTN = + '' + + '' + + '' + + '' +const pack = tinymce.IconManager.has('vertical-content') +if (!pack) { + tinymce.IconManager.add('vertical-content', { + icons: { + 'top-align': TOP_ALIGN_BTN, + 'center-align': CENTER_ALIGN_BTN, + 'bottom-align': BOTTOM_ALIGN_BTN + } + }) +} + +export default { + name: 'vertical-content', + plugin: function (editor: Editor) { + const wrapperDom = editor.targetElm + const verticalAlign = editor.settings.vertical_align + const btnMap = { + 'top-align': { + component: null, + tooltip: '置顶' + }, + 'center-align': { + component: null, + tooltip: '居中' + }, + 'bottom-align': { + component: null, + tooltip: '置底' + } + } + for (const key in btnMap) { + editor.ui.registry.addToggleButton(key, { + icon: key, + tooltip: btnMap[key].tooltip, + onAction: api => { + for (const btnKey in btnMap) { + if (btnKey === key) { + // 反选清空样式 + const align = api.isActive() ? '' : key + emitter.emit('vertical-change-' + wrapperDom.id, align) + btnMap[key].component.setActive(!api.isActive()) + } else { + const active = btnMap[btnKey].component.isActive() + if (active) { + btnMap[btnKey].component.setActive(false) + } + } + } + }, + onSetup(a) { + if (verticalAlign === key) { + a.setActive(true) + } + return api => (btnMap[key].component = api) + } + }) + } + return { + getMetadata: function () { + return { + name: 'Vertical align', + url: 'https://dataease.io' + } + } + } + } +}