Просмотр исходного кода

计算模板,设计量明细,批量导入导出

MaiXinRong 3 недель назад
Родитель
Сommit
7c77af7b33

+ 38 - 1
app/controller/template_controller.js

@@ -188,6 +188,14 @@ module.exports = app => {
         }
         async exportTemplate(ctx) {
             try {
+                if (ctx.query.tids) {
+                    const templates = await ctx.service.calcTmpl.exportTemplates(JSON.parse(ctx.query.tids));
+                    const Cpd = require('../lib/crypt').cpd;
+                    ctx.attachment('设计量明细计算模板.ctdp');
+                    ctx.type = 'application/octet-stream';
+                    ctx.body = Buffer.from(new Cpd().doEncrypt(templates), 'utf8');
+                    return;
+                }
                 const template = await ctx.service.calcTmpl.getTemplate(ctx.query.tid);
                 if (!template) throw '计算模板不存在';
                 if (template.pid !== ctx.session.sessionProject.id) throw '您无权导出该模板数据';
@@ -219,6 +227,10 @@ module.exports = app => {
             }
         }
         async importTemplate(ctx) {
+            if (ctx.query.batch === '1') {
+                await this.importTemplates(ctx);
+                return;
+            }
             const template = await ctx.service.calcTmpl.getTemplate(ctx.query.tid);
             if (!template) throw '计算模板不存在';
 
@@ -268,7 +280,32 @@ module.exports = app => {
                 ctx.ajaxErrorBody(err, '修改数据失败');
             }
         }
+
+        async importTemplates(ctx) {
+            let stream;
+            const filepath = path.join(this.app.baseDir, 'temp', `计算模板-${require('uuid').v4()}.ctdp`);
+            try {
+                const parts = ctx.multipart({ autoFields: true });
+                stream = await parts();
+                if (!stream || !stream.filename) throw '未发现上传文件';
+                if (path.extname(stream.filename).toLowerCase() !== '.ctdp') throw '请选择计算模板ctdp文件';
+                await ctx.helper.saveStreamFile(stream, filepath);
+                await sendToWormhole(stream);
+                stream = await parts();
+                if (stream) throw '一次只能导入一个模板文件';
+                const Cpd = require('../lib/crypt').cpd;
+                const templates = await new Cpd().decrypt(filepath);
+                const result = await ctx.service.calcTmpl.importTemplates(templates, ctx.query.folder_id);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                if (stream) await sendToWormhole(stream);
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '导入模板文件失败');
+            } finally {
+                if (fs.existsSync(filepath)) fs.unlinkSync(filepath);
+            }
+        }
     }
 
     return TemplateController;
-};
+};

+ 44 - 3
app/public/js/pos_calc_tmpl.js

@@ -314,7 +314,6 @@ $(document).ready(() => {
         }
     }
     const folderObj = new FolderObj();
