Browse Source

概算投资,造价对比,v0.9

MaiXinRong 3 năm trước cách đây
mục cha
commit
eab0b13372

+ 7 - 0
app/base/base_bills_service.js

@@ -26,6 +26,13 @@ class BaseBillsSerivce extends TreeService {
         this.relaPosService = relaPosName;
     }
 
+    async getFinalData(mid, columns) {
+        return await this.db.select(this.departTableName(mid), {
+            where: this.getCondition({mid: mid}),
+            columns,
+        });
+    }
+
     set relaPosService(posName) {
         this._posName = posName;
     }

+ 71 - 4
app/controller/budget_controller.js

@@ -88,13 +88,79 @@ module.exports = app => {
             try {
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.budget.compare),
+                    auditConst,
                 };
+                renderData.tenderList = await ctx.service.tender.getList4Select('stage');
                 await this.layout('budget/compare.ejs', renderData, 'budget/compare_modal.ejs');
             } catch (err) {
                 ctx.log(err);
             }
         }
 
+        async compareLoad(ctx) {
+            try {
+                const gu = await ctx.service.budgetGu.getData(ctx.budget.id);
+                const gai = await ctx.service.budgetGai.getData(ctx.budget.id);
+                const yu = await ctx.service.budgetYu.getData(ctx.budget.id);
+                ctx.body = { err: 0, msg: '', data: { gu, gai, yu } }
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '获取数据错误');
+            }
+        }
+        async compareFinal(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const final = [], helper = this.ctx.helper;
+                for (const id of data.id) {
+                    const bills = await ctx.service.ledger.getFinalData(id, ['id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
+                        'code', 'b_code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'total_price']);
+                    // 使用完成数据对比
+                    // const stage = ctx.service.stage.getLastestCompleteStage(id);
+                    // const finalBills = ctx.service.stageBillsFinal.getFinalData(id, stage.id);
+                    // ctx.helper.assignRelaData(bills, [
+                    //     { data: finalBills, fields: ['contract_tp', 'qc_tp', 'used'], prefix: 'end_', relaId: 'lid' },
+                    // ]);
+
+                    // 使用最新一期对比
+                    const stage = await ctx.service.stage.getLastestStage(id);
+                    if (stage.status === auditConst.stage.status.checked) {
+                        const finalBills = await ctx.service.stageBillsFinal.getFinalData({id}, stage.order);
+                        ctx.helper.assignRelaData(bills, [
+                            { data: finalBills, fields: ['contract_tp', 'qc_tp'], prefix: 'end_', relaId: 'lid' },
+                        ]);
+                        bills.forEach(b => {
+                            b.end_gather_tp = helper.add(b.end_qc_tp, b.end_contract_tp);
+                            delete b.end_contract_tp;
+                            delete b.end_qc_tp;
+                        });
+                    } else {
+                        await ctx.service.stage.doCheckStage(stage);
+                        const curBills = stage.readOnly
+                            ? await ctx.service.stageBills.getAuditorStageData2(id, stage.id, stage.curTimes, stage.curOrder)
+                            : await ctx.service.stageBills.getLastestStageData2(id, stage.id);
+                        const preBills = stage.order > 1 ? await ctx.service.stageBillsFinal.getFinalData({id}, stage.order - 1) : [];
+                        ctx.helper.assignRelaData(bills, [
+                            { data: curBills, fields: ['contract_tp', 'qc_tp'], prefix: '', relaId: 'lid' },
+                            { data: preBills, fields: ['contract_tp', 'qc_tp'], prefix: 'pre_', relaId: 'lid' },
+                        ]);
+                        bills.forEach(b => {
+                            b.end_gather_tp = helper.sum([b.qc_tp, b.contract_tp, b.pre_qc_tp, b.pre_contract_tp]);
+                            delete b.contract_tp;
+                            delete b.qc_tp;
+                            delete b.pre_contract_tp;
+                            delete b.pre_qc_tp;
+                        });
+                    }
+                    final.push(bills);
+                }
+                ctx.body = { err: 0, msg: '', data: final }
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '获取决算数据错误');
+            }
+        }
+
         _getSpreadSetting(type) {
             const spreadSetting = {
                 cols: [
@@ -134,9 +200,10 @@ module.exports = app => {
                 default: return null;
             }
         }
-        _getNeedGcl() {
+        async _getNeedGcl() {
             if (!this.ctx.params.btype) throw '参数错误';
-            return this.ctx.params.btype === 'yu';
+            const funRela = this.ctx.service.project.getFunRela(this.ctx.session.sessionProject.id);
+            return this.ctx.params.btype === 'yu' && !!funRela.needGcl;
         }
 
         async detail(ctx) {
@@ -144,7 +211,7 @@ module.exports = app => {
                 const renderData = {
                     spreadSetting: this._getSpreadSetting(ctx.params.btype),
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.budget.detail),
-                    needGcl: this._getNeedGcl(),
+                    needGcl: await this._getNeedGcl(),
                 };
                 [renderData.stdBills, renderData.stdChapters] = await ctx.service.budgetStd.getStdList(ctx.budget.std_id, ctx.params.btype);
                 await this.layout('budget/detail.ejs', renderData, 'budget/detail_modal.ejs');
@@ -261,7 +328,7 @@ module.exports = app => {
         async detailUploadExcel(ctx) {
             try {
                 const relaService = this._getRelaService(ctx.params.btype);
-                const needGcl = this._getNeedGcl();
+                const needGcl = await this._getNeedGcl();
                 const ueType = ctx.params.ueType;
                 const compressData = ctx.request.body.data;
                 const data = JSON.parse(LzString.decompressFromUTF16(compressData));

+ 118 - 9
app/public/js/budget_compare.js

@@ -18,20 +18,17 @@ $(document).ready(() => {
             {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 150, formatter: '@', cellType: 'tree'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'},
-            {title: '投资估算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gu_qty', hAlign: 2, width: 80, type: 'Number'},
+            {title: '投资估算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gu_dgn_qty', hAlign: 2, width: 80, type: 'Number'},
             {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gu_dgn_price', hAlign: 2, width: 80, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gu_tp', hAlign: 2, width: 80, type: 'Number'},
-            {title: '初步概算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gai_qty', hAlign: 2, width: 80, type: 'Number'},
-            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gu_dgn_price', hAlign: 2, width: 80, type: 'Number'},
+            {title: '初步概算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'gai_dgn_qty', hAlign: 2, width: 80, type: 'Number'},
+            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gai_dgn_price', hAlign: 2, width: 80, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gai_tp', hAlign: 2, width: 80, type: 'Number'},
-            {title: '施工图预算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'yu_qty', hAlign: 2, width: 80, type: 'Number'},
-            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gu_dgn_price', hAlign: 2, width: 80, type: 'Number'},
+            {title: '施工图预算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'yu_dgn_qty', hAlign: 2, width: 80, type: 'Number'},
+            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'yu_dgn_price', hAlign: 2, width: 80, type: 'Number'},
             {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'yu_tp', hAlign: 2, width: 80, type: 'Number'},
-            {title: '决算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_qty', hAlign: 2, width: 80, type: 'Number'},
-            {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'gu_dgn_price', hAlign: 2, width: 80, type: 'Number'},
-            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'final_tp', hAlign: 2, width: 80, type: 'Number'},
         ],
-        emptyRows: 3,
+        emptyRows: 0,
         headRows: 2,
         headRowHeight: [25, 25],
         defaultRowHeight: 21,
@@ -39,8 +36,63 @@ $(document).ready(() => {
         font: '12px 微软雅黑',
         readOnly: true,
     };
+    sjsSettingObj.setFxTreeStyle(spreadSetting, sjsSettingObj.FxTreeStyle.jz);
     SpreadJsObj.initSheet(compareSheet, spreadSetting);
 
+    const compareTree = createNewPathTree('final', {
+        id: 'id',
+        pid: 'pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+    });
+
+    postData(window.location.pathname + '/load', {}, function (result) {
+        const setting = { id: 'tree_id', pid: 'tree_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price'] };
+        const guTree = createNewPathTree('ledger', setting);
+        guTree.loadDatas(result.gu);
+        treeCalc.calculateAll(guTree);
+        compareTree.loadTree(guTree, function (cur, source) {
+            cur.base = true;
+            cur.gu_dgn_qty1 = ZhCalc.add(cur.gu_dgn_qty1, source.dgn_qty1);
+            cur.gu_dgn_qty2 = ZhCalc.add(cur.gu_dgn_qty2, source.dgn_qty2);
+            cur.gu_tp = ZhCalc.add(cur.gu_tp, source.total_price);
+        });
+        const gaiTree = createNewPathTree('ledger', setting);
+        gaiTree.loadDatas(result.gai);
+        treeCalc.calculateAll(gaiTree);
+        compareTree.loadTree(gaiTree, function (cur, source) {
+            cur.base = true;
+            cur.gai_dgn_qty1 = ZhCalc.add(cur.gai_dgn_qty1, source.dgn_qty1);
+            cur.gai_dgn_qty2 = ZhCalc.add(cur.gai_dgn_qty2, source.dgn_qty2);
+            cur.gai_tp = ZhCalc.add(cur.gai_tp, source.total_price);
+        });
+        const yuTree = createNewPathTree('ledger', setting);
+        yuTree.loadDatas(result.yu);
+        treeCalc.calculateAll(yuTree);
+        compareTree.loadTree(yuTree, function (cur, source) {
+            cur.base = true;
+            cur.yu_dgn_qty1 = ZhCalc.add(cur.yu_dgn_qty1, source.dgn_qty1);
+            cur.yu_dgn_qty2 = ZhCalc.add(cur.yu_dgn_qty2, source.dgn_qty2);
+            cur.yu_tp = ZhCalc.add(cur.yu_tp, source.total_price);
+        });
+        compareTree.afterLoad(node => {
+            node.gu_dgn_price = ZhCalc.div(node.gu_tp, node.gu_dgn_qty1, 2);
+            node.gu_dgn_qty = node.gu_dgn_qty1
+                ? (node.gu_dgn_qty2 ? node.gu_dgn_qty1 + '/' + node.gu_dgn_qty2 : node.gu_dgn_qty1)
+                : (node.gu_dgn_qty2 ? '/' + node.gu_dgn_qty2 : '');
+            node.gai_dgn_price = ZhCalc.div(node.gai_tp, node.gai_dgn_qty1, 2);
+            node.gai_dgn_qty = node.gai_dgn_qty1
+                ? (node.gai_dgn_qty2 ? node.gai_dgn_qty1 + '/' + node.gai_dgn_qty2 : node.gai_dgn_qty1)
+                : (node.gai_dgn_qty2 ? '/' + node.gai_dgn_qty2 : '');
+            node.yu_dgn_price = ZhCalc.div(node.yu_tp, node.gu_dgn_qty1, 2);
+            node.yu_dgn_qty = node.yu_dgn_qty1
+                ? (node.yu_dgn_qty2 ? node.yu_dgn_qty1 + '/' + node.yu_dgn_qty2 : node.yu_dgn_qty1)
+                : (node.yu_dgn_qty2 ? '/' + node.yu_dgn_qty2 : '');
+        });
+        SpreadJsObj.loadSheetData(compareSheet, SpreadJsObj.DataType.Tree, compareTree);
+    });
+
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
         toMenu: '#to-menu', toMiniMenu: '#to-mini-menu',
@@ -58,4 +110,61 @@ $(document).ready(() => {
             compareSpread.refresh();
         }
     });
+
+    $('#select-final-ok').click(() => {
+        const rela = [];
+        const select = $('[name=sf-tender]:checked');
+        for (const s of select) {
+            rela.push(parseInt(s.getAttribute('tid')));
+        }
+        if (rela.length === 0) return;
+        postData(window.location.pathname + '/final', {id: rela}, function(result) {
+            if (spreadSetting.cols.length < 13) {
+                spreadSetting.cols.push(...[
+                    {title: '台账|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'dgn_qty', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'dgn_price', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '决算|数量1/数量2', colSpan: '3|1', rowSpan: '1|1', field: 'final_dgn_qty', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '|经济指标', colSpan: '|1', rowSpan: '|1', field: 'final_dgn_price', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'final_tp', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '增幅%|数量1/数量2', colSpan: '2|1', rowSpan: '1|1', field: 'grow_dgn_qty', hAlign: 2, width: 80, type: 'Number'},
+                    {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'grow_tp', hAlign: 2, width: 80, type: 'Number'},
+                ]);
+            }
+            const setting = { id: 'ledger_id', pid: 'ledger_pid', order: 'order', level: 'level', rootId: -1, calcFields: ['total_price', 'end_gather_tp'] };
+            compareTree.clearFinal();
+            for (const r of result) {
+                const tree = createNewPathTree('ledger', setting);
+                tree.loadDatas(r);
+                treeCalc.calculateAll(tree);
+                compareTree.loadTree(tree, function (cur, source) {
+                    cur.total_price = ZhCalc.add(cur.total_price, source.total_price);
+                    cur.dgn_qty1 = ZhCalc.add(cur.dgn_qty1, source.dgn_qty1);
+                    cur.dgn_qty2 = ZhCalc.add(cur.dgn_qty2, source.dgn_qty2);
+                    cur.final_dgn_qty1 = ZhCalc.add(cur.final_dgn_qty1, source.end_dgn_qty1);
+                    cur.final_dgn_qty2 = ZhCalc.add(cur.final_dgn_qty2, source.end_dgn_qty2);
+                    cur.final_tp = ZhCalc.add(cur.final_tp, source.end_gather_tp);
+                });
+            }
+            compareTree.afterLoad(node => {
+                node.dgn_price = ZhCalc.div(node.total_price, node.dgn_qty1, 2);
+                node.dgn_qty = node.dgn_qty1
+                    ? (node.dgn_qty2 ? node.dgn_qty1 + '/' + node.dgn_qty2 : node.dgn_qty1)
+                    : (node.dgn_qty2 ? '/' + node.dgn_qty2 : '');
+                node.final_dgn_price = ZhCalc.div(node.final_tp, node.final_dgn_qty1, 2);
+                node.final_dgn_qty = node.final_dgn_qty1
+                    ? (node.final_dgn_qty2 ? node.final_dgn_qty1 + '/' + node.final_dgn_qty2 : node.final_dgn_qty1)
+                    : (node.final_dgn_qty2 ? '/' + node.final_dgn_qty2 : '');
+                node.grow_dgn_qty1 = ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node.final_dgn_qty1, node.gai_dgn_qty1), node.gai_dgn_qty1, 4), 100);
+                node.grow_dgn_qty2 = ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node.final_dgn_qty2, node.gai_dgn_qty2), node.gai_dgn_qty2, 4), 100);
+                node.grow_dgn_qty = node.grow_dgn_qty1
+                    ? (node.grow_dgn_qty2 ? node.grow_dgn_qty1 + '/' + node.grow_dgn_qty2 : node.grow_dgn_qty1)
+                    : (node.grow_dgn_qty2 ? '/' + node.grow_dgn_qty2 : '');
+                node.grow_tp = ZhCalc.mul(ZhCalc.div(ZhCalc.sub(node.final_tp, node.gai_tp), node.gai_tp, 4), 100);
+            });
+            SpreadJsObj.reLoadSheetHeader(compareSheet);
+            SpreadJsObj.reLoadSheetData(compareSheet);
+            $('#select-final').modal('hide');
+        });
+    })
 });

+ 0 - 1
app/public/js/budget_list.js

@@ -55,7 +55,6 @@ const relaTender = function () {
     for (const s of select) {
         rela.push(s.getAttribute('tid'));
     }
-    console.log(rela);
     postData('/budget/save', { id: curBudget.id, rela_tender: rela.join(',') }, function () {
         $(`[bid=${curBudget.id}]`)[0].setAttribute('brela', rela.join(','));
         $('#select-rela').modal('hide');

+ 91 - 1
app/public/js/path_tree.js

@@ -1699,7 +1699,7 @@ const createNewPathTree = function (type, setting) {
                 for (const c of node.children) {
                     addSortNode(c);
                 }
-            }
+            };
             this.nodes = [];
             for (const n of this.children) {
                 addSortNode(n);
@@ -1737,6 +1737,94 @@ const createNewPathTree = function (type, setting) {
         }
     }
 
+    class FinalTree extends BaseTree {
+        constructor(setting) {
+            super(setting);
+            this._newId = 1;
+        }
+        get newId() {
+            return this._newId++;
+        }
+
+        loadNode(node, parent, loadFun) {
+            if (node.b_code) return;
+            const siblings = parent ? parent.children : this.children;
+            let cur = siblings.find(function (x) {
+                return x.code === node.code && x.name === node.name;
+            });
+            if (!cur) {
+                const id = this.newId;
+                cur = {
+                    id: id,
+                    pid: parent ? parent.id : this.setting.rootId,
+                    full_path: parent ? parent.full_path + '-' + id : '' + id,
+                    level: parent ? parent.level + 1 : 1,
+                    order: siblings.length + 1,
+                    children: [],
+                    code: node.code, name: node.name, unit: node.unit,
+                };
+                siblings.push(cur);
+                this.datas.push(cur);
+            }
+            loadFun(cur, node);
+            for (const c of node.children) {
+                this.loadNode(c, cur, loadFun);
+            }
+        }
+        loadTree(tree, loadFun) {
+            for (const node of tree.children) {
+                this.loadNode(node, null, loadFun);
+            }
+        }
+
+        generateSortNodes() {
+            const self = this;
+            const addSortNode = function (node) {
+                self.nodes.push(node);
+                for (const c of node.children) {
+                    addSortNode(c);
+                }
+            };
+            this.nodes = [];
+            for (const n of this.children) {
+                addSortNode(n);
+            }
+        }
+        afterLoad(fun) {
+            for (const d of this.datas) {
+                fun && fun(d);
+                d.is_leaf = d.children.length === 0;
+                d.expanded = true;
+                d.visible = true;
+                this.items[itemsPre + d[this.setting.id]] = d;
+            }
+            this.generateSortNodes();
+        }
+        clearFinal() {
+            this.datas = this.datas.filter(x => {
+                return !!x.base;
+            });
+            this.datas.forEach(x => {
+                delete x.total_price;
+                delete x.dgn_qty1;
+                delete x.dgn_qty2;
+                delete x.dgn_qty;
+                delete x.dgn_price;
+
+                delete x.final_dgn_qty1;
+                delete x.final_dgn_qty2;
+                delete x.final_dgn_qty;
+                delete x.final_dgn_price;
+                delete x.final_tp;
+
+                delete x.grow_dgn_qty1;
+                delete x.grow_dgn_qty2;
+                delete x.grow_dgn_qty;
+                delete x.grow_tp;
+            });
+        }
+    }
+
     if (type === 'base') {
         return new BaseTree(setting);
     } else if (type === 'fx') {
@@ -1759,6 +1847,8 @@ const createNewPathTree = function (type, setting) {
         return new CompareTree(setting);
     } else if (type === 'tree-gather') {
         return new TreeGatherTree(setting);
+    } else if (type === 'final') {
+        return new FinalTree(setting);
     }
 };
 

+ 2 - 0
app/router.js

@@ -581,6 +581,8 @@ module.exports = app => {
     app.post('/budget/del', sessionAuth, 'budgetController.del');
     app.post('/budget/save', sessionAuth, 'budgetController.save');
     app.get('/budget/:id/compare', sessionAuth, budgetCheck, 'budgetController.compare');
+    app.post('/budget/:id/compare/load', sessionAuth, budgetCheck, 'budgetController.compareLoad');
+    app.post('/budget/:id/compare/final', sessionAuth, budgetCheck, 'budgetController.compareFinal');
     app.get('/budget/:id/:btype', sessionAuth, budgetCheck, 'budgetController.detail');
     app.post('/budget/:id/:btype/load', sessionAuth, budgetCheck, 'budgetController.detailLoad');
     app.post('/budget/:id/:btype/update', sessionAuth, budgetCheck, 'budgetController.detailUpdate');

+ 1 - 0
app/service/project.js

@@ -12,6 +12,7 @@ const defaultFunRela = {
     banOver: true,
     hintOver: true,
     imType: imType.zl.value,
+    needGcl: false,
 };
 const sjsRelaConst = require('../const/setting').sjsRela;
 

+ 4 - 0
app/service/tender.js

@@ -167,6 +167,7 @@ module.exports = app => {
             for (const t of tenderList) {
                 if (t.ledger_status === auditConst.ledger.status.checked) {
                     t.lastStage = await this.ctx.service.stage.getLastestStage(t.id, false);
+                    t.completeStage = await this.ctx.service.stage.getLastestCompleteStage(t.id);
                 }
             }
             switch (selectType) {
@@ -179,6 +180,9 @@ module.exports = app => {
                 case 'stage': return tenderList.filter(x => {
                     return x.ledger_status === auditConst.ledger.status.checked && !!x.lastStage;
                 });
+                case 'stage-checked': return tenderList.filter(x => {
+                    return !!x.completeStage;
+                });
                 default: return tenderList;
             }
         }

+ 1 - 1
app/view/budget/compare.ejs

@@ -21,7 +21,7 @@
                     </div>
                 </div>
                 <div class="d-inline-block ml-3">
-                    <a class="btn btn-sm btn-primary" href="javascript: void(0);" id="final-compare">决算对比</a>
+                    <a class="btn btn-sm btn-primary" href="#select-final" data-toggle="modal" data-target="#select-final">决算对比</a>
                 </div>
             </div>
         </div>

+ 35 - 0
app/view/budget/compare_modal.ejs

@@ -0,0 +1,35 @@
+<div class="modal fade show" id="select-final" data-backdrop="static">
+    <div class="modal-dialog" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择决算标段</h5>
+            </div>
+            <div class="modal-body" style="display: block; overflow: auto; height: 500px">
+                <h5>可选标段</h5>
+                <table class="table table-sm table-bordered">
+                    <thead>
+                    <tr class="text-center">
+                        <th>选择</th>
+                        <th>标段名称</th>
+                        <th>期数</th>
+                        <th>状态</th>
+                    </tr>
+                    </thead>
+                    <tbody>
+                    <% for (const t of tenderList) { %>
+                    <tr>
+                        <td class="text-center"><input type="checkbox" name="sf-tender" tid="<%- t.id %>"><td><%- t.name %></td><td>第<%- t.lastStage.order %>期</td><td><%- auditConst.stage.statusString[t.lastStage.status] %></td>
+                    </tr>
+                    <% } %>
+                    </tbody>
+                </table>
+            </div>
+            <div class="modal-footer">
+                <div>
+                    <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                    <button type="button" class="btn btn-sm btn-primary" id="select-final-ok">确定</button>
+                </div>
+            </div>
+        </div>
+    </div>
+</div>

+ 14 - 0
app/view/setting/fun.ejs

@@ -38,6 +38,19 @@
                                 <label class="form-text text-danger">切换模式,仅对未开始第一期计量的标段生效。</label>
                             </div>
                         </div>
+
+                        <div class="form-group">
+                            <label>概算投资</label>
+                            <div>
+                                <div class="form-check form-check-inline">
+                                    <input class="form-check-input" type="checkbox" id="need_gcl" name="need_gcl" <% if (funRela.needGcl) { %>checked<% } %> onchange="updateSetting();">
+                                    <label class="form-check-label" for="need_gcl">显示清单信息</label>
+                                </div>
+                            </div>
+                            <div>
+                                <label class="form-text text-danger">做施工图三级清单预算时,请进行勾选。</label>
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>
@@ -54,6 +67,7 @@
             imType: parseInt($('[name=im_type]:checked').val()),
             banOver: $('[name=ban_over]')[0].checked,
             hintOver: $('#hint_over')[0].checked,
+            needGcl: $('#need_gcl')[0].checked,
         });
     }
 </script>