bootstrap-treeview.js

 

/* =========================================================
 * bootstrap-treeview.js v1.2.0
 * =========================================================
 * Copyright 2013 Jonathan Miles
 * Project URL : http://www.jondmiles.com/bootstrap-treeview
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================= */

(function($, window, document, undefined) {
  /*global jQuery, console*/

  "use strict";

  var pluginName = "treeview";

  var _default = {};

  _default.settings = {
    injectStyle: true,

    levels: 2,

    expandIcon: "glyphicon glyphicon-folder-open",
    collapseIcon: "glyphicon glyphicon-folder-open",
    emptyIcon: "glyphicon glyphicon-file",
    nodeIcon: "glyphicon",
    selectedIcon: "",
    checkedIcon: "glyphicon glyphicon-check",
    uncheckedIcon: "glyphicon glyphicon-unchecked",

    color: undefined, // '#000000',
    backColor: undefined, // '#FFFFFF',
    borderColor: undefined, // '#dddddd',
    onhoverColor: "#F5F5F5",
    selectedColor: "#337ab7",
    selectedBackColor: "#f5f5f5",
    searchResultColor: "#D9534F",
    searchResultBackColor: undefined, //'#FFFFFF',

    enableLinks: false,
    highlightSelected: true,
    highlightSearchResults: true,
    showBorder: true,
    showIcon: true,
    showCheckbox: false,
    showTags: false,
    multiSelect: false,

    // Event handlers
    onNodeChecked: undefined,
    onNodeCollapsed: undefined,
    onNodeDisabled: undefined,
    onNodeEnabled: undefined,
    onNodeExpanded: undefined,
    onNodeSelected: undefined,
    onNodeUnchecked: undefined,
    onNodeUnselected: undefined,
    onSearchComplete: undefined,
    onSearchCompleteV2: undefined,
    onSearchCleared: undefined
  };

  _default.options = {
    silent: false,
    ignoreChildren: false
  };

  _default.searchOptions = {
    ignoreCase: true,
    exactMatch: false,
    revealResults: true
  };

  var Tree = function(element, options) {
    this.$element = $(element);
    this.elementId = element.id;
    this.styleId = this.elementId + "-style";

    this.init(options);

    return {
      // Options (public access)
      options: this.options,

      // Initialize / destroy methods
      init: $.proxy(this.init, this),
      initTree: $.proxy(this.initTree, this),
      remove: $.proxy(this.remove, this),
      addNode: $.proxy(this.addNode, this),
      refreshTree: $.proxy(this.refreshTree, this),
      updateNode: $.proxy(this.updateNode, this),
      //删除节点
      deleteNode: $.proxy(this.deleteNode, this),
      setDeleteNode: $.proxy(this.setDeleteNode, this),

      // Get methods
      getNode: $.proxy(this.getNode, this),
      getNodeV2: $.proxy(this.getNodeV2, this),
      getParent: $.proxy(this.getParent, this),
      getSiblings: $.proxy(this.getSiblings, this),
      getSelected: $.proxy(this.getSelected, this),
      getUnselected: $.proxy(this.getUnselected, this),
      getExpanded: $.proxy(this.getExpanded, this),
      getCollapsed: $.proxy(this.getCollapsed, this),
      getChecked: $.proxy(this.getChecked, this),
      getUnchecked: $.proxy(this.getUnchecked, this),
      getDisabled: $.proxy(this.getDisabled, this),
      getEnabled: $.proxy(this.getEnabled, this),

      // Select methods
      selectNode: $.proxy(this.selectNode, this),
      unselectNode: $.proxy(this.unselectNode, this),
      toggleNodeSelected: $.proxy(this.toggleNodeSelected, this),

      // Expand / collapse methods
      collapseAll: $.proxy(this.collapseAll, this),
      collapseNode: $.proxy(this.collapseNode, this),
      expandAll: $.proxy(this.expandAll, this),
      expandNode: $.proxy(this.expandNode, this),
      toggleNodeExpanded: $.proxy(this.toggleNodeExpanded, this),
      revealNode: $.proxy(this.revealNode, this),

      // Expand / collapse methods
      checkAll: $.proxy(this.checkAll, this),
      checkNode: $.proxy(this.checkNode, this),
      uncheckAll: $.proxy(this.uncheckAll, this),
      uncheckNode: $.proxy(this.uncheckNode, this),
      toggleNodeChecked: $.proxy(this.toggleNodeChecked, this),

      // Disable / enable methods
      disableAll: $.proxy(this.disableAll, this),
      disableNode: $.proxy(this.disableNode, this),
      enableAll: $.proxy(this.enableAll, this),
      enableNode: $.proxy(this.enableNode, this),
      toggleNodeDisabled: $.proxy(this.toggleNodeDisabled, this),

      // Search methods
      search: $.proxy(this.search, this),

      clearSearch: $.proxy(this.clearSearch, this),
      searchV2: $.proxy(this.searchV2, this)
    };
  };

  Tree.prototype.init = function(options) {
    this.tree = [];
    this.nodes = [];

    if (options.data) {
      if (typeof options.data === "string") {
        options.data = $.parseJSON(options.data);
      }
      this.tree = $.extend(true, [], options.data);
      delete options.data;
    }
    this.options = $.extend({}, _default.settings, options);

    this.destroy();
    this.subscribeEvents();
    this.setInitialStates({ nodes: this.tree }, 0);
    this.render();
  };

  // Tree.prototype.initTree = function(data) {
  //   this.tree = [];
  //   this.nodes = [];
  //   console.log("initTree data", data);
  //   if (data) {
  //     if (typeof data === "string") {
  //       data = $.parseJSON(data);
  //     }
  //     this.tree = $.extend(true, [], data);
  //     //delete data;
  //   }
  //   //this.options = $.extend({}, _default.settings, options);
  //   this.setInitialStates({ nodes: this.tree }, 0);
  //   this.render();
  // };

  Tree.prototype.remove = function() {
    this.destroy();
    $.removeData(this, pluginName);
    $("#" + this.styleId).remove();
  };

  Tree.prototype.destroy = function() {
    if (!this.initialized) return;

    this.$wrapper.remove();
    this.$wrapper = null;

    // Switch off events
    this.unsubscribeEvents();

    // Reset this.initialized flag
    this.initialized = false;
  };

  Tree.prototype.unsubscribeEvents = function() {
    this.$element.off("click");
    this.$element.off("nodeChecked");
    this.$element.off("nodeCollapsed");
    this.$element.off("nodeDisabled");
    this.$element.off("nodeEnabled");
    this.$element.off("nodeExpanded");
    this.$element.off("nodeSelected");
    this.$element.off("nodeUnchecked");
    this.$element.off("nodeUnselected");
    this.$element.off("searchComplete");
    this.$element.off("searchCompleteV2");
    this.$element.off("searchCleared");
  };

  Tree.prototype.subscribeEvents = function() {
    this.unsubscribeEvents();

    this.$element.on("click", $.proxy(this.clickHandler, this));

    if (typeof this.options.onNodeChecked === "function") {
      this.$element.on("nodeChecked", this.options.onNodeChecked);
    }

    if (typeof this.options.onNodeCollapsed === "function") {
      this.$element.on("nodeCollapsed", this.options.onNodeCollapsed);
    }

    if (typeof this.options.onNodeDisabled === "function") {
      this.$element.on("nodeDisabled", this.options.onNodeDisabled);
    }

    if (typeof this.options.onNodeEnabled === "function") {
      this.$element.on("nodeEnabled", this.options.onNodeEnabled);
    }

    if (typeof this.options.onNodeExpanded === "function") {
      this.$element.on("nodeExpanded", this.options.onNodeExpanded);
    }

    if (typeof this.options.onNodeSelected === "function") {
      this.$element.on("nodeSelected", this.options.onNodeSelected);
    }

    if (typeof this.options.onNodeUnchecked === "function") {
      this.$element.on("nodeUnchecked", this.options.onNodeUnchecked);
    }

    if (typeof this.options.onNodeUnselected === "function") {
      this.$element.on("nodeUnselected", this.options.onNodeUnselected);
    }

    if (typeof this.options.onSearchComplete === "function") {
      this.$element.on("searchComplete", this.options.onSearchComplete);
    }

    if (typeof this.options.onSearchCompleteV2 === "function") {
      this.$element.on("searchCompleteV2", this.options.onSearchCompleteV2);
    }

    if (typeof this.options.onSearchCleared === "function") {
      this.$element.on("searchCleared", this.options.onSearchCleared);
    }
  };

  /*
		Recurse the tree structure and ensure all nodes have
		valid initial states.  User defined states will be preserved.
		For performance we also take this opportunity to
		index nodes in a flattened structure
	*/
  Tree.prototype.setInitialStates = function(node, level) {
    if (!node.nodes) return;
    level += 1;
    // debugger;
    var parent = node;
    var _this = this;
    $.each(node.nodes, function checkStates(index, node) {
      // nodeId : unique, incremental identifier

      //console.log("setInitialStates=node", node);

      //node.nodeId = _this.nodes.length;
      node.nodeId = _this.nodes.length;
      // parentId : transversing up the tree
      //node.parentId = parent.nodeId;
      node.parentId = parent.nodeId;
      // if not provided set selectable default value
      if (!node.hasOwnProperty("selectable")) {
        node.selectable = true;
      }

      // where provided we should preserve states
      node.state = node.state || {};

      // set checked state; unless set always false
      if (!node.state.hasOwnProperty("checked")) {
        node.state.checked = false;
      }

      // set enabled state; unless set always false
      if (!node.state.hasOwnProperty("disabled")) {
        node.state.disabled = false;
      }

      // set expanded state; if not provided based on levels
      if (!node.state.hasOwnProperty("expanded")) {
        if (
          !node.state.disabled &&
          level < _this.options.levels &&
          (node.nodes && node.nodes.length > 0)
        ) {
          node.state.expanded = true;
        } else {
          node.state.expanded = false;
        }
      }

      // set selected state; unless set always false
      if (!node.state.hasOwnProperty("selected")) {
        node.state.selected = false;
      }

      // index nodes in a flattened structure for use later
      _this.nodes.push(node);

      // recurse child nodes and transverse the tree
      if (node.nodes) {
        _this.setInitialStates(node, level);
      }
    });
  };

  Tree.prototype.clickHandler = function(event) {
    if (!this.options.enableLinks) event.preventDefault();
    //console.log("event", event);
    var target = $(event.target);

    //console.log("target", target);
    var node = this.findNode(target);
    if (!node || node.state.disabled) return;

    var classList = target.attr("class") ? target.attr("class").split(" ") : [];
    if (classList.indexOf("expand-icon") !== -1) {
      this.toggleExpandedState(node, _default.options);
      this.render();
    } else if (classList.indexOf("check-icon") !== -1) {
      this.toggleCheckedState(node, _default.options);
      this.render();
    } else {
      if (node.selectable) {
        this.toggleSelectedState(node, _default.options);
      } else {
        this.toggleExpandedState(node, _default.options);
      }

      this.render();
    }
  };
  Tree.prototype.refreshTree = function(identifiers, options) {
    this.setInitialStates({ nodes: this.tree }, 0);
    this.render();
  };
  /**
	 	Updates / replaces a given tree node
		@param {Object} node  - A single node to be replaced
		@param {Object} newNode  - THe replacement node
		@param {optional Object} options
	*/
  Tree.prototype.updateNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, newnode, options) {
        // console.log("updateNode", node);
        // console.log("options", options);
        var parentNode = this.getParent(node);
        //console.log("parentNode", parentNode);
        this.setUpdateNode(parentNode, newnode, options);
      }, this)
    );
  };

  Tree.prototype.setUpdateNode = function(node, newnode, options) {
    // console.log("newnode", newnode);
    // console.log("node", node);
    //当修改最上级的名称时node==undefined
    //debugger;
    if (node == undefined) {
      node = this.tree;
      for (var i = this.tree.length - 1; i >= 0; i--) {
        //console.log("this.tree[i]", this.tree[i]);
        var mynode = this.tree[i];
        // console.log("mynode", mynode);
        // console.log("newnode", newnode);
        if (mynode.id === newnode.id) {
          // this.tree[i].text = newnode.text;
          // this.tree[i].spdd_orderid = newnode.spdd_orderid;
          this.tree[i] = newnode;
          break;
        }
      }
    } else if (node.nodes != null) {
      for (var i = node.nodes.length - 1; i >= 0; i--) {
        var mynode = node.nodes[i];
        // console.log("mynode", mynode);
        // console.log("newnode", newnode);
        if (mynode.id === newnode.id) {
          node.nodes[i] = newnode;
          break;
          // node.nodes[i].text = newnode.text;
          // node.nodes[i].spdd_orderid = newnode.spdd_orderid;
        }
      }
      // console.log("this.tree_finish", this.tree);
      // console.log("node.nodes", this.tree.nodes);
    }
    this.nodes = [];
    this.setInitialStates({ nodes: this.tree }, 0);
    this.render();
  };
  Tree.prototype.addNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        //console.log("node", node);
        this.setAddNode(node, options);
      }, this)
    );
    this.nodes = [];
    this.setInitialStates({ nodes: this.tree }, 0);
    this.render();
  };

  Tree.prototype.setAddNode = function(node, options) {
    if (node.nodes == null) node.nodes = [];
    if (options.node) {
      node.nodes.push(options.node);
    }
    //console.log("node-v2", node);
  };

  Tree.prototype.deleteNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        var parentNode = this.getParent(node);
        //console.log("parentNode", parentNode);
        this.setDeleteNode(parentNode, node, options);
      }, this)
    );
  };

  Tree.prototype.setDeleteNode = function(node, deletenode, options) {
    if (node == undefined) {
      //如果父级为空,直接在最外层删除
      //如果树节点在最外层删除,没有父节点,那么直接删除最后一个
      for (var i = this.tree.length - 1; i >= 0; i--) {
        var mynode = this.tree[i];
        if (mynode.id === deletenode.id) {
          this.tree.splice(i, 1);
        }
      }
    } else if (node.nodes != null) {
      for (var i = node.nodes.length - 1; i >= 0; i--) {
        var mynode = node.nodes[i];
        if (mynode.id === deletenode.id) {
          node.nodes.splice(i, 1);
        }
      }
    }
    this.nodes = [];
    this.setInitialStates({ nodes: this.tree }, 0);
    this.render();
  };

  // Looks up the DOM for the closest parent list item to retrieve the
  // data attribute nodeid, which is used to lookup the node in the flattened structure.
  Tree.prototype.findNodeV2 = function(id) {
    var _this = this;
    return this.nodes.find((item, index) => {
      var val = _this.getNodeValueV2(item, id);
      if (val != undefined) {
        return val;
      }
    });
  };
  /**
		Recursive find for retrieving nested attributes values
		All values are return as strings, unless invalid
		@param {Object} obj - Typically a node, could be any object
		@param {String} attr - Identifies an object property using dot notation
		@return {String} value - Matching attributes string representation
	*/
  Tree.prototype.getNodeValueV2 = function(obj, id) {
    if (obj.id == id) {
      return obj;
    } else {
      return undefined;
    }
  };
  // Looks up the DOM for the closest parent list item to retrieve the
  // data attribute nodeid, which is used to lookup the node in the flattened structure.
  Tree.prototype.findNode = function(target) {
    var nodeId = target.closest("li.list-group-item").attr("data-nodeid");
    //console.log("this.nodes", this.nodes);
    //console.log("findNode-nodeId", nodeId);
    var node = this.nodes[nodeId];

    if (!node) {
      console.log("Error: node does not exist");
    }
    return node;
  };

  Tree.prototype.toggleExpandedState = function(node, options) {
    if (!node) return;
    this.setExpandedState(node, !node.state.expanded, options);
  };

  Tree.prototype.setExpandedState = function(node, state, options) {
    if (state === node.state.expanded) return;

    if (state && node.nodes) {
      // Expand a node
      node.state.expanded = true;
      if (!options.silent) {
        this.$element.trigger("nodeExpanded", $.extend(true, {}, node));
      }
    } else if (!state) {
      // Collapse a node
      node.state.expanded = false;
      if (!options.silent) {
        this.$element.trigger("nodeCollapsed", $.extend(true, {}, node));
      }

      // Collapse child nodes
      if (node.nodes && !options.ignoreChildren) {
        $.each(
          node.nodes,
          $.proxy(function(index, node) {
            this.setExpandedState(node, false, options);
          }, this)
        );
      }
    }
  };

  Tree.prototype.toggleSelectedState = function(node, options) {
    if (!node) return;
    this.setSelectedState(node, !node.state.selected, options);
  };

  Tree.prototype.setSelectedState = function(node, state, options) {
    if (state === node.state.selected) return;

    if (state) {
      // If multiSelect false, unselect previously selected
      if (!this.options.multiSelect) {
        $.each(
          this.findNodes("true", "g", "state.selected"),
          $.proxy(function(index, node) {
            this.setSelectedState(node, false, options);
          }, this)
        );
      }

      // Continue selecting node
      node.state.selected = true;
      if (!options.silent) {
        this.$element.trigger("nodeSelected", $.extend(true, {}, node));
      }
    } else {
      // Unselect node
      node.state.selected = false;
      if (!options.silent) {
        this.$element.trigger("nodeUnselected", $.extend(true, {}, node));
      }
    }
  };

  Tree.prototype.toggleCheckedState = function(node, options) {
    if (!node) return;
    this.setCheckedState(node, !node.state.checked, options);
  };

  Tree.prototype.setCheckedState = function(node, state, options) {
    if (state === node.state.checked) return;

    if (state) {
      // Check node
      node.state.checked = true;

      if (!options.silent) {
        this.$element.trigger("nodeChecked", $.extend(true, {}, node));
      }
    } else {
      // Uncheck node
      node.state.checked = false;
      if (!options.silent) {
        this.$element.trigger("nodeUnchecked", $.extend(true, {}, node));
      }
    }
  };

  Tree.prototype.setDisabledState = function(node, state, options) {
    if (state === node.state.disabled) return;

    if (state) {
      // Disable node
      node.state.disabled = true;

      // Disable all other states
      this.setExpandedState(node, false, options);
      this.setSelectedState(node, false, options);
      this.setCheckedState(node, false, options);

      if (!options.silent) {
        this.$element.trigger("nodeDisabled", $.extend(true, {}, node));
      }
    } else {
      // Enabled node
      node.state.disabled = false;
      if (!options.silent) {
        this.$element.trigger("nodeEnabled", $.extend(true, {}, node));
      }
    }
  };

  Tree.prototype.render = function() {
    if (!this.initialized) {
      // Setup first time only components
      this.$element.addClass(pluginName);
      this.$wrapper = $(this.template.list);

      this.injectStyle();

      this.initialized = true;
    }

    this.$element.empty().append(this.$wrapper.empty());

    // Build tree
    this.buildTree(this.tree, 0);
  };

  // Starting from the root node, and recursing down the
  // structure we build the tree one node at a time
  Tree.prototype.buildTree = function(nodes, level) {
    if (!nodes) return;
    level += 1;

    var _this = this;
    $.each(nodes, function addNodes(id, node) {
      var treeItem = $(_this.template.item)
        .addClass("node-" + _this.elementId)
        .addClass(node.state.checked ? "node-checked" : "")
        .addClass(node.state.disabled ? "node-disabled" : "")
        .addClass(node.state.selected ? "node-selected" : "")
        .addClass(node.searchResult ? "search-result" : "")
        .attr("data-nodeid", node.nodeId)
        .attr("id", node.id)
        .attr("style", _this.buildStyleOverride(node));

      // Add indent/spacer to mimic tree structure
      for (var i = 0; i < level - 1; i++) {
        treeItem.append(_this.template.indent);
      }

      // Add expand, collapse or empty spacer icons
      var classList = [];
      if (node.nodes) {
        classList.push("expand-icon");
        if (node.state.expanded) {
          classList.push(_this.options.collapseIcon);
        } else {
          classList.push(_this.options.expandIcon);
        }
      } else {
        classList.push(_this.options.emptyIcon);
      }

      treeItem.append($(_this.template.icon).addClass(classList.join(" ")));

      // Add node icon
      if (_this.options.showIcon) {
        var classList = ["node-icon"];

        classList.push(node.icon || _this.options.nodeIcon);
        if (node.state.selected) {
          classList.pop();
          classList.push(
            node.selectedIcon ||
              _this.options.selectedIcon ||
              node.icon ||
              _this.options.nodeIcon
          );
        }

        treeItem.append($(_this.template.icon).addClass(classList.join(" ")));
      }

      // Add check / unchecked icon
      if (_this.options.showCheckbox) {
        var classList = ["check-icon"];
        if (node.state.checked) {
          classList.push(_this.options.checkedIcon);
        } else {
          classList.push(_this.options.uncheckedIcon);
        }

        treeItem.append($(_this.template.icon).addClass(classList.join(" ")));
      }

      // Add text
      if (_this.options.enableLinks) {
        // Add hyperlink
        treeItem.append(
          $(_this.template.link)
            .attr("href", node.href)
            .append(node.text)
        );
      } else {
        // otherwise just text
        treeItem.append(node.text);
      }

      // Add tags as badges
      if (_this.options.showTags && node.tags) {
        $.each(node.tags, function addTag(id, tag) {
          treeItem.append($(_this.template.badge).append(tag));
        });
      }

      //如果有html内容,增加
      if (_this.options.showAfterHtml && node.after_html) {
        treeItem.append(node.after_html);
      }

      // Add item to the tree
      _this.$wrapper.append(treeItem);

      // Recursively add child ndoes
      if (node.nodes && node.state.expanded && !node.state.disabled) {
        return _this.buildTree(node.nodes, level);
      }
    });
  };

  // Define any node level style override for
  // 1. selectedNode
  // 2. node|data assigned color overrides
  Tree.prototype.buildStyleOverride = function(node) {
    if (node.state.disabled) return "";

    var color = node.color;
    var backColor = node.backColor;

    if (this.options.highlightSelected && node.state.selected) {
      if (this.options.selectedColor) {
        color = this.options.selectedColor;
      }
      if (this.options.selectedBackColor) {
        backColor = this.options.selectedBackColor;
      }
    }

    if (
      this.options.highlightSearchResults &&
      node.searchResult &&
      !node.state.disabled
    ) {
      if (this.options.searchResultColor) {
        color = this.options.searchResultColor;
      }
      if (this.options.searchResultBackColor) {
        backColor = this.options.searchResultBackColor;
      }
    }

    return "color:" + color + ";background-color:" + backColor + ";";
  };

  // Add inline style into head
  Tree.prototype.injectStyle = function() {
    if (this.options.injectStyle && !document.getElementById(this.styleId)) {
      $(
        '<style type="text/css" id="' +
          this.styleId +
          '"> ' +
          this.buildStyle() +
          " </style>"
      ).appendTo("head");
    }
  };

  // Construct trees style based on user options
  Tree.prototype.buildStyle = function() {
    var style = ".node-" + this.elementId + "{";

    if (this.options.color) {
      style += "color:" + this.options.color + ";";
    }

    if (this.options.backColor) {
      style += "background-color:" + this.options.backColor + ";";
    }

    if (!this.options.showBorder) {
      style += "border:none;";
    } else if (this.options.borderColor) {
      style += "border:1px solid " + this.options.borderColor + ";";
    }
    style += "}";

    if (this.options.onhoverColor) {
      style +=
        ".node-" +
        this.elementId +
        ":not(.node-disabled):hover{" +
        "background-color:" +
        this.options.onhoverColor +
        ";" +
        "}";
    }

    return this.css + style;
  };

  Tree.prototype.template = {
    list: '<ul class="list-group"></ul>',
    item: '<li class="list-group-item"></li>',
    indent: '<span class="indent"></span>',
    icon: '<span class="icon"></span>',
    link: '<a href="#" style="color:inherit;"></a>',
    badge: '<span class="badge"></span>'
  };

  Tree.prototype.css =
    ".treeview .list-group-item{cursor:pointer}.treeview span.indent{margin-left:10px;margin-right:10px}.treeview span.icon{width:12px;margin-right:5px}.treeview .node-disabled{color:silver;cursor:not-allowed}";

  /**
		Returns a single node object that matches the given node id.
		@param {Number} nodeId - A node's unique identifier
		@return {Object} node - Matching node
	*/
  Tree.prototype.getNode = function(nodeId) {
    return this.nodes[nodeId];
  };
  /**
		Returns a single node object that matches the given node id.
		@param {Number} nodeId - A node's unique identifier
		@return {Object} node - Matching node
	*/
  Tree.prototype.getNodeV2 = function(id) {
    return this.findNodeV2(id);
  };

  /**
		Returns the parent node of a given node, if valid otherwise returns undefined.
		@param {Object|Number} identifier - A valid node or node id
		@returns {Object} node - The parent node
	*/
  Tree.prototype.getParent = function(identifier) {
    var node = this.identifyNode(identifier);
    return this.nodes[node.parentId];
  };

  /**
		Returns an array of sibling nodes for a given node, if valid otherwise returns undefined.
		@param {Object|Number} identifier - A valid node or node id
		@returns {Array} nodes - Sibling nodes
	*/
  Tree.prototype.getSiblings = function(identifier) {
    var node = this.identifyNode(identifier);
    var parent = this.getParent(node);
    var nodes = parent ? parent.nodes : this.tree;
    return nodes.filter(function(obj) {
      return obj.nodeId !== node.nodeId;
    });
  };

  /**
		Returns an array of selected nodes.
		@returns {Array} nodes - Selected nodes
	*/
  Tree.prototype.getSelected = function() {
    return this.findNodes("true", "g", "state.selected");
  };

  /**
		Returns an array of unselected nodes.
		@returns {Array} nodes - Unselected nodes
	*/
  Tree.prototype.getUnselected = function() {
    return this.findNodes("false", "g", "state.selected");
  };

  /**
		Returns an array of expanded nodes.
		@returns {Array} nodes - Expanded nodes
	*/
  Tree.prototype.getExpanded = function() {
    return this.findNodes("true", "g", "state.expanded");
  };

  /**
		Returns an array of collapsed nodes.
		@returns {Array} nodes - Collapsed nodes
	*/
  Tree.prototype.getCollapsed = function() {
    return this.findNodes("false", "g", "state.expanded");
  };

  /**
		Returns an array of checked nodes.
		@returns {Array} nodes - Checked nodes
	*/
  Tree.prototype.getChecked = function() {
    return this.findNodes("true", "g", "state.checked");
  };

  /**
		Returns an array of unchecked nodes.
		@returns {Array} nodes - Unchecked nodes
	*/
  Tree.prototype.getUnchecked = function() {
    return this.findNodes("false", "g", "state.checked");
  };

  /**
		Returns an array of disabled nodes.
		@returns {Array} nodes - Disabled nodes
	*/
  Tree.prototype.getDisabled = function() {
    return this.findNodes("true", "g", "state.disabled");
  };

  /**
		Returns an array of enabled nodes.
		@returns {Array} nodes - Enabled nodes
	*/
  Tree.prototype.getEnabled = function() {
    return this.findNodes("false", "g", "state.disabled");
  };

  /**
		Set a node state to selected
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.selectNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setSelectedState(node, true, options);
      }, this)
    );

    this.render();
  };

  /**
		Set a node state to unselected
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.unselectNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setSelectedState(node, false, options);
      }, this)
    );

    this.render();
  };

  /**
		Toggles a node selected state; selecting if unselected, unselecting if selected.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.toggleNodeSelected = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.toggleSelectedState(node, options);
      }, this)
    );

    this.render();
  };

  /**
		Collapse all tree nodes
		@param {optional Object} options
	*/
  Tree.prototype.collapseAll = function(options) {
    var identifiers = this.findNodes("true", "g", "state.expanded");
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setExpandedState(node, false, options);
      }, this)
    );

    this.render();
  };

  /**
		Collapse a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.collapseNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setExpandedState(node, false, options);
      }, this)
    );

    this.render();
  };

  /**
		Expand all tree nodes
		@param {optional Object} options
	*/
  Tree.prototype.expandAll = function(options) {
    options = $.extend({}, _default.options, options);

    if (options && options.levels) {
      this.expandLevels(this.tree, options.levels, options);
    } else {
      var identifiers = this.findNodes("false", "g", "state.expanded");
      this.forEachIdentifier(
        identifiers,
        options,
        $.proxy(function(node, options) {
          this.setExpandedState(node, true, options);
        }, this)
      );
    }

    this.render();
  };

  /**
		Expand a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.expandNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        //console.log("expandNode node", node);
        this.setExpandedState(node, true, options);
        if (node.nodes && (options && options.levels)) {
          this.expandLevels(node.nodes, options.levels - 1, options);
        }
      }, this)
    );
    this.render();
  };

  Tree.prototype.expandLevels = function(nodes, level, options) {
    options = $.extend({}, _default.options, options);

    $.each(
      nodes,
      $.proxy(function(index, node) {
        this.setExpandedState(node, level > 0 ? true : false, options);
        if (node.nodes) {
          this.expandLevels(node.nodes, level - 1, options);
        }
      }, this)
    );
  };

  /**
		Reveals a given tree node, expanding the tree from node to root.
		@param {Object|Number|Array} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.revealNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        var parentNode = this.getParent(node);
        while (parentNode) {
          this.setExpandedState(parentNode, true, options);
          parentNode = this.getParent(parentNode);
        }
      }, this)
    );

    this.render();
  };

  /**
		Toggles a nodes expanded state; collapsing if expanded, expanding if collapsed.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.toggleNodeExpanded = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.toggleExpandedState(node, options);
      }, this)
    );

    this.render();
  };

  /**
		Check all tree nodes
		@param {optional Object} options
	*/
  Tree.prototype.checkAll = function(options) {
    var identifiers = this.findNodes("false", "g", "state.checked");
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setCheckedState(node, true, options);
      }, this)
    );

    this.render();
  };

  /**
		Check a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.checkNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setCheckedState(node, true, options);
      }, this)
    );

    this.render();
  };

  /**
		Uncheck all tree nodes
		@param {optional Object} options
	*/
  Tree.prototype.uncheckAll = function(options) {
    var identifiers = this.findNodes("true", "g", "state.checked");
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setCheckedState(node, false, options);
      }, this)
    );

    this.render();
  };

  /**
		Uncheck a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.uncheckNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setCheckedState(node, false, options);
      }, this)
    );

    this.render();
  };

  /**
		Toggles a nodes checked state; checking if unchecked, unchecking if checked.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.toggleNodeChecked = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.toggleCheckedState(node, options);
      }, this)
    );

    this.render();
  };

  /**
		Disable all tree nodes
		@param {optional Object} options
	*/
  Tree.prototype.disableAll = function(options) {
    var identifiers = this.findNodes("false", "g", "state.disabled");
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setDisabledState(node, true, options);
      }, this)
    );

    this.render();
  };

  /**
		Disable a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.disableNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setDisabledState(node, true, options);
      }, this)
    );

    this.render();
  };

  /**
		Enable all tree nodes
		@param {optional Object} options
	*/
  Tree.prototype.enableAll = function(options) {
    var identifiers = this.findNodes("true", "g", "state.disabled");
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setDisabledState(node, false, options);
      }, this)
    );

    this.render();
  };

  /**
		Enable a given tree node
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.enableNode = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setDisabledState(node, false, options);
      }, this)
    );

    this.render();
  };

  /**
		Toggles a nodes disabled state; disabling is enabled, enabling if disabled.
		@param {Object|Number} identifiers - A valid node, node id or array of node identifiers
		@param {optional Object} options
	*/
  Tree.prototype.toggleNodeDisabled = function(identifiers, options) {
    this.forEachIdentifier(
      identifiers,
      options,
      $.proxy(function(node, options) {
        this.setDisabledState(node, !node.state.disabled, options);
      }, this)
    );

    this.render();
  };

  /**
		Common code for processing multiple identifiers
	*/
  Tree.prototype.forEachIdentifier = function(identifiers, options, callback) {
    options = $.extend({}, _default.options, options);

    if (!(identifiers instanceof Array)) {
      identifiers = [identifiers];
    }

    $.each(
      identifiers,
      $.proxy(function(index, identifier) {
        callback(this.identifyNode(identifier), options);
      }, this)
    );
  };

  /*
		Identifies a node from either a node id or object
	*/
  Tree.prototype.identifyNode = function(identifier) {
    return typeof identifier === "number" ? this.nodes[identifier] : identifier;
  };

  /**
		Searches the tree for nodes (text) that match given criteria
		@param {String} pattern - A given string to match against
		@param {optional Object} options - Search criteria options
		@return {Array} nodes - Matching nodes
	*/
  Tree.prototype.search = function(pattern, options) {
    options = $.extend({}, _default.searchOptions, options);

    this.clearSearch({ render: false });

    var results = [];
    if (pattern && pattern.length > 0) {
      if (options.exactMatch) {
        pattern = "^" + pattern + "$";
      }

      var modifier = "g";
      if (options.ignoreCase) {
        modifier += "i";
      }

      results = this.findNodes(pattern, modifier);

      // Add searchResult property to all matching nodes
      // This will be used to apply custom styles
      // and when identifying result to be cleared
      $.each(results, function(index, node) {
        node.searchResult = true;
      });
    }

    // If revealResults, then render is triggered from revealNode
    // otherwise we just call render.
    if (options.revealResults) {
      this.revealNode(results);
    } else {
      this.render();
    }

    this.$element.trigger("searchComplete", $.extend(true, {}, results));

    return results;
  };

  /**
		Clears previous search results
	*/
  Tree.prototype.clearSearch = function(options) {
    options = $.extend({}, { render: true }, options);

    var results = $.each(this.findNodes("true", "g", "searchResult"), function(
      index,
      node
    ) {
      node.searchResult = false;
    });

    if (options.render) {
      this.render();
    }

    this.$element.trigger("searchCleared", $.extend(true, {}, results));
  };

  /**
		Searches the tree for nodes (text) that match given criteria
		@param {String} pattern - A given string to match against
		@param {optional Object} options - Search criteria options
		@return {Array} nodes - Matching nodes
	*/
  Tree.prototype.searchV2 = function(id, options) {
    //console.log(" search: $.proxy(this.search, this),", id);
    options = $.extend({}, _default.searchOptions, options);

    this.clearSearch({ render: false });
    var results = [];
    results = this.findNodesV2(id);
    // Add searchResult property to all matching nodes
    // This will be used to apply custom styles
    // and when identifying result to be cleared
    $.each(results, function(index, node) {
      node.searchResult = true;
    });
    // If revealResults, then render is triggered from revealNode
    // otherwise we just call render.
    if (options.revealResults) {
      this.revealNode(results);
    } else {
      this.render();
    }

    this.$element.trigger("searchCompleteV2", $.extend(true, {}, results));

    return results;
  };

  // /**
  // 	Clears previous search results
  // */
  // Tree.prototype.clearSearchV2 = function(options) {
  //   options = $.extend({}, { render: true }, options);

  //   var results = $.each(this.findNodes("true", "g", "searchResult"), function(
  //     index,
  //     node
  //   ) {
  //     node.searchResult = false;
  //   });

  //   if (options.render) {
  //     this.render();
  //   }

  //   this.$element.trigger("searchCleared", $.extend(true, {}, results));
  // };

  /**
		Find nodes that match a given criteria
		@param {String} pattern - A given string to match against
		@param {optional String} modifier - Valid RegEx modifiers
		@param {optional String} attribute - Attribute to compare pattern against
		@return {Array} nodes - Nodes that match your criteria
	*/
  Tree.prototype.findNodes = function(pattern, modifier, attribute) {
    modifier = modifier || "g";
    attribute = attribute || "text";

    var _this = this;
    return $.grep(this.nodes, function(node) {
      var val = _this.getNodeValue(node, attribute);
      if (typeof val === "string") {
        return val.match(new RegExp(pattern, modifier));
      }
    });
  };
  /**
		Find nodes that match a given criteria
		@param {String} pattern - A given string to match against
		@param {optional String} modifier - Valid RegEx modifiers
		@param {optional String} attribute - Attribute to compare pattern against
		@return {Array} nodes - Nodes that match your criteria
	*/
  Tree.prototype.findNodesV2 = function(id) {
    // modifier = modifier || "g";
    // attribute = attribute || "text";

    var _this = this;
    return $.grep(this.nodes, function(node) {
      var val = _this.getNodeValueV2(node, id);
      if (val != undefined) {
        return val;
        //return val.match(new RegExp(pattern, modifier));
      }
    });
  };
  /**
		Recursive find for retrieving nested attributes values
		All values are return as strings, unless invalid
		@param {Object} obj - Typically a node, could be any object
		@param {String} attr - Identifies an object property using dot notation
		@return {String} value - Matching attributes string representation
	*/
  Tree.prototype.getNodeValue = function(obj, attr) {
    var index = attr.indexOf(".");
    if (index > 0) {
      var _obj = obj[attr.substring(0, index)];
      var _attr = attr.substring(index + 1, attr.length);
      return this.getNodeValue(_obj, _attr);
    } else {
      if (obj.hasOwnProperty(attr)) {
        return obj[attr].toString();
      } else {
        return undefined;
      }
    }
  };

  var logError = function(message) {
    if (window.console) {
      window.console.error(message);
    }
  };

  // Prevent against multiple instantiations,
  // handle updates and method calls
  $.fn[pluginName] = function(options, args) {
    var result;

    this.each(function() {
      var _this = $.data(this, pluginName);
      if (typeof options === "string") {
        if (!_this) {
          logError("Not initialized, can not call method : " + options);
        } else if (!$.isFunction(_this[options]) || options.charAt(0) === "_") {
          logError("No such method : " + options);
        } else {
          if (!(args instanceof Array)) {
            args = [args];
          }
          result = _this[options].apply(_this, args);
        }
      } else if (typeof options === "boolean") {
        result = _this;
      } else {
        $.data(this, pluginName, new Tree(this, $.extend(true, {}, options)));
      }
    });

    return result || this;
  };
})(jQuery, window, document);
<!DOCTYPE html>
<html>

