function setAlign(sheet, headers) { const fuc = () => { headers.forEach(({ hAlign, vAlign }, index) => { sheetCommonObj.setAreaAlign(sheet.getRange(-1, index, -1, 1), hAlign, vAlign) }); }; sheetCommonObj.renderSheetFunc(sheet, fuc); } function setFormatter(sheet, headers) { const fuc = () => { headers.forEach(({ formatter }, index) => { if (formatter) { sheet.setFormatter(-1, index, formatter); } }); }; sheetCommonObj.renderSheetFunc(sheet, fuc); } function initSheet(dom, setting) { const workBook = sheetCommonObj.buildSheet(dom, setting); const sheet = workBook.getSheet(0); setAlign(sheet, setting.header); setFormatter(sheet, setting.header); return workBook; } function showData(sheet, data, headers, emptyRows) { const fuc = () => { sheet.setRowCount(data.length); data.forEach((item, row) => { headers.forEach(({ dataCode }, col) => { sheet.setValue(row, col, item[dataCode] || ''); }); }); if (emptyRows) { sheet.addRows(data.length, emptyRows); } }; sheetCommonObj.renderSheetFunc(sheet, fuc); } const TIME_OUT = 10000; const libID = window.location.search.match(/libID=([^&]+)/)[1]; const UpdateType = { UPDATE: 'update', DELETE: 'delete', CREATE: 'create', }; const DEBOUNCE_TIME = 200; const locked = lockUtil.getLocked(); // 地区表 const AREA_BOOK = (() => { const cache = areaList; const setting = { header: [{ headerName: '地区', headerWidth: $('#area-spread').width(), dataCode: 'name', dataType: 'String', hAlign: 'center', vAlign: 'center' }] }; // 初始化表格 const workBook = initSheet($('#area-spread')[0], setting); lockUtil.lockSpreads([workBook], locked); workBook.options.allowExtendPasteRange = false; workBook.options.allowUserDragDrop = true; workBook.options.allowUserDragFill = true; const sheet = workBook.getSheet(0); // 排序显示 cache.sort((a, b) => a.serialNo - b.serialNo); // 显示数据 showData(sheet, cache, setting.header); // 编辑处理 async function handleEdit(changedCells) { const updateData = []; changedCells.forEach(({ row, col }) => { updateData.push({ row, ID: cache[row].ID, name: sheet.getValue(row, col) }); }); try { await ajaxPost('/priceInfo/editArea', { updateData }, TIME_OUT); updateData.forEach(({ row, name }) => cache[row].name = name); } catch (err) { // 恢复各单元格数据 sheetCommonObj.renderSheetFunc(sheet, () => { changedCells.forEach(({ row }) => { sheet.setValue(cache[row].name); }); }); } } sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) { const changedCells = [{ row: info.row, col: info.col }]; handleEdit(changedCells); }); sheet.bind(GC.Spread.Sheets.Events.RangeChanged, function (e, info) { handleEdit(info.changedCells); }); const curArea = { ID: null }; // 焦点变更处理 const debounceSelectionChanged = _.debounce(function (e, info) { const row = info.newSelections && info.newSelections[0] ? info.newSelections[0].row : 0; handleSelectionChanged(row); }, DEBOUNCE_TIME, { leading: true }); // leading = true : 先触发再延迟 function handleSelectionChanged(row) { const areaItem = cache[row]; curArea.ID = areaItem && areaItem.ID || null; CLASS_BOOK.initData(libID, curArea.ID); } sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, debounceSelectionChanged); // 新增 async function insert() { const data = { compilationID, ID: uuid.v1(), name: '', }; try { $.bootstrapLoading.start(); await ajaxPost('/priceInfo/insertArea', { insertData: [data] }); // 新增的数据总是添加在最后 sheet.addRows(cache.length, 1); cache.push(data); const lastRow = cache.length - 1; sheet.setSelection(lastRow, 0, 1, 1); sheet.showRow(lastRow, GC.Spread.Sheets.VerticalPosition.top); handleSelectionChanged(lastRow); } catch (err) { alert(err); } finally { $.bootstrapLoading.end(); } } // 删除 async function del() { try { $.bootstrapLoading.start(); await ajaxPost('/priceInfo/deleteArea', { deleteData: [curArea.ID] }); const index = cache.findIndex(item => item.ID === curArea.ID); sheet.deleteRows(index, 1); cache.splice(index, 1); const row = sheet.getActiveRowIndex(); handleSelectionChanged(row); } catch (err) { alert(err); } finally { $.bootstrapLoading.end(); } } // 右键功能 function buildContextMenu() { $.contextMenu({ selector: '#area-spread', build: function ($triggerElement, e) { // 控制允许右键菜单在哪个位置出现 const offset = $('#area-spread').offset(); const x = e.pageX - offset.left; const y = e.pageY - offset.top; const target = sheet.hitTest(x, y); if (target.hitTestType === 3) { // 在表格内 const sel = sheet.getSelections()[0]; if (sel && sel.rowCount === 1 && typeof target.row !== 'undefined') { const orgRow = sheet.getActiveRowIndex(); if (orgRow !== target.row) { sheet.setActiveCell(target.row, target.col); handleSelectionChanged(target.row); } } return { items: { insert: { name: '新增', icon: "fa-arrow-left", disabled: function () { return locked; }, callback: function (key, opt) { insert(); } }, del: { name: '删除', icon: "fa-arrow-left", disabled: function () { return locked || !cache[target.row]; }, callback: function (key, opt) { del(); } }, } }; } else { return false; } } }); } buildContextMenu(); return { handleSelectionChanged, curArea, } })(); // 分类表 const CLASS_BOOK = (() => { const setting = { header: [{ headerName: '分类', headerWidth: $('#area-spread').width(), dataCode: 'name', dataType: 'String', hAlign: 'left', vAlign: 'center' }], controller: { cols: [ { data: { field: 'name', vAlign: 1, hAlign: 0, font: 'Arial' }, } ], headRows: 1, headRowHeight: [30], emptyRows: 0, treeCol: 0 }, tree: { id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1 } }; // 初始化表格 const workBook = initSheet($('#class-spread')[0], setting); workBook.options.allowExtendPasteRange = false; workBook.options.allowUserDragDrop = true; workBook.options.allowUserDragFill = true; const sheet = workBook.getSheet(0); let tree; let controller; // 初始化数据 async function initData(libID, areaID) { if (!areaID) { tree = null; controller = null; sheet.setRowCount(0); PRICE_BOOK.clear(); return; } $.bootstrapLoading.start(); try { const data = await ajaxPost('/priceInfo/getClassData', { libID, areaID }, TIME_OUT); tree = idTree.createNew(setting.tree); tree.loadDatas(data); tree.selected = tree.items.length > 0 ? tree.items[0] : null; controller = TREE_SHEET_CONTROLLER.createNew(tree, sheet, setting.controller, false); controller.showTreeData(); handleSelectionChanged(0); sheet.setSelection(0, 0, 1, 1); lockUtil.lockSpreads([workBook], locked); } catch (err) { tree = null; controller = null; sheet.setRowCount(0); alert(err); } finally { $.bootstrapLoading.end(); } } // 编辑处理 async function handleEdit(changedCells) { const updateData = []; changedCells.forEach(({ row, col }) => { updateData.push({ row, type: UpdateType.UPDATE, filter: { ID: tree.items[row].data.ID }, update: { name: sheet.getValue(row, col) } }); }); try { await ajaxPost('/priceInfo/editClassData', { updateData }, TIME_OUT); updateData.forEach(({ row, update: { name } }) => tree.items[row].data.name = name); } catch (err) { // 恢复各单元格数据 sheetCommonObj.renderSheetFunc(sheet, () => { changedCells.forEach(({ row }) => { sheet.setValue(tree.items[row].data.name); }); }); } } sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) { const changedCells = [{ row: info.row, col: info.col }]; handleEdit(changedCells); }); sheet.bind(GC.Spread.Sheets.Events.RangeChanged, function (e, info) { handleEdit(info.changedCells); }); // 树操作相关 const $insert = $('#tree-insert'); const $remove = $('#tree-remove'); const $upLevel = $('#tree-up-level'); const $downLevel = $('#tree-down-level'); const $downMove = $('#tree-down-move'); const $upMove = $('#tree-up-move'); const $calcPriceIndex = $('#calc-price-index'); // 插入 let canInsert = true; async function insert() { try { if (!canInsert) { return false; } canInsert = false; $.bootstrapLoading.start(); const updateData = []; const selected = tree.selected; const newItem = { libID, areaID: AREA_BOOK.curArea.ID, ID: uuid.v1(), name: '', ParentID: '-1', NextSiblingID: '-1' }; if (selected) { newItem.ParentID = selected.data.ParentID; updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.data.ID }, update: { NextSiblingID: newItem.ID } }); if (selected.nextSibling) { newItem.NextSiblingID = selected.nextSibling.data.ID; } } updateData.push({ type: UpdateType.CREATE, document: newItem }); await ajaxPost('/priceInfo/editClassData', { updateData }); controller.insertByID(newItem.ID); handleSelectionChanged(sheet.getActiveRowIndex()); } catch (err) { alert(err); } finally { canInsert = true; $.bootstrapLoading.end(); } } $insert.click(_.debounce(insert, DEBOUNCE_TIME, { leading: true })); // 删除 let canRemove = true; async function remove() { try { if (!canRemove) { return false; } canRemove = false; $.bootstrapLoading.start(); const updateData = []; const selected = tree.selected; const children = selected.getPosterity(); [selected, ...children].forEach(node => updateData.push({ type: UpdateType.DELETE, filter: { ID: node.data.ID } })); if (selected.preSibling) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.preSibling.data.ID }, update: { NextSiblingID: selected.data.NextSiblingID } }); } await ajaxPost('/priceInfo/editClassData', { updateData }); controller.delete(); handleSelectionChanged(sheet.getActiveRowIndex()); } catch (err) { alert(err); } finally { canRemove = true; $.bootstrapLoading.end(); } } $remove.click(_.debounce(remove, DEBOUNCE_TIME, { leading: true })); // 升级 let canUpLevel = true; async function upLevel() { try { if (!canUpLevel) { return false; } canUpLevel = false; $.bootstrapLoading.start(); const updateData = []; const selected = tree.selected; if (selected.preSibling) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.preSibling.data.ID }, update: { NextSiblingID: -1 } }); } if (selected.parent) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.parent.data.ID }, update: { NextSiblingID: selected.data.ID } }); } updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.data.ID }, update: { ParentID: selected.parent.data.ParentID, NextSiblingID: selected.parent.data.NextSiblingID } }); let curNode = selected.nextSibling; while (curNode) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: curNode.data.ID }, update: { ParentID: selected.data.ID } }); curNode = curNode.nextSibling; } await ajaxPost('/priceInfo/editClassData', { updateData }); controller.upLevel(); refreshTreeButton(tree.selected); } catch (err) { alert(err); } finally { canUpLevel = true; $.bootstrapLoading.end(); } } $upLevel.click(_.debounce(upLevel, DEBOUNCE_TIME, { leading: true })); // 降级 let canDownLevel = true; async function downLevel() { try { if (!canDownLevel) { return false; } canDownLevel = false; $.bootstrapLoading.start(); const updateData = []; const selected = tree.selected; if (selected.preSibling) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.preSibling.data.ID }, update: { NextSiblingID: selected.data.NextSiblingID } }); const preSiblingLastChild = selected.preSibling.children[selected.preSibling.children.length - 1]; if (preSiblingLastChild) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: preSiblingLastChild.data.ID }, update: { NextSiblingID: selected.data.ID } }); } updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.data.ID }, update: { ParentID: selected.preSibling.data.ID, NextSiblingID: -1 } }); } await ajaxPost('/priceInfo/editClassData', { updateData }); controller.downLevel(); refreshTreeButton(tree.selected); } catch (err) { alert(err); } finally { canDownLevel = true; $.bootstrapLoading.end(); } } $downLevel.click(_.debounce(downLevel, DEBOUNCE_TIME, { leading: true })); // 下移 let canDownMove = true; async function downMove() { try { if (!canDownMove) { return false; } canDownMove = false; $.bootstrapLoading.start(); const updateData = []; const selected = tree.selected; if (selected.preSibling) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.preSibling.data.ID }, update: { NextSiblingID: selected.data.NextSiblingID } }); } updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.data.ID }, update: { NextSiblingID: selected.nextSibling.data.NextSiblingID } }); updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.nextSibling.data.ID }, update: { NextSiblingID: selected.data.ID } }); await ajaxPost('/priceInfo/editClassData', { updateData }); controller.downMove(); refreshTreeButton(tree.selected); } catch (err) { alert(err); } finally { canDownMove = true; $.bootstrapLoading.end(); } } $downMove.click(_.debounce(downMove, DEBOUNCE_TIME, { leading: true })); // 上移 let canUpMove = true; async function upMove() { try { if (!canUpMove) { return false; } canUpMove = false; $.bootstrapLoading.start(); const updateData = []; const selected = tree.selected; if (selected.preSibling) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.preSibling.data.ID }, update: { NextSiblingID: selected.data.NextSiblingID } }); } const prePreSibling = selected.preSibling.preSibling; if (prePreSibling) { updateData.push({ type: UpdateType.UPDATE, filter: { ID: prePreSibling.data.ID }, update: { NextSiblingID: selected.data.ID } }); } updateData.push({ type: UpdateType.UPDATE, filter: { ID: selected.data.ID }, update: { NextSiblingID: selected.preSibling.data.ID } }); await ajaxPost('/priceInfo/editClassData', { updateData }); controller.upMove(); refreshTreeButton(tree.selected); } catch (err) { alert(err); } finally { canUpMove = true; $.bootstrapLoading.end(); } } $upMove.click(_.debounce(upMove, DEBOUNCE_TIME, { leading: true })); // 刷新树操作按钮有效性 function refreshTreeButton(selected) { if (locked) { return; } $insert.removeClass('disabled'); $remove.removeClass('disabled'); $upLevel.removeClass('disabled'); $downLevel.removeClass('disabled'); $downMove.removeClass('disabled'); $upMove.removeClass('disabled'); if (!selected) { $remove.addClass('disabled'); $upLevel.addClass('disabled'); $downLevel.addClass('disabled'); $downMove.addClass('disabled'); $upMove.addClass('disabled'); } else { if (!selected.preSibling) { $downLevel.addClass('disabled'); $upMove.addClass('disabled'); } if (!selected.nextSibling) { $downMove.addClass('disabled'); } if (!selected.parent) { $upLevel.addClass('disabled'); } } } // 焦点变更处理 const curClass = { ID: null }; function handleSelectionChanged(row) { const classNode = tree.items[row] || null; tree.selected = classNode; refreshTreeButton(classNode); curClass.ID = classNode && classNode.data && classNode.data.ID || null; const classIDList = [] if (classNode) { classIDList.push(classNode.data.ID); const children = classNode.getPosterity(); children.forEach(child => classIDList.push(child.data.ID)); } PRICE_BOOK.initData(classIDList); } const debounceSelectionChanged = _.debounce(function (e, info) { const row = info.newSelections && info.newSelections[0] ? info.newSelections[0].row : 0; handleSelectionChanged(row); }, DEBOUNCE_TIME, { leading: true }); sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, debounceSelectionChanged); $calcPriceIndex.click(_.debounce(async()=>{ $.bootstrapLoading.start(); try { const data = await ajaxPost('/priceInfo/calcPriceIndex', { libID, period:curLibPeriod,compilationID }, TIME_OUT); //alert(data); if(data){ const htmlStr = data.replace(/\n/gm,'
'); //replaceAll('\n','
',data); $("#result-info-body").html(htmlStr); $("#result-info").modal('show'); }else{ alert('计算完成!') } } catch (error) { console.log(error); } $.bootstrapLoading.end(); }, DEBOUNCE_TIME, { leading: true })); return { initData, handleSelectionChanged, curClass, } })(); // 关键字表 const KEYWORD_BOOK = (() => { const setting = { header: [ { headerName: '关键字', headerWidth: 200, dataCode: 'keyword', dataType: 'String', hAlign: 'left', vAlign: 'center' }, { headerName: '单位', headerWidth: 70, dataCode: 'unit', dataType: 'String', hAlign: 'center', vAlign: 'center' }, { headerName: '关键字效果', headerWidth: 100, dataCode: 'coe', dataType: 'String', hAlign: 'center', vAlign: 'center' }, { headerName: '组别', headerWidth: 50, dataCode: 'group', dataType: 'String', hAlign: 'center', vAlign: 'center' }, { headerName: '选项号', headerWidth: 70, dataCode: 'optionCode', dataType: 'String', hAlign: 'center', vAlign: 'center' }, ], }; // 初始化表格 const workBook = initSheet($('#keyword-spread')[0], setting); workBook.options.allowUserDragDrop = false; workBook.options.allowUserDragFill = false; lockUtil.lockSpreads([workBook], true); const sheet = workBook.getSheet(0); // 显示关键字数据 const showKeywordData = (keywordList) => { showData(sheet, keywordList, setting.header); } return { showKeywordData } })(); // 价格信息表 const PRICE_BOOK = (() => { const setting = { header: [ { headerName: '编码', headerWidth: 100, dataCode: 'code', dataType: 'String', hAlign: 'left', vAlign: 'center' ,formatter: "@"}, { headerName: '别名编码', headerWidth: 70, dataCode: 'classCode', dataType: 'String', hAlign: 'left', vAlign: 'center' ,formatter: "@"}, { headerName: '名称', headerWidth: 200, dataCode: 'name', dataType: 'String', hAlign: 'left', vAlign: 'center' }, { headerName: '规格型号', headerWidth: 120, dataCode: 'specs', dataType: 'String', hAlign: 'left', vAlign: 'center' }, { headerName: '单位', headerWidth: 80, dataCode: 'unit', dataType: 'String', hAlign: 'center', vAlign: 'center' }, { headerName: '不含税价', headerWidth: 80, dataCode: 'noTaxPrice', dataType: 'String', hAlign: 'right', vAlign: 'center' }, { headerName: '含税价', headerWidth: 80, dataCode: 'taxPrice', dataType: 'String', hAlign: 'right', vAlign: 'center' }, { headerName: '月份备注', headerWidth: 140, dataCode: 'dateRemark', dataType: 'String', hAlign: 'left', vAlign: 'center' }, { headerName: '计算式', headerWidth: 100, dataCode: 'expString', dataType: 'String', hAlign: 'left', vAlign: 'center' }, ], }; // 初始化表格 const workBook = initSheet($('#price-spread')[0], setting); workBook.options.allowUserDragDrop = true; workBook.options.allowUserDragFill = true; lockUtil.lockSpreads([workBook], locked); const sheet = workBook.getSheet(0); let cache = []; // 清空 function clear() { cache = []; sheet.setRowCount(0); } // 初始化数据 async function initData(classIDList) { if (!classIDList || !classIDList.length) { return clear(); } $.bootstrapLoading.start(); try { cache = await ajaxPost('/priceInfo/getPriceData', { classIDList }, TIME_OUT); cache = _.sortBy(cache,'classCode'); showData(sheet, cache, setting.header, 5); const row = sheet.getActiveRowIndex(); const keywordList = cache[row] && cache[row].keywordList || []; KEYWORD_BOOK.showKeywordData(keywordList); } catch (err) { cache = []; sheet.setRowCount(0); alert(err); } finally { $.bootstrapLoading.end(); } } // 获取当前表中行数据 function getRowData(sheet, row, headers) { const item = {}; headers.forEach(({ dataCode }, index) => { const value = sheet.getValue(row, index) || ''; if (value) { item[dataCode] = value; } }); return item; } // 获取表数据和缓存数据的不同数据 function getRowDiffData(curRowData, cacheRowData, headers) { let item = null; headers.forEach(({ dataCode }) => { const curValue = curRowData[dataCode]; const cacheValue = cacheRowData[dataCode]; if (!cacheValue && !curValue) { return; } if (cacheValue !== curValue) { if (!item) { item = {}; } item[dataCode] = curValue || ''; } }); return item; } // 编辑处理 async function handleEdit(changedCells) { const postData = []; // 请求用 // 更新缓存用 const updateData = []; const deleteData = []; const insertData = []; try { changedCells.forEach(({ row }) => { if (cache[row]) { const rowData = getRowData(sheet, row, setting.header); if (Object.keys(rowData).length) { // 还有数据,更新 const diffData = getRowDiffData(rowData, cache[row], setting.header); if (diffData) { postData.push({ type: UpdateType.UPDATE, ID: cache[row].ID, data: diffData }); updateData.push({ row, data: diffData }); } } else { // 该行无数据了,删除 postData.push({ type: UpdateType.DELETE, ID: cache[row].ID }); deleteData.push(cache[row]); } } else { // 新增 const rowData = getRowData(sheet, row, setting.header); if (Object.keys(rowData).length) { rowData.ID = uuid.v1(); rowData.libID = libID; rowData.compilationID = compilationID; rowData.areaID = AREA_BOOK.curArea.ID; rowData.classID = CLASS_BOOK.curClass.ID; rowData.period = curLibPeriod; postData.push({ type: UpdateType.CREATE, data: rowData }); insertData.push(rowData); } } }); if (postData.length) { await ajaxPost('/priceInfo/editPriceData', { postData }, TIME_OUT); // 更新缓存,先更新然后删除,最后再新增,防止先新增后缓存数据的下标与更新、删除数据的下标对应不上 updateData.forEach(item => { Object.assign(cache[item.row], item.data); }); deleteData.forEach(item => { const index = cache.indexOf(item); if (index >= 0) { cache.splice(index, 1); } }); insertData.forEach(item => cache.push(item)); if (deleteData.length || insertData.length) { showData(sheet, cache, setting.header, 5); } } } catch (err) { // 恢复各单元格数据 showData(sheet, cache, setting.header, 5); } } sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) { const changedCells = [{ row: info.row }]; handleEdit(changedCells); }); sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, function (e, info) { const row = info.newSelections && info.newSelections[0] ? info.newSelections[0].row : 0; // 显示关键字数据 const keywordList = cache[row] && cache[row].keywordList || []; KEYWORD_BOOK.showKeywordData(keywordList); }); sheet.bind(GC.Spread.Sheets.Events.RangeChanged, function (e, info) { const changedRows = []; let preRow; info.changedCells.forEach(({ row }) => { if (row !== preRow) { changedRows.push({ row }); } preRow = row; }); handleEdit(changedRows); }); return { clear, initData, } })(); $(document).ready(() => { console.log('进入信息价'); $('[data-toggle="tooltip"]').tooltip(); AREA_BOOK.handleSelectionChanged(0); const $range = $(document.body); lockUtil.lockTools($range, locked); });