Browse Source

部分结算代码

MaiXinRong 1 year ago
parent
commit
b6edd0204d

+ 65 - 13
app/controller/settle_controller.js

@@ -157,8 +157,11 @@ module.exports = app => {
                 settle: ctx.settle,
                 shenpiConst,
                 auditType: auditConst.auditType,
+                thirdParty: {
+                    gxby: ctx.session.sessionProject.gxby_status,
+                    dagl: ctx.session.sessionProject.dagl_status,
+                },
             };
-            data.tenderMenu.back.children[0].url = '/tender/' + ctx.tender.id + '/measure/stage';
             // 是否已验证手机短信
             const pa = await ctx.service.projectAccount.getDataById(ctx.session.sessionUser.accountId);
             data.authMobile = pa.auth_mobile;
@@ -183,14 +186,32 @@ module.exports = app => {
         }
 
         async index(ctx) {
+            const status = auditConst.settle.status;
+            let url = (ctx.settle.status === status.uncheck || ctx.settle.status === status.checkNo) && (ctx.session.sessionUser.is_admin || ctx.session.sessionUser.accountId === ctx.settle.user_id)? 'select' : 'ledger';
+            ctx.redirect(ctx.url + '/' + url);
+        }
+        async select(ctx) {
             try {
                 await ctx.service.settle.loadAuditViewData(ctx.settle);
                 const renderData = await this._getDefaultRenderData(ctx);
+                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.settle.select);
+
+                const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
+                renderData.whiteList = this.ctx.app.config.multipart.whitelist;
+                await this.layout('settle/select.ejs', renderData, 'settle/select_modal.ejs');
+            } catch(err) {
+                console.log(err);
+                ctx.log(err);
+                ctx.redirect('/tender/' + ctx.tender.id + '/settle');
+            }
+        }
+        async ledger(ctx) {
+            try {
+                await ctx.service.settle.loadAuditViewData(ctx.settle);
+                const renderData = await this._getDefaultRenderData(ctx);
+                renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.settle.ledger);
 
                 const projectFunInfo = await this.ctx.service.project.getFunRela(ctx.session.sessionProject.id);
-                renderData.minusNoValue = projectFunInfo.minusNoValue && ctx.tender.info.fun_rela.stage_change.minusNoValue;
-                [renderData.ledgerSpread, renderData.posSpread] = await spreadSetting.getStageSpreadSetting(ctx, ctx.tender.id,
-                    this.ctx.stage.readOnly || this.ctx.stage.revising, {minusNoValue: renderData.minusNoValue});
                 renderData.whiteList = this.ctx.app.config.multipart.whitelist;
                 await this.layout('settle/index.ejs', renderData, 'settle/modal.ejs');
             } catch(err) {
@@ -200,26 +221,57 @@ module.exports = app => {
             }
         }
 
+        async _getStageBillsData(ctx) {
+            this.ledgerColumn = [
+                'id', 'tender_id', 'ledger_id', 'ledger_pid', 'level', 'order', 'full_path', 'is_leaf',
+                'code', 'b_code', 'name', 'unit', 'unit_price',
+                'quantity', 'total_price', 'memo', 'drawing_code', 'node_type'];
+            const ledgerData = await ctx.service.ledger.getAllDataByCondition({ columns: this.ledgerColumn, where: { tender_id: ctx.tender.id } });
+            const endStageData = await ctx.service.stageBillsFinal.getAllDataByCondition({ where: { sid: ctx.settle.latestStage.id } });
+            this.ctx.helper.assignRelaData(ledgerData, [
+                { data: endStageData, fields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp', 'qc_minus_qty'], prefix: 'end_', relaId: 'lid' },
+            ]);
+            return ledgerData;
+        }
+        async _getStagePosData(ctx) {
+            this.posColumn = ['id', 'tid', 'lid', 'name', 'position', 'porder', 'quantity', 'add_stage_order', 'drawing_code'];
+            const posData = await ctx.service.pos.getAllDataByCondition({ columns: this.posColumn, where: { tid: ctx.tender.id } });
+            const endStageData = await ctx.service.stagePosFinal.getAllDataByCondition({ where: { sid: ctx.settle.latestStage.id } });
+            this.ctx.helper.assignRelaData(posData, [
+                { data: endStageData, fields: ['contract_qty', 'qc_qty', 'qc_minus_qty'], prefix: 'end_', relaId: 'pid' },
+            ]);
+            return posData;
+        }
         async _loadSettleDataByKey(ctx, key, hpack) {
             switch (key) {
                 case 'stageBills':
-                    if (!ctx.settle.latestStage) ctx.settle.latestStage = await this.ctx.service.stage.getLastestCompleteStage(ctx.tender.id);
+                    if (!ctx.settle.latestStage)
+                        ctx.settle.latestStage = ctx.settle.final_sid
+                            ? await this.ctx.service.stage.getDataById(ctx.settle.final_sid)
+                            : await this.ctx.service.stage.getLastestCompleteStage(ctx.tender.id);
                     const bills = await this._getStageBillsData(ctx);
                     return hpack ? [this.ctx.helper.hpackArr(bills), 'stageBills'] : [bills, ''];
                 case 'stagePos':
-                    if (!ctx.settle.latestStage) ctx.settle.latestStage = await this.ctx.service.stage.getLastestCompleteStage(ctx.tender.id);
+                    if (!ctx.settle.latestStage)
+                        ctx.settle.latestStage = ctx.settle.final_sid
+                            ? await this.ctx.service.stage.getDataById(ctx.settle.final_sid)
+                            : await this.ctx.service.stage.getLastestCompleteStage(ctx.tender.id);
                     const pos = await this._getStagePosData(ctx);
                     return hpack ? [this.ctx.helper.hpackArr(pos), 'stagePos'] : [pos, ''];
                 case 'settleBills':
-                    return ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.settle.id } });
+                    const settleBills = await ctx.service.settleBills.getAllDataByCondition({ where: { settle_id: ctx.settle.id } });
+                    return hpack ? [this.ctx.helper.hpackArr(settleBills), 'settleBills'] : [settleBills, ''];
                 case 'settlePos':
