index.js 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. function setAlign(sheet, headers) {
  2. const fuc = () => {
  3. headers.forEach(({ hAlign, vAlign }, index) => {
  4. sheetCommonObj.setAreaAlign(sheet.getRange(-1, index, -1, 1), hAlign, vAlign)
  5. });
  6. };
  7. sheetCommonObj.renderSheetFunc(sheet, fuc);
  8. }
  9. function setFormatter(sheet, headers) {
  10. const fuc = () => {
  11. headers.forEach(({ formatter }, index) => {
  12. if (formatter) {
  13. sheet.setFormatter(-1, index, formatter);
  14. }
  15. });
  16. };
  17. sheetCommonObj.renderSheetFunc(sheet, fuc);
  18. }
  19. function initSheet(dom, setting) {
  20. const workBook = sheetCommonObj.buildSheet(dom, setting);
  21. const sheet = workBook.getSheet(0);
  22. setAlign(sheet, setting.header);
  23. setFormatter(sheet, setting.header);
  24. return workBook;
  25. }
  26. function showData(sheet, data, headers, emptyRows) {
  27. const fuc = () => {
  28. sheet.setRowCount(data.length);
  29. data.forEach((item, row) => {
  30. headers.forEach(({ dataCode }, col) => {
  31. sheet.setValue(row, col, item[dataCode] || '');
  32. });
  33. });
  34. if (emptyRows) {
  35. sheet.addRows(data.length, emptyRows);
  36. }
  37. };
  38. sheetCommonObj.renderSheetFunc(sheet, fuc);
  39. }
  40. const TIME_OUT = 10000;
  41. const libID = window.location.search.match(/libID=([^&]+)/)[1];
  42. const UpdateType = {
  43. UPDATE: 'update',
  44. DELETE: 'delete',
  45. CREATE: 'create',
  46. };
  47. const DEBOUNCE_TIME = 200;
  48. const locked = lockUtil.getLocked();
  49. // 地区表
  50. const AREA_BOOK = (() => {
  51. const cache = areaList;
  52. const setting = {
  53. header: [
  54. { headerName: '序号', headerWidth: 60, dataCode: 'serialNo', dataType: 'Number', hAlign: 'center', vAlign: 'center' },
  55. { headerName: '地区', headerWidth: $('#area-spread').width() - 80, dataCode: 'name', dataType: 'String', hAlign: 'center', vAlign: 'center' },
  56. ]
  57. };
  58. // 初始化表格
  59. const workBook = initSheet($('#area-spread')[0], setting);
  60. lockUtil.lockSpreads([workBook], locked);
  61. workBook.options.allowExtendPasteRange = false;
  62. workBook.options.allowUserDragDrop = true;
  63. workBook.options.allowUserDragFill = true;
  64. const sheet = workBook.getSheet(0);
  65. // 排序显示
  66. cache.sort((a, b) => a.serialNo - b.serialNo);
  67. // 显示数据
  68. showData(sheet, cache, setting.header);
  69. // 编辑处理
  70. async function handleEdit(changedCells) {
  71. const updateData = [];
  72. let reSort = false;
  73. changedCells.forEach(({ row, col }) => {
  74. const field = setting.header[col].dataCode;
  75. let value = sheet.getValue(row, col);
  76. if (field === 'serialNo') {
  77. reSort = true;
  78. value = +value;
  79. }
  80. updateData.push({
  81. row,
  82. field,
  83. value,
  84. ID: cache[row].ID,
  85. });
  86. });
  87. try {
  88. await ajaxPost('/priceInfo/editArea', { updateData }, TIME_OUT);
  89. updateData.forEach(({ row, field, value }) => cache[row][field] = value);
  90. if (reSort) {
  91. cache.sort((a, b) => a.serialNo - b.serialNo);
  92. showData(sheet, cache, setting.header);
  93. }
  94. } catch (err) {
  95. // 恢复各单元格数据
  96. sheetCommonObj.renderSheetFunc(sheet, () => {
  97. changedCells.forEach(({ row, col, field }) => {
  98. sheet.setValue(row, col, cache[row][field]);
  99. });
  100. });
  101. }
  102. }
  103. sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) {
  104. const changedCells = [{ row: info.row, col: info.col }];
  105. handleEdit(changedCells);
  106. });
  107. sheet.bind(GC.Spread.Sheets.Events.RangeChanged, function (e, info) {
  108. handleEdit(info.changedCells);
  109. });
  110. const curArea = { ID: null };
  111. // 焦点变更处理
  112. const debounceSelectionChanged = _.debounce(function (e, info) {
  113. const row = info.newSelections && info.newSelections[0] ? info.newSelections[0].row : 0;
  114. handleSelectionChanged(row);
  115. }, DEBOUNCE_TIME, { leading: true }); // leading = true : 先触发再延迟
  116. function handleSelectionChanged(row) {
  117. const areaItem = cache[row];
  118. curArea.ID = areaItem && areaItem.ID || null;
  119. CLASS_BOOK.initData(libID, curArea.ID);
  120. }
  121. sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, debounceSelectionChanged);
  122. // 新增
  123. async function insert() {
  124. const data = {
  125. compilationID,
  126. ID: uuid.v1(),
  127. name: '',
  128. };
  129. try {
  130. $.bootstrapLoading.start();
  131. await ajaxPost('/priceInfo/insertArea', { insertData: [data] });
  132. // 新增的数据总是添加在最后
  133. sheet.addRows(cache.length, 1);
  134. cache.push(data);
  135. const lastRow = cache.length - 1;
  136. sheet.setSelection(lastRow, 0, 1, 1);
  137. sheet.showRow(lastRow, GC.Spread.Sheets.VerticalPosition.top);
  138. handleSelectionChanged(lastRow);
  139. } catch (err) {
  140. alert(err);
  141. } finally {
  142. $.bootstrapLoading.end();
  143. }
  144. }
  145. // 删除
  146. async function del() {
  147. try {
  148. $.bootstrapLoading.start();
  149. await ajaxPost('/priceInfo/deleteArea', { deleteData: [curArea.ID] });
  150. const index = cache.findIndex(item => item.ID === curArea.ID);
  151. sheet.deleteRows(index, 1);
  152. cache.splice(index, 1);
  153. const row = sheet.getActiveRowIndex();
  154. handleSelectionChanged(row);
  155. } catch (err) {
  156. alert(err);
  157. } finally {
  158. $.bootstrapLoading.end();
  159. }
  160. }
  161. // 右键功能
  162. function buildContextMenu() {
  163. $.contextMenu({
  164. selector: '#area-spread',
  165. build: function ($triggerElement, e) {
  166. // 控制允许右键菜单在哪个位置出现
  167. const offset = $('#area-spread').offset();
  168. const x = e.pageX - offset.left;
  169. const y = e.pageY - offset.top;
  170. const target = sheet.hitTest(x, y);
  171. if (target.hitTestType === 3) { // 在表格内
  172. const sel = sheet.getSelections()[0];
  173. if (sel && sel.rowCount === 1 && typeof target.row !== 'undefined') {
  174. const orgRow = sheet.getActiveRowIndex();
  175. if (orgRow !== target.row) {
  176. sheet.setActiveCell(target.row, target.col);
  177. handleSelectionChanged(target.row);
  178. }
  179. }
  180. return {
  181. items: {
  182. insert: {
  183. name: '新增',
  184. icon: "fa-arrow-left",
  185. disabled: function () {
  186. return locked;
  187. },
  188. callback: function (key, opt) {
  189. insert();
  190. }
  191. },
  192. del: {
  193. name: '删除',
  194. icon: "fa-arrow-left",
  195. disabled: function () {
  196. return locked || !cache[target.row];
  197. },
  198. callback: function (key, opt) {
  199. del();
  200. }
  201. },
  202. }
  203. };
  204. }
  205. else {
  206. return false;
  207. }
  208. }
  209. });
  210. }
  211. buildContextMenu();
  212. return {
  213. handleSelectionChanged,
  214. curArea,
  215. }
  216. })();
  217. // 分类表
  218. const CLASS_BOOK = (() => {
  219. const setting = {
  220. header: [{ headerName: '分类', headerWidth: $('#area-spread').width(), dataCode: 'name', dataType: 'String', hAlign: 'left', vAlign: 'center' }],
  221. controller: {
  222. cols: [
  223. {
  224. data: {
  225. field: 'name',
  226. vAlign: 1,
  227. hAlign: 0,
  228. font: 'Arial'
  229. },
  230. }
  231. ],
  232. headRows: 1,
  233. headRowHeight: [30],
  234. emptyRows: 0,
  235. treeCol: 0
  236. },
  237. tree: {
  238. id: 'ID',
  239. pid: 'ParentID',
  240. nid: 'NextSiblingID',
  241. rootId: -1
  242. }
  243. };
  244. // 初始化表格
  245. const workBook = initSheet($('#class-spread')[0], setting);
  246. workBook.options.allowExtendPasteRange = false;
  247. workBook.options.allowUserDragDrop = true;
  248. workBook.options.allowUserDragFill = true;
  249. const sheet = workBook.getSheet(0);
  250. let tree;
  251. let controller;
  252. // 初始化数据
  253. async function initData(libID, areaID) {
  254. if (!areaID) {
  255. tree = null;
  256. controller = null;
  257. sheet.setRowCount(0);
  258. PRICE_BOOK.clear();
  259. return;
  260. }
  261. $.bootstrapLoading.start();
  262. try {
  263. const data = await ajaxPost('/priceInfo/getClassData', { libID, areaID }, TIME_OUT);
  264. tree = idTree.createNew(setting.tree);
  265. tree.loadDatas(data);
  266. tree.selected = tree.items.length > 0 ? tree.items[0] : null;
  267. controller = TREE_SHEET_CONTROLLER.createNew(tree, sheet, setting.controller, false);
  268. controller.showTreeData();
  269. handleSelectionChanged(0);
  270. sheet.setSelection(0, 0, 1, 1);
  271. lockUtil.lockSpreads([workBook], locked);
  272. } catch (err) {
  273. console.log(err);
  274. tree = null;
  275. controller = null;
  276. sheet.setRowCount(0);
  277. alert(err);
  278. } finally {
  279. $.bootstrapLoading.end();
  280. }
  281. }
  282. // 编辑处理
  283. async function handleEdit(changedCells) {
  284. const updateData = [];
  285. changedCells.forEach(({ row, col }) => {
  286. updateData.push({
  287. row,
  288. type: UpdateType.UPDATE,
  289. filter: { ID: tree.items[row].data.ID },
  290. update: { name: sheet.getValue(row, col) }
  291. });
  292. });
  293. try {
  294. await ajaxPost('/priceInfo/editClassData', { updateData }, TIME_OUT);
  295. updateData.forEach(({ row, update: { name } }) => tree.items[row].data.name = name);
  296. } catch (err) {
  297. // 恢复各单元格数据
  298. sheetCommonObj.renderSheetFunc(sheet, () => {
  299. changedCells.forEach(({ row }) => {
  300. sheet.setValue(tree.items[row].data.name);
  301. });
  302. });
  303. }
  304. }
  305. sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) {
  306. const changedCells = [{ row: info.row, col: info.col }];
  307. handleEdit(changedCells);
  308. });
  309. sheet.bind(GC.Spread.Sheets.Events.RangeChanged, function (e, info) {
  310. handleEdit(info.changedCells);
  311. });
  312. // 树操作相关
  313. const $insert = $('#tree-insert');
  314. const $remove = $('#tree-remove');
  315. const $upLevel = $('#tree-up-level');
  316. const $downLevel = $('#tree-down-level');
  317. const $downMove = $('#tree-down-move');
  318. const $upMove = $('#tree-up-move');
  319. const $calcPriceIndex = $('#calc-price-index');
  320. // 插入
  321. let canInsert = true;
  322. async function insert() {
  323. try {
  324. if (!canInsert) {
  325. return false;
  326. }
  327. canInsert = false;
  328. $.bootstrapLoading.start();
  329. const updateData = [];
  330. const selected = tree.selected;
  331. const newItem = {
  332. libID,
  333. areaID: AREA_BOOK.curArea.ID,
  334. ID: uuid.v1(),
  335. name: '',
  336. ParentID: '-1',
  337. NextSiblingID: '-1'
  338. };
  339. if (selected) {
  340. newItem.ParentID = selected.data.ParentID;
  341. updateData.push({
  342. type: UpdateType.UPDATE,
  343. filter: { ID: selected.data.ID },
  344. update: { NextSiblingID: newItem.ID }
  345. });
  346. if (selected.nextSibling) {
  347. newItem.NextSiblingID = selected.nextSibling.data.ID;
  348. }
  349. }
  350. updateData.push({
  351. type: UpdateType.CREATE,
  352. document: newItem
  353. });
  354. await ajaxPost('/priceInfo/editClassData', { updateData });
  355. controller.insertByID(newItem.ID);
  356. handleSelectionChanged(sheet.getActiveRowIndex());
  357. } catch (err) {
  358. console.log(err);
  359. alert(err);
  360. } finally {
  361. canInsert = true;
  362. $.bootstrapLoading.end();
  363. }
  364. }
  365. $insert.click(_.debounce(insert, DEBOUNCE_TIME, { leading: true }));
  366. // 删除
  367. let canRemove = true;
  368. async function remove() {
  369. try {
  370. if (!canRemove) {
  371. return false;
  372. }
  373. canRemove = false;
  374. $.bootstrapLoading.start();
  375. const updateData = [];
  376. const selected = tree.selected;
  377. const children = selected.getPosterity();
  378. [selected, ...children].forEach(node => updateData.push({
  379. type: UpdateType.DELETE,
  380. filter: { ID: node.data.ID }
  381. }));
  382. if (selected.preSibling) {
  383. updateData.push({
  384. type: UpdateType.UPDATE,
  385. filter: { ID: selected.preSibling.data.ID },
  386. update: { NextSiblingID: selected.data.NextSiblingID }
  387. });
  388. }
  389. await ajaxPost('/priceInfo/editClassData', { updateData });
  390. controller.delete();
  391. handleSelectionChanged(sheet.getActiveRowIndex());
  392. } catch (err) {
  393. alert(err);
  394. } finally {
  395. canRemove = true;
  396. $.bootstrapLoading.end();
  397. }
  398. }
  399. $remove.click(_.debounce(remove, DEBOUNCE_TIME, { leading: true }));
  400. // 升级
  401. let canUpLevel = true;
  402. async function upLevel() {
  403. try {
  404. if (!canUpLevel) {
  405. return false;
  406. }
  407. canUpLevel = false;
  408. $.bootstrapLoading.start();
  409. const updateData = [];
  410. const selected = tree.selected;
  411. if (selected.preSibling) {
  412. updateData.push({
  413. type: UpdateType.UPDATE,
  414. filter: { ID: selected.preSibling.data.ID },
  415. update: { NextSiblingID: -1 }
  416. });
  417. }
  418. if (selected.parent) {
  419. updateData.push({
  420. type: UpdateType.UPDATE,
  421. filter: { ID: selected.parent.data.ID },
  422. update: { NextSiblingID: selected.data.ID }
  423. });
  424. }
  425. updateData.push({
  426. type: UpdateType.UPDATE,
  427. filter: { ID: selected.data.ID },
  428. update: { ParentID: selected.parent.data.ParentID, NextSiblingID: selected.parent.data.NextSiblingID }
  429. });
  430. let curNode = selected.nextSibling;
  431. while (curNode) {
  432. updateData.push({
  433. type: UpdateType.UPDATE,
  434. filter: { ID: curNode.data.ID },
  435. update: { ParentID: selected.data.ID }
  436. });
  437. curNode = curNode.nextSibling;
  438. }
  439. await ajaxPost('/priceInfo/editClassData', { updateData });
  440. controller.upLevel();
  441. refreshTreeButton(tree.selected);
  442. } catch (err) {
  443. alert(err);
  444. } finally {
  445. canUpLevel = true;
  446. $.bootstrapLoading.end();
  447. }
  448. }
  449. $upLevel.click(_.debounce(upLevel, DEBOUNCE_TIME, { leading: true }));
  450. // 降级
  451. let canDownLevel = true;
  452. async function downLevel() {
  453. try {
  454. if (!canDownLevel) {
  455. return false;
  456. }
  457. canDownLevel = false;
  458. $.bootstrapLoading.start();
  459. const updateData = [];
  460. const selected = tree.selected;
  461. if (selected.preSibling) {
  462. updateData.push({
  463. type: UpdateType.UPDATE,
  464. filter: { ID: selected.preSibling.data.ID },
  465. update: { NextSiblingID: selected.data.NextSiblingID }
  466. });
  467. const preSiblingLastChild = selected.preSibling.children[selected.preSibling.children.length - 1];
  468. if (preSiblingLastChild) {
  469. updateData.push({
  470. type: UpdateType.UPDATE,
  471. filter: { ID: preSiblingLastChild.data.ID },
  472. update: { NextSiblingID: selected.data.ID }
  473. });
  474. }
  475. updateData.push({
  476. type: UpdateType.UPDATE,
  477. filter: { ID: selected.data.ID },
  478. update: { ParentID: selected.preSibling.data.ID, NextSiblingID: -1 }
  479. });
  480. }
  481. await ajaxPost('/priceInfo/editClassData', { updateData });
  482. controller.downLevel();
  483. refreshTreeButton(tree.selected);
  484. } catch (err) {
  485. alert(err);
  486. } finally {
  487. canDownLevel = true;
  488. $.bootstrapLoading.end();
  489. }
  490. }
  491. $downLevel.click(_.debounce(downLevel, DEBOUNCE_TIME, { leading: true }));
  492. // 下移
  493. let canDownMove = true;
  494. async function downMove() {
  495. try {
  496. if (!canDownMove) {
  497. return false;
  498. }
  499. canDownMove = false;
  500. $.bootstrapLoading.start();
  501. const updateData = [];
  502. const selected = tree.selected;
  503. if (selected.preSibling) {
  504. updateData.push({
  505. type: UpdateType.UPDATE,
  506. filter: { ID: selected.preSibling.data.ID },
  507. update: { NextSiblingID: selected.data.NextSiblingID }
  508. });
  509. }
  510. updateData.push({
  511. type: UpdateType.UPDATE,
  512. filter: { ID: selected.data.ID },
  513. update: { NextSiblingID: selected.nextSibling.data.NextSiblingID }
  514. });
  515. updateData.push({
  516. type: UpdateType.UPDATE,
  517. filter: { ID: selected.nextSibling.data.ID },
  518. update: { NextSiblingID: selected.data.ID }
  519. });
  520. await ajaxPost('/priceInfo/editClassData', { updateData });
  521. controller.downMove();
  522. refreshTreeButton(tree.selected);
  523. } catch (err) {
  524. alert(err);
  525. } finally {
  526. canDownMove = true;
  527. $.bootstrapLoading.end();
  528. }
  529. }
  530. $downMove.click(_.debounce(downMove, DEBOUNCE_TIME, { leading: true }));
  531. // 上移
  532. let canUpMove = true;
  533. async function upMove() {
  534. try {
  535. if (!canUpMove) {
  536. return false;
  537. }
  538. canUpMove = false;
  539. $.bootstrapLoading.start();
  540. const updateData = [];
  541. const selected = tree.selected;
  542. if (selected.preSibling) {
  543. updateData.push({
  544. type: UpdateType.UPDATE,
  545. filter: { ID: selected.preSibling.data.ID },
  546. update: { NextSiblingID: selected.data.NextSiblingID }
  547. });
  548. }
  549. const prePreSibling = selected.preSibling.preSibling;
  550. if (prePreSibling) {
  551. updateData.push({
  552. type: UpdateType.UPDATE,
  553. filter: { ID: prePreSibling.data.ID },
  554. update: { NextSiblingID: selected.data.ID }
  555. });
  556. }
  557. updateData.push({
  558. type: UpdateType.UPDATE,
  559. filter: { ID: selected.data.ID },
  560. update: { NextSiblingID: selected.preSibling.data.ID }
  561. });
  562. await ajaxPost('/priceInfo/editClassData', { updateData });
  563. controller.upMove();
  564. refreshTreeButton(tree.selected);
  565. } catch (err) {
  566. alert(err);
  567. } finally {
  568. canUpMove = true;
  569. $.bootstrapLoading.end();
  570. }
  571. }
  572. $upMove.click(_.debounce(upMove, DEBOUNCE_TIME, { leading: true }));
  573. // 刷新树操作按钮有效性
  574. function refreshTreeButton(selected) {
  575. if (locked) {
  576. return;
  577. }
  578. $insert.removeClass('disabled');
  579. $remove.removeClass('disabled');
  580. $upLevel.removeClass('disabled');
  581. $downLevel.removeClass('disabled');
  582. $downMove.removeClass('disabled');
  583. $upMove.removeClass('disabled');
  584. if (!selected) {
  585. $remove.addClass('disabled');
  586. $upLevel.addClass('disabled');
  587. $downLevel.addClass('disabled');
  588. $downMove.addClass('disabled');
  589. $upMove.addClass('disabled');
  590. } else {
  591. if (!selected.preSibling) {
  592. $downLevel.addClass('disabled');
  593. $upMove.addClass('disabled');
  594. }
  595. if (!selected.nextSibling) {
  596. $downMove.addClass('disabled');
  597. }
  598. if (!selected.parent) {
  599. $upLevel.addClass('disabled');
  600. }
  601. }
  602. }
  603. // 焦点变更处理
  604. const curClass = { ID: null };
  605. function handleSelectionChanged(row) {
  606. const classNode = tree.items[row] || null;
  607. tree.selected = classNode;
  608. refreshTreeButton(classNode);
  609. curClass.ID = classNode && classNode.data && classNode.data.ID || null;
  610. const classIDList = []
  611. if (classNode) {
  612. classIDList.push(classNode.data.ID);
  613. const children = classNode.getPosterity();
  614. children.forEach(child => classIDList.push(child.data.ID));
  615. }
  616. PRICE_BOOK.initData(classIDList);
  617. }
  618. const debounceSelectionChanged = _.debounce(function (e, info) {
  619. const row = info.newSelections && info.newSelections[0] ? info.newSelections[0].row : 0;
  620. handleSelectionChanged(row);
  621. }, DEBOUNCE_TIME, { leading: true });
  622. sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, debounceSelectionChanged);
  623. $calcPriceIndex.click(_.debounce(async () => {
  624. $.bootstrapLoading.start();
  625. try {
  626. const data = await ajaxPost('/priceInfo/calcPriceIndex', { libID, period: curLibPeriod, compilationID }, TIME_OUT);
  627. //alert(data);
  628. if (data) {
  629. const htmlStr = data.replace(/\n/gm, '<br>'); //replaceAll('\n','<br>',data);
  630. $("#result-info-body").html(htmlStr);
  631. $("#result-info").modal('show');
  632. } else {
  633. alert('计算完成!')
  634. }
  635. } catch (error) {
  636. console.log(error);
  637. }
  638. $.bootstrapLoading.end();
  639. }, DEBOUNCE_TIME, { leading: true }));
  640. return {
  641. initData,
  642. handleSelectionChanged,
  643. curClass,
  644. }
  645. })();
  646. // 关键字表
  647. const KEYWORD_BOOK = (() => {
  648. const setting = {
  649. header: [
  650. { headerName: '关键字', headerWidth: 200, dataCode: 'keyword', dataType: 'String', hAlign: 'left', vAlign: 'center' },
  651. { headerName: '单位', headerWidth: 70, dataCode: 'unit', dataType: 'String', hAlign: 'center', vAlign: 'center' },
  652. { headerName: '关键字效果', headerWidth: 100, dataCode: 'coe', dataType: 'String', hAlign: 'center', vAlign: 'center' },
  653. { headerName: '组别', headerWidth: 50, dataCode: 'group', dataType: 'String', hAlign: 'center', vAlign: 'center' },
  654. { headerName: '选项号', headerWidth: 70, dataCode: 'optionCode', dataType: 'String', hAlign: 'center', vAlign: 'center' },
  655. ],
  656. };
  657. // 初始化表格
  658. const workBook = initSheet($('#keyword-spread')[0], setting);
  659. workBook.options.allowUserDragDrop = false;
  660. workBook.options.allowUserDragFill = false;
  661. lockUtil.lockSpreads([workBook], true);
  662. const sheet = workBook.getSheet(0);
  663. // 显示关键字数据
  664. const showKeywordData = (keywordList) => {
  665. showData(sheet, keywordList, setting.header);
  666. }
  667. return {
  668. showKeywordData
  669. }
  670. })();
  671. // 价格信息表
  672. const PRICE_BOOK = (() => {
  673. const setting = {
  674. header: [
  675. { headerName: '编码', headerWidth: 100, dataCode: 'code', dataType: 'String', hAlign: 'left', vAlign: 'center', formatter: "@" },
  676. { headerName: '别名编码', headerWidth: 70, dataCode: 'classCode', dataType: 'String', hAlign: 'left', vAlign: 'center', formatter: "@" },
  677. { headerName: '名称', headerWidth: 200, dataCode: 'name', dataType: 'String', hAlign: 'left', vAlign: 'center' },
  678. { headerName: '规格型号', headerWidth: 120, dataCode: 'specs', dataType: 'String', hAlign: 'left', vAlign: 'center' },
  679. { headerName: '单位', headerWidth: 80, dataCode: 'unit', dataType: 'String', hAlign: 'center', vAlign: 'center' },
  680. { headerName: '不含税价', headerWidth: 80, dataCode: 'noTaxPrice', dataType: 'String', hAlign: 'right', vAlign: 'center' },
  681. { headerName: '含税价', headerWidth: 80, dataCode: 'taxPrice', dataType: 'String', hAlign: 'right', vAlign: 'center' },
  682. { headerName: '月份备注', headerWidth: 140, dataCode: 'dateRemark', dataType: 'String', hAlign: 'left', vAlign: 'center' },
  683. { headerName: '计算式', headerWidth: 100, dataCode: 'expString', dataType: 'String', hAlign: 'left', vAlign: 'center' },
  684. ],
  685. };
  686. // 初始化表格
  687. const workBook = initSheet($('#price-spread')[0], setting);
  688. workBook.options.allowUserDragDrop = true;
  689. workBook.options.allowUserDragFill = true;
  690. lockUtil.lockSpreads([workBook], locked);
  691. const sheet = workBook.getSheet(0);
  692. let cache = [];
  693. // 清空
  694. function clear() {
  695. cache = [];
  696. sheet.setRowCount(0);
  697. }
  698. // 初始化数据
  699. async function initData(classIDList) {
  700. if (!classIDList || !classIDList.length) {
  701. return clear();
  702. }
  703. $.bootstrapLoading.start();
  704. try {
  705. cache = await ajaxPost('/priceInfo/getPriceData', { classIDList }, TIME_OUT);
  706. cache = _.sortBy(cache, 'classCode');
  707. showData(sheet, cache, setting.header, 5);
  708. const row = sheet.getActiveRowIndex();
  709. const keywordList = cache[row] && cache[row].keywordList || [];
  710. KEYWORD_BOOK.showKeywordData(keywordList);
  711. } catch (err) {
  712. cache = [];
  713. sheet.setRowCount(0);
  714. alert(err);
  715. } finally {
  716. $.bootstrapLoading.end();
  717. }
  718. }
  719. // 获取当前表中行数据
  720. function getRowData(sheet, row, headers) {
  721. const item = {};
  722. headers.forEach(({ dataCode }, index) => {
  723. const value = sheet.getValue(row, index) || '';
  724. if (value) {
  725. item[dataCode] = value;
  726. }
  727. });
  728. return item;
  729. }
  730. // 获取表数据和缓存数据的不同数据
  731. function getRowDiffData(curRowData, cacheRowData, headers) {
  732. let item = null;
  733. headers.forEach(({ dataCode }) => {
  734. const curValue = curRowData[dataCode];
  735. const cacheValue = cacheRowData[dataCode];
  736. if (!cacheValue && !curValue) {
  737. return;
  738. }
  739. if (cacheValue !== curValue) {
  740. if (!item) {
  741. item = {};
  742. }
  743. item[dataCode] = curValue || '';
  744. }
  745. });
  746. return item;
  747. }
  748. // 编辑处理
  749. async function handleEdit(changedCells) {
  750. const postData = []; // 请求用
  751. // 更新缓存用
  752. const updateData = [];
  753. const deleteData = [];
  754. const insertData = [];
  755. try {
  756. changedCells.forEach(({ row }) => {
  757. if (cache[row]) {
  758. const rowData = getRowData(sheet, row, setting.header);
  759. if (Object.keys(rowData).length) { // 还有数据,更新
  760. const diffData = getRowDiffData(rowData, cache[row], setting.header);
  761. if (diffData) {
  762. postData.push({ type: UpdateType.UPDATE, ID: cache[row].ID, data: diffData });
  763. updateData.push({ row, data: diffData });
  764. }
  765. } else { // 该行无数据了,删除
  766. postData.push({ type: UpdateType.DELETE, ID: cache[row].ID });
  767. deleteData.push(cache[row]);
  768. }
  769. } else { // 新增
  770. const rowData = getRowData(sheet, row, setting.header);
  771. if (Object.keys(rowData).length) {
  772. rowData.ID = uuid.v1();
  773. rowData.libID = libID;
  774. rowData.compilationID = compilationID;
  775. rowData.areaID = AREA_BOOK.curArea.ID;
  776. rowData.classID = CLASS_BOOK.curClass.ID;
  777. rowData.period = curLibPeriod;
  778. postData.push({ type: UpdateType.CREATE, data: rowData });
  779. insertData.push(rowData);
  780. }
  781. }
  782. });
  783. if (postData.length) {
  784. await ajaxPost('/priceInfo/editPriceData', { postData }, TIME_OUT);
  785. // 更新缓存,先更新然后删除,最后再新增,防止先新增后缓存数据的下标与更新、删除数据的下标对应不上
  786. updateData.forEach(item => {
  787. Object.assign(cache[item.row], item.data);
  788. });
  789. deleteData.forEach(item => {
  790. const index = cache.indexOf(item);
  791. if (index >= 0) {
  792. cache.splice(index, 1);
  793. }
  794. });
  795. insertData.forEach(item => cache.push(item));
  796. if (deleteData.length || insertData.length) {
  797. showData(sheet, cache, setting.header, 5);
  798. }
  799. }
  800. } catch (err) {
  801. // 恢复各单元格数据
  802. showData(sheet, cache, setting.header, 5);
  803. }
  804. }
  805. sheet.bind(GC.Spread.Sheets.Events.ValueChanged, function (e, info) {
  806. const changedCells = [{ row: info.row }];
  807. handleEdit(changedCells);
  808. });
  809. sheet.bind(GC.Spread.Sheets.Events.SelectionChanged, function (e, info) {
  810. const row = info.newSelections && info.newSelections[0] ? info.newSelections[0].row : 0;
  811. // 显示关键字数据
  812. const keywordList = cache[row] && cache[row].keywordList || [];
  813. KEYWORD_BOOK.showKeywordData(keywordList);
  814. });
  815. sheet.bind(GC.Spread.Sheets.Events.RangeChanged, function (e, info) {
  816. const changedRows = [];
  817. let preRow;
  818. info.changedCells.forEach(({ row }) => {
  819. if (row !== preRow) {
  820. changedRows.push({ row });
  821. }
  822. preRow = row;
  823. });
  824. handleEdit(changedRows);
  825. });
  826. return {
  827. clear,
  828. initData,
  829. }
  830. })();
  831. $(document).ready(() => {
  832. console.log('进入信息价');
  833. $('[data-toggle="tooltip"]').tooltip();
  834. AREA_BOOK.handleSelectionChanged(0);
  835. const $range = $(document.body);
  836. lockUtil.lockTools($range, locked);
  837. });