Jelajahi Sumber

计算模板,导入导出功能

MaiXinRong 2 bulan lalu
induk
melakukan
0b5b90efb6

+ 0 - 3
app/controller/ledger_controller.js

@@ -601,7 +601,6 @@ module.exports = app => {
                 const ybpData = { header: {}, subjects: [] };
                 const dsk = new DSK(ctx);
                 for (const s of data.subjects) {
-                    console.log(s);
                     const subject = { project: { ID: s.ID, parentID: s.parentID, type: s.type, name: s.name, compilationID: s.compilationId }, rations: [], };
                     subject.bills = await dsk.getProjectBills(s.compilationId, s.subjectId);
                     // 拼凑fees.marketCommon.totalFee,fees.marketLabour.totalFee防止出错
@@ -759,9 +758,7 @@ module.exports = app => {
                     ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
                 const convert = new billsPosConvert(ctx);
                 convert.loadData(ledgerData, posData, []);
-                console.time('convert-all');
                 const result = await convert.convert();
-                console.timeEnd('convert-all');
                 // const wbsCodeHis = await ctx.service.externalData.getExValue(ctx.tender.id, -1,
                 //     externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode) || [];
                 // const [result, needUpdate] = convert.convertByWbsCode(wbsCodeHis);

+ 68 - 0
app/controller/template_controller.js

@@ -10,6 +10,7 @@
 
 const path = require('path');
 const fs = require('fs');
+const sendToWormhole = require('stream-wormhole');
 
 module.exports = app => {
 
@@ -142,6 +143,73 @@ module.exports = app => {
             }
             ctx.redirect(ctx.request.header.referer);
         }
+        async exportTemplate(ctx) {
+            try {
+                const template = await ctx.service.calcTmpl.getTemplate(ctx.query.tid);
+                if (!template) throw '计算模板不存在';
+                if (template.spid !== ctx.subProject.id) throw '您无权导出该模板数据';
+
+                const Cpd = require('../lib/crypt').cpd;
+                const cpd = new Cpd();
+                const filename = `${template.name}.ctd`;
+                const filepath = path.join(this.ctx.app.baseDir, 'temp', filename);
+                await cpd.encrypt({ type: template.type, col_set: template.col_set, multi_header: template.multi_header }, filepath);
+                const userAgent = (ctx.request.header['user-agent'] || '').toLowerCase();
+                let disposition = '';
+                if (userAgent.indexOf('msie') >= 0 || userAgent.indexOf('chrome') >= 0) {
+                    disposition = 'attachment; filename=' + encodeURIComponent(filename);
+                } else if (userAgent.indexOf('firefox') >= 0) {
+                    disposition = 'attachment; filename*="utf8\'\'' + encodeURIComponent(filename) + '"';
+                } else {
+                    /* safari等其他非主流浏览器只能自求多福了 */
+                    disposition = 'attachment; filename=' + new Buffer(filename).toString('binary');
+                }
+                ctx.response.set({
+                    'Content-Type': 'application/octet-stream',
+                    'Content-Disposition': disposition,
+                });
+                ctx.body = await fs.readFileSync(filepath);
+            } catch(err) {
+                ctx.log(err);
+                ctx.postError(err, '导出模板数据失败');
+            }
+        }
+        async importTemplate(ctx) {
+            const template = await ctx.service.calcTmpl.getTemplate(ctx.query.tid);
+            if (!template) throw '计算模板不存在';
+
+            const create_time = Date.parse(new Date()) / 1000;
+            const filepath = path.join(this.ctx.app.baseDir, 'temp', `计算模板${create_time}.ctd`);
+            let stream, index = 0;
+            try {
+                const parts = ctx.multipart({ autoFields: true });
+                stream = await parts();
+                while (stream !== undefined) {
+                    if (!stream.filename) throw '未发现上传文件!';
+                    // 保存文件
+                    await ctx.helper.saveStreamFile(stream, filepath);
+                    await sendToWormhole(stream);
+                    ++index;
+                    if (Array.isArray(parts.field.size) && index < parts.field.size.length) {
+                        stream = await parts();
+                    } else {
+                        stream = undefined;
+                    }
+                }
+                const Cpd = require('../lib/crypt').cpd;
+                const cpd = new Cpd();
+                const data = await cpd.decrypt(filepath);
+                if (template.type !== data.type) throw '导入的模板文件与当前模板类型不一致,不可导入';
+
+                const result =await ctx.service.calcTmpl.saveTemplate({ update: { id: template.id, col_set: data.col_set, multi_header: data.multi_header } });
+                ctx.body = { err: 0, mgs: '', data: result };
+            } catch (err) {
+                // 失败需要消耗掉stream 以防卡死
+                if (stream) await sendToWormhole(stream);
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '导入模板文件失败');
+            }
+        }
         // ---------------------------------------------------
     }
 

+ 0 - 6
app/lib/bills_pos_convert.js

@@ -377,9 +377,7 @@ class BillsPosConvert {
     }
     _calculateAndSortResult() {
         this.resultTree.sortTreeNode();
-        console.time('recurCalc');
         this._recursiveCalculateAndSort(this.resultTree.children);
-        console.timeEnd('recurCalc')
     }
 
     _generateCodeByWbsCodeWithHis(wbsCode) {
@@ -439,12 +437,8 @@ class BillsPosConvert {
     }
     // 转换数据
     convert(filter) {
-        console.time('convert');
         this._recursiveConvertNode(this.bpcTree.children);
-        console.timeEnd('convert');
-        console.time('calc');
         this._calculateAndSortResult();
-        console.timeEnd('calc');
         this._generateCodeByWbsCode();
         switch (filter) {
             case 'cur':

+ 22 - 0
app/public/js/cost_tmpl.js

@@ -365,6 +365,21 @@ $(document).ready(() => {
             }
             return this.colSetData;
         }
+        export() {
+            window.open(`/sp/${spid}/template/ctd?tid=${this.template.id}`);
+        }
+        import() {
+            const self = this;
+            BaseImportFile.show({
+                validList: ['.ctd'],
+                url: `/sp/${spid}/template/ctd/load?tid=${this.template.id}`,
+                afterImport: function (result) {
+                    self.template.col_set = result.update.col_set;
+                    self.template.multi_header = result.update.multi_header;
+                    self.reset();
+                }
+            });
+        }
     }
     const detailObj = new TemplateDetailObj();
 
@@ -488,6 +503,13 @@ $(document).ready(() => {
         $(`.table-file[templateId=${templateId}]`).html(templateObj.getTemplateCaptionHtml(template));
     });
 
+    $('#export').click(function() {
+        detailObj.export();
+    });
+    $('#import').click(function() {
+        detailObj.import();
+    });
+
     $('#addTemplate').click(function() {
         templateObj.addTemplate();
     });

+ 2 - 0
app/router.js

@@ -627,6 +627,8 @@ module.exports = app => {
     app.post('/sp/:id/template/preview', sessionAuth, subProjectCheck, 'templateController.preview');
     app.get('/sp/:id/template/reCalc', sessionAuth, subProjectCheck, 'templateController.reCalcTemplate');
     app.post('/sp/:id/template/reCalc', sessionAuth, subProjectCheck, 'templateController.reCalcTemplate');
+    app.get('/sp/:id/template/ctd', sessionAuth, subProjectCheck, 'templateController.exportTemplate');
+    app.post('/sp/:id/template/ctd/load', sessionAuth, subProjectCheck, 'templateController.importTemplate');
     // 台账管理相关
     app.get('/tender/:id/ledger', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, ledgerAuditCheck, 'ledgerController.explode');
     app.post('/tender/:id/ledger/load', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.loadExplodeData');

+ 3 - 3
app/service/calc_tmpl.js

@@ -124,7 +124,7 @@ module.exports = app => {
                 x.field_cache = x.field_cache ? JSON.parse(x.field_cache) : {};
                 x.decimal = x.decimal ? JSON.parse(x.decimal) : {};
                 x.multi_header = x.multi_header ? JSON.parse(x.multi_header) : undefined;
-                x.calc_expr = x.calc_expr ? JSON.parse(x.calc_expr) : {};
+                x.calc_expr = x.calc_expr ? JSON.parse(x.calc_expr) : [];
                 x.calc_order = x.calc_order ? x.calc_order.split(',') : [];
             });
             for (const d of datas) {
@@ -354,7 +354,6 @@ module.exports = app => {
                 spread_cache: JSON.stringify(relaConst.EmptySpreadCache),
                 field_cache: '{}',
                 decimal: JSON.stringify([{ def: 2 }]),
-                calc_expr: '',
             };
             await this.db.insert(this.tableName, insertData);
             return await this.getTemplate(insertData.id);
@@ -364,6 +363,7 @@ module.exports = app => {
             if (!template) throw '编辑的模板不存在';
             if (template.spid !== this.ctx.subProject.id) throw '模板不属于当前项目,请刷新后重试';
             if (template.create_user_id !== this.ctx.session.sessionUser.accountId) throw '非您创建的模板';
+            if (template.is_locked) throw '模板已被锁定,不可修改';
             return template;
         }
         _getCalcLeaf(cur, cols, parentField) {
@@ -405,7 +405,7 @@ module.exports = app => {
             const updateData = { id };
             if (data.name !== undefined) updateData.name = data.name;
             if (data.col_set !== undefined || data.multi_header !== undefined) {
-                await this.checkTemplateUsed(org);
+                await this.checkTemplateUsed(org, org.type);
                 if (org.used.length > 0) throw '模板已使用,不可修改配置!';
 
                 updateData.col_set = JSON.stringify(data.col_set);

+ 2 - 0
app/view/template/cost.ejs

@@ -13,6 +13,8 @@
                     <div id="detail-ctrl" style="display: none;">
                         <a href="javascript: void(0);" class="btn btn-sm btn-light text-primary" id="reset"> 重置</a>
                         <a href="javascript: void(0);" class="btn btn-sm btn-light text-primary" id="save"> 保存</a>
+                        <a href="javascript: void(0);" class="btn btn-sm btn-light text-primary" id="export"> 导出</a>
+                        <a href="javascript: void(0);" class="btn btn-sm btn-light text-primary" id="import"> 导入</a>
                     </div>
                 </div>
             </div>

+ 2 - 1
app/view/template/cost_modal.ejs

@@ -1,2 +1,3 @@
 <% include ./multi_header_modal.ejs %>
-<% include ./preview_modal.ejs %>
+<% include ./preview_modal.ejs %>
+<% include ../shares/import_file_modal.ejs %>

+ 1 - 1
config/config.default.js

@@ -124,7 +124,7 @@ module.exports = appInfo => {
 
     // 上传设置
     config.multipart = {
-        whitelist: ['.cpd', '.yup', '.ybpx',
+        whitelist: ['.cpd', '.ctd', '.yup', '.ybpx',
             '.json', '.txt',
             '.xls', '.xlsx',
             '.doc', '.docx',