<head>
	<meta charset="UTF-8" />
	<meta name="viewport"
		content="width=device-width, initial-scale=1.0, minimum-scale=1, maximum-scale=1, user-scalable=no" />
	<!--表头设置禁止缓存-->
	<meta http-equiv="pragma" content="no-cache" />
	<meta http-equiv="Cache-Control" content="no-cache, must-revalidate" />
	<meta http-equiv="expires" content="0" />
	<title>铁将军项目文档</title>
	<link rel="shortcut icon" href="./icon/favicon.ico" />
	<link rel="stylesheet" href="css/index.css?id=9090705" />
	<link href="css/layer.css" rel="stylesheet" type="text/css" media="all" />
	<link href="css/bootstrap.css" rel="stylesheet" type="text/css" />
	<link href="css/bootstrap.min.css" rel="stylesheet" type="text/css" />
	
	<link href="css/vditor.css" rel="stylesheet" type="text/css" />
	<!-- <link href="js/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css" /> -->
	<script type="text/javascript" src="js/jquery-1.8.3.js"></script>
	<script type="text/javascript" src="js/vue.js"></script>

	<script type="text/javascript" src="js/bootstrap-treeview.js?id=9090705"></script>
	<!-- <script type="text/javascript" src="js/bootstrap/js/bootstrap.min.js"></script> -->
	<script type="text/javascript" src="js/bootstrap.min.js"></script> 
	
	<script type="text/javascript" src="js/layer.js"></script>
	<script type="text/javascript" src="js/axios.js"></script>
	<script type="text/javascript" src="js/vditor.js"></script>
