一、背景
在本次公司的开发任务中,偶然接触到了畅写office的文档在线编辑集成开发,在开发中遇到点问题,觉得这个东西挺有趣的,写此文章保存开发心得。
二、前期准备
文档在线编辑功能只有一个api.文件,保存在我的下载里面,公司中使用的是layui框架,开发文档地址:开发文档地址 三、代码展示
fileEdit.js
/**
* author:fzt
* date:2022-11-16
* describe:File viewing and editing
*/
let baseUrl = "/api";
layui.extend({
mapletrutil: '../../../../../static/js/util/mapletrutil',
fileService: '../../../../../static/js/components/fileUpload/fileService',
/* dfExtendUtil: '../../../../../static/js/tdmproject/dftdm/public/dfExtendUtil',*/
});
layui.define(['layer','mapletrutil','fileService', 'jquery'],function (exports) {
let layer = layui.layer;
let $ = layui.$;
let mapletrutil = layui.mapletrutil;
let fileService = layui.fileService;
/*let dfExtendUtil = layui.dfExtendUtil;*/
//定义参数:
//要打开的文件类型,具体格式见下方注释
let fileType = "";
//定义是否可编辑,默认为false
let isEdit = "view";
//定义文档id
let fileId = "";
//指定打开文档的唯一ID,畅写Office以key为依据决定是否加载上次编辑过程产生的缓存记录。
let key = "";
//标题,可使用文档名称
let title = "";
//指定要打开的文档的地址
let url = "";
//指明文档类型,文字处理:"word"、电子表格:"cell"、演示文稿:"slide"
let documentType = "";
//回调接口地址
let callbackUrl = "http://tdm-backend-sit.tc.dfmc.com.cn/online/callback";
/*
* fileType
* 描述 指明要打开的具体文件类型
* 数据类型string
* 参数
* 文字处理 (.doc, .docm, .docx, .dot, .dotm, .dotx, .epub, .fodt, .htm, .html, .mht, .odt, .ott, .pdf, .rtf, .txt,.xps, wps)
* 电子表格 (.csv, .fods, .ods, .ots, .xls, .xlsm, .xlsx, .xlt, .xltm, .xltx,.et) 演示文稿
*(fodp, .odp, .otp, .pot, .potm, .potx, .pps, .ppsm, .ppsx, .ppt, .pptm, .pptx,dps)
* */
let wordType = ["doc","docm","docx","dot","dotm","dotx","epub","fodt","htm","html","mht","odt","ott","pdf","rtf","txt","xps","wps"];
let excelType = ["csv","fods","ods","ots","xls","xlsm","xlsx","xlt","xltm","xltx","et"];
let pptType = ["fodp","odp","otp","pot","potm","potx","pps","ppsm","ppsx","ppt","pptm","pptx","dps"];
let init = function(){
let param = getUrlParam();
fileId = param.get("fileId");
let edit = param.get("isEdit");
let fileInfo = fileService.getFileInfoByFileIds(fileId);
let metaUrl = fileService.getMetaUrl();
key=fileId;
if(edit=="true"){
isEdit="edit";
}
if(!fileId||fileId==""||fileId==null){
layer.msg("文件ID不能为空",{'icon':5});
return false;
}
title = fileInfo.originalname;
let fileSuffix = fileInfo.fileSuffix;
if(!fileSuffix||!fileSuffix==""|!fileSuffix==undefined||!fileSuffix==null||!fileSuffix.indexOf(".")==-1){
fileType= fileSuffix.substr(fileSuffix.lastIndexOf(".") + 1);
}
if(documentType==""){
for(let i=0;i<wordType.length;i++) {
if(fileType==wordType[i]){
documentType=="word";
break;
}
}
}
if(documentType=="") {
for (let i = 0; i < excelType.length; i++) {
if (fileType == excelType[i]) {
documentType == "cell";
break;
}
}
}
if(documentType=="") {
for (let i = 0; i < pptType.length; i++) {
if (fileType == pptType[i]) {
documentType == "slide";
break;
}
}
}
let fileServerUri = fileInfo.fileServerUri;
url=fileServerUri+"/file/down?metaUrl="+metaUrl+"&fileId="+fileId;
debugger;
let editor_SDK = new CXO_API.CXEditor("CXO_Editor_SDK",{
"document": { //文档参数集
"fileType": fileType, //指明要打开的文件类型,例如"xlsx"、"pptx"
"key": key, //文档唯一ID
"title": title, //文档标题名称
"usePdfjs":true,//是否使用pdf.js插件打开pdf类型文档
"url": url //文档存放路径
},
"documentType": documentType,//指明文档类型,文字处理:"word"、电子表格:"cell"、演示文稿:"slide"
"height": "100%",//设置文档编辑器在浏览器中的高度,默认"100%"
"type": "desktop",//设置平台类型:PC端"desktop",手机端"mobile"
"width": "100%",//设置文档编辑器在浏览器中的宽度,默认"100%"
"token":"2333333333333331", //设置访问服务token
"editorConfig": {
"callbackUrl": callbackUrl,//回调接口URL
"mode": isEdit,//指定文档打开模式,默认值"edit"。只读模式"view",编辑模式"edit"
"limitEditMode":"nolimit", //部分编辑模式,"nolimit"无限制,"ctctrl"只能对未设置只读的内容域进行编辑
"customization": {
"chat": true, //是否开启IM,默认true
"disabledAddTypeData":false,
"disabledDeleteTypeData":false,
"leftMenu": true,//是否显示左侧菜单,默认true
"rightMenu": true,//是否显示右侧菜单,默认true
"toolbar": true,//是否显示顶部菜单栏,默认true
"statusBar": true,//是否开启状态栏,默认true
"autosave": true,//是否开启自动保存,默认true
"logo":{
"image":""//image为空隐藏logo,授权后也可以替换logo地址
},
"about":false,//设置是否显示关于畅写,默认true不隐藏
"forcesave":true,//是否开启强制保存,默认false,开启后用户点击保存直接触发回调接口
"displayTitle":true,//是否显示标题,默认true
"plugins":true,//是否显示扩展菜单,默认true
"reviewDisplay":'markup',//修订显示模式,"final"最终状态、"markup"标记状态、"original"原始状态
"hideDownLoadOnMobile":false, //移动端是否隐藏下载按钮
"isCompareWithUrl":false,//开启在文档加载完成后与参数compareWithUrl指向的文档进行比较
"compareWithUrl":'http://exampke.com/aa.docx', //要进行比较的目标文档地址(该地址要能下载到文档)
"goback": {
"blank": true,//是否在新窗口打开
"text": "Go to Documents",//显示文字
"url": "https://example.com"//回跳的url地址
},
"watermark":{
"transparent" : 0.5, //透明度,默认为 0.3,范围是0-1
"type" : "rect",//水印类型,"rect"为矩形,"ellipse"为椭圆
"width" : 150,//水印宽度,单位mm
"height" : 150,//水印高度,单位mm
"rotate" : 0,//水印旋转角度,360为旋转一周,正数为顺时针旋转,负数逆时针旋转
"margins" : [ 10, 2, 5, 0 ],//段落距边框的留白区域[l,t,r,b]
"fill" : [255, 0, 0],//水印填充颜色
"stroke-width" : 1,//描边,宽度单位mm
"stroke" : [0, 0, 255],//描边的颜色
"align" : 1,// 垂直方向文本对齐方式,4顶端对齐,1居中对齐,0底端对齐
"paragraphs" : [ {//整个水印段落设置
"align" : 0,//水平方向文本对齐方式,1左对齐,2居中对齐,0右对齐
"fill" : [255, 255, 0],//段落背景色
"linespacing" : 10.0, //行间距(倍数)
"runs" : [ //可以换行,不同行可以设置不同样式,目前写在前面的元素会后显示,写在后面的会先显示,也就是说显示结果和书写顺序成倒序
{
"text" : "%user_name%!",//文字内容,其中%user_name%会被替换为当前用户的名字
"fill" : [0, 255, 0],//文字颜色
"font-family" : "simsun",//文字使用的字体属性
"font-size" : 10,//文字字号
"bold" : true,//是否加粗
"italic" : false,//是否倾斜
"strikeout" : false,//是否加删除线
"underline" : false//是否加下划线
},
{"text" : "%<br>%"},//新起一行,需单独书写,不可和其它文本内容写到一起
{"text" : "textcontent"}//另一行内容设置
]
}]
},
"zoom": 100//指定文档缩放大小,0-100,文字处理和演示文稿可以设置为-1(文档适应页面)或-2(文档页面适应编辑器页面),默认值100
}
},
});
let forceSave = function(){
editor_SDK.forceSave();
};
let destroyEditor = function(){
editor_SDK.destroyEditor();
};
let selected = function(){
editor_SDK.getDocumentContent({
"object":"text", //操作文本内容,必填项
"type":"selected", //操作类型是选择,必填项
});
};
let onGetDocumentContent = function(event){
console.log(event.data);
};
let hyperlink = function(){
editor_SDK.setDocumentContent({
"object":"hyperlink", //表示是超链接对象,必填项
"type":"insert", //表示是插入操作,目前只支持插入,必填项
"link":"https://www.baidu.com", //超链接的链接地址,必填项
"text":"百度浏览器", //在文档中显示的内容,如果不设置则以超链接的文字内容做为内容插入
"tip":"tip", //鼠标放到超链接时显示的提示信息
});
};
let text = function(){
editor_SDK.setDocumentContent({
"object":"text", //表示是文字,必填项
"type":"insert", //表示是插入操作必填项
"text":"新文本", //插入的文字内容,必填项
});
};
let bookmark = function () {
editor_SDK.setDocumentContent({
"object":"bookmark", //表示操作对象为书签,必填项
"type":"insert", //表示操作行为是插入,必填项
"name":"book", //书签的名字,书签名字只能为数字、字母及下划线,且以字母开头,必填项
"value":"书签内容" //在文档中书签位置要插入的文本内容
});
};
let getAllbookmark = function () {
editor_SDK.getDocumentContent({
"object":"bookmark", //表示操作对象为书签,必填项
"type":"names", //提取书签名字,必填项
});
};
let getProperty = function () {
editor_SDK.getDocumentContent({
"object":"content", //表示操作对象为内容域,必填项
"type":"property", //提取内容域属性,必填项
"name":"", //提取指定标签名称内容域的属性,name和id互斥,name优先级高于id,如name及id都未填写,则获取当前文档全部内容域
"id":"" //提取指定id内容域的属性,name和id互斥,name优先级低于id,如name及id都未填写,则获取当前文档全部内容域
});
};
let getText = function () {
editor_SDK.getDocumentContent({
"object":"content", //表示操作对象为内容域,必填项
"type":"text", //提取类型为文本,必填项
"name":"", //提取指定标签名称内容域的属性,name和id互斥,name优先级高于id,如name及id都未填写,则获取当前文档全部内容域
"id":"" //提取指定id内容域的属性,name和id互斥,name优先级低于id,如name及id都未填写,则获取当前文档全部内容域
});
};
let getAll = function () {
editor_SDK.getDocumentContent({
"object":"content", //表示操作对象为内容域,必填项
"type":"all", //提取类型,必填项 all全部提取 table提取表格 image提取图片
"name":"con", //内容域的标签,name和id互斥,name优先级高于id,如name及id都未填写,则获取当前文档全部内容域
"id":"" //内容域的id,name和id互斥,name优先级低于id,如name及id都未填写,则获取当前文档全部内容域
});
};
let replace = function () {
editor_SDK.setDocumentContent({
"object":"content", //表示操作对象为内容域,必填项
"type":"replace", //表示操作行为是替换内容,必填项
"name":"con", //内容域的标签,可以是英文数字,优先级高于ID
"id":"", //内容域的ID,name和id必须有一
"value":"新内容" //替换完成后内容域内的文字内容
});
};
let replaceall = function () {
editor_SDK.setDocumentContent({
"object":"content", //表示操作对象为内容域,必填项
"type":"replaceAll", //表示操作行为是替换内容,必填项
"value":[{
"name":"con", //内容域标签
"id":"", //内容域ID
"value":"新内容1" //内容域内容
}]
});
};
let appendAll = function () {
editor_SDK.setDocumentContent({
object: 'content',
type: 'appendAll',
value:[
{
name : 'xxxxx',
id : '0000',
text : '追加的内容'
}
]
});
};
let select = function () {
editor_SDK.setDocumentContent({
"object":"content", //表示操作对象为内容域,必填项
"type":"select", //表示是选中操作,必填项
"id":"370312776" //内容域的id
});
};
let clear = function () {
editor_SDK.setDocumentContent({
"object":"content", //表示操作对象为内容域,必填项
"type":"clear", //表示是清除内容域内容,必填项
"name":"con" , //内容域的标签
"id":"" //内容域的id
});
};
/*let editor= new CXO_API.CXEditor("CXO_Editor_SDK", {
"events": {
"forceSave":forceSave,
"destroyEditor":destroyEditor,
"selected":selected,
"hyperlink":hyperlink,
"bookmark":bookmark,
"getAllbookmark":getAllbookmark,
"getProperty":getProperty,
"getText":getText,
"getAll":getAll,
"replace":replace,
"replaceall":replaceall,
"select":select,
"clear":clear
},
});*/
editor_SDK.setDocumentContent({
object: 'content',
type: 'dropdownValue',
value:[
{
name : 'xxxxx',
id : '0000',
value : 'v1' //要选中选项的值(value)
}
]
})
editor_SDK.setDocumentContent({
object: 'content',
type: 'comboboxValue',
value:[
{
name : 'xxxxx',
id : '0000',
value : 'v1' //要选中选项的值(value)
}
]
});
editor_SDK.setDocumentContent({
object: 'content',
type: 'checkboxValue',
value:[
{
name : 'xxxxx',
id : '0000',
value : true //true勾选 false不勾选
}
]
});
editor_SDK.setDocumentContent({
object: 'content',
type: 'pictureValue',
value:[
{
name : 'xxxxx',
id : '0000',
value : '' //可访问图片网络地址 如:http://image.xxxx.com/a.jpg
}
]
});
editor_SDK.setDocumentContent({
object: 'content',
type: 'empty', //表示是清空内容域内容,必填项
id: '0000',
name: 'xxxxx',
})
};
getUrlParam = function () {
let data = new Map();
let url=window.location.search; //获取url中"?"符后的字串
if(url.indexOf("?")!=-1){
let result = url.substr(url.indexOf("?")+1);
/*result = url.substr(url.indexOf("=")+1);*/
/*result = result.substr(url.indexOf("&")+1);*/
let params=new URLSearchParams(result);
data.set("fileId",params.get("fileId"));
data.set("isEdit",params.get("isEdit"));
}
return data;
};
let getFileInfoByFileId = function(fileId) {
let fileInfo = {};
mapletrutil.ajax({
type: 'post',
url: baseUrl+'/fileinfo/find/' + fileId,
async: false,
success: function(result) {
if (result.status == 0) {
fileInfo = result.data;
} else {
layer.msg("获取文件信息失败,失败原因:" + result.msg, {
icon: 5
});
}
},
error: function(XMLHttpRequest, textStatus,
errorThrown) {
layer.open({
title: '获取文件信息失败',
content: "请求异常,错误码:" + XMLHttpRequest.status,
icon: 5
});
}
});
return fileInfo;
};
let fileChecked = {
init:init
}
exports("fileChecked", fileChecked);
});
fileEdit.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<script src="../../../../../lib/layui-v2.6.7/layui.js" type="text/javascript" charset="utf-8"></script>
<style>
html {
height: 100%;
width: 100%;
}
body {
background: #fff;
color: #333;
font-family: Arial, Tahoma,sans-serif;
font-size: 12px;
font-weight: normal;
height: 100%;
margin: 0;
padding: 0;
text-decoration: none;
}
</style>
<script type = "text/javascript" src="http://10.4.10.20:8080/web-apps/apps/api/documents/api.js"></script>
<!--<script type = "text/javascript" src="../../../../../static/js/tdmproject/dftdm/public/api.js"></script>-->
<title>Document</title>
</head>
<body>
<div id = "CXO_Editor_SDK" ></div>
<script>
layui.extend
({
fileChecked:"../../../../../static/js/tdmproject/dftdm/fileChecked/fileChecked"
});
layui.use('fileChecked', function(){
var fileChecked = layui.fileChecked;
fileChecked.init();
});
</script>
</body>
</html>