refactor(fields): 增强字段管理逻辑以支持唯一值和调试
- 在FieldsManager类中为子表字段节点添加表名前缀,确保值的唯一性 - 添加调试日志以检查树节点的重复值,提升代码可维护性 - 更新路径转换逻辑,确保字段回显时的有效性检查 - 增强树节点查找逻辑,支持更灵活的路径匹配
This commit is contained in:
parent
3d75cfec1d
commit
143199dcd3
14
web/main.js
14
web/main.js
|
|
@ -134,19 +134,29 @@ const app = createApp({
|
||||||
*/
|
*/
|
||||||
const setTreeChecked = (kind, values) => {
|
const setTreeChecked = (kind, values) => {
|
||||||
const tree = kind === 'create' ? createFieldsTree.value : editFieldsTree.value;
|
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;
|
const treeData = kind === 'create' ? fieldTreeData.value : editFieldTreeData.value;
|
||||||
|
console.log(`[设置树勾选] kind=${kind}, values数量=${values.length}, 树数据存在=${!!treeData}`);
|
||||||
|
|
||||||
const keys = TreeUtils.pathsToNodeKeys(values, treeData);
|
const keys = TreeUtils.pathsToNodeKeys(values, treeData);
|
||||||
|
console.log(`[设置树勾选] 转换后keys数量=${keys.length}`);
|
||||||
|
|
||||||
Vue.nextTick(() => {
|
Vue.nextTick(() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
try {
|
try {
|
||||||
if (keys.length > 0 && tree) {
|
if (keys.length > 0 && tree) {
|
||||||
|
console.log(`[设置树勾选] 调用 setCheckedKeys,keys数量: ${keys.length}`);
|
||||||
tree.setCheckedKeys(keys);
|
tree.setCheckedKeys(keys);
|
||||||
|
console.log(`[设置树勾选] setCheckedKeys 调用成功`);
|
||||||
|
} else {
|
||||||
|
console.warn(`[设置树勾选] 未调用 setCheckedKeys: keys.length=${keys.length}, tree存在=${!!tree}`);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn('设置树形选择器选中状态失败:', error);
|
console.warn('[设置树勾选] 设置树形选择器选中状态失败:', error);
|
||||||
}
|
}
|
||||||
}, CONSTANTS.TREE_RENDER_DELAY);
|
}, CONSTANTS.TREE_RENDER_DELAY);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -91,13 +91,28 @@ class FieldsManager {
|
||||||
* 构建树节点
|
* 构建树节点
|
||||||
* @param {string} table - 表名
|
* @param {string} table - 表名
|
||||||
* @param {Array} children - 子节点
|
* @param {Array} children - 子节点
|
||||||
|
* @param {string} parentPrefix - 父级前缀,用于构建唯一value
|
||||||
* @returns {Object} 树节点
|
* @returns {Object} 树节点
|
||||||
*/
|
*/
|
||||||
buildTreeNode(table, children = []) {
|
buildTreeNode(table, children = [], parentPrefix = '') {
|
||||||
|
// 为子表的字段节点添加表名前缀,确保 value 唯一
|
||||||
|
const childrenWithPrefix = children.map(child => {
|
||||||
|
if (child.children) {
|
||||||
|
// 是子表节点,递归处理
|
||||||
|
return child;
|
||||||
|
} else {
|
||||||
|
// 是字段节点,添加表名前缀
|
||||||
|
return {
|
||||||
|
value: `${table}.${child.value}`,
|
||||||
|
label: child.label
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
value: table,
|
value: parentPrefix ? `${parentPrefix}.${table}` : table,
|
||||||
label: this.getTableLabel(table),
|
label: this.getTableLabel(table),
|
||||||
children
|
children: childrenWithPrefix
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -108,7 +123,8 @@ class FieldsManager {
|
||||||
*/
|
*/
|
||||||
buildYmtOrderChildren(orderType) {
|
buildYmtOrderChildren(orderType) {
|
||||||
const orderFields = this.getTableFields('order');
|
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')));
|
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')));
|
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;
|
return children;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -145,25 +178,26 @@ class FieldsManager {
|
||||||
*/
|
*/
|
||||||
buildMarketingOrderChildren(orderType) {
|
buildMarketingOrderChildren(orderType) {
|
||||||
const orderFields = this.getTableFields('order');
|
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')));
|
children.push(this.buildTreeNode('order_detail', this.getTableFields('order_detail')));
|
||||||
|
|
||||||
// 构建计划节点(包含 key_batch -> code_batch 嵌套)
|
// 构建计划节点(包含 key_batch -> code_batch 嵌套)
|
||||||
const planChildren = [
|
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.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'))
|
this.buildTreeNode('code_batch', this.getTableFields('code_batch'))
|
||||||
])
|
])
|
||||||
];
|
];
|
||||||
|
|
||||||
// 构建立减金节点(包含 voucher -> voucher_batch 嵌套)
|
// 构建立减金节点(包含 voucher -> voucher_batch 嵌套)
|
||||||
const voucherChildren = [
|
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.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'))
|
this.buildTreeNode('voucher_batch', this.getTableFields('voucher_batch'))
|
||||||
])
|
])
|
||||||
];
|
];
|
||||||
|
|
@ -253,15 +287,24 @@ class FieldsManager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return fields
|
const paths = fields
|
||||||
.map(toPath)
|
.map(toPath)
|
||||||
.filter(path => Array.isArray(path) && path.length >= 2)
|
.filter(path => Array.isArray(path) && path.length >= 2);
|
||||||
.filter(path => {
|
|
||||||
// 验证字段存在
|
// 添加调试日志:验证字段存在性
|
||||||
const table = path[path.length - 2];
|
const validPaths = paths.filter(path => {
|
||||||
const field = path[path.length - 1];
|
const table = path[path.length - 2];
|
||||||
return this.hasField(table, field);
|
const field = path[path.length - 1];
|
||||||
});
|
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
|
* 根据路径数组查找节点 value
|
||||||
* @param {Array} nodes - 树节点数组
|
* @param {Array} nodes - 树节点数组
|
||||||
* @param {string[]} path - 路径数组
|
* @param {string[]} path - 路径数组,如 ['order', 'status'] 或 ['order', 'merchant', 'name']
|
||||||
* @param {number} index - 当前索引
|
* @param {number} index - 当前索引
|
||||||
* @returns {string|null} 节点 value
|
* @returns {string|null} 节点 value
|
||||||
*/
|
*/
|
||||||
|
|
@ -392,16 +435,41 @@ const TreeUtils = {
|
||||||
if (!nodes || index >= path.length) return null;
|
if (!nodes || index >= path.length) return null;
|
||||||
|
|
||||||
const target = path[index];
|
const target = path[index];
|
||||||
|
|
||||||
for (const node of nodes) {
|
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) {
|
if (index === path.length - 1) {
|
||||||
|
// 到达路径末尾,返回节点value
|
||||||
return node.value;
|
return node.value;
|
||||||
} else if (node.children && node.children.length > 0) {
|
} else if (node.children && node.children.length > 0) {
|
||||||
|
// 继续在子节点中查找
|
||||||
const found = TreeUtils.findNodeByPath(node.children, path, index + 1);
|
const found = TreeUtils.findNodeByPath(node.children, path, index + 1);
|
||||||
if (found) return found;
|
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;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -416,9 +484,9 @@ const TreeUtils = {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 将选中的 keys 转换为路径数组
|
* 将选中的 keys 转换为路径数组
|
||||||
* @param {string[]} checkedKeys - 选中的节点 keys
|
* @param {string[]} checkedKeys - 选中的节点 keys,格式如 ['order.status', 'merchant.name']
|
||||||
* @param {Array} treeData - 树数据
|
* @param {Array} treeData - 树数据
|
||||||
* @returns {Array<string[]>} 路径数组(仅叶子节点)
|
* @returns {Array<string[]>} 路径数组(仅叶子节点),格式如 [['order', 'status'], ['order', 'merchant', 'name']]
|
||||||
*/
|
*/
|
||||||
checkedKeysToLeafPaths(checkedKeys, treeData) {
|
checkedKeysToLeafPaths(checkedKeys, treeData) {
|
||||||
return checkedKeys
|
return checkedKeys
|
||||||
|
|
@ -427,8 +495,23 @@ const TreeUtils = {
|
||||||
return node && TreeUtils.isLeafNode(node);
|
return node && TreeUtils.isLeafNode(node);
|
||||||
})
|
})
|
||||||
.map(key => {
|
.map(key => {
|
||||||
|
// key 格式可能是 'order.status' 或 'merchant.name'
|
||||||
|
// 需要根据树结构找到完整路径
|
||||||
const path = TreeUtils.findNodePath(treeData, key);
|
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);
|
.filter(path => path.length >= 2);
|
||||||
},
|
},
|
||||||
|
|
@ -440,17 +523,27 @@ const TreeUtils = {
|
||||||
* @returns {string[]} 节点 keys
|
* @returns {string[]} 节点 keys
|
||||||
*/
|
*/
|
||||||
pathsToNodeKeys(paths, treeData) {
|
pathsToNodeKeys(paths, treeData) {
|
||||||
return paths.map(path => {
|
const keys = paths.map(path => {
|
||||||
if (Array.isArray(path)) {
|
if (Array.isArray(path)) {
|
||||||
const nodeValue = TreeUtils.findNodeByPath(treeData, path);
|
const nodeValue = TreeUtils.findNodeByPath(treeData, path);
|
||||||
if (nodeValue) return nodeValue;
|
if (nodeValue) {
|
||||||
// 找不到时使用路径最后一部分
|
return nodeValue;
|
||||||
return path[path.length - 1];
|
} else {
|
||||||
|
// 找不到时使用路径最后一部分
|
||||||
|
const fallbackKey = path[path.length - 1];
|
||||||
|
console.warn(`[树节点转换] 路径未找到对应节点: ${JSON.stringify(path)}, 使用回退key: ${fallbackKey}`);
|
||||||
|
return fallbackKey;
|
||||||
|
}
|
||||||
} else if (typeof path === 'string') {
|
} else if (typeof path === 'string') {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}).filter(Boolean);
|
}).filter(Boolean);
|
||||||
|
|
||||||
|
console.log(`[树节点转换] 共转换 ${paths.length} 个路径为 ${keys.length} 个节点key`);
|
||||||
|
console.log(`[树节点转换] 节点keys:`, keys);
|
||||||
|
|
||||||
|
return keys;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue