/** * 字段处理模块 - 字段路径转换、树结构构建 * @module fields */ ;(function() { 'use strict'; // 依赖:AppConfig /** * 字段管理器类 * 管理字段元数据、树结构构建和路径转换 */ class FieldsManager { constructor() { // 字段映射:{ tableName: [{ value, label }] } this.fieldsMap = {}; // 表标签映射:{ tableName: label } this.tableLabels = {}; // 推荐字段列表 this.recommendedFields = []; } /** * 更新字段元数据 * @param {Array} tables - 表定义数组 * @param {Array} recommended - 推荐字段数组 */ updateMetadata(tables, recommended = []) { const map = {}; const labels = {}; tables.forEach(table => { const fields = Array.isArray(table.fields) ? table.fields : []; const visibleFields = fields.filter(f => !f.hidden); map[table.table] = visibleFields.map(f => ({ value: f.field, label: f.label })); if (table.label) { labels[table.table] = table.label; } }); this.fieldsMap = map; this.tableLabels = labels; this.recommendedFields = recommended; } /** * 获取表标签 * @param {string} table - 表名 * @returns {string} 表标签 */ getTableLabel(table) { return this.tableLabels[table] || window.AppConfig?.DEFAULT_TABLE_LABELS[table] || table; } /** * 获取指定表的字段列表 * @param {string} table - 表名 * @returns {Array<{value: string, label: string}>} 字段列表 */ getTableFields(table) { return this.fieldsMap[table] || []; } /** * 检查表中是否存在某字段 * @param {string} table - 表名 * @param {string} field - 字段名 * @returns {boolean} 是否存在 */ hasField(table, field) { const fields = this.fieldsMap[table] || []; return fields.some(f => f.value === field); } /** * 获取所有表名 * @returns {string[]} 表名列表 */ getAllTables() { return Object.keys(this.fieldsMap); } /** * 构建树节点 * @param {string} table - 表名 * @param {Array} children - 子节点 * @returns {Object} 树节点 */ buildTreeNode(table, children = []) { return { value: table, label: this.getTableLabel(table), children }; } /** * 构建易码通订单子节点 * @param {number} orderType - 订单类型 * @returns {Array} 子节点数组 */ buildYmtOrderChildren(orderType) { const orderFields = this.getTableFields('order'); const children = orderFields.map(f => ({ value: f.value, label: f.label })); // 添加公共子表(所有订单类型都有) children.push(this.buildTreeNode('merchant', this.getTableFields('merchant'))); children.push(this.buildTreeNode('activity', this.getTableFields('activity'))); // 根据订单类型添加特定子表 if (orderType === 2) { // 直充卡密 children.push(this.buildTreeNode('order_digit', this.getTableFields('order_digit'))); } else if (orderType === 3) { // 立减金 children.push(this.buildTreeNode('order_voucher', this.getTableFields('order_voucher'))); children.push(this.buildTreeNode('goods_voucher_batch', this.getTableFields('goods_voucher_batch'))); children.push(this.buildTreeNode('goods_voucher_subject_config', this.getTableFields('goods_voucher_subject_config'))); } else if (orderType === 1) { // 红包 children.push(this.buildTreeNode('order_cash', this.getTableFields('order_cash'))); } else { // 未指定类型或全部类型(orderType = 0 或其他) children.push(this.buildTreeNode('order_voucher', this.getTableFields('order_voucher'))); children.push(this.buildTreeNode('order_cash', this.getTableFields('order_cash'))); children.push(this.buildTreeNode('order_digit', this.getTableFields('order_digit'))); children.push(this.buildTreeNode('goods_voucher_batch', this.getTableFields('goods_voucher_batch'))); children.push(this.buildTreeNode('goods_voucher_subject_config', this.getTableFields('goods_voucher_subject_config'))); } return children; } /** * 构建营销系统订单子节点 * @param {number} orderType - 订单类型 * @returns {Array} 子节点数组 */ buildMarketingOrderChildren(orderType) { const orderFields = this.getTableFields('order'); const children = orderFields.map(f => ({ value: 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.buildTreeNode('key_batch', [ ...this.getTableFields('key_batch').map(f => ({ value: 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.buildTreeNode('voucher', [ ...this.getTableFields('voucher').map(f => ({ value: f.value, label: f.label })), this.buildTreeNode('voucher_batch', this.getTableFields('voucher_batch')) ]) ]; // 根据订单类型添加特定子表 if (orderType === 1) { // 直充卡密 children.push(this.buildTreeNode('plan', planChildren)); children.push(this.buildTreeNode('merchant_key_send', this.getTableFields('merchant_key_send'))); } else if (orderType === 2) { // 立减金 children.push(this.buildTreeNode('order_voucher', voucherChildren)); children.push(this.buildTreeNode('plan', planChildren)); } else if (orderType === 3) { // 红包 children.push(this.buildTreeNode('order_cash', this.getTableFields('order_cash'))); children.push(this.buildTreeNode('plan', planChildren)); } else { // 全部类型 children.push(this.buildTreeNode('order_cash', this.getTableFields('order_cash'))); children.push(this.buildTreeNode('order_voucher', voucherChildren)); children.push(this.buildTreeNode('plan', planChildren)); children.push(this.buildTreeNode('merchant_key_send', this.getTableFields('merchant_key_send'))); } return children; } /** * 构建字段选择树数据 * @param {string} datasource - 数据源 * @param {number} orderType - 订单类型 * @param {string} [mainTable] - 主表名(可选,用于编辑模式) * @returns {Array} 树数据 */ buildFieldTree(datasource, orderType, mainTable) { const type = Number(orderType || 0); if (datasource === 'ymt') { const actualMainTable = (mainTable === 'order_info') ? 'order' : (mainTable || 'order'); const orderNode = this.buildTreeNode(actualMainTable, this.buildYmtOrderChildren(type)); return type ? [orderNode] : [{ value: 'scene_order', label: '订单数据', children: [orderNode] }]; } else { const tableForTree = mainTable || 'order'; const orderNode = this.buildTreeNode(tableForTree, this.buildMarketingOrderChildren(type)); return type ? [orderNode] : [{ value: 'scene_order', label: '订单数据', children: [orderNode] }]; } } /** * 将 table.field 格式转换为路径数组格式 * @param {string[]} fields - 字段数组,格式为 ['table.field', ...] * @param {string} datasource - 数据源 * @param {string} mainTable - 主表名 * @returns {Array} 路径数组,格式为 [['table', 'field'], ...] */ convertFieldsToPaths(fields, datasource, mainTable) { const actualMainTable = (datasource === 'ymt' && mainTable === 'order_info') ? 'order' : mainTable; const relations = window.AppConfig?.getTableRelations(datasource) || {}; const toPath = (tableField) => { const parts = String(tableField || '').split('.'); if (parts.length !== 2) return null; let table = parts[0]; const field = parts[1]; // ymt 数据源 order_info 映射为 order if (datasource === 'ymt' && table === 'order_info') { table = 'order'; } // 查找表的路径关系 const relation = relations[table]; if (!relation) { // 未知表,尝试直接返回 return [actualMainTable, table, field]; } const pathToTable = relation.path || []; if (pathToTable.length === 0) { // 主表字段 return [actualMainTable, field]; } else { // 子表字段 return [actualMainTable, ...pathToTable, field]; } }; return fields .map(toPath) .filter(path => Array.isArray(path) && path.length >= 2) .filter(path => { // 验证字段存在 const table = path[path.length - 2]; const field = path[path.length - 1]; return this.hasField(table, field); }); } /** * 将路径数组格式转换为 table.field 格式 * @param {Array} paths - 路径数组 * @param {string} datasource - 数据源 * @param {string} mainTable - 主表名 * @returns {string[]} 字段数组,格式为 ['table.field', ...] */ convertPathsToFields(paths, datasource, mainTable) { const actualMainTable = (datasource === 'ymt' && mainTable === 'order_info') ? 'order' : mainTable; const allTables = this.getAllTables(); // 检查是否只选中了主表节点(表示选择整表) const hasMainTableOnly = paths.some( p => Array.isArray(p) && p.length === 1 && p[0] === actualMainTable ); if (hasMainTableOnly) { // 返回主表所有字段 return this.getTableFields(actualMainTable).map(f => `${mainTable}.${f.value}`); } return paths.flatMap(path => { if (!Array.isArray(path)) return []; // 检查是否是组节点(表节点而非字段节点) const lastPart = path[path.length - 1]; if (allTables.includes(lastPart)) { // 是表节点,跳过 return []; } if (path.length >= 2) { const table = path[path.length - 2]; const field = path[path.length - 1]; // ymt 数据源需要将 order 转回 order_info const finalTable = (datasource === 'ymt' && table === 'order') ? 'order_info' : table; return [`${finalTable}.${field}`]; } return []; }); } /** * 路径数组去重 * @param {Array} paths - 路径数组 * @returns {Array} 去重后的路径数组 */ deduplicatePaths(paths = []) { const seen = new Set(); const result = []; for (const path of paths) { if (!Array.isArray(path)) continue; const key = path.join('|'); if (seen.has(key)) continue; seen.add(key); result.push(path); } return result; } /** * 获取主表所有叶子字段的路径 * @param {string} datasource - 数据源 * @returns {Array} 路径数组 */ getMainTableLeafPaths(datasource) { const mainTable = datasource === 'ymt' ? 'order' : 'order'; return this.getTableFields(mainTable).map(f => [mainTable, f.value]); } } /** * 树节点工具函数 */ const TreeUtils = { /** * 在树中查找节点 * @param {Array} nodes - 树节点数组 * @param {string} targetKey - 目标节点 key * @returns {Object|null} 找到的节点 */ findNode(nodes, targetKey) { for (const node of nodes) { if (node.value === targetKey) { return node; } if (node.children) { const found = TreeUtils.findNode(node.children, targetKey); if (found) return found; } } return null; }, /** * 查找节点的完整路径 * @param {Array} nodes - 树节点数组 * @param {string} targetKey - 目标节点 key * @param {string[]} currentPath - 当前路径 * @returns {string[]|null} 节点路径 */ findNodePath(nodes, targetKey, currentPath = []) { for (const node of nodes) { const newPath = [...currentPath, node.value]; if (node.value === targetKey) { return newPath; } if (node.children && node.children.length > 0) { const found = TreeUtils.findNodePath(node.children, targetKey, newPath); if (found) return found; } } return null; }, /** * 根据路径数组查找节点 value * @param {Array} nodes - 树节点数组 * @param {string[]} path - 路径数组 * @param {number} index - 当前索引 * @returns {string|null} 节点 value */ findNodeByPath(nodes, path, index = 0) { if (!nodes || index >= path.length) return null; const target = path[index]; for (const node of nodes) { if (node.value === target) { if (index === path.length - 1) { return node.value; } else if (node.children && node.children.length > 0) { const found = TreeUtils.findNodeByPath(node.children, path, index + 1); if (found) return found; } } } return null; }, /** * 判断节点是否为叶子节点 * @param {Object} node - 节点对象 * @returns {boolean} 是否为叶子节点 */ isLeafNode(node) { return !node.children || node.children.length === 0; }, /** * 将选中的 keys 转换为路径数组 * @param {string[]} checkedKeys - 选中的节点 keys * @param {Array} treeData - 树数据 * @returns {Array} 路径数组(仅叶子节点) */ checkedKeysToLeafPaths(checkedKeys, treeData) { return checkedKeys .filter(key => { const node = TreeUtils.findNode(treeData, key); return node && TreeUtils.isLeafNode(node); }) .map(key => { const path = TreeUtils.findNodePath(treeData, key); return path || [key]; }) .filter(path => path.length >= 2); }, /** * 将路径数组转换为节点 keys * @param {Array} paths - 路径数组 * @param {Array} treeData - 树数据 * @returns {string[]} 节点 keys */ pathsToNodeKeys(paths, treeData) { return paths.map(path => { if (Array.isArray(path)) { const nodeValue = TreeUtils.findNodeByPath(treeData, path); if (nodeValue) return nodeValue; // 找不到时使用路径最后一部分 return path[path.length - 1]; } else if (typeof path === 'string') { return path; } return null; }).filter(Boolean); } }; // 创建全局实例 const fieldsManager = new FieldsManager(); // 导出模块 window.FieldsModule = { FieldsManager, TreeUtils, fieldsManager }; })();