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'
+ }
+ }
+ }
+ }
+}