Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: add image-upload-to-server demo #52

Merged
merged 1 commit into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 87 additions & 0 deletions packages/docs/fluent-editor/demos/image-upload-to-server.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
<script setup lang="ts">
import { onMounted } from 'vue'
import FluentEditor from '@opentiny/fluent-editor'

let editor

const TOOLBAR_CONFIG = [
[{ header: [] }],
['bold', 'italic', 'underline', 'link'],
[{ list: 'ordered' }, { list: 'bullet' }],
['clean'],
['image'],
]

// 这里需要换成你自己的图片上传服务地址
const IMG_API_URL = 'https://run.mocky.io/v3/f34365b4-679d-4e8f-8313-ddb11d6750be'

/**
* 上传图片到服务器
* @param image File 格式的图片
* @param callback 回调函数,用来处理服务器返回的图片 URL
* 除了 XMLHttpRequest,也可以使用 fetch / axios 等工具实现图片上传。
*/
function imageHandler(image, callback) {
const data = new FormData()
data.append('image', image)
const xhr = new XMLHttpRequest()
xhr.open('POST', IMG_API_URL, true)
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
// 这里需要换成实际的接口返回值,比如:JSON.parse(xhr.responseText)
const response = {
status: 200,
success: 'Upload success!',
data: {
link: 'https://res.hc-cdn.com/tiny-vue-web-doc/3.18.9.20240902190525/static/images/mountain.png',
},
}
if (response.status === 200 && response.success) {
callback(response.data.link)
}
else {
// 图片上传失败了,就转成 Base64 格式
var reader = new FileReader()
reader.onload = function (e) {
callback(e.target.result)
}
reader.readAsDataURL(image)
}
}
}
xhr.send(data)
}

onMounted(() => {
// ssr compat, reference: https://vitepress.dev/guide/ssr-compat#importing-in-mounted-hook
import('@opentiny/fluent-editor').then((module) => {
const FluentEditor = module.default

editor = new FluentEditor('#editor-image-upload-to-server', {
theme: 'snow',
modules: {
toolbar: TOOLBAR_CONFIG,
},
uploadOption: {
imageUpload: ({ file, callback }) => {
imageHandler(file, (imageUrl) => {
// 调用 callback,传入 imageUrl 即可实现图片渲染
callback({
code: 0,
message: 'Upload success!',
data: {
imageUrl,
},
})
})
},
},
})
})
})

</script>

<template>
<div id="editor-image-upload-to-server" />
</template>
Comment on lines +1 to +87
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approve the script setup and suggest improvements for error handling.

The script setup in this Vue component is well-organized and uses modern Vue features effectively. However, consider enhancing the error handling in the imageHandler function to provide more detailed feedback to the user in case of upload failures.

5 changes: 5 additions & 0 deletions packages/docs/fluent-editor/docs/image-upload.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,8 @@

:::demo src=demos/image-upload.vue
:::

## 上传到服务器

:::demo src=demos/image-upload-to-server.vue
:::
2 changes: 1 addition & 1 deletion packages/fluent-editor/src/custom-clipboard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ class CustomClipboard extends Clipboard {
const files = Array.from(e.clipboardData.files || [])
const msExcelCheck = /<meta.*?Microsoft Excel\s[\d].*?>/

if (html.search(msExcelCheck) === -1 && this.quill.options.uploadOption?.imageUploadToServer && files.length > 0) {
if (html.search(msExcelCheck) === -1 && files.length > 0) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential Security Risk: Simplified File Upload Condition

The change in the conditional logic at line 120 removes the check for the image upload option, which simplifies the process but could potentially allow unintended file uploads. This could lead to security risks or functional issues if not handled properly.

Please ensure that this change aligns with the intended use cases and consider adding safeguards or additional checks to prevent unwanted file uploads.

this.quill.uploader.upload(range, files)
}
else {
Expand Down
27 changes: 24 additions & 3 deletions packages/fluent-editor/src/custom-uploader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,10 +113,31 @@ class CustomUploader extends Uploader {
}
}

// 将图片插入编辑器
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)
this.quill.updateContents(newContent, Quill.sources.USER)
this.quill.setSelection(range.index + 1)
}
else {
console.error('error message:', message)
}
}

// 处理上传图片
handleUploadImage(range, { file, files }, hasRejectedImage) {
if (this.imageUploadToServer) {
const imageEnableMultiUpload = this.enableMultiUpload === true || this.enableMultiUpload['image']
if (this.quill.options.uploadOption?.imageUpload) {
const imageEnableMultiUpload = this.enableMultiUpload === true || this.enableMultiUpload?.['image']

const result = {
file,
Expand All @@ -137,7 +158,7 @@ class CustomUploader extends Uploader {
if (imageEnableMultiUpload) {
result['data'] = { files }
}
this.imageUpload.emit(result)
this.quill.options.uploadOption?.imageUpload(result)
}
else {
const promises = files.map((fileItem) => {
Expand Down
Loading