Quellcode durchsuchen

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

Tony Kang vor 1 Jahr
Ursprung
Commit
462368b7a2

+ 124 - 49
app/base/base_bills_service.js

@@ -21,9 +21,10 @@ const billsUtils = require('../lib/bills_utils');
 
 class BaseBillsSerivce extends TreeService {
 
-    constructor (ctx, setting, relaPosName) {
+    constructor (ctx, setting, relaPosName, relaAncGclName) {
         super(ctx, setting);
         this.relaPosService = relaPosName;
+        this.relaAncGclService = relaAncGclName;
     }
 
     async getFinalData(mid, columns) {
@@ -41,6 +42,14 @@ class BaseBillsSerivce extends TreeService {
         return this.ctx.service[this._posName];
     }
 
+    set relaAncGclService(ancGclName) {
+        this._ancGclName = ancGclName;
+    }
+
+    get relaAncGclService() {
+        return this._ancGclName ? this.ctx.service[this._ancGclName] : undefined;
+    }
+
     // 继承方法
     clearParentingData(data) {
         data.unit_price = null;
@@ -151,8 +160,7 @@ class BaseBillsSerivce extends TreeService {
      * @return {Promise<*>}
      */
     async addStdNode(tenderId, selectId, stdData, reviseId) {
-        const newData = this._filterStdData(stdData);
-        const result = await this.addBillsNode(tenderId, selectId, newData, reviseId);
+        const result = await this.addBillsNode(tenderId, selectId, stdData, reviseId);
         return result;
     }
 
@@ -164,8 +172,7 @@ class BaseBillsSerivce extends TreeService {
      * @returns {Promise<*>}
      */
     async addStdNodeAsChild(tenderId, selectId, stdData, reviseId) {
-        const newData = this._filterStdData(stdData);
-        const result = await this.addChild(tenderId, selectId, newData, reviseId);
+        const result = await this.addChild(tenderId, selectId, stdData, reviseId);
         return result;
     }
 
@@ -217,12 +224,12 @@ class BaseBillsSerivce extends TreeService {
      * @return {Promise<void>}
      * @private
      */
-    async _addChildAutoOrder(tenderId, parentData, data, reviseId) {
+    async _addChildAutoOrder(tenderId, parentData, data, orderField, reviseId) {
         const self = this;
         const findPreData = function(list, a) {
             if (!list || list.length === 0) { return null; }
             for (let i = 0, iLen = list.length; i < iLen; i++) {
-                if (billsUtils.compareCode(list[i].code, a.code) > 0) {
+                if (billsUtils.compareCode(list[i][orderField], a[orderField]) > 0) {
                     return i > 0 ? list[i - 1] : null;
                 }
             }
@@ -249,51 +256,36 @@ class BaseBillsSerivce extends TreeService {
      * @param {StandardLib} stdLib - 标准库
      * @return {Promise<void>}
      */
-    async addStdNodeWithParent(tenderId, stdData, stdLib, reviseId) {
-        // 查询完整标准清单,并按层次排序
-        const fullLevel = await stdLib.getFullLevelDataByFullPath(stdData.list_id, stdData.full_path);
-        fullLevel.sort(function(x, y) {
-            return x.level - y.level;
-        });
-
-        let isNew = false,
-            node,
-            firstNew,
-            updateParent,
-            addResult;
+    async addStdNodeWithParent(tenderId, stdData, reviseId) {
+        let node, firstNew, updateParent, addResult;
         const expandIds = [];
         this.transaction = await this.db.beginTransaction();
         try {
             // 从最顶层节点依次查询是否存在,否则添加
-            for (let i = 0, len = fullLevel.length; i < len; i++) {
-                const stdNode = fullLevel[i];
+            for (let i = 0, len = stdData.length; i < len; i++) {
+                const stdNode = stdData[i];
+                const parent = node;
+                node = await this.getDataByCondition({
+                    tender_id: tenderId, ledger_pid: parent ? parent.ledger_id : rootId,
+                    code: stdNode.code, name: stdNode.name,
+                });
+                if (node) {
+                    expandIds.push(node.ledger_id);
+                    continue;
+                }
 
-                if (isNew) {
-                    const newData = this._filterStdData(stdNode);
-                    newData.is_leaf = (i === len - 1);
-                    [addResult, node] = await this._addChildNodeData(tenderId, node, newData, reviseId);
+                if (firstNew) {
+                    stdNode.is_leaf = (i === len - 1);
+                    [addResult, node] = await this._addChildNodeData(tenderId, parent, stdNode, reviseId);
                 } else {
-                    const parent = node;
-                    node = await this.getDataByCondition({
-                        tender_id: tenderId,
-                        ledger_pid: parent ? parent.ledger_id : rootId,
-                        code: stdNode.code,
-                        name: stdNode.name,
-                    });
-                    if (!node) {
-                        isNew = true;
-                        const newData = this._filterStdData(stdNode);
-                        newData.is_leaf = (i === len - 1);
-                        [addResult, node] = await this._addChildAutoOrder(tenderId, parent, newData, reviseId);
-                        if (parent && parent.is_leaf) {
-                            await this.transaction.update(this.tableName, { id: parent.id, is_leaf: false,
-                                unit_price: null, quantity: null, total_price: null, deal_qty: null, deal_tp: null});
-                            updateParent = parent;
-                        }
-                        firstNew = node;
-                    } else {
-                        expandIds.push(node.ledger_id);
+                    stdNode.is_leaf = (i === len - 1);
+                    [addResult, node] = await this._addChildAutoOrder(tenderId, parent, stdNode, 'code', reviseId);
+                    if (parent && parent.is_leaf) {
+                        await this.transaction.update(this.tableName, { id: parent.id, is_leaf: false,
+                            unit_price: null, quantity: null, total_price: null, deal_qty: null, deal_tp: null});
+                        updateParent = parent;
                     }
+                    firstNew = node;
                 }
             }
             await this.transaction.commit();
@@ -303,8 +295,75 @@ class BaseBillsSerivce extends TreeService {
         }
 
         // 查询应返回的结果
-        let createData = [],
-            updateData = [];
+        let createData = [], updateData = [];
+        if (firstNew) {
+            createData = await this.getDataByFullPath(tenderId, firstNew.full_path + '%');
+            updateData = await this.getNextsData(tenderId, firstNew.ledger_pid, firstNew.order);
+            if (updateParent) {
+                updateData.push(await this.getDataByCondition({ id: updateParent.id }));
+            }
+        }
+        return { create: createData, update: updateData };
+    }
+
+    async getLeafXmj(select) {
+        const relaId = select.full_path.split('-');
+        const parents = await this.getAllDataByCondition({ where: { tender_id: select.tender_id, ledger_id: relaId }, orders: [['level', 'asc']]});
+        const xmjs = parents.filter(x => { return !!x.code; });
+        return xmjs[xmjs.length - 1];
+    }
+
+    async addGclStdNode(tenderId, selectId, stdData, reviseId) {
+        const selectNode = await this.ctx.service.ledger.getDataByNodeId(tenderId, selectId);
+        if (selectNode.b_code) {
+            return await this.ctx.service.ledger.addStdNode(tenderId, selectId, stdData, reviseId);
+        } else {
+            return await this.ctx.service.ledger.addStdNodeAsChild(tenderId, selectId, stdData, reviseId);
+        }
+    }
+
+    async addGclStdNodeWithParent(tenderId, selectId, stdData, reviseId) {
+        const selectData = await this.getDataByKid(tenderId, selectId);
+        if (!selectData) throw '新增节点数据错误,请刷新后重试';
+
+        let leafXmj = selectData.b_code ? await this.getLeafXmj(selectData) : selectData;
+        if (!leafXmj) throw '找不到可插入清单的项目节,请刷新后重试';
+
+        let node = leafXmj, firstNew, updateParent, addResult;
+        this.transaction = await this.db.beginTransaction();
+        try {
+            // 从最顶层节点依次查询是否存在,否则添加
+            for (let i = 0, len = stdData.length; i < len; i++) {
+                const stdNode = stdData[i];
+                if (!stdNode.b_code) continue;
+
+                const parent = node;
+                node = await this.getDataByCondition({
+                    tender_id: tenderId, ledger_pid: parent.ledger_id,
+                    code: stdNode.code, name: stdNode.name,
+                });
+                if (node) continue;
+
+                stdNode.is_leaf = (i === len - 1);
+                if (firstNew) {
+                    [addResult, node] = await this._addChildNodeData(selectData.tender_id, parent, stdNode, reviseId);
+                } else {
+                    [addResult, node] = await this._addChildAutoOrder(selectData.tender_id, parent, stdNode, 'b_code', reviseId);
+                    if (parent && parent.is_leaf) {
+                        await this.transaction.update(this.tableName, { id: parent.id, is_leaf: false,
+                            unit_price: null, quantity: null, total_price: null, deal_qty: null, deal_tp: null});
+                        updateParent = parent;
+                    }
+                    firstNew = node;
+                }
+            }
+            await this.transaction.commit();
+        } catch (err) {
+            await this.transaction.rollback();
+            throw err;
+        }
+
+        let createData = [], updateData = [];
         if (firstNew) {
             createData = await this.getDataByFullPath(tenderId, firstNew.full_path + '%');
             updateData = await this.getNextsData(tenderId, firstNew.ledger_pid, firstNew.order);
@@ -570,12 +629,13 @@ class BaseBillsSerivce extends TreeService {
             });
             if (pd[0].ledger_pid !== pasteData[0][0].ledger_pid) throw '复制数据错误:仅可操作同层节点';
         }
+        const userId = this.ctx.session.sessionUser.accountId;
         this.newBills = false;
         const selectData = await this.getDataByKid(tid, sid);
         if (!selectData) throw '粘贴数据错误';
         const newParentPath = selectData.full_path.replace(selectData.ledger_id, '');
 
-        const pasteBillsData = [], pastePosData = [], leafBillsId = [];
+        const pasteBillsData = [], pastePosData = [], pasteAncGclData = [], leafBillsId = [];
         const tpDecimal = this.ctx.tender.info.decimal.tp;
         let maxId = await this._getMaxLid(this.ctx.tender.id);
         for (const [i, pd] of pasteData.entries()) {
@@ -665,6 +725,19 @@ class BaseBillsSerivce extends TreeService {
                 newBills.total_price = this.ctx.helper.mul(newBills.quantity, newBills.unit_price, tpDecimal);
                 newBills.deal_tp = this.ctx.helper.mul(newBills.deal_qty, newBills.unit_price, tpDecimal);
                 if (defaultData) this.ctx.helper._.assignIn(newBills, defaultData);
+                if (d.ancGcl && d.ancGcl.length > 0) {
+                    for (const gcl of d.ancGcl) {
+                        const newAncGcl = {
+                            id: this.uuid.v4(), tid: tid, lid: newBills.id,
+                            add_user_id: userId, update_user_id: userId,
+                            name: gcl.name, unit: gcl.unit, g_order: gcl.g_order, is_aux: gcl.is_aux,
+                            quantity: gcl.quantity, expr: gcl.expr,
+                            drawing_code: gcl.drawing_code, memo: gcl.memo,
+                        };
+                        if (defaultData) this.ctx.helper._.assignIn(newAncGcl, defaultData);
+                        pasteAncGclData.push(newAncGcl);
+                    }
+                }
                 pbd.push(newBills);
             }
             for (const d of pbd) {
@@ -692,6 +765,7 @@ class BaseBillsSerivce extends TreeService {
             if (pastePosData.length > 0) {
                 await this.transaction.insert(this.relaPosService.tableName, pastePosData);
             }
+            if (pasteAncGclData.length > 0 && this.relaAncGclService) await this.transaction.insert(this.relaAncGclService.tableName, pasteAncGclData);
             await this.transaction.commit();
         } catch (err) {
             await this.transaction.rollback();
@@ -700,9 +774,10 @@ class BaseBillsSerivce extends TreeService {
 
         // 查询应返回的结果
         const updateData = await this.getNextsData(selectData.tender_id, selectData.ledger_pid, selectData.order + pasteData.length);
+        const ancGcl = this.relaAncGclService ? { add: pasteAncGclData } : undefined;
         return {
             ledger: { create: pasteBillsData, update: updateData },
-            pos: pastePosData,
+            pos: pastePosData, ancGcl,
         };
     }
 

+ 1 - 0
app/const/tender_info.js

@@ -135,6 +135,7 @@ const defaultInfo = {
             deal: false,
             dgnQty: false,
             clQty: false,
+            ancillaryGcl: false,
         },
         exMemo: true,
         thousandth: false,

+ 24 - 34
app/controller/ledger_controller.js

@@ -7,12 +7,6 @@
  * @date 2017/11/30
  * @version
  */
-
-const stdDataAddType = {
-    withParent: 1,
-    child: 2,
-    next: 3,
-};
 const audit = require('../const/audit');
 const moment = require('moment');
 const auditConst = audit.ledger;
@@ -254,39 +248,19 @@ module.exports = app => {
          * @return {Promise<void>}
          */
         async _addStd(ctx, data) {
-            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';
+            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdData) throw '参数错误';
             // todo 校验项目是否使用该库的权限
 
-            let stdLib,
-                addType;
             switch (data.stdType) {
                 case 'xmj':
-                    stdLib = ctx.service.stdXmj;
-                    addType = stdDataAddType.withParent;
-                    break;
+                    return await ctx.service.ledger.addStdNodeWithParent(ctx.tender.id, data.stdData);
                 case 'gcl':
-                    stdLib = ctx.service.stdGcl;
-                    const selectNode = await ctx.service.ledger.getDataByNodeId(ctx.tender.id, data.id);
-                    if (selectNode.b_code) {
-                        addType = stdDataAddType.next;
-                    } else {
-                        addType = stdDataAddType.child;
-                    }
-                    break;
+                    return data.withParent
+                        ? await ctx.service.ledger.addGclStdNodeWithParent(ctx.tender.id, data.id, data.stdData)
+                        : await ctx.service.ledger.addGclStdNode(ctx.tender.id, data.id, data.stdData[data.stdData.length - 1]);
                 default:
                     throw '未知标准库';
             }
-            const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
-            switch (addType) {
-                case stdDataAddType.child:
-                    return await ctx.service.ledger.addStdNodeAsChild(ctx.tender.id, data.id, stdData);
-                case stdDataAddType.next:
-                    return await ctx.service.ledger.addStdNode(ctx.tender.id, data.id, stdData);
-                case stdDataAddType.withParent:
-                    return await ctx.service.ledger.addStdNodeWithParent(ctx.tender.id, stdData, stdLib);
-                default:
-                    throw '未知添加方式';
-            }
         }
         /**
          * 从签约清单添加节点
@@ -461,9 +435,11 @@ module.exports = app => {
                         ? await ctx.helper.loadLedgerDataFromOss(ctx.tender.his.pos_file)
                         : await ctx.service.pos.getPosData({tid: ctx.tender.id}, posColumn))
                     : [];
+                const ancillaryGclData = this.ctx.tender.data.measure_type === measureType.tz.value
+                    ? await ctx.service.ancillaryGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } })
+                    : [];
                 const ledgerTags = await this.ctx.service.ledgerTag.getDatas(ctx.tender.id);
-                //ctx.body = { err: 0, msg: '', data: { bills: this.ctx.helper.hpackArr(ledgerData), pos: this.ctx.helper.hpackArr(posData), tags: ledgerTags }, hpack: ['bills', 'pos'] };
-                ctx.body = { err: 0, msg: '', data: { bills: ledgerData, pos: posData, tags: ledgerTags } };
+                ctx.body = { err: 0, msg: '', data: { bills: ledgerData, pos: posData, ancGcl: ancillaryGclData, tags: ledgerTags } };
             } catch (err) {
                 this.log(err);
                 ctx.body = { err: 1, msg: err.toString(), data: [] };
@@ -495,6 +471,18 @@ module.exports = app => {
             }
         }
 
+        async ancGclUpdate(ctx) {
+            try {
+                await this.checkMeasureType(measureType.tz.value);
+                const data = JSON.parse(ctx.request.body.data);
+                const responseData = await ctx.service.ancillaryGcl.updateDatas(data);
+                ctx.body = { err: 0, msg: '', data: responseData };
+            } catch (err) {
+                this.log(err);
+                ctx.body = { err: 1, msg: err.toString(), data: null };
+            }
+        }
+
         /**
          * 更新 部位明细数据
          *
@@ -730,10 +718,12 @@ module.exports = app => {
                 const posData = this.ctx.tender.data.measure_type === measureType.tz.value
                     ? await ctx.service.pos.getPosData({ tid: ctx.tender.id }) : [];
                 const dealBills = await ctx.service.dealBills.getAllDataByCondition({ where: { tender_id: this.ctx.tender.id } });
+                const ancGcl = this.ctx.tender.data.measure_type === measureType.tz.value
+                    ? await ctx.service.ancillaryGcl.getAllDataByCondition({ where: { tid: ctx.tender.id } }) : [];
                 const zlj = JSON.parse(JSON.stringify(stdConst.zlj));
                 zlj.deal_bills_tp = ctx.tender.info.deal_param.zanLiePrice;
                 ctx.body = { err: 0, msg: '', data: {
-                    bills: billsData, pos: posData, dealBills, spec: {zlj: zlj, jrg: stdConst.jrg},
+                    bills: billsData, pos: posData, ancGcl, dealBills, spec: {zlj: zlj, jrg: stdConst.jrg},
                 }};
             } catch (err) {
                 this.log(err);

+ 5 - 26
app/controller/revise_controller.js

@@ -585,40 +585,19 @@ module.exports = app => {
             return await this.ctx.service.reviseBills.pasteBlockData(this.ctx.tender.id, data.id, data.block, { crid: revise.id });
         }
         async _addStd(revise, data) {
-            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';
+            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdData) throw '参数错误';
             // todo 校验项目是否使用该库的权限
 
-            let stdLib,
-                addType;
             switch (data.stdType) {
                 case 'xmj':
-                    stdLib = this.ctx.service.stdXmj;
-                    addType = stdDataAddType.withParent;
-                    break;
+                    return await this.ctx.service.reviseBills.addStdNodeWithParent(revise.tid, data.stdData, revise.id);
                 case 'gcl':
-                    stdLib = this.ctx.service.stdGcl;
-                    const selectNode = await this.ctx.service.reviseBills.getDataByKid(revise.tid, data.id);
-                    if (selectNode.b_code) {
-                        addType = stdDataAddType.next;
-                    } else {
-                        addType = stdDataAddType.child;
-                    }
-                    break;
+                    return data.withParent
+                        ? await this.ctx.service.reviseBills.addGclStdNodeWithParent(revise.tid, data.id, data.stdData, revise.id)
+                        : await this.ctx.service.reviseBills.addGclStdNode(revise.tid, data.id, data.stdData[data.stdData.length - 1], revise.id);
                 default:
                     throw '未知标准库';
             }
-            const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
-            switch (addType) {
-                case stdDataAddType.child:
-                    return await this.ctx.service.reviseBills.addStdNodeAsChild(revise.tid, data.id, stdData, revise.id);
-                    break;
-                case stdDataAddType.next:
-                    return await this.ctx.service.reviseBills.addStdNode(revise.tid, data.id, stdData, revise.id);
-                case stdDataAddType.withParent:
-                    return await this.ctx.service.reviseBills.addStdNodeWithParent(revise.tid, stdData, stdLib, revise.id);
-                default:
-                    throw '未知添加方式';
-            }
         }
         async _addDeal(revise, data) {
             if (!data.type || !data.dealBills) throw '数据错误';

+ 14 - 2
app/controller/tender_controller.js

@@ -496,11 +496,21 @@ module.exports = app => {
                 // 调差最新期数据
                 const materials = await ctx.service.material.getValidMaterials(ctx.tender.id);
                 let materialData = null;
+                let openMaterialTax = ctx.session.sessionProject.page_show.openMaterialTax;
+                let allMaterialTax = true;
                 if (materials && materials.length > 0) {
                     materialData = materials[0];
                     materialData.curAuditor = await ctx.service.materialAudit.getAuditorByStatus(materialData.id, materialData.status, materialData.times);
                     const times = materialData.status === auditConst.material.status.checkNo ? materialData.times - 1 : materialData.times;
                     materialData.auditors = materialData.status === auditConst.material.status.uncheck ? [] : await ctx.service.materialAudit.getFinalAuditGroup(materialData.id, times);
+                    for (const s of materials) {
+                        if (allMaterialTax && s.material_tax === 0) {
+                            allMaterialTax = false;
+                        }
+                        if (!openMaterialTax && s.material_tax === 1) {
+                            openMaterialTax = 1;
+                        }
+                    }
                 }
                 // 修订完成数目
                 // const reviseNum = await ctx.service.ledgerRevise.count({ tid: tender.id, status: auditConst.revise.status.checked });
@@ -535,7 +545,7 @@ module.exports = app => {
                 }
                 const canFinish = await this.ctx.service.tender.checkTenderCanFinish(tender);
 
-                const projectSpread  = await ctx.service.projectSpread.getProjectSpreadType(tender.project_id);
+                const projectSpread = await ctx.service.projectSpread.getProjectSpreadType(tender.project_id);
 
                 const renderData = {
                     tenders,
@@ -553,6 +563,8 @@ module.exports = app => {
                     change_status_total,
                     change_quality_total,
                     materialData,
+                    openMaterialTax,
+                    allMaterialTax,
                     // reviseNum,
                     stage_total,
                     hadMap,
@@ -562,7 +574,7 @@ module.exports = app => {
                     tenderMapList,
                     map_json,
                     fujianOssPath: ctx.app.config.fujianOssPath,
-                    canFinish: canFinish,
+                    canFinish,
                     buildStatus: tenderConst.buildStatus,
                     projectSpread,
                 };

+ 24 - 6
app/lib/stage_im.js

@@ -12,6 +12,10 @@ const Ledger = require('./ledger');
 const imType = require('../const/tender').imType;
 const mergeChar = ';';
 const version = '1.0';
+const SortType = {
+    GCL: 'gcl',
+    TZ: 'tz'
+};
 
 class StageIm {
     constructor(ctx) {
@@ -1030,10 +1034,29 @@ class StageIm {
         }
     }
 
+    _sortImData() {
+        const st = (['fj', 'sz'].indexOf(this.tender.s_type) > 0 && [imType.zl.value, imType.bw.value].indexOf(stage.im_type) >= 0)
+            ? SortType.TZ
+            : (stage.im_type !== imType.tz.value && stage.im_type !== imType.bb.value ? SortType.TZ : SortType.GCL);
+        if (st === SortType.GCL) {
+            const helper = this.ctx.helper;
+            this.ImData.sort(function(x, y) {
+                const iCode = helper.compareCode(x.code, y.code);
+                return iCode === 0 ? x.lIndex - y.lIndex : iCode;
+            });
+        } else {
+            this.ImData.sort(function(x, y) {
+                return x.lIndex - y.lIndex;
+            });
+        }
+
+    }
+
     // 生成中间计量数据
     async buildImData() {
         this.up_field = 'unit_price';
         const self = this;
+        this.tender = this.ctx.tender;
         this.stage = this.ctx.stage;
         // 初始化
         await this._loadMainData();
@@ -1044,12 +1067,7 @@ class StageIm {
         // 生成数据
         this._recursiveBuildImData(this.billsTree.children);
         // 排序
-        if (this.stage.im_type !== imType.tz.value && this.stage.im_type !== imType.bb.value) {
-            this.ImData.sort(function(x, y) {
-                const iCode = self.ctx.helper.compareCode(x.code, y.code);
-                return iCode === 0 ? x.lIndex - y.lIndex : iCode;
-            });
-        }
+        this._sortImData();
         // 生成数据(需要缓存,并清理缓存)
         const pre = (this.stage.im_pre && this.stage.im_pre !== '') ? this.stage.im_pre + this.splitChar : '';
         for (const [i, im] of this.ImData.entries()) {

+ 3 - 6
app/public/js/change_revise.js

@@ -3005,13 +3005,10 @@ $(document).ready(() => {
     billsSpread.refresh();
     if (posSpread) posSpread.refresh();
 
-    const stdLibCellDoubleClick = function (e, info) {
-        const stdSheet = info.sheet;
+    const stdLibCellDoubleClick = function (updateData, stdNode, stdTree) {
         const mainSheet = billsSheet;
-        if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
+        if (!stdTree || !mainSheet.zh_tree) { return; }
 
-        const stdTree = stdSheet.zh_tree;
-        const stdNode = stdTree.nodes[info.row];
         const mainTree = mainSheet.zh_tree;
         const sel = mainSheet.getSelections()[0];
         const mainNode = mainTree.nodes[sel.row];
@@ -3042,7 +3039,7 @@ $(document).ready(() => {
             postData: {
                 id: mainTree.getNodeKey(mainNode),
                 tender_id: mainNode.tender_id,
-                stdType: info.sheet.zh_setting.stdType,
+                stdType: updateData.postData.stdType,
                 stdLibId: stdNode.list_id,
                 stdNode: stdTree.getNodeKey(stdNode)
             }

+ 50 - 0
app/public/js/gcl_gather.js

@@ -18,6 +18,7 @@ const gclGatherModel = (function () {
     let posGatherFields = ['quantity', 'contract_qty', 'qc_qty', 'gather_qty','qc_minus_qty',
         'pre_contract_qty', 'pre_qc_qty', 'pre_gather_qty', 'pre_qc_minus_qty',
         'end_contract_qty', 'end_qc_qty', 'end_gather_qty', 'end_qc_minus_qty'];
+    let ancillaryGclGatherFields = ['quantity'];
     // 初始化 清单树
     const gsTreeSetting = {
         id: 'ledger_id',
@@ -36,6 +37,7 @@ const gclGatherModel = (function () {
         updateFields: ['contract_qty', 'qc_qty', 'qc_minus_qty'],
     };
     const gsPos = new StagePosData(posSetting);
+    const gsAncGcl = createAncillaryGcl({ id: 'id', masterId: 'lid', sort: [['g_order', 'asc']] });
     let deal = [], change;
 
     const gclList = [], leafXmjs = [];
@@ -77,6 +79,10 @@ const gclGatherModel = (function () {
         gsPos.loadPreStageData(prePos);
     }
 
+    function loadAncillaryGclData(datas) {
+        gsAncGcl.loadDatas(datas);
+    }
+
     function loadDealBillsData(dealBills) {
         deal = dealBills;
     }
@@ -114,6 +120,7 @@ const gclGatherModel = (function () {
             unit_price: node.unit_price,
             org_price: node.org_price,
             leafXmjs: [],
+            ancGcl: [],
         };
         gclList.push(gcl);
         return gcl;
@@ -290,6 +297,22 @@ const gclGatherModel = (function () {
         }
     }
 
+    function gatherAncGclData(node, gcl, cacheLeafXmj) {
+        const gclPart = gsAncGcl.getPartData(node.id);
+        if (!gclPart || gclPart.length === 0) return;
+
+        for (const ag of gclPart) {
+            const condition = _.assign({ is_aux: ag.is_aux, name: ag.name, unit: ag.unit }, cacheLeafXmj);
+            let nag = _.find(gcl.ancGcl, condition);
+            if (!nag) {
+                nag = condition;
+                gcl.ancGcl.push(condition);
+            }
+            gatherfields(nag, ag, ancillaryGclGatherFields);
+        }
+
+    }
+
     /**
      * 汇总节点
      * @param node - 最底层 工程量清单 节点
@@ -317,6 +340,7 @@ const gclGatherModel = (function () {
             dx.settle_status = (posRange && posRange.length > 0 ? d.settle_status : node.settle_status) || 0 ;
             gcl.leafXmjs.push(dx);
         }
+        gatherAncGclData(node, gcl, cacheLeafXmj);
     }
 
     /**
@@ -631,16 +655,42 @@ const gclGatherModel = (function () {
         });
     }
 
+    function _gatherAncillaryGcl(gcl, fields = ['jldy']) {
+        fields.push('is_aux', 'name', 'unit');
+        gcl.gatherAncGcl = [];
+        for (const lx of gcl.ancGcl) {
+            const condition = {};
+            for (const f of fields) {
+                condition[f] = lx[f];
+            }
+            let glx = _.find(gcl.gatherAncGcl, condition);
+            if (!glx) {
+                glx = JSON.parse(JSON.stringify(lx));
+                gcl.gatherAncGcl.push(glx);
+            } else {
+                gatherfields(glx, lx, ancillaryGclGatherFields);
+            }
+        }
+    }
+
+    function reGatherAncillaryGcl(fields) {
+        gclList.forEach(g => {
+            _gatherAncillaryGcl(g, fields);
+        });
+    }
+
     return {
         loadGatherField,
         loadDecimal,
         loadLedgerData,
         loadPosData,
+        loadAncillaryGclData,
         loadDealBillsData,
         loadChangeBillsData,
         gatherGclData,
         checkDiffer,
         gatherChapterData,
         reGatherLeafXmj,
+        reGatherAncillaryGcl,
     };
 })();

+ 542 - 76
app/public/js/ledger.js

@@ -63,6 +63,7 @@ $(document).ready(function() {
     autoFlashHeight();
     // 初始化台账
     const ledgerSpread = SpreadJsObj.createNewSpread($('#ledger-spread')[0]);
+    const ledgerSheet = ledgerSpread.getActiveSheet();
     removeLocalCache('bills-fold');
     const treeSetting = {
         id: 'ledger_id',
@@ -94,6 +95,11 @@ $(document).ready(function() {
     });
     const posSpread = SpreadJsObj.createNewSpread($('#pos-spread')[0]);
 
+    // 初始化 附属工程量
+    const ancGcl = createAncillaryGcl({ id: 'id', masterId: 'lid', sort: [['g_order', 'asc']] });
+    const ancGclSpread = SpreadJsObj.createNewSpread($('#anc-gcl-spread')[0]);
+    const ancGclSheet = ancGclSpread.getActiveSheet();
+
     const billsTag = $.billsTag({
         selector: '#bills-tag',
         relaSpread: ledgerSpread,
@@ -104,6 +110,7 @@ $(document).ready(function() {
         },
         afterLocated:  function (lid, pos_id) {
             posOperationObj.loadCurPosData();
+            ancGclObj.loadCurAncillaryGcl();
             if (pos_id) {
                 const posSheet = posSpread.getActiveSheet();
                 const relaPos = posSheet.zh_data.find(x => { return x.id === pos_id; });
@@ -113,6 +120,7 @@ $(document).ready(function() {
         afterShow: function () {
             ledgerSpread.refresh();
             if (posSpread) posSpread.refresh();
+            if (ancGclSpread) ancGclSpread.refresh();
         },
     });
     const errorList = $.cs_errorList({
@@ -122,10 +130,12 @@ $(document).ready(function() {
         storeKey: 'ledger-error-' + getTenderId(),
         afterLocated:  function () {
             posOperationObj.loadCurPosData();
+            ancGclObj.loadCurAncillaryGcl();
         },
         afterShow: function () {
             ledgerSpread.refresh();
             if (posSpread) posSpread.refresh();
+            if (ancGclSpread) ancGclSpread.refresh();
         },
     });
     const checkList = $.ledger_checkList({
@@ -137,10 +147,12 @@ $(document).ready(function() {
         checkType: getCheckType(checkOption),
         afterLocated:  function () {
             posOperationObj.loadCurPosData();
+            ancGclObj.loadCurAncillaryGcl();
         },
         afterShow: function () {
             ledgerSpread.refresh();
             if (posSpread) posSpread.refresh();
+            if (ancGclSpread) ancGclSpread.refresh();
         },
     });
 
@@ -165,27 +177,14 @@ $(document).ready(function() {
             }
             autoFlashHeight();
             ledgerSpread.refresh();
-            if (posSpread) {
-                posSpread.refresh();
-            }
-            if (stdXmj) {
-                stdXmj.spread.refresh();
-            }
-            if (stdGcl) {
-                stdGcl.spread.refresh();
-            }
-            if (dealBills) {
-                dealBills.spread.refresh();
-            }
-            if (searchLedger) {
-                searchLedger.spread.refresh();
-            }
-            if (errorList) {
-                errorList.spread.refresh();
-            }
-            if (checkList) {
-                checkList.spread.refresh();
-            }
+            if (posSpread) posSpread.refresh();
+            if (ancGclSpread) ancGclSpread.refresh();
+            if (stdXmj) stdXmj.spread.refresh();
+            if (stdGcl) stdGcl.spread.refresh();
+            if (dealBills) dealBills.spread.refresh();
+            if (searchLedger) searchLedger.spread.refresh();
+            if (errorList) errorList.spread.refresh();
+            if (checkList) checkList.spread.refresh();
             if (gclGather) gclGather.spread.refresh();
         }
     });
@@ -421,10 +420,14 @@ $(document).ready(function() {
                 deleteAfterHint(function () {
                     postData(window.location.pathname + '/update', data, function (result) {
                         const refreshNode = tree.loadPostData(result);
-                        for (const d of refreshNode.delete) pos.removeDatasByMasterId(d.id);
+                        for (const d of refreshNode.delete) {
+                            pos.removeDatasByMasterId(d.id);
+                            ancGcl.removeDatasByMasterId(d.id);
+                        }
                         self.refreshTree(sheet, refreshNode);
                         self.refreshOperationValid(sheet);
                         posOperationObj.loadCurPosData();
+                        ancGclObj.loadCurAncillaryGcl();
                         billsTag.afterDeleteBills(refreshNode.delete);
                     });
                 });
@@ -890,6 +893,7 @@ $(document).ready(function() {
                 }
             }, function (data) {
                 pos.updateDatas(data.pos);
+                if (data.ancGcl) ancGcl.updateDatas(data.ancGcl);
                 const result = tree.loadPostData(data.ledger);
                 self.refreshTree(sheet, result);
                 sheet.setSelection(result.create[0].index, sel.col, sel.rowCount, sel.colCount);
@@ -900,6 +904,7 @@ $(document).ready(function() {
         },
         loadRelaData: function () {
             posOperationObj.loadCurPosData();
+            ancGclObj.loadCurAncillaryGcl();
             posSearch.search($('#pos-keyword').val());
             treeOperationObj.loadExprToInput(ledgerSpread.getActiveSheet());
         },
@@ -1051,6 +1056,29 @@ $(document).ready(function() {
                 })
             }
         },
+        batchReplace: function(orgInfo, newInfo) {
+            const updateData = [];
+            for (const d of ledgerTree.nodes) {
+                if (!d.b_code) continue;
+                const checkInfo = { code: d.b_code || '', name: d.name || '', unit: d.unit || '', unit_price: d.unit_price || 0 };
+                if (_.isMatch(checkInfo, orgInfo)) {
+                    const data = ledgerTree.getNodeKeyData(d);
+                    data.b_code = newInfo.code;
+                    data.name = newInfo.name;
+                    data.unit = newInfo.unit;
+                    data.unit_price = newInfo.unit_price;
+                    updateData.push(data);
+                }
+            }
+            if (updateData.length === 0) {
+                toastr.warning('没有可以替换的数据');
+                return;
+            }
+            postData(window.location.pathname + '/update', {postType: 'update', postData: updateData}, function (result) {
+                const refreshNode = ledgerTree.loadPostData(result);
+                treeOperationObj.refreshTree(ledgerSheet, refreshNode);
+            });
+        },
     };
     sjsSettingObj.setFxTreeStyle(ledgerSpreadSetting, sjsSettingObj.FxTreeStyle.jz);
     if (thousandth) sjsSettingObj.setTpThousandthFormat(ledgerSpreadSetting);
@@ -1417,6 +1445,23 @@ $(document).ready(function() {
                 return !readOnly;
             }
         };
+        billsContextMenuOptions.items.batchReplace = {
+            name: '批量替换清单',
+            icon: 'fa-sign-in',
+            callback: function(key, opt) {
+                const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                if (!node) return;
+
+                BatchReplace.load(node.b_code, node.name, node.unit, node.unit_price, treeOperationObj.batchReplace);
+            },
+            disabled: function (key, opt) {
+                const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                return !node || !node.b_code;
+            },
+            visible: function (key, opt) {
+                return !readOnly;
+            }
+        };
         billsContextMenuOptions.items.sprBatch = '----';
     }
     billsContextMenuOptions.items.copyBlock = {
@@ -1448,6 +1493,8 @@ $(document).ready(function() {
                 for (const b of cbl) {
                     const posRange = pos.getLedgerPos(b.id);
                     if (posRange && posRange.length > 0) b.pos = posRange;
+                    const gclRange = ancGcl.getPartData(b.id);
+                    if (gclRange && gclRange.length > 0) b.ancGcl = gclRange;
                 }
             }
             setLocalCache(copyBlockTag, JSON.stringify({block: copyBlockList}));
@@ -1595,6 +1642,7 @@ $(document).ready(function() {
                             SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
                             pos.loadDatas(result.pos);
                             posOperationObj.loadCurPosData();
+                            ancGclObj.loadCurAncillaryGcl();
                             checkShowLast(result.bills.length);
                         }, null);
                     },
@@ -1745,6 +1793,7 @@ $(document).ready(function() {
                 let bcontent = $(".bcontent-wrap") ? $(".bcontent-wrap").height() : 0;
                 $(".sp-wrap").height(bcontent-30);
                 posSpread.refresh();
+                if (ancGclSpread) ancGclSpread.refresh();
             }
         });
         sjsSettingObj.setGridSelectStyle(posSpreadSetting);
@@ -2386,11 +2435,401 @@ $(document).ready(function() {
         }
     });
 
+    // 附属工程量相关
+    const ancGclSpreadSetting = {
+        cols: [
+            {title: '辅材', colSpan: '1', rowSpan: '1', field: 'is_aux', hAlign: 1, width: 40, cellType: 'checkbox'},
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 120, formatter: '@'},
+            {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 40, formatter: '@', cellType: 'unit'},
+            {title: '设计量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 60, type: 'Number'},
+            {title: '设计量公式', colSpan: '1', rowSpan: '1', field: 'expr', hAlign: 0, width: 80, formatter: '@'},
+            {title: '图册号', colSpan: '1', rowSpan: '1', field: 'drawing_code', hAlign: 0, width: 80, formatter: '@'},
+            {title: '备注', colSpan: '1', rowSpan: '1', field: 'memo', hAlign: 0, width: 80, formatter: '@'},
+        ],
+        emptyRows: 3,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+    };
+    SpreadJsObj.initSheet(ancGclSheet, ancGclSpreadSetting);
+    const ancGclObj = {
+        loadCurAncillaryGcl: function () {
+            const node = treeOperationObj.getSelectNode(ledgerSheet);
+            const gclData = node ? ancGcl.getPartData(node.id) || [] : [];
+            SpreadJsObj.loadSheetData(ancGclSheet, SpreadJsObj.DataType.Data, gclData);
+        },
+        baseOpr: function (type) {
+            const data = {};
+
+            const gclRange = ancGclSheet.zh_data;
+            if (type !== 'insert' && (!gclRange || gclRange.length === 0)) return;
+
+            const sel = ancGclSheet.getSelections();
+            if (!sel[0]) return;
+
+            const row = sel[0].row, count = sel[0].rowCount;
+            const first = gclRange[row];
+            if (type === 'insert') {
+                const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                data.add = [{ lid: node.id, g_order: gclRange.length + 1 }];
+            } else if (type === 'delete') {
+                data.del = [];
+                for (let iRow = 0; iRow < count; iRow++) {
+                    const gclData = gclRange[row + iRow];
+                    if (!gclData) continue;
+                    data.del.push(gclData.id);
+                }
+
+                if (data.del.length === 0) return;
+            } else if (type === 'up-move') {
+                data.update = [];
+                const pre = gclRange[row - 1];
+                if (!pre) return;
+
+                const preUpdate = { id: pre.id };
+                for (let iRow = 0; iRow < count; iRow++) {
+                    const gclData = gclRange[iRow + row];
+                    if (!gclData) continue;
+                    data.update.push({ id: gclData.id, g_order: gclRange[iRow + row - 1].g_order });
+                    preUpdate.g_order = gclData.g_order;
+                }
+                data.update.push(preUpdate);
+
+                if (data.update <= 1) return;
+            } else if (type === 'down-move') {
+                data.update = [];
+                const next = gclRange[row + count];
+                if (!next) return;
+
+                const nextUpdate = { id: next.id };
+                for (let iRow = count - 1; iRow >= 0; iRow--) {
+                    const gclData = gclRange[iRow + row];
+                    if (!gclData) continue;
+
+                    data.update.push({ id: gclData.id, g_order: gclRange[iRow + row + 1].g_order});
+                    nextUpdate.g_order = gclData.g_order;
+                }
+                data.update.push(nextUpdate);
+
+                if (data.update <= 1) return;
+            }
+
+            postData('/tender/' + getTenderId() + '/anc-gcl/update', data, function(result) {
+                ancGcl.updateDatas(result);
+                ancGclObj.loadCurAncillaryGcl();
+                if (type !== 'delete') SpreadJsObj.locateData(ancGclSheet, first);
+            });
+        },
+        editStarting: function (e, info) {
+            ancGclObj.ledgerTreeNode = SpreadJsObj.getSelectObject(ledgerSheet);
+        },
+        editEnded: function (e, info) {
+            const setting = info.sheet.zh_setting;
+            if (!setting) return;
+            const gclData = SpreadJsObj.getSelectObject(info.sheet);
+
+            const col = setting.cols[info.col];
+            const orgText = gclData ? gclData[col.field] : '', newText = trimInvalidChar(info.editingText);
+            if (orgText === newText || (!orgText && !newText)) return;
+
+            const node = ancGclObj.ledgerTreeNode;
+            if (!node) {
+                toastr.error('数据错误,请选择台账节点后再试');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            if (!!newText && node.children && node.children.length > 0) {
+                toastr.error('父节点不可添加附属工程量');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            if (!!newText && (!node.b_code || node.b_code === '')) {
+                toastr.error('项目节不可插入附属工程量');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+
+            const data = {};
+            if (gclData) {
+                const updateData = { id: gclData.id };
+                if (col.type === 'Number') {
+                    const num = _.toNumber(newText);
+                    if (!_.isFinite(num)) {
+                        toastr.error('输入的数字非法');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    updateData[col.field] = num;
+                    if (gclData.expr) updateData.expr = '';
+                } else if (col.field === 'expr') {
+                    try {
+                        updateData.expr = newText;
+                        updateData.quantity = math.evaluate(transExpr(newText));
+                    } catch(err) {
+                        toastr.error('输入的计算式非法');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                } else {
+                    updateData[col.field] = newText;
+                }
+                data.update = [ updateData ];
+            } else {
+                const sortData = info.sheet.zh_data;
+                const order = (!sortData || sortData.length === 0) ? 1 : Math.max(sortData[sortData.length - 1].g_order + 1, sortData.length + 1);
+                const addData = { lid: node.id, g_order: order };
+                if (col.type === 'Number') {
+                    const num = _.toNumber(newText);
+                    if (!_.isFinite(num)) {
+                        toastr.error('输入的数字非法');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                    addData[col.field] = num;
+                    if (gclData.expr) addData.expr = '';
+                } else if (col.field === 'expr') {
+                    try {
+                        addData.expr = newText;
+                        addData.quantity = math.evaluate(transExpr(newText));
+                    } catch(err) {
+                        toastr.error('输入的计算式非法');
+                        SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                        return;
+                    }
+                } else {
+                    addData[col.field] = newText;
+                }
+                data.add = [addData];
+            }
+            postData('/tender/' + getTenderId() + '/anc-gcl/update', data, function (result) {
+                ancGcl.updateDatas(result);
+                ancGclObj.loadCurAncillaryGcl();
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            });
+        },
+        deletePress: function (sheet) {
+            const setting = sheet.zh_setting;
+            if (!setting) return;
+
+            const sortData = sheet.zh_data;
+            if (!sortData || sortData.length === 0) return;
+
+            const sel = sheet.getSelections()[0];
+            const data = { update: [] };
+            for (let iRow = sel.row; iRow < sel.row + sel.rowCount; iRow++) {
+                let bDel = false;
+                const node = sortData[iRow];
+                if (!node) continue;
+
+                const updateData = { id: node.id };
+                for (let iCol = sel.col; iCol < sel.col + sel.colCount; iCol++) {
+                    const style = sheet.getStyle(iRow, iCol);
+                    if (style.locked) continue;
+
+                    const col = setting.cols[iCol];
+                    if (col.field === 'expr') {
+                        updateData.expr = '';
+                        updateData.quantity = 0;
+                    } else {
+                        updateData[col.field] = col.type === 'Number' ? 0 : '';
+                        if (col.field === 'quantity') updateDataexpr = '';
+                    }
+                    bDel = true;
+                }
+                if (bDel) data.update.push(updateData);
+            }
+            if (data.update.length === 0) return;
+
+            postData('/tender/' + getTenderId() + '/anc-gcl/update', data, function (result) {
+                ancGcl.updateDatas(result);
+                ancGclObj.loadCurAncillaryGcl();
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            });
+        },
+        clipboardPasting: function(e, info) {
+            info.cancel = true;
+            const node = SpreadJsObj.getSelectObject(ledgerSheet);
+            if (!node) {
+                toastr.error('数据错误,请选择台账节点后再试');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            if (node.children && node.children.length > 0) {
+                toastr.error('父节点不可添加附属工程量');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+            if ((!node.b_code || node.b_code === '')) {
+                toastr.error('项目节不可插入附属工程量');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+
+            const hint = {
+                expr: {type: 'warning', msg: '输入的 计算式 非法,已过滤'},
+                num: {type: 'warning', msg: '输入的 数字 非法,已过滤'},
+            };
+            const setting = info.sheet.zh_setting;
+            const sortData = info.sheet.zh_data || [];
+            const pasteData = SpreadJsObj.analysisPasteText(info.pasteData.text);
+            const data = {};
+            const analysisData = function(pasteRow, targetData) {
+                pasteRow.forEach((value, iCol) => {
+                    const col = setting.cols[info.cellRange.col + iCol];
+                    if (col.field === 'expr') {
+                        try {
+                            targetData.expr = trimInvalidChar(value);
+                            targetData.quantity = math.evaluate(transExpr(targetData.expr));
+                        } catch(err) {
+                            toastMessageUniq(hint.expr);
+                            targetData.expr = '';
+                            return;
+                        }
+                    } else if (col.type === 'Number') {
+                        const num = _.toNumber(value);
+                        if (!_.isFinite(num)) {
+                            toastMessageUniq(hint.num);
+                            return;
+                        }
+                        targetData[col.field] = num;
+                        if (col.field === 'quantity') targetData.expr = '';
+                    } else {
+                        targetData[col.field] = value;
+                    }
+                });
+            };
+            for (let iRow = 0; iRow < pasteData.length; iRow++) {
+                const curRow = iRow + info.cellRange.row;
+                const gclData = sortData[curRow];
+                if (gclData) {
+                    if (!data.update) data.update = [];
+                    const updateData = { id: gclData.id };
+                    analysisData(pasteData[iRow], updateData);
+                    data.update.push(updateData);
+                } else {
+                    if (!data.add) data.add = [];
+                    const addData = { lid: node.id, g_order: curRow + 1};
+                    analysisData(pasteData[iRow], addData);
+                    data.add.push(addData);
+                }
+            }
+            if ((!data.update || data.update.length === 0) && (!data.add || data.add.length === 0)) return;
+
+            postData('/tender/' + getTenderId() + '/anc-gcl/update', data, function (result) {
+                ancGcl.updateDatas(result);
+                ancGclObj.loadCurAncillaryGcl();
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            });
+        },
+        buttonClicked: function (e, info) {
+            const setting = info.sheet.zh_setting;
+            if (!setting) return;
+
+            const node = SpreadJsObj.getSelectObject(info.sheet);
+            if (!node) {
+                toastr.warning('请先添加附属工程量再设置辅材');
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+                return;
+            }
+
+            const col = setting.cols[info.col];
+            const updateData = { id: node.id };
+            updateData[col.field] = node[col.field] ? 0 : 1;
+            postData('/tender/' + getTenderId() + '/anc-gcl/update', { update: [updateData]}, function (result) {
+                ancGcl.updateDatas(result);
+                ancGclObj.loadCurAncillaryGcl();
+            }, function () {
+                SpreadJsObj.reLoadRowData(info.sheet, info.row);
+            });
+        },
+    };
+    SpreadJsObj.addDeleteBind(ancGclSpread, ancGclObj.deletePress);
+    ancGclSpread.bind(spreadNS.Events.EditStarting, ancGclObj.editStarting);
+    ancGclSpread.bind(spreadNS.Events.EditEnded, ancGclObj.editEnded);
+    ancGclSpread.bind(spreadNS.Events.ClipboardPasting, ancGclObj.clipboardPasting);
+    ancGclSpread.bind(spreadNS.Events.ButtonClicked, ancGclObj.buttonClicked);
+    $.contextMenu({
+        selector: '#anc-gcl-spread',
+        build: function ($trigger, e) {
+            const target = SpreadJsObj.safeRightClickSelection($trigger, e, ancGclSpread);
+            return target.hitTestType === GC.Spread.Sheets.SheetArea.viewport || target.hitTestType === GC.Spread.Sheets.SheetArea.rowHeader;
+        },
+        items: {
+            'insert': {
+                name: '插入',
+                icon: 'fa-plus',
+                disabled: function (key, opt) {
+                    const node = SpreadJsObj.getSelectObject(ledgerSheet);
+                    return !node || !node.b_code || (node.children && node.children.length > 0);
+                },
+                callback: function (key, opt) {
+                    ancGclObj.baseOpr('insert');
+                }
+            },
+            'delete': {
+                name: '删除',
+                icon: 'fa-remove',
+                disabled: function (key, opt) {
+                    const gclData = SpreadJsObj.getSelectObject(ancGclSheet);
+                    return !gclData;
+                },
+                callback: function (key, opt) {
+                    ancGclObj.baseOpr('delete');
+                }
+            },
+            'down-move': {
+                name: '下移',
+                icon: 'fa-arrow-down',
+                disabled: function(key, opt) {
+                    const sel = ancGclSheet.getSelections()[0];
+                    const row = sel ? sel.row : -1;
+                    const first = ancGclSheet.zh_data[row];
+                    const next = ancGclSheet.zh_data[sel.row + sel.rowCount];
+                    return !first || !next;
+                },
+                callback: function(key, opt) {
+                    ancGclObj.baseOpr('down-move');
+                }
+            },
+            'up-move': {
+                name: '上移',
+                icon: 'fa-arrow-up',
+                disabled: function(key, opt) {
+                    const sel = ancGclSheet.getSelections()[0];
+                    const row = sel ? sel.row : -1;
+                    const first = ancGclSheet.zh_data[row];
+                    const preNode = ancGclSheet.zh_data[row - 1];
+                    return !first || !preNode;
+                },
+                callback: function(key, opt) {
+                    ancGclObj.baseOpr('up-move');
+                }
+            },
+        }
+    });
+    $('#filter-anc-gcl').change(function() {
+        if (this.checked) {
+            const condition = {};
+            condition[this.getAttribute('name')] = parseInt(this.value);
+            ancGcl.filter = condition;
+        } else {
+            ancGcl.filter = null;
+        }
+        ancGclObj.loadCurAncillaryGcl();
+    });
+
     postData(window.location.pathname + '/load', {}, function (data) {
         ledgerTree.loadDatas(data.bills);
         treeCalc.calculateAll(ledgerTree);
         checkShowLast(data.bills.length);
         pos.loadDatas(data.pos);
+        ancGcl.loadDatas(data.ancGcl);
 
         for (const t of data.tags) {
             t.node = ledgerTree.datas.find(x => { return x.id === t.lid; });
@@ -2406,6 +2845,9 @@ $(document).ready(function() {
         posOperationObj.loadCurPosData();
         SpreadJsObj.resetTopAndSelect(posSpread.getActiveSheet());
 
+        ancGclObj.loadCurAncillaryGcl();
+        SpreadJsObj.resetTopAndSelect(ancGclSheet);
+
         treeOperationObj.refreshOperationValid(ledgerSpread.getActiveSheet());
         treeOperationObj.loadExprToInput(ledgerSpread.getActiveSheet());
 
@@ -2416,77 +2858,51 @@ $(document).ready(function() {
         select: '#right-spr',
         callback: function () {
             ledgerSpread.refresh();
-            if (posSpread) {
-                posSpread.refresh();
-            }
-            if (stdXmj) {
-                stdXmj.spread.refresh();
-            }
-            if (stdGcl) {
-                stdGcl.spread.refresh();
-            }
-            if (dealBills) {
-                dealBills.spread.refresh();
-            }
-            if (searchLedger) {
-                searchLedger.spread.refresh();
-            }
-            if (errorList) {
-                errorList.spread.refresh();
-            }
-            if (checkList) {
-                checkList.spread.refresh();
-            }
+            if (posSpread) posSpread.refresh();
+            if (ancGclSpread) ancGclSpread.refresh();
+            if (stdXmj) stdXmj.spread.refresh();
+            if (stdGcl) stdGcl.spread.refresh();
+            if (dealBills) dealBills.spread.refresh();
+            if (searchLedger) searchLedger.spread.refresh();
+            if (errorList) errorList.spread.refresh();
+            if (checkList) checkList.spread.refresh();
             if (gclGather) gclGather.spread.refresh();
         }
     });
-    const stdLibCellDoubleClick = function (e, info) {
-        const stdSheet = info.sheet;
-        const mainSheet = ledgerSpread.getActiveSheet();
-        if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
-
-        const stdTree = stdSheet.zh_tree;
-        const stdNode = stdTree.nodes[info.row];
-
-        const mainTree = mainSheet.zh_tree;
-        const sel = mainSheet.getSelections()[0];
-        const mainNode = mainTree.nodes[sel.row];
-        if (!stdNode) { return; }
-        if (info.sheet.zh_setting.stdType === 'gcl') {
-            if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
+    const stdLibCellDoubleClick = function (updateData, stdNode) {
+        if (!stdNode) return;
+        const mainNode = SpreadJsObj.getSelectObject(ledgerSheet);
+        if (!mainNode) return;
+        if (updateData.postData.stdType === 'gcl') {
+            if (!!mainNode.code && !ledgerTree.isLeafXmj(mainNode)) {
                 toastr.warning('非最底层项目下,不应添加清单');
                 return;
             }
         }
 
-        postData(window.location.pathname + '/update', {
-            postType: 'add-std',
-            postData: {
-                id: ledgerTree.getNodeKey(mainNode),
-                tender_id: mainNode.tender_id,
-                stdType: info.sheet.zh_setting.stdType,
-                stdLibId: stdNode.list_id,
-                stdNode: stdTree.getNodeKey(stdNode)
-            }
-        }, function (result) {
-            const refreshNode = mainTree.loadPostData(result);
-            treeOperationObj.refreshTree(mainSheet, refreshNode);
+        updateData.postData.id = ledgerTree.getNodeKey(mainNode);
+        updateData.postData.tender_id = mainNode.tender_id;
+
+        postData(window.location.pathname + '/update', updateData, function (result) {
+            const sel = ledgerSheet.getSelections()[0];
+            const refreshNode = ledgerTree.loadPostData(result);
+            treeOperationObj.refreshTree(ledgerSheet, refreshNode);
             if (refreshNode.create && refreshNode.create.length > 0) {
-                mainSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
+                ledgerSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
                 const refreshRow = [sel.row, refreshNode.create[refreshNode.create.length - 1].index];
-                const curIndex = mainTree.nodes.indexOf(mainNode);
+                const curIndex = ledgerTree.nodes.indexOf(mainNode);
                 if (sel.row !== curIndex && curIndex >= 0) refreshRow.push(curIndex);
-                SpreadJsObj.reloadRowsBackColor(mainSheet, refreshRow);
+                SpreadJsObj.reloadRowsBackColor(ledgerSheet, refreshRow);
             } else {
                 const node = _.find(ledgerTree.nodes, {code: stdNode.code, name: stdNode.name});
                 if (node) {
-                    mainSheet.setSelection(ledgerTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
-                    SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, ledgerTree.nodes.indexOf(node)]);
+                    ledgerSheet.setSelection(ledgerTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
+                    SpreadJsObj.reloadRowsBackColor(ledgerSheet, [sel.row, ledgerTree.nodes.indexOf(node)]);
                 }
             }
-            treeOperationObj.refreshOperationValid(mainSheet);
+            treeOperationObj.refreshOperationValid(ledgerSheet);
             ledgerSpread.focus();
-            posOperationObj.loadCurPosData();
+            treeOperationObj.loadRelaData();
         });
     };
     const stdXmjSetting = {
@@ -2627,6 +3043,7 @@ $(document).ready(function() {
                         },
                         afterLocated: function () {
                             posOperationObj.loadCurPosData();
+                            ancGclObj.loadCurAncillaryGcl();
                         },
                         calcSum: function (result) {
                             const sum = { name: '合计' };
@@ -2661,6 +3078,7 @@ $(document).ready(function() {
         if (posSpread) {
             posSpread.refresh();
         }
+        if (ancGclSpread) ancGclSpread.refresh();
     });
     class DealBills {
         constructor (selector, spreadSetting) {
@@ -2882,6 +3300,7 @@ $(document).ready(function() {
                         treeOperationObj.refreshOperationValid(mainSheet);
                         ledgerSpread.focus();
                         posOperationObj.loadCurPosData();
+                        ancGclObj.loadCurAncillaryGcl();
                     });
                 });
             }
@@ -3285,6 +3704,7 @@ $(document).ready(function() {
                             SpreadJsObj.reloadRowsBackColor(sheet, [sel.row, result.create[0].index]);
                             treeOperationObj.refreshOperationValid(sheet);
                             posOperationObj.loadCurPosData();
+                            ancGclObj.loadCurAncillaryGcl();
                             self.obj.modal('hide');
                         }, null, true);
                     } else {
@@ -3586,6 +4006,49 @@ $(document).ready(function() {
         }
     }
 
+    $('a', '#anc-gcl-nav').bind('click', function(e) {
+        e.preventDefault();
+        const tab = $(this);
+        const showSideTab = function (show) {
+            const left = $('#pos-spread'), right = $('#pos-right'), parent = left.parent();
+            if (show) {
+                right.show();
+                autoFlashHeight();
+                /**
+                 * right.show()后, parent被撑开成2倍left.height, 导致parent.width减少了10px
+                 * 第一次left.width调整后,parent的缩回left.height, 此时parent.width又增加了10px
+                 * 故需要通过最终的parent.width再计算一次left.width
+                 *
+                 * Q: 为什么不通过先计算left.width的宽度,以避免计算两次left.width?
+                 * A: 右侧工具栏不一定显示,当右侧工具栏显示过一次后,就必须使用parent和right来计算left.width
+                 *
+                 */
+                const percent = 100 - right.outerWidth() /parent.width() * 100;
+                left.css('width', percent + '%');
+            } else {
+                left.width(parent.width());
+                right.hide();
+            }
+
+        };
+        if (!tab.hasClass('active')) {
+            tab.addClass('active');
+            showSideTab(tab.hasClass('active'));
+        } else {
+            tab.removeClass('active');
+            showSideTab(tab.hasClass('active'));
+        }
+        if (posSpread) posSpread.refresh();
+        if (ancGclSpread) ancGclSpread.refresh();
+    });
+    $.divResizer({
+        select: '#pos-right-spr',
+        callback: function () {
+            if (posSpread) posSpread.refresh();
+            if (ancGclSpread) ancGclSpread.refresh();
+        }
+    });
+
     // $('#searchAccount').click(() => {
     //     const data = {
     //         keyword: $('#searchName').val(),
@@ -3871,6 +4334,7 @@ $(document).ready(function() {
             SpreadJsObj.loadSheetData(ledgerSpread.getActiveSheet(), 'tree', ledgerTree);
             pos.loadDatas(result.pos);
             posOperationObj.loadCurPosData();
+            ancGclObj.loadCurAncillaryGcl();
             checkShowLast(result.bills.length);
         });
     });
@@ -3882,6 +4346,7 @@ $(document).ready(function() {
             const loadResult = ledgerTree.loadPostData({update: data.source.bills});
             treeOperationObj.refreshTree(ledgerSpread.getActiveSheet(), loadResult);
             posOperationObj.loadCurPosData();
+            ancGclObj.loadCurAncillaryGcl();
             for (const e of data.error) {
                 e.serialNo = ledgerTree.getNodeIndex(ledgerTree.getItems(e.ledger_id)) + 1;
             }
@@ -4110,6 +4575,7 @@ $(document).ready(function() {
           if (Object.keys(att).length) {
               SpreadJsObj.locateTreeNode(ledgerSpread.getActiveSheet(), att.ledger_id, true);
               posOperationObj.loadCurPosData();
+              ancGclObj.loadCurAncillaryGcl();
           }
       }
   });

+ 71 - 1
app/public/js/ledger_gather.js

@@ -131,6 +131,35 @@ $(document).ready(() => {
     const gatherLeafXmjSheet = gatherLeafXmjSpread.getActiveSheet();
     SpreadJsObj.initSheet(gatherLeafXmjSheet, gatherLeafXmjSpreadSetting);
 
+    const gatherAncGclSpread = SpreadJsObj.createNewSpread($('#anc-gcl-gather-spread')[0]);
+    const gatherAncGclSpreadSetting = {
+        cols: [
+            {title: '单位工程', colSpan: '1', rowSpan: '1', field: 'dwgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '分部工程', colSpan: '1', rowSpan: '1', field: 'fbgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '分项工程', colSpan: '1', rowSpan: '1', field: 'fxgc', hAlign: 0, width: 100, formatter: '@', visible: false},
+            {title: '细目', colSpan: '1', rowSpan: '1', field: 'jldy', hAlign: 0, width: 100, formatter: '@'},
+            {title: '辅材', colSpan: '1', rowSpan: '1', field: 'is_aux', hAlign: 1, width: 60, formatter: '@', fixed: 1, cellType: 'checkbox'},
+            {title: '名称', colSpan: '1', rowSpan: '1', field: 'name', hAlign: 0, width: 100, formatter: '@', fixed: 1},
+            {title: '单位', colSpan: '1', rowSpan: '1', field: 'unit', hAlign: 1, width: 50, formatter: '@', fixed: 1},
+            {title: '设计量', colSpan: '1', rowSpan: '1', field: 'quantity', hAlign: 2, width: 80, type: 'Number', fixed: 1},
+        ],
+        emptyRows: 0,
+        headRows: 1,
+        headRowHeight: [32],
+        headColWidth: [30],
+        defaultRowHeight: 21,
+        headerFont: '12px 微软雅黑',
+        font: '12px 微软雅黑',
+        readOnly: true,
+        localCache: {
+            key: 'ledger-gather-ancGcl',
+            colWidth: true,
+        },
+    };
+    if (thousandth) sjsSettingObj.setTpThousandthFormat(gatherAncGclSpreadSetting);
+    const gatherAncGclSheet = gatherAncGclSpread.getActiveSheet();
+    SpreadJsObj.initSheet(gatherAncGclSheet, gatherAncGclSpreadSetting);
+
     let gclGatherData;
     // 获取项目节数据
     function loadLeafXmjData(iGclRow) {
@@ -145,13 +174,22 @@ $(document).ready(() => {
     function loadGatherLeafXmjData(iGclRow) {
         const gcl = iGclRow ? gclGatherData[iGclRow] : SpreadJsObj.getSelectObject(gclSheet);
         SpreadJsObj.resetTopAndSelect(gatherLeafXmjSheet);
-        console.log(gcl.gatherLeafXmjs);
         if (gcl) {
             SpreadJsObj.loadSheetData(gatherLeafXmjSheet, SpreadJsObj.DataType.Data, gcl.gatherLeafXmjs);
         } else {
             SpreadJsObj.loadSheetData(gatherLeafXmjSheet, SpreadJsObj.DataType.Data, []);
         }
     }
+    function loadGatherAncGclData(iGclRow) {
+        const gcl = iGclRow ? gclGatherData[iGclRow] : SpreadJsObj.getSelectObject(gclSheet);
+        SpreadJsObj.resetTopAndSelect(gatherAncGclSheet);
+        console.log(gcl.ancGcl, gcl.gatherAncGcl);
+        if (gcl) {
+            SpreadJsObj.loadSheetData(gatherAncGclSheet, SpreadJsObj.DataType.Data, gcl.gatherAncGcl);
+        } else {
+            SpreadJsObj.loadSheetData(gatherAncGclSheet, SpreadJsObj.DataType.Data, []);
+        }
+    }
     // 标红显示 签约-台账≠0
     function checkCompareData() {
         const sheet = gclSheet;
@@ -173,6 +211,7 @@ $(document).ready(() => {
         if (!info.oldSelections || iNewRow !== info.oldSelections[0].row) {
             loadLeafXmjData(iNewRow);
             loadGatherLeafXmjData(iNewRow);
+            loadGatherAncGclData(iNewRow);
         }
     });
 
@@ -205,9 +244,11 @@ $(document).ready(() => {
         gclGatherModel.loadLedgerData(data.bills);
         gclGatherModel.loadPosData(data.pos);
         gclGatherModel.loadDealBillsData(data.dealBills);
+        gclGatherModel.loadAncillaryGclData(data.ancGcl);
         gclGatherData = gclGatherModel.gatherGclData();
         gclGatherModel.checkDiffer(gclGatherData);
         gclGatherModel.reGatherLeafXmj();
+        gclGatherModel.reGatherAncillaryGcl();
         for (const gcl of gclGatherData) {
             gcl.compare_qty = ZhCalc.sub(gcl.quantity, gcl.deal_bills_qty);
             gcl.compare_tp = ZhCalc.sub(gcl.total_price, gcl.deal_bills_tp);
@@ -217,6 +258,7 @@ $(document).ready(() => {
         checkCompareData();
         loadLeafXmjData(0);
         loadGatherLeafXmjData(0);
+        loadGatherAncGclData(0);
 
         const chapterData = gclGatherModel.gatherChapterData(chapter, data.spec, ['total_price']);
         for (const c of chapterData) {
@@ -264,6 +306,7 @@ $(document).ready(() => {
         afterLocated: function () {
             loadLeafXmjData();
             loadGatherLeafXmjData();
+            loadGatherAncGclData();
         },
         calcSum: function (result) {
             const sum = { name: '合计', searchIndex: -1 };
@@ -293,6 +336,7 @@ $(document).ready(() => {
         gclSpread.refresh();
         leafXmjSpread.refresh();
         gatherLeafXmjSpread.refresh();
+        gatherAncGclSpread.refresh();
     });
 
     // 下部设置显示相关
@@ -306,6 +350,11 @@ $(document).ready(() => {
                 $(ot).hide();
             }
         }
+        setTimeout(function() {
+            leafXmjSpread.refresh();
+            gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
+        }, 100);
     });
     $('[name=gather-xmj]').change(function() {
         const checkOption = $('[name=gather-xmj]:checked');
@@ -325,6 +374,24 @@ $(document).ready(() => {
         SpreadJsObj.refreshColumnVisible(gatherLeafXmjSheet);
         loadGatherLeafXmjData();
     });
+    $('[name=gather-anc-gcl]').change(function() {
+        const checkOption = $('[name=gather-anc-gcl]:checked');
+        if (checkOption.length === 0) {
+            toastr.warning('请至少选择一个汇总条件');
+            return;
+        }
+
+        const fields = [];
+        for (const co of checkOption) {
+            fields.push(co.value);
+        }
+        for (const col of gatherAncGclSpreadSetting.cols) {
+            col.visible = col.fixed || fields.indexOf(col.field) >= 0;
+        }
+        gclGatherModel.reGatherAncillaryGcl(fields);
+        SpreadJsObj.refreshColumnVisible(gatherAncGclSheet);
+        loadGatherAncGclData();
+    });
 
     $.subMenu({
         menu: '#sub-menu', miniMenu: '#sub-mini-menu', miniMenuList: '#mini-menu-list',
@@ -343,6 +410,7 @@ $(document).ready(() => {
             gclSpread.refresh();
             leafXmjSpread.refresh();
             gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
         }
     });
     $.divResizer({
@@ -353,6 +421,7 @@ $(document).ready(() => {
             $(".sp-wrap").height(bcontent-30);
             leafXmjSpread.refresh();
             gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
         }
     });
     $.divResizer({
@@ -361,6 +430,7 @@ $(document).ready(() => {
             gclSpread.refresh();
             leafXmjSpread.refresh();
             gatherLeafXmjSpread.refresh();
+            gatherAncGclSpread.refresh();
         }
     });
     $('#exportExcel').click(function () {

+ 4 - 3
app/public/js/material_checklist.js

@@ -162,9 +162,10 @@ $(document).ready(() => {
         // 解析清单汇总数据
         gclGatherModel.loadLedgerData(ledger, curLedgerData);
         gclGatherModel.loadPosData(pos, curPosData);
-        gclGatherData = gclGatherModel.gatherGclData().filter(function (item) {
-            return item.unit_price || item.quantity;
-        });
+        // gclGatherData = gclGatherModel.gatherGclData().filter(function (item) {
+        //     return item.unit_price || item.quantity;
+        // });
+        gclGatherData = gclGatherModel.gatherGclData();
         console.log(gclGatherData);
         const hadBillsidList = _.uniq(_.map(gclList, 'gcl_id'));
         console.log(hadBillsidList, materialChecklistData);

+ 168 - 0
app/public/js/path_tree.js

@@ -2058,6 +2058,174 @@ const createNewPathTree = function (type, setting) {
     }
 };
 
+const createAncillaryGcl = function (setting) {
+    class AncillaryGcl {
+        /**
+         * 构造函数
+         * @param {id|Number, masterId|Number} setting
+         */
+        constructor(setting) {
+            this.itemPre = 'id_';
+            // 无索引
+            this.datas = [];
+            // 以key为索引
+            this.items = {};
+            // 以分类id为索引的有序
+            this.masterIndex = {};
+            // 设置
+            this.setting = setting;
+        }
+
+        resortPart(partData) {
+            const sortRule = this.setting.sort || [['g_order', 'asc']];
+            if (partData instanceof Array) {
+                partData.sort(function (a, b) {
+                    for (const sr of sortRule) {
+                        const iSort = sr[1] === 'asc' ? a[sr[0]] - b[sr[0]] : b[sr[0]] - a[sr[0]];
+                        if (iSort) return iSort;
+                    }
+                })
+            }
+        }
+
+        filterPart(partData) {
+            const check = this._filter;
+            partData.forEach(x => {
+                x.visible = true;
+                if (check) {
+                    for (const prop in check) {
+                        if (x[prop] !== check[prop]) x.visible = false;
+                    }
+                }
+            });
+        }
+
+        /**
+         * 加载数据
+         * @param datas
+         */
+        loadDatas(datas) {
+            this.datas = datas;
+            this.items = {};
+            this.masterIndex = {};
+            for (const data of this.datas) {
+                const key = this.itemPre + data[this.setting.id];
+                this.items[key] = data;
+
+                const masterKey = this.itemPre + data[this.setting.masterId];
+                if (!this.masterIndex[masterKey]) {
+                    this.masterIndex[masterKey] = [];
+                }
+                this.masterIndex[masterKey].push(data);
+            }
+            for (const prop in this.masterIndex) {
+                this.resortPart(this.masterIndex[prop]);
+            }
+        }
+
+        _addDatas(data, resort) {
+            const datas = data instanceof Array ? data : [data];
+            for (const d of datas) {
+                const key = this.itemPre + d[this.setting.id];
+                this.items[key] = d;
+
+                const masterKey = this.itemPre + d[this.setting.masterId];
+                if (!this.masterIndex[masterKey]) this.masterIndex[masterKey] = [];
+                this.masterIndex[masterKey].push(d);
+                if (resort.indexOf(masterKey) < -1) resort.push(masterKey);
+            }
+        }
+
+        /**
+         * 更新数据
+         * @param datas
+         */
+        _updateDatas(data, resort) {
+            const datas = data instanceof Array ? data : [data];
+            for (const d of datas) {
+                const item = this.getItem(d[this.setting.id]);
+                if (!item) continue;
+                for (const prop in d) {
+                    item[prop] = d[prop];
+                }
+                const masterKey = this.itemPre + item[this.setting.masterId];
+                if (resort.indexOf(masterKey) === -1) resort.push(masterKey);
+            }
+
+        }
+
+        /**
+         * 移除数据
+         * @param datas
+         */
+        _removeDatas(data, resort) {
+            if (!data) { return; }
+            const datas = data instanceof Array ? data : [data];
+            for (let i = datas.length - 1; i >= 0; i--) {
+                const id = datas[i];
+                const d = this.getItem(id);
+                this.datas.splice(this.datas.indexOf(d), 1);
+                const key = this.itemPre + d[this.setting.id];
+                delete this.items[key];
+                const range = this.getPartData(d[this.setting.masterId]);
+                range.splice(range.indexOf(d), 1);
+            }
+        }
+
+        updateDatas(data) {
+            const resort = [];
+            if (data.add) this._addDatas(data.add, resort);
+            if (data.del) this._removeDatas(data.del, resort);
+            if (data.update) this._updateDatas(data.update, resort);
+            for (const s of resort) {
+                this.resortPart(this.masterIndex[s]);
+                this.filterPart(this.masterIndex[s]);
+            }
+        }
+
+        /**
+         * 移除数据 - 根据分类id
+         * @param mid
+         */
+        removeDatasByMasterId(mid) {
+            const masterKey = this.itemPre + mid;
+            const range = this.masterIndex[masterKey];
+            if (range) {
+                delete this.masterIndex[masterKey];
+                for (const r of range) {
+                    this.datas.splice(this.datas.indexOf(r), 1);
+                    const key = this.itemPre + r[this.setting.id];
+                    delete this.items[key];
+                }
+            }
+        }
+
+        getItem(id) {
+            return this.items[this.itemPre + id];
+        }
+
+        getPartData(mid) {
+            return this.masterIndex[this.itemPre + mid];
+        }
+
+        set sort(sort) {
+            this.setting.sort = sort;
+            for (const key in this.masterIndex) {
+                this.resortPart(this.masterIndex[key]);
+            }
+        }
+
+        set filter(condition) {
+            this._filter = condition;
+            for (const key in this.masterIndex) {
+                this.filterPart(this.masterIndex[key]);
+            }
+        }
+    }
+
+    return new AncillaryGcl(setting);
+};
+
 const treeCalc = {
     mapTreeNode: function (tree) {
         const setting = tree.setting;

+ 57 - 29
app/public/js/revise.js

@@ -915,7 +915,6 @@ $(document).ready(() => {
                     info.cancel = posRange && posRange.length > 0;
                     break;
                 case 'unit_price':
-                    console.log(node);
                     info.cancel = (node.children && node.children.length > 0) || node.used || node.settle_status === settleStatus.finish;
                     break;
                 case 'sgfh_qty':
@@ -1021,7 +1020,31 @@ $(document).ready(() => {
                     billsTreeSpreadObj.refreshTree(sheet, refreshNode);
                 })
             }
-        }
+        },
+        batchReplace: function(orgInfo, newInfo) {
+            const updateData = [];
+            for (const d of billsTree.nodes) {
+                if (!d.b_code) continue;
+                if (d.used || d.settle_status === settleStatus.finish) continue;
+                const checkInfo = { code: d.b_code || '', name: d.name || '', unit: d.unit || '', unit_price: d.unit_price || 0 };
+                if (_.isMatch(checkInfo, orgInfo)) {
+                    const data = billsTree.getNodeKeyData(d);
+                    data.b_code = newInfo.code;
+                    data.name = newInfo.name;
+                    data.unit = newInfo.unit;
+                    data.unit_price = newInfo.unit_price;
+                    updateData.push(data);
+                }
+            }
+            if (updateData.length === 0) {
+                toastr.warning('没有可以替换的数据');
+                return;
+            }
+            postData(window.location.pathname + '/update', {postType: 'update', postData: updateData}, function (result) {
+                const refreshNode = billsTree.loadPostData(result);
+                billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+            });
+        },
     };
     billsTreeSpreadObj.refreshOperationValid(billsSheet);
     billsTreeSpreadObj.loadExprToInput(billsSheet);
@@ -1247,6 +1270,23 @@ $(document).ready(() => {
                 $('#batch').modal('show');
             }
         };
+        billsContextMenuOptions.items.batchReplace = {
+            name: '批量替换清单',
+            icon: 'fa-sign-in',
+            callback: function(key, opt) {
+                const node = SpreadJsObj.getSelectObject(billsSheet);
+                if (!node) return;
+
+                BatchReplace.load(node.b_code, node.name, node.unit, node.unit_price, billsTreeSpreadObj.batchReplace);
+            },
+            disabled: function (key, opt) {
+                const node = SpreadJsObj.getSelectObject(billsSheet);
+                return !node || !node.b_code;
+            },
+            visible: function (key, opt) {
+                return !readOnly;
+            }
+        };
         billsContextMenuOptions.items.sprBatch = '----';
     }
     billsContextMenuOptions.items.copyBlock = {
@@ -2704,19 +2744,13 @@ $(document).ready(() => {
     billsSpread.refresh();
     if (posSpread) posSpread.refresh();
 
-    const stdLibCellDoubleClick = function (e, info) {
-        const stdSheet = info.sheet;
-        const mainSheet = billsSheet;
-        if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
-
-        const stdTree = stdSheet.zh_tree;
-        const stdNode = stdTree.nodes[info.row];
-        const mainTree = mainSheet.zh_tree;
-        const sel = mainSheet.getSelections()[0];
-        const mainNode = mainTree.nodes[sel.row];
+    const stdLibCellDoubleClick = function (updateData, stdNode, stdTree) {
         if (!stdNode) return;
+        const mainTree = billsTree;
+        const mainNode = SpreadJsObj.getSelectObject(billsSheet);
+        if (!mainNode) return;
 
-        if (info.sheet.zh_setting.stdType === 'gcl') {
+        if (updateData.postData.stdType === 'gcl') {
             if (mainNode.code && mainNode.code !== '' && !mainTree.isLeafXmj(mainNode)) {
                 toastr.warning('非最底层项目下,不应添加节点');
                 return;
@@ -2736,31 +2770,25 @@ $(document).ready(() => {
             }
         }
 
-        postData(window.location.pathname + '/update', {
-            postType: 'add-std',
-            postData: {
-                id: mainTree.getNodeKey(mainNode),
-                tender_id: mainNode.tender_id,
-                stdType: info.sheet.zh_setting.stdType,
-                stdLibId: stdNode.list_id,
-                stdNode: stdTree.getNodeKey(stdNode)
-            }
-        }, function (result) {
+        updateData.postData.id = billsTree.getNodeKey(mainNode);
+        updateData.postData.tender_id = mainNode.tender_id;
+        postData(window.location.pathname + '/update', updateData, function (result) {
             const refreshNode = mainTree.loadPostData(result);
-            billsTreeSpreadObj.refreshTree(mainSheet, refreshNode);
+            billsTreeSpreadObj.refreshTree(billsSheet, refreshNode);
+            const sel = billsSheet.getSelections()[0];
             if (sel) {
                 if (refreshNode.create && refreshNode.create.length > 0) {
-                    mainSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
-                    SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, refreshNode.create[refreshNode.create.length - 1].index]);
+                    billsSheet.setSelection(refreshNode.create[refreshNode.create.length - 1].index, sel.col, sel.rowCount, sel.colCount);
+                    SpreadJsObj.reloadRowsBackColor(billsSheet, [sel.row, refreshNode.create[refreshNode.create.length - 1].index]);
                 } else {
                     const node = _.find(mainTree.nodes, {code: stdNode.code, name: stdNode.name});
                     if (node) {
-                        mainSheet.setSelection(mainTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
-                        SpreadJsObj.reloadRowsBackColor(mainSheet, [sel.row, mainTree.nodes.indexOf(node)]);
+                        billsSheet.setSelection(mainTree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
+                        SpreadJsObj.reloadRowsBackColor(billsSheet, [sel.row, mainTree.nodes.indexOf(node)]);
                     }
                 }
             }
-            billsTreeSpreadObj.refreshOperationValid(mainSheet);
+            billsTreeSpreadObj.refreshOperationValid(billsSheet);
             billsSpread.focus();
             posSpreadObj.loadCurPosData();
         });

+ 36 - 1
app/public/js/shares/cs_tools.js

@@ -1312,7 +1312,9 @@ const showSelectTab = function(select, spread, afterShow) {
             searchPre: `std-${setting.stdType}-sp`,
             searchNext: `std-${setting.stdType}-sn`,
             searchClose: `std-${setting.stdType}-sc`,
+            addParent: `std-${setting.stdType}-wp`
         };
+        const hiddenOption = setting.showAddType ? '' : 'style="display: none;"';
         obj.html(
             '<div class="sjs-bar d-flex">\n' +
             '    <div class="dropdown mr-1">\n' +
@@ -1351,6 +1353,21 @@ const showSelectTab = function(select, spread, afterShow) {
             '            </div>\n' +
             '        </div>\n' +
             '    </div>' +
+            `    <div class="ml-1" ${hiddenOption}>\n` +
+            '        <div class="dropdown">\n' +
+            '            <button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">\n' +
+            '                <i class="fa fa-cog"></i>\n' +
+            '            </button>\n' +
+            '            <div class="dropdown-menu dropdown-menu-right">\n' +
+            '                <div class="px-2 border-primary border-1 d-flex" style="width: 100px">\n' +
+            '                     <div class="form-check form-check-inline">\n' +
+            `                          <input class="form-check-input pt-1" type="checkbox" id="${relaSelect.addParent}">\n` +
+            `                          <label class="form-check-label" for="${relaSelect.addParent}">添加父项</label>\n` +
+            '                     </div>' +
+            '                </div>\n' +
+            '            </div>\n' +
+            '        </div>\n' +
+            '    </div>' +
             '</div>\n' +
             `<div id="std-${setting.stdType}-spread" class="sjs-sh"></div>\n`
         );
@@ -1383,7 +1400,25 @@ const showSelectTab = function(select, spread, afterShow) {
         if (setting.spreadSetting) setting.spreadSetting.stdType = setting.stdType;
         SpreadJsObj.initSheet(sheet, setting.spreadSetting || spreadSetting);
 
-        if (setting.cellDoubleClick) sheet.bind(spreadNS.Events.CellDoubleClick, setting.cellDoubleClick);
+        if (setting.cellDoubleClick) sheet.bind(spreadNS.Events.CellDoubleClick, function(e, info) {
+            const stdSheet = info.sheet;
+            if (!stdSheet.zh_setting || !stdSheet.zh_tree) return;
+
+            const stdTree = stdSheet.zh_tree;
+            const stdNode = stdTree.nodes[info.row];
+            if (!stdNode) { return; }
+
+            const updateData = { postType: 'add-std', postData: { stdType: stdSheet.zh_setting.stdType, withParent: $(`#${relaSelect.addParent}`)[0].checked } };
+            const fullPath = stdTree.getAllParents(stdNode);
+            fullPath.push(stdNode);
+            fullPath.sort((x, y) => { return x.level - y.level; });
+            updateData.postData.stdData = fullPath.map(x => {
+                return { code: x.code || '', b_code: x.b_code || '', name: x.name, unit: x.unit, source: x.source, node_type: x.node_type }
+            });
+            updateData.postData.stdNode = stdNode;
+
+            setting.cellDoubleClick(updateData, stdNode, stdTree);
+        });
         sheet.bind(spreadNS.Events.SelectionChanged, function (e, info) {
             if (!info.oldSelections || !info.oldSelections[0] || info.newSelections[0].row !== info.oldSelections[0].row) {
                 SpreadJsObj.saveTopAndSelect(info.sheet, cacheKey.node);

+ 21 - 0
app/public/js/spreadjs_rela/spreadjs_zh.js

@@ -3219,3 +3219,24 @@ const SpreadJsObj = {
     })(),
 };
 
+
+const GridSpreadUtils = {
+    getDefaultSelectInfo: function (sheet, isMulti = true) {
+        const gridData = sheet.zh_data;
+        if (!gridData) return[gridData, null, -1, 0];
+
+        const sel = sheet.getSelections()[0];
+        const node = gridData[sel.row];
+        if (!node) return [gridData, node, sel.row, 0];
+
+        if (!isMulti || sel.rowCount === 1) return [gridData, node, sel.row, 1];
+
+        let count = 1;
+        for (let r = 1; r < sel.rowCount; r++) {
+            const rNode = gridData[sel.row + r];
+            if (!rNode) break;
+            count += 1;
+        }
+        return [gridData, node, sel.row, count];
+    },
+};

+ 15 - 2
app/public/js/stage.js

@@ -1422,7 +1422,7 @@ $(document).ready(() => {
                 $('#calc-by-ratio').modal('hide');
             });
         },
-        exportExcel: function (filename, nodes) {
+        exportExcel: function (filename, nodes, withPos = true) {
             const exportNodesData = function (data, nodes) {
                 for (const node of nodes) {
                     data.push({
@@ -1438,10 +1438,12 @@ $(document).ready(() => {
                         c_dgn_qty1: node.c_dgn_qty1, c_dgn_qty2: node.c_dgn_qty2,
                         final_dgn_price: node.final_dgn_price,
                         postil: node.postil, drawing_code: node.drawing_code, memo: node.memo,
+                        contract_expr_str: node.calc_expr ? TreeExprCalc.expr2ExprStr(node.calc_expr) : '',
                     });
                     if (node.children && node.children.length > 0) {
                         exportNodesData(data, node.children);
                     } else {
+                        if (!withPos) continue;
                         const posRange = stagePos.getLedgerPos(node.id);
                         if (posRange && posRange.length > 0) {
                             for (const [i, p] of posRange.entries()) {
@@ -1802,6 +1804,17 @@ $(document).ready(() => {
                     return !node;
                 }
             },
+            exportContractExprXlsx: {
+                name: '导出本期合同计量公式',
+                callback: function(key, opt, menu, e) {
+                    exportExcelSetting.cols.push({title: '本期合同计量计算式', colSpan: '1', rowSpan: '2', field: 'contract_expr_str', hAlign: 0, width: 140, formatter: '@'});
+                    stageTreeSpreadObj.exportExcel($('.sidebar-title').attr('data-original-title') + '本期合同计量公式.xlsx', null, false);
+                    exportExcelSetting.cols.splice(exportExcelSetting.cols.length - 1, 1);
+                },
+                visible: function(key, opt) {
+                    return contractExpr;
+                }
+            },
             importSpr: '---',
             importStageGcl: {
                 name: '导入(其他标段)工程量清单计量数据',
@@ -2341,7 +2354,7 @@ $(document).ready(() => {
             exprCacheKey: ['bqhtje'],
         });
         // 加载中间计量
-        stageIm.init(stage, imType, tenderInfo.decimal, stage.assist ? stage.assist.ass_ledger_id : '');
+        stageIm.init(tender, stage, imType, tenderInfo.decimal, stage.assist ? stage.assist.ass_ledger_id : '');
         stageIm.loadData(result.ledgerData, result.posData, result.detailData, result.changeData, result.import_change, result.detailAtt);
 
         errorList.loadHisErrorData();

+ 12 - 3
app/public/js/stage_im.js

@@ -13,7 +13,11 @@ const stageIm = (function () {
     const resetFields = ['peg', 'bw', 'xm', 'drawing_code', 'calc_memo', 'position', 'jldy'];
     const splitChar = '-';
     const mergeChar = ';';
-    let stage, imType, decimal, filter, details, changes, importChanges, detailsAtt, ImData, pre, orgImData;
+    const SortType = {
+        GCL: 'gcl',
+        TZ: 'tz'
+    };
+    let tender, stage, imType, decimal, filter, details, changes, importChanges, detailsAtt, ImData, pre, orgImData;
     let up_field = 'unit_price';
     const gsTreeSetting = {
         id: 'ledger_id',
@@ -55,7 +59,8 @@ const stageIm = (function () {
     };
     const gsPos = new StagePosData(gsPosSetting);
 
-    function init (s, i, d, f) {
+    function init (t, s, i, d, f) {
+        tender = t;
         stage = s;
         imType = i;
         decimal = d;
@@ -951,7 +956,10 @@ const stageIm = (function () {
     }
 
     function _sortImData() {
-        if (stage.im_type !== imType.tz.value && stage.im_type !== imType.bb.value) {
+        const st = (['fj', 'sz'].indexOf(tender.s_type) > 0 && [imType.zl.value, imType.bw.value].indexOf(stage.im_type) >= 0)
+            ? SortType.TZ
+            : (stage.im_type !== imType.tz.value && stage.im_type !== imType.bb.value ? SortType.TZ : SortType.GCL);
+        if (st === SortType.GCL) {
             ImData.sort(function (x, y) {
                 const iCode = compareCode(x.code, y.code);
                 return iCode === 0 ? x.lIndex - y.lIndex : iCode;
@@ -1233,5 +1241,6 @@ const stageIm = (function () {
         getRelaImData: getRelaImData,
         getRelaImData4Rela: getRelaImData4Rela,
         resetFields,
+        SortType,
     }
 })();

+ 0 - 4
app/public/js/tender_list_progress.js

@@ -54,10 +54,6 @@ const tenderListSpec = (function(){
         // 当前流程
         html.push('<td style="width: 8%">');
         if (!node.cid && node.cur_flow) {
-            if (node.id === 4811) console.log(node.cur_flow);
-            if (node.id === 3866) {
-                console.log(node);
-            }
             const curUser = node.cur_flow instanceof Array && node.cur_flow[0].audit_type && node.cur_flow[0].audit_type !== auditType.key.common
                 ? transFormToChinese(node.cur_flow[0].audit_order) + '审'
                 : node.cur_flow instanceof Array ? (node.cur_flow[0].name + (node.cur_flow[0].role ? '-'+node.cur_flow[0].role  : '')): (node.cur_flow.name + (node.cur_flow.role ? '-'+node.cur_flow.role  : ''));

+ 1 - 0
app/router.js

@@ -234,6 +234,7 @@ module.exports = app => {
     app.post('/tender/:id/ledger/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.update');
     app.post('/tender/:id/ledger/upload-excel/:ueType', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.uploadExcel');
     app.get('/tender/:id/ledger/download/:file', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.download');
+    app.post('/tender/:id/anc-gcl/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.ancGclUpdate');
     app.post('/tender/:id/pos/update', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.posUpdate');
     app.post('/tender/:id/pos/paste', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.posPaste');
     app.post('/tender/:id/ledger/deal2sgfh', sessionAuth, tenderCheck, uncheckTenderCheck, 'ledgerController.deal2sgfh');

+ 148 - 0
app/service/ancillary_gcl.js

@@ -0,0 +1,148 @@
+'use strict';
+
+/**
+ * 附属工程量(ancillary gcl)
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+module.exports = app => {
+
+    class AncillaryGcl extends app.BaseService {
+        /**
+         * 构造函数
+         *
+         * @param {Object} ctx - egg全局变量
+         * @return {void}
+         */
+        constructor(ctx) {
+            super(ctx);
+            this.tableName = 'ancillary_gcl';
+        }
+
+        async _addDatas(data) {
+            const qtyDecimal = this.ctx.tender.info.decimal.qty;
+            const user_id = this.ctx.session.sessionUser.accountId;
+
+            const datas = data instanceof Array ? data : [data];
+            const insertData = [];
+            for (const d of datas) {
+                if (!d.lid || !d.g_order) throw '新增其他数据,提交的数据错误';
+                const nd = {
+                    id: this.uuid.v4(), tid: this.ctx.tender.id,
+                    add_user_id: user_id, update_user_id: user_id,
+                    lid: d.lid, g_order: d.g_order,
+                };
+                if (d.is_aux !== undefined) nd.is_aux = d.is_aux;
+                if (d.name) nd.name = d.name || '';
+                if (d.unit) nd.unit = d.unit || '';
+                if (d.quantity) nd.quantity = this.ctx.helper.round(d.quantity || 0, qtyDecimal);
+                if (d.expr) nd.expr = d.expr || '';
+                if (d.drawing_code !== undefined) nd.drawing_code = d.drawing_code;
+                if (d.memo) nd.memo = d.memo || '';
+                insertData.push(nd);
+            }
+            await this.db.insert(this.tableName, insertData);
+            return await this.getAllDataByCondition({
+                where: { id: this.ctx.helper._.map(insertData, 'id') }
+            });
+        }
+
+        async _delDatas (data) {
+            if (!data || data.length === 0) throw '提交数据错误';
+            const orgDatas = await this.getAllDataByCondition({ where: { id: data } });
+
+            if (!orgDatas || orgDatas.length === 0) throw '删除的辅助工程量不存在';
+
+            const bills = await this.ctx.service.ledger.getDataById(orgDatas[0].lid);
+            let billsAnc = await this.getAllDataByCondition({ where: { tid: bills.tid, lid: bills.id } });
+
+            billsAnc = billsAnc.filter(ba => {
+                return data.indexOf(ba.id) < 0;
+            });
+            billsAnc.sort((x, y) => { return x.g_order - y.g_order; });
+
+            const updateData = [];
+            billsAnc.forEach((x, i) => {
+                if (x.g_order !== i + 1) updateData.push({ id: x.id, g_order: i + 1});
+            });
+
+            const conn = await this.db.beginTransaction();
+            try {
+                await conn.delete(this.tableName, { id: data });
+                if (updateData.length > 0) await conn.updateRows(this.tableName, updateData);
+                await conn.commit();
+                return [data, updateData];
+            } catch (err) {
+                await conn.rollback();
+                throw err;
+            }
+        }
+
+        async _updateDatas (data) {
+            if (!data || data.length === 0) throw '提交数据错误';
+            const qtyDecimal = this.ctx.tender.info.decimal.qty;
+            const user_id = this.ctx.session.sessionUser.accountId;
+
+            const datas = data instanceof Array ? data : [data];
+            const orgDatas = await this.getAllDataByCondition({
+                where: { id: this.ctx.helper._.map(datas, 'id') }
+            });
+            if (!orgDatas || orgDatas.length === 0) throw '修改的辅助工程量不存在';
+
+            const uDatas = [];
+            for (const d of datas) {
+                const od = orgDatas.find(x => { return x.id === d.id; });
+                if (!od) continue;
+
+                const nd = { id: od.id, update_user_id: user_id };
+                if (d.is_aux !== undefined) nd.is_aux = d.is_aux;
+                if (d.name !== undefined) nd.name = d.name;
+                if (d.g_order !== undefined) nd.g_order = d.g_order;
+                if (d.unit !== undefined) nd.unit = d.unit;
+                if (d.quantity !== undefined) nd.quantity = this.ctx.helper.round(d.quantity, qtyDecimal);
+                if (d.expr !== undefined) nd.expr = d.expr || '';
+                if (d.drawing_code !== undefined) nd.drawing_code = d.drawing_code;
+                if (d.memo !== undefined) nd.memo = d.memo || '';
+                uDatas.push(nd);
+            }
+            if (uDatas.length > 0) {
+                await this.db.updateRows(this.tableName, uDatas);
+                return uDatas;
+            } else {
+                return [];
+            }
+        }
+
+        async updateDatas(data) {
+            const result = {add: [], del: [], update: []};
+            try {
+                if (data.add) {
+                    result.add = await this._addDatas(data.add);
+                }
+                if (data.update) {
+                    result.update = await this._updateDatas(data.update);
+                }
+                if (data.del) {
+                    [result.del, result.update] = await this._delDatas(data.del);
+                }
+                return result;
+            } catch (err) {
+                if (err.stack) {
+                    throw err;
+                } else {
+                    result.err = err.toString();
+                    return result;
+                }
+            }
+        }
+
+        async deletePartData(transaction, tid, lid) {
+            await transaction.delete(this.tableName, { tid: tid, lid: lid });
+        }
+    }
+
+    return AncillaryGcl;
+};

+ 2 - 1
app/service/ledger.js

@@ -52,7 +52,7 @@ module.exports = app => {
                 fullPath: 'full_path',
                 keyPre: 'ledger_bills_maxLid:',
                 uuid: true,
-            }, 'pos');
+            }, 'pos', 'ancillaryGcl');
             this.depart = ctx.app.config.table_depart.heavy;
             this.tableName = 'ledger';
         }
@@ -283,6 +283,7 @@ module.exports = app => {
          */
         async _deleteRelaData(mid, deleteData) {
             await this.ctx.service.pos.deletePosData(this.transaction, mid, this._.map(deleteData, 'id'));
+            await this.ctx.service.ancillaryGcl.deletePartData(this.transaction, mid, this._.map(deleteData, 'id'));
         }
 
         _checkField(data, field) {

+ 27 - 26
app/view/datacollect/index4GY18Y.ejs

@@ -296,12 +296,12 @@
                     interval: 0,
                     textStyle: {
                         color: "#fff",
-                        fontSize: 14
+                        fontSize: 12
                     },
                     formatter: function(value) {
                         var res = value;
-                        if(res.length > 6) {
-                            res = res.substring(0, 5) + "..";
+                        if(res.length > 10) {
+                            res = res.substring(0, 10) + "..";
                         }
                         return res;
                     }
@@ -423,8 +423,8 @@
                 interval: 0,
                 formatter: function(value) {
                     var res = value;
-                    if(res.length > 6) {
-                        res = res.substring(0, 5) + "..";
+                    if(res.length > 10) {
+                        res = res.substring(0, 10) + "..";
                     }
                     return res;
                 }
@@ -730,12 +730,12 @@
                     interval: 0,
                     textStyle: {
                         color: "#fff",
-                        fontSize: 14
+                        fontSize: 12
                     },
                     formatter: function(value) {
                         var res = value;
-                        if(res.length > 6) {
-                            res = res.substring(0, 5) + "..";
+                        if(res.length > 10) {
+                            res = res.substring(0, 10) + "..";
                         }
                         return res;
                     }
@@ -858,6 +858,7 @@
             for (const t of tenders) {
                 calculateTender(t);
             }
+            console.log(tenders);
             setData(tenders);
         });
 
@@ -888,7 +889,7 @@
                 return _.findIndex(item.category, {cid: daping06Set.cb, value: daping06Set.cb_value}) !== -1;
             });
             const sr_chart_option_data = {
-                end_contract_tp: 0,
+                contract_price: 0,
                 end_gather_tp: 0,
                 end_yf_tp: 0,
                 end_sf_tp: 0,
@@ -896,20 +897,20 @@
             };
             const srmx_chart_option_name = [];
             const srmx_chart_option_data = {
-                end_contract_tp: [],
+                end_gather_tp: [],
                 end_yf_tp:[],
                 end_sf_tp:[],
             };
             const yingsf_option_data = [];
             const yisf_option_data = [];
             for (const sr of sr_tenders) {
-                sr_chart_option_data.end_contract_tp = ZhCalc.add(sr_chart_option_data.end_contract_tp, sr.end_contract_tp);
+                sr_chart_option_data.contract_price = ZhCalc.add(sr_chart_option_data.contract_price, sr.contract_price);
                 sr_chart_option_data.end_gather_tp = ZhCalc.add(sr_chart_option_data.end_gather_tp, sr.end_gather_tp);
                 sr_chart_option_data.end_yf_tp = ZhCalc.add(sr_chart_option_data.end_yf_tp, sr.end_yf_tp);
                 sr_chart_option_data.end_sf_tp = ZhCalc.add(sr_chart_option_data.end_sf_tp, sr.end_sf_tp);
 
                 srmx_chart_option_name.push(sr.name);
-                srmx_chart_option_data.end_contract_tp.push(sr.end_contract_tp ? sr.end_contract_tp : 0);
+                srmx_chart_option_data.end_gather_tp.push(sr.end_gather_tp ? sr.end_gather_tp : 0);
                 srmx_chart_option_data.end_yf_tp.push(sr.end_yf_tp ? sr.end_yf_tp : 0);
                 srmx_chart_option_data.end_sf_tp.push(sr.end_sf_tp ? sr.end_sf_tp : 0);
                 for (const s of sr.month_stage) {
@@ -942,7 +943,7 @@
             gcsrmx_chart_option.dataZoom[0].start = 0;
             gcsrmx_chart_option.dataZoom[0].end = computedPosition(sr_tenders.length);
             gcsrmx_chart_option.xAxis[0].data = srmx_chart_option_name;
-            gcsrmx_chart_option.series[0].data = srmx_chart_option_data.end_contract_tp;
+            gcsrmx_chart_option.series[0].data = srmx_chart_option_data.end_gather_tp;
             gcsrmx_chart_option.series[1].data = srmx_chart_option_data.end_yf_tp;
             gcsrmx_chart_option.series[2].data = srmx_chart_option_data.end_sf_tp;
             if (sr_tenders.length >= 8) {
@@ -956,7 +957,7 @@
             const gcsrqk_chart_option = gcsrqkChart.getOption();
             gcsrqk_chart_option.dataZoom[0].start = 0;
             gcsrqk_chart_option.dataZoom[0].end = computedPosition(sr_tenders.length);
-            gcsrqk_chart_option.series[0].data = [sr_chart_option_data.end_ds_tp, sr_chart_option_data.end_sf_tp, sr_chart_option_data.end_yf_tp, sr_chart_option_data.end_gather_tp, sr_chart_option_data.end_contract_tp];
+            gcsrqk_chart_option.series[0].data = [sr_chart_option_data.end_ds_tp, sr_chart_option_data.end_sf_tp, sr_chart_option_data.end_yf_tp, sr_chart_option_data.end_gather_tp, sr_chart_option_data.contract_price];
             if (sr_tenders.length >= 8) {
                 gcsrqk_chart_option.dataZoom[0].show = true;
             } else {
@@ -965,7 +966,7 @@
             gcsrqkChart.setOption(gcsrqk_chart_option);
 
             const cb_chart_option_data = {
-                end_contract_tp: 0,
+                // end_contract_tp: 0,
                 end_gather_tp: 0,
                 end_yf_tp: 0,
                 end_sf_tp: 0,
@@ -974,7 +975,7 @@
             const cb_chart_option_name = [];
             for (const cb of cb_tenders) {
                 cb_chart_option_name.push(cb.name);
-                cb_chart_option_data.end_contract_tp = ZhCalc.add(cb_chart_option_data.end_contract_tp, cb.end_contract_tp);
+                // cb_chart_option_data.end_contract_tp = ZhCalc.add(cb_chart_option_data.end_contract_tp, cb.end_contract_tp);
                 cb_chart_option_data.end_gather_tp = ZhCalc.add(cb_chart_option_data.end_gather_tp, cb.end_gather_tp);
                 cb_chart_option_data.end_yf_tp = ZhCalc.add(cb_chart_option_data.end_yf_tp, cb.end_yf_tp);
                 cb_chart_option_data.end_sf_tp = ZhCalc.add(cb_chart_option_data.end_sf_tp, cb.end_sf_tp);
@@ -1022,18 +1023,18 @@
             if (yingsf_option_data.length > 0) {
                 // chart_option4_data 排序
                 const new_yingsf_option_data = _.sortBy(yingsf_option_data, 'yearmonth');
-                for (const [i,c4] of new_yingsf_option_data.entries()) {
+                for (const c4 of new_yingsf_option_data) {
                     show_yingsf_option_data.yearmonth.push(c4.yearmonth);
-                    show_yingsf_option_data.yf_tp.push(c4.yf_tp);
                     show_yingsf_option_data.ys_tp.push(c4.ys_tp);
+                    show_yingsf_option_data.yf_tp.push(c4.yf_tp);
                 }
             }
             const yingsf_option = yingsfChart.getOption();
             yingsf_option.dataZoom[0].start = 0;
             yingsf_option.dataZoom[0].end = computedPosition(show_yingsf_option_data.yearmonth.length);
             yingsf_option.xAxis[0].data = show_yingsf_option_data.yearmonth;
-            yingsf_option.series[0].data = show_yingsf_option_data.yf_tp;
-            yingsf_option.series[1].data = show_yingsf_option_data.ys_tp;
+            yingsf_option.series[0].data = show_yingsf_option_data.ys_tp;
+            yingsf_option.series[1].data = show_yingsf_option_data.yf_tp;
             if (show_yingsf_option_data.yearmonth.length >= 8) {
                 yingsf_option.dataZoom[0].show = true;
             } else {
@@ -1047,18 +1048,18 @@
             };
             if (yisf_option_data.length > 0) {
                 const new_yisf_option_data = _.sortBy(yisf_option_data, 'yearmonth');
-                for (const [i,c4] of new_yisf_option_data.entries()) {
+                for (const c4 of new_yisf_option_data) {
                     show_yisf_option_data.yearmonth.push(c4.yearmonth);
-                    show_yisf_option_data.yf_tp.push(c4.yf_tp);
                     show_yisf_option_data.ys_tp.push(c4.ys_tp);
+                    show_yisf_option_data.yf_tp.push(c4.yf_tp);
                 }
             }
             const yisf_option = yisfChart.getOption();
             yisf_option.dataZoom[0].start = 0;
             yisf_option.dataZoom[0].end = computedPosition(show_yisf_option_data.yearmonth.length);
             yisf_option.xAxis[0].data = show_yisf_option_data.yearmonth;
-            yisf_option.series[0].data = show_yisf_option_data.yf_tp;
-            yisf_option.series[1].data = show_yisf_option_data.ys_tp;
+            yisf_option.series[0].data = show_yisf_option_data.ys_tp;
+            yisf_option.series[1].data = show_yisf_option_data.yf_tp;
             if (show_yisf_option_data.yearmonth.length >= 8) {
                 yisf_option.dataZoom[0].show = true;
             } else {
@@ -1121,10 +1122,10 @@
                 allgccb.end_gather_tp = ZhCalc.add(allgccb.end_gather_tp, cb.end_gather_tp);
                 allgccb.end_sf_tp = ZhCalc.add(allgccb.end_sf_tp, cb.end_sf_tp);
                 allgccb.end_yf_tp = ZhCalc.add(allgccb.end_yf_tp, cb.end_yf_tp);
-                hzHtml += '<tr class="bg-dark" style="line-height: 2rem;text-align: right;"><td style="text-align: left">' + cb.name + '</td><td>' + cb.end_gather_tp + '</td><td>' + cb.end_sf_tp + '</td><td>' + cb.end_yf_tp + '</td><td>' + cb.end_df_tp + '</td></tr>';
+                hzHtml += '<tr class="bg-dark" style="line-height: 2rem;text-align: center;"><td style="text-align: left">' + cb.name + '</td><td>' + cb.end_gather_tp + '</td><td>' + cb.end_sf_tp + '</td><td>' + cb.end_yf_tp + '</td><td>' + cb.end_df_tp + '</td></tr>';
             }
             allgccb.end_df_tp = ZhCalc.sub(allgccb.end_yf_tp, allgccb.end_sf_tp);
-            hzHtml += '<tr class="bg-dark" style="line-height: 2rem;text-align: right;"><td style="text-align: left">' + allgccb.name + '</td><td>' + allgccb.end_gather_tp + '</td><td>' + allgccb.end_sf_tp + '</td><td>' + allgccb.end_yf_tp + '</td><td>' + allgccb.end_df_tp + '</td></tr>';
+            hzHtml += '<tr class="bg-dark" style="line-height: 2rem;text-align: center;"><td style="text-align: left">' + allgccb.name + '</td><td>' + allgccb.end_gather_tp + '</td><td>' + allgccb.end_sf_tp + '</td><td>' + allgccb.end_yf_tp + '</td><td>' + allgccb.end_df_tp + '</td></tr>';
             $('.stage-data').html(hzHtml);
             setTableboxHeight();
             // console.log(sr_tenders, cb_tenders);

+ 48 - 23
app/view/ledger/explode.ejs

@@ -86,32 +86,57 @@
                 <% if (tender.measure_type === measureType.tz.value) { %>
                 <div class="bcontent-wrap" id="main-bottom">
                     <div id="main-resize" class="resize-y" id="top-spr" r-Type="height" div1=".sjs-height-1" div2=".bcontent-wrap" title="调整大小"><!--调整上下高度条--></div>
-                    <div class="bc-bar mb-1">
-                        <ul class="nav nav-tabs">
-                            <li class="nav-item">
-                                <a class="nav-link active" href="javascript:void(0)">计量单元</a>
-                            </li>
-                            <li class="ml-2 nav-item">
-                                <div class="d-inline-flex">
-                                    <a href="javascript: void(0);" name="pos-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
-                                    <a href="javascript: void(0);" name="pos-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
-                                </div>
-                            </li>
-                            <li class="nav-item" id="pos-search">
-                            </li>
-                            <li class="nav-item">
-                                <div class="d-inline-block">
-                                    <div class="input-group input-group-sm ml-2">
-                                        <div class="input-group-prepend">
-                                            <span class="input-group-text" id="basic-addon1">表达式</span>
+                    <div class="bc-bar mb-1 d-flex">
+                        <div class="d-inline-block">
+                            <ul class="nav nav-tabs">
+                                <li class="nav-item">
+                                    <a class="nav-link active" href="javascript:void(0)">计量单元</a>
+                                </li>
+                                <li class="ml-2 nav-item">
+                                    <div class="d-inline-flex">
+                                        <a href="javascript: void(0);" name="pos-opr" type="down-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="下移"><i class="fa fa-arrow-down" aria-hidden="true"></i></a>
+                                        <a href="javascript: void(0);" name="pos-opr" type="up-move" class="btn btn-sm btn-light text-primary" data-toggle="tooltip" data-placement="bottom" title="" data-original-title="上移"><i class="fa fa-arrow-up" aria-hidden="true"></i></a>
+                                    </div>
+                                </li>
+                                <li class="nav-item" id="pos-search">
+                                </li>
+                                <li class="nav-item">
+                                    <div class="d-inline-block">
+                                        <div class="input-group input-group-sm ml-2">
+                                            <div class="input-group-prepend">
+                                                <span class="input-group-text" id="basic-addon1">表达式</span>
+                                            </div>
+                                            <input type="text" class="form-control form-control-sm m-0" id="pos-expr" readonly="">
                                         </div>
-                                        <input type="text" class="form-control form-control-sm m-0" id="pos-expr" readonly="">
                                     </div>
-                                </div>
-                            </li>
-                        </ul>
+                                </li>
+                            </ul>
+                        </div>
+                        <div class="ml-auto">
+                            <!--<a class="btn btn-sm btn-primary mr-1 mt-1" id="anc-gcl-btn" href="javascript: void(0);">附属工程量</a>-->
+                            <% if (tenderInfo.display.ledger.ancillaryGcl) {%>
+                            <ul class="nav nav-tabs" id="anc-gcl-nav" >
+                                <li class="nav-item">
+                                    <div class="form-check form-check-inline mt-1">
+                                        <input class="form-check-input pt-1" type="checkbox" id="filter-anc-gcl" value="0" name="is_aux">
+                                        <label class="form-check-label" for="filter-anc-gcl">不显示辅材</label>
+                                    </div>
+                                </li>
+                                <li class="nav-item">
+                                    <a class="nav-link" href="javascript:void(0)">附属工程量</a>
+                                </li>
+                            </ul>
+                            <% } %>
+                        </div>
                     </div>
-                    <div class="sp-wrap" id="pos-spread">
+                    <div class="sp-wrap" style="display: flex; flex-wrap: wrap;">
+                        <div class="c-body" id="pos-spread" style="width: 100%">
+                        </div>
+                        <div class="c-body" id="pos-right" style="display: none; width: 40%;">
+                            <div class="resize-x" id="pos-right-spr" r-Type="width" div1="#pos-spread" div2="#pos-right" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
+                            <div class="sp-wrap" id="anc-gcl-spread">
+                            </div>
+                        </div>
                     </div>
                 </div>
                 <% } %>

+ 1 - 0
app/view/ledger/explode_modal.ejs

@@ -518,3 +518,4 @@
 <% include ../shares/db2full_code.ejs %>
 <% include ./audit_modal.ejs %>
 <% include ../shares/hint_modal.ejs %>
+<% include ../shares/batch_replace_modal.ejs %>

+ 31 - 1
app/view/ledger/gather.ejs

@@ -32,6 +32,9 @@
                             <li class="nav-item">
                                 <a class="nav-link" data-toggle="tab" href="#xmjGather" role="tab">项目节汇总</a>
                             </li>
+                            <li class="nav-item">
+                                <a class="nav-link" data-toggle="tab" href="#ancGclGather" role="tab">附属工程量汇总</a>
+                            </li>
                             <li class="nav-item" id="optionTab">
                                 <div rela-tab="#xmjGather" style="display: none;">
                                     <div class="d-inline-block ml-2 mt-1">
@@ -60,6 +63,29 @@
                                         </div>
                                     </div>
                                 </div>
+                                <div rela-tab="#ancGclGather" style="display: none;">
+                                    <div class="d-inline-block ml-2 mt-1">
+                                        <span>汇总条件:</span>
+                                        <div class="d-inline-block" style="vertical-align: middle">
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input pt-1" type="checkbox" id="gather-anc-gcl-dwgc" value="dwgc" name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-dwgc">单位工程</label>
+                                            </div>
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input" type="checkbox" id="gather-anc-gcl-fbgc" value="fbgc" name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-fbgc">分部工程</label>
+                                            </div>
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input" type="checkbox" id="gather-anc-gcl-fxgc" value="fxgc" name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-fxgc">分项工程</label>
+                                            </div>
+                                            <div class="form-check form-check-inline">
+                                                <input class="form-check-input" type="checkbox" id="gather-anc-gcl-xm" value="jldy" checked name="gather-anc-gcl">
+                                                <label class="form-check-label" for="gather-anc-gcl-xm">细目</label>
+                                            </div>
+                                        </div>
+                                    </div>
+                                </div>
                             </li>
                         </ul>
                     </div>
@@ -68,10 +94,14 @@
                             <div class="sp-wrap" id="leaf-xmj-spread">
                             </div>
                         </div>
-                        <div class="tab-pane active" id="xmjGather">
+                        <div class="tab-pane" id="xmjGather">
                             <div class="sp-wrap" id="leaf-xmj-gather-spread">
                             </div>
                         </div>
+                        <div class="tab-pane" id="ancGclGather">
+                            <div class="sp-wrap" id="anc-gcl-gather-spread">
+                            </div>
+                        </div>
                     </div>
                 </div>
             </div>

+ 2 - 1
app/view/revise/info_modal.ejs

@@ -740,13 +740,14 @@
             </div>
         </div>
     </div>
+<% include ../shares/batch_replace_modal.ejs %>
     <% include ../shares/merge_peg_modal.ejs %>
     <% include ../shares/import_excel_modal.ejs %>
     <% include ../shares/delete_hint_modal.ejs %>
     <% include ../shares/check_data_modal.ejs %>
     <% include ../shares/check_modal2.ejs %>
 <% include ../shares/new_tag_modal.ejs %>
-    <% include ../shares/tender_select_modal.ejs %>
+    <% include ../shares/tender_select_modal.ejs %>';
     <% if(ctx.session.sessionUser.accountId === revise.uid && (revise.status === auditConst.status.uncheck || revise.status === auditConst.status.checkNo)) { %>
     <script>
         const cur_uid = '<%- ctx.session.sessionUser.accountId %>';

+ 14 - 1
app/view/setting/datacollect_modal.ejs

@@ -160,7 +160,7 @@
                             </select>
                           </span>
                     </li>
-                    <li class="d-flex justify-content-start align-items-center mb-3">
+                    <li class="d-flex justify-content-start align-items-center mb-1">
                         <span class="col-auto">工程成本费用分类(最多只能选择4个,界面只有4个):</span>
                         <span class="mr-2">
                             <div class="dropdown show">
@@ -181,6 +181,9 @@
                             </div>
                         </span>
                     </li>
+                    <li class="d-flex justify-content-start align-items-center mb-3">
+                        <span class="col-auto" id="show_xmgl"></span>
+                    </li>
                 </ul>
             </div>
             <div class="modal-footer">
@@ -217,11 +220,14 @@
                 $('#cb_cate_value_select').html('<option value="0">请选择值</option>')
             }
             $('#cb_show_select').find('input').prop('checked', false);
+            const show_xmgl_array = [];
             $('#cb_show_select').find('input').each(function () {
                 if (daPing06Set.cb_show && daPing06Set.cb_show.length > 0 && daPing06Set.cb_show.includes(parseInt($(this).val()))) {
                     $(this).prop('checked', true);
+                    show_xmgl_array.push($(this).next().text());
                 }
             });
+            $('#show_xmgl').text(show_xmgl_array.join('、'));
         });
         $('#cb_show_select').on('click', function (e) {
             e.stopPropagation();
@@ -233,6 +239,13 @@
                 toastr.warning('最多只能选择4个');
                 return;
             }
+            const show_xmgl_array = [];
+            $('#cb_show_select').find('input').each(function () {
+                if ($(this).is(':checked')) {
+                    show_xmgl_array.push($(this).next().text());
+                }
+            });
+            $('#show_xmgl').text(show_xmgl_array.join('、'));
         });
         $('#dp06_save_btn').click(function () {
             if ($('#sr_cate_select').val() === '0') {

+ 60 - 0
app/view/shares/batch_replace_modal.ejs

@@ -0,0 +1,60 @@
+<div class="modal fade" id="batch-replace" 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-bordered">
+                    <tr class="text-center"><th width="25%">编号</th><th width="40%">名称</th><th width="15%">单位</th><th width="20%">单价</th></tr>
+                    <tr><td id="br-org-code"></td><td id="br-org-name"></td><td id="br-org-unit"></td><td id="br-org-up">单价</td></tr>
+                </table>
+
+                替换为:
+                <table class="table table-bordered">
+                    <tr class="text-center"><th width="25%">编号</th><th width="40%">名称</th><th width="15%">单位</th><th width="20%">单价</th></tr>
+                    <tr>
+                        <td><input id="br-new-code" type="text" class="form-control form-control-sm"></td>
+                        <td><input id="br-new-name" type="text" class="form-control form-control-sm"></td>
+                        <td><input id="br-new-unit" type="text" class="form-control form-control-sm"></td>
+                        <td><input id="br-new-up" type="text" class="form-control form-control-sm"></td></tr>
+                </table>
+            </div>
+            <div class="modal-footer">
+                <button type="button" class="btn btn-sm btn-secondary" data-dismiss="modal">关闭</button>
+                <a href="javascript: void(0);" class="btn btn-sm btn-primary" id="batch-replace-ok">确定</a>
+            </div>
+        </div>
+    </div>
+</div>
+<script>
+    const BatchReplace = (function() {
+        const orgValue = { code: '', name: '', unit: '', unit_price: 0 };
+        let doReplace;
+        const load = function(code, name, unit, unit_price, callback) {
+            orgValue.code = code || '';
+            orgValue.name = name || '';
+            orgValue.unit = unit || '';
+            orgValue.unit_price = unit_price || 0;
+            $('#br-org-code').html(orgValue.code);
+            $('#br-org-name').html(orgValue.name);
+            $('#br-org-unit').html(orgValue.unit);
+            $('#br-org-up').html(orgValue.unit_price || '');
+            $('#br-new-code').val(orgValue.code);
+            $('#br-new-name').val(orgValue.name);
+            $('#br-new-unit').val(orgValue.unit);
+            $('#br-new-up').val(orgValue.unit_price || '');
+            doReplace = callback;
+            $('#batch-replace').modal('show');
+        }
+        $('#batch-replace-ok').click(function() {
+            if (doReplace) {
+                const newValue = { code: $('#br-new-code').val(), name: $('#br-new-name').val(), unit: $('#br-new-unit').val(), unit_price: parseFloat($('#br-new-up').val()), };
+                doReplace(orgValue, newValue);
+            }
+            $('#batch-replace').modal('hide');
+        });
+        return { load };
+    })();
+</script>

Datei-Diff unterdrückt, da er zu groß ist
+ 1 - 1
app/view/tender/detail.ejs


+ 6 - 1
app/view/tender/detail_modal.ejs

@@ -824,6 +824,10 @@
                         <label class="custom-control-label" for="ledger-cl-qty">错漏增减</label>
                     </div>
                     <div class="custom-control custom-checkbox mb-2">
+                        <input type="checkbox" class="custom-control-input" id="ancillary-gcl" checked="">
+                        <label class="custom-control-label" for="ancillary-gcl">附属工程量</label>
+                    </div>
+                    <div class="custom-control custom-checkbox mb-2">
                         <input type="checkbox" class="custom-control-input" id="thousandth" checked="">
                         <label class="custom-control-label" for="thousandth">千分位</label>
                     </div>
@@ -1649,6 +1653,7 @@
         $('#ledger-deal')[0].checked = property.display.ledger.deal;
         $('#ledger-dgn-qty')[0].checked = property.display.ledger.dgnQty;
         $('#ledger-cl-qty')[0].checked = property.display.ledger.clQty;
+        $('#ancillary-gcl')[0].checked = property.display.ledger.ancillaryGcl;
         $('#thousandth')[0].checked = property.display.thousandth;
         $('#stage-rc')[0].checked = property.display.stage.realComplete;
         $('#stage-priceDiff')[0].checked = property.display.stage.priceDiff;
@@ -1661,7 +1666,7 @@
     function post5 () {
         const prop = {
             display: {
-                ledger: { deal: $('#ledger-deal')[0].checked, dgnQty: $('#ledger-dgn-qty')[0].checked, clQty: $('#ledger-cl-qty')[0].checked, },
+                ledger: { deal: $('#ledger-deal')[0].checked, dgnQty: $('#ledger-dgn-qty')[0].checked, clQty: $('#ledger-cl-qty')[0].checked, ancillaryGcl: $('#ancillary-gcl')[0].checked, },
                 thousandth: $('#thousandth')[0].checked,
                 stage: { realComplete: $('#stage-rc')[0].checked, correct: $('#stage-correct')[0].checked, priceDiff: $('#stage-priceDiff')[0].checked },
                 dayMode: $('#dayMode')[0].checked,

+ 8 - 2
app/view/tender/modal.ejs

@@ -247,13 +247,15 @@
                         historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-level-up"></i></div>');
                     } else if (group.status === auditConst2.status.checking) {
                         historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-ellipsis-h"></i></div>');
-                    } else {
+                    } else if(group.status === auditConst2.status.checkAgain) {
+                        historyHTML.push('<div class="timeline-item-icon bg-warning text-light"><i class="fa fa-check"></i></div>');
+                    }  else {
                         historyHTML.push('<div class="timeline-item-icon bg-secondary text-light"></div>');
                     }
 
                     historyHTML.push('<div class="timeline-item-content">');
                     const statuStr = group.status !== auditConst2.status.uncheck ?
-                        `<span class="pull-right ${auditConst2.statusClass[group.status]}">${auditConst2.statusString[group.status]}</span>` : '';
+                        `<span class="pull-right ${auditConst2.auditStringClass[group.status]}">${auditConst2.statusString[group.status]}</span>` : '';
                     historyHTML.push(`<div class="py-1">
                         <span class="text-black-50">
                         ${ !group.is_final ? group.audit_order + '' : '终' }审 ${getAuditTypeText(group.audit_type)}
@@ -269,6 +271,10 @@
                             historyHTML.push('<span class="pull-right text-success"><i class="fa fa-check-circle"></i></span>');
                         } if (auditor.status === auditConst2.status.checkNo || auditor.status === auditConst2.status.checkNoPre || auditor.status === auditConst2.status.checkCancel) {
                             historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-share-square fa-rotate-270"></i></span>');
+                        } else if (auditor.status === auditConst2.status.checking) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-commenting"></i></span>');
+                        }  else if (auditor.status === auditConst2.status.checkAgain) {
+                            historyHTML.push('<span class="pull-right text-warning"><i class="fa fa-check-circle"></i></span>');
                         }
                         historyHTML.push('</div>');
                         if (auditor.opinion) {