onlyoffice 音转文插件开发
由于公司最近的业务需求,需要前端传入word模版的时候保留样式,并且需要支持音转文实时插入(一想到这就一头大)
word 导入到网页中保留样式(想过富文本的方法)
docx-preview 这个js库,感觉挺不错, word支持度测试验证:
- 文字颜色,正常
- 文字背景色,正常
- 多种字号大小,正常
- 有序列表、无序列表,正常
- 下标、上标,正常
- word中的图片转换html后,正常(可选base64 和 blob 方式)
- 表格,正常
- 合并单元格,正常
- 带背景色的、代码高亮,正常
docx-preview.js 更好,其他的没他这么好的效果
对格式支持较差,字号、右对齐、居中等不支持;文字颜色、文字背景色,不支持 给个体验地址体验一下docx-preview.js和mammoth.js word 转html
转换的预览效果
无奈都达不到要求,中间已经要放弃了,因为word->html的转化始终是一个痛点。
上周看到了onlyoffice插件形式开发,决定再试一次👏👏
onlyoffice插件开发
大致而言 onlyoffice插件开始指南 这上面会告诉你怎么去开发一款自己的插件
开发前置需要的环境配置
在社区看到了程序员未央-# 如何构建基于 Onlyoffice 的私有云 Office 办公体验的这篇文章,docker以及在vue中使用onlyoffice
在这里我们只需要前面的docker 就可以了,后面的就在插件里面编写
一个插件的基本配置文件
. ├── config.json # 插件配置文件
├── icon.png # 插件图标
├── icon@2x.png
├── index.html # 插件入口文件
├── main.js # 插件主程序入口文件
└── translations # 国际化配置
└── zh-CN.json
这里面是我的index.html文件
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>开始填充</title>
<script src="scripts/jquery.min.js"></script>
<script type="text/javascript" src="scripts/plugins.js"></script>
<script type="text/javascript" src="scripts/plugins-ui.js"></script>
<link rel="stylesÏheet" href="resources/css/plugins.css">
<link rel="stylesheet" href="resources/css/plugin_style.css">
<script type="text/javascript" src="scripts/js-cookie.js"></script>
<!-- <script>
function setCurrentCookies() {
Cookies.set('trailId','170363935175654303000000002',{path:'/'})
Cookies.set('onlyofficeHost','192.168.0.167',{path:'/'})
Cookies.set('transliterationList', '[{"label":"首席审理员","value":"01"},{"label":"审理员","value":"02"},{"label":"助理审理员","value":"03"},{"label":"记录员","value":"04"},{"label":"被申请人","value":"05"},{"label":"被申请人代理人","value":"06"},{"label":"申请人","value":"07"},{"label":"申请人代理人","value":"08"},{"label":"证人","value":"09"},{"label":"证人","value":"10"}]')
}
setCurrentCookies()
</script> -->
<script type="text/javascript" src="scripts/webscoket.js"></script>
<script type="text/javascript" src="scripts/helloworld.js"></script>
<!-- <script type="text/javascript" src="scripts/translation.js"></script> -->
</head>
<body id="body"></body>
<div id="translationBox">
</div>
</html>
然后我是直接在vscode 上 运行了liveserver插件
音转文部分的代码
;(function (window, undefined) {
var websocketHandler = ''
function GetQueryString(key) {
var url = window.location.href //首先获取url
if (url.indexOf('?') != -1) {
//判断是否有参数
var strSub = null
var str = url.split('?') //根据?截取url
var strs = str[1].split('&') //str[1]为获取?号后的字符串,并且根据&符号进行截取,得到新的字符串数组,这个字符串数组中有n个key=value字符串
var value = ''
for (var i = 0; i < strs.length; i++) {
//遍历字符串数组
strSub = strs[i].split('=') //取第i个key=value字符串,并用“=”截取获得新的字符串数组 这个数组里面第0个字符是key,第1个字符value
if (strSub[0] == key) {
//判断第0个字符与该方法的参数key是否相等,如果相等 则返回对应的值value。
value = strSub[1]
}
}
return value
}
return ''
}
function returnCurrentSpeaker(speaker) {
let translationsList = JSON.parse(Cookies.get('transliterationList'))
let speakerRole = ''
translationsList.map((item) => {
if (item.value == speaker) {
speakerRole = item.label
}
})
return speakerRole
}
// window.Asc.plugin.name = '停止转写'
// console.log(window.Asc.plugin)
window.Asc.plugin.init = function () {
localStorage.setItem('instertElement', 0)
// Cookies.set('instertElement', 0, { path: '/', domain: window.location.hostname })
websocketHandler = new createWebSocket(function () {
return `wss://${Cookies.get('onlyofficeHost')}:30070/trailv2/api/voice/msg?trailId=${Cookies.get('trailId')}`
})
// console.log(window.Asc.plugin)
// 设置接收到 WebSocket 消息时的回调函数
websocketHandler.onMessageCallback = function (data) {
handleWebSocketMessage(data)
}
function handleWebSocketMessage(data) {
// 根据你的数据结构更新 OnlyOffice 文档
if (data.msgType === '1002') {
window.Asc.scope.text = returnCurrentSpeaker(data.speaker) + ':' + data.result
window.Asc.scope.bookName = data.index.toString()
window.Asc.scope.isInsert = data.sentenceEndTime
window.Asc.scope.replaceStringText = '[正在转写中....]'
//这个是第一个可行的方案
if (data.result) {
// if (!window.Asc.scope.isInsert) {
// var oProperties = {
// searchString: Asc.scope.replaceStringText,
// replaceString: data.result,
// matchCase: true,
// }
// //execute method for search and replace
// window.Asc.plugin.executeMethod('SearchAndReplace', [oProperties])
// } else {
// window.Asc.plugin.callCommand(function () {
// console.log('this', this)
// // console.log(window.Asc.scope)
// var oDocument = Api.GetDocument()
// var oParagraph = Api.CreateParagraph()
// oParagraph.AddText(Asc.scope.text)
// oDocument.InsertContent([oParagraph])
// // Asc.scope.TextArray.push(Asc.scope.bookName)
// // Asc.scope.replaceStringText = Asc.scope.text
// }, false)
// }
//现在实验第二种方案
window.Asc.plugin.callCommand(
function () {
var oDocument = Api.GetDocument()
var oRun = Api.CreateRun()
var oNormalStyle = oDocument.GetDefaultStyle('paragraph')
/// 将 index 转换为字符串,确保作为标识符使用
var uniqueTag = 'Tag_' + Asc.scope.bookName
// 标识是否找到了相同标识符的书签
var foundBookmark = false
var instertElement = 0
// 遍历文档中的所有元素
for (let i = 0; i < oDocument.GetElementsCount(); i++) {
var oElement = oDocument.GetElement(i)
// 判断元素是否存在并且是段落类型
if (oElement) {
var oParagraph = oElement
// 获取段落的文本内容
var paragraphText = oParagraph.GetText()
// 检查文本内容中是否包含标识符
if (paragraphText.indexOf(Asc.scope.replaceStringText) !== -1) {
// 删除旧的段落
oParagraph.RemoveAllElements()
// 创建新的段落并插入
// var oNewParagraph = Api.CreateParagraph()
if (Asc.scope.isInsert === 0) {
oParagraph.SetStyle(oNormalStyle)
oParagraph.AddText(Asc.scope.text + ' ')
oRun.AddText(Asc.scope.replaceStringText)
oRun.SetColor(255, 111, 61)
oRun.SetBold(true)
oRun.SetHighlight('darkRed')
oParagraph.AddElement(oRun)
} else {
oParagraph.SetStyle(oNormalStyle)
oParagraph.AddText(Asc.scope.text)
}
localStorage.setItem('instertElement', i)
// Cookies.set('instertElement',i,{path:'/'})
oDocument.InsertContent([oParagraph])
// 标记找到标识符并执行操作
foundBookmark = true
break
}
}
}
console.log('foundBookmark', foundBookmark)
// 如果没有找到相同标识符的段落,创建一个新的段落并插入
if (!foundBookmark) {
var oParagraph = Api.CreateParagraph()
oParagraph.SetStyle(oNormalStyle)
oParagraph.AddText(Asc.scope.text + ' ')
oRun.AddText(Asc.scope.replaceStringText)
oRun.SetColor(255, 111, 61)
oRun.SetBold(true)
oRun.SetHighlight('darkRed')
// 插入新段落
oParagraph.AddElement(oRun)
if (localStorage.getItem('instertElement') && localStorage.getItem('instertElement') != 0) {
console.log(localStorage.getItem('instertElement'))
oDocument.AddElement(Number(localStorage.getItem('instertElement')) + 1, oParagraph)
} else {
oDocument.InsertContent([oParagraph])
}
}
},
false,
true,
function () {}
)
}
}
}
}
window.Asc.plugin.button = function (id) {
console.log(id)
}
})(window, undefined)
当然在写这一部分代码的时候遇到过几个问题
- 插件怎么和当前页面通信?
- 怎么保证音转文可以一边插入一边修改?
- 怎么保证我不想在当前位置插入时,可以让插件在新的位置插入?
- 怎么让插入的那一段不可编辑?
前面的3个问题都已经解决了,但是第4个问题还没有想到好的解决方案,后续解决了有更新的话再来补充吧
插件怎么和当前页面通信?
当时想到的是通过localStorage或者sessionStorage来进行通信,理想很美好,现实很骨感啊!!!!
可以看到onlyoffice和前端的项目地址 不同源。。。。难搞
后面想到了用Cookies的方式去做,因为cookie在域名相同端口不同的情况下是可以设置path的,我这聪明的大脑一下就有了思路,我直接把path 设置到根域名下不就可以了
第二和第三个问题在代码中已经写了