Procházet zdrojové kódy

Merge branch 'dev' of http://192.168.1.41:3000/maixinrong/Calculation into dev

Tony Kang před 8 hodinami
rodič
revize
fed5dfbb49

+ 1 - 1
app/base/base_bills_service.js

@@ -814,7 +814,7 @@ class BaseBillsSerivce extends TreeService {
                                 quantity: ag.quantity || 0, expr: ag.expr || '', calc_template: ag.calc_template || ''
                             };
                             pasteAncGclData.push(nig);
-                            if (!ag.calc_template) continue;
+                            if (!ag.calc_template || ag.calcDetail.length === 0) continue;
 
                             let sumQty = 0;
                             for (const cd of ag.calcDetail) {

+ 20 - 0
app/const/project_setting.js

@@ -58,6 +58,16 @@ const colSet = {
         { field: 'end_sf_tp', name: '截止本期实付', fixed: [] },
         { field: 'wf_tp', name: '未付金额', fixed: [], hint:'截止本期应付-截止本期实付' },
     ],
+    projectList: [
+        { field: 'name', name: '项目名称', fixed: ['show'] },
+        { field: 'budget_std', name: '概预算标准', fixed: ['show'] },
+        { field: 'tender_count', name: '标段个数', fixed: [] },
+        { field: 'file_count', name: '文件个数', fixed: [] },
+        { field: 'contract_count', name: '合同个数', fixed: [] },
+        { field: 'management', name: '管理单位', fixed: ['show'] },
+        { field: 'create_time', name: '创建时间', fixed: [] },
+        { field: 'control', name: '操作', fixed: ['show'] },
+    ],
 };
 const defaultProjectColSet = {
     info: [
@@ -76,6 +86,16 @@ const defaultProjectColSet = {
         { field: 'end_sf_tp', show: 0, alias: '' },
         { field: 'wf_tp', show: 0, alias: '' },
     ],
+    list: [
+        { field: 'name', show: 1 },
+        { field: 'budget_std', show: 1, alias: '' },
+        { field: 'tender_count', show: 1, alias: '' },
+        { field: 'file_count', show: 1, alias: '' },
+        { field: 'contract_count', show: 1, alias: '' },
+        { field: 'management', show: 1, alias: '' },
+        { field: 'create_time', show: 1, alias: '' },
+        { field: 'control', show: 1, alias: '' },
+    ],
 };
 const defaultColSet = {
     info: [

+ 32 - 14
app/controller/ledger_controller.js

@@ -1265,21 +1265,39 @@ module.exports = app => {
             const AIDify = require('../lib/ai_dify');
             const aiDify = new AIDify({ apiKey: ctx.difyInfo.apiKey.image2PosCalcDetail, baseUrl: ctx.difyInfo.baseUrl });
             const transResult = await aiDify.posCalcDetailRead(validColSet, data.billsName, data.keyword, data.files);
-            const rows = transResult.res.split('\n').filter(x => { return !!x; });
-            rows.shift();
-            const pcdData = rows.map(x => {
-                const rd = x.split('|');
-                const nd = { pid: pos.id, lid: pos.lid };
-                for (const [i, c] of validColSet.entries()) {
-                    if (c.type === 'num') {
-                        nd[c.field] = ctx.helper._.toNumber(rd[i]);
-                    } else {
-                        nd[c.field] = rd[i];
+            if (!transResult.res) throw '未识别出设计量明细、附属工程量';
+
+            let responseData = {};
+            const [detail, ancGcl] = transResult.res.split(`\n==+ancGcl+==\n`);
+            if (detail) {
+                const rows = detail.split('\n').filter(x => { return !!x; });
+                rows.shift();
+                const pcdData = rows.map(x => {
+                    const rd = x.split('|');
+                    const nd = { pid: pos.id, lid: pos.lid };
+                    for (const [i, c] of validColSet.entries()) {
+                        if (c.type === 'num') {
+                            nd[c.field] = ctx.helper._.toNumber(rd[i]);
+                        } else {
+                            nd[c.field] = rd[i];
+                        }
                     }
-                }
-                return nd;
-            });
-            const responseData = await ctx.service.posCalcDetail.updateDatas({ add: pcdData });
+                    return nd;
+                });
+                responseData = await ctx.service.posCalcDetail.updateDatas({ add: pcdData });
+            }
+            if (ancGcl) {
+                const existAncGcl = await ctx.service.ancillaryGcl.getAllDataByCondition({ where: { pid: pos.id }, orders: [['g_order', 'desc']]});
+                const maxOrder = existAncGcl.length > 0 ? existAncGcl[0].g_order : 0;
+                const agRows = ancGcl.split('\n').filter(x => { return !!x; });
+                agRows.shift();
+                const agData = agRows.map((x, i) => {
+                    const rd = x.split('|');
+                    return { pid: pos.id, lid: pos.lid, name: rd[0] || '', unit: rd[1] || 0, quantity:  ctx.helper._.toNumber(rd[2]) || 0, g_order: i + maxOrder + 1 };
+                });
+                if (agData.length > 0) responseData.ancGcl = await ctx.service.ancillaryGcl.updateDatas({ add: agData });
+                console.log(responseData.ancGcl);
+            }
             ctx.body = { err: 0, msg: '', data: responseData };
         }
         async _aiComplieSteelPos(ctx, bills, data) {

+ 0 - 1
app/controller/report_controller.js

@@ -1262,7 +1262,6 @@ module.exports = app => {
                 pageShow.closeWatermark = 1;
                 pageShow.showArchive = 1;
                 pageShow.closeShowAllCustomized = 0;
-                pageShow.isPreset = 0;
                 // const tenderId = ctx.params.tid || -1; // 标段级别有
                 const tenderId = ctx.params.tid || -1;
                 const bglObj = {};

+ 2 - 1
app/controller/stage_controller.js

@@ -563,7 +563,8 @@ module.exports = app => {
                     await this._innerCheckChangeBillsOver(ctx, checkData);
                 }
                 if (!ctx.stage.update_expr_calc_time || (ctx.stage.update_calc_time && ctx.stage.update_expr_calc_time < ctx.stage.update_calc_time)) {
-                    const exprCount = await ctx.service.expr.count({ tid: ctx.stage.tid, calc_module: 'stage' });
+                    const expr = await ctx.service.expr.getAllDataByCondition({ where: { tid: ctx.stage.tid, calc_module: 'stage' } });
+                    const exprCount = expr.filter(x => { return !!x.expr }).length;
                     if (exprCount > 0) {
                         checkData.checkResult.error.push({ errorType: 'expr_no_calc', name: '修改数据后,未计算本期合同计量' });
                     }

+ 50 - 1
app/controller/sub_proj_controller.js

@@ -54,6 +54,9 @@ module.exports = app => {
                 renderData.permissionConst = ctx.service.subProjPermission.PermissionConst;
                 renderData.companys = await this.ctx.service.constructionUnit.getAllDataByCondition({ where: { pid: ctx.session.sessionProject.id } });
                 renderData.templates = await this.ctx.service.filingTemplateList.getAllTemplate(ctx.session.sessionProject.id);
+
+                const projectColSet = await ctx.service.projectColSet.getProjectColSet(ctx.session.sessionProject.id);
+                renderData.colSet = ctx.service.projectColSet.analysisColSetWithDefine(projectSetting.colSet.projectList, projectColSet.list, projectSetting.defaultProjectColSet.list);
                 await this.layout('sub_proj/index.ejs', renderData, 'sub_proj/modal.ejs');
             } catch (err) {
                 ctx.log(err);
@@ -290,6 +293,31 @@ module.exports = app => {
             }
         }
 
+        async _loadContract(type, contract_type = 1, tid) {
+            if (type === 'project') {
+                const contractTree = await this.ctx.service.contractTree.getAllDataByCondition({
+                    columns: ['id', 'contract_id', 'contract_pid', 'order', 'level', 'is_leaf', 'full_path', 'code', 'name', 'selected'],
+                    where: {spid: this.ctx.subProject.id, contract_type}
+                });
+                const contractData = await this.ctx.service.contract.getAllDataByCondition({
+                    columns: ['id', 'contract_id', 'contract_pid', 'order', 'level', 'is_leaf', 'full_path', 'c_code', 'name', 'selected'],
+                    where:  {spid: this.ctx.subProject.id, contract_type}
+                });
+                return [...contractTree, ...contractData];
+            } else if (type === 'tender') {
+                const contractTree = await this.ctx.service.contractTree.getAllDataByCondition({
+                    columns: ['id', 'contract_id', 'contract_pid', 'order', 'level', 'is_leaf', 'full_path', 'code', 'name', 'selected'],
+                    where: {tid, contract_type}
+                });
+                const contractData = await this.ctx.service.contract.getAllDataByCondition({
+                    columns: ['id', 'contract_id', 'contract_pid', 'order', 'level', 'is_leaf', 'full_path', 'c_code', 'name', 'selected'],
+                    where:  {tid, contract_type}
+                });
+                return [...contractTree, ...contractData];
+            } else {
+                return [];
+            }
+        }
         async load(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
@@ -309,6 +337,9 @@ module.exports = app => {
                         case 'push_file':
                             result[f] = await ctx.service.subProjFile.getData(ctx.subProject.id, 'push');
                             break;
+                        case 'contract':
+                            result[f] = await this._loadContract(data.type, data.contractType, data.tid);
+                            break;
                         default:
                             continue;
                     }
@@ -316,7 +347,25 @@ module.exports = app => {
                 ctx.body = { err: 0, msg: '', data: result };
             } catch(err) {
                 ctx.log(err);
-                ctx.ajaxErrorBody(err, '获取阶段进度数据有误');
+                ctx.ajaxErrorBody(err, '获取数据有误');
+            }
+        }
+        async saveSubProjOther(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                if (!data.updateType || !data.updateData) throw '数据错误';
+                let result = null;
+                switch(data.updateType) {
+                    case 'contract':
+                        await ctx.service.contract.savePresetData(data.updateData);
+                        break;
+                    default:
+                        throw '未知数据类型';
+                }
+                ctx.body = { err: 0, msg: '', data: result };
+            } catch (err) {
+                ctx.log(err);
+                ctx.ajaxErrorBody(err, '保存数据失败');
             }
         }
 

+ 23 - 11
app/public/js/contract_detail.js

@@ -270,12 +270,12 @@ $(document).ready(function() {
             if (sel.rowCount > 1 && first) {
                 for (let r = 1; r < sel.rowCount; r++) {
                     const rNode = tree.nodes[sel.row + r];
-                    childCanDel = this.checkDelete(rNode, childCanDel);
-                    // if (!hadContract) hadContract = this.checkContract(rNode);
                     if (!rNode) {
                         sameParent = false;
                         break;
                     }
+                    childCanDel = this.checkDelete(rNode, childCanDel);
+                    // if (!hadContract) hadContract = this.checkContract(rNode);
                     if (rNode.level > first.level) continue;
                     if ((rNode.level < first.level) || (rNode.level === first.level && rNode.contract_pid !== first.contract_pid)) {
                         sameParent = false;
@@ -1137,6 +1137,7 @@ $(document).ready(function() {
             return haveContract;
         },
         checkDelete: function (select, oldFlag = true) {
+            if (!select) return false;
             let canDelete = oldFlag;
             if (select.c_code) {
                 if (!(select.uid === user_id || is_admin)) {
@@ -1460,11 +1461,11 @@ $(document).ready(function() {
             if (sel.rowCount > 1 && first) {
                 for (let r = 1; r < sel.rowCount; r++) {
                     const rNode = tree.nodes[sel.row + r];
-                    childCanDel = contractTreeSpreadObj.checkDelete(rNode, childCanDel);
                     if (!rNode) {
                         sameParent = false;
                         break;
                     }
+                    childCanDel = contractTreeSpreadObj.checkDelete(rNode, childCanDel);
                     if (rNode.level > first.level) continue;
                     if ((rNode.level < first.level) || (rNode.level === first.level && rNode.contract_pid !== first.contract_pid)) {
                         sameParent = false;
@@ -2644,6 +2645,7 @@ $(document).ready(function() {
         // 列显示控制
         const colShowMap = {};
         colSet.forEach(function (col) { colShowMap[col.field] = col.show; });
+        const showSettlePrice = colShowMap.settle_price;
         const showYfPrice = colShowMap.yf_price;
         const showStackedBar = colShowMap.stackedBar;
         const showSfPrice = colShowMap.sf_price;
@@ -2660,8 +2662,10 @@ $(document).ready(function() {
         };
         // 构建表头
         let theadHtml = '<tr>';
-        theadHtml += '<th style="width: 30%">节点名称</th>';
+        theadHtml += '<th style="width: 6%">编号</th>';
+        theadHtml += '<th style="width: 24%">项目名称</th>';
         theadHtml += '<th>合同金额</th>';
+        if (showSettlePrice) theadHtml += '<th>' + getColTitle('settle_price') + '</th>';
         if (showYfPrice) theadHtml += '<th>' + getColTitle('yf_price') + '</th>';
         if (showStackedBar) theadHtml += '<th>' + getColTitle('stackedBar') + '</th>';
         if (showSfPrice) theadHtml += '<th>' + getColTitle('sf_price') + '</th>';
@@ -2670,32 +2674,40 @@ $(document).ready(function() {
         $('#contract-total-thead').html(theadHtml);
         // 构建表体(仅第一层节点)
         let tbodyHtml = '';
-        let sum_total = 0, sum_yf = 0, sum_sf = 0;
-        for (const node of topLevelNodes) {
+        let sum_total = 0, sum_settle = 0, sum_yf = 0, sum_sf = 0;
+        for (const [index, node] of topLevelNodes.entries()) {
             const nodeTotal = Number(node.total_price || 0);
+            const nodeSettle = Number(node.settle_price || 0);
+            const nodeProgressBase = showSettlePrice && nodeSettle !== 0 ? nodeSettle : nodeTotal;
             const nodeYf = Number(node.yf_price || 0);
             const nodeSf = Number(node.sf_price || 0);
             sum_total = ZhCalc.add(sum_total, nodeTotal);
+            sum_settle = ZhCalc.add(sum_settle, nodeSettle);
             sum_yf = ZhCalc.add(sum_yf, nodeYf);
             sum_sf = ZhCalc.add(sum_sf, nodeSf);
             tbodyHtml += '<tr>';
+            tbodyHtml += '<td>' + (index + 1) + '</td>';
             tbodyHtml += '<td class="text-left">' + (node.name || '') + '</td>';
             tbodyHtml += '<td class="text-right">' + (nodeTotal || 0) + '</td>';
+            if (showSettlePrice) tbodyHtml += '<td class="text-right">' + (nodeSettle || 0) + '</td>';
             if (showYfPrice) tbodyHtml += '<td class="text-right">' + (nodeYf || 0) + '</td>';
-            if (showStackedBar) tbodyHtml += '<td class="text-right">' + getProgress(nodeYf, nodeTotal) + '</td>';
+            if (showStackedBar) tbodyHtml += '<td class="text-right">' + getProgress(nodeYf, nodeProgressBase) + '</td>';
             if (showSfPrice) tbodyHtml += '<td class="text-right">' + (nodeSf || 0) + '</td>';
-            if (showStackedBarSf) tbodyHtml += '<td class="text-right">' + getProgress(nodeSf, nodeTotal) + '</td>';
+            if (showStackedBarSf) tbodyHtml += '<td class="text-right">' + getProgress(nodeSf, nodeProgressBase) + '</td>';
             tbodyHtml += '</tr>';
         }
         $('#contract-total-tbody').html(tbodyHtml);
         // 构建合计行
         let tfootHtml = '<tr class="font-weight-bold">';
-        tfootHtml += '<td class="text-left">合计</td>';
+        tfootHtml += '<td></td>';
+        tfootHtml += '<td class="text-center">合计</td>';
         tfootHtml += '<td class="text-right">' + (sum_total || 0) + '</td>';
+        if (showSettlePrice) tfootHtml += '<td class="text-right">' + (sum_settle || 0) + '</td>';
         if (showYfPrice) tfootHtml += '<td class="text-right">' + (sum_yf || 0) + '</td>';
-        if (showStackedBar) tfootHtml += '<td class="text-right">' + getProgress(sum_yf, sum_total) + '</td>';
+        const totalProgressBase = showSettlePrice && sum_settle !== 0 ? sum_settle : sum_total;
+        if (showStackedBar) tfootHtml += '<td class="text-right">' + getProgress(sum_yf, totalProgressBase) + '</td>';
         if (showSfPrice) tfootHtml += '<td class="text-right">' + (sum_sf || 0) + '</td>';
-        if (showStackedBarSf) tfootHtml += '<td class="text-right">' + getProgress(sum_sf, sum_total) + '</td>';
+        if (showStackedBarSf) tfootHtml += '<td class="text-right">' + getProgress(sum_sf, totalProgressBase) + '</td>';
         tfootHtml += '</tr>';
         $('#contract-total-tfoot').html(tfootHtml);
         // 显示弹窗

+ 9 - 2
app/public/js/ledger.js

@@ -4068,7 +4068,11 @@ $(document).ready(function() {
                 if (result.details) ancGclDetail.detail.updateDatas(result.details);
                 ancGclObj.loadCurAncillaryGcl();
             });
-        }
+        },
+        afterPostData: function(data) {
+            ancGcl.updateDatas(data);
+            ancGclObj.loadCurAncillaryGcl();
+        },
     };
     ancGclSpread.bind(spreadNS.Events.SelectionChanged, ancGclObj.selectionChanged);
     if (!readOnly) {
@@ -6518,7 +6522,10 @@ $(document).ready(function() {
                 return;
             }
 
-            if (aiTrans.rela_type() === 'pos') aiTrans.transDetail(posCalcDetail.afterPostData);
+            if (aiTrans.rela_type() === 'pos') aiTrans.transDetail(function(result) {
+                posCalcDetail.afterPostData(result);
+                if (result.ancGcl) ancGclObj.afterPostData(result.ancGcl);
+            });
             if (aiTrans.rela_type() === 'bills') aiTrans.transDetail(posOperationObj.afterPostData);
         });
     });

+ 2 - 0
app/public/js/revise.js

@@ -55,6 +55,8 @@ const checkOption = {
 $(document).ready(() => {
     const unit = billsSpreadSetting.cols.find(x => { return x.field === 'unit'; });
     if (unit) unit.comboEdit = true;
+    sjsSettingObj.setFromCol(posSpreadSetting.cols, [{field: 'from'}]);
+    sjsSettingObj.setFromCol(billsSpreadSetting.cols, [{ field: 'from'}]);
     let stdXmj, stdGcl, searchLedger, reviseAtt, posDisplay;
     autoFlashHeight();
     // 初始化spread

+ 2 - 0
app/public/js/revise_history.js

@@ -16,6 +16,8 @@ $(document).ready(() => {
     const billsSpread = SpreadJsObj.createNewSpread($('#bills-spread')[0]);
     const billsSheet = billsSpread.getActiveSheet();
 
+    sjsSettingObj.setFromCol(posSpreadSetting.cols, [{field: 'from'}]);
+    sjsSettingObj.setFromCol(billsSpreadSetting.cols, [{ field: 'from'}]);
     billsSpreadSetting.cols.push({title: '截止本期合同计量|数量', colSpan: '3|1', rowSpan: '1|1', field: 'end_contract_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
         {title: '|完成率(%)', colSpan: '1', rowSpan: '|1', field: 'end_percent', hAlign: 2, width: 80, readOnly: true, type: 'Number'});
     sjsSettingObj.setFxTreeStyle(billsSpreadSetting, sjsSettingObj.FxTreeStyle.jz);

+ 65 - 29
app/public/js/sub_project.js

@@ -12,56 +12,90 @@ $(document).ready(function() {
         const ProjectTree = createDragTree(setting.treeSetting);
         ProjectTree.loadDatas(setting.source);
         const TableObj = $(setting.table);
+        const TableHeaderObj = $(setting.tableHeader);
         let tenderTreeShowLevel;
+        let colSetCache;
 
         const Utils = {
+            generateColSetCache: function() {
+                const result = {};
+                colSet.forEach(x => {
+                    result[x.field] = { show: x.show, alias: x.alias || x.name };
+                });
+                return result;
+            },
+            getHeaderHtml: function() {
+                colSetCache = this.generateColSetCache();
+                const html = [];
+                html.push('<tr class="text-center">');
+                if (colSetCache.name.show) html.push('<th style="min-width: 300px">', colSetCache.name.alias, '</th>');
+                if (colSetCache.budget_std.show) html.push('<th width="8%">', colSetCache.budget_std.alias, '</th>');
+                if (colSetCache.tender_count.show) html.push('<th width="5%">', colSetCache.tender_count.alias, '</th>');
+                if (colSetCache.file_count.show) html.push('<th width="5%">', colSetCache.file_count.alias, '<i class="fa fa-question-circle text-primary" data-placement="bottom" data-toggle="tooltip" data-original-title="指资料归集的所有文件个数"></i>', '</th>');
+                if (colSetCache.contract_count.show) html.push('<th width="5%">', colSetCache.contract_count.alias, '<i class="fa fa-question-circle text-primary" data-placement="bottom" data-toggle="tooltip" data-original-title="项目支出合同个数"></i>', '</th>');
+                if (colSetCache.management.show) html.push('<th width="20%">', colSetCache.management.alias, '</th>');
+                if (colSetCache.create_time.show) html.push('<th width="8%">', colSetCache.create_time.alias, '</th>');
+                if (colSetCache.control.show && canEdit) html.push('<th width="20%">', colSetCache.control.alias, '</th>');
+                html.push('</tr>');
+                console.log(html);
+                return html.join('');
+            },
             getRowTdHtml: function (node, tree) {
                 const html = [];
                 // 名称
-                html.push('<td width="20%" class="in-' + node.tree_level + '">');
-                if (node.is_folder) {
-                    if (node.children.length > 0) {
-                        html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" id="'+ node.id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
+                if (colSetCache.name.show) {
+                    html.push('<td width="20%" class="in-' + node.tree_level + '">');
+                    if (node.is_folder) {
+                        if (node.children.length > 0) {
+                            html.push('<span onselectstart="return false" style="{-moz-user-select:none}" class="fold-switch mr-1" title="收起" id="'+ node.id +'"><i class="fa fa-minus-square-o"></i></span> <i class="fa fa-folder-o"></i> ', node.name);
+                        } else {
+                            html.push('<i class="fa fa-folder-o"></i> ', node.name);
+                        }
                     } else {
-                        html.push('<i class="fa fa-folder-o"></i> ', node.name);
+                        html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
+                        html.push(`<a href="sp/${node.id}/dashboard" name="name" id="${node.id}">`, node.name, '</a>');
                     }
-                } else {
-                    html.push(`<span class="text-muted mr-2">${tree.isLastSibling(node) ? '└' : '├'}</span>`);
-                    html.push(`<a href="sp/${node.id}/dashboard" name="name" id="${node.id}">`, node.name, '</a>');
+                    html.push('</td>');
                 }
-                html.push('</td>');
                 // 概预算标准
-                if (node.is_folder) {
-                    html.push(`<td class="text-center"></td>`);
-                } else {
-                    if (node.std_name) {
-                        html.push(`<td class="text-center">${node.std_name}</td>`);
-                    } else if (canEdit) {
-                        html.push(`<td class="text-center"><button class="btn btn-outline-primary btn-sm ml-1" name="set-std">选择</button></td>`);
-                    } else {
+                if (colSetCache.budget_std.show) {
+                    if (node.is_folder) {
                         html.push(`<td class="text-center"></td>`);
+                    } else {
+                        if (node.std_name) {
+                            html.push(`<td class="text-center">${node.std_name}</td>`);
+                        } else if (canEdit) {
+                            html.push(`<td class="text-center"><button class="btn btn-outline-primary btn-sm ml-1" name="set-std">选择</button></td>`);
+                        } else {
+                            html.push(`<td class="text-center"></td>`);
+                        }
                     }
                 }
                 // 创建时间
                 if (node.is_folder) {
-                    html.push(`<td class="text-center"></td>`);
-                    html.push(`<td class="text-center"></td>`);
-                    html.push(`<td class="text-center"></td>`);
-                    html.push(`<td class="text-center"></td>`);
+                    if (colSetCache.tender_count.show) html.push(`<td class="text-center"></td>`);
+                    if (colSetCache.file_count.show) html.push(`<td class="text-center"></td>`);
+                    if (colSetCache.contract_count.show) html.push(`<td class="text-center"></td>`);
+                    if (colSetCache.management.show) html.push(`<td class="text-center"></td>`);
+                    if (colSetCache.create_time.show) html.push(`<td class="text-center"></td>`);
                 } else {
-                    html.push(`<td class="text-center">${moment(node.create_time).format('YYYY-MM-DD')}</td>`);
                     // 标段个数
-                    html.push(`<td class="text-center">${node.tender_count}</td>`);
+                    if (colSetCache.tender_count.show) html.push(`<td class="text-center">${node.tender_count}</td>`);
                     // 文件个数
-                    html.push(`<td class="text-center">${node.file_count}</td>`);
-                    if (canEdit) {
-                        html.push(`<td class="text-center">${node.management || ''}<a class="ml-2" href="javascript: void(0)" name="set-management"><i class="fa fa-pencil-square-o "></i></a>${node.management ? '<a class="ml-2" href="javascript: void(0)" name="refresh-management"><i class="fa fa-refresh "></i></a>': ''}</td>`);
-                    } else {
-                        html.push(`<td class="text-center">${node.management || ''}</td>`);
+                    if (colSetCache.file_count.show) html.push(`<td class="text-center">${node.file_count}</td>`);
+                    // 合同个数
+                    if (colSetCache.contract_count.show) html.push(`<td class="text-center">${node.contract_count}</td>`);
+                    if (colSetCache.management.show) {
+                        if (canEdit) {
+                            html.push(`<td class="text-center">${node.management || ''}<a class="ml-2" href="javascript: void(0)" name="set-management"><i class="fa fa-pencil-square-o "></i></a>${node.management ? '<a class="ml-2" href="javascript: void(0)" name="refresh-management"><i class="fa fa-refresh "></i></a>': ''}</td>`);
+                        } else {
+                            html.push(`<td class="text-center">${node.management || ''}</td>`);
+                        }
                     }
+                    if (colSetCache.create_time.show) html.push(`<td class="text-center">${moment(node.create_time).format('YYYY-MM-DD')}</td>`);
                 }
                 // 操作
-                if (canEdit) {
+                if (canEdit && colSetCache.control.show) {
                 html.push(`<td>`);
                     html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="edit">编辑</button>');
                     html.push('<button class="btn btn-outline-primary btn-sm ml-1" name="up"><i class="fa fa-arrow-up"></i></button>');
@@ -91,6 +125,7 @@ $(document).ready(function() {
                 return html.join('');
             },
             reloadTable: function () {
+                TableHeaderObj.html(Utils.getHeaderHtml());
                 const html = [];
                 for (const node of ProjectTree.nodes) {
                     html.push(Utils.getNodeTrHtml(node, ProjectTree));
@@ -343,6 +378,7 @@ $(document).ready(function() {
         treeSetting: { id: 'id', pid: 'tree_pid', level: 'tree_level', order: 'tree_order', rootId: '-1' },
         source: projectList,
         table: '#projectList',
+        tableHeader: '#projectListHeader',
     });
 
     $('#add-folder').on('show.bs.modal', function(e) {

+ 85 - 0
app/public/report/js/rpt_contract.js

@@ -0,0 +1,85 @@
+const rptContract = (function(){
+
+    let contractSpread, contractSheet, contractTree;
+    const initSpread = function() {
+        if (!contractSpread) {
+            contractSpread = SpreadJsObj.createNewSpread($('#rpt-contract-spread')[0]);
+            contractSheet = contractSpread.getActiveSheet();
+            SpreadJsObj.initSheet(contractSheet, {
+                cols: [
+                    {title: '选择', colSpan: '1', rowSpan: '1', field: 'selected', hAlign: 1, width: 40, formatter: '@', cellType: 'checkbox'},
+                    {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
+                    {title: '合同编号', colSpan: '1', rowSpan: '1', field: 'c_code', hAlign: 0, width: 120, formatter: '@'},
+                    {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 200, formatter: '@'},
+                ],
+                emptyRows: 0,
+                headRows: 1,
+                headRowHeight: [32],
+                defaultRowHeight: 21,
+                headerFont: '12px 微软雅黑',
+                font: '12px 微软雅黑',
+                readOnly: true,
+            });
+            contractSpread.bind(spreadNS.Events.ButtonClicked, function(e, info) {
+                if (!info.sheet.zh_setting || !info.sheet.zh_tree) 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 = info.sheet.zh_tree.getPosterity(node);
+                    for (const p of posterity) {
+                        p.selected = node.selected;
+                    }
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row, posterity.length + 1);
+                } else {
+                    SpreadJsObj.reLoadRowData(info.sheet, info.row, 1);
+                }
+                if (node.selected) {
+                    const rows = [];
+                    const parents = info.sheet.zh_tree.getAllParents(node);
+                    for (const p of parents) {
+                        p.selected = true;
+                        rows.push(info.sheet.zh_tree.getNodeIndex(p));
+                    }
+                    SpreadJsObj.reLoadRowsData(info.sheet, rows);
+                }
+            });
+            $('#select-contract').on('shown.bs.modal', function() {
+                contractSpread.refresh();
+            });
+        }
+    };
+    const loadContractTree = function(type, contractType, tid) {
+        postData(`/sp/${spid}/load`, { filter: 'contract', type, contractType, tid }, function(result) {
+            contractTree = createNewPathTree('base', { id: 'contract_id', pid: 'contract_pid', order: 'order', level: 'level', fullPath: 'full_path', rootId: -1 });
+            contractTree.loadDatas(result.contract);
+            SpreadJsObj.loadSheetData(contractSheet, SpreadJsObj.DataType.Tree, contractTree);
+        });
+    };
+    const update = function() {
+        const updateData = {
+            contractTree: contractTree.nodes.filter(x => {
+                return x.c_code === undefined;
+            }).map(x => {
+                return {id: x.id, selected: x.selected};
+            }),
+            contract: contractTree.nodes.filter(x => {
+                return x.c_code !== undefined;
+            }).map(x => {
+                return {id: x.id, selected: x.selected};
+            })
+        };
+        postData(`/sp/${spid}/save`, { updateType: 'contract', updateData }, function(result) {
+            $('#select-contract').modal('hide');
+        });
+    };
+    const showContract = function(type, contractType, id) {
+        initSpread();
+        loadContractTree(type, contractType, id);
+        $('#select-contract').modal('show');
+    };
+    return { showContract, update };
+})();

+ 1 - 0
app/router.js

@@ -266,6 +266,7 @@ module.exports = app => {
     app.get('/sp/:id/data', sessionAuth, subProjectCheck, 'subProjController.dataIndex');
     app.post('/sp/:id/info/save', sessionAuth, subProjectCheck, 'subProjController.saveInfo');
     app.post('/sp/:id/load', sessionAuth, subProjectCheck, 'subProjController.load');
+    app.post('/sp/:id/save', sessionAuth, subProjectCheck, 'subProjController.saveSubProjOther');
     // 阶段进度
     app.get('/sp/:id/progress', sessionAuth, subProjectCheck, 'subProjController.progress');
     app.post('/sp/:id/progress/update', sessionAuth, subProjectCheck, 'subProjController.progressUpdate');

+ 16 - 0
app/service/contract.js

@@ -267,6 +267,22 @@ module.exports = app => {
         // async getCountByUser(stid, type, user) {
         //
         // }
+
+        async savePresetData(updateData) {
+            const contract = updateData.contract.map(x => { return { id: x.id, selected: x.selected ? 1 : 0 }});
+            const contractTree = updateData.contractTree.map(x => { return { id: x.id, selected: x.selected ? 1 : 0 }});
+            if (contract.length === 0 && contractTree.length === 0) return;
+
+            const conn = await this.db.beginTransaction();
+            try {
+                if (contract.length > 0) await conn.updateRows(this.tableName, contract);
+                if (contractTree.length > 0) await conn.updateRows(this.ctx.service.contractTree.tableName, contractTree);
+                await conn.commit();
+            } catch(err) {
+                this.ctx.log(err);
+                await conn.rollback();
+            }
+        }
     }
     return Contract;
 };

+ 1 - 1
app/service/contract_pay.js

@@ -27,7 +27,7 @@ module.exports = app => {
 
         async _isSettlePriceEnabled(options, node) {
             const colSet = await this.ctx.service.contractColSet.getContractColSet(
-                options.spid || null,
+                options.spid || this.ctx.subProject.id || null,
                 options.tid || null,
                 node.contract_type
             );

+ 2 - 0
app/service/contract_sp_audit.js

@@ -84,6 +84,8 @@ module.exports = app => {
                     const lastAuditors = auditList.filter(x => { return x.audit_order === auditList.length - 1; });
                     if (shenpiInfo && (lastAuditors.length === 0 || (lastAuditors.length > 1 || shenpiInfo.audit_id !== lastAuditors[0].aid))) {
                         await this.updateLastAudit(contract, type, auditList, shenpiInfo.audit_id);
+                        await this.loadUser(contract, type);
+                        await this.loadAuditViewData(contract, type);
                     } else if (!shenpiInfo) {
                         // 不存在终审人的状态下这里恢复为授权审批人
                         shenpi.contract = shenpiConst.sp_status.sqspr;

+ 2 - 2
app/service/ledger.js

@@ -879,9 +879,9 @@ module.exports = app => {
                     x.add_times = 0;
                 });
                 await conn.delete(this.tableName, { tender_id: this.ctx.tender.id });
-                await conn.insert(this.ctx.service.ledger.tableName, ledger);
+                if (ledger.length > 0) await conn.insert(this.ctx.service.ledger.tableName, ledger);
                 await conn.delete(this.ctx.service.pos.tableName, { tid: this.ctx.tender.id });
-                await conn.insert(this.ctx.service.pos.tableName, pos);
+                if (pos.length > 0) await conn.insert(this.ctx.service.pos.tableName, pos);
                 this._removeCacheMaxLid(this.ctx.tender.id);
                 await conn.commit();
             } catch (err) {

+ 10 - 4
app/service/project_col_set.js

@@ -8,7 +8,7 @@
  * @version
  */
 
-const JsonFields = [ 'info' ];
+const JsonFields = [ 'info', 'list' ];
 const ProjectSetting = require('../const/project_setting');
 
 module.exports = app => {
@@ -28,7 +28,13 @@ module.exports = app => {
 
         _analysisData(data) {
             if (!data) return;
-            JsonFields.forEach(jf => { if(data[jf]) data[jf] = JSON.parse(data[jf]); });
+            JsonFields.forEach(jf => {
+                if (data[jf]) {
+                    data[jf] = JSON.parse(data[jf]);
+                } else {
+                    data[jf] = JSON.parse(JSON.stringify(data.spid ? ProjectSetting.defaultColSet[jf] : ProjectSetting.defaultProjectColSet[jf]));
+                }
+            });
         }
 
         async loadProjectColSet(pid, spid = '') {
@@ -48,8 +54,8 @@ module.exports = app => {
         async getProjectColSet(pid, spid) {
             const curSet = await this.loadProjectColSet(pid, spid);
             if (curSet) return curSet;
-            await this.initProjectColSet(pid, spid);
-            return await this.loadProjectColSet(pid, spid);
+            await this.initProjectColSet(pid, spid, colSetType);
+            return await this.loadProjectColSet(pid, spid, colSetType);
         }
 
         async setProjectColSet(pid, spid, colSetType, colSet) {

+ 12 - 9
app/view/dashboard/workspace.ejs

@@ -533,54 +533,57 @@
                                                         </tr>
                                                     <% } else if (db.shenpi_type === 'contract') { %>
                                                         <% const ct = db; %>
+                                                        <% const ctDetailUrl = '/sp/' + ct.spid + '/contract/detail' + (ct.contract_type === 2 ? '/income' : ''); %>
                                                         <tr data-type="contract">
                                                             <% if (ct.start_audit === 2) { %><td class="text-center text-danger"><i class="fa fa-exclamation-triangle"></i></td><% } else if (ct.start_audit === 1) { %><td class="text-center text-warning"><i class="fa fa-bell"></i></td><% } else { %><td></td><% } %>
                                                             <td><span class="badge text-width" style="background-color: #e3f2fd; color: #1976d2;">合同审批</span></td>
                                                             <td><a href="/sp/<%- ct.spid %>/dashboard"><%- ct.sp_name || '项目合同' %></a></td>
                                                             <td>
                                                                 <% if (ct.is_project_contract) { %>
-                                                                    <a href="/contract/<%- ct.id %>"><%- ct.name %></a>
+                                                                    <a href="<%= ctDetailUrl %>"><%- ct.name %></a>
                                                                 <% } else { %>
-                                                                    <a href="/tender/<%- ct.tid %>"><%- ct.tender_name %></a> - <a href="/contract/<%- ct.id %>"><%- ct.name %></a>
+                                                                    <a href="/tender/<%- ct.tid %>"><%- ct.tender_name %></a> - <a href="<%= ctDetailUrl %>"><%- ct.name %></a>
                                                                 <% } %>
                                                             </td>
                                                             <td><%- ct.status_text %></td>
                                                             <td><%- (ct.begin_time ? ctx.moment(ct.begin_time).format('YYYY/MM/DD HH:mm') : '') %></td>
-                                                            <td><a href="/contract/<%- ct.id %>/audit" class="btn btn-outline-primary btn-sm btn-table" role="button">审批</a></td>
+                                                            <td><a href="<%= ctDetailUrl %>" class="btn btn-outline-primary btn-sm btn-table" role="button">审批</a></td>
                                                         </tr>
                                                     <% } else if (db.shenpi_type === 'contractPay') { %>
                                                         <% const cp = db; %>
+                                                        <% const cpDetailUrl = '/sp/' + cp.spid + '/contract/detail' + (cp.contract_type === 2 ? '/income' : '') + '?tab=pay'; %>
                                                         <tr data-type="contract">
                                                             <% if (cp.start_audit === 2) { %><td class="text-center text-danger"><i class="fa fa-exclamation-triangle"></i></td><% } else if (cp.start_audit === 1) { %><td class="text-center text-warning"><i class="fa fa-bell"></i></td><% } else { %><td></td><% } %>
                                                             <td><span class="badge text-width" style="background-color: #fff3e0; color: #f57c00;">合同支付</span></td>
                                                             <td><a href="/sp/<%- cp.spid %>/dashboard"><%- cp.sp_name %></a></td>
                                                             <td>
                                                                 <% if (cp.is_project_contract) { %>
-                                                                    <a href="/contract/<%- cp.cid %>"><%- cp.contract_name %></a> - 第<%- cp.times %>期
+                                                                    <a href="<%= cpDetailUrl %>"><%- cp.contract_name %></a> - 第<%- cp.times %>期
                                                                 <% } else { %>
-                                                                    <a href="/tender/<%- cp.tid %>"><%- cp.tender_name %></a> - <a href="/contract/<%- cp.cid %>"><%- cp.contract_name %></a> - 第<%- cp.times %>期
+                                                                    <a href="/tender/<%- cp.tid %>"><%- cp.tender_name %></a> - <a href="<%= cpDetailUrl %>"><%- cp.contract_name %></a> - 第<%- cp.times %>期
                                                                 <% } %>
                                                             </td>
                                                             <td>支付</td>
                                                             <td><%- (cp.begin_time ? ctx.moment(cp.begin_time).format('YYYY/MM/DD HH:mm') : '') %></td>
-                                                            <td><a href="/contract/<%- cp.cid %>/pay/<%- cp.id %>/audit" class="btn btn-outline-primary btn-sm btn-table" role="button">审批</a></td>
+                                                            <td><a href="<%= cpDetailUrl %>" class="btn btn-outline-primary btn-sm btn-table" role="button">审批</a></td>
                                                         </tr>
                                                     <% } else if (db.shenpi_type === 'contractReceipt') { %>
                                                         <% const cr = db; %>
+                                                        <% const crDetailUrl = '/sp/' + cr.spid + '/contract/detail' + (cr.contract_type === 2 ? '/income' : '') + '?tab=pay'; %>
                                                         <tr data-type="contract">
                                                             <% if (cr.start_audit === 2) { %><td class="text-center text-danger"><i class="fa fa-exclamation-triangle"></i></td><% } else if (cr.start_audit === 1) { %><td class="text-center text-warning"><i class="fa fa-bell"></i></td><% } else { %><td></td><% } %>
                                                             <td><span class="badge text-width" style="background-color: #e8f5e9; color: #388e3c;">合同回款</span></td>
                                                             <td><a href="/sp/<%- cr.spid %>/dashboard"><%- cr.sp_name %></a></td>
                                                             <td>
                                                                 <% if (cr.is_project_contract) { %>
-                                                                    <a href="/contract/<%- cr.cid %>"><%- cr.contract_name %></a> - 第<%- cr.times %>期
+                                                                    <a href="<%= crDetailUrl %>"><%- cr.contract_name %></a> - 第<%- cr.times %>期
                                                                 <% } else { %>
-                                                                    <a href="/tender/<%- cr.tid %>"><%- cr.tender_name %></a> - <a href="/contract/<%- cr.cid %>"><%- cr.contract_name %></a> - 第<%- cr.times %>期
+                                                                    <a href="/tender/<%- cr.tid %>"><%- cr.tender_name %></a> - <a href="<%= crDetailUrl %>"><%- cr.contract_name %></a> - 第<%- cr.times %>期
                                                                 <% } %>
                                                             </td>
                                                             <td>回款</td>
                                                             <td><%- (cr.begin_time ? ctx.moment(cr.begin_time).format('YYYY/MM/DD HH:mm') : '') %></td>
-                                                            <td><a href="/contract/<%- cr.cid %>/pay/<%- cr.id %>/audit" class="btn btn-outline-primary btn-sm btn-table" role="button">审批</a></td>
+                                                            <td><a href="<%= crDetailUrl %>" class="btn btn-outline-primary btn-sm btn-table" role="button">审批</a></td>
                                                         </tr>
                                                     <% } %>
                                                 <% } %>

+ 10 - 0
app/view/report/index.ejs

@@ -118,6 +118,15 @@
                     <div class="dropdown" id="divPresets">
                         <button class="btn btn-sm btn-light dropdown-toggle text-primary" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">报表数据预设</button>
                         <div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+                            <% if (stg_id === -600) { %>
+                            <% if (tender_id > 0) { %>
+                            <a class="dropdown-item" href="javascript: void(0)" onclick="rptContract.showContract('tender', 1, <%- tender_id %>);">选择标段支出合同</a>
+                            <a class="dropdown-item" href="javascript: void(0)" onclick="rptContract.showContract('tender', 2, <%- tender_id %>);">选择标段收入合同</a>
+                            <% } else { %>
+                            <a class="dropdown-item" href="javascript: void(0)" onclick="rptContract.showContract('project', 1);">选择项目支出合同</a>
+                            <a class="dropdown-item" href="javascript: void(0)" onclick="rptContract.showContract('project', 2);">选择项目收入合同</a>
+                            <% } %>
+                            <% } else { %>
                             <a class="dropdown-item" href="javascript: void(0)" onclick="rptShowLevel.show('show_level');">显示层级</a>
                             <a class="dropdown-item" href="javascript: void(0)" onclick="rptOtherStage.showOtherStage('advance');">预付款</a>
                             <a class="dropdown-item" href="javascript: void(0)" onclick="rptChangeRela.showChangeRela('change_project');">变更立项</a>
@@ -127,6 +136,7 @@
                             <% if (viewPmData) { %>
                             <a class="dropdown-item" href="javascript: void(0)" onclick="rptOtherStage.showOtherStage('pm_deal');">项目管理-合同管理</a>
                             <% } %>
+                            <% } %>
                         </div>
                     </div>
                 </div>

+ 16 - 0
app/view/report/rpt_all_popup.ejs

@@ -738,6 +738,22 @@
         </div>
     </div>
 </div>
+<div class="modal fade" id="select-contract" data-backdrop="static">
+    <div class="modal-dialog" role="document" style="max-width: 600px">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">选择合同</h5>
+            </div>
+            <div class="modal-body">
+                <div class="modal-height-400" id="rpt-contract-spread"></div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">取消</button>
+                <button type="button" class="btn btn-sm btn-primary" id="sc-ok" onclick="rptContract.update();">确定</button>
+            </div>
+        </div>
+    </div>
+</div>
 <!--管理推荐报表-->
 <div class="modal fade" id="man-c" data-backdrop="static">
     <div class="modal-dialog" role="document">

+ 4 - 1
app/view/sub_proj/index.ejs

@@ -13,6 +13,7 @@
                 <a href="#add-folder" name="add" data-toggle="modal" data-target="#add-folder" class="btn btn-sm btn-primary">新建文件夹</a>
                 <a href="#add-project" name="add" data-toggle="modal" data-target="#add-project" class="btn btn-sm btn-primary ml-2">新建项目</a>
                 <a href="/file/template" class="btn btn-sm btn-primary ml-2">资料模板库</a>
+                <a href="#col-set" class="btn btn-sm btn-primary ml-1" data-toggle="modal" data-target="#col-set">列设置</a>
                 <% } %>
             </div>
         </div>
@@ -26,7 +27,8 @@
                 </div>
                 <% } else { %>
                 <table class="table table-bordered">
-                    <tr class="text-center"><th style="min-width: 300px">项目名称</th><th width="8%">概预算标准</th><th width="8%">创建时间</th><th width="5%">标段个数</th><th width="5%">文件个数<i class="fa fa-question-circle text-primary" data-placement="bottom" data-toggle="tooltip" data-original-title="指资料归集的所有文件个数"></i></th><th width="20%">管理单位</th><% if (ctx.session.sessionUser.is_admin) { %><th width="20%">操作</th><% } %></tr>
+                    <thead id="projectListHeader">
+                    </thead>
                     <tbody id="projectList">
                     </tbody>
                 </table>
@@ -39,4 +41,5 @@
     autoFlashHeight();
     const projectList = JSON.parse(unescape('<%- escape(JSON.stringify(projectList)) %>'));
     const canEdit = <%- !!ctx.session.sessionUser.is_admin %>;
+    const colSet = JSON.parse('<%- JSON.stringify(colSet) %>');
 </script>

+ 57 - 0
app/view/sub_proj/modal.ejs

@@ -220,8 +220,65 @@
             </div>
         </div>
     </div>
+</div><!--弹出列设置-->
+<div class="modal fade" id="col-set" 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">
+                <table class="table table-hover table-bordered">
+                    <thead><tr class="text-center">
+                        <th width="150px">列名</th>
+                        <th width="50px">显示</th>
+                    </tr></thead>
+                    <tbody class="text-center">
+                    <% for (const cs of colSet) { %>
+                    <tr code="<%- cs.field %>">
+                        <td><%- cs.name %><% if (cs.hint) { %><i class="fa fa-question-circle text-primary ml-1" data-placement="bottom" data-toggle="tooltip" data-original-title="<%- cs.hint %>"></i><% } %></td>
+                        <% if (cs.fixed.indexOf('show') >= 0 ) { %>
+                        <td>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- cs.code %>" checked="" disabled="">
+                                <label class="form-check-label" for="inlineCheckbox-<%- cs.code %>"></label>
+                            </div>
+                        </td>
+                        <% } else { %>
+                        <td>
+                            <div class="form-check form-check-inline">
+                                <input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- cs.code %>" <% if (cs.show) { %> checked <% } %> >
+                                <label class="form-check-label" for="inlineCheckbox-<%- cs.code %>"></label>
+                            </div>
+                        </td>
+                        <% } %>
+                    </tr>
+                    <% } %>
+                    </tbody>
+                </table>
+                <div class="text-danger">请勿全选(页面显示不下);谨慎修改别名(修改后,各页面概念将不一致)</div>
+            </div>
+            <form class="modal-footer" action="/subproj/col-set" method="post" onsubmit="return onSetCol();">
+                <input type="hidden" name="_csrf_j" value="<%= ctx.csrf %>">
+                <input type="hidden" name="col_set" value="">
+                <input type="hidden" name="col_type" value="list">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+                <button type="submit" class="btn btn-primary btn-sm" id="add-bd-ok">确定修改</button>
+            </form>
+        </div>
+    </div>
 </div>
 <script>
+    const onSetCol = function() {
+        const trs = $('tr[code]', '#col-set');
+        const colSet = [];
+        for (const tr of trs) {
+            colSet.push({ field: $(tr).attr('code'), show: $('input[type=checkbox]', tr)[0].checked, alias: ''});
+        }
+        $('input[name=col_set]').val(JSON.stringify(colSet));
+    };
+</script>
+<script>
     const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');
     const accountGroup = JSON.parse('<%- JSON.stringify(accountGroup) %>');
     const permissionConst = JSON.parse('<%- JSON.stringify(permissionConst) %>');

+ 1 - 0
config/web.js

@@ -953,6 +953,7 @@ const JsFiles = {
                     '/public/report/js/rpt_change_rela.js',
                     '/public/report/js/rpt_other_stage.js',
                     '/public/report/js/rpt_show_level.js',
+                    '/public/report/js/rpt_contract.js',
                 ],
                 mergeFile: 'report_main',
             },

+ 9 - 0
sql/update.sql

@@ -26,6 +26,15 @@ ADD COLUMN `settle_price` decimal(30, 8) NOT NULL DEFAULT 0 COMMENT '结算金
 ALTER TABLE `zh_contract_pay`
 ADD COLUMN `rela_stage` varchar(255) NULL DEFAULT NULL COMMENT '关联的标段期id或者独立支付期id' AFTER `remark`;
 
+ALTER TABLE `zh_project_col_set`
+ADD COLUMN `list` json NULL COMMENT '列表页列设置(见/const/project_setting)' AFTER `info`;
+
+ALTER TABLE `zh_contract`
+ADD COLUMN `selected` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '报表用,是否选择' AFTER `create_time`;
+
+ALTER TABLE `zh_contract_tree`
+ADD COLUMN `selected` tinyint(4) UNSIGNED NOT NULL DEFAULT 0 COMMENT '报表用,是否选择' AFTER `calc2`;
+
 ------------------------------------
 -- 表数据
 ------------------------------------