-    folderObj.loadData(folderList);
     class TemplateObj {
         constructor() {
             this.spread = SpreadJsObj.createNewSpread($('#template-spread')[0]);
@@ -322,7 +321,8 @@ $(document).ready(() => {
             this.data = createAncillaryGcl({ id: 'id', masterId: 'folder_id', sort: [['create_time', 'desc']] });
             this.templateSpreadSetting = {
                 cols: [
-                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 240, formatter: '@'},
+                    {title: '选择', colSpan: '1', rowSpan: '1', field: 'selected', hAlign: 1, width: 35, readOnly: true, cellType: "checkbox"},
+                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 210, formatter: '@'},
                     {title: '创建人', colSpan: '1', rowSpan: '1', field: 'user_name', hAlign: 1, width: 80, formatter: '@', readOnly: true},
                 ],
                 emptyRows: 0,
@@ -362,6 +362,7 @@ $(document).ready(() => {
             this.spread.bind(spreadNS.Events.EditStarting, this.editStarting);
             this.spread.bind(spreadNS.Events.EditEnded, this.editEnded);
             this.spread.bind(spreadNS.Events.ClipboardPasting, this.clipboardPasting);
+            this.spread.bind(spreadNS.Events.ButtonClicked, this.buttonClicked);
             SpreadJsObj.addDeleteBind(this.spread, this.deletePress);
         }
         initOtherEvent() {
@@ -526,9 +527,40 @@ $(document).ready(() => {
         clipboardPasting(e, info) {
             info.cancel = true;
         }
+        import(){
+            if (!this.folder) {
+                toastr.warning('请先选择要导入模板的分类');
+                return;
+            }
+            BaseImportFile.show({
+                validList: ['.ctdp'],
+                url: `/template/ctd/load?batch=1&folder_id=${encodeURIComponent(this.folder.id)}`,
+                afterImport: function(result) {
+                    templateObj.data.updateDatas({ add: result.add });
+                    templateObj.refreshSheet();
+                    templateObj.loadTemplate();
+                },
+            });
+        }
+        export() {
+            this.sheet.endEdit();
+            const ids = this.sheet.zh_data.filter(item => item.selected).map(item => item.id);
+            if (!ids.length) {
+                toastr.warning('请先勾选要导出的模板');
+                return;
+            }
+            window.open(`/template/ctd?tids=${encodeURIComponent(JSON.stringify(ids))}`);
+        }
+        buttonClicked(e, info) {
+            const col = info.sheet.zh_setting.cols[info.col];
+            if (col.field !== 'selected') return;
+            const template = info.sheet.zh_data[info.row];
+            if (!template) return;
+            template.selected = !template.selected;
+            SpreadJsObj.reloadColData(info.sheet, info.col);
+        }
     }
     const templateObj = new TemplateObj();
-    templateObj.loadData(templateList);
 
     class TemplateDetailObj {
         constructor() {
@@ -873,6 +905,8 @@ $(document).ready(() => {
         }
     }
     const detailObj = new TemplateDetailObj();
+    folderObj.loadData(folderList);
+    templateObj.loadData(templateList);
 
     $('#preview').click(function() {
         const data = { type: 'posCalc', col_set: detailObj.getCurrentColSet() };
@@ -901,6 +935,13 @@ $(document).ready(() => {
     $('#import').click(function() {
         detailObj.import();
     });
+    $('#batch-export').click(function() {
+        templateObj.export();
+    });
+    $('#batch-import').click(function() {
+        templateObj.import();
+    });
+
 
     $('#rela-no-folder').on('show.bs.modal', function() {
         const html = [];

+ 48 - 1
app/service/calc_tmpl.js

@@ -652,7 +652,54 @@ module.exports = app => {
                 }
             }
         }
+        async exportTemplates(ids) {
+            if (!Array.isArray(ids) || !ids.length || ids.some(id => typeof id !== 'string' || !id)) throw '请选择要导出的模板';
+            const rows = await this.getAllDataByCondition({ where: { id: this._.uniq(ids) } });
+            if (rows.length !== this._.uniq(ids).length) throw '选择的模板不存在,请刷新后重试';
+            if (rows.some(row => row.pid !== this.ctx.session.sessionProject.id || row.type !== 'posCalc')) {
+                throw '您无权导出该模板数据';
+            }
+            await this.analysisTemplate(rows);
+            return this._.uniq(ids).map(id => {
+                const row = rows.find(item => item.id === id);
+                return { name: row.name, type: row.type, col_set: row.col_set, multi_header: row.multi_header };
+            });
+        }
+
+        async importTemplates(data, folderId) {
+            const templates = Array.isArray(data) ? data : [data];
+            if (!templates.length || templates.some(item => !item || item.type !== 'posCalc' || !Array.isArray(item.col_set))) {
+                throw '模板文件格式错误或模板类型不一致';
+            }
+            const folder = folderId && await this.ctx.service.calcTmplFolder.getDataById(folderId);
+            if (!folder || folder.pid !== this.ctx.session.sessionProject.id || folder.type !== 'posCalc') {
+                throw '请选择当前项目下的有效模板分类';
+            }
+            await this.getProjectCalcTmplSet();
+            const count = await this.count({ pid: this.ctx.session.sessionProject.id, type: 'posCalc' });
+            if (count + templates.length > this.projectSet.posCalc.validCount) throw '导入后将超过模板使用上限';
+            const database = this.db;
+            const transaction = await database.beginTransaction();
+            const result = { add: [] };
+            try {
+                this.db = transaction;
+                for (const item of templates) {
+                    const added = await this._addTemplate(item.name, 'posCalc', folderId);
+                    const saved = await this._saveTemplate(added.id, { col_set: item.col_set, multi_header: item.multi_header });
+                    saved.user_name = this.ctx.session.sessionUser.name;
+                    saved.used_count = 0;
+                    result.add.push(saved);
+                }
+                await transaction.commit();
+                return result;
+            } catch (err) {
+                await transaction.rollback();
+                throw err;
+            } finally {
+                this.db = database;
+            }
+        }
     }
 
     return CalcTmpl;
-};
+};

+ 1 - 1
config/config.default.js

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