Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
8337e507
authored
Sep 03, 2025
by
孙美琪
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
还原富文本代码
parent
ca480512
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
185 additions
and
143 deletions
+185
-143
src/components/Editor/src/Editor.vue
+185
-143
No files found.
src/components/Editor/src/Editor.vue
View file @
8337e507
<
template
>
<
script
lang=
"ts"
setup
>
<div>
import
{
PropType
}
from
'vue'
<!-- 工具栏 -->
<Toolbar
:editor=
"editor"
/>
<!-- 编辑器主体,监听 customPaste 事件 -->
<Editor
v-model=
"content"
:defaultConfig=
"editorConfig"
:toolbarConfig=
"toolbarConfig"
@
on-created=
"onCreated"
@
custom-paste=
"customPaste"
/>
</div>
</
template
>
<
script
>
import
{
Editor
,
Toolbar
}
from
'@wangeditor/editor-for-vue'
import
{
Editor
,
Toolbar
}
from
'@wangeditor/editor-for-vue'
import
{
getUploadUrl
}
from
"@/components/UploadFile/src/useUpload"
;
import
{
i18nChangeLanguage
,
IDomEditor
,
IEditorConfig
}
from
'@wangeditor/editor'
import
{
getRefreshToken
,
getTenantId
}
from
"@/utils/auth"
;
import
{
propTypes
}
from
'@/utils/propTypes'
import
{
isNumber
}
from
'@/utils/is'
import
{
ElMessage
}
from
'element-plus'
import
{
useLocaleStore
}
from
'@/store/modules/locale'
import
{
getRefreshToken
,
getTenantId
}
from
'@/utils/auth'
import
{
getUploadUrl
}
from
'@/components/UploadFile/src/useUpload'
export
default
{
defineOptions
({
name
:
'Editor'
})
components
:
{
Editor
,
Toolbar
},
data
()
{
type
InsertFnType
=
(
url
:
string
,
alt
:
string
,
href
:
string
)
=>
void
return
{
editor
:
null
,
const
localeStore
=
useLocaleStore
()
content
:
''
,
toolbarConfig
:
{
const
currentLocale
=
computed
(()
=>
localeStore
.
getCurrentLocale
)
// 可以根据需要配置工具栏项目
i18nChangeLanguage
(
unref
(
currentLocale
).
lang
)
const
props
=
defineProps
({
editorId
:
propTypes
.
string
.
def
(
'wangeEditor-1'
),
height
:
propTypes
.
oneOfType
([
Number
,
String
]).
def
(
'500px'
),
editorConfig
:
{
type
:
Object
as
PropType
<
Partial
<
IEditorConfig
>>
,
default
:
()
=>
undefined
},
readonly
:
propTypes
.
bool
.
def
(
false
),
modelValue
:
propTypes
.
string
.
def
(
''
)
})
const
emit
=
defineEmits
([
'change'
,
'update:modelValue'
])
// 编辑器实例,必须用 shallowRef
const
editorRef
=
shallowRef
<
IDomEditor
>
()
const
valueHtml
=
ref
(
''
)
watch
(
()
=>
props
.
modelValue
,
(
val
:
string
)
=>
{
if
(
val
===
unref
(
valueHtml
))
return
valueHtml
.
value
=
val
},
{
immediate
:
true
}
)
// 监听
watch
(
()
=>
valueHtml
.
value
,
(
val
:
string
)
=>
{
emit
(
'update:modelValue'
,
val
)
}
)
const
handleCreated
=
(
editor
:
IDomEditor
)
=>
{
editorRef
.
value
=
editor
}
// 编辑器配置
const
editorConfig
=
computed
(():
IEditorConfig
=>
{
return
Object
.
assign
(
{
placeholder
:
'请输入内容...'
,
placeholder
:
'请输入内容...'
,
customAlert
:
(
s
,
t
)
=>
{
readOnly
:
props
.
readonly
,
customAlert
:
(
s
:
string
,
t
:
string
)
=>
{
switch
(
t
)
{
switch
(
t
)
{
case
'success'
:
ElMessage
.
success
(
s
);
break
case
'success'
:
case
'info'
:
ElMessage
.
info
(
s
);
break
ElMessage
.
success
(
s
)
case
'warning'
:
ElMessage
.
warning
(
s
);
break
break
case
'error'
:
ElMessage
.
error
(
s
);
break
case
'info'
:
default
:
ElMessage
.
info
(
s
)
ElMessage
.
info
(
s
)
break
case
'warning'
:
ElMessage
.
warning
(
s
)
break
case
'error'
:
ElMessage
.
error
(
s
)
break
default
:
ElMessage
.
info
(
s
)
break
}
}
},
},
autoFocus
:
false
,
autoFocus
:
false
,
scroll
:
true
,
scroll
:
true
,
MENU_CONF
:
{
MENU_CONF
:
{
uploadImage
:
{
[
'uploadImage'
]
:
{
server
:
getUploadUrl
(),
server
:
getUploadUrl
(),
// 单个文件的最大体积限制,默认为 2M
maxFileSize
:
5
*
1024
*
1024
,
maxFileSize
:
5
*
1024
*
1024
,
maxNumberOfFiles
:
100
,
// 最多可上传几个文件,默认为 100
allowedFileTypes
:
[
''
],
maxNumberOfFiles
:
10
,
// 选择文件时的类型限制,默认为 ['image/*'] 。如不想限制,则设置为 []
allowedFileTypes
:
[
'image/*'
],
// 自定义增加 http header
headers
:
{
headers
:
{
Accept
:
'*'
,
Accept
:
'*'
,
Authorization
:
'Bearer '
+
getRefreshToken
(),
Authorization
:
'Bearer '
+
getRefreshToken
(),
// 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:Editor 无法方便的刷新访问令牌
'tenant-id'
:
getTenantId
()
'tenant-id'
:
getTenantId
()
},
},
timeout
:
15
*
1000
,
// 超时时间,默认为 10 秒
timeout
:
15
*
1000
,
// 15 秒
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
fieldName
:
'file'
,
fieldName
:
'file'
,
onBeforeUpload
(
file
)
{
base64LimitSize
:
Infinity
,
// 允许粘贴图走 base64
// base64LimitSize: 10 * 1024 * 1024,
// 上传之前触发
onBeforeUpload
(
file
:
File
)
{
// console.log(file)
return
file
return
file
},
},
onProgress
(
progress
)
{
// 上传进度的回调函数
onProgress
(
progress
:
number
)
{
// progress 是 0-100 的数字
console
.
log
(
'progress'
,
progress
)
console
.
log
(
'progress'
,
progress
)
},
},
onSuccess
(
file
,
res
)
{
onSuccess
(
file
:
File
,
res
:
any
)
{
console
.
log
(
'onSuccess'
,
file
,
res
)
console
.
log
(
'onSuccess'
,
file
,
res
)
},
},
onFailed
(
file
,
res
)
{
onFailed
(
file
:
File
,
res
:
any
)
{
ElMessage
.
error
(
res
.
message
)
alert
(
res
.
message
)
console
.
log
(
'onFailed'
,
file
,
res
)
},
},
onError
(
file
,
err
,
res
)
{
onError
(
file
:
File
,
err
:
any
,
res
:
any
)
{
ElMessage
.
error
(
err
.
message
)
alert
(
err
.
message
)
console
.
error
(
'onError'
,
file
,
err
,
res
)
},
},
customInsert
(
res
,
insertFn
)
{
// 自定义插入图片
customInsert
(
res
:
any
,
insertFn
:
InsertFnType
)
{
insertFn
(
res
.
data
,
'image'
,
res
.
data
)
insertFn
(
res
.
data
,
'image'
,
res
.
data
)
}
}
},
},
uploadVideo
:
{
[
'uploadVideo'
]
:
{
server
:
getUploadUrl
(),
server
:
getUploadUrl
(),
// 单个文件的最大体积限制,默认为 10M
maxFileSize
:
10
*
1024
*
1024
,
maxFileSize
:
10
*
1024
*
1024
,
// 最多可上传几个文件,默认为 100
maxNumberOfFiles
:
10
,
maxNumberOfFiles
:
10
,
// 选择文件时的类型限制,默认为 ['video/*'] 。如不想限制,则设置为 []
allowedFileTypes
:
[
'video/*'
],
allowedFileTypes
:
[
'video/*'
],
// 自定义增加 http header
headers
:
{
headers
:
{
Accept
:
'*'
,
Accept
:
'*'
,
Authorization
:
'Bearer '
+
getRefreshToken
(),
Authorization
:
'Bearer '
+
getRefreshToken
(),
// 使用 getRefreshToken() 方法,而不使用 getAccessToken() 方法的原因:Editor 无法方便的刷新访问令牌
'tenant-id'
:
getTenantId
()
'tenant-id'
:
getTenantId
()
},
},
timeout
:
15
*
1000
,
// 超时时间,默认为 30 秒
timeout
:
15
*
1000
,
// 15 秒
// form-data fieldName,后端接口参数名称,默认值wangeditor-uploaded-image
fieldName
:
'file'
,
fieldName
:
'file'
,
onBeforeUpload
(
file
)
{
// 上传之前触发
onBeforeUpload
(
file
:
File
)
{
// console.log(file)
return
file
return
file
},
},
onProgress
(
progress
)
{
// 上传进度的回调函数
onProgress
(
progress
:
number
)
{
// progress 是 0-100 的数字
console
.
log
(
'progress'
,
progress
)
console
.
log
(
'progress'
,
progress
)
},
},
onSuccess
(
file
,
res
)
{
onSuccess
(
file
:
File
,
res
:
any
)
{
console
.
log
(
'onSuccess'
,
file
,
res
)
console
.
log
(
'onSuccess'
,
file
,
res
)
},
},
onFailed
(
file
,
res
)
{
onFailed
(
file
:
File
,
res
:
any
)
{
ElMessage
.
error
(
res
.
message
)
alert
(
res
.
message
)
console
.
log
(
'onFailed'
,
file
,
res
)
},
},
onError
(
file
,
err
,
res
)
{
onError
(
file
:
File
,
err
:
any
,
res
:
any
)
{
ElMessage
.
error
(
err
.
message
)
alert
(
err
.
message
)
console
.
error
(
'onError'
,
file
,
err
,
res
)
},
},
customInsert
(
res
,
insertFn
)
{
// 自定义插入图片
customInsert
(
res
:
any
,
insertFn
:
InsertFnType
)
{
insertFn
(
res
.
data
,
'mp4'
,
res
.
data
)
insertFn
(
res
.
data
,
'mp4'
,
res
.
data
)
}
}
}
}
},
},
uploadImgShowBase64
:
true
uploadImgShowBase64
:
true
},
},
editorConfig
:
{
props
.
editorConfig
||
{}
placeholder
:
'请输入内容...'
)
}
})
}
},
methods
:
{
// 编辑器创建后保存实例
onCreated
(
editorInstance
)
{
this
.
editor
=
editorInstance
},
// 自定义粘贴事件处理
const
editorStyle
=
computed
(()
=>
{
async
customPaste
(
editor
,
event
)
{
return
{
const
clipboardData
=
event
.
clipboardData
height
:
isNumber
(
props
.
height
)
?
`
${
props
.
height
}
px`
:
props
.
height
if
(
!
clipboardData
)
{
return
true
// 如果没有剪贴板数据则使用默认
}
}
})
// 获取粘贴的 HTML 和 RTF 内容
// 回调函数
const
html
=
clipboardData
.
getData
(
'text/html'
)
const
handleChange
=
(
editor
:
IDomEditor
)
=>
{
const
rtf
=
clipboardData
.
getData
(
'text/rtf'
)
emit
(
'change'
,
editor
)
}
// 只有在 HTML 和 RTF 同时存在(通常是来自 Word/WPS)时才自定义处理
if
(
html
&&
rtf
)
{
// 使用 DOMParser 解析 HTML
const
parser
=
new
DOMParser
()
const
doc
=
parser
.
parseFromString
(
html
,
'text/html'
)
const
imgTags
=
doc
.
querySelectorAll
(
'img'
)
if
(
imgTags
.
length
>
0
)
{
// 从剪贴板项中提取文件(图片)对象列表
const
items
=
clipboardData
.
items
const
files
=
[]
for
(
const
item
of
items
)
{
if
(
item
.
kind
===
'file'
)
{
const
file
=
item
.
getAsFile
()
if
(
file
)
{
files
.
push
(
file
)
}
}
}
// 依次处理每个
<
img
>
标签
// 组件销毁时,及时销毁编辑器
let
fileIndex
=
0
onBeforeUnmount
(()
=>
{
for
(
const
img
of
imgTags
)
{
const
editor
=
unref
(
editorRef
.
value
)
const
src
=
img
.
getAttribute
(
'src'
)
||
''
// 如果 src 是本地文件路径(file:// 开头)
if
(
src
.
startsWith
(
'file://'
))
{
if
(
fileIndex
<
files
.
length
)
{
const
file
=
files
[
fileIndex
++
]
// 通过指定的上传接口上传文件
const
uploadUrl
=
getUploadUrl
()
// 请实现该函数返回上传端点
const
formData
=
new
FormData
()
formData
.
append
(
'file'
,
file
)
try
{
const
response
=
await
fetch
(
uploadUrl
,
{
method
:
'POST'
,
body
:
formData
})
const
data
=
await
response
.
json
()
// 假设返回的 JSON 中包含字段 `url` 为图片线上地址
const
url
=
data
.
url
if
(
url
)
{
img
.
setAttribute
(
'src'
,
url
)
}
else
{
// 上传成功但无 URL,移除图片
img
.
remove
()
}
}
catch
(
err
)
{
console
.
error
(
'图片上传失败:'
,
err
)
img
.
remove
()
}
}
else
{
// 剪贴板文件不够用,移除此
<
img
>
img
.
remove
()
}
}
}
}
// 最终 HTML 内容
// 销毁,并移除 editor
const
finalHtml
=
doc
.
body
.
innerHTML
editor
?.
destroy
()
// 将处理后的内容插入编辑器
})
editor
.
dangerouslyInsertHtml
(
finalHtml
)
// 阻止默认粘贴(已由我们手动插入)
event
.
preventDefault
()
return
false
}
// 其他情况使用默认粘贴行为
const
getEditorRef
=
async
():
Promise
<
IDomEditor
>
=>
{
return
true
await
nextTick
()
}
return
unref
(
editorRef
.
value
)
as
IDomEditor
}
}
}
defineExpose
({
getEditorRef
})
</
script
>
</
script
>
<
template
>
<div
class=
"border-1 border-solid border-[var(--tags-view-border-color)] z-10"
>
<!-- 工具栏 -->
<Toolbar
:editor=
"editorRef"
:editorId=
"editorId"
class=
"border-0 b-b-1 border-solid border-[var(--tags-view-border-color)]"
/>
<!-- 编辑器 -->
<Editor
v-model=
"valueHtml"
:defaultConfig=
"editorConfig"
:editorId=
"editorId"
:style=
"editorStyle"
@
on-change=
"handleChange"
@
on-created=
"handleCreated"
/>
</div>
</
template
>
<
style
src=
"@wangeditor/editor/dist/css/style.css"
></
style
>
<
style
src=
"@wangeditor/editor/dist/css/style.css"
></
style
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment