331 lines
10 KiB
Vue
331 lines
10 KiB
Vue
<template>
|
|
<div id="vertretungsplan">
|
|
<v-data-table
|
|
:headers="headers"
|
|
:fixed-header="true"
|
|
class="overflow-y-auto"
|
|
height="85vh"
|
|
:items="vp"
|
|
:items-per-page="10000"
|
|
item-key="nr"
|
|
sort-by="stunde"
|
|
group-by="lehrkraft_vertretung"
|
|
flat
|
|
rounded
|
|
hide-default-footer
|
|
:expanded.sync="expanded"
|
|
:single-expand="true"
|
|
>
|
|
<!-- Titelzeile -->
|
|
<template v-slot:top>
|
|
<v-toolbar
|
|
flat
|
|
>
|
|
<v-toolbar-title class="pa-2 align-self-start">Vertretungsplan für den </v-toolbar-title>
|
|
|
|
<v-menu
|
|
v-model="datepicker"
|
|
:close-on-content-click="false"
|
|
transition="scale-transition"
|
|
offset-y
|
|
min-width="auto"
|
|
>
|
|
<template v-slot:activator="{ on, attrs }">
|
|
<v-btn
|
|
class="pa-2 align-self-baseline"
|
|
v-bind="attrs"
|
|
v-on="on"
|
|
rounded
|
|
:class="(today) ? '' : 'red'"
|
|
><v-icon :color="(today) ? '' : 'white'">mdi-calendar</v-icon><span :class="(today) ? '' : 'white--text'">{{ vpDatum.getDate() }}.{{ vpDatum.getMonth() + 1 }}.{{ vpDatum.getFullYear() }}</span></v-btn>
|
|
</template>
|
|
<v-date-picker
|
|
v-model="neuesDatum"
|
|
@input="datepicker = false"
|
|
first-day-of-week="1"
|
|
:allowedDates= "allowedDates"
|
|
:show-week="true"
|
|
locale="de-DE"
|
|
></v-date-picker>
|
|
</v-menu>
|
|
<v-checkbox
|
|
v-if="today"
|
|
v-model="vergangeneAusblenden"
|
|
label="vergangene Vertretungen ausblenden"
|
|
class="align-self-baseline pa-2"
|
|
></v-checkbox>
|
|
<v-spacer></v-spacer>
|
|
</v-toolbar>
|
|
</template>
|
|
|
|
<!-- Kopfzeile für Gruppen -->
|
|
<template v-slot:group.header="{ group, headers, isOpen, toggle }">
|
|
<td :colspan="headers.length">
|
|
<v-btn @click="toggle" v-if="isOpen"><v-icon color="grey">mdi-account</v-icon>{{ group }}</v-btn>
|
|
<v-btn @click="toggle" v-if="!isOpen"><v-icon color="grey">mdi-account-outline</v-icon>{{ group }}</v-btn>
|
|
</td>
|
|
</template>
|
|
|
|
<!-- Darstellung des Datumsfelds -->
|
|
<template v-slot:item.datum></template>
|
|
<template v-slot:header.datum></template>
|
|
|
|
<!-- Darstellung der Stunde (inkl. Badge für neue Einträge) -->
|
|
<template v-slot:item.stunde="{ item }">
|
|
<v-badge
|
|
color="yellow"
|
|
dot
|
|
:value="item.letzte_aenderung > vpDatumsgrenze"
|
|
>
|
|
{{ item.stunde }}
|
|
</v-badge>
|
|
</template>
|
|
|
|
<!-- Darstellung der Klasse -->
|
|
<template v-slot:item.klassen="{ item }">
|
|
<Klasse v-for="(k, i) in klassen(item.klassen)" :key="i" :id="k" />
|
|
</template>
|
|
|
|
<!-- Darstellung der Vertretungsart -->
|
|
<template v-slot:item.art_vertretung="{ item }">
|
|
{{ vertretungsart(item.art_vertretung) }}
|
|
</template>
|
|
|
|
<!-- Darstellung des Raums (Orts für Pausenaufsichten) -->
|
|
<template v-slot:item.raum="{ item }">
|
|
{{ (item.art_vertretung !== 'B') ? item.raum : paOrt(vpDatum.getDay(), item.stunde, item.lehrkraft) }}
|
|
</template>
|
|
|
|
<template v-slot:item.raum_vertretung="{ item }">
|
|
{{ (item.art_vertretung !== 'B') ? item.raum_vertretung : paOrt(vpDatum.getDay(), item.stunde, item.lehrkraft) }}
|
|
</template>
|
|
|
|
<!-- Button für Anmerkungen -->
|
|
<template v-slot:item.expand="{ item }">
|
|
<v-btn @click="expanded = [item]" v-if="item.anmerkung !== '' && !expanded.includes(item)"><v-icon>mdi-comment-plus</v-icon></v-btn>
|
|
<v-btn @click="expanded = []" v-if="item.anmerkung !== '' && expanded.includes(item)"><v-icon>mdi-comment-minus</v-icon></v-btn>
|
|
</template>
|
|
|
|
<!-- Anmerkungen -->
|
|
<template v-slot:expanded-item="{ headers, item }">
|
|
<td :colspan="headers.length" v-if="item.anmerkung !== ''">
|
|
<pre>{{item.anmerkung}}</pre>
|
|
</td>
|
|
</template>
|
|
</v-data-table>
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import Klasse from '@/components/Klasse.vue'
|
|
import Zeitraster from '@/Zeitraster.js'
|
|
|
|
var vertretungsarten = {
|
|
'T': 'verlegt',
|
|
'F': 'verlegt von',
|
|
'W': 'Tausch',
|
|
'S': 'Betreuung',
|
|
'A': 'Sondereinsatz',
|
|
'C': 'Entfall',
|
|
'L': 'Freisetzung',
|
|
'P': 'Teil_vertretung',
|
|
'R': 'Raumvertretung',
|
|
'B': 'Pausenaufsicht',
|
|
'~': 'Lehrertausch',
|
|
'E': 'Klausur',
|
|
'': 'Vertretung'
|
|
}
|
|
|
|
export default {
|
|
name: 'Vertretungsplan',
|
|
components: {
|
|
Klasse
|
|
},
|
|
data() {
|
|
return {
|
|
vp: [],
|
|
pa: [],
|
|
zeitraster: new Zeitraster(),
|
|
// Felddefinitionen für die Datatable
|
|
headers: [
|
|
{
|
|
text: 'Datum',
|
|
value: 'datum',
|
|
sortable: false,
|
|
width: 0,
|
|
// nur ausgewähltes Datum
|
|
filter: value => {
|
|
if (value !== this.datumIso) {
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
text: 'Stunde',
|
|
value: 'stunde',
|
|
sortable: false,
|
|
filter: value => {
|
|
if (this.today && this.vergangeneAusblenden) {
|
|
if (value <= this.zeitraster.gibVergangeneStunden()) {
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
text: 'Durch',
|
|
value: 'lehrkraft_vertretung',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Klasse(n)',
|
|
value: 'klassen',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Fach (neu)',
|
|
value: 'fach_vertretung',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Raum (neu)',
|
|
value: 'raum_vertretung',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Für',
|
|
value: 'lehrkraft',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Fach',
|
|
value: 'fach',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Raum',
|
|
value: 'raum',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Durch',
|
|
value: 'lehrkraft_vertretung',
|
|
sortable: false,
|
|
// nur Zeilen mit Vertreter (keine Entfälle)
|
|
filter: value => {
|
|
if (value === '') {
|
|
return false
|
|
} else {
|
|
return true
|
|
}
|
|
}
|
|
},
|
|
{
|
|
text: 'Art',
|
|
value: 'art_vertretung',
|
|
sortable: false
|
|
},
|
|
{
|
|
text: 'Anmerkung',
|
|
value: 'expand',
|
|
sortable: false,
|
|
align: 'end'
|
|
},
|
|
],
|
|
expanded:[],
|
|
datepicker: false,
|
|
vergangeneAusblenden: true,
|
|
vpDatum: new Date(),
|
|
vpDatumsgrenze: 0 // Grenze für "neue" Einträge
|
|
};
|
|
},
|
|
props: {
|
|
vpUrl: String, // URL der GPU014,
|
|
paUrl: String, // URL der GPU009
|
|
},
|
|
computed: {
|
|
// Vertretungsplandatum als ISO-String formatieren (YYYY-MM-DD)
|
|
datumIso () {
|
|
return this.vpDatum.getFullYear() + "" + (this.vpDatum.getMonth() + 1) + "" + this.vpDatum.getDate()
|
|
},
|
|
// neues Datum über Datepicker festgelegt
|
|
neuesDatum: {
|
|
get: function () {
|
|
return this.vpDatum.toISOString().substr(0, 10)
|
|
},
|
|
set: function (val) {
|
|
this.vpDatum = new Date(val)
|
|
this.vpDatumsgrenze = parseInt(this.vpDatum.toISOString().split('T')[0].replace(/-/g,'') + "0800")
|
|
}
|
|
},
|
|
// Ist das ausgewählte Datum heute?
|
|
today () {
|
|
var d = new Date()
|
|
return d.toDateString() === this.vpDatum.toDateString()
|
|
}
|
|
},
|
|
methods: {
|
|
async reload () {
|
|
// Datum aktualisieren
|
|
this.vpDatum = new Date()
|
|
this.vpDatumsgrenze = parseInt(this.vpDatum.toISOString().split('T')[0].replace(/-/g,'') + "0800")
|
|
// Wochenenden überspringen
|
|
if (this.vpDatum.getDay() === 6) {
|
|
// Samstag => Montag
|
|
this.vpDatum.setDate(this.vpDatum.getDate() + 2)
|
|
} else if (this.vpDatum.getDay() === 0) {
|
|
// Sonntag => Montag
|
|
this.vpDatum.setDate(this.vpDatum.getDate() + 1)
|
|
}
|
|
|
|
// PAUSENAUFSICHTEN
|
|
const paHeader = 'ort,lehrkraft,tag,vor_stunde,dauer,extra\n'
|
|
// Vertretungsplan abrufen
|
|
const paRes = await fetch(this.paUrl)
|
|
// Text extrahieren und Feldnamen hinzufügen
|
|
const paText = paHeader + await paRes.text()
|
|
// Parsen und Array in Daten ablegen
|
|
this.pa = this.$papa.parse(paText, {'header': 'true', 'skipEmptyLines': 'greedy'}).data
|
|
|
|
// VERTRETUNGPLAN
|
|
// Feldnamen definieren
|
|
const vpHeader = 'nr,datum,stunde,absenznr,id_unterricht,lehrkraft,lehrkraft_vertretung,fach,fach_statistik_id,fach_vertretung,fach_vertretung_statistik_id,raum,raum_vertretung,statistik_id,klassen,absenzgrund,anmerkung,art,klassen_vertretung,art_vertretung,letzte_aenderung,extra\n'
|
|
// Vertretungsplan abrufen
|
|
const vpRes = await fetch(this.vpUrl)
|
|
// Text extrahieren und Feldnamen hinzufügen
|
|
const vpText = vpHeader + await vpRes.text()
|
|
// Parsen und Array in Daten ablegen
|
|
this.vp = this.$papa.parse(vpText, {'header': 'true', 'skipEmptyLines': 'greedy'}).data
|
|
},
|
|
// Filter für Wochenenden (Datepicker)
|
|
allowedDates: val => ![0, 6].includes(new Date(val).getDay()),
|
|
vertretungsart (code) {
|
|
return vertretungsarten[code]
|
|
},
|
|
// Klassen aufsplitten und als Array zurückgeben
|
|
klassen (klassenString) {
|
|
return klassenString.split('~')
|
|
},
|
|
// Ort zu einer Pausenaufsicht ermitteln
|
|
paOrt (tag, vor_stunde, lehrkraft) {
|
|
var regAufsicht = this.pa.filter(aufsicht => parseInt(aufsicht.tag) === parseInt(tag) && parseInt(aufsicht.vor_stunde) === parseInt(vor_stunde) && aufsicht.lehrkraft === lehrkraft)[0];
|
|
if (typeof regAufsicht != 'undefined') {
|
|
return regAufsicht.ort
|
|
} else {
|
|
return 'PAUSE'
|
|
}
|
|
}
|
|
},
|
|
created () {
|
|
// Vertretungsplan neu laden
|
|
this.reload()
|
|
}
|
|
}
|
|
</script>
|
|
|