Browse Source

TreeNode editor a zobrazovac ve Vue

export_seznamu_prednasek
parent
commit
403434e10b
  1. 7
      mamweb/routers.py
  2. 24
      mamweb/settings_common.py
  3. 5
      mamweb/urls.py
  4. 3
      requirements.txt
  5. 7
      seminar/templates/seminar/vuetest.html
  6. 3
      seminar/templatetags/treenodes.py
  7. 4
      seminar/urls.py
  8. 1
      seminar/views/__init__.py
  9. 72
      seminar/views/views_all.py
  10. 89
      seminar/views/views_rest.py
  11. 7
      seminar/viewsets.py
  12. 22
      vue_frontend/.gitignore
  13. 5
      vue_frontend/babel.config.js
  14. 46
      vue_frontend/package.json
  15. 53
      vue_frontend/src/App.vue
  16. 45
      vue_frontend/src/components/CastNode.vue
  17. 33
      vue_frontend/src/components/CisloNode.vue
  18. 33
      vue_frontend/src/components/RocnikNode.vue
  19. 19
      vue_frontend/src/components/TemaVCisleNode.vue
  20. 88
      vue_frontend/src/components/TextNode.vue
  21. 84
      vue_frontend/src/components/TreeNode.vue
  22. 15
      vue_frontend/src/components/UlohaVzorakNode.vue
  23. 15
      vue_frontend/src/components/UlohaZadaniNode.vue
  24. 10
      vue_frontend/src/main.js
  25. 60
      vue_frontend/vue.config.js
  26. 8305
      vue_frontend/yarn.lock

7
mamweb/routers.py

@ -0,0 +1,7 @@
from rest_framework import routers
from seminar import viewsets as vs
router = routers.DefaultRouter()
router.register(r'ulohavzoraknode', vs.UlohaVzorakNodeViewSet)

24
mamweb/settings_common.py

@ -121,6 +121,9 @@ INSTALLED_APPS = (
'polymorphic', 'polymorphic',
'webpack_loader',
'rest_framework',
# MaMweb # MaMweb
'mamweb', 'mamweb',
'seminar', 'seminar',
@ -183,6 +186,27 @@ CKEDITOR_CONFIGS = {
}, },
} }
# Webpack loader
VUE_FRONTEND_DIR = os.path.join(BASE_DIR, 'vue_frontend')
WEBPACK_LOADER = {
'DEFAULT': {
'CACHE': False,
'BUNDLE_DIR_NAME': 'vue/', # must end with slash
'STATS_FILE': os.path.join(VUE_FRONTEND_DIR, 'webpack-stats.json'),
'POLL_INTERVAL': 0.1,
'TIMEOUT': None,
'IGNORE': [r'.+\.hot-update.js', r'.+\.map']
}
}
# Dajngo REST Framework
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.LimitOffsetPagination',
'PAGE_SIZE': 100
}
# Comments # Comments

5
mamweb/urls.py

