Browse Source

1. 台账修订,统一update接口
2. 从工程量清单、项目节、签约清单,添加数据

MaiXinRong 5 years ago
parent
commit
bd43e7838d

+ 2 - 0
app.js

@@ -17,6 +17,7 @@ const crypto = require('crypto');
 
 const BaseService = require('./app/base/base_service');
 const BaseTreeService = require('./app/base/base_tree_service');
+const BaseBillsService = require('./app/base/base_bills_service');
 const BaseController = require('./app/base/base_controller');
 
 const menu = require('./config/menu');
@@ -31,6 +32,7 @@ module.exports = app => {
     // 数据模型基类
     app.BaseService = BaseService;
     app.BaseTreeService = BaseTreeService;
+    app.BaseBillsService = BaseBillsService;
 
     // 控制器基类
     app.BaseController = BaseController;

+ 271 - 0
app/base/base_bills_service.js

@@ -0,0 +1,271 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date
+ * @version
+ */
+
+const TreeService = require('./base_tree_service');
+// sql拼装器
+const rootId = -1;
+
+class BaseBillsSerivce extends TreeService {
+
+    /**
+     * 从标准数据中提取有效数据
+     * @param {Object} stdData - 从标准库中查询所得
+     * @returns {name, unit, source, code, b_code}
+     * @private
+     */
+    _filterStdData(stdData) {
+        const result = {
+            name: stdData.name,
+            unit: stdData.unit,
+            source: stdData.source,
+            node_type: stdData.node_type,
+        };
+        result.code = stdData.code ? stdData.code : '';
+        result.b_code = stdData.b_code ? stdData.b_code : '';
+        return result;
+    }
+
+    async addBillsNode(tenderId, selectId, data, reviseId) {
+        if (data) {
+            if (reviseId) data.crid = reviseId;
+            return await this.addNode(tenderId, selectId, data);
+        } else {
+            return await this.addNode(tenderId, selectId, {crid: reviseId});
+        }
+    }
+
+    /**
+     * 新增子节点,并排在所有子节点的末尾
+     * @param {Number} tenderId - 标段Id
+     * @param {Number} selectId - 父节点Id
+     * @param {Object} data - 新增节点数据(编号名称等)
+     * @returns {Promise<*>}
+     */
+    async addChild(tenderId, selectId, data, reviseId) {
+        if ((tenderId <= 0) || (selectId <= 0)) {
+            return [];
+        }
+        const selectData = await this.getDataByKid(tenderId, selectId);
+        if (!selectData) {
+            throw '新增节点数据错误';
+        }
+        const children = await this.getChildrenByParentId(tenderId, selectId);
+
+        const maxId = await this._getMaxLid(tenderId);
+
+        data.id = this.uuid.v4();
+        data.tender_id = tenderId;
+        data.ledger_id = maxId + 1;
+        data.ledger_pid = selectData.ledger_id;
+        data.level = selectData.level + 1;
+        data.order = children.length + 1;
+        data.full_path = selectData.full_path + '-' + data.ledger_id;
+        data.is_leaf = true;
+        if (reviseId) data.crid = reviseId;
+
+        this.transaction = await this.db.beginTransaction();
+        try {
+            const result = await this.transaction.insert(this.tableName, data);
+            if (children.length === 0) {
+                await this.transaction.update(this.tableName,
+                    {is_leaf: false, quantity: null, unit_price: null, total_price: null, deal_qty: null, deal_tp: null},
+                    { where: {tender_id: tenderId, ledger_id: selectData.ledger_id} });
+            }
+            await this.transaction.commit();
+        } catch(err) {
+            this.transaction.rollback();
+            throw err;
+        }
+
+        this._cacheMaxLid(tenderId, maxId + 1);
+
+        // 查询应返回的结果
+        const resultData = {};
+        resultData.create = await this.getDataByKid(tenderId, data.ledger_id);
+        if (children.length === 0) {
+            resultData.update = await this.getDataByKid(tenderId, selectId);
+        }
+        return resultData;
+    }
+
+    /**
+     * 添加节点(来自标准清单)
+     * @param {Number} tenderId
+     * @param {Number} selectId
+     * @param {Object} stdData
+     * @return {Promise<*>}
+     */
+    async addStdNode(tenderId, selectId, stdData, reviseId) {
+        const newData = this._filterStdData(stdData);
+        const result = await this.addBillsNode(tenderId, selectId, newData, reviseId);
+        return result;
+    }
+
+    /**
+     * 添加标准节点,将选择的标准节点,添加为子节点(排序为末尾)
+     * @param {Number} tenderId - 标段Id
+     * @param {Number} selectId - 添加目标节点Id
+     * @param {Object} stdData - 标准节点数据
+     * @returns {Promise<*>}
+     */
+    async addStdNodeAsChild(tenderId, selectId, stdData, reviseId) {
+        const newData = this._filterStdData(stdData);
+        const result = await this.addChild(tenderId, selectId, newData, reviseId);
+        return result;
+    }
+
+    /**
+     * 根据parentData, data新增数据(新增为parentData的最后一个子项)
+     * @param {Number} tenderId - 标段id
+     * @param {Object} parentData - 父项数据
+     * @param {Object} data - 新增节点,初始数据
+     * @return {Promise<*>} - 新增结果
+     * @private
+     */
+    async _addChildNodeData(tenderId, parentData, data, reviseId) {
+        if (tenderId <= 0) {
+            return undefined;
+        }
+        if (!data) {
+            data = {};
+        }
+        const pid = parentData ? parentData.ledger_id : rootId;
+
+        const maxId = await this._getMaxLid(tenderId);
+
+        data.id = this.uuid.v4();
+        data.tender_id = tenderId;
+        data.ledger_id = maxId + 1;
+        data.ledger_pid = pid;
+        if (data.order === undefined) {
+            data.order = 1;
+        }
+        data.level = parentData ? parentData.level + 1 : 1;
+        data.full_path = parentData ? parentData.full_path + '-' + data.ledger_id : '' + data.ledger_id;
+        if (data.is_leaf === undefined) {
+            data.is_leaf = true;
+        }
+        if (reviseId) data.crid = reviseId;
+        const result = await this.transaction.insert(this.tableName, data);
+
+        this._cacheMaxLid(tenderId, maxId + 1);
+
+        return [result, data];
+    }
+
+    /**
+     * 根据parentData, data新增数据(自动排序)
+     * @param tenderId
+     * @param parentData
+     * @param data
+     * @return {Promise<void>}
+     * @private
+     */
+    async _addChildAutoOrder(tenderId, parentData, data, 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 (self.ctx.helper.compareCode(list[i].code, a.code) > 0) {
+                    return i > 0 ? list[i - 1] : null;
+                }
+            }
+            return list[list.length - 1];
+        };
+
+        const pid = parentData ? parentData.ledger_id : rootId;
+        const children = await this.getChildrenByParentId(tenderId, pid);
+        const preData = findPreData(children, data);
+        if (!preData || children.indexOf(preData) < children.length - 1) {
+            await this._updateChildrenOrder(tenderId, pid, preData ? preData.order + 1 : 1);
+        }
+        data.order = preData ? preData.order + 1 : 1;
+        const [addResult, node] = await this._addChildNodeData(tenderId, parentData, data, reviseId);
+
+        return [addResult, node];
+    }
+
+    /**
+     * 添加节点,并同步添加父节点
+     * @param {Number} tenderId - 标段id
+     * @param {Number} selectId - 选中节点id
+     * @param {Object} stdData - 节点数据
+     * @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;
+        const expandIds = [];
+        this.transaction = await this.db.beginTransaction();
+        try {
+            // 从最顶层节点依次查询是否存在,否则添加
+            for (let i = 0, len = fullLevel.length; i < len; i++) {
+                const stdNode = fullLevel[i];
+
+                if (isNew) {
+                    const newData = this._filterStdData(stdNode);
+                    newData.is_leaf = (i === len - 1);
+                    [addResult, node] = await this._addChildNodeData(tenderId, node, newData, 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);
+                    }
+                }
+            }
+            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);
+            if (updateParent) {
+                updateData.push(await this.getDataByCondition({ id: updateParent.id }));
+            }
+        }
+        return { create: createData, update: updateData };
+    }
+
+}
+
+module.exports = BaseBillsSerivce;

+ 53 - 0
app/base/base_tree_service.js

@@ -236,6 +236,59 @@ class TreeService extends Service {
     }
 
     /**
+     * 根据 父节点id 获取子节点
+     * @param tenderId
+     * @param nodeId
+     * @return {Promise<*>}
+     */
+    async getChildrenByParentId(mid, pid) {
+        if (mid <= 0 || !pid) return undefined;
+        const pids = pid instanceof Array ? pid : [pid];
+
+        this.initSqlBuilder();
+        this.sqlBuilder.setAndWhere(this.setting.mid, {
+            value: mid,
+            operate: '=',
+        });
+        this.sqlBuilder.setAndWhere(this.setting.pid, {
+            value: pids,
+            operate: 'in',
+        });
+        this.sqlBuilder.orderBy = [['order', 'ASC']];
+
+        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+        const data = await this.db.query(sql, sqlParam);
+
+        return data;
+    }
+    /**
+     * 根据 父节点id 获取孙子节点
+     * @param tenderId
+     * @param nodeId
+     * @return {Promise<void>}
+     */
+    async getPosterityByParentId(mid, pid) {
+        if (mid <= 0 || !pid) return undefined;
+
+        const node = await this.getDataByKid(mid, pid);
+
+        this.initSqlBuilder();
+        this.sqlBuilder.setAndWhere(this.setting.mid, {
+            value: mid,
+            operate: '=',
+        });
+        this.sqlBuilder.setAndWhere(this.setting.fullPath, {
+            value: this.db.escape(node[this.setting.fullPath] + '-%'),
+            operate: 'like',
+        });
+
+        const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
+        const data = await this.db.query(sql, sqlParam);
+
+        return data;
+    }
+
+    /**
      * 获取最大节点id
      *
      * @param {Number} mid - master id

+ 110 - 46
app/controller/revise_controller.js

@@ -199,12 +199,15 @@ module.exports = app => {
             const reviseBills = await ctx.service.reviseBills.getData(ctx.tender.id);
             const revisePos = await ctx.service.revisePos.getData(ctx.tender.id, revise.id);
             const [ledgerSpread, posSpread] = this._getSpreadSetting(revise);
+            const [stdBills, stdChapters] = await this.ctx.service.valuation.getValuationStdList(ctx.tender.data.valuation);
             return {
                 revise: revise, tender: ctx.tender.data,
                 reviseBills, revisePos, ledgerSpread, posSpread, tenderMenu, measureType,
                 preUrl: '/tender/' + ctx.tender.id,
                 audit: audit.revise,
                 jsFiles: this.app.jsFiles.common.concat(this.app.jsFiles.ledger.revise),
+                stdBills,
+                stdChapters,
             };
         }
 
@@ -281,42 +284,26 @@ module.exports = app => {
          * @param ctx
          * @returns {Promise<void>}
          */
