mirror of
https://github.com/vbenjs/vue-vben-admin.git
synced 2025-08-27 14:13:40 +08:00
perf: review tinymce code
This commit is contained in:
@@ -1,59 +1,169 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="tinymce-container" :style="{ width: containerWidth }">
|
<div class="tinymce-container" :style="{ width: containerWidth }">
|
||||||
<tinymce-editor
|
<textarea :id="tinymceId" visibility="hidden" ref="elRef"></textarea>
|
||||||
:id="id"
|
|
||||||
:init="initOptions"
|
|
||||||
:modelValue="tinymceContent"
|
|
||||||
@update:modelValue="handleChange"
|
|
||||||
:tinymceScriptSrc="tinymceScriptSrc"
|
|
||||||
></tinymce-editor>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import TinymceEditor from './lib'; // TinyMCE vue wrapper
|
import {
|
||||||
import { defineComponent, computed } from 'vue';
|
defineComponent,
|
||||||
|
computed,
|
||||||
|
onMounted,
|
||||||
|
nextTick,
|
||||||
|
ref,
|
||||||
|
unref,
|
||||||
|
watch,
|
||||||
|
onUnmounted,
|
||||||
|
onDeactivated,
|
||||||
|
} from 'vue';
|
||||||
import { basicProps } from './props';
|
import { basicProps } from './props';
|
||||||
import toolbar from './toolbar';
|
import toolbar from './toolbar';
|
||||||
import plugins from './plugins';
|
import plugins from './plugins';
|
||||||
|
import { getTinymce } from './getTinymce';
|
||||||
|
import { useScript } from '/@/hooks/web/useScript';
|
||||||
|
import { snowUuid } from '/@/utils/uuid';
|
||||||
|
import { bindHandlers } from './helper';
|
||||||
|
|
||||||
const CDN_URL = 'https://cdn.bootcdn.net/ajax/libs/tinymce/5.5.1';
|
const CDN_URL = 'https://cdn.bootcdn.net/ajax/libs/tinymce/5.5.1';
|
||||||
|
|
||||||
const tinymceScriptSrc = `${CDN_URL}/tinymce.min.js`;
|
const tinymceScriptSrc = `${CDN_URL}/tinymce.min.js`;
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Tinymce',
|
name: 'Tinymce',
|
||||||
components: { TinymceEditor },
|
|
||||||
props: basicProps,
|
props: basicProps,
|
||||||
setup(props, { emit }) {
|
emits: ['change', 'update:modelValue'],
|
||||||
const tinymceContent = computed(() => {
|
setup(props, { emit, attrs }) {
|
||||||
return props.value;
|
const editorRef = ref<any>(null);
|
||||||
|
const elRef = ref<Nullable<HTMLElement>>(null);
|
||||||
|
|
||||||
|
const tinymceId = computed(() => {
|
||||||
|
return snowUuid('tiny-vue');
|
||||||
});
|
});
|
||||||
function handleChange(value: string) {
|
|
||||||
emit('change', value);
|
const tinymceContent = computed(() => {
|
||||||
}
|
return props.modelValue;
|
||||||
|
});
|
||||||
|
|
||||||
const containerWidth = computed(() => {
|
const containerWidth = computed(() => {
|
||||||
const width = props.width;
|
const width = props.width;
|
||||||
// Test matches `100`, `'100'`
|
|
||||||
if (/^[\d]+(\.[\d]+)?$/.test(width.toString())) {
|
if (/^[\d]+(\.[\d]+)?$/.test(width.toString())) {
|
||||||
return `${width}px`;
|
return `${width}px`;
|
||||||
}
|
}
|
||||||
return width;
|
return width;
|
||||||
});
|
});
|
||||||
|
|
||||||
const initOptions = computed(() => {
|
const initOptions = computed(() => {
|
||||||
const { id, height, menubar } = props;
|
const { height, menubar } = props;
|
||||||
return {
|
return {
|
||||||
selector: `#${id}`,
|
selector: `#${unref(tinymceId)}`,
|
||||||
height: height,
|
height: height,
|
||||||
toolbar: toolbar,
|
toolbar: toolbar,
|
||||||
|
theme: 'silver',
|
||||||
menubar: menubar,
|
menubar: menubar,
|
||||||
plugins: plugins,
|
plugins: plugins,
|
||||||
// 语言包
|
// 语言包
|
||||||
language_url: 'resource/tinymce/langs/zh_CN.js',
|
language_url: 'resource/tinymce/langs/zh_CN.js',
|
||||||
// 中文
|
// 中文
|
||||||
language: 'zh_CN',
|
language: 'zh_CN',
|
||||||
|
default_link_target: '_blank',
|
||||||
|
link_title: false,
|
||||||
|
advlist_bullet_styles: 'square',
|
||||||
|
advlist_number_styles: 'default',
|
||||||
|
object_resizing: false,
|
||||||
|
setup: (editor: any) => {
|
||||||
|
editorRef.value = editor;
|
||||||
|
editor.on('init', (e: Event) => initSetup(e));
|
||||||
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
return { containerWidth, initOptions, tinymceContent, handleChange, tinymceScriptSrc };
|
|
||||||
|
const { toPromise } = useScript({
|
||||||
|
src: tinymceScriptSrc,
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => attrs.disabled,
|
||||||
|
() => {
|
||||||
|
const editor = unref(editorRef);
|
||||||
|
if (!editor) return;
|
||||||
|
editor.setMode(attrs.disabled ? 'readonly' : 'design');
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
init();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
destory();
|
||||||
|
});
|
||||||
|
|
||||||
|
onDeactivated(() => {
|
||||||
|
destory();
|
||||||
|
});
|
||||||
|
|
||||||
|
function destory() {
|
||||||
|
if (getTinymce() !== null) {
|
||||||
|
getTinymce().remove(unref(editorRef));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
toPromise().then(() => {
|
||||||
|
initEditor();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function initEditor() {
|
||||||
|
getTinymce().init(unref(initOptions));
|
||||||
|
}
|
||||||
|
|
||||||
|
function initSetup(e: Event) {
|
||||||
|
const editor = unref(editorRef);
|
||||||
|
if (!editor) return;
|
||||||
|
const value = props.modelValue || '';
|
||||||
|
|
||||||
|
editor.setContent(value);
|
||||||
|
bindModelHandlers(editor);
|
||||||
|
bindHandlers(e, attrs, unref(editorRef));
|
||||||
|
}
|
||||||
|
|
||||||
|
function bindModelHandlers(editor: any) {
|
||||||
|
const modelEvents = attrs.modelEvents ? attrs.modelEvents : null;
|
||||||
|
const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents;
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
(val: string, prevVal: string) => {
|
||||||
|
if (
|
||||||
|
editor &&
|
||||||
|
typeof val === 'string' &&
|
||||||
|
val !== prevVal &&
|
||||||
|
val !== editor.getContent({ format: attrs.outputFormat })
|
||||||
|
) {
|
||||||
|
editor.setContent(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {
|
||||||
|
emit('update:modelValue', editor.getContent({ format: attrs.outputFormat }));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleChange(value: string) {
|
||||||
|
emit('change', value);
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
containerWidth,
|
||||||
|
initOptions,
|
||||||
|
tinymceContent,
|
||||||
|
handleChange,
|
||||||
|
tinymceScriptSrc,
|
||||||
|
elRef,
|
||||||
|
tinymceId,
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
@@ -1,9 +1,6 @@
|
|||||||
const getGlobal = (): any => (typeof window !== 'undefined' ? window : global);
|
const getGlobal = (): any => (typeof window !== 'undefined' ? window : global);
|
||||||
|
|
||||||
const getTinymce = () => {
|
export const getTinymce = () => {
|
||||||
const global = getGlobal();
|
const global = getGlobal();
|
||||||
|
|
||||||
return global && global.tinymce ? global.tinymce : null;
|
return global && global.tinymce ? global.tinymce : null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export { getTinymce };
|
|
81
src/components/Tinymce/src/helper.ts
Normal file
81
src/components/Tinymce/src/helper.ts
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
const validEvents = [
|
||||||
|
'onActivate',
|
||||||
|
'onAddUndo',
|
||||||
|
'onBeforeAddUndo',
|
||||||
|
'onBeforeExecCommand',
|
||||||
|
'onBeforeGetContent',
|
||||||
|
'onBeforeRenderUI',
|
||||||
|
'onBeforeSetContent',
|
||||||
|
'onBeforePaste',
|
||||||
|
'onBlur',
|
||||||
|
'onChange',
|
||||||
|
'onClearUndos',
|
||||||
|
'onClick',
|
||||||
|
'onContextMenu',
|
||||||
|
'onCopy',
|
||||||
|
'onCut',
|
||||||
|
'onDblclick',
|
||||||
|
'onDeactivate',
|
||||||
|
'onDirty',
|
||||||
|
'onDrag',
|
||||||
|
'onDragDrop',
|
||||||
|
'onDragEnd',
|
||||||
|
'onDragGesture',
|
||||||
|
'onDragOver',
|
||||||
|
'onDrop',
|
||||||
|
'onExecCommand',
|
||||||
|
'onFocus',
|
||||||
|
'onFocusIn',
|
||||||
|
'onFocusOut',
|
||||||
|
'onGetContent',
|
||||||
|
'onHide',
|
||||||
|
'onInit',
|
||||||
|
'onKeyDown',
|
||||||
|
'onKeyPress',
|
||||||
|
'onKeyUp',
|
||||||
|
'onLoadContent',
|
||||||
|
'onMouseDown',
|
||||||
|
'onMouseEnter',
|
||||||
|
'onMouseLeave',
|
||||||
|
'onMouseMove',
|
||||||
|
'onMouseOut',
|
||||||
|
'onMouseOver',
|
||||||
|
'onMouseUp',
|
||||||
|
'onNodeChange',
|
||||||
|
'onObjectResizeStart',
|
||||||
|
'onObjectResized',
|
||||||
|
'onObjectSelected',
|
||||||
|
'onPaste',
|
||||||
|
'onPostProcess',
|
||||||
|
'onPostRender',
|
||||||
|
'onPreProcess',
|
||||||
|
'onProgressState',
|
||||||
|
'onRedo',
|
||||||
|
'onRemove',
|
||||||
|
'onReset',
|
||||||
|
'onSaveContent',
|
||||||
|
'onSelectionChange',
|
||||||
|
'onSetAttrib',
|
||||||
|
'onSetContent',
|
||||||
|
'onShow',
|
||||||
|
'onSubmit',
|
||||||
|
'onUndo',
|
||||||
|
'onVisualAid',
|
||||||
|
];
|
||||||
|
|
||||||
|
const isValidKey = (key: string) => validEvents.indexOf(key) !== -1;
|
||||||
|
|
||||||
|
export const bindHandlers = (initEvent: Event, listeners: any, editor: any): void => {
|
||||||
|
Object.keys(listeners)
|
||||||
|
.filter(isValidKey)
|
||||||
|
.forEach((key: string) => {
|
||||||
|
const handler = listeners[key];
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
if (key === 'onInit') {
|
||||||
|
handler(initEvent, editor);
|
||||||
|
} else {
|
||||||
|
editor.on(key.substring(2), (e: any) => handler(e, editor));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
@@ -1,72 +0,0 @@
|
|||||||
import { uuid } from './Utils';
|
|
||||||
|
|
||||||
export type callbackFn = () => void;
|
|
||||||
export interface IStateObj {
|
|
||||||
listeners: callbackFn[];
|
|
||||||
scriptId: string;
|
|
||||||
scriptLoaded: boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
const createState = (): IStateObj => {
|
|
||||||
return {
|
|
||||||
listeners: [],
|
|
||||||
scriptId: uuid('tiny-script'),
|
|
||||||
scriptLoaded: false
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
interface ScriptLoader {
|
|
||||||
load: (doc: Document, url: string, callback: callbackFn) => void;
|
|
||||||
reinitialize: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const CreateScriptLoader = (): ScriptLoader => {
|
|
||||||
let state: IStateObj = createState();
|
|
||||||
|
|
||||||
const injectScriptTag = (scriptId: string, doc: Document, url: string, callback: callbackFn) => {
|
|
||||||
const scriptTag = doc.createElement('script');
|
|
||||||
scriptTag.referrerPolicy = 'origin';
|
|
||||||
scriptTag.type = 'application/javascript';
|
|
||||||
scriptTag.id = scriptId;
|
|
||||||
scriptTag.src = url;
|
|
||||||
|
|
||||||
const handler = () => {
|
|
||||||
scriptTag.removeEventListener('load', handler);
|
|
||||||
callback();
|
|
||||||
};
|
|
||||||
scriptTag.addEventListener('load', handler);
|
|
||||||
if (doc.head) {
|
|
||||||
doc.head.appendChild(scriptTag);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const load = (doc: Document, url: string, callback: callbackFn) => {
|
|
||||||
if (state.scriptLoaded) {
|
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
state.listeners.push(callback);
|
|
||||||
if (!doc.getElementById(state.scriptId)) {
|
|
||||||
injectScriptTag(state.scriptId, doc, url, () => {
|
|
||||||
state.listeners.forEach((fn) => fn());
|
|
||||||
state.scriptLoaded = true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only to be used by tests.
|
|
||||||
const reinitialize = () => {
|
|
||||||
state = createState();
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
load,
|
|
||||||
reinitialize
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
const ScriptLoader = CreateScriptLoader();
|
|
||||||
|
|
||||||
export {
|
|
||||||
ScriptLoader
|
|
||||||
};
|
|
@@ -1,151 +0,0 @@
|
|||||||
import { ComponentPublicInstance } from 'vue';
|
|
||||||
|
|
||||||
const validEvents = [
|
|
||||||
'onActivate',
|
|
||||||
'onAddUndo',
|
|
||||||
'onBeforeAddUndo',
|
|
||||||
'onBeforeExecCommand',
|
|
||||||
'onBeforeGetContent',
|
|
||||||
'onBeforeRenderUI',
|
|
||||||
'onBeforeSetContent',
|
|
||||||
'onBeforePaste',
|
|
||||||
'onBlur',
|
|
||||||
'onChange',
|
|
||||||
'onClearUndos',
|
|
||||||
'onClick',
|
|
||||||
'onContextMenu',
|
|
||||||
'onCopy',
|
|
||||||
'onCut',
|
|
||||||
'onDblclick',
|
|
||||||
'onDeactivate',
|
|
||||||
'onDirty',
|
|
||||||
'onDrag',
|
|
||||||
'onDragDrop',
|
|
||||||
'onDragEnd',
|
|
||||||
'onDragGesture',
|
|
||||||
'onDragOver',
|
|
||||||
'onDrop',
|
|
||||||
'onExecCommand',
|
|
||||||
'onFocus',
|
|
||||||
'onFocusIn',
|
|
||||||
'onFocusOut',
|
|
||||||
'onGetContent',
|
|
||||||
'onHide',
|
|
||||||
'onInit',
|
|
||||||
'onKeyDown',
|
|
||||||
'onKeyPress',
|
|
||||||
'onKeyUp',
|
|
||||||
'onLoadContent',
|
|
||||||
'onMouseDown',
|
|
||||||
'onMouseEnter',
|
|
||||||
'onMouseLeave',
|
|
||||||
'onMouseMove',
|
|
||||||
'onMouseOut',
|
|
||||||
'onMouseOver',
|
|
||||||
'onMouseUp',
|
|
||||||
'onNodeChange',
|
|
||||||
'onObjectResizeStart',
|
|
||||||
'onObjectResized',
|
|
||||||
'onObjectSelected',
|
|
||||||
'onPaste',
|
|
||||||
'onPostProcess',
|
|
||||||
'onPostRender',
|
|
||||||
'onPreProcess',
|
|
||||||
'onProgressState',
|
|
||||||
'onRedo',
|
|
||||||
'onRemove',
|
|
||||||
'onReset',
|
|
||||||
'onSaveContent',
|
|
||||||
'onSelectionChange',
|
|
||||||
'onSetAttrib',
|
|
||||||
'onSetContent',
|
|
||||||
'onShow',
|
|
||||||
'onSubmit',
|
|
||||||
'onUndo',
|
|
||||||
'onVisualAid'
|
|
||||||
];
|
|
||||||
|
|
||||||
const isValidKey = (key: string) => validEvents.indexOf(key) !== -1;
|
|
||||||
|
|
||||||
const bindHandlers = (initEvent: Event, listeners: any, editor: any): void => {
|
|
||||||
Object.keys(listeners)
|
|
||||||
.filter(isValidKey)
|
|
||||||
.forEach((key: string) => {
|
|
||||||
const handler = listeners[key];
|
|
||||||
if (typeof handler === 'function') {
|
|
||||||
if (key === 'onInit') {
|
|
||||||
handler(initEvent, editor);
|
|
||||||
} else {
|
|
||||||
editor.on(key.substring(2), (e: any) => handler(e, editor));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const bindModelHandlers = (ctx: ComponentPublicInstance, editor: any) => {
|
|
||||||
const modelEvents = ctx.$props.modelEvents ? ctx.$props.modelEvents : null;
|
|
||||||
const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents;
|
|
||||||
// @ts-ignore
|
|
||||||
ctx.$watch('modelValue', (val: string, prevVal: string) => {
|
|
||||||
if (editor && typeof val === 'string' && val !== prevVal && val !== editor.getContent({ format: ctx.$props.outputFormat })) {
|
|
||||||
editor.setContent(val);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => {
|
|
||||||
ctx.$emit('update:modelValue', editor.getContent({ format: ctx.$props.outputFormat }));
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initEditor = (initEvent: Event, ctx: ComponentPublicInstance, editor: any) => {
|
|
||||||
const value = ctx.$props.modelValue ? ctx.$props.modelValue : '';
|
|
||||||
const initialValue = ctx.$props.initialValue ? ctx.$props.initialValue : '';
|
|
||||||
|
|
||||||
editor.setContent(value || initialValue);
|
|
||||||
|
|
||||||
// checks if the v-model shorthand is used (which sets an v-on:input listener) and then binds either
|
|
||||||
// specified the events or defaults to "change keyup" event and emits the editor content on that event
|
|
||||||
if (ctx.$attrs['onUpdate:modelValue']) {
|
|
||||||
bindModelHandlers(ctx, editor);
|
|
||||||
}
|
|
||||||
|
|
||||||
bindHandlers(initEvent, ctx.$attrs, editor);
|
|
||||||
};
|
|
||||||
|
|
||||||
let unique = 0;
|
|
||||||
|
|
||||||
const uuid = (prefix: string): string => {
|
|
||||||
const time = Date.now();
|
|
||||||
const random = Math.floor(Math.random() * 1000000000);
|
|
||||||
|
|
||||||
unique++;
|
|
||||||
|
|
||||||
return prefix + '_' + random + unique + String(time);
|
|
||||||
};
|
|
||||||
|
|
||||||
const isTextarea = (element: Element | null): element is HTMLTextAreaElement => {
|
|
||||||
return element !== null && element.tagName.toLowerCase() === 'textarea';
|
|
||||||
};
|
|
||||||
|
|
||||||
const normalizePluginArray = (plugins?: string | string[]): string[] => {
|
|
||||||
if (typeof plugins === 'undefined' || plugins === '') {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
return Array.isArray(plugins) ? plugins : plugins.split(' ');
|
|
||||||
};
|
|
||||||
|
|
||||||
const mergePlugins = (initPlugins: string | string[], inputPlugins?: string | string[]) =>
|
|
||||||
normalizePluginArray(initPlugins).concat(normalizePluginArray(inputPlugins));
|
|
||||||
|
|
||||||
const isNullOrUndefined = (value: any): value is null | undefined => value === null || value === undefined;
|
|
||||||
|
|
||||||
export {
|
|
||||||
bindHandlers,
|
|
||||||
bindModelHandlers,
|
|
||||||
initEditor,
|
|
||||||
uuid,
|
|
||||||
isTextarea,
|
|
||||||
mergePlugins,
|
|
||||||
isNullOrUndefined
|
|
||||||
};
|
|
@@ -1,111 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2018-present, Ephox, Inc.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the Apache 2 license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
// import { ThisTypedComponentOptionsWithRecordProps } from 'vue/types/options';
|
|
||||||
// import { CreateElement, Vue } from 'vue/types/vue';
|
|
||||||
|
|
||||||
import { ScriptLoader } from '../ScriptLoader';
|
|
||||||
import { getTinymce } from '../TinyMCE';
|
|
||||||
import { initEditor, isTextarea, mergePlugins, uuid, isNullOrUndefined } from '../Utils';
|
|
||||||
import { editorProps, IPropTypes } from './EditorPropTypes';
|
|
||||||
import { h, defineComponent, ComponentPublicInstance } from 'vue'
|
|
||||||
|
|
||||||
|
|
||||||
export interface IEditor {
|
|
||||||
$props: Partial<IPropTypes>
|
|
||||||
}
|
|
||||||
|
|
||||||
declare module '@vue/runtime-core' {
|
|
||||||
interface ComponentCustomProperties {
|
|
||||||
elementId: string;
|
|
||||||
element: Element | null;
|
|
||||||
editor: any;
|
|
||||||
inlineEditor: boolean;
|
|
||||||
$props: Partial<IPropTypes>;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const renderInline = (id: string, tagName?: string) => {
|
|
||||||
return h(tagName ? tagName : 'div', {
|
|
||||||
id
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderIframe = (id: string) => {
|
|
||||||
return h('textarea', {
|
|
||||||
id,
|
|
||||||
visibility: 'hidden'
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const initialise = (ctx: ComponentPublicInstance) => () => {
|
|
||||||
const finalInit = {
|
|
||||||
...ctx.$props.init,
|
|
||||||
readonly: ctx.$props.disabled,
|
|
||||||
selector: `#${ctx.elementId}`,
|
|
||||||
plugins: mergePlugins(ctx.$props.init && ctx.$props.init.plugins, ctx.$props.plugins),
|
|
||||||
toolbar: ctx.$props.toolbar || (ctx.$props.init && ctx.$props.init.toolbar),
|
|
||||||
inline: ctx.inlineEditor,
|
|
||||||
setup: (editor: any) => {
|
|
||||||
ctx.editor = editor;
|
|
||||||
editor.on('init', (e: Event) => initEditor(e, ctx, editor));
|
|
||||||
|
|
||||||
if (ctx.$props.init && typeof ctx.$props.init.setup === 'function') {
|
|
||||||
ctx.$props.init.setup(editor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isTextarea(ctx.element)) {
|
|
||||||
ctx.element.style.visibility = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
getTinymce().init(finalInit);
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Editor = defineComponent({
|
|
||||||
props: editorProps,
|
|
||||||
created() {
|
|
||||||
this.elementId = this.$props.id || uuid('tiny-vue');
|
|
||||||
this.inlineEditor = (this.$props.init && this.$props.init.inline) || this.$props.inline;
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
disabled() {
|
|
||||||
(this as any).editor.setMode(this.disabled ? 'readonly' : 'design');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.element = this.$el;
|
|
||||||
|
|
||||||
if (getTinymce() !== null) {
|
|
||||||
initialise(this)();
|
|
||||||
} else if (this.element && this.element.ownerDocument) {
|
|
||||||
const channel = this.$props.cloudChannel ? this.$props.cloudChannel : '5';
|
|
||||||
const apiKey = this.$props.apiKey ? this.$props.apiKey : 'no-api-key';
|
|
||||||
|
|
||||||
const scriptSrc = isNullOrUndefined(this.$props.tinymceScriptSrc) ?
|
|
||||||
`https://cdn.tiny.cloud/1/${apiKey}/tinymce/${channel}/tinymce.min.js` :
|
|
||||||
this.$props.tinymceScriptSrc;
|
|
||||||
|
|
||||||
ScriptLoader.load(
|
|
||||||
this.element.ownerDocument,
|
|
||||||
scriptSrc,
|
|
||||||
initialise(this)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
beforeUnmount() {
|
|
||||||
if (getTinymce() !== null) {
|
|
||||||
getTinymce().remove(this.editor);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
render() {
|
|
||||||
return this.inlineEditor ? renderInline(this.elementId, this.$props.tagName) : renderIframe(this.elementId);
|
|
||||||
}
|
|
||||||
})
|
|
@@ -1,46 +0,0 @@
|
|||||||
/**
|
|
||||||
* Copyright (c) 2018-present, Ephox, Inc.
|
|
||||||
*
|
|
||||||
* This source code is licensed under the Apache 2 license found in the
|
|
||||||
* LICENSE file in the root directory of this source tree.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
export type CopyProps<T> = { [P in keyof T]: any };
|
|
||||||
|
|
||||||
export interface IPropTypes {
|
|
||||||
apiKey: string;
|
|
||||||
cloudChannel: string;
|
|
||||||
id: string;
|
|
||||||
init: any;
|
|
||||||
initialValue: string;
|
|
||||||
outputFormat: 'html' | 'text';
|
|
||||||
inline: boolean;
|
|
||||||
modelEvents: string[] | string;
|
|
||||||
plugins: string[] | string;
|
|
||||||
tagName: string;
|
|
||||||
toolbar: string[] | string;
|
|
||||||
modelValue: string;
|
|
||||||
disabled: boolean;
|
|
||||||
tinymceScriptSrc: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const editorProps: CopyProps<IPropTypes> = {
|
|
||||||
apiKey: String,
|
|
||||||
cloudChannel: String,
|
|
||||||
id: String,
|
|
||||||
init: Object,
|
|
||||||
initialValue: String,
|
|
||||||
inline: Boolean,
|
|
||||||
modelEvents: [String, Array],
|
|
||||||
plugins: [String, Array],
|
|
||||||
tagName: String,
|
|
||||||
toolbar: [String, Array],
|
|
||||||
modelValue: String,
|
|
||||||
disabled: Boolean,
|
|
||||||
tinymceScriptSrc: String,
|
|
||||||
outputFormat: {
|
|
||||||
type: String,
|
|
||||||
validator: (prop: string) => prop === 'html' || prop === 'text'
|
|
||||||
},
|
|
||||||
};
|
|
4
src/components/Tinymce/src/lib/global.d.ts
vendored
4
src/components/Tinymce/src/lib/global.d.ts
vendored
@@ -1,4 +0,0 @@
|
|||||||
// Global compile-time constants
|
|
||||||
declare var __DEV__: boolean
|
|
||||||
declare var __BROWSER__: boolean
|
|
||||||
declare var __CI__: boolean
|
|
@@ -1,3 +0,0 @@
|
|||||||
import { Editor } from './components/Editor';
|
|
||||||
|
|
||||||
export default Editor;
|
|
@@ -1,12 +1,6 @@
|
|||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
export const basicProps = {
|
export const basicProps = {
|
||||||
id: {
|
|
||||||
type: String as PropType<string>,
|
|
||||||
default: () => {
|
|
||||||
return `tinymce-${new Date().getTime()}${(Math.random() * 1000).toFixed(0)}`;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
menubar: {
|
menubar: {
|
||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
default: 'file edit insert view format table',
|
default: 'file edit insert view format table',
|
||||||
@@ -15,6 +9,10 @@ export const basicProps = {
|
|||||||
type: String as PropType<string>,
|
type: String as PropType<string>,
|
||||||
// default: ''
|
// default: ''
|
||||||
},
|
},
|
||||||
|
modelValue: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
// default: ''
|
||||||
|
},
|
||||||
// 高度
|
// 高度
|
||||||
height: {
|
height: {
|
||||||
type: [Number, String] as PropType<string | number>,
|
type: [Number, String] as PropType<string | number>,
|
||||||
|
@@ -6,23 +6,23 @@ const menu: MenuModule = {
|
|||||||
path: '/charts',
|
path: '/charts',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/apexChart',
|
path: 'apexChart',
|
||||||
name: 'ApexChart',
|
name: 'ApexChart',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/echarts',
|
path: 'echarts',
|
||||||
name: 'Echarts',
|
name: 'Echarts',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/map',
|
path: 'map',
|
||||||
name: '地图',
|
name: '地图',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/line',
|
path: 'line',
|
||||||
name: '折线图',
|
name: '折线图',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/pie',
|
path: 'pie',
|
||||||
name: '饼图',
|
name: '饼图',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -6,16 +6,16 @@ const menu: MenuModule = {
|
|||||||
path: '/comp',
|
path: '/comp',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/basic',
|
path: 'basic',
|
||||||
name: '基础组件',
|
name: '基础组件',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/countTo',
|
path: 'countTo',
|
||||||
name: '数字动画',
|
name: '数字动画',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: '/scroll',
|
path: 'scroll',
|
||||||
name: '滚动组件',
|
name: '滚动组件',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
@@ -33,53 +33,39 @@ const menu: MenuModule = {
|
|||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/modal',
|
path: 'modal',
|
||||||
name: '弹窗扩展',
|
name: '弹窗扩展',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/drawer',
|
path: 'drawer',
|
||||||
name: '抽屉扩展',
|
name: '抽屉扩展',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/desc',
|
path: 'desc',
|
||||||
name: '详情组件',
|
name: '详情组件',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/verify',
|
path: 'verify',
|
||||||
name: '验证组件',
|
name: '验证组件',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/drag',
|
path: 'drag',
|
||||||
name: '拖拽校验',
|
name: '拖拽校验',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/rotate',
|
path: 'rotate',
|
||||||
name: '图片还原校验',
|
name: '图片还原校验',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/qrcode',
|
path: 'qrcode',
|
||||||
name: '二维码组件',
|
name: '二维码组件',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/strength-meter',
|
path: 'strength-meter',
|
||||||
name: '密码强度组件',
|
name: '密码强度组件',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/tinymce',
|
|
||||||
name: '富文本',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '/index',
|
|
||||||
name: '基础使用',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: '/editor',
|
|
||||||
name: '嵌入form使用',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -6,9 +6,23 @@ const menu: MenuModule = {
|
|||||||
path: '/editor',
|
path: '/editor',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/markdown',
|
path: 'markdown',
|
||||||
name: 'markdown编辑器',
|
name: 'markdown编辑器',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'tinymce',
|
||||||
|
name: '富文本',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
name: '基础使用',
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// path: 'editor',
|
||||||
|
// name: '嵌入form使用',
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -6,23 +6,21 @@ const menu: MenuModule = {
|
|||||||
path: '/excel',
|
path: '/excel',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/customExport',
|
path: 'customExport',
|
||||||
name: '选择导出格式',
|
name: '选择导出格式',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/jsonExport',
|
path: 'jsonExport',
|
||||||
name: 'JSON数据导出',
|
name: 'JSON数据导出',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/arrayExport',
|
path: 'arrayExport',
|
||||||
name: 'Array数据导出',
|
name: 'Array数据导出',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/importExcel',
|
path: 'importExcel',
|
||||||
name: '导入',
|
name: '导入',
|
||||||
},
|
},
|
||||||
// ],
|
|
||||||
// },
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@@ -6,27 +6,27 @@ const menu: MenuModule = {
|
|||||||
path: '/exception',
|
path: '/exception',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/404',
|
path: '404',
|
||||||
name: '404',
|
name: '404',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/500',
|
path: '500',
|
||||||
name: '500',
|
name: '500',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/net-work-error',
|
path: 'net-work-error',
|
||||||
name: '网络错误',
|
name: '网络错误',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/page-time-out',
|
path: 'page-time-out',
|
||||||
name: '页面超时',
|
name: '页面超时',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/not-data',
|
path: 'not-data',
|
||||||
name: '无数据',
|
name: '无数据',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/error-log',
|
path: 'error-log',
|
||||||
name: '错误日志',
|
name: '错误日志',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -6,55 +6,55 @@ const menu: MenuModule = {
|
|||||||
path: '/feat',
|
path: '/feat',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/icon',
|
path: 'icon',
|
||||||
name: '图标',
|
name: '图标',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/tabs',
|
path: 'tabs',
|
||||||
name: '标签页操作',
|
name: '标签页操作',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/context-menu',
|
path: 'context-menu',
|
||||||
name: '右键菜单',
|
name: '右键菜单',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/click-out-side',
|
path: 'click-out-side',
|
||||||
name: 'ClickOutSide',
|
name: 'ClickOutSide',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/img-preview',
|
path: 'img-preview',
|
||||||
name: '图片预览',
|
name: '图片预览',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/i18n',
|
path: 'i18n',
|
||||||
name: '国际化',
|
name: '国际化',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/copy',
|
path: 'copy',
|
||||||
name: '剪切板',
|
name: '剪切板',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/msg',
|
path: 'msg',
|
||||||
name: '消息提示',
|
name: '消息提示',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/watermark',
|
path: 'watermark',
|
||||||
name: '水印',
|
name: '水印',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/full-screen',
|
path: 'full-screen',
|
||||||
name: '全屏',
|
name: '全屏',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/testTab',
|
path: 'testTab',
|
||||||
name: '带参Tab',
|
name: '带参Tab',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/id1',
|
path: 'id1',
|
||||||
name: '带参tab1',
|
name: '带参tab1',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/id2',
|
path: 'id2',
|
||||||
name: '带参tab2',
|
name: '带参tab2',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -6,31 +6,31 @@ const menu: MenuModule = {
|
|||||||
name: 'Form',
|
name: 'Form',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/basic',
|
path: 'basic',
|
||||||
name: '基础表单',
|
name: '基础表单',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/useForm',
|
path: 'useForm',
|
||||||
name: 'useForm',
|
name: 'useForm',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/refForm',
|
path: 'refForm',
|
||||||
name: 'RefForm',
|
name: 'RefForm',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/advancedForm',
|
path: 'advancedForm',
|
||||||
name: '可收缩表单',
|
name: '可收缩表单',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/ruleForm',
|
path: 'ruleForm',
|
||||||
name: '表单校验',
|
name: '表单校验',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/dynamicForm',
|
path: 'dynamicForm',
|
||||||
name: '动态表单',
|
name: '动态表单',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/customerForm',
|
path: 'customerForm',
|
||||||
name: '自定义组件',
|
name: '自定义组件',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -6,15 +6,15 @@ const menu: MenuModule = {
|
|||||||
path: '/frame',
|
path: '/frame',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/antv',
|
path: 'antv',
|
||||||
name: 'antVue文档(内嵌)',
|
name: 'antVue文档(内嵌)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/doc',
|
path: 'doc',
|
||||||
name: '项目文档(内嵌)',
|
name: '项目文档(内嵌)',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/docExternal',
|
path: 'docExternal',
|
||||||
name: '项目文档(外链)',
|
name: '项目文档(外链)',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -6,37 +6,37 @@ const menu: MenuModule = {
|
|||||||
path: '/permission',
|
path: '/permission',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/front',
|
path: 'front',
|
||||||
name: '基于前端',
|
name: '基于前端',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/page',
|
path: 'page',
|
||||||
name: '页面权限',
|
name: '页面权限',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/btn',
|
path: 'btn',
|
||||||
name: '按钮权限',
|
name: '按钮权限',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/auth-pageA',
|
path: 'auth-pageA',
|
||||||
name: '权限测试页A',
|
name: '权限测试页A',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/auth-pageB',
|
path: 'auth-pageB',
|
||||||
name: '权限测试页B',
|
name: '权限测试页B',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/back',
|
path: 'back',
|
||||||
name: '基于后台',
|
name: '基于后台',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/page',
|
path: 'page',
|
||||||
name: '页面权限',
|
name: '页面权限',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/btn',
|
path: 'btn',
|
||||||
name: '按钮权限',
|
name: '按钮权限',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -6,59 +6,59 @@ const menu: MenuModule = {
|
|||||||
name: 'Table',
|
name: 'Table',
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/basic',
|
path: 'basic',
|
||||||
name: '基础表格',
|
name: '基础表格',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/treeTable',
|
path: 'treeTable',
|
||||||
name: '树形表格',
|
name: '树形表格',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/fetchTable',
|
path: 'fetchTable',
|
||||||
name: '远程加载',
|
name: '远程加载',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/fixedColumn',
|
path: 'fixedColumn',
|
||||||
name: '固定列',
|
name: '固定列',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/customerCell',
|
path: 'customerCell',
|
||||||
name: '自定义列',
|
name: '自定义列',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/formTable',
|
path: 'formTable',
|
||||||
name: '开启搜索区域',
|
name: '开启搜索区域',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/useTable',
|
path: 'useTable',
|
||||||
name: 'UseTable',
|
name: 'UseTable',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/refTable',
|
path: 'refTable',
|
||||||
name: 'RefTable',
|
name: 'RefTable',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/multipleHeader',
|
path: 'multipleHeader',
|
||||||
name: '多级表头',
|
name: '多级表头',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/mergeHeader',
|
path: 'mergeHeader',
|
||||||
name: '合并单元格',
|
name: '合并单元格',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/expandTable',
|
path: 'expandTable',
|
||||||
name: '可展开表格',
|
name: '可展开表格',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/fixedHeight',
|
path: 'fixedHeight',
|
||||||
name: '定高/头部自定义',
|
name: '定高/头部自定义',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/footerTable',
|
path: 'footerTable',
|
||||||
name: '表尾行合计',
|
name: '表尾行合计',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/editCellTable',
|
path: 'editCellTable',
|
||||||
name: '可编辑单元格',
|
name: '可编辑单元格',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
@@ -136,31 +136,5 @@ export default {
|
|||||||
title: '密码强度组件',
|
title: '密码强度组件',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
path: '/tinymce',
|
|
||||||
name: 'TinymceDemo',
|
|
||||||
meta: {
|
|
||||||
title: '富文本',
|
|
||||||
},
|
|
||||||
redirect: '/comp/tinymce/index',
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: 'index',
|
|
||||||
name: 'Tinymce',
|
|
||||||
component: () => import('/@/views/demo/comp/tinymce/index.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '基础使用',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'editor',
|
|
||||||
name: 'TinymceEditor',
|
|
||||||
component: () => import('/@/views/demo/comp/tinymce/Editor.vue'),
|
|
||||||
meta: {
|
|
||||||
title: '嵌入form使用',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
} as AppRouteModule;
|
} as AppRouteModule;
|
||||||
|
@@ -23,5 +23,32 @@ export default {
|
|||||||
title: 'markdown编辑器',
|
title: 'markdown编辑器',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/tinymce',
|
||||||
|
name: 'TinymceDemo',
|
||||||
|
meta: {
|
||||||
|
title: '富文本',
|
||||||
|
},
|
||||||
|
redirect: '/editor/tinymce/index',
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'index',
|
||||||
|
name: 'TinymceBasicDemo',
|
||||||
|
component: () => import('/@/views/demo/editor/tinymce/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '基础使用',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// TODO
|
||||||
|
// {
|
||||||
|
// path: 'editor',
|
||||||
|
// name: 'TinymceFormDemo',
|
||||||
|
// component: () => import('/@/views/demo/comp/tinymce/Editor.vue'),
|
||||||
|
// meta: {
|
||||||
|
// title: '嵌入form使用',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
],
|
||||||
|
},
|
||||||
],
|
],
|
||||||
} as AppRouteModule;
|
} as AppRouteModule;
|
||||||
|
@@ -67,3 +67,7 @@ export const isServer = typeof window === 'undefined';
|
|||||||
export function isImageDom(o: Element) {
|
export function isImageDom(o: Element) {
|
||||||
return o && ['IMAGE', 'IMG'].includes(o.tagName);
|
return o && ['IMAGE', 'IMG'].includes(o.tagName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const isTextarea = (element: Element | null): element is HTMLTextAreaElement => {
|
||||||
|
return element !== null && element.tagName.toLowerCase() === 'textarea';
|
||||||
|
};
|
||||||
|
@@ -17,3 +17,11 @@ export function buildUUID(): string {
|
|||||||
}
|
}
|
||||||
return uuid.replace(/-/g, '');
|
return uuid.replace(/-/g, '');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let unique = 0;
|
||||||
|
export function snowUuid(prefix: string): string {
|
||||||
|
const time = Date.now();
|
||||||
|
const random = Math.floor(Math.random() * 1000000000);
|
||||||
|
unique++;
|
||||||
|
return prefix + '_' + random + unique + String(time);
|
||||||
|
}
|
||||||
|
@@ -43,7 +43,7 @@
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { BasicForm, CollapseContainer, Tinymce },
|
components: { BasicForm, CollapseContainer },
|
||||||
setup() {
|
setup() {
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
|
|
@@ -1,19 +1,24 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex p-4">
|
<div class="flex p-4">
|
||||||
<Tinymce value="Hello, World!" @change="handleChange" width="100%" />
|
{{ value }}
|
||||||
|
<Tinymce v-model="value" @change="handleChange" width="100%" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { defineComponent } from 'vue';
|
import { defineComponent, ref } from 'vue';
|
||||||
import { Tinymce } from '/@/components/Tinymce/index';
|
import { Tinymce } from '/@/components/Tinymce/index';
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
components: { Tinymce },
|
components: { Tinymce },
|
||||||
setup() {
|
setup() {
|
||||||
|
const value = ref('hello world!');
|
||||||
function handleChange(value: string) {
|
function handleChange(value: string) {
|
||||||
console.log(value);
|
console.log(value);
|
||||||
}
|
}
|
||||||
return { handleChange };
|
// setTimeout(() => {
|
||||||
|
// value.value = '1233';
|
||||||
|
// }, 5000);
|
||||||
|
return { handleChange, value };
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
Reference in New Issue
Block a user