Explorar el Código

feat: 合同管理列控制相关功能

lanjianrong hace 17 horas
padre
commit
9cfcb091c0

+ 48 - 0
app/const/contract.js

@@ -23,8 +23,56 @@ const typeName = {
     2: '回款',
 };
 
+const colSet = {
+    [type.expenses]: [
+        { name: '累计应付', field: 'yf_price', fixed: ['alias'] },
+        { name: '应付进度', field: 'stackedBar', fixed: ['alias'] },
+        { name: '累计实付', field: 'sf_price', fixed: ['alias'] },
+        { name: '实付进度', field: 'stackedBarSf', fixed: ['alias'] },
+        { name: '备注1', field: 'remark', fixed: [] },
+        { name: '备注2', field: 'remark2', fixed: [] },
+        { name: '计算1', field: 'calc', fixed: [] },
+        { name: '计算2', field: 'calc2', fixed: [] },
+    ],
+    [type.income]: [
+        { name: '累计回款', field: 'yf_price', fixed: ['alias'] },
+        { name: '回款进度', field: 'stackedBar', fixed: ['alias'] },
+        { name: '累计实回', field: 'sf_price', fixed: ['alias'] },
+        { name: '实回进度', field: 'stackedBarSf', fixed: ['alias'] },
+        { name: '备注1', field: 'remark', fixed: [] },
+        { name: '备注2', field: 'remark2', fixed: [] },
+        { name: '计算1', field: 'calc', fixed: [] },
+        { name: '计算2', field: 'calc2', fixed: [] },
+    ],
+};
+
+const defaultColSet = {
+    [type.expenses]: [
+        { field: 'yf_price', show: 1 },
+        { field: 'stackedBar', show: 1 },
+        { field: 'sf_price', show: 0 },
+        { field: 'stackedBarSf', show: 0 },
+        { field: 'remark', show: 0, alias: '文本框1' },
+        { field: 'remark2', show: 0, alias: '文本框2' },
+        { field: 'calc', show: 0, alias: '计算1' },
+        { field: 'calc2', show: 0, alias: '计算2' },
+    ],
+    [type.income]: [
+        { field: 'yf_price', show: 1 },
+        { field: 'stackedBar', show: 1 },
+        { field: 'sf_price', show: 0 },
+        { field: 'stackedBarSf', show: 0 },
+        { field: 'remark', show: 0, alias: '文本框1' },
+        { field: 'remark2', show: 0, alias: '文本框2' },
+        { field: 'calc', show: 0, alias: '计算1' },
+        { field: 'calc2', show: 0, alias: '计算2' },
+    ],
+};
+
 module.exports = {
     type,
     typeMap,
     typeName,
+    colSet,
+    defaultColSet,
 };

+ 23 - 0
app/controller/contract_controller.js

