forked from opentiny/tiny-vue
711 lines
24 KiB
HTML
711 lines
24 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8" />
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||
<title>FluentEditor Demo</title>
|
||
</head>
|
||
<body>
|
||
<link href="./dist/style.css" rel="stylesheet" />
|
||
|
||
<!-- Create the editor container -->
|
||
<div id="editor">
|
||
<p>Hello World!</p>
|
||
<p>富文本编辑器</p>
|
||
</div>
|
||
|
||
<script type="module">
|
||
import FluentEditor from '/dist/index.es.js'
|
||
|
||
// console.log('FluentEditor:', FluentEditor.imports)
|
||
|
||
const SizeStyle = FluentEditor.imports['formats/size'] // 文字大小
|
||
const FontClass = FluentEditor.imports['formats/font'] // 文字字体
|
||
const lineheight = FluentEditor.imports['formats/lineheight'] // 行高
|
||
const Range = FluentEditor.imports['core/selection/range'];
|
||
|
||
// 工具栏显示的按钮总览
|
||
const container = [
|
||
['undo', 'redo', 'clean'],
|
||
[
|
||
{ font: FontClass.whitelist },
|
||
{ size: SizeStyle.whitelist },
|
||
{ lineheight: lineheight.whitelist },
|
||
{ header: [1, 2, 3, 4, 5, 6, false] },
|
||
],
|
||
['bold', 'italic', 'underline', 'strike'],
|
||
['blockquote', 'code-block'],
|
||
[{ header: 1 }, { header: 2 }],
|
||
[{ list: 'ordered' }, { list: 'bullet' }],
|
||
[{ script: 'sub' }, { script: 'super' }],
|
||
[{ indent: '-1' }, { indent: '+1' }],
|
||
[{ direction: 'rtl' }],
|
||
[{ color: [] }, { background: [] }],
|
||
[{ align: [] }],
|
||
['link', 'image', 'video', 'file'],
|
||
['better-table'],
|
||
]
|
||
|
||
// @提醒
|
||
const mentionObj = {
|
||
data: [
|
||
{
|
||
name: 'Jack',
|
||
age: 26,
|
||
cn: 'Jack杰克',
|
||
},
|
||
{
|
||
name: 'Lucy',
|
||
age: 22,
|
||
cn: 'Lucy露西',
|
||
},
|
||
],
|
||
searchKey: 'name',
|
||
}
|
||
|
||
// 图片:导入\custom-image\Options.ts DefaultOptions默认参数,也可以自定义对象
|
||
const CustomImageSpec = FluentEditor.imports['modules/image-spec']
|
||
// 表格
|
||
const BetterTable = FluentEditor.imports['modules/better-table']
|
||
|
||
const modules = {
|
||
file: true, // 上传文件需开启
|
||
image: {
|
||
specs: [CustomImageSpec],
|
||
},
|
||
// counter: true, // 字符计数器,两种配置方式都可以
|
||
counter: {
|
||
format: 'html',
|
||
count: 102400, // 默认500
|
||
errorTemplate:
|
||
'<span style="color:red">字数不能超过102400个字符</span>',
|
||
},
|
||
toolbar: {
|
||
container,
|
||
handlers: {
|
||
undo: function () {
|
||
// 如果当前有表格被激活则清除表格工具条
|
||
const betterTableModule = EDITOR.getModule('better-table')
|
||
if (betterTableModule.table) {
|
||
betterTableModule.hideTableTools()
|
||
}
|
||
EDITOR.history.undo()
|
||
},
|
||
redo: function () {
|
||
// 如果当前有表格被激活则清除表格工具条
|
||
const betterTableModule = EDITOR.getModule('better-table')
|
||
if (betterTableModule.table) {
|
||
betterTableModule.hideTableTools()
|
||
}
|
||
EDITOR.history.redo()
|
||
},
|
||
lineheight: function (value) {
|
||
EDITOR.format('lineheight', value, FluentEditor.sources.USER)
|
||
},
|
||
file: function () {
|
||
const option = EDITOR.options.uploadOption
|
||
const accept = option && option.fileAccept
|
||
inputFile.call(this, 'file', accept)
|
||
},
|
||
image: function () {
|
||
const option = EDITOR.options.uploadOption
|
||
const accept = option && option.imageAccept
|
||
inputFile.call(this, 'image', accept)
|
||
},
|
||
link: function (value) {
|
||
const range = this.quill.getSelection();
|
||
const { tooltip } = this.quill.theme;
|
||
|
||
const insertDefaultLink = () => {
|
||
const linkPlaceholder = '链接';
|
||
this.quill.insertText(range.index, linkPlaceholder, FluentEditor.sources.USER);
|
||
this.quill.setSelection(range.index, linkPlaceholder.length, FluentEditor.sources.USER);
|
||
|
||
const newRange = this.quill.getSelection();
|
||
if (newRange) {
|
||
// 初次生成blot时,如果值为空,不会走blot的create()方法
|
||
this.quill.formatText(newRange, 'link', ' ', FluentEditor.sources.USER);
|
||
this.quill.formatText(newRange, 'link', '', FluentEditor.sources.USER);
|
||
tooltip.edit('link', '', newRange);
|
||
}
|
||
};
|
||
|
||
if (value) {
|
||
if (isNullOrUndefined(range) || range.length === 0) {
|
||
// 直接插入超链接
|
||
insertDefaultLink();
|
||
} else {
|
||
// 选中文本插入超链接
|
||
this.quill.formatText(range, 'link', ' ', Emitter.sources.USER);
|
||
this.quill.formatText(range, 'link', '', Emitter.sources.USER);
|
||
tooltip.edit('link', '', range);
|
||
}
|
||
} else {
|
||
const [link, offset] = this.quill.scroll.descendant(LinkBlot, range.index);
|
||
if (isNullOrUndefined(link)) {
|
||
// 在超链接后面插入超链接
|
||
insertDefaultLink();
|
||
} else {
|
||
// 取消超链接
|
||
const fullRange = new Range(range.index - offset, link.length());
|
||
this.quill.formatText(fullRange, 'link', false, FluentEditor.sources.USER);
|
||
}
|
||
}
|
||
},
|
||
},
|
||
},
|
||
'better-table': {
|
||
operationMenu: {
|
||
items: {
|
||
copyCells: {
|
||
text: '复制',
|
||
},
|
||
copyTable: {
|
||
text: '复制表格',
|
||
},
|
||
emptyCells: {
|
||
text: '清空内容',
|
||
},
|
||
insertRowUp: {
|
||
text: '上插入行',
|
||
},
|
||
insertRowDown: {
|
||
text: '下插入行',
|
||
},
|
||
insertColumnLeft: {
|
||
text: '左插入列',
|
||
},
|
||
insertColumnRight: {
|
||
text: '右插入列',
|
||
},
|
||
mergeCells: {
|
||
text: '合并单元格',
|
||
},
|
||
unmergeCells: {
|
||
text: '拆分单元格',
|
||
},
|
||
deleteRow: {
|
||
text: '删除当前行',
|
||
},
|
||
deleteColumn: {
|
||
text: '删除当前列',
|
||
},
|
||
deleteTable: {
|
||
text: '删除表格',
|
||
},
|
||
},
|
||
color: true,
|
||
},
|
||
},
|
||
mention: {
|
||
search: async term => {
|
||
const { data, searchKey } = mentionObj
|
||
try {
|
||
return data.filter(d => {
|
||
return d[searchKey] && String(d[searchKey]).includes(term)
|
||
})
|
||
} catch (e) {
|
||
console.warn(e)
|
||
return []
|
||
}
|
||
},
|
||
},
|
||
keyboard: {
|
||
bindings: {
|
||
...BetterTable.keyboardBindings,
|
||
'list autofill': {
|
||
key: ' ',
|
||
shiftKey: null,
|
||
collapsed: true,
|
||
format: {
|
||
list: false,
|
||
'code-block': false,
|
||
blockquote: false,
|
||
header: false,
|
||
table: true,
|
||
},
|
||
prefix: /^\s*?(\d+\.|-|\*|\[ ?\]|\[x\])$/,
|
||
handler(range, context) {
|
||
const formats = EDITOR.getFormat(range)
|
||
if (formats['table-cell-line']) {
|
||
return true
|
||
}
|
||
if (isNullOrUndefined(EDITOR.scroll.query('list'))) {
|
||
return true
|
||
}
|
||
const { length } = context.prefix
|
||
const [line, offset] = EDITOR.getLine(range.index)
|
||
if (offset > length) {
|
||
return true
|
||
}
|
||
let value
|
||
switch (context.prefix.trim()) {
|
||
case '[]':
|
||
case '[ ]':
|
||
value = 'unchecked'
|
||
break
|
||
case '[x]':
|
||
value = 'checked'
|
||
break
|
||
case '-':
|
||
case '*':
|
||
value = 'bullet'
|
||
break
|
||
default:
|
||
value = 'ordered'
|
||
}
|
||
EDITOR.insertText(range.index, ' ', FluentEditor.sources.USER)
|
||
EDITOR.history.cutoff()
|
||
const delta = new Delta()
|
||
.retain(range.index - offset)
|
||
.delete(length + 1)
|
||
.retain(line.length() - 2 - offset)
|
||
.retain(1, { list: { value: value } })
|
||
EDITOR.updateContents(delta, FluentEditor.sources.USER)
|
||
EDITOR.history.cutoff()
|
||
EDITOR.setSelection(
|
||
range.index - length,
|
||
FluentEditor.sources.SILENT,
|
||
)
|
||
return false
|
||
},
|
||
},
|
||
// fix:在list的offset为0时(即只在最左侧触发)加键盘事件,使其跳过contenteditable=false的span进行移动
|
||
'list ignoreRight': {
|
||
key: 'ArrowRight',
|
||
shiftKey: null,
|
||
format: ['list'],
|
||
collapsed: true,
|
||
offset: 0,
|
||
handler(range) {
|
||
EDITOR.setSelection(
|
||
range.index + 1,
|
||
FluentEditor.sources.SILENT,
|
||
)
|
||
},
|
||
},
|
||
'list ignoreLeft': {
|
||
key: 'ArrowLeft',
|
||
shiftKey: null,
|
||
format: ['list'],
|
||
collapsed: true,
|
||
offset: 0,
|
||
handler(range) {
|
||
EDITOR.setSelection(
|
||
range.index - 1,
|
||
FluentEditor.sources.SILENT,
|
||
)
|
||
},
|
||
},
|
||
'list softBreak': {
|
||
key: 'Enter',
|
||
format: ['list'],
|
||
shiftKey: true,
|
||
collapsed: true,
|
||
handler(range) {
|
||
const [line, offset] = EDITOR.getLine(range.index)
|
||
const length = line.length()
|
||
// li的末尾不能添加软回车
|
||
if (length > offset + 1) {
|
||
EDITOR.insertEmbed(
|
||
range.index,
|
||
'soft-break',
|
||
true,
|
||
FluentEditor.sources.USER,
|
||
)
|
||
}
|
||
},
|
||
},
|
||
},
|
||
},
|
||
}
|
||
|
||
// 上传:图片、视频和文件
|
||
const { DEFAULTS: UploaderDfls } = FluentEditor.imports['modules/uploader']
|
||
UploaderDfls.enableMultiUpload = { file: true, image: false }
|
||
UploaderDfls.handler = (range, files, fileFlags, rejectFlags) => {
|
||
const fileArr = []
|
||
const imgArr = []
|
||
files.forEach((file, index) =>
|
||
fileFlags[index] ? fileArr.push(file) : imgArr.push(file),
|
||
)
|
||
if (modules.file && (fileArr.length || rejectFlags.file)) {
|
||
handleUploadFile(range, fileArr, rejectFlags.file)
|
||
}
|
||
if (imgArr.length || rejectFlags.image) {
|
||
handleUploadImage(
|
||
range,
|
||
{ file: imgArr[0], files: imgArr },
|
||
rejectFlags.image,
|
||
)
|
||
}
|
||
}
|
||
|
||
// 构建一个富文本框的实例,以方便我们的操作
|
||
var EDITOR = new FluentEditor('#editor', {
|
||
theme: 'snow',
|
||
modules,
|
||
// 上传:图片、视频和文件,相关配置
|
||
uploadOption: {
|
||
fileAccept: '.mp4,.xls,.doc,.docx,.ppt,.pptx',
|
||
imageAccept: '.gif,.png,.tiff,image/jpeg',
|
||
isVideoPlay: true,
|
||
imageUploadToServer: false, // 是否需要上传到服务器
|
||
},
|
||
})
|
||
|
||
// 点击工具栏插入表格 ql-better-table
|
||
var insertButton = document.querySelector('.ql-better-table')
|
||
const tableModule = EDITOR.getModule('better-table')
|
||
EDITOR.emitter.on(
|
||
'file-change',
|
||
fileOperationToSev
|
||
);
|
||
var insertTableHandler = function (event, row = 3, col = 3) {
|
||
tableModule.insertTable(row, col)
|
||
}
|
||
insertButton.addEventListener('click', insertTableHandler)
|
||
|
||
// 上传:图片、视频和文件,相关配置
|
||
// 触发上传
|
||
function inputFile(type, accept) {
|
||
const defaultMIMETypes = EDITOR.uploader.options[type].join(', ')
|
||
const mimeTypes = accept || defaultMIMETypes
|
||
let fileInput = this.container.querySelector(
|
||
`input.ql-${type}[type=file]`,
|
||
)
|
||
if (isNullOrUndefined(fileInput)) {
|
||
fileInput = document.createElement('input')
|
||
fileInput.classList.add(`ql-${type}`)
|
||
fileInput.setAttribute('type', 'file')
|
||
fileInput.setAttribute('style','display:none');
|
||
fileInput.setAttribute('accept', mimeTypes)
|
||
if (
|
||
UploaderDfls.enableMultiUpload === true ||
|
||
(UploaderDfls.enableMultiUpload['file'] && type === 'file') ||
|
||
(UploaderDfls.enableMultiUpload['image'] && type === 'image')
|
||
) {
|
||
fileInput.setAttribute('multiple', '')
|
||
}
|
||
fileInput.addEventListener('change', () => {
|
||
const range = EDITOR.getSelection(true)
|
||
EDITOR.uploader.upload(range, fileInput.files, type === 'file')
|
||
fileInput.value = ''
|
||
})
|
||
this.container.appendChild(fileInput)
|
||
}
|
||
fileInput.click()
|
||
}
|
||
|
||
function isNullOrUndefined(param) {
|
||
return param === null || param === undefined
|
||
}
|
||
|
||
const Delta = FluentEditor.imports['delta']
|
||
const popoverComponentRef = []
|
||
|
||
// 处理上传图片
|
||
function handleUploadImage(range, { file, files }, hasRejectedImage) {
|
||
|
||
if (EDITOR.options.uploadOption.imageUploadToServer) {
|
||
const imageEnableMultiUpload =
|
||
UploaderDfls.enableMultiUpload === true ||
|
||
UploaderDfls.enableMultiUpload['image']
|
||
const target = document.querySelector('.ql-image')
|
||
if (files.length) {
|
||
// showUploadingTips('image', target)
|
||
console.log('showUploadingTips')
|
||
}
|
||
const result = {
|
||
file,
|
||
data: { files: [file] },
|
||
hasRejectedImage: hasRejectedImage,
|
||
callback: res => {
|
||
// closeUploadingTips('image')
|
||
console.log('closeUploadingTips')
|
||
if (!res) {
|
||
return
|
||
}
|
||
if (imageEnableMultiUpload && Array.isArray(res)) {
|
||
res.forEach(value => insertImageToEditor(range, value))
|
||
} else {
|
||
insertImageToEditor(range, res)
|
||
}
|
||
},
|
||
}
|
||
if (imageEnableMultiUpload) {
|
||
result['data'] = { files }
|
||
}
|
||
uploadImageToSev(result)
|
||
} else {
|
||
const promises = files.map(fileItem => {
|
||
return new Promise(resolve => {
|
||
const reader = new FileReader()
|
||
reader.onload = e => {
|
||
resolve(e.target.result)
|
||
}
|
||
reader.readAsDataURL(fileItem)
|
||
})
|
||
})
|
||
Promise.all(promises).then(images => {
|
||
const update = images.reduce((delta, image) => {
|
||
return delta.insert({ image })
|
||
}, new Delta().retain(range.index).delete(range.length))
|
||
|
||
EDITOR.updateContents(update, FluentEditor.sources.USER)
|
||
EDITOR.setSelection(
|
||
range.index + images.length,
|
||
FluentEditor.sources.SILENT,
|
||
)
|
||
})
|
||
}
|
||
}
|
||
|
||
// 处理上传文件
|
||
function handleUploadFile(range, files, hasRejectedFile) {
|
||
const fileEnableMultiUpload =
|
||
UploaderDfls.enableMultiUpload === true ||
|
||
UploaderDfls.enableMultiUpload['file']
|
||
const target = document.querySelector('.ql-file')
|
||
if (files.length) {
|
||
// showUploadingTips('files', target)
|
||
console.log('showUploadingTips')
|
||
}
|
||
fileOperationToSev({
|
||
operation: 'upload',
|
||
data: fileEnableMultiUpload ? { files } : { file: files[0] },
|
||
hasRejectedFile: hasRejectedFile,
|
||
callback: res => {
|
||
// this.closeUploadingTips('file')
|
||
console.log('closeUploadingTips')
|
||
if (!res) {
|
||
return
|
||
}
|
||
console.log('fileEnableMultiUpload')
|
||
console.log(fileEnableMultiUpload)
|
||
if (fileEnableMultiUpload && Array.isArray(res)) {
|
||
res.forEach((value, index) =>
|
||
insertFileToEditor(range, files[index], value),
|
||
)
|
||
} else {
|
||
insertFileToEditor(range, files[0], res)
|
||
}
|
||
},
|
||
})
|
||
}
|
||
|
||
// 图片上传到服务器,需要将示例中的上传接口替换成真实的后台接口才能正确运行。
|
||
function uploadImageToSev(event) {
|
||
const {
|
||
file,
|
||
hasRejectedImage,
|
||
data: { files},
|
||
callback,
|
||
} = event
|
||
if (hasRejectedImage) {
|
||
this.msgs = [
|
||
{
|
||
severity: 'error',
|
||
summary: '上传图片失败',
|
||
detail: `只支持上传以下类型:\n${EDITOR.options.uploadOption.imageAccept}`,
|
||
},
|
||
]
|
||
}
|
||
if (file || files) {
|
||
/* 使用时取消注释,填写您的上传接口 */
|
||
/* setTimeout用于模拟接口延迟,演示上传提示,使用时请移除 */
|
||
// const formData = new FormData()
|
||
// formData.append('file', file)
|
||
// this.http.post('url/to/your/api', formData).subscribe(res => {
|
||
let res
|
||
const imageUrl = './src/assets/image1.png' /* 后台返回的图片URL */
|
||
if (UploaderDfls.enableMultiUpload.image) {
|
||
res = files.map(fileItem => {
|
||
return {
|
||
code: 0,
|
||
message: 'Upload image successfully!',
|
||
data: { imageId: 100, imageUrl },
|
||
}
|
||
})
|
||
} else {
|
||
res = {
|
||
code: 0,
|
||
message: 'Upload image successfully!',
|
||
data: { imageId: 100, imageUrl },
|
||
}
|
||
}
|
||
setTimeout(() => callback(res), 500)
|
||
// })
|
||
}
|
||
}
|
||
|
||
// 文件上传到服务器,需要将示例中的上传接口替换成真实的后台接口才能正确运行。
|
||
function fileOperationToSev(event) {
|
||
const {
|
||
operation,
|
||
hasRejectedFile,
|
||
data: { fileId, file, files, fileDownloadUrl },
|
||
callback,
|
||
} = event
|
||
switch (operation) {
|
||
case 'upload':
|
||
if (hasRejectedFile) {
|
||
this.msgs = [
|
||
{
|
||
severity: 'error',
|
||
summary: '上传文件失败',
|
||
detail: `只支持上传以下类型:\n${EDITOR.options.uploadOption.fileAccept}`,
|
||
},
|
||
]
|
||
}
|
||
if (file || files) {
|
||
/* 使用时取消注释,填写您的上传接口 */
|
||
/* setTimeout用于模拟接口延迟,演示上传提示,使用时请移除 */
|
||
/* const formData = new FormData();
|
||
formData.append('file', file);
|
||
this.http.post('url/to/your/api', formData)
|
||
.subscribe(res => { */
|
||
let res
|
||
if (UploaderDfls.enableMultiUpload.file) {
|
||
res = files.map(fileItem => {
|
||
return {
|
||
code: 0,
|
||
message: 'Upload file successfully!',
|
||
data: {
|
||
id: 'faf40644468b497d90cbaf4304c39e1a',
|
||
title: fileItem.name,
|
||
size: fileItem.size,
|
||
lastModified: fileItem.lastModified,
|
||
src: 'http://www.baidu.com',
|
||
},
|
||
}
|
||
})
|
||
} else {
|
||
res = {
|
||
code: 0,
|
||
message: 'Upload file successfully!',
|
||
data: {
|
||
id: 'faf40644468b497d90cbaf4304c39e1a',
|
||
title: file.name,
|
||
size: file.size,
|
||
lastModified: file.lastModified,
|
||
src: '已上传视频地址',
|
||
},
|
||
}
|
||
}
|
||
setTimeout(() => callback(res), 500)
|
||
/* }); */
|
||
}
|
||
break
|
||
case 'download':
|
||
console.log('download file:' + fileId)
|
||
break
|
||
case 'delete':
|
||
this.http.post(fileDownloadUrl, { id: fileId })
|
||
break
|
||
}
|
||
}
|
||
|
||
// 将图片插入编辑器
|
||
function insertImageToEditor(range, { code, message, data }) {
|
||
if (code === 0) {
|
||
const { imageId, imageUrl } = data
|
||
// 粘贴截图或者从外源直接拷贝的单图时,需要将编辑器中已选中的内容删除
|
||
const oldContent = new Delta()
|
||
.retain(range.index)
|
||
.delete(range.length)
|
||
const currentContent = new Delta([
|
||
{
|
||
insert: { image: imageUrl },
|
||
attributes: { 'image-id': imageId },
|
||
},
|
||
])
|
||
const newContent = oldContent.concat(currentContent)
|
||
EDITOR.updateContents(newContent, FluentEditor.sources.USER)
|
||
} else {
|
||
console.error('error message:', message)
|
||
}
|
||
}
|
||
|
||
// 将文件插入编辑器
|
||
function insertFileToEditor(range, file, { code, message, data }) {
|
||
if (code === 0) {
|
||
const oldContent = new Delta()
|
||
.retain(range.index)
|
||
.delete(range.length)
|
||
const videoFlag =
|
||
EDITOR.options.uploadOption &&
|
||
EDITOR.options.uploadOption.isVideoPlay &&
|
||
/^video\/[-\w.]+$/.test(file.type)
|
||
const insertObj = videoFlag ? { video: data } : { file: data }
|
||
const currentContent = new Delta([{ insert: insertObj }])
|
||
const newContent = oldContent.concat(currentContent)
|
||
EDITOR.updateContents(newContent, FluentEditor.sources.USER)
|
||
} else {
|
||
console.error('error message:', message)
|
||
}
|
||
}
|
||
|
||
// 显示上传中提示
|
||
// function showUploadingTips(type, target) {
|
||
// popoverComponentRef[type] = this.overlayContainerRef.createComponent(
|
||
// this.componentFactoryResolver.resolveComponentFactory(
|
||
// PopoverComponent,
|
||
// ),
|
||
// )
|
||
// Object.assign(popoverComponentRef[type].instance, {
|
||
// content: LANG_CONF['uploading'],
|
||
// triggerElementRef: { nativeElement: target },
|
||
// position: 'top',
|
||
// popType: 'default',
|
||
// popMaxWidth: 200,
|
||
// appendToBody: true,
|
||
// zIndex: 1060,
|
||
// })
|
||
// }
|
||
|
||
// 移除上传中提示
|
||
// function closeUploadingTips(type) {
|
||
// if (popoverComponentRef[type]) {
|
||
// popoverComponentRef[type].destroy()
|
||
// }
|
||
// if (popoverComponentRef.length === 1) {
|
||
// popoverComponentRef = []
|
||
// }
|
||
// }
|
||
|
||
function onPaste({ files, callback }) {
|
||
const _this = this
|
||
function uploadImage(file) {
|
||
return new Promise((resolve, reject) => {
|
||
const formData = new FormData()
|
||
formData.append('file', file)
|
||
_this.http.post('url/to/your/api', formData).subscribe(res => {
|
||
const imageUrl = 'url/image.png' /* 后台返回的图片URL */
|
||
resolve(imageUrl)
|
||
})
|
||
})
|
||
}
|
||
|
||
;(async () => {
|
||
try {
|
||
const imageUrls = []
|
||
for (let i = 0; i < files.length; i++) {
|
||
imageUrls.push(await uploadImage(files[i]))
|
||
}
|
||
callback({
|
||
code: 0,
|
||
message: 'Upload all images successfully!',
|
||
data: {
|
||
imageUrls: imageUrls,
|
||
},
|
||
})
|
||
} catch (e) {
|
||
throw new Error('Upload failed.')
|
||
}
|
||
})()
|
||
}
|
||
</script>
|
||
</body>
|
||
</html>
|