</head>

<body>
	<div id="app">
		<div class="treeNav">
			<div class="searchBox">
				<div class="input-group">
					<input type="text" class="searchInput form-control" placeholder="请输入关键词查找" aria-describedby="basic-addon2" />
					<span class="input-group-btn">
						<button class="btn btn-primary" type="button" @click="searchDocument">
							查询
						</button>
					</span>
				</div>
			</div>
			<div class="left_nav">
				<!-- 默认显示目录 -->
				<div id="treeview"></div>
			</div>
		</div>
		<div class="main">
			<div class="head_title">
				<!-- 默认显示 -->
				<p class="project-doc-name"></p>
				<div class="head_desc">
					发布时间:<span class="project-doc-publish-time"></span> 版本:<span class="project-doc-version"></span>
				</div>
			</div>
			<div class="right_content">
				<div class="edit_desc">
					最后编辑时间:<span class="doc-end-edit-time"></span> 版本:<span class="project-doc-internal-version"></span>
				</div>
				<div class="detailsBox">
					<!-- 富文本内容示例start -->
					<!-- <div id="editor-md"> -->
					<div id="vditor"></div>
					<!-- <div style="margin-top:5px;margin-left:455px;">
						<button class="el-button el-button--primary el-button--mini" type="button" @click="updateContent">
							临时保存
						</button>
						<button class="el-button el-button--danger el-button--mini" type="button" @click="releaseData">
							发布正式版本
						</button>
					</div> -->
					<!-- </div> -->
					<!-- 富文本内容示例end -->
				</div>
			</div>
		</div>
		<!-- 新增文件填写弹层 -->
		<div class="modal fade" id="myModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
			<div class="modal-dialog">
				<div class="modal-content">
					<div class="modal-header">
						<button type="button" class="close" data-dismiss="modal" aria-hidden="true">
							&times;
						</button>
						<!-- <h4 v-if="isAdd" class="modal-title" id="myModalLabel">上级:{{model.text}}</h4> -->
						<h4 v-if="isAdd" class="modal-title">添加</h4>
						<h4 v-else class="modal-title">编辑</h4>
					</div>
					<div class="modal-body">
						<div  class="btn-group">
							<button type="button" class="btn btn-default dropdown-toggle xlBtn" data-toggle="dropdown"
								aria-haspopup="true" aria-expanded="false">
								选择上级
								<span class="caret"></span>
								<span class="sr-only">Toggle Dropdown</span>
							</button>
							<button type="button" class="btn btn-default selectBtn disabled">{{model.parentid_name}}</button>
							<ul class="dropdown-menu">
								<li :class="{ docu_active: index==checkIndex }" @click="select_up(item,index)"
									v-for="(item,index) in docDetailsOptions" :key="index"><a
										href="#">{{item.name}}-({{item.orderid}})</a>
								</li>
							</ul>
						</div>
						<div class="input-group" style="margin-top:10px;">
							<span class="input-group-addon" id="basic-addon1">名称</span>
							<input type="text" class="form-control" placeholder="请输入文件名称" v-model="model.name">
						</div>
						<div v-if="!isAdd" style="margin-top:10px;" class="input-group">
							<span class="input-group-addon" id="basic-addon1">排序</span>
							<input type="text" class="form-control" placeholder="请输入排序ID" v-model="model.spdd_orderid">
						</div>
					</div>
					<div class="modal-footer">
						<button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>
						<button v-if="isAdd" type="button" class="btn btn-primary" id="submit" @click="addDocInfo()">添加</button>
						<button v-else type="button" class="btn btn-primary" id="submit" @click="editDocInfo()">编辑</button>
					</div>
				</div>
			</div>
		</div>
		<!-- 删除弹层 -->
		<div class="modal fade" id="removeModal" tabindex="-1" role="dialog" aria-labelledby="removeModalLabel"
			aria-hidden="true">
			<div class="modal-dialog modal-sm">
				<div class="modal-content">
					<div class="modal-body">确定删除该文档吗?</div>
					<div class="modal-footer">
						<button type="button" class="btn btn-default" data-dismiss="modal">取消</button>
						<button type="button" class="btn btn-primary" id="removeBtn" @click="delDocInfo()">确定</button>
					</div>
				</div>
			</div>
		</div>
		<!-- 悬浮按钮 保存和发布 -->
		<div class="func_btn">
			<div class="saveBtn" @click="updateContent">保存</div>
			<div class="releaseBtn" @click="releaseData">发布</div>
		</div>
	</div>



	<script type="text/javascript">
		function getRequestArgs(query) {
			var url = query; //获取url中"?"符后的字串
			var theRequest = new Object();
			if (url.indexOf("?") != -1) {
				var str = url.substr(1);
				strs = str.split("&");
				if (strs.length > 1) {
					for (var i = 0; i < strs.length; i++) {
						theRequest[strs[i].split("=")[0]] = decodeURI(
							strs[i].split("=")[1]
						);
					}
				} else {
					theRequest[str.split("=")[0]] = decodeURI(str.split("=")[1]);
				}
			} else {
				return null;
			}
			return theRequest;
		}

		new Vue({
			el: "#app",
			data: {
				pathType: 10,
				search_text: "测试文档",
				defaultData: [], // 示例数据 [{text:'测试文夹',nodes:[{text:'测试文档'}]}]
				contentEditor: null,
				value: "",
				height: window.innerHeight - 130,
				model: {
					after_html: "",
					child_count: "",
					id: 0,
					name: "",
					nodeId: 0,
					nodes: [],
					parentId: "",
					selectable: true,
					spdd_id: 0,
					spdd_parent_id: 0,
					spdd_spd_id: 0,
					parentid_name: "",
				},
				tempNode: null,
				tempParentNode: null,
				docid: 0,
				docDetailid: 0,
				childData: {},
				isDisable: false,
				operateType: 0, //30=添加;40=编辑;50=删除
				isAdd: true,
				getDefaultContentNum: 0,
				checkIndex: 0,
				docDetailsOptions: {}, //文档细节下拉框
				// downList: [{
				// 	id: 0,
				// 	name: '项目文档1'
				// }, {
				// 	id: 1,
				// 	name: '项目文档2'
				// }, {
				// 	id: 2,
				// 	name: '项目文档3'
				// }, {
				// 	id: 3,
				// 	name: '项目文档4'
				// }]
			},

			created() {
				window.addDoc = this.addDoc
				window.editDoc = this.editDoc
				window.delDoc = this.delDoc

			},
			async mounted() {
				this.initVditor()
				const obj = getRequestArgs(location.search);
				try {

					this.docDetailid = parseInt(obj.spdd_id)
					this.docid = parseInt(obj.spdd_spd_id)
          this.initTree()
				} catch (err) {
					console.error(err);
					return;

				}

				this.getDocDetailsOptions()
			},

			methods: {
				//重新加载整棵树
				initTree(detailid) {
					try {
						let params = new URLSearchParams();
						params.append('spdd_spd_id', parseInt(this.docid));
           
						axios.post("/api/manage/v1/projectdoc/systemprojectdocumentinfo?type=100", params)
						.then(({
							data
						}) => {
							this.isDisable = false;
						
							if (data.status) {
								this.defaultData = data.data.tree || this.defaultData;
							
						// 设置项目文档名称
						$(".project-doc-name").text(data.data.spd_name);
						// 设置项目文档发布时间
						$(".project-doc-publish-time").text(data.data.spd_creation_time);
						$(".doc-end-edit-time").text(data.data.spd_creation_time);
						// 设置项目文档版本
						$(".project-doc-version").text(data.data.spd_show_version);
						$(".project-doc-internal-version").text(data.data.spd_show_version);
					if (!detailid){
						detailid=this.docDetailid
					}
					this.getData()
					this.getDefaultContentNum=0
					setTimeout(() => {
					
					this.getDefaultContent(detailid);
			  	}, 300)
							} else {
								//错误提示
								layer.msg(data.msg);
							}
						})
						.catch((err) => {
							this.isDisable = false;
							console.error(err);
						});
					
					} catch (err) {
						console.error(err);
						return;
					}
				
				},
				// 选择上级
				select_up(item, index) {
					this.checkIndex = index
					this.model.spdd_parent_id = item.id
					this.model.parentid_name = item.name
					this.$forceUpdate()
				},
				setDefaultOptionIndex(id) {
					this.docDetailsOptions.find((item, index) => {

						if (item.id == id) {
							this.select_up(item, index)
							return
						}
					});

				},
				searchNodeByid(id) {
					var tempid = this.docDetailid
					if (id) {
						tempid = id
					}
					$("#treeview").treeview("searchV2", [
						tempid,
						{
							ignoreCase: true, // case insensitive
							exactMatch: false, // like or equals
							revealResults: true, // reveal matching nodes
						},
					]);
				},
				addDoc(id) {
					this.isAdd = true
					//阻止事件冒泡
					window.event.cancelBubble = true;
					var node = $("#treeview").treeview("getNodeV2", id);
					this.model = JSON.parse(JSON.stringify(node))
					this.model.name = ""
					this.tempNode = JSON.parse(JSON.stringify(node))

					var defaultParentid=this.model.spdd_id
					if (this.model.spdd_orderid==0){
						defaultParentid=0
					}
					this.setDefaultOptionIndex(defaultParentid)
					this.$forceUpdate()
					$('#filename').val('');
					$("#myModal").modal();
				},
				editDoc(id) {
					this.isAdd = false
					//阻止事件冒泡
					window.event.cancelBubble = true;
					var node = $("#treeview").treeview("getNodeV2", id);
					this.model = JSON.parse(JSON.stringify(node))
					this.model.name = this.model.text
					this.model.parentid_name = ""
					this.model.old_spdd_parent_id = this.model.spdd_parent_id
					$('#filename').val('');
					$("#myModal").modal();
					this.setDefaultOptionIndex(this.model.spdd_parent_id)
					this.$forceUpdate()
				},
				delDoc(id) {
					//阻止事件冒泡
					window.event.cancelBubble = true;
					var node = $("#treeview").treeview("getNodeV2", id);
					this.tempNode = JSON.parse(JSON.stringify(node))
					tempParentNodeData= $("#treeview").treeview("getNode", this.tempNode.parentId);
					this.tempParentNode = JSON.parse(JSON.stringify(tempParentNodeData))
					$("#removeModal").modal();
				},
				delDocInfo() {
					let params = new URLSearchParams();
					params.append('spdd_id', this.tempNode.spdd_id);
				
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentdetail?type=50", params)
						.then(({
							data
						}) => {
							this.isDisable = false;
							if (data.status) {
								if (this.model.spdd_id == this.tempNode.spdd_id) {
									this.model = {}
									this.value = ""
									this.contentEditor.setValue("")
								}
								//请求成功
								$("#removeModal").modal('hide');
								layer.msg(`删除成功`);
								if (this.tempParentNode && this.tempParentNode.nodes && this.tempParentNode.nodes.length == 1) {
									this.tempParentNode.nodes = null
									$('#treeview').treeview('updateNode', [this.tempNode.parentId, this.tempParentNode, {
										silent: true
									}]);
								} else{
									$("#treeview").treeview("deleteNode", [this.tempNode.nodeId, {
										silent: true
									}]);
								
								}
								this.getDocDetailsOptions()
							} else {
								//错误提示
								layer.msg(data.msg);
							}
						})
						.catch((err) => {
							this.isDisable = false;
							console.error(err);
						});
				},
				addDocInfo() {
					if (this.model.name.trim() == "") {
						layer.msg("请填写文件名称");
						return false
					}
					this.isDisable = true;
					let params = new URLSearchParams();
					params.append('spdd_id', 0);
					params.append('spdd_parent_id', this.model.spdd_parent_id);
					params.append('spdd_spd_id', this.model.spdd_spd_id);
					params.append('spdd_name', this.model.name);
					params.append('spdd_state', 10);
					params.append('spdd_orderid', 1);
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentdetail?type=30", params)
						.then(({
							data
						}) => {
							this.isDisable = false;
							if (data.status) {
								//请求成功
								$("#myModal").modal('hide');
								layer.msg(`添加成功`);
						
                //如果上级是点击添加的节点,那么数据追加在节点下面,
								if (this.model.spdd_parent_id==	this.tempNode.spdd_id){
									$("#treeview").treeview("addNode", [this.model.nodeId, {
									node: data.data
								}]);

								// $("#treeview").treeview("addNode", [this.tempNode, {
								// 	node: data.data
								// }]);

								}else{//重新加载整棵树
									this.initTree( data.data.spdd_id)
								}
								this.getDocDetailsOptions()
							} else {
								//错误提示
								layer.msg(data.msg);

							}
						})
						.catch((err) => {
							this.isDisable = false;
							//console.error(err);
						});
				},

				editDocInfo() {
					if (this.model.name.trim() == "") {
						layer.msg("请填写文件名称");
						return false
					}
					if (parseInt(this.model.spdd_orderid) < 0) {
						layer.msg("请填写大于0的排序id");
						return false
					}
					if (this.model.spdd_orderid.length > 9 || this.model.spdd_orderid.length == 0) {
						layer.msg("请填写9位数字的排序id");
						return false
					}
					//this.model.text = this.model.name
					newNode = this.model

					//console.log("newNode",newNode)
					this.isDisable = true;
					let params = new URLSearchParams();
					params.append('spdd_id', this.model.spdd_id);
					params.append('spdd_orderid', this.model.spdd_orderid);
					params.append('spdd_parent_id', this.model.spdd_parent_id);
					params.append('spdd_spd_id', this.model.spdd_spd_id);
					params.append('spdd_state', this.model.spdd_state);
					params.append('old_spdd_name', this.model.text);
					params.append('spdd_name', this.model.name);
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentdetail?type=40", params)
						.then(({
							data
						}) => {
							this.isDisable = false;
							if (data.status) {
								//请求成功
								$("#myModal").modal('hide');
								layer.msg(`保存成功`);
								this.model.text = this.model.name
								if (this.model.old_spdd_parent_id == this.model.spdd_parent_id) {
									$('#treeview').treeview('updateNode', [this.model.nodeId, newNode, {
										silent: true
									}]);
								} else {//修改了父级,重新加载整棵树
                  this.initTree(this.model.spdd_id)
								}
								this.getDocDetailsOptions()
							} else {
								//错误提示
								layer.msg(data.msg);

							}
						})
						.catch((err) => {
							this.isDisable = false;
							console.error(err);
						});
				},
				getData() {
					const that = this;
					// treeview参考文档: https://www.jq22.com/jquery-info10461
					$("#treeview").treeview({
						levels: 1,
						highlightSelected: true,
						highlightSearchResults: true,
						enableLinks: true, //必须在节点属性给出href属性
						//searchResultBackColor: "#00ff00", // TODO: 这里需要改一个合适的颜色
						data: this.defaultData,
						showAfterHtml: true,
						searchResultColor: "#409eff",
						//onhoverColor:"#409eff",
						onNodeSelected: function (event, node) {
							if (JSON.stringify(node) === "{}") {
								return;
							}
							// 1.有点击节点的操作, 先清空搜索高亮的状态
							$("#treeview").treeview("clearSearch");
							// 2.选择选中了文件夹, 则让文件夹展开(组件本身只能点击图标展开文件夹, 这里需要做一个兼容处理)
							
							if (node.nodes) {
								if (node.nodes.length > 0 && node.state.selected) {
									// 10=文件夹 20=文件
									if (!node.state.expanded) {
										// 展开文件夹
										$("#treeview").treeview("expandNode", [
											node.nodeId,
											{
												levels: 1,
												silent: true
											},
										]);
									} else {
										// 折叠文件夹
										$("#treeview").treeview("collapseNode", [
											node.nodeId,
											{
												silent: true,
												ignoreChildren: false
											},
										]);
									}
									return;
								}
							}
						
							// 3.选中了文件, 去请求文档详细内容, 渲染内容
							let params = new URLSearchParams();
							that.model = JSON.parse(JSON.stringify(node))
							params.append('spdd_id', node.spdd_id);
							params.append('spdd_parent_id', node.spdd_parent_id);
							params.append('spdd_spd_id', node.spdd_spd_id);
							//console.log("getDocContent",node)
							that.getDocContent(params)
						},
						onSearchComplete: function (event, node) {
							if (JSON.stringify(node) === "{}") {
								// 未搜索到该文档
								const searchInput = $(".searchInput").val().trim();
								layer.msg(`未搜索到相关文档 "${searchInput}"`);
								return;
							}
						},
						onSearchCompleteV2: function (event, node) {
							
							if (JSON.stringify(node) === "{}") {
								//layer.msg(`文档不存在`);
								return;
							} else {
								let params = new URLSearchParams();
								that.model = JSON.parse(JSON.stringify(node[0])) 
							
								params.append('spdd_id', that.model.spdd_id);
								params.append('spdd_parent_id', that.model.spdd_parent_id);
								params.append('spdd_spd_id', that.model.spdd_spd_id);
								that.getDocContent(params)
							}
						},
					});
				},

				getDefaultContent(id) {
					if (id > 0) {
						this.getDefaultContentNum++;
					} else {
						this.getDefaultContentNum = 2
					}
					if (this.getDefaultContentNum > 2) {
						return
					}

					let params = new URLSearchParams();
					params.append('spdd_id', id);
					params.append('spdd_spd_id', this.docid);
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentinfo?type=111", params)
						.then(({
							data
						}) => {
							if (data.status) {

								// 最后编辑时间
								$(".doc-end-edit-time").text(data.data.spdd_update_time);
								$(".project-doc-internal-version").text(data.data.spddc_internal_version);
							
								this.model = data.data
								this.value = this.model.spdd_content
							  this.contentEditor.setValue(this.model.spdd_content)


								this.searchNodeByid(this.model.spdd_id)
							} else {
								if (this.getDefaultContentNum >= 2) {
									//layer.msg(data.msg);
								} else {
									this.getDefaultContent(0)
								}
							}
						})
						.catch((err) => {
							console.error(err);
						});
				},
				getDocContent(params) {
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentinfo?type=110", params)
						.then(({
							data
						}) => {
							if (data.status) {
								// 最后编辑时间
								$(".doc-end-edit-time").text(
									data.data.spdd_update_time
								);
								$(".project-doc-internal-version").text(data.data.spddc_internal_version);
							
								this.model = data.data
								this.value = this.model.spdd_content
								this.contentEditor.setValue(this.model.spdd_content)
								

							} else {
								layer.msg(data.msg);
							}
						})
						.catch((err) => {
							console.error(err);
						});
				},
				edit(data) {
					console.log("编辑", data)
				},
				getDocDetailsOptions() {
					let params = new URLSearchParams();
					params.append('spdd_spd_id', this.docid);
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentdetail?type=12", params)
						.then(({
							data
						}) => {
							if (data.status) {
							
								var arr = [{ "id": 0, "name": "顶级", "parent_id": 0, "spdd_spd_id": 0, "orderid": -1 }]
								this.docDetailsOptions = arr.concat(data.data);
								
							} else {
								layer.msg(data.msg);
							}
						})
						.catch((err) => {
							console.error(err);
						});
				},
				edit(data) {
					console.log("编辑", data)
				},
				//======================临时保存=========================
				updateContent() {
					if (this.model.spdd_id == undefined) {
						layer.msg("文档找不到了请重新选中文档");
						return false
					}
					this.model.spdd_content = this.contentEditor.getValue()
					if (this.model.spdd_content.trim() == "") {
						layer.msg("请填写项目文档细节内容");
						return false
					}
					this.isDisable = true;
					let params = new URLSearchParams();
					params.append('spdd_id', this.model.spdd_id);
					params.append('spdd_content', this.model.spdd_content);
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentdetail?type=45", params)
						.then(({
							data
						}) => {
							this.isDisable = false;

						
							if (data.status) {
								//请求成功
								layer.msg(`保存成功`);
								let params = new URLSearchParams();
								params.append('spdd_id', this.model.spdd_id);
								params.append('spdd_parent_id', this.model.spdd_parent_id);
								params.append('spdd_spd_id', this.model.spdd_spd_id);
								this.getDocContent(params)
							} else {
								//错误提示
								layer.msg(data.msg);
							}
						})
						.catch(err => {
							this.isDisable = false;
							layer.msg(err);
						});


				},
				//======================发布=========================
				releaseData() {
					if (this.model.spdd_id == undefined) {
						layer.msg("文档找不到了请重新选中文档");
						return false
					}
					this.model.spdd_content = this.contentEditor.getValue()
					if (this.model.spdd_content.trim() == "") {
						layer.msg("请填写项目文档细节内容");
						return false
					}

				
					this.isDisable = true;
					let params = new URLSearchParams();
					params.append('spdd_id', this.model.spdd_id);
					params.append('spdd_spd_id', this.model.spdd_spd_id);
					params.append('spdd_content', this.model.spdd_content);
					axios.post("/api/manage/v1/projectdoc/systemprojectdocumentdetail?type=48", params)
						.then(({
							data
						}) => {
							this.isDisable = false;
							if (data.status) {
								//请求成功
								layer.msg(`发布成功`);
								let params = new URLSearchParams();
								params.append('spdd_id', this.model.spdd_id);
								params.append('spdd_parent_id', this.model.spdd_parent_id);
								params.append('spdd_spd_id', this.model.spdd_spd_id);

								this.getDocContent(params)
							} else {
								//错误提示
								layer.msg(data.msg);
							}
						})
						.catch(err => {
							this.isDisable = false;
							layer.msg(err);
						});
				},
				// 搜索文档
				searchDocument() {
					var searchInput = $('.searchInput').val().trim();
					if (searchInput.length < 1) {
						layer.msg('请输入关键词');
						return
					}
					$("#treeview").treeview("search", [
						searchInput,
						{
							ignoreCase: true, // case insensitive
							exactMatch: false, // like or equals
							revealResults: true, // reveal matching nodes
						},
					]);
				},

				// vditor start
				initVditor() {
					const that = this
					const options = {
						height: this.height,
						value: this.value,
						cache: {
							enable: false
						},
						outline :{
							enable:true,
						},
						toolbar: [
							// "emoji",
							"headings",
							"bold",
							"italic",
							"strike",
							"link",
							"|",
							"list",
							"ordered-list",
							"check",
							"outdent",
							"indent",
							"|",
							"quote",
							"line",
							"code",
							"inline-code",
							"insert-before",
							"insert-after",
							"|",
							{ //自定义上传
								hotkey: "",
								name: "upload",

								tip: "上传图片",
								className: "right",
							},
							"table",
							"|",
							"undo",
							"redo",
							"|",
							"edit-mode",
							{
								name: "more",
								toolbar: [
									//"both",
									"code-theme",
									"content-theme",
									"export",
									"outline",
									"preview",

								],
							},
						],
						after: () => {
							this.contentEditor.setValue(this.value)
						},
						mode: "ir",
						preview: {
							mode: "both",
							actions: []
						},
						//这里写上传
						upload: {
							accept: 'image/jpg, image/jpeg, image/png', //规定上传的图片格式
							url: "/api/admin/uploadFile?type=99", //请求的接口
							multiple: false,
							fieldName: 'file',
							max: 2 * 1024 * 1024, //上传图片的大小
							// extraData: { 'access_token': this.token }, //为 FormData 添加额外的参数
							linkToImgUrl: "/api/admin/uploadFile?type=99",
							filename(name) {
								return name.replace(/[^(a-zA-Z0-9\u4e00-\u9fa5\.)]/g, "")
									.replace(/[\?\\/:|<>\*\[\]\(\)\$%\{\}@~]/g, "")
									.replace("/\\s/g", "");
							},
							validate(files) {
								const isLt2M = files[0].size / 1024 / 1024 < 2
								if (!isLt2M) {
									layer.msg("上传图片大小不能超过 2MB!");
								}
							},
							//粘贴图片回显处理,如果有图片加了防盗链,则让后台代理替换成自己的图片
							linkToImgFormat(files) {
								let code = 0
								let msg = ''
							},
							//上传图片回显处理
							format(files, responseText) {
								var self = this;
								var data = JSON.parse(responseText)
								//上传图片请求状态
								if (data.status) {
									let filName = data.msg
									let lastTipNum = filName.substr(filName.lastIndexOf('/', filName.lastIndexOf(
										'/') - 1) + 1);
							
									let index = lastTipNum.lastIndexOf("\/");
									let succ = {}
									succ[filName] = "/api" + data.data
									//图片回显
									return JSON.stringify({
										data,
										data,
										data: {
											errFiles: [],
											succMap: succ
										}
									})
								} else {
									layer.msg("图片上传失败!");
								}

							},
							error(msg) {
								console.log(msg + "上传失败了")
							},
						}
					}
					this.contentEditor = new Vditor('vditor', options)
				}
				// /vditor end/
			},
		});
	</script>
		<style scoped>
			/deep/ #app {
				height: 100%;
				overflow-y: hidden;
			}
			/deep/.glyphicon-file:before {
				content: "";
			}
	
			/deep/ a:hover,
			a:focus {
				color: #409eff !important;
			}
			
			.el-button--primary:focus,
			.el-button--primary:hover {
				background: #66b1ff;
				border-color: #66b1ff;
				color: #fff;
			}
	
			.el-button:focus,
			.el-button:hover {
				color: #409eff;
				border-color: #c6e2ff;
				background-color: #ecf5ff;
			}
	
			.el-button+.el-button {
				margin-left: 10px;
			}
	
			[type=reset],
			[type=submit],
			button,
			html [type=button] {
				-webkit-appearance: button;
			}
	
			.el-button--mini,
			.el-button--mini.is-round {
				padding: 7px 15px;
			}
	
			.el-button--mini,
			.el-button--small {
				font-size: 12px;
				border-radius: 3px;
			}
	
			.el-button--primary {
				color: #fff;
				background-color: #409eff;
				border-color: #409eff;
			}
	
			.el-button--danger {
				color: #FFF;
				background-color: #F56C6C;
				border-color: #F56C6C;
			}
	
			.button_z {
				float: right;
			}
	
			.imgbtn {
				width: 22px;
				height: 22px;
			}
	
			.el-button {
				display: inline-block;
				line-height: 1;
				white-space: nowrap;
				cursor: pointer;
				border: 1px solid #dcdfe6;
				text-align: center;
				box-sizing: border-box;
				outline: 0;
				margin: 0;
				transition: .1s;
				font-weight: 500;
			}
	
			.glyphicon-folder-open {
				color: #efc013;
			}
	
		
	
			.glyphicon-file:before {
				content: "";
				display: inline-block;
				background-image: url(img/file.png);
				width: 18px;
				height: 18px;
				/* border: 1px solid red; */
				background-size: cover;
			}
	
			.list-group-item {
				border: none;
			}
	
	
	
			.el-button,
			.el-checkbox,
			.el-image-viewer__btn,
			.el-step__icon-inner {
				-webkit-user-select: none;
			}
	
			button,
			select {
				text-transform: none;
			}
	
			button,
			input {
				overflow: visible;
			}
	
			button,
			input,
			optgroup,
			select,
			textarea {
				font-family: sans-serif;
			}
	
			button {
				writing-mode: horizontal-tb !important;
				font-style: ;
				font-variant-ligatures: ;
				font-variant-caps: ;
				font-variant-numeric: ;
				font-variant-east-asian: ;
				font-stretch: ;
				text-rendering: auto;
				letter-spacing: normal;
				word-spacing: normal;
				text-indent: 0px;
				text-shadow: none;
				align-items: flex-start;
			}
	
			.treeview span.icon {
				width: 18px;
				height: 18px;
				margin-right: -5px;
			}
		</style>
</body>

</html>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值