Commit f75425d13bc9f6003021fd4b5d6451ae096c09b7
1 parent
9c02d8ec
perf: review tinymce code
Showing
25 changed files
with
270 additions
and
455 deletions
src/components/Tinymce/src/Editor.vue
1 | <template> | 1 | <template> |
2 | <div class="tinymce-container" :style="{ width: containerWidth }"> | 2 | <div class="tinymce-container" :style="{ width: containerWidth }"> |
3 | - <tinymce-editor | ||
4 | - :id="id" | ||
5 | - :init="initOptions" | ||
6 | - :modelValue="tinymceContent" | ||
7 | - @update:modelValue="handleChange" | ||
8 | - :tinymceScriptSrc="tinymceScriptSrc" | ||
9 | - ></tinymce-editor> | 3 | + <textarea :id="tinymceId" visibility="hidden" ref="elRef"></textarea> |
10 | </div> | 4 | </div> |
11 | </template> | 5 | </template> |
12 | 6 | ||
13 | <script lang="ts"> | 7 | <script lang="ts"> |
14 | - import TinymceEditor from './lib'; // TinyMCE vue wrapper | ||
15 | - import { defineComponent, computed } from 'vue'; | 8 | + import { |
9 | + defineComponent, | ||
10 | + computed, | ||
11 | + onMounted, | ||
12 | + nextTick, | ||
13 | + ref, | ||
14 | + unref, | ||
15 | + watch, | ||
16 | + onUnmounted, | ||
17 | + onDeactivated, | ||
18 | + } from 'vue'; | ||
16 | import { basicProps } from './props'; | 19 | import { basicProps } from './props'; |
17 | import toolbar from './toolbar'; | 20 | import toolbar from './toolbar'; |
18 | import plugins from './plugins'; | 21 | import plugins from './plugins'; |
22 | + import { getTinymce } from './getTinymce'; | ||
23 | + import { useScript } from '/@/hooks/web/useScript'; | ||
24 | + import { snowUuid } from '/@/utils/uuid'; | ||
25 | + import { bindHandlers } from './helper'; | ||
19 | 26 | ||
20 | const CDN_URL = 'https://cdn.bootcdn.net/ajax/libs/tinymce/5.5.1'; | 27 | const CDN_URL = 'https://cdn.bootcdn.net/ajax/libs/tinymce/5.5.1'; |
28 | + | ||
21 | const tinymceScriptSrc = `${CDN_URL}/tinymce.min.js`; | 29 | const tinymceScriptSrc = `${CDN_URL}/tinymce.min.js`; |
22 | 30 | ||
23 | export default defineComponent({ | 31 | export default defineComponent({ |
24 | name: 'Tinymce', | 32 | name: 'Tinymce', |
25 | - components: { TinymceEditor }, | ||
26 | props: basicProps, | 33 | props: basicProps, |
27 | - setup(props, { emit }) { | 34 | + emits: ['change', 'update:modelValue'], |
35 | + setup(props, { emit, attrs }) { | ||
36 | + const editorRef = ref<any>(null); | ||
37 | + const elRef = ref<Nullable<HTMLElement>>(null); | ||
38 | + | ||
39 | + const tinymceId = computed(() => { | ||
40 | + return snowUuid('tiny-vue'); | ||
41 | + }); | ||
42 | + | ||
28 | const tinymceContent = computed(() => { | 43 | const tinymceContent = computed(() => { |
29 | - return props.value; | 44 | + return props.modelValue; |
30 | }); | 45 | }); |
31 | - function handleChange(value: string) { | ||
32 | - emit('change', value); | ||
33 | - } | 46 | + |
34 | const containerWidth = computed(() => { | 47 | const containerWidth = computed(() => { |
35 | const width = props.width; | 48 | const width = props.width; |
36 | - // Test matches `100`, `'100'` | ||
37 | if (/^[\d]+(\.[\d]+)?$/.test(width.toString())) { | 49 | if (/^[\d]+(\.[\d]+)?$/.test(width.toString())) { |
38 | return `${width}px`; | 50 | return `${width}px`; |
39 | } | 51 | } |
40 | return width; | 52 | return width; |
41 | }); | 53 | }); |
54 | + | ||
42 | const initOptions = computed(() => { | 55 | const initOptions = computed(() => { |
43 | - const { id, height, menubar } = props; | 56 | + const { height, menubar } = props; |
44 | return { | 57 | return { |
45 | - selector: `#${id}`, | 58 | + selector: `#${unref(tinymceId)}`, |
46 | height: height, | 59 | height: height, |
47 | toolbar: toolbar, | 60 | toolbar: toolbar, |
61 | + theme: 'silver', | ||
48 | menubar: menubar, | 62 | menubar: menubar, |
49 | plugins: plugins, | 63 | plugins: plugins, |
50 | // 语言包 | 64 | // 语言包 |
51 | language_url: 'resource/tinymce/langs/zh_CN.js', | 65 | language_url: 'resource/tinymce/langs/zh_CN.js', |
52 | // 中文 | 66 | // 中文 |
53 | language: 'zh_CN', | 67 | language: 'zh_CN', |
68 | + default_link_target: '_blank', | ||
69 | + link_title: false, | ||
70 | + advlist_bullet_styles: 'square', | ||
71 | + advlist_number_styles: 'default', | ||
72 | + object_resizing: false, | ||
73 | + setup: (editor: any) => { | ||
74 | + editorRef.value = editor; | ||
75 | + editor.on('init', (e: Event) => initSetup(e)); | ||
76 | + }, | ||
54 | }; | 77 | }; |
55 | }); | 78 | }); |
56 | - return { containerWidth, initOptions, tinymceContent, handleChange, tinymceScriptSrc }; | 79 | + |
80 | + const { toPromise } = useScript({ | ||
81 | + src: tinymceScriptSrc, | ||
82 | + }); | ||
83 | + | ||
84 | + watch( | ||
85 | + () => attrs.disabled, | ||
86 | + () => { | ||
87 | + const editor = unref(editorRef); | ||
88 | + if (!editor) return; | ||
89 | + editor.setMode(attrs.disabled ? 'readonly' : 'design'); | ||
90 | + } | ||
91 | + ); | ||
92 | + | ||
93 | + onMounted(() => { | ||
94 | + nextTick(() => { | ||
95 | + init(); | ||
96 | + }); | ||
97 | + }); | ||
98 | + | ||
99 | + onUnmounted(() => { | ||
100 | + destory(); | ||
101 | + }); | ||
102 | + | ||
103 | + onDeactivated(() => { | ||
104 | + destory(); | ||
105 | + }); | ||
106 | + | ||
107 | + function destory() { | ||
108 | + if (getTinymce() !== null) { | ||
109 | + getTinymce().remove(unref(editorRef)); | ||
110 | + } | ||
111 | + } | ||
112 | + | ||
113 | + function init() { | ||
114 | + toPromise().then(() => { | ||
115 | + initEditor(); | ||
116 | + }); | ||
117 | + } | ||
118 | + | ||
119 | + function initEditor() { | ||
120 | + getTinymce().init(unref(initOptions)); | ||
121 | + } | ||
122 | + | ||
123 | + function initSetup(e: Event) { | ||
124 | + const editor = unref(editorRef); | ||
125 | + if (!editor) return; | ||
126 | + const value = props.modelValue || ''; | ||
127 | + | ||
128 | + editor.setContent(value); | ||
129 | + bindModelHandlers(editor); | ||
130 | + bindHandlers(e, attrs, unref(editorRef)); | ||
131 | + } | ||
132 | + | ||
133 | + function bindModelHandlers(editor: any) { | ||
134 | + const modelEvents = attrs.modelEvents ? attrs.modelEvents : null; | ||
135 | + const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents; | ||
136 | + watch( | ||
137 | + () => props.modelValue, | ||
138 | + (val: string, prevVal: string) => { | ||
139 | + if ( | ||
140 | + editor && | ||
141 | + typeof val === 'string' && | ||
142 | + val !== prevVal && | ||
143 | + val !== editor.getContent({ format: attrs.outputFormat }) | ||
144 | + ) { | ||
145 | + editor.setContent(val); | ||
146 | + } | ||
147 | + } | ||
148 | + ); | ||
149 | + | ||
150 | + editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => { | ||
151 | + emit('update:modelValue', editor.getContent({ format: attrs.outputFormat })); | ||
152 | + }); | ||
153 | + } | ||
154 | + | ||
155 | + function handleChange(value: string) { | ||
156 | + emit('change', value); | ||
157 | + } | ||
158 | + return { | ||
159 | + containerWidth, | ||
160 | + initOptions, | ||
161 | + tinymceContent, | ||
162 | + handleChange, | ||
163 | + tinymceScriptSrc, | ||
164 | + elRef, | ||
165 | + tinymceId, | ||
166 | + }; | ||
57 | }, | 167 | }, |
58 | }); | 168 | }); |
59 | </script> | 169 | </script> |
src/components/Tinymce/src/lib/TinyMCE.ts renamed to src/components/Tinymce/src/getTinymce.ts
1 | const getGlobal = (): any => (typeof window !== 'undefined' ? window : global); | 1 | const getGlobal = (): any => (typeof window !== 'undefined' ? window : global); |
2 | 2 | ||
3 | -const getTinymce = () => { | 3 | +export const getTinymce = () => { |
4 | const global = getGlobal(); | 4 | const global = getGlobal(); |
5 | - | ||
6 | return global && global.tinymce ? global.tinymce : null; | 5 | return global && global.tinymce ? global.tinymce : null; |
7 | }; | 6 | }; |
8 | - | ||
9 | -export { getTinymce }; |
src/components/Tinymce/src/lib/Utils.ts renamed to src/components/Tinymce/src/helper.ts
1 | -import { ComponentPublicInstance } from 'vue'; | ||
2 | - | ||
3 | const validEvents = [ | 1 | const validEvents = [ |
4 | 'onActivate', | 2 | 'onActivate', |
5 | 'onAddUndo', | 3 | 'onAddUndo', |
@@ -62,12 +60,12 @@ const validEvents = [ | @@ -62,12 +60,12 @@ const validEvents = [ | ||
62 | 'onShow', | 60 | 'onShow', |
63 | 'onSubmit', | 61 | 'onSubmit', |
64 | 'onUndo', | 62 | 'onUndo', |
65 | - 'onVisualAid' | 63 | + 'onVisualAid', |
66 | ]; | 64 | ]; |
67 | 65 | ||
68 | const isValidKey = (key: string) => validEvents.indexOf(key) !== -1; | 66 | const isValidKey = (key: string) => validEvents.indexOf(key) !== -1; |
69 | 67 | ||
70 | -const bindHandlers = (initEvent: Event, listeners: any, editor: any): void => { | 68 | +export const bindHandlers = (initEvent: Event, listeners: any, editor: any): void => { |
71 | Object.keys(listeners) | 69 | Object.keys(listeners) |
72 | .filter(isValidKey) | 70 | .filter(isValidKey) |
73 | .forEach((key: string) => { | 71 | .forEach((key: string) => { |
@@ -81,71 +79,3 @@ const bindHandlers = (initEvent: Event, listeners: any, editor: any): void => { | @@ -81,71 +79,3 @@ const bindHandlers = (initEvent: Event, listeners: any, editor: any): void => { | ||
81 | } | 79 | } |
82 | }); | 80 | }); |
83 | }; | 81 | }; |
84 | - | ||
85 | -const bindModelHandlers = (ctx: ComponentPublicInstance, editor: any) => { | ||
86 | - const modelEvents = ctx.$props.modelEvents ? ctx.$props.modelEvents : null; | ||
87 | - const normalizedEvents = Array.isArray(modelEvents) ? modelEvents.join(' ') : modelEvents; | ||
88 | - // @ts-ignore | ||
89 | - ctx.$watch('modelValue', (val: string, prevVal: string) => { | ||
90 | - if (editor && typeof val === 'string' && val !== prevVal && val !== editor.getContent({ format: ctx.$props.outputFormat })) { | ||
91 | - editor.setContent(val); | ||
92 | - } | ||
93 | - }); | ||
94 | - | ||
95 | - editor.on(normalizedEvents ? normalizedEvents : 'change keyup undo redo', () => { | ||
96 | - ctx.$emit('update:modelValue', editor.getContent({ format: ctx.$props.outputFormat })); | ||
97 | - }); | ||
98 | -}; | ||
99 | - | ||
100 | -const initEditor = (initEvent: Event, ctx: ComponentPublicInstance, editor: any) => { | ||
101 | - const value = ctx.$props.modelValue ? ctx.$props.modelValue : ''; | ||
102 | - const initialValue = ctx.$props.initialValue ? ctx.$props.initialValue : ''; | ||
103 | - | ||
104 | - editor.setContent(value || initialValue); | ||
105 | - | ||
106 | - // checks if the v-model shorthand is used (which sets an v-on:input listener) and then binds either | ||
107 | - // specified the events or defaults to "change keyup" event and emits the editor content on that event | ||
108 | - if (ctx.$attrs['onUpdate:modelValue']) { | ||
109 | - bindModelHandlers(ctx, editor); | ||
110 | - } | ||
111 | - | ||
112 | - bindHandlers(initEvent, ctx.$attrs, editor); | ||
113 | -}; | ||
114 | - | ||
115 | -let unique = 0; | ||
116 | - | ||
117 | -const uuid = (prefix: string): string => { | ||
118 | - const time = Date.now(); | ||
119 | - const random = Math.floor(Math.random() * 1000000000); | ||
120 | - | ||
121 | - unique++; | ||
122 | - | ||
123 | - return prefix + '_' + random + unique + String(time); | ||
124 | -}; | ||
125 | - | ||
126 | -const isTextarea = (element: Element | null): element is HTMLTextAreaElement => { | ||
127 | - return element !== null && element.tagName.toLowerCase() === 'textarea'; | ||
128 | -}; | ||
129 | - | ||
130 | -const normalizePluginArray = (plugins?: string | string[]): string[] => { | ||
131 | - if (typeof plugins === 'undefined' || plugins === '') { | ||
132 | - return []; | ||
133 | - } | ||
134 | - | ||
135 | - return Array.isArray(plugins) ? plugins : plugins.split(' '); | ||
136 | -}; | ||
137 | - | ||
138 | -const mergePlugins = (initPlugins: string | string[], inputPlugins?: string | string[]) => | ||
139 | - normalizePluginArray(initPlugins).concat(normalizePluginArray(inputPlugins)); | ||
140 | - | ||
141 | -const isNullOrUndefined = (value: any): value is null | undefined => value === null || value === undefined; | ||
142 | - | ||
143 | -export { | ||
144 | - bindHandlers, | ||
145 | - bindModelHandlers, | ||
146 | - initEditor, | ||
147 | - uuid, | ||
148 | - isTextarea, | ||
149 | - mergePlugins, | ||
150 | - isNullOrUndefined | ||
151 | -}; | ||
152 | \ No newline at end of file | 82 | \ No newline at end of file |
src/components/Tinymce/src/lib/ScriptLoader.ts deleted
100644 → 0
1 | -import { uuid } from './Utils'; | ||
2 | - | ||
3 | -export type callbackFn = () => void; | ||
4 | -export interface IStateObj { | ||
5 | - listeners: callbackFn[]; | ||
6 | - scriptId: string; | ||
7 | - scriptLoaded: boolean; | ||
8 | -} | ||
9 | - | ||
10 | -const createState = (): IStateObj => { | ||
11 | - return { | ||
12 | - listeners: [], | ||
13 | - scriptId: uuid('tiny-script'), | ||
14 | - scriptLoaded: false | ||
15 | - }; | ||
16 | -}; | ||
17 | - | ||
18 | -interface ScriptLoader { | ||
19 | - load: (doc: Document, url: string, callback: callbackFn) => void; | ||
20 | - reinitialize: () => void; | ||
21 | -} | ||
22 | - | ||
23 | -const CreateScriptLoader = (): ScriptLoader => { | ||
24 | - let state: IStateObj = createState(); | ||
25 | - | ||
26 | - const injectScriptTag = (scriptId: string, doc: Document, url: string, callback: callbackFn) => { | ||
27 | - const scriptTag = doc.createElement('script'); | ||
28 | - scriptTag.referrerPolicy = 'origin'; | ||
29 | - scriptTag.type = 'application/javascript'; | ||
30 | - scriptTag.id = scriptId; | ||
31 | - scriptTag.src = url; | ||
32 | - | ||
33 | - const handler = () => { | ||
34 | - scriptTag.removeEventListener('load', handler); | ||
35 | - callback(); | ||
36 | - }; | ||
37 | - scriptTag.addEventListener('load', handler); | ||
38 | - if (doc.head) { | ||
39 | - doc.head.appendChild(scriptTag); | ||
40 | - } | ||
41 | - }; | ||
42 | - | ||
43 | - const load = (doc: Document, url: string, callback: callbackFn) => { | ||
44 | - if (state.scriptLoaded) { | ||
45 | - callback(); | ||
46 | - } else { | ||
47 | - state.listeners.push(callback); | ||
48 | - if (!doc.getElementById(state.scriptId)) { | ||
49 | - injectScriptTag(state.scriptId, doc, url, () => { | ||
50 | - state.listeners.forEach((fn) => fn()); | ||
51 | - state.scriptLoaded = true; | ||
52 | - }); | ||
53 | - } | ||
54 | - } | ||
55 | - }; | ||
56 | - | ||
57 | - // Only to be used by tests. | ||
58 | - const reinitialize = () => { | ||
59 | - state = createState(); | ||
60 | - }; | ||
61 | - | ||
62 | - return { | ||
63 | - load, | ||
64 | - reinitialize | ||
65 | - }; | ||
66 | -}; | ||
67 | - | ||
68 | -const ScriptLoader = CreateScriptLoader(); | ||
69 | - | ||
70 | -export { | ||
71 | - ScriptLoader | ||
72 | -}; | ||
73 | \ No newline at end of file | 0 | \ No newline at end of file |
src/components/Tinymce/src/lib/components/Editor.ts deleted
100644 → 0
1 | -/** | ||
2 | - * Copyright (c) 2018-present, Ephox, Inc. | ||
3 | - * | ||
4 | - * This source code is licensed under the Apache 2 license found in the | ||
5 | - * LICENSE file in the root directory of this source tree. | ||
6 | - * | ||
7 | - */ | ||
8 | - | ||
9 | -// import { ThisTypedComponentOptionsWithRecordProps } from 'vue/types/options'; | ||
10 | -// import { CreateElement, Vue } from 'vue/types/vue'; | ||
11 | - | ||
12 | -import { ScriptLoader } from '../ScriptLoader'; | ||
13 | -import { getTinymce } from '../TinyMCE'; | ||
14 | -import { initEditor, isTextarea, mergePlugins, uuid, isNullOrUndefined } from '../Utils'; | ||
15 | -import { editorProps, IPropTypes } from './EditorPropTypes'; | ||
16 | -import { h, defineComponent, ComponentPublicInstance } from 'vue' | ||
17 | - | ||
18 | - | ||
19 | -export interface IEditor { | ||
20 | - $props: Partial<IPropTypes> | ||
21 | -} | ||
22 | - | ||
23 | -declare module '@vue/runtime-core' { | ||
24 | - interface ComponentCustomProperties { | ||
25 | - elementId: string; | ||
26 | - element: Element | null; | ||
27 | - editor: any; | ||
28 | - inlineEditor: boolean; | ||
29 | - $props: Partial<IPropTypes>; | ||
30 | - } | ||
31 | -} | ||
32 | - | ||
33 | - | ||
34 | -const renderInline = (id: string, tagName?: string) => { | ||
35 | - return h(tagName ? tagName : 'div', { | ||
36 | - id | ||
37 | - }); | ||
38 | -}; | ||
39 | - | ||
40 | -const renderIframe = (id: string) => { | ||
41 | - return h('textarea', { | ||
42 | - id, | ||
43 | - visibility: 'hidden' | ||
44 | - }); | ||
45 | -}; | ||
46 | - | ||
47 | -const initialise = (ctx: ComponentPublicInstance) => () => { | ||
48 | - const finalInit = { | ||
49 | - ...ctx.$props.init, | ||
50 | - readonly: ctx.$props.disabled, | ||
51 | - selector: `#${ctx.elementId}`, | ||
52 | - plugins: mergePlugins(ctx.$props.init && ctx.$props.init.plugins, ctx.$props.plugins), | ||
53 | - toolbar: ctx.$props.toolbar || (ctx.$props.init && ctx.$props.init.toolbar), | ||
54 | - inline: ctx.inlineEditor, | ||
55 | - setup: (editor: any) => { | ||
56 | - ctx.editor = editor; | ||
57 | - editor.on('init', (e: Event) => initEditor(e, ctx, editor)); | ||
58 | - | ||
59 | - if (ctx.$props.init && typeof ctx.$props.init.setup === 'function') { | ||
60 | - ctx.$props.init.setup(editor); | ||
61 | - } | ||
62 | - } | ||
63 | - }; | ||
64 | - | ||
65 | - if (isTextarea(ctx.element)) { | ||
66 | - ctx.element.style.visibility = ''; | ||
67 | - } | ||
68 | - | ||
69 | - getTinymce().init(finalInit); | ||
70 | -}; | ||
71 | - | ||
72 | -export const Editor = defineComponent({ | ||
73 | - props: editorProps, | ||
74 | - created() { | ||
75 | - this.elementId = this.$props.id || uuid('tiny-vue'); | ||
76 | - this.inlineEditor = (this.$props.init && this.$props.init.inline) || this.$props.inline; | ||
77 | - }, | ||
78 | - watch: { | ||
79 | - disabled() { | ||
80 | - (this as any).editor.setMode(this.disabled ? 'readonly' : 'design'); | ||
81 | - } | ||
82 | - }, | ||
83 | - mounted() { | ||
84 | - this.element = this.$el; | ||
85 | - | ||
86 | - if (getTinymce() !== null) { | ||
87 | - initialise(this)(); | ||
88 | - } else if (this.element && this.element.ownerDocument) { | ||
89 | - const channel = this.$props.cloudChannel ? this.$props.cloudChannel : '5'; | ||
90 | - const apiKey = this.$props.apiKey ? this.$props.apiKey : 'no-api-key'; | ||
91 | - | ||
92 | - const scriptSrc = isNullOrUndefined(this.$props.tinymceScriptSrc) ? | ||
93 | - `https://cdn.tiny.cloud/1/${apiKey}/tinymce/${channel}/tinymce.min.js` : | ||
94 | - this.$props.tinymceScriptSrc; | ||
95 | - | ||
96 | - ScriptLoader.load( | ||
97 | - this.element.ownerDocument, | ||
98 | - scriptSrc, | ||
99 | - initialise(this) | ||
100 | - ); | ||
101 | - } | ||
102 | - }, | ||
103 | - beforeUnmount() { | ||
104 | - if (getTinymce() !== null) { | ||
105 | - getTinymce().remove(this.editor); | ||
106 | - } | ||
107 | - }, | ||
108 | - render() { | ||
109 | - return this.inlineEditor ? renderInline(this.elementId, this.$props.tagName) : renderIframe(this.elementId); | ||
110 | - } | ||
111 | -}) |
src/components/Tinymce/src/lib/components/EditorPropTypes.ts deleted
100644 → 0
1 | -/** | ||
2 | - * Copyright (c) 2018-present, Ephox, Inc. | ||
3 | - * | ||
4 | - * This source code is licensed under the Apache 2 license found in the | ||
5 | - * LICENSE file in the root directory of this source tree. | ||
6 | - * | ||
7 | - */ | ||
8 | - | ||
9 | -export type CopyProps<T> = { [P in keyof T]: any }; | ||
10 | - | ||
11 | -export interface IPropTypes { | ||
12 | - apiKey: string; | ||
13 | - cloudChannel: string; | ||
14 | - id: string; | ||
15 | - init: any; | ||
16 | - initialValue: string; | ||
17 | - outputFormat: 'html' | 'text'; | ||
18 | - inline: boolean; | ||
19 | - modelEvents: string[] | string; | ||
20 | - plugins: string[] | string; | ||
21 | - tagName: string; | ||
22 | - toolbar: string[] | string; | ||
23 | - modelValue: string; | ||
24 | - disabled: boolean; | ||
25 | - tinymceScriptSrc: string; | ||
26 | -} | ||
27 | - | ||
28 | -export const editorProps: CopyProps<IPropTypes> = { | ||
29 | - apiKey: String, | ||
30 | - cloudChannel: String, | ||
31 | - id: String, | ||
32 | - init: Object, | ||
33 | - initialValue: String, | ||
34 | - inline: Boolean, | ||
35 | - modelEvents: [String, Array], | ||
36 | - plugins: [String, Array], | ||
37 | - tagName: String, | ||
38 | - toolbar: [String, Array], | ||
39 | - modelValue: String, | ||
40 | - disabled: Boolean, | ||
41 | - tinymceScriptSrc: String, | ||
42 | - outputFormat: { | ||
43 | - type: String, | ||
44 | - validator: (prop: string) => prop === 'html' || prop === 'text' | ||
45 | - }, | ||
46 | -}; |
src/components/Tinymce/src/lib/global.d.ts deleted
100644 → 0
src/components/Tinymce/src/lib/index.ts deleted
100644 → 0
src/components/Tinymce/src/props.ts
1 | import { PropType } from 'vue'; | 1 | import { PropType } from 'vue'; |
2 | 2 | ||
3 | export const basicProps = { | 3 | export const basicProps = { |
4 | - id: { | ||
5 | - type: String as PropType<string>, | ||
6 | - default: () => { | ||
7 | - return `tinymce-${new Date().getTime()}${(Math.random() * 1000).toFixed(0)}`; | ||
8 | - }, | ||
9 | - }, | ||
10 | menubar: { | 4 | menubar: { |
11 | type: String as PropType<string>, | 5 | type: String as PropType<string>, |
12 | default: 'file edit insert view format table', | 6 | default: 'file edit insert view format table', |
@@ -15,6 +9,10 @@ export const basicProps = { | @@ -15,6 +9,10 @@ export const basicProps = { | ||
15 | type: String as PropType<string>, | 9 | type: String as PropType<string>, |
16 | // default: '' | 10 | // default: '' |
17 | }, | 11 | }, |
12 | + modelValue: { | ||
13 | + type: String as PropType<string>, | ||
14 | + // default: '' | ||
15 | + }, | ||
18 | // 高度 | 16 | // 高度 |
19 | height: { | 17 | height: { |
20 | type: [Number, String] as PropType<string | number>, | 18 | type: [Number, String] as PropType<string | number>, |
src/router/menus/modules/demo/charts.ts
@@ -6,23 +6,23 @@ const menu: MenuModule = { | @@ -6,23 +6,23 @@ const menu: MenuModule = { | ||
6 | path: '/charts', | 6 | path: '/charts', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/apexChart', | 9 | + path: 'apexChart', |
10 | name: 'ApexChart', | 10 | name: 'ApexChart', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/echarts', | 13 | + path: 'echarts', |
14 | name: 'Echarts', | 14 | name: 'Echarts', |
15 | children: [ | 15 | children: [ |
16 | { | 16 | { |
17 | - path: '/map', | 17 | + path: 'map', |
18 | name: '地图', | 18 | name: '地图', |
19 | }, | 19 | }, |
20 | { | 20 | { |
21 | - path: '/line', | 21 | + path: 'line', |
22 | name: '折线图', | 22 | name: '折线图', |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | - path: '/pie', | 25 | + path: 'pie', |
26 | name: '饼图', | 26 | name: '饼图', |
27 | }, | 27 | }, |
28 | ], | 28 | ], |
src/router/menus/modules/demo/comp.ts
@@ -6,16 +6,16 @@ const menu: MenuModule = { | @@ -6,16 +6,16 @@ const menu: MenuModule = { | ||
6 | path: '/comp', | 6 | path: '/comp', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/basic', | 9 | + path: 'basic', |
10 | name: '基础组件', | 10 | name: '基础组件', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/countTo', | 13 | + path: 'countTo', |
14 | name: '数字动画', | 14 | name: '数字动画', |
15 | }, | 15 | }, |
16 | 16 | ||
17 | { | 17 | { |
18 | - path: '/scroll', | 18 | + path: 'scroll', |
19 | name: '滚动组件', | 19 | name: '滚动组件', |
20 | children: [ | 20 | children: [ |
21 | { | 21 | { |
@@ -33,53 +33,39 @@ const menu: MenuModule = { | @@ -33,53 +33,39 @@ const menu: MenuModule = { | ||
33 | ], | 33 | ], |
34 | }, | 34 | }, |
35 | { | 35 | { |
36 | - path: '/modal', | 36 | + path: 'modal', |
37 | name: '弹窗扩展', | 37 | name: '弹窗扩展', |
38 | }, | 38 | }, |
39 | { | 39 | { |
40 | - path: '/drawer', | 40 | + path: 'drawer', |
41 | name: '抽屉扩展', | 41 | name: '抽屉扩展', |
42 | }, | 42 | }, |
43 | { | 43 | { |
44 | - path: '/desc', | 44 | + path: 'desc', |
45 | name: '详情组件', | 45 | name: '详情组件', |
46 | }, | 46 | }, |
47 | { | 47 | { |
48 | - path: '/verify', | 48 | + path: 'verify', |
49 | name: '验证组件', | 49 | name: '验证组件', |
50 | children: [ | 50 | children: [ |
51 | { | 51 | { |
52 | - path: '/drag', | 52 | + path: 'drag', |
53 | name: '拖拽校验', | 53 | name: '拖拽校验', |
54 | }, | 54 | }, |
55 | { | 55 | { |
56 | - path: '/rotate', | 56 | + path: 'rotate', |
57 | name: '图片还原校验', | 57 | name: '图片还原校验', |
58 | }, | 58 | }, |
59 | ], | 59 | ], |
60 | }, | 60 | }, |
61 | { | 61 | { |
62 | - path: '/qrcode', | 62 | + path: 'qrcode', |
63 | name: '二维码组件', | 63 | name: '二维码组件', |
64 | }, | 64 | }, |
65 | { | 65 | { |
66 | - path: '/strength-meter', | 66 | + path: 'strength-meter', |
67 | name: '密码强度组件', | 67 | name: '密码强度组件', |
68 | }, | 68 | }, |
69 | - { | ||
70 | - path: '/tinymce', | ||
71 | - name: '富文本', | ||
72 | - children: [ | ||
73 | - { | ||
74 | - path: '/index', | ||
75 | - name: '基础使用', | ||
76 | - }, | ||
77 | - { | ||
78 | - path: '/editor', | ||
79 | - name: '嵌入form使用', | ||
80 | - }, | ||
81 | - ], | ||
82 | - }, | ||
83 | ], | 69 | ], |
84 | }, | 70 | }, |
85 | }; | 71 | }; |
src/router/menus/modules/demo/editor.ts
@@ -6,9 +6,23 @@ const menu: MenuModule = { | @@ -6,9 +6,23 @@ const menu: MenuModule = { | ||
6 | path: '/editor', | 6 | path: '/editor', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/markdown', | 9 | + path: 'markdown', |
10 | name: 'markdown编辑器', | 10 | name: 'markdown编辑器', |
11 | }, | 11 | }, |
12 | + { | ||
13 | + path: 'tinymce', | ||
14 | + name: '富文本', | ||
15 | + children: [ | ||
16 | + { | ||
17 | + path: 'index', | ||
18 | + name: '基础使用', | ||
19 | + }, | ||
20 | + // { | ||
21 | + // path: 'editor', | ||
22 | + // name: '嵌入form使用', | ||
23 | + // }, | ||
24 | + ], | ||
25 | + }, | ||
12 | ], | 26 | ], |
13 | }, | 27 | }, |
14 | }; | 28 | }; |
src/router/menus/modules/demo/excel.ts
@@ -6,23 +6,21 @@ const menu: MenuModule = { | @@ -6,23 +6,21 @@ const menu: MenuModule = { | ||
6 | path: '/excel', | 6 | path: '/excel', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/customExport', | 9 | + path: 'customExport', |
10 | name: '选择导出格式', | 10 | name: '选择导出格式', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/jsonExport', | 13 | + path: 'jsonExport', |
14 | name: 'JSON数据导出', | 14 | name: 'JSON数据导出', |
15 | }, | 15 | }, |
16 | { | 16 | { |
17 | - path: '/arrayExport', | 17 | + path: 'arrayExport', |
18 | name: 'Array数据导出', | 18 | name: 'Array数据导出', |
19 | }, | 19 | }, |
20 | { | 20 | { |
21 | - path: '/importExcel', | 21 | + path: 'importExcel', |
22 | name: '导入', | 22 | name: '导入', |
23 | }, | 23 | }, |
24 | - // ], | ||
25 | - // }, | ||
26 | ], | 24 | ], |
27 | }, | 25 | }, |
28 | }; | 26 | }; |
src/router/menus/modules/demo/exception.ts
@@ -6,27 +6,27 @@ const menu: MenuModule = { | @@ -6,27 +6,27 @@ const menu: MenuModule = { | ||
6 | path: '/exception', | 6 | path: '/exception', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/404', | 9 | + path: '404', |
10 | name: '404', | 10 | name: '404', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/500', | 13 | + path: '500', |
14 | name: '500', | 14 | name: '500', |
15 | }, | 15 | }, |
16 | { | 16 | { |
17 | - path: '/net-work-error', | 17 | + path: 'net-work-error', |
18 | name: '网络错误', | 18 | name: '网络错误', |
19 | }, | 19 | }, |
20 | { | 20 | { |
21 | - path: '/page-time-out', | 21 | + path: 'page-time-out', |
22 | name: '页面超时', | 22 | name: '页面超时', |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | - path: '/not-data', | 25 | + path: 'not-data', |
26 | name: '无数据', | 26 | name: '无数据', |
27 | }, | 27 | }, |
28 | { | 28 | { |
29 | - path: '/error-log', | 29 | + path: 'error-log', |
30 | name: '错误日志', | 30 | name: '错误日志', |
31 | }, | 31 | }, |
32 | ], | 32 | ], |
src/router/menus/modules/demo/feat.ts
@@ -6,55 +6,55 @@ const menu: MenuModule = { | @@ -6,55 +6,55 @@ const menu: MenuModule = { | ||
6 | path: '/feat', | 6 | path: '/feat', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/icon', | 9 | + path: 'icon', |
10 | name: '图标', | 10 | name: '图标', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/tabs', | 13 | + path: 'tabs', |
14 | name: '标签页操作', | 14 | name: '标签页操作', |
15 | }, | 15 | }, |
16 | { | 16 | { |
17 | - path: '/context-menu', | 17 | + path: 'context-menu', |
18 | name: '右键菜单', | 18 | name: '右键菜单', |
19 | }, | 19 | }, |
20 | { | 20 | { |
21 | - path: '/click-out-side', | 21 | + path: 'click-out-side', |
22 | name: 'ClickOutSide', | 22 | name: 'ClickOutSide', |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | - path: '/img-preview', | 25 | + path: 'img-preview', |
26 | name: '图片预览', | 26 | name: '图片预览', |
27 | }, | 27 | }, |
28 | { | 28 | { |
29 | - path: '/i18n', | 29 | + path: 'i18n', |
30 | name: '国际化', | 30 | name: '国际化', |
31 | }, | 31 | }, |
32 | { | 32 | { |
33 | - path: '/copy', | 33 | + path: 'copy', |
34 | name: '剪切板', | 34 | name: '剪切板', |
35 | }, | 35 | }, |
36 | { | 36 | { |
37 | - path: '/msg', | 37 | + path: 'msg', |
38 | name: '消息提示', | 38 | name: '消息提示', |
39 | }, | 39 | }, |
40 | { | 40 | { |
41 | - path: '/watermark', | 41 | + path: 'watermark', |
42 | name: '水印', | 42 | name: '水印', |
43 | }, | 43 | }, |
44 | { | 44 | { |
45 | - path: '/full-screen', | 45 | + path: 'full-screen', |
46 | name: '全屏', | 46 | name: '全屏', |
47 | }, | 47 | }, |
48 | { | 48 | { |
49 | - path: '/testTab', | 49 | + path: 'testTab', |
50 | name: '带参Tab', | 50 | name: '带参Tab', |
51 | children: [ | 51 | children: [ |
52 | { | 52 | { |
53 | - path: '/id1', | 53 | + path: 'id1', |
54 | name: '带参tab1', | 54 | name: '带参tab1', |
55 | }, | 55 | }, |
56 | { | 56 | { |
57 | - path: '/id2', | 57 | + path: 'id2', |
58 | name: '带参tab2', | 58 | name: '带参tab2', |
59 | }, | 59 | }, |
60 | ], | 60 | ], |
src/router/menus/modules/demo/form.ts
@@ -6,31 +6,31 @@ const menu: MenuModule = { | @@ -6,31 +6,31 @@ const menu: MenuModule = { | ||
6 | name: 'Form', | 6 | name: 'Form', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/basic', | 9 | + path: 'basic', |
10 | name: '基础表单', | 10 | name: '基础表单', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/useForm', | 13 | + path: 'useForm', |
14 | name: 'useForm', | 14 | name: 'useForm', |
15 | }, | 15 | }, |
16 | { | 16 | { |
17 | - path: '/refForm', | 17 | + path: 'refForm', |
18 | name: 'RefForm', | 18 | name: 'RefForm', |
19 | }, | 19 | }, |
20 | { | 20 | { |
21 | - path: '/advancedForm', | 21 | + path: 'advancedForm', |
22 | name: '可收缩表单', | 22 | name: '可收缩表单', |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | - path: '/ruleForm', | 25 | + path: 'ruleForm', |
26 | name: '表单校验', | 26 | name: '表单校验', |
27 | }, | 27 | }, |
28 | { | 28 | { |
29 | - path: '/dynamicForm', | 29 | + path: 'dynamicForm', |
30 | name: '动态表单', | 30 | name: '动态表单', |
31 | }, | 31 | }, |
32 | { | 32 | { |
33 | - path: '/customerForm', | 33 | + path: 'customerForm', |
34 | name: '自定义组件', | 34 | name: '自定义组件', |
35 | }, | 35 | }, |
36 | ], | 36 | ], |
src/router/menus/modules/demo/iframe.ts
@@ -6,15 +6,15 @@ const menu: MenuModule = { | @@ -6,15 +6,15 @@ const menu: MenuModule = { | ||
6 | path: '/frame', | 6 | path: '/frame', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/antv', | 9 | + path: 'antv', |
10 | name: 'antVue文档(内嵌)', | 10 | name: 'antVue文档(内嵌)', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/doc', | 13 | + path: 'doc', |
14 | name: '项目文档(内嵌)', | 14 | name: '项目文档(内嵌)', |
15 | }, | 15 | }, |
16 | { | 16 | { |
17 | - path: '/docExternal', | 17 | + path: 'docExternal', |
18 | name: '项目文档(外链)', | 18 | name: '项目文档(外链)', |
19 | }, | 19 | }, |
20 | ], | 20 | ], |
src/router/menus/modules/demo/permission.ts
@@ -6,37 +6,37 @@ const menu: MenuModule = { | @@ -6,37 +6,37 @@ const menu: MenuModule = { | ||
6 | path: '/permission', | 6 | path: '/permission', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/front', | 9 | + path: 'front', |
10 | name: '基于前端', | 10 | name: '基于前端', |
11 | children: [ | 11 | children: [ |
12 | { | 12 | { |
13 | - path: '/page', | 13 | + path: 'page', |
14 | name: '页面权限', | 14 | name: '页面权限', |
15 | }, | 15 | }, |
16 | { | 16 | { |
17 | - path: '/btn', | 17 | + path: 'btn', |
18 | name: '按钮权限', | 18 | name: '按钮权限', |
19 | }, | 19 | }, |
20 | { | 20 | { |
21 | - path: '/auth-pageA', | 21 | + path: 'auth-pageA', |
22 | name: '权限测试页A', | 22 | name: '权限测试页A', |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | - path: '/auth-pageB', | 25 | + path: 'auth-pageB', |
26 | name: '权限测试页B', | 26 | name: '权限测试页B', |
27 | }, | 27 | }, |
28 | ], | 28 | ], |
29 | }, | 29 | }, |
30 | { | 30 | { |
31 | - path: '/back', | 31 | + path: 'back', |
32 | name: '基于后台', | 32 | name: '基于后台', |
33 | children: [ | 33 | children: [ |
34 | { | 34 | { |
35 | - path: '/page', | 35 | + path: 'page', |
36 | name: '页面权限', | 36 | name: '页面权限', |
37 | }, | 37 | }, |
38 | { | 38 | { |
39 | - path: '/btn', | 39 | + path: 'btn', |
40 | name: '按钮权限', | 40 | name: '按钮权限', |
41 | }, | 41 | }, |
42 | ], | 42 | ], |
src/router/menus/modules/demo/table.ts
@@ -6,59 +6,59 @@ const menu: MenuModule = { | @@ -6,59 +6,59 @@ const menu: MenuModule = { | ||
6 | name: 'Table', | 6 | name: 'Table', |
7 | children: [ | 7 | children: [ |
8 | { | 8 | { |
9 | - path: '/basic', | 9 | + path: 'basic', |
10 | name: '基础表格', | 10 | name: '基础表格', |
11 | }, | 11 | }, |
12 | { | 12 | { |
13 | - path: '/treeTable', | 13 | + path: 'treeTable', |
14 | name: '树形表格', | 14 | name: '树形表格', |
15 | }, | 15 | }, |
16 | { | 16 | { |
17 | - path: '/fetchTable', | 17 | + path: 'fetchTable', |
18 | name: '远程加载', | 18 | name: '远程加载', |
19 | }, | 19 | }, |
20 | { | 20 | { |
21 | - path: '/fixedColumn', | 21 | + path: 'fixedColumn', |
22 | name: '固定列', | 22 | name: '固定列', |
23 | }, | 23 | }, |
24 | { | 24 | { |
25 | - path: '/customerCell', | 25 | + path: 'customerCell', |
26 | name: '自定义列', | 26 | name: '自定义列', |
27 | }, | 27 | }, |
28 | { | 28 | { |
29 | - path: '/formTable', | 29 | + path: 'formTable', |
30 | name: '开启搜索区域', | 30 | name: '开启搜索区域', |
31 | }, | 31 | }, |
32 | { | 32 | { |
33 | - path: '/useTable', | 33 | + path: 'useTable', |
34 | name: 'UseTable', | 34 | name: 'UseTable', |
35 | }, | 35 | }, |
36 | { | 36 | { |
37 | - path: '/refTable', | 37 | + path: 'refTable', |
38 | name: 'RefTable', | 38 | name: 'RefTable', |
39 | }, | 39 | }, |
40 | { | 40 | { |
41 | - path: '/multipleHeader', | 41 | + path: 'multipleHeader', |
42 | name: '多级表头', | 42 | name: '多级表头', |
43 | }, | 43 | }, |
44 | { | 44 | { |
45 | - path: '/mergeHeader', | 45 | + path: 'mergeHeader', |
46 | name: '合并单元格', | 46 | name: '合并单元格', |
47 | }, | 47 | }, |
48 | { | 48 | { |
49 | - path: '/expandTable', | 49 | + path: 'expandTable', |
50 | name: '可展开表格', | 50 | name: '可展开表格', |
51 | }, | 51 | }, |
52 | { | 52 | { |
53 | - path: '/fixedHeight', | 53 | + path: 'fixedHeight', |
54 | name: '定高/头部自定义', | 54 | name: '定高/头部自定义', |
55 | }, | 55 | }, |
56 | { | 56 | { |
57 | - path: '/footerTable', | 57 | + path: 'footerTable', |
58 | name: '表尾行合计', | 58 | name: '表尾行合计', |
59 | }, | 59 | }, |
60 | { | 60 | { |
61 | - path: '/editCellTable', | 61 | + path: 'editCellTable', |
62 | name: '可编辑单元格', | 62 | name: '可编辑单元格', |
63 | }, | 63 | }, |
64 | ], | 64 | ], |
src/router/routes/modules/demo/comp.ts
@@ -136,31 +136,5 @@ export default { | @@ -136,31 +136,5 @@ export default { | ||
136 | title: '密码强度组件', | 136 | title: '密码强度组件', |
137 | }, | 137 | }, |
138 | }, | 138 | }, |
139 | - { | ||
140 | - path: '/tinymce', | ||
141 | - name: 'TinymceDemo', | ||
142 | - meta: { | ||
143 | - title: '富文本', | ||
144 | - }, | ||
145 | - redirect: '/comp/tinymce/index', | ||
146 | - children: [ | ||
147 | - { | ||
148 | - path: 'index', | ||
149 | - name: 'Tinymce', | ||
150 | - component: () => import('/@/views/demo/comp/tinymce/index.vue'), | ||
151 | - meta: { | ||
152 | - title: '基础使用', | ||
153 | - }, | ||
154 | - }, | ||
155 | - { | ||
156 | - path: 'editor', | ||
157 | - name: 'TinymceEditor', | ||
158 | - component: () => import('/@/views/demo/comp/tinymce/Editor.vue'), | ||
159 | - meta: { | ||
160 | - title: '嵌入form使用', | ||
161 | - }, | ||
162 | - }, | ||
163 | - ], | ||
164 | - }, | ||
165 | ], | 139 | ], |
166 | } as AppRouteModule; | 140 | } as AppRouteModule; |
src/router/routes/modules/demo/editor.ts
@@ -23,5 +23,32 @@ export default { | @@ -23,5 +23,32 @@ export default { | ||
23 | title: 'markdown编辑器', | 23 | title: 'markdown编辑器', |
24 | }, | 24 | }, |
25 | }, | 25 | }, |
26 | + { | ||
27 | + path: '/tinymce', | ||
28 | + name: 'TinymceDemo', | ||
29 | + meta: { | ||
30 | + title: '富文本', | ||
31 | + }, | ||
32 | + redirect: '/editor/tinymce/index', | ||
33 | + children: [ | ||
34 | + { | ||
35 | + path: 'index', | ||
36 | + name: 'TinymceBasicDemo', | ||
37 | + component: () => import('/@/views/demo/editor/tinymce/index.vue'), | ||
38 | + meta: { | ||
39 | + title: '基础使用', | ||
40 | + }, | ||
41 | + }, | ||
42 | + // TODO | ||
43 | + // { | ||
44 | + // path: 'editor', | ||
45 | + // name: 'TinymceFormDemo', | ||
46 | + // component: () => import('/@/views/demo/comp/tinymce/Editor.vue'), | ||
47 | + // meta: { | ||
48 | + // title: '嵌入form使用', | ||
49 | + // }, | ||
50 | + // }, | ||
51 | + ], | ||
52 | + }, | ||
26 | ], | 53 | ], |
27 | } as AppRouteModule; | 54 | } as AppRouteModule; |
src/utils/is.ts
@@ -67,3 +67,7 @@ export const isServer = typeof window === 'undefined'; | @@ -67,3 +67,7 @@ export const isServer = typeof window === 'undefined'; | ||
67 | export function isImageDom(o: Element) { | 67 | export function isImageDom(o: Element) { |
68 | return o && ['IMAGE', 'IMG'].includes(o.tagName); | 68 | return o && ['IMAGE', 'IMG'].includes(o.tagName); |
69 | } | 69 | } |
70 | + | ||
71 | +export const isTextarea = (element: Element | null): element is HTMLTextAreaElement => { | ||
72 | + return element !== null && element.tagName.toLowerCase() === 'textarea'; | ||
73 | +}; |
src/utils/uuid.ts
@@ -17,3 +17,11 @@ export function buildUUID(): string { | @@ -17,3 +17,11 @@ export function buildUUID(): string { | ||
17 | } | 17 | } |
18 | return uuid.replace(/-/g, ''); | 18 | return uuid.replace(/-/g, ''); |
19 | } | 19 | } |
20 | + | ||
21 | +let unique = 0; | ||
22 | +export function snowUuid(prefix: string): string { | ||
23 | + const time = Date.now(); | ||
24 | + const random = Math.floor(Math.random() * 1000000000); | ||
25 | + unique++; | ||
26 | + return prefix + '_' + random + unique + String(time); | ||
27 | +} |
src/views/demo/comp/tinymce/Editor.vue renamed to src/views/demo/editor/tinymce/Editor.vue
@@ -43,7 +43,7 @@ | @@ -43,7 +43,7 @@ | ||
43 | }, | 43 | }, |
44 | ]; | 44 | ]; |
45 | export default defineComponent({ | 45 | export default defineComponent({ |
46 | - components: { BasicForm, CollapseContainer, Tinymce }, | 46 | + components: { BasicForm, CollapseContainer }, |
47 | setup() { | 47 | setup() { |
48 | const { createMessage } = useMessage(); | 48 | const { createMessage } = useMessage(); |
49 | 49 |
src/views/demo/comp/tinymce/index.vue renamed to src/views/demo/editor/tinymce/index.vue
1 | <template> | 1 | <template> |
2 | <div class="flex p-4"> | 2 | <div class="flex p-4"> |
3 | - <Tinymce value="Hello, World!" @change="handleChange" width="100%" /> | 3 | + {{ value }} |
4 | + <Tinymce v-model="value" @change="handleChange" width="100%" /> | ||
4 | </div> | 5 | </div> |
5 | </template> | 6 | </template> |
6 | <script lang="ts"> | 7 | <script lang="ts"> |
7 | - import { defineComponent } from 'vue'; | 8 | + import { defineComponent, ref } from 'vue'; |
8 | import { Tinymce } from '/@/components/Tinymce/index'; | 9 | import { Tinymce } from '/@/components/Tinymce/index'; |
9 | 10 | ||
10 | export default defineComponent({ | 11 | export default defineComponent({ |
11 | components: { Tinymce }, | 12 | components: { Tinymce }, |
12 | setup() { | 13 | setup() { |
14 | + const value = ref('hello world!'); | ||
13 | function handleChange(value: string) { | 15 | function handleChange(value: string) { |
14 | console.log(value); | 16 | console.log(value); |
15 | } | 17 | } |
16 | - return { handleChange }; | 18 | + // setTimeout(() => { |
19 | + // value.value = '1233'; | ||
20 | + // }, 5000); | ||
21 | + return { handleChange, value }; | ||
17 | }, | 22 | }, |
18 | }); | 23 | }); |
19 | </script> | 24 | </script> |