payment_safe.js 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023
  1. function getTenderId() {
  2. return window.location.pathname.split('/')[2];
  3. }
  4. const invalidFields = {
  5. parent: ['cur_qty', 'cur_tp', 'unit_price'],
  6. };
  7. function transExpr(expr) {
  8. return $.trim(expr).replace('\t', '').replace('=', '').replace('%', '/100');
  9. }
  10. $(document).ready(function() {
  11. let stdGcl;
  12. autoFlashHeight();
  13. class BillsObj {
  14. constructor() {
  15. this.spread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
  16. this.sheet = this.spread.getActiveSheet();
  17. this.treeSetting = {
  18. id: 'tree_id',
  19. pid: 'tree_pid',
  20. order: 'tree_order',
  21. level: 'tree_level',
  22. isLeaf: 'tree_is_leaf',
  23. fullPath: 'tree_full_path',
  24. rootId: -1,
  25. calcFields: ['cur_tp', 'pre_tp', 'end_tp'],
  26. keys: ['id', 'detail_id', 'tree_id'],
  27. };
  28. this.tree = createNewPathTree('ledger', this.treeSetting);
  29. this.spreadSetting = {
  30. cols: [
  31. {title: '编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 230, formatter: '@', cellType: 'tree'},
  32. {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', cellType: 'autoTip'},
  33. {title: '规格', colSpan: '1', rowSpan: '2', field: 'spec', hAlign: 0, width: 150, formatter: '@'},
  34. {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
  35. {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 80, type: 'Number'},
  36. {title: '本期|数量', colSpan: '2|1', rowSpan: '1|1', field: 'cur_qty', hAlign: 2, width: 80, type: 'Number'},
  37. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'cur_tp', hAlign: 2, width: 80, type: 'Number'},
  38. {title: '截止本期|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qty', hAlign: 2, width: 80, type: 'Number'},
  39. {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_tp', hAlign: 2, width: 80, type: 'Number', readOnly: true},
  40. {title: '发票号', colSpan: '1', rowSpan: '2', field: 'invoice_code', hAlign: 0, width: 100, formatter: '@'},
  41. {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 120, formatter: '@', cellType: 'ellipsisAutoTip'},
  42. ],
  43. emptyRows: 3,
  44. headRows: 2,
  45. headRowHeight: [25, 25],
  46. defaultRowHeight: 21,
  47. headerFont: '12px 微软雅黑',
  48. font: '12px 微软雅黑',
  49. readOnly: readOnly,
  50. localCache: {
  51. key: 'payment-safe-bills',
  52. colWidth: true,
  53. },
  54. };
  55. this.ckBillsSpread = window.location.pathname + '-billsSelect';
  56. this.initSpread();
  57. this.initOtherEvent();
  58. }
  59. initSpread() {
  60. SpreadJsObj.initSheet(this.sheet, this.spreadSetting);
  61. this.spread.bind(spreadNS.Events.SelectionChanged, this.selectionChanged);
  62. this.spread.bind(spreadNS.Events.topRowChanged, this.topRowChanged);
  63. this.spread.bind(spreadNS.Events.ClipboardChanging, function (e, info) {
  64. const copyText = SpreadJsObj.getFilterCopyText(info.sheet);
  65. SpreadJsObj.Clipboard.setCopyData(copyText);
  66. });
  67. if (readOnly) return;
  68. this.spread.bind(spreadNS.Events.EditEnded, this.editEnded);
  69. this.spread.bind(spreadNS.Events.EditStarting, this.editStarting);
  70. this.spread.bind(spreadNS.Events.ClipboardPasting, this.clipboardPasting);
  71. SpreadJsObj.addDeleteBind(this.spread, this.deletePress);
  72. }
  73. initOtherEvent() {
  74. const self = this;
  75. // 增删上下移升降级
  76. $('a[name="base-opr"]').click(function () {
  77. self.baseOpr(this.getAttribute('type'));
  78. });
  79. }
  80. refreshOperationValid() {
  81. const setObjEnable = function (obj, enable) {
  82. if (enable) {
  83. obj.removeClass('disabled');
  84. } else {
  85. obj.addClass('disabled');
  86. }
  87. };
  88. const invalidAll = function () {
  89. setObjEnable($('a[name=base-opr][type=add]'), false);
  90. setObjEnable($('a[name=base-opr][type=delete]'), false);
  91. setObjEnable($('a[name=base-opr][type=up-move]'), false);
  92. setObjEnable($('a[name=base-opr][type=down-move]'), false);
  93. setObjEnable($('a[name=base-opr][type=up-level]'), false);
  94. setObjEnable($('a[name=base-opr][type=down-level]'), false);
  95. };
  96. const sel = this.sheet.getSelections()[0];
  97. const row = sel ? sel.row : -1;
  98. const tree = this.sheet.zh_tree;
  99. if (!tree) {
  100. invalidAll();
  101. return;
  102. }
  103. const first = tree.nodes[row];
  104. if (!first) {
  105. invalidAll();
  106. return;
  107. }
  108. let last = first, sameParent = true, nodeUsed = this.checkNodeUsed(tree, first);
  109. if (sel.rowCount > 1 && first) {
  110. for (let r = 1; r < sel.rowCount; r++) {
  111. const rNode = tree.nodes[sel.row + r];
  112. if (!rNode) {
  113. sameParent = false;
  114. break;
  115. }
  116. nodeUsed = nodeUsed || this.checkNodeUsed(tree, rNode);
  117. if (rNode.tree_level > first.tree_level) continue;
  118. if ((rNode.tree_level < first.tree_level) || (rNode.tree_level === first.tree_level && rNode.tree_pid !== first.tree_pid)) {
  119. sameParent = false;
  120. break;
  121. }
  122. last = rNode;
  123. }
  124. }
  125. const preNode = tree.getPreSiblingNode(first);
  126. const valid = !this.sheet.zh_setting.readOnly;
  127. setObjEnable($('a[name=base-opr][type=add]'), valid && first && first.tree_level > 1);
  128. setObjEnable($('a[name=base-opr][type=delete]'), valid && first && sameParent && first.tree_level > 1 && !nodeUsed);
  129. setObjEnable($('a[name=base-opr][type=up-move]'), valid && first && sameParent && first.tree_level > 1 && preNode);
  130. setObjEnable($('a[name=base-opr][type=down-move]'), valid && first && sameParent && first.tree_level > 1 && !tree.isLastSibling(last));
  131. setObjEnable($('a[name=base-opr][type=up-level]'), valid && first && sameParent && tree.getParent(first) && !nodeUsed && first.tree_level > 2);
  132. setObjEnable($('a[name=base-opr][type=down-level]'), valid && first && sameParent && first.tree_level > 1 && preNode && !this.checkNodeUsed(tree, preNode));
  133. }
  134. loadRelaData() {
  135. this.refreshOperationValid();
  136. SpreadJsObj.saveTopAndSelect(this.sheet, this.ckBillsSpread);
  137. attObj.setSafeBills();
  138. }
  139. refreshTree(data) {
  140. const sheet = this.sheet;
  141. SpreadJsObj.massOperationSheet(sheet, function () {
  142. const tree = sheet.zh_tree;
  143. // 处理删除
  144. if (data.delete) {
  145. data.delete.sort(function (a, b) {
  146. return b.deleteIndex - a.deleteIndex;
  147. });
  148. for (const d of data.delete) {
  149. sheet.deleteRows(d.deleteIndex, 1);
  150. }
  151. }
  152. // 处理新增
  153. if (data.create) {
  154. const newNodes = data.create;
  155. if (newNodes) {
  156. newNodes.sort(function (a, b) {
  157. return a.index - b.index;
  158. });
  159. for (const node of newNodes) {
  160. sheet.addRows(node.index, 1);
  161. SpreadJsObj.reLoadRowData(sheet, tree.nodes.indexOf(node), 1);
  162. }
  163. }
  164. }
  165. // 处理更新
  166. if (data.update) {
  167. const rows = [];
  168. for (const u of data.update) {
  169. rows.push(tree.nodes.indexOf(u));
  170. }
  171. SpreadJsObj.reLoadRowsData(sheet, rows);
  172. }
  173. // 处理展开
  174. if (data.expand) {
  175. const expanded = [];
  176. for (const e of data.expand) {
  177. if (expanded.indexOf(e) === -1) {
  178. const posterity = tree.getPosterity(e);
  179. for (const p of posterity) {
  180. sheet.setRowVisible(tree.nodes.indexOf(p), p.visible);
  181. expanded.push(p);
  182. }
  183. }
  184. }
  185. }
  186. });
  187. }
  188. loadData(datas) {
  189. this.tree.loadDatas(datas);
  190. treeCalc.calculateAll(this.tree);
  191. SpreadJsObj.loadSheetData(this.sheet, SpreadJsObj.DataType.Tree, this.tree);
  192. SpreadJsObj.loadTopAndSelect(this.sheet, this.ckBillsSpread);
  193. this.refreshOperationValid();
  194. }
  195. getDefaultSelectInfo() {
  196. if (!this.tree) return;
  197. const sel = this.sheet.getSelections()[0];
  198. const node = this.sheet.zh_tree.nodes[sel.row];
  199. if (!node) return;
  200. let count = 1;
  201. if (sel.rowCount > 1) {
  202. for (let r = 1; r < sel.rowCount; r++) {
  203. const rNode = this.sheet.zh_tree.nodes[sel.row + r];
  204. if (rNode.tree_level > node.tree_level) continue;
  205. if ((rNode.tree_level < node.tree_level) || (rNode.tree_level === node.tree_level && rNode.tree_pid !== node.tree_pid)) {
  206. toastr.warning('请选择同一节点下的节点,进行该操作');
  207. return;
  208. }
  209. count += 1;
  210. }
  211. }
  212. return [this.tree, node, count];
  213. }
  214. checkNodeUsed(tree, node) {
  215. if (node.pre_qty || node.pre_tp) return true;
  216. const posterity = tree.getPosterity(node);
  217. for (const p of posterity) {
  218. if (p.pre_qty || p.pre_tp) return true;
  219. }
  220. return false;
  221. }
  222. baseOpr(type, addCount = 1) {
  223. const self = this;
  224. const sheet = self.sheet;
  225. const sel = sheet.getSelections()[0];
  226. const [tree, node, count] = this.getDefaultSelectInfo();
  227. if (!tree || !node || !count) return;
  228. if (type === 'delete') {
  229. const parent = tree.getParent(node);
  230. const children = parent ? parent.children : tree.children;
  231. const index = children.indexOf(node);
  232. for (let i = 0; i < count; i++) {
  233. const child = children[i+index];
  234. if (this.checkNodeUsed(tree, child)) {
  235. toastr.warning('选中的节点已计量,不可删除');
  236. return;
  237. }
  238. }
  239. } else if (type === 'up-level') {
  240. const parent = tree.getParent(node);
  241. const children = parent ? parent.children : tree.children;
  242. const index = children.indexOf(node);
  243. for (let i = index; i < children.length; i++) {
  244. const child = children[index];
  245. if (this.checkNodeUsed(tree, child)) {
  246. if (i >= index + count) {
  247. toastr.warning('其后节点已计量,选中的节点不可升级');
  248. } else {
  249. toastr.warning('选中的节点已计量,不可升级');
  250. }
  251. return;
  252. }
  253. }
  254. } else if (type === 'down-level') {
  255. const parent = tree.getParent(node);
  256. const children = parent ? parent.children : tree.children;
  257. const index = children.indexOf(node);
  258. if (index > 0 && this.checkNodeUsed(tree, children[index-1])) {
  259. toastr.warning('其前节点已计量,选中的节点不可降级');
  260. return;
  261. }
  262. for (let i = index; i < count; i++) {
  263. const child = children[i+index];
  264. if (this.checkNodeUsed(tree, child)) {
  265. toastr.warning('选中的节点已计量,不可降级');
  266. return;
  267. }
  268. }
  269. }
  270. const updateData = {
  271. postType: type,
  272. postData: {
  273. id: node.tree_id,
  274. count: type === 'add' ? addCount : count,
  275. }
  276. };
  277. if (type === 'delete') {
  278. deleteAfterHint(function () {
  279. postData('update', updateData, function (result) {
  280. const refreshData = tree.loadPostData(result);
  281. self.refreshTree(refreshData);
  282. if (sel) {
  283. sheet.setSelection(sel.row, sel.col, 1, sel.colCount);
  284. }
  285. self.refreshOperationValid();
  286. });
  287. });
  288. } else {
  289. postData('update', updateData, function (result) {
  290. const refreshData = tree.loadPostData(result);
  291. self.refreshTree(refreshData);
  292. if (['up-move', 'down-move'].indexOf(type) > -1) {
  293. if (sel) {
  294. sheet.setSelection(tree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
  295. // SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(node)]);
  296. }
  297. } else if (type === 'add') {
  298. const sel = sheet.getSelections()[0];
  299. if (sel) {
  300. sheet.setSelection(tree.nodes.indexOf(refreshData.create[0]), sel.col, sel.rowCount, sel.colCount);
  301. // SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, tree.nodes.indexOf(refreshData.create[0])]);
  302. }
  303. }
  304. self.refreshOperationValid();
  305. });
  306. }
  307. }
  308. // 事件
  309. selectionChanged(e, info) {
  310. if (info.newSelections) {
  311. if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
  312. billsObj.loadRelaData();
  313. }
  314. }
  315. }
  316. topRowChanged(e, info) {
  317. SpreadJsObj.saveTopAndSelect(info.sheet, billsObj.ckBillsSpread);
  318. }
  319. editEnded(e, info) {
  320. if (!info.sheet.zh_setting) return;
  321. const tree = info.sheet.zh_tree;
  322. const node = SpreadJsObj.getSelectObject(info.sheet);
  323. const data = { id: node.id, detail_id: node.detail_id, tree_id: node.tree_id };
  324. // 未改变值则不提交
  325. const col = info.sheet.zh_setting.cols[info.col];
  326. const orgValue = node[col.field];
  327. const newValue = trimInvalidChar(info.editingText);
  328. if (orgValue == info.editingText || ((!orgValue || orgValue === '') && (newValue === ''))) return;
  329. if (info.editingText) {
  330. const text = newValue;
  331. if (billsObj.checkNodeUsed(tree, node) && col.field ==='b_code' && orgValue !== '' && text === '') {
  332. toastr.error('节点已计量,请勿删除编号');
  333. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  334. return;
  335. }
  336. if (col.type === 'Number') {
  337. const num = _.toNumber(text);
  338. if (_.isFinite(num)) {
  339. data[col.field] = num;
  340. } else {
  341. try {
  342. data[col.field] = ZhCalc.mathCalcExpr(transExpr(text));
  343. } catch(err) {
  344. toastr.error('输入的表达式非法');
  345. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  346. return;
  347. }
  348. }
  349. } else {
  350. data[col.field] = text;
  351. }
  352. } else {
  353. if (billsObj.checkNodeUsed(tree, node) && (col.field ==='b_code') && orgValue !== '') {
  354. toastr.error('节点已计量,请勿删除编号');
  355. SpreadJsObj.reLoadRowData(info.sheet, info.row);
  356. return;
  357. }
  358. data[col.field] = col.type === 'Number' ? 0 : '';
  359. }
  360. // 更新至服务器
  361. postData('update', {postType: 'update', postData: data}, function (result) {
  362. const refreshNode = billsObj.tree.loadPostData(result);
  363. billsObj.refreshTree(refreshNode);
  364. }, function () {
  365. SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
  366. });
  367. }
  368. editStarting(e, info) {
  369. if (!info.sheet.zh_setting || !info.sheet.zh_tree) return;
  370. const tree = info.sheet.zh_tree;
  371. const col = info.sheet.zh_setting.cols[info.col];
  372. const node = info.sheet.zh_tree.nodes[info.row];
  373. if (!node) {
  374. info.cancel = true;
  375. return;
  376. }
  377. switch (col.field) {
  378. case 'b_code':
  379. info.cancel = readOnly || billsObj.checkNodeUsed(tree, node);
  380. break;
  381. case 'unit_price':
  382. info.cancel = readOnly || (node.children && node.children.length > 0);
  383. break;
  384. case 'cur_qty':
  385. case 'cur_tp':
  386. info.cancel = (node.children && node.children.length > 0);
  387. break;
  388. }
  389. }
  390. deletePress (sheet) {
  391. if (!sheet.zh_setting) return;
  392. const sel = sheet.getSelections()[0], datas = [];
  393. for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
  394. let bDel = false;
  395. const node = sheet.zh_tree.nodes[iRow];
  396. const data = sheet.zh_tree.getNodeKeyData(node);
  397. for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
  398. const col = sheet.zh_setting.cols[iCol];
  399. const style = sheet.getStyle(iRow, iCol);
  400. if (style.locked) continue;
  401. if (col.field === 'b_code' && sheet.zh_tree.checkNodeUsed(node, pos)) {
  402. toastr.warning(`"${node.b_code || ''} ${node.name}"已计量,请勿修改`);
  403. return;
  404. }
  405. data[col.field] = col.type === 'Number' ? 0 : '';
  406. bDel = true;
  407. }
  408. if (bDel) datas.push(data);
  409. }
  410. if (datas.length > 0) {
  411. postData('update', {postType: 'update', postData: datas}, function (result) {
  412. const refreshNode = sheet.zh_tree.loadPostData(result);
  413. billsObj.refreshTree(refreshNode);
  414. }, function () {
  415. SpreadJsObj.reLoadRowData(info.sheet, sel.row, sel.rowCount);
  416. });
  417. }
  418. }
  419. clipboardPasting(e, info) {
  420. info.cancel = true;
  421. const tree = info.sheet.zh_tree, setting = info.sheet.zh_setting;
  422. if (!setting || !tree) return;
  423. const pasteData = info.pasteData.html
  424. ? SpreadJsObj.analysisPasteHtml(info.pasteData.html)
  425. : (info.pasteData.text === ''
  426. ? SpreadJsObj.Clipboard.getAnalysisPasteText()
  427. : SpreadJsObj.analysisPasteText(info.pasteData.text));
  428. const hint = {
  429. usedUp: {type: 'warning', msg: '节点已计量,不可修改单价'},
  430. usedCode: {type: 'warning', msg: '节点已计量,编号不可修改'},
  431. invalidExpr: {type: 'warning', msg: '粘贴的表达式非法'},
  432. parent: {type: 'warning', msg: '含有子项的清单,不可粘贴数量、单价、金额'},
  433. };
  434. const datas = [], filterNodes = [];
  435. let level, filterRow = 0;
  436. for (let iRow = 0; iRow < info.cellRange.rowCount; iRow ++) {
  437. const curRow = info.cellRange.row + iRow;
  438. const node = tree.nodes[curRow];
  439. if (!node) continue;
  440. if (!level) level = node.level;
  441. if (node.level < level) break;
  442. let bPaste = false;
  443. const data = info.sheet.zh_tree.getNodeKeyData(node);
  444. for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
  445. const curCol = info.cellRange.col + iCol;
  446. const colSetting = info.sheet.zh_setting.cols[curCol];
  447. const value = trimInvalidChar(pasteData[iRow-filterRow][iCol]);
  448. if (node.children && node.children.length > 0 && invalidFields.parent.indexOf(colSetting.field) >= 0) {
  449. toastMessageUniq(hint.parent);
  450. continue;
  451. }
  452. if (billsObj.checkNodeUsed(tree, node) && colSetting.field === 'unit_price') {
  453. toastMessageUniq (hint.usedUp);
  454. continue;
  455. }
  456. if (colSetting.type === 'Number') {
  457. const num = _.toNumber(value);
  458. if (num) {
  459. data[colSetting.field] = num;
  460. } else {
  461. try {
  462. data[colSetting.field] = ZhCalc.mathCalcExpr(transExpr(value));
  463. bPaste = true;
  464. } catch (err) {
  465. toastMessageUniq(hint.invalidExpr);
  466. continue;
  467. }
  468. }
  469. } else {
  470. if (node.used && (colSetting.field ==='b_code') && data[colSetting.field] !== '' && value === '') {
  471. toastMessageUniq(hint.usedCode);
  472. continue;
  473. }
  474. data[colSetting.field] = value;
  475. }
  476. bPaste = true;
  477. }
  478. if (bPaste) {
  479. datas.push(data);
  480. } else {
  481. filterNodes.push(node);
  482. }
  483. }
  484. if (datas.length > 0) {
  485. postData('update', {postType: 'update', postData: datas}, function (result) {
  486. const refreshNode = tree.loadPostData(result);
  487. if (refreshNode.update) refreshNode.update = refreshNode.update.concat(filterNodes);
  488. billsObj.refreshTree(refreshNode);
  489. }, function () {
  490. SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
  491. });
  492. } else {
  493. SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
  494. }
  495. }
  496. }
  497. const billsObj = new BillsObj();
  498. // 清单右键菜单
  499. const billsContextMenuOptions = {
  500. selector: '#bills-spread',
  501. build: function ($trigger, e) {
  502. const target = SpreadJsObj.safeRightClickSelection($trigger, e, billsSpread);
  503. billsObj.loadRelaData();
  504. return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
  505. },
  506. items: {}
  507. };
  508. if (!readOnly) {
  509. billsContextMenuOptions.items.create = {
  510. name: '新增',
  511. icon: 'fa-sign-in',
  512. callback: function (key, opt) {
  513. billsObj.baseOpr('add');
  514. },
  515. disabled: function (key, opt) {
  516. const sheet = billsObj.sheet;
  517. const selection = sheet.getSelections();
  518. const sel = selection ? selection[0] : sheet.getSelections()[0];
  519. const row = sel ? sel.row : -1;
  520. const tree = sheet.zh_tree;
  521. if (!tree) return true;
  522. const first = sheet.zh_tree.nodes[row];
  523. const valid = !sheet.zh_setting.readOnly;
  524. return !(valid && first && first.tree_level > 1);
  525. }
  526. };
  527. billsContextMenuOptions.items.delete = {
  528. name: '删除',
  529. icon: 'fa-remove',
  530. callback: function (key, opt) {
  531. billsObj.baseOpr('delete');
  532. },
  533. disabled: function (key, opt) {
  534. const sheet = billsObj.sheet;
  535. const selection = sheet.getSelections();
  536. const sel = selection ? selection[0] : sheet.getSelections()[0];
  537. const row = sel ? sel.row : -1;
  538. const tree = sheet.zh_tree;
  539. if (!tree) return true;
  540. const first = sheet.zh_tree.nodes[row];
  541. let last = first, sameParent = true, nodeUsed = billsObj.checkNodeUsed(tree, first);
  542. if (sel.rowCount > 1 && first) {
  543. for (let r = 1; r < sel.rowCount; r++) {
  544. const rNode = tree.nodes[sel.row + r];
  545. if (!rNode) {
  546. sameParent = false;
  547. break;
  548. }
  549. nodeUsed = nodeUsed || billsObj.checkNodeUsed(tree, rNode);
  550. if (rNode.tree_level > first.tree_level) continue;
  551. if ((rNode.tree_level < first.tree_level) || (rNode.tree_level === first.tree_level && rNode.tree_pid !== first.tree_pid)) {
  552. sameParent = false;
  553. break;
  554. }
  555. last = rNode;
  556. }
  557. }
  558. const valid = !sheet.zh_setting.readOnly;
  559. return !(valid && first && sameParent && !(first.tree_level === 1) && !nodeUsed);
  560. }
  561. };
  562. $('#psb-decimal').on('show.bs.modal', () => {
  563. const decimalObjs = [$('#psb-qty'), $('#psb-up'), $('#psb-tp')];
  564. for (const obj of decimalObjs) {
  565. obj.val(obj.attr('org'));
  566. }
  567. });
  568. $('#psb-decimal-ok').click(() => {
  569. const decimal = { qty: parseInt($('#psb-qty').val()), up: parseInt($('#psb-up').val()), tp: parseInt($('#psb-tp').val()) };
  570. postData('decimal', { decimal }, function(result) {
  571. if (result.calc) {
  572. const refreshNode = billsObj.tree.loadPostData(result);
  573. billsObj.refreshTree(refreshNode);
  574. }
  575. $('#psb-qty').attr('org', decimal.qty);
  576. $('#psb-up').attr('org', decimal.up);
  577. $('#psb-tp').attr('org', decimal.tp);
  578. $('#psb-decimal').modal('hide');
  579. });
  580. });
  581. }
  582. class AttObj {
  583. constructor() {
  584. this.atts = [];
  585. this.billsIndexes = {};
  586. this.pageCount = 15;
  587. this.activeTab = 'cur';
  588. this.curPageIndex = 1;
  589. this.curTotalPage = 1;
  590. this.allPageIndex = 1;
  591. this.allTotalPage = 1;
  592. }
  593. refreshShowPage() {
  594. const prefix = this.activeTab;
  595. $('#totalPage').html(this[prefix + 'TotalPage']);
  596. $('#currentPage').html(this[prefix + 'PageIndex']);
  597. if (this[prefix + 'TotalPage'] > 1) {
  598. $('#showPage').show();
  599. } else {
  600. $('#showPage').hide();
  601. }
  602. }
  603. reCalcPage() {
  604. this.allTotalPage = Math.ceil(this.atts.length / this.pageCount);
  605. const curNode = SpreadJsObj.getSelectObject(billsObj.sheet);
  606. const curAttIndex = curNode ? this.billsIndexes[curNode.safe_id] || [] : [];
  607. this.curTotalPage = Math.ceil(curAttIndex.length / this.pageCount);
  608. this.refreshShowPage();
  609. }
  610. loadDatas(datas) {
  611. for (const d of datas) {
  612. this.atts.push(d);
  613. if (!this.billsIndexes[d.safe_id]) {
  614. this.billsIndexes[d.safe_id] = [];
  615. }
  616. this.billsIndexes[d.safe_id].push(d);
  617. }
  618. this.refreshTable();
  619. }
  620. loadUpdateAtt(files) {
  621. for (const d of files) {
  622. this.atts.unshift(d);
  623. if (!this.billsIndexes[d.safe_id]) {
  624. this.billsIndexes[d.safe_id] = [];
  625. }
  626. this.billsIndexes[d.safe_id].unshift(d);
  627. }
  628. }
  629. loadDeleteAtt(file) {
  630. let fileIndex = this.atts.findIndex(x => { return x.id === file.id; });
  631. this.atts.splice(fileIndex, 1);
  632. if (this.billsIndexes[file.safe_id]) {
  633. fileIndex = this.billsIndexes[file.safe_id].findIndex(x => { return x.id === file.id; });
  634. this.billsIndexes[file.safe_id].splice(fileIndex, 1);
  635. }
  636. }
  637. getFileHtml(file) {
  638. const html = [];
  639. const downHtml = `<a href="javascript: void(0);" onclick="AliOss.downloadFile('${file.filepath}', '${file.filename + file.fileext}')" class="mr-1"><i class="fa fa-download fa-fw"></i></a>`;
  640. const delHtml = file.uid === userID ? `<a href="javascript: void(0);" class="mr-1 text-danger" name="del-file" file_id="${file.id}"><i class="fa fa-trash-o fa-fw"></i></a>` : '';
  641. const locateHtml = `<a href="javascript: void(0);" name="locate-bills" file_id="${file.id}" class="mr-1"><i data-toggle="tooltip" data-placement="left" data-original-title="定位" class="fa fa-crosshairs"></i></a>`;
  642. html.push('<tr>');
  643. html.push(`<td><input type="checkbox" name="check-att" file_id="${file.id}"></td>`);
  644. html.push(`<td><div class="d-flex justify-content-between align-items-center table-file"><a href="javascript: void(0);" file_id="${file.id}" name="view-file">${file.filename}${file.fileext}</a><div class="btn-group-table" style="display: none;">${locateHtml}${downHtml}${delHtml}</div></div></td>`);
  645. html.push(`<td>${file.u_name}</td>`);
  646. html.push('</tr>');
  647. return html.join('');
  648. }
  649. refreshCurTable() {
  650. const curNode = SpreadJsObj.getSelectObject(billsObj.sheet);
  651. const html = [];
  652. if (curNode) {
  653. const attIndex = this.billsIndexes[curNode.safe_id] || [];
  654. const beginIndex = (this.curPageIndex - 1) * this.pageCount;
  655. const endIndex = (this.curPageIndex) * this.pageCount - 1;
  656. for (const [i, ai] of attIndex.entries()) {
  657. if (i < beginIndex) continue;
  658. if (i > endIndex) continue;
  659. html.push(this.getFileHtml(ai));
  660. }
  661. }
  662. $('#cur-att-list').html(html.join(''));
  663. }
  664. refreshAllTable() {
  665. const html = [];
  666. const attIndex = this.atts;
  667. const beginIndex = (this.allPageIndex - 1) * this.pageCount;
  668. const endIndex = (this.allPageIndex) * this.pageCount - 1;
  669. for (const [i, ai] of attIndex.entries()) {
  670. if (i < beginIndex) continue;
  671. if (i > endIndex) continue;
  672. html.push(this.getFileHtml(ai));
  673. }
  674. $('#all-att-list').html(html.join(''));
  675. }
  676. prePage(){
  677. const pageIndex = this.activeTab + 'PageIndex';
  678. if (this[pageIndex] <= 1) return;
  679. this[pageIndex] = this[pageIndex] - 1;
  680. this.refreshShowTable();
  681. this.refreshShowPage();
  682. }
  683. nextPage() {
  684. const pageIndex = this.activeTab + 'PageIndex';
  685. if (this[pageIndex] >= this[this.activeTab + 'TotalPage']) return;
  686. this[pageIndex] = this[pageIndex] + 1;
  687. this.refreshShowTable();
  688. this.refreshShowPage();
  689. }
  690. refreshTable() {
  691. this.reCalcPage();
  692. this.refreshCurTable();
  693. this.refreshAllTable();
  694. }
  695. refreshShowTable() {
  696. if (this.activeTab === 'cur') {
  697. this.refreshCurTable();
  698. } else {
  699. this.refreshAllTable();
  700. }
  701. }
  702. batchDownload() {
  703. const checkes = $('[name=check-att]:checked');
  704. if (checkes.length === 0) {
  705. toastr.warning('请选择需要批量下载的文件');
  706. return;
  707. }
  708. const files = [], fileIds = [];
  709. for (const c of checkes) {
  710. const file = this.atts.find(x => { return x.id === parseInt(c.getAttribute('file_id'))});
  711. if (file && fileIds.indexOf(file.id) < 0) {
  712. fileIds.push(file.id);
  713. files.push({ filename: file.filename, fileext: file.fileext, filepath: c.viewpath || file.filepath});
  714. }
  715. }
  716. AliOss.zipFiles(files, '安全生产费-附件.zip', (fails) => {
  717. $(self).removeAttr('disabled');
  718. if (fails.length === 0) {
  719. toastr.success('下载成功');
  720. } else {
  721. toastr.warning(`下载成功(${fails.length}个文件下载失败)`);
  722. }
  723. }, () => {
  724. $(self).removeAttr('disabled');
  725. toastr.error('批量下载失败');
  726. });
  727. }
  728. uploadAtt(files, callback) {
  729. const curNode = SpreadJsObj.getSelectObject(billsObj.sheet);
  730. const formData = new FormData();
  731. if (curNode) formData.append('safe_id', curNode.safe_id);
  732. for (const file of files) {
  733. if (file === undefined) {
  734. toastr.error('未选择上传文件。');
  735. return false;
  736. }
  737. if (file.size > 50 * 1024 * 1024) {
  738. toastr.error('上传文件大小超过50MB。');
  739. return false;
  740. }
  741. const fileext = '.' + file.name.toLowerCase().split('.').splice(-1)[0];
  742. if (whiteList.indexOf(fileext) === -1) {
  743. toastr.error('仅支持office文档、图片、压缩包格式,请勿上传' + fileext + '格式文件。');
  744. return false;
  745. }
  746. formData.append('size', file.size);
  747. formData.append('file[]', file);
  748. }
  749. postDataWithFile('file/upload', formData, function (files) {
  750. attObj.loadUpdateAtt(files);
  751. attObj.refreshTable();
  752. if (callback) callback();
  753. });
  754. }
  755. deleteAtt(file_id) {
  756. const file = this.atts.find(x => { return x.id === file_id; });
  757. if (!file) return;
  758. postData('file/delete', {id: file_id}, function() {
  759. attObj.loadDeleteAtt(file);
  760. attObj.refreshTable();
  761. })
  762. }
  763. locateBills(file_id) {
  764. const file = this.atts.find(x => { return x.id === file_id; });
  765. if (!file || !file.safe_id) return;
  766. const safeBills = billsObj.tree.nodes.find(x => { return x.safe_id === file.safe_id; });
  767. SpreadJsObj.locateTreeNode(billsObj.sheet, safeBills.tree_id, true);
  768. this.refreshCurTable();
  769. }
  770. setTab(tab) {
  771. this.activeTab = tab;
  772. this.refreshShowPage();
  773. }
  774. setSafeBills() {
  775. this.curPageIndex = 1;
  776. this.reCalcPage();
  777. this.refreshCurTable();
  778. }
  779. }
  780. const attObj = new AttObj();
  781. $('#upload-ok').click(function() {
  782. const input = $('#upload-file');
  783. attObj.uploadAtt(input[0].files, function() {
  784. $(input).val('');
  785. $('#upload').modal('hide');
  786. });
  787. });
  788. $('#batch-download-att').click(() => {
  789. attObj.batchDownload();
  790. });
  791. $('body').on('mouseenter', ".table-file", function(){
  792. $(this).children(".btn-group-table").css("display","block");
  793. });
  794. $('body').on('mouseleave', ".table-file", function(){
  795. $(this).children(".btn-group-table").css("display","none");
  796. });
  797. $('body').on('click', '[name=del-file]', function() {
  798. attObj.deleteAtt(parseInt(this.getAttribute('file_id')));
  799. });
  800. $('body').on('click', 'a[name=view-file]', function() {
  801. const file_id = parseInt(this.getAttribute('file_id'));
  802. const file = attObj.atts.find(x => { return x.id === file_id; });
  803. if (!file) return;
  804. if (file.viewpath) {
  805. window.open(file.viewpath, '_blank');
  806. } else {
  807. AliOss.downloadFile(file.filepath, `${file.filename}${file.fileext}`)
  808. }
  809. });
  810. $('body').on('click', 'a[name=locate-bills]', function() {
  811. attObj.locateBills(parseInt(this.getAttribute('file_id')));
  812. });
  813. $('.page-select').click(function() {
  814. const content = this.getAttribute('content');
  815. if (content === 'pre') {
  816. attObj.prePage();
  817. } else if (content === 'next') {
  818. attObj.nextPage();
  819. }
  820. });
  821. $('[fujian-content]').click(function() {
  822. const content = this.getAttribute('fujian-content');
  823. attObj.setTab(content.replace('-att', ''));
  824. });
  825. $('.check-all-file').change(function() {
  826. const checked = this.checked;
  827. const table = $(this).parent().parent().parent().parent();
  828. const checkes = $('[name=check-att]', table);
  829. checkes.each(function(){
  830. this.checked = checked;
  831. })
  832. });
  833. // 加载安全生产费数据
  834. postData('load', { filter: 'bills;att' }, function(result) {
  835. billsObj.loadData(result.bills);
  836. attObj.loadDatas(result.att);
  837. });
  838. const stdGclSetting = {
  839. selector: '#std-gcl',
  840. stdType: 'gcl',
  841. libs: stdBills,
  842. treeSetting: {
  843. id: 'bill_id',
  844. pid: 'pid',
  845. order: 'order',
  846. level: 'level',
  847. isLeaf: 'is_leaf',
  848. fullPath: 'full_path',
  849. rootId: -1,
  850. keys: ['id', 'list_id', 'bill_id']
  851. },
  852. spreadSetting: {
  853. cols: [
  854. {title: '清单编号', field: 'b_code', hAlign: 0, width: 170, formatter: '@', cellType: 'tree'},
  855. {title: '名称', field: 'name', hAlign: 0, width: 150, formatter: '@'},
  856. {title: '单位', field: 'unit', hAlign: 1, width: 50, formatter: '@'}
  857. ],
  858. treeCol: 0,
  859. emptyRows: 0,
  860. headRows: 1,
  861. headRowHeight: [32],
  862. defaultRowHeight: 21,
  863. headerFont: '12px 微软雅黑',
  864. font: '12px 微软雅黑',
  865. headColWidth: [30],
  866. selectedBackColor: '#fffacd',
  867. readOnly: true,
  868. },
  869. page: 'paymentSafe',
  870. tid: getTenderId(),
  871. cellDoubleClick: function (updateData, stdNode, stdTree) {
  872. if (!stdTree || !stdNode || !stdNode.b_code) return;
  873. const mainSheet = billsObj.sheet;
  874. if ( !mainSheet.zh_tree) return;
  875. const mainTree = mainSheet.zh_tree;
  876. const sel = mainSheet.getSelections()[0];
  877. const nodes = [stdNode, ...stdTree.getAllParents(stdNode)];
  878. nodes.sort((a, b) => { return a.level - b.level; });
  879. const stdData = [];
  880. let mainChildren = mainTree.children, mainCur, checkNode;
  881. for (const sd of nodes) {
  882. const field = sd.b_code ? 'b_code' : 'name';
  883. checkNode = mainChildren.find(x => { return x[field] === sd[field]; });
  884. if (!checkNode) {
  885. stdData.push({ b_code: sd.b_code, name: sd.name, unit: sd.unit });
  886. } else {
  887. mainCur = checkNode;
  888. mainChildren = mainCur ? mainCur.children : [];
  889. }
  890. }
  891. if (stdData.length > 0) {
  892. postData('update', { postType: 'add-std',
  893. postData: {
  894. id: mainCur ? mainCur.tree_id : mainTree.setting.rootId,
  895. stdData
  896. }
  897. }, function (result) {
  898. const refreshNode = mainTree.loadPostData(result);
  899. billsObj.refreshTree(refreshNode);
  900. const node = _.find(billsObj.tree.nodes, { b_code: stdNode.b_code, name: stdNode.name });
  901. if (node) {
  902. mainSheet.setSelection(billsObj.tree.nodes.indexOf(node), sel.col, 1, sel.colCount);
  903. SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, billsObj.tree.nodes.indexOf(node)]);
  904. }
  905. billsObj.refreshOperationValid();
  906. billsObj.spread.focus();
  907. });
  908. } else {
  909. const node = _.find(billsObj.tree.nodes, { b_code: stdNode.b_code, name: stdNode.name });
  910. mainSheet.setSelection(billsObj.tree.nodes.indexOf(node), sel.col, 1, sel.colCount);
  911. billsObj.spread.focus();
  912. }
  913. },
  914. };
  915. // 展开收起标准清单
  916. $('a', '#side-menu').bind('click', function (e) {
  917. e.preventDefault();
  918. const tab = $(this), tabPanel = $(tab.attr('content'));
  919. // 展开工具栏、切换标签
  920. if (!tab.hasClass('active')) {
  921. // const close = $('.active', '#side-menu').length === 0;
  922. $('a', '#side-menu').removeClass('active');
  923. $('.tab-content .tab-select-show.tab-pane.active').removeClass('active');
  924. tab.addClass('active');
  925. tabPanel.addClass('active');
  926. // $('.tab-content .tab-pane').removeClass('active');
  927. showSideTools(tab.hasClass('active'));
  928. if (tab.attr('content') === '#std-gcl') {
  929. if (!stdGcl) stdGcl = $.stdLib(stdGclSetting);
  930. stdGcl.spread.refresh();
  931. }
  932. } else { // 收起工具栏
  933. tab.removeClass('active');
  934. tabPanel.removeClass('active');
  935. showSideTools(tab.hasClass('active'));
  936. }
  937. billsObj.spread.refresh();
  938. });
  939. // 工具栏spr
  940. $.divResizer({
  941. select: '#right-spr',
  942. callback: function () {
  943. billsObj.spread.refresh();
  944. if (stdGcl) stdGcl.spread.refresh();
  945. }
  946. });
  947. // 导航Menu
  948. $.subMenu({
  949. menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
  950. toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
  951. key: 'menu.1.0.0',
  952. miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
  953. callback: function (info) {
  954. if (info.mini) {
  955. $('.panel-title').addClass('fluid');
  956. $('#sub-menu').removeClass('panel-sidebar');
  957. } else {
  958. $('.panel-title').removeClass('fluid');
  959. $('#sub-menu').addClass('panel-sidebar');
  960. }
  961. autoFlashHeight();
  962. billsObj.spread.refresh();
  963. if (stdGcl) stdGcl.spread.refresh();
  964. }
  965. });
  966. // 显示层次
  967. (function (select, sheet) {
  968. $(select).click(function () {
  969. if (!sheet.zh_tree) return;
  970. const tag = $(this).attr('tag');
  971. const tree = sheet.zh_tree;
  972. setTimeout(() => {
  973. showWaitingView();
  974. switch (tag) {
  975. case "1":
  976. case "2":
  977. case "3":
  978. case "4":
  979. tree.expandByLevel(parseInt(tag));
  980. SpreadJsObj.refreshTreeRowVisible(sheet);
  981. break;
  982. case "last":
  983. tree.expandByCustom(() => { return true; });
  984. SpreadJsObj.refreshTreeRowVisible(sheet);
  985. break;
  986. }
  987. closeWaitingView();
  988. }, 100);
  989. });
  990. })('a[name=showLevel]', billsObj.sheet);
  991. });