Преглед изворни кода

Merge branch 'master' of http://192.168.1.41:3000/maixinrong/Calculation

TonyKang пре 5 година
родитељ
комит
f304d160ba

+ 18 - 0
app/const/external_data.js

@@ -0,0 +1,18 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Tag = {
+    FuLong: {
+        exType: 'fulong',
+        exFields: { wbsCode: 'wbs-code' }
+    }
+};
+
+module.exports = Tag;

+ 24 - 0
app/controller/ledger_controller.js

@@ -18,11 +18,13 @@ const auditConst = audit.ledger;
 const tenderMenu = require('../../config/menu').tenderMenu;
 const measureType = require('../const/tender').measureType;
 const spreadConst = require('../const/spread');
+const externalDataConst = require('../const/external_data.js');
 const fs = require('fs');
 const LzString = require('lz-string');
 const accountGroup = require('../const/account_group').group;
 const path = require('path');
 const exportExcel = require('../lib/export_excel');
+const billsPosConvert = require('../lib/bills_pos_convert');
 const xlsx = require('js-xlsx');
 
 module.exports = app => {
@@ -617,6 +619,28 @@ module.exports = app => {
             }
         }
 
+        async loadBwtz(ctx) {
+            try {
+                const ledgerData = await ctx.service.ledger.getData(ctx.tender.id);
+                const posData = this.ctx.tender.data.measure_type === measureType.tz.value
+                    ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
+                const convert = new billsPosConvert(ctx);
+                convert.loadData(ledgerData, posData, []);
+                //const result = await convert.convert();
+                const wbsCodeHis = await ctx.service.externalData.getExValue(ctx.tender.id, -1,
+                    externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode) || [];
+                const [result, needUpdate] = convert.convertByWbsCode(wbsCodeHis);
+                if (needUpdate) {
+                    await ctx.service.externalData.saveExValue(ctx.tender.id, -1,
+                        externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode, wbsCodeHis);
+                }
+                ctx.body = {err: 0, msg: '', data: result};
+            } catch(err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '加载合同支付数据错误');
+            }
+        }
+
         /**
          * 台账对比 页面 (Get)
          * @param ctx

+ 3 - 0
app/controller/login_controller.js

@@ -60,6 +60,9 @@ module.exports = app => {
                     throw '该账号已被停用,请联系销售人员';
                 }
 
+                // 调用 rotateCsrfSecret 刷新用户的 CSRF token
+                ctx.rotateCsrfSecret();
+
                 // 判断是否已经有对应用户信息,没有则跳转初始化页面
                 const needBoot = await ctx.service.customer.isNeedBoot(ctx.request.body);
                 const url = needBoot ? '/boot' : '/dashboard';

+ 1 - 1
app/controller/material_controller.js

@@ -362,7 +362,7 @@ module.exports = app => {
                             throw '请先输入编号';
                         }
                         // 判断编号为纯数字时,不能为小数
-                        if (!isNaN(data.updateData.code) && data.updateData.code.indexOf('.') !== -1) {
+                        if (!isNaN(data.updateData.code) && data.updateData.code.toString().indexOf('.') !== -1) {
                             throw '编号为纯数字时,不能为小数';
                         }
                         if (data.updateData.code.length > 15) {

+ 26 - 2
app/controller/stage_controller.js

@@ -14,12 +14,14 @@ const changeAudit = require('../const/audit').flow;
 const spreadConst = require('../const/spread');
 const tenderConst = require('../const/tender');
 const payConst = require('../const/deal_pay.js');
+const externalDataConst = require('../const/external_data.js');
 const changeConst = require('../const/change');
 const measureType = tenderConst.measureType;
 const path = require('path');
 const PayCalculator = require('../lib/pay_calc');
 const accountGroup = require('../const/account_group').group;
 const sendToWormhole = require('stream-wormhole');
+const billsPosConvert = require('../lib/bills_pos_convert');
 const fs = require('fs');
 
 module.exports = app => {
@@ -1090,7 +1092,7 @@ module.exports = app => {
                 ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order);
             }
         }
- 
+
         /**
          * 审核比较 页面 (Get)
          * @param ctx
@@ -1145,13 +1147,35 @@ module.exports = app => {
                 await this._getStageAuditViewData(ctx);
                 const renderData = await this._getDefaultRenderData(ctx);
                 renderData.jsFiles = this.app.jsFiles.common.concat(this.app.jsFiles.stage.bwtz);
-                await this.layout('stage/bwtz.ejs', renderData);
+                await this.layout('stage/bwtz.ejs', renderData, 'stage/audit_modal.ejs');
             } catch (err) {
                 this.log(err);
                 ctx.redirect('/tender/' + ctx.tender.id + '/measure/stage/' + ctx.params.order);
             }
         }
 
+        async loadBwtz(ctx) {
+            try {
+                const ledgerData = await this._getStageLedgerData(ctx);
+                const posData = await this._getStagePosData(ctx);
+                const changeData = await this._getStageChangeData(ctx);
+                const convert = new billsPosConvert(ctx);
+                convert.loadData(ledgerData, posData, changeData);
+                // const result = convert.convert();
+                const wbsCodeHis = await ctx.service.externalData.getExValue(ctx.tender.id, -1,
+                    externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode) || [];
+                const [result, needUpdate] = convert.convertByWbsCode(wbsCodeHis);
+                if (needUpdate) {
+                    await ctx.service.externalData.saveExValue(ctx.tender.id, -1,
+                        externalDataConst.FuLong.exType, externalDataConst.FuLong.exFields.wbsCode, wbsCodeHis);
+                }
+                ctx.body = {err: 0, msg: '', data: result};
+            } catch(err) {
+                this.log(err);
+                ctx.body = this.ajaxErrorBody(err, '加载合同支付数据错误');
+            }
+        }
+
         /**
          * 报表
          * @param ctx

+ 1 - 1
app/controller/stage_extra_controller.js

@@ -89,7 +89,7 @@ module.exports = app => {
                 const renderData = {
                     jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.stageExtra.bonus)
                 }
-                await this.layout('stage_extra/bonus.ejs', renderData);
+                await this.layout('stage_extra/bonus.ejs', renderData, 'stage_extra/bonus_modal.ejs');
             } catch (err) {
                 ctx.helper.log(err);
             }

+ 2 - 2
app/extend/helper.js

@@ -936,9 +936,9 @@ module.exports = {
             this.ctx.debugInfo = { key: {}, other: [] };
         }
         if (key) {
-             this.ctx.debugInfo.key[key] = data;
+            this.ctx.debugInfo.key[key] = data;
         } else {
             this.ctx.debugInfo.other.push(data);
         }
-    }
+    },
 };

+ 322 - 0
app/lib/bills_pos_convert.js

@@ -0,0 +1,322 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const Ledger = require('./ledger');
+const splitChar = '-';
+
+class BillsPosConvert {
+
+    constructor(ctx) {
+        const self = this;
+        this.ctx = ctx;
+        this._ = this.ctx.helper._;
+
+        this.tpFields = ['total_price', 'contract_tp', 'qc_tp', 'gather_tp',
+            'pre_contract_tp', 'pre_qc_tp', 'pre_gather_tp', 'end_contract_tp', 'end_qc_tp'];
+        this.baseCalcFields = ['quantity', 'contract_qty', 'qc_qty', 'gather_qty',
+            'pre_contract_qty', 'pre_qc_qty', 'pre_gather_qty', 'end_contract_qty', 'end_qc_qty', 'end_gather_qty'];
+        // mainData
+        this.bpcTree = new Ledger.billsTree(this.ctx, {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            keys: ['id', 'tender_id', 'ledger_id'],
+            stageId: 'id',
+            updateFields: ['contract_qty', 'contract_tp', 'qc_qty', 'qc_tp'],
+            calcFields: [],
+        });
+        this.bpcPos = new Ledger.pos({
+            id: 'id', ledgerId: 'lid',
+            updateFields: ['contract_qty', 'qc_qty'],
+        });
+        this.bpcChange = []
+        // reusltData
+        this.resultTreeSetting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+        };
+        this.resultTree = new Ledger.filterTree(this.ctx, this.resultTreeSetting);
+    }
+
+    // 加载基础数据
+    _loadLedgerData (ledger) {
+        const self = this;
+        // 加载树结构数据
+        this.bpcTree.loadDatas(ledger);
+    }
+    _loadPosData(pos) {
+        this.bpcPos.loadDatas(pos);
+    }
+    loadData (ledger, pos, change) {
+        this._loadLedgerData(ledger);
+        this._loadPosData(pos);
+        this.bpcChange = change;
+    }
+
+    _convertXmj(data) {
+        const xmj = this.resultTree.addData(data, ['ledger_id', 'ledger_pid', 'order', 'level', 'tender_id', 'full_path',
+            'code', 'name', 'unit', 'dgn_qty1', 'dgn_qty2', 'drawing_code', 'postil', 'memo']);
+        return xmj;
+    }
+    // v1
+    _loadField(node, data, fields) {
+        for (const prop in data) {
+            if (fields.indexOf(prop) >= 0) node[prop] = data[prop];
+        }
+    }
+    // v2
+    _loadPosCalcFields(node, data) {
+        const tpDecimal = this.ctx.tender.info.decimal.tp;
+        node.quantity = this.ctx.helper.add(node.quantity, data.quantity);
+        node.total_price = this.ctx.helper.add(node.total_price, this.ctx.helper.mul(node.unit_price, data.quantity, tpDecimal));
+
+        node.contract_qty = this.ctx.helper.add(node.contract_qty, data.contract_qty);
+        node.contract_tp = this.ctx.helper.add(node.contract_tp, this.ctx.helper.mul(node.unit_price, data.contract_qty, tpDecimal));
+        node.qc_qty = this.ctx.helper.add(node.qc_qty, data.qc_qty);
+        node.qc_tp = this.ctx.helper.add(node.qc_tp, this.ctx.helper.mul(node.unit_price, data.qc_qty, tpDecimal));
+        node.gather_qty = this.ctx.helper.add(node.gather_qty, data.gather_qty);
+
+        node.pre_contract_qty = this.ctx.helper.add(node.pre_contract_qty, data.pre_contract_qty);
+        node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, this.ctx.helper.mul(node.unit_price, data.pre_contract_qty, tpDecimal));
+        node.pre_qc_qty = this.ctx.helper.add(node.pre_qc_qty, data.pre_qc_qty);
+        node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, this.ctx.helper.mul(node.unit_price, data.pre_qc_qty, tpDecimal));
+        node.pre_gather_qty = this.ctx.helper.add(node.pre_gather_qty, data.pre_gather_qty);
+    }
+    _loadBillsCalcFields(node, data) {
+        node.quantity = this.ctx.helper.add(node.quantity, data.quantity);
+        node.total_price = this.ctx.helper.add(node.total_price, data.total_price);
+
+        node.contract_qty = this.ctx.helper.add(node.contract_qty, data.contract_qty);
+        node.contract_tp = this.ctx.helper.add(node.contract_tp, data.contract_tp);
+        node.qc_qty = this.ctx.helper.add(node.qc_qty, data.qc_qty);
+        node.qc_tp = this.ctx.helper.add(node.qc_tp, );
+        node.gather_qty = this.ctx.helper.add(node.gather_qty, data.gather_qty);
+
+        node.pre_contract_qty = this.ctx.helper.add(node.pre_contract_qty, data.pre_contract_qty);
+        node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, data.pre_contract_tp);
+        node.pre_qc_qty = this.ctx.helper.add(node.pre_qc_qty, data.pre_qc_qty);
+        node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, data.pre_qc_tp);
+        node.pre_gather_qty = this.ctx.helper.add(node.pre_gather_qty, data.pre_gather_qty);
+    }
+    _convertGcl(node, xmj) {
+        if (!xmj) return;
+
+        if (!xmj.unitTree) {
+            xmj.unitTree = new Ledger.filterGatherTree(this.ctx, this.resultTreeSetting);
+        }
+        const pos = this.bpcPos.getLedgerPos(node.id);
+        if (pos && pos.length > 0) {
+            for (const p of pos) {
+                const posUnit = xmj.unitTree.addNode({pos_name: p.name,
+                    b_code: null, name: '', unit: null, unit_price: null});
+                const gclUnit = xmj.unitTree.addNode({pos_name: '',
+                    b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price
+                }, posUnit);
+                //loadField(gclUnit, p, baseCalcFields);
+                this._loadPosCalcFields(gclUnit, p);
+                if (!gclUnit.changes) gclUnit.changes = [];
+                for (const c of this.bpcChange) {
+                    if (c.lid === node.id && c.pid === p.id && c.qty && c.qty !== 0) {
+                        gclUnit.changes.push(c);
+                    }
+                }
+            }
+        } else {
+            const unit = xmj.unitTree.addNode({pos_name: '',
+                b_code: node.b_code, name: node.name, unit: node.unit, unit_price: node.unit_price});
+            //loadField(unit, node, baseCalcFields);
+            this._loadBillsCalcFields(unit, node);
+            if (!unit.changes) unit.changes = [];
+            for (const c of this.bpcChange) {
+                if (c.lid === node.id && c.pid == -1 && c.qty && c.qty !== 0) {
+                    unit.changes.push(c);
+                }
+            }
+        }
+    }
+    _recursiveConvertNode(nodes, xmj = null) {
+        if (!nodes || !nodes instanceof Array || nodes.length === 0) return;
+
+        for (const node of nodes) {
+            if (node.b_code && node.b_code !== '') {
+                if (!node.children || node.children.length === 0) {
+                    this._convertGcl(node, xmj);
+                }
+                this._recursiveConvertNode(node.children, xmj);
+            } else {
+                const curXmj = this._convertXmj(node);
+                this._recursiveConvertNode(node.children, curXmj);
+                if (!node.children || node.children.length === 0) {
+                    this._loadField(curXmj, node, this.tpFields);
+                }
+            }
+
+        }
+    }
+
+    _calculateChild(child) {
+        //const tpDecimal = this.ctx.tender.info.decimal.tp;
+
+        child.gather_qty = this.ctx.helper.add(child.contract_qty, child.qc_qty);
+        child.pre_gather_qty = this.ctx.helper.add(child.pre_contract_qty, child.pre_qc_qty);
+        child.end_contract_qty = this.ctx.helper.add(child.contract_qty, child.pre_contract_qty);
+        child.end_qc_qty = this.ctx.helper.add(child.qc_qty, child.pre_qc_qty);
+        child.end_gather_qty = this.ctx.helper.add(child.gather_qty, child.pre_gather_qty);
+
+        // v1
+        // child.total_price = this.ctx.helper.mul(child.unit_price, child.quantity, tpDecimal);
+        // child.contract_tp = this.ctx.helper.mul(child.unit_price, child.contract_qty, tpDecimal);
+        // child.qc_tp = this.ctx.helper.mul(child.unit_price, child.qc_qty, tpDecimal);
+        // child.gather_tp = this.ctx.helper.mul(child.unit_price, child.gather_qty, tpDecimal);
+        // child.pre_contract_tp = this.ctx.helper.mul(child.unit_price, child.pre_contract_qty, tpDecimal);
+        // child.pre_qc_tp = this.ctx.helper.mul(child.unit_price, child.pre_qc_qty, tpDecimal);
+        // child.pre_gather_tp = this.ctx.helper.mul(child.unit_price, child.pre_gather_qty, tpDecimal);
+        // child.end_contract_tp = this.ctx.helper.mul(child.unit_price, child.end_contract_qty, tpDecimal);
+        // child.end_qc_tp = this.ctx.helper.mul(child.unit_price, child.end_qc_qty, tpDecimal);
+        // child.end_gather_tp = this.ctx.helper.mul(child.unit_price, child.end_gather_qty, tpDecimal);
+
+        // v2
+        child.gather_tp = this.ctx.helper.add(child.contract_tp, child.qc_tp);
+        child.pre_gather_tp = this.ctx.helper.add(child.pre_contract_tp, child.pre_qc_tp);
+        child.end_contract_tp = this.ctx.helper.add(child.pre_contract_tp, child.contract_tp);
+        child.end_qc_tp = this.ctx.helper.add(child.pre_qc_tp, child.qc_tp);
+        child.end_gather_tp = this.ctx.helper.add(child.end_contract_tp, child.end_qc_tp);
+
+        child.final_tp = this.ctx.helper.add(child.total_price, child.end_qc_tp);
+        child.end_gather_percent = this.ctx.helper.mul(this.ctx.helper.div(child.end_gather_tp, child.final_tp, 4), 100);
+
+        child.bgl_code = this.ctx.helper._.uniq(this.ctx.helper._.map(child.changes, 'c_code')).join(';');
+    }
+    _calculateNode(node, children) {
+        for (const child of children) {
+            node.total_price = this.ctx.helper.add(node.total_price, child.total_price);
+            node.contract_tp = this.ctx.helper.add(node.contract_tp, child.contract_tp);
+            node.qc_tp = this.ctx.helper.add(node.qc_tp, child.qc_tp);
+            node.gather_tp = this.ctx.helper.add(node.gather_tp, child.gather_tp);
+            node.pre_contract_tp = this.ctx.helper.add(node.pre_contract_tp, child.pre_contract_tp);
+            node.pre_qc_tp = this.ctx.helper.add(node.pre_qc_tp, child.pre_qc_tp);
+            node.pre_gather_tp = this.ctx.helper.add(node.pre_gather_tp, child.pre_gather_tp);
+            node.end_contract_tp = this.ctx.helper.add(node.end_contract_tp, child.end_contract_tp);
+            node.end_qc_tp = this.ctx.helper.add(node.end_qc_tp, child.end_qc_tp);
+            node.end_gather_tp = this.ctx.helper.add(node.end_gather_tp, child.end_gather_tp);
+        }
+        node.final_tp = this.ctx.helper.add(node.total_price, node.end_qc_tp);
+        node.end_gather_percent = this.ctx.helper.mul(this.ctx.helper.div(node.end_gather_tp, node.final_tp, 4), 100);
+    }
+    _recursiveCalcUnitNodes(nodes) {
+        if (!nodes || !nodes instanceof Array || nodes.length === 0) return;
+
+        for (const node of nodes) {
+            this._recursiveCalcUnitNodes(node.children);
+            if (node.children && node.children.length > 0) {
+                this._calculateNode(node, node.children);
+            } else {
+                this._calculateChild(node);
+            }
+        }
+    }
+    _recursiveCalculateAndSort(nodes) {
+        const self = this;
+        if (!nodes || !nodes instanceof Array || nodes.length === 0) return;
+
+        for (const node of nodes) {
+            this._recursiveCalculateAndSort(node.children);
+            if (node.unitTree) {
+                node.unitTree.sortTreeNodeCustom('b_code', function (a, b) {
+                    const numReg = /^[0-9]+$/;
+                    const aCodes = a ? a.split('-') : [], bCodes = b ? b.split('-') : [];
+                    for (let i = 0, iLength = Math.min(aCodes.length, bCodes.length); i < iLength; ++i) {
+                        const iCompare = self.ctx.helper.compareCode(aCodes[i], bCodes[i]);
+                        if (iCompare !== 0) {
+                            return iCompare;
+                        }
+                    }
+                }, false);
+                this._recursiveCalcUnitNodes(node.unitTree.children);
+                this._calculateNode(node, node.unitTree.children, this.tpFields);
+            } else if (node.children && node.children.length > 0) {
+                this._calculateNode(node, node.children, this.tpFields);
+            } else {
+                this._calculateChild(node);
+            }
+        }
+    }
+    _calculateAndSortResult() {
+        this.resultTree.sortTreeNode();
+        this._recursiveCalculateAndSort(this.resultTree.children);
+    }
+
+    _generateCodeByWbsCode(wbsCode) {
+        let needUpdate = false;
+        for (const node of this.resultTree.nodes) {
+            if (!node.unitTree) continue;
+
+            let xmj = wbsCode.find(function (x) {
+                return x.xmj_code === node.code;
+            });
+            for (const unit of node.unitTree.nodes) {
+                if (!unit.pos_name || unit.pos_name === '') continue;
+
+                if (!xmj) {
+                    xmj = {xmj_code: node.code, pos: []};
+                    wbsCode.push(xmj);
+                    needUpdate = true;
+                }
+                let index = xmj.pos.indexOf(unit.pos_name);
+                if (index < 0) {
+                    unit.code = xmj.xmj_code + splitChar + (xmj.pos.length + 1);
+                    xmj.pos.push(unit.pos_name);
+                    needUpdate = true;
+                } else {
+                    unit.code = xmj.xmj_code + splitChar + (index + 1);
+                }
+            }
+        }
+        return needUpdate;
+    }
+
+    _getResultData() {
+        const result = this.resultTree.getDefaultDatas();
+        for (const r of result) {
+            if (r.unitTree) {
+                r.unitTreeData = r.unitTree.getDefaultDatas();
+                delete r.unitTree;
+            }
+        }
+        return result;
+    }
+    // 转换数据
+    convert() {
+        this._recursiveConvertNode(this.bpcTree.children);
+        this._calculateAndSortResult();
+        return this._getResultData();
+    }
+
+    /**
+     * 转换数据,会根据wbsCodeHis生成计量单元的code
+     * 如果计量单元在wbsCodeHis中没有历史,则会更新传入的wbsCodeHis,并返回[result, true], 反之返回[result, false]
+     * @param wbsCodeHis
+     * @returns {*[]}
+     */
+    convertByWbsCode(wbsCodeHis) {
+        this._recursiveConvertNode(this.bpcTree.children);
+        this._calculateAndSortResult();
+        const needUpdate = this._generateCodeByWbsCode(wbsCodeHis);
+        return [this._getResultData(), needUpdate];
+    }
+}
+
+module.exports = BillsPosConvert;

