Explorar o código

指标对比1.0

MaiXinRong %!s(int64=7) %!d(string=hai) anos
pai
achega
f19986ccfe

+ 3 - 0
app/controller/compare_controller.js

@@ -24,6 +24,9 @@ module.exports = app => {
             };
             await this.layout('compare/index.ejs', renderData, 'compare/modal.ejs');
         }
+        // async search (ctx) {
+        //     const
+        // }
     }
 
     return CompareController;

+ 287 - 0
app/public/css/jquery-contextmenu/jquery.contextMenu.css

@@ -0,0 +1,287 @@
+@charset "UTF-8";
+/*!
+ * jQuery contextMenu - Plugin for simple contextMenu handling
+ *
+ * Version: v2.4.5
+ *
+ * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF)
+ * Web: http://swisnl.github.io/jQuery-contextMenu/
+ *
+ * Copyright (c) 2011-2017 SWIS BV and contributors
+ *
+ * Licensed under
+ *   MIT License http://www.opensource.org/licenses/mit-license
+ *
+ * Date: 2017-05-05T14:40:37.763Z
+ */
+@-webkit-keyframes cm-spin {
+  0% {
+    -webkit-transform: translateY(-50%) rotate(0deg);
+            transform: translateY(-50%) rotate(0deg);
+  }
+  100% {
+    -webkit-transform: translateY(-50%) rotate(359deg);
+            transform: translateY(-50%) rotate(359deg);
+  }
+}
+@-o-keyframes cm-spin {
+  0% {
+    -webkit-transform: translateY(-50%) rotate(0deg);
+         -o-transform: translateY(-50%) rotate(0deg);
+            transform: translateY(-50%) rotate(0deg);
+  }
+  100% {
+    -webkit-transform: translateY(-50%) rotate(359deg);
+         -o-transform: translateY(-50%) rotate(359deg);
+            transform: translateY(-50%) rotate(359deg);
+  }
+}
+@keyframes cm-spin {
+  0% {
+    -webkit-transform: translateY(-50%) rotate(0deg);
+         -o-transform: translateY(-50%) rotate(0deg);
+            transform: translateY(-50%) rotate(0deg);
+  }
+  100% {
+    -webkit-transform: translateY(-50%) rotate(359deg);
+         -o-transform: translateY(-50%) rotate(359deg);
+            transform: translateY(-50%) rotate(359deg);
+  }
+}
+
+@font-face {
+  font-family: "context-menu-icons";
+  font-style: normal; 
+  font-weight: normal;
+
+  src: url("font/context-menu-icons.eot?lnvb");
+  src: url("font/context-menu-icons.eot?lnvb#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?lnvb") format("woff2"), url("font/context-menu-icons.woff?lnvb") format("woff"), url("font/context-menu-icons.ttf?lnvb") format("truetype");
+}
+
+.context-menu-icon-add:before {
+  content: "\EA01";
+}
+
+.context-menu-icon-copy:before {
+  content: "\EA02";
+}
+
+.context-menu-icon-cut:before {
+  content: "\EA03";
+}
+
+.context-menu-icon-delete:before {
+  content: "\EA04";
+}
+
+.context-menu-icon-edit:before {
+  content: "\EA05";
+}
+
+.context-menu-icon-loading:before {
+  content: "\EA06";
+}
+
+.context-menu-icon-paste:before {
+  content: "\EA07";
+}
+
+.context-menu-icon-quit:before {
+  content: "\EA08";
+}
+
+.context-menu-icon::before {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  width: 2em; 
+  font-family: "context-menu-icons";
+  font-size: 1em;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1;
+  color: #2980b9;
+  text-align: center;
+  -webkit-transform: translateY(-50%);
+      -ms-transform: translateY(-50%);
+       -o-transform: translateY(-50%);
+          transform: translateY(-50%);
+
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+
+.context-menu-icon.context-menu-hover:before {
+  color: #fff;
+}
+
+.context-menu-icon.context-menu-disabled::before {
+  color: #bbb;
+}
+
+.context-menu-icon.context-menu-icon-loading:before {
+  -webkit-animation: cm-spin 2s infinite;
+       -o-animation: cm-spin 2s infinite;
+          animation: cm-spin 2s infinite;
+}
+
+.context-menu-icon.context-menu-icon--fa {
+  display: list-item;
+  font-family: inherit;
+}
+.context-menu-icon.context-menu-icon--fa::before {
+  position: absolute;
+  top: 50%;
+  left: 0;
+  width: 2em; 
+  font-family: FontAwesome;
+  font-size: 1em;
+  font-style: normal;
+  font-weight: normal;
+  line-height: 1;
+  color: #2980b9;
+  text-align: center;
+  -webkit-transform: translateY(-50%);
+      -ms-transform: translateY(-50%);
+       -o-transform: translateY(-50%);
+          transform: translateY(-50%);
+
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+}
+.context-menu-icon.context-menu-icon--fa.context-menu-hover:before {
+  color: #fff;
+}
+.context-menu-icon.context-menu-icon--fa.context-menu-disabled::before {
+  color: #bbb;
+}
+
+.context-menu-list {
+  position: absolute; 
+  display: inline-block;
+  min-width: 13em;
+  max-width: 26em;
+  padding: .25em 0;
+  margin: .3em;
+  font-family: inherit;
+  font-size: inherit;
+  list-style-type: none;
+  background: #fff;
+  border: 1px solid #bebebe;
+  border-radius: .2em;
+  -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
+          box-shadow: 0 2px 5px rgba(0, 0, 0, .5);
+}
+
+.context-menu-item {
+  position: relative;
+  padding: .2em 2em;
+  color: #2f2f2f;
+  -webkit-user-select: none;
+     -moz-user-select: none;
+      -ms-user-select: none;
+          user-select: none; 
+  background-color: #fff;
+}
+
+.context-menu-separator {
+  padding: 0; 
+  margin: .35em 0;
+  border-bottom: 1px solid #e6e6e6;
+}
+
+.context-menu-item > label > input,
+.context-menu-item > label > textarea {
+  -webkit-user-select: text;
+     -moz-user-select: text;
+      -ms-user-select: text;
+          user-select: text;
+}
+
+.context-menu-item.context-menu-hover {
+  color: #fff;
+  cursor: pointer; 
+  background-color: #2980b9;
+}
+
+.context-menu-item.context-menu-disabled {
+  color: #bbb;
+  cursor: default; 
+  background-color: #fff;
+}
+
+.context-menu-input.context-menu-hover {
+  color: #2f2f2f; 
+  cursor: default;
+}
+
+.context-menu-submenu:after {
+  position: absolute;
+  top: 50%;
+  right: .5em;
+  z-index: 1; 
+  width: 0;
+  height: 0;
+  content: '';
+  border-color: transparent transparent transparent #2f2f2f;
+  border-style: solid;
+  border-width: .25em 0 .25em .25em;
+  -webkit-transform: translateY(-50%);
+      -ms-transform: translateY(-50%);
+       -o-transform: translateY(-50%);
+          transform: translateY(-50%);
+}
+
+/**
+ * Inputs
+ */
+.context-menu-item.context-menu-input {
+  padding: .3em .6em;
+}
+
+/* vertically align inside labels */
+.context-menu-input > label > * {
+  vertical-align: top;
+}
+
+/* position checkboxes and radios as icons */
+.context-menu-input > label > input[type="checkbox"],
+.context-menu-input > label > input[type="radio"] {
+  position: relative;
+  top: .12em; 
+  margin-right: .4em;
+}
+
+.context-menu-input > label {
+  margin: 0;
+}
+
+.context-menu-input > label,
+.context-menu-input > label > input[type="text"],
+.context-menu-input > label > textarea,
+.context-menu-input > label > select {
+  display: block;
+  width: 100%; 
+  -webkit-box-sizing: border-box;
+     -moz-box-sizing: border-box;
+          box-sizing: border-box;
+}
+
+.context-menu-input > label > textarea {
+  height: 7em;
+}
+
+.context-menu-item > .context-menu-list {
+  top: .3em; 
+  /* re-positioned by js */
+  right: -.3em;
+  display: none;
+}
+
+.context-menu-item.context-menu-visible > .context-menu-list {
+  display: block;
+}
+
+.context-menu-accesskey {
+  text-decoration: underline;
+}

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 16 - 0
app/public/css/jquery-contextmenu/jquery.contextMenu.min.css


+ 136 - 0
app/public/js/compare.js

@@ -0,0 +1,136 @@
+'use strict';
+
+/**
+ *
+ *
+ * @author Mai
+ * @date 2018/5/15
+ * @version
+ */
+
+$(document).ready(function () {
+    const spreadNS = GC.Spread.Sheets;
+
+    class CompareObj {
+        constructor (obj) {
+            this.spread = new spreadNS.Workbook(obj, {sheetCount: 1});
+            this.spread.options.tabStripVisible = false;
+            this.spread.options.scrollbarMaxAlign = true;
+            this.spread.options.cutCopyIndicatorVisible = false;
+            this.spread.options.allowCopyPasteExcelStyle = false;
+            this.spread.options.allowUserDragDrop = false;
+
+            this.sheet = this.spread.getActiveSheet();
+            this.sheet.options.rowHeaderVisible = false;
+            this.sheet.options.colHeaderVisible = false;
+
+            this.borderLine = new spreadNS.LineBorder('black', spreadNS.LineStyle.thin);
+            this.paperSize = new spreadNS.Print.PaperSize(spreadNS.Print.PaperKind.a4);
+
+            this.initCompareHeader([]);
+        }
+        initCompareHeader (tenders) {
+            //this.sheet.clear(0, 0, this.sheet.getRowCount(), this.sheet.getColumnCount(), spreadNS.SheetArea.viewport, spreadNS.StorageType.data);
+            this.tenders = tenders;
+            const indexColCount = this.tenders.length > 0 ? this.tenders.length + 1 : 0;
+            this.sheet.setColumnCount(6 + indexColCount);
+            this.sheet.setRowCount(2);
+
+            const hCenter = spreadNS.HorizontalAlign.center;
+            const vCenter = spreadNS.VerticalAlign.center;
+
+            this.sheet.getCell(0, 0).text('指标编号').hAlign(hCenter).vAlign(vCenter);
+            this.sheet.addSpan(0, 0, 2, 1);
+            this.sheet.setColumnWidth(0, 120);
+
+            this.sheet.getCell(0, 1).text('项目或费用名称').hAlign(hCenter).vAlign(vCenter);
+            this.sheet.addSpan(0, 1, 2, 1);
+            this.sheet.setColumnWidth(1, 200);
+
+            this.sheet.getCell(0, 2).text('指标单位').hAlign(hCenter).vAlign(vCenter);
+            this.sheet.addSpan(0, 2, 2, 2);
+            this.sheet.setColumnWidth(2, 50);
+            this.sheet.setColumnWidth(3, 50);
+
+            if (this.tenders.length > 0) {
+                this.sheet.getCell(0, 4).text('经济指标').hAlign(hCenter).vAlign(vCenter);
+                this.sheet.removeSpan(0, 4, spreadNS.SheetArea.viewport);
+                this.sheet.addSpan(0, 4, 1, this.tenders.length);
+                for (let i = 0, iLen = this.tenders.length; i < iLen; i++) {
+                    this.sheet.getCell(1, 4+i).text(tenders[i].filename).hAlign(hCenter).vAlign(vCenter);
+                    this.sheet.setColumnWidth(4+i, 100);
+                }
+                this.sheet.getCell(0, 4+this.tenders.length).text('平均指标').hAlign(hCenter).vAlign(vCenter);
+                this.sheet.removeSpan(0, 4+this.tenders.length, spreadNS.SheetArea.viewport);
+                this.sheet.addSpan(0, 4+this.tenders.length, 2, 1);
+                this.sheet.setColumnWidth(4+this.tenders.length, 100);
+            }
+
+            this.sheet.getCell(0, 4 + indexColCount).text('计算规则').hAlign(hCenter).vAlign(vCenter);
+            this.sheet.addSpan(0, 4 + indexColCount, 2, 1);
+            this.sheet.setColumnWidth(4 + indexColCount, 300);
+
+            this.sheet.getCell(0, 5 + indexColCount).text('备注').hAlign(hCenter).vAlign(vCenter);
+            this.sheet.addSpan(0, 5 + indexColCount, 2, 1);
+            this.sheet.setColumnWidth(5 + indexColCount, 100);
+
+            this.sheet.getRange(0, 0, this.sheet.getRowCount(), this.sheet.getColumnCount()).setBorder(this.borderLine, {all: true});
+        }
+        sortData (data) {
+            const result = [];
+            return result;
+        }
+        loadData (data) {
+            this.searchData = data;
+            const showData = this.sortData(this.searchData);
+
+        }
+        searchIndex (keyword) {
+            const self = this;
+            const data = {
+                tenders: this.tenders,
+                keyword: keyword,
+            };
+            // postData('/compare/search', data, function (datas) {
+            //     self.loadData(datas);
+            // });
+        }
+    }
+    const compareObj = new CompareObj($('#compare-spread')[0]);
+
+    $('#generate-ok').click(function () {
+        const select = $('tr[class!=table-secondary][lib_id]');
+        const tender= [];
+        for (let i = 0, iLen = select.length; i < iLen; i++) {
+            tender.push({
+                lib_id: parseInt($(select[i]).attr('lib_id')),
+                filename: $('td:first', $(select[i])).text(),
+            });
+        }
+        compareObj.initCompareHeader(tender);
+        $('#generate-data').modal('hide');
+    });
+
+    // $.contextMenu({
+    //     selector: '#compare-spread',
+    //     build: function ($trigger, e) {
+    //         const target = SpreadJsObj.safeRightClickSelection($trigger, e, compareObj.spread);
+    //         return target.hitTestType === spreadNS.SheetArea.viewport || target.hitTestType === spreadNS.SheetArea.rowHeader;
+    //     },
+    //     items: {
+    //         'print': {
+    //             name: '打印',
+    //             icon: 'fa-sign-in',
+    //             callback: function (key, opt) {
+    //                 const printInfo = new spreadNS.Print.PrintInfo();
+    //                 printInfo.showBorder(false);
+    //                 printInfo.paperSize(compareObj.paperSize);
+    //                 printInfo.orientation(spreadNS.Print.PrintPageOrientation.landscape);
+    //                 printInfo.qualityFactor(6);
+    //                 compareObj.sheet.printInfo(printInfo);
+    //                 compareObj.spread.print();
+    //             }
+    //         },
+    //     }
+    // });
+});

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2051 - 0
app/public/js/jquery-contextmenu/jquery.contextMenu.js


A diferenza do arquivo foi suprimida porque é demasiado grande
+ 2 - 0
app/public/js/jquery-contextmenu/jquery.contextMenu.min.js


+ 513 - 0
app/public/js/jquery-contextmenu/jquery.ui.position.js

@@ -0,0 +1,513 @@
+/*! jQuery UI - v1.12.1 - 2016-09-16
+ * http://jqueryui.com
+ * Includes: position.js
+ * Copyright jQuery Foundation and other contributors; Licensed MIT */
+
+(function( factory ) {
+    if ( typeof define === "function" && define.amd ) {
+
+        // AMD. Register as an anonymous module.
+        define([ "jquery" ], factory );
+    } else {
+
+        // Browser globals
+        factory( jQuery );
+    }
+}(function( $ ) {
+
+    $.ui = $.ui || {};
+
+    var version = $.ui.version = "1.12.1";
+
+
+    /*!
+     * jQuery UI Position 1.12.1
+     * http://jqueryui.com
+     *
+     * Copyright jQuery Foundation and other contributors
+     * Released under the MIT license.
+     * http://jquery.org/license
+     *
+     * http://api.jqueryui.com/position/
+     */
+
+//>>label: Position
+//>>group: Core
+//>>description: Positions elements relative to other elements.
+//>>docs: http://api.jqueryui.com/position/
+//>>demos: http://jqueryui.com/position/
+
+
+    ( function() {
+        var cachedScrollbarWidth,
+            max = Math.max,
+            abs = Math.abs,
+            rhorizontal = /left|center|right/,
+            rvertical = /top|center|bottom/,
+            roffset = /[\+\-]\d+(\.[\d]+)?%?/,
+            rposition = /^\w+/,
+            rpercent = /%$/,
+            _position = $.fn.position;
+
+        function getOffsets( offsets, width, height ) {
+            return [
+                parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ),
+                parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 )
+            ];
+        }
+
+        function parseCss( element, property ) {
+            return parseInt( $.css( element, property ), 10 ) || 0;
+        }
+
+        function getDimensions( elem ) {
+            var raw = elem[ 0 ];
+            if ( raw.nodeType === 9 ) {
+                return {
+                    width: elem.width(),
+                    height: elem.height(),
+                    offset: { top: 0, left: 0 }
+                };
+            }
+            if ( $.isWindow( raw ) ) {
+                return {
+                    width: elem.width(),
+                    height: elem.height(),
+                    offset: { top: elem.scrollTop(), left: elem.scrollLeft() }
+                };
+            }
+            if ( raw.preventDefault ) {
+                return {
+                    width: 0,
+                    height: 0,
+                    offset: { top: raw.pageY, left: raw.pageX }
+                };
+            }
+            return {
+                width: elem.outerWidth(),
+                height: elem.outerHeight(),
+                offset: elem.offset()
+            };
+        }
+
+        $.position = {
+            scrollbarWidth: function() {
+                if ( cachedScrollbarWidth !== undefined ) {
+                    return cachedScrollbarWidth;
+                }
+                var w1, w2,
+                    div = $( "<div " +
+                        "style='display:block;position:absolute;width:50px;height:50px;overflow:hidden;'>" +
+                        "<div style='height:100px;width:auto;'></div></div>" ),
+                    innerDiv = div.children()[ 0 ];
+
+                $( "body" ).append( div );
+                w1 = innerDiv.offsetWidth;
+                div.css( "overflow", "scroll" );
+
+                w2 = innerDiv.offsetWidth;
+
+                if ( w1 === w2 ) {
+                    w2 = div[ 0 ].clientWidth;
+                }
+
+                div.remove();
+
+                return ( cachedScrollbarWidth = w1 - w2 );
+            },
+            getScrollInfo: function( within ) {
+                var overflowX = within.isWindow || within.isDocument ? "" :
+                        within.element.css( "overflow-x" ),
+                    overflowY = within.isWindow || within.isDocument ? "" :
+                        within.element.css( "overflow-y" ),
+                    hasOverflowX = overflowX === "scroll" ||
+                        ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ),
+                    hasOverflowY = overflowY === "scroll" ||
+                        ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight );
+                return {
+                    width: hasOverflowY ? $.position.scrollbarWidth() : 0,
+                    height: hasOverflowX ? $.position.scrollbarWidth() : 0
+                };
+            },
+            getWithinInfo: function( element ) {
+                var withinElement = $( element || window ),
+                    isWindow = $.isWindow( withinElement[ 0 ] ),
+                    isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9,
+                    hasOffset = !isWindow && !isDocument;
+                return {
+                    element: withinElement,
+                    isWindow: isWindow,
+                    isDocument: isDocument,
+                    offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 },
+                    scrollLeft: withinElement.scrollLeft(),
+                    scrollTop: withinElement.scrollTop(),
+                    width: withinElement.outerWidth(),
+                    height: withinElement.outerHeight()
+                };
+            }
+        };
+
+        $.fn.position = function( options ) {
+            if ( !options || !options.of ) {
+                return _position.apply( this, arguments );
+            }
+
+            // Make a copy, we don't want to modify arguments
+            options = $.extend( {}, options );
+
+            var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions,
+                target = $( options.of ),
+                within = $.position.getWithinInfo( options.within ),
+                scrollInfo = $.position.getScrollInfo( within ),
+                collision = ( options.collision || "flip" ).split( " " ),
+                offsets = {};
+
+            dimensions = getDimensions( target );
+            if ( target[ 0 ].preventDefault ) {
+
+                // Force left top to allow flipping
+                options.at = "left top";
+            }
+            targetWidth = dimensions.width;
+            targetHeight = dimensions.height;
+            targetOffset = dimensions.offset;
+
+            // Clone to reuse original targetOffset later
+            basePosition = $.extend( {}, targetOffset );
+
+            // Force my and at to have valid horizontal and vertical positions
+            // if a value is missing or invalid, it will be converted to center
+            $.each( [ "my", "at" ], function() {
+                var pos = ( options[ this ] || "" ).split( " " ),
+                    horizontalOffset,
+                    verticalOffset;
+
+                if ( pos.length === 1 ) {
+                    pos = rhorizontal.test( pos[ 0 ] ) ?
+                        pos.concat( [ "center" ] ) :
+                        rvertical.test( pos[ 0 ] ) ?
+                            [ "center" ].concat( pos ) :
+                            [ "center", "center" ];
+                }
+                pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center";
+                pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center";
+
+                // Calculate offsets
+                horizontalOffset = roffset.exec( pos[ 0 ] );
+                verticalOffset = roffset.exec( pos[ 1 ] );
+                offsets[ this ] = [
+                    horizontalOffset ? horizontalOffset[ 0 ] : 0,
+                    verticalOffset ? verticalOffset[ 0 ] : 0
+                ];
+
+                // Reduce to just the positions without the offsets
+                options[ this ] = [
+                    rposition.exec( pos[ 0 ] )[ 0 ],
+                    rposition.exec( pos[ 1 ] )[ 0 ]
+                ];
+            } );
+
+            // Normalize collision option
+            if ( collision.length === 1 ) {
+                collision[ 1 ] = collision[ 0 ];
+            }
+
+            if ( options.at[ 0 ] === "right" ) {
+                basePosition.left += targetWidth;
+            } else if ( options.at[ 0 ] === "center" ) {
+                basePosition.left += targetWidth / 2;
+            }
+
+            if ( options.at[ 1 ] === "bottom" ) {
+                basePosition.top += targetHeight;
+            } else if ( options.at[ 1 ] === "center" ) {
+                basePosition.top += targetHeight / 2;
+            }
+
+            atOffset = getOffsets( offsets.at, targetWidth, targetHeight );
+            basePosition.left += atOffset[ 0 ];
+            basePosition.top += atOffset[ 1 ];
+
+            return this.each( function() {
+                var collisionPosition, using,
+                    elem = $( this ),
+                    elemWidth = elem.outerWidth(),
+                    elemHeight = elem.outerHeight(),
+                    marginLeft = parseCss( this, "marginLeft" ),
+                    marginTop = parseCss( this, "marginTop" ),
+                    collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) +
+                        scrollInfo.width,
+                    collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) +
+                        scrollInfo.height,
+                    position = $.extend( {}, basePosition ),
+                    myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() );
+
+                if ( options.my[ 0 ] === "right" ) {
+                    position.left -= elemWidth;
+                } else if ( options.my[ 0 ] === "center" ) {
+                    position.left -= elemWidth / 2;
+                }
+
+                if ( options.my[ 1 ] === "bottom" ) {
+                    position.top -= elemHeight;
+                } else if ( options.my[ 1 ] === "center" ) {
+                    position.top -= elemHeight / 2;
+                }
+
+                position.left += myOffset[ 0 ];
+                position.top += myOffset[ 1 ];
+
+                collisionPosition = {
+                    marginLeft: marginLeft,
+                    marginTop: marginTop
+                };
+
+                $.each( [ "left", "top" ], function( i, dir ) {
+                    if ( $.ui.position[ collision[ i ] ] ) {
+                        $.ui.position[ collision[ i ] ][ dir ]( position, {
+                            targetWidth: targetWidth,
+                            targetHeight: targetHeight,
+                            elemWidth: elemWidth,
+                            elemHeight: elemHeight,
+                            collisionPosition: collisionPosition,
+                            collisionWidth: collisionWidth,
+                            collisionHeight: collisionHeight,
+                            offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ],
+                            my: options.my,
+                            at: options.at,
+                            within: within,
+                            elem: elem
+                        } );
+                    }
+                } );
+
+                if ( options.using ) {
+
+                    // Adds feedback as second argument to using callback, if present
+                    using = function( props ) {
+                        var left = targetOffset.left - position.left,
+                            right = left + targetWidth - elemWidth,
+                            top = targetOffset.top - position.top,
+                            bottom = top + targetHeight - elemHeight,
+                            feedback = {
+                                target: {
+                                    element: target,
+                                    left: targetOffset.left,
+                                    top: targetOffset.top,
+                                    width: targetWidth,
+                                    height: targetHeight
+                                },
+                                element: {
+                                    element: elem,
+                                    left: position.left,
+                                    top: position.top,
+                                    width: elemWidth,
+                                    height: elemHeight
+                                },
+                                horizontal: right < 0 ? "left" : left > 0 ? "right" : "center",
+                                vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle"
+                            };
+                        if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) {
+                            feedback.horizontal = "center";
+                        }
+                        if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) {
+                            feedback.vertical = "middle";
+                        }
+                        if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) {
+                            feedback.important = "horizontal";
+                        } else {
+                            feedback.important = "vertical";
+                        }
+                        options.using.call( this, props, feedback );
+                    };
+                }
+
+                elem.offset( $.extend( position, { using: using } ) );
+            } );
+        };
+
+        $.ui.position = {
+            fit: {
+                left: function( position, data ) {
+                    var within = data.within,
+                        withinOffset = within.isWindow ? within.scrollLeft : within.offset.left,
+                        outerWidth = within.width,
+                        collisionPosLeft = position.left - data.collisionPosition.marginLeft,
+                        overLeft = withinOffset - collisionPosLeft,
+                        overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset,
+                        newOverRight;
+
+                    // Element is wider than within
+                    if ( data.collisionWidth > outerWidth ) {
+
+                        // Element is initially over the left side of within
+                        if ( overLeft > 0 && overRight <= 0 ) {
+                            newOverRight = position.left + overLeft + data.collisionWidth - outerWidth -
+                                withinOffset;
+                            position.left += overLeft - newOverRight;
+
+                            // Element is initially over right side of within
+                        } else if ( overRight > 0 && overLeft <= 0 ) {
+                            position.left = withinOffset;
+
+                            // Element is initially over both left and right sides of within
+                        } else {
+                            if ( overLeft > overRight ) {
+                                position.left = withinOffset + outerWidth - data.collisionWidth;
+                            } else {
+                                position.left = withinOffset;
+                            }
+                        }
+
+                        // Too far left -> align with left edge
+                    } else if ( overLeft > 0 ) {
+                        position.left += overLeft;
+
+                        // Too far right -> align with right edge
+                    } else if ( overRight > 0 ) {
+                        position.left -= overRight;
+
+                        // Adjust based on position and margin
+                    } else {
+                        position.left = max( position.left - collisionPosLeft, position.left );
+                    }
+                },
+                top: function( position, data ) {
+                    var within = data.within,
+                        withinOffset = within.isWindow ? within.scrollTop : within.offset.top,
+                        outerHeight = data.within.height,
+                        collisionPosTop = position.top - data.collisionPosition.marginTop,
+                        overTop = withinOffset - collisionPosTop,
+                        overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset,
+                        newOverBottom;
+
+                    // Element is taller than within
+                    if ( data.collisionHeight > outerHeight ) {
+
+                        // Element is initially over the top of within
+                        if ( overTop > 0 && overBottom <= 0 ) {
+                            newOverBottom = position.top + overTop + data.collisionHeight - outerHeight -
+                                withinOffset;
+                            position.top += overTop - newOverBottom;
+
+                            // Element is initially over bottom of within
+                        } else if ( overBottom > 0 && overTop <= 0 ) {
+                            position.top = withinOffset;
+
+                            // Element is initially over both top and bottom of within
+                        } else {
+                            if ( overTop > overBottom ) {
+                                position.top = withinOffset + outerHeight - data.collisionHeight;
+                            } else {
+                                position.top = withinOffset;
+                            }
+                        }
+
+                        // Too far up -> align with top
+                    } else if ( overTop > 0 ) {
+                        position.top += overTop;
+
+                        // Too far down -> align with bottom edge
+                    } else if ( overBottom > 0 ) {
+                        position.top -= overBottom;
+
+                        // Adjust based on position and margin
+                    } else {
+                        position.top = max( position.top - collisionPosTop, position.top );
+                    }
+                }
+            },
+            flip: {
+                left: function( position, data ) {
+                    var within = data.within,
+                        withinOffset = within.offset.left + within.scrollLeft,
+                        outerWidth = within.width,
+                        offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left,
+                        collisionPosLeft = position.left - data.collisionPosition.marginLeft,
+                        overLeft = collisionPosLeft - offsetLeft,
+                        overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft,
+                        myOffset = data.my[ 0 ] === "left" ?
+                            -data.elemWidth :
+                            data.my[ 0 ] === "right" ?
+                                data.elemWidth :
+                                0,
+                        atOffset = data.at[ 0 ] === "left" ?
+                            data.targetWidth :
+                            data.at[ 0 ] === "right" ?
+                                -data.targetWidth :
+                                0,
+                        offset = -2 * data.offset[ 0 ],
+                        newOverRight,
+                        newOverLeft;
+
+                    if ( overLeft < 0 ) {
+                        newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth -
+                            outerWidth - withinOffset;
+                        if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) {
+                            position.left += myOffset + atOffset + offset;
+                        }
+                    } else if ( overRight > 0 ) {
+                        newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset +
+                            atOffset + offset - offsetLeft;
+                        if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) {
+                            position.left += myOffset + atOffset + offset;
+                        }
+                    }
+                },
+                top: function( position, data ) {
+                    var within = data.within,
+                        withinOffset = within.offset.top + within.scrollTop,
+                        outerHeight = within.height,
+                        offsetTop = within.isWindow ? within.scrollTop : within.offset.top,
+                        collisionPosTop = position.top - data.collisionPosition.marginTop,
+                        overTop = collisionPosTop - offsetTop,
+                        overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop,
+                        top = data.my[ 1 ] === "top",
+                        myOffset = top ?
+                            -data.elemHeight :
+                            data.my[ 1 ] === "bottom" ?
+                                data.elemHeight :
+                                0,
+                        atOffset = data.at[ 1 ] === "top" ?
+                            data.targetHeight :
+                            data.at[ 1 ] === "bottom" ?
+                                -data.targetHeight :
+                                0,
+                        offset = -2 * data.offset[ 1 ],
+                        newOverTop,
+                        newOverBottom;
+                    if ( overTop < 0 ) {
+                        newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight -
+                            outerHeight - withinOffset;
+                        if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) {
+                            position.top += myOffset + atOffset + offset;
+                        }
+                    } else if ( overBottom > 0 ) {
+                        newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset +
+                            offset - offsetTop;
+                        if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) {
+                            position.top += myOffset + atOffset + offset;
+                        }
+                    }
+                }
+            },
+            flipfit: {
+                left: function() {
+                    $.ui.position.flip.left.apply( this, arguments );
+                    $.ui.position.fit.left.apply( this, arguments );
+                },
+                top: function() {
+                    $.ui.position.flip.top.apply( this, arguments );
+                    $.ui.position.fit.top.apply( this, arguments );
+                }
+            }
+        };
+
+    } )();
+
+    var position = $.ui.position;
+
+
+
+
+}));

