python3+flask+jquery+mysql 實現前端呈現思維導圖 後臺數據管理

環境

受此篇博客啓發:https://blog.csdn.net/btt2013/article/details/51037356,感謝該作者!

python 3.6

Flask 0.12.2

jquery 1.8.0 其中引用zTree-bootstrap 插件實現後臺管理頁面,下載地址https://github.com/lisztj/ZTREE-BOOTSTRAP,存入static文件夾。引用d3插件實現前端頁面。

mysql 8.0.11

文件目錄


前端主頁面_tree.html

<!DOCTYPE html>
<html>
<meta charset="utf-8">  

<style>  
  
	.node {  
	  cursor: pointer;  
	}  
	  
	.node circle {  
	  fill: #fff;  
	  stroke: steelblue;  
	  stroke-width: 1.5px;  
	}  
	  
	.node text {  
	  font: 10px sans-serif;  
	}  
	  
	.link {  
	  fill: none;  
	  stroke: #ccc;  
	  stroke-width: 1.5px;  
	}  
	.tree{
		width: 800px;
		height: 800px;
		margin: 0 auto;
		background: #E0E0E0;
	}
	.tree svg{
	  	width: 100%;
	  	height: 100%;
	}
  
</style>  
<body>  
	
	<div class="tree" id="tree"></div>

<script src="http://apps.bdimg.com/libs/d3/3.4.8/d3.min.js"></script>
	<script src="http://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.8.0.js"></script>

	<script >
        $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
    </script>
<script >

	$(document).ready(function(){
		$.getJSON($SCRIPT_ROOT+'/_get_front_tree',function(data){
			var json = data
			console.info(json)
			root = json[0];
			var margin = [20, 120, 20, 120],
	    width = document.getElementById("tree").offsetWidth,
	    height = document.getElementById("tree").offsetHeight;

	var i = 0,
	    duration = 750,
	    root;

	var tree = d3.layout.tree()
	    .size([height, width]);

	var diagonal = d3.svg.diagonal()
	    .projection(function(d) { return [d.y, d.x]; });

	var zoom = d3.behavior.zoom().scaleExtent([0.1, 100]).on("zoom", zoomed);//添加放大縮小事件

	var svg = d3.select("body").select("#tree").append("svg")
	.call(zoom)//給svg綁定zoom事件
	  .append("g")
	  .call(zoom)//給g綁定zoom事件
	  .append("g")
	    .attr("transform", "translate(" + margin[3] + "," + margin[0] + ")");


	  root.x0 = height / 2;
	  root.y0 = 0;

	  function collapse(d) {
	    if (d.children) {
	      d._children = d.children;
	      d._children.forEach(collapse);
	      d.children = null;
	    }
	  }

	  root.children.forEach(collapse);
	  update(root);

	function zoomed() {
	    svg.attr("transform",
	        "translate(" + zoom.translate() + ")" +
	        "scale(" + zoom.scale() + ")"
	    );
	}

	function update(source) {

	  // Compute the new tree layout.
	  var nodes = tree.nodes(root).reverse(),
	      links = tree.links(nodes);

	  // Normalize for fixed-depth.
	  nodes.forEach(function(d) { d.y = d.depth * 180; });

	  // Update the nodes…
	  var node = svg.selectAll("g.node")
	      .data(nodes, function(d) { return d.id || (d.id = ++i); });

	  // Enter any new nodes at the parent's previous position.
	  var nodeEnter = node.enter().append("g")
	      .attr("class", "node")
	      .attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
	      .on("click", click);

	  nodeEnter.append("circle")
	      .attr("r", 1e-6)
	      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

	  nodeEnter.append("text")
	      .attr("x", function(d) { return d.children || d._children ? -10 : 10; })
	      .attr("dy", ".35em")
	      .attr("text-anchor", function(d) { return d.children || d._children ? "end" : "start"; })
	      .text(function(d) { return d.name; })
	      .style("fill-opacity", 1e-6);

	  var nodeUpdate = node.transition()
	      .duration(duration)
	      .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });

	  nodeUpdate.select("circle")
	      .attr("r", 4.5)
	      .style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });

	  nodeUpdate.select("text")
	      .style("fill-opacity", 1);

	  var nodeExit = node.exit().transition()
	      .duration(duration)
	      .attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
	      .remove();

	  nodeExit.select("circle")
	      .attr("r", 1e-6);

	  nodeExit.select("text")
	      .style("fill-opacity", 1e-6);

	  var link = svg.selectAll("path.link")
	      .data(links, function(d) { return d.target.id; });

	  link.enter().insert("path", "g")
	      .attr("class", "link")
	      .attr("d", function(d) {
	        var o = {x: source.x0, y: source.y0};
	        return diagonal({source: o, target: o});
	      });

	  link.transition()
	      .duration(duration)
	      .attr("d", diagonal);

	  link.exit().transition()
	      .duration(duration)
	      .attr("d", function(d) {
	        var o = {x: source.x, y: source.y};
	        return diagonal({source: o, target: o});
	      })
	      .remove();

	  nodes.forEach(function(d) {
	    d.x0 = d.x;
	    d.y0 = d.y;
	  });
	}

	function click(d) {
	  if (d.children) {
	    d._children = d.children;
	    d.children = null;
	  } else {
	    d.children = d._children;
	    d._children = null;
	  }
	  update(d);
	}
		})
	})
