MENU

[泛微 ecode] 流程节点Html模板检查一致性比对

• 2024 年 12 月 19 日 • 阅读: 1036 • 泛微OA

你的泛微 E9 流程节点 HTML 模板同步小助手!

在日常 OA 系统管理中,随着流程节点数量的增多,管理和同步这些节点的 HTML 模板常常成为一项耗时又繁琐的任务。作为一名 OA 工程师,我经常遇到以下问题:

  • 不知道哪些节点模板完全一致,导致反复比对浪费时间,但没有比对又不敢直接复制模板。
  • 难以区分需要单独处理的模板,管理起来不够直观。

为了高效解决这些问题,我开发了「流程节点 HTML 模板一致性比对功能」,能够快速分析流程节点的模板一致性,并用可视化的方式帮助用户理解和管理模板。今天,我将分享这一工具的实现与优化过程,希望能对大家有所帮助!

流程节点HTML一致性比对

2024年12月19日 10:19:39代码BUG修复

修复Table组件污染BUG

2024年12月19日 10:19:39代码优化

感谢wintsa佬友的修正和建议


功能背景与目标

在泛微 E9 的工作流引擎中,每个流程节点都有自己的 HTML 模板和脚本,由于节点数量众多且分布复杂,手动检查模板一致性常常需要耗费大量时间。基于这一痛点,我设计并实现了以下功能:

  1. 自动分析节点模板:通过 API 接口提取每个节点的 HTML 模板和脚本信息。
  2. 生成唯一一致性标签:自动比对模板和脚本内容,为每个节点生成唯一的标签。
  3. 颜色标识可视化:通过颜色标识,直观展示节点模板和脚本的一致性分组。
  4. 一键操作简单高效:用户只需点击一个按钮,即可完成所有节点的模板一致性检查。

代码实现

完整代码

此处内容需要评论回复后方可阅读

核心功能实现

1. 自定义路径校验,限定功能适用范围

为了确保该功能仅在特定页面生效,我设计了路径校验逻辑 customCheck,仅当用户在特定的工作流节点页面中时才启用功能。

const customCheck = () => {
    return ecodeSDK.checkLPath('/wui/engine.html#/workflowengine/path/pathSet/pathDetail/flowSet/nodeInfo');
};

2. 数据获取与处理

通过两个 API 接口分别获取节点的 HTML 模板信息和脚本设计信息:

  • 获取 HTML 模板信息:调用 /api/workflow/nodeformedit/getModeInfo 接口,提取模板的基本信息(例如模板 ID)。
  • 获取脚本设计信息:调用 /api/workflow/wfexceldesign/doLoadDesigner 接口,提取模板的布局和脚本内容。

代码示例如下:

const fetchModeInfo = async (nodeid) => {
    try {
        const response = await fetch(`/api/workflow/nodeformedit/getModeInfo?workflowid=${workflowId}&nodeid=${nodeid}`);
        return response.json();
    } catch {
        return null;
    }
};

const fetchDesigner = async (modeid) => {
    try {
        const response = await fetch('/api/workflow/wfexceldesign/doLoadDesigner', {
            method: 'POST',
            headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
            body: new URLSearchParams({ layoutid: modeid }).toString(),
        });
        return response.json();
    } catch {
        return null;
    }
};

3. 模板与脚本一致性比对

在获取到节点的模板和脚本信息后,我使用 Map 数据结构存储唯一的模板和脚本内容,并生成对应的标签和颜色标识。通过遍历所有节点,比对内容,给每个节点添加唯一的可视化标签。

  • 模板与脚本的去重逻辑:模板和脚本的内容作为 Map 的 key,确保重复的内容不会生成重复标签。
  • 颜色标识分配:通过 generatePastelColor 方法生成随机柔和颜色,帮助用户快速区分模板和脚本分组。

具体代码实现如下:

