index.js 27 KB

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