Files
t-admin/uni_modules/uni-cms/components/editor/web/modules/image-extend.js
T
2025-06-13 21:16:12 +08:00

200 lines
5.4 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import './image-uploading.css'
export default function (Quill) {
// const Delta = Quill.import('delta')
class ImageExtend {
constructor(quill, config) {
this.quill = quill
this.config = config
// 通过监听内容变化判断是否上传图片
quill.on(Quill.events.TEXT_CHANGE, (delta) => {
const imageElements = quill.container.querySelectorAll('img[data-local]:not(.uploading)')
imageElements.forEach(element => {
if (element.classList.contains('uploaded')) {
element.removeAttribute('data-local')
element.classList.remove('uploaded')
if(element.classList.length <= 0) {
element.removeAttribute('class')
}
} else {
this.uploader(element)
}
})
})
// 插入粘贴的文件图片
quill.root.addEventListener('paste', (e) => {
const files = Array.from(e.clipboardData.files || []);
if (files.length > 0) {
e.preventDefault();
setTimeout(() => {
this.insertFileImage(files[0])
}, 0)
}
}, false)
// 插入粘贴的网络图片
quill.clipboard.addMatcher('IMG', function (node, delta) {
if (!delta.ops[0].attributes) {
delta.ops[0].attributes = {}
}
delta.ops[0].attributes['data-local'] = node.src
delta.ops[0].attributes = Object.keys(delta.ops[0].attributes).reduce((attributes, key) => {
if (['data-local', 'alt'].includes(key)) {
attributes[key] = delta.ops[0].attributes[key]
}
return attributes
}, {})
return delta
});
}
async uploader(element) {
// 标记元素正在上传
element.classList.add('uploading')
// 检查是否配置了上传方法
if (typeof this.config.uploadHandler !== 'function') {
console.warn('没有配置图片上传方法,移除图片:' + element.src)
Quill.find(element).deleteAt(0)
return
}
const imageFile = await this.convertImageFile(element.src)
// 构建上传元素
const uploadNode = this.getUploadNode(element.src)
// 获取位置
const parent = this.quill.root.parentNode
setTimeout(() => {
const elementRect = element.getBoundingClientRect()
const containerRect = parent.getBoundingClientRect()
uploadNode.style.left = `${elementRect.left - containerRect.left - 1 + parent.scrollLeft}px`
uploadNode.style.top = `${elementRect.top - containerRect.top + parent.scrollTop}px`
uploadNode.style.width = `${elementRect.width}px`
uploadNode.style.height = `${elementRect.height}px`
// 渲染到element上
parent.appendChild(uploadNode)
}, 100)
// 上传图片 展示进度
Promise.resolve({
then: (resolve, reject) => {
try {
const handler = this.config.uploadHandler(imageFile, element)
resolve(handler)
} catch (e) {
reject(e)
}
}
}).then((url) => {
const customData = (element.getAttribute('data-custom') || '').split('&').filter(item => item).reduce((res, item) => {
const [key, value] = item.split('=')
res[key] = value
return res
}, {})
customData.source = url
element.src = url
element.removeAttribute('data-local')
element.classList.remove('uploading')
element.setAttribute('data-custom', Object.keys(customData).map(key => `${key}=${customData[key]}`).join('&'))
parent.removeChild(uploadNode)
}).catch((e) => {
parent.removeChild(uploadNode)
console.error(e)
})
}
async convertImageFile(imageUrl) {
const result = {
blob: imageUrl,
ext: 'jpg',
size: 0,
}
// if (!/^blob:/.test(imageUrl)) {
const image = await fetch(imageUrl)
const blob = await image.blob()
const mimeType = this.getBlobImageMimeType(blob, imageUrl)
const ext = mimeType.replace('image/', '')
const filename = `image.${ext}`
const file = new File([blob], filename, {type: mimeType})
result.blob = this.file2blob(file)
result.ext = ext
result.size = file.size
// }
return result
}
getBlobImageMimeType(blob, imageUrl) {
if (blob.type) {
return blob.type
}
if (imageUrl.match(/data:(image\/\w+);base64/)) {
return imageUrl.match(/data:(image\/\w+);base64/)[1].toLowerCase()
}
return 'image/jpeg'
}
getUploadNode(imageUrl) {
const div = document.createElement('div')
const img = document.createElement('img')
img.src = imageUrl
img.setAttribute('οndragstart', 'return false')
div.appendChild(img)
div.classList.add('image-uploading')
return div
}
file2blob(file) {
let url
if (window.createObjectURL !== undefined) {
url = window.createObjectURL(file);
} else if (window.URL !== undefined) {
url = window.URL.createObjectURL(file);
} else if (window.webkitURL !== undefined) {
url = window.webkitURL.createObjectURL(file);
}
return url
}
insertFileImage(file) {
const range = this.quill.getSelection(true)
const url = this.file2blob(file)
if (range.index > 0 && this.quill.getText(range.index - 1, 1) !== '\n') {
this.quill.insertText(range.index, '\n', Quill.sources.SILENT)
range.index += 1
}
this.quill.insertEmbed(range.index, 'image', url, Quill.sources.SILENT)
this.quill.formatText(range.index, 1, 'data-local', url, Quill.sources.SILENT)
this.quill.insertText(range.index + 1, '\n', Quill.sources.SILENT)
this.quill.setSelection(range.index + 2, Quill.sources.SILENT)
this.quill.scrollIntoView()
}
}
return {
'modules/ImageExtend': ImageExtend
}
}