瀏覽代碼

清单指引编辑器、备注、复制整块

zhongzewei 6 年之前
父節點
當前提交
6dad82fdbf

+ 1 - 0
modules/all_models/std_billsGuidance_items.js

@@ -18,6 +18,7 @@ const stdBillsGuidanceItems = new Schema({
     NextSiblingID: String,
     billsID: String, //关联清单的ID
     name: String,
+    comment: String, //备注
     type: Number, //0:工作内容 1:定额
     rationID: {type: Number, default: null}, //定额类型时
     deleted: {type: Boolean, default: false}

+ 36 - 1
public/web/id_tree.js

@@ -630,7 +630,42 @@ var idTree = {
                 return node;
             }
         };
-
+        //批量新增节点到节点后项,节点已有树结构数据
+        Tree.prototype.insertDatasTo = function (preData, datas) {
+            let rst = [];
+            for(let data of datas){
+                this.nodes[this.prefix + data.ID] = new Node(this, data.ID);
+                this.nodes[this.prefix + data.ID]['data'] = data;
+                rst.push(this.nodes[this.prefix + data.ID]);
+            }
+            for(let data of datas){
+                let node = this.nodes[this.prefix + data.ID];
+                let parent = data.ParentID == -1 ? null : this.nodes[this.prefix + data.ParentID];
+                node.parent = parent;
+                if(!parent){
+                    this.roots.push(node);
+                }
+                else {
+                    parent.children.push(node);
+                }
+                let next = data.NextSiblingID == -1 ? null : this.nodes[this.prefix + data.NextSiblingID];
+                node.nextSibling = next;
+                if(next){
+                    next.preSibling = node;
+                }
+            }
+            let preNode = this.nodes[this.prefix + preData.ID];
+            if(preNode){
+                preNode.nextSibling = this.nodes[this.prefix + preData.NextSiblingID] ? this.nodes[this.prefix + preData.NextSiblingID] : null;
+                if(preNode.nextSibling){
+                    preNode.nextSibling.preSibling = preNode;
+                }
+            }
+            //resort
+            this.roots = tools.reSortNodes(this.roots, true);
+            tools.sortTreeItems(this);
+            return rst;
+        };
         Tree.prototype.delete = function (node) {
             var success = false, that = this;
             if(node) success = that.m_delete([node]);

+ 25 - 0
public/web/tree_sheet/tree_sheet_controller.js

@@ -23,6 +23,31 @@ var TREE_SHEET_CONTROLLER = {
             });
         };
 