-                    return ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: ctx.settle.id } });
+                    const settlePos = await ctx.service.settlePos.getAllDataByCondition({ where: { settle_id: ctx.settle.id } });
+                    return hpack ? [this.ctx.helper.hpackArr(settlePos), 'settlePos'] : [settlePos, ''];
                 case 'settleSelect':
-                    return ctx.service.settleSelect.getAllDataByCondition({ where: { settle_id: ctx.settle.id } });
-                    break;
+                    const settleSelect = []; // await ctx.service.settleSelect.getAllDataByCondition({ where: { settle_id: ctx.settle.id } });
+                    return [settleSelect, ''];
                 case 'tag':
-                    return await ctx.service.ledgerTag.getDatas(ctx.tender.id, ctx.stage.id, ctx.settle.id);
-                    break;
+                    const tag = await ctx.service.ledgerTag.getDatas(ctx.tender.id, -1, ctx.settle.id);
+                    return [tag, ''];
+                default:
+                    return [null, ''];
             }
         }
 
@@ -230,7 +282,7 @@ module.exports = app => {
                 const responseData = { err: 0, msg: '', data: {}, hpack: [] };
                 const hpack = true;
                 for (const f of filter) {
-                    const [relaData, hpackKey] = await this._loadSettleDataByKey(f, hpack);
+                    const [relaData, hpackKey] = await this._loadSettleDataByKey(ctx, f, hpack);
                     responseData.data[f] = relaData;
                     if (hpackKey) responseData.hpack.push(hpackKey);
                 }

+ 219 - 0
app/public/js/settle_ledger.js

@@ -0,0 +1,219 @@
+function getGxbyText(data) {
+    const def = thirdParty.gxby.find(function (x) {
+        return x.value === data.gxby_status;
+    });
+    return def ? def.name : '';
+}
+function getDaglText(data) {
+    const def = thirdParty.dagl.find(function (x) {
+        return x.value === data.dagl_status;
+    });
+    return def ? def.name : '';
+}
+
+const ckBillsSpread = window.location.pathname + '-billsSelect';
+
+$(document).ready(() => {
+    autoFlashHeight();
+
+    let searchLedger;
+    const settleTreeSetting = {
+        id: 'ledger_id',
+        pid: 'ledger_pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        keys: ['id', 'tender_id', 'ledger_id'],
+        stageId: 'id',
+        autoExpand: 3,
+        markExpandKey: 'settle-select-expand',
+        markExpandSubKey: window.location.pathname.split('/')[2],
+        calcFields: ['total_price', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp'],
+        calcFun: function(node) {
+            if (!node.children || node.children.length === 0) {
+                node.end_gather_qty = ZhCalc.add(node.end_contract_qty, node.end_qc_qty);
+                if (node.end_contract_qty) {
+                    node.end_correct_tp = ZhCalc.add(node.end_qc_tp, ZhCalc.mul(node.end_contract_qty, node.unit_price, tenderInfo.decimal.tp));
+                } else {
+                    node.end_correct_tp = node.end_gather_tp;
+                }
+            }
+            node.end_gather_tp = ZhCalc.add(node.end_contract_tp, node.end_qc_tp);
+            node.end_gather_percent = ZhCalc.mul(ZhCalc.div(node.end_gather_tp, node.end_final_tp), 100, 2);
+            node.end_correct_percent = ZhCalc.mul(ZhCalc.div(node.end_correct_tp, node.end_final_tp), 100, 2);
+        }
+    };
+    const settleTree = createNewPathTree('stage', settleTreeSetting);
+    const settlePosSetting = {
+        id: 'id', ledgerId: 'lid',
+        calcFun: function(pos) {
+            pos.end_gather_qty = ZhCalc.add(pos.end_contract_qty, pos.end_qc_qty);
+            pos.sum = ZhCalc.add(pos.end_qc_qty, pos.quantity);
+            pos.end_gather_percent = ZhCalc.mul(ZhCalc.div(pos.end_gather_qty, pos.sum), 100, 2);
+        }
+    };
+    const settlePos = new StagePosData(settlePosSetting);
+
+    const slSpread = SpreadJsObj.createNewSpread($('#settle-bills')[0]);
+    const slSheet = slSpread.getActiveSheet();
+    slSheet.frozenColumnCount(billsSpreadSetting.cols.findIndex(x => { return x.field === 'total_price'; }) + 1);
+    slSheet.options.frozenlineColor = '#93b5e4';
+    const ratioCol = billsSpreadSetting.cols.find(x => {return x.field === 'end_final_1_percent' || x.field === 'end_correct_1_percent'});
+    if (ratioCol) ratioCol.field = tenderInfo.display.stage.correct ? 'end_correct_1_percent' : 'end_final_1_percent';
+    billsSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (!data) return defaultColor;
+        if (data.children && data.children.length > 0) return defaultColor;
+
+        if (col.field === 'gxby') {
+            const def = thirdParty.gxby.find(function (x) {
+                return x.value === data.gxby_status;
+            });
+            if (def && def.color) return def.color;
+        } else if (col.field === 'dagl') {
+            const def = thirdParty.dagl.find(function (x) {
+                return x.value === data.dagl_status;
+            });
+            if (def && def.color) return def.color;
+        }
+    };
+    sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    sjsSettingObj.set3FCols(billsSpreadSetting.cols, [
+        {field: 'gxby', getValue: getGxbyText, url_field: 'gxby_url'},
+        {field: 'dagl', getValue: getDaglText, url_field: 'dagl_url'},
+    ]);
+    SpreadJsObj.initSheet(slSheet, billsSpreadSetting);
+
+    const spSpread = SpreadJsObj.createNewSpread($('#settle-pos')[0]);
+    const spSheet = spSpread.getActiveSheet();
+    spSheet.frozenColumnCount(posSpreadSetting.cols.findIndex(x => { return x.field === 'total_price'; }) + 1);
+    spSheet.options.frozenlineColor = '#93b5e4';
+    posSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (!data) return defaultColor;
+
+        if (col.field === 'gxby') {
+            const def = thirdParty.gxby.find(function (x) {
+                return x.value === data.gxby_status;
+            });
+            if (def && def.color) return def.color;
+        } else if (col.field === 'dagl') {
+            const def = thirdParty.dagl.find(function (x) {
+                return x.value === data.dagl_status;
+            });
+            if (def && def.color) return def.color;
+        }
+    };
+    sjsSettingObj.set3FCols(posSpreadSetting.cols, [
+        {field: 'gxby', getValue: getGxbyText, url_field: 'gxby_url'},
+        {field: 'dagl', getValue: getDaglText, url_field: 'dagl_url'},
+    ]);
+    SpreadJsObj.initSheet(spSheet, posSpreadSetting);
+
+    const settleBillsObj = {
+        loadRelaData: function() {
+            SpreadJsObj.saveTopAndSelect(slSheet, ckBillsSpread);
+            SpreadJsObj.resetTopAndSelect(spSheet);
+            settlePosObj.loadCurPosData();
+        },
+        selectionChanged: function(e, info) {
+            if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
+                settleBillsObj.loadRelaData();
+            }
+        },
+        topRowChanged(e, info) {
+            SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
+        },
+    };
+    slSpread.bind(spreadNS.Events.SelectionChanged, settleBillsObj.selectionChanged);
+    slSpread.bind(spreadNS.Events.TopRowChanged, settleBillsObj.topRowChanged);
+
+    const settlePosObj = {
+        loadCurPosData: function() {
+            const billsNode = SpreadJsObj.getSelectObject(slSheet);
+            if (billsNode) {
+                spSheet.zh_setting.readOnly = readOnly;
+                const posRange = settlePos.getLedgerPos(billsNode.id) || [];
+                SpreadJsObj.loadSheetData(spSheet, SpreadJsObj.DataType.Data, posRange, readOnly);
+            } else {
+                spSheet.zh_setting.readOnly = true;
+                SpreadJsObj.loadSheetData(spSheet, SpreadJsObj.DataType.Data, [], true);
+            }
+        }
+    };
+
+    postData('load', {filter: 'settleBills;settlePos;tag'}, function(result) {
+        settleTree.loadDatas(result.settleBills);
+        treeCalc.calculateAll(settleTree);
+        settlePos.loadDatas(result.settlePos);
+        settlePos.calculateAll();
+
+        SpreadJsObj.loadSheetData(slSheet, SpreadJsObj.DataType.Tree, settleTree);
+        SpreadJsObj.loadTopAndSelect(slSpread.getActiveSheet(), ckBillsSpread);
+        settlePosObj.loadCurPosData();
+    });
+
+    // 展开收起工具栏
+    $('a', '.right-nav').bind('click', function () {
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        if (!tab.hasClass('active')) {
+            $('a', '.side-menu').removeClass('active');
+            $('.tab-content .tab-select-show').removeClass('active');
+            tab.addClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            if (tab.attr('content') === '#search' && !searchLedger) {
+                searchLedger = $.billsSearch({
+                    selector: '#search',
+                    searchSpread: slSpread,
+                    searchOver: true,
+                    searchEmpty: true,
+                    resultSpreadSetting: {
+                        cols: [
+                            {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@'},
+                            {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
+                            {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@'},
+                            {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@'},
+                            {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
+                            {title: '数量', field: 'quantity', hAlign: 2, width: 50},
+                        ],
+                        emptyRows: 0,
+                        headRows: 1,
+                        headRowHeight: [32],
+                        headColWidth: [30],
+                        defaultRowHeight: 21,
+                        headerFont: '12px 微软雅黑',
+                        font: '12px 微软雅黑',
+                        selectedBackColor: '#fffacd',
+                        readOnly: true,
+                    },
+                    afterLocated: function () {
+                        settlePosObj.loadCurPosData();
+                    },
+                });
+                searchLedger.spread.refresh();
+            }
+        } else {
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        slSpread.refresh();
+        spSpread.refresh();
+    });
+
+    $.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();
+        }
+    });
+});

+ 279 - 0
app/public/js/settle_select.js

@@ -0,0 +1,279 @@
+function getGxbyText(data) {
+    const def = thirdParty.gxby.find(function (x) {
+        return x.value === data.gxby_status;
+    });
+    return def ? def.name : '';
+}
+function getDaglText(data) {
+    const def = thirdParty.dagl.find(function (x) {
+        return x.value === data.dagl_status;
+    });
+    return def ? def.name : '';
+}
+
+const ckBillsSpread = window.location.pathname + '-billsSelect';
+$(document).ready(() => {
+    autoFlashHeight();
+
+    let searchLedger;
+    const settleTreeSetting = {
+        id: 'ledger_id',
+        pid: 'ledger_pid',
+        order: 'order',
+        level: 'level',
+        rootId: -1,
+        keys: ['id', 'tender_id', 'ledger_id'],
+        stageId: 'id',
+        autoExpand: 3,
+        markExpandKey: 'settle-select-expand',
+        markExpandSubKey: window.location.pathname.split('/')[2],
+        calcFields: ['total_price', 'end_contract_tp', 'end_qc_tp', 'end_gather_tp', 'end_correct_tp'],
+        calcFun: function(node) {
+            if (!node.children || node.children.length === 0) {
+                node.end_gather_qty = ZhCalc.add(node.end_contract_qty, node.end_qc_qty);
+                if (node.end_contract_qty) {
+                    node.end_correct_tp = ZhCalc.add(node.end_qc_tp, ZhCalc.mul(node.end_contract_qty, node.unit_price, tenderInfo.decimal.tp));
+                } else {
+                    node.end_correct_tp = node.end_gather_tp;
+                }
+            }
+            node.end_gather_tp = ZhCalc.add(node.end_contract_tp, node.end_qc_tp);
+            node.end_gather_percent = ZhCalc.mul(ZhCalc.div(node.end_gather_tp, node.end_final_tp), 100, 2);
+            node.end_correct_percent = ZhCalc.mul(ZhCalc.div(node.end_correct_tp, node.end_final_tp), 100, 2);
+        }
+    };
+    const settleTree = createNewPathTree('stage', settleTreeSetting);
+    const settlePosSetting = {
+        id: 'id', ledgerId: 'lid',
+        calcFun: function(pos) {
+            pos.end_gather_qty = ZhCalc.add(pos.end_contract_qty, pos.end_qc_qty);
+            pos.sum = ZhCalc.add(pos.end_qc_qty, pos.quantity);
+            pos.end_gather_percent = ZhCalc.mul(ZhCalc.div(pos.end_gather_qty, pos.sum), 100, 2);
+        }
+    };
+    const settlePos = new StagePosData(settlePosSetting);
+
+    const slSpread = SpreadJsObj.createNewSpread($('#settle-bills')[0]);
+    const slSheet = slSpread.getActiveSheet();
+    slSheet.frozenColumnCount(billsSpreadSetting.cols.findIndex(x => { return x.field === 'total_price'; }) + 1);
+    slSheet.options.frozenlineColor = '#93b5e4';
+    const ratioCol = billsSpreadSetting.cols.find(x => {return x.field === 'end_final_1_percent' || x.field === 'end_correct_1_percent'});
+    if (ratioCol) ratioCol.field = tenderInfo.display.stage.correct ? 'end_correct_1_percent' : 'end_final_1_percent';
+    billsSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (!data) return defaultColor;
+        if (data.children && data.children.length > 0) return defaultColor;
+
+        if (col.field === 'gxby') {
+            const def = thirdParty.gxby.find(function (x) {
+                return x.value === data.gxby_status;
+            });
+            if (def && def.color) return def.color;
+        } else if (col.field === 'dagl') {
+            const def = thirdParty.dagl.find(function (x) {
+                return x.value === data.dagl_status;
+            });
+            if (def && def.color) return def.color;
+        }
+    };
+    sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
+    sjsSettingObj.set3FCols(billsSpreadSetting.cols, [
+        {field: 'gxby', getValue: getGxbyText, url_field: 'gxby_url'},
+        {field: 'dagl', getValue: getDaglText, url_field: 'dagl_url'},
+    ]);
+    SpreadJsObj.initSheet(slSheet, billsSpreadSetting);
+
+    const spSpread = SpreadJsObj.createNewSpread($('#settle-pos')[0]);
+    const spSheet = spSpread.getActiveSheet();
+    spSheet.frozenColumnCount(posSpreadSetting.cols.findIndex(x => { return x.field === 'total_price'; }) + 1);
+    spSheet.options.frozenlineColor = '#93b5e4';
+    posSpreadSetting.getColor = function (sheet, data, row, col, defaultColor) {
+        if (!data) return defaultColor;
+
+        if (col.field === 'gxby') {
+            const def = thirdParty.gxby.find(function (x) {
+                return x.value === data.gxby_status;
+            });
+            if (def && def.color) return def.color;
+        } else if (col.field === 'dagl') {
+            const def = thirdParty.dagl.find(function (x) {
+                return x.value === data.dagl_status;
+            });
+            if (def && def.color) return def.color;
+        }
+    };
+    sjsSettingObj.set3FCols(posSpreadSetting.cols, [
+        {field: 'gxby', getValue: getGxbyText, url_field: 'gxby_url'},
+        {field: 'dagl', getValue: getDaglText, url_field: 'dagl_url'},
+    ]);
+    SpreadJsObj.initSheet(spSheet, posSpreadSetting);
+
+    const settleBillsObj = {
+        loadRelaData: function() {
+            SpreadJsObj.saveTopAndSelect(slSheet, ckBillsSpread);
+            SpreadJsObj.resetTopAndSelect(spSheet);
+            settlePosObj.loadCurPosData();
+        },
+        selectionChanged: function(e, info) {
+            if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
+                settleBillsObj.loadRelaData();
+            }
+        },
+        topRowChanged(e, info) {
+            SpreadJsObj.saveTopAndSelect(info.sheet, ckBillsSpread);
+        },
+        buttonClicked: function(e, info) {
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            if (col.field !== 'selected') return;
+
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            node.selected = !node.selected;
+            if (node.children && node.children.length > 0) {
+                const posterity = tsObj.tenderSourceTree.getPosterity(node);
+                for (const p of posterity) {
+                    p.selected = node.selected;
+                    if (!p.children || p.children.length === 0){
+                        if (p.selected) {
+                            tsObj._addTender(p);
+                        } else {
+                            tsObj._removeTender(p);
+                        }
+                    }
+                }
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, posterity.length + 1);
+            } else {
+                if (node.selected) {
+                    tsObj._addTender(node);
+                } else {
+                    tsObj._removeTender(node);
+                }
+                SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+            }
+            tsObj.reloadResultData();
+        },
+    };
+    slSpread.bind(spreadNS.Events.ButtonClicked, stageTreeSpreadObj.buttonClicked);
+    slSpread.bind(spreadNS.Events.SelectionChanged, settleBillsObj.selectionChanged);
+    slSpread.bind(spreadNS.Events.TopRowChanged, settleBillsObj.topRowChanged);
+
+    const settlePosObj = {
+        loadCurPosData: function() {
+            const billsNode = SpreadJsObj.getSelectObject(slSheet);
+            if (billsNode) {
+                spSheet.zh_setting.readOnly = readOnly;
+                const posRange = settlePos.getLedgerPos(billsNode.id) || [];
+                SpreadJsObj.loadSheetData(spSheet, SpreadJsObj.DataType.Data, posRange, readOnly);
+            } else {
+                spSheet.zh_setting.readOnly = true;
+                SpreadJsObj.loadSheetData(spSheet, SpreadJsObj.DataType.Data, [], true);
+            }
+        }
+    };
+
+    postData('load', {filter: 'stageBills;stagePos;settleSelect;tag'}, function(result) {
+        for (const select of result.settleSelect) {
+            if (select.pid) {
+                const sp = result.stagePos.find(x => { return x.id === select.pid });
+                if (sp) sp.selected = true;
+            } else {
+                const sb = result.stageBills.find(x => { return x.id === select.lid });
+                if (sb) sb.selected = true;
+            }
+        }
+        settleTree.loadDatas(result.stageBills);
+        treeCalc.calculateAll(settleTree);
+        settlePos.loadDatas(result.stagePos);
+        settlePos.calculateAll();
+
+        SpreadJsObj.loadSheetData(slSheet, SpreadJsObj.DataType.Tree, settleTree);
+        SpreadJsObj.loadTopAndSelect(slSpread.getActiveSheet(), ckBillsSpread);
+        settlePosObj.loadCurPosData();
+    });
+
+    // 展开收起工具栏
+    $('a', '.right-nav').bind('click', function () {
+        const tab = $(this), tabPanel = $(tab.attr('content'));
+        if (!tab.hasClass('active')) {
+            $('a', '.side-menu').removeClass('active');
+            $('.tab-content .tab-select-show').removeClass('active');
+            tab.addClass('active');
+            tabPanel.addClass('active');
+            showSideTools(tab.hasClass('active'));
+            if (tab.attr('content') === '#search' && !searchLedger) {
+                searchLedger = $.billsSearch({
+                    selector: '#search',
+                    searchSpread: slSpread,
+                    searchOver: true,
+                    searchEmpty: true,
+                    resultSpreadSetting: {
+                        cols: [
+                            {title: '项目节编号', field: 'code', hAlign: 0, width: 90, formatter: '@'},
+                            {title: '清单编号', field: 'b_code', hAlign: 0, width: 80, formatter: '@'},
+                            {title: '名称', field: 'name', width: 150, hAlign: 0, formatter: '@'},
+                            {title: '单位', field: 'unit', width: 50, hAlign: 1, formatter: '@'},
+                            {title: '单价', field: 'unit_price', hAlign: 2, width: 50},
+                            {title: '数量', field: 'quantity', hAlign: 2, width: 50},
+                        ],
+                        emptyRows: 0,
+                        headRows: 1,
+                        headRowHeight: [32],
+                        headColWidth: [30],
+                        defaultRowHeight: 21,
+                        headerFont: '12px 微软雅黑',
+                        font: '12px 微软雅黑',
+                        selectedBackColor: '#fffacd',
+                        readOnly: true,
+                    },
+                    afterLocated: function () {
+                        settlePosObj.loadCurPosData();
+                    },
+                });
+                searchLedger.spread.refresh();
+            }
+        } else {
+            tab.removeClass('active');
+            tabPanel.removeClass('active');
+            showSideTools(tab.hasClass('active'));
+        }
+        slSpread.refresh();
+        spSpread.refresh();
+    });
+
+    $.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();
+        }
+    });
+    // 加载上下窗口resizer
+    $.divResizer({
+        select: '#main-resize',
+        callback: function () {
+            slSpread.refresh();
+            let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
+            $(".sp-wrap").height(bcontent-30);
+            spSpread.refresh();
+            window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
+        }
+    });
+    // 工具栏resizer
+    $.divResizer({
+        select: '#right-spr',
+        callback: function () {
+            slSpread.refresh();
+            spSpread.refresh();
+            window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
+        }
+    });
+});

