以下为低代码MK开发的一点小知识笔记

1. 使用案例(明细表字段添加跳转链接)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
/**
* onLoad_3 - 明细表加载完成后触发的回调事件
* 核心功能:遍历明细表数据,为每个合同字段查询对应的流程链接,为文本元素包裹a标签实现跳转
*/
function onLoad_3() {
// 仅在"查看"模式下执行(低代码平台视图状态:view=查看,edit=编辑,add=新增)
if (MKXFORM.viewStatus == 'view') {
// 字段标识前缀:对应明细表中合同字段的基础标识(后续拼接行唯一key)
const fieldPrefix = "mk_payment_request_detail.fd_col_contract~";
// 获取明细表数据:MKXFORM.getValue("明细表字段标识").values 是低代码平台获取明细表数据的固定方式
const tables = MKXFORM.getValue("mk_payment_request_detail").values;

// 前置校验:确保明细表有数据
if (tables && Array.isArray(tables) && tables.length > 0) {
// 遍历明细表每一行数据
for (let i = 0; i < tables.length; i++) {
// 动态拼接当前行合同字段的唯一标识(tables[i].key 是每行的唯一key,确保字段标识不重复)
const field_contract = fieldPrefix + tables[i].key;
// 获取当前行的合同数据(格式:[{fdId: "xxx", fdName: "xxx"}])
const contract = tables[i]["fd_col_contract"];

// 校验合同数据有效性:确保存在且有fdId(fdId为合同台账的唯一标识,用于查询流程链接)
if (contract && Array.isArray(contract) && contract.length > 0 && contract[0].fdId) {
/**
* 调用集成平台第三方接口:【合同台账】根据ID查询流程ID
* 接口参数:fdId(合同唯一标识)
* 回调函数:处理接口返回结果,生成跳转链接并设置a标签
*/
MKXFORM.callTic({
interfaceCode: "function_6f73n7dgw6vr7xi5", // 集成平台注册的接口编码(固定值)
param: { "fdId": contract[0].fdId } // 接口请求参数:合同fdId
}, function (error, res) {
// 接口调用成功且返回有效流程ID(fdSourceId为流程详情页的核心标识)
if (res && res.data && res.data.fdSourceId) {
// 拼接流程详情页跳转链接(低代码平台流程查看页固定URL格式)
const targetUrl = 'https://xxx.xxx/xxx/view/' + res.data.fdSourceId;
// 调用工具函数:为当前字段的文本元素包裹a标签
window.MKXFORM.GLOBALJS.setA2Target(field_contract, targetUrl);

// 重试逻辑:解决低代码平台DOM渲染延迟导致的元素未找到问题
let retryCount = 0; // 重试计数器
const maxRetry = 5; // 最大重试次数(避免无限循环)
const timer = setInterval(() => {
// 字段标识转义:处理特殊字符,确保CSS选择器合法
const fieldReplace = window.MKXFORM.GLOBALJS.escapeFieldForSelector(field_contract);
// 查询是否已成功创建a标签(通过转义后的类名定位)
const allCus = document.querySelectorAll('.custom-parent-a-' + fieldReplace);

if (allCus.length > 0) {
// 已找到a标签,停止重试
clearInterval(timer);
} else if (retryCount >= maxRetry) {
// 重试次数用尽,停止重试并打印警告
clearInterval(timer);
console.warn("设置a标签超时,目标元素未找到", { field: field_contract });
} else {
// 未找到,重新执行a标签设置
window.MKXFORM.GLOBALJS.setA2Target(field_contract, targetUrl);
retryCount++; // 计数器自增
}
}, 1000); // 重试间隔:1秒(适配DOM渲染延迟)

} else {
// 接口调用失败或未返回有效fdSourceId,打印错误日志(便于排查问题)
console.error("onLoad_3:查询流程ID失败,未获取到有效fdSourceId", { error, res, field: field_contract });
}
});
} else {
// 合同数据无效,打印警告日志(非错误,仅告知无需执行后续逻辑)
console.warn("onLoad_3:未获取到有效项目ID,无需调用接口", { contract, field: field_contract });
}
}
}
}
}