+        controller.prototype.syncDisplayNewNodes = function (newNodes) {
+            let me = this;
+            TREE_SHEET_HELPER.massOperationSheet(me.sheet, function () {
+                var sels = me.sheet.getSelections();
+                newNodes.sort(function (a, b) {
+                    let rst = 0;
+                    if(a.serialNo() > b.serialNo()){
+                        rst = 1;
+                    }
+                    else {
+                        rst = -1;
+                    }
+                    return rst;
+                });
+                for(let newNode of newNodes){
+                    me.sheet.addRows(newNode.serialNo(), 1);
+                }
+                for(let newNode of newNodes){
+                    me.setTreeSelected(newNode);
+                    TREE_SHEET_HELPER.refreshTreeNodeData(me.setting, me.sheet, [newNode], false);
+                    me.sheet.setSelection(newNode.serialNo(), sels[0].col, 1, 1);
+                }
+            });
+        };
+
         controller.prototype.insert = function () {
             var newNode = null, that = this,  sels = this.sheet.getSelections();
             if (this.tree) {

+ 8 - 1
web/maintain/billsGuidance_lib/html/zhiyin.html

@@ -10,6 +10,7 @@
     <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">
 </head>
 
 <body>
@@ -43,8 +44,12 @@
                         <a id="upMove" href="javascript:void(0);" class="btn btn-sm" data-toggle="tooltip" data-placement="bottom" title=""><i class="fa fa-arrow-up" aria-hidden="true"></i> 上移</a>
                       </div>
                     </div>
-                    <div id="guideItemSpread" class="main-data">
+                      <div class="main-top-content">
+                          <div id="guideItemSpread" class="main-data"></div>
                     </div>
+                      <div class="main-bottom-content">
+                          <textarea class="form-control"></textarea>
+                      </div>
                   </div>
                     <div class="main-side col-lg-4 p-" style="margin: 0; padding: 0;">
                         <div class="sidebar-tools-bar container-fluid tools-bar-height-q">
@@ -108,6 +113,8 @@
     <script src="/lib/jquery/jquery.min.js"></script>
     <script src="/lib/tether/tether.min.js"></script>
     <script src="/lib/bootstrap/bootstrap.min.js"></script>
+    <script src="/lib/jquery-contextmenu/jquery.contextMenu.js"></script>
+    <script src="/lib/jquery-contextmenu/jquery.ui.position.js"></script>
     <script src = "/lib/spreadjs/sheets/gc.spread.sheets.all.11.1.2.min.js"></script>
     <script>GC.Spread.Sheets.LicenseKey =  '<%- LicenseKey %>';</script>
     <script src="/lib/lodash/lodash.js"></script>

+ 248 - 6
web/maintain/billsGuidance_lib/js/billsGuidance.js

@@ -78,6 +78,8 @@ const billsGuidance = (function () {
         job: 0,
         ration: 1
     };
+    //项目指引复制整块localStorage key
+    const itemCopyBlockKey = 'guideItemCopyBlock';
     const updateType = {
         create: 'create',
         update: 'update',
@@ -393,6 +395,7 @@ const billsGuidance = (function () {
         //全部设为无效
         $('.tools-btn').children().addClass('disabled');
         $('#insertRation').addClass('disabled');
+        $('.main-bottom-content').find('textarea').attr('readonly', true);
         //插入
         if(bills.tree.selected && bills.tree.selected.guidance.tree){
             $('#insert').removeClass('disabled');
@@ -432,17 +435,22 @@ const billsGuidance = (function () {
         if(node && (node.children.length === 0 || allRationChildren(node))){
             $('#insertRation').removeClass('disabled');
         }
+        //备注,奇数节点可用
+        if(node && (node.depth() + 1) % 2 === 1 && node.data.type !== itemType.ration){
+            $('.main-bottom-content').find('textarea').attr('readonly', false);
+        }
     }
     //项目指引表焦点控制
     //@param {Number}row @return {void}
     function guideItemInitSel(row){
-        console.log('et');
         let billsNode = bills.tree.selected;
         let node = null;
         if(billsNode && billsNode.guidance.tree){
             node = billsNode.guidance.tree.items[row];
             if(node){
                 billsNode.guidance.tree.selected = node;
+                //显示备注
+                $('.main-bottom-content').find('textarea').val(node.data.comment ? node.data.comment : '');
             }
         }
         refreshBtn(node);
@@ -491,9 +499,6 @@ const billsGuidance = (function () {
         TipCellType.prototype.processMouseEnter = function (hitinfo) {
             let text = hitinfo.sheet.getText(hitinfo.row, hitinfo.col);
             let tag = hitinfo.sheet.getTag(hitinfo.row, hitinfo.col);
-            /*     let hintHeight = datas[hitinfo.row] ?
-             datas[hitinfo.row].hintHeight ? datas[hitinfo.row].hintHeight : null
-             : null; //定额库定额悬浮提示位置相关*/
             if(tag !== undefined && tag){
                 text = tag;
             }
@@ -543,8 +548,23 @@ const billsGuidance = (function () {
                     else{
                         $(this._toolTipElement).css("top", setting.pos.y + hitinfo.y +15).css("left", setting.pos.x + hitinfo.x + 15);
                     }
-                    $(this._toolTipElement).show("fast");
-                    ration.tipDiv = 'show';//做个标记
+                    //名称
+                    if(hitinfo.col === 2){
+                        let acStyle = hitinfo.sheet.getActualStyle(hitinfo.row, hitinfo.col),
+                            zoom = hitinfo.sheet.zoom();
+                        let value = hitinfo.sheet.getValue(hitinfo.row, hitinfo.col);
+                        let textLength = this.getAutoFitWidth(value, text, acStyle, zoom, {sheet: hitinfo.sheet, row: hitinfo.row, col: hitinfo.col, sheetArea: GC.Spread.Sheets.SheetArea.viewport});
+                        let cellWidth = hitinfo.sheet.getCell(-1, hitinfo.col).width();
+                        if(textLength > cellWidth){
+                            $(this._toolTipElement).css("top", setting.pos.y + hitinfo.y +15).css("left", setting.pos.x + hitinfo.x + 15);
+                            $(this._toolTipElement).show("fast");
+                            ration.tipDiv = 'show';//做个标记
+                        }
+                    }
+                    else {
+                        $(this._toolTipElement).show("fast");
+                        ration.tipDiv = 'show';//做个标记
+                    }
                 }
             }
         };
@@ -576,6 +596,7 @@ const billsGuidance = (function () {
                 }
             }
             sheet.setCellType(-1, 1, tipCellType);
+            sheet.setCellType(-1, 2, tipCellType);
         };
         renderSheetFunc(sheet, fuc);
     }
@@ -961,6 +982,215 @@ const billsGuidance = (function () {
         }
         return rst;
     }
+    //获取块节点父项不存在于选中节点中的节点
+    //@param {Array}nodes(选中的节点) @return {Array}
+    function getBlockNodes(nodes) {
+        let nodeMapping = {};
+        for(let node of nodes){
+            nodeMapping[node.data.ID] = node;
+        }
+        //块节点,父项不存在于选中节点中的节点
+        let blockNodes = [];
+        for(let node of nodes){
+            if(!nodeMapping[node.data.ParentID]){
+                blockNodes.push(node);
+            }
+        }
+        return blockNodes;
+    }
+    //允许复制整块,如果有多个块节点,且块节点的父项不同,则不可复制
+    //@param {Array}nodes(块节点) @return {Boolean}
+    function canCopyBlock(nodes) {
+        if(!nodes || nodes.length === 0){
+            return false;
+        }
+        let pID = nodes[0].data.ParentID;
+        for(let node of nodes){
+            if(node.data.ParentID !== pID){
+                return false;
+            }
+        }
+        return true;
+    }
+    //允许粘贴整块 有粘贴数据,节点存在,如果粘贴到的节点为定额数据,粘贴数据为全定额数据
+    //@param {Object}node(粘贴到的节点)
+    function canPasteBlock(node) {
+        let pasteDatas = JSON.parse(getLocalCache(itemCopyBlockKey));
+        if(!pasteDatas || pasteDatas.length === 0){
+            return false;
+        }
+        if(!node){
+            return false;
+        }
+        //若粘贴到定额节点,则数据须全为定额
+        if(node.data.type === itemType.ration){
+            for(let data of pasteDatas){
+                if(data.type !== itemType.ration){
+                    return false;
+                }
+            }
+        }
+        //若粘贴到非定额节点,则粘贴的顶层数据须全为非定额
+        else {
+            let topDatas = _.filter(pasteDatas, {ParentID: -1});
+            for(let topData of topDatas){
+                if(topData.type === itemType.ration){
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+    //复制整块,将块节点下所有节点数据复制一份,并且重新生成ID、ParentID、NextSiblingID,使用localStorage存储
+    //@param {Array}nodes(块节点) @return {void}
+    function copyBlocks(nodes) {
+        nodes = _.cloneDeep(nodes);
+        //将块节点的ParentID暂时设置为-1
+        for(let topNode of nodes){
+            topNode.data.ParentID = -1;
+        }
+        let copyDatas = [];
+        let copyNodes = [];
+        //获取块节点包含的所有节点(包括自己)
+        function containNodes(nodes) {
+            for(let node of nodes){
+                copyNodes.push(node);
+                if(node.children.length > 0){
+                    containNodes(node.children);
+                }
+            }
+        }
+        containNodes(nodes);
+        for(let node of copyNodes){
+            copyDatas.push(node.data);
+        }
+        console.log(`copyDatas`);
+        console.log(copyDatas);
+        setLocalCache(itemCopyBlockKey, JSON.stringify(copyDatas));
+    }
+    //粘贴整块,整块数据粘贴到相关节点,并成为其后项
+    //@param {Object}node(粘贴到的节点) @return {void}
+    function pasteBlock(node) {
+        let itemObj = bills.tree.selected.guidance;
+        let pasteDatas = JSON.parse(getLocalCache(itemCopyBlockKey));
+        //整理ID
+        let IDMapping = {};
+        for(let data of pasteDatas){
+            data.newID = uuid.v1();
+            IDMapping[data.ID] = data;
+        }
+        for(let data of pasteDatas){
+            let nextData = IDMapping[data.NextSiblingID];
+            data.NextSiblingID = nextData ? nextData.newID : -1;
+            let parentData = IDMapping[data.ParentID];
+            data.ParentID = parentData ? parentData.newID : -1;
+        }
+        for(let data of pasteDatas){
+            data.ID = data.newID;
+            delete data.newID;
+        }
+        let updateDatas = [];
+        //将最顶层的块数据的ParentID设置成粘贴到节点的ParentID,并设置新的billsID
+        let topDatas = _.filter(pasteDatas, {ParentID: -1});
+        for(let topData of topDatas){
+            topData.ParentID = node.getParentID();
+        }
+        //更新数据
+        //更新插入的最末顶层数据NextSiblingID
+        if(node.nextSibling){
+            topDatas[topDatas.length - 1].NextSiblingID = node.getNextSiblingID();
+        }
+        //新建节点
+        for(let data of pasteDatas){
+            data.libID = libID;
+            data.billsID = node.data.billsID;
+            delete data._id;
+            updateDatas.push({updateType: updateType.create, updateData: data});
+        }
+        console.log(`node`);
+        console.log(node);
+        console.log(`pasteDatas`);
+        console.log(pasteDatas);
+        //更新粘贴到的节点的NextSiblingID
+        updateDatas.push({updateType: updateType.update, findData: {ID: node.data.ID}, updateData: {NextSiblingID: topDatas[0].ID}})
+        $.bootstrapLoading.start();
+        updateGuideItems(updateDatas, function (rstData) {
+            $.bootstrapLoading.end();
+            node.data.NextSiblingID = topDatas[0].ID;
+            let newNodes = itemObj.tree.insertDatasTo(node.data, pasteDatas);
+            cleanData(guideItem.workBook.getActiveSheet(), guideItem.headers, -1);
+            itemObj.controller.showTreeData();
+            setNodesColor(guideItem.workBook.getActiveSheet(), bills.tree.selected.guidance.tree.items);
+        }, function () {
+            $.bootstrapLoading.end();
+        });
+    }
+    //初始化右键菜单
+    //@return {void}
+    function initContextMenu() {
+        $.contextMenu({
+            selector: '#guideItemSpread',
+            build: function($triggerElement, e){
+                //控制允许右键菜单在哪个位置出现
+                let sheet = guideItem.workBook.getSheet(0);
+                let offset = $("#guideItemSpread").offset(),
+                    x = e.pageX - offset.left,
+                    y = e.pageY - offset.top;
+                let target = sheet.hitTest(x, y);
+                if(target.hitTestType === 3 && typeof target.row !== 'undefined' && typeof target.col !== 'undefined'){//在表格内
+                    let sel = sheet.getSelections()[0];
+                    if(sel && sel.rowCount === 1){
+                        sheet.setActiveCell(target.row, target.col);
+                    }
+                    sel = sheet.getSelections()[0];
+                    let selNodes = [];
+                    if(sel){
+                        sel.row =  sel.row === -1 ? 0 : sel.row;
+                        for(let i = 0; i < sel.rowCount; i++){
+                            if(bills.tree.selected.guidance.tree.items[sel.row + i]){
+                                selNodes.push(bills.tree.selected.guidance.tree.items[sel.row + i]);
+                            }
+                        }
+                    }
+                    //块节点
+                    let blockNodes = getBlockNodes(selNodes);
+                    //右键在多选内则不重设焦点
+                    if(!sel || sel.rowCount === 1 || !(target.row >= sel.row && target.row <= sel.row + sel.rowCount - 1)){
+                        sheet.setActiveCell(target.row, target.col);
+                    }
+                    guideItemInitSel(target.row);
+                    return {
+                        callback: function(){},
+                        items: {
+                            "copy": {
+                                name: "复制整块",
+                                disabled: function () {
+                                    return !canCopyBlock(blockNodes);
+                                },
+                                icon: "fa-copy",
+                                callback: function (key, opt) {
+                                    copyBlocks(blockNodes);
+                                }},
+                            "paste": {
+                                name: "粘贴整块",
+                                disabled: function () {
+                                    let pasteNode = bills.tree.selected.guidance.tree.items[target.row];
+                                    return !canPasteBlock(pasteNode);
+                                },
+                                icon: "fa-paste",
+                                callback: function (key, opt) {
+                                    let pasteNode = bills.tree.selected.guidance.tree.items[target.row];
+                                    pasteBlock(pasteNode);
+                                }},
+                        }
+                    };
+                }
+                else{
+                    return false;
+                }
+            }
+        });
+    }
     //初始化个按钮点击
     //@return {void}
     function initBtn(){
@@ -1040,6 +1270,17 @@ const billsGuidance = (function () {
         $('#searchText').keyup(function (e) {
             $('#searchBtn').click();
         });
+        //编辑备注
+        $('.main-bottom-content').find('textarea').keyup(function () {
+            let node = bills.tree.selected.guidance.tree.selected;
+            if(node){
+                let comment = $(this).val();
+                let updateDatas = [{updateType: updateType.update, findData: {ID: node.getID()}, updateData: {comment: comment}}];
+                updateGuideItems(updateDatas, function (rstData) {
+                    node.data.comment = comment;
+                });
+            }
+        });
     }
     //初始化视图
     //@param {void} @return {void}
@@ -1048,6 +1289,7 @@ const billsGuidance = (function () {
         initWorkBooks(modules);
         getLibWithBills(libID);
         initBtn();
+        initContextMenu();
     }
 
 

+ 31 - 1
web/maintain/billsGuidance_lib/js/global.js

@@ -10,7 +10,11 @@ function autoFlashHeight(){
     $(".side-content").height($(window).height()-headerHeight);
     $(".poj-list").height($(window).height()-headerHeight);
     $(".form-list").height($(window).height()-headerHeight-50);
-    $(".main-data").height($(window).height()-headerHeight-toolsBar);
+    $('.main-top-content').height(($(window).height()-headerHeight-toolsBar)*0.7);
+    $('.main-bottom-content').height(($(window).height()-headerHeight-toolsBar)*0.3);
+    $('.main-bottom-content').find('textarea').height($('.main-bottom-content').height() - 20);
+    $('.main-bottom-content').find('textarea').width($('.main-bottom-content').width() - 25);
+    $(".main-data").height($('.main-top-content').height());
     $(".main-data-full").height($(window).height()-headerHeight);
     $(".main-data-bottom").height($(window).height()-headerHeight-toolsBarHeightQ-topContentHeight-$('#rationSearchResult').height() + 30);
     $('.bottom-content').height($('.main-data-bottom').height());
@@ -44,3 +48,29 @@ $('*[data-toggle=tooltip]').mouseover(function() {
   });
 /*工具提示*/
 });
+
+function setLocalCache(key, value) {
+    const storage = window.localStorage;
+    if (!storage || key === '' || value === '') {
+        return;
+    }
+
+    storage.setItem(key, value);
+}
+
+function getLocalCache(key) {
+    const storage = window.localStorage;
+    if (!storage || key === '') {
+        return null;
+    }
+
+    return storage.getItem(key);
+}
+
+function removeLocalCache(key) {
+    const storage = window.localStorage;
+    if (!storage || key === '') {
+        return null;
+    }
+    return storage.removeItem(key);
+}