+ 2 - 0
app/router.js

@@ -429,6 +429,8 @@ module.exports = app => {
     app.post('/tender/:id/settle/delete', sessionAuth, tenderCheck, uncheckTenderCheck, tenderBuildCheck, 'settleController.delete');
     // 结算期
     app.get('/tender/:id/settle/:sorder', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.index');
+    app.get('/tender/:id/settle/:sorder/select', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.select');
+    app.get('/tender/:id/settle/:sorder/ledger', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.ledger');
     app.post('/tender/:id/settle/:sorder/load', sessionAuth, tenderCheck, uncheckTenderCheck, settleCheck, 'settleController.loadSettleData');
     // 结算汇总
     app.get('/tender/:id/settle/gather', sessionAuth, tenderCheck, uncheckTenderCheck, 'settleController.gather');

+ 63 - 0
app/view/settle/audit_btn.ejs

@@ -0,0 +1,63 @@
+<div class="contarl-box">
+    <% if (ctx.settle.audit_status === auditConst.status.uncheck) { %>
+        <% if (ctx.session.sessionUser.accountId === ctx.settle.user_id) { %>
+            <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-primary btn-sm btn-block">上报审批</a>
+            <% if (ctx.settle.order === ctx.settle.highOrder) { %>
+                <a href="#del-qi" data-toggle="modal" data-target="#del-qi" class="btn btn-outline-danger btn-sm btn-block mt-5">删除本期</a>
+            <% } %>
+        <% } else { %>
+            <a id="sub-sp-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sub-sp" class="btn btn-outline-secondary btn-sm btn-block">上报中</a>
+        <% } %>
+
+    <% } %>
+
+    <% if (ctx.settle.audit_status === auditConst.status.checking) { %>
+        <% if (ctx.settle.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
+            <a id="sp-done-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm btn-block">审批通过</a>
+            <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm btn-block">审批退回</a>
+        <% } else { %>
+            <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm btn-block">审批中</a>
+        <% } %>
+    <% } %>
+
+    <% if (ctx.settle.audit_status === auditConst.status.checked) { %>
+        <a href="#sp-list" data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-secondary btn-sm btn-block sp-list-btn">审批完成</a>
+        <% if (ctx.settle.finalAuditorIds && ctx.settle.finalAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0 && ctx.settle.order === ctx.settle.highOrder) { %>
+            <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-back" class="btn btn-warning btn-sm btn-block">重新审批</a>
+        <% } %>
+    <% } %>
+
+    <% if (ctx.settle.audit_status === auditConst.status.checkNo) { %>
+        <a href="#sp-list"  data-type="hide" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm btn-block text-muted sp-list-btn">审批退回</a>
+        <% if (ctx.session.sessionUser.accountId === ctx.settle.user_id) { %>
+            <a href="#sp-list" data-type="show" data-toggle="modal" data-target="#sp-list"  class="btn btn-primary btn-sm btn-block sp-list-btn">重新上报</a>
+            <% if (ctx.settle.order === ctx.settle.highOrder) { %>
+                <a href="#del-qi" data-toggle="modal" data-target="#del-qi" class="btn btn-outline-danger btn-sm btn-block mt-5">删除本期</a>
+            <% } %>
+        <% } %>
+    <% } %>
+
+    <% if (ctx.settle.audit_status === auditConst.status.checkNoPre) { %>
+        <a href="#sp-list" data-toggle="modal" data-target="#sp-list" class="btn btn-outline-warning btn-sm btn-block text-muted">审批退回</a>
+        <% if (ctx.settle.curAuditorIds.indexOf(ctx.session.sessionUser.accountId) >= 0) { %>
+            <a id="sp-done-btn" href="javascript: void(0);" data-toggle="modal" data-target="#sp-done" class="btn btn-success btn-sm btn-block">审批通过</a>
+            <a href="#sp-back" data-toggle="modal" data-target="#sp-back" class="btn btn-warning btn-sm btn-block">审批退回</a>
+        <% } %>
+    <% } %>
+
+    <% if (ctx.settle.cancancel) { %>
+        <a href="javascript: void(0);" data-toggle="modal" data-target="#sp-down-cancel" class="btn btn-danger btn-sm btn-block">撤回</a>
+    <% } %>
+</div>
+<script>
+    $('.sp-list-btn').click(function () {
+        const type = $(this).data('type')
+        if (type === 'hide') {
+            $('.sp-list-item').hide()
+            $('.modal-title').text('审批流程')
+        } else {
+            $('.sp-list-item').show()
+            $('.modal-title').text('重新上报')
+        }
+    });
+</script>

+ 154 - 0
app/view/settle/index.ejs

@@ -0,0 +1,154 @@
+<% include ./settle_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ./settle_mini_menu.ejs %>
+            <div>
+                <div class="d-inline-block">
+                    <div class="dropdown">
+                        <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            <i class="fa fa-list-ol"></i> 显示层级
+                        </button>
+                        <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                            <a class="dropdown-item" name="showLevel" tag="1" href="javascript: void(0);">第一层</a>
+                            <a class="dropdown-item" name="showLevel" tag="2" href="javascript: void(0);">第二层</a>
+                            <a class="dropdown-item" name="showLevel" tag="3" href="javascript: void(0);">第三层</a>
+                            <a class="dropdown-item" name="showLevel" tag="4" href="javascript: void(0);">第四层</a>
+                            <a class="dropdown-item" name="showLevel" tag="5" href="javascript: void(0);">第五层</a>
+                            <a class="dropdown-item" name="showLevel" tag="last" href="javascript: void(0);">最底层</a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="ml-auto">
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap pr-46">
+        <div class="c-header p-0">
+        </div>
+        <div class="row w-100 sub-content">
+            <div id="left-view" class="c-body" style="width: 100%">
+                <!--上部分-->
+                <div class="sjs-height-1" id="stage-ledger">
+                </div>
+                <!--下部分-->
+                <div class="bcontent-wrap" id="main-bottom">
+                    <div id="main-resize" class="resize-y" r-Type="height" div1="#stage-bills" div2="#main-bottom" store-id="settle-main" store-version="1.0.0" min="100"></div>
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" href="#">计量单元</a>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="sp-wrap" id="stage-pos">
+                    </div>
+                </div>
+            </div>
+            <div id="right-view" class="c-body" style="display: none; width: 33%;">
+                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <!--查找定位-->
+                    <div id="search" class="tab-pane tab-select-show">
+                        <div class="sjs-bar-1">
+                            <div class="input-group input-group-sm pb-1">
+                                <div class="input-group-prepend">
+                                    <div class="input-group-text">
+                                        <input type="radio" name="searchType" id="over"> 超计
+                                    </div>
+                                    <div class="input-group-text">
+                                        <input type="radio" name="searchType" id="empty"> 漏计
+                                    </div>
+                                </div>
+                                <input type="text" class="form-control form-control-sm" placeholder="可查找 项目节编号 / 清单编号 /名称" id="keyword">
+                                <div class="input-group-append">
+                                    <button class="btn btn-outline-secondary btn-sm" type="button" id="searchLedger">搜索</button>
+                                </div>
+                            </div>
+                        </div>
+                        <div id="search-result" class="sjs-sh-1">
+                        </div>
+                    </div>
+                    <div id="bills-tag" class="tab-pane tab-select-show">
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav">
+                <li class="nav-item">
+                    <a class="nav-link" content="#search" href="javascript: void(0);">查找定位</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#bills-tag" href="javascript: void(0);">书签</a>
+                </li>
+                </li>
+            </ul>
+        </div>
+    </div>
+</div>
+<script>
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+    const billsSpreadSetting = {
+        cols: [
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
+            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@', readOnly: true},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true, cellType: 'unit'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '本期合同结算|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '本期数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '本期完成结算|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'},
+            {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip', true},
+            <% if (ctx.session.sessionProject.gxby) { %>
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+            <% if (ctx.session.sessionProject.dagl) { %>
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        frozenColCount: 5,
+        frozenLineColor: '#93b5e4',
+    };
+    const posSpreadSetting = {
+        cols: [
+            {title: '选择', colSpan: '1', rowSpan: '2', field: 'selected', hAlign: 0, width: 145, formatter: '@'},
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
+            {title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@', readOnly: true},
+            {title: '累计计量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 80, type: 'Number', readOnly: true},
+            {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
+            <% if (ctx.session.sessionProject.gxby) { %>
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+            <% if (ctx.session.sessionProject.dagl) { %>
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+        ],
+        emptyRows: 20,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+    };
+</script>

+ 164 - 0
app/view/settle/select.ejs

@@ -0,0 +1,164 @@
+<% include ./settle_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <% include ./settle_mini_menu.ejs %>
+            <div>
+                <div class="d-inline-block">
+                    <div class="dropdown">
+                        <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            <i class="fa fa-list-ol"></i> 显示层级
+                        </button>
+                        <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                            <a class="dropdown-item" name="showLevel" tag="1" href="javascript: void(0);">第一层</a>
+                            <a class="dropdown-item" name="showLevel" tag="2" href="javascript: void(0);">第二层</a>
+                            <a class="dropdown-item" name="showLevel" tag="3" href="javascript: void(0);">第三层</a>
+                            <a class="dropdown-item" name="showLevel" tag="4" href="javascript: void(0);">第四层</a>
+                            <a class="dropdown-item" name="showLevel" tag="5" href="javascript: void(0);">第五层</a>
+                            <a class="dropdown-item" name="showLevel" tag="last" href="javascript: void(0);">最底层</a>
+                        </div>
+                    </div>
+                </div>
+            </div>
+            <div class="ml-auto">
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap pr-46">
+        <div class="c-header p-0">
+        </div>
+        <div class="row w-100 sub-content">
+            <div id="left-view" class="c-body" style="width: 100%">
+                <!--上部分-->
+                <div class="sjs-height-1" id="settle-bills">
+                </div>
+                <!--下部分-->
+                <div class="bcontent-wrap" id="main-bottom">
+                    <div id="main-resize" class="resize-y" r-Type="height" div1="#settle-bills" div2="#main-bottom" store-id="settle-main" store-version="1.0.0" min="100"></div>
+                    <div class="bc-bar mb-1">
+                        <ul class="nav nav-tabs">
+                            <li class="nav-item">
+                                <a class="nav-link active" href="#">计量单元</a>
+                            </li>
+                        </ul>
+                    </div>
+                    <div class="sp-wrap" id="settle-pos">
+                    </div>
+                </div>
+            </div>
+            <div id="right-view" class="c-body" style="display: none; width: 33%;">
+                <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                <div class="tab-content">
+                    <!--查找定位-->
+                    <div id="search" class="tab-pane tab-select-show">
+                        <div class="sjs-bar-1">
+                            <div class="input-group input-group-sm pb-1">
+                                <div class="input-group-prepend">
+                                    <div class="input-group-text">
+                                        <input type="radio" name="searchType" id="over"> 超计
+                                    </div>
+                                    <div class="input-group-text">
+                                        <input type="radio" name="searchType" id="empty"> 漏计
+                                    </div>
+                                </div>
+                                <input type="text" class="form-control form-control-sm" placeholder="可查找 项目节编号 / 清单编号 /名称" id="keyword">
+                                <div class="input-group-append">
+                                    <button class="btn btn-outline-secondary btn-sm" type="button" id="searchLedger">搜索</button>
+                                </div>
+                            </div>
+                        </div>
+                        <div id="search-result" class="sjs-sh-1">
+                        </div>
+                    </div>
+                    <div id="bills-tag" class="tab-pane tab-select-show">
+                    </div>
+                </div>
+            </div>
+        </div>
+        <div class="side-menu">
+            <!--右侧菜单-->
+            <ul class="nav flex-column right-nav">
+                <li class="nav-item">
+                    <a class="nav-link" content="#search" href="javascript: void(0);">查找定位</a>
+                </li>
+                <li class="nav-item">
+                    <a class="nav-link" content="#bills-tag" href="javascript: void(0);">书签</a>
+                </li>
+                </li>
+            </ul>
+        </div>
+    </div>
+    <div style="display: none">
+        <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
+        <img src="/public/images/icon-ok.png" id="icon-ok" />
+        <img src="/public/images/file_clip.png" id="rela-file-icon">
+        <img src="/public/images/file_clip_hover.png" id="rela-file-hover">
+    </div>
+</div>
+<script>
+    const readOnly = <%- settle.readOnly %>;
+    const tenderInfo = JSON.parse(unescape('<%- escape(JSON.stringify(ctx.tender.info)) %>'));
+    const thousandth = <%- ctx.tender.info.display.thousandth %>;
+    const thirdParty = JSON.parse('<%- JSON.stringify(thirdParty) %>');
+    const billsSpreadSetting = {
+        cols: [
+            {title: '选择', colSpan: '1', rowSpan: '2', field: 'selected', hAlign: 1, width: 30, formatter: '@', cellType: 'checkbox'},
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'},
+            {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@', readOnly: true},
+            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true},
+            {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true, cellType: 'unit'},
+            {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '累计合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_contract_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '累计数量变更|数量', colSpan: '2|1', rowSpan: '1|1', field: 'end_qc_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_qc_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '累计完成计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'end_gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_final_1_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'},
+            {title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@', readOnly: true},
+            {title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip', readOnly: true},
+            <% if (ctx.session.sessionProject.gxby) { %>
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+            <% if (ctx.session.sessionProject.dagl) { %>
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        frozenColCount: 5,
+        frozenLineColor: '#93b5e4',
+    };
+    const posSpreadSetting = {
+        cols: [
+            {title: '选择', colSpan: '1', rowSpan: '2', field: 'selected', hAlign: 0, width: 30, formatter: '@', cellType: 'checkbox'},
+            {title: '结算状态', colSpan: '1', rowSpan: '2', field: 'settle_status', hAlign: 1, width: 60, formatter: '@', readOnly: true},
+            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true},
+            {title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@', readOnly: true},
+            {title: '累计计量|合同', colSpan: '3|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'end_qc_qty', hAlign: 2, width: 80, type: 'Number', readOnly: true},
+            {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'end_gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            {title: '图册号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
+            <% if (ctx.session.sessionProject.gxby) { %>
+            {title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+            <% if (ctx.session.sessionProject.dagl) { %>
+            {title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true},
+            <% } %>
+        ],
+        emptyRows: 0,
+        headRows: 2,
+        headRowHeight: [25, 25],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+    };
+</script>

+ 0 - 0
app/view/settle/select_modal.ejs


+ 3 - 2
app/view/settle/settle_menu.ejs

@@ -1,9 +1,10 @@
 <div class="panel-sidebar" id="sub-menu">
-    <div class="sidebar-title" data-toggle="tooltip" data-placement="right" data-original-title="第<%- ctx.stage.order%>期 - 关联台账">
-        关联台账
+    <div class="sidebar-title" data-toggle="tooltip" data-placement="right" data-original-title="<%- ctx.tender.data.name %>">
+        <%- (ctx.tender.data.name.length > 15 ? ctx.tender.data.name.substring(0,15) + '...' : ctx.tender.data.name) %>
     </div>
     <div class="scrollbar-auto">
         <% include ./settle_menu_list.ejs %>
+        <% include ./audit_btn.ejs %>
         <div class="side-fold"><a href="javascript: void(0)" data-toggle="tooltip" data-placement="top" data-original-title="折叠侧栏" id="to-mini-menu"><i class="fa fa-upload fa-rotate-270"></i></a></div>
     </div>
     <script>

+ 5 - 2
app/view/settle/settle_menu_list.ejs

@@ -1,3 +1,6 @@
 <nav-menu title="返回" url="/tender/<%= ctx.tender.id %>/settle" tclass="text-primary" ml="1" icon="fa-chevron-left"></nav-menu>
-<nav-menu title="结算列表" url="/tender/<%= ctx.tender.id %>/settle/<%= ctx.settle.settle_order%>" ml="3" active="<%= (ctx.url.indexOf(ctx.settle.settle.order) > 0 ? 1 : -1) %>"></nav-menu>
-<nav-menu title="汇总台账" url="/tender/<%= ctx.tender.id %>/settle/gather" ml="3" active="<%= (ctx.url.indexOf('gather') > 0 ? 1 : -1) %>"></nav-menu>
+<% if (ctx.session.sessionUser.is_admin || ctx.session.sessionUser.accountId === ctx.settle.user_id) { %>
+<nav-menu title="结算部位" url="/tender/<%= ctx.tender.id %>/settle/<%= ctx.settle.settle_order%>/select" ml="3" active="<%= (ctx.url.indexOf('select') > 0 ? 1 : -1) %>"></nav-menu>
+<% } %>
+<nav-menu title="结算台账" url="/tender/<%= ctx.tender.id %>/settle/<%= ctx.settle.settle_order%>/ledger" ml="3" active="<%= (ctx.url.indexOf('ledger') > 0 ? 1 : -1) %>"></nav-menu>
+<nav-menu title="结算报表" url="/tender/<%= ctx.tender.id %>/settle/<%= ctx.settle.settle_order%>/report" ml="3" active="<%= (ctx.url.indexOf('report') > 0 ? 1 : -1) %>"></nav-menu>

+ 40 - 0
config/web.js

@@ -1346,6 +1346,46 @@ const JsFiles = {
                 ],
                 mergeFile: 'settle_list',
             },
+            select: {
+                files: [
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/component/menu.js',
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/settle_select.js',
+
+                ],
+                mergeFile: 'settle_select',
+            },
+            ledger: {
+                files: [
+                    '/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',
+                    '/public/js/spreadjs/sheets/v11/interop/gc.spread.excelio.11.2.2.min.js',
+                    '/public/js/decimal.min.js',
+                ],
+                mergeFiles: [
+                    '/public/js/shares/cs_tools.js',
+                    '/public/js/component/menu.js',
+                    '/public/js/sub_menu.js',
+                    '/public/js/div_resizer.js',
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/shares/sjs_setting.js',
+                    '/public/js/zh_calc.js',
+                    '/public/js/path_tree.js',
+                    '/public/js/settle_ledger.js',
+
+                ],
+                mergeFile: 'settle',
+            }
         }
     },
 };