</script>
	
</body>
</html>

前臺頁面效果

後臺頁面 index.html

<!DOCTYPE html>
<HTML>

<HEAD>
    <TITLE>後臺管理</TITLE>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <link rel="stylesheet" href="/static/css/bootstrapStyle/bootstrapStyle.css" type="text/css">
    <script type="text/javascript" src="/static/js/jquery.min.js"></script>
    <script type="text/javascript" src="/static/js/jquery.ztree.core.js"></script>
    <script type="text/javascript" src="/static/js/jquery.ztree.excheck.js"></script>
    <script type="text/javascript" src="/static/js/jquery.ztree.exedit.js"></script>
    <script >
        $SCRIPT_ROOT = {{ request.script_root|tojson|safe }};
    </script>
    <SCRIPT type="text/javascript">
        var setting = {
            view: {
                addHoverDom: addHoverDom,
                removeHoverDom: removeHoverDom,
                selectedMulti: false
            },
            check: {
                enable: true
            },
            data: {
                simpleData: {
                    enable: true
                }
            },
            edit: {
                enable: true
            },
            callback : {
            beforeRemove : beforeRemove,
            beforeRename : beforeRename,
            }
        };



        $(document).ready(function() {
            $.getJSON($SCRIPT_ROOT+'/_get_tree','',function(data){
                    $.fn.zTree.init($("#treeDemo"), setting, data);
            })

        });

        var newCount = 1;

        function addHoverDom(treeId, treeNode) {
            var sObj = $("#" + treeNode.tId + "_span");
            if (treeNode.editNameFlag || $("#addBtn_" + treeNode.tId).length > 0) return;
            var addStr = "<span class='button add' id='addBtn_" + treeNode.tId +
                "' title='add node' onfocus='this.blur();'></span>";
            sObj.after(addStr);
            var btn = $("#addBtn_" + treeNode.tId);
            if (btn) btn.bind("click", function() {

                var ppname = prompt("請輸入新節點名稱");
                if (ppname == null){
                    return;
                }else if(ppname == ""){
                    alert("節點名稱不能爲空");
                }else{

                    var zTree = $.fn.zTree.getZTreeObj("treeDemo");
                    $.post($SCRIPT_ROOT+'/_add_tree',
                            {pId:treeNode.id, name:ppname},
                            function(data){

                                if ($.trim(data) != null){
                                    var treenode = $.trim(data);
                                    zTree.addNodes(treeNode, {
                                        pId : treeNode.id,
                                        name : ppname

                                    });
                                    return false;

                                }
                            }

                    )
                }



            });
        };

        function removeHoverDom(treeId, treeNode) {
            $("#addBtn_" + treeNode.tId).unbind().remove();
        };


        function beforeRemove(treeId, treeNode) {
            if (confirm("確認刪除節點--" + treeNode.name + "--嗎?")) {

                $.ajax({
                    type: 'delete',
                    url: $SCRIPT_ROOT+'/_delete_tree',
                    data:{id:treeNode.id},
                    success: true
                  });

            } else {
            return false;
            }
        }


        function beforeRename(treeId, treeNode, newName) {
            if (newName.length == 0) {
                alert("節點名稱不能爲空.");
                return false;
            }

            $.ajax({
                    type: 'post',
                    url: $SCRIPT_ROOT+'/_update_tree',
                    data:{id:treeNode.id, newname:newName},
                    success: true

            })

            return true;
        }



    </SCRIPT>
</HEAD>

<BODY>
    <h1>後臺管理</h1>
    <ul id="treeDemo" class="ztree"></ul>
</BODY>

</HTML>

後臺效果圖

數據庫配置



後臺Flask實現代碼 tree.py

import op_sql
import json

from flask import Flask, render_template, request
app = Flask(__name__)


# 前端主頁面
@app.route('/')
def tree_front():
    return render_template('_tree.html')


# 前端處理數據並返回數據
@app.route('/_get_front_tree')
def get_front_tree():

    return json.dumps(op_sql.get_name(0))


# 後臺頁面
@app.route('/background')
def tree():

    return render_template("index.html")


# 後臺返回所有數據
@app.route('/_get_tree')
def get_tree():

    return json.dumps(op_sql.transport())


# 後臺增加節點的函數
@app.route('/_add_tree', methods = ['GET','POST','DELETE'])
def add_node():
    if request.method == 'POST':
        parent_id = request.form['pId']
        ename = request.form['name']
        op_sql.insert('tree_db','employees',ename,parent_id)
        return json.dumps({'pId':parent_id,'name':ename})


# 後臺刪除結點的函數
@app.route('/_delete_tree', methods = ['DELETE'])
def delete_node():
    if request.method == 'DELETE':
        eid = request.form['id']

        op_sql.delete('tree_db','employees',eid)

    return ''