const processNodeData = async (dataSource) => {
    const templateMap = new Map();
    const jsMap = new Map();
    const colorMap = new Map();

    function removeSuffix(str, suffixLength) {
        if (str.length <= suffixLength) {
            return '';
        }
        return str.slice(0, str.length - suffixLength);
    }

    await Promise.all(
        dataSource.map(async (node) => {
            const nodeid = node._node_2;

            const modeInfo = await fetchModeInfo(nodeid);
            if (!modeInfo || !modeInfo.html || modeInfo.html.modeid === 0) {
                return;
            }

            const Designer = await fetchDesigner(modeInfo.html.modeid);
            const { layoutInfo } = Designer;
            if (!layoutInfo || !layoutInfo.pluginjson) {
                return;
            }

            const templateKey = layoutInfo.pluginjson;
            if (!templateMap.has(templateKey)) {
                templateMap.set(templateKey, `模板${templateMap.size + 1}`);
                colorMap.set(`模板${templateMap.size}`, generatePastelColor());
            }
            const templateTag = templateMap.get(templateKey);

            const jsKey = removeSuffix(layoutInfo.scripts, 33) || '无脚本';
            if (!jsMap.has(jsKey)) {
                jsMap.set(jsKey, `JS${jsMap.size + 1}`);
                colorMap.set(`JS${jsMap.size}`, generatePastelColor());
            }
            const jsTag = jsMap.get(jsKey);

            const container = document.createElement('div');
            container.innerHTML = node._node_2span || '';
            const existingTags = container.querySelector('.customflow-tag-container');
            if (existingTags) {
                existingTags.remove();
            }

            const newTags = `<div class="customflow-tag-container"><div class="customflow-tag" style="background-color: ${colorMap.get(templateTag)}">${templateTag}</div><div class="customflow-tag" style="background-color: ${colorMap.get(jsTag)}">${jsTag}</div></div>`;
            container.innerHTML += newTags;

            node._node_2span = container.innerHTML;
        })
    );

    return { success: true };
};

4. 一键触发比对功能

在页面顶部按钮区域,添加一个「模板一致比对」按钮,通过点击按钮即可触发模板一致性检查流程。此按钮的注入逻辑如下:

const handleComparison = async () => {
    if (!tableProps) {
        antd.message.warn('获取节点信息数据失败,请刷新页面后重试!');
        return;
    }
    const dataSourceCopy = JSON.parse(JSON.stringify(tableProps.dataSource));
    tableProps.loading = true;

    const result = await processNodeData(dataSourceCopy);

    if (result.success) {
        tableProps.dataSource = [...dataSourceCopy];
        tableProps.onChange && tableProps.onChange([...dataSourceCopy]);
        antd.message.success('模板一致比对完成!');
    } else {
        antd.message.error('比对过程中发生错误,请稍后重试!');
    }

    tableProps.loading = false;
};

ecodeSDK.overwritePropsFnQueueMapSet('WeaTop', {
    fn: (newProps) => {
        if (!customCheck()) {
            return;
        }

        let { buttons } = newProps;
        buttons = buttons || [];
        const { Button } = antd;

        buttons.push(
            <Button key="handleComparison" onClick={handleComparison}>模板一致比对</Button>
        );
    },
});

测试与优化建议

测试结果

  1. 模板一致性快速识别:节点表格中显示了颜色标记的模板和脚本标签,相同模板的节点被分配了相同的颜色,直观明了。
  2. 一键完成比对:无需繁琐的手动操作,点击按钮即可完成所有节点的模板检查。
  3. 高效而稳定:在节点数量较多的情况下,仍能保持较好的响应速度。

希望这篇分享能够帮助到更多的开发者,简化泛微 E9 流程节点模板管理的复杂度。如果大家有任何建议或问题,欢迎随时交流!

最后编辑于: 2024 年 12 月 20 日
添加新评论

