丸子·张丨OnesZhang | 一个小小IT工程师
type: channel
link: https://oneszhang.com/
description: 丸子·张（OnesZhang） - 一位热爱分享的小小IT工程师，提供实用技巧、软件推荐、网站资源、踩坑经验和泛微OA相关内容，探索更多精彩！
items: Top一个"EcologyOA工程师导航"网站
items: Top[EcologyEC9] 之ecodeSDK API文档
items: 《别拿你的工作碰瓷我的热爱》
items: [泛微 Ecology10] 浏览按钮搜索按钮样式处理（真按钮）
items: [泛微Ecology10] 获取当前表单所有字段值的方法
items: [泛微Ecology10] 丸子的 E10 小本本
items: [泛微 E9] [ 高阶精华 ] 泛微 E9 新插件：kkfileview，让文件预览 “零门槛”
items: [泛微 EC9] GETE9LOG.jsp 直接查看当前E9日志
items: [泛微 EC9] 开发踩坑：weaver.conn.RecordSet-报错"字符串文字太长"
items: [泛微 EC9] 流程数据建模同表同步 & SQL插入建模数据
items: [泛微 E10] 明细表处理示例JS 1：字段联动后判断明细表数据隐藏选项
items: [泛微 E10] 批量下载当前表单内所有附件

Top一个"EcologyOA工程师导航"网站
type: item
date: 2024-02-26T13:51:00+08:00
description: AI辅助制作标识 该文章由丸子引导AI辅助制作生成，如有改进意见，欢迎评论区留言。 引言作为一个泛微OA工程师，我深知在日常工作中寻找技术资料和解决方案的困难。因此，我决定创建一个专门为泛微OA工程师设计的导航网站——huahan.cc。本文旨在分享这一旅程的起点、过程以及网站的主要功能。点击访问“huahan.cc-泛微OA工程师导航”网站的创立初衷在泛微OA系统的开发和维护过程中，找到高质量、实用的资源一直是一个挑战。市面上虽有许多资源，但分散且不易找到。因此，我希望通过这个导航站，为同行们提供一个汇总这些资源的平台，让寻找资料变得更加简单和高效。网站内容与特色huahan.cc致力于为泛微OA工程师提供一个全面的资源导航站，无论是OA论坛、技术文档还是知识文档，这里都应有尽有。网站的设计旨在简化信息探索过程，加速个人和团队的成长。特色功能资源汇总：聚合多种泛微OA相关资源，包括官方文档、技术论坛和教程等。易用性设计：界面简洁，分类明确，便于快速找到所需资源。社区互动：提供反馈和建议通道，鼓励用户贡献资源，形成互助的社区氛围。未来展望我希望huahan.cc能持续成长，不仅限于现有资源的汇总，还将探索更多便利工程师工作和学习的新功能。我也期待更多同行的参与和反馈，一起打造更完善的泛微OA工程师社区。结语感谢每一位访问huahan.cc的朋友。我将继续努力，不断优化和扩展网站内容，希望能为泛微OA工程师的工作带来真正的便利。如果你有任何建议或想要申请友链，欢迎通过mail@oneszhang.com与我联系。 欢迎分享这篇文章，并在评论区留下你的看法和建议！

