TreeNode editor a zobrazovac ve Vue

This commit is contained in:
Tomas "Jethro" Pokorny 2020-09-03 23:02:28 +02:00
parent c6b21a231e
commit 403434e10b
26 changed files with 9054 additions and 1 deletions

7
mamweb/routers.py Normal file
View file

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

View file

@ -120,6 +120,9 @@ INSTALLED_APPS = (
'imagekit', 'imagekit',
'polymorphic', 'polymorphic',
'webpack_loader',
'rest_framework',
# MaMweb # MaMweb
'mamweb', 'mamweb',
@ -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

View file

@ -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.

View file

@ -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

View file

@ -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' %}

View file

@ -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)

View file

@ -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

View file

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

View file

@ -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'

View file

@ -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 Normal file
View file

@ -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 vendored Normal file
View file

@ -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?

View file

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

46
vue_frontend/package.json Normal file
View file

@ -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 Normal file
View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -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 Normal file
View file

@ -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')

View file

@ -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 Normal file

File diff suppressed because it is too large Load diff