index.js 31 KB

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