priceClass.js 15 KB

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