提交 | 用户 | 时间
|
314507
|
1 |
<script lang="ts" setup> |
H |
2 |
import { PropType } from 'vue' |
|
3 |
import { Editor, Toolbar } from '@wangeditor/editor-for-vue' |
|
4 |
import { i18nChangeLanguage, IDomEditor, IEditorConfig } from '@wangeditor/editor' |
|
5 |
import { propTypes } from '@/utils/propTypes' |
|
6 |
import { isNumber } from '@/utils/is' |
|
7 |
import { ElMessage } from 'element-plus' |
|
8 |
import { useLocaleStore } from '@/store/modules/locale' |
|
9 |
import { getAccessToken, getTenantId } from '@/utils/auth' |
|
10 |
|
|
11 |
defineOptions({ name: 'Editor' }) |
|
12 |
|
|
13 |
type InsertFnType = (url: string, alt: string, href: string) => void |
|
14 |
|
|
15 |
const localeStore = useLocaleStore() |
|
16 |
|
|
17 |
const currentLocale = computed(() => localeStore.getCurrentLocale) |
|
18 |
|
|
19 |
i18nChangeLanguage(unref(currentLocale).lang) |
|
20 |
|
|
21 |
const props = defineProps({ |
|
22 |
editorId: propTypes.string.def('wangeEditor-1'), |
|
23 |
height: propTypes.oneOfType([Number, String]).def('500px'), |
|
24 |
editorConfig: { |
|
25 |
type: Object as PropType<Partial<IEditorConfig>>, |
|
26 |
default: () => undefined |
|
27 |
}, |
|
28 |
readonly: propTypes.bool.def(false), |
|
29 |
modelValue: propTypes.string.def('') |
|
30 |
}) |
|
31 |
|
|
32 |
const emit = defineEmits(['change', 'update:modelValue']) |
|
33 |
|
|
34 |
// 编辑器实例,必须用 shallowRef |
|
35 |
const editorRef = shallowRef<IDomEditor>() |
|
36 |
|
|
37 |
const valueHtml = ref('') |
|
38 |
|
|
39 |
watch( |
|
40 |
() => props.modelValue, |
|
41 |
(val: string) => { |
|
42 |
if (val === unref(valueHtml)) return |
|
43 |
valueHtml.value = val |
|
44 |
}, |
|
45 |
{ |
|
46 |
immediate: true |
|
47 |
} |
|
48 |
) |
|
49 |
|
|
50 |
// 监听 |
|
51 |
watch( |
|
52 |
() => valueHtml.value, |
|
53 |
(val: string) => { |
|
54 |
emit('update:modelValue', val) |
|
55 |
} |
|
56 |
) |
|
57 |
|
|
58 |
const handleCreated = (editor: IDomEditor) => { |
|
59 |
editorRef.value = editor |
|
60 |
} |
|
61 |
|
|
62 |
// 编辑器配置 |
|
63 |
const editorConfig = computed((): IEditorConfig => { |
|
64 |
return Object.assign( |
|
65 |
{ |
|
66 |
placeholder: '请输入内容...', |
|
67 |
readOnly: props.readonly, |
|
68 |
customAlert: (s: string, t: string) => { |
|
69 |
switch (t) { |
|
70 |
case 'success': |
|
71 |
ElMessage.success(s) |
|
72 |
break |
|
73 |
case 'info': |
|
74 |
ElMessage.info(s) |
|
75 |
break |
|
76 |
case 'warning': |
|
77 |
ElMessage.warning(s) |
|
78 |
break |
|
79 |
case 'error': |
|
80 |
ElMessage.error(s) |
|
81 |
break |
|
82 |
default: |
|
83 |
ElMessage.info(s) |
|
84 |
break |
|
85 |
} |
|
86 |
}, |
|
87 |
autoFocus: false, |
|
88 |
scroll: true, |
|
89 |
MENU_CONF: { |
|
90 |
['uploadImage']: { |
|
91 |
server: import.meta.env.VITE_UPLOAD_URL, |
|
92 |
// 单个文件的最大体积限制,默认为 2M |
|
93 |
maxFileSize: 5 * 1024 * 1024, |
|
94 |
// 最多可上传几个文件,默认为 100 |
|
95 |
maxNumberOfFiles: 10, |
|
96 |
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 [] |
|
97 |
allowedFileTypes: ['image/*'], |
|
98 |
|
|
99 |
// 自定义增加 http header |
|
100 |
headers: { |
|
101 |
Accept: '*', |
|
102 |
Authorization: 'Bearer ' + getAccessToken(), |
|
103 |
'tenant-id': getTenantId() |
|
104 |
}, |
|
105 |
|
|
106 |
// 超时时间,默认为 10 秒 |
|
107 |
timeout: 5 * 1000, // 5 秒 |
|
108 |
|
|
109 |
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image |
|
110 |
fieldName: 'file', |
|
111 |
|
|
112 |
// 上传之前触发 |
|
113 |
onBeforeUpload(file: File) { |
|
114 |
// console.log(file) |
|
115 |
return file |
|
116 |
}, |
|
117 |
// 上传进度的回调函数 |
|
118 |
onProgress(progress: number) { |
|
119 |
// progress 是 0-100 的数字 |
|
120 |
console.log('progress', progress) |
|
121 |
}, |
|
122 |
onSuccess(file: File, res: any) { |
|
123 |
console.log('onSuccess', file, res) |
|
124 |
}, |
|
125 |
onFailed(file: File, res: any) { |
|
126 |
alert(res.message) |
|
127 |
console.log('onFailed', file, res) |
|
128 |
}, |
|
129 |
onError(file: File, err: any, res: any) { |
|
130 |
alert(err.message) |
|
131 |
console.error('onError', file, err, res) |
|
132 |
}, |
|
133 |
// 自定义插入图片 |
|
134 |
customInsert(res: any, insertFn: InsertFnType) { |
|
135 |
insertFn(res.data, 'image', res.data) |
|
136 |
} |
|
137 |
}, |
|
138 |
['uploadVideo']: { |
|
139 |
server: import.meta.env.VITE_UPLOAD_URL, |
|
140 |
// 单个文件的最大体积限制,默认为 10M |
|
141 |
maxFileSize: 10 * 1024 * 1024, |
|
142 |
// 最多可上传几个文件,默认为 100 |
|
143 |
maxNumberOfFiles: 10, |
|
144 |
// 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 [] |
|
145 |
allowedFileTypes: ['video/*'], |
|
146 |
|
|
147 |
// 自定义增加 http header |
|
148 |
headers: { |
|
149 |
Accept: '*', |
|
150 |
Authorization: 'Bearer ' + getAccessToken(), |
|
151 |
'tenant-id': getTenantId() |
|
152 |
}, |
|
153 |
|
|
154 |
// 超时时间,默认为 30 秒 |
|
155 |
timeout: 15 * 1000, // 15 秒 |
|
156 |
|
|
157 |
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image |
|
158 |
fieldName: 'file', |
|
159 |
|
|
160 |
// 上传之前触发 |
|
161 |
onBeforeUpload(file: File) { |
|
162 |
// console.log(file) |
|
163 |
return file |
|
164 |
}, |
|
165 |
// 上传进度的回调函数 |
|
166 |
onProgress(progress: number) { |
|
167 |
// progress 是 0-100 的数字 |
|
168 |
console.log('progress', progress) |
|
169 |
}, |
|
170 |
onSuccess(file: File, res: any) { |
|
171 |
console.log('onSuccess', file, res) |
|
172 |
}, |
|
173 |
onFailed(file: File, res: any) { |
|
174 |
alert(res.message) |
|
175 |
console.log('onFailed', file, res) |
|
176 |
}, |
|
177 |
onError(file: File, err: any, res: any) { |
|
178 |
alert(err.message) |
|
179 |
console.error('onError', file, err, res) |
|
180 |
}, |
|
181 |
// 自定义插入图片 |
|
182 |
customInsert(res: any, insertFn: InsertFnType) { |
|
183 |
insertFn(res.data, 'mp4', res.data) |
|
184 |
} |
|
185 |
} |
|
186 |
}, |
|
187 |
uploadImgShowBase64: true |
|
188 |
}, |
|
189 |
props.editorConfig || {} |
|
190 |
) |
|
191 |
}) |
|
192 |
|
|
193 |
const editorStyle = computed(() => { |
|
194 |
return { |
|
195 |
height: isNumber(props.height) ? `${props.height}px` : props.height |
|
196 |
} |
|
197 |
}) |
|
198 |
|
|
199 |
// 回调函数 |
|
200 |
const handleChange = (editor: IDomEditor) => { |
|
201 |
emit('change', editor) |
|
202 |
} |
|
203 |
|
|
204 |
// 组件销毁时,及时销毁编辑器 |
|
205 |
onBeforeUnmount(() => { |
|
206 |
const editor = unref(editorRef.value) |
|
207 |
|
|
208 |
// 销毁,并移除 editor |
|
209 |
editor?.destroy() |
|
210 |
}) |
|
211 |
|
|
212 |
const getEditorRef = async (): Promise<IDomEditor> => { |
|
213 |
await nextTick() |
|
214 |
return unref(editorRef.value) as IDomEditor |
|
215 |
} |
|
216 |
|
|
217 |
defineExpose({ |
|
218 |
getEditorRef |
|
219 |
}) |
|
220 |
</script> |
|
221 |
|
|
222 |
<template> |
|
223 |
<div class="border-1 border-solid border-[var(--tags-view-border-color)] z-10"> |
|
224 |
<!-- 工具栏 --> |
|
225 |
<Toolbar |
|
226 |
:editor="editorRef" |
|
227 |
:editorId="editorId" |
|
228 |
class="border-0 b-b-1 border-solid border-[var(--tags-view-border-color)]" |
|
229 |
/> |
|
230 |
<!-- 编辑器 --> |
|
231 |
<Editor |
|
232 |
v-model="valueHtml" |
|
233 |
:defaultConfig="editorConfig" |
|
234 |
:editorId="editorId" |
|
235 |
:style="editorStyle" |
|
236 |
@on-change="handleChange" |
|
237 |
@on-created="handleCreated" |
|
238 |
/> |
|
239 |
</div> |
|
240 |
</template> |
|
241 |
|
|
242 |
<style src="@wangeditor/editor/dist/css/style.css"></style> |