2. 关键代码:setA2Target(为元素包裹 a 标签)

为目标字段的文本元素添加<a>标签父元素,实现跳转功能,其他标签(如 divspan)可参考此逻辑修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* setA2Target - 为目标字段的文本元素包裹a标签,配置跳转链接
* @param {string} field - 字段唯一标识(如:mk_payment_request_detail.fd_col_contract~xxx)
* @param {string} url - 跳转链接(流程详情页URL)
*/
function setA2Target(field, url) {
try {
// 步骤1:字段标识转义(处理.、~等CSS特殊字符,避免选择器语法错误)
const fieldReplace = window.MKXFORM.GLOBALJS.escapeFieldForSelector(field);
// 步骤2:查询目标元素(低代码平台字段文本元素固定结构:[data-name="字段标识"] > span.ele-item-text)
const targetEl = document.querySelector("[data-name='" + field + "'] span.ele-item-text");

// 校验:若未找到目标元素,打印警告并返回(避免后续代码报错)
if (!targetEl) {
console.warn("setA2Target:未找到目标元素", { field, targetSelector: "[data-name='" + field + "'] span.ele-item-text" });
return;
}

// 校验:避免重复创建a标签(通过closest查询当前元素是否已被目标a标签包裹)
const existingA = targetEl.closest('.custom-parent-a-' + fieldReplace);
if (existingA) {
existingA.href = url; // 若已存在,直接更新链接(适配数据刷新场景)
return;
}

// 步骤3:创建a标签(作为目标元素的父元素)
const parentWrap = document.createElement('a');
// 给a标签添加类名(含转义后的字段标识,便于后续查询)
parentWrap.className = 'custom-parent-a-' + fieldReplace;
// 设置跳转链接
parentWrap.setAttribute('href', url);
// 设置新窗口打开(避免当前页面跳转,提升用户体验)
parentWrap.setAttribute('target', '_blank');
// 安全优化:防止新窗口劫持(低代码平台安全规范要求)
parentWrap.setAttribute('rel', 'noopener noreferrer');

// 步骤4:插入a标签并包裹目标元素
// 1. 将a标签插入到目标元素的父节点中,位于目标元素之前
targetEl.parentNode.insertBefore(parentWrap, targetEl);
// 2. 将目标元素移动到a标签内部(完成包裹)
parentWrap.appendChild(targetEl);

} catch (e) {
// 捕获异常并打印详细日志(便于问题排查)
console.error("设置a元素失败", { field, url, error: e.message, stack: e.stack });
}
}

3. 关键代码:escapeFieldForSelector(字段标识转义工具)

处理字段标识中的 CSS 特殊字符(如 . ~ $ 等),避免生成无效的 CSS 选择器,导致元素查询失败。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* escapeFieldForSelector - 字段标识转义工具函数
* 核心作用:将含特殊字符的字段标识转为CSS选择器支持的格式,避免语法错误
* @param {string} field - 原始字段标识(可能含.、~、$、数字开头等特殊场景)
* @returns {string} 转义后的字段标识(可安全用于CSS选择器)
*/
function escapeFieldForSelector(field) {
if (!field) return '';
return field
.replace(/\./g, '\\.') // 转义小数点(.在CSS中表示多类名分隔符)
.replace(/~/g, '\\~') // 转义波浪号(~在CSS中表示相邻兄弟选择器)
.replace(/\$/g, '\\$') // 转义美元符号($在CSS中是特殊字符)
.replace(/^(\d)/, '\\3$1 ') // 转义以数字开头的字段(CSS类名不能直接以数字开头,需用\3+数字+空格转义)
.replace(/__(\d)/, '__\\3$1 '); // 转义双下划线后紧跟数字的场景(如__0.xxx)
}