| 
					
				 | 
			
			
				@@ -765,6 +765,152 @@ const showSelectTab = function(select, spread, afterShow) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         return {spread: resultSpread}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    $.ledgerSearch = function (setting) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!setting.selector || !setting.ledger || !setting.resultSpreadSetting) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!setting.ledger.billsTree) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!setting.ledger.pos && !setting.ledger.getLedgerPos) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!setting.searchRangeStr) setting.searchRangeStr = '项目节编号/清单编号/名称/台账数量'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (!setting.keyId) setting.keyId = 'ledger_id'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const ledger = setting.ledger; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const resultId = setting.id + '-search-result'; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const obj = $(setting.selector); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let filter = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        if (setting.searchOver || setting.searchEmpty) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            filter.push('<select class="input-group-text" id="search-filter">'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            filter.push('<option value="">台账</option>'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (setting.customSearch) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for (const cs of setting.customSearch) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (cs.valid) filter.push('<option value="' + cs.key + '">' + cs.title + '</option>'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            filter.push('</select>'); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        obj.html( 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                        <div class="sjs-bar">\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                            <div class="input-group input-group-sm pb-1">\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                                <div class="input-group-prepend">\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            filter.join('') + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                                </div>' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                                <input id="searchKeyword" type="text" class="form-control" autocomplete="off" placeholder="可查找 ' + setting.searchRangeStr + '" aria-label="Recipient\'s username" aria-describedby="button-addon2">\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                                <div class="input-group-append">\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                                    <button class="btn btn-outline-secondary" type="button"">搜索</button>\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                                </div>\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                            </div>\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                        </div>\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                        <div id="' + resultId + '" class="sjs-sh">\n' + 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            '                        </div>' 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        ); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        autoFlashHeight(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const resultSpread = SpreadJsObj.createNewSpread($('#' + resultId)[0]); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        SpreadJsObj.initSheet(resultSpread.getActiveSheet(), setting.resultSpreadSetting); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const searchSheet = setting.searchSpread.getActiveSheet(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        let searchResult = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const defaultCheck = function(node, keyword) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const keyNum = _.toNumber(keyword); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return (node.code && node.code.indexOf(keyword) > -1) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (node.b_code && node.b_code.indexOf(keyword) > -1) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (node.name && node.name.indexOf(keyword) > -1) || 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                (!_.isNaN(keyNum) && checkZero(ZhCalc.sub(keyNum, node.quantity))); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const getCheckFun = function (key) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const cs = setting.customSearch.find(function (x) {return x.key === key}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return cs ? [cs.billsCheck, cs.posCheck] : [null, null]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const search = function () { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const filter = $('#search-filter').val(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const [billsCheck, posCheck] = filter ? getCheckFun(filter) : [null, null]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            searchCustom(billsCheck || defaultCheck, posCheck || defaultCheck); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const get18Bw = function(tree, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let parent = tree.getParent(data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            while (parent && (!parent.code || /^[a-zA-Z]/.test(parent.code || ''))) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                parent = tree.getParent(parent); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return parent ? parent.name : ''; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const get08Bw = function(tree, data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let parent = tree.getParent(data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            let lastXmj = '', level4Xmj = ''; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            while (parent) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (parent.code && !lastXmj) lastXmj = parent.name; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (parent.code && parent.level === 4) level4Xmj = parent.name; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                parent = tree.getParent(parent); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            return level4Xmj || lastXmj; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const getBw = function (data) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!data.b_code) return ''; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const sortTree = ledger.billsTree; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!sortTree.checkCodeType) return ''; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (sortTree.checkCodeType() === '18') { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return get18Bw(sortTree, data) 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                return get08Bw(sortTree, data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const searchCustom = function (billsCheckFun, posCheckFun) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const keyword = $('#searchKeyword', obj).val(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            searchResult = []; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (const node of ledger.billsTree.nodes) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (billsCheckFun(node, keyword)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    const convertData = { lid: node[ledger.billsTree.setting.id], pid: -1 }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    for (const col of setting.resultSpreadSetting.cols) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        if (col.field === 'bw') { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            convertData.bw = getBw(node); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } else { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            convertData[col.field] = node[col.field]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    searchResult.push(data); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                const posRange = ledger.pos ? ledger.pos.getLedgerPos(node.id) : ledger.getLedgerPos(node); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                if (!posRange || posRange.length === 0) continue; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                for (const p of posRange) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    if (posCheckFun(p, keyword)) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        const convertData = { lid: node[ledger.billsTree.setting.id], pid: p[ledger.pos.setting.id] }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        for (const col of setting.resultSpreadSetting.cols) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                            convertData[col.field] = p[col.field]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                        searchResult.push(convertData); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                    } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            calculateCompletePercent(searchResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            calculateSum(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            SpreadJsObj.loadSheetData(resultSpread.getActiveSheet(), 'data', searchResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const calculateSum = function () { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!searchResult || searchResult.length === 0 || !setting.calcSum) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const sum = setting.calcSum(searchResult); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (sum) searchResult.unshift(sum); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        const calculateCompletePercent = function (searchResult) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!searchResult) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            for (const sr of searchResult) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                const base = ZhCalc.add(sr.total_price, sr.end_qc_tp); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+                sr.complete_percent = base !== 0 ? ZhCalc.mul(ZhCalc.div(sr.end_gather_tp, base), 100, 2) : 0; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        $('input', obj).bind('keydown', function (e) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (e.keyCode == 13) search(); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        $('button', obj).bind('click', () => {search()}); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        resultSpread.getActiveSheet().bind(spreadNS.Events.CellDoubleClick, function (e, info) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const sheet = info.sheet; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const data = sheet.zh_data; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!data) { return } 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+ 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            const curBills = data[info.row]; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            if (!curBills) return; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+            setting.locate(curBills); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        }); 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+        return {spread: resultSpread}; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				+    }; 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				  
			 | 
		
	
		
			
				 | 
				 | 
			
			
				     $.xmjSearch = function (setting) { 
			 | 
		
	
		
			
				 | 
				 | 
			
			
				         if (!setting.selector || !setting.searchSpread) return; 
			 |