A diferenza do arquivo foi suprimida porque é demasiado grande
+ 6 - 0
app/public/js/jquery-contextmenu/jquery.ui.position.min.js


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

@@ -33,7 +33,7 @@ $(document).ready(function() {
             {title: '数量2', field: 'dgn_quantity2', width: 60, type: 'Number', vAlign: 1, readOnly: true},
             {title: '金额', field: 'total_price', width: 60, type: 'Number', vAlign: 1, readOnly: true},
         ],
-        treeCol: 0,
+        //treeCol: 0,
         emptyRows: 3,
         headRows: 2,
         headRowHeight: [28],

+ 15 - 65
app/view/compare/index.ejs

@@ -2,13 +2,13 @@
     <div class="panel-title fluid">
         <div class="title-main">
             <div class="btn-group">
-                <a class="btn btn-primary btn-sm" href="#generate-data" data-toggle="modal" data-target="#generate-data">指标源:<%= libList.length %></a>
+                <a class="btn btn-primary btn-sm" href="#generate-data" data-toggle="modal" data-target="#generate-data">造价文件:<%= libList.length %></a>
             </div>
             <div class="btn-group col-2 pr-0">
-                <input type="text" class="form-control form-control-sm m-0" placeholder="输入项目节名称" >
+                <input type="text" class="form-control form-control-sm m-0" placeholder="输入指标节点编号或者名称" >
             </div>
             <div class="btn-group">
-                <button class="btn btn-sm btn-primary " type="button">定位项目节</button>
+                <button class="btn btn-sm btn-primary " type="button">搜索</button>
             </div>
         </div>
     </div>
@@ -16,68 +16,18 @@
         <div class="c-header p-0 col-12">
         </div>
         <div class="c-body">
-            <div class="sjs-height-1">
-                <table class="table table-bordered table-sm">
-                    <thead>
-                    <tr>
-                        <th class="text-center" rowspan="2"></th>
-                        <th class="text-center" rowspan="2">指标编号</th>
-                        <th class="text-center" rowspan="2">项目或费用名称</th>
-                        <th class="text-center" rowspan="2" colspan="2">指标单位</th>
-                        <th class="text-center" colspan="3">经济指标</th>
-                        <th class="text-center" rowspan="2">平均指标</th>
-                        <th class="text-center" rowspan="2">计算规则</th>
-                        <th class="text-center" rowspan="2">备注</th>
-                    </tr>
-                    <tr>
-                        <th>XXX标段</th>
-                        <th>YYY标段</th>
-                        <th>ZZZ标段</th>
-                    </tr>
-                    </thead>
-                    <tbody>
-                    <tr>
-                        <td>1</td>
-                        <td>z1</td>
-                        <td>临时工程</td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                    </tr>
-                    <tr>
-                        <td>2</td>
-                        <td>z1-a</td>
-                        <td>临时道路</td>
-                        <td></td>
-                        <td>km</td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td>指新建便道与利用原有道路的总长</td>
-                        <td></td>
-                    </tr>
-                    <tr>
-                        <td>2</td>
-                        <td>z1-a-1</td>
-                        <td>临时道路公路公里造价</td>
-                        <td>元</td>
-                        <td>公路公里</td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td></td>
-                        <td>合价/路线总长度</td>
-                        <td></td>
-                    </tr>
-                    </tbody>
-                </table>
+            <div class="sjs-height-1" id="compare-spread">
             </div>
         </div>
     </div>
-</div>
+</div>
+<style type="text/css" media="print">
+    @page { size: landscape; }
+</style>
+<script src="/public/js/spreadjs/sheets/gc.spread.sheets.all.10.0.1.min.js"></script>
+<script src="/public/js/spreadjs/sheets/pluggable/gc.spread.sheets.print.10.0.1.min.js"></script>
+<script>
+    GC.Spread.Sheets.LicenseKey = "559432293813965#A0y3iTOzEDOzkjMyMDN9UTNiojIklkI1pjIEJCLi4TPB9mM5AFNTd4cvZ7SaJUVy3CWKtWYXx4VVhjMpp7dYNGdx2ia9sEVlZGOTh7NRlTUwkWR9wEV4gmbjBDZ4ElR8N7cGdHVvEWVBtCOwIGW0ZmeYVWVr3mI0IyUiwCMzETN8kzNzYTM0IicfJye&Qf35VfiEzRwEkI0IyQiwiIwEjL6ByUKBCZhVmcwNlI0IiTis7W0ICZyBlIsIyNyMzM5ADI5ADNwcTMwIjI0ICdyNkIsIibj9SbvNmL4N7bjRnch56ciojIz5GRiwiI8+Y9sWY9QmZ0Jyp96uL9v6L0wap9biY9qiq95q197Wr9g+89iojIh94Wiqi";
+</script>
+<script src="/public/js/spreadjs_rela/spreadjs_zh.js"></script>
+<script src="/public/js/compare.js"></script>

+ 14 - 5
app/view/compare/modal.ejs

@@ -3,23 +3,23 @@
     <div class="modal-dialog">
         <div class="modal-content">
             <div class="modal-header">
-                <h5 class="modal-title">选择需要对比指标源</h5>
+                <h5 class="modal-title">选择需要对比造价文件</h5>
                 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
             </div>
             <div class="modal-body">
                 <div class="modal-height-500-scroll">
                     <table class="table table-bordered">
-                        <tr><th>指标源</th><th>选择</th></tr>
+                        <tr><th>造价文件</th><th>选择</th></tr>
                         <% if(libList.length > 0) { %>
                         <% libList.forEach(function(lib) { %>
-                        <tr class="table-secondary"><td><%= lib.filename %></td><td><input type="checkbox"></td></tr>
+                        <tr class="table-secondary" lib_id = "<%= lib.id %>"><td><%= lib.filename %></td><td><input type="checkbox"></td></tr>
                         <% }) %>
                         <% } %>
                     </table>
                 </div>
             </div>
             <div class="modal-footer">
-                <button class="btn btn-primary">生成对比结果</button>
+                <button class="btn btn-primary" id="generate-ok">生成对比结果</button>
                 <button class="btn btn-secondary" data-dismiss="modal" aria-hidden="true">取消</button>
             </div>
         </div>
@@ -27,5 +27,14 @@
 </div>
 <!-- JS. -->
 <script type="text/javascript">
-    $('#generate-data').modal('show');
+    $(document).ready(function () {
+        $('#generate-data').modal('show');
+        $('input[type=checkbox]').bind('click', function () {
+            if (this.checked) {
+                $(this).parent().parent().removeClass('table-secondary');
+            } else {
+                $(this).parent().parent().addClass('table-secondary');
+            }
+        });
+    });
 </script>

+ 5 - 0
app/view/layout/layout.ejs

@@ -13,12 +13,17 @@
     <link rel="stylesheet" href="/public/css/ztree/zTreeStyle.css" type="text/css">
     <!--spreadJs-->
     <link rel="stylesheet" href="/public/css/spreadjs/sheets/gc.spread.sheets.excelsmartcost.css">
+    <!--jquery-contextmenu-->
+    <link rel="stylesheet" href="/public/css/jquery-contextmenu/jquery.contextMenu.min.css">
 
     <script src="/public/js/jquery/jquery-3.2.1.min.js"></script>
     <script src="/public/js/popper/popper.min.js"></script>
     <script src="/public/js/bootstrap/bootstrap.min.js"></script>
     <script type="text/javascript" src="/public/js/ztree/jquery.ztree.core.js"></script>
     <script type="text/javascript" src="/public/js/ztree/jquery.ztree.excheck.js"></script>
+    <!--jquery-contextmenu-->
+    <script src="/public/js/jquery-contextmenu/jquery.ui.position.min.js"></script>
+    <script src="/public/js/jquery-contextmenu/jquery.contextMenu.min.js"></script>
 </head>
 
 <body>

+ 0 - 1
app/view/template/index.ejs

@@ -2,7 +2,6 @@
     <div class="panel-title">
         <div class="title-bar">
             <h2 class="">项目节 <a href="#upload" data-toggle="modal" data-target="#upload" class="pull-right mr-3" title="导入项目节"><i class="fa fa-upload"></i></a></h2>
-
         </div>
     </div>
     <div class="scrollbar-auto">

+ 6 - 6
config/menu.js

@@ -21,12 +21,12 @@ const menu = {
         display: true,
         url: '/lib',
     },
-    // compare: {
-    //     name: '指标对比',
-    //     icon: 'fa-search',
-    //     display: true,
-    //     url: '/compare',
-    // }
+    compare: {
+        name: '指标对比',
+        icon: 'fa-search',
+        display: true,
+        url: '/compare',
+    }
 };
 
 module.exports = menu;