-        async baseOpr(ctx) {
-            try {
-                await this._checkRevise();
-                const data = JSON.parse(ctx.request.body.data);
-                let result;
-
-                switch (data.postType) {
-                    case 'add':
-                        result = await ctx.service.reviseBills.addReviseNode(ctx.tender.id, ctx.revise.id, data.id);
-                        break;
-                    // case 'delete':
-                    //     result = await ctx.service.reviseBills.deleteNode(ctx.revise.id, data.id);
-                    //     break;
-                    case 'up-move':
-                        result = await ctx.service.reviseBills.upMoveNode(ctx.tender.id, data.id);
-                        break;
-                    case 'down-move':
-                        result = await ctx.service.reviseBills.downMoveNode(ctx.tender.id, data.id);
-                        break;
-                    // case 'up-level':
-                    //     result = await ctx.service.reviseBills.upLevelNode(ctx.revise.id, data.id);
-                    //     break;
-                    // case 'down-level':
-                    //     result = await ctx.service.reviseBills.downLevelNode(ctx.revise.id, data.id);
-                    //     break;
-                    default:
-                        throw '未知操作';
-                }
-                console.log(result);
-                ctx.body = {err: 0, msg: '', data: result};
-            } catch (err) {
-                this.log(err);
-                ctx.body = this.ajaxErrorBody(err, '数据错误');
+        async _billsBase(ctx, data) {
+            if (isNaN(data.id) || data.id <= 0) throw '数据错误';
+            if (type !== 'add') {
+                if (isNaN(data.count) || data.count <= 0) data.count = 1;
+            }
+            switch (type) {
+                case 'add':
+                    return await ctx.service.reviseBills.addNode(ctx.tender.id, ctx.revise.id, data.id);
+                case 'delete':
+                    return await ctx.service.reviseBills.delete(ctx.tender.id, data.id, data.count);
+                case 'up-move':
+                    return await ctx.service.reviseBills.upMoveNode(ctx.tender.id, data.id, data.count);
+                case 'down-move':
+                    return await ctx.service.reviseBills.downMoveNode(ctx.tender.id, data.id, data.count);
+                case 'up-level':
+                    return await ctx.service.reviseBills.upLevelNode(ctx.tender.id, data.id, data.count);
+                case 'down-level':
+                    return await ctx.service.reviseBills.downLevelNode(ctx.tender.id, data.id, data.count);
             }
         }
-
         /**
          * 批量插入数据 (Ajax)
          *
@@ -328,26 +315,103 @@ module.exports = app => {
          * @param ctx
          * @return {Promise<void>}
          */
-        async batchInsert(ctx) {
+        async _batchInsert(ctx, data) {
+            if ((isNaN(data.id) || data.id <= 0) || !data.batchType) {
+                throw '参数错误';
+            }
+            switch (data.batchType) {
+                case 'child':
+                    return await ctx.service.reviseBills.batchInsertChild(ctx.tender.id, ctx.revise.id, data.id, data.batchData);
+                case 'next':
+                    return await ctx.service.reviseBills.batchInsertNext(ctx.tender.id, ctx.revise.id, data.id, data.batchData);
+                default:
+                    throw '参数错误';
+            }
+        }
+        async _pasteBlock(ctx, data) {
+            if ((isNaN(data.id) || data.id <= 0) ||
+                (!data.tid && data.tid <= 0) ||
+                (!data.block || data.block.length <= 0)) throw '参数错误';
+            return await ctx.service.revise.pasteBlock(data.tid, data.id, data.block);
+        }
+        async _addStd(ctx, data) {
+            if ((isNaN(data.id) || data.id <= 0) || !data.stdType || !data.stdNode) throw '参数错误';
+            // todo 校验项目是否使用该库的权限
+
+            let stdLib, addType;
+            switch (data.stdType) {
+                case 'xmj':
+                    stdLib = ctx.service.stdXmj;
+                    addType = stdDataAddType.withParent;
+                    break;
+                case 'gcl':
+                    stdLib = ctx.service.stdGcl;
+                    const selectNode = await ctx.service.reviseBills.getDataByKid(ctx.tender.id, data.id);
+                    if (selectNode.b_code) {
+                        addType = stdDataAddType.next;
+                    } else {
+                        addType = stdDataAddType.child;
+                    }
+                    break;
+                default:
+                    throw '未知标准库';
+            }
+            const stdData = await stdLib.getDataByDataId(data.stdLibId, data.stdNode);
+            switch (addType) {
+                case stdDataAddType.child:
+                    return await ctx.service.reviseBills.addStdNodeAsChild(ctx.tender.id, data.id, stdData, ctx.revise.id);
+                    break;
+                case stdDataAddType.next:
+                    return await ctx.service.reviseBills.addStdNode(ctx.tender.id, data.id, stdData, ctx.revise.id);
+                case stdDataAddType.withParent:
+                    return await ctx.service.reviseBills.addStdNodeWithParent(ctx.tender.id, stdData, stdLib, ctx.revise.id);
+                default:
+                    throw '未知添加方式';
+            }
+        }
+        async _addDeal(ctx, data) {
+            if (!data.type || !data.dealBills) throw '数据错误';
+            if (data.type === 'child') {
+                return await ctx.service.reviseBills.addChild(ctx.tender.id, data.id, data.dealBills, ctx.revise.id);
+            } else if (data.type === 'next') {
+                return await ctx.service.reviseBills.addBillsNode(ctx.tender.id, data.id, data.dealBills, ctx.revise.id);
+            } else {
+                throw '数据错误';
+            }
+        }
+        async update(ctx) {
             try {
+                if (!ctx.tender.data) throw '标段数据错误';
                 await this._checkRevise();
                 const data = JSON.parse(ctx.request.body.data);
-                if ((isNaN(data.id) || data.id <= 0) || !data.batchType) {
-                    throw '参数错误';
-                }
+                if (!data.postType || !data.postData) throw '数据错误';
+                const responseData = {err: 0, msg: '', data: {}};
 
-                let result;
-                switch (data.batchType) {
-                    case 'child':
-                        result = await ctx.service.reviseBills.batchInsertChild(ctx.tender.id, ctx.revise.id, data.id, data.batchData);
+                switch (data.postType) {
+                    case 'add':
+                    // case 'delete':
+                    case 'up-move':
+                    case 'down-move':
+                    // case 'up-level':
+                    // case 'down-level':
+                        responseData.data = await this._billsBase(ctx, data.postData);
+                        break;
+                    case 'batch-insert':
+                        responseData.data = await this._batchInsert(ctx, data.postData);
+                        break;
+                    case 'add-deal':
+                        responseData.data = await this._addDeal(ctx, data.postData);
                         break;
-                    case 'next':
-                        result = await ctx.service.reviseBills.batchInsertNext(ctx.tender.id, ctx.revise.id, data.id, data.batchData);
+                    case 'add-std':
+                        responseData.data = await this._addStd(ctx, data.postData);
+                        break;
+                    case 'paste-block':
+                        responseData.data = await this._pasteBlock(ctx, data.postData);
                         break;
                     default:
-                        throw '参数错误';
+                        throw '未知操作';
                 }
-                ctx.body = {err: 0, msg: '', data: result};
+                ctx.body = responseData;
             } catch (err) {
                 this.log(err);
                 ctx.body = this.ajaxErrorBody(err, '数据错误');

+ 1 - 1
app/lib/analysis_excel.js

@@ -103,7 +103,7 @@ class ImportBaseTree {
         node.ledger_pid = parent ? parent.ledger_id : -1;
         node.level = parent ? parent.level + 1 : 1;
         node.order = parent ? parent.children.length + 1 : this.roots.length + 1;
-        node.full_path = parent ? parent.full_path + '.' + node.ledger_id : '' + node.ledger_id;
+        node.full_path = parent ? parent.full_path + '-' + node.ledger_id : '' + node.ledger_id;
         if (parent) {
             parent.children.push(node);
         } else {

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

@@ -123,7 +123,7 @@ $(document).ready(() => {
             setObjEnable($('#delete'), valid && first && sameParent && first.level > 1);
             setObjEnable($('#up-move'), valid && first && sameParent && first.level > 1 && preNode);
             setObjEnable($('#down-move'), valid && first && sameParent && first.level > 1 && !tree.isLastSibling(last));
-            if (checkTzMeasureType()) {
+            if (isTz) {
                 const posRange = last ? pos.getLedgerPos(last.id) : [];
                 setObjEnable($('#up-level'), valid && first && sameParent && tree.getParent(first) && first.level > 2 && (!posRange || posRange.length === 0));
                 const preNodePosRange = preNode ? pos.getLedgerPos(preNode.id) : [];
@@ -200,14 +200,24 @@ $(document).ready(() => {
          */
         baseOpr: function (sheet, type) {
             const self = this;
-            const tree = sheet.zh_tree;
-            const node = SpreadJsObj.getSelectObject(sheet);
-            if (!tree || !node) return;
+            const [tree, node, count] = this.getDefaultSelectInfo(sheet);
+            if (!tree || !node || !count) return;
 
-            postData(window.location.pathname + '/base-opr', {id: node.ledger_id, postType: type}, function (result) {
+            postData(window.location.pathname + '/update', {
+                postType: type,
+                postData: {
+                    id: node.ledger_id,
+                    count: count,
+                }
+            }, function (result) {
                 const refreshData = tree.loadPostData(result);
                 self.refreshTree(sheet, refreshData);
-                if (['up-move', 'down-move', 'up-level', 'down-level'].indexOf(type) > -1) {
+                if (type === 'delete') {
+                    const sel = sheet.getSelections()[0];
+                    if (sel) {
+                        sheet.setSelection(tree.nodes.indexOf(node), sel.col, 1, sel.colCount);
+                    }
+                } else if (['up-move', 'down-move', 'up-level', 'down-level'].indexOf(type) > -1) {
                     const sel = sheet.getSelections()[0];
                     if (sel) {
                         sheet.setSelection(tree.nodes.indexOf(node), sel.col, sel.rowCount, sel.colCount);
@@ -266,8 +276,9 @@ $(document).ready(() => {
                     data[col.field] = null;
                 }
                 // 更新至服务器
-                info.sheet.zh_tree.update('/tender/' + getTenderId() + '/ledger/update', data, function (result) {
-                    treeOperationObj.refreshTree(info.sheet, result);
+                postData(window.location.pathname + '/update', {postType: 'update', postData: data}, function (result) {
+                    const refreshNode = billsTree.loadPostData(result);
+                    billsTreeSpreadObj.refreshTree(info.sheet, refreshNode);
                 });
             }
         },
@@ -364,7 +375,7 @@ $(document).ready(() => {
             SpreadJsObj.initSheet(this.spread.getActiveSheet(), this.spreadSetting);
             this.spread.getActiveSheet().bind(GC.Spread.Sheets.Events.CellDoubleClick, function (e, info) {
                 const stdSheet = info.sheet;
-                const mainSheet = ledgerSpread.getActiveSheet();
+                const mainSheet = billsSheet;
                 if (!stdSheet.zh_setting || !stdSheet.zh_tree || !mainSheet.zh_tree) { return; }
 
                 const stdTree = stdSheet.zh_tree;
@@ -372,16 +383,28 @@ $(document).ready(() => {
                 const mainTree = mainSheet.zh_tree;
                 const sel = mainSheet.getSelections()[0];
                 const mainNode = mainTree.nodes[sel.row];
-                if (!stdNode) { return; }
+                if (!stdNode) return;
+
+                if (stdType === 'bills') {
+                    if (!(mainNode.b_code && mainNode.b_code !== '') && !mainTree.isLeafXmj(mainNode)) {
+                        toast('非最底层项目下,不应添加清单', 'warning');
+                        return;
+                    }
+                }
 
-                mainTree.postData('/tender/' + getTenderId() + '/ledger/add-by-std', mainNode, {
-                    tender_id: mainNode.tender_id,
-                    stdType: stdType,
-                    stdLibId: stdNode.list_id,
-                    stdNode: stdTree.getNodeKey(stdNode)
+                postData(window.location.pathname + '/update', {
+                    postType: 'add-std',
+                    postData: {
+                        id: mainTree.getNodeKey(mainNode),
+                        tender_id: mainNode.tender_id,
+                        stdType: stdType,
+                        stdLibId: stdNode.list_id,
+                        stdNode: stdTree.getNodeKey(stdNode)
+                    }
                 }, function (result) {
-                    treeOperationObj.refreshTree(mainSheet, result);
-                    treeOperationObj.refreshOperationValid(mainSheet);
+                    const refreshNode = mainTree.loadPostData(result);
+                    billsTreeSpreadObj.refreshTree(mainSheet, refreshNode);
+                    billsTreeSpreadObj.refreshOperationValid(mainSheet);
                 });
             });
             this.pathTree = createNewPathTree('base', this.treeSetting);
@@ -431,17 +454,20 @@ $(document).ready(() => {
                     return;
                 }
 
-                postData('/tender/' + getTenderId() + '/ledger/add-by-deal', {
-                    id: mainNode.ledger_id,
-                    type: mainNode.code ? 'child' : 'next',
-                    dealBills: {
-                        b_code: dealBills.code, name: dealBills.name, unit: dealBills.unit,
-                        unit_price: dealBills.unit_price,
+                postData(window.location.pathname + '/update', {
+                    postType: 'add-deal',
+                    postData: {
+                        id: mainNode.ledger_id,
+                        type: mainNode.code ? 'child' : 'next',
+                        dealBills: {
+                            b_code: dealBills.code, name: dealBills.name, unit: dealBills.unit,
+                            unit_price: dealBills.unit_price,
+                        }
                     },
                 }, function (result) {
                     const refreshData = mainTree.loadPostData(result);
-                    treeOperationObj.refreshTree(mainSheet, refreshData);
-                    treeOperationObj.refreshOperationValid(mainSheet);
+                    billsTreeSpreadObj.refreshTree(mainSheet, refreshData);
+                    billsTreeSpreadObj.refreshOperationValid(mainSheet);
                 });
             });
             SpreadJsObj.forbiddenSpreadContextMenu(selector, this.spread);
@@ -451,7 +477,6 @@ $(document).ready(() => {
             const self = this;
             postData(this.url+'/get-data', {}, function (data) {
                 self.data = data;
-                //self.calculateData();
                 SpreadJsObj.loadSheetData(self.spread.getActiveSheet(), 'data', data);
                 self.loaded = true;
             });
@@ -556,13 +581,14 @@ $(document).ready(() => {
                     insertData.batchType = (select.code && select.code !== '') ? 'child' : 'next';
                     insertData.id = select[billsTree.setting.id];
                     insertData.batchData = self.getBatchData();
-                    postData(window.location.pathname + '/batch-insert', insertData, function (data) {
+
+                    postData(window.location.pathname + '/update', {postType: 'batch-insert', postData: insertData}, function (data) {
                         pos.updateDatas(data.pos);
                         const result = billsTree.loadPostData(data.ledger);
-                        billsTreeSpreadObj.refreshTree(billsSheet, result);
-                        billsTreeSpreadObj.refreshOperationValid(billsSheet, selection);
+                        billsTreeSpreadObj.refreshTree(sheet, result);
+                        billsTreeSpreadObj.refreshOperationValid(sheet, selection);
                         self.obj.modal('hide');
-                    });
+                    }, null, true);
                 }
             });
         }
@@ -713,7 +739,7 @@ $(document).ready(() => {
                     stdXmj.loadLib($('select', '#std-xmj').val());
                 }
                 stdXmj.spread.refresh();
-            } else if (tab.attr('content') === '#std-bills') {
+            } else if (tab.attr('content') === '#std-gcl') {
                 if (!stdGcl) {
                     stdGcl = new stdLib('#std-gcl', 'gcl', {
                         id: 'bill_id',

+ 1 - 32
app/router.js

@@ -100,11 +100,8 @@ module.exports = app => {
     app.post('/tender/:id/revise/cancel', sessionAuth, tenderCheck, 'reviseController.cancel');
     app.post('/tender/:id/revise/save', sessionAuth, tenderCheck, 'reviseController.save');
     app.get('/tender/:id/revise/info', sessionAuth, tenderCheck, 'reviseController.info');
-    app.post('/tender/:id/revise/info/base-opr', sessionAuth, tenderCheck, 'reviseController.baseOpr');
-    app.post('/tender/:id/revise/info/batch-insert', sessionAuth, tenderCheck, 'reviseController.batchInsert');
+    app.post('/tender/:id/revise/info/update', sessionAuth, tenderCheck, 'reviseController.update');
     //app.post('/tender/:id/ledger/revise/audit/', sessionAuth, tenderCheck, 'ledgerController.reviseStatus');
-    //app.get('/tender/:id/ledger/index', sessionAuth, 'ledgerController.index');
-
     // 台账审批相关
     app.get('/tender/:id/ledger/audit', sessionAuth, tenderCheck, 'ledgerAuditController.index');
     app.post('/tender/:id/ledger/audit/add', sessionAuth, tenderCheck, 'ledgerAuditController.add');
@@ -211,34 +208,6 @@ module.exports = app => {
     app.post('/profile/bind', sessionAuth, 'profileController.bindMobile');
     app.get('/profile/qrCode', sessionAuth, 'profileController.qrCode');
 
-    // 中间计量 - 计量编制相关
-    // app.get('/measure/wlist', sessionAuth, tenderSelect, 'measureController.list');
-    //
-    // app.get('/measure/work/:mid', sessionAuth, tenderSelect, 'measureController.work');
-    // app.post('/measure/newCode', sessionAuth, 'measureController.newCode');
-    // app.post('/measure/add', sessionAuth, 'measureController.addMeasure');
-    // app.post('/measure/delete', sessionAuth, 'measureController.deleteMeasure');
-    // app.post('/measure/search', sessionAuth, 'measureController.search');
-    // app.post('/measure/pos', sessionAuth, 'measureController.pos');
-    // app.post('/measure/bills', sessionAuth, 'measureController.bills');
-    // app.post('/measure/detail', sessionAuth, 'measureController.measureDetail');
-    // app.post('/measure/billsUpdate', sessionAuth, 'measureController.billsUpdate');
-
-    // 中间计量 - 计量审批相关
-    // app.get('/measure/alist', sessionAuth, tenderSelect, 'measureAuditController.list');
-    // app.get('/measure/audit/:mid', sessionAuth, 'measureAuditController.index');
-    // app.post('/measure/audit/add', sessionAuth, 'measureAuditController.add');
-    // app.post('/measure/audit/remove', sessionAuth, 'measureAuditController.remove');
-    // app.post('/measure/audit/start', sessionAuth, 'measureAuditController.start');
-    // app.post('/measure/audit/check', sessionAuth, 'measureAuditController.check');
-
-    // 期计量管理相关
-    // app.get('/tender/:id/measure/stage', sessionAuth, tenderCheck, 'stageController.index');
-    // app.post('/tender/:id/stage/add', sessionAuth, tenderCheck, 'stageController.add');
-    // app.get('/tender/:id/stage/:order/measure', sessionAuth, tenderSelect, 'stageController.stageMeasure');
-    // app.get('/tender/:id/stage/:order/deal', sessionAuth, tenderSelect, 'stageController.stageDeal');
-    // app.get('/tender/:id/stage/:order/report', sessionAuth, tenderSelect, 'stageController.stageReport');
-
     // 标准库相关
     app.post('/std-lib/get-data', sessionAuth, 'standardLibController.getData');
 

+ 9 - 333
app/service/ledger.js

@@ -31,7 +31,7 @@ const keyPre = 'tender_node_maxId:';
 
 module.exports = app => {
 
-    class Ledger extends app.BaseTreeService {
+    class Ledger extends app.BaseBillsService {
 
         /**
          * 构造函数
@@ -221,34 +221,6 @@ module.exports = app => {
 
             return data;
         }
-        /**
-         * 根据 父节点id 获取孙子节点
-         * @param tenderId
-         * @param nodeId
-         * @return {Promise<void>}
-         */
-        async getPosterityByParentId(tenderId, nodeId) {
-            if (tenderId <= 0 || !nodeId) {
-                return undefined;
-            }
-
-            const node = await this.getDataByNodeId(tenderId, nodeId);
-
-            this.initSqlBuilder();
-            this.sqlBuilder.setAndWhere('tender_id', {
-                value: tenderId,
-                operate: '=',
-            });
-            this.sqlBuilder.setAndWhere('full_path', {
-                value: this.db.escape(node.full_path + '-%'),
-                operate: 'like',
-            });
-
-            const [sql, sqlParam] = this.sqlBuilder.build(this.tableName);
-            const data = await this.db.query(sql, sqlParam);
-
-            return data;
-        }
 
         /**
          * 获取项目工程量
@@ -290,269 +262,6 @@ module.exports = app => {
         }
 
         /**
-         * 从数据库获取标段的最大节点id
-         *
-         * @param {Number} tenderId - 标段id
-         * @return {Number}
-         * @private
-         */
-        async _getMaxNodeId(tenderId) {
-            const sql = 'SELECT Max(??) As max_id FROM ?? Where tender_id = ' + tenderId;
-            const sqlParam = ['ledger_id', this.tableName];
-            const queryResult = await this.db.queryOne(sql, sqlParam);
-            return queryResult.max_id;
-        }
-
-        /**
-         * 根据parentData, data新增数据(新增为parentData的最后一个子项)
-         * @param {Number} tenderId - 标段id
-         * @param {Object} parentData - 父项数据
-         * @param {Object} data - 新增节点,初始数据
-         * @return {Promise<*>} - 新增结果
-         * @private
-         */
-        async _addChildNodeData(tenderId, parentData, data) {
-            if (tenderId <= 0) {
-                return undefined;
-            }
-            if (!data) {
-                data = {};
-            }
-            const pid = parentData ? parentData.ledger_id : rootId;
-
-            const cacheKey = keyPre + tenderId;
-            let maxId = parseInt(await this.cache.get(cacheKey));
-            if (!maxId) {
-                maxId = await this._getMaxNodeId(tenderId);
-            }
-            this.cache.set(cacheKey, maxId + 1, 'EX', this.ctx.app.config.cacheTime);
-
-            data.id = this.uuid.v4();
-            data.tender_id = tenderId;
-            data.ledger_id = maxId + 1;
-            data.ledger_pid = pid;
-            if (data.order === undefined) {
-                data.order = 1;
-            }
-            data.level = parentData ? parentData.level + 1 : 1;
-            data.full_path = parentData ? parentData.full_path + '-' + data.ledger_id : '' + data.ledger_id;
-            if (data.is_leaf === undefined) {
-                data.is_leaf = true;
-            }
-            const result = await this.transaction.insert(this.tableName, data);
-
-            return [result, data];
-        }
-
-        /**
-         * 根据parentData, data新增数据(自动排序)
-         * @param tenderId
-         * @param parentData
-         * @param data
-         * @return {Promise<void>}
-         * @private
-         */
-        async _addChildAutoOrder(tenderId, parentData, data) {
-            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 (self.ctx.helper.compareCode(list[i].code, a.code) > 0) {
-                        return i > 0 ? list[i - 1] : null;
-                    }
-                }
-                return list[list.length - 1];
-            };
-
-            const pid = parentData ? parentData.ledger_id : rootId;
-            const children = await this.getChildrenByParentId(tenderId, pid);
-            const preData = findPreData(children, data);
-            if (!preData || children.indexOf(preData) < children.length - 1) {
-                await this._updateChildrenOrder(tenderId, pid, preData ? preData.order + 1 : 1);
-            }
-            data.order = preData ? preData.order + 1 : 1;
-            const [addResult, node] = await this._addChildNodeData(tenderId, parentData, data);
-
-            return [addResult, node];
-        }
-
-        /**
-         * 从标准数据中提取有效数据
-         * @param {Object} stdData - 从标准库中查询所得
-         * @returns {name, unit, source, code, b_code}
-         * @private
-         */
-        _filterStdData(stdData) {
-            const result = {
-                name: stdData.name,
-                unit: stdData.unit,
-                source: stdData.source,
-                node_type: stdData.node_type,
-            };
-            result.code = stdData.code ? stdData.code : '';
-            result.b_code = stdData.b_code ? stdData.b_code : '';
-            return result;
-        }
-
-        /**
-         * 添加节点(来自标准清单)
-         * @param {Number} tenderId
-         * @param {Number} selectId
-         * @param {Object} stdData
-         * @return {Promise<*>}
-         */
-        async addStdNode(tenderId, selectId, stdData) {
-            const newData = this._filterStdData(stdData);
-            const result = await this.addNode(tenderId, selectId, newData);
-            return result;
-        }
-
-        /**
-         * 新增子节点,并排在所有子节点的末尾
-         * @param {Number} tenderId - 标段Id
-         * @param {Number} selectId - 父节点Id
-         * @param {Object} data - 新增节点数据(编号名称等)
-         * @returns {Promise<*>}
-         */
-        async addChild(tenderId, selectId, data) {
-            if ((tenderId <= 0) || (selectId <= 0)) {
-                return [];
-            }
-            const selectData = await this.getDataByNodeId(tenderId, selectId);
-            if (!selectData) {
-                throw '新增节点数据错误';
-            }
-            const children = await this.getChildrenByParentId(tenderId, selectId);
-
-            const cacheKey = keyPre + tenderId;
-            let maxId = parseInt(await this.cache.get(cacheKey));
-            if (!maxId) {
-                maxId = await this._getMaxNodeId(tenderId);
-                this.cache.set(cacheKey, maxId, 'EX', this.ctx.app.config.cacheTime);
-            }
-
-            data.id = this.uuid.v4();
-            data.tender_id = tenderId;
-            data.ledger_id = maxId + 1;
-            data.ledger_pid = selectData.ledger_id;
-            data.level = selectData.level + 1;
-            data.order = children.length + 1;
-            data.full_path = selectData.full_path + '-' + data.ledger_id;
-            data.is_leaf = true;
-
-            this.transaction = await this.db.beginTransaction();
-            try {
-                const result = await this.transaction.insert(this.tableName, data);
-                if (children.length === 0) {
-                    await this.transaction.update(this.tableName,
-                        {is_leaf: false, quantity: null, unit_price: null, total_price: null, deal_qty: null, deal_tp: null},
-                        { where: {tender_id: tenderId, ledger_id: selectData.ledger_id} });
-                }
-                await this.transaction.commit();
-            } catch(err) {
-                this.transaction.rollback();
-                throw err;
-            }
-
-            this.cache.set(cacheKey, maxId + 1, 'EX', this.ctx.app.config.cacheTime);
-
-            // 查询应返回的结果
-            const resultData = {};
-            resultData.create = await this.getDataByNodeId(tenderId, data.ledger_id);
-            if (children.length === 0) {
-                resultData.update = await this.getDataByNodeId(tenderId, selectId);
-            }
-            return resultData;
-        }
-
-        /**
-         * 添加标准节点,将选择的标准节点,添加为子节点(排序为末尾)
-         * @param {Number} tenderId - 标段Id
-         * @param {Number} selectId - 添加目标节点Id
-         * @param {Object} stdData - 标准节点数据
-         * @returns {Promise<*>}
-         */
-        async addStdNodeAsChild(tenderId, selectId, stdData) {
-            const newData = this._filterStdData(stdData);
-            const result = await this.addChild(tenderId, selectId, newData);
-            return result;
-        }
-
-        /**
-         * 添加节点,并同步添加父节点
-         * @param {Number} tenderId - 标段id
-         * @param {Number} selectId - 选中节点id
-         * @param {Object} stdData - 节点数据
-         * @param {StandardLib} stdLib - 标准库
-         * @return {Promise<void>}
-         */
-        async addStdNodeWithParent(tenderId, stdData, stdLib) {
-            // 查询完整标准清单,并按层次排序
-            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;
-            const expandIds = [];
-            this.transaction = await this.db.beginTransaction();
-            try {
-                // 从最顶层节点依次查询是否存在,否则添加
-                for (let i = 0, len = fullLevel.length; i < len; i++) {
-                    const stdNode = fullLevel[i];
-
-                    if (isNew) {
-                        const newData = this._filterStdData(stdNode);
-                        newData.is_leaf = (i === len - 1);
-                        [addResult, node] = await this._addChildNodeData(tenderId, node, newData);
-                    } 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);
-                            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);
-                        }
-                    }
-                }
-                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);
-                if (updateParent) {
-                    updateData.push(await this.getDataByCondition({ id: updateParent.id }));
-                }
-            }
-            return { create: createData, update: updateData };
-        }
-
-        /**
          * 删除相关数据 用于继承
          * @param mid
          * @param deleteData
@@ -669,12 +378,7 @@ module.exports = app => {
                     let datas = await this.getDataByFullPath(tenderId, node.full_path + '%');
                     datas = this._.sortBy(datas, 'level');
 
-                    const cacheKey = keyPre + this.ctx.tender.id;
-                    let maxId = parseInt(await this.cache.get(cacheKey));
-                    if (!maxId) {
-                        maxId = await this._getMaxNodeId(this.ctx.tender.id);
-                    }
-                    this.cache.set(cacheKey, maxId + datas.length, 'EX', this.ctx.app.config.cacheTime);
+                    const maxId = await this._getMaxLid(this.ctx.tender.id);
 
                     const leafBillsId = [];
                     // 计算粘贴数据中需更新部分
@@ -713,6 +417,7 @@ module.exports = app => {
                     for (const id of leafBillsId) {
                         await this.ctx.service.pos.copyBillsPosData(id.org, id.new, this.transaction);
                     }
+                    this._cacheMaxLid(tenderId, maxId + datas.length);
                 }
                 await this.transaction.commit();
             } catch (err) {
@@ -720,6 +425,7 @@ module.exports = app => {
                 throw err;
             }
 
+
             // 查询应返回的结果
             const order = [];
             for (let i = 1; i <= copyNodes.length; i++) {
@@ -844,12 +550,7 @@ module.exports = app => {
             const result = [],
                 newIds = [];
             let tp = 0;
-            const cacheKey = keyPre + tenderId;
-            let maxId = parseInt(await this.cache.get(cacheKey));
-            if (!maxId) {
-                maxId = await this._getMaxNodeId(tenderId);
-            }
-            this.cache.set(cacheKey, maxId + xmj.children.length + 1, 'EX', this.ctx.app.config.cacheTime);
+            const maxId = await this._getMaxLid(tenderId);
             // 添加xmj数据
             const parent = {
                 tender_id: tenderId,
@@ -924,11 +625,7 @@ module.exports = app => {
                 }
                 const order = lastChild ? lastChild.order : 0;
                 // 计算id
-                const cacheKey = keyPre + tenderId;
-                let maxId = parseInt(await this.cache.get(cacheKey));
-                if (!maxId) {
-                    maxId = await this._getMaxNodeId(tenderId);
-                }
+                const maxId = await this._getMaxLid(tenderId);
 
                 // 数据库创建新增节点数据
                 for (let i = 0, iLen = data.length; i < iLen; i++) {
@@ -954,7 +651,7 @@ module.exports = app => {
                         await this._calcNode(qd, this.transaction);
                     }
                 }
-                this.cache.set(cacheKey, maxId + data.length + 1, 'EX', this.ctx.app.config.cacheTime);
+                this._cacheMaxLid(tenderId, maxId + data.length);
                 await this.transaction.commit();
             } catch (err) {
                 await this.transaction.rollback();
@@ -997,11 +694,7 @@ module.exports = app => {
                 // 选中节点的所有后兄弟节点,order+粘贴节点个数
                 await this._updateChildrenOrder(tenderId, selectData.ledger_pid, selectData.order + 1, data.length);
                 // 计算id和order
-                const cacheKey = keyPre + tenderId;
-                let maxId = parseInt(await this.cache.get(cacheKey));
-                if (!maxId) {
-                    maxId = await this._getMaxNodeId(tenderId);
-                }
+                const maxId = await this._getMaxLid(tenderId);
                 const order = selectData.order;
                 // 数据库创建新增节点数据
                 for (let i = 0, iLen = data.length; i < iLen; i++) {
@@ -1027,7 +720,7 @@ module.exports = app => {
                         await this._calcNode(qd, this.transaction);
                     }
                 }
-                this.cache.set(cacheKey, maxId + data.length + 1, 'EX', this.ctx.app.config.cacheTime);
+                this._cacheMaxLid(tenderId, maxId + data.length);
                 await this.transaction.commit();
             } catch (err) {
                 await this.transaction.rollback();
@@ -1081,23 +774,6 @@ module.exports = app => {
             await this._calcNode(node, transaction);
         }
 
-        /**
-         * 查找定位 --> 废弃
-         * @param tenderId
-         * @param nodeId
-         * @return {Promise<{expand: *}>}
-         */
-        async locateNode(tenderId, nodeId) {
-            const node = await this.getDataByNodeId(tenderId, nodeId);
-            if (!node) {
-                throw '查询数据有误';
-            }
-            const expandIds = node.full_path.split('-');
-            expandIds.pop();
-            const expandData = await this.getChildrenByParentId(tenderId, expandIds);
-            return { expand: expandData };
-        }
-
         async _importCacheTreeNodes(transaction, nodes) {
             const datas = [];
             for (const node of nodes) {

+ 1 - 1
app/service/revise_bills.js

@@ -9,7 +9,7 @@
  */
 
 module.exports = app => {
-    class ReviseBills extends app.BaseTreeService {
+    class ReviseBills extends app.BaseBillsService {
 
         /**
          * 构造函数

+ 0 - 10
app/view/ledger/explode.ejs

@@ -87,16 +87,6 @@
                 <div class="resize-x" id="right-spr" r-Type="width" div1="#left-view" div2="#right-view" title="调整大小" a-type="percent"><!--调整左右高度条--></div>
                 <div class="tab-content">
                     <div id="search" class="tab-pane">
-                        <!--<div class="sjs-bar-1">-->
-                            <!--<div class="input-group input-group-sm">-->
-                                <!--<input id="searchKeyword" type="text" class="form-control" placeholder="可查找 项目节编号/清单编号/名称" aria-label="Recipient's username" aria-describedby="button-addon2">-->
-                                <!--<div class="input-group-append">-->
-                                    <!--<button class="btn btn-outline-secondary" type="button" id="searchLedger">搜索</button>-->
-                                <!--</div>-->
-                            <!--</div>-->
-                        <!--</div>-->
-                        <!--<div id="search-result" class="sjs-sh-1">-->
-                        <!--</div>-->
                     </div>
                     <div id="std-xmj" class="tab-pane">
                         <div class="sjs-bar-2">

+ 13 - 5
app/view/revise/info.ejs

@@ -114,16 +114,24 @@
                     <div id="std-xmj" class="tab-pane">
                         <div class="sjs-bar-3">
                             <div class="pb-1">
-                                <select class="form-control form-control-sm"><option>0号计量台帐部位参考(项目节)</option></select>
+                                <select class="form-control form-control-sm">
+                                    <% for (const c of stdChapters) { %>
+                                    <option value="<%- c.id %>" <% if (stdChapters.indexOf(c) === 0) { %>selected<% } %>><%- c.name %></option>
+                                    <% } %>
+                                </select>
                             </div>
                         </div>
-                        <div id="std-gcl-spread" class="sjs-sh-3">
+                        <div id="std-xmj-spread" class="sjs-sh-3">
                         </div>
                     </div>
                     <div id="std-gcl" class="tab-pane">
                         <div class="sjs-bar-4">
                             <div class="pb-1">
-                                <select class="form-control form-control-sm"><option>0号计量台帐部位参考(工程量清單)</option></select>
+                                <select class="form-control form-control-sm">
+                                    <% for (const b of stdBills) { %>
+                                    <option value="<%- b.id %>" <% if (stdBills.indexOf(b) === 0) { %>selected<% } %>><%- b.name %></option>
+                                    <% } %>
+                                </select>
                             </div>
                         </div>
                         <div id="std-gcl-spread" class="sjs-sh-4">
@@ -147,10 +155,10 @@
                     <a class="nav-link" content="#search" href="javascript: void(0);">查找定位</a>
                 </li>
                 <li class="nav-item">
-                    <a class="nav-link" content="#std-chapter" href="javascript: void(0);">项目节</a>
+                    <a class="nav-link" content="#std-xmj" href="javascript: void(0);">项目节</a>
                 </li>
                 <li class="nav-item">
-                    <a class="nav-link" content="#std-bills" href="javascript: void(0);">工程量清单</a>
+                    <a class="nav-link" content="#std-gcl" href="javascript: void(0);">工程量清单</a>
                 </li>
                 <li class="nav-item">
                     <a class="nav-link" content="#deal-bills" href="javascript: void(0);">签约清单</a>