zydx-plus 1.10.34 → 1.10.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -2
- package/src/components/editor/src/editor.vue +1 -1
- package/src/components/editor/src/wangeditor5-for-vue3/core/editable.js +328 -0
- package/src/components/editor/src/wangeditor5-for-vue3/core/editor.js +53 -0
- package/src/components/editor/src/wangeditor5-for-vue3/core/hooks.js +134 -0
- package/src/components/editor/src/wangeditor5-for-vue3/core/toolbar.js +79 -0
- package/src/components/editor/src/wangeditor5-for-vue3/core/types.js +2 -0
- package/src/components/editor/src/wangeditor5-for-vue3/core/utils.js +65 -0
- package/src/components/editor/src/wangeditor5-for-vue3/index.js +5 -0
- package/src/index.js +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zydx-plus",
|
|
3
|
-
"version": "1.10.
|
|
3
|
+
"version": "1.10.36",
|
|
4
4
|
"description": "Vue.js",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"files": [
|
|
@@ -24,7 +24,6 @@
|
|
|
24
24
|
"pdfjs-dist": "^2.0.943",
|
|
25
25
|
"flipbook-vue": "^1.0.0-beta.4",
|
|
26
26
|
"@wangeditor/editor": "^5.1.23",
|
|
27
|
-
"wangeditor5-for-vue3": "^1.0.0",
|
|
28
27
|
"@wangeditor/plugin-upload-attachment": "^1.1.0",
|
|
29
28
|
"snabbdom": "^3.5.1"
|
|
30
29
|
},
|
|
@@ -12,7 +12,7 @@ import { Boot } from '@wangeditor/editor'
|
|
|
12
12
|
import attachmentModule from '@wangeditor/plugin-upload-attachment'
|
|
13
13
|
import { menuModule } from './menu'
|
|
14
14
|
import { nodeModule } from './node'
|
|
15
|
-
import { useWangEditor,WeEditable, WeToolbar } from 'wangeditor5-for-vue3'
|
|
15
|
+
import { useWangEditor,WeEditable, WeToolbar } from './wangeditor5-for-vue3/index'
|
|
16
16
|
Boot.registerModule(attachmentModule)
|
|
17
17
|
Boot.registerModule(nodeModule)
|
|
18
18
|
Boot.registerModule(menuModule)
|
|
@@ -0,0 +1,328 @@
|
|
|
1
|
+
import { createEditor, SlateEditor, SlateTransforms } from '@wangeditor/editor';
|
|
2
|
+
import debounce from 'lodash.debounce';
|
|
3
|
+
import { computed, defineComponent, h, nextTick, onBeforeUnmount, onMounted, ref, shallowReactive, toRaw, toRef, watch, watchEffect } from 'vue';
|
|
4
|
+
import { useWeContent } from './hooks';
|
|
5
|
+
import { withInstall, is } from './utils';
|
|
6
|
+
export const EditableProps = {
|
|
7
|
+
handle: {
|
|
8
|
+
type: Symbol,
|
|
9
|
+
required: true
|
|
10
|
+
},
|
|
11
|
+
json: {
|
|
12
|
+
type: [Array, String]
|
|
13
|
+
},
|
|
14
|
+
// v-model:json.string
|
|
15
|
+
jsonModifiers: {
|
|
16
|
+
default: () => ({ string: false })
|
|
17
|
+
},
|
|
18
|
+
html: {
|
|
19
|
+
type: String
|
|
20
|
+
}
|
|
21
|
+
};
|
|
22
|
+
export const EditableEmits = ['reload', 'reloaded', 'update:json', 'update:html'];
|
|
23
|
+
const Editable = defineComponent({
|
|
24
|
+
name: 'WeEditable',
|
|
25
|
+
props: EditableProps,
|
|
26
|
+
emits: [...EditableEmits],
|
|
27
|
+
setup(props, { emit, expose }) {
|
|
28
|
+
const rootRef = ref();
|
|
29
|
+
const content = shallowReactive({
|
|
30
|
+
json: [],
|
|
31
|
+
jstr: '',
|
|
32
|
+
html: ''
|
|
33
|
+
});
|
|
34
|
+
const modelJson = computed(() => {
|
|
35
|
+
const { json } = props;
|
|
36
|
+
if (is(json, 'array'))
|
|
37
|
+
return true;
|
|
38
|
+
if (is(json, 'string'))
|
|
39
|
+
return true;
|
|
40
|
+
return false;
|
|
41
|
+
});
|
|
42
|
+
const modelJstr = computed(() => modelJson.value && props.jsonModifiers.string);
|
|
43
|
+
const modelHtml = computed(() => is(props.html, 'string'));
|
|
44
|
+
const { cache, opts, instance, globalAttrs, formFieldInit } = useWeContent(props.handle);
|
|
45
|
+
const formField = formFieldInit();
|
|
46
|
+
/**
|
|
47
|
+
* 生成 reload/reloaded 事件传递的数据
|
|
48
|
+
*/
|
|
49
|
+
function createEvent() {
|
|
50
|
+
return {
|
|
51
|
+
type: 'editable',
|
|
52
|
+
instance: instance.editable
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 菜单编辑区
|
|
57
|
+
* @param dispatch 是否需要触发 reload 事件。组件卸载时的销毁不需要触发 reload 事件
|
|
58
|
+
* @return 是否需要触发 reloaded 事件
|
|
59
|
+
*/
|
|
60
|
+
function destory(dispatch) {
|
|
61
|
+
if (!instance.editable)
|
|
62
|
+
return false;
|
|
63
|
+
syncContent();
|
|
64
|
+
dispatch && emit('reload', createEvent());
|
|
65
|
+
instance.editable.destroy();
|
|
66
|
+
instance.editable = null;
|
|
67
|
+
return true;
|
|
68
|
+
}
|
|
69
|
+
/** 封装 change 事件,实现数据 v-model v-model:json 和 v-model:html */
|
|
70
|
+
const changes = [];
|
|
71
|
+
watchEffect(() => {
|
|
72
|
+
changes.length = 0;
|
|
73
|
+
if (modelJson.value || modelHtml.value) {
|
|
74
|
+
const { delay } = opts.editable;
|
|
75
|
+
changes.push(delay > 0 ? debounce(syncContent, delay) : syncContent);
|
|
76
|
+
}
|
|
77
|
+
const { onChange } = opts.editable.config;
|
|
78
|
+
is(onChange, 'function') && changes.push(onChange);
|
|
79
|
+
});
|
|
80
|
+
/**
|
|
81
|
+
* 生成编辑区初始化所需的配置对象
|
|
82
|
+
*/
|
|
83
|
+
function createOption() {
|
|
84
|
+
const { mode, config, defaultContent, extendCache } = toRaw(opts).editable;
|
|
85
|
+
const option = {
|
|
86
|
+
selector: rootRef.value,
|
|
87
|
+
mode: mode || 'default',
|
|
88
|
+
config: Object.assign({}, config, {
|
|
89
|
+
customAlert(info, type) {
|
|
90
|
+
// opts.editable.config.customAlert?.(info, type)
|
|
91
|
+
if(opts.editable.config.customAlert) opts.editable.config.customAlert(info, type);
|
|
92
|
+
},
|
|
93
|
+
onCreated(editor) {
|
|
94
|
+
// opts.editable.config.onCreated?.(editor);
|
|
95
|
+
if(opts.editable.config.onCreated) opts.editable.config.onCreated(editor);
|
|
96
|
+
},
|
|
97
|
+
onDestroyed(editor) {
|
|
98
|
+
if(opts.editable.config.onDestroyed) opts.editable.config.onDestroyed(editor);
|
|
99
|
+
// opts.editable.config.onDestroyed?.(editor);
|
|
100
|
+
},
|
|
101
|
+
onMaxLength(editor) {
|
|
102
|
+
if(opts.editable.config.onMaxLength) opts.editable.config.onMaxLength(editor);
|
|
103
|
+
// opts.editable.config.onMaxLength?.(editor);
|
|
104
|
+
},
|
|
105
|
+
onFocus(editor) {
|
|
106
|
+
if(opts.editable.config.onFocus) opts.editable.config.onFocus(editor);
|
|
107
|
+
// opts.editable.config.onFocus?.(editor);
|
|
108
|
+
},
|
|
109
|
+
onBlur(editor) {
|
|
110
|
+
if(opts.editable.config.onBlur) opts.editable.config.onBlur(editor);
|
|
111
|
+
// opts.editable.config.onBlur?.(editor);
|
|
112
|
+
formField.blur();
|
|
113
|
+
},
|
|
114
|
+
onChange(editor) {
|
|
115
|
+
nextTick(() => changes.forEach((change) => change(editor)));
|
|
116
|
+
}
|
|
117
|
+
})
|
|
118
|
+
};
|
|
119
|
+
let data = '';
|
|
120
|
+
if (extendCache) {
|
|
121
|
+
data =
|
|
122
|
+
content.jstr.length > 2
|
|
123
|
+
? content.jstr
|
|
124
|
+
: Array.isArray(defaultContent)
|
|
125
|
+
? JSON.stringify(defaultContent)
|
|
126
|
+
: defaultContent || '';
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
data = Array.isArray(defaultContent) ? JSON.stringify(defaultContent) : defaultContent || content.jstr;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const json = JSON.parse(data);
|
|
133
|
+
return { content: json, ...option };
|
|
134
|
+
}
|
|
135
|
+
catch (e) {
|
|
136
|
+
return { html: data, ...option };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
const editable = {
|
|
140
|
+
timer: null,
|
|
141
|
+
create() {
|
|
142
|
+
if (!rootRef.value)
|
|
143
|
+
return;
|
|
144
|
+
const dispatch = destory(true);
|
|
145
|
+
instance.editable = createEditor(createOption());
|
|
146
|
+
syncContent();
|
|
147
|
+
dispatch && emit('reloaded', createEvent());
|
|
148
|
+
if (cache.toolbar.timer)
|
|
149
|
+
clearTimeout(cache.toolbar.timer);
|
|
150
|
+
cache.toolbar.create();
|
|
151
|
+
},
|
|
152
|
+
throttle() {
|
|
153
|
+
if (editable.timer)
|
|
154
|
+
clearTimeout(editable.timer);
|
|
155
|
+
editable.timer = setTimeout(() => {
|
|
156
|
+
editable.timer = null;
|
|
157
|
+
editable.create();
|
|
158
|
+
}, opts.reloadDelay);
|
|
159
|
+
}
|
|
160
|
+
};
|
|
161
|
+
Object.assign(cache, {
|
|
162
|
+
editable,
|
|
163
|
+
clearContent,
|
|
164
|
+
syncContent
|
|
165
|
+
});
|
|
166
|
+
const { create, throttle } = editable;
|
|
167
|
+
onMounted(create);
|
|
168
|
+
onBeforeUnmount(destory);
|
|
169
|
+
/**
|
|
170
|
+
* 设置 v-model 数据
|
|
171
|
+
* @param inst 编辑器实例
|
|
172
|
+
* @param insert 执行插入操作
|
|
173
|
+
*/
|
|
174
|
+
function setContent(inst, insert) {
|
|
175
|
+
const focus = inst.isFocused();
|
|
176
|
+
const disable = opts.editable.config.readOnly;
|
|
177
|
+
const selection = JSON.stringify(inst.selection);
|
|
178
|
+
disable && inst.enable();
|
|
179
|
+
inst.clear();
|
|
180
|
+
insert();
|
|
181
|
+
if (disable) {
|
|
182
|
+
inst.disable();
|
|
183
|
+
}
|
|
184
|
+
else if (!focus) {
|
|
185
|
+
inst.blur();
|
|
186
|
+
}
|
|
187
|
+
else {
|
|
188
|
+
try {
|
|
189
|
+
inst.select(JSON.parse(selection)); // 恢复选区
|
|
190
|
+
}
|
|
191
|
+
catch (e) {
|
|
192
|
+
inst.select(SlateEditor.start(inst, [])); // 选中开头
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// 监听 v-model:json
|
|
197
|
+
watch(() => props.json, (json) => {
|
|
198
|
+
if (!modelJson.value)
|
|
199
|
+
return;
|
|
200
|
+
const value = toRaw(json);
|
|
201
|
+
if (value === content.json || value === content.jstr)
|
|
202
|
+
return;
|
|
203
|
+
let jstr = is(value, 'array') ? JSON.stringify(value) : value;
|
|
204
|
+
if (jstr === content.jstr)
|
|
205
|
+
return;
|
|
206
|
+
try {
|
|
207
|
+
json = JSON.parse(jstr);
|
|
208
|
+
}
|
|
209
|
+
catch (e) {
|
|
210
|
+
json = [];
|
|
211
|
+
jstr = '';
|
|
212
|
+
console.error(Error(`v-model:json${modelJstr.value ? '.string' : ''}'s value is not a json string or object!`));
|
|
213
|
+
}
|
|
214
|
+
const { editable: inst } = instance;
|
|
215
|
+
if (inst) {
|
|
216
|
+
setContent(inst, () => inst.insertFragment(json));
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
Object.assign(content, { json, jstr });
|
|
220
|
+
}
|
|
221
|
+
}, { immediate: true, deep: true });
|
|
222
|
+
// v-model:html
|
|
223
|
+
watch(() => props.html, (html) => {
|
|
224
|
+
// 以 v-model:json 为主,无 v-model:json 时才会继续操作
|
|
225
|
+
if (modelJson.value || !modelHtml.value)
|
|
226
|
+
return;
|
|
227
|
+
html = html || '';
|
|
228
|
+
if (content.html === html)
|
|
229
|
+
return;
|
|
230
|
+
const { editable: inst } = instance;
|
|
231
|
+
if (inst) {
|
|
232
|
+
setContent(inst, () => {
|
|
233
|
+
// @ts-ignore
|
|
234
|
+
SlateTransforms.setNodes(inst, { type: 'paragraph' }, { mode: 'highest' });
|
|
235
|
+
inst.dangerouslyInsertHtml(html);
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
content.html = html;
|
|
240
|
+
}
|
|
241
|
+
}, { immediate: true });
|
|
242
|
+
// 编辑器支持重载的配置项
|
|
243
|
+
watch(() => opts.editable.mode, throttle);
|
|
244
|
+
watch(() => opts.editable.config.maxLength, throttle);
|
|
245
|
+
watch(() => opts.editable.config.decorate, throttle);
|
|
246
|
+
watch(() => opts.editable.config.customPaste, throttle);
|
|
247
|
+
watch(() => opts.editable.config.hoverbarKeys, throttle, { deep: true });
|
|
248
|
+
watch(() => opts.editable.config.MENU_CONF, throttle, { deep: true });
|
|
249
|
+
watch(() => opts.editable.config.EXTEND_CONF, throttle, { deep: true });
|
|
250
|
+
// readOnly
|
|
251
|
+
watch(() => opts.editable.config.readOnly, (nv) => {
|
|
252
|
+
const { editable: inst } = instance;
|
|
253
|
+
inst && (nv ? inst.disable() : inst.enable());
|
|
254
|
+
});
|
|
255
|
+
// placeholder
|
|
256
|
+
watch(() => opts.editable.config.placeholder, (nv) => {
|
|
257
|
+
const target = rootRef.value?.querySelector('.w-e-text-placeholder');
|
|
258
|
+
console.log(nv)
|
|
259
|
+
if (target instanceof HTMLElement)
|
|
260
|
+
target.innerHTML = nv || '';
|
|
261
|
+
});
|
|
262
|
+
// scroll
|
|
263
|
+
watch(() => opts.editable.config.scroll, (nv) => {
|
|
264
|
+
const target = rootRef.value?.querySelector('.w-e-scroll');
|
|
265
|
+
if (target instanceof HTMLElement)
|
|
266
|
+
target.style.overflowY = nv ? 'auto' : '';
|
|
267
|
+
});
|
|
268
|
+
/**
|
|
269
|
+
* 更新数据,将编辑器内容同步到父组件。
|
|
270
|
+
*/
|
|
271
|
+
function syncContent() {
|
|
272
|
+
if (!modelJson.value && !modelHtml.value)
|
|
273
|
+
return;
|
|
274
|
+
const { editable: inst } = instance;
|
|
275
|
+
if (!inst)
|
|
276
|
+
return;
|
|
277
|
+
content.json = inst.isEmpty() ? [] : inst.children;
|
|
278
|
+
const { json, jstr: oldJstr } = content;
|
|
279
|
+
const jstr = json.length ? JSON.stringify(json) : '';
|
|
280
|
+
if (oldJstr === jstr)
|
|
281
|
+
return;
|
|
282
|
+
Object.assign(content, { json, jstr });
|
|
283
|
+
if (modelJstr.value) {
|
|
284
|
+
emit('update:json', jstr);
|
|
285
|
+
}
|
|
286
|
+
else if (modelJson.value) {
|
|
287
|
+
emit('update:json', json);
|
|
288
|
+
}
|
|
289
|
+
if (modelHtml.value) {
|
|
290
|
+
content.html = inst.isEmpty() ? '' : inst.getHtml();
|
|
291
|
+
emit('update:html', content.html);
|
|
292
|
+
}
|
|
293
|
+
formField.change();
|
|
294
|
+
}
|
|
295
|
+
/**
|
|
296
|
+
* 清除组件中的富文本内容和缓存
|
|
297
|
+
*/
|
|
298
|
+
function clearContent() {
|
|
299
|
+
const { editable: inst } = instance;
|
|
300
|
+
// 为初始 || 无内容
|
|
301
|
+
if (!inst || inst.isEmpty())
|
|
302
|
+
return;
|
|
303
|
+
const disable = inst.isDisabled();
|
|
304
|
+
inst.enable();
|
|
305
|
+
inst.clear();
|
|
306
|
+
disable && inst.disable();
|
|
307
|
+
syncContent();
|
|
308
|
+
}
|
|
309
|
+
expose({ rootRef, modelJson, modelJstr, modelHtml, instance, syncContent, clearContent });
|
|
310
|
+
return {
|
|
311
|
+
rootRef,
|
|
312
|
+
globalAttrs,
|
|
313
|
+
modelJson,
|
|
314
|
+
modelJstr,
|
|
315
|
+
modelHtml,
|
|
316
|
+
content,
|
|
317
|
+
instance,
|
|
318
|
+
syncContent,
|
|
319
|
+
clearContent,
|
|
320
|
+
option: toRef(opts, 'editable')
|
|
321
|
+
};
|
|
322
|
+
},
|
|
323
|
+
render() {
|
|
324
|
+
return h('div', { ref: 'rootRef', ...this.globalAttrs.editable });
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
export const WeEditable = withInstall(Editable);
|
|
328
|
+
//# sourceMappingURL=editable.js.map
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { WeToolbar } from './toolbar';
|
|
2
|
+
import { computed, defineComponent, h, mergeProps } from 'vue';
|
|
3
|
+
import { EditableEmits, EditableProps, WeEditable } from './editable';
|
|
4
|
+
import { omit, pick, withInstall } from './utils';
|
|
5
|
+
import { useWeContent } from './hooks';
|
|
6
|
+
// const PierceEmits = EditableEmits.map((e) => `on${e.charAt(0).toUpperCase()}${e.slice(1)}`)
|
|
7
|
+
const Editor = defineComponent({
|
|
8
|
+
inheritAttrs: false,
|
|
9
|
+
name: 'WeEditor',
|
|
10
|
+
props: {
|
|
11
|
+
toolbarAttrs: {
|
|
12
|
+
type: Object,
|
|
13
|
+
default: () => ({})
|
|
14
|
+
},
|
|
15
|
+
editableAttrs: {
|
|
16
|
+
type: Object,
|
|
17
|
+
default: () => ({})
|
|
18
|
+
},
|
|
19
|
+
...EditableProps
|
|
20
|
+
},
|
|
21
|
+
emits: [...EditableEmits],
|
|
22
|
+
setup(props, { attrs, emit }) {
|
|
23
|
+
const { globalAttrs, instance } = useWeContent(props.handle);
|
|
24
|
+
const editorProps = computed(() => {
|
|
25
|
+
return mergeProps(omit(attrs, ['toolbarAttrs', 'editableAttrs']), globalAttrs.value.editor);
|
|
26
|
+
});
|
|
27
|
+
const editableEmits = EditableEmits.reduce((events, e) => {
|
|
28
|
+
events[`on${e.charAt(0).toUpperCase()}${e.slice(1)}`] = (data) => emit(e, data);
|
|
29
|
+
return events;
|
|
30
|
+
}, {});
|
|
31
|
+
const editableProps = computed(() => {
|
|
32
|
+
return mergeProps(editableEmits, pick(props, ['handle', 'json', 'jsonModifiers', 'html']), props.editableAttrs);
|
|
33
|
+
});
|
|
34
|
+
const toolbarProps = computed(() => {
|
|
35
|
+
const { handle, toolbarAttrs } = props;
|
|
36
|
+
return mergeProps({
|
|
37
|
+
handle,
|
|
38
|
+
onReload(e) {
|
|
39
|
+
emit('reload', e);
|
|
40
|
+
},
|
|
41
|
+
onReloaded(e) {
|
|
42
|
+
emit('reloaded', e);
|
|
43
|
+
}
|
|
44
|
+
}, toolbarAttrs);
|
|
45
|
+
});
|
|
46
|
+
return { editorProps, editableProps, toolbarProps, instance };
|
|
47
|
+
},
|
|
48
|
+
render() {
|
|
49
|
+
return h('div', this.editorProps, [h(WeToolbar, this.toolbarProps), h(WeEditable, this.editableProps)]);
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
export const WeEditor = withInstall(Editor);
|
|
53
|
+
//# sourceMappingURL=editor.js.map
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { getCurrentInstance, inject, provide, reactive, ref, shallowReactive, toRefs, watch } from 'vue';
|
|
2
|
+
import { deepMerge, is } from './utils';
|
|
3
|
+
let HANDLE_INDEX = 1;
|
|
4
|
+
function defaultOptions() {
|
|
5
|
+
return {
|
|
6
|
+
reloadDelay: 500,
|
|
7
|
+
toolbar: {
|
|
8
|
+
mode: 'default',
|
|
9
|
+
config: {}
|
|
10
|
+
},
|
|
11
|
+
editable: {
|
|
12
|
+
mode: 'default',
|
|
13
|
+
defaultContent: null,
|
|
14
|
+
config: {},
|
|
15
|
+
delay: 3000,
|
|
16
|
+
extendCache: true
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
function defaultAttrs() {
|
|
21
|
+
return { editor: {}, toolbar: {}, editable: {} };
|
|
22
|
+
}
|
|
23
|
+
function defaultFormFieldInit() {
|
|
24
|
+
return { blur() { }, change() { } };
|
|
25
|
+
}
|
|
26
|
+
function defaultGlobalConfig() {
|
|
27
|
+
return { opts: defaultOptions(), attrs: defaultAttrs(), formFieldInit: defaultFormFieldInit };
|
|
28
|
+
}
|
|
29
|
+
function createGlobalPropertyName(name) {
|
|
30
|
+
return `WE-${name.toUpperCase()}`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* 创建 wangEditor 编辑器的全局配置注册函数
|
|
34
|
+
* @param config - 全局配置
|
|
35
|
+
*/
|
|
36
|
+
export function createWeGlobalConfig(config) {
|
|
37
|
+
return function (app) {
|
|
38
|
+
deepMerge(defaultGlobalConfig(), config);
|
|
39
|
+
const configRefs = toRefs(reactive(config));
|
|
40
|
+
const keys = Object.keys(configRefs);
|
|
41
|
+
keys.forEach((key) => {
|
|
42
|
+
app.config.globalProperties[createGlobalPropertyName(key)] = configRefs[key];
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
const GlobalPropertiesInit = {
|
|
47
|
+
opts: defaultOptions,
|
|
48
|
+
attrs: defaultAttrs,
|
|
49
|
+
formFieldInit: () => defaultFormFieldInit
|
|
50
|
+
};
|
|
51
|
+
export function useWeGlobalConfig(key) {
|
|
52
|
+
// const properties = getCurrentInstance()?.appContext.config.globalProperties;
|
|
53
|
+
let properties = null
|
|
54
|
+
if(getCurrentInstance()) {
|
|
55
|
+
properties = getCurrentInstance().appContext.config.globalProperties;
|
|
56
|
+
}
|
|
57
|
+
if (properties) {
|
|
58
|
+
const name = createGlobalPropertyName(key);
|
|
59
|
+
if (!Reflect.has(properties, name)) {
|
|
60
|
+
properties[name] = ref(GlobalPropertiesInit[key]());
|
|
61
|
+
}
|
|
62
|
+
return properties[name];
|
|
63
|
+
}
|
|
64
|
+
return ref(GlobalPropertiesInit[key]());
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 创建编辑器配置
|
|
68
|
+
* @param option - 初始化配置项
|
|
69
|
+
*/
|
|
70
|
+
export function useWangEditor(option = null, formField) {
|
|
71
|
+
const opts = reactive((is(option, 'object') ? option : {}));
|
|
72
|
+
const globalOpts = useWeGlobalConfig('opts');
|
|
73
|
+
watch(globalOpts, (options) => {
|
|
74
|
+
if (is(options, 'object')) {
|
|
75
|
+
deepMerge(options, opts);
|
|
76
|
+
}
|
|
77
|
+
}, { immediate: true, deep: true });
|
|
78
|
+
const instance = shallowReactive({
|
|
79
|
+
editable: null,
|
|
80
|
+
toolbar: null
|
|
81
|
+
});
|
|
82
|
+
const cache = {};
|
|
83
|
+
const globalFormField = useWeGlobalConfig('formFieldInit');
|
|
84
|
+
function formFieldInit() {
|
|
85
|
+
return [globalFormField.value, formField].reduce((field, init) => {
|
|
86
|
+
if (typeof init === 'function') {
|
|
87
|
+
const { blur, change } = init();
|
|
88
|
+
if (typeof blur === 'function')
|
|
89
|
+
field.blur = blur;
|
|
90
|
+
if (typeof change === 'function')
|
|
91
|
+
field.change = change;
|
|
92
|
+
}
|
|
93
|
+
return field;
|
|
94
|
+
}, defaultFormFieldInit());
|
|
95
|
+
}
|
|
96
|
+
const handle = Symbol(`WE ${String(HANDLE_INDEX++).padStart(6, '0')}`);
|
|
97
|
+
provide(handle, {
|
|
98
|
+
opts,
|
|
99
|
+
instance,
|
|
100
|
+
cache,
|
|
101
|
+
formFieldInit,
|
|
102
|
+
globalAttrs: useWeGlobalConfig('attrs')
|
|
103
|
+
});
|
|
104
|
+
function clearContent() {
|
|
105
|
+
if(cache.clearContent) cache.clearContent()
|
|
106
|
+
// cache.clearContent?.();
|
|
107
|
+
}
|
|
108
|
+
function syncContent() {
|
|
109
|
+
if(cache.syncContent) cache.syncContent()
|
|
110
|
+
// cache.syncContent?.();
|
|
111
|
+
}
|
|
112
|
+
function reloadEditor() {
|
|
113
|
+
if(cache.create) cache.create()
|
|
114
|
+
// cache.editable?.create();
|
|
115
|
+
}
|
|
116
|
+
return { opts, instance, handle, clearContent, syncContent, reloadEditor };
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* 获取 useWangEditor 中传递的 provide 数据
|
|
120
|
+
* @param handle - 句柄
|
|
121
|
+
*/
|
|
122
|
+
export function useWeContent(handle) {
|
|
123
|
+
return (inject(handle) || {
|
|
124
|
+
cache: {},
|
|
125
|
+
opts: reactive(defaultOptions()),
|
|
126
|
+
instance: shallowReactive({
|
|
127
|
+
toolbar: null,
|
|
128
|
+
editable: null
|
|
129
|
+
}),
|
|
130
|
+
globalAttrs: useWeGlobalConfig('attrs'),
|
|
131
|
+
formFieldInit: useWeGlobalConfig('formFieldInit').value
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { createToolbar } from '@wangeditor/editor';
|
|
2
|
+
import { defineComponent, h, onBeforeUnmount, ref, toRaw, unref, watch } from 'vue';
|
|
3
|
+
import { useWeContent } from './hooks';
|
|
4
|
+
import { withInstall } from './utils';
|
|
5
|
+
const Toolbar = defineComponent({
|
|
6
|
+
name: 'WeToolbar',
|
|
7
|
+
props: {
|
|
8
|
+
handle: {
|
|
9
|
+
type: Symbol,
|
|
10
|
+
required: true
|
|
11
|
+
}
|
|
12
|
+
},
|
|
13
|
+
emits: ['reload', 'reloaded'],
|
|
14
|
+
setup(props, { emit }) {
|
|
15
|
+
const rootRef = ref();
|
|
16
|
+
const { cache, opts, instance, globalAttrs } = useWeContent(props.handle);
|
|
17
|
+
/**
|
|
18
|
+
* 生成 reload/reloaded 事件传递的数据
|
|
19
|
+
*/
|
|
20
|
+
function createEvent() {
|
|
21
|
+
return {
|
|
22
|
+
type: 'toolbar',
|
|
23
|
+
instance: instance.toolbar
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 菜单栏销毁
|
|
28
|
+
* @param dispatch 是否需要触发 reload 事件,用于区分销毁前的重建和组件的销毁
|
|
29
|
+
* @return 是否需要触发 reloaded 事件
|
|
30
|
+
*/
|
|
31
|
+
function destory(dispatch) {
|
|
32
|
+
if (!instance.toolbar)
|
|
33
|
+
return false;
|
|
34
|
+
dispatch && emit('reload', createEvent());
|
|
35
|
+
instance.toolbar.destroy();
|
|
36
|
+
instance.toolbar = null;
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
const toolbar = {
|
|
40
|
+
timer: null,
|
|
41
|
+
create() {
|
|
42
|
+
const selector = unref(rootRef);
|
|
43
|
+
if (!instance.editable || !selector)
|
|
44
|
+
return;
|
|
45
|
+
// 是否需要触发 reloaded 事件
|
|
46
|
+
const dispatch = destory(true);
|
|
47
|
+
delete selector.dataset.wEToolbar;
|
|
48
|
+
instance.toolbar = createToolbar({
|
|
49
|
+
selector,
|
|
50
|
+
editor: instance.editable,
|
|
51
|
+
...toRaw(opts).toolbar
|
|
52
|
+
});
|
|
53
|
+
dispatch && emit('reloaded', createEvent());
|
|
54
|
+
},
|
|
55
|
+
throttle() {
|
|
56
|
+
if (toolbar.timer)
|
|
57
|
+
clearTimeout(toolbar.timer);
|
|
58
|
+
if (cache.editable || cache.editable.timer)
|
|
59
|
+
return;
|
|
60
|
+
toolbar.timer = setTimeout(() => {
|
|
61
|
+
toolbar.timer = null;
|
|
62
|
+
toolbar.create();
|
|
63
|
+
}, opts.reloadDelay);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
watch(() => opts.toolbar, toolbar.throttle, { deep: true });
|
|
67
|
+
cache.toolbar = toolbar;
|
|
68
|
+
onBeforeUnmount(() => {
|
|
69
|
+
destory();
|
|
70
|
+
delete cache.toolbar;
|
|
71
|
+
});
|
|
72
|
+
return { rootRef, globalAttrs, instance };
|
|
73
|
+
},
|
|
74
|
+
render() {
|
|
75
|
+
return h('div', { ref: 'rootRef', ...this.globalAttrs.toolbar });
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
export const WeToolbar = withInstall(Toolbar);
|
|
79
|
+
//# sourceMappingURL=toolbar.js.map
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import deepClone from 'lodash.clonedeep';
|
|
2
|
+
/**
|
|
3
|
+
* 类型判断
|
|
4
|
+
* @param tar 需要被判断类型的变量
|
|
5
|
+
* @param type 数据类型
|
|
6
|
+
*/
|
|
7
|
+
export function is(tar, type) {
|
|
8
|
+
return (Object.prototype.toString
|
|
9
|
+
.call(tar)
|
|
10
|
+
.replace(/(\[.+?\s|\])/g, '')
|
|
11
|
+
.toLowerCase() === type);
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 给组件包装 install 函数
|
|
15
|
+
* @param component 组件
|
|
16
|
+
*/
|
|
17
|
+
export function withInstall(component) {
|
|
18
|
+
function install(app) {
|
|
19
|
+
app.component(component.name, component);
|
|
20
|
+
}
|
|
21
|
+
return Object.assign(component, { install });
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* 数据合并(深度合并)
|
|
25
|
+
* @param from 被合并的
|
|
26
|
+
* @param to 合并到
|
|
27
|
+
* @param compel 强制覆盖
|
|
28
|
+
*/
|
|
29
|
+
export function deepMerge(from, to, compel = false) {
|
|
30
|
+
for (let key in from) {
|
|
31
|
+
const value = from[key];
|
|
32
|
+
if (Reflect.has(to, key)) {
|
|
33
|
+
const oldValue = Reflect.get(to, key);
|
|
34
|
+
if (is(oldValue, 'object') && is(value, 'object')) {
|
|
35
|
+
deepMerge(value, oldValue, compel);
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
else if (!compel) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
Reflect.set(to, key, is(value, 'object') ? deepClone(value) : value);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 对 TypeScript 类型推导 Pick 的 JavaScript 实现
|
|
47
|
+
* @param data 数据源
|
|
48
|
+
* @param keys 被保留的键
|
|
49
|
+
*/
|
|
50
|
+
export function pick(data, keys) {
|
|
51
|
+
const temp = {};
|
|
52
|
+
keys.forEach((key) => Reflect.set(temp, key, data[key]));
|
|
53
|
+
return temp;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 对 TypeScript 类型推导 Omit 的 JavaScript 实现
|
|
57
|
+
* @param data 数据源
|
|
58
|
+
* @param keys 被剔除的键
|
|
59
|
+
*/
|
|
60
|
+
export function omit(data, keys) {
|
|
61
|
+
const temp = { ...data };
|
|
62
|
+
keys.forEach((key) => Reflect.deleteProperty(temp, key));
|
|
63
|
+
return temp;
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=utils.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { createWeGlobalConfig, useWangEditor, useWeContent, useWeGlobalConfig } from './core/hooks';
|
|
2
|
+
export { WeEditor } from './core/editor';
|
|
3
|
+
export { WeEditable } from './core/editable';
|
|
4
|
+
export { WeToolbar } from './core/toolbar';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|