// 分类表
function setTimeoutSync(handle, time) {
return new Promise(function (resolve, reject) {
setTimeout(function () {
if (handle && typeof handle === 'function') {
handle();
}
resolve();
}, time);
});
}
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');
const $matchSummary = $('#match-summary');
const $batchMatchSummary = $('#batch-match-summary');
const $showEmpty = $('#show-empty');
// 插入
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) {
curRow = 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);
const reload = () => {
if (curClass.ID) {
const node = tree.nodes[tree.prefix + curClass.ID];
if (node) {
handleSelectionChanged(node.serialNo())
}
}
}
$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 }));
const doMatchSummary = async (libID, compilationID, areaID) => {
await ajaxPost('/priceInfo/matchSummary', { libID, compilationID, areaID }, 1000 * 60 * 10);
}
// 匹配总表
$matchSummary.click(_.debounce(async () => {
$.bootstrapLoading.progressStart('匹配总表', true);
$("#progress_modal_body").text('正在匹配总表,请稍后...');
try {
const matchAll = $('#match-all-input')[0].checked;
const areaID = matchAll ? '' : AREA_BOOK.curArea?.ID;
await doMatchSummary(libID, compilationID, areaID);
setTimeout(() => {
$.bootstrapLoading.progressEnd();
window.location.reload()
}, 1000)
} catch (error) {
alert(error)
console.log(error);
$.bootstrapLoading.progressEnd();
}
}, DEBOUNCE_TIME, { leading: true }));
// 批量匹配总表
$batchMatchSummary.click(_.debounce(async () => {
$.bootstrapLoading.progressStart('批量匹配总表', true);
try {
const libs = await ajaxPost('/priceInfo/getAllLibs');
for (const lib of libs) {
$("#progress_modal_body").text(`${lib.name},正在匹配总表,请稍后(${libs.indexOf(lib) + 1}/${libs.length})...`);
await doMatchSummary(lib.ID, lib.compilationID, '');
await setTimeoutSync(() => { }, 100);
}
await setTimeoutSync(() => {
$.bootstrapLoading.progressEnd();
window.location.reload()
}, 1000);
} catch (error) {
alert(error)
console.log(error);
$.bootstrapLoading.progressEnd();
}
}, DEBOUNCE_TIME, { leading: true }));
// 显示空数据
$showEmpty.click(() => {
$('#empty-area').modal('show');
});
return {
initData,
handleSelectionChanged,
curClass,
reload,
}
})();