20260728173026

This commit is contained in:
oneao committed 2026-07-28 17:30:27 +08:00
1 parent 63588bd217
commit 5ec7aadef9
25 files changed
+627 -462

No files matched your search

+67
View File
@@ -62,3 +62,70 @@ export function groupI18nRows(rows) {
});
return groups;
}
// 将同步字段生成的默认翻译条目并入透视行数组:
// - 已存在同 b_key 的行则补全其空语言值(不覆盖用户已填内容);
// - 否则新增一行;managedId 用于关联字段,使重命名字段时复用同一行(改名而非新增)。
// 返回新的数组,不修改入参。
export function mergeI18nDefaults(rows, entries) {
const next = [...rows];
const byKey = new Map(next.map((r) => [String(r.b_key), r]));
const managedIds = new Set(
next
.map((r) => r._managedId)
.filter(Boolean)
.map(String),
);
for (const e of entries || []) {
if (!e.key) continue;
const existing = byKey.get(String(e.key));
if (existing) {
// 已存在同键行:仅补全空值,保留用户已配置的翻译
if (existing[e.locale] == null || existing[e.locale] === "") existing[e.locale] = e.value;
continue;
}
// 同键不存在,但该受管键已由其它行代表时不再新建,避免重复键
if (e.managedId && managedIds.has(String(e.managedId))) continue;
const row = {
_id: "i18n_" + Math.random().toString(36).slice(2, 10),
b_key: e.key,
_localeIds: {},
_managedId: e.managedId ? String(e.managedId) : undefined,
_syncInjected: e.managedId ? true : undefined,
[e.locale]: e.value,
};
next.push(row);
byKey.set(String(e.key), row);
if (e.managedId) managedIds.add(String(e.managedId));
}
return next;
}
// 合并同 b_key 的重复透视行,保证一个键仅一行:
// 多行同键时保留各自非空的语言值,受管标记(_managedId)、_syncInjected、排序/默认值取优先者。
// 返回新的数组,不修改入参。
export function consolidatePivotRows(rows) {
const byKey = new Map();
const merged = [];
for (const row of rows) {
const key = String(row.b_key || "");
const hit = byKey.get(key);
if (hit) {
for (const [k, v] of Object.entries(row)) {
if (k === "_id" || k === "_localeIds") continue;
// 仅当目标为空(null/undefined/空串)且来源非空时才补全,保留首个非空值
if ((hit[k] == null || hit[k] === "") && v != null) hit[k] = v;
}
Object.assign(hit._localeIds || (hit._localeIds = {}), row._localeIds || {});
if (row._managedId && !hit._managedId) hit._managedId = row._managedId;
if (row._syncInjected && !hit._syncInjected) hit._syncInjected = row._syncInjected;
if (row._defaultValue != null && hit._defaultValue == null)
hit._defaultValue = row._defaultValue;
if (row._sortType && !hit._sortType) hit._sortType = row._sortType;
} else {
byKey.set(key, row);
merged.push(row);
}
}
return merged;
}
+56
View File
@@ -0,0 +1,56 @@
/**
* 将扁平数组构建为树结构
* @param {Array} items - 扁平数据
* @param {object} [options]
* @param {string} [options.idKey='b_id'] - ID 字段名
* @param {string} [options.parentKey='b_parent_id'] - 父级 ID 字段名
* @param {string} [options.childrenKey='children'] - 子节点字段名
* @param {Function} [options.mapNode] - 节点映射函数 (item) => node,用于创建新的树节点对象
* @returns {Array} 树形结构
*/
export function buildTree(items, options = {}) {
const { idKey = "b_id", parentKey = "b_parent_id", childrenKey = "children", mapNode } = options;
const map = new Map();
const roots = [];
for (const item of items) {
const node = mapNode ? mapNode(item) : item;
map.set(String(item[idKey]), node);
}
for (const item of items) {
const node = map.get(String(item[idKey]));
const parentId = item[parentKey] != null ? String(item[parentKey]) : null;
if (parentId && map.has(parentId)) {
const parent = map.get(parentId);
if (!parent[childrenKey]) parent[childrenKey] = [];
parent[childrenKey].push(node);
} else {
roots.push(node);
}
}
return roots;
}
/**
* 递归排序树节点(默认按 b_xh 升序,b_id 升序)
* @param {Array} nodes - 树节点
* @param {object} [options]
* @param {string} [options.sortKey='b_xh'] - 排序字段
* @param {string} [options.idKey='b_id'] - 第二排序字段
* @param {string} [options.childrenKey='children'] - 子节点字段名
*/
export function sortTree(
nodes,
{ sortKey = "b_xh", idKey = "b_id", childrenKey = "children" } = {},
) {
nodes.sort(
(a, b) =>
Number(a[sortKey] || 0) - Number(b[sortKey] || 0) ||
String(a[idKey]).localeCompare(String(b[idKey])),
);
for (const node of nodes) {
if (node[childrenKey]?.length) sortTree(node[childrenKey], { sortKey, idKey, childrenKey });
}
}