@lambo-design/wang-editor 1.0.0-beta.9 → 2.0.0-beta.3
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 +7 -6
- package/src/index.vue +190 -112
- package/src/js/wang-editor.js +4710 -0
- package/src/styles/css/index.less +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lambo-design/wang-editor",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0-beta.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"author": "lambo",
|
|
@@ -9,12 +9,13 @@
|
|
|
9
9
|
"access": "public",
|
|
10
10
|
"registry": "https://registry.npmjs.org/"
|
|
11
11
|
},
|
|
12
|
-
"dependencies": {
|
|
13
|
-
"wangeditor": "^3.1.1"
|
|
14
|
-
},
|
|
15
12
|
"devDependencies": {
|
|
16
|
-
"@lambo-design/shared": "^1.0.0-beta.
|
|
17
|
-
"@lambo-design/core": "^4.7.1-beta.
|
|
13
|
+
"@lambo-design/shared": "^1.0.0-beta.334",
|
|
14
|
+
"@lambo-design/core": "^4.7.1-beta.174"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@wangeditor/editor": "5.1.23",
|
|
18
|
+
"@wangeditor/editor-for-vue": "^1.0.2"
|
|
18
19
|
},
|
|
19
20
|
"scripts": {
|
|
20
21
|
"release": "pnpm release-beta && git push --follow-tags && pnpm re-publish",
|
package/src/index.vue
CHANGED
|
@@ -1,15 +1,45 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<div
|
|
3
|
-
<
|
|
2
|
+
<div style="border: 1px solid #ccc; z-index: 999">
|
|
3
|
+
<Toolbar
|
|
4
|
+
style="border-bottom: 1px solid #ccc"
|
|
5
|
+
:editor="editor"
|
|
6
|
+
:defaultConfig="toolbarConfig"
|
|
7
|
+
:mode="mode"
|
|
8
|
+
/>
|
|
9
|
+
<Editor
|
|
10
|
+
style="height: 500px; overflow-y: hidden;"
|
|
11
|
+
v-model="localValue"
|
|
12
|
+
:defaultConfig="editorConfig"
|
|
13
|
+
:mode="mode"
|
|
14
|
+
@onCreated="onCreated"
|
|
15
|
+
@customPaste="handlePaste"
|
|
16
|
+
@onChange="handleChange"
|
|
17
|
+
/>
|
|
4
18
|
</div>
|
|
5
19
|
</template>
|
|
6
20
|
|
|
7
21
|
<script>
|
|
8
22
|
import axios from 'axios';
|
|
9
23
|
import {oneOf} from '@lambo-design/shared/utils/platform';
|
|
24
|
+
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
|
|
25
|
+
import '@wangeditor/editor/dist/css/style.css'
|
|
10
26
|
|
|
11
27
|
export default {
|
|
12
28
|
name: 'LamboWangEditor',
|
|
29
|
+
components: { Editor, Toolbar },
|
|
30
|
+
data() {
|
|
31
|
+
return {
|
|
32
|
+
editor: null,
|
|
33
|
+
toolbarConfig: {},
|
|
34
|
+
editorConfig: {
|
|
35
|
+
placeholder: '请输入内容...',
|
|
36
|
+
zIndex: 999,
|
|
37
|
+
MENU_CONF: {}
|
|
38
|
+
},
|
|
39
|
+
localValue: this.value,
|
|
40
|
+
pendingContent: null,
|
|
41
|
+
}
|
|
42
|
+
},
|
|
13
43
|
props: {
|
|
14
44
|
value: {
|
|
15
45
|
type: String,
|
|
@@ -67,130 +97,178 @@ export default {
|
|
|
67
97
|
type: String,
|
|
68
98
|
default: "/oss/file/get/"
|
|
69
99
|
},
|
|
100
|
+
/**
|
|
101
|
+
* @description 是否保留粘贴内容的样式
|
|
102
|
+
*/
|
|
103
|
+
preservePasteStyle: {
|
|
104
|
+
type: Boolean,
|
|
105
|
+
default: false
|
|
106
|
+
},
|
|
107
|
+
/**
|
|
108
|
+
* @description 模式, enum: ['default', 'simple']
|
|
109
|
+
*/
|
|
110
|
+
mode: {
|
|
111
|
+
type: String,
|
|
112
|
+
default: 'default',
|
|
113
|
+
validator: (val) => {
|
|
114
|
+
return oneOf(val, ['default', 'simple'])
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
/**
|
|
118
|
+
* @description 排除菜单组,写菜单组 key 的值即可
|
|
119
|
+
*/
|
|
120
|
+
excludeKeys: {
|
|
121
|
+
type: Array,
|
|
122
|
+
default: () => []
|
|
123
|
+
}
|
|
70
124
|
|
|
71
125
|
},
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
126
|
+
watch: {
|
|
127
|
+
value: {
|
|
128
|
+
handler: function (newVal, oldVal) {
|
|
129
|
+
if (oldVal === '' && this.editor) {
|
|
130
|
+
if (this.valueType === 'html') {
|
|
131
|
+
this.editor.setHtml(newVal);
|
|
132
|
+
} else {
|
|
133
|
+
// 如果是文本模式,也需要转换为 HTML 格式
|
|
134
|
+
this.editor.setHtml(`<p>${newVal}</p>`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
},
|
|
76
139
|
},
|
|
77
140
|
methods: {
|
|
78
141
|
setHtml(val) {
|
|
79
|
-
this.editor
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
142
|
+
if (this.editor) {
|
|
143
|
+
this.editor.setHtml(val)
|
|
144
|
+
} else {
|
|
145
|
+
// 编辑器尚未初始化时的处理
|
|
146
|
+
console.warn('Editor not initialized yet, queuing the content update...');
|
|
147
|
+
// 可以考虑缓存内容,等待编辑器初始化后设置
|
|
148
|
+
this.pendingContent = val;
|
|
149
|
+
}
|
|
150
|
+
},
|
|
151
|
+
handleChange(editor) {
|
|
152
|
+
const html = editor.getHtml();
|
|
153
|
+
const text = editor.getText();
|
|
154
|
+
if (this.cache) {
|
|
155
|
+
localStorage.editorCache = html;
|
|
156
|
+
}
|
|
157
|
+
this.$emit('input', this.valueType === 'html' ? html : text);
|
|
158
|
+
this.$emit('on-change', html, text);
|
|
159
|
+
},
|
|
160
|
+
onCreated(editor) {
|
|
161
|
+
this.editor = Object.seal(editor) // 一定要用 Object.seal() ,否则会报错
|
|
162
|
+
// 加载本地存储内容或传入的 value
|
|
163
|
+
const htmlCache = this.value || localStorage.getItem('editorCache');
|
|
164
|
+
if (this.cache && htmlCache && !this.value) {
|
|
165
|
+
this.editor.setHtml(htmlCache);
|
|
166
|
+
}
|
|
167
|
+
// 处理初始化前设置的内容
|
|
168
|
+
if (this.pendingContent != null) {
|
|
169
|
+
this.editor.setHtml(this.pendingContent);
|
|
170
|
+
this.pendingContent = null;
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
handlePaste(editor, event, callback) {
|
|
174
|
+
if (this.preservePasteStyle) {
|
|
175
|
+
// 保留源格式的处理
|
|
176
|
+
const html = event.clipboardData.getData('text/html')
|
|
177
|
+
|
|
178
|
+
const items = event.clipboardData.items;
|
|
179
|
+
// 检查是否有图片数据
|
|
180
|
+
for (let i = 0; i < items.length; i++) {
|
|
181
|
+
const item = items[i];
|
|
182
|
+
if (item.type.indexOf('image') !== -1) {
|
|
183
|
+
// 有图片数据,让编辑器处理
|
|
184
|
+
callback(true); // 允许默认粘贴行为
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
if (html) {
|
|
189
|
+
// 插入带格式的HTML
|
|
190
|
+
editor.dangerouslyInsertHtml(html)
|
|
191
|
+
} else {
|
|
192
|
+
// 没有HTML内容时回退到纯文本
|
|
193
|
+
editor.insertText(event.clipboardData.getData('text/plain'))
|
|
194
|
+
}
|
|
195
|
+
} else {
|
|
196
|
+
// 不保留格式,强制转为纯文本
|
|
197
|
+
editor.insertText(event.clipboardData.getData('text/plain'))
|
|
198
|
+
}
|
|
92
199
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
200
|
+
// 阻止默认行为
|
|
201
|
+
event.preventDefault()
|
|
202
|
+
callback(false)
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
created() {
|
|
206
|
+
let self = this
|
|
207
|
+
// 如果仅仅想排除掉某些菜单,其他都保留,可以使用 excludeKeys 来配置。可通过 toolbar.getConfig().toolbarKeys 查看工具栏的默认配置
|
|
208
|
+
this.toolbarConfig.excludeKeys = [
|
|
209
|
+
...this.excludeKeys,
|
|
210
|
+
'insertImage', // 网络图片
|
|
211
|
+
'group-video', // 视频菜单组
|
|
212
|
+
]
|
|
97
213
|
|
|
98
|
-
|
|
214
|
+
this.editorConfig.MENU_CONF['uploadImage'] = {
|
|
215
|
+
// base64 限制大小,小于该值就插入 base64 格式(而不上传),默认为 0
|
|
216
|
+
base64LimitSize: this.uploadImgShowBase64 ? 5 * 1024 * 1024 : 0,
|
|
99
217
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
// 'enctype':'multipart/form-data'
|
|
112
|
-
// =""
|
|
113
|
-
}
|
|
114
|
-
let ossServer = this.ossServerContext;
|
|
115
|
-
if(!self.uploadImgShowBase64){
|
|
116
|
-
self.editor.customConfig.customUploadImg = function (files, insert) {
|
|
117
|
-
let formData = new FormData();
|
|
118
|
-
for (let file of files) {
|
|
119
|
-
formData.append('file', file);
|
|
218
|
+
async customUpload(files, insertFn) {
|
|
219
|
+
console.log("customUpload",files)
|
|
220
|
+
const formData = new FormData();
|
|
221
|
+
// 检查 files 是否为数组
|
|
222
|
+
if (Array.isArray(files)) {
|
|
223
|
+
for (const file of files) {
|
|
224
|
+
formData.append('file', file);
|
|
225
|
+
}
|
|
226
|
+
} else {
|
|
227
|
+
// 如果 files 不是数组,直接 append
|
|
228
|
+
formData.append('file', files);
|
|
120
229
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// 上传代码返回结果之后,将图片插入到编辑器中
|
|
127
|
-
let imgUrl = []
|
|
128
|
-
result.forEach(item => {
|
|
129
|
-
insert(ossServer + self.ossFileGetImgUrl + item.fileId)
|
|
130
|
-
// imgUrl.push()
|
|
131
|
-
})
|
|
230
|
+
try {
|
|
231
|
+
const response = await axios.post(
|
|
232
|
+
self.ossServerContext + self.ossFilePutUrl,
|
|
233
|
+
formData
|
|
234
|
+
);
|
|
132
235
|
|
|
236
|
+
if (response.data.code == "1") {
|
|
237
|
+
const result = response.data.data;
|
|
238
|
+
const imageList = result.map(item => ({
|
|
239
|
+
url: self.ossServerContext + self.ossFileGetImgUrl + item.fileId,
|
|
240
|
+
alt: item.fileName
|
|
241
|
+
}));
|
|
242
|
+
// 循环插入每张图片
|
|
243
|
+
imageList.forEach(imageData => {
|
|
244
|
+
insertFn(imageData.url, imageData.alt, imageData.url);
|
|
245
|
+
});
|
|
133
246
|
}
|
|
134
|
-
})
|
|
247
|
+
} catch (error) {
|
|
248
|
+
console.error('图片上传失败:', error);
|
|
249
|
+
}
|
|
135
250
|
}
|
|
136
|
-
|
|
251
|
+
// 其他图片上传配置...
|
|
252
|
+
// 注意:不需要修改的配置可以省略,wangEditor 会自动合并默认配置
|
|
137
253
|
}
|
|
138
|
-
// 自定义菜单配置
|
|
139
|
-
self.editor.customConfig.menus = [
|
|
140
|
-
'head', // 标题
|
|
141
|
-
'bold', // 粗体
|
|
142
|
-
'fontSize', // 字号
|
|
143
|
-
'fontName', // 字体
|
|
144
|
-
'italic', // 斜体
|
|
145
|
-
'underline', // 下划线
|
|
146
|
-
'strikeThrough', // 删除线
|
|
147
|
-
'foreColor', // 文字颜色
|
|
148
|
-
'backColor', // 背景颜色
|
|
149
|
-
'link', // 插入链接
|
|
150
|
-
'list', // 列表
|
|
151
|
-
'justify', // 对齐方式
|
|
152
|
-
'quote', // 引用
|
|
153
|
-
// 'emoticon', // 表情
|
|
154
|
-
'image', // 插入图片
|
|
155
|
-
'table', // 表格
|
|
156
|
-
'video', // 插入视频
|
|
157
|
-
'code', // 插入代码
|
|
158
|
-
'undo', // 撤销
|
|
159
|
-
'redo' // 重复
|
|
160
|
-
]
|
|
161
|
-
// 自定义配置颜色(字体颜色、背景色)
|
|
162
|
-
self.editor.customConfig.colors = [
|
|
163
|
-
'#000000',
|
|
164
|
-
'#eeece0',
|
|
165
|
-
'#1c487f',
|
|
166
|
-
'#4d80bf',
|
|
167
|
-
'#c24f4a',
|
|
168
|
-
'#8baa4a',
|
|
169
|
-
'#7b5ba1',
|
|
170
|
-
'#46acc8',
|
|
171
|
-
'#f9963b',
|
|
172
|
-
'#ffffff'
|
|
173
|
-
]
|
|
174
|
-
self.editor.customConfig.pasteTextHandle = function (content) {
|
|
175
|
-
// content 即粘贴过来的内容(html 或 纯文本),可进行自定义处理然后返回
|
|
176
|
-
if (content == '' && !content) return ''
|
|
177
|
-
var str = content
|
|
178
|
-
str = str.replace(/<xml>[\s\S]*?<\/xml>/ig, '')
|
|
179
|
-
str = str.replace(/<style>[\s\S]*?<\/style>/ig, '')
|
|
180
|
-
str = str.replace(/<\/?[^>]*>/g, '')
|
|
181
|
-
str = str.replace(/[ | ]*\n/g, '\n')
|
|
182
|
-
// str = str.replace(/ /ig, '')
|
|
183
|
-
return str
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// create这个方法一定要在所有配置项之后调用
|
|
187
|
-
self.editor.create()
|
|
188
|
-
// 如果本地有存储加载本地存储内容
|
|
189
|
-
let html = self.value || localStorage.editorCache
|
|
190
254
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
255
|
+
this.editorConfig.MENU_CONF['insertLink'] = {
|
|
256
|
+
checkLink: (text, link) => {
|
|
257
|
+
if (!/^https?:\/\/[^\s/$.?#].[^\s]*$/.test(link)) {
|
|
258
|
+
return '链接格式不正确,请输入有效的HTTP/HTTPS地址';
|
|
259
|
+
}
|
|
260
|
+
if (link.includes('javascript:')) {
|
|
261
|
+
return '禁止使用javascript协议';
|
|
262
|
+
}
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
beforeDestroy() {
|
|
268
|
+
const editor = this.editor
|
|
269
|
+
if (editor == null) return
|
|
270
|
+
editor.destroy() // 组件销毁时,及时销毁编辑器
|
|
271
|
+
},
|
|
194
272
|
}
|
|
195
273
|
</script>
|
|
196
274
|
|