wangEditor 5
官網(wǎng):https://www.wangeditor.com
demo 示例:https://www.wangeditor.com/demo/
快速開(kāi)始:https://www.wangeditor.com/v5/getting-started.html
內(nèi)容處理:https://www.wangeditor.com/v5/content.html
上傳圖片:https://www.wangeditor.com/v5/menu-config.html#%E4%B8%8A%E4%BC%A0%E5%9B%BE%E7%89%87
上傳視頻:https://www.wangeditor.com/v5/menu-config.html#%E4%B8%8A%E4%BC%A0%E8%A7%86%E9%A2%91
1、 創(chuàng)建空白編輯器
引入 CSS 定義樣式
<link rel="stylesheet">
<style>
#editor—wrapper {
border: 1px solid #ccc;
z-index: 100; /* 按需定義 */
}
#toolbar-container { border-bottom: 1px solid #ccc; }
#editor-container { height: 500px; }
</style>
定義 HTML 結(jié)構(gòu)
<div id="editor—wrapper">
<div id="toolbar-container"><!-- 工具欄 --></div>
<div id="editor-container"><!-- 編輯器 --></div>
</div>
引入 JS 創(chuàng)建編輯器
<script src="https://unpkg.com/@wangeditor/editor@latest/dist/index.js"></script>
<script>
const { createEditor, createToolbar } = window.wangEditor
const editorConfig = {
placeholder: 'Type here...',
onChange(editor) {
const html = editor.getHtml()
console.log('editor content', html)
// 也可以同步到 <textarea>
}
}
const editor = createEditor({
selector: '#editor-container',
html: '<p><br></p>',
config: editorConfig,
mode: 'default', // or 'simple'
})
const toolbarConfig = {}
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default', // or 'simple'
})
</script>
- mode: 'default' 默認(rèn)模式 - 集成了 wangEditor 所有功能
default模式.png
- mode: 'simple' 簡(jiǎn)潔模式 - 僅有部分常見(jiàn)功能,但更加簡(jiǎn)潔易用
simple模式.png
2、 內(nèi)容處理
獲取 HTML 和 Text
使用 editor.getHtml() 獲取 HTML 內(nèi)容,可參考 demo。使用 editor.getText() 獲取純文本內(nèi)容。
推薦使用 HTML 格式存儲(chǔ)數(shù)據(jù)。
設(shè)置內(nèi)容
設(shè)置 HTML
【注意】這里的 HTML 內(nèi)容必須是 wangEditor 生成的(即 editor.getHtml() 返回的) HTML 格式,不可以自己隨意寫。HTML 格式非常靈活,wangEditor 無(wú)法兼容所有的 HTML 格式。
例如,wangEditor 可以識(shí)別 <strong>hello</strong> 為加粗,但無(wú)法識(shí)別 <span style="font-weight: bold;">hello</span> 等其他加粗方式。
創(chuàng)建時(shí)設(shè)置 HTML
const editor = createEditor({
html: '<p>hello <strong>world</strong></p>', // 從 editor.getHtml() 獲取的 html 內(nèi)容
// 其他屬性...
})
動(dòng)態(tài)設(shè)置 HTML
參考 demo
editor.setHtml('<p>hello <strong>world</strong></p>')
3、 工具欄配置
使用toolbar.getConfig().toolbarKeys獲取所有工具欄的配置信息
const toolbarConfig = {}
// 自定義工具欄
toolbarConfig.toolbarKeys = [
'color',
'bgColor',
'clearStyle',
'justifyLeft',
'justifyRight',
'justifyCenter',
// 菜單組,包含多個(gè)菜單
{
key: 'group-image',
title: '圖片',
iconSvg: '<svg viewBox="0 0 1024 1024"><path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z"></path></svg>',
menuKeys: ["insertImage", "uploadImage"]
},
{
key: 'group-video',
title: '視頻',
iconSvg: '<svg viewBox="0 0 1024 1024"><path d="M981.184 160.096C837.568 139.456 678.848 128 512 128S186.432 139.456 42.816 160.096C15.296 267.808 0 386.848 0 512s15.264 244.16 42.816 351.904C186.464 884.544 345.152 896 512 896s325.568-11.456 469.184-32.096C1008.704 756.192 1024 637.152 1024 512s-15.264-244.16-42.816-351.904zM384 704V320l320 192-320 192z"></path></svg>',
menuKeys: ["insertVideo", "uploadVideo"]
},
'undo',
'redo',
]

