refactor(fields): 增强字段管理逻辑以支持唯一值和调试

- 在FieldsManager类中为子表字段节点添加表名前缀,确保值的唯一性
- 添加调试日志以检查树节点的重复值,提升代码可维护性
- 更新路径转换逻辑,确保字段回显时的有效性检查
- 增强树节点查找逻辑,支持更灵活的路径匹配
This commit is contained in:
zhouyonggao 2025-12-17 16:41:29 +08:00
parent 3d75cfec1d
commit 143199dcd3
2 changed files with 131 additions and 28 deletions

View File

@ -134,19 +134,29 @@ const app = createApp({
*/
const setTreeChecked = (kind, values) => {
const tree = kind === 'create' ? createFieldsTree.value : editFieldsTree.value;
if (!tree || !values || !Array.isArray(values)) return;
if (!tree || !values || !Array.isArray(values)) {
console.log(`[设置树勾选] 跳过: kind=${kind}, tree存在=${!!tree}, values类型=${Array.isArray(values) ? 'array' : typeof values}`);
return;
}
const treeData = kind === 'create' ? fieldTreeData.value : editFieldTreeData.value;
console.log(`[设置树勾选] kind=${kind}, values数量=${values.length}, 树数据存在=${!!treeData}`);
const keys = TreeUtils.pathsToNodeKeys(values, treeData);
console.log(`[设置树勾选] 转换后keys数量=${keys.length}`);
Vue.nextTick(() => {
setTimeout(() => {
try {
if (keys.length > 0 && tree) {
console.log(`[设置树勾选] 调用 setCheckedKeyskeys数量: ${keys.length}`);
tree.setCheckedKeys(keys);
console.log(`[设置树勾选] setCheckedKeys 调用成功`);
} else {
console.warn(`[设置树勾选] 未调用 setCheckedKeys: keys.length=${keys.length}, tree存在=${!!tree}`);
}
} catch (error) {
console.warn('设置树形选择器选中状态失败:', error);
console.warn('[设置树勾选] 设置树形选择器选中状态失败:', error);
}
}, CONSTANTS.TREE_RENDER_DELAY);
});

View File

@ -91,13 +91,28 @@ class FieldsManager {
* 构建树节点
* @param {string} table - 表名
* @param {Array} children - 子节点
* @param {string} parentPrefix - 父级前缀用于构建唯一value
* @returns {Object} 树节点
*/
buildTreeNode(table, children = []) {
buildTreeNode(table, children = [], parentPrefix = '') {
// 为子表的字段节点添加表名前缀,确保 value 唯一
const childrenWithPrefix = children.map(child => {
if (child.children) {
// 是子表节点,递归处理
return child;
} else {
// 是字段节点,添加表名前缀
return {
value: table,
value: `${table}.${child.value}`,
label: child.label
};
}
});
return {
value: parentPrefix ? `${parentPrefix}.${table}` : table,
label: this.getTableLabel(table),
children
children: childrenWithPrefix
};
}
@ -108,7 +123,8 @@ class FieldsManager {
*/
buildYmtOrderChildren(orderType) {
const orderFields = this.getTableFields('order');
const children = orderFields.map(f => ({ value: f.value, label: f.label }));
// 使用 table.field 格式作为唯一 value
const children = orderFields.map(f => ({ value: `order.${f.value}`, label: f.label }));
// 添加公共子表(所有订单类型都有)
children.push(this.buildTreeNode('merchant', this.getTableFields('merchant')));
@ -135,6 +151,23 @@ class FieldsManager {
children.push(this.buildTreeNode('goods_voucher_subject_config', this.getTableFields('goods_voucher_subject_config')));
}
// 调试:检查是否有重复的 value
const valueMap = new Map();
const checkDuplicates = (nodes, prefix = '') => {
nodes.forEach(node => {
const fullPath = prefix ? `${prefix}/${node.value}` : node.value;
if (valueMap.has(node.value)) {
console.warn(`[树节点重复] 检测到重复的节点value: "${node.value}", 路径: ${fullPath}, 之前出现在: ${valueMap.get(node.value)}`);
} else {
valueMap.set(node.value, fullPath);
}
if (node.children && node.children.length > 0) {
checkDuplicates(node.children, fullPath);
}
});
};
checkDuplicates(children, 'order');
return children;
}
@ -145,25 +178,26 @@ class FieldsManager {
*/
buildMarketingOrderChildren(orderType) {
const orderFields = this.getTableFields('order');
const children = orderFields.map(f => ({ value: f.value, label: f.label }));
// 使用 table.field 格式作为唯一 value
const children = orderFields.map(f => ({ value: `order.${f.value}`, label: f.label }));
// 订单详情
children.push(this.buildTreeNode('order_detail', this.getTableFields('order_detail')));
// 构建计划节点(包含 key_batch -> code_batch 嵌套)
const planChildren = [
...this.getTableFields('plan').map(f => ({ value: f.value, label: f.label })),
...this.getTableFields('plan').map(f => ({ value: `plan.${f.value}`, label: f.label })),
this.buildTreeNode('key_batch', [
...this.getTableFields('key_batch').map(f => ({ value: f.value, label: f.label })),
...this.getTableFields('key_batch').map(f => ({ value: `key_batch.${f.value}`, label: f.label })),
this.buildTreeNode('code_batch', this.getTableFields('code_batch'))
])
];
// 构建立减金节点(包含 voucher -> voucher_batch 嵌套)
const voucherChildren = [
...this.getTableFields('order_voucher').map(f => ({ value: f.value, label: f.label })),
...this.getTableFields('order_voucher').map(f => ({ value: `order_voucher.${f.value}`, label: f.label })),
this.buildTreeNode('voucher', [
...this.getTableFields('voucher').map(f => ({ value: f.value, label: f.label })),
...this.getTableFields('voucher').map(f => ({ value: `voucher.${f.value}`, label: f.label })),
this.buildTreeNode('voucher_batch', this.getTableFields('voucher_batch'))
])
];
@ -253,15 +287,24 @@ class FieldsManager {
}
};
return fields
const paths = fields
.map(toPath)
.filter(path => Array.isArray(path) && path.length >= 2)
.filter(path => {
// 验证字段存在
.filter(path => Array.isArray(path) && path.length >= 2);
// 添加调试日志:验证字段存在性
const validPaths = paths.filter(path => {
const table = path[path.length - 2];
const field = path[path.length - 1];
return this.hasField(table, field);
const exists = this.hasField(table, field);
if (!exists) {
console.warn(`[字段回显] 字段不存在于元数据中: ${table}.${field}, 路径: ${JSON.stringify(path)}`);
}
return exists;
});
console.log(`[字段回显] 共 ${fields.length} 个字段, 转换为 ${paths.length} 个路径, 验证通过 ${validPaths.length}`);
return validPaths;
}
/**
@ -384,7 +427,7 @@ const TreeUtils = {
/**
* 根据路径数组查找节点 value
* @param {Array} nodes - 树节点数组
* @param {string[]} path - 路径数组
* @param {string[]} path - 路径数组 ['order', 'status'] ['order', 'merchant', 'name']
* @param {number} index - 当前索引
* @returns {string|null} 节点 value
*/
@ -392,16 +435,41 @@ const TreeUtils = {
if (!nodes || index >= path.length) return null;
const target = path[index];
for (const node of nodes) {
if (node.value === target) {
// 节点value可能是 'order', 'order.status', 'merchant' 等格式
const nodeTableOrField = node.value.split('.').pop(); // 取最后一部分
const nodeValue = node.value;
// 检查是否匹配当前路径段
const isMatch = nodeValue === target || nodeTableOrField === target || node.value.endsWith(`.${target}`);
if (isMatch) {
if (index === path.length - 1) {
// 到达路径末尾返回节点value
return node.value;
} else if (node.children && node.children.length > 0) {
// 继续在子节点中查找
const found = TreeUtils.findNodeByPath(node.children, path, index + 1);
if (found) return found;
}
}
}
// 特殊处理:如果路径是 ['order', 'status'],直接尝试查找 'order.status'
if (index === 0 && path.length === 2) {
const combinedValue = `${path[0]}.${path[1]}`;
for (const node of nodes) {
if (node.value === combinedValue) {
return node.value;
}
if (node.children) {
const found = TreeUtils.findNode(node.children, combinedValue);
if (found) return found.value;
}
}
}
return null;
},
@ -416,9 +484,9 @@ const TreeUtils = {
/**
* 将选中的 keys 转换为路径数组
* @param {string[]} checkedKeys - 选中的节点 keys
* @param {string[]} checkedKeys - 选中的节点 keys格式如 ['order.status', 'merchant.name']
* @param {Array} treeData - 树数据
* @returns {Array<string[]>} 路径数组仅叶子节点
* @returns {Array<string[]>} 路径数组仅叶子节点格式如 [['order', 'status'], ['order', 'merchant', 'name']]
*/
checkedKeysToLeafPaths(checkedKeys, treeData) {
return checkedKeys
@ -427,8 +495,23 @@ const TreeUtils = {
return node && TreeUtils.isLeafNode(node);
})
.map(key => {
// key 格式可能是 'order.status' 或 'merchant.name'
// 需要根据树结构找到完整路径
const path = TreeUtils.findNodePath(treeData, key);
return path || [key];
if (path && path.length >= 2) {
// path 是节点value的数组需要转换为实际的表/字段路径
// 例如:['order', 'order.status'] -> ['order', 'status']
// ['order', 'merchant', 'merchant.name'] -> ['order', 'merchant', 'name']
return path.map((segment, index) => {
if (segment.includes('.')) {
// 是字段节点,取字段名部分
return segment.split('.').pop();
}
return segment;
});
}
// 回退方案:直接使用 key
return key.includes('.') ? key.split('.') : [key];
})
.filter(path => path.length >= 2);
},
@ -440,17 +523,27 @@ const TreeUtils = {
* @returns {string[]} 节点 keys
*/
pathsToNodeKeys(paths, treeData) {
return paths.map(path => {
const keys = paths.map(path => {
if (Array.isArray(path)) {
const nodeValue = TreeUtils.findNodeByPath(treeData, path);
if (nodeValue) return nodeValue;
if (nodeValue) {
return nodeValue;
} else {
// 找不到时使用路径最后一部分
return path[path.length - 1];
const fallbackKey = path[path.length - 1];
console.warn(`[树节点转换] 路径未找到对应节点: ${JSON.stringify(path)}, 使用回退key: ${fallbackKey}`);
return fallbackKey;
}
} else if (typeof path === 'string') {
return path;
}
return null;
}).filter(Boolean);
console.log(`[树节点转换] 共转换 ${paths.length} 个路径为 ${keys.length} 个节点key`);
console.log(`[树节点转换] 节点keys:`, keys);
return keys;
}
};