Top[EcologyEC9] 之ecodeSDK API文档
type: item
date: 2023-07-25T11:15:00+08:00
description: 写在前面主要内容本文档主要对泛微 ecodeSDK 的 API 列表和详细信息进行展示，以帮助开发人员更好地了解和使用该 SDK 的功能。由于未能找到完整的 SDK API 列表文档，因此制作此文档以记录供大家及自己使用。本文档包括以下内容：API 列表：提供 SDK 中可用的所有 API 的名称和功能简介。API 详细：提供每个 API 的详细信息，包括参数、返回值、使用示例等。使用示例：提供一些使用示例，以便开发人员更好地理解如何使用 SDK 中的 API。需要注意的是，由于文档可能存在更新和变化，建议在使用前再次查阅官方文档以获取最新的 SDK API 信息。 特别注意 本文档由丸子本人收集整理制作，内容多由丸子自行分析整理，部分来源自网络，难免存在错误和不详细之处，使用时请自行甄别；欢迎评论区反馈，引用请务必注明出处！ 泛微 ecode 简介ecode 是内嵌在 E-cology9 系统中，为了解决 E-cology9 所有标准界面无侵入定制和扩展二次开发的开发工具。泛微官方 ecode 使用说明：e-cloudstore.com 展开详情 ecode 核心是解决 EC 所有界面无侵入定制和扩展二次开发，采用动态注册，不直接修改源码的方案解决开发需求。除了新开发页面的之外，它还支持所有标准页面的修改。ecode 包含了整套前端编辑器、编译器，可以让用户无需搭建开发环境直接通过浏览器开始 single page 前端开发，同时采用 ecode 开发的内容都是插件化，支持一键共享、复用、二次封装。ecode 中可能用到的技术栈（Technology Stack）：PC 端：js、css、es6、react16.x、react-router3、mobxMOBILE 端：js、css、es6、react16.x、react-router4、mobx 关于如何使用 ecode 关于如何使用 ecode，官方已给出详细的使用说明，你可以直接点击上方链接或访问官方说明文档：https://e-cloudstore.com/doc.html 进行学习，丸子不在此进行过多介绍 * SDK API 列表ecodeSDK1. 数组排序（ecode 运行顺序）对 item 结构体数组按照 order 属性进行排序，该 API 无需使用ecodeSDK.ecodeQueueSort(arr)参数说明参数参数类型必须说明arritem 结构体数组是需要排序的数组样例let arr = [ { order: 3, name: "C" }, { order: 1, name: "A" }, { order: 4, name: "D" }, { order: 2, name: "B" } ]; ecodeSDK.ecodeQueueSort(arr);2. PC 端组件参数复写对 EC 全系统 PC 端组件参数进行复写，包含 组件站 PC 组件库 ecCom、antd 大部分组件 ，其中 Radio、Radio.Button、Tree.TreeNode、Menu.ItemGruop、Select.Option、Select.OptGroup、SelectNew.Option、SelectNew.OptGroup 暂不支持复写ecodeSDK.overwritePropsFnQueueMapSet(name,option)参数说明参数参数类型必须说明nameString是组件库 ecCom、antd 组件名称optionObject是复写配置option.fnFunction是复写钩子函数 (newProps)=>{}option.orderInteger否复写排序option.descString否复写说明样例ecodeSDK.overwritePropsFnQueueMapSet('WeaTop',{ //组件名 fn:(newProps)=>{ //newProps 代表组件参数 //建议添加进行位置判断操作 console.log('WeaTop:',newProps); //在这里输出日志，如果成功输出代表组件成功定位 }, order:1, //排序字段，如果存在同一个页面复写了同一个组件，控制顺序时使用 desc:'在这里写此复写的作用，在调试的时候方便查找' });3. PC 端组件重写对 EC 全系统 PC 端组件参数进行重写，重新定义的组件只要遵循原组件的交互要求，即可进行自由定义ecodeSDK.overwriteClassFnQueueMapSet(name,option)参数说明参数参数类型必须说明nameString是组件库 ecCom、antd 组件名称optionObject是重写配置option.fnFunction是重…

《别拿你的工作碰瓷我的热爱》
type: item
date: 2025-11-14T04:04:23+08:00
description: ·

