# 金格外部中间件JSAPI实现逻辑

# ⅠPDF实现逻辑

# 控件加载要求

页面加载的时候需要初始化一个id为officeEditorFrame的ifram标签,并且加载basePdf.js,basePdf.js需要和iframe在同一个页面

示例:

<iframe id="officeEditorFrame" name="officeEditorFrame" 
         frameborder="0" height="100%" width="100%" scrolling="no" marginheight="0" marginwidth="0"></iframe>
 <script src="./basePdf.js"></script>

# 代码加载顺序

basePdf.js>pdf_kinggrid_brower.js

basePdf.js中通过懒加载的方式引入对应插件所需要的js

# 加载流程

1699961190096.png

# 各文件的作用

1、pdf.html:控件挂载点

2、pdfBrowser.css:样式文件

3、basePdf.js:调用pdf控件的基础js,提供了业务方法的调用

4、pdf.js:用于postMessage通信,根据插件类型懒加载对应插件所需的js,并返回加载消息给basePdf.js

5、enum.js:不同控件需要的js枚举

6、utils.js:实现一些内部所需要的工具方法

7、pdf_kinggrid_browse.js:金格pdf控件jsapi原型对象js

8、pdf_invoke.js:金格pdf控件jsapi双向通信封装的方法

9、tgPdf.js:金格控件jsapi插件端执行的js

# 实现逻辑

1、方法入口为basePdf.js中的initPdf()方法

	/**
     * 初始化控件
     * @param {*} params 
     */
    initPdf: async function (params, callBack) {
        //老控件需求
        PdfUtils.setEditorFrameOptions(params);
        // callback执行顺序 1: parasm.callback 2:第二个参数callback
        const data = await this.showPdfDiv(params.fileType, params.isFullSize, params.callback, params);
        if (typeof callBack == 'function') {
            callBack(data);
        }
        return data;
    }

2、initPdf()调用basePdf.js文件的showPdfDiv()方法

1).判断插件类型,根据环境判断当前插件是否可用(jsapi支持谷歌内核107以上,不支持mac环境、windows环境火狐浏览器不支持)

2).判断是否立即加载控件

3).调用loadHtmlForPlugin()加载pdf.html页面,懒加载js(basePdf.js与pdf.html中的pdf.js通过postMessage进行通信)pdf.html加载完成后basePdf.js会通过postMessage与pdf.js进行通信

关键代码:

basePdf.js

if (iframeDom.attachEvent) {
	iframeDom.attachEvent("onload", async () => {
		var targetWindow = iframeDom.contentWindow;
		await this.emitMessage(param, targetWindow);
		resolve(targetWindow.PDFOffice);
	})
} else {
	iframeDom.onload = async () => {
		var targetWindow = iframeDom.contentWindow;
		await this.emitMessage(param, targetWindow);
		resolve(targetWindow.PDFOffice);
	}
}

以上代码标识pdf.html页面加载完成后向pdf.js发送消息,pdf.js会监听postMessage发送过来的消息,然后左响应的js加载

pdf.js

/**
 * 懒加载js文件
 * @returns 
 */
function lazyLoadJS(params, callback) {
    return new Promise((resolve, reject) => {
        const {
            pluginType,
            webRoot
        } = params;
        let urls = JsFilesEnums[pluginType];
        // 文件接口拼接
        urls = urls.map(item => {
            if (item.needWebRoot) {
                return webRoot + item.jsUrl;
            } else {
                return item.jsUrl;
            }
        })
        loadJS(urls, () => {
            if (typeof callback == 'function') {
                callback();
            }
            resolve(true);
        });
    });
}

这里的控件类型是“kingsoft_pdf_jsapi”,因此会加载enum.js文件中JsFilesEnums对象中kingsoft_pdf_ppapi对应的js文件

4).实例化PDFOffice对象

5).调用PDFOffice对象的initPdf()初始化控件(pdf_kinggrid_browser.js对应的initPdf方法)

6).调用basePdf.js中的doShowPdfDiv()打开PDF控件,加载文档

3、doShowPdfDiv()调用basePdf.js中的loadPdf()加载文件

1).在每个插件对应的js中检查控件是否可用

2).设置记录文件打开值isLoadPdfFile