4、上傳圖片
上傳圖片的配置比較復(fù)雜,拆分為幾個(gè)部分來(lái)講解??蓞⒖歼@個(gè) demo。
editorConfig.MENU_CONF['uploadImage'] = {
// 上傳圖片的配置
}
服務(wù)端地址
必填,否則上傳圖片會(huì)報(bào)錯(cuò)。
editorConfig.MENU_CONF['uploadImage'] = {
server: '/api/upload',
}
【特別注意】服務(wù)端 response body 格式要求如下:
上傳成功的返回格式:
{
"errno": 0, // 注意:值是數(shù)字,不能是字符串
"data": {
"url": "xxx", // 圖片 src ,必須
"alt": "yyy", // 圖片描述文字,非必須
"href": "zzz" // 圖片的鏈接,非必須
}
}
上傳失敗的返回格式:
{
"errno": 1, // 只要不等于 0 就行
"message": "失敗信息"
}
如果你的服務(wù)端 response body 無(wú)法按照上述格式,可以使用下文的
customInsert
自定義功能
如果用于 Typescript ,則要定義插入函數(shù)的類型。
type InsertFnType = (url: string, alt: string, href: string) => void
自定義插入
如果你的服務(wù)端 response body 無(wú)法按照上文規(guī)定的格式,則無(wú)法插入圖片,提示失敗。
但你可以使用 customInsert 來(lái)自定義插入圖片。
editorConfig.MENU_CONF['uploadImage'] = {
// 自定義插入圖片
customInsert(res: any, insertFn: InsertFnType) { // TS 語(yǔ)法
// customInsert(res, insertFn) { // JS 語(yǔ)法
// res 即服務(wù)端的返回結(jié)果
// 從 res 中找到 url alt href ,然后插圖圖片
insertFn(url, alt, href)
},
}
基本配置
editorConfig.MENU_CONF['uploadImage'] = {
// form-data fieldName ,默認(rèn)值 'wangeditor-uploaded-image'
fieldName: 'your-custom-name',
// 單個(gè)文件的最大體積限制,默認(rèn)為 2M
maxFileSize: 1 * 1024 * 1024, // 1M
// 最多可上傳幾個(gè)文件,默認(rèn)為 100
maxNumberOfFiles: 10,
// 選擇文件時(shí)的類型限制,默認(rèn)為 ['image/*'] 。如不想限制,則設(shè)置為 []
allowedFileTypes: ['image/*'],
// 自定義上傳參數(shù),例如傳遞驗(yàn)證的 token 等。參數(shù)會(huì)被添加到 formData 中,一起上傳到服務(wù)端。
meta: {
token: 'xxx',
otherKey: 'yyy'
},
// 將 meta 拼接到 url 參數(shù)中,默認(rèn) false
metaWithUrl: false,
// 自定義增加 http header
headers: {
Accept: 'text/x-json',
otherKey: 'xxx'
},
// 跨域是否傳遞 cookie ,默認(rèn)為 false
withCredentials: true,
// 超時(shí)時(shí)間,默認(rèn)為 10 秒
timeout: 5 * 1000, // 5 秒
}
回調(diào)函數(shù)
editorConfig.MENU_CONF['uploadImage'] = {
// 上傳之前觸發(fā)
onBeforeUpload(file: File) { // TS 語(yǔ)法
// onBeforeUpload(file) { // JS 語(yǔ)法
// file 選中的文件,格式如 { key: file }
return file
// 可以 return
// 1\. return file 或者 new 一個(gè) file ,接下來(lái)將上傳
// 2\. return false ,不上傳這個(gè) file
},
// 上傳進(jìn)度的回調(diào)函數(shù)
onProgress(progress: number) { // TS 語(yǔ)法
// onProgress(progress) { // JS 語(yǔ)法
// progress 是 0-100 的數(shù)字
console.log('progress', progress)
},
// 單個(gè)文件上傳成功之后
onSuccess(file: File, res: any) { // TS 語(yǔ)法
// onSuccess(file, res) { // JS 語(yǔ)法
console.log(`${file.name} 上傳成功`, res)
},
// 單個(gè)文件上傳失敗
onFailed(file: File, res: any) { // TS 語(yǔ)法
// onFailed(file, res) { // JS 語(yǔ)法
console.log(`${file.name} 上傳失敗`, res)
},
// 上傳錯(cuò)誤,或者觸發(fā) timeout 超時(shí)
onError(file: File, err: any, res: any) { // TS 語(yǔ)法
// onError(file, err, res) { // JS 語(yǔ)法
console.log(`${file.name} 上傳出錯(cuò)`, err, res)
},
}
5、上傳視頻
上傳視頻的配置比較復(fù)雜,拆分為幾個(gè)部分來(lái)講解。可參考這個(gè) demo。
editorConfig.MENU_CONF['uploadVideo'] = {
// 上傳視頻的配置
}
服務(wù)端地址
必填,否則上傳視頻會(huì)報(bào)錯(cuò)。
editorConfig.MENU_CONF['uploadVideo'] = {
server: '/api/upload',
}
【特別注意】服務(wù)端 response body 格式要求如下:
上傳成功的返回格式:
{
"errno": 0, // 注意:值是數(shù)字,不能是字符串
"data": {
"url": "xxx", // 視頻 src ,必須
"poster": "xxx.png" // 視頻封面圖片 url ,可選
}
}
// 注意:@wangeditor/editor 版本 >= 5.1.8 才支持 video poster
上傳失敗的返回格式:
{
"errno": 1, // 只要不等于 0 就行
"message": "失敗信息"
}
如果你的服務(wù)端 response body 無(wú)法按照上述格式,可以使用下文的
customInsert
基本配置
editorConfig.MENU_CONF['uploadVideo'] = {
// form-data fieldName ,默認(rèn)值 'wangeditor-uploaded-video'
fieldName: 'your-custom-name',
// 單個(gè)文件的最大體積限制,默認(rèn)為 10M
maxFileSize: 5 * 1024 * 1024, // 5M
// 最多可上傳幾個(gè)文件,默認(rèn)為 5
maxNumberOfFiles: 3,
// 選擇文件時(shí)的類型限制,默認(rèn)為 ['video/*'] 。如不想限制,則設(shè)置為 []
allowedFileTypes: ['video/*'],
// 自定義上傳參數(shù),例如傳遞驗(yàn)證的 token 等。參數(shù)會(huì)被添加到 formData 中,一起上傳到服務(wù)端。
meta: {
token: 'xxx',
otherKey: 'yyy'
},
// 將 meta 拼接到 url 參數(shù)中,默認(rèn) false
metaWithUrl: false,
// 自定義增加 http header
headers: {
Accept: 'text/x-json',
otherKey: 'xxx'
},
// 跨域是否傳遞 cookie ,默認(rèn)為 false
withCredentials: true,
// 超時(shí)時(shí)間,默認(rèn)為 30 秒
timeout: 15 * 1000, // 15 秒
// 視頻不支持 base64 格式插入
}
回調(diào)函數(shù)
editorConfig.MENU_CONF['uploadVideo'] = {
// 上傳之前觸發(fā)
onBeforeUpload(file: File) { // TS 語(yǔ)法
// onBeforeUpload(file) { // JS 語(yǔ)法
// file 選中的文件,格式如 { key: file }
return file
// 可以 return
// 1\. return file 或者 new 一個(gè) file ,接下來(lái)將上傳
// 2\. return false ,不上傳這個(gè) file
},
// 上傳進(jìn)度的回調(diào)函數(shù)
onProgress(progress: number) { // TS 語(yǔ)法
// onProgress(progress) { // JS 語(yǔ)法
// progress 是 0-100 的數(shù)字
console.log('progress', progress)
},
// 單個(gè)文件上傳成功之后
onSuccess(file: File, res: any) { // TS 語(yǔ)法
// onSuccess(file, res) { // JS 語(yǔ)法
console.log(`${file.name} 上傳成功`, res)
},
// 單個(gè)文件上傳失敗
onFailed(file: File, res: any) { // TS 語(yǔ)法
// onFailed(file, res) { // JS 語(yǔ)法
console.log(`${file.name} 上傳失敗`, res)
},
// 上傳錯(cuò)誤,或者觸發(fā) timeout 超時(shí)
onError(file: File, err: any, res: any) { // TS 語(yǔ)法
// onError(file, err, res) { // JS 語(yǔ)法
console.log(`${file.name} 上傳出錯(cuò)`, err, res)
},
}
自定義功能
如果用于 Typescript ,則要定義插入函數(shù)的類型。
type InsertFnType = (url: string, poster: string = '') => void
自定義插入
如果你的服務(wù)端 response body 無(wú)法按照上文規(guī)定的格式,則無(wú)法插入視頻,提示失敗。
但你可以使用 customInsert 來(lái)自定義插入視頻。
editorConfig.MENU_CONF['uploadVideo'] = {
// 自定義插入視頻
customInsert(res: any, insertFn: InsertFnType) { // TS 語(yǔ)法
// customInsert(res, insertFn) { // JS 語(yǔ)法
// res 即服務(wù)端的返回結(jié)果
// 從 res 中找到 url poster ,然后插入視頻
insertFn(url, poster)
},
}
示例代碼:
ajax-create.blade.php
<style>
#editor—wrapper {
border: 1px solid #ccc;
z-index: 100; /* 按需定義 */
}
#toolbar-container { border-bottom: 1px solid #ccc; }
#editor-container { height: 500px; }
</style>
<form action="{{route('order.note.ajax.create')}}" method="post">
@csrf
<input type="hidden" name="order_id" value="{{$orderId}}"/>
<div class="mb-3">
<textarea style="display:none;" class="form-control" name="content" rows="5"></textarea>
<div id="editor—wrapper">
<div id="toolbar-container"><!-- 工具欄 --></div>
<div id="editor-container"><!-- 編輯器 --></div>
</div>
<div class="invalid-feedback"></div>
</div>
<button type="button" class="btn btn-primary" onclick="ajaxSubmitForm(this)">保存</button>
<div class="js-ajax-msg"></div>
</form>
<script>
$(document).ready(function(){
// 自定義校驗(yàn)圖片
function customCheckImageFn(src, alt, url) {
if (!src) {
return
}
if (src.indexOf('http') !== 0) {
return '圖片網(wǎng)址必須以 http/https 開(kāi)頭'
}
return true
// 返回值有三種選擇:
// 1. 返回 true ,說(shuō)明檢查通過(guò),編輯器將正常插入圖片
// 2. 返回一個(gè)字符串,說(shuō)明檢查未通過(guò),編輯器會(huì)阻止插入。會(huì) alert 出錯(cuò)誤信息(即返回的字符串)
// 3. 返回 undefined(即沒(méi)有任何返回),說(shuō)明檢查未通過(guò),編輯器會(huì)阻止插入。但不會(huì)提示任何信息
}
// 轉(zhuǎn)換圖片鏈接
function customParseImageSrc(src) {
if (src.indexOf('http') !== 0) {
return `http://${src}`
}
return src
}
const { createEditor, createToolbar } = window.wangEditor
const editorConfig = {
MENU_CONF: {},
placeholder: 'Type here...',
onChange(editor) {
const html = editor.getHtml()
console.log('editor content', html)
// 同步到 <textarea>
if('<p><br></p>' !== html){
$("textarea[name=content]").text(html)
}
}
}
// 配置上傳圖片
editorConfig.MENU_CONF['uploadImage'] = {
server: '{{ route("api.upload.file") }}',
fieldName: 'image',
// 單個(gè)文件的最大體積限制,默認(rèn)為 2M
maxFileSize: 1 * 1024 * 1024, // 1M
// 最多可上傳幾個(gè)文件,默認(rèn)為 100
maxNumberOfFiles: 10,
// 選擇文件時(shí)的類型限制,默認(rèn)為 ['image/*'] 。如不想限制,則設(shè)置為 []
allowedFileTypes: ['image/*'],
// 自定義上傳參數(shù),例如傳遞驗(yàn)證的 token 等。參數(shù)會(huì)被添加到 formData 中,一起上傳到服務(wù)端。
meta: {
_token: "{{ csrf_token() }}"
},
// 將 meta 拼接到 url 參數(shù)中,默認(rèn) false
metaWithUrl: false,
// 自定義增加 http header
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
// 跨域是否傳遞 cookie ,默認(rèn)為 false
withCredentials: true,
// 超時(shí)時(shí)間,默認(rèn)為 10 秒
timeout: 5 * 1000, // 5 秒
// 上傳之前觸發(fā)
onBeforeUpload(file) {
// file 選中的文件,格式如 { key: file }
return file
// 可以 return
// 1. return file 或者 new 一個(gè) file ,接下來(lái)將上傳
// 2. return false ,不上傳這個(gè) file
},
// 上傳進(jìn)度的回調(diào)函數(shù)
onProgress(progress) {
// progress 是 0-100 的數(shù)字
console.log('progress', progress)
},
// 單個(gè)文件上傳成功之后
onSuccess(file, res) {
console.log(`${file.name} 上傳成功`, res)
},
// 單個(gè)文件上傳失敗
onFailed(file, res) {
console.log(`${file.name} 上傳失敗`, res)
},
// 上傳錯(cuò)誤,或者觸發(fā) timeout 超時(shí)
onError(file, err, res) {
// onError(file, err, res) {
console.log(`${file.name} 上傳出錯(cuò)`, err, res)
},
}
// 配置上傳視頻
editorConfig.MENU_CONF['uploadVideo'] = {
server: '{{ route("api.upload.file") }}',
fieldName: 'video',
// 單個(gè)文件的最大體積限制,默認(rèn)為 10M
maxFileSize: 5 * 1024 * 1024, // 5M
// 最多可上傳幾個(gè)文件,默認(rèn)為 5
maxNumberOfFiles: 3,
// 選擇文件時(shí)的類型限制,默認(rèn)為 ['video/*'] 。如不想限制,則設(shè)置為 []
allowedFileTypes: ['video/*'],
// 自定義上傳參數(shù),例如傳遞驗(yàn)證的 token 等。參數(shù)會(huì)被添加到 formData 中,一起上傳到服務(wù)端。
meta: {
_token: "{{ csrf_token() }}"
},
// 將 meta 拼接到 url 參數(shù)中,默認(rèn) false
metaWithUrl: false,
// 自定義增加 http header
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
// 跨域是否傳遞 cookie ,默認(rèn)為 false
withCredentials: true,
// 超時(shí)時(shí)間,默認(rèn)為 30 秒
timeout: 15 * 1000, // 15 秒
// 視頻不支持 base64 格式插入
// 上傳之前觸發(fā)
onBeforeUpload(file) {
// file 選中的文件,格式如 { key: file }
return file
// 可以 return
// 1. return file 或者 new 一個(gè) file ,接下來(lái)將上傳
// 2. return false ,不上傳這個(gè) file
},
// 上傳進(jìn)度的回調(diào)函數(shù)
onProgress(progress) {
// progress 是 0-100 的數(shù)字
console.log('progress', progress)
},
// 單個(gè)文件上傳成功之后
onSuccess(file, res) {
console.log(`${file.name} 上傳成功`, res)
},
// 單個(gè)文件上傳失敗
onFailed(file, res) {
console.log(`${file.name} 上傳失敗`, res)
},
// 上傳錯(cuò)誤,或者觸發(fā) timeout 超時(shí)
onError(file, err, res) {
console.log(`${file.name} 上傳出錯(cuò)`, err, res)
},
}
// 插入圖片
editorConfig.MENU_CONF['insertImage'] = {
onInsertedImage(imageNode) {
if (imageNode == null) return
const { src, alt, url, href } = imageNode
console.log('inserted image', src, alt, url, href)
},
checkImage: customCheckImageFn, // 也支持 async 函數(shù)
parseImageSrc: customParseImageSrc, // 也支持 async 函數(shù)
}
// 編輯圖片
editorConfig.MENU_CONF['editImage'] = {
onUpdatedImage(imageNode) {
if (imageNode == null) return
const { src, alt, url } = imageNode
console.log('updated image', src, alt, url)
},
checkImage: customCheckImageFn, // 也支持 async 函數(shù)
parseImageSrc: customParseImageSrc, // 也支持 async 函數(shù)
}
// 自定義校驗(yàn)視頻
function customCheckVideoFn(src){
if (!src) {
return
}
if (src.indexOf('http') !== 0) {
return '視頻地址必須以 http/https 開(kāi)頭'
}
return true
// 返回值有三種選擇:
// 1. 返回 true ,說(shuō)明檢查通過(guò),編輯器將正常插入視頻
// 2. 返回一個(gè)字符串,說(shuō)明檢查未通過(guò),編輯器會(huì)阻止插入。會(huì) alert 出錯(cuò)誤信息(即返回的字符串)
// 3. 返回 undefined(即沒(méi)有任何返回),說(shuō)明檢查未通過(guò),編輯器會(huì)阻止插入。但不會(huì)提示任何信息
}
// 自定義轉(zhuǎn)換視頻
function customParseVideoSrc(src) {
if (src.includes('.bilibili.com')) {
// 轉(zhuǎn)換 bilibili url 為 iframe (僅作為示例,不保證代碼正確和完整)
const arr = location.pathname.split('/')
const vid = arr[arr.length - 1]
return `<iframe src="http://player.bilibili.com/player.html?bvid=${vid}" scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true"> </iframe>`
}
return src
}
editorConfig.MENU_CONF['insertVideo'] = {
onInsertedVideo(videoNode) {
if (videoNode == null) return
const { src } = videoNode
console.log('inserted video', src)
},
checkVideo: customCheckVideoFn, // 也支持 async 函數(shù)
parseVideoSrc: customParseVideoSrc, // 也支持 async 函數(shù)
}
const editor = createEditor({
selector: '#editor-container',
// html: '<p>hello <strong>world</strong></p>',
config: editorConfig,
mode: 'default', // or 'simple'
})
const toolbarConfig = {}
// 自定義工具欄
toolbarConfig.toolbarKeys = [
'color',
'bgColor',
'clearStyle',
'justifyLeft',
'justifyRight',
'justifyCenter',
// 菜單組,包含多個(gè)菜單
{
key: 'group-image',
title: '圖片',
iconSvg: '<svg viewBox="0 0 1024 1024"><path d="M959.877 128l0.123 0.123v767.775l-0.123 0.122H64.102l-0.122-0.122V128.123l0.122-0.123h895.775zM960 64H64C28.795 64 0 92.795 0 128v768c0 35.205 28.795 64 64 64h896c35.205 0 64-28.795 64-64V128c0-35.205-28.795-64-64-64zM832 288.01c0 53.023-42.988 96.01-96.01 96.01s-96.01-42.987-96.01-96.01S682.967 192 735.99 192 832 234.988 832 288.01zM896 832H128V704l224.01-384 256 320h64l224.01-192z"></path></svg>',
menuKeys: ["insertImage", "uploadImage"]
},
{
key: 'group-video',
title: '視頻',
iconSvg: '<svg viewBox="0 0 1024 1024"><path d="M981.184 160.096C837.568 139.456 678.848 128 512 128S186.432 139.456 42.816 160.096C15.296 267.808 0 386.848 0 512s15.264 244.16 42.816 351.904C186.464 884.544 345.152 896 512 896s325.568-11.456 469.184-32.096C1008.704 756.192 1024 637.152 1024 512s-15.264-244.16-42.816-351.904zM384 704V320l320 192-320 192z"></path></svg>',
menuKeys: ["insertVideo", "uploadVideo"]
},
'undo',
'redo',
]
const toolbar = createToolbar({
editor,
selector: '#toolbar-container',
config: toolbarConfig,
mode: 'default', // 'simple' or 'default'
})
});
</script>
UploadController.php
<?php
namespace App\Http\Controllers\WangEditor;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Storage;
class UploadController extends Controller
{
public function handle(Request $request)
{
try{
$resData = [
'errno'=>0,
'data'=>[]
];
// 圖片上傳
if($file = $request->file('image')) {
// wangeditor/image/20220819/OZqFtvH6vAu52ZMFLeq3w5bfPgeHPhGyqYVUeQFr.png
$imageFile = $file->store('wangeditor/image/'.date('Ymd'));
$resData = [
'errno'=>0,
'data'=>
[
'url'=> Storage::url($imageFile), // 圖片 src ,必須
'alt'=> '', // 圖片描述文字,非必須
'href'=> '' // 圖片的鏈接,非必須
]
];
}
// 視頻上傳
if ($file = $request->file('video')){
// wangeditor/video/20220819/GOVnjEqXH1aI7hhuAskf05QR8t62vKKXPKThYDfH.mp4
$videoFile = $file->store('wangeditor/video/'.date('Ymd'));
$resData = [
'errno'=>0,
'data'=>
[
'url'=> Storage::url($videoFile), // 視頻 src ,必須
'poster'=>'' // 視頻封面圖片 url ,可選
]
];
}
return response()->json($resData);
}catch(\Exception $e){
return response()->json([
'errno'=>1,
'message'=> $e->getMessage()
]);
}
}
}
web.php
<?php
namespace App\Http\Controllers;
use Illuminate\Support\Facades\Route;
Route::middleware('auth.user')->group(function () {
// wangeditor 圖片、視頻上傳
Route::post('api-upload-file',[WangEditor\UploadController::class,'handle'])->name('api.upload.file');
});

