2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 20:16:44 +00:00

Implemented tree view

Using library bootstrap-treeview
- part category tree
- stock location tree
- Currenly is functional but looks terrible
This commit is contained in:
Oliver 2018-04-28 23:22:12 +10:00
parent 095492203f
commit 8d0789c37c
13 changed files with 1471 additions and 9 deletions

View File

@ -12,6 +12,7 @@ from build.urls import build_urls
from part.api import part_api_urls from part.api import part_api_urls
from company.api import company_api_urls from company.api import company_api_urls
from stock.api import stock_api_urls
from django.conf import settings from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
@ -25,6 +26,7 @@ admin.site.site_header = "InvenTree Admin"
apipatterns = [ apipatterns = [
url(r'^part/', include(part_api_urls)), url(r'^part/', include(part_api_urls)),
url(r'^company/', include(company_api_urls)), url(r'^company/', include(company_api_urls)),
url(r'^stock/', include(stock_api_urls)),
# User URLs # User URLs
url(r'^user/', include(user_urls)), url(r'^user/', include(user_urls)),

View File

@ -5,6 +5,48 @@ from django.template.loader import render_to_string
from django.http import JsonResponse from django.http import JsonResponse
from django.views.generic import UpdateView, CreateView, DeleteView from django.views.generic import UpdateView, CreateView, DeleteView
from rest_framework import views
from django.http import JsonResponse
class TreeSerializer(views.APIView):
def itemToJson(self, item):
data = {
'text': item.name,
'href': item.get_absolute_url(),
}
if item.has_children:
nodes = []
for child in item.children.all().order_by('name'):
nodes.append(self.itemToJson(child))
data['nodes'] = nodes
return data
def get(self, request, *args, **kwargs):
top_items = self.model.objects.filter(parent=None).order_by('name')
nodes = []
for item in top_items:
nodes.append(self.itemToJson(item))
top = {
'text': self.title,
'nodes': nodes,
}
response = {
'tree': [top]
}
return JsonResponse(response, safe=False)
class AjaxView(object): class AjaxView(object):

View File

@ -7,9 +7,17 @@ from rest_framework import generics, permissions
from django.conf.urls import url from django.conf.urls import url
from .models import Part from .models import Part, PartCategory
from .serializers import PartSerializer from .serializers import PartSerializer
from InvenTree.views import TreeSerializer
class PartCategoryTree(TreeSerializer):
title = "Parts"
model = PartCategory
class PartList(generics.ListCreateAPIView): class PartList(generics.ListCreateAPIView):
@ -44,5 +52,7 @@ class PartList(generics.ListCreateAPIView):
part_api_urls = [ part_api_urls = [
url(r'^tree/?', PartCategoryTree.as_view(), name='api-part-tree'),
url(r'^.*$', PartList.as_view(), name='api-part-list'), url(r'^.*$', PartList.as_view(), name='api-part-list'),
] ]

View File

@ -31,7 +31,6 @@
<script type='text/javascript' src="{% static 'script/footable.js' %}"></script> <script type='text/javascript' src="{% static 'script/footable.js' %}"></script>
<script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script> <script type='text/javascript' src="{% static 'script/modal_form.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap-treeview.js' %}"></script>
{% endblock %} {% endblock %}
{% block js_ready %} {% block js_ready %}
$('#part-list').footable(); $('#part-list').footable();
@ -44,7 +43,36 @@
}); });
}); });
function loadTree() {
var requestData = {};
{% if category %}
requestData.category = {{ category.id }};
{% endif %}
$.ajax({
url: "{% url 'api-part-tree' %}",
type: 'get',
dataType: 'json',
data: requestData,
success: function (response) {
if (response.tree) {
$("#part-tree").treeview({
data: response.tree,
enableLinks: true
});
}
},
error: function (xhr, ajaxOptions, thrownError) {
alert('Error retrieving part tree:\n' + thrownError);
}
});
}
$("#create-part").click(function() { $("#create-part").click(function() {
launchModalForm("#modal-form", "{% url 'part-create' %}"); launchModalForm("#modal-form", "{% url 'part-create' %}");
}); });
loadTree();
{% endblock %} {% endblock %}

View File

@ -0,0 +1,37 @@
/* =========================================================
* bootstrap-treeview.css 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.
* ========================================================= */
.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;
}

View File

@ -41,8 +41,17 @@
} }
.inventree-content { .inventree-content {
padding-left: 15px; padding-left: 5px;
padding-right: 15px; padding-right: 5px;
padding-top: 15px;
margin-right: 50px;
margin-left: 50px;
width: 100%;
transition: 0.5s;
}
.body {
padding-top: 70px;
} }
.modal { .modal {
@ -61,4 +70,19 @@
max-height: calc(100vh - 200px) !important; max-height: calc(100vh - 200px) !important;
overflow-y: scroll; overflow-y: scroll;
padding: 10px; padding: 10px;
}
/* The side navigation menu */
.sidenav {
height: 100%; /* 100% Full-height */
width: 0px; /* 0 width - change this with JavaScript */
position: fixed; /* Stay in place */
background-color: #fff; /* Black*/
overflow-x: hidden; /* Disable horizontal scroll */
transition: 0.5s; /* 0.5 second transition effect to slide in the sidenav */
}
.wrapper {
align-items: stretch;
display: flex;
} }

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,9 @@
function openSideNav() {
document.getElementById("sidenav").style.width = "250px";
document.getElementById("inventree-content").style.marginLeft = "270px";
}
function closeSideNav() {
document.getElementById("sidenav").style.width = "0";
document.getElementById("inventree-content").style.marginLeft = "0";
}