3).调用插件对应的loadPdf()加载pdf正文(pdf_kinggrid_browser.js文件中的loadPdf())

4、调用pdf_kinggrid_brower.js文件中的loadPdf()方法

1).获取令牌,设置令牌

2).验证控件是否可用

// 获取证书
const verification = KGPdfUtils.getVerification();
// 验证PDF控件是否可用
// 第一步检测金格中间件应用 第二步检测iwebpdf是否安装
if (!this.isOfficeAvailable() || !await this.checkOfficeStatus(params)) {
   this.dialog();
   this.isLoading = false;
   return false;
}

3).如果控件可用,会调用唤起App,然后打开文件

const res = await KGBrowserPdfUtils.StartApp(params);
if(res){
	data = await KGBrowserPdfUtils.openFile(params);   //data就代表文档是否打开成功,如果不用打开或不用保存,则返回true
	setIsLoadPdfFile(data);
}else{
	setIsLoadPdfFile(false);
}

备注:这里的打开文件并不是真的打开,是调用pdf_invoke.js的openFile方法是发送了一个消息给tgPdf.js执行tgPdf.js中的openFile方法,发送消息见pdf_invoke.js如下:

/**
 * 金格JSAPI统一发送消息的方法
 * @param { String } func 需要执行的方法名称
 * @param { Object } params 参数信息
 */
function _KgInvoke(func, params = {}) {
    const { reqType = 'post' } = params;
    // 金格sdk中获取的方法
    return new Promise(function (resolve, reject) {
        if(KgPdfApp) {
            KgPdfApp.invoke({
                method: func.method,
                reqType: reqType,
                params: params,
                success: function (data) {
                    resolve(data);
                },
                error: function (err) {
                    reject(err);
                }
            });
        } else {
            reject(false);
        }
    });
}

4).调用initOcxMenu()进行菜单初始化

/**
 * 初始化菜单
 */
PDFOffice.prototype.initOcxMenu = function () {
    var pdfFun = document.getElementById("WebPDF").iWebPDFFun;
    pdfFun.AppendTools("101", KGPdfUtils.getPdfLanguage("menu_57"), 7);
}

5).调用adjustMenu()调整菜单显示

/**
 * 调整菜单显示
 */
PDFOffice.prototype.adjustMenu = function () {
try {
        //修改正文
        var pdfObj = document.getElementById("WebPDF").iWebPDFFun;
        pdfObj.Zoom = 100;
        var menuStr = "";
        var _canOpen = true;
        var hideMenuStr = "";
        var signMenu = "";
        if (parent.editType == "0,0" || parent.editType == "4,0") { //查看只读文档状态,不能打开文档,保存文档
            menuStr = "menu_1,menu_10,menu_89,menu_90";
            hideMenuStr = "menu_118";
            _canOpen = false;
            signMenu = ",menu_102,menu_103";
            try {
                pdfObj.AllowMoveAnnot = false;
            } catch (e) {}
        } else if (parent.editType == "1,0") { // 新建文档状态,可以打开文档,保存文档
            _canOpen = true;
        }
        /*************************************隐藏菜单开始***********************************************/
        for (var i = 2; i < 118; i++) {
            if (_canOpen) {
                if (i == 8 || i == 10 || i == 12 || i == 57 || i == 88 || i == 89 || i == 91 || i == 92 || i == 93 || i == 94 || i == 95 || i == 96 || i == 102 || i == 103 || i == 104 || i == 105 || i == 106 || i == 107 || i == 108) {
                    continue;
                }
                if(menuStr == ""){
                    menuStr = menuStr + "menu_" + i;
                }else{
                    menuStr = menuStr + ",menu_" + i;
                }
            } else {
                if (i == 8 || i == 10 || i == 12 || i == 51 || i == 52 || i == 57 || i == 88 || i == 91 || i == 92 || i == 93 || i == 94 || i == 95 || i == 96 || i == 102 || i == 103 || i == 104 || i == 105 || i == 106 || i == 107 || i == 108) {
                    continue;
                }
                if(menuStr == ""){
                    menuStr = menuStr + "menu_" + i;
                }else{
                    menuStr = menuStr + ",menu_" + i;
                }
            }
        }
        /*************************************隐藏菜单结束***********************************************/
        //-----------------------------
        var canSaveLocal = parent.contentSaveLocal && parent.contentSaveLocal == "true";
        canSaveLocal = canSaveLocal || parent.officecanSaveLocal && parent.officecanSaveLocal == "true";
        canSaveLocal = canSaveLocal || parent.parent.officecanSaveLocal && parent.parent.officecanSaveLocal == "true";
        //如果是快速发文,默认有下载权限
        if (parent.parent && parent.parent.isQuickSend === "true") {
            canSaveLocal = true;
        }
        //要禁用的菜单 本地另存为按钮 只有在没权限保存的时候禁用
        if (!canSaveLocal) {
            menuStr = menuStr + ",menu_8";
        }
        menuStr += signMenu;
        //-----------------------------
        //打印
        menuStr = this.setToolsPrintState(menuStr);
        menuStr = KGPdfUtils.getPdfLanguage(menuStr).replace(/,/g, ";");
        hideMenuStr = this.setToolsPrintState(hideMenuStr);
        hideMenuStr = KGPdfUtils.getPdfLanguage(hideMenuStr).replace(/,/g, ";");
        pdfObj.EnableTools(menuStr, 0);
        pdfObj.EnableTools(hideMenuStr, 2);
        pdfObj.EnableTools(KGPdfUtils.getPdfLanguage("menu_51,menu_52").replace(/,/g, ";"), 1);
        //文字选择按钮,控制复制粘特
        this.isAuthorityToCopy(pdfObj);
    } catch (e) {
        window.console && console.log(e);
    }
}