已有 49 条评论
  1. quq quq

    666

  2. Euphoria Euphoria

    丸子!开开门开开门

  3. 归鸿 归鸿

    丸子666

  4. null null

    开门开门

  5. 学泛微,当牛马 学泛微,当牛马

    丸子牛啊,666

  6. 江边踏青 江边踏青

    丸子!开开门开开门

  7. z z

    强!

  8. 夜寒 夜寒

    来拿代码了

  9. 11 11

    牛哇牛哇

  10. 土土土土土 土土土土土

    丸子上班了!

  11. 牛

    666

  12. 张三 张三

    大佬6666

  13. QAQ QAQ

    牛子丸啊

  14. 1 1

    666

  15. GaryYou GaryYou

    芝麻开门

  16. 66 66

    render@

  17. wintsa wintsa

    6666

  18. wintsa wintsa

    js的生成好像不太对,我没仔细检查代码,但是我尝试 1 2 3 4节点同一个js和html模块,这时候修改2的js和模块,出现的问题是1 3 4 判定的脚本居然不属于同一个

    1. @wintsa昨天我检查了这个,但是不计划修复这个问题,比如js的换行空格等,都属于一个新的js
      对于html模板来说,泛微在保存模板时保存了用户最后选中的单元格,如果模板一致,但是最后选中的单元格不一致,也是会判断为属于一个新的html模板

  19. wintsa wintsa

    再提个建议,这个’比对已完成,请勿重复操作‘的判断条件,comparisonCompleted变量可以通过监听接口保存之类的更新@(哈哈)

    1. @wintsa哇哈哈哈哈哈,有道理~

  20. wintsa wintsa

    const processNodeData = async (dataSource) => {

    const templateMap = new Map(); const jsMap = new Map(); const colorMap = new Map(); try { await Promise.all( await Promise.all( dataSource.map(async (node) => { const nodeid = node._node_2; // 获取 modeInfo 和 Designer 数据 const modeInfo = await fetchModeInfo(nodeid); if (!modeInfo || !modeInfo.html || modeInfo.html.modeid === 0) { return; } const Designer = await fetchDesigner(modeInfo.html.modeid); const { layoutInfo } = Designer; if (!layoutInfo || !layoutInfo.pluginjson) { return; } // 处理模板标签 const templateKey = layoutInfo.pluginjson; if (!templateMap.has(templateKey)) { templateMap.set(templateKey, `模板${templateMap.size + 1}`); colorMap.set(`模板${templateMap.size}`, generatePastelColor()); } const templateTag = templateMap.get(templateKey); // 处理脚本标签 const jsKey = layoutInfo.scripts || '无脚本'; if (!jsMap.has(jsKey)) { jsMap.set(jsKey, `JS${jsMap.size + 1}`); colorMap.set(`JS${jsMap.size}`, generatePastelColor()); } const jsTag = jsMap.get(jsKey); // 清除旧的动态内容 const container = document.createElement('div'); container.innerHTML = node._node_2span || ''; const existingTags = container.querySelector('.customflow-tag-container'); if (existingTags) { existingTags.remove(); // 删除已有的标签容器 } // 添加新的动态内容 const newTags = `<div class="customflow-tag-container">` + `<div class="customflow-tag" style="background-color: ${colorMap.get(templateTag)}">${templateTag}</div>` + `<div class="customflow-tag" style="background-color: ${colorMap.get(jsTag)}">${jsTag}</div>` + `</div>`; container.innerHTML += newTags; // 更新节点内容 node._node_2span = container.innerHTML; }) ) ); return { success: true }; } catch { return { success: false }; }

    };

    我见到改了一下这个方法,if (comparisonCompleted) {

    antd.message.warn('比对已完成,请勿重复操作!'); return; }

    这个条件可以去掉了,但是js生成还是会乱的问题我没仔细看

  21. wintsa wintsa

    let tableProps = {};
    let workflowId;
    let comparisonCompleted = false;

    const generatePastelColor = () => {

    const hue = Math.floor(Math.random() * 360); const lightness = Math.random() * (60 - 30) + 30; return `hsl(${hue}, 70%, ${lightness}%)`;

    };

    const customCheck = () => {

    return ecodeSDK.checkLPath('/wui/engine.html#/workflowengine/path/pathSet/pathDetail/flowSet/nodeInfo');

    };

    const fetchModeInfo = async (nodeid) => {

    try { const response = await fetch(`/api/workflow/nodeformedit/getModeInfo?workflowid=${workflowId}&nodeid=${nodeid}`); return response.json(); } catch { return null; }

    };

    const fetchDesigner = async (modeid) => {

    try { const response = await fetch('/api/workflow/wfexceldesign/doLoadDesigner', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ layoutid: modeid }).toString(), }); return response.json(); } catch { return null; }

    };

    const processNodeData = async (dataSource) => {

    const templateMap = new Map(); const jsMap = new Map(); const colorMap = new Map(); function removeSuffix(str, suffixLength) { if (str.length <= suffixLength) { return ''; // 如果后缀长度大于等于字符串长度,返回空字符串 } return str.slice(0, str.length - suffixLength); // 去掉后缀 } try { await Promise.all( await Promise.all( dataSource.map(async (node) => { const nodeid = node._node_2; // 获取 modeInfo 和 Designer 数据 const modeInfo = await fetchModeInfo(nodeid); if (!modeInfo || !modeInfo.html || modeInfo.html.modeid === 0) { return; } const Designer = await fetchDesigner(modeInfo.html.modeid); const { layoutInfo } = Designer; if (!layoutInfo || !layoutInfo.pluginjson) { return; } // 处理模板标签 const templateKey = layoutInfo.pluginjson; if (!templateMap.has(templateKey)) { templateMap.set(templateKey, `模板${templateMap.size + 1}`); colorMap.set(`模板${templateMap.size}`, generatePastelColor()); } const templateTag = templateMap.get(templateKey); // 处理脚本标签 const jsKey = removeSuffix(layoutInfo.scripts, 33) || '无脚本'; if (!jsMap.has(jsKey)) { jsMap.set(jsKey, `JS${jsMap.size + 1}`); colorMap.set(`JS${jsMap.size}`, generatePastelColor()); } const jsTag = jsMap.get(jsKey); // 清除旧的动态内容 const container = document.createElement('div'); container.innerHTML = node._node_2span || ''; const existingTags = container.querySelector('.customflow-tag-container'); if (existingTags) { existingTags.remove(); // 删除已有的标签容器 } // 添加新的动态内容 const newTags = `<div class="customflow-tag-container">` + `<div class="customflow-tag" style="background-color: ${colorMap.get(templateTag)}">${templateTag}</div>` + `<div class="customflow-tag" style="background-color: ${colorMap.get(jsTag)}">${jsTag}</div>` + `</div>`; container.innerHTML += newTags; // 更新节点内容 node._node_2span = container.innerHTML; }) ) ); return { success: true }; } catch { return { success: false }; }

    };

    const handleComparison = async () => {

    // if (comparisonCompleted) { // antd.message.warn('比对已完成,请勿重复操作!'); // return; // } if (!tableProps) { antd.message.warn('获取节点信息数据失败,请刷新页面后重试!'); return; } const dataSourceCopy = JSON.parse(JSON.stringify(tableProps.dataSource)); tableProps.loading = true; const result = await processNodeData(dataSourceCopy); if (result.success) { tableProps.dataSource = [...dataSourceCopy]; tableProps.onChange && tableProps.onChange([...dataSourceCopy]); antd.message.success('模板一致比对完成!'); comparisonCompleted = true; } else { antd.message.error('比对过程中发生错误,请稍后重试!'); } tableProps.loading = false;

    };

    ecodeSDK.overwritePropsFnQueueMapSet('WeaTop', {

    fn: (newProps) => { if (!customCheck()) { return; } if (!workflowId) { workflowId = window.weaJs.getFrameParams().workflowId; } let { buttons } = newProps; buttons = buttons || []; const { Button } = antd; buttons.push( <Button key="handleComparison" onClick={handleComparison}>模板一致比对</Button> ); return newProps },

    });

    ecodeSDK.overwritePropsFnQueueMapSet('Table', {

    fn: (newProps) => { if (!customCheck()) return; if (!newProps.rootMap || newProps.rootMap.tabletype !== 'none' || newProps.rootMap.pageId !== 'wf:nodeinfolist') return; const currentWorkflowId = window.weaJs.getFrameParams().workflowId; if (workflowId !== currentWorkflowId) { workflowId = currentWorkflowId; tableProps = null; comparisonCompleted = false; } if (tableProps && tableProps.dataSource && tableProps.dataSource.length > 0) { // 确保 newProps.dataSource 已存在,如果没有则初始化 if (!newProps.dataSource) { newProps.dataSource = []; } // 遍历 tableProps.dataSource 并将 _node_2span 赋值到 newProps.dataSource 中 tableProps.dataSource.forEach((item, index) => { // 如果 newProps.dataSource 中没有相应的对象,则初始化 if (newProps.dataSource[index]) { newProps.dataSource[index]._node_2span = item._node_2span; } }); } tableProps = { ...newProps }; return newProps; },

    });

    发现了三个bug,顺便修复了@(吐舌)

    1. @wintsa十分感谢~~,BUG我已复现,马上更新博客代码为最新~

    2. wintsa wintsa

      @丸子突然发现这段代码在添加节点的时候,旧的tag还是会错位。最后那段_node_2span还可以再加个判断条件优化下。不过现在已经足够满足使用了~@(吐舌)

    3. @wintsa咦,我的不会错位捏,但是标签不会自动跟着刷新,比如排序变化那些,但是考虑到不是用户使用,还好,暂时就酱紫用,哈哈哈哈哈哈

    4. wintsa wintsa

      @丸子
      ecodeSDK.overwritePropsFnQueueMapSet('Table', {

      fn: (newProps) => { if (!customCheck()) return; if (!newProps.rootMap || newProps.rootMap.tabletype !== 'none' || newProps.rootMap.pageId !== 'wf:nodeinfolist') return; const currentWorkflowId = window.weaJs.getFrameParams().workflowId; if (workflowId !== currentWorkflowId) { workflowId = currentWorkflowId; tableProps = null; comparisonCompleted = false; } if (tableProps && tableProps.dataSource && tableProps.dataSource.length > 0) { // 确保 newProps.dataSource 已存在,如果没有则初始化 if (!newProps.dataSource) { newProps.dataSource = []; } // 遍历 tableProps.dataSource 并将 _node_2span 赋值到 newProps.dataSource 中 tableProps.dataSource.forEach((item) => { // 找到 newProps.dataSource 中与当前 item 匹配的对象 const matchedItem = newProps.dataSource.find( (newItem) => newItem.randomFieldId === item.randomFieldId ); // 如果找到匹配项,进行比较和更新 if (matchedItem) { // 提取 item 和 matchedItem 的 <a> 标签 innerText const itemContainer = document.createElement('div'); itemContainer.innerHTML = item._node_2span; const matchedContainer = document.createElement('div'); matchedContainer.innerHTML = matchedItem._node_2span; const itemInnerText = itemContainer.querySelector('a').innerText; const matchedInnerText = matchedContainer.querySelector('a').innerText; // 如果 innerText 相同,则更新 _node_2span if (itemInnerText === matchedInnerText) { matchedItem._node_2span = item._node_2span; } } }); } tableProps = { ...newProps }; return newProps; },

      });

      最后那一段我修改了一下,这就很完美了

    5. wintsa wintsa

      @丸子 if (!layoutInfo || !layoutInfo.datajson) {

      return; } // 处理模板标签 let tmp=JSON.parse(layoutInfo.datajson) delete tmp.eformdesign.eattr const templateKey = JSON.stringify(tmp); if (!templateMap.has(templateKey)) { templateMap.set(templateKey, `模板${templateMap.size + 1}`); colorMap.set(`模板${templateMap.size}`, generatePastelColor()); } const templateTag = templateMap.get(templateKey);

      我再修改了一下,模板那里应该对比datajson才对。

    6. wintsa wintsa

      @丸子还是有点问题,我用同步功能后,明明字段属性的可编辑不一样,但是他还是显示同模板

    7. hax hax

      @丸子厉害

  22. 6 6

    6666

  23. quq quq

    666

  24. 橙熟 橙熟

    丸子大佬牛啊

  25. wintsa wintsa

    let tableProps = {};
    let workflowId;
    let comparisonCompleted = false;

    const generatePastelColor = () => {

    const hue = Math.floor(Math.random() * 360); const lightness = Math.random() * (60 - 30) + 30; return `hsl(${hue}, 70%, ${lightness}%)`;

    };

    const customCheck = () => {

    return ecodeSDK.checkLPath('/wui/engine.html#/workflowengine/path/pathSet/pathDetail/flowSet/nodeInfo');

    };

    const fetchModeInfo = async (nodeid) => {

    try { const response = await fetch(`/api/workflow/nodeformedit/getModeInfo?workflowid=${workflowId}&nodeid=${nodeid}`); return response.json(); } catch { return null; }

    };

    const fetchDesigner = async (modeid) => {

    try { const response = await fetch('/api/workflow/wfexceldesign/doLoadDesigner', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ layoutid: modeid }).toString(), }); return response.json(); } catch { return null; }

    };
    const fetchFormInfo = async (formid,nodeid) => {

    try { const response = await fetch('/api/workflow/wfexceldesign/doLoadFormInfo', { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: new URLSearchParams({ formid,isbill:1,nodeid }).toString(), }); return response.json(); } catch { return null; }

    };
    const processNodeData = async (dataSource) => {

    const templateMap = new Map(); const jsMap = new Map(); const colorMap = new Map(); function removeSuffix(str, suffixLength) { if (str.length <= suffixLength) { return ''; // 如果后缀长度大于等于字符串长度,返回空字符串 } return str.slice(0, str.length - suffixLength); // 去掉后缀 } try { await Promise.all( await Promise.all( dataSource.map(async (node) => { const nodeid = node._node_2; // 获取 modeInfo 和 Designer 数据 console.log(nodeid,'1') const modeInfo = await fetchModeInfo(nodeid); if (!modeInfo || !modeInfo.html || modeInfo.html.modeid === 0) { return; } console.log(modeInfo.html.modeid,'2') const Designer = await fetchDesigner(modeInfo.html.modeid); const { layoutInfo } = Designer; if (!layoutInfo || !layoutInfo.datajson) { return; } const {formInfo} = await fetchFormInfo(JSON.parse(node.moreSetspan).formid,nodeid); if (!formInfo ) { return; } // 处理模板标签 let tmp=JSON.parse(layoutInfo.datajson) delete tmp.eformdesign.eattr.nodeid const templateKey = JSON.stringify(tmp)+JSON.stringify(formInfo) // const templateKey = layoutInfo.pluginjson; if (!templateMap.has(templateKey)) { templateMap.set(templateKey, `模板${templateMap.size + 1}`); colorMap.set(`模板${templateMap.size}`, generatePastelColor()); } const templateTag = templateMap.get(templateKey); // 处理脚本标签 const jsKey = removeSuffix(layoutInfo.scripts, 33) || '无脚本'; if (!jsMap.has(jsKey)) { jsMap.set(jsKey, `JS${jsMap.size + 1}`); colorMap.set(`JS${jsMap.size}`, generatePastelColor()); } const jsTag = jsMap.get(jsKey); // 清除旧的动态内容 const container = document.createElement('div'); container.innerHTML = node._node_2span || ''; const existingTags = container.querySelector('.customflow-tag-container'); if (existingTags) { existingTags.remove(); // 删除已有的标签容器 } // 添加新的动态内容 const newTags = `<div class="customflow-tag-container">` + `<div class="customflow-tag" style="background-color: ${colorMap.get(templateTag)}">${templateTag}</div>` + `<div class="customflow-tag" style="background-color: ${colorMap.get(jsTag)}">${jsTag}</div>` + `</div>`; container.innerHTML += newTags; // 更新节点内容 node._node_2span = container.innerHTML; }) ) ); return { success: true }; } catch { return { success: false }; }

    };

    const handleComparison = async () => {

    // if (comparisonCompleted) { // antd.message.warn('比对已完成,请勿重复操作!'); // return; // } if (!tableProps) { antd.message.warn('获取节点信息数据失败,请刷新页面后重试!'); return; } const dataSourceCopy = JSON.parse(JSON.stringify(tableProps.dataSource)); tableProps.loading = true; const result = await processNodeData(dataSourceCopy); if (result.success) { tableProps.dataSource = [...dataSourceCopy]; tableProps.onChange && tableProps.onChange([...dataSourceCopy]); antd.message.success('模板一致比对完成!'); comparisonCompleted = true; } else { antd.message.error('比对过程中发生错误,请稍后重试!'); } tableProps.loading = false;

    };

    ecodeSDK.overwritePropsFnQueueMapSet('WeaTop', {

    fn: (newProps) => { if (!customCheck()) { return; } if (!workflowId) { workflowId = window.weaJs.getFrameParams().workflowId; } let { buttons } = newProps; buttons = buttons || []; const { Button } = antd; buttons.push( <Button key="handleComparison" onClick={handleComparison}>模板一致比对</Button> ); return newProps },

    });

    ecodeSDK.overwritePropsFnQueueMapSet('Table', {

    fn: (newProps) => { if (!customCheck()) return; if (!newProps.rootMap || newProps.rootMap.tabletype !== 'none' || newProps.rootMap.pageId !== 'wf:nodeinfolist') return; const currentWorkflowId = window.weaJs.getFrameParams().workflowId; if (workflowId !== currentWorkflowId) { workflowId = currentWorkflowId; tableProps = null; comparisonCompleted = false; } if (tableProps && tableProps.dataSource && tableProps.dataSource.length > 0) { // 确保 newProps.dataSource 已存在,如果没有则初始化 if (!newProps.dataSource) { newProps.dataSource = []; } // 遍历 tableProps.dataSource 并将 _node_2span 赋值到 newProps.dataSource 中 tableProps.dataSource.forEach((item) => { // 找到 newProps.dataSource 中与当前 item 匹配的对象 const matchedItem = newProps.dataSource.find( (newItem) => newItem.randomFieldId === item.randomFieldId ); // 如果找到匹配项,进行比较和更新 if (matchedItem) { // 提取 item 和 matchedItem 的 <a> 标签 innerText const itemContainer = document.createElement('div'); itemContainer.innerHTML = item._node_2span; const matchedContainer = document.createElement('div'); matchedContainer.innerHTML = matchedItem._node_2span; const itemInnerText = itemContainer.querySelector('a').innerText; const matchedInnerText = matchedContainer.querySelector('a').innerText; // 如果 innerText 相同,则更新 _node_2span if (itemInnerText === matchedInnerText) { matchedItem._node_2span = item._node_2span; } } }); } tableProps = { ...newProps }; return newProps; },

    });

    重新改了一下,之前的版本,对比模板没有对比字段属性。添加多了一个接口获取字段属性

  26. lemon lemon

    学习!

  27. 1 1

    666

  28. 孟

    大佬厉害

  29. 咸鱼 咸鱼

    膜拜大佬

  30. 世界 世界

    6666

  31. cwx cwx

    6666

  32. cwx cwx

    @丸子,这个完整得Ecode代码怎么写呢?

    1. @cwx完整就是文章中的代码

  33. cwx cwx

    666666

  34. cws cws

    厉害

    1. cws cws

      @cws22222

  35. cws cws

    丸子,貌似不对哈,同步模板之后,但是这时我在一个模板添加公式、禁止手动编辑、隐藏行、字段属性,然后对比的时候是不一致,但是这时我再去把这个配置给删了,再次对比,还是显示模板不一致耶 。。

    1. @cws保存时选中单元格不一致也会被判定为不一致,只要改动过没有完完全全的还原就会不对,他是自动比对的后台报告的json。

  36. 1 1

    1

  37. hax hax

    正痛苦如何检查节点的一致性,来学习了