@ -6,6 +6,8 @@ from django.views.generic.base import TemplateView
from django import views from django import views
from django.urls import path # As per docs. from django.urls import path # As per docs.
from .routers import router
urlpatterns = [ urlpatterns = [
# Admin a nastroje # Admin a nastroje
@ -25,6 +27,9 @@ urlpatterns = [
path('comments_dj/', include('django_comments.urls')), path('comments_dj/', include('django_comments.urls')),
path('comments_fl/', include('fluent_comments.urls')), path('comments_fl/', include('fluent_comments.urls')),
# REST API
path('api/', include(router.urls)),
] ]
# This is only needed when using runserver. # This is only needed when using runserver.

3
requirements.txt

@ -28,6 +28,9 @@ django-imagekit
django-polymorphic django-polymorphic
django-sitetree django-sitetree
django_reverse_admin django_reverse_admin
django-rest-framework
django-webpack-loader
django-rest-polymorphic
# Comments # Comments
akismet==1.0.1 akismet==1.0.1

7
seminar/templates/seminar/vuetest.html

@ -0,0 +1,7 @@
{% load render_bundle from webpack_loader %}
<div id="app">
<app></app>
</div>
{% render_bundle 'chunk-vendors' %}
{% render_bundle 'vue_app_01' %}

3
seminar/templatetags/treenodes.py

@ -17,6 +17,9 @@ def nodeType(value):
if isinstance(value,UlohaZadaniNode): return "Zadání úlohy" if isinstance(value,UlohaZadaniNode): return "Zadání úlohy"
if isinstance(value,PohadkaNode): return "Pohádka" if isinstance(value,PohadkaNode): return "Pohádka"
### NASLEDUJICI FUNKCE SE POUZIVAJI VE views_all.py V SEKCI PRIPRAVJICI TNLData
### NEMAZAT, PRESUNOUT S TNLDaty NEKAM BOKEM
@register.filter @register.filter
def isRocnik(value): def isRocnik(value):
return isinstance(value, m.RocnikNode) return isinstance(value, m.RocnikNode)

4
seminar/urls.py

@ -26,6 +26,8 @@ urlpatterns = [
path('cislo/<int:rocnik>.<int:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), # odkomentované jenom kvůli testování archivu path('cislo/<int:rocnik>.<int:cislo>/', views.CisloView.as_view(), name='seminar_cislo'), # odkomentované jenom kvůli testování archivu
path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'), path('problem/<int:pk>/', views.ProblemView.as_view(), name='seminar_problem'),
path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'), path('treenode/<int:pk>/', views.TreeNodeView.as_view(), name='seminar_treenode'),
path('treenode/<int:pk>/json/', views.TreeNodeJSONView.as_view(), name='seminar_treenode_json'),
path('treenode/text/<int:pk>/', views.TextWebView.as_view(), name='seminar_textnode_web'),
path('treenode/editor/pridat/<str:co>/<int:pk>/<str:kam>/', views.TreeNodePridatView.as_view(), name='treenode_pridat'), path('treenode/editor/pridat/<str:co>/<int:pk>/<str:kam>/', views.TreeNodePridatView.as_view(), name='treenode_pridat'),
path('treenode/editor/smazat/<int:pk>/', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'), path('treenode/editor/smazat/<int:pk>/', views.TreeNodeSmazatView.as_view(), name='treenode_smazat'),
path('treenode/editor/odvesitpryc/<int:pk>/', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'), path('treenode/editor/odvesitpryc/<int:pk>/', views.TreeNodeOdvesitPrycView.as_view(), name='treenode_odvesitpryc'),
@ -118,6 +120,8 @@ urlpatterns = [
path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'), path('temp/add_solution', views.AddSolutionView.as_view(),name='seminar_vloz_reseni'),
path('temp/nahraj_reseni', views.NahrajReseniView.as_view(),name='seminar_nahraj_reseni'), path('temp/nahraj_reseni', views.NahrajReseniView.as_view(),name='seminar_nahraj_reseni'),
path('temp/vue',views.VueTestView.as_view(),name='vue_test_view'),
path('', views.TitulniStranaView.as_view(), name='titulni_strana'), path('', views.TitulniStranaView.as_view(), name='titulni_strana'),
# Ceka na autocomplete v3 # Ceka na autocomplete v3

1
seminar/views/__init__.py

@ -1,2 +1,3 @@
from .views_all import * from .views_all import *
from .autocomplete import * from .autocomplete import *
from .views_rest import *

72
seminar/views/views_all.py

@ -15,6 +15,8 @@ from django.contrib.auth import views as auth_views
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.db import transaction from django.db import transaction
from django.core import serializers
from django.forms.models import model_to_dict
import seminar.models as s import seminar.models as s
import seminar.models as m import seminar.models as m
@ -23,6 +25,8 @@ from seminar.models import Problem, Cislo, Reseni, Nastaveni, Rocnik, Soustreden
from seminar import utils, treelib from seminar import utils, treelib
from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm from seminar.forms import PrihlaskaForm, LoginForm, ProfileEditForm
import seminar.forms as f import seminar.forms as f
import seminar.templatetags.treenodes as tnltt
import seminar.views.views_rest as vr
from datetime import timedelta, date, datetime from datetime import timedelta, date, datetime
from django.utils import timezone from django.utils import timezone
@ -88,21 +92,39 @@ class ObalkovaniView(generic.ListView):
class TNLData(object): class TNLData(object):
def __init__(self,anode,parent=None, index=None): def __init__(self,anode,parent=None, index=None):
self.node = anode self.node = anode
self.sernode = vr.TreeNodeSerializer(anode)
self.children = [] self.children = []
self.parent = parent self.parent = parent
self.tema_in_path = False self.tema_in_path = False
self.index = index self.index = index
if parent: if parent:
self.tema_in_path = parent.tema_in_path self.tema_in_path = parent.tema_in_path
if isinstance(anode, m.TemaVCisleNode): if isinstance(anode, m.TemaVCisleNode):
self.tema_in_path = True self.tema_in_path = True
def add_edit_options(self):
self.deletable = tnltt.deletable(self)
self.editable_siblings = tnltt.editableSiblings(self)
self.editable_children = tnltt.editableChildren(self)
self.text_only_subtree = tnltt.textOnlySubtree(self)
self.can_podvesit_za = tnltt.canPodvesitZa(self)
self.can_podvesit_pred = tnltt.canPodvesitPred(self)
self.appendable_children = tnltt.appendableChildren(self)
if self.parent:
self.appendable_siblings = tnltt.appendableChildren(self.parent)
else:
self.appendable_siblings = []
@classmethod @classmethod
def from_treenode(cls,anode,parent=None,index=None): def from_treenode(cls,anode,parent=None,index=None):
out = cls(anode,parent,index) out = cls(anode,parent,index)
for (idx,ch) in enumerate(treelib.all_children(anode)): for (idx,ch) in enumerate(treelib.all_children(anode)):
outitem = cls.from_treenode(ch,out,idx) outitem = cls.from_treenode(ch,out,idx)
out.children.append(outitem) out.children.append(outitem)
out.add_edit_options()
return out return out
@classmethod @classmethod
@ -113,6 +135,7 @@ class TNLData(object):
result.children.append(tnl) result.children.append(tnl)
tnl.parent = result tnl.parent = result
tnl.index = idx tnl.index = idx
result.add_edit_options()
return result return result
@classmethod @classmethod
@ -132,6 +155,31 @@ class TNLData(object):
for tnl in result: for tnl in result:
found.append(tnl) found.append(tnl)
return found return found
def to_json(self):
#self.node = anode
#self.children = []
#self.parent = parent
#self.tema_in_path = False
#self.index = index
out = {}
out['node'] = self.sernode.data
out['children'] = [n.to_json() for n in self.children]
out['tema_in_path'] = self.tema_in_path
out['index'] = self.index
out['deletable'] = self.deletable
out['editable_siblings'] = self.editable_siblings
out['editable_children'] = self.editable_children
out['text_only_subtree'] = self.text_only_subtree
out['can_podvesit_za'] = self.can_podvesit_za
out['can_podvesit_pod'] = self.can_podvesit_pred
out['appendable_children'] = self.appendable_children
out['appendable_siblings'] = self.appendable_siblings
return out
def __repr__(self): def __repr__(self):
return("TNL({})".format(self.node)) return("TNL({})".format(self.node))
@ -144,6 +192,16 @@ class TreeNodeView(generic.DetailView):
context['tnldata'] = TNLData.from_treenode(self.object) context['tnldata'] = TNLData.from_treenode(self.object)
return context return context
class TreeNodeJSONView(generic.DetailView):
model = s.TreeNode
def get(self,request,*args, **kwargs):
self.object = self.get_object()
data = TNLData.from_treenode(self.object).to_json()
return JsonResponse(data)
class TreeNodePridatView(generic.View): class TreeNodePridatView(generic.View):
type_from_str = { type_from_str = {
'rocnikNode': m.RocnikNode, 'rocnikNode': m.RocnikNode,
@ -178,7 +236,7 @@ class TreeNodePridatView(generic.View):
new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam)) new_obj.nadpis = request.POST.get('pridat-castNode-{}-{}'.format(node.id,kam))
new_obj.save() new_obj.save()
elif co == m.ReseniNode: elif co == m.ReseniNode:
new_obj = m. new_obj = m
pass pass
elif co == m.UlohaZadaniNode: elif co == m.UlohaZadaniNode:
pass pass
@ -252,6 +310,15 @@ class SirotcinecView(generic.ListView):
def get_queryset(self): def get_queryset(self):
return s.TreeNode.objects.not_instance_of(s.RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None) return s.TreeNode.objects.not_instance_of(s.RocnikNode).filter(root=None,prev=None,succ=None,father_of_first=None)
# FIXME pouzit Django REST Framework
class TextWebView(generic.DetailView):
model = s.Text
def get(self,request,*args, **kwargs):
self.object = self.get_object()
return JsonResponse(model_to_dict(self.object,exclude='do_cisla'))
class ProblemView(generic.DetailView): class ProblemView(generic.DetailView):
model = s.Problem model = s.Problem
# Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView # Zkopírujeme template_name od TreeNodeView, protože jsme prakticky jen trošku upravený TreeNodeView
@ -1422,3 +1489,6 @@ class PasswordResetCompleteView(auth_views.PasswordResetCompleteView):
class PasswordChangeView(auth_views.PasswordChangeView): class PasswordChangeView(auth_views.PasswordChangeView):
#template_name = 'seminar/password_change.html' #template_name = 'seminar/password_change.html'
success_url = reverse_lazy('titulni_strana') success_url = reverse_lazy('titulni_strana')
class VueTestView(generic.TemplateView):
template_name = 'seminar/vuetest.html'

89
seminar/views/views_rest.py

@ -0,0 +1,89 @@
from rest_framework import serializers
from rest_polymorphic.serializers import PolymorphicSerializer
import seminar.models as m
DEFAULT_NODE_DEPTH = 2
class UlohaVzorakNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.UlohaVzorakNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class UlohaZadaniNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.UlohaZadaniNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class RocnikNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.RocnikNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class CisloNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.CisloNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class MezicisloNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.MezicisloNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class TemaVCisleNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.TemaVCisleNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class OrgTextNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.OrgTextNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class PohadkaNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.PohadkaNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class TextNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.TextNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class CastNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.CastNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class ReseniNodeSerializer(serializers.ModelSerializer):
class Meta:
model = m.ReseniNode
fields = '__all__'
depth = DEFAULT_NODE_DEPTH
class TreeNodeSerializer(PolymorphicSerializer):
model_serializer_mapping = {
m.RocnikNode: RocnikNodeSerializer,
m.CisloNode: CisloNodeSerializer,
m.MezicisloNode: MezicisloNodeSerializer,
m.TemaVCisleNode: TemaVCisleNodeSerializer,
m.OrgTextNode: OrgTextNodeSerializer,
m.UlohaZadaniNode: UlohaZadaniNodeSerializer,
m.UlohaVzorakNode: UlohaVzorakNodeSerializer,
m.PohadkaNode: PohadkaNodeSerializer,
m.TextNode: TextNodeSerializer,
m.CastNode: CastNodeSerializer,
m.ReseniNode: ReseniNodeSerializer,
}

7
seminar/viewsets.py

@ -0,0 +1,7 @@
from rest_framework import viewsets,filters
from . import models as m
from . import views
class UlohaVzorakNodeViewSet(viewsets.ModelViewSet):
queryset = m.UlohaVzorakNode.objects.all()
serializer_class = views.UlohaVzorakNodeSerializer

22
vue_frontend/.gitignore

@ -0,0 +1,22 @@
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

5
vue_frontend/babel.config.js

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

46
vue_frontend/package.json

@ -0,0 +1,46 @@
{
"name": "vue_frontend",
"version": "0.1.0",
"private": true,
"scripts": {
"serve": "vue-cli-service serve",
"build": "vue-cli-service build",
"lint": "vue-cli-service lint"
},
"dependencies": {
"@ckeditor/ckeditor5-build-classic": "^22.0.0",
"@ckeditor/ckeditor5-vue": "^1.0.1",
"axios": "^0.19.2",
"core-js": "^3.6.5",
"vue": "^2.6.11"
},
"devDependencies": {
"@vue/cli-plugin-babel": "~4.4.0",
"@vue/cli-plugin-eslint": "~4.4.0",
"@vue/cli-service": "~4.4.0",
"babel-eslint": "^10.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"vue-template-compiler": "^2.6.11",
"webpack-bundle-tracker": "0.4.3"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/essential",
"eslint:recommended"
],
"parserOptions": {
"parser": "babel-eslint"
},
"rules": {}
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
]
}

53
vue_frontend/src/App.vue

@ -0,0 +1,53 @@
<template>
<div v-if="loading">
Loading...
</div>
<div v-else id="app">
<!--pre>
{{item}}
</pre-->
<TreeNode :item="item"/>
</div>
</template>
<script>
import TreeNode from './components/TreeNode.vue'
import axios from 'axios'
export default {
name: 'App',
components: {
TreeNode,
},
data: () => ({
loading: true,
item: null
}),
mounted: function() {
this.getArticles();
},
methods: {
getArticles: function() {
this.loading = true;
axios.get('/treenode/1/json/')
.then((response) => {
this.item = response.data;
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
}
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
margin-top: 60px;
}
</style>

45
vue_frontend/src/components/CastNode.vue

@ -0,0 +1,45 @@
<template>
<div class="castnode">
<!--pre>CastNode {{item}} {{typeof(item)}}</pre-->
<div v-if="editorShow">
<input type="text" v-model="currentText" />
<button v-on:click="updateText">Uložit</button>
<button v-on:click="currentText = originalText;editorShow=!editorShow;">Zahodit úpravy</button>
</div>
<div v-else>
<h4>{{ currentText }} <button v-on:click="editorShow=!editorShow">Upravit</button> </h4>
</div>
</div>
</template>
<script>
export default {
name: 'CastNode',
data: () => ({
editorShow: false,
currentText: "",
originalText: "",
}),
props: {
item: Object
},
mounted: function() {
console.log("mounted");
this.currentText = this.item.node.nadpis;
this.originalText = this.item.node.nadpis;
//this.getText();
},
methods: {
updateText: function() {
console.log("Saving text");
console.log(this.currentText);
// FIXME really save!
this.editorShow = false;
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

33
vue_frontend/src/components/CisloNode.vue

@ -0,0 +1,33 @@
<template>
<div class="cislonode">
<!--pre>CisloNode {{item}} {{typeof(item)}}</pre-->
<h2>Číslo {{ item.node.cislo.poradi }}</h2>
</div>
</template>
<script>
export default {
name: 'CisloNode',
props: {
item: Object
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
/* list-style-type: none;*/
padding: 0;
}
li {
/*display: inline-block;*/
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

33
vue_frontend/src/components/RocnikNode.vue

@ -0,0 +1,33 @@
<template>
<div class="rocniknode">
<!--pre>RocnikNode {{item}} {{typeof(item)}}</pre-->
<h1>Ročník {{ item.node.rocnik.rocnik }} ({{ item.node.rocnik.prvni_rok }}/{{item.node.rocnik.prvni_rok+1 }})</h1>
</div>
</template>
<script>
export default {
name: 'RocnikNode',
props: {
item: Object
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
/* list-style-type: none;*/
padding: 0;
}
li {
/*display: inline-block;*/
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

19
vue_frontend/src/components/TemaVCisleNode.vue

@ -0,0 +1,19 @@
<template>
<div class="temavcislenode">
<!--pre>TemaVCisleNode {{item}} {{typeof(item)}}</pre-->
<h4>Téma {{ item.node.tema.kod }}: {{ item.node.tema.nazev }}</h4>
</div>
</template>
<script>
export default {
name: 'TemaVCisleNode',
props: {
item: Object
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
</style>

88
vue_frontend/src/components/TextNode.vue

@ -0,0 +1,88 @@
<template>
<div v-if="loading" class="loading">
<p>Loading...</p>
</div>
<div v-else class="textnode">
<!--pre>TextNode {{item}} {{typeof(item)}}</pre-->
<div v-if="editorShow">
<component v-bind:is="editorComponent" :editor="editor" v-model="currentText" :config="editorConfig"></component>
<button v-on:click="updateText">Uložit</button>
<button v-on:click="currentText = originalText;editorShow=!editorShow;">Zahodit úpravy</button>
</div>
<div v-else v-bind:class="changedObject">
<button v-on:click="editorShow=!editorShow">Upravit</button>
<p v-html="currentText"></p>
</div>
</div>
</template>
<script>
import axios from 'axios'
import ClassicEditor from '@ckeditor/ckeditor5-build-classic'
import CKEditor from '@ckeditor/ckeditor5-vue';
export default {
name: 'TextNode',
data: () => ({
loading: false,
editor: ClassicEditor,
editorData: '<p>Content of the editor.</p>',
editorConfig: {
// The configuration of the editor.
},
editorShow: false,
editorComponent: CKEditor.component,
currentText: "",// this.item.node.text.na_web,
originalText: "",// this.item.node.text.na_web,
}),
computed: {
changedObject: function () {
console.log(this.currentText);
//console.log(this.originalText);
return {
changed: this.currentText !== this.originalText,
}
}
},
props: {
item: Object
},
mounted: function() {
console.log("mounted");
this.currentText = this.item.node.text.na_web;
this.originalText = this.item.node.text.na_web;
//this.getText();
},
methods: {
getText: function() {
this.loading = true;
console.log(this.item);
console.log(this.item.node.text);
axios.get('/treenode/text/'+this.item.node.text)
.then((response) => {
this.text = response.data.na_web;
this.loading = false;
})
.catch((err) => {
this.loading = false;
console.log(err);
})
},
updateText: function() {
console.log("Saving text");
console.log(this.currentText);
// FIXME really save!
this.editorShow = false;
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.changed {
background-color: yellow;
}
</style>

84
vue_frontend/src/components/TreeNode.vue

@ -0,0 +1,84 @@
<template>
<div class="treenode">
<button v-on:click="debugShow = !debugShow">Ladící data</button>
<div v-if="debugShow">
<pre>{{ item.node.polymorphic_ctype.model }}</pre>
<pre>{{ item }}</pre>
</div>
<component :is='item.node.polymorphic_ctype.model' :item='item'></component>
<div v-if="item.children.length === 0">
<div v-if="item.appendable_children.length > 0">
<h1>Vložit jako syna</h1>
<ul>
<li v-for="chld in item.appendable_children" :key="chld[0]">
<a href="">{{chld[1]}}</a>
</li>
</ul>
</div>
</div>
<div v-else>
<h1>Vložit před</h1>
<ul>
<li v-for="chld in item.appendable_children" :key="chld[0]">
<a href="">{{chld[1]}}</a>
</li>
</ul>
<ul>
<li v-for="chld in item.children" v-bind:key="chld.nazev" >
<TreeNode :item="chld">
</TreeNode>
</li>
</ul>
</div>
</div>
</template>
<script>
import rocniknode from './RocnikNode.vue'
import cislonode from './CisloNode.vue'
import temavcislenode from './TemaVCisleNode.vue'
import castnode from './CastNode.vue'
import textnode from './TextNode.vue'
import ulohazadaninode from './UlohaZadaniNode.vue'
import ulohavzoraknode from './UlohaVzorakNode.vue'
export default {
name: 'TreeNode',
components: {
rocniknode,
cislonode,
temavcislenode,
castnode,
textnode,
ulohazadaninode,
ulohavzoraknode,
},
data: () => ({
debugShow: false,
}),
props: {
item: Object
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
/* list-style-type: none;*/
padding: 0;
}
li {
/*display: inline-block;*/
margin: 0 10px;
}
a {
color: #42b983;
}
</style>

15
vue_frontend/src/components/UlohaVzorakNode.vue

@ -0,0 +1,15 @@
<template>
<div class="ulohavzoraknode">
<!--pre>UlohaVzorakNode {{item}} {{typeof(item)}}</pre-->
<h5>Řešení {{item.node.uloha.cislo_zadani.poradi}}.{{ item.node.uloha.kod }}: {{ item.node.uloha.nazev }}</h5>
</div>
</template>
<script>
export default {
name: 'UlohaVzorakNode',
props: {
item: Object
}
}
</script>

15
vue_frontend/src/components/UlohaZadaniNode.vue

@ -0,0 +1,15 @@
<template>
<div class="ulohazadaninode">
<!--pre>UlohaZadaniNode {{item.node.uloha}} {{typeof(item)}}</pre-->
<h5>Zadání {{item.node.uloha.cislo_zadani.poradi}}.{{ item.node.uloha.kod }}: {{ item.node.uloha.nazev }}</h5>
</div>
</template>
<script>
export default {
name: 'UlohaZadaniNode',
props: {
item: Object
}
}
</script>

10
vue_frontend/src/main.js

@ -0,0 +1,10 @@
import Vue from 'vue'
import App from './App.vue'
import CKEditor from '@ckeditor/ckeditor5-vue'
Vue.config.productionTip = false
Vue.use(CKEditor);
new Vue({
render: h => h(App),
}).$mount('#app')

60
vue_frontend/vue.config.js

@ -0,0 +1,60 @@
const BundleTracker = require("webpack-bundle-tracker");
const pages = {
'vue_app_01': {
entry: './src/main.js',
chunks: ['chunk-vendors']
},
'vue_app_02': {
entry: './src/newhampshir.js',
chunks: ['chunk-vendors']
},
}
module.exports = {
pages: pages,
filenameHashing: false,
productionSourceMap: false,
publicPath: process.env.NODE_ENV === 'production'
? '/static/seminar/vue/'
: 'http://localhost:8080/',
outputDir: '../seminar/static/seminar/vue/',
chainWebpack: config => {
config.optimization
.splitChunks({
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: "chunk-vendors",
chunks: "all",
priority: 1
},
},
});
Object.keys(pages).forEach(page => {
config.plugins.delete(`html-${page}`);
config.plugins.delete(`preload-${page}`);
config.plugins.delete(`prefetch-${page}`);
})
config
.plugin('BundleTracker')
.use(BundleTracker, [{filename: '../vue_frontend/webpack-stats.json'}]);
config.resolve.alias
.set('__STATIC__', 'static')
config.devServer
.public('http://localhost:8080')
.host('localhost')
.port(8080)
.hotOnly(true)
.watchOptions({poll: 1000})
.https(false)
.headers({"Access-Control-Allow-Origin": ["*"]})
}
};

8305
vue_frontend/yarn.lock

File diff suppressed because it is too large
Loading…
Cancel
Save