MENU

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

• 2024 年 12 月 19 日 • 阅读: 3339 • 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 日
添加新评论

已有 75 条评论
  1. quq quq

    666

  2. Euphoria Euphoria

    丸子!开开门开开门

  3. 归鸿 归鸿

    丸子666

  4. null null

    开门开门

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

    丸子牛啊,666

  6. 江边踏青 江边踏青

    丸子!开开门开开门

  7. z z

    强!

  8. 夜寒 夜寒

    来拿代码了

  9. 11 11

    牛哇牛哇

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

    丸子上班了!

    1. hhh hhh

      @土土土土土6666

  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

      @丸子厉害

    8. hhh hhh

      @丸子大佬,现在这个博客的完整代码是修复bug后最新的嘛?@(呵呵)

    9. @hhh嗯呐是的,不过应该还有一些不影响使用的小BUG~比如会影响列表刷新等的,问题不大的bug

  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

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

  38. kk kk

    666

  39. mallstar mallstar

    厉害了!@(呵呵)

  40. coco coco

    666

  41. Hugo Hugo

    丸子!开开门开开门

  42. liucg liucg

    大佬 6666

  43. 用脚丈量 用脚丈量

    厉害厉害@(呵呵)

  44. DC DC

    666

  45. ml ml

    学习学习

  46. hhh hhh

    66666

  47. Cenvy Cenvy

    丸子!开开门开开门

  48. 曾不错 曾不错

    666

  49. 围观 丸子 和 wintsa 两位大佬

  50. coco coco

    团子,只对比了pc模板吗

    1. coco coco

      @coco不要意思,丸子@(捂嘴笑)

    2. @coco是滴,移动端还没做

  51. 王先生 王先生

    厉害

  52. siggraph siggraph

    666

  53. 唐九七 唐九七

    这个功能确实强大,解决很多问题

  54. 学习学学 学习学学

    学习学学

  55. pandacatmc pandacatmc

    开门

  56. 233 233

    太猛了!

  57. 冯宝宝 冯宝宝

    666

  58. sean sean

    666