+ 174 - 47
app/lib/ledger.js

@@ -10,7 +10,7 @@
 
 const itemsPre = 'id_';
 
-class billsTree {
+class baseTree {
     /**
      * 构造函数
      */
@@ -60,7 +60,11 @@ class billsTree {
         });
         return children;
     };
-
+    /**
+     * 获取节点的 index
+     * @param node
+     * @returns {number}
+     */
     getNodeSerialNo(node) {
         return this.nodes.indexOf(node);
     }
@@ -95,7 +99,6 @@ class billsTree {
         }
         addSortNodes(this.children);
     }
-
     /**
      * 加载数据(初始化), 并给数据添加部分树结构必须数据
      * @param datas
@@ -165,37 +168,47 @@ class billsTree {
     };
 
     /**
-     * 检查节点是否是最底层项目节
-     * @param node
-     * @returns {boolean}
+     * 根据 字段名称 获取数据
+     * @param fields
+     * @returns {Array}
      */
-    isLeafXmj(node) {
-        if (node.b_code && node.b_code !== '') {
-            return false;
-        }
-        for (const child of node.children) {
-            if (!child.b_code || child.b_code === '') {
-                return false;
+    getDatas (fields) {
+        const datas = [];
+        for (const node of this.nodes) {
+            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
+            const data = {};
+            for (const field of fields) {
+                data[field] = node[field];
             }
+            datas.push(data);
         }
-        return true;
+        return datas;
     }
-
     /**
-     * 查询最底层项目节(本身或父项)
-     * @param {Object} node - 查询节点
-     * @returns {Object}
+     * 排除 某些字段 获取数据
+     * @param fields
+     * @returns {Array}
      */
-    getLeafXmjParent(node) {
-        let parent = node;
-        while (parent) {
-            if (this.isLeafXmj(parent)) {
-                return parent;
-            } else {
-                parent = this.getParent(parent);
+    getDatasWithout (fields) {
+        const datas = [];
+        for (const node of this.nodes) {
+            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
+            const data = {};
+            for (const field in node) {
+                if (fields.indexOf(field) === -1) {
+                    data[field] = node[field];
+                }
             }
+            datas.push(data);
         }
-        return null;
+        return datas;
+    }
+    /**
+     * 获取默认数据 剔除一些树结构需要的缓存数据
+     * @returns {Array}
+     */
+    getDefaultDatas() {
+        return this.getDatasWithout(['expanded', 'visible', 'children', 'index']);
     }
 
     _mapTreeNode () {
@@ -250,37 +263,149 @@ class billsTree {
             }
         }
     }
+}
 
-    getDatas (fields) {
-        const datas = [];
-        for (const node of this.nodes) {
-            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
-            const data = {};
-            for (const field of fields) {
-                data[field] = node[field];
+class billsTree extends baseTree {
+    /**
+     * 检查节点是否是最底层项目节
+     * @param node
+     * @returns {boolean}
+     */
+    isLeafXmj(node) {
+        if (node.b_code && node.b_code !== '') {
+            return false;
+        }
+        for (const child of node.children) {
+            if (!child.b_code || child.b_code === '') {
+                return false;
             }
-            datas.push(data);
         }
-        return datas;
+        return true;
     }
+    /**
+     * 查询最底层项目节(本身或父项)
+     * @param {Object} node - 查询节点
+     * @returns {Object}
+     */
+    getLeafXmjParent(node) {
+        let parent = node;
+        while (parent) {
+            if (this.isLeafXmj(parent)) {
+                return parent;
+            } else {
+                parent = this.getParent(parent);
+            }
+        }
+        return null;
+    }
+}
 
-    getDatasWithout (fields) {
-        const datas = [];
-        for (const node of this.nodes) {
-            if (node.b_code && node.b_code !== '') node.chapter = this.ctx.helper.getChapterCode(node.b_code);
-            const data = {};
-            for (const field in node) {
-                if (fields.indexOf(field) === -1) {
-                    data[field] = node[field];
+class filterTree extends baseTree {
+    addData(data, fields) {
+        const item = {};
+        for (const prop in data) {
+            if (fields.indexOf(prop) >= 0) {
+                item[prop] = data[prop];
+            }
+        }
+        const keyName = itemsPre + item[this.setting.id];
+        if (!this.items[keyName]) {
+            item.children = [];
+            item.is_leaf = true;
+            item.expanded = true;
+            item.visible = true;
+            this.items[keyName] = item;
+            this.datas.push(item);
+            if (item[this.setting.pid] === this.setting.rootId) {
+                this.children.push(item);
+            } else {
+                const parent = this.getParent(item);
+                if (parent) {
+                    parent.is_leaf = false;
+                    parent.children.push(item);
                 }
             }
-            datas.push(data);
+        } else {
+            return this.items[keyName];
         }
-        return datas;
+        return item;
+    }
+}
+
+class filterGatherTree extends baseTree {
+
+    clearDatas() {
+        this.items = {};
+        this.nodes = [];
+        this.datas = [];
+        this.children = [];
     }
 
-    getDefaultDatas() {
-        return this.getDatasWithout(['expanded', 'visible', 'children', 'index']);
+    get newId() {
+        if (!this._maxId) {
+            this._maxId = 0;
+        }
+        this._maxId++;
+        return this._maxId;
+    }
+
+    addNode(data, parent) {
+        data[this.setting.pid] = parent ? parent[this.setting.id] : this.setting.rootId;
+        let item = this.ctx.helper._.find(this.items, data);
+        if (item) return item;
+
+        item = data;
+        item[this.setting.id] = this.newId;
+        const keyName = itemsPre + item[this.setting.id];
+        item.children = [];
+        item.is_leaf = true;
+        item.expanded = true;
+        item.visible = true;
+        this.items[keyName] = item;
+        this.datas.push(item);
+        if (parent) {
+            item[this.setting.fullPath] = parent[this.setting.fullPath] + '-' + item[this.setting.id];
+            item[this.setting.level] = parent[this.setting.level] + 1;
+            item[this.setting.order] = parent.children.length + 1;
+            parent.is_leaf = false;
+            parent.children.push(item);
+        } else {
+            item[this.setting.fullPath] = '' + item[this.setting.id];
+            item[this.setting.level] = 1;
+            item[this.setting.order] = this.children.length + 1;
+            this.children.push(item);
+        }
+        return item;
+    }
+
+    sortTreeNodeCustom(field, fun, isResort) {
+        const self = this;
+        const sortNodes = function (nodes) {
+            nodes.sort(function (a, b) {
+                return fun(a[field], b[field]);
+            });
+            for (const [i, node] of nodes.entries()) {
+                node.order = i + 1;
+            }
+        };
+        const addSortNodes = function (nodes) {
+            if (!nodes) { return }
+            for (let i = 0; i < nodes.length; i++) {
+                self.nodes.push(nodes[i]);
+                nodes[i].index = self.nodes.length - 1;
+                if (!isResort) {
+                    nodes[i].children = self.getChildren(nodes[i]);
+                }
+                sortNodes(nodes[i].children);
+                addSortNodes(nodes[i].children);
+            }
+        };
+        this.nodes = [];
+        if (!isResort) {
+            this.children = this.getChildren();
+        }
+        sortNodes(this.children);
+        addSortNodes(this.children);
     }
 }
 
@@ -355,4 +480,6 @@ class pos {
 module.exports = {
     billsTree,
     pos,
+    filterTree,
+    filterGatherTree,
 };

+ 27 - 7
app/lib/rpt_data_analysis.js

@@ -228,10 +228,10 @@ const gatherChapter = {
             order: 1,
         },
         custom: [
-            {name: '已包含在清单合计中的材料、工程设备、专业工程暂估价', order: 2},
+            {name: '已包含在清单合计中的材料、工程设备、专业工程暂估价', order: 2, visible: false},
             {name: '清单合计减去材料、工程设备、专业工程暂估价(即8-9=10)', order_calc: 'o1-o2', order: 3},
             {name: '计日工合计', node_type: '计日工', order: 4},
-            {name: '暂列金额(不含计日工总额)(即10×暂列金额比列)', order: 5, match: [{node_type: '暂列金额'}, {field: 'name', part: '暂列金额'}, {field: 'name', part: '暂定金额'}]},
+            {name: '暂列金额(不含计日工总额)(即10×暂列金额比列)', order: 5, match: [{node_type: '暂列金额'}, {field: 'name', part: '暂列金额'}, {field: 'name', all: '暂定金额'}]},
             {name: '投标报价、台账价(8+11+12)=13', order_calc: 'o1+o4+o5', order: 6},
         ],
         rela: [
@@ -253,6 +253,7 @@ const gatherChapter = {
             const cc = { code: c.code, name: c.name, cType: 1 };
             cc.serialNo = serialNo++;
             cc.filter = '^' + c.code.substr(0, c.code.length - 2) + '[0-9]{2}-';
+            //cc.visible = true;
             gclChapter.push(cc);
 
             if (options.activeFields) {
@@ -263,16 +264,32 @@ const gatherChapter = {
         }
 
         if (options.unChapter) {
-            gclChapter.push({ name: options.unChapter.name, cType: 21, serialNo: serialNo + options.unChapter.order, order: options.unChapter.order });
+            gclChapter.push({
+                name: options.unChapter.name, cType: 21,
+                serialNo: serialNo + options.unChapter.order, order: options.unChapter.order,
+                visible: options.unChapter.visible,
+            });
         }
         if (options.gclSum) {
-            otherChapter.push({ name: options.gclSum.name, cType: 11, serialNo: serialNo + options.gclSum.order, order: options.gclSum.order  });
+            otherChapter.push({
+                name: options.gclSum.name, cType: 11,
+                serialNo: serialNo + options.gclSum.order, order: options.gclSum.order,
+                visible: options.gclSum.visible,
+            });
         }
         if (options.unGcl) {
-            otherChapter.push({ name: options.unGcl.name, cType: 31, serialNo: serialNo + options.unGcl.order, order: options.unGcl.order  });
+            otherChapter.push({
+                name: options.unGcl.name, cType: 31,
+                serialNo: serialNo + options.unGcl.order, order: options.unGcl.order,
+                visible: options.unGcl.visible,
+            });
         }
         if (options.sum) {
-            otherChapter.push({ name: options.sum.name , cType: 41, serialNo: serialNo + options.sum.order, order: options.sum.order  });
+            otherChapter.push({
+                name: options.sum.name , cType: 41,
+                serialNo: serialNo + options.sum.order, order: options.sum.order,
+                visible: options.sum.visible,
+            });
         }
 
         if (options.custom && options.custom instanceof Array) {
@@ -281,6 +298,7 @@ const gatherChapter = {
                     name: c.name, serialNo: serialNo + c.order,
                     order_calc: c.order_calc,
                     cType: 5, order: c.order,
+                    visible: c.visible,
                 };
                 if (c.match) {
                     cc.match = JSON.parse(JSON.stringify(c.match));
@@ -382,6 +400,8 @@ const gatherChapter = {
                 const value = d[m.field];
                 if (m.part && value) {
                     if (value.indexOf(m.part) >= 0) return true;
+                } else if (m.all && value) {
+                    if (m.all === value) return true;
                 }
             }
         }
@@ -450,7 +470,7 @@ const gatherChapter = {
                 }
                 return false;
             } else {
-                return true;
+                return (x.visible !== undefined && x.visible !== null) ? x.visible : true;
             }
         });
     },

+ 5 - 5
app/lib/stage_im.js

@@ -252,7 +252,7 @@ class StageIm {
         if (im.calc_memo !== undefined && im.calc_memo !== null && im.calc_memo !== '') return;
 
         if (im.leafXmjs && im.leafXmjs.length > 0) {
-            const memo = ['本期计量:' + im.jl + ' ' + im.unit];
+            const memo = ['本期计量:' + (this.ctx.helper.checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit];
             for (const lx of im.leafXmjs) {
                 for (const p of lx.pos) {
                     memo.push(p.name + ':' + p.jl + ' ' + im.unit);
@@ -268,7 +268,7 @@ class StageIm {
                         memo.push(p.name + ':' + p.jl + ' ' + b.unit);
                     }
                 } else {
-                    memo.push('清单' + (i + 1) + ':' + b.b_code + ' ' + b.name + ':' + b.jl + ' ' + b.unit);
+                    memo.push('清单' + (i + 1) + ':' + b.b_code + ' ' + b.name + ':' + (this.ctx.helper.checkZero(b.jl) ? 0 : b.jl) + ' ' + b.unit);
                 }
             }
             im.calc_memo = memo.join('\n');
@@ -607,7 +607,7 @@ class StageIm {
                     im.end_tp = this.ctx.helper.mul(im.end_jl, im.unit_price, tp_decimal);
                     im.end_contract_tp = this.ctx.helper.mul(im.end_contract_jl, im.unit_price, tp_decimal);
                     im.end_qc_tp = this.ctx.helper.mul(im.end_qc_jl, im.unit_price, tp_decimal);
-                    im.calc_memo = '本期计量:' + im.jl + ' ' + im.unit;
+                    im.calc_memo = '本期计量:' + (this.ctx.helper.checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                     this._checkCustomDetail(im);
                     this.ImData.push(im);
                     if (pp.qc_qty && pp.qc_qty !== 0) {
@@ -619,7 +619,7 @@ class StageIm {
                     }
                 }
             } else {
-                if (this.ctx.helper.checkZero(p.contract_qty) && this.ctx.helper.checkZero(p.qc_qty)) { continue; }
+                if (this.ctx.helper.checkZero(p.gather_qty) && this.ctx.helper.checkZero(p.gather_tp)) { continue; }
 
                 const im = {
                     id: this.ImData.length + 1,
@@ -639,7 +639,7 @@ class StageIm {
                     position: '',
                     lIndex: this.billsTree.getNodeSerialNo(node),
                 };
-                im.calc_memo = '本期计量:' + im.jl + ' ' + im.unit;
+                im.calc_memo = '本期计量:' + (this.ctx.helper.checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                 this._checkCustomDetail(im);
                 this.ImData.push(im);
                 if (p.qc_qty && p.qc_qty !== 0) {

+ 1 - 1
app/public/js/global.js

@@ -665,7 +665,7 @@ function toastMessageUniq (obj) {
 }
 
 function trimInvalidChar(str) {
-    return $.trim(str).replace('\n', '').replace('\r', '').replace('\t', '');
+    return $.trim(str).replace(/\n/g, '').replace(/\r/g, '').replace(/\t/g, '');
 }
 
 jQuery.bootstrapLoading = {

+ 17 - 4
app/public/js/ledger_bwtz.js

@@ -9,7 +9,6 @@
  */
 
 $(document).ready(() => {
-    const preUrl = window.location.pathname.split('/').slice(0, 4).join('/');
     autoFlashHeight();
     const xmjSpread = SpreadJsObj.createNewSpread($('#xmj-spread')[0]);
     const xmjSheet = xmjSpread.getActiveSheet();
@@ -34,9 +33,23 @@ $(document).ready(() => {
         unitTreeObj.loadCurUnitData();
     });
 
-    postData(preUrl + '/load', {}, function (result) {
-        billsPosConvertModel.loadData(result.bills, result.pos, [], decimal);
-        const xmjTree = billsPosConvertModel.convert();
+    postData(window.location.pathname + '/load', {}, function (result) {
+        const setting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+        };
+        const xmjTree = createNewPathTree('base', setting);
+        xmjTree.loadDatas(result);
+        for (const n of xmjTree.nodes) {
+            if (n.unitTreeData) {
+                n.unitTree = createNewPathTree('base', setting);
+                n.unitTree.loadDatas(n.unitTreeData);
+            }
+        }
         SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
         unitTreeObj.loadCurUnitData();
     });

+ 215 - 8
app/public/js/material.js

@@ -20,6 +20,63 @@ function loadUpdateMaterials(newMaterial, fields) {
     }
 }
 
+// function DatePickerCellType() {
+// }
+// DatePickerCellType.prototype = new GC.Spread.Sheets.CellTypes.Base();
+// DatePickerCellType.prototype.createEditorElement = function () {
+//     //Create input presenter.
+//     var input = document.createElement("input");
+//     return input;
+// };
+// DatePickerCellType.prototype.activateEditor = function (editorContext, cellStyle, cellRect) {
+//     //Initialize input editor.
+//     if (editorContext) {
+//         const $editor = $(editorContext);
+//         GC.Spread.Sheets.CellTypes.Base.prototype.activateEditor.apply(this, arguments);
+//         $editor.datepicker({
+//             language: 'zh',
+//         });
+//         $editor.css("position", "absolute");
+//         $editor.attr("gcUIElement", "gcEditingInput");
+//         $(".ui-datepicker").attr("gcUIElement", "gcEditingInput");
+//     }
+// }
+// // DatePickerCellType.prototype.deactivateEditor = function (editorContext) {
+// //     //Remove input editor when end editor status.
+// //     if (editorContext) {
+// //         var element = editorContext;
+// //         $(element).datepicker().data('datepicker').hide();
+// //         $(element).datepicker().data('datepicker').destroy();
+// //         // $(element).datepicker("hide");
+// //         // $(element).datepicker("destroy");
+// //     }
+// //     GC.Spread.Sheets.CellTypes.Base.prototype.deactivateEditor.apply(this, arguments)
+// // };
+// // DatePickerCellType.prototype.setEditorValue = function (editor, value) {
+// //     //Sync value from Cell value to editor value.
+// //     console.log($(editor), value);
+// //     // $(editor).datepicker("setDate", value);
+// //     $(editor).datepicker().data('datepicker').selectDate(value);
+// // };
+// // DatePickerCellType.prototype.getEditorValue = function (editor) {
+// //     //Sync value from editor value to cell value.
+// //     $(editor).datepicker({
+// //         onSelect: function onSelect(fd, date) {
+// //             console.log(fd);
+// //             $(editor).val(fd);
+// //         }
+// //     });
+// //     // return $(editor).datepicker("getDate");
+// //     return $(editor).val();
+// // };
+// DatePickerCellType.prototype.updateEditor = function (editorContext, cellStyle, cellRect) {
+//     if (editorContext) {
+//         const $editor = $(editorContext);
+//         $editor.css("width", cellRect.width - 1);
+//         $editor.css("height", cellRect.height - 3);
+//     }
+// };
+
 function resetTpTable() {
     const rate = $('#changeRate').val();
     const bqhs = ZhCalc.round(ZhCalc.mul(m_tp, 1+rate/100), 2);
@@ -34,12 +91,12 @@ $(document).ready(() => {
     const materialSpread = SpreadJsObj.createNewSpread($('#material-spread')[0]);
     const materialSpreadSetting = {
         cols: [
-            {title: '调差类型', colSpan: '1', rowSpan: '2', field: 't_type', hAlign: 0, width: 80, formatter: '@', readOnly: 'readOnly.isEdit', cellType: 'customizeCombo', comboItems: materialType.t_type, cellTypeKey: 1},
+            {title: '调差类型', colSpan: '1', rowSpan: '2', field: 't_type', hAlign: 1, width: 80, formatter: '@', readOnly: 'readOnly.isEdit', cellType: 'customizeCombo', comboItems: materialType.t_type, cellTypeKey: 1},
             {title: '编号', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 180, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
             {title: '规格', colSpan: '1', rowSpan: '2', field: 'spec', hAlign: 0, width: 180, formatter: '@', readOnly: 'readOnly.isEdit'},
-            {title: '工料分类', colSpan: '1', rowSpan: '2', field: 'm_type', hAlign: 0, width: 60, readOnly: 'readOnly.isEdit', cellType: 'customizeCombo', comboItems: materialType.m_type, cellTypeKey: 2},
+            {title: '工料分类', colSpan: '1', rowSpan: '2', field: 'm_type', hAlign: 1, width: 60, readOnly: 'readOnly.isEdit', cellType: 'customizeCombo', comboItems: materialType.m_type, cellTypeKey: 2},
             {title: '本期应耗数量', colSpan: '1', rowSpan: '2', field: 'quantity', hAlign: 2, width: 100, type: 'Number', readOnly: true},
             {title: '基准价', colSpan: '1', rowSpan: '2', field: 'basic_price', hAlign: 2, width: 60, type: 'Number', readOnly: 'readOnly.isEdit'},
             {title: '基准时间', colSpan: '1', rowSpan: '2', field: 'basic_times', hAlign: 0, width: 60, formatter: '@', readOnly: 'readOnly.isEdit'},
@@ -71,6 +128,7 @@ $(document).ready(() => {
         // return !readOnly && data.t_type === 2 && data.mid === materialID;
         return data.t_type === 2;
     };
+
     materialSpreadSetting.imageClick = function (data) {
         if (data.t_type === 2) {
             $('#bcyy').modal('show');
@@ -184,7 +242,7 @@ $(document).ready(() => {
                 const select = SpreadJsObj.getSelectObject(info.sheet);
                 const col = info.sheet.zh_setting.cols[info.col];
                 // 未改变值则不提交
-                const validText = info.editingText ? (typeof(info.editingText) === 'String' ? info.editingText.replace('\n', '') : info.editingText) : null;
+                const validText = _.isNumber(info.editingText) ? parseFloat(info.editingText) : (info.editingText ? trimInvalidChar(info.editingText) : null);
                 const orgValue = select[col.field];
                 if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
                     SpreadJsObj.reLoadRowData(info.sheet, info.row);
@@ -198,7 +256,7 @@ $(document).ready(() => {
                         return;
                     }
                     const num = parseFloat(validText);
-                    if (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num)) {
+                    if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
                         toastr.error('请输入大于0并且小于3位小数的浮点数');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
@@ -211,7 +269,7 @@ $(document).ready(() => {
                         return;
                     }
                     const num = parseFloat(validText);
-                    if (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num)) {
+                    if (validText !== null && (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num))) {
                         toastr.error('请输入大于0并且小于3位小数的浮点数');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
@@ -225,7 +283,7 @@ $(document).ready(() => {
                         return;
                     }
                     const num = parseFloat(validText);
-                    if (num < 0 || num > 100 || !/^\d+$/.test(num)) {
+                    if (validText !== null && (num < 0 || num > 100 || !/^\d+$/.test(num))) {
                         toastr.error('只能输入0-100的正整数');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
@@ -239,7 +297,7 @@ $(document).ready(() => {
                         return;
                     }
                     const num = parseFloat(validText);
-                    if (num < 0 || num > 100 || !/^\d+$/.test(num)) {
+                    if (validText !== null && (num < 0 || num > 100 || !/^\d+$/.test(num))) {
                         toastr.error('只能输入0-100的正整数');
                         SpreadJsObj.reLoadRowData(info.sheet, info.row);
                         return;
@@ -267,12 +325,161 @@ $(document).ready(() => {
                 });
             }
         },
+        deletePress: function (sheet) {
+            return;
+        },
+        clipboardPasted(e, info) {
+            // const tree = info.sheet.zh_tree;
+            // if (!tree) { return; }
+            const hint = {
+                codeEmpty: {type: 'error', msg: '请先输入编号'},
+                codeUsed: {type: 'error', msg: '该编号已存在,请重新输入。'},
+                numberExpr: {type: 'error', msg: '不能粘贴其它非数字类型字符'},
+                riskCan: {type: 'error', msg: '只能粘贴0-100的正整数'},
+                numberCan: {type: 'error', msg: '请粘贴大于0并且小于3位小数的浮点数'},
+            };
+            console.log(info);
+            const range = info.cellRange;
+            const sortData = info.sheet.zh_data || [];
+            if (info.cellRange.row + info.cellRange.rowCount > sortData.length) {
+                SpreadJsObj.loadSheetData(materialSpread.getActiveSheet(), SpreadJsObj.DataType.Data, materialBillsData);
+                return;
+            }
+            const data = [];
+            for (let iRow = 0; iRow < range.rowCount; iRow++) {
+                let bPaste = true;
+                const curRow = range.row + iRow;
+                const materialData = {id: sortData[curRow].id};
+                for (let iCol = 0; iCol < info.cellRange.colCount; iCol++) {
+                    const curCol = range.col + iCol;
+                    const colSetting = info.sheet.zh_setting.cols[curCol];
+                    if (!colSetting) continue;
+
+
+                    let validText = info.sheet.getText(curRow, curCol);
+                    console.log(validText, isNaN(parseFloat(validText)));
+                    validText = _.isNumber(validText) ? parseFloat(validText) : (validText ? trimInvalidChar(validText) : null);
+                    console.log(validText);
+                    const orgValue = sortData[curRow][colSetting.field];
+                    console.log(orgValue);
+                    if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+                        continue;
+                    }
+                    materialData[colSetting.field] = validText;
+
+                    // if (colSetting.type === 'Number') {
+                    //     const num = _.toNumber(materialData[colSetting.field]);
+                    // }
+                }
+                console.log(materialData);
+            }
+            if (data.length === 0) {
+                SpreadJsObj.reLoadRowData(info.sheet, info.cellRange.row, info.cellRange.rowCount);
+                return;
+            }
+            // if (info.sheet.zh_setting) {
+            //     const select = SpreadJsObj.getSelectObject(info.sheet);
+            //     const col = info.sheet.zh_setting.cols[info.col];
+            //     // 未改变值则不提交
+            //     const validText = info.editingText ? trimInvalidChar(info.editingText) : null;
+            //     const orgValue = select[col.field];
+            //     if (orgValue == validText || ((!orgValue || orgValue === '') && (validText === ''))) {
+            //         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //         return;
+            //     }
+            //     // 判断部分值是否输入的是数字判断和数据计算
+            //     if (col.field === 'basic_price') {
+            //         if (isNaN(validText)) {
+            //             toastr.error('不能输入其它非数字类型字符');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //         const num = parseFloat(validText);
+            //         if (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num)) {
+            //             toastr.error('请输入大于0并且小于3位小数的浮点数');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //     }
+            //     if (col.field === 'msg_tp') {
+            //         if (isNaN(validText)) {
+            //             toastr.error('不能输入其它非数字类型字符');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //         const num = parseFloat(validText);
+            //         if (num < 0 || !/^\d+(\.\d{1,3})?$/.test(num)) {
+            //             toastr.error('请输入大于0并且小于3位小数的浮点数');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //     }
+            //     if (col.field === 'm_up_risk') {
+            //         // 只能输入正整数
+            //         if (isNaN(validText)) {
+            //             toastr.error('不能输入其它非数字类型字符');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //         const num = parseFloat(validText);
+            //         if (num < 0 || num > 100 || !/^\d+$/.test(num)) {
+            //             toastr.error('只能输入0-100的正整数');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //     }
+            //     if (col.field === 'm_down_risk') {
+            //         // 只能输入正整数
+            //         if (isNaN(validText)) {
+            //             toastr.error('不能输入其它非数字类型字符');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //         const num = parseFloat(validText);
+            //         if (num < 0 || num > 100 || !/^\d+$/.test(num)) {
+            //             toastr.error('只能输入0-100的正整数');
+            //             SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //             return;
+            //         }
+            //     }
+            //     if (col.field === 't_type') {
+            //         if (validText === 1) {
+            //             select.quantity = null;
+            //             select.expr = null;
+            //             select.m_tp = null;
+            //         }
+            //     }
+            //     select[col.field] = validText;
+            //     select.msg_spread = materialCol.getValue.msg_spread(select);
+            //     select.m_spread = materialCol.getValue.m_spread(select);
+            //     select.m_tp = materialCol.getValue.m_tp(select);
+            //     // 更新至服务器
+            //     postData(window.location.pathname + '/save', { type:'update', updateData: select }, function (result) {
+            //         m_tp = result.m_tp;
+            //         resetTpTable();
+            //         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //     }, function () {
+            //         select[col.field] = orgValue;
+            //         SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            //     });
+            // }
+        },
         setReadOnly: function(readOnly) {
-            SpreadJsObj.resetFieldReadOnly(materialSpread.getActiveSheet(), 'msg_spread', 'm_spread', 'm_tp', 'pre_tp', readOnly);
+            // SpreadJsObj.resetFieldReadOnly(materialSpread.getActiveSheet(), 'msg_spread', 'm_spread', 'm_tp', 'pre_tp', readOnly);
         }
     };
     materialSpreadObj.refreshActn();
     materialSpread.bind(spreadNS.Events.SelectionChanged, materialSpreadObj.selectionChanged);
+    materialSpread.bind(spreadNS.Events.ClipboardPasted, materialSpreadObj.clipboardPasted);
+    SpreadJsObj.addDeleteBind(materialSpread, materialSpreadObj.deletePress);
+    // const sheet = materialSpread.getActiveSheet();
+    // sheet.suspendPaint();
+    // var range = sheet.getRange(-1, 8, -1, 1);
+    // console.log(range);
+    // range.cellType(new DatePickerCellType());
+    // // range.formatter("yyyy-MM-dd");
+    // sheet.resumePaint();
+
     if (!readOnly) {
         $('#add').click(materialSpreadObj.add);
         $('#del').click(materialSpreadObj.del);

+ 72 - 30
app/public/js/se_bonus.js

@@ -14,6 +14,31 @@ const isPre = function (data) {
 $(document).ready(() => {
     autoFlashHeight();
 
+    const fileObj = {
+        generateFilesHtml(data) {
+            const id = data.id, files = data.proof_file;
+            let html = [];
+            if (files !== null && files !== undefined) {
+                for (const [i, f] of files.entries()) {
+                    html.push('<tr>');
+                    html.push('<td style="width: 200px">', f.filename + f.fileext, '</td>');
+                    html.push('<td>', f.username, '</td>');
+                    html.push('<td>', f.in_time, '</td>');
+                    html.push('<td>');
+                    // 下载
+                    html.push('<a href="' + window.location.pathname + '/file/download?id=' + id + '&index=' + i + ' title="下载><i class="fa fa-download "></i></a>');
+                    // 删除
+                    if (!readOnly && uploadPermission && !isPre(data)) {
+                        html.push('<a class="delete-att text-danger" href="javascript:void(0);" data-id ="' + id + '"file-index="' + i + '" title="删除"><i class="fa fa-remove "></i></a>');
+                    }
+                    html.push('</td>');
+                    html.push('</tr>');
+                }
+            }
+            $('#file-list').html(html.join(''));
+        },
+    };
+
     let datepicker;
     const spreadSetting = {
         cols: [
@@ -27,7 +52,17 @@ $(document).ready(() => {
                 },
             },
             {title: '编号', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: isPre, },
-            {title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof', hAlign: 0, width: 180, formatter: '@', readOnly: isPre, },
+            {title: '发文单位', colSpan: '1', rowSpan: '1', field: 'code', hAlign: 0, width: 150, formatter: '@', readOnly: isPre},
+            {
+                title: '依据材料证明', colSpan: '1', rowSpan: '1', field: 'proof_file', hAlign: 1, width: 80, formatter: '@',
+                readOnly: true, cellType: 'imageBtn', normalImg: '#rela-file-icon', hoverImg: '#rela-file-hover',
+                getValue: function (data) {
+                    return data.proof_file ? data.proof_file.length : 0;
+                },
+                showImage: function (data) {
+                    return data !== undefined && data !== null;
+                },
+            },
             {
                 title: '计量期', colSpan: '1', rowSpan: '1', field: 'sorder', hAlign: 1, width: 100, formatter: '@',
                 getValue: function (data) {
@@ -49,38 +84,45 @@ $(document).ready(() => {
             const setting = hitinfo.sheet.zh_setting;
             if (!setting) return;
             const col = setting.cols[hitinfo.col];
-            if (!col || col.field !== 'real_time') return;
-
-            const pos = SpreadJsObj.getObjPos(hitinfo.sheet.getParent().qo);
-            if (!datepicker) {
-                datepicker = $('.datepicker-here').datepicker({
-                    language: 'zh',
-                    dateFormat: 'yyyy-MM-dd',
-                    autoClose: true,
-                    onSelect: function (formattedDate, date, inst) {
-                        if (!inst.visible) return;
-                        const sels = hitinfo.sheet.getSelections();
-                        if (!sels || !sels[0]) return;
-                        const node = SpreadJsObj.getSelectObject(hitinfo.sheet);
-                        const uData = { update: {id: node.id, real_time: date} };
+            if (!col) return;
 
-                        postData(window.location.pathname + '/update', uData, function (result) {
-                            bonusObj.loadUpdateData(result);
-                            SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
-                        }, function () {
-                            SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
-                        });
-                    }
-                }).data('datepicker');
+            if (col.field === 'proof_file') {
+                fileObj.generateFilesHtml(data);
+                $('#file').modal('show');
             }
-            const value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
-            if (value) {
-                datepicker.selectDate(value);
-            } else {
-                datepicker.clear();
+
+            if (col.field === 'real_time') {
+                const pos = SpreadJsObj.getObjPos(hitinfo.sheet.getParent().qo);
+                if (!datepicker) {
+                    datepicker = $('.datepicker-here').datepicker({
+                        language: 'zh',
+                        dateFormat: 'yyyy-MM-dd',
+                        autoClose: true,
+                        onSelect: function (formattedDate, date, inst) {
+                            if (!inst.visible) return;
+                            const sels = hitinfo.sheet.getSelections();
+                            if (!sels || !sels[0]) return;
+                            const node = SpreadJsObj.getSelectObject(hitinfo.sheet);
+                            const uData = { update: {id: node.id, real_time: date} };
+
+                            postData(window.location.pathname + '/update', uData, function (result) {
+                                bonusObj.loadUpdateData(result);
+                                SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
+                            }, function () {
+                                SpreadJsObj.reLoadRowData(hitinfo.sheet, sels[0].row);
+                            });
+                        }
+                    }).data('datepicker');
+                }
+                const value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
+                if (value) {
+                    datepicker.selectDate(value);
+                } else {
+                    datepicker.clear();
+                }
+                datepicker.show();
+                $('#datepickers-container').css('top', hitinfo.cellRect.y + pos.y).css('left', hitinfo.cellRect.x + pos.x);
             }
-            datepicker.show();
-            $('#datepickers-container').css('top', hitinfo.cellRect.y + pos.y).css('left', hitinfo.cellRect.x + pos.x);
         }
     };
 

+ 17 - 3
app/public/js/stage_bwtz.js

@@ -34,9 +34,23 @@ $(document).ready(() => {
         unitTreeObj.loadCurUnitData();
     });
 
-    postData(preUrl + '/load', { filter: 'ledger;pos;change' }, function (result) {
-        billsPosConvertModel.loadData(result.ledgerData, result.posData, result.changeData, decimal);
-        const xmjTree = billsPosConvertModel.convert();
+    postData(window.location.pathname + '/load', {}, function (result) {
+        const setting = {
+            id: 'ledger_id',
+            pid: 'ledger_pid',
+            order: 'order',
+            level: 'level',
+            rootId: -1,
+            fullPath: 'full_path',
+        };
+        const xmjTree = createNewPathTree('base', setting);
+        xmjTree.loadDatas(result);
+        for (const n of xmjTree.nodes) {
+            if (n.unitTreeData) {
+                n.unitTree = createNewPathTree('base', setting);
+                n.unitTree.loadDatas(n.unitTreeData);
+            }
+        }
         SpreadJsObj.loadSheetData(xmjSheet, SpreadJsObj.DataType.Tree, xmjTree);
         unitTreeObj.loadCurUnitData();
     });

+ 5 - 5
app/public/js/stage_im.js

@@ -261,7 +261,7 @@ const stageIm = (function () {
         if (im.calc_memo !== undefined && im.calc_memo !== null && im.calc_memo !== '') return;
 
         if (im.leafXmjs && im.leafXmjs.length > 0) {
-            const memo = ['本期计量:' + im.jl + ' ' + im.unit];
+            const memo = ['本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit];
             for (const lx of im.leafXmjs) {
                 for (const p of lx.pos) {
                     memo.push(p.name + ':' + p.jl + ' ' + im.unit);
@@ -277,7 +277,7 @@ const stageIm = (function () {
                         memo.push(p.name + ':' + p.jl + ' ' + b.unit);
                     }
                 } else {
-                    memo.push('清单' + (i+1) + ':' + b.b_code + ' ' + b.name + ':' + b.jl + ' ' + b.unit);
+                    memo.push('清单' + (i+1) + ':' + b.b_code + ' ' + b.name + ':' + (checkZero(b.jl) ? 0 : b.jl) + ' ' + b.unit);
                 }
             }
             im.calc_memo = memo.join('\n');
@@ -567,7 +567,7 @@ const stageIm = (function () {
                         position: pp.position,
                         lIndex: nodeIndex,
                     };
-                    im.calc_memo = '本期计量:' + im.jl + ' ' + im.unit;
+                    im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                     checkCustomDetail(im);
                     ImData.push(im);
                     if (pp.qc_qty && pp.qc_qty !== 0) {
@@ -579,7 +579,7 @@ const stageIm = (function () {
                     }
                 }
             } else {
-                if ((!p.contract_qty || p.contract_qty === 0) && (!p.qc_qty || p.qc_qty === 0)) { continue }
+                if (checkZero(p.gather_qty) && checkZero(p.gather_tp)) { continue }
 
                 const im = {
                     lid: node.id, code: p.b_code, name: p.name, unit: p.unit, unit_price: p.unit_price, pid: '',
@@ -593,7 +593,7 @@ const stageIm = (function () {
                     position: '',
                     lIndex: gsTree.getNodeIndex(node),
                 };
-                im.calc_memo = '本期计量:' + im.jl + ' ' + im.unit;
+                im.calc_memo = '本期计量:' + (checkZero(im.jl) ? 0 : im.jl) + ' ' + im.unit;
                 checkCustomDetail(im);
                 ImData.push(im);
                 if (p.qc_qty && p.qc_qty !== 0) {

+ 2 - 0
app/router.js

@@ -113,6 +113,7 @@ module.exports = app => {
 
     // 部位台账
     app.get('/tender/:id/ledger/bwtz', sessionAuth, tenderCheck, 'ledgerController.bwtz');
+    app.post('/tender/:id/ledger/bwtz/load', sessionAuth, tenderCheck, 'ledgerController.loadBwtz');
 
     // 台账对比
     app.get('/tender/:id/ledger/gather', sessionAuth, tenderCheck, 'ledgerController.gather');
@@ -198,6 +199,7 @@ module.exports = app => {
     app.get('/tender/:id/measure/stage/:order/audit/check/again', sessionAuth, tenderCheck, stageCheck, 'stageController.checkAuditAgain');
     // 部位台账
     app.get('/tender/:id/measure/stage/:order/bwtz', sessionAuth, tenderCheck, stageCheck, 'stageController.bwtz');
+    app.post('/tender/:id/measure/stage/:order/bwtz/load', sessionAuth, tenderCheck, stageCheck, 'stageController.loadBwtz');
     // 清单汇总
     app.get('/tender/:id/measure/stage/:order/gather', sessionAuth, tenderCheck, stageCheck, 'stageController.gather');
     // 审核比较

+ 47 - 0
app/service/external_data.js

@@ -0,0 +1,47 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+    class ExternalData extends app.BaseService {
+
+        /**
+         * 构造函数
+         * @param ctx
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'external_data';
+        }
+
+        async getExValue(tid, sid, ex_type, ex_field) {
+            const result = await this.getDataByCondition({
+                tid: tid, sid: sid,
+                ex_type: ex_type, ex_field: ex_field
+            });
+            return result && result.ex_value !== null ? JSON.parse(result.ex_value) : null;
+        }
+
+        async saveExValue(tid, sid, ex_type, ex_field, ex_value) {
+            const filter = {
+                tid: tid, sid: sid,
+                ex_type: ex_type, ex_field: ex_field
+            };
+            const count = await this.count(filter);
+            if (count >= 0) {
+                await this.db.update(this.tableName, { ex_value: JSON.stringify(ex_value) }, filter);
+            } else {
+                filter.ex_value = JSON.stringify(ex_value);
+                await this.db.insert(this.tableName, filter);
+            }
+        }
+    }
+
+    return ExternalData;
+};

+ 1 - 1
app/service/material_audit.js

@@ -530,7 +530,7 @@ module.exports = app => {
                 newAuditors.push(na);
             }
             const result = await transaction.insert(this.tableName, newAuditors);
-            return result.effectRows = auditors.length;
+            return result.affectedRows === auditors.length;
         }
 
         /**

+ 1 - 1
app/service/material_list_notjoin.js

@@ -143,7 +143,7 @@ module.exports = app => {
                 notJoinlist.push(newLists);
             }
             // 复制上一期不参与调差的清单
-            return await transaction.insert(this.tableName, notJoinlist);
+            return notJoinlist.length > 0 ? await transaction.insert(this.tableName, notJoinlist) : true;
         }
     }
     return MaterialListNotJoin;

+ 3 - 3
app/service/report_memory.js

@@ -620,13 +620,13 @@ module.exports = app => {
             }
 
             if (this._checkFieldsExistReg(fields, 'r[0-9]+_tp')) {
-                const validRole = this._getStageValidRole();
+                this._getStageValidRole();
                 const allStagePays = await this.ctx.service.stagePay.getAllDataByCondition({
                     where: {sid: stage.id, stimes: stage.curTimes}
                 });
 
-                for (const [i, role] of validRole.entries()) {
-                    if (i < validRole.length - 1) {
+                for (const [i, role] of this.stageValidRole.entries()) {
+                    if (i < this.stageValidRole.length - 1) {
                         const stagePays = this.ctx.helper._.filter(allStagePays, function (x) {
                             return x.stimes === stage.curTimes && x.sorder === role.dataOrder;
                         });

+ 3 - 1
app/service/stage_bonus.js

@@ -67,9 +67,10 @@ module.exports = app => {
                 };
                 nd.tp = d.tp ? this.ctx.helper.round(d.to, this.ctx.tender.info.decimal.tp) : 0;
                 nd.code = d.code ? d.code: null;
-                nd.proof = d.proof ? d.proof : null;
+                //nd.proof = d.proof ? d.proof : null;
                 nd.real_time = d.real_time ? d.real_time : null;
                 nd.memo = d.memo ? d.memo : null;
+                nd.doc_co = d.doc_co ? d.doc_co : null;
                 insertData.push(nd);
             }
             await this.db.insert(this.tableName, insertData);
@@ -105,6 +106,7 @@ module.exports = app => {
                 if (d.real_time !== undefined) nd.real_time = new Date(d.real_time);
                 if (d.memo !== undefined) nd.memo = d.memo;
                 if (d.order !== undefined) nd.order = d.order;
+                if (d.doc_co !== undefined) nd.doc_co = d.doc_co;
                 uDatas.push(nd);
                 console.log(nd);
             }

+ 7 - 2
app/view/ledger/bwtz.ejs

@@ -57,9 +57,14 @@
     };
     const unitSpreadSetting = {
         cols: [
-            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'pos_name', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
+            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
-            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@'},
+            {
+                title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@',
+                getValue: function (data) {
+                    return data.pos_name + data.name;
+                }
+            },
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},
             {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
             {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 80, type: 'Number'},

+ 3 - 1
app/view/material/info.ejs

@@ -49,7 +49,7 @@
                             <table class="table table-sm table-bordered">
                                 <tr><th></th><th>本期金额</th><th>截止本期金额</th></tr>
                                 <tr id="tp_set"><td>材料价差费用</td><td><%= material.m_tp %></td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.add(material.pre_tp, material.m_tp), 2) : null %></td></tr>
-                                <tr id="rate_set"><td>材料价差费用(含税)</td><td><%= material.m_tp !== null ? ctx.helper.mul(material.m_tp, 1+material.rate/100) : null %></td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(ctx.helper.add(material.pre_tp, material.m_tp), 1+material.rate/100), 2) : null %></td></tr>
+                                <tr id="rate_set"><td>材料价差费用(含税)</td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(material.m_tp, 1+material.rate/100), 2) : null %></td><td><%= material.m_tp !== null ? ctx.helper.round(ctx.helper.mul(ctx.helper.add(material.pre_tp, material.m_tp), 1+material.rate/100), 2) : null %></td></tr>
                             </table>
                         </div>
                     </div>
@@ -62,6 +62,8 @@
     <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
     <img src="/public/images/icon-ok.png" id="icon-ok" />
 </div>
+<!--<script src="/public/js/datepicker/datepicker.min.js"></script>-->
+<!--<script src="/public/js/datepicker/datepicker.zh.js"></script>-->
 <% if ((material.status === auditConst.status.uncheck || material.status === auditConst.status.checkNo) && ctx.session.sessionUser.accountId === material.user_id) {%>
 <script>
     const accountList = JSON.parse('<%- JSON.stringify(accountList) %>');

+ 7 - 2
app/view/stage/bwtz.ejs

@@ -65,9 +65,14 @@
     };
     const unitSpreadSetting = {
         cols: [
-            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'pos_name', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
+            {title: '计量单元', colSpan: '1', rowSpan: '2', field: 'code', hAlign: 0, width: 120, formatter: '@', cellType: 'tree'},
             {title: '清单编号', colSpan: '1', rowSpan: '2', field: 'b_code', hAlign: 0, width: 70, formatter: '@'},
-            {title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@'},
+            {
+                title: '名称', colSpan: '1', rowSpan: '2', field: 'name', hAlign: 0, width: 185, formatter: '@',
+                getValue: function (data) {
+                    return data.pos_name + data.name;
+                }
+            },
             {title: '单位', colSpan: '1', rowSpan: '2', field: 'unit', hAlign: 1, width: 60, formatter: '@', cellType: 'unit'},
             {title: '单价', colSpan: '1', rowSpan: '2', field: 'unit_price', hAlign: 2, width: 60, type: 'Number'},
             {title: '台账|数量', colSpan: '2|1', rowSpan: '1|1', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},

+ 2 - 0
app/view/stage_extra/bonus.ejs

@@ -21,6 +21,8 @@
     </div>
     <div style="display: none">
         <img src="/public/images/ellipsis_horizontal.png" id="ellipsis-icon" />
+        <img src="/public/images/file_clip.png" id="rela-file-icon" />
+        <img src="/public/images/file_clip_hover.png" id="rela-file-hover" />
     </div>
 </div>
 <script>

+ 36 - 0
app/view/stage_extra/bonus_modal.ejs

@@ -0,0 +1,36 @@
+<!--附件-->
+<div class="modal fade" id="file" 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">
+                <% if (uploadPermission) { %>
+                <div class="form-group">
+                    <label for="formGroupExampleInput">大小限制:30MB,支持<span data-toggle="tooltip" data-placement="bottom" title="doc,docx,xls,xlsx,ppt,pptx,pdf">office等文档格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="jpg,png,bmp">图片格式</span>、<span data-toggle="tooltip" data-placement="bottom" title="rar,zip">压缩包格式</span></label>
+                    <input type="file" class="" id="upload-file" multiple>
+                </div>
+                <% } %>
+                <div class="modal-height-500" style="overflow:auto;">
+                    <table class="table table-sm table-bordered" style="word-break:break-all; table-layout: fixed">
+                        <thead>
+                        <tr><th width="240">文件名</th><th>上传人</th><th>上传时间</th><th width="40">操作</th></tr>
+                        </thead>
+                        <tbody id="file-list">
+                        <tr>
+                            <td>XXXX.jpg</td><td>张三</td><td>2018-01-01</td><td><a href="#" target="_blank" title="下载"><i class="fa fa-download "></i></a> <a class="text-danger" href="#" target="_blank" title="删除"><i class="fa fa-remove "></i></a></td>
+                        </tr>
+                        <tr>
+                            <td>YYYY.jpg</td><td>李四</td><td>2018-01-01</td><td><a href="#" target="_blank" title="下载"><i class="fa fa-download "></i></a></td>
+                        </tr>
+                        </tbody>
+                    </table>
+                </div>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-secondary btn-sm" data-dismiss="modal">关闭</button>
+            </div>
+        </div>
+    </div>
+</div>

+ 28 - 0
sql/update.sql

@@ -0,0 +1,28 @@
+
+-- ----------------------------
+-- Table structure for zh_external_data
+-- ----------------------------
+
+CREATE TABLE `zh_external_data` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `tid` int(11) NOT NULL COMMENT '标段id',
+  `sid` int(11) NOT NULL DEFAULT '-1' COMMENT '所属期id,-1表示整个标段共用',
+  `ex_type` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '额外数据 - 类型(''fl'': 富龙接口数据)',
+  `ex_field` varchar(20) CHARACTER SET utf8 NOT NULL COMMENT '额外数据 - 名称(具体细分由ex_type决定)',
+  `ex_value` text CHARACTER SET utf8 COMMENT '额外数据 - 值',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci COMMENT='外部所需数据\r\n非计量支付内部运行所需数据,当接入外部系统时,这些系统可能要求我们提供某些额外的数据,例如:富龙接口,wbs_code\r\n类似数据,均存储在这里';
+
+-- ----------------------------
+-- zh_stage_bonus add column 发文单位
+-- ----------------------------
+ALTER TABLE `zh_stage_bonus`
+ADD COLUMN `doc_co`  varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL COMMENT '发文单位' AFTER `code`;
+
+-- ----------------------------
+-- zh_stage_bonus add column 依据材料 - 文件列表
+-- ----------------------------
+ALTER TABLE `zh_stage_bonus`
+ADD COLUMN `proof_file`  text CHARACTER SET utf8 NULL COMMENT '依据材料 - 文件列表(json)' AFTER `proof`;
+
+