index.js 28 KB

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