/* 建设其他费表格相关 */ const budgetSummaryObj = (() => { const { isDef, isNumber } = window.commonUtil; const { fixedFlag, BudgetArea } = window.commonConstants; // 原始数据 let rawData = []; // 建设其他费表格对象 let spread = null; // 建设其他费树 let tree = null; // 单位设置下拉框 const setUnitCombo = (sheet, data) => { const unitCol = budgetSummaryTreeSetting.cols.findIndex(item => item.data.field === 'unit'); if (unitCol >= 0) { TREE_SHEET_HELPER.massOperationSheet(sheet, () => { const comboBox = sheetCommonObj.getDynamicCombo(); comboBox .itemHeight(10) .items(['m', 'm2', 'm3', 'km', 't', 'kg', '台班', '工日', '昼夜', '元', '项', '处', '个', '件', '根', '组', '系统', '台', '套', '株', '丛', '缸', '支', '只', '块', '座', '对', '份', '樘', '攒', '榀']) .editable(true); data.forEach((item, index) => { sheet.getCell(index, unitCol).cellType(comboBox); }) }); } } const getFieldByCol = (col) => { const item = budgetSummaryTreeSetting.cols[col]; return item && item.data && item.data.field || null; } // 单元格值验证器 const validator = { text() { return true }, number(val) { return !isDef(val) || isNumber(val); }, }; const getValidator = (col) => { const item = budgetSummaryTreeSetting.cols[col]; if (!item) { return 'text'; } return validator[item.data.type || 'text']; } /* 表格事件相关 */ // 恢复数据 const recover = (sheet, changedCells) => { if (!tree) { return; } changedCells.forEach(({ row, col }) => { const node = tree.items[row]; const field = getFieldByCol(col); if (!field || !node) { return; } const orgVal = node.data[field]; sheet.setValue(row, col, orgVal); }) } // 编辑相关 const edit = (sheet, changedCells) => { // 单元格值验证 const isValid = changedCells.every(({ row, col }) => { const val = sheet.getValue(row, col); return getValidator(col)(val); }); // 验证不通过,恢复 if (!isValid) { recover(sheet, changedCells); } } const events = { EnterCell(sender, args) { args.sheet.repaint(); }, ValueChanged(sender, args) { edit(args.sheet, [{ row: args.row, col: args.col }]); }, RangeChanged(sender, args) { edit(args.sheet, args.changedCells); } } const bindEvents = (sheet) => { Object.entries(events).forEach(([ev, evFunc]) => { sheet.bind(GC.Spread.Sheets.Events[ev], evFunc) }) } /* 只读相关 */ const lockData = (sheet, nodes) => { TREE_SHEET_HELPER.massOperationSheet(sheet, () => { // 工程费用区域,只读 const equipmentNode = nodes.find(node => node.getFlag() === fixedFlag.CONSTRUCTION_EQUIPMENT_FEE); if (equipmentNode) { sheet.getRange(0, 0, equipmentNode.serialNo() + 1, budgetSummaryTreeSetting.cols.length, GC.Spread.Sheets.SheetArea.viewport).locked(true); } }); } /* 初始化表格 */ const initSpread = () => { if (!spread) { // spread = sheetCommonObj.createSpread($('#budget-summary-sheet')[0], 1); spread = SheetDataHelper.createNewSpread($('#budget-summary-sheet')[0]); sheetCommonObj.spreadDefaultStyle(spread); // 设置表头 const sheet = spread.getSheet(0); bindEvents(sheet); const headers = sheetCommonObj.getHeadersFromTreeSetting(budgetSummaryTreeSetting); sheetCommonObj.setHeader(sheet, headers); initContextMenu(); } else { spread.refresh(); } return spread; } // 初始化树 const initTree = (data, sheet, setting) => { tree = idTree.createNew({ id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true }); const controller = TREE_SHEET_CONTROLLER.createNew(tree, sheet, setting, false); tree.loadDatas(data); controller.showTreeData(); sheet.setRowCount(data.length); setUnitCombo(sheet, data); lockData(sheet, tree.items); } /* 右键菜单 */ // 更新树结构数据 const updateTree = (sheet, updateData) => { // 更新数据 updateData.forEach(item => { if (item.type === 'new') { rawData.push(item.data) } else if (item.type === 'update') { const node = tree.findNode(item.data.ID); if (node) { Object.assign(node.data, item.data); } } else { const removeIndex = rawData.findIndex(d => d.ID === item.data.ID); if (removeIndex > -1) { rawData.splice(removeIndex, 1); } } }); // 重新初始化树 initTree(rawData, sheet, budgetSummaryTreeSetting); } // 插入 const insert = (sheet, selected) => { const updateData = tree.getInsertData(selected.data.ParentID, selected.data.NextSiblingID, uuid.v1()); updateTree(sheet, updateData); sheet.setActiveCell(sheet.getActiveRowIndex() + selected.posterityCount() + 1, sheet.getActiveColumnIndex()) } // 删除 const remove = (sheet, selected) => { const updateData = tree.getDeleteData(selected); updateTree(sheet, updateData); } // 升级 const upLevel = (sheet, selected) => { const updateData = selected.getUpLevelData(); updateTree(sheet, updateData); } // 降级 const downLevel = (sheet, selected) => { const updateData = selected.getDownLevelData(); updateTree(sheet, updateData); } // 上移 const upMove = (sheet, selected) => { const updateData = selected.getUpMoveData(); updateTree(sheet, updateData); const prev = selected.preSibling; const row = sheet.getActiveRowIndex() - prev.posterityCount() - 1; sheet.setActiveCell(row, sheet.getActiveColumnIndex()); sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center); } // 下移 const downMove = (sheet, selected) => { const updateData = selected.getDownMoveData(); updateTree(sheet, updateData); const next = selected.nextSibling; const row = sheet.getActiveRowIndex() + next.posterityCount() + 1; sheet.setActiveCell(row, sheet.getActiveColumnIndex()) sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center); } // 是否是属于工程费用区域的节点 const isConstructionFeeArea = (node) => { return node && node.data && node.data.area === BudgetArea.CONSTRUCTION_FEE; } // 初始化右键菜单 const initContextMenu = () => { if (!spread) { return; } let curRow; let curNode; const sheet = spread.getSheet(0); $.contextMenu({ selector: '#budget-summary-sheet', build: function ($trigger, e) { const target = SheetDataHelper.safeRightClickSelection($trigger, e, spread); curRow = target.row; curNode = tree && tree.items[curRow] || null; sheet.setActiveCell(target.row, target.col); return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader; }, items: { insert: { name: '插入行', icon: 'fa-sign-in', disabled() { return !curNode || (curNode.data.area === BudgetArea.CONSTRUCTION_FEE && curNode.getFlag() !== fixedFlag.CONSTRUCTION_FEE); }, callback() { insert(sheet, curNode); } }, remove: { name: '删除', icon: 'fa-remove', disabled() { return !curNode || isConstructionFeeArea(curNode); }, callback() { remove(sheet, curNode); } }, upLevel: { name: '升级', icon: 'fa-arrow-left', disabled() { return !curNode || !curNode.canUpLevel() || isConstructionFeeArea(curNode); }, callback() { upLevel(sheet, curNode); } }, downLevel: { name: '降级', icon: 'fa-arrow-right', disabled() { return !curNode || !curNode.canDownLevel() || isConstructionFeeArea(curNode); }, callback() { downLevel(sheet, curNode); } }, upMove: { name: '上移', icon: 'fa-arrow-up', disabled() { return !curNode || !curNode.canUpMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.preSibling); }, callback() { upMove(sheet, curNode); } }, downMove: { name: '下移', icon: 'fa-arrow-down', disabled() { return !curNode || !curNode.canDownMove() || isConstructionFeeArea(curNode) || isConstructionFeeArea(curNode.nextSibling); }, callback() { downMove(sheet, curNode); } }, refresh: { name: '刷新数据', icon: 'fa-refresh', callback() { init(projectObj.project.property.rootProjectID); } }, } }); } // 初始化 const init = async (constructionID) => { try { $.bootstrapLoading.start(); rawData = await ajaxPost('/bills/getBudgetSummary', { constructionID }); rawData.forEach((item) => { if (item.quantity) { item.quantity = parseFloat(item.quantity); } item.feesIndex = getFeeIndex(item.fees); item.flagsIndex = {}; if (item.flags) { item.flags.forEach((flag) => { item.flagsIndex[flag.fieldName] = flag; }); } }); const spread = initSpread(); const sheet = spread.getSheet(0); initTree(rawData, sheet, budgetSummaryTreeSetting); } catch (err) { console.log(err); alert(err); } finally { $.bootstrapLoading.end(); } } // 点击tab,重新初始化 $('#tab-budget-summary').click(function () { if (!$(this).hasClass('active')) { init(projectObj.project.property.rootProjectID); } }); // 对外暴露 return { getTree: () => tree, }; })();