# 後臺更新節點名字的函數
@app.route('/_update_tree',methods = ['POST'])
def update_node():
    if request.method == 'POST':
        eid = request.form['id']
        newname = request.form['newname']
        op_sql.update("tree_db", "employees", eid, newname)
    return ''


# 啓動主頁面
if __name__ == "__main__":
    app.run()

數據庫處理層 op_sql.py

import mysql.connector as mysql


def query(sql_select):
    db = mysql.connect(user="root", password="password", host="127.0.0.1", database="tree_db")

    cursor = db.cursor()
    # 查詢語句
    # sql_select = "select ename from employees where parent_id=%s"%x
    try:

        cursor.execute(sql_select)
        result = cursor.fetchall()
        return result
    finally:
        db.close()


def insert(database, table, ename, parent_id):
    db = mysql.connect(user="root", password="password", host="127.0.0.1", database=database)

    cursor = db.cursor()
    # 寫入語句
    sql_insert = 'insert into %s(ename,parent_id)values("%s","%s");' % (table,ename,parent_id)
    try:
        cursor.execute(sql_insert)
        db.commit()

    # except Exception as e:
    #     db.rollback()

    finally:
        db.close()


def update(database, table, eid, newname):
    db = mysql.connect(user="root", password="password", host="127.0.0.1", database=database)

    cursor = db.cursor()
    # 更新語句
    sql_update = 'update %s set ename = "%s" where eid = "%s";' % (table, newname, eid)
    try:
        cursor.execute(sql_update)
        db.commit()
    finally:
        db.close()


def delete(database, table, id):
    db = mysql.connect(user="root", password="password", host="127.0.0.1", database=database)

    cursor = db.cursor()
    # 刪除語句
    sql_delete = 'delete from %s where eid =%s;' % (table, id)
    try:
        cursor.execute(sql_delete)
        db.commit()
    finally:
        db.close()


# 將數據庫查詢數據標準化爲json數據以便傳入前臺
def get_name(parent_id):
        lis = []
        for i in query("select eid,ename from employees where parent_id='%s';" % parent_id):
            dic = {}
            current_id = i[0]
            ename = i[1]
            dic['name'] = ename
            dic['parent_id'] = parent_id

            children = query("select eid, ename from employees where parent_id='%s';" % current_id)
            if len(children):

                dic['children'] = get_name(current_id)
                lis.append(dic)

            else:
                dic['children'] = []
                lis.append(dic)

        return lis

# 將數據庫查詢數據標準化json數據以便傳入後臺
def transport():
    zNdodes = []

    for i in query("select eid, ename, parent_id from employees;"):
        zNdode = {}
        zNdode['id'] = i[0]
        zNdode['name'] = i[1]
        zNdode['pId'] = i[2]

        zNdodes.append(zNdode)
    return  zNdodes



工程易錯難點記錄:

1、將[{"eid": 1, "ename": "老王", "parent_id": 0}, {"eid": 2, "ename": "老宋", "parent_id": 1}, {"eid": 3, "ename": "老牛", "parent_id": 1}, {"eid": 4, "ename": "小吳", "parent_id": 2}, {"eid": 5, "ename": "小李", "parent_id": 2}, {"eid": 6, "ename": "小歡", "parent_id": 3}, {"eid": 7, "ename": "小小", "parent_id": 3}, {"eid": 8, "ename": "小天", "parent_id": 4}, {"eid": 9, "ename": "小裏", "parent_id": 4}, {"eid": 10, "ename": "小黑", "parent_id": 5}, {"eid": 11, "ename": "小胡", "parent_id": 5}, {"eid": 12, "ename": "小麗", "parent_id": 6}, {"eid": 14, "ename": "小黃黃", "parent_id": 7}, {"eid": 22, "ename": "小綠綠", "parent_id": 7}]

轉化爲

[
    {
        "name": "老王",
        "parent_id": 0,
        "children": [
            {
                "name": "老宋",
                "parent_id": 1,
                "children": [
                    {
                        "name": "小吳",
                        "parent_id": 2,
                        "children": [
                            {
                                "name": "小天",
                                "parent_id": 4,
                                "children": []
                            },
                            {
                                "name": "小裏",
                                "parent_id": 4,
                                "children": []
                            }
                        ]
                    },
                    {
                        "name": "小李",
                        "parent_id": 2,
                        "children": [
                            {
                                "name": "小黑",
                                "parent_id": 5,
                                "children": []
                            },
                            {
                                "name": "小胡",
                                "parent_id": 5,
                                "children": []
                            }
                        ]
                    }
                ]
            },
            {
                "name": "老牛",
                "parent_id": 1,
                "children": [
                    {
                        "name": "小歡",
                        "parent_id": 3,
                        "children": [
                            {
                                "name": "小麗",
                                "parent_id": 6,
                                "children": []
                            }
                        ]
                    },
                    {
                        "name": "小小",
                        "parent_id": 3,
                        "children": [
                            {
                                "name": "小黃黃",
                                "parent_id": 7,
                                "children": []
                            },
                            {
                                "name": "小綠綠",
                                "parent_id": 7,
                                "children": []
                            }
                        ]
                    }
                ]
            }
        ]
    }
]

需進行遞歸處理,過程見以上代碼。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章