# Ⅱ Word、WPS实现逻辑

# 控件加载要求

页面加载的时候需要初始化一个id为officeEditorFrame的ifram标签,并且加载baseOffice.js,baseOffice.js需要和iframe在同一个页面

示例:

<iframe id="officeEditorFrame" name="officeEditorFrame" 
         frameborder="0" height="100%" width="100%" scrolling="no" marginheight="0" marginwidth="0"></iframe>
 <script src="./baseOffice.js"></script>

# 各文件的作用

  1. officeBrowse.html:挂载点

  2. officeBrower.css:样式文件

  3. baseOffice.js:调用pdf控件的基础js,提供了业务方法的调用

  4. kinggirdBaseOffice.js:金格office控件jsapi原型对象js

  5. kinggirdInvoke.js:金格office控件jsapi双向通信封装的方法

  6. tgOffice.js:金格控件jsapi插件端执行的js

# 实现逻辑

1、方法入口为baseOffice.js中的initOffice()方法

initOffice: async function(params) {
	await this.buildOfficePlugin(params);
}

2、调用buildOfficePlugin()方法

1).获取webOffice在线编辑的类型

2).获取是否启用了webOffice配置

3).如果是wps类型需要判断webOffice是否可用

4).如果webOffice不可用,并且客户端混网,没有购买国标office插件,就走金格

5).如果是非混网,或者pdf被wps打开了

3、调用baseOffice.js的setKinggriddPlugin()方法设置参数

1)、获取谷歌内核版本,如果是高于107版本就使用金格jsapi

2)、实例化金格插件对象,设置相关参数

4、调用kinggridBaseOffice.js的loadOffice()方法加载office

1).判断插件是否可用

2).调用_loadKGBrowerHtml()设置正文挂载点

3).设置一下最新的参数

4).判断金格app是否安装,控件未加载成功后续逻辑不再执行

5).调用checkOfficeStatus()检查控件即当前状态

6).调用kinggridOffice.js的StartApp()方法,备注:kinggridOffice.js的startApp()并不是打开控件,而是调用kinggridinvoke.js的StartApp()方法打开控件

7).调用kinggridOffice.closeDocumentByAll()关闭金格控件,这个方法会调用kinggridinvoke.js的closeDocumentByAll()方法,向tgOffice.js发送关闭文档的指令。备注:tgOffice.js是在金格外部中间件的浏览器中加载,OA不能直接调用,所有交互都是采用消息发送。

8).判断是否立即加载,如果需要立即加载再次调用startApp打开控件

7、打开金格外部中间件

调用kinggridOffice.js的StartApp()方法即可打开控件

编撰人:ranjunfeng、het、ranjf