Selaa lähdekoodia

Merge branch 'master' of http://192.168.1.41:3000/SmartCost/ConstructionOperation

zhangweicheng 3 vuotta sitten
vanhempi
commit
d849749996

+ 3 - 1
modules/all_models/std_billsGuidance_items.js

@@ -21,7 +21,9 @@ const stdBillsGuidanceItems = new Schema({
     comment: String, //备注
     type: Number, //0:工作内容 1:定额
     rationID: {type: Number, default: null}, //定额类型时
-    deleted: {type: Boolean, default: false}
+    deleted: {type: Boolean, default: false},
+    outputItemCharacter: {type: Boolean, default: false},
+    required: {type: Boolean, default: false},
 }, {versionKey: false});
 
 mongoose.model('std_billsGuidance_items', stdBillsGuidanceItems, 'std_billsGuidance_items');

+ 8 - 0
modules/bills_lib/controllers/bills_lib_controllers.js

@@ -117,6 +117,14 @@ module.exports = {
         });
     },
     updateBills: function(req, res){
+        const zhLibID = 'cf851660-3534-11ec-9641-2da8021b8e4e';
+        if (req.session.managerData.isTemporary) {
+            const match = req.headers.referer.match(/libID=([\d,a-z,A-Z,-]{36})/);
+            if (match && match[1] && match[1] !== zhLibID) {
+                callback(req, res, '无此清单精灵库权限', '无此清单精灵库权限', null);
+                return;
+            }
+        }
         let data = JSON.parse(req.body.data);
         billsLibDao.updateBills(data, function(err, message){
             callback(req, res, err, message, null);

+ 6 - 5
modules/common/base/base_controller.js

@@ -42,6 +42,7 @@ class BaseController {
      * @return {void}
      */
     init(request, response, next) {
+        const referer = request.headers.referer;
         // 获取当前控制器和动作名称
         let urlInfo = Url.parse(request.originalUrl, true);
         let url = urlInfo.pathname.substr(1);
@@ -61,10 +62,9 @@ class BaseController {
                 break;
         }
 
+        let sessionManager = request.session.managerData;
         try {
-            console.log('enterINit');
             // 如果不适超级管理员则判断权限
-            let sessionManager = request.session.managerData;
             let MenuPermission = sessionManager.menuData;
             if (sessionManager.superAdmin !== 1) {
                 let currentPermission = sessionManager.toolPermission;
@@ -126,10 +126,11 @@ class BaseController {
             // moment工具
             response.locals.moment = Moment;
         } catch (error) {
-            console.log('enterAURE');
             console.log(error);
-            response.redirect('/dashboard');
-            return;
+            if (!(sessionManager.isTemporary && /\/billsGuidance\/guidance\//.test(referer))) {
+                response.redirect('/dashboard');
+                return;
+            }
         }
 
         next();

+ 9 - 0
modules/ration_repository/controllers/search_controller.js

@@ -18,6 +18,15 @@ class SearchController extends BaseController{
             callback(req, res, 1, err, null);
         }
     }
+    async getRationByID (req, res) {
+        try {
+            let data = JSON.parse(req.body.data);
+            let ration = await rationItem.getRationByID(data.ID);
+            callback(req, res, 0, '', ration);
+        } catch (err) {
+            callback(req, res, 1, err, null);
+        }
+    }
     findRation (req, res) {
         var rId = req.body.rationLibId, keyword = req.body.keyword;
         rationItem.findRation(rId, keyword, function (err, message, rst) {

+ 7 - 2
modules/ration_repository/models/ration_item.js

@@ -298,7 +298,7 @@ rationItemDAO.prototype.getRationItemsByLib = async function (rationRepId, showH
         return [];
     }
     let startDate = new Date();
-    let rations = await rationItemModel.find({rationRepId: rationRepId}, returnFields);
+    let rations = await rationItemModel.find({rationRepId: rationRepId}, returnFields).lean();
     console.log(`Date: ${new Date() - startDate}====================================`);
     if(!showHint){
         return rations;
@@ -343,7 +343,7 @@ rationItemDAO.prototype.getRationItemsByLib = async function (rationRepId, showH
                 hintsArr.push(`附注:`);
                 hintsArr = hintsArr.concat(ration.annotation.split('\n'));
             }
-            ration._doc.hint = hintsArr.join('<br>');
+            ration.hint = hintsArr.join('<br>');
         }
         return rations;
     }
@@ -505,6 +505,11 @@ rationItemDAO.prototype.getRationItem = async function (repId, code) {
     return ration;
 };
 
+rationItemDAO.prototype.getRationByID = async function (ID) {
+    let ration = await rationItemModel.findOne({ ID: +ID }).lean();
+    return ration;
+};
+
 rationItemDAO.prototype.addRationItems = function(rationLibId, lastOpr, sectionId, items,callback){
     if (items && items.length > 0) {
         counter.counterDAO.getIDAfterCount(counter.moduleName.rations, items.length, function(err, result){

+ 1 - 0
modules/ration_repository/routes/ration_rep_routes.js

@@ -87,6 +87,7 @@ module.exports =  function (app) {
     apiRouter.post('/updateSection', installationController.auth, installationController.init, installationController.updateSection);
     apiRouter.post('/batchUpdateInst', installationController.auth, installationController.init, installationController.batchUpdateInst);
 
+    apiRouter.post('/getRationByID', searchController.auth, searchController.init, searchController.getRationByID);
     apiRouter.post('/getRationItem',searchController.auth, searchController.init, searchController.getRationItem);
     apiRouter.post('/findRation', searchController.auth, searchController.init, searchController.findRation);
 

+ 13 - 2
modules/std_billsGuidance_lib/controllers/libController.js

@@ -15,7 +15,10 @@ let callback = function (req, res, err, msg, data) {
     res.json({error: err, message: msg, data: data});
 };
 
+const zhLibID = 'cf851660-3534-11ec-9641-2da8021b8e4e';
+
 class BillsGuideLibController extends BaseController{
+
     //获取编办及编办清单库信息
     async getComBillsLibInfo(req, res){
         try{
@@ -29,7 +32,7 @@ class BillsGuideLibController extends BaseController{
 
     async getBillsGuideLibs(req, res){
         try{
-            let libs = await billsGuidanceFacade.getBillsGuideLibs({deleted: false});
+            let libs = await billsGuidanceFacade.getBillsGuideLibs({deleted: false}, req.session.managerData.isTemporary);
             callback(req, res, 0, '', libs);
         }
         catch(err){
@@ -82,9 +85,17 @@ class BillsGuideLibController extends BaseController{
 
     async updateItems(req, res){
         try{
+
+            if (req.session.managerData.isTemporary) {
+                const match = req.headers.referer.match(/libID=([\d,a-z,A-Z,-]{36})/);
+                if (match && match[1] && match[1] !== zhLibID) {
+                    throw '无此清单精灵库权限';
+                }
+            }
+
             let data = JSON.parse(req.body.data);
             let updateDatas = data.updateDatas;
-            await billsGuidanceFacade.updateItems(updateDatas);
+            await billsGuidanceFacade.updateItems(updateDatas, req.session.managerData.isTemporary);
             callback(req, res, 0, '', null);
         }
         catch(err){

+ 5 - 2
modules/std_billsGuidance_lib/controllers/viewController.js

@@ -13,14 +13,17 @@ class ViewsController extends BaseController{
     redirectMain(req, res){
         res.render('maintain/billsGuidance_lib/html/main.html',
             {
-                userAccount: req.session.managerData.username
+                userAccount: req.session.managerData.username,
+                manager: req.session.managerData,
             });
     }
     redirectGuidance(req, res){
+        let sessionManager = req.session.managerData;
         res.render('maintain/billsGuidance_lib/html/zhiyin.html',
             {
                 userAccount: req.session.managerData.username,
-                LicenseKey:config.getLicenseKey(process.env.NODE_ENV)
+                LicenseKey:config.getLicenseKey(process.env.NODE_ENV),
+                manager: req.session.managerData,
             });
     }
 }

+ 19 - 3
modules/std_billsGuidance_lib/facade/facades.js

@@ -21,7 +21,8 @@ const stdRationModel = mongoose.model('std_ration_lib_ration_items');
 const engLibModel = mongoose.model('engineering_lib');
 const compilationModel = mongoose.model('compilation');
 const _ = require('lodash');
-
+const zhLibID = 'cf851660-3534-11ec-9641-2da8021b8e4e';
+const cqLibID = '90c51220-a740-11e8-a354-ab5db7d42428';
 module.exports = {
     handleCopyItems,
     getComBillsLibInfo,
@@ -174,7 +175,11 @@ async function getComBillsLibInfo() {
     }
 }
 
-async function getBillsGuideLibs(findData) {
+async function getBillsGuideLibs(findData, isTemporary) {
+    if (isTemporary) {
+        const libs = await billsGuideLibModel.find({ ID: { $in: [zhLibID, cqLibID] } }).lean();
+        return libs;
+    }
     return await billsGuideLibModel.find(findData);
 }
 
@@ -274,7 +279,18 @@ async function getLibWithBills(libID){
     if(!billsLib){
         throw '引用的清单规则库不存在!';
     }
-    let bills = await stdBillsModel.find({billsLibId: billsLib.billsLibId}, '-_id code name ID NextSiblingID ParentID jobs items comment');
+    let bills = await stdBillsModel.find({billsLibId: billsLib.billsLibId}, '-_id code name ID NextSiblingID ParentID jobs items comment').lean();
+    const guideItems = await billsGuideItemsModel.find({ libID: guidanceLib[0].ID }, '-_id billsID').lean();
+    const billsMap = {};
+    for (const item of guideItems) {
+        billsMap[item.billsID] = true;
+    }
+    for (const item of bills) {
+        if (billsMap[item.ID]) {
+            item.hasGuide = true;
+        }
+    }
+    
     return {guidanceLib: guidanceLib[0], bills};
 }
 

+ 5 - 1
modules/users/controllers/dashboard_controller.js

@@ -37,7 +37,11 @@ class DashboardController extends BaseController {
         // 获取已发布的通知
         let messageModel = new MessageModel();
         let messageList = await messageModel.getList({status: 1}, 1, 5, {release_time: -1});
-
+        let sessionManager = request.session.managerData;
+        if (sessionManager && sessionManager.isTemporary) {
+            return response.redirect('/billsGuidance/main');
+            
+        }
         let renderData = {
             parentTitle: DashboardController.parentTitle,
             parentIndex: DashboardController.parentIndex,

+ 13 - 3
modules/users/controllers/login_controller.js

@@ -33,7 +33,11 @@ class LoginController extends BaseController {
         };
         let managerSessionData = request.session.managerData;
         if (managerSessionData !== undefined) {
-            return response.redirect("/dashboard");
+            if (managerSessionData.isTemporary) {
+                return response.redirect("/billsGuidance/main");
+            } else {
+                return response.redirect("/dashboard");
+            }
         }
         response.render('users/views/login/index', renderData);
     }
@@ -53,11 +57,15 @@ class LoginController extends BaseController {
         let permissionGroupModel = new PermissionGroupModel();
 
         let responseData = {
+            isTemporary: false,
             error: 0,
             msg: ''
         };
         try {
             let managerData = await managerModel.validLogin(username, password);
+            if (managerData.isTemporary) {
+                responseData.isTemporary = true;
+            }
 
             // 成功后写入session
             let currentTime = new Date().getTime();
@@ -208,13 +216,15 @@ class LoginController extends BaseController {
                 loginTime: currentTime,
                 sessionToken: sessionToken,
                 userID: managerData.id,
-                toolPermission: toolPermissionController.join(','),
+                toolPermission: managerData.isTemporary ? 'billsGuidance' : toolPermissionController.join(','),
                 toolMenuData: toolMenuData,
                 toolAllPermission: toolAllPermission.join(','),
                 menuData: menuData,
-                superAdmin: managerData.super_admin
+                superAdmin: managerData.super_admin,
+                isTemporary: managerData.isTemporary,
             };
             request.session.managerData = managerSession;
+            console.log(managerSession);
 
             // 更新登录信息
             let ip = request.connection.remoteAddress;

+ 37 - 0
modules/users/models/manager_model.js

@@ -143,6 +143,39 @@ class ManagerModel extends BaseModel {
     }
 
     /**
+     * 财审平台需要临时登录,只能看某个清单精灵库
+     */
+    temporaryLogin(username, password) {
+        const users = [
+            { name: '中洲一', pwd: '123456' },
+            { name: '中洲二', pwd: '123456' },
+            { name: '中洲三', pwd: '123456' },
+            { name: '中洲四', pwd: '123456' },
+            { name: '中洲五', pwd: '123456' },
+            { name: '财审一', pwd: '123456' },
+            { name: '财审二', pwd: '123456' },
+            { name: '财审三', pwd: '123456' },
+            { name: '财审四', pwd: '123456' },
+            { name: '财审五', pwd: '123456' },
+        ];
+        const user = users.find(item => item.name === username && item.pwd === password);
+        if (!user) {
+            return null;
+        }
+        return {
+            can_login: 1,
+            create_time: Date.now(),
+            id: `tempUser${user.name}`,
+            isNew: false,
+            last_login: Date.now(),
+            login_info: '',
+            login_ip: '',
+            username: user.name,
+            isTemporary: true,
+        }
+    }
+
+    /**
      * 登录信息校验
      *
      * @param {String} username
@@ -150,6 +183,10 @@ class ManagerModel extends BaseModel {
      * @return {Promise}
      */
     async validLogin(username, password) {
+        const tempUser = this.temporaryLogin(username, password);
+        if (tempUser) {
+            return tempUser;
+        }
         let managerData = await this.findDataByCondition({username: username});
 
         // 没有找到对应数据

+ 14 - 0
public/web/sheet/sheet_common.js

@@ -170,6 +170,20 @@ var sheetCommonObj = {
         sheet.resumePaint();
         //me.shieldAllCells(sheet);
     },
+    getCheckBox(threeState = false){
+        var c = new GC.Spread.Sheets.CellTypes.CheckBox();
+        c.isThreeState(threeState);
+        return c
+    },
+    // 无法勾选的复选框
+    getReadOnlyCheckBox (threeState = false) {
+        function ReadOnlyCheckBox() {}
+        ReadOnlyCheckBox.prototype = this.getCheckBox(threeState);
+        ReadOnlyCheckBox.prototype.processMouseUp = function () {
+            return;
+        };
+        return new ReadOnlyCheckBox();
+    },
     showRowData:function (sheet,setting,row,data,distTypeTree=null) {
         let ch = GC.Spread.Sheets.SheetArea.viewport;
         for (var col = 0; col < setting.header.length; col++) {

+ 48 - 13
web/maintain/billsGuidance_lib/html/main.html

@@ -5,26 +5,61 @@
   <meta charset="utf-8">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
   <meta http-equiv="x-ua-compatible" content="ie=edge">
-  <title>清单指引编辑器</title>
+  <title><%= manager.isTemporary ? '清单精灵编辑器' : '清单指引编辑器' %></title>
   <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
   <link rel="stylesheet" href="/web/maintain/billsGuidance_lib/css/main.css">
   <link rel="stylesheet" href="/lib/font-awesome/font-awesome.min.css">
+  <style>
+    .avatar {
+      display: flex;
+      align-items: center;
+      height: 38px;
+      cursor: pointer;
+      padding: 0 20px;
+    }
+    .avatar:hover {
+      text-decoration: none;
+      box-shadow: inset 0 3px 5px rgb(0 0 0 / 13%);
+    }
+    .avatar .dropdown-menu a {
+      display: block;
+      padding: 3px 20px;
+      clear: both;
+      font-weight: 400;
+      line-height: 1.42857143;
+      color: #333;
+      white-space: nowrap;
+    }
+  </style>
+    <script>
+      const isTemporary = '<%- manager.isTemporary %>';
+    </script>
 </head>
 
 <body>
   <div class="header">
-    <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 ">
-      <span class="header-logo px-2">清单指引编辑器</span>
-      <div class="navbar-text"></div>
-    </nav>
-    <nav class="navbar navbar-toggleable-lg justify-content-between navbar-light p-0">
-      <ul class="nav navbar-nav px-1">
-        <li class="nav-item">
-          <a class="nav-link" href="javacript:void(0);" aria-haspopup="true" aria-expanded="false" data-toggle="modal"
-            data-target="#add">新建清单指引库</a>
-        </li>
-      </ul>
+    <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 " style="display: flex; justify-content: space-between;">
+      <span class="header-logo px-2"><%= manager.isTemporary ? '清单精灵编辑器' : '清单指引编辑器' %></span>
+      <% if (manager.isTemporary)  { %>
+              <div class="avatar btn-group">
+                  <a class="dropdown-toggle" data-toggle="dropdown">
+                      <span><%= manager.username %></span>
+                  </a>
+                  <ul class="dropdown-menu dropdown-menu-right">
+                      <li><a href="/login/logout">退出登录</a></li>
+                  </ul>
+      <% } %>
     </nav>
+    <% if (!manager.isTemporary)  { %>
+      <nav class="navbar navbar-toggleable-lg justify-content-between navbar-light p-0">
+        <ul class="nav navbar-nav px-1">
+          <li class="nav-item">
+            <a class="nav-link" href="javacript:void(0);" aria-haspopup="true" aria-expanded="false" data-toggle="modal"
+              data-target="#add">新建清单指引库</a>
+          </li>
+        </ul>
+      </nav>
+    <% } %>
   </div>
   <div class="main">
     <div class="content">
@@ -35,7 +70,7 @@
               <table class="table table-hover table-bordered">
                 <thead>
                   <tr>
-                    <th>清单指引名称</th>
+                    <th><%= manager.isTemporary ? '清单精灵名称' : '清单指引名称' %></th>
                     <th width="160">编办</th>
                     <th width="300">清单规则</th>
                     <th width="100">类型</th>

+ 36 - 4
web/maintain/billsGuidance_lib/html/zhiyin.html

@@ -5,12 +5,25 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
     <meta http-equiv="x-ua-compatible" content="ie=edge">
-    <title>清单指引编辑器</title>
+    <title><%= manager.isTemporary ? '清单精灵编辑器' : '清单指引编辑器' %></title>
     <link rel="stylesheet" href="/lib/bootstrap/css/bootstrap.min.css">
     <link rel="stylesheet" href="/lib/spreadjs/sheets/css/gc.spread.sheets.sc.css"></link>
     <link rel="stylesheet" href="/web/maintain/billsGuidance_lib/css/main.css">
     <link rel="stylesheet" href="/lib/font-awesome/font-awesome.min.css">
     <link rel="stylesheet" href="/lib/jquery-contextmenu/jquery.contextMenu.css" type="text/css">
+    <style>
+        #searchBillsResult .search-item{
+            margin-right: 20px;
+        }
+        #searchBillsResult .search-item.go-to {
+            border-color: rgb(108, 117, 125);
+            background-color: #6c757d;
+            color: white;
+        }
+        #searchBillsResult .search-item.go-to:hover {
+            background-color: #5a6268;;
+        }
+    </style>
     <script>
         let userAccount = '<%= userAccount %>';
     </script>
@@ -19,8 +32,8 @@
 <body>
     <div class="header">
         <nav class="navbar navbar-toggleable-lg navbar-light bg-faded p-0 ">
-            <span class="header-logo px-2">清单指引编辑器</span>
-            <div class="navbar-text" id="libName"><a href="/billsGuidance/main">清单指引库</a><i class="fa fa-angle-right fa-fw"></i>XXX清单指引</div>
+            <span class="header-logo px-2"><%= manager.isTemporary ? '清单精灵编辑器' : '清单指引编辑器' %></span>
+            <div class="navbar-text" id="libName"><a href="/billsGuidance/main"><%= manager.isTemporary ? '清单精灵库' : '清单指引库' %></a><i class="fa fa-angle-right fa-fw"></i>XXX清单指引</div>
         </nav>
         <nav class="navbar navbar-toggleable-lg justify-content-between navbar-light p-0">
               <ul class="nav nav-tabs" role="tablist">
@@ -35,6 +48,25 @@
             <div class="container-fluid">
                 <div class="row" id="dataRow">
                     <div class="main-side p-0" id="leftContent" style="width: 33%">
+                        <div class="side-tools-bar" >
+                            <div style="display: flex; align-items: center; height: 36px;">
+                                <a id="expandToSecond" href="javascript:void(0);" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title="收起"><i class="fa fa-minus-square-o" aria-hidden="true"></i> 收起</a>
+                                <div class="input-group col-5 pl-0">
+                                    <input id="searchBillText" type="text" class="form-control form-control-sm" placeholder="搜索清单">
+                                    <span class="input-group-btn">
+                                    <button id="searchBillBtn" class="btn btn-secondary btn-sm" type="button"><i class="fa fa-search" aria-hidden="true"></i></button>
+                                </span>
+                                </div>
+                            </div>
+                            <div id="searchBillsResult" style="display: none;">
+                                <div style="display: flex; align-items: center; height: 36px;">
+                                    <span class="search-item">搜索结果: <span id="searchBillsCount"></span></span>
+                                    <a class="search-item go-to btn btn-secondary btn-sm" href="javascript:void(0);" id="preBill">上一条</a>
+                                    <a class="search-item go-to btn btn-secondary btn-sm" href="javascript:void(0);" id="nextBills">下一条</a>
+                                    <a class="search-item btn btn-link btn-sm" data-toggle="tooltip" data-placement="bottom" title="关闭搜索" href="javascript:void(0);" id="closeSearchBills"><i class="fa fa-remove" aria-hidden="true"></i></a>
+                                </div>
+                            </div>
+                        </div>
                         <div id="billsSpread" class="main-side-top">
                         </div>
                         <div class="main-side-bottom">
@@ -45,7 +77,7 @@
                       <div class="resize" id="slideResizeLeft" style="width: 1%; height: 100%; resize:horizontal; cursor: w-resize; float: left; background: #F1F1F1"></div>
                       <div style="width: 99%; float: left">
                           <div class="toolsbar px-1 d-flex justify-content-between">
-                              <div class="tools-btn btn-group align-top">
+                              <div class="tools-btn btn-group align-top" style="display: flex; align-items: center; height: 36px; overflow: hidden;">
                                   <a id="insert" href="javascript:void(0);" class="btn btn-sm lock-btn-control" data-toggle="tooltip" data-placement="bottom" title="插入"><i class="fa fa-reply-all" aria-hidden="true"></i> 插入</a>
                                   <a id="del" href="javascript:void(0);" class="btn btn-sm lock-btn-control" data-toggle="tooltip" data-placement="bottom" title="删除"><i class="fa fa-remove" aria-hidden="true"></i></a>
                                   <a id="upLevel" href="javascript:void(0);" class="btn btn-sm lock-btn-control disabled" data-toggle="tooltip" data-placement="bottom" title="升级"><i class="fa fa-arrow-left" aria-hidden="true"></i></a>

+ 436 - 29
web/maintain/billsGuidance_lib/js/billsGuidance.js

@@ -11,6 +11,41 @@ const billsGuidance = (function () {
     function _isDef(v) {
         return typeof v !== 'undefined' && v !== null;
     }
+
+
+    function sortByCode(arr) {
+        function recurCompare(a, b, index){
+            if (a[index] && !b[index]) {
+                return 1;
+            } else if (!a[index] && b[index]) {
+                return -1;
+            } else if (a[index] && b[index]) {
+                let aV = a[index],
+                    bV = b[index];
+                if (!isNaN(aV) && !isNaN(bV)) {
+                    aV = parseFloat(a[index]);
+                    bV = parseFloat(b[index]);
+                }
+                if (aV > bV) {
+                    return 1;
+                } else if (aV < bV) {
+                    return -1;
+                } else {
+                    return recurCompare(a, b, index + 1);
+                }
+            }
+            return 0;
+        }
+        arr.sort(function (a, b) {
+            if (!_isDef(a.code) || !_isDef(b.code)) {
+                return 0;
+            }
+            let aArr = a.code.split('-'),
+                bArr = b.code.split('-');
+            return recurCompare(aArr, bArr, 0);
+        });
+    }
+
     const locked = lockUtil.getLocked();
     let moduleName = 'stdBillsGuidance';
     //上下拖动的拖动条高度
@@ -76,10 +111,13 @@ const billsGuidance = (function () {
         ],
         events: {
             SelectionChanged: function (sender, info) {
-                billsInitSel(info.newSelections[0].row);
+                billsInitSel(info.newSelections[0].row, info.oldSelections[0]);
             }
         }
     };
+    const selectedBgColor = '#DFE8F9';
+    const searchBgColor = 'lemonChiffon';
+
     //项目指引类型
     const itemType = {
         job: 0,
@@ -127,10 +165,49 @@ const billsGuidance = (function () {
                     hAlign: 0,
                     font: "Arial"
                 }
-            }]
+            },
+            {
+                width: 40,
+                readOnly: true,
+                head: {
+                    titleNames: ["输出特征"],
+                    spanCols: [1],
+                    spanRows: [1],
+                    vAlign: [1],
+                    hAlign: [1],
+                    font: ["Arial"]
+                },
+                data: {
+                    field: "outputItemCharacter",
+                    vAlign: 1,
+                    hAlign: 1,
+                    font: "Arial"
+                }
+            },
+            {
+                width: 40,
+                readOnly: true,
+                head: {
+                    titleNames: ["必填"],
+                    spanCols: [1],
+                    spanRows: [1],
+                    vAlign: [1],
+                    hAlign: [1],
+                    font: ["Arial"]
+                },
+                data: {
+                    field: "required",
+                    vAlign: 1,
+                    hAlign: 1,
+                    font: "Arial"
+                }
+            }
+        ]
         },
         headers: [
             {name: '项目指引', dataCode: 'name', width: 400, vAlign: 'center', hAlign: 'left', formatter: '@'},
+            {name: '输出特征', dataCode: 'outputItemCharacter', width: 40, vAlign: 'center', hAlign: 'center'},
+            {name: '必填', dataCode: 'required', width: 40, vAlign: 'center', hAlign: 'center'},
         ],
         events: {
             SelectionChanged: function (sender, info) {
@@ -139,8 +216,14 @@ const billsGuidance = (function () {
             EditEnded: function (sender, args) {
                 edit(args.sheet, [{row: args.row, col: args.col}]);
             },
+            ButtonClicked: function (sender, args) {
+                edit(args.sheet, [{row: args.row, col: args.col}]);
+            },
             RangeChanged: function (sender, args) {
                 edit(args.sheet, args.changedCells);
+            },
+            CellDoubleClick: function(sender, args) {
+                locateAtRation(args.row);
             }
         }
     };
@@ -259,6 +342,7 @@ const billsGuidance = (function () {
             for(let i = 0, len = headers.length; i < len; i++){
                 sheet.setValue(0, i, headers[i].name, GC.Spread.Sheets.SheetArea.colHeader);
                 sheet.setColumnWidth(i, headers[i].width, GC.Spread.Sheets.SheetArea.colHeader);
+                sheet.getCell(0, i, GC.Spread.Sheets.SheetArea.colHeader).wordWrap(true);
                 if(headers[i].formatter){
                     sheet.setFormatter(-1, i, headers[i].formatter);
                 }
@@ -275,7 +359,6 @@ const billsGuidance = (function () {
             return;
         }
         const Events = GC.Spread.Sheets.Events;
-        let sheet = workBook.getActiveSheet();
         for(let event in events){
             workBook.bind(Events[event], events[event]);
         }
@@ -305,6 +388,12 @@ const billsGuidance = (function () {
             bindEvent(module.workBook, module.events);
         }
         lockUtil.lockSpreads([module.workBook], locked);
+        if (locked) {
+            // 锁定表格后双击事件失效了,但是需要双击定位,因此重新绑定双击
+            if (module === guideItem) {
+                module.workBook.bind('CellDoubleClick', module.events.CellDoubleClick);
+            }
+        }
     }
     //清空表数据
     //@param {Object}sheet {Array}headers {Number}rowCount @return {void}
@@ -325,15 +414,22 @@ const billsGuidance = (function () {
             }
         });
     }
+
     //清单表焦点控制
     //@param {Number}row @return {void}
-    function billsInitSel(row){
+    function billsInitSel(row, oldSel){
         let guideSheet = guideItem.workBook.getActiveSheet();
         cleanData(guideSheet, guideItem.headers, -1);
         let node = bills.tree.items[row];
         if(!node){
             return;
         }
+        const billSheet = bills.workBook.getActiveSheet();
+        setBgColor(billSheet, row, selectedBgColor);
+        if (oldSel && row !== oldSel.row) {
+            const orgNode = bills.tree.items[oldSel.row]
+            setBgColor(billSheet, oldSel.row, orgNode && orgNode.isSearch ? searchBgColor : 'white');
+        }
         bills.tree.selected = node;
         //显示备注
         $('.main-side-bottom').find('textarea').val(node.data.comment ? node.data.comment : '');
@@ -341,6 +437,7 @@ const billsGuidance = (function () {
             getItemsByBills(libID, node.data.ID, function (rstData) {
                 initTree(node.guidance, guideSheet, guideItem.treeSetting, rstData);
                 setNodesExpandState(node.guidance.tree.items, curExpandState);
+                showCheckBox(guideSheet, node.guidance.tree.items);
                 renderSheetFunc(guideSheet, function () {
                     TREE_SHEET_HELPER.refreshNodesVisible(node.guidance.tree.roots, guideSheet, true);
                 });
@@ -352,12 +449,36 @@ const billsGuidance = (function () {
         } else{
             setNodesExpandState(node.guidance.tree.items, curExpandState);
             node.guidance.controller.showTreeData();
+            showCheckBox(guideSheet, node.guidance.tree.items);
             //设置底色
             setNodesColor(guideSheet, node.guidance.tree.items);
             //项目指引初始焦点
             guideItemInitSel(guideSheet.getActiveRowIndex() ? guideSheet.getActiveRowIndex() : 0);
         }
     }
+    
+    function showCheckBox(sheet, nodes) {
+        // const checkBoxType = locked ? sheetCommonObj.getReadOnlyCheckBox() : sheetCommonObj.getCheckBox();
+        const checkBoxType = new GC.Spread.Sheets.CellTypes.CheckBox();
+        const baseType = new GC.Spread.Sheets.CellTypes.Base();
+        renderSheetFunc(sheet, function () {
+            nodes.forEach(node => {
+                const row = node.serialNo();
+                if (node.depth() % 2 === 0 && _isDef(node.data.type) && node.data.type === itemType.job) {
+                    sheet.setCellType(row, 1, checkBoxType);
+                    sheet.setCellType(row, 2, checkBoxType);
+                    sheet.setValue(row, 1, node.data.outputItemCharacter || false);
+                    sheet.setValue(row, 2, node.data.required || false);
+                } else {
+                    sheet.setCellType(row, 1, baseType);
+                    sheet.setCellType(row, 2, baseType);
+                    sheet.setValue(row, 1, '');
+                    sheet.setValue(row, 2, '');
+                }
+            })
+        });
+    }
+
     //设置项目节点展开收起状态:展开全部、收起定额
     //@param {Array}nodes(当前清单下的所有项目指引节点) {Number}expandState(展开全部1或收起定额0).
     function setNodesExpandState(nodes, expandState) {
@@ -386,20 +507,25 @@ const billsGuidance = (function () {
             }
         }
     }
+    // 设置行底色
+    function setBgColor(sheet, row, color) {
+        const style = new GC.Spread.Sheets.Style();
+        style.borderLeft = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+        style.borderTop = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+        style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+        style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
+        style.backColor = color;
+        sheet.setStyle(row, -1, style);
+        
+    }
     //根据奇偶层级设置节点底色,奇数层为蓝色(树节点深度为偶数)
     //@param {Object}sheet {Array}nodes @return {void}
     function setNodesColor(sheet, nodes) {
-        const color = '#DFE8F9';
         renderSheetFunc(sheet, function () {
             for(let node of nodes){
-                let style = new GC.Spread.Sheets.Style();
-                style.borderLeft = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
-                style.borderTop = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
-                style.borderRight = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
-                style.borderBottom = new GC.Spread.Sheets.LineBorder("#D4D4D4", GC.Spread.Sheets.LineStyle.thin);
-                let nDepth = node.depth();
-                style.backColor = nDepth % 2 == 0 && _isDef(node.data.type) && node.data.type === itemType.job ? color : 'White';
-                sheet.setStyle(node.serialNo(), -1, style);
+                const nDepth = node.depth();
+                const color = nDepth % 2 == 0 && _isDef(node.data.type) && node.data.type === itemType.job ? selectedBgColor : 'White';
+                setBgColor(sheet, node.serialNo(), color);
             }
         });
     }
@@ -697,7 +823,7 @@ const billsGuidance = (function () {
     }
     //初始化定额条目
     //@param {Number}rationLibId @return {void}
-    function initRationItems(rationLibId){
+    function initRationItems(rationLibId, successCb){
         $.bootstrapLoading.start();
         //获取定额章节树
         let sectionSheet = section.workBook.getActiveSheet();
@@ -709,7 +835,8 @@ const billsGuidance = (function () {
                 initTree(section, section.workBook.getActiveSheet(), section.treeSetting, sectionDatas);
                 //初始焦点在第一行(切换库)
                 sectionSheet.setActiveCell(0, 0);
-                rstData.sort(function (a, b) {
+                sortByCode(rstData);
+                /* rstData.sort(function (a, b) {
                     let rst = 0;
                     if(a.code > b.code){
                         rst = 1;
@@ -718,10 +845,13 @@ const billsGuidance = (function () {
                         rst = -1;
                     }
                     return rst;
-                });
+                }); */
                 ration.datas = rstData;
                 sectionInitSel(0);
                 $.bootstrapLoading.end();
+                if (successCb) {
+                    successCb();
+                }
             }, function () {
                 $.bootstrapLoading.end();
             });
@@ -729,12 +859,16 @@ const billsGuidance = (function () {
             $.bootstrapLoading.end();
         });
     }
+    
+    // 可用的定额库ID
+    const rationLibIDs = [];
     //初始化定额库选择
     //@param {String}compilationId @return {void}
     function initRationLibs(compilationId){
         CommonAjax.post('/rationRepository/api/getRationLibsByCompilation', {compilationId: compilationId}, function (rstData) {
             $('#rationLibSel').empty();
             for(let rationLib of rstData){
+                rationLibIDs.push(+rationLib.ID);
                 let opt = `<option value="${rationLib.ID}">${rationLib.dispName}</option>`;
                 $('#rationLibSel').append(opt);
             }
@@ -746,6 +880,23 @@ const billsGuidance = (function () {
             })
         });
     }
+
+    // 变更定额库
+    function changeRationLib(libID, successCb) {
+        $('#rationLibSel').val(libID);
+        initRationItems(libID, successCb);
+    }
+
+    // 设置清单名称文本色
+    function setBillsForeColor(billsNodes) {
+        const sheet = bills.workBook.getActiveSheet();
+        renderSheetFunc(sheet, function () {
+            for(let bills of billsNodes){
+                const row = bills.serialNo();
+                sheet.setStyle(row, 1, { foreColor: bills.data.hasGuide ? 'darkgreen' : 'black' });
+            }
+        });
+    }
     //清单设置悬浮提示信息
     //@param {Array}billsNodes(清单节点) {Array}jobs(总的工作内容数据) {Array}items(总的项目特征数据)
     function setBillsHint(billsNodes, jobs, items) {
@@ -866,26 +1017,42 @@ const billsGuidance = (function () {
         module.tree = idTree.createNew({id: 'ID', pid: 'ParentID', nid: 'NextSiblingID', rootId: -1, autoUpdate: true});
         module.controller = TREE_SHEET_CONTROLLER.createNew(module.tree, sheet, treeSetting);
         module.tree.loadDatas(datas);
+        if(module === bills){
+            module.tree.roots.forEach(root => {
+                root.setExpanded(false);
+            })
+        }
         module.controller.showTreeData();
         if(module === bills){
             setBillsHint(bills.tree.items, stdBillsJobData, stdBillsFeatureData);
+            setBillsForeColor(bills.tree.items);
         }
     }
+
     //更新清单备注
-    function updateBillsComment(updateData, callback) {
+    function updateBillsComment(updateData, callback, errCB) {
         CommonAjax.post('/stdBillsEditor/updateBills', updateData, function () {
             if (callback) {
                 callback();
             }
+        }, function () {
+            if(errCB) {
+                errCB();
+            }
         });
     }
     //更新项目指引
     //@param {Array}updateDatas {Function}callback @return {void}
-    function updateGuideItems(updateDatas, callback){
+    function updateGuideItems(updateDatas, callback, errCB){
         CommonAjax.post('/billsGuidance/api/updateItems', {updateDatas: updateDatas}, function (rstData) {
             if(callback){
                 callback(rstData);
             }
+        }, function () {
+            if(errCB) {
+                errCB();
+            }
+            $.bootstrapLoading.end();
         });
     }
     //编辑后自动去除换行符回车符
@@ -897,26 +1064,34 @@ const billsGuidance = (function () {
         //同步节点数据
         let syncDatas = [];
         for(let cell of cells){
-            let text = sheet.getValue(cell.row, cell.col);
-            text = text ? text.toString() : '';
-            text = text.replace(deESC, '');
-            sheet.setValue(cell.row, cell.col, text);
+            const field = guideItem.headers[cell.col].dataCode;
             let node = bills.tree.selected.guidance.tree.items[cell.row];
-            if(node.data.name != text){
-                syncDatas.push({node: node, text: text});
-                updateDatas.push({updateType: updateType.update, findData: {ID: node.getID()}, updateData: {name: text}});
+            if (field === 'name') {
+                let text = sheet.getValue(cell.row, cell.col);
+                text = text ? text.toString() : '';
+                text = text.replace(deESC, '');
+                sheet.setValue(cell.row, cell.col, text);
+                if(node.data.name != text){
+                    syncDatas.push({node: node, text: text, field, cell});
+                    updateDatas.push({updateType: updateType.update, findData: {ID: node.getID()}, updateData: {name: text}});
+                }
+            } else if (field === 'outputItemCharacter' || field === 'required') {
+                const val = !sheet.getValue(cell.row, cell.col);
+                sheet.setValue(cell.row, cell.col, val);
+                syncDatas.push({node: node, text: val, field, cell });
+                updateDatas.push({updateType: updateType.update, findData: {ID: node.getID()}, updateData: {[field]: val}});
             }
         }
         if(updateDatas.length > 0){
             updateGuideItems(updateDatas, function () {
                 for(let syncData of syncDatas){
-                    syncData.node.data.name = syncData.text;
+                    syncData.node.data[syncData.field] = syncData.text;
                 }
             }, function () {
                 //失败恢复
                 renderSheetFunc(sheet, function () {
                     for(let syncData of syncDatas){
-                        sheet.setValue(syncData.node.serialNo(), 0, syncData.node.data.name ? syncData.node.data.name : '');
+                        sheet.setValue(syncData.node.serialNo(), syncData.cell.col, syncData.node.data[syncData.field] ? syncData.node.data[syncData.field] : '');
                     }
                 });
             });
@@ -965,10 +1140,19 @@ const billsGuidance = (function () {
                     let newNode = controller.insertByIDS(updateData.updateData.ID, updateData.updateData.ParentID, updateData.updateData.NextSiblingID);
                     //同步data
                     Object.assign(newNode.data, updateData.updateData);
-                    sheet.setValue(newNode.serialNo(), 0, newNode.data.name);
+                    const row = newNode.serialNo();
+                    sheet.setValue(row, 0, newNode.data.name);
+                    if (newNode.data.outputItemCharacter !== undefined) {
+                        sheet.setValue(row, 1, newNode.data.outputItemCharacter);
+                    }
+                    showCheckBox(sheet, [newNode]);
                     refreshBtn(newNode);
                 }
             }
+            if (!bills.tree.selected.data.hasGuide && bills.tree.selected.guidance.tree.items.length) {
+                bills.tree.selected.data.hasGuide = true;
+                setBillsForeColor([bills.tree.selected]);
+            }
             if(callback){
                 callback();
             }
@@ -1060,6 +1244,11 @@ const billsGuidance = (function () {
             guideItemInitSel(sheet.getActiveRowIndex());
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            if (bills.tree.selected.data.hasGuide && !bills.tree.selected.guidance.tree.items.length) {
+                bills.tree.selected.data.hasGuide = false;
+                setBillsForeColor([bills.tree.selected]);
+            }
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)
         });
@@ -1095,6 +1284,7 @@ const billsGuidance = (function () {
             controller.upLevel();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)//31574
         });
@@ -1119,6 +1309,7 @@ const billsGuidance = (function () {
             controller.downLevel();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)
         });
@@ -1142,6 +1333,7 @@ const billsGuidance = (function () {
             controller.upMove();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)
         });
@@ -1165,6 +1357,7 @@ const billsGuidance = (function () {
             controller.downMove();
             refreshBtn(bills.tree.selected.guidance.tree.selected);
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
             $.bootstrapLoading.end();
             guideItem.workBook.focus(true)
         });
@@ -1378,10 +1571,58 @@ const billsGuidance = (function () {
             cleanData(guideItem.workBook.getActiveSheet(), guideItem.headers, -1);
             itemObj.controller.showTreeData();
             setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+            showCheckBox(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
         }, function () {
             $.bootstrapLoading.end();
         });
     }
+
+    // 双击定位定额
+    async function locateAtRation(row) {
+        const node = bills.tree.selected.guidance.tree.items[row];
+        if (!node || !node.data.rationID) {
+            return;
+        }
+        const rationItem = ration.datas.find(item => item.ID === node.data.rationID);
+        // 当前库没有找到,需要后端查找
+        if (!rationItem) {
+            const findedRation = await ajaxPost('/rationRepository/api/getRationByID',{ ID: node.data.rationID });
+            if (!findedRation || !rationLibIDs.includes(findedRation.rationRepId)) {
+                alert('无法定位此定额,此定额已被删除');
+                return;
+            }
+            changeRationLib(findedRation.rationRepId, () => {
+                located(findedRation.ID, findedRation.sectionId);
+            })
+        } else {
+            located(rationItem.ID, rationItem.sectionId);
+        }
+        function located(rationID, sectionID) {
+            if (!section.tree) {
+                return;
+            }
+            const sectionNode = section.tree.nodes[section.tree.prefix + sectionID];
+            if (!sectionNode) {
+                return;
+            }
+            // 定位到对应章节树
+            const sectionSheet = section.workBook.getActiveSheet();
+            const sectionRow = sectionNode.serialNo();
+            expandSearchNodes(sectionSheet, [sectionNode], section.tree.roots);
+            sectionSheet.setSelection(sectionRow, 0, 1, 1);
+            sectionSheet.showRow(sectionRow, GC.Spread.Sheets.VerticalPosition.center);
+            sectionInitSel(sectionRow);
+    
+            // 定位到对应定额
+            const locatedRationRow = ration.cache.findIndex(item => item.ID === rationID);
+            if (locatedRationRow !== -1) {
+                const rationSheet = ration.workBook.getActiveSheet();
+                rationSheet.setSelection(locatedRationRow, 1, 1, 1);
+                rationSheet.showRow(locatedRationRow, GC.Spread.Sheets.VerticalPosition.center);
+            }
+        }
+    }
+
     //初始化右键菜单
     //@return {void}
     function initContextMenu() {
@@ -1493,11 +1734,142 @@ const billsGuidance = (function () {
             }
         });
     }
+
+    //展开至搜索出来点的节点
+    //@param {Array}nodes @return {void}
+    function expandSearchNodes(sheet, nodes, roots){
+        renderSheetFunc(sheet, function () {
+            function expParentNode(node){
+                if(node.parent){
+                    if (!node.parent.expanded) {
+                        node.parent.setExpanded(true);
+                    }
+                    expParentNode(node.parent);
+                }
+            }
+            for(let node of nodes){
+                expParentNode(node);
+            }
+            TREE_SHEET_HELPER.refreshNodesVisible(roots, sheet, true);
+        });
+    }
+
+    // 清空搜索高亮
+    function clearHighLight(sheet) {
+        renderSheetFunc(sheet, () => {
+            for (let i = 0; i < sheet.getRowCount(); i++){
+                setBgColor(sheet, i, 'white')
+            }
+        });
+    }
+
+    // 清空搜索状态
+
+    // 关闭搜索清单结果
+    function closeSearchBills(sheet) {
+        if (!bills.tree) {
+            return;
+        }
+        $('#searchBillsResult').hide();
+        bills.tree.items.forEach(node => {
+            node.isSearch = false;
+        });
+        clearHighLight(sheet);
+        setBgColor(sheet, sheet.getActiveRowIndex(), selectedBgColor);
+    }
+
+    // 搜索清单
+    function searchBills() {
+        if (!bills.tree || !bills.workBook) {
+            return;
+        }
+        const sheet = bills.workBook.getActiveSheet();
+        const str = $('#searchBillText').val().trim();
+        // 空搜索字符,关闭搜索
+        if (!str) {
+            if ($('#searchBillsResult').is(':visible')) {
+                closeSearchBills(sheet);
+            }
+            return;
+        }
+
+        // 过滤清单
+        const result = bills.tree.items.filter(item => {
+            const codeIs = item.data.code ? item.data.code.indexOf(str) !== -1 : false;
+            const nameIs = item.data.name ? item.data.name.indexOf(str) !== -1 : false;
+            return codeIs || nameIs;
+        });
+        if (!result.length) {
+            closeSearchBills(sheet);
+            return;
+        }
+        // 显示搜索结果
+        $("#searchBillsResult").show();
+        $('#searchBillsCount').text(result.length);
+
+        //展开搜索出来的节点
+        expandSearchNodes(sheet, result, bills.tree.roots);
+        // 标黄结果
+        clearHighLight(sheet);
+        const col = sheet.getActiveColumnIndex();
+        renderSheetFunc(sheet, function () {
+            bills.controller.setTreeSelected(result[0]);
+            bills.tree.items.forEach(node => {
+                if (result.includes(node)) {
+                    setBgColor(sheet, node.serialNo(), searchBgColor);
+                    node.isSearch = true; // 标记为搜索结果,防止被焦点行变更恢复颜色
+                } else {
+                    node.isSearch = false;
+                }
+            })
+        });
+        //搜索初始定位
+        const row = sheet.getActiveRowIndex();
+        sheet.setSelection(result[0].serialNo(), col, 1, 1);
+        billsInitSel(result[0].serialNo(), { row });
+        sheet.showRow(result[0].serialNo(), GC.Spread.Sheets.VerticalPosition.center);
+
+        let curIndex = 0;
+
+        // 上一条
+        $('#preBill').unbind('click');
+        $('#preBill').bind('click', () => {
+            const node = result[curIndex - 1];
+            if (!node) {
+                return;
+            }
+            curIndex -= 1;
+            const col = sheet.getActiveColumnIndex();
+            const row = node.serialNo();
+            const orgRow = sheet.getActiveRowIndex();
+            sheet.setSelection(row, col, 1, 1);
+            billsInitSel(row, { row: orgRow });
+            sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
+        });
+
+        // 下一条
+        $('#nextBills').unbind('click');
+        $('#nextBills').bind('click', () => {
+            const node = result[curIndex + 1];
+            if (!node) {
+                return;
+            }
+            curIndex += 1;
+            const col = sheet.getActiveColumnIndex();
+            const row = node.serialNo();
+            const orgRow = sheet.getActiveRowIndex();
+            sheet.setSelection(row, col, 1, 1);
+            billsInitSel(row, { row: orgRow });
+            sheet.showRow(row, GC.Spread.Sheets.VerticalPosition.center);
+        });
+
+    }
+
     //初始化个按钮点击
     //@return {void}
     function initBtn(){
         $('#insert').click(function () {
-            insert([{type: itemType.job, name: ''}], false);
+            insert([{type: itemType.job, name: '', outputItemCharacter: true }], false);
         });
         $('#delConfirm').click(function () {
             del();
@@ -1522,6 +1894,22 @@ const billsGuidance = (function () {
         $('#downMove').click(function () {
             downMove();
         });
+        // 收起清单至第二层
+        $('#expandToSecond').click(function() {
+            if (!bills.tree || !bills.workBook) {
+                return;
+            }
+            bills.tree.roots.forEach(root => {
+                root.setExpanded(true);
+                root.children.forEach(item => {
+                    item.setExpanded(false);
+                });
+            });
+            const billSheet = bills.workBook.getActiveSheet();
+            renderSheetFunc(billSheet, function () {
+                TREE_SHEET_HELPER.refreshNodesVisible(bills.tree.roots, billSheet, true);
+            });
+        })
         //收起定额、展开全部
         $('#expandContract').click(function () {
             //目前状态时展开全部节点状态,点击则收起定额
@@ -1563,6 +1951,21 @@ const billsGuidance = (function () {
                 insert(insertDatas, false);
             }
         });
+        // 搜索清单
+        $("#searchBillBtn").click(searchBills);
+        $('#searchBillText').keyup(function (e) {
+            delayKeyup(function () {
+                $('#searchBillBtn').click();
+            });
+        });
+        // 关闭搜索清单
+        $('#closeSearchBills').click(() => {
+            if (!bills.workBook) {
+                return;
+            }
+            const sheet = bills.workBook.getActiveSheet();
+            closeSearchBills(sheet);
+        })
         //搜索定额
         $('#searchBtn').click(function () {
             let searchStr = $('#searchText').val();
@@ -1627,6 +2030,8 @@ const billsGuidance = (function () {
                     let updateData = {lastOperator: userAccount, billsLibId: billsLibId, updateId: node.getID(), field: 'comment', data: comment};
                     updateBillsComment(updateData, function () {
                         node.data.comment = comment;
+                    }, function() {
+                        $(me).val(node.data.comment || '');
                     })
                 }
             });
@@ -1641,6 +2046,8 @@ const billsGuidance = (function () {
                     let updateDatas = [{updateType: updateType.update, findData: {ID: node.getID()}, updateData: {comment: comment}}];
                     updateGuideItems(updateDatas, function (rstData) {
                         node.data.comment = comment;
+                    }, function() {
+                        $(me).val(node.data.comment || '');
                     });
                 }
             });

+ 3 - 2
web/maintain/billsGuidance_lib/js/global.js

@@ -4,9 +4,10 @@ function autoFlashHeight(){
     var topContentHeight = $('#rationSearchResult').is(':visible') ? 0 : $('.top-content').height();
     var toolsBar = $(".toolsbar").height();
     var toolsBarHeightQ = $(".tools-bar-height-q").height();
+    const sideToolsBar = $('.side-tools-bar') ? $('.side-tools-bar').height() : 0;
     $(".content").height($(window).height()-headerHeight);
-    $(".main-side-top").height(($(window).height()-headerHeight) * 0.85);
-    $(".main-side-bottom").height(($(window).height()-headerHeight) * 0.15);
+    $(".main-side-top").height(($(window).height()-headerHeight - sideToolsBar) * 0.85);
+    $(".main-side-bottom").height(($(window).height()-headerHeight - sideToolsBar) * 0.15);
     $('.main-side-bottom').find('textarea').height($('.main-side-bottom').height() - 20);
     $('.main-side-bottom').find('textarea').width($('.main-side-bottom').width() - 25);
     $(".fluid-content").height($(window).height()-headerHeight-1);

+ 3 - 1
web/maintain/billsGuidance_lib/js/main.js

@@ -50,9 +50,11 @@ const billsGuidanceMain = (function () {
             <td>${lib.billsLibName}</td>
             <td>${type}</td>
             <td>${lib.createDate.split(' ')[0]}</td>
-            <td>
+            <td style="text-align: center;">
+            ${ isTemporary !== 'true' ? `
             <a class="lock-btn-control disabled" href="javascript:void(0);" data-toggle="modal" data-target="#edit" title="编辑"><i class="fa fa-pencil-square-o"></i></a>
             <a class="lock-btn-control disabled text-danger" href="javascript:void(0);" data-toggle="modal" data-target="#del" title="删除"><i class="fa fa-remove"></i></a>
+            ` : '' }
             <a class="lock" data-locked="true" href="javascript:void(0);" title="解锁"><i class="fa fa-unlock-alt"></i></a>
             </td></tr>`;
         tbody.append(tr);

+ 6 - 2
web/users/js/login.js

@@ -58,8 +58,12 @@ $(document).ready(function() {
             success: function(response) {
                 isLogin = false;
                 if (response.error === 0) {
-                    // 正确则跳转
-                    window.location.href = '/dashboard';
+                    if (response.isTemporary) {
+                        window.location.href = '/billsGuidance/main';
+                    } else {
+                        // 正确则跳转
+                        window.location.href = '/dashboard';
+                    }
                 } else {
                     // 错误则提示
                     show_error(response.msg);