index.js 31 KB

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