소스 검색

1. 计量模板,迁移至平台下
2. 台账模板相关

MaiXinRong 3 주 전
부모
커밋
35d9a7ef83

+ 1 - 1
app/base/base_bills_service.js

@@ -665,7 +665,7 @@ class BaseBillsSerivce extends TreeService {
         const pasteBillsData = [], pastePosData = [], pasteAncGclData = [], pasteBillsExtraData = [], pastePosDetailData = [], leafBillsId = [];
         const tpDecimal = this.ctx.tender.info.decimal.tp;
         let maxId = await this._getMaxLid(this.ctx.tender.id);
-        const calcTemplate = await this.ctx.service.calcTmpl.getAllTemplateDetail(this.ctx.subProject.id, 'posCalc');
+        const calcTemplate = await this.ctx.service.calcTmpl.getAllTemplateDetail(this.ctx.session.sessionProject.id, 'posCalc');
         for (const [i, pd] of pasteData.entries()) {
             for (const d of pd) {
                 d.children = pd.filter(function (x) {

+ 1 - 1
app/base/base_controller.js

@@ -11,7 +11,7 @@ const moment = require('moment');
 const messageType = require('../const/message_type');
 const Controller = require('egg').Controller;
 const maintainConst = require('../const/maintain');
-const otherProjectController = ['setting', 'file', 'profile', 'devTest'];
+const otherProjectController = ['setting', 'file', 'profile', 'devTest', 'template'];
 
 class BaseController extends Controller {
 

+ 16 - 2
app/controller/ledger_controller.js

@@ -129,7 +129,7 @@ module.exports = app => {
                 tender.data.hasRevise = !!revise;
                 const stage = await this.ctx.service.stage.getDataByCondition({ tid: tender.id });
                 tender.data.hasStage = !!stage;
-                const posCalcTemplate = await this.ctx.service.calcTmpl.getAllTemplateDetail(ctx.subProject.id, 'posCalc');
+                const posCalcTemplate = await this.ctx.service.calcTmpl.getAllTemplateDetail(ctx.session.sessionProject.id, 'posCalc');
                 tender.data.isAssUser = tender.isAssUser;
                 tender.data.assLedger = tender.assLedger;
                 const renderData = {
@@ -247,6 +247,11 @@ module.exports = app => {
                 (!data.block || data.block.length <= 0)) throw '参数错误';
             return await ctx.service.ledger.pasteBlockData(ctx.tender.id, data.id, data.block);
         }
+        async _applyTemplate(ctx, data) {
+            if ((isNaN(data.id) || data.id <= 0) || !data.templateId) throw '参数错误';
+            const template = await this.ctx.service.ledgerTemplate.getTemplateData(data.templateId);
+            return await ctx.service.ledger.pasteBlockData(ctx.tender.id, data.id, template);
+        }
         /**
          * 从标准项目表添加数据
          * @param ctx
@@ -352,6 +357,9 @@ module.exports = app => {
                     case 'batch-insert':
                         responseData.data = await this._batchInsert(ctx, data.postData);
                         break;
+                    case 'apply-template':
+                        responseData.data = await this._applyTemplate(ctx, data.postData);
+                        break;
                     default:
                         throw '未知操作';
                 }
@@ -460,7 +468,13 @@ module.exports = app => {
                     ? await ctx.service.posCalcDetail.getAllDataByCondition({ where: { tid: ctx.tender.id } })
                     : [];
                 const ledgerTags = await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
-                ctx.body = { err: 0, msg: '', data: { bills: ledgerData, pos: posData, ancGcl: ancillaryGclData, posCalcDetail: posCalcDetailData, tags: ledgerTags } };
+                const ledgerTemplates = await this.ctx.service.ledgerTemplate.getAllTemplate(this.ctx.session.sessionProject.id);
+                ctx.body = {
+                    err: 0, msg: '', data: {
+                        bills: ledgerData, pos: posData, ancGcl: ancillaryGclData, posCalcDetail: posCalcDetailData,
+                        tags: ledgerTags, ledgerTemplates,
+                    }
+                };
             } catch (err) {
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: [] };

+ 14 - 5
app/controller/template_controller.js

@@ -57,12 +57,11 @@ module.exports = app => {
 
         async posCalc(ctx) {
             try {
-                if (!ctx.subProject.page_show.posCalcDetail) throw '该功能已关闭';
                 const renderData = {
                     validColInfo: ctx.service.calcTmpl.TemplateRela.posCalc.ValidColInfo,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.template.posCalc),
                 };
-                renderData.templateList = await ctx.service.calcTmpl.getAllTemplate(ctx.subProject.id, 'posCalc');
+                renderData.templateList = await ctx.service.calcTmpl.getAllTemplate(ctx.session.sessionProject.id, 'posCalc');
                 await ctx.service.calcTmpl.checkTemplateUsed(renderData.templateList, 'posCalc');
                 const specList = await ctx.service.stdExtraList.getList(0);
                 renderData.specList = specList.map(x => { return { value: x.id, text: x.name }; });
@@ -75,12 +74,11 @@ module.exports = app => {
         }
         async cost(ctx) {
             try {
-                // if (!ctx.subProject.page_show.cost) throw '该功能已关闭';
                 const renderData = {
                     validColInfo: ctx.service.calcTmpl.TemplateRela.cost.ValidColInfo,
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.template.cost),
                 };
-                renderData.templateList = await ctx.service.calcTmpl.getAllTemplate(ctx.subProject.id, 'cost');
+                renderData.templateList = await ctx.service.calcTmpl.getAllTemplate(ctx.session.sessionProject.id, 'cost');
                 await ctx.service.calcTmpl.checkTemplateUsed(renderData.templateList, 'cost');
                 await this.layout('template/cost.ejs', renderData, 'template/cost_modal.ejs');
             } catch (err) {
@@ -147,7 +145,7 @@ module.exports = app => {
             try {
                 const template = await ctx.service.calcTmpl.getTemplate(ctx.query.tid);
                 if (!template) throw '计算模板不存在';
-                if (template.spid !== ctx.subProject.id) throw '您无权导出该模板数据';
+                if (template.project_id !== ctx.session.sessionProject.id) throw '您无权导出该模板数据';
 
                 const Cpd = require('../lib/crypt').cpd;
                 const cpd = new Cpd();
@@ -211,6 +209,17 @@ module.exports = app => {
             }
         }
         // ---------------------------------------------------
+
+        async saveLedgerTemplate(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const result = await ctx.service.ledgerTemplate.saveTemplate(data);
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '修改数据失败');
+            }
+        }
     }
 
     return TemplateController;

+ 122 - 63
app/public/js/ledger.js

@@ -525,34 +525,6 @@ $(document).ready(function() {
         relaSheet: ledgerSpread.getActiveSheet(),
     });
 
-    $.subMenu({
-        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
-        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
-        key: 'menu.1.0.0',
-        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
-        callback: function (info) {
-            if (info.mini) {
-                $('.panel-title').addClass('fluid');
-                $('#sub-menu').removeClass('panel-sidebar');
-            } else {
-                $('.panel-title').removeClass('fluid');
-                $('#sub-menu').addClass('panel-sidebar');
-            }
-            autoFlashHeight();
-            ledgerSpread.refresh();
-            if (posSpread) posSpread.refresh();
-            if (ancGclSpread) ancGclSpread.refresh();
-            if (posCalcDetail) posCalcDetail.refresh();
-            if (stdXmj) stdXmj.spread.refresh();
-            if (stdGcl) stdGcl.spread.refresh();
-            if (dealBills) dealBills.spread.refresh();
-            if (searchLedger) searchLedger.spread.refresh();
-            if (errorList) errorList.spread.refresh();
-            if (checkList) checkList.spread.refresh();
-            if (gclGather) gclGather.spread.refresh();
-        }
-    });
-
     // 定义事件
     const treeOperationObj = {
         loadExprToInput(sheet) {
@@ -1498,7 +1470,101 @@ $(document).ready(function() {
                 treeOperationObj.refreshTree(ledgerSheet, refreshNode);
             });
         },
+        applyTemplate: function(template) {
+            const node = SpreadJsObj.getSelectObject(ledgerSheet);
+            if (!node || !template) return;
+            if (tender.isAssUser && node._invalid) {
+                toastr.warning('请在协作范围内应用模板');
+                return;
+            }
+
+            postData(window.location.pathname + '/update', {
+                postType: 'apply-template', postData: { id: ledgerTree.getNodeKey(node), templateId: template.id }
+            }, function(data) {
+                pos.updateDatas(data.pos);
+                if (data.ancGcl) ancGcl.updateDatas(data.ancGcl);
+                if (data.posCalcDetail) posCalcDetail.detail.updateDatas(data.posCalcDetail);
+                const result = ledgerTree.loadPostData(data.ledger);
+                treeOperationObj.refreshTree(ledgerSheet, result);
+                const sel = ledgerSheet.getSelections()[0];
+                ledgerSheet.setSelection(result.create[0].index, sel.col, sel.rowCount, sel.colCount);
+                SpreadJsObj.reloadRowsBackColor(ledgerSheet, [sel.row, result.create[0].index]);
+                treeOperationObj.refreshOperationValid(ledgerSheet);
+                posOperationObj.loadCurPosData();
+            }, null, true);
+        },
+        getBlockData: function() {
+            const copyBlockList = [];
+            const sheet = ledgerSpread.getActiveSheet();
+            const sel = sheet.getSelections()[0];
+            let iRow = sel.row;
+            const pid = sheet.zh_tree.nodes[iRow].ledger_pid;
+            while (iRow < sel.row + sel.rowCount) {
+                const node = sheet.zh_tree.nodes[iRow];
+                if (node.ledger_pid !== pid) {
+                    toastr.error('仅可同时选中同层节点');
+                    return;
+                }
+                const posterity = sheet.zh_tree.getPosterity(node);
+                iRow += posterity.length + 1;
+                posterity.unshift(node);
+                copyBlockList.push(sheet.zh_tree.getDefaultData(posterity));
+            }
+            for (const cbl of copyBlockList) {
+                for (const b of cbl) {
+                    const posRange = pos.getLedgerPos(b.id);
+                    if (posRange && posRange.length > 0) b.pos = posRange;
+                    if (b.calc_template) {
+                        const template = posCalcTemplate.find(x => { return x.id === b.calc_template; });
+                        b.calc_template_str = template ? template.name : '';
+                        if (b.pos) {
+                            for (const p of b.pos) {
+                                const detailRange = posCalcDetail.detail.getPartData(p.id);
+                                p.calcDetail = detailRange;
+                            }
+                        }
+                    }
+                    const gclRange = ancGcl.getPartData(b.id);
+                    if (gclRange && gclRange.length > 0) b.ancGcl = gclRange;
+                }
+            }
+            return copyBlockList;
+        }
     };
+    const ledgerTemplate = $.ledger_template({
+        selector: '#ledger-template',
+        id: 'ledger-template',
+        applyTemplate: readOnly ? null : treeOperationObj.applyTemplate,
+    });
+    $.subMenu({
+        menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
+        toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
+        key: 'menu.1.0.0',
+        miniHint: '#sub-mini-hint', hintKey: 'menu.hint.1.0.1',
+        callback: function (info) {
+            if (info.mini) {
+                $('.panel-title').addClass('fluid');
+                $('#sub-menu').removeClass('panel-sidebar');
+            } else {
+                $('.panel-title').removeClass('fluid');
+                $('#sub-menu').addClass('panel-sidebar');
+            }
+            autoFlashHeight();
+            ledgerSpread.refresh();
+            if (posSpread) posSpread.refresh();
+            if (ancGclSpread) ancGclSpread.refresh();
+            if (posCalcDetail) posCalcDetail.refresh();
+            if (stdXmj) stdXmj.spread.refresh();
+            if (stdGcl) stdGcl.spread.refresh();
+            if (dealBills) dealBills.spread.refresh();
+            if (searchLedger) searchLedger.spread.refresh();
+            if (errorList) errorList.spread.refresh();
+            if (checkList) checkList.spread.refresh();
+            if (gclGather) gclGather.spread.refresh();
+            if (ledgerTemplate) ledgerTemplate.spread.refresh();
+        }
+    });
+
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
     ledgerTreeCol.initSpreadSetting(ledgerSpreadSetting);
@@ -1544,7 +1610,7 @@ $(document).ready(function() {
     sjsSettingObj.setNodeTypeCol(ledgerSpreadSetting.cols, [{ field: 'node_type' }]);
     sjsSettingObj.setCalcTemplateCol(ledgerSpreadSetting.cols, [{ field: 'calc_template' }],
         (typeof posCalcTemplate === 'undefined') ? [] : posCalcTemplate.map(x => { return { value: x.id, text: x.name }; }),
-        `/sp/${spid}/template/posCalc`);
+        `/template/posCalc`);
     sjsSettingObj.setFromCol(posSpreadSetting.cols, [{field: 'from'}]);
     sjsSettingObj.setFromCol(ledgerSpreadSetting.cols, [{ field: 'from'}]);
     SpreadJsObj.initSheet(ledgerSpread.getActiveSheet(), ledgerSpreadSetting);
@@ -1894,6 +1960,27 @@ $(document).ready(function() {
         };
         billsContextMenuOptions.items.sprBatch = '----';
     }
+    billsContextMenuOptions.items.addTemplate = {
+        name: '保存为台账模板',
+        icon: 'fa-file-archive-o',
+        callback: function (key, opt) {
+            ledgerTemplate.addTemplate(SpreadJsObj.getSelectObject(ledgerSheet), treeOperationObj.getBlockData());
+        },
+        visible: function (key, opt) {
+            const sheet = ledgerSpread.getActiveSheet();
+            const selection = sheet.getSelections();
+            const row = selection[0].row;
+            const select = ledgerTree.nodes[row];
+            return select;
+        },
+        disabled: function (key, opt) {
+            const sheet = ledgerSpread.getActiveSheet();
+            const selection = sheet.getSelections();
+            const row = selection[0].row;
+            const select = ledgerTree.nodes[row];
+            return select && select.level <= 1;
+        }
+    };
     billsContextMenuOptions.items.copyBlock = {
         name: '复制整块',
         icon: 'fa-files-o',
@@ -1903,40 +1990,7 @@ $(document).ready(function() {
                 sheetName:ledgerSpread.getActiveSheet().name()
             });*/
             treeOperationObj.block = [];
-            const copyBlockList = [];
-            const sheet = ledgerSpread.getActiveSheet();
-            const sel = sheet.getSelections()[0];
-            let iRow = sel.row;
-            const pid = sheet.zh_tree.nodes[iRow].ledger_pid;
-            while (iRow < sel.row + sel.rowCount) {
-                const node = sheet.zh_tree.nodes[iRow];
-                if (node.ledger_pid !== pid) {
-                    toastr.error('仅可同时选中同层节点');
-                    return;
-                }
-                const posterity = sheet.zh_tree.getPosterity(node);
-                iRow += posterity.length + 1;
-                posterity.unshift(node);
-                copyBlockList.push(sheet.zh_tree.getDefaultData(posterity));
-            }
-            for (const cbl of copyBlockList) {
-                for (const b of cbl) {
-                    const posRange = pos.getLedgerPos(b.id);
-                    if (posRange && posRange.length > 0) b.pos = posRange;
-                    if (b.calc_template) {
-                        const template = posCalcTemplate.find(x => { return x.id === b.calc_template; });
-                        b.calc_template_str = template ? template.name : '';
-                        if (b.pos) {
-                            for (const p of b.pos) {
-                                const detailRange = posCalcDetail.detail.getPartData(p.id);
-                                p.calcDetail = detailRange;
-                            }
-                        }
-                    }
-                    const gclRange = ancGcl.getPartData(b.id);
-                    if (gclRange && gclRange.length > 0) b.ancGcl = gclRange;
-                }
-            }
+            const copyBlockList = treeOperationObj.getBlockData();
             setLocalCache(copyBlockTag, JSON.stringify({block: copyBlockList}));
         },
         visible: function (key, opt) {
@@ -3337,6 +3391,8 @@ $(document).ready(function() {
         treeOperationObj.loadExprToInput(ledgerSpread.getActiveSheet());
 
         checkList.loadHisCheckData();
+
+        ledgerTemplate.loadDatas(data.ledgerTemplates);
     }, null, true);
 
     $.divResizer({
@@ -3353,6 +3409,7 @@ $(document).ready(function() {
             if (errorList) errorList.spread.refresh();
             if (checkList) checkList.spread.refresh();
             if (gclGather) gclGather.spread.refresh();
+            if (ledgerTemplate) ledgerTemplate.spread.refresh();
         }
     });
     const stdLibCellDoubleClick = function (updateData, stdNode) {
@@ -3582,6 +3639,8 @@ $(document).ready(function() {
               getAllList();
             } else if (tab.attr('content') === '#gcl-gather') {
                 gclGather.spread.refresh();
+            } else if (tab.attr('content') === '#ledger-template') {
+                ledgerTemplate.spread.refresh();
             }
         } else { // 收起工具栏
             tab.removeClass('active');

+ 4 - 1
app/public/js/pos_calc_tmpl.js

@@ -255,7 +255,10 @@ $(document).ready(() => {
         }
         save(){
             const self = this;
-            postData('save', { update: { id: this.template.id, col_set: this.colSetData } }, function(result) {
+            const update = { id: this.template.id, col_set: this.getCurrentColSet() };
+            if (!update.col_set) return;
+
+            postData('save', { update }, function(result) {
                 self.template.col_set = result.update.col_set;
             });
         }

+ 137 - 0
app/public/js/shares/ledger_template.js

@@ -0,0 +1,137 @@
+'use strict';
+(function($){
+    $.ledger_template = function (setting) {
+        if (!setting.selector) return;
+        if (!setting.searchRangeStr) setting.searchRangeStr = '名称/来源';
+        const obj = $(setting.selector);
+        const spreadId = setting.id + '-spread';
+        obj.html(
+            '                        <div class="sjs-bar">\n' +
+            '                            <div class="input-group input-group-sm pb-1">\n' +
+            '                                <div class="input-group-prepend">\n' +
+            '                                </div>' +
+            '                                <input id="temp-keyword" type="text" class="form-control" autocomplete="off" placeholder="可查找 ' + setting.searchRangeStr + '" aria-label="Recipient\'s username" aria-describedby="button-addon2">\n' +
+            '                                <div class="input-group-append">\n' +
+            '                                    <button class="btn btn-outline-secondary" type="button" id="temp-search">搜索</button>\n' +
+            '                                </div>\n' +
+            '                            </div>\n' +
+            '                        </div>\n' +
+            '                        <div id="' + spreadId + '" class="sjs-sh">\n' +
+            '                        </div>'
+        );
+        autoFlashHeight();
+        const tempSpread = SpreadJsObj.createNewSpread($('#' + spreadId)[0]);
+        const tempSheet = tempSpread.getActiveSheet();
+        SpreadJsObj.initSheet(tempSheet, {
+            cols: [
+                {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 145, formatter: '@'},
+                {title: '创建人', colSpan: '1', rowSpan: '1', field: 'user_name', hAlign: 1, width: 70, formatter: '@', readOnly: true},
+                {title: '创建时间', colSpan: '1', rowSpan: '1', field: 'create_time', hAlign: 1, width: 80, formatter: '@', getValue: function(data) { return moment(data.create_time).format('YYYY-HH-DD')}, readOnly: true},
+                {title: '来源', colSpan: '1', rowSpan: '1', field: 'source', hAlign: 0, width: 200, formatter: '@', readOnly: true},
+            ],
+            emptyRows: 0,
+            headRows: 1,
+            headRowHeight: [32],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+        });
+        let templateList = [];
+        $.contextMenu({
+            selector: `#${spreadId}`,
+            build: function ($trigger, e) {
+                const target = SpreadJsObj.safeRightClickSelection($trigger, e, tempSpread);
+                return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+            },
+            items: {
+                del: {
+                    name: '删除',
+                    icon: 'fa-remove',
+                    callback: function() {
+                        const node = SpreadJsObj.getSelectObject(tempSheet);
+                        delTemplate(node);
+                    },
+                    disabled: function() {
+                        const node = SpreadJsObj.getSelectObject(tempSheet);
+                        return node.create_user_id === userID;
+                    },
+                },
+                apply: {
+                    name: '应用',
+                    icon: 'fa-reply',
+                    callback: function() {
+                        const node = SpreadJsObj.getSelectObject(tempSheet);
+                        if (setting.applyTemplate) setting.applyTemplate(node);
+                    },
+                    visible: function() {
+                        return !!setting.applyTemplate;
+                    }
+                }
+            }
+        });
+        $('#temp-search').click(function() {
+            searchTemplate($('#temp-keyword').val());
+        });
+        $('#temp-keyword').bind('keydown', function(e){
+            if (e.keyCode == 13) searchTemplate($('#temp-keyword').val());
+        });
+        tempSpread.bind(spreadNS.Events.EditStarting, function(e, info) {
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            info.cancel = node.create_user_id !== userID;
+        });
+        tempSpread.bind(spreadNS.Events.EditEnded, function(e, info) {
+            const col = info.sheet.zh_setting.cols[info.col];
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            if (!node) return;
+
+            let data;
+            if (col.field === 'name') {
+                data = { id: node.id, name: info.editingText };
+            }
+            if (!data) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+                return;
+            }
+            postData('/template/ledger/save', { update: data }, function(result) {
+                node.name = result.update.name;
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+            });
+        });
+
+        const searchTemplate = function(keyword) {
+            if (!keyword) {
+                templateList.forEach(t => { t.visible = true; });
+            } else {
+                templateList.forEach(t => {
+                    t.visible = t.name.indexOf(keyword) >= 0 || t.source.indexOf(keyword) >= 0;
+                });
+            }
+            SpreadJsObj.refreshTreeRowVisible(tempSheet);
+        };
+        const loadDatas = function (datas) {
+            templateList = datas;
+            SpreadJsObj.loadSheetData(tempSheet, SpreadJsObj.DataType.Data, templateList);
+        };
+        const addTemplate = function(node, blockData) {
+            const source = [];
+            if (node.code) source.push(node.code);
+            if (node.b_code) source.push(node.b_code);
+            if (node.name) source.push(node.name);
+            const data = { name: node.name || node.code || node.b_code, source: source.join('/'), content: blockData };
+            postData('/template/ledger/save', { add: data }, function(result) {
+                templateList.push(result.add);
+                tempSheet.addRows(templateList.length - 1, 1);
+                SpreadJsObj.reLoadRowData(tempSheet, templateList.length - 1, 1);
+            })
+        };
+        const delTemplate = function(template) {
+            postData('/template/ledger/save', { del: template.id }, function(result) {
+                const index = templateList.findIndex(t => { return t.id === template.id; });
+                templateList.splice(index, 1);
+                tempSheet.deleteRows(index, 1);
+            });
+        };
+
+        return { spread: tempSpread, loadDatas, addTemplate }
+    };
+})(jQuery);

+ 10 - 9
app/router.js

@@ -622,15 +622,16 @@ module.exports = app => {
     app.post('/tender/:id/ass/load', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.loadAss');
     app.post('/tender/:id/ass/save', sessionAuth, tenderCheck, subProjectCheck, 'tenderController.saveAss');
 
-    app.get('/sp/:id/template/posCalc', sessionAuth, subProjectCheck, 'templateController.posCalc');
-    app.get('/sp/:id/template/cost', sessionAuth, subProjectCheck, 'templateController.cost');
-    app.post('/sp/:id/template/save', sessionAuth, subProjectCheck, 'templateController.saveTemplate');
-    app.post('/sp/:id/template/load', sessionAuth, subProjectCheck, 'templateController.load');
-    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('/template/posCalc', sessionAuth, 'templateController.posCalc');
+    app.get('/template/cost', sessionAuth, 'templateController.cost');
+    app.post('/template/save', sessionAuth, 'templateController.saveTemplate');
+    app.post('/template/load', sessionAuth, 'templateController.load');
+    app.post('/template/preview', sessionAuth, 'templateController.preview');
+    app.get('/template/reCalc', sessionAuth, 'templateController.reCalcTemplate');
+    app.post('/template/reCalc', sessionAuth, 'templateController.reCalcTemplate');
+    app.get('/template/ctd', sessionAuth, 'templateController.exportTemplate');
+    app.post('/template/ctd/load', sessionAuth, 'templateController.importTemplate');
+    app.post('/template/ledger/save', sessionAuth, 'templateController.saveLedgerTemplate');
     // 台账管理相关
     app.get('/tender/:id/ledger', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, ledgerAuditCheck, 'ledgerController.explode');
     app.post('/tender/:id/ledger/load', sessionAuth, tenderCheck, subProjectCheck, uncheckTenderCheck, 'ledgerController.loadExplodeData');

+ 9 - 8
app/service/calc_tmpl.js

@@ -109,10 +109,10 @@ module.exports = app => {
             this.TemplateRela = { posCalc: PosCalc, cost: Cost };
         }
 
-        async getAllTemplate(spid, type, sort = 'asc') {
+        async getAllTemplate(pid, type, sort = 'asc') {
             return this.getAllDataByCondition({
-                columns: ['id', 'spid', 'name', 'create_user_id'],
-                where: { spid, type },
+                columns: ['id', 'pid', 'name', 'create_user_id', 'is_locked'],
+                where: { pid, type },
                 orders: [['create_time', sort]]
             });
         }
@@ -135,9 +135,9 @@ module.exports = app => {
                 }
             }
         }
-        async getAllTemplateDetail(spid, type) {
+        async getAllTemplateDetail(pid, type) {
             const result = await this.getAllDataByCondition({
-                where: { spid, type },
+                where: { pid, type },
                 orders: [['create_time', 'desc']]
             });
             await this.analysisTemplate(result);
@@ -311,7 +311,8 @@ module.exports = app => {
         async _checkTemplateUsed_posCalc(template) {
             const templates = template instanceof Array ? template : [template];
             templates.forEach(x => { x.used = []; x.used_count = 0; });
-            const tender = await this.ctx.service.tender.getAllDataByCondition({ columns: ['id'], where: { spid: templates[0].spid }});
+            const tender = await this.ctx.service.tender.getAllDataByCondition({ columns: ['id'], where: { project_id: templates[0].pid }});
+            console.log(tender.length);
             for (const t of tender) {
                 const used = await this.ctx.service.ledgerExtra.getUsedCalcTemplate(t.id);
                 templates.forEach(x => {
@@ -343,7 +344,7 @@ module.exports = app => {
             const insertData = {
                 id: this.uuid.v4(),
                 pid: this.ctx.session.sessionProject.id,
-                spid: this.ctx.subProject.id,
+                // spid: this.ctx.subProject.id,
                 // tid: this.ctx.tender.id,
                 name: name || '新增计算模板',
                 type,
@@ -361,7 +362,7 @@ module.exports = app => {
         async checkTemplateEdit(id) {
             const template = await this.getDataById(id);
             if (!template) throw '编辑的模板不存在';
-            if (template.spid !== this.ctx.subProject.id) throw '模板不属于当前项目,请刷新后重试';
+            if (template.pid !== this.ctx.session.sessionProject.id) throw '模板不属于当前项目,请刷新后重试';
             if (template.create_user_id !== this.ctx.session.sessionUser.accountId) throw '非您创建的模板';
             if (template.is_locked) throw '模板已被锁定,不可修改';
             return template;

+ 83 - 0
app/service/ledger_template.js

@@ -0,0 +1,83 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class LedgerTemplate extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'ledger_template';
+        }
+
+        async getAllTemplate(pid) {
+            const result = await this.getAllDataByCondition({
+                columns: ['id', 'create_user_id', 'user_name', 'create_time', 'name', 'source', 'is_share'],
+                where: { pid },
+                orders: [['create_time', 'asc']],
+            });
+            return result;
+        }
+        async getTemplateData(id) {
+            const template = await this.getDataById(id);
+            if (!template) throw '模板不存在';
+            return JSON.parse(template.content);
+        }
+
+        async checkTemplateEdit(id) {
+            const template = await this.getDataById(id);
+            if (!template) throw '编辑的模板不存在';
+            if (template.pid !== this.ctx.session.sessionProject.id) throw '模板不属于当前项目,请刷新后重试';
+            if (template.create_user_id !== this.ctx.session.sessionUser.accountId) throw '您无权修改该模板';
+            return template;
+        }
+        async _addTemplate(data) {
+            const user = await this.ctx.service.projectAccount.getDataById(this.ctx.session.sessionUser.accountId);
+            const addData = {
+                id: this.uuid.v4(), pid: this.ctx.session.sessionProject.id,
+                create_user_id: user.id, user_name: user.name, user_company: user.company, user_role: user.role,
+                name: data.name || '', source: data.source || '', content: JSON.stringify(data.content),
+                is_share: data.is_share ? 1 :0,
+            };
+            const result = await this.db.insert(this.tableName, addData);
+            return addData;
+        }
+        async _delTemplate(id) {
+            const template = await this.checkTemplateEdit(id);
+            await this.deleteById(id);
+            return id;
+        }
+        async _updateTemplate(data) {
+            const template = await this.checkTemplateEdit(data.id);
+
+            const updateData = { id: template.id };
+            if (data.name !== undefined) updateData.name = data.name || '';
+            if (data.is_share !== undefined) updateData.is_share = data.is_share ? 1 : 0;
+
+            const result = await this.db.update(this.tableName, updateData);
+            if (result.affectedRows === 1) return updateData;
+        }
+
+        async saveTemplate(data) {
+            const result = {};
+            if (data.add) result.add = await this._addTemplate(data.add);
+            if (data.del) result.del = await this._delTemplate(data.del);
+            if (data.update) result.update = await this._updateTemplate(data.update);
+            return result;
+        }
+    }
+
+    return LedgerTemplate;
+};

+ 5 - 0
app/view/ledger/explode.ejs

@@ -219,6 +219,8 @@
                     </div>
                     <div id="bills-tag" class="tab-pane tab-select-show">
                     </div>
+                    <div id="ledger-template" class="tab-pane tab-select-show">
+                    </div>
                     <div id="error-list" class="tab-pane tab-select-show">
                     </div>
                     <div id="check-list" class="tab-pane tab-select-show">
@@ -363,6 +365,9 @@
                     <a class="nav-link" content="#bills-tag" href="javascript: void(0);">书签</a>
                 </li>
                 <li class="nav-item">
+                    <a class="nav-link" content="#ledger-template" href="javascript: void(0);">台账模板</a>
+                </li>
+                <li class="nav-item">
                     <a class="nav-link" content="#fujian" href="javascript: void(0);">附件</a>
                 </li>
                 <li class="nav-item">

+ 3 - 5
app/view/template/sub_menu.ejs

@@ -5,14 +5,12 @@
     <div class="scrollbar-auto">
         <div class="nav-box">
             <ul class="nav-list list-unstyled">
-                <% if (ctx.subProject.page_show.posCalcDetail) { %>
                 <li <% if (ctx.url.indexOf('posCalc') >= 0) { %>class="active"<% } %>>
-                    <a href="/sp/<%- ctx.subProject.id %>/template/posCalc"><span>设计量明细</span></a>
+                    <a href="/template/posCalc"><span>设计量明细</span></a>
                 </li>
-                <% } %>
-                <% if (ctx.subProject.page_show.cost) { %>
+                <% if (ctx.session.sessionUser.is_admin) { %>
                 <li <% if (ctx.url.indexOf('cost') >= 0) { %>class="active"<% } %>>
-                    <a href="/sp/<%- ctx.subProject.id %>/template/cost"><span>成本管理</span></a>
+                    <a href="/template/cost"><span>成本管理</span></a>
                 </li>
                 <% } %>
             </ul>

+ 1 - 0
config/web.js

@@ -223,6 +223,7 @@ const JsFiles = {
                     '/public/js/shares/tree_expr_calc.js',
                     '/public/js/ledger_tree_col.js',
                     '/public/js/std_lib.js',
+                    '/public/js/shares/ledger_template.js',
                     '/public/js/ledger_check.js',
                     '/public/js/shares/tree_ass.js',
                     '/public/js/shares/tenders2tree.js',