@@ -267,6 +267,7 @@ module.exports = app => {
                 const contractTreeAudits = await ctx.service.contractTreeAudit.getAllDataByCondition({ where: contractOptions });
                 const [stdBills, stdChapters] = ctx.contract.tender ? await this.ctx.service.valuation.getValuationStdList(
                     ctx.contract.valuation, ctx.contract.measure_type) : await ctx.service.budgetStd.getStdList(ctx.subProject.std_id, 'yu');
+
                 const renderData = {
                     contractTreeAudits,
                     audit_permission: ctx.contract_audit_permission,
@@ -278,6 +279,9 @@ module.exports = app => {
                     thisUrl: `/sp/${ctx.subProject.id}` + (ctx.contract_tender ? `/contract/tender/${ctx.contract.id}/detail` : '/contract/detail'),
                     stdChapters,
                 };
+
+                const contractColSet = await ctx.service.contractColSet.getContractColSet(ctx.session.sessionProject.id, ctx.contractOptions.tid, ctx.contract_type);
+                renderData.colSet = ctx.service.contractColSet.analysisColSetWithDefine(contractConst.colSet[ctx.contract_type], contractColSet.info, contractConst.defaultColSet[ctx.contract_type]);
                 if (ctx.session.sessionUser.is_admin) {
                     const accountList = await ctx.service.projectAccount.getAllSubProjectAccount(ctx.subProject);
                     renderData.accountList = accountList;
@@ -295,6 +299,24 @@ module.exports = app => {
             }
         }
 
+        /**
+         * 保存列设置
+         * @param ctx
+         * @returns {Promise<void>}
+         */
+        async colSet(ctx) {
+            try {
+                const colType = ctx.request.body.col_type;
+                const colSet = JSON.parse(ctx.request.body.col_set);
+                const contractType = ctx.request.body.type;
+                await ctx.service.contractColSet.setContractColSet(ctx.session.sessionProject.id, ctx.contractOptions.tid, contractType, colType, colSet);
+                ctx.redirect(ctx.request.header.referer);
+            } catch (err) {
+                ctx.log(err);
+                ctx.redirect(ctx.request.header.referer);
+            }
+        }
+
         async loadDetail(ctx) {
             try {
                 const responseData = {
@@ -335,6 +357,7 @@ module.exports = app => {
         async updateBills(ctx) {
             try {
                 const data = JSON.parse(ctx.request.body.data);
+                console.log('data', data);
                 if (!data.postType || !data.postData) throw '数据错误';
                 const responseData = { err: 0, msg: '', data: {} };
                 const options = ctx.helper._.cloneDeep(ctx.contractOptions);

+ 38 - 15
app/public/js/contract_detail.js

@@ -10,6 +10,7 @@ $(document).ready(function() {
             {title: '项目名称/合同名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 360, formatter: '@', readOnly: 'readOnly.code', wordWrap: true},
             {title: '创建人', colSpan: '1', rowSpan: '2', field: 'username', hAlign: 1, width: 80, formatter: '@', readOnly: true},
             {title: '合同金额', colSpan: '1', rowSpan: '2', field: 'total_price', hAlign: 2, width: 120, formatter: '@', readOnly: true},
+            {title: '合同状态', colSpan: '1', rowSpan: '2', field: 'status', hAlign: 1, width: 100, formatter: '@', readOnly: true, getValue:'getValue.status', foreColor:'foreColor.status'},
         ],
         emptyRows: 0,
         headRows: 2,
@@ -54,21 +55,36 @@ $(document).ready(function() {
         readOnly: {
             code: function (data) {
                 return !permission_edit || data.c_code;
-            }
-        }
+            },
+            calc: function (data) {
+                return !permission_edit || data?.children?.length > 0;
+            },
+        },
     }
     const getStackedBarTip = function (data) {
         return data.stackedBarTips ? data.stackedBarTips.join('\n') : '';
     };
-    if (contract_type === contractConst.type.expenses) {
-        contractSpreadSetting.cols.push({title: '累计应付', colSpan: '1', rowSpan: '2', field: 'yf_price', hAlign: 2, width: 120, formatter: '@', readOnly: true});
-        contractSpreadSetting.cols.push({title: '支付进度', colSpan: '1', rowSpan: '2', formatter: '@', readOnly: true, field: 'stackedBar', hAlign: 0, width: 200, cellType: 'stackedBar', stackedBarCover: true, bc_type: 'grid', getTip: getStackedBarTip, hintNum: true});
-    } else if (contract_type === contractConst.type.income) {
-        contractSpreadSetting.cols.push({title: '累计应回', colSpan: '1', rowSpan: '2', field: 'yf_price', hAlign: 2, width: 120, formatter: '@', readOnly: true});
-        contractSpreadSetting.cols.push({title: '回款进度', colSpan: '1', rowSpan: '2', formatter: '@', readOnly: true, field: 'stackedBar', hAlign: 0, width: 200, cellType: 'stackedBar', stackedBarCover: true, bc_type: 'grid', getTip: getStackedBarTip, hintNum: true});
+    const getStackedBarTipSf = function (data) {
+        return data.stackedBarSfTips ? data.stackedBarSfTips.join('\n') : '';
+    };
+    const colMap = {
+      yf_price: {title: '累计应付', colSpan: '1', rowSpan: '2', field: 'yf_price', hAlign: 2, width: 120, formatter: '@', readOnly: true},
+      stackedBar: {title: '应付进度', colSpan: '1', rowSpan: '2', formatter: '@', readOnly: true, field: 'stackedBar', hAlign: 0, width: 200, cellType: 'stackedBar', stackedBarCover: true, bc_type: 'grid', getTip: getStackedBarTip, hintNum: true},
+      sf_price: {title: '累计实付', colSpan: '1', rowSpan: '2', field: 'sf_price', hAlign: 2, width: 120, formatter: '@', readOnly: true},
+      stackedBarSf: {title: '实付进度', colSpan: '1', rowSpan: '2', formatter: '@', readOnly: true, field: 'stackedBarSf', hAlign: 0, width: 200, cellType: 'stackedBar', stackedBarCover: true, bc_type: 'grid', getTip: getStackedBarTipSf, hintNum: true},
+      remark: {title: '备注1', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 120, formatter: '@', cellType: 'ellipsisAutoTip', scrollHeightClass: '.sjs-height-1'},
+      remark2: {title: '备注2', colSpan: '1', rowSpan: '2', field: 'remark2', hAlign: 0, width: 120, formatter: '@', cellType: 'ellipsisAutoTip', scrollHeightClass: '.sjs-height-1'},
+      calc: {title: '计算1', colSpan: '1', rowSpan: '2', field: 'calc', hAlign: 2, width: 120, type: 'Number', readOnly: 'readOnly.calc'},
+      calc2: {title: '计算2', colSpan: '1', rowSpan: '2', field: 'calc2', hAlign: 2, width: 120, type: 'Number', readOnly: 'readOnly.calc'},
     }
-    contractSpreadSetting.cols.push({title: '合同状态', colSpan: '1', rowSpan: '2', field: 'status', hAlign: 1, width: 100, formatter: '@', readOnly: true, getValue:'getValue.status', foreColor:'foreColor.status'});
-    contractSpreadSetting.cols.push({title: '备注', colSpan: '1', rowSpan: '2', field: 'remark', hAlign: 0, width: 120, formatter: '@', readOnly: 'readOnly.code', cellType: 'ellipsisAutoTip', scrollHeightClass: '.sjs-height-1'});
+    // 根据 colSet 组装最终显示的列配置
+    colSet.forEach(col => {
+      const colInfo = colMap?.[col.field];
+      if (colInfo && col.show) {
+        contractSpreadSetting.cols.push({...colInfo, title: col?.alias || col.name});
+      }
+    });
+
 
     const contractSpread = SpreadJsObj.createNewSpread($('#contract-spread')[0]);
     const contractSheet = contractSpread.getActiveSheet();
@@ -83,7 +99,7 @@ $(document).ready(function() {
         level: 'level',
         rootId: -1,
         keys: ['id', 'tid', 'spid', 'contract_type'],
-        calcFields: ['pay_price', 'total_price', 'sf_price', 'debit_price', 'yf_price'],
+        calcFields: ['pay_price', 'total_price', 'sf_price', 'debit_price', 'yf_price', 'calc', 'calc2'],
         autoExpand: 3,
         markExpandKey: 'contract-bills-expand' + window.location.pathname.split('/')[2] + contractConst.typeMap[contract_type],
         markExpandSubKey: window.location.pathname.split('/')[2] + contractConst.typeMap[contract_type],
@@ -100,6 +116,13 @@ $(document).ready(function() {
                 node.stackedBar.push({ color: calcFieldColor[cf], percent: ZhCalc.div(node[cf], base), field: cf, hintNum: cf === 'yf_price' });
                 node.stackedBarTips.push(`${calcFieldCaption[cf]}: ${node[cf] || 0}`);
             }
+            // 计算实付进度(单独的 stackedBarSf)
+            node.stackedBarSf = [];
+            node.stackedBarSfTips = [];
+            node.stackedBarSf.push({ color: '#bbb', percent: ZhCalc.div(node.total_price, base), field: 'total_price', hintNum: false });
+            node.stackedBarSf.push({ color: '#007bff', percent: ZhCalc.div(node.sf_price, base), field: 'sf_price', hintNum: true });
+            node.stackedBarSfTips.push(`合同金额: ${node.total_price || 0}`);
+            node.stackedBarSfTips.push(`累计${contract_type === contractConst.type.expenses ? '实付' : '实回'}: ${node.sf_price || 0}`);
         }
     };
     const contractTree = createNewPathTree('revise', treeSetting);
@@ -392,6 +415,7 @@ $(document).ready(function() {
         },
         setContract: function (sheet) {
             const node = SpreadJsObj.getSelectObject(sheet);
+            
             if (node && node.c_code) {
                 $('#htdetail-table').show();
                 $('#htpay-table').show();
@@ -573,6 +597,7 @@ $(document).ready(function() {
                 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 data = {
                     id: node.id,
                 };
@@ -589,9 +614,9 @@ $(document).ready(function() {
                 } else {
                     data[col.field] = null;
                 }
-                console.log(data);
                 // 更新至服务器
-                postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
+                
+                postData(window.location.pathname + '/update', {postType: node?.c_code ? 'update-contract' : 'update', postData: data}, function (result) {
                     const refreshNode = contractTree.loadPostData(result);
                     contractTreeSpreadObj.refreshTree(info.sheet, refreshNode);
                 });
@@ -1229,12 +1254,10 @@ $(document).ready(function() {
                 toastr.warning('签订单位(乙方)不能为空');
                 return;
             }
-            console.log(data);
             // 更新至服务器
             postData(window.location.pathname + '/update', {postType: 'update-contract', postData: data}, function (result) {
                 toastr.success('已编辑成功');
                 const refreshNode = contractTree.loadPostData(result);
-                console.log(refreshNode);
                 contractTreeSpreadObj.refreshTree(contractSheet, refreshNode);
                 const newNode = SpreadJsObj.getSelectObject(contractSheet);
                 contractTreeSpreadObj.changeContractTab(newNode, true);

+ 94 - 0
app/service/contract_col_set.js

@@ -0,0 +1,94 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Lan
+ * @date 2025/11/24
+ * @version
+ */
+
+const ContractSetting = require('../const/contract');
+
+module.exports = app => {
+
+    class ContractColSet extends app.BaseService {
+
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'contract_col_set';
+        }
+
+        _analysisData(data) {
+            if (!data) return;
+            if(data['info']) data['info'] = JSON.parse(data['info']);
+        }
+
+        async loadContractColSet(spid, tid = null, type) {
+            const result = await this.getDataByCondition({ spid, tid, type });
+            this._analysisData(result);
+            return result;
+        }
+
+        async initContractColSet(spid, tid = null, type) {
+            const data = {
+              spid,
+              tid,
+              type,
+              info: JSON.stringify(ContractSetting.defaultColSet[type])
+            }
+            await this.db.insert(this.tableName, data);
+        }
+
+        async getContractColSet(spid, tid = null, type) {
+            const curSet = await this.loadContractColSet(spid, tid, type);
+            if (curSet) return curSet;
+            await this.initContractColSet(spid, tid, type);
+            return await this.loadContractColSet(spid, tid, type);
+        }
+
+        async setContractColSet(spid, tid = null, type, colSetType, colSet) {
+            const data = {};
+            data[colSetType] = JSON.stringify(colSet);
+            await this.defaultUpdate(data, { where: { spid, tid, type } });
+        }
+
+        analysisColSetWithDefine(colSetDefine, colSet, defaultColSet) {
+            const result = [];
+            const colSetDefineMap = new Map(); // 用 Map 优化字段查找(O(1) 复杂度)
+            const colSetFieldSet = new Set(); // 记录 colSet 中已存在的字段,避免重复
+            colSetDefine.forEach(csd => {
+                colSetDefineMap.set(csd.field, csd);
+            });
+            for (const cs of colSet) {
+              const field = cs.field;
+              const csd = colSetDefineMap.get(field); // 从字段定义中取 name、fixed
+              const dcs = defaultColSet.find(x => x.field === field); // 取默认配置(兜底)
+
+              if (csd) {
+                  // 合并:colSetDefine(name、fixed)> colSet(show、alias、顺序)> defaultColSet(兜底)
+                  result.push({ ...dcs, ...cs, ...csd });
+              }
+              colSetFieldSet.add(field); // 标记该字段已处理
+            }
+            // 按 colSetDefine 中未处理的顺序,补充到结果末尾
+            for (const csd of colSetDefine) {
+                const field = csd.field;
+                if (!colSetFieldSet.has(field)) { // 未在 colSet 中找到的字段
+                    const dcs = defaultColSet.find(x => x.field === field); // 取默认配置
+                    result.push({ ...dcs, ...csd }); 
+                }
+            }
+            
+            return result;
+        }
+    }
+
+    return ContractColSet;
+};

+ 147 - 0
app/view/contract/col_set.ejs

@@ -0,0 +1,147 @@
+<!--弹出列设置-->
+<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><th width="150px">列名</th><th width="50px">显示</th><th width="150px">别名</th><th width="80px">操作</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>
+                          <% } %>
+                          <% if (cs.fixed.indexOf('alias') >= 0) { %>
+                          <td class="disabled">-</td>
+                          <% } else {%>
+                          <td><input type="text" class="form-control form-control-sm" value="<%- cs.alias %>"></td>
+                          <% } %>
+                          <td>
+                              <a href="javascript:;" class="move-up text-primary mr-2" style="text-decoration: none;">上移</a>
+                              <a href="javascript:;" class="move-down text-primary" style="text-decoration: none;">下移</a>
+                          </td>
+                      </tr>
+                      <% } %>
+                    </tbody>
+                </table>
+            </div>
+            <form class="modal-footer" action="/sp/<%- ctx.subProject.id %>/contract/<%- ctx.contractOptions.tid ? 'tender/'+ctx.contractOptions.tid + '/' : '' %>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="type" value="<%- ctx.contract_type %>">
+                <input type="hidden" name="col_type" value="info">
+                <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>
+// 立即获取表格 tbody 并绑定事件(确保脚本加载时即可初始化)
+const $tbody = $('#col-set').find('.modal-body table tbody');
+
+// 更新上下移链接显示状态
+function updateMoveButtons() {
+    const $currentRows = $tbody.find('tr');
+    const totalRows = $currentRows.length;
+
+    $currentRows.each(function(index) {
+        const $row = $(this);
+        const $upLink = $row.find('.move-up');
+        const $downLink = $row.find('.move-down');
+
+        if (totalRows === 1) {
+            $upLink.hide();
+            $downLink.hide();
+        } else if (index === 0) {
+            $upLink.hide();
+            $downLink.show();
+        } else if (index === totalRows - 1) {
+            $upLink.show();
+            $downLink.hide();
+        } else {
+            $upLink.show();
+            $downLink.show();
+        }
+    });
+}
+
+// 立即初始化链接状态(页面渲染完成后就计算显示)
+updateMoveButtons();
+
+// 若模态框打开后表格有异步或动态变更,打开时再次校验状态
+$('#col-set').on('shown.bs.modal', function() {
+    updateMoveButtons();
+});
+
+// 事件委托:从一开始就绑定上/下移事件
+$tbody.on('click', '.move-up', function(e) {
+    e.preventDefault();
+    const $currentRow = $(this).closest('tr');
+    const $prevRow = $currentRow.prev('tr');
+    
+    if ($prevRow.length) {
+        $currentRow.insertBefore($prevRow);
+        updateMoveButtons();
+    }
+});
+
+$tbody.on('click', '.move-down', function(e) {
+    e.preventDefault();
+    const $currentRow = $(this).closest('tr');
+    const $nextRow = $currentRow.next('tr');
+    
+    if ($nextRow.length) {
+        $currentRow.insertAfter($nextRow);
+        updateMoveButtons();
+    }
+});
+
+function onSetCol() {
+    const $tbody = $('#col-set').find('.modal-body table tbody');
+    const $currentRows = $tbody.find('tr');
+    const colSet = [];
+
+    $currentRows.each(function() {
+        const $row = $(this);
+        let alias = '';
+        const $aliasInput = $row.find('input[type="text"]');
+        if ($aliasInput.length) {
+            alias = $aliasInput.val().trim();
+        }
+
+        // 仅组装数据库需要的3个字段
+        const rowData = {
+            field: $row.attr('code'), // 字段标识(必填)
+            show: Number($row.find('.form-check-input').is(':checked')), // 是否显示(必填)
+            alias: alias // 别名(必填,固定列保存"-")
+        };
+
+        colSet.push(rowData); // 所有行按顺序存入数组,顺序=表格排序
+    });
+
+    $('input[name=col_set]').val(JSON.stringify(colSet));
+    return true;
+}
+</script>

+ 3 - 0
app/view/contract/detail.ejs

@@ -35,6 +35,7 @@
             <% } %>
             <div class="ml-auto">
                 <% if (ctx.session.sessionUser.is_admin) { %>
+                  <a href="#col-set" data-toggle="modal" data-target="#col-set" class="btn btn-sm btn-primary mr-2">列设置</a>
                 <a href="javascript:void(0);" data-stid="<%- ctx.contract_tender ? ctx.contractOptions.tid : '' %>" class="btn btn-sm btn-primary get-audits mr-2">成员管理</a>
                 <a href="#empower" data-toggle="modal" data-target="#empower" class="btn btn-sm btn-primary mr-2">节点授权</a>
                 <% } %>
@@ -220,6 +221,8 @@
     const whiteList = JSON.parse(unescape('<%- escape(JSON.stringify(whiteList)) %>'));
     const contractConst = JSON.parse(unescape('<%- escape(JSON.stringify(contractConst)) %>'));
     let contractTreeAudits = JSON.parse(unescape('<%- escape(JSON.stringify(contractTreeAudits)) %>'));
+    const colSet = JSON.parse(unescape('<%- escape(JSON.stringify(colSet)) %>'));
+    
     const thisUrl = JSON.parse(unescape('<%- escape(JSON.stringify(thisUrl)) %>'));
     const stdChapters = JSON.parse(unescape('<%- escape(JSON.stringify(stdChapters)) %>'));
     let contractPays = [];

+ 4 - 0
app/view/contract/detail_modal.ejs

@@ -1,4 +1,5 @@
 <% include ../shares/delete_hint_modal.ejs %>
+<% include ./col_set.ejs %>
 <% if (ctx.session.sessionUser.is_admin || audit_permission.permission_add) { %>
 <!--新增合同-->
 <div class="modal fade" id="cons-add" data-backdrop="static">
@@ -262,3 +263,6 @@
         </div>
     </div>
 </div>
+
+
+

+ 19 - 0
sql/update.sql

@@ -109,6 +109,16 @@ CREATE TABLE `zh_safe_inspection_attachment`  (
   PRIMARY KEY (`id`) USING BTREE,
   INDEX `idx_cid`(`qiid`) USING BTREE
 ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '安全巡检附件表' ROW_FORMAT = Dynamic;
+CREATE TABLE `zh_contract_col_set`  (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `spid` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '项目id',
+  `tid` int(11) NULL DEFAULT NULL COMMENT '标段id',
+  `type` tinyint(1) NOT NULL COMMENT '合同类型(1是支出,2是收入)',
+  `info` json NULL COMMENT '列设置(见/const/contract)',
+  `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '最后修改时间',
+  PRIMARY KEY (`id`)
+); ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci COMMENT = '合同列设置表';
 
 ALTER TABLE `zh_s2b_spec_pull`
 ADD COLUMN `extra_option` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NOT NULL DEFAULT '{}' COMMENT '额外配置' AFTER `pull_class`,
@@ -138,6 +148,15 @@ ALTER TABLE `zh_budget_final`
 ADD COLUMN `tz_qc_qty` decimal(24, 8) NOT NULL COMMENT '决算-变更令数量' AFTER `grow_dgn_qty`,
 ADD COLUMN `tz_qc_tp` decimal(24, 8) NOT NULL COMMENT '决算-变更令金额' AFTER `tz_qc_qty`;
 
+ALTER TABLE `zh_contract_tree` 
+ADD COLUMN `remark2` varchar(1000) NULL COMMENT '备注2' AFTER `remark`,
+ADD COLUMN `calc` decimal(30, 6) NULL COMMENT '计算1' AFTER `remark2`,
+ADD COLUMN `calc2` decimal(30, 6) NULL COMMENT '计算2' AFTER `calc`;
+
+ALTER TABLE `zh_contract` 
+ADD COLUMN `remark2` varchar(1000) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT '' COMMENT '备注2' AFTER `remark`,
+ADD COLUMN `calc` decimal(30, 6) NULL DEFAULT NULL COMMENT '计算1' AFTER `remark2`,
+ADD COLUMN `calc2` decimal(30, 6) NULL DEFAULT NULL COMMENT '计算2' AFTER `calc`;
 ------------------------------------
 -- 表数据
 ------------------------------------