pm_import.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. 'use strict';
  2. /**
  3. *
  4. *
  5. * @author Zhong
  6. * @date 2019/5/15
  7. * @version
  8. */
  9. //操作接口为eventListen
  10. const importView = (() => {
  11. let importXML = null;
  12. let xmlObj = null; //导入xml转化后的对象
  13. let tbcObj = null; //待确认对象
  14. //显示、隐藏提示上传文件窗口相关提示信息
  15. function showUploadAlert(success, msg){
  16. if(!success){
  17. $('#uploadAlert').removeClass('alert-success');
  18. $('#uploadAlert').addClass('alert-danger');
  19. } else{
  20. $('#uploadAlert').removeClass('alert-danger');
  21. $('#uploadAlert').addClass('alert-success');
  22. }
  23. $('#uploadAlert').text(msg);
  24. $('#uploadAlert').show();
  25. }
  26. /*
  27. * 获取单位工程数据(将所有单项工程里的单位工程放到一起,含有单项工程的一些信息)
  28. * @param {Object} xmlObj @return {Array}
  29. * */
  30. function getTenderDatas(xmlObj) {
  31. if (!xmlObj || !xmlObj.engs) {
  32. return [];
  33. }
  34. let rst = [];
  35. for (let eng of xmlObj.engs) {
  36. if (Array.isArray(eng.tenders)) {
  37. for (let tender of eng.tenders) {
  38. tender.temp = {}; //暂时存放的信息(不是从导入文件中获取的,记录当前工程已阅、选择的费用标准、计算程序)
  39. tender.engName = eng.name;
  40. }
  41. rst.push(...eng.tenders);
  42. }
  43. }
  44. return rst;
  45. }
  46. //确认界面提示信息隐藏
  47. function hideTBCInfo() {
  48. $('#tbc-engName-info').hide();
  49. $('#tbc-tenderName-info').hide();
  50. $('#tbc-engineering-info').hide();
  51. $('#tbc-calcProgram-info').hide();
  52. }
  53. //待确认界面相关
  54. class TBC {
  55. constructor(xmlObj, fileKind, valuation, taxType) {
  56. this.datas = getTenderDatas(xmlObj);
  57. this.fileKind = fileKind; //文件类型
  58. this.valuationType = 'bill'; //计价方式 暂时默认为清单计价
  59. this.valuation = valuation; //计价规则数据
  60. this.taxType = taxType; //计税方法
  61. this.engineeringList = valuation ? valuation.engineering_list : []; //工程专业库数据
  62. this.curIdx = 0; //当前单位工程数据索引
  63. this.init();
  64. }
  65. //初始化
  66. init() {
  67. this.next(this.curIdx);
  68. $('#importInterface .modal-content:eq(1) p:eq(0)').text(`该文件共有${this.datas.length}个单位工程待导入`); //信息统计信息
  69. }
  70. checkConfirmed() {
  71. //检查数据是否都确认过了
  72. let allConfirmed = this.datas.every(data => data.temp.confirmed);
  73. if (allConfirmed) {
  74. $('#import-confirm').prop('disabled', false);
  75. }
  76. }
  77. //检测当前待确认工程是否有效,有效才可确认
  78. checkValid() {
  79. let engName = $('#tbc-engName').val();
  80. if (!engName) {
  81. return $('#tbc-engName-info').show();
  82. } else {
  83. $('#tbc-engName-info').hide();
  84. }
  85. let tenderName = $('#tbc-tenderName').val();
  86. if (!tenderName) {
  87. return $('#tbc-tenderName-info').show();
  88. } else {
  89. $('#tbc-tenderName-info').hide();
  90. }
  91. let engineering = $('#tbc-engineering').val();
  92. if (!engineering) {
  93. return $('#tbc-engineering-info').show();
  94. } else {
  95. $('#tbc-engineering-info').hide();
  96. }
  97. let feeStandard = $('#tbc-feeStandard').val();
  98. if (!feeStandard) {
  99. return $('#tbc-feeStandard-info').show();
  100. } else {
  101. $('#tbc-feeStandard-info').hide();
  102. }
  103. let calcProgram = $('#tbc-calcProgram').val();
  104. if (!calcProgram) {
  105. return $('#tbc-calcProgram-info').show();
  106. } else {
  107. $('#tbc-calcProgram-info').hide();
  108. }
  109. return null;
  110. }
  111. //下一工程
  112. next(idx = null) {
  113. this.curIdx = idx !== null ? idx : this.curIdx + 1;
  114. if (this.curIdx && this.checkValid()) {
  115. this.curIdx--;
  116. return;
  117. }
  118. let curData = this.datas[this.curIdx];
  119. //显示这个工程的相关信息
  120. $('#tbc-engName').val(curData.engName || '');
  121. $('#tbc-tenderName').val(curData.name || '');
  122. $('#tbc-engineering').val(curData.engineering || '');
  123. curData.temp.confirmed = true; //增加确认信息(已读)
  124. if (this.curIdx === this.datas.length -1) { //到最后一个数据了,无法点击下一工程
  125. $('#tbc-next').prop('disabled', true);
  126. } else {
  127. $('#tbc-next').prop('disabled', false);
  128. }
  129. if (this.curIdx !== 0) { //不为第一个数据,可以点击上一工程
  130. $('#tbc-prev').prop('disabled', false);
  131. } else {
  132. $('#tbc-prev').prop('disabled', true);
  133. }
  134. //更新x/x信息
  135. $('#tbc-info').text(`${this.curIdx + 1}/${this.datas.length} 已确认`);
  136. //更新选项信息
  137. this.initFeeOpts(curData);
  138. this.checkConfirmed();
  139. }
  140. //上一工程
  141. prev(idx = null) {
  142. this.curIdx = idx !== null ? idx : this.curIdx - 1;
  143. let curData = this.datas[this.curIdx];
  144. $('#tbc-engName').val(curData.engName || '');
  145. $('#tbc-tenderName').val(curData.name || '');
  146. $('#tbc-engineering').val(curData.engineering || '');
  147. if (this.curIdx === 0) { //到第一个数据了,无法点击上一工程
  148. $('#tbc-prev').prop('disabled', true);
  149. } else {
  150. $('#tbc-prev').prop('disabled', false);
  151. }
  152. if (this.curIdx !== this.datas.length -1 ) { //不为最后一个数据了,可以点击下一工程
  153. $('#tbc-next').prop('disabled', false);
  154. } else {
  155. $('#tbc-next').prop('disabled', true);
  156. }
  157. //更新x/x信息
  158. $('#tbc-info').text(`${this.curIdx + 1}/${this.datas.length} 已确认`);
  159. //更新选项信息
  160. this.initFeeOpts(curData);
  161. this.checkConfirmed();
  162. }
  163. /*
  164. * 初始化工程的费用选项等相关信息(专业工程、费用标准、计算程序等)
  165. * 根据导入文件提取的专业工程等数据确定,需要我们软件有对应的工程专业数据,否则下拉选择为空
  166. * @param {Object}tenderData(当前确认单位工程数据) {Array}engineeringList(当前计价规则下的工程专业数据)
  167. * @return {void}
  168. * */
  169. initFeeOpts(tenderData) {
  170. $('#tbc-engineering').empty(); //清空工程专业选项
  171. $('#tbc-feeStandard').empty(); //清空费用标准选项
  172. $('#tbc-calcProgram').empty(); //清空计算程序选项
  173. //工程专业
  174. let engineerings = this.engineeringList.filter(data => data.lib.name === tenderData.engineering);
  175. if (!engineerings.length) {
  176. return;
  177. }
  178. $('#tbc-engineering').html(`<option selected value="${tenderData.engineering}">${tenderData.engineering}</option>`);
  179. //费用标准,若当前工程有费用标准数据(该数据本身没有费用标准数据,选择过了,就会记录),则选中该费用标准
  180. let feeOptsHtml = engineerings.map(data =>
  181. `<option ${tenderData.temp.feeStandard === data.lib.feeName ? 'selected' : ''} value="${data.lib.feeName}">${data.lib.feeName}</option>`).join('');
  182. $('#tbc-feeStandard').html(feeOptsHtml);
  183. if (!tenderData.temp.feeStandard) { //记录选中的费用标准
  184. tenderData.temp.feeStandard = $('#tbc-feeStandard').val();
  185. }
  186. //根据工程专业及费用标准确定的当前选中的工程专业库
  187. let curEngineering = engineerings.find(data => data.lib.feeName === tenderData.temp.feeStandard) || engineerings[0];
  188. //根据计税方法过滤出来的计税组合
  189. let taxDatas = curEngineering.lib.tax_group.filter(data => data.taxType == this.taxType);
  190. //计算程序,若当前工程有计算程序(该数据本身没有计算程序数据,选择过了,就会记录),则选中该计算程序
  191. let calcOptsHtml = taxDatas.map(data =>
  192. `<option ${tenderData.temp.calcProgram === data.program_lib.name ? 'selected' : ''} value="${data.program_lib.name}">${data.program_lib.name}</option>`);
  193. $('#tbc-calcProgram').html(calcOptsHtml);
  194. if (!tenderData.temp.calcProgram) {
  195. tenderData.temp.calcProgram = $('#tbc-calcProgram').val();
  196. }
  197. }
  198. }
  199. /*
  200. * 根据条件获取计税组合数据
  201. * @param {Object}query ({engineeringName, feeName, taxType, calcProgram}) {Array}engineeringList(当前计价规则下的所有工程专业数据)
  202. * @return {Object}
  203. * */
  204. function getTaxData(query, engineeringList) {
  205. //工程专业名称 + 费用标准名称 确定一个工程专业数据
  206. let engineering = engineeringList.find(data => query.engineeringName === data.lib.name &&
  207. query.feeStandard === data.lib.feeName);
  208. if (!engineering || !Array.isArray(engineering.lib.tax_group)) {
  209. return null;
  210. }
  211. return engineering.lib.tax_group.find(data => query.taxType == data.taxType &&
  212. query.calcProgram === data.program_lib.name);
  213. }
  214. //获取单位工程项目属性的一些数据(新建单位工程需要,前端能获取到的一些数据,还有一些数据需要后端获取: rootProjectID, projectFeature..)
  215. /*
  216. * @param {Object}tbcObj(TBC实例) {Object}curData(单位工程数据)
  217. * @return {Object}
  218. * */
  219. function getProperty(tbcObj, curData) {
  220. if (!tbcObj) {
  221. throw '存在无效数据,无法生成项目。';
  222. }
  223. //计税数据
  224. let query = {
  225. engineeringName: curData.engineering,
  226. feeStandard: curData.temp.feeStandard,
  227. taxType: tbcObj.taxType, //取导入时选择的计税方法,不读取文件中的计税方法
  228. calcProgram: curData.temp.calcProgram
  229. };
  230. let taxData = getTaxData(query, tbcObj.engineeringList);
  231. if (!taxData) {
  232. throw '无效计税数据,无法生成项目。';
  233. }
  234. //当前工程专业数据
  235. let curEngineering = tbcObj.engineeringList.find(data => query.engineeringName === data.lib.name &&
  236. query.feeStandard === data.lib.feeName);
  237. return {
  238. region: '全省', //地区
  239. valuationType: tbcObj.valuationType, //计价方式
  240. valuation: tbcObj.valuation.id, //计价规则
  241. valuationName: tbcObj.valuation.name,
  242. engineering_id: curEngineering.engineering_id, //工程专业
  243. engineeringName: curEngineering.lib.name,
  244. taxType: parseInt(tbcObj.taxType), //计税方法
  245. isInstall: !!curEngineering.lib.isInstall, //是安装工程?
  246. feeStandardName: curEngineering.lib.feeName, //费用标准
  247. engineering: curEngineering.lib.engineering, //定额取费专业
  248. projectEngineering: curEngineering.lib.projectEngineering, //单位工程取费专业
  249. featureLibID: curEngineering.lib.feature_lib[0] ? curEngineering.lib.feature_lib[0].id : '', //工程特征
  250. calcProgram: {name: taxData.program_lib.name, id: taxData.program_lib.id}, //计算程序
  251. colLibID: taxData.col_lib.id, //列设置
  252. templateLibID: taxData.template_lib.id, //清单模板
  253. unitPriceFile: {name: curData.name, id: ''}, //新建单价文件
  254. feeFile: {name: curData.name, id: `newFeeRate@@${taxData.fee_lib.id}`} //新建费率文件
  255. };
  256. }
  257. function eventListen() {
  258. //选择文件
  259. $('#customFile').change(async function() {
  260. let file = $(this)[0].files[0];
  261. $('#import-confirm').prop('disabled', true); //确认导入无效
  262. $('.custom-file-label').text(`${file ? file.name : ''}`); //设置选择框文本
  263. $('#uploadAlert').hide();
  264. $('.selFile').hide();
  265. hideTBCInfo();
  266. if (file) {
  267. let reg = /(xml|XML|qtf|QTF)$/;
  268. if(file.name && !reg.test(file.name)){
  269. $('.selFile').hide();
  270. showUploadAlert(false, '请选择xml或qtf文件。');
  271. return;
  272. }
  273. $.bootstrapLoading.start();
  274. $('#loadingPage').css('z-index', '2000');
  275. //转换数据
  276. importXML = new ImportXML();
  277. try {
  278. xmlObj = await importXML.extractData(file);
  279. $('.selFile input:eq(0)').val(xmlObj && xmlObj.name ? xmlObj.name : '');
  280. $('.selFile input[name="fileKind-import"]:eq(0)').prop('checked', true); //文件类型恢复成投标
  281. $('#import-taxType').val('1'); //计税方法显示回默认的一般计税法
  282. $('.selFile').show(); //显示建设项目、计价规则
  283. } catch (err) {
  284. console.log(err);
  285. showUploadAlert(false, err);
  286. $(this).val('');
  287. }
  288. $.bootstrapLoading.end();
  289. }
  290. });
  291. //下一步
  292. $('#import-next').click(function () {
  293. let file = $('#customFile')[0].files[0];
  294. if (!file) {
  295. showUploadAlert(false, '请选择导入文件。');
  296. return;
  297. }
  298. if (!xmlObj) {
  299. showUploadAlert(false, '不存在有效数据。');
  300. return;
  301. }
  302. let projectName = $('.selFile input:eq(0)').val();
  303. if (!projectName) {
  304. showUploadAlert(false, '不存在有效建设项目。');
  305. return;
  306. }
  307. //文件类型
  308. let fileKind = $('.selFile input[name="fileKind-import"]:checked').val();
  309. if (!fileKind) {
  310. showUploadAlert(false, '不存在有效文件类型。');
  311. }
  312. //计价规则
  313. let valuation = $('#import-valuation').val();
  314. if (!valuation) {
  315. showUploadAlert(false, '不存在有效计价规则。');
  316. return;
  317. }
  318. //计税方法
  319. let taxType = $('#import-taxType').val();
  320. if (!taxType) {
  321. showUploadAlert(false, '不存在有效计税方法。');
  322. return;
  323. }
  324. if (!xmlObj.engs.length) {
  325. showUploadAlert(false, '不存在单项工程数据。');
  326. return;
  327. }
  328. if (!getTenderDatas(xmlObj).length) {
  329. showUploadAlert(false, '不存在单位工程数据。');
  330. return;
  331. }
  332. $('#importInterface .modal-content:eq(0)').hide(); //隐藏第一步内容
  333. $('#importInterface .modal-content:eq(1)').show(); //显示第二步内容
  334. //console.log(getTenderDatas(xmlObj));
  335. //console.log(xmlObj);
  336. let selValuation = billValuation.find(data => data.id === valuation);
  337. tbcObj = new TBC(xmlObj, fileKind, selValuation, taxType);
  338. });
  339. //上一步
  340. $('#import-prev').click(function () {
  341. $('#importInterface .modal-content:eq(0)').show(); //显示第一步内容
  342. $('#importInterface .modal-content:eq(1)').hide(); //隐藏第二步内容
  343. hideTBCInfo();
  344. $('#import-confirm').prop('disabled', true); //确认导入无效
  345. //清空确认字段
  346. if (tbcObj) {
  347. for (let data of tbcObj.datas) {
  348. delete data.temp.confirmed;
  349. }
  350. }
  351. });
  352. //变换费用标准
  353. $('#tbc-feeStandard').change(function () {
  354. let curData = tbcObj ? tbcObj.datas[tbcObj.curIdx] : null;
  355. if (!curData) {
  356. return;
  357. }
  358. curData.temp.feeStandard = $(this).val();
  359. });
  360. //变换计算程序
  361. $('#tbc-calcProgram').change(function () {
  362. let curData = tbcObj ? tbcObj.datas[tbcObj.curIdx] : null;
  363. if (!curData) {
  364. return;
  365. }
  366. curData.temp.calcProgram = $(this).val();
  367. });
  368. //待确认-下一工程
  369. $('#tbc-next').click(function () {
  370. tbcObj.next();
  371. });
  372. //待确认-上一工程
  373. $('#tbc-prev').click(function () {
  374. tbcObj.prev();
  375. hideTBCInfo();
  376. });
  377. //确认导入
  378. $('#import-confirm').click(async function () {
  379. if (tbcObj && tbcObj.checkValid()) {
  380. return;
  381. }
  382. //建设项目设置选择的文件类型和选择的计税方法
  383. xmlObj.property.fileKind = tbcObj.fileKind;
  384. xmlObj.property.taxType = tbcObj.taxType;
  385. //确定使用的计税组合
  386. tbcObj.datas.map(data => {
  387. data.property = getProperty(tbcObj, data);
  388. //默认定额库
  389. let curEngineering = tbcObj.engineeringList.find(enData => data.engineering === enData.lib.name &&
  390. data.temp.feeStandard === enData.lib.feeName);
  391. let defaultLib = curEngineering.lib.ration_lib.find(data => data.isDefault) || curEngineering.lib.ration_lib[0];
  392. data.defaultRationLib = parseInt(defaultLib.id);
  393. //此费用定额下可用的定额库id,人材机库id
  394. data.rationLibIDs = curEngineering.lib.ration_lib.map(data => parseInt(data.id));
  395. data.gljLibIDs = curEngineering.lib.glj_lib.map(data => parseInt(data.id));
  396. });
  397. //确定生成建设项目的父、前、后节点ID
  398. let {parentProjectID, preProjectID, nextProjectID} = projTreeObj.getRelProjectID(projTreeObj.tree.selected);
  399. xmlObj.ParentID = parentProjectID;
  400. xmlObj.preID = preProjectID;
  401. xmlObj.NextSiblingID = nextProjectID;
  402. //确定建设项目的名称(不允许重复)
  403. let sameDepthProjs = getProjs(projTreeObj.tree.selected);
  404. if (sameDepthProjs.find(node => node.data.name === xmlObj.name)) {
  405. xmlObj.name += `(${moment(Date.now()).format('YYYY-MM-DD HH:mm:ss')})`;
  406. }
  407. console.log(xmlObj);
  408. $('#importInterface').modal('hide');
  409. //let importData = await importXML.transformData(xmlObj);
  410. let pr = new SCComponent.InitProgressBar($('#progress'), $('#progress-title'), $('#progress-content'), $('#progressBar'));
  411. pr.start('导入文件', '正在生成文件,请稍候……');
  412. try {
  413. let importData = await importXML.transformData(xmlObj);
  414. let blob = new Blob([JSON.stringify(importData)], {type: 'text/plain;charset=utf-8'});
  415. console.log(blob);
  416. let formData = new FormData();
  417. formData.append('file', blob);
  418. $.ajax({
  419. url: '/pm/import/importInterface',
  420. type: 'POST',
  421. data: formData,
  422. cache: false,
  423. contentType: false,
  424. processData: false,
  425. timeout: 1000 * 60 * 3, //3分钟
  426. success: function(response){
  427. if (response.data && Array.isArray(response.data)) {
  428. doAfterImport(response.data);
  429. }
  430. pr.end();
  431. },
  432. error: function(jqXHR){
  433. pr.end();
  434. throw `与服务器通信发生错误${jqXHR.status} ${jqXHR.statusText}`;
  435. }
  436. });
  437. } catch (err) {
  438. console.log(err);
  439. pr.end();
  440. alert(err);
  441. }
  442. });
  443. //导入窗口消失后
  444. $('#importInterface').on('hidden.bs.modal', function () {
  445. importXML = null;
  446. xmlObj = null; //重置数据
  447. tbcObj = null;
  448. $('#importInterface .modal-content:eq(0)').show(); //显示第一步内容
  449. $('#importInterface .modal-content:eq(1)').hide(); //隐藏第二步内容
  450. $('#customFile').val(''); //清除选择
  451. $('.custom-file-label').text(''); //设置选择框文本
  452. $('#uploadAlert').hide(); //隐藏提示
  453. $('.selFile').hide(); //隐藏建设项目及计价规则
  454. $('#import-confirm').prop('disabled', true); //确认导入无效
  455. $('.selFile input[name="fileKind-import"]:eq(0)').prop('checked', true); //文件类型恢复成投标
  456. $('#import-taxType').val('1'); //显示回一般计税
  457. hideTBCInfo();
  458. });
  459. }
  460. //projectDatas在后端已经按照顺序排过了: project eng1 tender1-1 tender1-2 eng2 tender2-1 tender2-2
  461. function doAfterImport(projectDatas) {
  462. //插入节点
  463. let lastNode;
  464. for (let data of projectDatas) {
  465. data.feeStandardName = data.property && data.property.feeStandardName || ''; //工程专业列显示用
  466. let parent = projTreeObj.tree.items.find(node => node.data.ID === data.ParentID),
  467. next = projTreeObj.tree.items.find(node => node.data.ID === data.NextSiblingID);
  468. lastNode = projTreeObj.insert(data, parent, next);
  469. }
  470. if(lastNode) {
  471. projTreeObj.workBook.getSheet(0).showRow(lastNode.serialNo(), GC.Spread.Sheets.VerticalPosition.center);
  472. }
  473. }
  474. return {eventListen};
  475. })();