[泛微 Ecology10] 浏览按钮搜索按钮样式处理（真按钮）
type: item
date: 2025-11-14T00:57:00+08:00
description: 效果对比：修改前修改后CSS代码/* 修改输入框长度 */ input[placeholder="Search Quickly"] { min-width: 100px !important; } /* 基础按钮样式 */ div.field1190285555456860160span .associative-search-icon { background-color: #5d9cec; border-radius: 4px; cursor: pointer; padding: 6px 12px; display: inline-flex; align-items: center; gap: 6px; min-width: 120px; max-height: 24px; } /* 修改图标颜色（与蓝色背景对比） */ div.field1190285555456860160span .associative-search-icon .ui-icon { color: white; } /* 添加文字（通过伪元素实现，无需改HTML） */ div.field1190285555456860160span .associative-search-icon::after { content: "Batch Select"; color: white; } /* hover效果增强 */ div.field1190285555456860160span .associative-search-icon:hover { background-color: #7bb0f2; }使用修改输入框长度代码中placeholder="Search Quickly"对应编辑时显示提示语field1190285555456860160span中的数字部分对应字段ID

[泛微Ecology10] 获取当前表单所有字段值的方法
type: item
date: 2025-11-14T00:34:00+08:00
description: 小白丸子一开始的做法（点击展开） /** * 获取表单内所有字段值 * @returns {Object} 包含主表字段和明细表数据的对象 */ const getAllFieldValues = () => { const formSdk = window.WeFormSDK.getWeFormInstance(); const fieldInfos = formSdk.listFieldInfo(); // 复用现有格式化逻辑 const formatFields = (fieldInfos) => { const result = { mainFields: [], detailTables: {} }; fieldInfos.forEach(field => { if (field.componentKey === 'DataTable') { result.detailTables[field.subFormId] = []; } else if (field.fieldId && /^\d+$/.test(field.fieldId)) { const target = field.subFormId && result.detailTables[field.subFormId] ? result.detailTables[field.subFormId] : result.mainFields; target.push(field.fieldId); } }); return result; }; // 复用现有数据获取逻辑 const getFormData = (fields) => { const result = { mainFields: {}, detailTables: {} }; fields.mainFields.forEach(fieldId => { try { result.mainFields[fieldId] = formSdk.getFieldObj(fieldId); } catch (error) { console.warn(`获取字段 ${fieldId} 失败:`, error); result.mainFields[fieldId] = null; } }); Object.keys(fields.detailTables).forEach(tableId => { try { result.detailTables[tableId] = formSdk.getDetailData(tableId) || []; } catch (error) { console.warn(`获取明细表 ${tableId} 失败:`, error); result.detailTables[tableId] = []; } }); return result; }; const fields = formatFields(fieldInfos); return getFormData(fields); }; 现在的做法formSdk.weFormApi.formStore.getFormData().dataDetails;

[泛微Ecology10] 丸子的 E10 小本本
type: item
date: 2025-11-14T00:32:00+08:00
description: 检查表单在打开后数据是否存在变更:window.WeaForm.getDataStatus();无刷(静默)保存：formSdk.weFormApi.formStore.submitFormData();获取整个表单数据: formSdk.weFormApi.formStore.getFormData().dataDetails;图片上传字段单图片上传样式优化：.ui-upload-line-of-row > div:nth-child(1):nth-last-child(2) { display: none !important ; }优化前优化后ESB调用const ebSdk=window.ebuilderSDK; ebSdk.callEsbFlow("1991410702295896092",{dept:"taet"},[]).then(res=>{/*动作流回调逻辑*/});// 1. 构造请求参数 const requestData = { customParams: { mainTable: { requestId: requestId } }, esbFlowId: "1981608218199199750" }; // 2. 发送POST请求到ESB接口 fetch('/api/esb/server/event/triggerActionFlow', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(requestData) }) .then((response) => { // 3. 解析响应JSON（捕获HTTP错误状态） if (!response.ok) { throw new Error(`HTTP错误！状态码：${response.status}`); } return response.json(); }) .then((data) => { // 4. 请求成功回调 console.log('ESB流程触发成功:', data); // wffpSdk.showMessage('触发流程成功', 1, 3); // 成功提示（按需启用） }) .catch((error) => { // 5. 异常捕获（网络错误/HTTP错误/解析错误） console.error('ESB流程触发失败:', error); wffpSdk.showMessage('触发刷新合同失败', 2, 3); // 失败提示 });

[泛微 E9] [ 高阶精华 ] 泛微 E9 新插件：kkfileview，让文件预览 “零门槛”
type: item
date: 2025-10-20T12:13:00+08:00
description: 本博客只涉及部分核心操作，不是保姆级教学，希望你也能有自己的思路，感悟破局的乐趣。背景在日常办公中，文件预览是一个高频且重要的需求。传统的文件预览方案往往存在格式支持有限、体验不佳等问题。泛微E9自带的文档预览功能虽然基础，但在面对复杂格式或特殊需求时往往力不从心。今天给大家介绍如何通过集成开源的kkfileview项目，为泛微E9打造一个强大、统一的文档预览解决方案。功能亮点多格式支持：支持Office、PDF、图片、视频、音频等常见格式无缝集成：与泛微E9原有文档模块完美融合零客户端：基于Web的纯前端预览，无需安装任何插件高性能：基于Spring Boot构建，响应迅速开源免费：基于Apache 2.0协议，可自由使用和修改实施步骤第一步：部署kkfileview服务丸子采用的是从官方GitHub下载源码并自行编译打包的方式。如果你不想自己打包，可以私信丸子获取编译好的版本（有时间的话会分享）。启动成功后访问演示页：访问成功界面：第二步：泛微E9文档功能改造核心工具类首先创建一个工具类，用于生成文件下载的ddcode（这是泛微文档外发的核心知识）：package com.onestools; import weaver.docs.docs.util.DesUtils; public class getFileDownloadUrl { /** * 根据文件ID生成临时下载URL * @param fileid 文件ID * @return 下载URL */ public static String getFileDownloadUrl(int fileid) { try { String encrypt = new DesUtils().encrypt(1 + "_" + fileid); return "/weaver/weaver.file.FileDownload?fileid=" + fileid + "&download=1&ddcode=" + encrypt; } catch (Exception e) { e.printStackTrace(); return null; } } /** * 根据文件ID和基础URL生成临时下载URL * @param fileid 文件ID * @param baseurl 基础URL * @return 下载URL */ public static String getFileDownloadUrl(int fileid, String baseurl) { try { String encrypt = new DesUtils().encrypt(1 + "_" + fileid); return baseurl + "/weaver/weaver.file.FileDownload?fileid=" + fileid + "&download=1&ddcode=" + encrypt; } catch (Exception e) { e.printStackTrace(); return null; } } }JSP页面改造在ecology目录下/docs/e9/index2file.jsp中增加kkfileview预览逻辑：增加引用：<%@ page import="com.onestools.getFileDownloadUrl" %> <%@ page import="sun.misc.BASE64Encoder" %>添加kkfileview预览逻辑：// 现在检查kkfileview配置 boolean useKKFileView = "1".equals(rs.getPropValue("kkfileview","IsUsekkFileView")); String kkFileViewUrl = rs.getPropValue("kkfileview","kkFileViewUrl"); // kkfileview服务地址 String e9BaseUrl = rs.getPropValue("kkfileview","E9BaseUrl"); // E9基础URL if(useKKFileView && !isEdit) { // KKFileView预览逻辑 if(!"".equals(Util.null2String(kkFileViewUrl)) && !"".equals(Util.null2String(e9BaseUrl))) { try { // 使用getFileDownloadUrl工具类生成下载链接 String originUrl = getFileDownloadUrl.getFileDownloadUrl(imagefil…

[泛微 EC9] GETE9LOG.jsp 直接查看当前E9日志
type: item
date: 2025-10-17T15:28:07+08:00
description: 为什么？ 无sysadmin限制，便于测试系统排查问题！速度！快！好看！ 怎么用？ 做一个xxxx.jsp，仍在ecology/目录下访问你的OA地址：/xxxx.jsp 注： 丸子直接用cursor写的！效果图上代码<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %> <%@ page import="weaver.hrm.User" %> <%@ page import="weaver.hrm.HrmUserVarify" %> <%@ page import="java.io.File" %> <%@ page import="java.io.RandomAccessFile" %> <%@ page import="java.io.IOException" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Collections" %> <%@ page import="weaver.hrm.User" %> <%@ page import="weaver.hrm.HrmUserVarify" %> <%@ page import="java.io.File" %> <%@ page import="java.io.RandomAccessFile" %> <%@ page import="java.io.IOException" %> <%@ page import="java.util.ArrayList" %> <%@ page import="java.util.List" %> <%@ page import="java.util.Collections" %> <%-- 1. 登录验证：未登录跳转到登录页 --%> <% User user = HrmUserVarify.getUser(request, response); if (user == null) { response.sendRedirect("/login/Login.jsp"); return; } // 方案A：页面与请求保持UTF-8 request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); // 源日志编码（默认GBK，可传GB2312/GB18030） String srcCharset = request.getParameter("srcCharset"); if (srcCharset == null || srcCharset.trim().isEmpty()) { srcCharset = "GBK"; } // 2. 核心参数：当前ecology日志路径（固定路径，无需日期） String logDirPath = request.getRealPath("/") + "log" + File.separator; File logFile = new File(logDirPath + "ecology"); // 3. 显示行数设置：默认100行，限制10-1000行 String lineCountStr = request.getParameter("lineCount"); int lineCount = 100; if (lineCountStr != null && !lineCountStr.trim().isEmpty()) { try { lineCount = Integer.parseInt(lineCountStr.trim()); lineCount = Math.max(10, lineCount); lineCount = Math.min(1000, lineCount); } catch (NumberFormatException e) { lineCount = 100; } } List<String> logContent = new ArrayList<>(); String errorMsg = ""; // 日志级别统计 int infoCount = 0, errorCount = 0, warnCount = 0, debugCount = 0; if ("POST".equalsIgnoreCase(request.getMethod())) { try { if (!logFile…

[泛微 EC9] 开发踩坑：weaver.conn.RecordSet-报错"字符串文字太长"
type: item
date: 2025-10-17T09:51:00+08:00
description: AI辅助制作标识 该文章由丸子引导AI辅助制作生成，如有改进意见，欢迎评论区留言。 记录一次为泛微OA集成AI流程审查功能时遇到的技术难题与解决历程问题背景在最近为泛微OA系统集成AI流程智能审查功能的开发过程中，遇到了一个令人困扰的问题：当尝试将AI返回的审查结果写入数据库时，表单赋值操作始终无法成功完成。经过仔细排查系统日志，发现了关键的错误信息：2025-04-29 19:01:51,512 ERROR A2 - [null] resin-port-8081-363-363[weaver.conn.RecordSet:1908] - weaver.conn.RecordSet java.sql.SQLSyntaxErrorException: ORA-01704: 字符串文字太长这个错误表明，AI返回的字符串内容长度超过了4000字符，导致数据库写入失败。问题排查过程第一阶段：数据库字段类型检查首先检查了数据库表结构，发现目标字段确实是CLOB类型，理论上应该能够存储大文本数据：-- 检查字段类型 SELECT column_name, data_type FROM user_tab_columns WHERE table_name = 'FORMTABLE_MAIN_314' AND column_name = 'AISHJG';结果显示字段类型为CLOB，这让我们一度感到困惑——既然字段类型正确，为什么还会出现长度限制的错误？第二阶段：技术原理探究经过深入研究和测试，终于找到了问题的根源：RecordSet在更新CLOB字段时的局限性在Oracle数据库环境下，SQL语句在执行之前会将所有字符类型的数据隐式转换为VARCHAR2类型，而VARCHAR2类型的最大长度限制为4000字符。当字符串长度超过这个限制时，转换就会失败，抛出ORA-01704: 字符串文字太长异常。即使目标字段是CLOB类型，使用RecordSet的更新方法仍然会受到这个限制的影响。解决方案错误做法 ❌// 以下是会导致错误的代码示例 RecordSet rs = new RecordSet(); String sql = "update formtable_main_314 set aishjg= ? where requestid=12345"; rs.executeUpdate(sql, longContent);正确做法 ✅使用ConnStatement来更新大字段：ConnStatement st = null; try { st = new ConnStatement(); st.setStatementSql("update formtable_main_314 set aishjg=? where requestid=?"); // 设置参数值 st.setString(1, aiResultContent); // 支持CLOB大字段 st.setInt(2, 12345); // 请求ID st.executeUpdate(); } catch(Exception e) { // 异常处理 e.printStackTrace(); } finally { // 资源释放 if (st != null) { st.close(); } }重要技术说明泛微OA字段类型与数据库存储关系在Oracle数据库环境下：多行文本字段（未勾选HTML编辑）：存储为VARCHAR2(4000)多行文本字段（勾选HTML编辑）：存储为CLOB类型最佳实践建议预判内容长度：在处理可能包含大文本的场景（如AI返回结果、富文本内容、日志信息等）时，优先考虑使用ConnStatement。统一的数据库操作封装：public class DatabaseUtil { /** * 更新CLOB字段的通用方法 */ public static boolean updateClobField(String tableName, String clobField, String value, String whereCondition) { ConnStatement st = null; try { st = new ConnStatement(); String sql = "update " + tableName + " set " + clobField + "=? where " + whereCondition; st.setStatementSql(sql); st.setString(1, value); return st.executeUpdate() > 0; } catch(Exception e) { e.printStackTrac…

[泛微 EC9] 流程数据建模同表同步 & SQL插入建模数据
type: item
date: 2025-09-17T12:13:00+08:00
description: 泛微 EC9 流程数据建模实战：2 招搞定数据同步，formmodeid 是关键！ AI辅助制作标识 该文章由丸子引导AI辅助制作生成，如有改进意见，欢迎评论区留言。 在泛微 EC9 的日常运维中，流程数据与建模模块的数据打通，常常是让人头疼的难题——要么需要复杂的转数据操作，要么手动插入数据后建模模块“看不见”。其实，解开这个困局的钥匙，就藏在 formmodeid 这一个字段里。今天就分享两个“ALL IN BOOM”的实用技巧，让流程与建模数据同步变得简单直接。一、流程与建模“同表共用”：零转换，数据实时同步很多时候，我们希望流程中新增的数据，能直接在建模模块中显示，省去中间的“流程转数据”步骤。核心思路就是让流程和建模“共用一张数据表”，而 formmodeid 就是让建模“识别”数据的关键。操作步骤拆解：创建建模模块，绑定流程数据表新建建模模块时，在“数据表配置”环节，直接选择与目标流程相同的数据表（比如流程使用的表是 formtable_main_474，建模就直接绑定这张表）。创建完成后，务必记住这个建模模块的 ID（比如示例中的“92”），这就是后续要用的 formmodeid 值。流程节点执行 DML，给数据“贴标签”在流程设计的某个节点（通常是“结束节点”或“数据提交节点”），添加一个“执行 SQL”的动作，通过 update 语句给表中的数据统一赋值 formmodeid。通用 SQL 模板：update 【流程数据表名】 set formmodeid = '【建模模块 ID】' where 1=1示例（对应表 formtable_main_474、建模 ID“92”）：update formtable_main_474 set formmodeid = '92' where 1=1效果立竿见影：流程中新增的数据，会自动带上 formmodeid = 92 的“标签”，而建模模块因为绑定了同一张表，且默认只识别对应 formmodeid 的数据，所以流程数据会直接在建模中显示，不需要任何额外的转换或同步操作。说白了：建模模块“看不见”数据，往往就差给数据赋上正确的 formmodeid 这一步。二、SQL 直接插入建模数据：formmodeid 是“通行证”无论是二次开发中需要批量导入数据，还是通过 DML 语句手动插入数据，想要让数据在泛微 EC9 建模模块中正常显示，核心还是不能漏掉 formmodeid 字段。关键操作：在写 insert 语句时，除了要插入业务字段（如姓名、日期、金额等），必须额外加上 formmodeid = 【建模模块 ID】 的赋值。示例（向建模模块 ID 为“92”的模块插入数据）：insert into formtable_main_474 (field1, field2, formmodeid) values ('业务数据 1', '业务数据 2', '92')原理很简单：泛微 EC9 的建模模块，会通过 formmodeid 来区分不同模块的数据归属。如果插入数据时没有指定 formmodeid，或者值错误，数据虽然会进入数据表，但建模模块无法“认领”它，自然就不会显示。这就像给数据办了一张“通行证”，formmodeid 就是通行证上的“目的地”，只有目的地正确，数据才能顺利“进入”建模模块。总结：formmodeid——流程与建模数据打通的“核心密钥”泛微 EC9 中流程与建模的数据同步，看似复杂，实则只要抓住 formmodeid 这个核心字段，就能化繁为简：同表共用时，用 update 给数据赋对 formmodeid，实现“数据即同步”；SQL 插入时，给数据带上 formmodeid，让建模模块“认得出”数据。不用纠结复杂的配置，也不用写大量代码，聚焦 formmodeid 这一个字段，就能轻松搞定流程与建模的数据打通，效率直接拉满！

[泛微 E10] 明细表处理示例JS 1：字段联动后判断明细表数据隐藏选项
type: item
date: 2025-07-09T11:00:19+08:00
description: 在表单开发中，明细表（动态表格）的字段联动和数据控制是常见需求。比如当某个字段的值满足特定条件时，需要动态隐藏另一个字段的部分选项。本文通过一个简化的 Demo，演示如何实现这一功能。场景说明假设我们有一个“学生成绩表”明细表，包含两个字段：成绩类型（type）：下拉选择框，选项为 ["优秀(0)", "良好(1)", "及格(2)", "不及格(3)"]分数（score）：数字输入框需求：当“分数”字段的值为空或 0 时，自动隐藏“成绩类型”中的“优秀”和“良好”选项（即移除选项值为 0 和 1 的选项）。实现思路获取明细表及字段的标识（ID）监听数据联动事件（如分数变化后触发）遍历明细表的每一行，判断分数值是否符合条件若符合条件，隐藏当前行“成绩类型”的指定选项Demo 代码实现// 明细表标识（实际开发中通常由表单 SDK 自动生成） const detailTableId = "student_score_table"; // 获取明细表中字段的 ID（参数：字段名，明细表 ID） const typeFieldId = formSdk.convertFieldNameToId("type", detailTableId); const scoreFieldId = formSdk.convertFieldNameToId("score", detailTableId); // 监听数据联动事件（当字段值变化时触发） formSdk.registerAction(window.WeFormSDK.ACTION_DATA_LINKAGE, (data) => { // 获取明细表所有行的 ID 字符串（格式："row1,row2,row3"） const allRowIdsStr = formSdk.getDetailAllRowIndexStr(detailTableId); if (!allRowIdsStr) return; // 无数据行时直接返回 // 解析行 ID 为数组 const rowIds = allRowIdsStr.split(','); // 遍历每一行 rowIds.forEach(rowId => { // 拼接当前行的分数字段 ID（格式：字段 ID_行 ID） const currentScoreField = `${scoreFieldId}_${rowId}`; // 获取当前行的分数值 const scoreValue = formSdk.getFieldValue(currentScoreField); // 判断分数是否为空或 0 if (scoreValue === '' || scoreValue === '0' || scoreValue === 0) { // 拼接当前行的成绩类型字段 ID const currentTypeField = `${typeFieldId}_${rowId}`; // 移除选项值为 0 和 1 的选项（即"优秀"和"良好"） formSdk.removeSelectOption(currentTypeField, "0,1"); } }); });代码解析字段标识获取通过 formSdk.convertFieldNameToId 方法，将字段名转换为表单内部使用的 ID，需传入字段名和明细表 ID（确保在明细表中唯一）。事件监听使用 formSdk.registerAction 监听 ACTION_DATA_LINKAGE 事件，该事件在数据联动（如字段值变化、表单加载完成等场景）时触发，确保逻辑在数据更新后执行。行遍历处理通过 formSdk.getDetailAllRowIndexStr 获取所有行的 ID 字符串，拆分后得到每一行的唯一标识（rowId）。对每一行，通过 formSdk.getFieldValue 获取分数值，判断是否为空或 0。选项隐藏当条件满足时，调用 formSdk.removeSelectOption 方法，传入当前行的“成绩类型”字段 ID 和需要移除的选项值（"0,1"），实现选项的动态隐藏。关键 API 说明方法作用convertFieldNameToId(fieldName, detailTableId)将字段名转换为表单内部 ID（明细表字段需传入明细表 ID）registerAction(actionName, callback)注册事件监听，这里监听数据联动事件getDetailAllRowIndexStr(detailTableId)获取明细表所有行的 ID 字符串（格式："row1,row2"）getFieldValue(fieldId)获取指定字段的值removeSelectOption(…

[泛微 E10] 批量下载当前表单内所有附件
type: item
date: 2025-07-07T00:18:18+08:00
description: 功能背景泛微 E10 支持单附件字段的批量下载能力。但默认情况下，用户只能逐个字段进行批量下载附件，当表单包含大量附件字段时，操作效率较低。通过开发批量下载功能，提升用户体验。技术实现方案下面是实现批量下载功能的核心代码，主要分为三个步骤：收集附件 ID、创建异步下载任务、轮询检查任务状态并下载：// 初始化 SDK 实例 const weFormSdk = window.WeFormSDK.getWeFormInstance(); // 定义需要下载附件的字段名称，请修改为对应的主表字段名称 const fieldNames = [ "sigvendatf", "poa", "regcer", "taxcer", "pscq", "pscend", "bancon", "conpo", "emaappandc" ]; // 收集所有附件 ID const values = []; fieldNames.forEach(fieldName => { try { const fieldId = weFormSdk.convertFieldNameToId(fieldName); const fieldValue = weFormSdk.getFieldValue(fieldId); if (fieldValue) { values.push(fieldValue); } } catch (error) { console.error(` 获取字段 ${fieldName} 失败:`, error); } }); // 创建异步下载任务 async function createDownloadTask() { if (values.length === 0) { window.WeFormSDK.showMessage("没有可下载的文件", 1, 3); return; } const today = new Date().toISOString().split('T')[0]; // 获取当前日期 try { const response = await fetch('/api/file/batch/asynchronous/downloadByFileIds', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ "module": "ebuildercard", "fileIds": values.join(','), "authModule": "ebuildercard", "source": "form", "zipName": `附件名称在这里-请修改或赋值变量-${today}` }) }); const data = await response.json(); if (!data.data.taskId) { throw new Error('获取任务 ID 失败'); } window.WeFormSDK.showMessage("开始创建下载任务", 3, 2); await checkDownloadStatus(data.data.taskId); } catch (error) { console.error('创建下载任务失败:', error); window.WeFormSDK.showMessage("创建下载任务失败", 2, 3); } } // 轮询检查下载任务状态 async function checkDownloadStatus(taskId) { let attempt = 0; const maxAttempts = 20; // 最多尝试 20 次 while (attempt < maxAttempts) { try { const response = await fetch(`/api/file/batchDownloadPercent?uuid=${taskId}`); const data = await response.json(); if (data.data.fileId) { window.WeFormSDK.showMessage("下载任务已完成", 3, 2); downloadFile(data.data.fileId); return; } attempt++; if (attempt < maxAttempts) { await new Promise(resolve => setTimeout(resolve, 500)); // 等待 500ms } } catch (error) { console.erro…