Selaa lähdekoodia

自定义列设置

MaiXinRong 1 vuosi sitten
vanhempi
commit
24a4628078

+ 378 - 0
app/const/spread.js

@@ -20,6 +20,379 @@ const thirdPartyCols = {
 };
 const minusNoValueCols = ['qc_minus_qty'];
 
+// 未定义列的基础spread配置
+const EmptySpreadSetting = {
+    tz_ledger_set: {
+        bills: {
+            cols: [],
+            emptyRows: 3,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+        },
+        pos: {
+            cols: [],
+            emptyRows: 3,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            headColWidth: [30],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+        }
+    },
+    tz_stage_set: {
+        bills: {
+            cols: [],
+            emptyRows: 0,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+            frozenColCount: 5,
+            frozenLineColor: '#93b5e4',
+        },
+        pos: {
+            cols: [],
+            emptyRows: 3,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            headColWidth: [30],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+        }
+    },
+    gcl_ledger_set:{
+        bills: {
+            cols: [],
+            emptyRows: 3,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+        },
+        pos: {
+            cols: [],
+            emptyRows: 3,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            headColWidth: [30],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+        }
+    },
+    gcl_stage_set: {
+        bills: {
+            cols: [],
+            emptyRows: 0,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+            frozenColCount: 5,
+            frozenLineColor: '#93b5e4',
+        },
+        pos: {
+            cols: [],
+            emptyRows: 3,
+            headRows: 2,
+            headRowHeight: [25, 25],
+            headColWidth: [30],
+            defaultRowHeight: 21,
+            headerFont: '12px 微软雅黑',
+            font: '12px 微软雅黑',
+        }
+    },
+};
+// 可设置列集合
+const BaseSetCol = {
+    Ledger: [
+        { key: 'code', name: '项目节编号', fixed: ['move', 'valid', 'alias'], bills: 1, pos: 0, },
+        { key: 'b_code', name: '清单编号', fixed: ['alias'], bills: 1, pos: 0, },
+        { key: 'ex_memo1', name: '备注1', fixed: [], bills: 1, pos: 1, },
+        { key: 'name', name: '名称/计量单元', fixed: ['alias'], bills: 1, pos: 1 },
+        { key: 'position', name: '位置', fixed: [], bills: 0, pos: 1 },
+        { key: 'unit', name: '单位', fixed: ['alias'], bills: 1, pos: 0 },
+        { key: 'unit_price', name: '单价', fixed: ['alias'], bills: 1, pos: 0 },
+        { key: 'dgn_qty', name: '项目节数量', fixed: [], bills: 1, pos: 0, },
+        { key: 'dgn_price', name: '经济指标', fixed: ['alias'], bills: 1, pos: 0},
+        { key: 'deal_calc', name: '签约', fixed: [], bills: 1, pos: 1, },
+        { key: 'tz_calc', name: '台账', fixed: ['alias'], bills: 1, pos: 1, },
+        { key: 'drawing_code', name: '图册号', fixed: ['alias'], bills: 1, pos: 1 },
+        { key: 'node_type', name: '费用类别', fixed: ['alias'], bills: 1, pos: 0 },
+        { key: 'memo', name: '备注', fixed: ['alias'], bills: 1, pos: 0 },
+        { key: 'ex_memo2', name: '备注2', fixed: [], bills: 1, pos: 1, },
+        { key: 'ex_memo3', name: '备注3', fixed: [], bills: 1, pos: 1, },
+    ],
+    Stage: [
+        { key: 'code', name: '项目节编号', fixed: ['move', 'valid', 'alias'], bills: 1, pos: 0 },
+        { key: 'b_code', name: '清单编号', fixed: ['alias'], bills: 1, pos: 0 },
+        { key: 'ex_memo1', name: '备注1', fixed: [], bills: 1, pos: 1, },
+        { key: 'name', name: '名称/计量单元', fixed: ['alias'], bills: 1, pos: 1 },
+        { key: 'position', name: '位置', fixed: [], bills: 0, pos: 1},
+        { key: 'unit', name: '单位', fixed: ['alias'], bills: 1, pos: 0 },
+        { key: 'unit_price', name: '单价', fixed: ['alias'], bills: 1, pos: 0 },
+        { key: 'deal_calc', name: '签约', fixed: [], bills: 1, pos: 0 },
+        { key: 'tz_calc', name: '台账', fixed: [], bills: 1, pos: 1},
+        { key: 'real_qty', name: '现场实际数量', fixed: ['valid', 'alias'], bills: 0, pos: 1},
+        { key: 'estimate_qty', name: '预计变更数量', fixed: ['valid', 'alias'], bills: 0, pos: 1},
+        { key: 'cur_calc', name: '本期计量', fixed: ['alias'], bills: 1, pos: 1, },
+        { key: 'end_calc', name: '截止本期计量', fixed: ['alias'], bills: 1, pos: 1, },
+        { key: 'deal_dgn_qty', name: '合同-项目节数量', fixed: [], bills: 1, pos: 0, },
+        { key: 'c_dgn_qty', name: '签约-项目节数量', fixed: [], bills: 1, pos: 0, },
+        { key: 'final_dgn_price', name: '经济指标', fixed: [], bills: 1, pos: 0 },
+        { key: 'postil', name: '本期批注', fixed: [], bills: 1, pos: 1 },
+        { key: 'drawing_code', name: '图册号', fixed: [], bills: 1, pos: 1 },
+        { key: 'memo', name: '备注', fixed: [], bills: 1, pos: 0 },
+        { key: 'ex_memo2', name: '备注2', fixed: [], bills: 1, pos: 1, },
+        { key: 'ex_memo3', name: '备注3', fixed: [], bills: 1, pos: 1, },
+        { key: 'is_tp', name: '总额计量', fixed: [], bills: 1, pos: 0 },
+        { key: 'gxby', name: '工序报验', fixed: ['valid', 'alias'], bills: 1, pos: 1, },
+        { key: 'dagl', name: '档案管理', fixed: ['valid', 'alias'], bills: 1, pos: 1, },
+    ],
+};
+// 默认的列设置
+const ProjectSpreadTemplate = {
+    tz_ledger_set: [
+        { key: 'code', valid: 1 },
+        { key: 'b_code', valid: 1 },
+        { key: 'ex_memo1', valid: 0 },
+        { key: 'name', valid: 1 },
+        { key: 'position', valid: 1 },
+        { key: 'unit', valid: 1 },
+        { key: 'unit_price', valid: 1 },
+        { key: 'dgn_qty', valid: 1 },
+        { key: 'dgn_price', valid: 1 },
+        { key: 'deal_calc', valid: 0, },
+        { key: 'tz_calc', valid: 1 },
+        { key: 'drawing_code', valid: 1 },
+        { key: 'node_type', valid: 1 },
+        { key: 'memo', valid: 1 },
+        { key: 'ex_memo2', valid: 0 },
+        { key: 'ex_memo3', valid: 0 },
+    ],
+    tz_stage_set: [
+        { key: 'code', valid: 1 },
+        { key: 'b_code', valid: 1 },
+        { key: 'ex_memo1', valid: 0 },
+        { key: 'name', valid: 1 },
+        { key: 'position', valid: 1 },
+        { key: 'unit', valid: 1 },
+        { key: 'unit_price', valid: 1 },
+        { key: 'deal_calc', valid: 0, },
+        { key: 'tz_calc', valid: 1 },
+        { key: 'real_qty', valid: 1 },
+        { key: 'estimate_qty', valid: 1 },
+        { key: 'cur_calc', valid: 1 },
+        { key: 'end_calc', valid: 1 },
+        { key: 'deal_dgn_qty', valid: 1 },
+        { key: 'c_dgn_qty', valid: 1 },
+        { key: 'final_dgn_price', valid: 1 },
+        { key: 'postil', valid: 1},
+        { key: 'drawing_code', valid: 1 },
+        { key: 'memo', valid: 1 },
+        { key: 'ex_memo2', valid: 0 },
+        { key: 'ex_memo3', valid: 0 },
+        { key: 'is_tp', valid: 1},
+        { key: 'gxby', valid: 1},
+        { key: 'dagl', valid: 1},
+    ],
+    gcl_ledger_set: [
+        { key: 'code', valid: 1 },
+        { key: 'b_code', valid: 1 },
+        { key: 'ex_memo1', valid: 0 },
+        { key: 'name', valid: 1 },
+        { key: 'position', valid: 1 },
+        { key: 'unit', valid: 1 },
+        { key: 'unit_price', valid: 1 },
+        { key: 'dgn_qty', valid: 1 },
+        { key: 'dgn_price', valid: 1 },
+        { key: 'deal_calc', valid: 1, },
+        { key: 'tz_calc', valid: 0 },
+        { key: 'drawing_code', valid: 1 },
+        { key: 'node_type', valid: 1 },
+        { key: 'memo', valid: 1 },
+        { key: 'ex_memo2', valid: 0 },
+        { key: 'ex_memo3', valid: 0 },
+    ],
+    gcl_stage_set: [
+        { key: 'code', valid: 1 },
+        { key: 'b_code', valid: 1 },
+        { key: 'ex_memo1', valid: 0 },
+        { key: 'name', valid: 1 },
+        { key: 'position', valid: 1 },
+        { key: 'unit', valid: 1 },
+        { key: 'unit_price', valid: 1 },
+        { key: 'deal_calc', valid: 0, },
+        { key: 'tz_calc', valid: 1 },
+        { key: 'real_qty', valid: 1 },
+        { key: 'estimate_qty', valid: 1 },
+        { key: 'cur_calc', valid: 1 },
+        { key: 'end_calc', valid: 1 },
+        { key: 'deal_dgn_qty', valid: 1 },
+        { key: 'c_dgn_qty', valid: 1 },
+        { key: 'final_dgn_price', valid: 1 },
+        { key: 'postil', valid: 1},
+        { key: 'drawing_code', valid: 1 },
+        { key: 'memo', valid: 1 },
+        { key: 'ex_memo2', valid: 0 },
+        { key: 'ex_memo3', valid: 0 },
+        { key: 'is_tp', valid: 1},
+        { key: 'gxby', valid: 1},
+        { key: 'dagl', valid: 1},
+    ],
+};
+const BaseSpreadColSetting = {
+    Ledger: {
+        bills: {
+            code: [{title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', cellType: 'tree'}],
+            b_code: [{title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@',}],
+            name: [{title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', }],
+            unit: [{title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 50, formatter: '@', cellType: 'unit'}],
+            unit_price: [{title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number',}],
+            dgn_qty: [
+                {title: '项目节数量|数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'dgn_qty1', hAlign: 2, width: 60, type: 'Number', aliasFormat: '{%s}|数量1'},
+                {title: '|数量2',  colSpan: '|1', rowSpan: '|1', field: 'dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
+            ],
+            dgn_price: [{title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'dgn_price', hAlign: 2, width: 60, type: 'Number', readOnly: true}],
+            deal_calc: [
+                {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, type: 'Number', aliasFormat: '{%s}|数量'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            ],
+            tz_calc: [
+                {title: '设计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sgfh_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+                {title: '设计错漏增减|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sjcl_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sjcl_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+                {title: '其他错漏增减|数量', colSpan: '2|1', rowSpan: '1|1', field: 'qtcl_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qtcl_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+                {title: '台账小计|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            ],
+            drawing_code: [{title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'}],
+            node_type: [{title: '费用类别', colSpan: '1', rowSpan: '2', field: 'node_type', hAlign: 0, width: 100, cellType: 'customizeCombo'}],
+            memo: [{title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            ex_memo1: [{title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}, ],
+            ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}, ],
+            ex_memo3: [{title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}]
+        },
+        pos: {
+            name: [{title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 230, formatter: '@'}],
+            position: [{title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@'}],
+            tz_calc: [
+                {title: '台账数量|设计量', colSpan: '4|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 100, type: 'Number'},
+                {title: '|设计错漏增减', colSpan: '|1', rowSpan: '|1', field: 'sjcl_qty', hAlign: 2, width: 100, type: 'Number'},
+                {title: '|其他错漏增减', colSpan: '|1', rowSpan: '|1', field: 'qtcl_qty', hAlign: 2, width: 100, type: 'Number'},
+                {title: '|小计', colSpan: '|1', rowSpan: '|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            ],
+            drawing_code: [{title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'}],
+            node_type: [{title: '费用类别', colSpan: '1', rowSpan: '2', field: 'node_type', hAlign: 0, width: 100, cellType: 'customizeCombo'}],
+            ex_memo1: [{title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            ex_memo3: [{title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}]
+        }
+    },
+    Stage: {
+        bills: {
+            code: [{title: '项目节编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 145, formatter: '@', readOnly: true, cellType: 'tree'}],
+            b_code: [{title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@', readOnly: true}],
+            name: [{title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@', readOnly: true}],
+            unit: [{title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: true, cellType: 'unit'}],
+            unit_price: [{title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'}],
+            tz_calc: [
+                {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, readOnly: true, type: 'Number', aliasFormat: '{%s}|数量'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'total_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            ],
+            deal_calc: [
+                {title: '签约|数量', colSpan: '2|1', rowSpan: '1|1', field: 'deal_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number', aliasFormat: '{%s}|数量'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'deal_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            ],
+            cur_calc: [
+                {title: '本期合同计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'contract_tp', hAlign: 2, width: 60, type: 'Number'},
+                {title: '本期数量变更|数量', colSpan: '3|1', rowSpan: '1|1', field: 'qc_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'qc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+                {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '本期补差|原单价', colSpan: '2|1', rowSpan: '1|1', field: 'org_price', hAlign: 2, width: 60, type: 'Number', readOnly: true },
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'pc_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true },
+                {title: '本期完成计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'gather_qty', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+                {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'gather_tp', hAlign: 2, width: 60, readOnly: true, type: 'Number'},
+            ],
+            end_calc: [
+                {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'},
+            ],
+            deal_dgn_qty: [
+                {title: '合同|项目节数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'deal_dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|项目节数量2',  colSpan: '|1', rowSpan: '|1', field: 'deal_dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
+            ],
+            c_dgn_qty: [
+                {title: '变更|项目节数量1',  colSpan: '2|1', rowSpan: '1|1', field: 'c_dgn_qty1', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|项目节数量2',  colSpan: '|1', rowSpan: '|1', field: 'c_dgn_qty2', hAlign: 2, width: 60, type: 'Number'},
+            ],
+            final_dgn_price: [{title: '经济指标',  colSpan: '1', rowSpan: '2', field: 'final_dgn_price', hAlign: 2, width: 60, readOnly: true, type: 'Number'},],
+            is_tp: [{title: '总额计量', colSpan: '1', rowSpan: '2', field: 'is_tp', hAlign: 1, width: 60, cellType: 'checkbox'}],
+            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
+            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
+            drawing_code: [{title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'}],
+            memo: [{title: '备注', colSpan: '1', rowSpan: '2', field: 'memo', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            postil: [{title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 100, formatter: '@', cellType: 'autoTip'},],
+            ex_memo1: [{title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            ex_memo3: [{title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+        },
+        pos: {
+            name: [{title: '计量单元', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: true}],
+            position: [{title: '位置', colSpan: '1', rowSpan: '2', field: 'position', hAlign: 0, width: 60, formatter: '@', readOnly: true}],
+            tz_calc: [{title: '台账数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 60, formatter: '@', readOnly: true, aliasFormat: '{%s}数量'}],
+            real_qty: [{title: '现场实际数量', colSpan: '1', rowSpan: '2', field: 'real_qty', hAlign: 2, width: 60, type: 'Number'}],
+            estimate_qty: [{title: '预计变更数量', colSpan: '1', rowSpan: '2', field: 'estimate_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true}],
+            cur_calc: [
+                {title: '本期计量|合同', colSpan: '4|1', rowSpan: '1|1', field: 'contract_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|数量变更', colSpan: '|1', rowSpan: '|1', field: 'qc_qty', hAlign: 2, width: 80, type: 'Number'},
+                {title: '|不计价', colSpan: '|1', rowSpan: '|1', field: 'qc_minus_qty', hAlign: 2, width: 60, type: 'Number'},
+                {title: '|完成', colSpan: '|1', rowSpan: '|1', field: 'gather_qty', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+            ],
+            end_calc: [
+                {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},
+            ],
+            postil: [{title: '本期批注', colSpan: '1', rowSpan: '2', field: 'postil', hAlign: 0, width: 80, formatter: '@', cellType: 'autoTip'},],
+            drawing_code: [{title: '图(册)号', colSpan: '1', rowSpan: '2', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'}],
+            ex_memo1: [{title: 'ex_memo1', colSpan: '1', rowSpan: '2', field: 'ex_memo1', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            ex_memo2: [{title: 'ex_memo2', colSpan: '1', rowSpan: '2', field: 'ex_memo2', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            ex_memo3: [{title: 'ex_memo3', colSpan: '1', rowSpan: '2', field: 'ex_memo3', hAlign: 0, width: 100, formatter: '@', cellType: 'ellipsisAutoTip'}],
+            add_stage_order: [{title: '添加期数', colSpan: '1', rowSpan: '2', field: 'add_stage_order', hAlign:1, width: 80, readOnly: true}],
+            gxby: [{title: '工序报验', colSpan: '1', rowSpan: '2', field: 'gxby', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
+            dagl: [{title: '档案管理', colSpan: '1', rowSpan: '2', field: 'dagl', hAlign: 1, width: 80, formatter: '@', readOnly: true}],
+        }
+    }
+};
+const withoutClReplace = {
+    indexField: 'sgfh_qty',
+    remove: ['sgfh_qty', 'sgfh_tp', 'sjcl_qty', 'sjcl_tp', 'qtcl_qty', 'qtcl_tp', 'quantity', 'total_price'],
+    billsCols: [
+        {title: '设计量|数量', colSpan: '2|1', rowSpan: '1|1', field: 'sgfh_qty', hAlign: 2, width: 60, type: 'Number'},
+        {title: '|金额', colSpan: '|1', rowSpan: '|1', field: 'sgfh_tp', hAlign: 2, width: 60, type: 'Number', readOnly: true},
+    ],
+    posCols: [
+        {title: '设计量', colSpan: '1', rowSpan: '2', field: 'sgfh_qty', hAlign: 2, width: 120, type: 'Number'},
+    ],
+};
+
 const withCl = {
     ledger: {
         cols: [
@@ -614,6 +987,11 @@ const blank = {
 };
 
 module.exports = {
+    EmptySpreadSetting,
+    BaseSetCol,
+    ProjectSpreadTemplate,
+    BaseSpreadColSetting,
+    withoutClReplace,
     withCl,
     withoutCl,
     withClGcl,

+ 2 - 38
app/controller/change_controller.js

@@ -26,6 +26,7 @@ const measureType = require('../const/tender').measureType;
 const spreadConst = require('../const/spread');
 const stdConst = require('../const/standard');
 // const tenderMenu = require('../../config/menu').tenderMenu;
+const spreadSetting = require('../lib/spread_setting');
 
 module.exports = app => {
     class ChangeController extends app.BaseController {
@@ -1832,9 +1833,7 @@ module.exports = app => {
         }
 
         async _getDefaultReviseInfoData(ctx, change, edit) {
-            const [ledgerSpread, posSpread] = this._getSpreadSetting(change, edit);
-            const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
-            this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
+            const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, change.tid, edit);
             const [stdBills, stdChapters] = await this.ctx.service.valuation.getValuationStdList(
                 ctx.tender.data.valuation, ctx.tender.data.measure_type);
             return {
@@ -1852,41 +1851,6 @@ module.exports = app => {
             };
         }
 
-        /**
-         * 获取SpreadSetting
-         * @private
-         */
-        _getSpreadSetting(change, edit) {
-            const _ = this.app._;
-            function removeFieldCols(setting, cols) {
-                _.remove(setting.cols, function(c) {
-                    return cols.indexOf(c.field) > -1;
-                });
-            }
-            const tender = this.ctx.tender;
-            const setting = tender.data.measure_type === measureType.tz.value
-                ? (tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl)
-                : (tender.info.display.ledger.clQty ? spreadConst.withClGcl : spreadConst.withoutClGcl);
-            const ledger = JSON.parse(JSON.stringify(setting.ledger));
-            const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
-
-            // if (change.status === audit.flow.status.checking || change.status === audit.flow.status.checked) {
-            //     ledger.readOnly = true;
-            //     pos.readOnly = true;
-            // }
-            if (!edit) {
-                ledger.readOnly = true;
-                pos.readOnly = true;
-            }
-            if (tender.data.measure_type === measureType.tz.value) {
-                removeFieldCols(ledger, spreadConst.filterCols.dealCols);
-            }
-            if (!tender.info.display.ledger.dgnQty) {
-                removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
-            }
-            return [ledger, pos];
-        }
-
         async updateRevise(ctx) {
             try {
                 if (!ctx.tender.data) throw '标段数据错误';

+ 0 - 2
app/controller/ledger_controller.js

@@ -102,8 +102,6 @@ module.exports = app => {
                 const tender = ctx.tender;
                 const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, tender.id,
                     this._ledgerReadOnly(tender.data));
-                const sjsRela = await this.ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
-                this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
                 const times = tender.data.ledger_status === auditConst.status.checkNo ? tender.data.ledger_times - 1 : tender.data.ledger_times;
 
                 const curAuditor = await ctx.service.ledgerAudit.getCurAuditor(tender.id, tender.data.ledger_times);

+ 0 - 2
app/controller/revise_controller.js

@@ -485,8 +485,6 @@ module.exports = app => {
 
                 const [ledgerSpread, posSpread] = await spreadSetting.getLedgerSpreadSetting(ctx, revise.tid,
                     revise.status === audit.revise.status.checking || revise.status === audit.revise.status.checked);
-                const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
-                this.ctx.helper.refreshSpreadShow(sjsRela.ledgerCol, [ledgerSpread, posSpread]);
                 ledgerSpread.readOnly = true;
                 posSpread.readOnly = true;
                 const historyRevise = await ctx.service.ledgerRevise.getAllReviseList(ctx.tender.id);

+ 62 - 0
app/controller/setting_controller.js

@@ -24,6 +24,7 @@ const sendToWormhole = require('stream-wormhole');
 const path = require('path');
 const funSet = require('../const/fun_set');
 const projectSettingConst = require('../const/project_setting');
+const SpreadConst = require('../const/spread');
 
 module.exports = app => {
 
@@ -1366,6 +1367,67 @@ module.exports = app => {
                 ctx.body = { err: 1, msg: err.toString(), data: null };
             }
         }
+
+        async spread(ctx) {
+            try {
+                // 获取项目数据
+                const projectId = ctx.session.sessionProject.id;
+                await this._checkMenu(projectId);
+                const projectData = await ctx.service.project.getDataById(projectId);
+                if (!projectData) throw '没有对应的项目数据';
+
+                const projectSpread = await this.ctx.service.projectSpread.getProjectSpread(projectData.id);
+                const sType = ctx.query.stype;
+                const renderData = {
+                    BaseSetCol: sType.indexOf('stage') > 0 ? SpreadConst.BaseSetCol.Stage : SpreadConst.BaseSetCol.Ledger,
+                    sType,
+                    colSet: projectSpread[sType],
+                    jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.setting.spread),
+                };
+                await this.layout('setting/spread.ejs', renderData, 'setting/spread_modal.ejs');
+            } catch (error) {
+                this.log(error);
+                ctx.redirect('/dashboard');
+            }
+        }
+
+        async saveSpread(ctx) {
+            try {
+                const projectId = ctx.session.sessionProject.id;
+                const data = JSON.parse(ctx.request.body.data);
+                await this.ctx.service.projectSpread.updateProjectSet(projectId, data.sType, data.colSet);
+                const projectSpread = await this.ctx.service.projectSpread.getProjectSpread(projectId);
+                ctx.body = { err: 0, msg: '', data: { colSet: projectSpread[data.sType] } };
+            } catch (error) {
+                ctx.log(error);
+                ctx.ajaxErrorBody(error, '保存列设置失败,请刷新后重试');
+            }
+        }
+
+        async previewSpread(ctx) {
+            try {
+                const data = JSON.parse(ctx.request.body.data);
+                const [billsSpread, posSpread] = this.ctx.service.projectSpread.generateRelaSpread(data.sType, data.colSet);
+                ctx.body = { err: 0, msg: '', data: { billsSpread, posSpread } };
+            } catch (error) {
+                ctx.log(error);
+                ctx.ajaxErrorBody(error, '保存列设置失败,请刷新后重试');
+            }
+        }
+
+        async resetSpread(ctx) {
+            try {
+                const projectId = ctx.session.sessionProject.id;
+                const data = JSON.parse(ctx.request.body.data);
+                const colSet = SpreadConst.ProjectSpreadTemplate[data.sType];
+                await this.ctx.service.projectSpread.updateProjectSet(projectId, data.sType, colSet);
+                const projectSpread = await this.ctx.service.projectSpread.getProjectSpread(projectId);
+                ctx.body = { err: 0, msg: '', data: { colSet: projectSpread[data.sType] } };
+            } catch (error) {
+                ctx.log(error);
+                ctx.ajaxErrorBody(error, '保存列设置失败,请刷新后重试');
+            }
+        }
     }
 
     return SettingController;

+ 0 - 23
app/extend/helper.js

@@ -1468,29 +1468,6 @@ module.exports = {
         });
     },
 
-    refreshSpreadShow(sjsRela, sjsSetting) {
-        function removeFieldCols(setting, cols) {
-            _.remove(setting.cols, function(c) {
-                return cols.indexOf(c.field) > -1;
-            });
-        }
-        const sjsSettings = sjsSetting instanceof Array ? sjsSetting : [sjsSetting];
-        for (const field of sjsRela) {
-            if (field.show) {
-                for (const ss of sjsSettings) {
-                    const lCol = ss.cols.find(x => {
-                        return x.field === field.field;
-                    });
-                    if (lCol) lCol.title = field.alias || field.name;
-                }
-            } else {
-                for (const ss of sjsSettings) {
-                    removeFieldCols(ss, [field.field]);
-                }
-            }
-        }
-    },
-
     /**
      * 发送请求获取详细地址
      * @param {String} type - 通知类型

+ 21 - 9
app/lib/spread_setting.js

@@ -70,19 +70,31 @@ function refreshSpreadShow(sjsRela, sjsSetting) {
 const getLedgerSpreadSetting = async function(ctx, tid, readOnly) {
     let tender = tid ? await getCtxTender(ctx, tid) : ctx.tender;
 
-    const setting = tender.data.measure_type === measureType.tz.value
-        ? (tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl)
-        : (tender.info.display.ledger.clQty ? spreadConst.withClGcl : spreadConst.withoutClGcl);
+    const projectSpread = await ctx.service.projectSpread.getProjectSpread(ctx.session.sessionProject.id);
+    const prefix = tender.data.measure_type === measureType.tz.value ? 'tz' : 'gcl';
+    const ledger = projectSpread[`${prefix}_ledger_bills_spread`];
+    const pos = projectSpread[`${prefix}_ledger_pos_spread`];
+    if (!tender.info.display.ledger.clQty) {
+        const billsColIndex = ledger.cols.findIndex(x => { return x.field === spreadConst.withoutClReplace.indexField; });
+        removeFieldCols(ledger, spreadConst.withoutClReplace.remove);
+        ledger.cols.splice(billsColIndex, 0, ...spreadConst.withoutClReplace.billsCols);
+        const posColIndex = pos.cols.findIndex(x => { return x.field === spreadConst.withoutClReplace.indexField; });
+        removeFieldCols(pos, spreadConst.withoutClReplace.remove);
+        pos.cols.splice(posColIndex, 0, ...spreadConst.withoutClReplace.posCols);
+    }
+    // const setting = tender.data.measure_type === measureType.tz.value
+    //     ? (tender.info.display.ledger.clQty ? spreadConst.withCl : spreadConst.withoutCl)
+    //     : (tender.info.display.ledger.clQty ? spreadConst.withClGcl : spreadConst.withoutClGcl);
 
-    const ledger = JSON.parse(JSON.stringify(setting.ledger));
-    const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
+    // const ledger = JSON.parse(JSON.stringify(setting.ledger));
+    // const pos = setting.pos ? JSON.parse(JSON.stringify(setting.pos)) : spreadConst.blank;
     ledger.readOnly = readOnly;
     pos.readOnly = readOnly;
     if (!tender.info.display.ledger.deal) removeFieldCols(ledger, spreadConst.filterCols.dealCols);
     if (!tender.info.display.ledger.dgnQty) removeFieldCols(ledger, spreadConst.filterCols.dgnCols);
 
-    const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
-    refreshSpreadShow(sjsRela.ledgerCol, [ledger, pos]);
+    // const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
+    // refreshSpreadShow(sjsRela.ledgerCol, [ledger, pos]);
 
     return [ledger, pos];
 };
@@ -117,8 +129,8 @@ const getStageSpreadSetting = async function (ctx, tid, readOnly, funInfo) {
     ledger.readOnly = readOnly;
     pos.readOnly = readOnly;
 
-    const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
-    refreshSpreadShow(sjsRela.ledgerCol, [ledger, pos]);
+    // const sjsRela = await ctx.service.project.getTenderSjsRela(ctx.session.sessionProject.id, ctx.tender.info.display.exMemo);
+    // refreshSpreadShow(sjsRela.ledgerCol, [ledger, pos]);
 
     return [ledger, pos];
 };

+ 237 - 0
app/public/js/project_spread.js

@@ -0,0 +1,237 @@
+$(document).ready(() => {
+    autoFlashHeight();
+    const colSpread = SpreadJsObj.createNewSpread($('#spread-col')[0]);
+    const colSheet = colSpread.getActiveSheet();
+    const colSpreadSetting = {
+        cols: [
+            { title: 'key', colSpan: '1', rowSpan: '2', field: 'key', hAlign: 0, width: 0, visible: false, formatter: '@', readOnly: true, },
+            { title: '可显示列', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 200, formatter: '@', readOnly: true, },
+            { title: '项目节\n清单', colSpan: '1', rowSpan: '2', field: 'bills', hAlign: 1, width: 80, readOnly: true, },
+            { title: '计量单元', colSpan: '1', rowSpan: '2', field: 'pos', hAlign: 1, width: 80, readOnly: true, },
+            { title: '配置项|是否显示', colSpan: '2|1', rowSpan: '1|1', field: 'valid', hAlign: 1, width: 60, cellType: 'checkbox', readOnly: true },
+            { title: '|别名', colSpan: '|1', rowSpan: '1|1', field: 'alias', hAlign: 0, width: 120, formatter: '@',  },
+        ],
+        headRows: 2,
+        headRowHeight: [25, 25],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        emptyRows: 0,
+        getColor: function (sheet, data, row, col, defaultColor) {
+            if (!data) return defaultColor;
+            switch (col.field) {
+                case 'valid':
+                case 'alias':
+                    return data.fixed.indexOf(col.field) >= 0 ? '#eeeeee' : defaultColor;
+                default:
+                    return defaultColor;
+            }
+        },
+    };
+    SpreadJsObj.initSheet(colSheet, colSpreadSetting);
+
+    const reloadData = function () {
+        const data = [];
+        colSet.forEach(x => {
+            const baseCol = BaseSetCol.find(b => { return x.key === b.key });
+            data.push({ ...x, ...baseCol });
+        });
+        SpreadJsObj.loadSheetData(colSheet, SpreadJsObj.DataType.Data, data);
+    };
+
+    const colSpreadObj = {
+        refreshOperationValid: function (sheet, selection) {
+            const setObjEnable = function (obj, enable) {
+                if (enable) {
+                    obj.removeClass('disabled');
+                } else {
+                    obj.addClass('disabled');
+                }
+            };
+            const invalidAll = function () {
+                setObjEnable($('a[name=base-opr][type=up-move]'), false);
+                setObjEnable($('a[name=base-opr][type=down-move]'), false);
+            };
+            const sel = selection ? selection[0] : sheet.getSelections()[0];
+            const row = sel ? sel.row : -1;
+            const data = sheet.zh_data;
+            const select = sheet.zh_data[row];
+            if (!select) {
+                invalidAll();
+                return;
+            }
+
+            const pre = data[row-1];
+            const next = data[row + 1];
+
+            setObjEnable($('a[name=base-opr][type=up-move]'), !!pre && pre.fixed.indexOf('move') < 0 && select.fixed.indexOf('move') < 0);
+            setObjEnable($('a[name=base-opr][type=down-move]'), !!next && next.fixed.indexOf('move') < 0 && select.fixed.indexOf('move') < 0);
+        },
+        selectionChanged: function (e, info) {
+            if (info.newSelections) {
+                if (!info.oldSelections || info.newSelections[0].row !== info.oldSelections[0].row) {
+                    colSpreadObj.refreshOperationValid(info.sheet, info.newSelections);
+                }
+            }
+        },
+        baseOpr: function (sheet, type) {
+            const sel = sheet.getSelections()[0];
+            const data = sheet.zh_data;
+            if (type === 'up-move') {
+                [data[sel.row - 1], data[sel.row]] = [data[sel.row], data[sel.row - 1]];
+                SpreadJsObj.reLoadRowsData(sheet, [sel.row - 1, sel.row]);
+                sheet.setSelection(sel.row - 1, sel.col, sel.rowCount, sel.colCount);
+                SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, sel.row - 1]);
+            } else if (type === 'down-move') {
+                [data[sel.row + 1], data[sel.row]] = [data[sel.row], data[sel.row + 1]];
+                SpreadJsObj.reLoadRowsData(sheet, [sel.row, sel.row + 1]);
+                sheet.setSelection(sel.row + 1, sel.col, sel.rowCount, sel.colCount);
+                SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, sel.row + 1]);
+            }
+            colSpreadObj.refreshOperationValid(sheet);
+        },
+        editStarting(e, info) {
+            if (!info.sheet.zh_setting || !info.sheet.zh_data) return;
+            const col = info.sheet.zh_setting.cols[info.col];
+            const node = info.sheet.zh_data[info.row];
+            if (!node) {
+                info.cancel = true;
+                return;
+            }
+            switch (col.field) {
+                case 'alias':
+                    info.cancel = node.fixed.indexOf('alias') >= 0;
+                    break;
+            }
+        },
+        editEnded: function (e, info) {
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            const sortData = info.sheet.zh_dataType === 'tree' ? info.sheet.zh_tree.nodes : info.sheet.zh_data;
+            const node = sortData[info.row];
+
+            // 未改变值则不提交
+            const orgValue = node[col.field];
+            const newValue = trimInvalidChar(info.editingText);
+            if (orgValue == info.editingText || ((!orgValue || orgValue === '') && (newValue === ''))) {
+                return;
+            }
+            node[col.field] = newValue;
+        },
+        deletePress: function (sheet) {
+            if (!sheet.zh_setting) return;
+            const sel = sheet.getSelections()[0], datas = [];
+            for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
+                let bDel = false;
+                const node = sheet.zh_tree.nodes[iRow];
+                const data = sheet.zh_tree.getNodeKeyData(node);
+                for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
+                    const col = sheet.zh_setting.cols[iCol];
+                    const style = sheet.getStyle(iRow, iCol);
+                    if (style.locked || (['dgn_qty1', 'dgn_qty2'].indexOf(col.field) >= 0 && node.b_code)) continue;
+                    if (col.type === 'Number' && node.settle_status === settleStatus.finish) continue;
+
+                    if (['dgn_qty1', 'dgn_qty2'].indexOf(col.field) < 0 && sheet.zh_tree.checkNodeUsed(node, pos)) {
+                        toastr.warning('"' + (node.code || '') + (node.b_code || '') + ' ' + node.name +'"已计量,请勿修改');
+                        return;
+                    }
+
+                    data[col.field] = null;
+                    const exprInfo = getExprInfo(col.field);
+                    if (exprInfo) {
+                        data[exprInfo.expr] = '';
+                    }
+                    bDel = true;
+                }
+                if (bDel) datas.push(data);
+            }
+            if (datas.length > 0) {
+                postData(window.location.pathname + '/update', {postType: 'update', postData: datas}, function (result) {
+                    const refreshNode = sheet.zh_tree.loadPostData(result);
+                    billsTreeSpreadObj.refreshTree(sheet, refreshNode);
+                    billsTreeSpreadObj.loadExprToInput(sheet);
+                });
+            }
+        },
+        buttonClicked: function(e, info) {
+            if (!info.sheet.zh_setting) return;
+
+            const col = info.sheet.zh_setting.cols[info.col];
+            if (col.field !== 'valid') return;
+
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            if (node.fixed.indexOf('valid') < 0) node.valid = !node.valid;
+            SpreadJsObj.reLoadRowData(info.sheet, info.row);
+        },
+    };
+
+    reloadData();
+    colSpreadObj.refreshOperationValid(colSheet);
+    colSpread.bind(spreadNS.Events.SelectionChanged, colSpreadObj.selectionChanged);
+    colSpread.bind(spreadNS.Events.EditStarting, colSpreadObj.editStarting);
+    colSpread.bind(spreadNS.Events.EditEnded, colSpreadObj.editEnded);
+    colSpread.bind(spreadNS.Events.ButtonClicked, colSpreadObj.buttonClicked);
+    $('a[name="base-opr"]').click(function () {
+        colSpreadObj.baseOpr(colSheet, this.getAttribute('type'));
+    });
+
+    const getCurrentColSet = function() {
+        const cols = { key: 0, name: 1, valid: 4, alias: 5};
+        const rowCount = colSheet.getRowCount();
+        const result = [];
+        for (let iRow = 0; iRow < rowCount; iRow++) {
+            const keyName = colSheet.getText(iRow, cols.key);
+            const baseCol = BaseSetCol.find(x => { return x.key === keyName; });
+            if (!baseCol) continue;
+
+            const valid = colSheet.getText(iRow, cols.valid);
+            const colSet = { key: keyName, valid: valid === '1' || valid === 'TRUE' ? 1 : 0 };
+            if (baseCol.fixed.indexOf('alias') < 0) {
+                const alias = colSheet.getText(iRow, cols.alias);
+                if (alias) colSet.alias = alias;
+            }
+            result.push(colSet);
+        }
+        return result;
+    };
+    let previewSpreadSetting, previewBillsSpread, previewPosSpread;
+    $('#preview-spread').on('shown.bs.modal', function () {
+        if (!previewBillsSpread) {
+            previewBillsSpread = SpreadJsObj.createNewSpread($('#preview-bills-spread')[0]);
+        }
+        const previewBillsSheet = previewBillsSpread.getActiveSheet();
+        SpreadJsObj.initSheet(previewBillsSheet, previewSpreadSetting.billsSpread);
+
+        if (!previewPosSpread) {
+            previewPosSpread = SpreadJsObj.createNewSpread($('#preview-pos-spread')[0]);
+        }
+        const previewPosSheet = previewPosSpread.getActiveSheet();
+        SpreadJsObj.initSheet(previewPosSheet, previewSpreadSetting.posSpread);
+    });
+
+    $('#spread-save').click(() => {
+        const data = { sType, colSet: getCurrentColSet() };
+        postData('/setting/spread/save', data, function(result) {
+            colSet.length = 0;
+            colSet.push(...result.colSet);
+            reloadData();
+        });
+    });
+    $('#spread-preview').click(() => {
+        const data = { sType, colSet: getCurrentColSet() };
+        postData('/setting/spread/preview', data, function(result) {
+            console.log(result);
+            previewSpreadSetting = result;
+            $('#preview-spread').modal('show');
+        })
+    });
+    $('#spread-reset').click(() => {
+        const data = { sType };
+        postData('/setting/spread/reset', data, function(result) {
+            colSet.length = 0;
+            colSet.push(...result.colSet);
+            reloadData();
+        });
+    });
+});

+ 4 - 0
app/router.js

@@ -111,6 +111,10 @@ module.exports = app => {
     app.get('/setting/show', sessionAuth, 'settingController.show');
     app.post('/setting/show/update', sessionAuth, 'settingController.showListUpdate');
     app.post('/setting/show/update-sjs', sessionAuth, 'settingController.showSjsUpdate');
+    app.get('/setting/spread', sessionAuth, projectManagerCheck, 'settingController.spread');
+    app.post('/setting/spread/save', sessionAuth, projectManagerCheck, 'settingController.saveSpread');
+    app.post('/setting/spread/preview', sessionAuth, projectManagerCheck, 'settingController.previewSpread');
+    app.post('/setting/spread/reset', sessionAuth, projectManagerCheck, 'settingController.resetSpread');
 
     // 标段自定义类别
     app.get('/setting/category', sessionAuth, 'settingController.category');

+ 112 - 0
app/service/project_spread.js

@@ -0,0 +1,112 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2024/3/5
+ * @version
+ */
+
+const SpreadConst = require('../const/spread');
+const JsonFields = [
+    'tz_ledger_set', 'tz_ledger_bills_spread', 'tz_ledger_pos_spread',
+    'gcl_ledger_set', 'gcl_ledger_bills_spread', 'gcl_ledger_pos_spread',
+    'tz_stage_set', 'tz_stage_bills_spread', 'tz_stage_pos_spread',
+    'gcl_stage_set', 'gcl_stage_bills_spread', 'gcl_stage_pos_spread',
+];
+
+module.exports = app => {
+
+    class ProjectSpread extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'project_spread';
+        }
+
+        async loadProjectSpread(id) {
+            const result = await this.getDataByCondition({ id });
+            if (result) {
+                JsonFields.forEach(jf => { if(result[jf]) result[jf] = JSON.parse(result[jf]); });
+            }
+            return result;
+        }
+
+        generateSpreadSetting(colSet, emptyBase, BaseSetCol, BaseSpreadColSetting, spreadType = 'bills') {
+            const spreadSetting = JSON.parse(JSON.stringify(emptyBase));
+            for (const col of colSet) {
+                if (!col.valid) continue;
+                const dc = BaseSetCol.find(x => { return x.key === col.key; });
+                if (!dc) continue;
+                const orgBaseCols = BaseSpreadColSetting[col.key];
+                if (!orgBaseCols) continue;
+
+                const baseCols = JSON.parse(JSON.stringify(orgBaseCols));
+                if (dc.fixed.indexOf('alias') < 0 && col.alias) {
+                    if (baseCols.length === 1) {
+                        if (baseCols[0].aliasFormat) {
+                            baseCols[0].title = baseCols[0].aliasFormat.replace('{%s}', col.alias);
+                            delete baseCols[0].aliasFormat;
+                        } else {
+                            baseCols[0].title = col.alias;
+                        }
+                    } else {
+                        baseCols.forEach(x => {
+                            if (x.aliasFormat) {
+                                x.title = x.aliasFormat.replace('{%s}', col.alias);
+                                delete x.aliasFormat;
+                            }
+                        });
+                    }
+                }
+                spreadSetting.cols.push(...baseCols);
+            }
+            return spreadSetting;
+        }
+
+        generateRelaSpread(colSetType, colSet) {
+            const baseSetCol = colSetType.indexOf('stage') > 0 ? SpreadConst.BaseSetCol.Stage : SpreadConst.BaseSetCol.Ledger;
+            const baseSpreadColSetting = colSetType.indexOf('stage') > 0 ? SpreadConst.BaseSpreadColSetting.Stage : SpreadConst.BaseSpreadColSetting.Ledger;
+            const billsSpread = this.generateSpreadSetting(colSet, SpreadConst.EmptySpreadSetting[colSetType].bills, baseSetCol, baseSpreadColSetting.bills);
+            const posSpread = this.generateSpreadSetting(colSet, SpreadConst.EmptySpreadSetting[colSetType].pos, baseSetCol, baseSpreadColSetting.pos, 'pos');
+            return [billsSpread, posSpread];
+        }
+
+        async initProjectSpread(id) {
+            const data = JSON.parse(JSON.stringify(SpreadConst.ProjectSpreadTemplate));
+            [data.tz_ledger_bills_spread, data.tz_ledger_pos_spread] = this.generateRelaSpread('tz_ledger_set', data.tz_ledger_set);
+            [data.tz_stage_bills_spread, data.tz_stage_pos_spread] = this.generateRelaSpread('tz_stage_set', data.tz_stage_set);
+            [data.gcl_ledger_bills_spread, data.gcl_ledger_pos_spread] = this.generateRelaSpread('gcl_ledger_set', data.gcl_ledger_set);
+            [data.gcl_stage_bills_spread, data.gcl_stage_pos_spread] = this.generateRelaSpread('gcl_stage_set', data.gcl_stage_set);
+            data.id = id;
+            const updateData = { id };
+            JsonFields.forEach(jf => { if (data[jf]) updateData[jf] = JSON.stringify(data[jf]) });
+            const result = await this.db.insert(this.tableName, updateData);
+            if (result.insertId = data.id) return data;
+        }
+
+        async getProjectSpread(id) {
+            const curSet = await this.loadProjectSpread(id);
+            if (curSet) return curSet;
+            return await this.initProjectSpread(id);
+        }
+
+        async updateProjectSet(id, colSetType, colSet) {
+            const updateData = { id };
+            const [billsSpread, posSpread] = this.generateRelaSpread(colSetType, colSet);
+            updateData[colSetType.replace('_set', '_bills_spread')] = JSON.stringify(billsSpread);
+            updateData[colSetType.replace('_set', '_pos_spread')] = JSON.stringify(posSpread);
+            updateData[colSetType] = JSON.stringify(colSet);
+            await this.db.update(this.tableName, updateData);
+        }
+    }
+
+    return ProjectSpread;
+};

+ 60 - 37
app/view/setting/show.ejs

@@ -11,7 +11,7 @@
                 <div class="row m-0 mt-3">
                     <div class="col-5">
                         <div class="form-group">
-                            <label>项目列表默认显示</label>
+                            <h6>项目列表默认显示</h6>
                             <div class="card w-50">
                                 <ul class="list-group list-group-flush">
                                     <% showList.forEach(function(item, idx) { %>
@@ -34,43 +34,66 @@
                                 </ul>
                             </div>
                         </div>
-                        <div class="form-group">
-                            <label>台账列显示</label>
-                            <table class="table table-hover table-bordered">
-                                <thead><tr><th>名称</th><th>是否显示</th><th>别名</th><th>位置</th></tr></thead>
-                                <tbody id="ledger_col_list">
-                                <% for (const sl of sjsRela.ledgerCol) { %>
-                                <tr>
-                                    <td><%- sl.name %></td>
-                                    <td>
-                                        <div class="form-check form-check-inline">
-                                            <input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- sl.field %>" <% if (sl.show) { %>checked<% } %> onchange="updateColShow('ledgerCol', '<%- sl.field %>', this);">
-                                            <label class="form-check-label" for="inlineCheckbox-<%- sl.field %>"></label>
-                                        </div>
-                                    </td>
-                                    <td>
-                                        <div class="input-group input-group-sm" style="width:90px">
-                                            <input type="text" class="form-control" value="<%- sl.alias %>" onchange="updateColAlias('ledgerCol', '<%- sl.field%>', this);">
-                                        </div>
-                                    </td>
-                                    <td>
-                                        <% if (sl.pos) { %>
-                                        <div class="input-group input-group-sm">
-                                            <select class="form-control form-control-sm" onchange="updateColPos('ledgerCol', '<%- sl.field%>', this)" value="<%- sl.pos %>" org="<%- sl.pos %>">
-                                                <% for (const r of settingConst.exMemoPosRange[sl.field]) { %>
-                                                <option value="<%- r.key %>" <% if (sl.pos === r.key) {%>selected<% } %>><%- r.hint %></option>
-                                                <% } %>
-                                            </select>
-                                        </div>
-                                        <% } else { %>
-                                        <span class="form-control-sm text-warning"><%- sl.hint || '' %></span>
-                                        <% } %>
-                                    </td>
-                                </tr>
-                                <% } %>
-                                </tbody>
-                            </table>
+                        <div class-="form-group">
+                            <h6>列显示</h6>
+                            <div class="row">
+                                <div class="col-6">
+                                    <label>0号台账模式</label>
+                                    <div class="card">
+                                        <ul class="list-group list-group-flush">
+                                            <li class="list-group-item">0号台账 <a href="/setting/spread?stype=tz_ledger_set" class="btn btn-primary btn-sm pull-right">配置</a></li>
+                                            <li class="list-group-item">计量台账 <a href="/setting/spread?stype=tz_stage_set" class="btn btn-primary btn-sm pull-right">配置</a></li>
+                                        </ul>
+                                    </div>
+                                </div>
+                                <div class="col-6">
+                                    <label>工程量清单模式</label>
+                                    <div class="card">
+                                        <ul class="list-group list-group-flush">
+                                            <li class="list-group-item">0号台账 <a href="/setting/spread?stype=gcl_ledger_set" class="btn btn-primary btn-sm pull-right">配置</a></li>
+                                            <li class="list-group-item">计量台账 <a href="/setting/spread?stype=gcl_stage_set" class="btn btn-primary btn-sm pull-right">配置</a></li>
+                                        </ul>
+                                    </div>
+                                </div>
+                            </div>
                         </div>
+                        <!--<div class="form-group">-->
+                            <!--<label>台账列显示</label>-->
+                            <!--<table class="table table-hover table-bordered">-->
+                                <!--<thead><tr><th>名称</th><th>是否显示</th><th>别名</th><th>位置</th></tr></thead>-->
+                                <!--<tbody id="ledger_col_list">-->
+                                <!--<% for (const sl of sjsRela.ledgerCol) { %>-->
+                                <!--<tr>-->
+                                    <!--<td><%- sl.name %></td>-->
+                                    <!--<td>-->
+                                        <!--<div class="form-check form-check-inline">-->
+                                            <!--<input class="form-check-input" type="checkbox" id="inlineCheckbox-<%- sl.field %>" <% if (sl.show) { %>checked<% } %> onchange="updateColShow('ledgerCol', '<%- sl.field %>', this);">-->
+                                            <!--<label class="form-check-label" for="inlineCheckbox-<%- sl.field %>"></label>-->
+                                        <!--</div>-->
+                                    <!--</td>-->
+                                    <!--<td>-->
+                                        <!--<div class="input-group input-group-sm" style="width:90px">-->
+                                            <!--<input type="text" class="form-control" value="<%- sl.alias %>" onchange="updateColAlias('ledgerCol', '<%- sl.field%>', this);">-->
+                                        <!--</div>-->
+                                    <!--</td>-->
+                                    <!--<td>-->
+                                        <!--<% if (sl.pos) { %>-->
+                                        <!--<div class="input-group input-group-sm">-->
+                                            <!--<select class="form-control form-control-sm" onchange="updateColPos('ledgerCol', '<%- sl.field%>', this)" value="<%- sl.pos %>" org="<%- sl.pos %>">-->
+                                                <!--<% for (const r of settingConst.exMemoPosRange[sl.field]) { %>-->
+                                                <!--<option value="<%- r.key %>" <% if (sl.pos === r.key) {%>selected<% } %>><%- r.hint %></option>-->
+                                                <!--<% } %>-->
+                                            <!--</select>-->
+                                        <!--</div>-->
+                                        <!--<% } else { %>-->
+                                        <!--<span class="form-control-sm text-warning"><%- sl.hint || '' %></span>-->
+                                        <!--<% } %>-->
+                                    <!--</td>-->
+                                <!--</tr>-->
+                                <!--<% } %>-->
+                                <!--</tbody>-->
+                            <!--</table>-->
+                        <!--</div>-->
                     </div>
                 </div>
             </div>

+ 30 - 0
app/view/setting/spread.ejs

@@ -0,0 +1,30 @@
+<% include ./sub_menu.ejs %>
+<div class="panel-content">
+    <div class="panel-title">
+        <div class="title-main d-flex">
+            <div>
+                <div class="d-inline-block">显示设置 - 列设置 -【<%= (sType.indexOf('gcl') === 0 ? '工程量清单模式' : '0号台账模式') %>】 <%= (sType.indexOf('stage') === 0 ? '计量台账' : '0号台账') %></div>
+                <div class="d-inline-block">
+                    <a href="javascript:void(0)" name="base-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                    <a href="javascript:void(0)" name="base-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                </div>
+            </div>
+            <div class="ml-auto">
+                <button href="javascript: void(0);" class="btn btn-sm btn-warning" id="spread-reset">重置</button>
+                <button href="javascript: void(0);" class="btn btn-sm btn-primary" id="spread-preview">预览</button>
+                <button href="javascript: void(0);" class="btn btn-sm btn-success" id="spread-save">保存</button>
+            </div>
+        </div>
+    </div>
+    <div class="content-wrap">
+        <div class="c-body">
+            <div class="sjs-height-0" id="spread-col">
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const BaseSetCol = JSON.parse('<%- JSON.stringify(BaseSetCol) %>');
+    const sType = '<%- sType %>';
+    const colSet = JSON.parse('<%- JSON.stringify(colSet) %>');
+</script>

+ 17 - 0
app/view/setting/spread_modal.ejs

@@ -0,0 +1,17 @@
+<!--章节合计-->
+<div class="modal fade" id="preview-spread" data-backdrop="static">
+    <div class="modal-dialog modal-xl" role="document">
+        <div class="modal-content">
+            <div class="modal-header">
+                <h5 class="modal-title">预览</h5>
+            </div>
+            <div class="modal-body">
+                <div style="height: 300px" id="preview-bills-spread"></div>
+                <div style="heigth: 300px" id="preview-pos-spread"></div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 8 - 0
config/web.js

@@ -1376,6 +1376,14 @@ const JsFiles = {
                 ],
                 mergeFile: 'setting_manage',
             },
+            spread: {
+                files: ['/public/js/spreadjs/sheets/v11/gc.spread.sheets.all.11.2.2.min.js',],
+                mergeFiles: [
+                    '/public/js/spreadjs_rela/spreadjs_zh.js',
+                    '/public/js/project_spread.js',
+                ],
+                mergeFile: 'project_spread',
+            }
         },
         settle: {
             list: {

+ 13 - 0
db_script/depart_table.js

@@ -81,3 +81,16 @@ BaseUtil.saveBufferFile(sqlArr.join('\n'), BaseUtil.getFileName('depart_table_qa
 // zh_stage_pos: 30s(148945条)
 // zh_stage_pos_final: 130s(658929条)
 
+
+// uat执行时间(分表100)
+// zh_ledger: 280s(2637137条)
+// zh_ledger_extra: 20s(33004条)
+// zh_revise_bills: 160s(1470789条)
+// zh_stage_bills: 100s(2017603条)
+// zh_stage_bills_final: 160s(7077632条)
+// zh_pos: 600s(2088342条)
+// zh_pos_extra: 20s(106698条)
+// zh_revise_pos: 300s(4270632条)
+// zh_stage_pos: 150s(4794451条)
+// zh_stage_pos_final: 800s(23842851条)
+

+ 37 - 0
db_script/project_spread.js

@@ -0,0 +1,37 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const spreadConst = require('../app/const/spread');
+const BaseUtil = require('./baseUtils');
+
+const initProjectSpread = async function (project) {
+    const DefaultSet = JSON.parse(JSON.stringify(spreadConst.DefaultProjectSpread));
+    const sjsRela = project.sjs_rela ? JSON.parse(project.sjs_rela) : null;
+    if (sjsRela) {
+        for (const )
+    }
+};
+
+const doComplete = async function(projectCode) {
+    try {
+        const whereSql = projectCode ? ` WHERE code = "${projectCode}"` : '';
+        const project = projectCode ? await querySql(`Select * From zh_project ${whereSql}`);
+        for (const p of project) {
+            console.log(`Update Project ${p.code}(${p.id}):`);
+            await initProjectSpread(p);
+        }
+    } catch (err) {
+        console.log(err);
+    }
+    BaseUtil.closePool();
+};
+
+
+doComplete(process.argv[3]);