priceClass.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. // 分类表
  2. function setTimeoutSync(handle, time) {
  3. return new Promise(function (resolve, reject) {
  4. setTimeout(function () {
  5. if (handle && typeof handle === 'function') {
  6. handle();
  7. }
  8. resolve();
  9. }, time);
  10. });
  11. }
  12. // 分类表
  13. const CLASS_BOOK = (() => {
  14. const setting = {
  15. header: [{ headerName: '分类', headerWidth: $('#area-spread').width(), dataCode: 'name', dataType: 'String', hAlign: 'left', vAlign: 'center' }],
  16. controller: {
  17. cols: [
  18. {
  19. data: {
  20. field: 'name',
  21. vAlign: 1,
  22. hAlign: 0,
  23. font: 'Arial'
  24. },
  25. }
  26. ],
  27. headRows: 1,
  28. headRowHeight: [30],
  29. emptyRows: 0,
  30. treeCol: 0
  31. },
  32. tree: {
  33. id: 'ID',
  34. pid: 'ParentID',
  35. nid: 'NextSiblingID',
  36. rootId: -1
  37. }
  38. };
  39. // 初始化表格
  40. const workBook = initSheet($('#class-spread')[0], setting);
  41. workBook.options.allowExtendPasteRange = false;
  42. workBook.options.allowUserDragDrop = true;
  43. workBook.options.allowUserDragFill = true;
  44. const sheet = workBook.getSheet(0);
  45. let tree;
  46. let controller;
  47. // 初始化数据
  48. async function initData(libID, areaID) {
  49. if (!areaID) {
  50. tree = null;
  51. controller = null;
  52. sheet.setRowCount(0);
  53. PRICE_BOOK.clear();
  54. return;
  55. }
  56. $.bootstrapLoading.start();
  57. try {
  58. const data = await ajaxPost('/priceInfo/getClassData', { libID, areaID }, TIME_OUT);
  59. tree = idTree.createNew(setting.tree);
  60. tree.loadDatas(data);
  61. tree.selected = tree.items.length > 0 ? tree.items[0] : null;
  62. controller = TREE_SHEET_CONTROLLER.createNew(tree, sheet, setting.controller, false);
  63. controller.showTreeData();
  64. handleSelectionChanged(0);
  65. sheet.setSelection(0, 0, 1, 1);
  66. lockUtil.lockSpreads([workBook], locked);
  67. } catch (err) {
  68. tree = null;
  69. controller = null;
  70. sheet.setRowCount(0);
  71. alert(err);
  72. } finally {
  73. $.bootstrapLoading.end();
  74. }
  75. }
  76. // 编辑处理
  77. async function handleEdit(changedCells) {
  78. const updateData = [];
  79. changedCells.forEach(({ row, col }) => {
  80. updateData.push({
  81. row,
  82. type: UpdateType.UPDATE,
  83. filter: { ID: tree.items[row].data.ID },
  84. update: { name: sheet.getValue(row, col) }
  85. });
  86. });
  87. try {
  88. await ajaxPost('/priceInfo/editClassData', { updateData }, TIME_OUT);
  89. updateData.forEach(({ row, update: { name } }) => tree.items[row].data.name = name);
  90. } catch (err) {
  91. // 恢复各单元格数据
  92. sheetCommonObj.renderSheetFunc(sheet, () => {
  93. changedCells.forEach(({ row }) => {
  94. sheet.setValue(tree.items[row].data.name);
  95. });
  96. });
  97. }
  98. }
  99. sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) {
  100. const changedCells = [{ row: info.row, col: info.col }];
  101. handleEdit(changedCells);
  102. });
  103. sheet.bind(GC.Spread.Sheets.Events.RangeChanged, function (e, info) {
  104. handleEdit(info.changedCells);
  105. });
  106. // 树操作相关
  107. const $insert = $('#tree-insert');
  108. const $remove = $('#tree-remove');
  109. const $upLevel = $('#tree-up-level');
  110. const $downLevel = $('#tree-down-level');
  111. const $downMove = $('#tree-down-move');
  112. const $upMove = $('#tree-up-move');
  113. const $calcPriceIndex = $('#calc-price-index');
  114. const $matchSummary = $('#match-summary');
  115. const $batchMatchSummary = $('#batch-match-summary');
  116. const $showEmpty = $('#show-empty');
  117. // 插入
  118. let canInsert = true;
  119. async function insert() {
  120. try {
  121. if (!canInsert) {
  122. return false;
  123. }
  124. canInsert = false;
  125. $.bootstrapLoading.start();
  126. const updateData = [];
  127. const selected = tree.selected;
  128. const newItem = {
  129. libID,
  130. areaID: AREA_BOOK.curArea.ID,
  131. ID: uuid.v1(),
  132. name: '',
  133. ParentID: '-1',
  134. NextSiblingID: '-1'
  135. };
  136. if (selected) {
  137. newItem.ParentID = selected.data.ParentID;
  138. updateData.push({
  139. type: UpdateType.UPDATE,
  140. filter: { ID: selected.data.ID },
  141. update: { NextSiblingID: newItem.ID }
  142. });
  143. if (selected.nextSibling) {
  144. newItem.NextSiblingID = selected.nextSibling.data.ID;
  145. }
  146. }
  147. updateData.push({
  148. type: UpdateType.CREATE,
  149. document: newItem
  150. });
  151. await ajaxPost('/priceInfo/editClassData', { updateData });
  152. controller.insertByID(newItem.ID);
  153. handleSelectionChanged(sheet.getActiveRowIndex());
  154. } catch (err) {
  155. alert(err);
  156. } finally {
  157. canInsert = true;
  158. $.bootstrapLoading.end();
  159. }
  160. }
  161. $insert.click(_.debounce(insert, DEBOUNCE_TIME, { leading: true }));
  162. // 删除
  163. let canRemove = true;
  164. async function remove() {
  165. try {
  166. if (!canRemove) {
  167. return false;
  168. }
  169. canRemove = false;
  170. $.bootstrapLoading.start();
  171. const updateData = [];
  172. const selected = tree.selected;
  173. const children = selected.getPosterity();
  174. [selected, ...children].forEach(node => updateData.push({
  175. type: UpdateType.DELETE,
  176. filter: { ID: node.data.ID }
  177. }));
  178. if (selected.preSibling) {
  179. updateData.push({
  180. type: UpdateType.UPDATE,
  181. filter: { ID: selected.preSibling.data.ID },
  182. update: { NextSiblingID: selected.data.NextSiblingID }
  183. });
  184. }
  185. await ajaxPost('/priceInfo/editClassData', { updateData });
  186. controller.delete();
  187. handleSelectionChanged(sheet.getActiveRowIndex());
  188. } catch (err) {
  189. alert(err);
  190. } finally {
  191. canRemove = true;
  192. $.bootstrapLoading.end();
  193. }
  194. }
  195. $remove.click(_.debounce(remove, DEBOUNCE_TIME, { leading: true }));
  196. // 升级
  197. let canUpLevel = true;
  198. async function upLevel() {
  199. try {
  200. if (!canUpLevel) {
  201. return false;
  202. }
  203. canUpLevel = false;
  204. $.bootstrapLoading.start();
  205. const updateData = [];
  206. const selected = tree.selected;
  207. if (selected.preSibling) {
  208. updateData.push({
  209. type: UpdateType.UPDATE,
  210. filter: { ID: selected.preSibling.data.ID },
  211. update: { NextSiblingID: -1 }
  212. });
  213. }
  214. if (selected.parent) {
  215. updateData.push({
  216. type: UpdateType.UPDATE,
  217. filter: { ID: selected.parent.data.ID },
  218. update: { NextSiblingID: selected.data.ID }
  219. });
  220. }
  221. updateData.push({
  222. type: UpdateType.UPDATE,
  223. filter: { ID: selected.data.ID },
  224. update: { ParentID: selected.parent.data.ParentID, NextSiblingID: selected.parent.data.NextSiblingID }
  225. });
  226. let curNode = selected.nextSibling;
  227. while (curNode) {
  228. updateData.push({
  229. type: UpdateType.UPDATE,
  230. filter: { ID: curNode.data.ID },
  231. update: { ParentID: selected.data.ID }
  232. });
  233. curNode = curNode.nextSibling;
  234. }
  235. await ajaxPost('/priceInfo/editClassData', { updateData });
  236. controller.upLevel();
  237. refreshTreeButton(tree.selected);
  238. } catch (err) {
  239. alert(err);
  240. } finally {
  241. canUpLevel = true;
  242. $.bootstrapLoading.end();
  243. }
  244. }
  245. $upLevel.click(_.debounce(upLevel, DEBOUNCE_TIME, { leading: true }));
  246. // 降级
  247. let canDownLevel = true;
  248. async function downLevel() {
  249. try {
  250. if (!canDownLevel) {
  251. return false;
  252. }
  253. canDownLevel = false;
  254. $.bootstrapLoading.start();
  255. const updateData = [];
  256. const selected = tree.selected;
  257. if (selected.preSibling) {
  258. updateData.push({
  259. type: UpdateType.UPDATE,
  260. filter: { ID: selected.preSibling.data.ID },
  261. update: { NextSiblingID: selected.data.NextSiblingID }
  262. });
  263. const preSiblingLastChild = selected.preSibling.children[selected.preSibling.children.length - 1];
  264. if (preSiblingLastChild) {
  265. updateData.push({
  266. type: UpdateType.UPDATE,
  267. filter: { ID: preSiblingLastChild.data.ID },
  268. update: { NextSiblingID: selected.data.ID }
  269. });
  270. }
  271. updateData.push({
  272. type: UpdateType.UPDATE,
  273. filter: { ID: selected.data.ID },
  274. update: { ParentID: selected.preSibling.data.ID, NextSiblingID: -1 }
  275. });
  276. }
  277. await ajaxPost('/priceInfo/editClassData', { updateData });
  278. controller.downLevel();
  279. refreshTreeButton(tree.selected);
  280. } catch (err) {
  281. alert(err);
  282. } finally {
  283. canDownLevel = true;
  284. $.bootstrapLoading.end();
  285. }
  286. }
  287. $downLevel.click(_.debounce(downLevel, DEBOUNCE_TIME, { leading: true }));
  288. // 下移
  289. let canDownMove = true;
  290. async function downMove() {
  291. try {
  292. if (!canDownMove) {
  293. return false;
  294. }
  295. canDownMove = false;
  296. $.bootstrapLoading.start();
  297. const updateData = [];
  298. const selected = tree.selected;
  299. if (selected.preSibling) {
  300. updateData.push({
  301. type: UpdateType.UPDATE,
  302. filter: { ID: selected.preSibling.data.ID },
  303. update: { NextSiblingID: selected.data.NextSiblingID }
  304. });
  305. }
  306. updateData.push({
  307. type: UpdateType.UPDATE,
  308. filter: { ID: selected.data.ID },
  309. update: { NextSiblingID: selected.nextSibling.data.NextSiblingID }
  310. });
  311. updateData.push({
  312. type: UpdateType.UPDATE,
  313. filter: { ID: selected.nextSibling.data.ID },
  314. update: { NextSiblingID: selected.data.ID }
  315. });
  316. await ajaxPost('/priceInfo/editClassData', { updateData });
  317. controller.downMove();
  318. refreshTreeButton(tree.selected);
  319. } catch (err) {
  320. alert(err);
  321. } finally {
  322. canDownMove = true;
  323. $.bootstrapLoading.end();
  324. }
  325. }
  326. $downMove.click(_.debounce(downMove, DEBOUNCE_TIME, { leading: true }));
  327. // 上移
  328. let canUpMove = true;
  329. async function upMove() {
  330. try {
  331. if (!canUpMove) {
  332. return false;
  333. }
  334. canUpMove = false;
  335. $.bootstrapLoading.start();
  336. const updateData = [];
  337. const selected = tree.selected;
  338. if (selected.preSibling) {
  339. updateData.push({
  340. type: UpdateType.UPDATE,
  341. filter: { ID: selected.preSibling.data.ID },
  342. update: { NextSiblingID: selected.data.NextSiblingID }
  343. });
  344. }
  345. const prePreSibling = selected.preSibling.preSibling;
  346. if (prePreSibling) {
  347. updateData.push({
  348. type: UpdateType.UPDATE,
  349. filter: { ID: prePreSibling.data.ID },
  350. update: { NextSiblingID: selected.data.ID }
  351. });
  352. }
  353. updateData.push({
  354. type: UpdateType.UPDATE,
  355. filter: { ID: selected.data.ID },
  356. update: { NextSiblingID: selected.preSibling.data.ID }
  357. });
  358. await ajaxPost('/priceInfo/editClassData', { updateData });
  359. controller.upMove();
  360. refreshTreeButton(tree.selected);
  361. } catch (err) {
  362. alert(err);
  363. } finally {
  364. canUpMove = true;
  365. $.bootstrapLoading.end();
  366. }
  367. }
  368. $upMove.click(_.debounce(upMove, DEBOUNCE_TIME, { leading: true }));
  369. // 刷新树操作按钮有效性
  370. function refreshTreeButton(selected) {
  371. if (locked) {
  372. return;
  373. }
  374. $insert.removeClass('disabled');
  375. $remove.removeClass('disabled');
  376. $upLevel.removeClass('disabled');
  377. $downLevel.removeClass('disabled');
  378. $downMove.removeClass('disabled');
  379. $upMove.removeClass('disabled');
  380. if (!selected) {
  381. $remove.addClass('disabled');
  382. $upLevel.addClass('disabled');
  383. $downLevel.addClass('disabled');
  384. $downMove.addClass('disabled');
  385. $upMove.addClass('disabled');
  386. } else {
  387. if (!selected.preSibling) {
  388. $downLevel.addClass('disabled');
  389. $upMove.addClass('disabled');
  390. }
  391. if (!selected.nextSibling) {
  392. $downMove.addClass('disabled');
  393. }
  394. if (!selected.parent) {
  395. $upLevel.addClass('disabled');
  396. }
  397. }
  398. }
  399. // 焦点变更处理
  400. const curClass = { ID: null, IDList: [] };
  401. function handleSelectionChanged(row) {
  402. curRow = row;
  403. const classNode = tree.items[row] || null;
  404. tree.selected = classNode;
  405. refreshTreeButton(classNode);
  406. curClass.ID = classNode && classNode.data && classNode.data.ID || null;
  407. const classIDList = []
  408. if (classNode) {
  409. classIDList.push(classNode.data.ID);
  410. const children = classNode.getPosterity();
  411. children.forEach(child => classIDList.push(child.data.ID));
  412. }
  413. curClass.classIDList = [];
  414. PRICE_BOOK.initData(classIDList);
  415. }
  416. const debounceSelectionChanged = _.debounce(function (e, info) {
  417. const row = info.newSelections && info.newSelections[0] ? info.newSelections[0].row : 0;
  418. handleSelectionChanged(row);
  419. }, DEBOUNCE_TIME, { leading: true });
  420. sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, debounceSelectionChanged);
  421. const reload = () => {
  422. if (curClass.ID) {
  423. const node = tree.nodes[tree.prefix + curClass.ID];
  424. if (node) {
  425. handleSelectionChanged(node.serialNo())
  426. }
  427. }
  428. }
  429. $calcPriceIndex.click(_.debounce(async () => {
  430. $.bootstrapLoading.start();
  431. try {
  432. const data = await ajaxPost('/priceInfo/calcPriceIndex', { libID, period: curLibPeriod, compilationID }, TIME_OUT);
  433. //alert(data);
  434. if (data) {
  435. const htmlStr = data.replace(/\n/gm, '<br>'); //replaceAll('\n','<br>',data);
  436. $("#result-info-body").html(htmlStr);
  437. $("#result-info").modal('show');
  438. } else {
  439. alert('计算完成!')
  440. }
  441. } catch (error) {
  442. console.log(error);
  443. }
  444. $.bootstrapLoading.end();
  445. }, DEBOUNCE_TIME, { leading: true }));
  446. const doMatchSummary = async (libID, compilationID, areaID) => {
  447. await ajaxPost('/priceInfo/matchSummary', { libID, compilationID, areaID }, 1000 * 60 * 10);
  448. }
  449. // 匹配总表
  450. $matchSummary.click(_.debounce(async () => {
  451. $.bootstrapLoading.progressStart('匹配总表', true);
  452. $("#progress_modal_body").text('正在匹配总表,请稍后...');
  453. try {
  454. const matchAll = $('#match-all-input')[0].checked;
  455. const areaID = matchAll ? '' : AREA_BOOK.curArea?.ID;
  456. await doMatchSummary(libID, compilationID, areaID);
  457. setTimeout(() => {
  458. $.bootstrapLoading.progressEnd();
  459. window.location.reload()
  460. }, 1000)
  461. } catch (error) {
  462. alert(error)
  463. console.log(error);
  464. $.bootstrapLoading.progressEnd();
  465. }
  466. }, DEBOUNCE_TIME, { leading: true }));
  467. // 批量匹配总表
  468. $batchMatchSummary.click(_.debounce(async () => {
  469. $.bootstrapLoading.progressStart('批量匹配总表', true);
  470. try {
  471. const libs = await ajaxPost('/priceInfo/getAllLibs');
  472. for (const lib of libs) {
  473. $("#progress_modal_body").text(`${lib.name},正在匹配总表,请稍后(${libs.indexOf(lib) + 1}/${libs.length})...`);
  474. await doMatchSummary(lib.ID, lib.compilationID, '');
  475. await setTimeoutSync(() => { }, 100);
  476. }
  477. await setTimeoutSync(() => {
  478. $.bootstrapLoading.progressEnd();
  479. window.location.reload()
  480. }, 1000);
  481. } catch (error) {
  482. alert(error)
  483. console.log(error);
  484. $.bootstrapLoading.progressEnd();
  485. }
  486. }, DEBOUNCE_TIME, { leading: true }));
  487. // 显示空数据
  488. $showEmpty.click(() => {
  489. $('#empty-area').modal('show');
  490. });
  491. return {
  492. initData,
  493. handleSelectionChanged,
  494. curClass,
  495. reload,
  496. }
  497. })();