View File

@ -0,0 +1,20 @@
function loadTree(url, tree, data) {
$.ajax({
url: url,
type: 'get',
dataType: 'json',
data: data,
success: function (response) {
if (response.tree) {
$(tree).treeview({
data: response.tree,
enableLinks: true
});
}
},
error: function (xhr, ajaxOptions, thrownError) {
//TODO
}
});
}

View File

@ -3,11 +3,20 @@ from django_filters import NumberFilter
from rest_framework import generics, permissions, response from rest_framework import generics, permissions, response
from django.conf.urls import url
# from InvenTree.models import FilterChildren # from InvenTree.models import FilterChildren
from .models import StockLocation, StockItem from .models import StockLocation, StockItem
from .serializers import StockItemSerializer, StockQuantitySerializer from .serializers import StockItemSerializer, StockQuantitySerializer
from .serializers import LocationSerializer from .serializers import LocationSerializer
from InvenTree.views import TreeSerializer
class StockCategoryTree(TreeSerializer):
title = 'Stock'
model = StockLocation
class StockDetail(generics.RetrieveUpdateDestroyAPIView): class StockDetail(generics.RetrieveUpdateDestroyAPIView):
""" """
@ -127,3 +136,8 @@ class LocationList(generics.ListCreateAPIView):
permission_classes = (permissions.IsAuthenticatedOrReadOnly,) permission_classes = (permissions.IsAuthenticatedOrReadOnly,)
filter_backends = (DjangoFilterBackend,) filter_backends = (DjangoFilterBackend,)
filter_class = StockLocationFilter filter_class = StockLocationFilter
stock_api_urls = [
url(r'^tree/?', StockCategoryTree.as_view(), name='api-stock-tree'),
]

View File

@ -15,9 +15,7 @@
{% block css %} {% block css %}
{% endblock %} {% endblock %}
{% block head %} {% block head %}
{% endblock %} {% endblock %}
<title> <title>
@ -31,22 +29,42 @@ InvenTree
{% include "navbar.html" %} {% include "navbar.html" %}
<div class="container container-fluid inventree-content"> <div class='main body wrapper'>
{% include "sidebar.html" %}
<div class="container container-fluid inventree-content" id='inventree-content'>
{% block content %} {% block content %}
<!-- Each view fills in here.. --> <!-- Each view fills in here.. -->
{% endblock %} {% endblock %}
</div> </div>
</div>
<!-- Scripts --> <!-- Scripts -->
<script type="text/javascript" src="{% static 'script/jquery_3.3.1_jquery.min.js' %}"></script> <script type="text/javascript" src="{% static 'script/jquery_3.3.1_jquery.min.js' %}"></script>
<script type="text/javascript" src="{% static 'script/bootstrap.min.js' %}"></script> <script type="text/javascript" src="{% static 'script/bootstrap.min.js' %}"></script>
<script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script> <script type="text/javascript" src="{% static 'script/select2/select2.js' %}"></script>
<script type='text/javascript' src="{% static 'script/bootstrap-treeview.js' %}"></script>
<script type='text/javascript' src="{% static 'script/trees.js' %}"></script>
<script type='text/javascript' src="{% static 'script/sidenav.js' %}"></script>
{% block js_load %} {% block js_load %}
{% endblock %} {% endblock %}
<script type='text/javascript'> <script type='text/javascript'>
$(document).ready(function () { $(document).ready(function () {
loadTree("{% url 'api-part-tree' %}",
"#part-tree");
loadTree("{% url 'api-stock-tree' %}",
"#stock-tree");
$('#logo').click(function() {
openSideNav();
});
{% block js_ready %} {% block js_ready %}
{% endblock %} {% endblock %}

View File

@ -1,9 +1,9 @@
{% load static %} {% load static %}
<nav class="navbar navbar-default"> <nav class="navbar navbar-default navbar-fixed-top">
<div class="container-fluid"> <div class="container-fluid">
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-brand" href="/"><img src="{% static 'img/inventree.png' %}" width="40" height="40"/></a> <a class="navbar-brand" id='logo'><img src="{% static 'img/inventree.png' %}" width="40" height="40"/></a>
</div> </div>
<ul class="nav navbar-nav"> <ul class="nav navbar-nav">
<li><a href="{% url 'part-index' %}">Parts</a></li> <li><a href="{% url 'part-index' %}">Parts</a></li>

View File

@ -0,0 +1,9 @@
<div class='sidenav' id='sidenav'>
<div id='part-tree'></div>
<hr>
<div id='stock-tree'></div>
</div>