'use strict'; /** * * * @author Zhong * @date 2018/6/1 * @version */ const billsGuidance = (function () { function sortByCode(arr) { function recurCompare(a, b, index) { if (a[index] && !b[index]) { return 1; } else if (!a[index] && b[index]) { return -1; } else if (a[index] && b[index]) { let aV = a[index], bV = b[index]; if (!isNaN(aV) && !isNaN(bV)) { aV = parseFloat(a[index]); bV = parseFloat(b[index]); } if (aV > bV) { return 1; } else if (aV < bV) { return -1; } else { return recurCompare(a, b, index + 1); } } return 0; } arr.sort(function (a, b) { if (!_isDef(a.code) || !_isDef(b.code)) { return 0; } let aArr = a.code.split('-'), bArr = b.code.split('-'); return recurCompare(aArr, bArr, 0); }); } // 导出实例 let curCompilationID = ''; const locked = lockUtil.getLocked(); let moduleName = 'stdBillsGuidance'; //上下拖动的拖动条高度 const verticalResize = 10; //自执行函数全局变量定义 const libID = getQueryString('libID'); //总工作内容数据 let stdBillsJobData = []; //总项目特征数据 let stdBillsFeatureData = []; const bills = { dom: $('#billsSpread'), workBook: null, cache: [], tree: null, controller: null, treeSetting: { treeCol: 0, emptyRows: 0, headRows: 1, headRowHeight: [40], defaultRowHeight: 21, cols: [{ width: 200, readOnly: true, showHint: true, head: { titleNames: ["项目编码"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "code", vAlign: 1, hAlign: 0, font: "Arial" } }, { width: 200, readOnly: true, head: { titleNames: ["项目名称"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "name", vAlign: 1, hAlign: 0, font: "Arial" } }] }, headers: [ { name: '项目编码', dataCode: 'code', width: 200, vAlign: 'center', hAlign: 'left', formatter: '@' }, { name: '项目名称', dataCode: 'name', width: 200, vAlign: 'center', hAlign: 'left', formatter: '@' } ], events: { SelectionChanged: function (sender, info) { billsInitSel(info.newSelections[0].row, info.oldSelections[0]); } } }; const selectedBgColor = '#DFE8F9'; const searchBgColor = 'lemonChiffon'; //项目指引复制整块localStorage key const itemCopyBlockKey = 'guideItemCopyBlock'; const updateType = { create: 'create', update: 'update', del: 'delete' }; //项目指引节点状态:展开全部、收起定额 const itemExpandState = { expand: 1, contract: 0 }; //项目指引当前节点展开收缩状态,默认展开全部 let curExpandState = 1; const guideItem = { dom: $('#guideItemSpread'), workBook: null, tree: null, controller: null, treeSetting: { treeCol: 0, emptyRows: 0, headRows: 1, headRowHeight: [40], defaultRowHeight: 21, cols: [{ width: 400, readOnly: locked, head: { titleNames: ["项目指引"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "name", vAlign: 1, hAlign: 0, font: "Arial", formatter: "@" } }, { width: 40, readOnly: true, head: { titleNames: ["输出特征"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "outputItemCharacter", vAlign: 1, hAlign: 1, font: "Arial" } }, { width: 30, readOnly: true, head: { titleNames: ["必填"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "required", vAlign: 1, hAlign: 1, font: "Arial" } }, { width: 30, readOnly: true, head: { titleNames: ["材料"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "isMaterial", vAlign: 1, hAlign: 1, font: "Arial" } }, { width: 30, readOnly: true, head: { titleNames: ["默认"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "isDefaultOption", vAlign: 1, hAlign: 1, font: "Arial" } }, { width: 40, readOnly: locked, head: { titleNames: ["单位"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "unit", vAlign: 1, hAlign: 1, font: "Arial", formatter: "@" } }, { width: 80, readOnly: locked, head: { titleNames: ["区间"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "interval", vAlign: 1, hAlign: 1, font: "Arial", formatter: "@" } }, ] }, headers: [ { name: '项目指引', dataCode: 'name', width: 400, vAlign: 'center', hAlign: 'left', formatter: '@' }, { name: '输出特征', dataCode: 'outputItemCharacter', width: 40, vAlign: 'center', hAlign: 'center' }, { name: '必填', dataCode: 'required', width: 30, vAlign: 'center', hAlign: 'center' }, { name: '材料', dataCode: 'isMaterial', width: 30, vAlign: 'center', hAlign: 'center' }, { name: '默认', dataCode: 'isDefaultOption', width: 30, vAlign: 'center', hAlign: 'center' }, { name: '单位', dataCode: 'unit', width: 40, vAlign: 'center', hAlign: 'center', formatter: '@' }, { name: '区间', dataCode: 'interval', width: 80, vAlign: 'center', hAlign: 'center', formatter: '@' }, ], events: { SelectionChanged: function (sender, info) { guideItemInitSel(info.newSelections[0].row) }, EditEnded: function (sender, args) { edit(args.sheet, [{ row: args.row, col: args.col }]); }, ButtonClicked: function (sender, args) { edit(args.sheet, [{ row: args.row, col: args.col }]); }, RangeChanged: function (sender, args) { edit(args.sheet, args.changedCells); }, CellDoubleClick: function (sender, args) { locateAtRation(args.row); }, EnterCell: function (sender, args) { console.log(args.row); } } }; //定额章节树 const section = { dom: $('#sectionSpread'), workBook: null, cache: [], tree: null, controller: null, treeSetting: { treeCol: 0, emptyRows: 0, headRows: 1, headRowHeight: [40], defaultRowHeight: 21, cols: [{ width: 400, readOnly: true, head: { titleNames: ["名称"], spanCols: [1], spanRows: [1], vAlign: [1], hAlign: [1], font: ["Arial"] }, data: { field: "name", vAlign: 1, hAlign: 0, font: "Arial" } }] }, headers: [ { name: '名称', dataCode: 'name', width: 400, vAlign: 'center', hAlign: 'left', formatter: '@' }, ], events: { SelectionChanged: function (sender, info) { sectionInitSel(info.newSelections[0].row) } } }; const ration = { dom: $('#rationSpread'), workBook: null, datas: [],//所有的数据,搜索定额时,从所有数据中筛选 cache: [],//显示在表格上的数据,添加定额可以有效根据行识别定额 headers: [ { name: '选择', dataCode: 'select', width: 50, vAlign: 'center', hAlign: 'center' }, { name: '编码', dataCode: 'code', width: 110, vAlign: 'center', hAlign: 'left', formatter: '@' }, { name: '名称', dataCode: 'name', width: 250, vAlign: 'center', hAlign: 'left', formatter: '@' }, { name: '单位', dataCode: 'unit', width: 100, vAlign: 'center', hAlign: 'left', formatter: '@' } ], events: { ButtonClicked: function (sender, args) { if (args.sheet.isEditing()) { args.sheet.endEdit(true); } }, CellDoubleClick: function (sender, args) { if (ration.headers[args.col].dataCode === 'name') { let insertDatas = getInsertRations([args.row]); if (insertDatas.length > 0) { insert(insertDatas, false); } } } } }; const options = { workBook: { tabStripVisible: false, allowContextMenu: false, allowCopyPasteExcelStyle: false, allowExtendPasteRange: false, allowUserDragDrop: false, allowUserDragFill: false, scrollbarMaxAlign: true }, sheet: { protectionOptions: { allowResizeRows: true, allowResizeColumns: true }, clipBoardOptions: GC.Spread.Sheets.ClipboardPasteOptions.values } }; /* 清单材料表 */ const materialHeaders = [ { name: '材料编码', dataCode: 'code', width: 90, vAlign: 'center', hAlign: 'left', formatter: '@' }, { name: '材料名称', dataCode: 'name', width: 150, vAlign: 'center', hAlign: 'left', formatter: '@' }, { name: '规格', dataCode: 'specs', width: 130, vAlign: 'center', hAlign: 'left', formatter: '@' } ]; const billMaterial = { dom: $('#bill-material-spread'), workBook: null, cache: [], headers: materialHeaders, events: { EditEnded: function (sender, args) { editMaterials(args.sheet, [{ row: args.row, col: args.col }]); }, RangeChanged: function (sender, args) { editMaterials(args.sheet, args.changedCells); }, } } /* 清单辅助材料录入表 */ const billMaterialHelper = { dom: $('#bill-material-helper-spread'), workBook: null, cache: [], headers: materialHeaders, events: { // 双击添加到清单材料表 CellDoubleClick: function (sender, args) { // 模拟清单材料表编辑(共用同一个接口) if (!billMaterialHelper.cache[args.row]) { return; } const code = billMaterialHelper.cache[args.row].code; const row = billMaterial.cache.length; const targetSheet = billMaterial.workBook.getSheet(0); targetSheet.setValue(row, 0, code); const changedCells = [{ row, col: 0 }]; editMaterials(billMaterial.workBook.getSheet(0), changedCells); } } } /* 导出excel */ let exportExcelWorkBook = null; // 显示清单材料数据 function showBillMaterialData(sheet, headers, datas, emptyRow = 0) { let fuc = function () { const rowCount = datas.length + emptyRow; sheet.setRowCount(rowCount); for (let col = 0, cLen = headers.length; col < cLen; col++) { for (let row = 0; row < rowCount; row++) { if (datas[row]) { sheet.setValue(row, col, datas[row][headers[col]['dataCode']] || ''); } else { sheet.setValue(row, col, ''); } } } }; renderSheetFunc(sheet, fuc); } // 获取清单材料数据 async function getBillMaterials() { if (!bills.tree.selected) { return; } billMaterial.cache = []; try { $.bootstrapLoading.start(); const { billMaterials, allGljList } = await ajaxPost('/billsGuidance/api/getBillMaterials', { libID, billID: bills.tree.selected.data.ID }); billMaterial.cache = billMaterials; console.log(allGljList); sortByCode(billMaterial.cache); billMaterialHelper.cache = allGljList sortByCode(billMaterialHelper.cache); } catch (error) { $('#alert-info').text(error.message); $('#alert').modal('show'); } finally { showBillMaterialData(billMaterial.workBook.getSheet(0), billMaterial.headers, billMaterial.cache, 30); billMaterial.workBook.getSheet(0).showRow(0, GC.Spread.Sheets.VerticalPosition.top); showBillMaterialData(billMaterialHelper.workBook.getSheet(0), billMaterialHelper.headers, billMaterialHelper.cache); billMaterialHelper.workBook.getSheet(0).showRow(0, GC.Spread.Sheets.VerticalPosition.top); $.bootstrapLoading.end(); } } function getText(sheet, row, col) { let text = sheet.getValue(row, col); text = text ? text.toString().trim() : ''; return text; } // 编辑材料表 async function editMaterials(sheet, cells) { let isChanged = false; for (let cell of cells) { const itemCode = billMaterial.cache[cell.row] && billMaterial.cache[cell.row].code || ''; if (itemCode !== getText(sheet, cell.row, 0)) { isChanged = true; } } if (!isChanged) { return; } let gljCodes = new Set(); const count = sheet.getRowCount(); for (let row = 0; row < count; row++) { const code = getText(sheet, row, 0); if (code) { gljCodes.add(code); } } // 提交数据 try { $.bootstrapLoading.start(); billMaterial.cache = await ajaxPost('/billsGuidance/api/editBillMaterials', { libID, billID: bills.tree.selected.data.ID, gljCodes: [...gljCodes], compilationID: curCompilationID }, undefined, true); sortByCode(billMaterial.cache); } catch (error) { $('#alert-info').html(error); $('#alert').modal('show'); } finally { showBillMaterialData(billMaterial.workBook.getSheet(0), billMaterial.headers, billMaterial.cache, 30); $.bootstrapLoading.end(); } } //渲染时方法,停止渲染 //@param {Object}sheet {Function}func @return {void} function renderSheetFunc(sheet, func) { sheet.suspendEvent(); sheet.suspendPaint(); if (func) { func(); } sheet.resumeEvent(); sheet.resumePaint(); } //设置表选项 //@param {Object}workBook {Object}opts @return {void} function setOptions(workBook, opts) { for (let opt in opts.workBook) { workBook.options[opt] = opts.workBook[opt]; } for (let opt in opts.sheet) { workBook.getActiveSheet().options[opt] = opts.sheet[opt]; } } //建表头 //@param {Object}sheet {Array}headers @return {void} function buildHeader(sheet, headers) { let fuc = function () { sheet.setColumnCount(headers.length); sheet.setRowHeight(0, 40, GC.Spread.Sheets.SheetArea.colHeader); for (let i = 0, len = headers.length; i < len; i++) { sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader); sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader); sheet.getCell(0, i, GC.Spread.Sheets.SheetArea.colHeader).wordWrap(true); if (headers[i].formatter) { sheet.setFormatter(-1, i, headers[i].formatter); } sheet.getRange(-1, i, -1, 1).hAlign(GC.Spread.Sheets.HorizontalAlign[headers[i]['hAlign']]); sheet.getRange(-1, i, -1, 1).vAlign(GC.Spread.Sheets.VerticalAlign[headers[i]['vAlign']]); } }; renderSheetFunc(sheet, fuc); } //表监听事件 //@param {Object}workBook @return {void} function bindEvent(workBook, events) { if (Object.keys(events).length === 0) { return; } const Events = GC.Spread.Sheets.Events; for (let event in events) { workBook.bind(Events[event], events[event]); } } //建表 //@param {Object}module @return {void} function buildSheet(module) { if (!module.workBook) { module.workBook = new GC.Spread.Sheets.Workbook(module.dom[0], { sheetCount: 1 }); let sheet = module.workBook.getActiveSheet(); if (module === bills) { sheet.name('stdBillsGuidance_bills'); //默认初始可控制焦点在清单表中 module.workBook.focus(); sheet.options.isProtected = true; } else if (module === ration) { sheet.options.isProtected = true; sheet.getRange(-1, 0, -1, 1).locked(locked); sheet.getRange(-1, 1, -1, -1).locked(true); } else if (module === guideItem) { sheet.name('stdBillsGuidance_guidance'); sheetCommonObj.bindEscKey(module.workBook, [{ sheet: sheet, editStarting: null, editEnded: module.events.EditEnded }]); } else if (module === billMaterial) { sheet.options.isProtected = true; sheet.getRange(-1, 0, -1, 1).locked(locked); sheet.getRange(-1, 1, -1, -1).locked(true); sheet.getRange(-1, 2, -1, -1).locked(true); } else if (module === billMaterialHelper) { sheet.options.isProtected = true; } setOptions(module.workBook, options); buildHeader(module.workBook.getActiveSheet(), module.headers); bindEvent(module.workBook, module.events); } lockUtil.lockSpreads([module.workBook], locked); if (locked) { // 锁定表格后双击事件失效了,但是需要双击定位,因此重新绑定双击 if (module === guideItem) { module.workBook.bind('CellDoubleClick', module.events.CellDoubleClick); } } } //清空表数据 //@param {Object}sheet {Array}headers {Number}rowCount @return {void} function cleanData(sheet, headers, rowCount) { renderSheetFunc(sheet, function () { sheet.clear(-1, 0, -1, headers.length, GC.Spread.Sheets.SheetArea.viewport, GC.Spread.Sheets.StorageType.data); if (rowCount >= 0) { sheet.setRowCount(rowCount); } }); } //根据清单获取项目指引 //@param {String}guidanceLibID {Number}billsID {Function}callback @return {void} function getItemsByBills(guidanceLibID, billsID, callback) { CommonAjax.post('/billsGuidance/api/getItemsByBills', { guidanceLibID: guidanceLibID, billsID: billsID }, function (rstData) { if (callback) { callback(rstData); } }); } //清单表焦点控制 //@param {Number}row @return {void} function billsInitSel(row, oldSel, force = false) { let guideSheet = guideItem.workBook.getActiveSheet(); cleanData(guideSheet, guideItem.headers, -1); let node = bills.tree.items[row]; if (!node) { $('#editMaterial').addClass('disabled'); return; } const billSheet = bills.workBook.getActiveSheet(); setBgColor(billSheet, row, selectedBgColor); if (oldSel && row !== oldSel.row) { const orgNode = bills.tree.items[oldSel.row] setBgColor(billSheet, oldSel.row, orgNode && orgNode.isSearch ? searchBgColor : 'white'); } bills.tree.selected = node; $('#editMaterial').removeClass('disabled'); //显示备注 $('.main-side-bottom').find('textarea').val(node.data.comment ? node.data.comment : ''); if (!node.guidance.tree || force) { getItemsByBills(libID, node.data.ID, function (rstData) { initTree(node.guidance, guideSheet, guideItem.treeSetting, rstData); setNodesExpandState(node.guidance.tree.items, curExpandState); setProcessNodes(guideSheet, node.guidance.tree.items); renderSheetFunc(guideSheet, function () { TREE_SHEET_HELPER.refreshNodesVisible(node.guidance.tree.roots, guideSheet, true); }); //设置底色 setNodesColor(guideSheet, node.guidance.tree.items); //项目指引初始焦点 guideItemInitSel(guideSheet.getActiveRowIndex() ? guideSheet.getActiveRowIndex() : 0); }); } else { setNodesExpandState(node.guidance.tree.items, curExpandState); node.guidance.controller.showTreeData(); setProcessNodes(guideSheet, node.guidance.tree.items); //设置底色 setNodesColor(guideSheet, node.guidance.tree.items); //项目指引初始焦点 guideItemInitSel(guideSheet.getActiveRowIndex() ? guideSheet.getActiveRowIndex() : 0); } } function showCheckBox(sheet, nodes) { // const checkBoxType = locked ? sheetCommonObj.getReadOnlyCheckBox() : sheetCommonObj.getCheckBox(); const checkBoxType = new GC.Spread.Sheets.CellTypes.CheckBox(); const baseType = new GC.Spread.Sheets.CellTypes.Base(); const outputItemCol = guideItem.headers.findIndex(item => item.dataCode === 'outputItemCharacter'); const requiredCol = guideItem.headers.findIndex(item => item.dataCode === 'required'); const materialCol = guideItem.headers.findIndex(item => item.dataCode === 'isMaterial'); const defaultOption = guideItem.headers.findIndex(item => item.dataCode === 'isDefaultOption'); renderSheetFunc(sheet, function () { nodes.forEach(node => { const row = node.serialNo(); if (isOptionNode(node)) { sheet.setCellType(row, defaultOption, checkBoxType); sheet.setValue(row, defaultOption, node.data.isDefaultOption || false); } else { sheet.setCellType(row, defaultOption, baseType); sheet.setValue(row, defaultOption, ''); } if (isProcessNode(node)) { sheet.setCellType(row, outputItemCol, checkBoxType); sheet.setCellType(row, requiredCol, checkBoxType); sheet.setCellType(row, materialCol, checkBoxType); sheet.setValue(row, outputItemCol, node.data.outputItemCharacter || false); sheet.setValue(row, requiredCol, node.data.required || false); sheet.setValue(row, materialCol, node.data.isMaterial || false); } else { sheet.setCellType(row, outputItemCol, baseType); sheet.setCellType(row, requiredCol, baseType); sheet.setCellType(row, materialCol, baseType); sheet.setValue(row, outputItemCol, ''); sheet.setValue(row, requiredCol, ''); sheet.setValue(row, materialCol, ''); } }) }); } function setReadOnly(sheet, nodes) { if (locked) { return; } // 单位仅特征/工序行可输入 // 区间仅仅选项行可输入 const unitCol = guideItem.headers.findIndex(item => item.dataCode === 'unit'); const intervalCol = guideItem.headers.findIndex(item => item.dataCode === 'interval'); renderSheetFunc(sheet, function () { nodes.forEach(node => { const row = node.serialNo(); sheet.getCell(row, unitCol).locked(!isProcessNode(node)); sheet.getCell(row, intervalCol).locked(!isOptionNode(node)); }); }); } // 设置选项行相关 function setProcessNodes(sheet, nodes) { showCheckBox(sheet, nodes); setReadOnly(sheet, nodes); } //设置项目节点展开收起状态:展开全部、收起定额 //@param {Array}nodes(当前清单下的所有项目指引节点) {Number}expandState(展开全部1或收起定额0). function setNodesExpandState(nodes, expandState) { if (expandState === itemExpandState.contract) { //找出所有定额的父节点 let rations = _.filter(nodes, function (node) { return node.data.type === itemType.ration; }); let rationParentIDs = []; for (let ration of rations) { if (ration.data.ParentID != -1) { rationParentIDs.push(ration.data.ParentID); } } rationParentIDs = Array.from(new Set(rationParentIDs)); let rationParentNodes = _.filter(nodes, function (node) { return rationParentIDs.includes(node.data.ID); }); //收起定额 for (let node of rationParentNodes) { node.setExpanded(false); } } else { for (let node of nodes) { node.setExpanded(true); } } } // 设置行底色 function setBgColor(sheet, row, color) { const style = new GC.Spread.Sheets.Style(); style.borderLeft = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin); style.borderTop = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin); style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin); style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin); style.backColor = color; sheet.setStyle(row, -1, style); } //根据奇偶层级设置节点底色,奇数层为蓝色(树节点深度为偶数) //@param {Object}sheet {Array}nodes @return {void} function setNodesColor(sheet, nodes) { renderSheetFunc(sheet, function () { for (let node of nodes) { const color = isProcessNode(node) ? selectedBgColor : 'White'; setBgColor(sheet, node.serialNo(), color); } }); } //选中的节点是否全是同层节点 //@param {Object}sheet {Array}items @return {Boolean} function itemsSameDepth(sheet, items) { let sels = sheet.getSelections(); if (sels.length === 0 || items.length === 0) { return false; } let depths = []; for (let i = 0; i < sels[0].rowCount; i++) { let row = sels[0].row + i; let node = items[row]; if (node) { depths.push(node.depth()); } } } //节点子项是否全是工作内容 //@param {Object}node @return {Boolean} function allJobChildren(node) { for (let c of node.children) { if (c.data.type === itemType.ration) { return false; } } return true; } //节点子项是否全是定额 //@param {Object}node @return {Boolean} function allRationChildren(node) { for (let c of node.children) { if (c.data.type === itemType.job) { return false; } } return true; } //刷新按钮有效性 //@param {Object}node @return {void} function refreshBtn(node) { if (locked) { return; } //全部设为无效 $('.tools-btn').children().addClass('disabled'); $('#generate-class').removeClass('disabled'); $('#insertRation').addClass('disabled'); $('#insertAll').addClass('disabled'); $('.main-bottom-content').find('textarea').attr('readonly', true); //插入 if (bills.tree.selected && bills.tree.selected.guidance.tree) { $('#insert').removeClass('disabled'); if (node && node.data.type === itemType.ration) { $('#insert').addClass('disabled'); } } //删除 if (node) { $('#del').removeClass('disabled'); } if (node && node.data.type === itemType.job) { //升级 if (node.parent) { $('#upLevel').removeClass('disabled'); if (node.nextSibling && node.children.length > 0 && !allJobChildren(node)) { $('#upLevel').addClass('disabled'); } } //降级 if (node.preSibling) { $('#downLevel').removeClass('disabled'); if (node.preSibling.children.length > 0 && !allJobChildren(node.preSibling)) { $('#downLevel').addClass('disabled'); } } } //上移 if (node && node.preSibling) { $('#upMove').removeClass('disabled') } //下移 if (node && node.nextSibling) { $('#downMove').removeClass('disabled'); } //收起定额、展开全部 $('#expandContract').removeClass('disabled'); //插入定额 if (node && (node.children.length === 0 || allRationChildren(node))) { $('#insertRation').removeClass('disabled'); $('#insertAll').removeClass('disabled'); } //备注,奇数节点可用 if (isProcessNode(node)) { $('.main-bottom-content').find('textarea').attr('readonly', false); } // 配置材料 $('#editMaterial').removeClass('disabled'); } //项目指引表焦点控制 //@param {Number}row @return {void} function guideItemInitSel(row) { let billsNode = bills.tree.selected; let node = null; if (billsNode && billsNode.guidance.tree) { node = billsNode.guidance.tree.items[row]; if (node) { billsNode.guidance.tree.selected = node; //显示备注 $('.main-bottom-content').find('textarea').val(node.data.comment ? node.data.comment : ''); } } refreshBtn(node); } //初始化当前库名 //@param {String} @return {void} function initLibName(libName) { $('#libName')[0].outerHTML = $('#libName')[0].outerHTML.replace("XXX清单指引", libName); } //初始化各工作表 //@param {Array}modules @return {void} function initWorkBooks(modules) { exportExcelWorkBook = new GC.Spread.Sheets.Workbook($('#excel-spread')[0], { sheetCount: 1 }); for (let module of modules) { buildSheet(module); } } function tipDivCheck() { setTimeout(function () { let tips = $('#autoTip'); if (ration.tipDiv == 'show') { return; } else if (ration.tipDiv == 'hide' && tips) { tips.hide(); ration._toolTipElement = null; } }, 600) } //获取悬浮提示单元格 //@param {Object}sheet @return {Object} function getTipCellType(sheet) { let setting = {}; let TipCellType = function () { }; TipCellType.prototype = new GC.Spread.Sheets.CellTypes.Text(); TipCellType.prototype.getHitInfo = function (x, y, cellStyle, cellRect, context) { return { x: x, y: y, row: context.row, col: context.col, cellStyle: cellStyle, cellRect: cellRect, sheet: context.sheet, sheetArea: context.sheetArea }; }; TipCellType.prototype.processMouseEnter = function (hitinfo) { let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col); let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col); if (tag !== undefined && tag) { text = tag; } if (sheet && sheet.getParent().qo) { setting.pos = SheetDataHelper.getObjPos(sheet.getParent().qo); } let delayTimes = 500; //延时时间 let now_timeStamp = +new Date(); this.tipTimeStamp = now_timeStamp; let me = this; setTimeout(function () { if (now_timeStamp - me.tipTimeStamp == 0) {//鼠标停下的时候才显示 if (setting.pos && text && text !== '') { //固定不显示的div,存储文本获取固定div宽度,toolTipElement由于显示和隐藏,获取宽度不正确 if (!me._fixedTipElement) { let div = $('#fixedTip1')[0]; if (!div) { div = document.createElement("div"); $(div).css("padding", 5) .attr("id", 'fixedTip'); $(div).hide(); document.body.insertBefore(div, null); } me._fixedTipElement = div; } $(me._fixedTipElement).width(''); $(me._fixedTipElement).html(text); if (!me._toolTipElement) { let div = $('#autoTip1')[0]; if (!div) { div = document.createElement("div"); $(div).css("position", "absolute") .css("border", "1px #C0C0C0 solid") .css("box-shadow", "1px 2px 5px rgba(0,0,0,0.4)") .css("font", "0.9rem Calibri") .css("padding", 5) .css("background", '#303133') .css("color", '#fff') .attr("id", 'autoTip1'); $(div).hide(); document.body.insertBefore(div, null); } me._toolTipElement = div; $(me._toolTipElement).width(''); //实时读取位置信息 if (hitinfo.sheet && hitinfo.sheet.getParent().qo) { setting.pos = SheetDataHelper.getObjPos(hitinfo.sheet.getParent().qo); } $(me._toolTipElement).html(text); //定额库定额特殊处理 if ($(hitinfo.sheet.getParent().qo).attr('id') === 'rationSpread') { let divWidth = $(me._fixedTipElement).width(), divHeight = $(me._fixedTipElement).height(); if (divWidth > 600) { divWidth = 590; $(me._toolTipElement).width(divWidth); } let top = setting.pos.y + hitinfo.y - divHeight < 0 ? 0 : setting.pos.y + hitinfo.cellRect.y - divHeight; $(me._toolTipElement).css("top", top).css("left", setting.pos.x - divWidth); } else { $(me._toolTipElement).css("top", setting.pos.y + hitinfo.y + 15).css("left", setting.pos.x + hitinfo.x + 15); } //名称 if (hitinfo.col === 2) { let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col), zoom = hitinfo.sheet.zoom(); let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col); let textLength = me.getAutoFitWidth(value, text, acStyle, zoom, { sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport }); let cellWidth = hitinfo.sheet.getCell(-1, hitinfo.col).width(); if (textLength > cellWidth) { $(me._toolTipElement).css("top", setting.pos.y + hitinfo.y + 15).css("left", setting.pos.x + hitinfo.x + 15); $(me._toolTipElement).show("fast"); ration.tipDiv = 'show';//做个标记 } } else { $(me._toolTipElement).show("fast"); ration.tipDiv = 'show';//做个标记 } } } } }, delayTimes); }; TipCellType.prototype.processMouseLeave = function (hininfo) { this.tipTimeStamp = +new Date(); ration.tipDiv = 'hide'; if (this._toolTipElement) { $(this._toolTipElement).hide(); this._toolTipElement = null; } tipDivCheck();//延时检查:当tips正在show的时候,就调用了hide方法,会导致tips一直存在,所以设置一个超时处理 } return new TipCellType(); } //输出表数据(定额表) //@param {Object}sheet {Array}headers {Array}datas @return {void} function showData(sheet, headers, datas) { let fuc = function () { sheet.setRowCount(datas.length); //复选框 let checkBoxType = new GC.Spread.Sheets.CellTypes.CheckBox(); let tipCellType = getTipCellType(sheet); sheet.setCellType(-1, 0, checkBoxType); for (let col = 0, cLen = headers.length; col < cLen; col++) { for (let row = 0, rLen = datas.length; row < rLen; row++) { sheet.setValue(row, col, datas[row][headers[col]['dataCode']]); if (col === 1) { sheet.setTag(row, col, datas[row]['hint']); } } } sheet.setCellType(-1, 1, tipCellType); sheet.setCellType(-1, 2, tipCellType); }; renderSheetFunc(sheet, fuc); } //根据定额章节树ID获取定额(从数据缓存中获取,定额数据一开始一次性拉取) //@param {Number}sectionId {Array}rations @return {Array} function getRationsBySectionId(sectionId, rations) { if (!sectionId || !rations) { return []; } return _.filter(rations, { sectionId }); } //定额章节树焦点控制 //@param {Number}row @return {void} function sectionInitSel(row) { let rationSheet = ration.workBook.getActiveSheet(); let sectionNode = section.tree ? section.tree.items[row] : null; if (sectionNode && sectionNode.children.length === 0) { let sectionRations = getRationsBySectionId(sectionNode.data.ID, ration.datas); ration.cache = sectionRations; showData(rationSheet, ration.headers, sectionRations); } else { cleanData(rationSheet, ration.headers, 0); } } //初始化定额条目 //@param {Number}rationLibId @return {void} function initRationItems(rationLibId, successCb) { $.bootstrapLoading.start(); //获取定额章节树 let sectionSheet = section.workBook.getActiveSheet(); CommonAjax.post('/rationRepository/api/getRationTree', { rationLibId: rationLibId }, function (sectionDatas) { //获取所有定额数据 let reqEntity = { rationLibId: rationLibId, showHint: true, returnFields: '-_id code ID sectionId name unit basePrice rationGljList jobContent annotation' }; CommonAjax.post('/rationRepository/api/getRationItemsByLib', reqEntity, function (rstData) { section.cache = sectionDatas; initTree(section, section.workBook.getActiveSheet(), section.treeSetting, sectionDatas); //初始焦点在第一行(切换库) sectionSheet.setActiveCell(0, 0); sortByCode(rstData); /* rstData.sort(function (a, b) { let rst = 0; if(a.code > b.code){ rst = 1; } else if(a.code < b.code){ rst = -1; } return rst; }); */ ration.datas = rstData; sectionInitSel(0); $.bootstrapLoading.end(); if (successCb) { successCb(); } }, function () { $.bootstrapLoading.end(); }); }, function () { $.bootstrapLoading.end(); }); } // 可用的定额库ID const rationLibIDs = []; //初始化定额库选择 //@param {String}compilationId @return {void} function initRationLibs(compilationId) { CommonAjax.post('/rationRepository/api/getRationLibsByCompilation', { compilationId: compilationId }, function (rstData) { $('#rationLibSel').empty(); for (let rationLib of rstData) { rationLibIDs.push(+rationLib.ID); let opt = ``; $('#rationLibSel').append(opt); } //初始选择 initRationItems(parseInt($('#rationLibSel').select().val())); $('#rationLibSel').change(function () { let rationLibId = parseInt($(this).select().val()); initRationItems(rationLibId); }) }); } // 变更定额库 function changeRationLib(libID, successCb) { $('#rationLibSel').val(libID); initRationItems(libID, successCb); } // 设置清单名称文本色 function setBillsForeColor(billsNodes) { const sheet = bills.workBook.getActiveSheet(); renderSheetFunc(sheet, function () { for (let bills of billsNodes) { const row = bills.serialNo(); sheet.setStyle(row, 1, { foreColor: bills.data.hasGuide ? 'darkgreen' : 'black' }); } }); } //清单设置悬浮提示信息 //@param {Array}billsNodes(清单节点) {Array}jobs(总的工作内容数据) {Array}items(总的项目特征数据) function setBillsHint(billsNodes, jobs, items) { let jobsMapping = {}, itemsMapping = {}; for (let job of jobs) { jobsMapping[job.id] = job; } for (let item of items) { itemsMapping[item.id] = item; } let tagInfo = []; for (let billsNode of billsNodes) { let hintArr = []; let billsItems = billsNode.data.items; if (billsItems.length > 0) { //项目特征 hintArr.push('项目特征:'); } let itemCount = 1, jobCount = 1; for (let billsItem of billsItems) { let itemData = itemsMapping[billsItem.id]; if (itemData) { //特征值 let eigens = []; for (let eigen of itemData.itemValue) { eigens.push(eigen.value); } eigens = eigens.join(';'); hintArr.push(`${itemCount}.${itemData.content}${eigens === '' ? '' : ': ' + eigens}`); itemCount++; } } //工作内容 let billsJobs = billsNode.data.jobs; if (billsJobs.length > 0) { hintArr.push('工作内容:'); } for (let billsJob of billsJobs) { let jobData = jobsMapping[billsJob.id]; if (jobData) { hintArr.push(`${jobCount}.${jobData.content}`); jobCount++; } } /*if(billsNode.data.ruleText && billsNode.data.ruleText !== ''){ hintArr.push('工程量计算规则:'); hintArr.push(billsNode.data.ruleText); } if(billsNode.data.recharge && billsNode.data.recharge !== ''){ hintArr.push('补注:'); hintArr.push(billsNode.data.recharge); }*/ if (hintArr.length > 0) { tagInfo.push({ row: billsNode.serialNo(), value: hintArr.join('\n') }); } } let sheet = bills.workBook.getActiveSheet(); renderSheetFunc(sheet, function () { for (let tagI of tagInfo) { sheet.setTag(tagI.row, 0, tagI.value); } }); } //初始化清单的工作内容和项目特征 //@param {Number}billsLibId {Function}callback @return {void} function initJobAndCharacter(billsLibId, callback) { CommonAjax.post('/stdBillsEditor/getJobContent', { billsLibId: billsLibId }, function (datas) { stdBillsJobData = datas; CommonAjax.post('/stdBillsEditor/getItemCharacter', { billsLibId: billsLibId }, function (datas) { stdBillsFeatureData = datas; if (callback) { callback(); } }); }); } let billsLibId = 0; //获取指引库信息及关联的清单 //@param {Number}libID {Function}callback @return {Object} function getLibWithBills(libID, callback) { CommonAjax.post('/billsGuidance/api/getLibWithBills', { libID: libID }, function (rstData) { billsLibId = rstData.guidanceLib.billsLibId; initRationLibs(rstData.guidanceLib.compilationId); curCompilationID = rstData.guidanceLib.compilationId; bills.cache = rstData.bills; initLibName(rstData.guidanceLib.name); /*initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, bills.cache); //每一棵项目指引树挂在清单节点上 for(let node of bills.tree.items){ node.guidance = {tree: null, controller: null}; } //默认初始节点 billsInitSel(0); if(callback){ callback(rstData); }*/ let initDataCallback = function () { initTree(bills, bills.workBook.getActiveSheet(), bills.treeSetting, bills.cache); //每一棵项目指引树挂在清单节点上 for (let node of bills.tree.items) { node.guidance = { tree: null, controller: null }; } //默认初始节点 billsInitSel(0); if (callback) { callback(rstData); } }; initJobAndCharacter(rstData.guidanceLib.billsLibId, initDataCallback); }, function (msg) { window.location.href = '/billsGuidance/main'; }); } //初始化并输出树 //@param {Object}module {Object}sheet {Object}treeSetting {Array}datas function initTree(module, sheet, treeSetting, datas) { module.tree = idTree.createNew({ id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true }); module.controller = TREE_SHEET_CONTROLLER.createNew(module.tree, sheet, treeSetting); module.tree.loadDatas(datas); if (module === bills) { module.tree.roots.forEach(root => { root.setExpanded(false); }) } module.controller.showTreeData(); if (module === bills) { setBillsHint(bills.tree.items, stdBillsJobData, stdBillsFeatureData); setBillsForeColor(bills.tree.items); } } //更新清单备注 function updateBillsComment(updateData, callback, errCB) { CommonAjax.post('/stdBillsEditor/updateBills', updateData, function () { if (callback) { callback(); } }, function () { if (errCB) { errCB(); } }); } //更新项目指引 //@param {Array}updateDatas {Function}callback @return {void} function updateGuideItems(updateDatas, callback, errCB) { CommonAjax.post('/billsGuidance/api/updateItems', { updateDatas: updateDatas }, function (rstData) { if (callback) { callback(rstData); } }, function () { if (errCB) { errCB(); } $.bootstrapLoading.end(); }); } //编辑后自动去除换行符回车符 const deESC = /[\n]/g; //项目指引编辑 //@param {Object}sheet {Array}cells function edit(sheet, cells) { let updateDatas = []; //同步节点数据 let syncDatas = []; for (let cell of cells) { const field = guideItem.headers[cell.col].dataCode; let node = bills.tree.selected.guidance.tree.items[cell.row]; if (field === 'name' || field === 'unit' || field === 'interval') { let text = sheet.getValue(cell.row, cell.col); text = text ? text.toString() : ''; text = text.replace(deESC, ''); sheet.setValue(cell.row, cell.col, text); if (node.data[field] != text) { syncDatas.push({ node: node, text: text, field, cell }); updateDatas.push({ updateType: updateType.update, findData: { ID: node.getID() }, updateData: { [field]: text } }); } } else if (field === 'outputItemCharacter' || field === 'required' || field === 'isMaterial' || field === 'isDefaultOption') { const val = !sheet.getValue(cell.row, cell.col); sheet.setValue(cell.row, cell.col, val); syncDatas.push({ node: node, text: val, field, cell }); updateDatas.push({ updateType: updateType.update, findData: { ID: node.getID() }, updateData: { [field]: val } }); } } if (updateDatas.length > 0) { updateGuideItems(updateDatas, function () { for (let syncData of syncDatas) { syncData.node.data[syncData.field] = syncData.text; } }, function () { //失败恢复 renderSheetFunc(sheet, function () { for (let syncData of syncDatas) { sheet.setValue(syncData.node.serialNo(), syncData.cell.col, syncData.node.data[syncData.field] ? syncData.node.data[syncData.field] : ''); } }); }); } } //项目指引插入,支持一次插入多条数据 //@param {Array}datas {Boolean}tobeChild(插入成为子项) {Function}callback @return {void} function insert(datas, tobeChild, callback = null) { $.bootstrapLoading.start(); let sheet = guideItem.workBook.getActiveSheet(); let controller = bills.tree.selected.guidance.controller; let selected = bills.tree.selected.guidance.tree.selected; let updateDatas = []; //建立数组下标索引 let newDataIndex = {}; for (let i = 0; i < datas.length; i++) { let newNodeData = { libID: libID, ID: uuid.v1(), ParentID: selected ? selected.getParentID() : -1, NextSiblingID: selected ? selected.getNextSiblingID() : -1, billsID: bills.tree.selected.getID(), outputItemCharacter: true, }; //定额类型插入当前工作内容焦点行, if (selected && ((selected.data.type === itemType.job && datas[i].type === itemType.ration) || tobeChild)) { newNodeData.ParentID = selected.getID(); newNodeData.NextSiblingID = -1; } Object.assign(newNodeData, datas[i]); newDataIndex[i] = newNodeData; } for (let i = 0; i < datas.length; i++) { //第一个节点 if (i === 0) { //非插入成子节点,更新选中节点NestSiblingID if (selected && !((selected.data.type === itemType.job && datas[i].type === itemType.ration) || tobeChild)) { updateDatas.push({ updateType: updateType.update, findData: { ID: selected.getID() }, updateData: { NextSiblingID: newDataIndex[i].ID } }); } } //非最后一个节点 if (i !== datas.length - 1) { newDataIndex[i].NextSiblingID = newDataIndex[i + 1].ID; } updateDatas.push({ updateType: updateType.create, updateData: newDataIndex[i] }); } updateGuideItems(updateDatas, function () { const outputItemCol = guideItem.headers.findIndex(item => item.dataCode === 'outputItemCharacter'); for (let updateData of updateDatas) { if (updateData.updateType === updateType.create) { let newNode = controller.insertByIDS(updateData.updateData.ID, updateData.updateData.ParentID, updateData.updateData.NextSiblingID); //同步data Object.assign(newNode.data, updateData.updateData); const row = newNode.serialNo(); sheet.setValue(row, 0, newNode.data.name); if (newNode.data.outputItemCharacter !== undefined) { sheet.setValue(row, outputItemCol, newNode.data.outputItemCharacter); } setProcessNodes(sheet, [newNode]); refreshBtn(newNode); } } if (!bills.tree.selected.data.hasGuide && bills.tree.selected.guidance.tree.items.length) { bills.tree.selected.data.hasGuide = true; setBillsForeColor([bills.tree.selected]); } if (callback) { callback(); } setNodesColor(sheet, bills.tree.selected.guidance.tree.items); guideItem.workBook.focus(true); $.bootstrapLoading.end(); }); } // 删除定额(嵌套删除自身及后代定额) function delRations() { const selected = bills.tree.selected.guidance.tree.selected; if (!selected) { return; } if (selected.data.type === itemType.ration) { del({ row: selected.serialNo(), col: 0, rowCount: 1, colCount: 1 }); return; } const rations = selected.getPosterity().filter(item => item.data.type === itemType.ration); if (!rations.length) { return; } const updateDatas = rations.map(item => ({ updateType: updateType.del, findData: { ID: item.data.ID } })); const rationIDs = rations.map(r => r.data.ID); $.bootstrapLoading.start(); updateGuideItems(updateDatas, function () { const guideSheet = guideItem.workBook.getSheet(0); const treeNodes = bills.tree.selected.guidance.tree.items.filter(item => !rationIDs.includes(item.data.ID)); const state = bills.tree.selected.guidance.tree.getExpState(treeNodes); const treeData = treeNodes.map(item => item.data); initTree(bills.tree.selected.guidance, guideSheet, guideItem.treeSetting, treeData); bills.tree.selected.guidance.tree.setExpandedByState(bills.tree.selected.guidance.tree.items, state); renderSheetFunc(guideSheet, function () { TREE_SHEET_HELPER.refreshNodesVisible(bills.tree.selected.guidance.tree.roots, guideSheet, true); }); //设置底色 setNodesColor(guideSheet, bills.tree.selected.guidance.tree.items); //项目指引初始焦点 guideItemInitSel(guideSheet.getActiveRowIndex() ? guideSheet.getActiveRowIndex() : 0); $.bootstrapLoading.end(); guideItem.workBook.focus(true) }); } //项目指引删除操作 //@return {void} function del(selArea = null) { $.bootstrapLoading.start(); let controller = bills.tree.selected.guidance.controller; let selNodes = []; let sheet = guideItem.workBook.getSheet(0); let sel = selArea ? selArea : sheet.getSelections()[0]; if (sel) { sel.row = sel.row === -1 ? 0 : sel.row; for (let i = 0; i < sel.rowCount; i++) { if (bills.tree.selected.guidance.tree.items[sel.row + i]) { selNodes.push(bills.tree.selected.guidance.tree.items[sel.row + i]); } } } //选中的块节点 let blockNodes = getBlockNodes(selNodes); let updateDatas = []; function getDelDatas(nodes) { for (let node of nodes) { updateDatas.push({ updateType: updateType.del, findData: { ID: node.getID() } }); if (node.children.length > 0) { getDelDatas(node.children); } } } getDelDatas(blockNodes); //更新相关的前节点 for (let node of blockNodes) { if (node.preSibling && !blockNodes.includes(node.preSibling)) { let next = node; while (next.nextSibling && blockNodes.includes(next.nextSibling)) { next = next.nextSibling; } updateDatas.push({ updateType: updateType.update, findData: { ID: node.preSibling.getID() }, updateData: { NextSiblingID: next.getNextSiblingID() } }); } } updateGuideItems(updateDatas, function () { console.log(blockNodes); controller.m_delete(blockNodes); guideItemInitSel(sheet.getActiveRowIndex()); refreshBtn(bills.tree.selected.guidance.tree.selected); setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); if (bills.tree.selected.data.hasGuide && !bills.tree.selected.guidance.tree.items.length) { bills.tree.selected.data.hasGuide = false; setBillsForeColor([bills.tree.selected]); } $.bootstrapLoading.end(); guideItem.workBook.focus(true) }); } //项目指引升级 //@return {void} function upLevel() { $.bootstrapLoading.start(); let controller = bills.tree.selected.guidance.controller; let selected = bills.tree.selected.guidance.tree.selected; let updateDatas = []; //更新父节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.getParentID() }, updateData: { NextSiblingID: selected.getID() } }); //更新选中节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.getID() }, updateData: { ParentID: selected.parent.getParentID(), NextSiblingID: selected.parent.getNextSiblingID(), unit: '', interval: '' } }); if (selected.nextSibling && selected.children.length > 0) { //更新选中节点最末子节点 let lastChild = selected.children[selected.children.length - 1]; updateDatas.push({ updateType: updateType.update, findData: { ID: lastChild.getID() }, updateData: { NextSiblingID: -1 } }); } //选中节点的所有后兄弟节点成为选中节点的子项 let selectedNextIDs = []; let sNext = selected.nextSibling; while (sNext) { selectedNextIDs.push(sNext.getID()); sNext = sNext.nextSibling; } for (let sID of selectedNextIDs) { updateDatas.push({ updateType: updateType.update, findData: { ID: sID }, updateData: { ParentID: selected.getID() } }); } updateGuideItems(updateDatas, function () { controller.upLevel(); const sheet = guideItem.workBook.getActiveSheet(); refreshBtn(bills.tree.selected.guidance.tree.selected); setNodesColor(sheet, bills.tree.selected.guidance.tree.items); setProcessNodes(sheet, bills.tree.selected.guidance.tree.items); const unitCol = guideItem.headers.findIndex(item => item.dataCode === 'unit'); const intervalCol = guideItem.headers.findIndex(item => item.dataCode === 'interval'); sheet.setValue(selected.serialNo(), unitCol, ''); sheet.setValue(selected.serialNo(), intervalCol, ''); $.bootstrapLoading.end(); guideItem.workBook.focus(true)//31574 }); } //项目指引降级 //@return {void} function downLevel() { $.bootstrapLoading.start(); let controller = bills.tree.selected.guidance.controller; let selected = bills.tree.selected.guidance.tree.selected; let updateDatas = []; //更新前兄弟节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.preSibling.getID() }, updateData: { NextSiblingID: selected.getNextSiblingID() } }); //更新前兄弟节点最末子节点 if (selected.preSibling.children.length > 0) { let lastChild = selected.preSibling.children[selected.preSibling.children.length - 1]; updateDatas.push({ updateType: updateType.update, findData: { ID: lastChild.getID() }, updateData: { NextSiblingID: selected.getID() } }); } //更新选中节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.getID() }, updateData: { ParentID: selected.preSibling.getID(), NextSiblingID: -1, unit: '', interval: '' } }); updateGuideItems(updateDatas, function () { controller.downLevel(); const sheet = guideItem.workBook.getActiveSheet(); refreshBtn(bills.tree.selected.guidance.tree.selected); setNodesColor(sheet, bills.tree.selected.guidance.tree.items); setProcessNodes(sheet, bills.tree.selected.guidance.tree.items); const unitCol = guideItem.headers.findIndex(item => item.dataCode === 'unit'); const intervalCol = guideItem.headers.findIndex(item => item.dataCode === 'interval'); sheet.setValue(selected.serialNo(), unitCol, ''); sheet.setValue(selected.serialNo(), intervalCol, ''); $.bootstrapLoading.end(); guideItem.workBook.focus(true) }); } //项目指引上移 //@return {void} function upMove() { $.bootstrapLoading.start(); let controller = bills.tree.selected.guidance.controller; let selected = bills.tree.selected.guidance.tree.selected; let updateDatas = []; //更新前节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.preSibling.getID() }, updateData: { NextSiblingID: selected.getNextSiblingID() } }); //更新前前节点 if (selected.preSibling.preSibling) { updateDatas.push({ updateType: updateType.update, findData: { ID: selected.preSibling.preSibling.getID() }, updateData: { NextSiblingID: selected.getID() } }); } //更新选中节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.getID() }, updateData: { NextSiblingID: selected.preSibling.getID() } }); updateGuideItems(updateDatas, function () { controller.upMove(); refreshBtn(bills.tree.selected.guidance.tree.selected); setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); $.bootstrapLoading.end(); guideItem.workBook.focus(true) }); } //项目指引下移 //@return {void} function downMove() { $.bootstrapLoading.start(); let controller = bills.tree.selected.guidance.controller; let selected = bills.tree.selected.guidance.tree.selected; let updateDatas = []; //更新下节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.getNextSiblingID() }, updateData: { NextSiblingID: selected.getID() } }); //更新前节点 if (selected.preSibling) { updateDatas.push({ updateType: updateType.update, findData: { ID: selected.preSibling.getID() }, updateData: { NextSiblingID: selected.getNextSiblingID() } }); } //更新选中节点 updateDatas.push({ updateType: updateType.update, findData: { ID: selected.getID() }, updateData: { NextSiblingID: selected.nextSibling.getNextSiblingID() } }); updateGuideItems(updateDatas, function () { controller.downMove(); refreshBtn(bills.tree.selected.guidance.tree.selected); setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); $.bootstrapLoading.end(); guideItem.workBook.focus(true) }); } //获取定额类型的项目指引名称,通过定额转换 //@param {Object}ration @return {String} function getRationItemName(ration) { let arr = []; arr.push(ration.code ? ration.code : ''); arr.push(ration.name ? ration.name : ''); arr.push(ration.basePrice ? ration.basePrice : ''); let rst = arr.join(' '); rst += `/${ration.unit ? ration.unit : ''}`; return rst; } //获取选中的定额表行 //@return {Array} function getCheckedRationRows(all) { let rst = []; let sheet = ration.workBook.getActiveSheet(); for (let i = 0; i < sheet.getRowCount(); i++) { // 全选 if (all) { rst.push(i); continue; } let checked = sheet.getValue(i, 0); if (checked) { rst.push(i); } } return rst; } //清空选中定额表行 //@param {Array}rows @return {void} function clearCheckedRation(rows) { let sheet = ration.workBook.getActiveSheet(); renderSheetFunc(sheet, function () { for (let row of rows) { sheet.setValue(row, 0, 0); } }); } //获取要插入的定额数据 //@param {Array}rows @return {Array} function getInsertRations(rows) { let rst = []; //当前已存在定额 let curRationIems = []; let selected = bills.tree.selected.guidance.tree.selected; if (selected) { if (selected.data.type === itemType.job) { curRationIems = selected.children; } else { curRationIems = selected.parent ? selected.parent.children : selected.tree.roots; } } for (let row of rows) { let selRation = ration.cache[row]; if (selRation) { //添加的定额是否已存在,不重复添加 let isExist = false; for (let curRation of curRationIems) { if (curRation.data.rationID == selRation.ID) { isExist = true; break; } } if (!isExist) { rst.push({ type: itemType.ration, name: getRationItemName(selRation), rationID: selRation.ID }); } } } return rst; } //获取块节点父项不存在于选中节点中的节点 //@param {Array}nodes(选中的节点) @return {Array} function getBlockNodes(nodes) { let nodeMapping = {}; for (let node of nodes) { nodeMapping[node.data.ID] = node; } //块节点,父项不存在于选中节点中的节点 let blockNodes = []; for (let node of nodes) { if (!nodeMapping[node.data.ParentID]) { blockNodes.push(node); } } return blockNodes; } //允许复制整块,如果有多个块节点,且块节点的父项不同,则不可复制 //@param {Array}nodes(块节点) @return {Boolean} function canCopyBlock(nodes) { if (!nodes || nodes.length === 0) { return false; } let pID = nodes[0].data.ParentID; for (let node of nodes) { if (node.data.ParentID !== pID) { return false; } } return true; } //允许粘贴整块 有粘贴数据,节点存在,如果粘贴到的节点为定额数据,粘贴数据为全定额数据 //@param {Object}node(粘贴到的节点) function canPasteBlock(node) { let pasteDatas = JSON.parse(getLocalCache(itemCopyBlockKey)); if (!pasteDatas || pasteDatas.length === 0) { return false; } if (!node) { return false; } //若粘贴到定额节点,则数据须全为定额 if (node.data.type === itemType.ration) { for (let data of pasteDatas) { if (data.type !== itemType.ration) { return false; } } } //若粘贴到非定额节点,则粘贴的顶层数据须全为非定额 else { let topDatas = _.filter(pasteDatas, { ParentID: -1 }); for (let topData of topDatas) { if (topData.type === itemType.ration) { return false; } } } return true; } //复制整块,将块节点下所有节点数据复制一份,并且重新生成ID、ParentID、NextSiblingID,使用localStorage存储 //@param {Array}nodes(块节点) @return {void} function copyBlocks(nodes) { nodes = _.cloneDeep(nodes); //将块节点的ParentID暂时设置为-1 for (let topNode of nodes) { topNode.data.ParentID = -1; } let copyDatas = []; let copyNodes = []; //获取块节点包含的所有节点(包括自己) function containNodes(nodes) { for (let node of nodes) { copyNodes.push(node); if (node.children.length > 0) { containNodes(node.children); } } } containNodes(nodes); for (let node of copyNodes) { copyDatas.push(node.data); } console.log(`copyDatas`); console.log(copyDatas); setLocalCache(itemCopyBlockKey, JSON.stringify(copyDatas)); } //粘贴整块,整块数据粘贴到相关节点,并成为其后项 //@param {Object}node(粘贴到的节点) @return {void} function pasteBlock(node) { let itemObj = bills.tree.selected.guidance; let pasteDatas = JSON.parse(getLocalCache(itemCopyBlockKey)); //整理ID let IDMapping = {}; for (let data of pasteDatas) { data.newID = uuid.v1(); IDMapping[data.ID] = data; } for (let data of pasteDatas) { let nextData = IDMapping[data.NextSiblingID]; data.NextSiblingID = nextData ? nextData.newID : -1; let parentData = IDMapping[data.ParentID]; data.ParentID = parentData ? parentData.newID : -1; } for (let data of pasteDatas) { data.ID = data.newID; delete data.newID; } let updateDatas = []; //将最顶层的块数据的ParentID设置成粘贴到节点的ParentID,并设置新的billsID let topDatas = _.filter(pasteDatas, { ParentID: -1 }); for (let topData of topDatas) { topData.ParentID = node.getParentID(); } //更新数据 //更新插入的最末顶层数据NextSiblingID if (node.nextSibling) { topDatas[topDatas.length - 1].NextSiblingID = node.getNextSiblingID(); } //新建节点 for (let data of pasteDatas) { data.libID = libID; data.billsID = node.data.billsID; delete data._id; updateDatas.push({ updateType: updateType.create, updateData: data }); } console.log(`pasteDatas`); console.log(pasteDatas); //更新粘贴到的节点的NextSiblingID updateDatas.push({ updateType: updateType.update, findData: { ID: node.data.ID }, updateData: { NextSiblingID: topDatas[0].ID } }) $.bootstrapLoading.start(); updateGuideItems(updateDatas, function (rstData) { $.bootstrapLoading.end(); node.data.NextSiblingID = topDatas[0].ID; let newNodes = itemObj.tree.insertDatasTo(node.data, pasteDatas); cleanData(guideItem.workBook.getActiveSheet(), guideItem.headers, -1); itemObj.controller.showTreeData(); setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); setProcessNodes(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items); }, function () { $.bootstrapLoading.end(); }); } // 双击定位定额 async function locateAtRation(row) { const node = bills.tree.selected.guidance.tree.items[row]; if (!node || !node.data.rationID) { return; } const rationItem = ration.datas.find(item => item.ID === node.data.rationID); // 当前库没有找到,需要后端查找 if (!rationItem) { const findedRation = await ajaxPost('/rationRepository/api/getRationByID', { ID: node.data.rationID }); if (!findedRation || !rationLibIDs.includes(findedRation.rationRepId)) { alert('无法定位此定额,此定额已被删除'); return; } changeRationLib(findedRation.rationRepId, () => { located(findedRation.ID, findedRation.sectionId); }) } else { located(rationItem.ID, rationItem.sectionId); } function located(rationID, sectionID) { if (!section.tree) { return; } const sectionNode = section.tree.nodes[section.tree.prefix + sectionID]; if (!sectionNode) { return; } // 定位到对应章节树 const sectionSheet = section.workBook.getActiveSheet(); const sectionRow = sectionNode.serialNo(); expandSearchNodes(sectionSheet, [sectionNode], section.tree.roots); sectionSheet.setSelection(sectionRow, 0, 1, 1); sectionSheet.showRow(sectionRow, GC.Spread.Sheets.VerticalPosition.center); sectionInitSel(sectionRow); // 定位到对应定额 const locatedRationRow = ration.cache.findIndex(item => item.ID === rationID); if (locatedRationRow !== -1) { const rationSheet = ration.workBook.getActiveSheet(); rationSheet.setSelection(locatedRationRow, 1, 1, 1); rationSheet.showRow(locatedRationRow, GC.Spread.Sheets.VerticalPosition.center); } } } //初始化右键菜单 //@return {void} function initContextMenu() { $.contextMenu({ selector: '#guideItemSpread', build: function ($triggerElement, e) { //控制允许右键菜单在哪个位置出现 let sheet = guideItem.workBook.getSheet(0); let offset = $("#guideItemSpread").offset(), x = e.pageX - offset.left, y = e.pageY - offset.top; let target = sheet.hitTest(x, y); if (target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined') {//在表格内 let sel = sheet.getSelections()[0]; if (sel && sel.rowCount === 1) { sheet.setActiveCell(target.row, target.col); } sel = sheet.getSelections()[0]; let selNodes = []; if (sel) { sel.row = sel.row === -1 ? 0 : sel.row; for (let i = 0; i < sel.rowCount; i++) { if (bills.tree.selected.guidance.tree.items[sel.row + i]) { selNodes.push(bills.tree.selected.guidance.tree.items[sel.row + i]); } } } //块节点 let blockNodes = getBlockNodes(selNodes); //右键在多选内则不重设焦点 if (!sel || sel.rowCount === 1 || !(target.row >= sel.row && target.row <= sel.row + sel.rowCount - 1)) { sheet.setActiveCell(target.row, target.col); } guideItemInitSel(target.row); return { callback: function () { }, items: { "copy": { name: "复制整块", disabled: function () { return locked || !canCopyBlock(blockNodes); }, icon: "fa-copy", callback: function (key, opt) { copyBlocks(blockNodes); } }, "paste": { name: "粘贴整块", disabled: function () { let pasteNode = bills.tree.selected.guidance.tree.items[target.row]; return locked || !canPasteBlock(pasteNode); }, icon: "fa-paste", callback: function (key, opt) { let pasteNode = bills.tree.selected.guidance.tree.items[target.row]; pasteBlock(pasteNode); } }, "delRations": { name: '删除定额', disabled: function () { let node = bills.tree.selected.guidance.tree.items[target.row]; return locked || !node }, icon: "fa-remove", callback: function (key, opt) { $('#delRationAlert').modal('show'); } }, "del": { name: '删除', disabled: function () { let node = bills.tree.selected.guidance.tree.items[target.row]; return locked || !node }, icon: "fa-remove", callback: function (key, opt) { $('#delAlert').modal('show'); } }, "insertSibling": { name: '插入行', disabled: function () { let node = bills.tree.selected.guidance.tree.items[target.row]; return locked || !node || node.data.type !== itemType.job; }, icon: "fa-arrow-left", callback: function (key, opt) { insert([{ type: itemType.job, name: '' }], false); } }, "insertChild": { name: '插入子项', disabled: function () { let node = bills.tree.selected.guidance.tree.items[target.row]; return locked || !node || node.data.type !== itemType.job || !allJobChildren(node); }, icon: 'fa-arrow-left', callback: function (key, opt) { insert([{ type: itemType.job, name: '' }], true); } } } }; } else { return false; } } }); } // 替换定额 function replaceRation(newRation, orgItem) { if (newRation.ID === orgItem.data.rationID) { return; } $.bootstrapLoading.start(); const updateAttrs = { name: getRationItemName(newRation), rationID: newRation.ID }; const updateData = [{ updateType: updateType.update, findData: { ID: orgItem.getID() }, updateData: updateAttrs }]; updateGuideItems(updateData, function () { Object.assign(orgItem.data, updateAttrs); const row = orgItem.serialNo(); guideItem.workBook.getSheet(0).setValue(row, 0, orgItem.data.name); $.bootstrapLoading.end(); }); } // 初始化清单右键菜单 function initRationContextMenu() { $.contextMenu({ selector: '#rationSpread', build: function ($triggerElement, e) { //控制允许右键菜单在哪个位置出现 let sheet = ration.workBook.getActiveSheet();; let offset = $("#rationSpread").offset(), x = e.pageX - offset.left, y = e.pageY - offset.top; let target = sheet.hitTest(x, y); if (target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined') {//在表格内 let sel = sheet.getSelections()[0]; if (sel && sel.rowCount === 1) { sheet.setActiveCell(target.row, target.col); } sel = sheet.getSelections()[0]; if (sel) { sel.row = sel.row === -1 ? 0 : sel.row; } //右键在多选内则不重设焦点 if (!sel || sel.rowCount === 1 || !(target.row >= sel.row && target.row <= sel.row + sel.rowCount - 1)) { sheet.setActiveCell(target.row, target.col); } return { callback: function () { }, items: { "replace": { name: "替换定额", disabled: function () { return locked || !bills.tree.selected || !bills.tree.selected.guidance || !bills.tree.selected.guidance.tree || !bills.tree.selected.guidance.tree.selected || bills.tree.selected.guidance.tree.selected.data.type !== itemType.ration; }, icon: "fa-copy", callback: function (key, opt) { const row = sheet.getActiveRowIndex(); if (!ration.cache[row]) { return; } replaceRation(ration.cache[row], bills.tree.selected.guidance.tree.selected); // copyBlocks(blockNodes); } }, } }; } else { return false; } } }); } // 初始化定额右键菜单 function initBillsContextMenu() { $.contextMenu({ selector: '#billsSpread', build: function ($triggerElement, e) { //控制允许右键菜单在哪个位置出现 let sheet = bills.workBook.getActiveSheet();; let offset = $("#billsSpread").offset(), x = e.pageX - offset.left, y = e.pageY - offset.top; let target = sheet.hitTest(x, y); if (target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined') {//在表格内 let sel = sheet.getSelections()[0]; if (sel && sel.rowCount === 1) { sheet.setActiveCell(target.row, target.col); } sel = sheet.getSelections()[0]; if (sel) { sel.row = sel.row === -1 ? 0 : sel.row; } //右键在多选内则不重设焦点 if (!sel || sel.rowCount === 1 || !(target.row >= sel.row && target.row <= sel.row + sel.rowCount - 1)) { sheet.setActiveCell(target.row, target.col); } billsInitSel(target.row, { row: bills.tree.selected.serialNo() }); return { callback: function () { }, items: { "replace": { name: "配置材料", disabled: function () { return !bills.tree.selected; }, icon: "fa-edit", callback: function (key, opt) { $('#bill-material-modal').modal('show'); } }, } }; } else { return false; } } }); } //展开至搜索出来点的节点 //@param {Array}nodes @return {void} function expandSearchNodes(sheet, nodes, roots) { renderSheetFunc(sheet, function () { function expParentNode(node) { if (node.parent) { if (!node.parent.expanded) { node.parent.setExpanded(true); } expParentNode(node.parent); } } for (let node of nodes) { expParentNode(node); } TREE_SHEET_HELPER.refreshNodesVisible(roots, sheet, true); }); } // 清空搜索高亮 function clearHighLight(sheet) { renderSheetFunc(sheet, () => { for (let i = 0; i < sheet.getRowCount(); i++) { setBgColor(sheet, i, 'white') } }); } // 清空搜索状态 // 关闭搜索清单结果 function closeSearchBills(sheet) { if (!bills.tree) { return; } $('#searchBillsResult').hide(); bills.tree.items.forEach(node => { node.isSearch = false; }); clearHighLight(sheet); setBgColor(sheet, sheet.getActiveRowIndex(), selectedBgColor); } // 搜索清单 function searchBills() { if (!bills.tree || !bills.workBook) { return; } const sheet = bills.workBook.getActiveSheet(); const str = $('#searchBillText').val().trim(); // 空搜索字符,关闭搜索 if (!str) { if ($('#searchBillsResult').is(':visible')) { closeSearchBills(sheet); } return; } // 过滤清单 const result = bills.tree.items.filter(item => { const codeIs = item.data.code ? item.data.code.indexOf(str) !== -1 : false; const nameIs = item.data.name ? item.data.name.indexOf(str) !== -1 : false; return codeIs || nameIs; }); if (!result.length) { closeSearchBills(sheet); return; } // 显示搜索结果 $("#searchBillsResult").show(); $('#searchBillsCount').text(result.length); //展开搜索出来的节点 expandSearchNodes(sheet, result, bills.tree.roots); // 标黄结果 clearHighLight(sheet); const col = sheet.getActiveColumnIndex(); renderSheetFunc(sheet, function () { bills.controller.setTreeSelected(result[0]); bills.tree.items.forEach(node => { if (result.includes(node)) { setBgColor(sheet, node.serialNo(), searchBgColor); node.isSearch = true; // 标记为搜索结果,防止被焦点行变更恢复颜色 } else { node.isSearch = false; } }) }); //搜索初始定位 const row = sheet.getActiveRowIndex(); sheet.setSelection(result[0].serialNo(), col, 1, 1); billsInitSel(result[0].serialNo(), { row }); sheet.showRow(result[0].serialNo(), GC.Spread.Sheets.VerticalPosition.center); let curIndex = 0; // 上一条 $('#preBill').unbind('click'); $('#preBill').bind('click', () => { const node = result[curIndex - 1]; if (!node) { return; } curIndex -= 1; const col = sheet.getActiveColumnIndex(); const row = node.serialNo(); const orgRow = sheet.getActiveRowIndex(); sheet.setSelection(row, col, 1, 1); billsInitSel(row, { row: orgRow }); sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center); }); // 下一条 $('#nextBills').unbind('click'); $('#nextBills').bind('click', () => { const node = result[curIndex + 1]; if (!node) { return; } curIndex += 1; const col = sheet.getActiveColumnIndex(); const row = node.serialNo(); const orgRow = sheet.getActiveRowIndex(); sheet.setSelection(row, col, 1, 1); billsInitSel(row, { row: orgRow }); sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center); }); } //初始化dom时间 //@return {void} function initDomEvents() { // 清单材料窗口 $("#bill-material-modal").on('hidden.bs.modal', function () { billMaterial.cache = []; showBillMaterialData(billMaterial.workBook.getSheet(0), billMaterial.headers, billMaterial.cache, 30); }); $("#bill-material-modal").on('shown.bs.modal', function () { if (billMaterial.workBook && billMaterialHelper.workBook) { billMaterialHelper.workBook.refresh(); billMaterial.workBook.refresh(); getBillMaterials(); } }); /* 自动配置材料 */ $('#autoSetMaterial').click(async function() { $.bootstrapLoading.start(); try { await ajaxPost('/billsGuidance/api/autoSetMaterial', { libID}, 1000 * 60 * 10); // 刷新显示 window.location.reload(); } catch (error) { alert(error); } $.bootstrapLoading.end(); }); /* excel */ // 生成excel $('#buildExcel').click(function () { if (bills && bills.tree) { $('#excel-modal').modal('show'); } }); $("#excel-modal").on('hidden.bs.modal', function () { $('#excel-info').text(`导出排版中:`); $('#excel-spread').hide(); $('#export-excel-confirm').hide(); $('#excel-dialog').width('500px'); if (exportExcelWorkBook) { exportExcelWorkBook.clearSheets(); exportExcelWorkBook.addSheet(); } }); let excelInstance; $("#excel-modal").on('shown.bs.modal', async function () { exportExcelWorkBook.refresh(); if (bills.tree) { excelInstance = new ExportExcel(exportExcelWorkBook, bills.tree, libID); try { // await excelInstance.paintOnSheet(); await excelInstance.paintOnWorkBook(); } catch (error) { console.log(error); alert(error); $("#excel-modal").modal('hide'); } } }); $('#export-excel-confirm').click(() => { if (excelInstance) { excelInstance.export(); } }) $('#insert').click(function () { insert([{ type: itemType.job, name: '', outputItemCharacter: true }], false); }); $('#delConfirm').click(function () { del(); $('#delAlert').modal('hide'); }); $('#delRationConfirm').click(function () { delRations(); $('#delRationAlert').modal('hide'); }); $('#del').click(function () { $('#delAlert').modal('show'); }); $('#upLevel').click(function () { upLevel(); }); $('#downLevel').click(function () { downLevel(); }); $('#upMove').click(function () { upMove(); }); $('#downMove').click(function () { downMove(); }); // 收起清单至第二层 $('#expandToSecond').click(function () { if (!bills.tree || !bills.workBook) { return; } bills.tree.roots.forEach(root => { root.setExpanded(true); root.children.forEach(item => { item.setExpanded(false); }); }); const billSheet = bills.workBook.getActiveSheet(); renderSheetFunc(billSheet, function () { TREE_SHEET_HELPER.refreshNodesVisible(bills.tree.roots, billSheet, true); }); }) //收起定额、展开全部 $('#expandContract').click(function () { //目前状态时展开全部节点状态,点击则收起定额 let tree = bills.tree.selected.guidance.tree, itemSheet = guideItem.workBook.getActiveSheet(); if (curExpandState === itemExpandState.expand) { curExpandState = itemExpandState.contract; $(this).html(' 展开全部'); setNodesExpandState(tree.items, itemExpandState.contract); } else { curExpandState = itemExpandState.expand; $(this).html(' 收起定额'); setNodesExpandState(tree.items, itemExpandState.expand); } renderSheetFunc(itemSheet, function () { TREE_SHEET_HELPER.refreshNodesVisible(tree.roots, itemSheet, true); }); }); // 配置材料 $('#editMaterial').click(function () { if (bills && bills.tree && bills.tree.selected) { $('#bill-material-modal').modal('show'); } }); // 插入选中定额 $('#insertRation').click(function () { let checkedRows = getCheckedRationRows(); let insertDatas = getInsertRations(checkedRows); if (insertDatas.length > 0) { insert(insertDatas, false, function () { //清空选择 clearCheckedRation(checkedRows); }); } else { clearCheckedRation(checkedRows); } }); // 插入全部定额 $('#insertAll').click(function () { let isAll = true; let checkedRows = getCheckedRationRows(isAll); let insertDatas = getInsertRations(checkedRows); if (insertDatas.length > 0) { insert(insertDatas, false); } }); // 搜索清单 $("#searchBillBtn").click(searchBills); $('#searchBillText').keyup(function (e) { delayKeyup(function () { $('#searchBillBtn').click(); }); }); // 关闭搜索清单 $('#closeSearchBills').click(() => { if (!bills.workBook) { return; } const sheet = bills.workBook.getActiveSheet(); closeSearchBills(sheet); }) //搜索定额 $('#searchBtn').click(function () { let searchStr = $('#searchText').val(); if (!searchStr || searchStr === '') { ration.cache = ration.datas; } else { let reg = new RegExp(searchStr, 'i'); ration.cache = _.filter(ration.datas, function (data) { return reg.test(data.code) || reg.test(data.name); }); } $('.top-content').hide(); $('#deResize').hide(); $('#searchCount').text(`搜索结果: ${ration.cache.length}`); $('#rationSearchResult').show(); autoFlashHeight(); ration.workBook.refresh(); let rationSheet = ration.workBook.getActiveSheet(); renderSheetFunc(rationSheet, function () { clearCheckedRation(getCheckedRationRows()); showData(rationSheet, ration.headers, ration.cache); }) }); //关闭搜索 $('#rationSearchResult a').click(function () { $('.top-content').show(); $('#deResize').show(); $('#rationSearchResult').hide(); autoFlashHeight(); renderSheetFunc(ration.workBook.getActiveSheet(), function () { clearCheckedRation(getCheckedRationRows()); }); section.workBook.refresh(); ration.workBook.refresh(); $('#searchText').val(''); //恢复章节树下的定额 sectionInitSel(section.workBook.getActiveSheet().getActiveRowIndex()); }); let keyupTime = 0, delayTime = 500; function delayKeyup(callback) { let nowTime = Date.now(); keyupTime = nowTime; setTimeout(function () { if (nowTime - keyupTime == 0) { callback(); } }, delayTime); } //执行搜索 $('#searchText').keyup(function (e) { delayKeyup(function () { $('#searchBtn').click(); }); }); //编辑清单备注 $('.main-side-bottom').find('textarea').keyup(function () { let me = this; let node = bills.tree.selected; let comment = $(me).val(); delayKeyup(function () { if (node) { let updateData = { lastOperator: userAccount, billsLibId: billsLibId, updateId: node.getID(), field: 'comment', data: comment }; updateBillsComment(updateData, function () { node.data.comment = comment; }, function () { $(me).val(node.data.comment || ''); }) } }); }); //编辑选项备注 $('.main-bottom-content').find('textarea').keyup(function () { let me = this; let node = bills.tree.selected.guidance.tree.selected; let comment = $(me).val(); delayKeyup(function () { if (node) { let updateDatas = [{ updateType: updateType.update, findData: { ID: node.getID() }, updateData: { comment: comment } }]; updateGuideItems(updateDatas, function (rstData) { node.data.comment = comment; }, function () { $(me).val(node.data.comment || ''); }); } }); }); //定额高度拖动调整 let heightEleObj = { module: moduleName, resize: $('#deResize'), top: $('#topContent'), topSpread: $('#sectionSpread'), bottom: $('#bottomContent'), bottomSpread: $('#rationSpread') }, heightLimit = { min: 150, max: `$(window).height()-$('.header').height()-$('.sidebar-tools-bar').height()-150-10`, notTopSpread: 0, notBottomSpread: 0, }; SlideResize.verticalSlide(heightEleObj, heightLimit, function () { if (section.workBook) { section.workBook.refresh(); } if (ration.workBook) { ration.workBook.refresh(); } }); /*slideResize(rationLibResizeEles, {min: 147, max: 680}, 'height', function() { //autoFlashHeight(); if(section.workBook){ section.workBook.refresh(); } if(ration.workBook){ ration.workBook.refresh(); } });*/ //左右拖动 //清单表与项目指引表 let leftElesObj = {}; leftElesObj.module = moduleName; leftElesObj.resize = $('#slideResizeLeft'); leftElesObj.parent = $('#dataRow'); leftElesObj.left = $('#leftContent'); leftElesObj.right = $('#midContent'); SlideResize.horizontalSlide(leftElesObj, { min: 200, max: `$('#dataRow').width() - $('#rightContent').width() - 200` }, function () { refreshALlWorkBook(); }); //人材机表与人材机组成物表 let rightElesObj = {}; rightElesObj.module = moduleName; rightElesObj.resize = $('#slideResizeRight'); rightElesObj.parent = $('#dataRow'); rightElesObj.left = $('#midContent'); rightElesObj.right = $('#rightContent'); SlideResize.horizontalSlide(rightElesObj, { min: 200, max: `$('#dataRow').width() - $('#leftContent').width() - 200` }, function () { refreshALlWorkBook(); }); } //刷新全部工作簿 //@return {void} function refreshALlWorkBook() { if (bills.workBook) { bills.workBook.refresh(); } if (guideItem.workBook) { guideItem.workBook.refresh(); } if (section.workBook) { section.workBook.refresh(); } if (ration.workBook) { ration.workBook.refresh(); } $('.main-side-bottom').find('textarea').height($('.main-side-bottom').height() - 20); $('.main-side-bottom').find('textarea').width($('.main-side-bottom').width() - 25); $('.main-bottom-content').find('textarea').height($('.main-bottom-content').height() - 20); $('.main-bottom-content').find('textarea').width($('.main-bottom-content').width() - 25); } //读取拖动相关 //@return {void} function initSlideSize() { //定额表上下 let heightEleObj = { module: moduleName, top: $('#topContent'), topSpread: $('#sectionSpread'), bottom: $('#bottomContent'), bottomSpread: $('#rationSpread') }; if (!$('#rationSearchResult').is(':visible')) { SlideResize.loadVerticalHeight(heightEleObj.module, heightEleObj, { totalHeight: `$(window).height()-$('.header').height()-$('.sidebar-tools-bar').height()-10`, notTopSpread: 0, notBottomSpread: 0 }, function () { if (section.workBook) { section.workBook.refresh(); } if (ration.workBook) { ration.workBook.refresh(); } }); } //水平 SlideResize.loadHorizonWidth(moduleName, [$('#slideResizeLeft'), $('#slideResizeRight')], [$('#leftContent'), $('#midContent'), $('#rightContent')], function () { refreshALlWorkBook(); }); } //初始化视图 //@param {void} @return {void} function initViews() { let modules = [bills, guideItem, section, ration, billMaterial, billMaterialHelper]; initWorkBooks(modules); lockUtil.lockTools($(document.body), locked); getLibWithBills(libID); initDomEvents(); initContextMenu(); initRationContextMenu(); //initBillsContextMenu(); initSlideSize(); } // 判断蓝色子项和孙子项是否打勾了必填 function hasRequireData(node) { const requiredList = node.getPosterity().filter(subNode => subNode.data.required === true ); if (requiredList.length) { return true } return false; } // 这里判断有无无定额的情况,有的就返回true,没有就返回false function isAllRationData(node) { let isRequired = true; if (node.children && node.children.length) { node.children.forEach(subNode => { if (!subNode.children || !subNode.children.length) { isRequired = false; } }) } //这里是兼容第一层直接是定额的白色节点 if (node.children && node.children.length) { node.children.forEach(subNode => { if (subNode.data.rationID) { isRequired = true; } }) } return isRequired; } // 获取选套定额 function getOptionalData(node, list = []) { if (isProcessNode(node)) { node.children.forEach(element => { if (element.children && element.children.length) { element.children.forEach(item => { if (item.data.rationID) { list.push(item.data.rationID); } else if (isProcessNode(item)) { getOptionalData(item, list) } }) } }); } else { node.children.forEach(element => { if (element.data.rationID) { list.push(element.data.rationID); } }); } return list; } // 获取必填项下的ID和name的键值对 function getClassCodeStrData(nodes,data={classGroups:{},keyGroup:{}}){ nodes.forEach(node=>{ if (isProcessNode(node)&&node.data.required) { node.children.forEach(subNode=>{ data.classGroups[subNode.data.ID]=subNode.data.name; data.keyGroup[subNode.data.ID]=`${subNode.parent.data.name}:${subNode.data.name}`; }) } getClassCodeStrData(node.children,data); }) return data; } //获取定额数据 // requireRationData必套定额对象 // optionalRationData 选逃定额对象 // classGroups classCode文字和id键值对 // classCodeList 各个classCode的pID和ID的关系 function getItemData(nodes, requireRationData = {}, optionalRationData = {}, classGroups = {}, prefixID = '', prefixSonID = '', IDData = {},keyGroup={}) { const processNodes = nodes.filter(node => isProcessNode(node)); // const classGroups = []; // 同层必填选项的数组(二维数组) processNodes.forEach(processNode => { // 蓝色节点,必填 if (processNode.data.required) { // 白色节点 const optionNodes = processNode.children.filter(node => isOptionNode(node)); optionNodes.forEach(optionNode => { if (!requireRationData[optionNode.data.ID]) requireRationData[optionNode.data.ID] = []; if (!optionalRationData[optionNode.data.ID]) optionalRationData[optionNode.data.ID] = []; //白色节点下没有蓝色节点,就是到底了 if (!optionNode.children.some(subOptionNode => isProcessNode(subOptionNode))) { // 是否必套定额 if (isAllRationData(optionNode)) { optionNode.children.forEach(subOptionNode => { requireRationData[optionNode.data.ID].push([subOptionNode.data.rationID]); }) } else { optionalRationData[optionNode.data.ID] = getOptionalData(optionNode); } const kV = {}; kV[optionNode.data.ID] = optionNode.data.name; Object.assign(classGroups, kV); const keyData={}; keyData[optionNode.data.ID] = `${optionNode.parent.data.name}:${optionNode.data.name}`; Object.assign(keyGroup,keyData); } else { const kV = {}; kV[optionNode.data.ID] = optionNode.data.name; Object.assign(classGroups, kV); const keyData={}; keyData[optionNode.data.ID] = `${optionNode.parent.data.name}:${optionNode.data.name}`; Object.assign(keyGroup,keyData); // 后代项是否有必填 if (hasRequireData(optionNode)) { //后代项有必填 prefixSonID = ''; getItemData(optionNode.children, requireRationData, optionalRationData, classGroups, optionNode.data.ID, prefixSonID, IDData,keyGroup); } else { //后代项无必填 optionNode.children.forEach(subOptionNode => { // 是否必套定额 if (isAllRationData(optionNode)) { if (!requireRationData[subOptionNode.parent.data.ID]) requireRationData[subOptionNode.parent.data.ID] = []; requireRationData[subOptionNode.parent.data.ID].push(getOptionalData(subOptionNode)); } else { if (!optionalRationData[subOptionNode.parent.data.ID]) optionalRationData[subOptionNode.parent.data.ID] = []; optionalRationData[subOptionNode.parent.data.ID].push(...getOptionalData(subOptionNode)); } }) } } }) } else { // 蓝色节点,非必填 if (hasRequireData(processNode)) { //后代项有必填 if (isProcessNode(processNode)) { //蓝色 // 是否必套定额 if (isAllRationData(processNode)) { processNode.children.forEach((subProcessNode) => { subProcessNode.children.forEach((sSubProcessNode) => { //这里是特殊处理,因为原来的逻辑是直接把定额绑到必填白色选项中下面的,每个蓝色的一组,但是这样是不对的,需要绑定在必填白色选项下的蓝色节点,所以这里就需要传入蓝色节点的id const requireChildrenID = sSubProcessNode.parent.data.required ? prefixSonID : sSubProcessNode.data.ID; IDData[sSubProcessNode.data.ID] = prefixID; getItemData( [sSubProcessNode], requireRationData, optionalRationData, classGroups, prefixID, requireChildrenID, IDData, keyGroup ) }) }) }else{ // 全部选套就不用走循环了,直接按照选套执行 let key = processNode.data.ID; if (prefixID) key = prefixID; if (prefixSonID) key = prefixSonID; if (!optionalRationData[key]) optionalRationData[key] = []; optionalRationData[key].push(...getOptionalData(processNode)); // 因为这里没有按照走整个流程,所以文字和ID的关系需要获取补充 if(hasRequireData(processNode)) { const result =getClassCodeStrData(processNode.children) Object.assign(classGroups,result.classGroups); Object.assign(keyGroup,result.keyGroup); } ; } } } else { let key = processNode.data.ID; if (prefixID) key = prefixID; if (prefixSonID) key = prefixSonID; // 是否必套定额 if (isAllRationData(processNode)) { if (!requireRationData[key]) requireRationData[key] = []; requireRationData[key].push(getOptionalData(processNode)); } else { if (!optionalRationData[key]) optionalRationData[key] = []; optionalRationData[key].push(...getOptionalData(processNode)); } } } }) return { requireRationData, optionalRationData, classGroups, IDData, keyGroup } } /* 生成特征分类: 前端测试使用,想要前端测试,需要在zhiyin.html中,将id=generate-classa的按钮放开 */ function getItemCharacterData(nodes, prefix, prefixID) { const processNodes = nodes.filter(node => isProcessNode(node)); const classGroups = []; // 同层必填选项的数组(二维数组) processNodes.forEach(processNode => { const classItems = []; const optionNodes = processNode.children.filter(node => isOptionNode(node)); optionNodes.forEach(optionNode => { // const name = prefix ? `${prefix}@${optionNode.data.name}` : optionNode.data.name; if (optionNode.parent && optionNode.parent.data.required && (!optionNode.children || !optionNode.children.length || (optionNode.children[0].data && optionNode.children[0].data.rationID) || !optionNode.children.some(node => isProcessNode(node)))) { // 必套定额 classItems.push({ name: optionNode.data.name, ID: optionNode.data.ID }); } else { // classItems.push(...getItemCharacterData(optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : '')); const childrenClassItem = getItemCharacterData( optionNode.children, optionNode.parent && optionNode.parent.data.required ? optionNode.data.name : '', optionNode.parent && optionNode.parent.data.required ? optionNode.data.ID : '' ); //如果返回的子项为空,但是父项又勾选了必填,则要把本身存入数组 if (optionNode.parent && optionNode.parent.data.required && childrenClassItem.length === 0 ) { classItems.push({ name: optionNode.data.name, ID: optionNode.data.ID }); } else { classItems.push(...childrenClassItem); } } }); if (classItems.length) { classGroups.push(classItems); } }); // 拼接上一文本 if (classGroups[0] && classGroups[0].length) { // classGroups[0] = classGroups[0].map(name => prefix ? `${prefix}@${name}` : name); classGroups[0] = classGroups[0].map(item => { item.name = prefix ? `${prefix}@${item.name}` : item.name item.ID = prefixID ? `${prefixID}@${item.ID}` : item.ID return item; }); } // 二维数组内容排列组合 while (classGroups.length > 1) { const prevClassItems = classGroups[0]; const nextClassItems = classGroups[1]; const mergedClassItems = []; for (let i = 0; i < prevClassItems.length; i++) { for (let j = 0; j < nextClassItems.length; j++) { // 拼接文本 const mergedName = `${prevClassItems[i].name}@${nextClassItems[j].name}`; const mergedID = `${prevClassItems[i].ID}@${nextClassItems[j].ID}`; mergedClassItems.push({ name: mergedName, ID: mergedID }); } } classGroups.splice(0, 2, mergedClassItems); } // 去重(类别别名要唯一) const items = classGroups[0] || []; const nameMap = {}; const rst = []; items.forEach(item => { if (!nameMap[item.name]) { rst.push(item); } nameMap[item.name] = true; }); return rst; } // 获取选套定额:把所有分类数据的必套定额确定好了先。选套定额就是清单下所有定额除了必套的 function getOptionalRationIDs(optionalRationData) { const optionalRationIDs = []; Object.values(optionalRationData).forEach(optionalRation => { if (optionalRation.length) optionalRationIDs.push(...optionalRation); }) return [...new Set(optionalRationIDs)]; } // 获取错套定额:清单下所有定额,除了分类对应的必套、选套定额 function getErrorRationIDs(requiredRationIDList, optionalRationIDs, guideNodes) { const finRequireData = []; requiredRationIDList.forEach(requiredRationIDs => { finRequireData.push(...requiredRationIDs); }) const errorRationIDs = []; guideNodes.forEach(node => { if (node.data.rationID && !finRequireData.includes(node.data.rationID) && !optionalRationIDs.includes(node.data.rationID)) { errorRationIDs.push(node.data.rationID); } }); return [...new Set(errorRationIDs)]; } //把classcode和必套选套定额结合在一起 function combineData(codeData, requireRationData, optionalRationData, classGroups, IDData,keyGroup) { // 这里要记录下已经被绑定的选套定额,因为没有被用的定额需要绑定到各个classcode下 const matchRationList = []; //这里需要把绑定在子节点的定额更新到必填的白色选项中(classcode的值) const requireCombineData = {}; const optionCombineData = {}; Object.keys(IDData).forEach(key => { if (!requireCombineData[IDData[key]]) requireCombineData[IDData[key]] = new Set(); if (!optionCombineData[IDData[key]]) optionCombineData[IDData[key]] = new Set(); if (requireRationData[key]) { requireRationData[key].forEach(subData => { subData.forEach(subItem => { requireCombineData[IDData[key]].add(subItem); }) }) } if (optionalRationData[key]) { optionalRationData[key].forEach(subData => { optionCombineData[IDData[key]].add(subData); }) } }) const finData = codeData.map(classCodeData => { const errorRationIDs = []; const optionalRationIDs = []; const requiredRationIDs = []; let name = ''; let key = ''; const classCodeIDs = classCodeData.ID; if (/@/.test(classCodeIDs)) { classCodeIDs.split('@').forEach((classCodeID) => { if (name) { name = name + '@' + classGroups[classCodeID]; key += keyGroup[classCodeID]; } else { name = classGroups[classCodeID]; key = keyGroup[classCodeID]; }; // 一组的必套定额,先去重 const unitRation = []; // 这里是必填选项下绑定必套定额 if (requireRationData[classCodeID] && requireRationData[classCodeID].length) { requireRationData[classCodeID].forEach(subItem => { unitRation.push(...new Set(subItem)); }) requiredRationIDs.push(unitRation); // 这里也要把用过的必套定额先存起来 matchRationList.push(...unitRation); } //这里是必填选项下绑定在蓝色节点下的必套定额 if (requireCombineData[classCodeID] && requireCombineData[classCodeID].size) { requiredRationIDs.push([...requireCombineData[classCodeID]]); // 这里也要把用过的必套定额先存起来 matchRationList.push(...requireCombineData[classCodeID]); } // 这里是必填选项下绑定选套定额 if (optionalRationData[classCodeID] && optionalRationData[classCodeID].length) { optionalRationIDs.push(...optionalRationData[classCodeID]); matchRationList.push(...optionalRationData[classCodeID]); } //这里是必填选项下绑定在蓝色节点下的选套定额,下同 if (optionCombineData[classCodeID] && optionCombineData[classCodeID].size) { optionalRationIDs.push(...optionCombineData[classCodeID]); matchRationList.push(...optionCombineData[classCodeID]); } }) return { name,key, requiredRationIDs, optionalRationIDs: [...new Set(optionalRationIDs)], errorRationIDs } } else { const unitRation = []; name = classGroups[classCodeIDs]; key = keyGroup[classCodeIDs]; if (requireRationData[classCodeIDs] && requireRationData[classCodeIDs].length){ requireRationData[classCodeIDs].forEach(subItem => { unitRation.push(...new Set(subItem)); }) requiredRationIDs.push(unitRation); // 这里也要把用过的必套定额先存起来 matchRationList.push(...unitRation); } if (requireCombineData[classCodeIDs] && requireCombineData[classCodeIDs].size) { requiredRationIDs.push([...requireCombineData[classCodeIDs]]) // 这里也要把用过的必套定额先存起来 matchRationList.push(...requireCombineData[classCodeIDs]); }; if (optionalRationData[classCodeIDs] && optionalRationData[classCodeIDs].length) { optionalRationIDs.push(...optionalRationData[classCodeIDs]); matchRationList.push(...optionalRationData[classCodeIDs]); } if (optionCombineData[classCodeIDs] && optionCombineData[classCodeIDs].size) { optionalRationIDs.push(...optionCombineData[classCodeIDs]); matchRationList.push(...optionalRationData[classCodeIDs]); } return { name,key, requiredRationIDs, optionalRationIDs: [...new Set(optionalRationIDs)], errorRationIDs } } }) const unMatchRation = []; Object.values(optionalRationData).forEach(data => { data.forEach((rationID) => { if (!matchRationList.includes(rationID)) { unMatchRation.push(rationID); } }) }) // 这里把没有使用过的必套定额也丢到了选套里面 Object.values(requireRationData).forEach(data => { data.forEach((rationData) => { rationData.forEach(rationID=>{ if (!matchRationList.includes(rationID)) { unMatchRation.push(rationID); } }) }) }) return { classData: finData, unMatchRation }; } $('#generate-class').click(() => { if (bills.tree.selected && bills.tree.selected.guidance.tree) { const classCodeData = getItemCharacterData(bills.tree.selected.guidance.tree.roots); const { requireRationData, optionalRationData, classGroups, IDData, keyGroup } = getItemData(bills.tree.selected.guidance.tree.roots); // const noMatchOptionRation=[]; const { classData, unMatchRation } = combineData(classCodeData, requireRationData, optionalRationData, classGroups, IDData,keyGroup); //因为所有的选套都是一样的,所以这里就直接赋值了 const optionalRationIDs = getOptionalRationIDs(optionalRationData); classData.forEach(item => { // 不在必填项下的选套都要绑定在每个classcode下 item.optionalRationIDs.push(...unMatchRation); item.errorRationIDs = getErrorRationIDs(item.requiredRationIDs, item.optionalRationIDs, bills.tree.selected.guidance.tree.items); }) console.log(optionalRationIDs); console.log(classData); } }); return { initViews, initSlideSize, bills }; })(); $(document).ready(function () { billsGuidance.initViews(); });