export var camelToKebab = function (camelStr) { const isUpper = camelStr[0].charCodeAt(0) >= 65 && camelStr[0].charCodeAt(0) <= 90; const handleStr = camelStr.replace(/([A-Z])/g, "-$1").toLowerCase(); let kebabStr = handleStr; if (isUpper) { kebabStr = handleStr.slice(1); } const newKebabArr = []; const kebabSplitArr = kebabStr.split("-"); kebabSplitArr.forEach((item, index) => { if (item.length > 1) { newKebabArr.push(item); } else { let combineStr = ""; const subKebabArr = kebabSplitArr.slice(index); for (let i = 0; i < subKebabArr.length; i++) { if (subKebabArr[i].length > 1) break; combineStr += subKebabArr[i]; } newKebabArr.push(combineStr); kebabSplitArr.splice(index + 1, combineStr.length - 1); } }); return newKebabArr.join("-"); }; export var isNum = function (num) { return typeof num === "number" && !isNaN(num); }; export var toStyleStr = (styleObj, unit = "rpx") => { if (Array.isArray(styleObj)) { return styleObj.join(";"); } else { if (typeof styleObj === "object" && styleObj !== null) { let style = []; for (const k in styleObj) { if (typeof k === "string") { let val = styleObj[k]; if (val !== void 0) { if (typeof val === "number") { if (addUnitAttr.includes(k)) { val = val + unit; } } style.push(`${camelToKebab(k)}:${val}`); } } } return style.length > 0 ? style.join(";") + ";" : ""; } else { return styleObj; } } }; function isObject(value) { var type = typeof value; return value != null && (type == 'object' || type == 'function'); } function toNumber(value) { if (typeof value == 'number') { return value; } if (isSymbol(value)) { return NAN; } if (isObject(value)) { var other = typeof value.valueOf == 'function' ? value.valueOf() : value; value = isObject(other) ? (other + '') : other; } if (typeof value != 'string') { return value === 0 ? value : +value; } value = value.replace(reTrim, ''); var isBinary = reIsBinary.test(value); return (isBinary || reIsOctal.test(value)) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : (reIsBadHex.test(value) ? NAN : +value); } export function debounce(func, wait, options) { var lastArgs, lastThis, maxWait, result, timerId, lastCallTime, lastInvokeTime = 0, leading = false, maxing = false, trailing = true; if (typeof func != 'function') { throw new TypeError(FUNC_ERROR_TEXT); } wait = toNumber(wait) || 0; if (isObject(options)) { leading = !!options.leading; maxing = 'maxWait' in options; maxWait = maxing ? nativeMax(toNumber(options.maxWait) || 0, wait) : maxWait; trailing = 'trailing' in options ? !!options.trailing : trailing; } function invokeFunc(time) { var args = lastArgs, thisArg = lastThis; lastArgs = lastThis = undefined; lastInvokeTime = time; result = func.apply(thisArg, args); return result; } function leadingEdge(time) { // Reset any `maxWait` timer. lastInvokeTime = time; // Start the timer for the trailing edge. timerId = setTimeout(timerExpired, wait); // Invoke the leading edge. return leading ? invokeFunc(time) : result; } function remainingWait(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime, timeWaiting = wait - timeSinceLastCall; return maxing ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke) : timeWaiting; } function shouldInvoke(time) { var timeSinceLastCall = time - lastCallTime, timeSinceLastInvoke = time - lastInvokeTime; // Either this is the first call, activity has stopped and we're at the // trailing edge, the system time has gone backwards and we're treating // it as the trailing edge, or we've hit the `maxWait` limit. return (lastCallTime === undefined || (timeSinceLastCall >= wait) || (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait)); } function timerExpired() { var time = now(); if (shouldInvoke(time)) { return trailingEdge(time); } // Restart the timer. timerId = setTimeout(timerExpired, remainingWait(time)); } function trailingEdge(time) { timerId = undefined; // Only invoke if we have `lastArgs` which means `func` has been // debounced at least once. if (trailing && lastArgs) { return invokeFunc(time); } lastArgs = lastThis = undefined; return result; } function cancel() { if (timerId !== undefined) { clearTimeout(timerId); } lastInvokeTime = 0; lastArgs = lastCallTime = lastThis = timerId = undefined; } function flush() { return timerId === undefined ? result : trailingEdge(now()); } function debounced() { var time = now(), isInvoking = shouldInvoke(time); lastArgs = arguments; lastThis = this; lastCallTime = time; if (isInvoking) { if (timerId === undefined) { return leadingEdge(lastCallTime); } if (maxing) { // Handle invocations in a tight loop. timerId = setTimeout(timerExpired, wait); return invokeFunc(lastCallTime); } } if (timerId === undefined) { timerId = setTimeout(timerExpired, wait); } return result; } debounced.cancel = cancel; debounced.flush = flush; return debounced; } // 功能说明:统一处理后端可能返回的 {val}/{value} 结构,供组件内部字段适配复用 by xu 20260122 export function normalizeMaybeVal(input) { if (input === undefined || input === null) return input; if (typeof input === "object") { if (Object.prototype.hasOwnProperty.call(input, "val")) return input.val; if (Object.prototype.hasOwnProperty.call(input, "value")) return input.value; } return input; } // 功能说明:缩略图 URL 构建(dlByHttp 直出图片),支持 thn 为字符串或 {val}/{value} by xu 20260122 export function buildThumbUrl(thn) { const v = normalizeMaybeVal(thn); if (v === undefined || v === null || v === "") return ""; return "/service?ssServ=dlByHttp&type=img&path=" + encodeURIComponent(String(v)); } // 功能说明:统一按 fmt 动态格式化 {val, fmt}(支持任意组合 yyyy/MM/dd/HH/mm/ss),供卡片 tags/标题复用 by xu 20260122 export function formatValByFmt(val, fmt) { try { if (!fmt || val === undefined || val === null || val === "") return val; const raw = typeof val === "string" ? val.replace(/[\u202f\u00a0]/g, " ").replace(/ /g, " ").trim() : val; const d = raw instanceof Date ? raw : new Date(raw); if (isNaN(d.getTime())) return val; const pad2 = (n) => String(n).padStart(2, "0"); const tokens = { yyyy: String(d.getFullYear()), MM: pad2(d.getMonth() + 1), M: String(d.getMonth() + 1), dd: pad2(d.getDate()), d: String(d.getDate()), HH: pad2(d.getHours()), H: String(d.getHours()), mm: pad2(d.getMinutes()), m: String(d.getMinutes()), ss: pad2(d.getSeconds()), s: String(d.getSeconds()), }; const f = String(fmt); if (!/(yyyy|MM|dd|HH|mm|ss|M|d|H|m|s)/.test(f)) return val; return f.replace(/yyyy|MM|dd|HH|mm|ss|M|d|H|m|s/g, (k) => tokens[k] ?? k); } catch (_) { return val; } } // 功能说明:解析 parm/param 参数(兼容 JSON 字符串、qs 字符串、对象) by xu 20260122 export function parseParmLike(parmOrParam) { if (!parmOrParam) return {}; if (typeof parmOrParam === "object") return parmOrParam; if (typeof parmOrParam !== "string") return {}; const s = String(parmOrParam).trim(); if (!s) return {}; // JSON 格式 if (s[0] === "{" || s[0] === "[") { try { const obj = JSON.parse(s); if (obj && typeof obj === "object") return obj; } catch (_) {} } // qs 格式(&a=b 或 a=b) return { __rawQs: s }; } // 功能说明:追加查询参数到 URL(支持去重、数组值) by xu 20260122 export function appendQueryParam(url, key, value) { if (!key) return url; if (value === undefined || value === null) return url; const s = String(value); if (s === "") return url; // 检查参数是否已存在 const escapeRegExp = (str) => String(str || "").replace(/[.*+?^{}$()|[\]\\]/g, "\\$&"); const hasParam = new RegExp("([?&])" + escapeRegExp(key) + "=").test(String(url || "")); if (hasParam) return url; const separator = url.indexOf("?") > -1 ? "&" : "?"; return url + separator + encodeURIComponent(String(key)) + "=" + encodeURIComponent(s); } // 功能说明:从搜索字段列表和表单中提取参数(用于透传搜索条件到弹窗) by xu 20260122 export function pickSearchParams(searchFieldList, form) { const params = {}; if (!Array.isArray(searchFieldList) || !form) return params; searchFieldList.forEach((field) => { const key = field && field.name; if (!key) return; const value = form[key]; if (value === undefined || value === null) return; if (Array.isArray(value)) { params[key] = value.filter((v) => v !== undefined && v !== null && v !== ""); } else if (value !== "") { params[key] = value; } }); return params; } // 功能说明:构建服务 URL(统一处理 ssToken/servName+dest+parm 三种模式) by xu 20260122 export function buildServiceUrl(srv, options = {}) { if (!srv) return ""; const { extraParams = {} } = options; let url = ""; // 模式1:优先 ssToken const token = String(srv.ssToken || "").trim(); if (token) { url = "/service?ssToken=" + encodeURIComponent(token); } else if (srv.servName) { // 模式2:servName + dest + parm url = "/service?ssServ=" + encodeURIComponent(String(srv.servName)); if (srv.dest) url += "&ssDest=" + encodeURIComponent(String(srv.dest)); // 解析 parm/param const parm = srv.parm !== undefined ? srv.parm : srv.param; const parsed = parseParmLike(parm); if (parsed.__rawQs) { // qs 格式直接拼接 const qs = parsed.__rawQs; url += (qs.startsWith("&") ? "" : "&") + qs; } else { // 对象格式遍历拼接 Object.entries(parsed).forEach(([k, v]) => { if (v === undefined || v === null) return; url += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(String(v)); }); } } else if (srv.dest) { // 模式3:仅 dest(报表场景) url = "/service?ssDest=" + encodeURIComponent(String(srv.dest)); const parm = srv.parm !== undefined ? srv.parm : srv.param; const parsed = parseParmLike(parm); if (parsed.__rawQs) { const qs = parsed.__rawQs; url += (qs.startsWith("&") ? "" : "&") + qs; } else { Object.entries(parsed).forEach(([k, v]) => { if (v === undefined || v === null) return; url += "&" + encodeURIComponent(k) + "=" + encodeURIComponent(String(v)); }); } } // 追加额外参数 Object.entries(extraParams).forEach(([key, value]) => { if (Array.isArray(value)) { value.forEach((v) => { url = appendQueryParam(url, key, v); }); } else { url = appendQueryParam(url, key, value); } }); return url; } // 功能说明:打开服务弹窗(统一封装 wd.display.showComponent) by xu 20260122 export function openServiceDialog(srv, options = {}) { if (!srv) return; const { extraParams = {}, onError = null } = options; const url = buildServiceUrl(srv, { extraParams }); if (!url) return; try { window.wd.display.showComponent({ show: ["wdDialog"], url, title: srv.title || srv.desc || "", width: srv.width, height: srv.height, minHeight: srv.minHeight, maxHeight: srv.maxHeight, showTitle: srv.showTitle, }); } catch (error) { console.error("[openServiceDialog] failed:", error); if (onError) onError(error); } } // 功能说明:全局暴露工具函数,供 JSP 中的 Vue 实例使用 by xu 20260122 if (typeof window !== "undefined") { window.ssTools = { normalizeMaybeVal, buildThumbUrl, formatValByFmt, parseParmLike, appendQueryParam, pickSearchParams, buildServiceUrl, openServiceDialog, }; }