mirror of
https://github.com/FH-Complete/FHC-Core.git
synced 2026-07-16 22:42:16 +00:00
Merge branch 'feature-30660/FHC4_StudierendenGUI_Prototyp' of github.com:FH-Complete/FHC-Core into feature-30660/FHC4_StudierendenGUI_Prototyp
This commit is contained in:
@@ -30,7 +30,7 @@ class Noten extends Auth_Controller
|
||||
return $this->outputJson($result);
|
||||
}
|
||||
|
||||
public function getZeugnis($prestudent_id)
|
||||
public function getZeugnis($prestudent_id, $all = null)
|
||||
{
|
||||
$this->load->model('crm/Student_model', 'StudentModel');
|
||||
$this->load->model('education/Zeugnisnote_model', 'ZeugnisnoteModel');
|
||||
@@ -47,7 +47,7 @@ class Noten extends Auth_Controller
|
||||
|
||||
$student_uid = current(getData($result))->student_uid;
|
||||
|
||||
$studiensemester_kurzbz = $this->variablelib->getVar('semester_aktuell');
|
||||
$studiensemester_kurzbz = ($all === null) ? $this->variablelib->getVar('semester_aktuell') : null;
|
||||
|
||||
$result = $this->ZeugnisnoteModel->getZeugnisnoten($student_uid, $studiensemester_kurzbz);
|
||||
if (isError($result)) {
|
||||
|
||||
@@ -77,6 +77,7 @@ class Student extends FHC_Controller
|
||||
$this->form_validation->set_rules('semester', 'Semester', 'integer');
|
||||
|
||||
if ($this->form_validation->run() == false) {
|
||||
$this->output->set_status_header(REST_Controller::HTTP_BAD_REQUEST);
|
||||
return $this->outputJsonError($this->form_validation->error_array());
|
||||
}
|
||||
|
||||
@@ -202,7 +203,11 @@ class Student extends FHC_Controller
|
||||
}
|
||||
}
|
||||
|
||||
$this->outputJsonSuccess(true);
|
||||
$this->outputJson(success(array_fill_keys(array_merge(
|
||||
array_keys($update_lehrverband),
|
||||
array_keys($update_person),
|
||||
array_keys($update_student)
|
||||
), ''), 1));
|
||||
}
|
||||
|
||||
public function check()
|
||||
|
||||
+4
-76
@@ -2,82 +2,10 @@
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
.accordion-button-primary {
|
||||
background-color: #e7f1ff;
|
||||
color: #0c63e4;
|
||||
}
|
||||
.accordion-button-primary:not(.collapsed) {
|
||||
background-color: #cfe2ff;
|
||||
color: #0a58ca;
|
||||
.btn-p-0 {
|
||||
padding: 0 .375rem;
|
||||
}
|
||||
|
||||
.accordion-button-secondary {
|
||||
background-color: #f0f1f2;
|
||||
color: #616971;
|
||||
.z-1 {
|
||||
z-index: 1;
|
||||
}
|
||||
.accordion-button-secondary:not(.collapsed) {
|
||||
background-color: #e2e3e5;
|
||||
color: #565e64;
|
||||
}
|
||||
|
||||
.accordion-button-success {
|
||||
background-color: #e8f3ee;
|
||||
color: #177a4c;
|
||||
}
|
||||
.accordion-button-success:not(.collapsed) {
|
||||
background-color: #d1e7dd;
|
||||
color: #146c43;
|
||||
}
|
||||
|
||||
.accordion-button-info {
|
||||
background-color: #e7fafe;
|
||||
color: #0cb6d8;
|
||||
}
|
||||
.accordion-button-info:not(.collapsed) {
|
||||
background-color: #cff4fc;
|
||||
color: #0aa2c0;
|
||||
}
|
||||
|
||||
.accordion-button-warning {
|
||||
background-color: #fff9e6;
|
||||
color: #e6ae06;
|
||||
}
|
||||
.accordion-button-warning:not(.collapsed) {
|
||||
background-color: #fff3cd;
|
||||
color: #cc9a06;
|
||||
}
|
||||
|
||||
.accordion-button-danger {
|
||||
background-color: #fcebec;
|
||||
color: #c6303e;
|
||||
}
|
||||
.accordion-button-danger:not(.collapsed) {
|
||||
background-color: #f8d7da;
|
||||
color: #b02a37;
|
||||
}
|
||||
|
||||
.accordion-button-light {
|
||||
background-color: #fefeff;
|
||||
color: #dfe0e1;
|
||||
}
|
||||
.accordion-button-light:not(.collapsed) {
|
||||
background-color: #fefefe;
|
||||
color: #c6c7c8;
|
||||
}
|
||||
|
||||
.accordion-button-dark {
|
||||
background-color: #e9e9ea;
|
||||
color: #1e2125;
|
||||
}
|
||||
.accordion-button-dark:not(.collapsed) {
|
||||
background-color: #d3d3d4;
|
||||
color: #1a1e21;
|
||||
}
|
||||
|
||||
.tabulator-edit-list .tabulator-edit-list-item {
|
||||
background-color: white;
|
||||
}
|
||||
.tabulator-edit-list .tabulator-edit-list-item:hover,
|
||||
.tabulator-edit-list .tabulator-edit-list-item.active {
|
||||
color: white;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
@import './Fhc.css';
|
||||
@import './components/searchbar.css';
|
||||
@import './components/verticalsplit.css';
|
||||
@import './components/FilterComponent.css';
|
||||
|
||||
@@ -14,7 +14,8 @@ export default {
|
||||
bsFeedback: Boolean,
|
||||
noAutoClass: Boolean,
|
||||
type: String,
|
||||
name: String
|
||||
name: String,
|
||||
containerClass: [String, Array, Object]
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -23,6 +24,13 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasContainer() {
|
||||
if (!this.bsFeedback)
|
||||
return true;
|
||||
if (this.containerClass)
|
||||
return true;
|
||||
return false;
|
||||
},
|
||||
lcType() {
|
||||
if (!this.type)
|
||||
return 'text';
|
||||
@@ -37,6 +45,8 @@ export default {
|
||||
return 'VueDatePicker';
|
||||
case 'autocomplete':
|
||||
return 'PvAutocomplete';
|
||||
case 'uploadimage':
|
||||
return 'UploadImage';
|
||||
default:
|
||||
return 'input';
|
||||
}
|
||||
@@ -133,7 +143,9 @@ export default {
|
||||
if (this.tag == 'VueDatePicker' && !this._.components.VueDatePicker) {
|
||||
this._.components.VueDatePicker = Vue.defineAsyncComponent(() => import("../vueDatepicker.js.php"));
|
||||
} else if (this.tag == 'PvAutocomplete' && !this._.components.PvAutocomplete) {
|
||||
this._.components.PvAutocomplete = Vue.defineAsyncComponent(() => import(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"));;
|
||||
this._.components.PvAutocomplete = Vue.defineAsyncComponent(() => import(FHC_JS_DATA_STORAGE_OBJECT.app_root + FHC_JS_DATA_STORAGE_OBJECT.ci_router + "/public/js/components/primevue/autocomplete/autocomplete.esm.min.js"));
|
||||
} else if (this.tag == 'UploadImage' && !this._.components.UploadImage) {
|
||||
this._.components.UploadImage = Vue.defineAsyncComponent(() => import("./Upload/Image.js"));
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -149,7 +161,7 @@ export default {
|
||||
// TODO(chris): wrap check in div?
|
||||
},
|
||||
template: `
|
||||
<component :is="bsFeedback ? 'FhcFragment' : 'div'" class="position-relative">
|
||||
<component :is="!hasContainer ? 'FhcFragment' : 'div'" class="position-relative" :class="containerClass">
|
||||
<label v-if="$attrs.label && lcType != 'radio' && lcType != 'checkbox'" :for="idCmp">{{$attrs.label}}</label>
|
||||
<input v-if="tag == 'input'" :type="lcType" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidation(); $emit('input', $event)">
|
||||
<textarea v-else-if="tag == 'textarea'" v-model="modelValueCmp" v-bind="$attrs" :id="idCmp" :name="name" :class="validationClass" :modelValue="undefined" @input="clearValidation(); $emit('input', $event)"></textarea>
|
||||
@@ -197,7 +209,7 @@ export default {
|
||||
>
|
||||
<slot></slot>
|
||||
</component>
|
||||
<div v-if="valid !== undefined" :class="feedbackClass">
|
||||
<div v-if="valid !== undefined && feedback.length" :class="feedbackClass">
|
||||
<template v-for="(msg, i) in feedback" :key="i">
|
||||
<hr v-if="i" class="m-0">
|
||||
{{msg}}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
export default {
|
||||
emits: [
|
||||
'update:modelValue'
|
||||
],
|
||||
props: {
|
||||
modelValue: {
|
||||
type: [ FileList, Array ],
|
||||
required: true
|
||||
},
|
||||
multiple: Boolean,
|
||||
id: String,
|
||||
name: String
|
||||
},
|
||||
methods: {
|
||||
stringifyFile(file) {
|
||||
return JSON.stringify({
|
||||
lastModified: file.lastModified,
|
||||
lastModifiedDate: file.lastModifiedDate,
|
||||
name: file.name,
|
||||
size: file.size,
|
||||
type: file.type
|
||||
});
|
||||
},
|
||||
addFiles(event) {
|
||||
if (!this.multiple)
|
||||
return this.$emit('update:modelValue', event.target.files);
|
||||
|
||||
const dt = new DataTransfer();
|
||||
const doubles = [];
|
||||
for (var file of this.modelValue) {
|
||||
dt.items.add(file);
|
||||
doubles.push(this.stringifyFile(file));
|
||||
}
|
||||
for (var file of event.target.files) {
|
||||
// NOTE(chris): deep check (with FileReader) would require an async function so we only check the basic attributes
|
||||
if (doubles.indexOf(this.stringifyFile(file)) < 0)
|
||||
dt.items.add(file);
|
||||
}
|
||||
this.$emit('update:modelValue', dt.files);
|
||||
},
|
||||
removeFile(id) {
|
||||
const fileToRemove = Array.from(this.modelValue)[id];
|
||||
|
||||
const dt = new DataTransfer();
|
||||
for (var file of this.modelValue) {
|
||||
if (file !== fileToRemove)
|
||||
dt.items.add(file);
|
||||
}
|
||||
this.$emit('update:modelValue', dt.files);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
modelValue(n) {
|
||||
if (n instanceof FileList)
|
||||
return this.$refs.upload.files = n;
|
||||
|
||||
const dt = new DataTransfer();
|
||||
const dms = [];
|
||||
for (var file of n) {
|
||||
if (file instanceof File) {
|
||||
dt.items.add(file);
|
||||
} else {
|
||||
const dmsFile = new File([JSON.stringify(file)], file.name, {
|
||||
type: 'application/x.fhc-dms+json'
|
||||
});
|
||||
dt.items.add(dmsFile);
|
||||
}
|
||||
}
|
||||
this.$emit('update:modelValue', dt.files);
|
||||
}
|
||||
},
|
||||
template: `
|
||||
<div class="form-upload-dms">
|
||||
<input ref="upload" class="form-control" :id="id" :name="name" :multiple="multiple" type="file" @change="addFiles">
|
||||
<ul v-if="modelValue.length && multiple" class="list-unstyled m-0">
|
||||
<li v-for="(file, index) in modelValue" :key="index" class="d-flex mx-1 mt-1">
|
||||
<span class="col-auto"><i class="fa fa-file me-1"></i></span>
|
||||
<span class="col">{{ file.name }}</span>
|
||||
<button class="col-auto btn btn-outline-secondary btn-p-0" @click="removeFile(index)">
|
||||
<i class="fa fa-close"></i>
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
</div>`
|
||||
}
|
||||
@@ -46,17 +46,17 @@ export default {
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot name="placeholder">
|
||||
<slot>
|
||||
<svg class="bd-placeholder-img img-thumbnail" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="A generic square placeholder image with a white border around it, making it resemble a photograph taken with an old instant camera: 200x200" preserveAspectRatio="xMidYMid slice" focusable="false"><title>A generic square placeholder image with a white border around it, making it resemble a photograph taken with an old instant camera</title><rect width="100%" height="100%" fill="#868e96"></rect><text x="50%" y="50%" fill="#dee2e6" dy=".3em"></text></svg>
|
||||
<div class="fotobutton-visible">
|
||||
<div class="d-grid gap-2 d-md-flex">
|
||||
<button type="button" class="btn btn-outline-dark btn-sm" @click="openUploadDialog">
|
||||
<i class="fa fa-pen"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</slot>
|
||||
<div class="fotobutton-visible">
|
||||
<div class="d-grid gap-2 d-md-flex">
|
||||
<button type="button" class="btn btn-outline-dark btn-sm" @click="openUploadDialog">
|
||||
<i class="fa fa-pen"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<input class="d-none" type="file" ref="fileInput" @input="pickFile" accept="image/*">
|
||||
<input :id="$attrs.id" class="d-none" type="file" ref="fileInput" @input="pickFile" accept="image/*">
|
||||
</div>`
|
||||
}
|
||||
@@ -1,16 +1,14 @@
|
||||
import VueDatePicker from '../../../vueDatepicker.js.php';
|
||||
import FormUploadImage from '../../../Form/Upload/Image.js';
|
||||
import {CoreRESTClient} from '../../../../RESTClient.js';
|
||||
|
||||
|
||||
import PvToast from "../../../../../../index.ci.php/public/js/components/primevue/toast/toast.esm.min.js";
|
||||
import FormForm from '../../../Form/Form.js';
|
||||
import FormInput from '../../../Form/Input.js';
|
||||
import FormUploadImage from '../../../Form/Upload/Image.js';
|
||||
|
||||
|
||||
export default {
|
||||
components: {
|
||||
VueDatePicker,
|
||||
FormUploadImage,
|
||||
PvToast
|
||||
FormForm,
|
||||
FormInput,
|
||||
FormUploadImage
|
||||
},
|
||||
inject: {
|
||||
showBpk: {
|
||||
@@ -62,6 +60,9 @@ export default {
|
||||
},
|
||||
changedLength() {
|
||||
return Object.keys(this.changed).length;
|
||||
},
|
||||
noImageSrc() {
|
||||
return FHC_JS_DATA_STORAGE_OBJECT.app_root + 'skin/images/profilbild_dummy.jpg';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -69,6 +70,7 @@ export default {
|
||||
this.updateStudent(n);
|
||||
},
|
||||
data: {
|
||||
// TODO(chris): use @input instead?
|
||||
handler(n) {
|
||||
let res = {};
|
||||
for (var k in this.original) {
|
||||
@@ -82,7 +84,6 @@ export default {
|
||||
}
|
||||
}
|
||||
this.changed = res;
|
||||
this.resetErrors();
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
@@ -98,213 +99,264 @@ export default {
|
||||
this.data.familienstand = '';
|
||||
this.original = {...this.data};
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err.response.data || err.message);
|
||||
});
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
},
|
||||
save() {
|
||||
CoreRESTClient
|
||||
.post('components/stv/Student/save/' + this.modelValue.prestudent_id, this.changed)
|
||||
.then(result => result.data)
|
||||
this.$refs.form
|
||||
.send(CoreRESTClient.post('components/stv/Student/save/' + this.modelValue.prestudent_id, this.changed))
|
||||
.then(result => {
|
||||
this.resetErrors();
|
||||
if (CoreRESTClient.isError(result)) {
|
||||
let errors = CoreRESTClient.getError(result);
|
||||
|
||||
if (errors === "Generic error")
|
||||
console.error(errors, result);
|
||||
else {
|
||||
for (var k in errors)
|
||||
this.addError(k, errors[k]);
|
||||
}
|
||||
} else {
|
||||
for (var node of document.querySelectorAll(Object.keys(this.changed).map(el => '#stv-details-' + el).join(',')))
|
||||
node.classList.add('is-valid');
|
||||
if (this.changed.gebdatum !== undefined)
|
||||
this.gebDatumIsValid = true;
|
||||
|
||||
// TODO(chris): phrase
|
||||
this.$fhcAlert.alertSuccess('Gespeichert');
|
||||
//this.addToast('Gespeichert', '', 'success');
|
||||
|
||||
this.original = {...this.data};
|
||||
this.changed = {};
|
||||
}
|
||||
this.original = {...this.data};
|
||||
this.changed = {};
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError/*err => {
|
||||
// TODO(chris): phrase
|
||||
this.addToast('Error', err?.response?.data || err?.message, 'error');
|
||||
}*/);
|
||||
},
|
||||
resetErrors() {
|
||||
Array.from(this.$refs.form.getElementsByClassName('is-valid')).forEach(el => el.classList.remove('is-valid'));
|
||||
Array.from(this.$refs.form.getElementsByClassName('is-invalid')).forEach(el => el.classList.remove('is-invalid'));
|
||||
Array.from(this.$refs.form.getElementsByClassName('invalid-feedback')).forEach(el => el.remove());
|
||||
this.gebDatumIsValid = false;
|
||||
this.gebDatumIsInvalid = false;
|
||||
},
|
||||
addError(field, msg) {
|
||||
let id = 'stv-details-' + field;
|
||||
|
||||
let input = document.getElementById(id);
|
||||
if (field === 'gebdatum') {
|
||||
this.gebDatumIsInvalid = true;
|
||||
input = document.getElementById('dp-input-' + id).parentNode;
|
||||
}
|
||||
|
||||
input.classList.add('is-invalid');
|
||||
|
||||
let feedback = document.createElement('div');
|
||||
feedback.classList.add('invalid-feedback');
|
||||
feedback.innerHTML = msg;
|
||||
input.after(feedback);
|
||||
/*},
|
||||
addToast(header, msg, severity) {
|
||||
this.$refs.responseToast.add({
|
||||
severity: severity,
|
||||
summary: header,
|
||||
detail: msg,
|
||||
life: 3000
|
||||
})*/
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.updateStudent(this.modelValue);
|
||||
},
|
||||
//TODO(chris): Phrasen
|
||||
//TODO(chris): Geburtszeit? Anzahl der Kinder?
|
||||
template: `
|
||||
<form ref="form" class="stv-details-details" @submit.prevent="save">
|
||||
<div class="position-sticky top-0">
|
||||
<form-form ref="form" class="stv-details-details" @submit.prevent="save">
|
||||
<div class="position-sticky top-0 z-1">
|
||||
<button type="submit" class="btn btn-primary position-absolute top-0 end-0" :disabled="!changedLength">Speichern</button>
|
||||
</div>
|
||||
<fieldset class="overflow-hidden">
|
||||
<legend>Person</legend>
|
||||
<template v-if="data">
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-person_id" class="col-sm-1 col-form-label">Person ID</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-person_id" type="text" class="form-control" v-model="data.person_id" disabled>
|
||||
</div>
|
||||
<label v-if="showZugangscode" for="stv-details-zugangscode" class="col-sm-1 col-form-label">Zugangscode</label>
|
||||
<div v-if="showZugangscode" class="col-sm-3 align-self-center">
|
||||
<span class="form-text">
|
||||
<a :href="cisRoot + 'addons/bewerbung/cis/registration.php?code=' + data.zugangscode + '&emailAdresse=' + data.email_privat" target="_blank">{{data.zugangscode}}</a>
|
||||
</span>
|
||||
</div>
|
||||
<label v-if="showBpk" for="stv-details-bpk" class="col-sm-1 col-form-label">BPK</label>
|
||||
<div v-if="showBpk" class="col-sm-3">
|
||||
<input id="stv-details-bpk" type="text" class="form-control" v-model="data.bpk" maxlength="28">
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Person ID"
|
||||
type="text"
|
||||
v-model="data.person_id"
|
||||
name="person_id"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<div v-if="showZugangscode" class="col-4">
|
||||
<label>Zugangscode</label>
|
||||
<div class="align-self-center">
|
||||
<span class="form-text">
|
||||
<a :href="cisRoot + 'addons/bewerbung/cis/registration.php?code=' + data.zugangscode + '&emailAdresse=' + data.email_privat" target="_blank">{{data.zugangscode}}</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<form-input
|
||||
v-if="showBpk"
|
||||
container-class="col-4"
|
||||
label="BPK"
|
||||
type="text"
|
||||
v-model="data.bpk"
|
||||
name="bpk"
|
||||
maxlength="28"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-anrede" class="col-sm-1 col-form-label">Anrede</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-anrede" type="text" class="form-control" v-model="data.anrede" maxlength="16">
|
||||
</div>
|
||||
<label for="stv-details-titelpre" class="col-sm-1 col-form-label">Titel Pre</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-titelpre" type="text" class="form-control" v-model="data.titelpre" maxlength="64">
|
||||
</div>
|
||||
<label for="stv-details-titelpost" class="col-sm-1 col-form-label">Titel Post</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-titelpost" type="text" class="form-control" v-model="data.titelpost" maxlength="32">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Anrede"
|
||||
type="text"
|
||||
v-model="data.anrede"
|
||||
name="anrede"
|
||||
maxlength="16"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Titel Pre"
|
||||
type="text"
|
||||
v-model="data.titelpre"
|
||||
name="titelpre"
|
||||
maxlength="64"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Titel Post"
|
||||
type="text"
|
||||
v-model="data.titelpost"
|
||||
name="titelpost"
|
||||
maxlength="32"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-nachname" class="col-sm-1 col-form-label">Nachname</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-nachname" type="text" class="form-control" v-model="data.nachname" maxlength="64">
|
||||
</div>
|
||||
<label for="stv-details-vorname" class="col-sm-1 col-form-label">Vorname</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-vorname" type="text" class="form-control" v-model="data.vorname" maxlength="32">
|
||||
</div>
|
||||
<label for="stv-details-vornamen" class="col-sm-1 col-form-label">Vornamen</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-vornamen" type="text" class="form-control" v-model="data.vornamen" maxlength="128">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Nachname"
|
||||
type="text"
|
||||
v-model="data.nachname"
|
||||
name="nachname"
|
||||
maxlength="64"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Vorname"
|
||||
type="text"
|
||||
v-model="data.vorname"
|
||||
name="vorname"
|
||||
maxlength="32"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Vornamen"
|
||||
type="text"
|
||||
v-model="data.vornamen"
|
||||
name="vornamen"
|
||||
maxlength="128"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-wahlname" class="col-sm-1 col-form-label">Wahlname</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-wahlname" type="text" class="form-control" v-model="data.wahlname" maxlength="128">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Wahlname"
|
||||
type="text"
|
||||
v-model="data.wahlname"
|
||||
name="wahlname"
|
||||
maxlength="128"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="dp-input-stv-details-gebdatum" class="col-sm-1 col-form-label">Geburtsdatum</label>
|
||||
<div class="col-sm-3">
|
||||
<vue-date-picker id="stv-details-gebdatum" :input-class-name="gebDatumIsInvalid ? 'form-control is-invalid' : (gebDatumIsValid ? 'form-control is-valid' : 'form-control')" uid="stv-details-gebdatum" v-model="data.gebdatum" :clearable="false" no-today auto-apply :enable-time-picker="false" format="dd.MM.yyyy" preview-format="dd.MM.yyyy"></vue-date-picker>
|
||||
</div>
|
||||
<label for="stv-details-gebort" class="col-sm-1 col-form-label">Geburtsort</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-gebort" type="text" class="form-control" v-model="data.gebort" maxlength="128">
|
||||
</div>
|
||||
<label for="stv-details-geburtsnation" class="col-sm-1 col-form-label">Geburtsnation</label>
|
||||
<div class="col-sm-3">
|
||||
<select id="stv-details-geburtsnation" class="form-control" v-model="data.geburtsnation">
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Geburtsdatum"
|
||||
type="DatePicker"
|
||||
v-model="data.gebdatum"
|
||||
name="gebdatum"
|
||||
:clearable="false"
|
||||
no-today
|
||||
auto-apply
|
||||
:enable-time-picker="false"
|
||||
format="dd.MM.yyyy"
|
||||
preview-format="dd.MM.yyyy"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Geburtsort"
|
||||
type="text"
|
||||
v-model="data.gebort"
|
||||
name="gebort"
|
||||
maxlength="128"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Geburtsnation"
|
||||
type="select"
|
||||
v-model="data.geburtsnation"
|
||||
name="geburtsnation"
|
||||
>
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-svnr" class="col-sm-1 col-form-label">SVNR</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-svnr" type="text" class="form-control" v-model="data.svnr" maxlength="16">
|
||||
</div>
|
||||
<label for="stv-details-ersatzkennzeichen" class="col-sm-1 col-form-label">Ersatzkennzeichen</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-ersatzkennzeichen" type="text" class="form-control" v-model="data.ersatzkennzeichen" maxlength="10">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="SVNR"
|
||||
type="text"
|
||||
v-model="data.svnr"
|
||||
name="svnr"
|
||||
maxlength="16"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Ersatzkennzeichen"
|
||||
type="text"
|
||||
v-model="data.ersatzkennzeichen"
|
||||
name="ersatzkennzeichen"
|
||||
maxlength="10"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-staatsbuergerschaft" class="col-sm-1 col-form-label">Staatsbürgerschaft</label>
|
||||
<div class="col-sm-3">
|
||||
<select id="stv-details-staatsbuergerschaft" class="form-control" v-model="data.staatsbuergerschaft">
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="stv-details-matr_nr" class="col-sm-1 col-form-label">Matrikelnummer</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-matr_nr" type="text" class="form-control" v-model="data.matr_nr" maxlength="32">
|
||||
</div>
|
||||
<label for="stv-details-sprache" class="col-sm-1 col-form-label">Sprache</label>
|
||||
<div class="col-sm-3">
|
||||
<select id="stv-details-sprache" class="form-control" v-model="data.sprache">
|
||||
<option v-for="sprache in lists.sprachen" :key="sprache.sprache" :value="sprache.sprache">{{sprache.sprache}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Staatsbürgerschaft"
|
||||
type="select"
|
||||
v-model="data.staatsbuergerschaft"
|
||||
name="staatsbuergerschaft"
|
||||
>
|
||||
<option value="">-- keine Auswahl --</option>
|
||||
<!-- TODO(chris): gesperrte nationen können nicht ausgewählt werden! Um das zu realisieren müsste man ein pseudo select machen -->
|
||||
<option v-for="nation in lists.nations" :key="nation.nation_code" :value="nation.nation_code" :disabled="nation.sperre">{{nation.kurztext}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Matrikelnummer"
|
||||
type="text"
|
||||
v-model="data.matr_nr"
|
||||
name="matr_nr"
|
||||
maxlength="32"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Sprache"
|
||||
type="select"
|
||||
v-model="data.sprache"
|
||||
name="sprache"
|
||||
>
|
||||
<option v-for="sprache in lists.sprachen" :key="sprache.sprache" :value="sprache.sprache">{{sprache.sprache}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-geschlecht" class="col-sm-1 col-form-label">Geschlecht</label>
|
||||
<div class="col-sm-3">
|
||||
<select id="stv-details-geschlecht" class="form-control" v-model="data.geschlecht">
|
||||
<option v-for="geschlecht in lists.geschlechter" :key="geschlecht.geschlecht" :value="geschlecht.geschlecht">{{geschlecht.bezeichnung}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<label for="stv-details-familienstand" class="col-sm-1 col-form-label">Familienstand</label>
|
||||
<div class="col-sm-3">
|
||||
<select id="stv-details-familienstand" class="form-control" v-model="data.familienstand">
|
||||
<option v-for="(bezeichnung, key) in familienstaende" :key="key" :value="key">{{bezeichnung}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Geschlecht"
|
||||
type="select"
|
||||
v-model="data.geschlecht"
|
||||
name="geschlecht"
|
||||
>
|
||||
<option v-for="geschlecht in lists.geschlechter" :key="geschlecht.geschlecht" :value="geschlecht.geschlecht">{{geschlecht.bezeichnung}}</option>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Familienstand"
|
||||
type="select"
|
||||
v-model="data.familienstand"
|
||||
name="familienstand"
|
||||
>
|
||||
<option v-for="(bezeichnung, key) in familienstaende" :key="key" :value="key">{{bezeichnung}}</option>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-foto" class="col-sm-1 col-form-label">Foto</label>
|
||||
<div class="col-sm-3">
|
||||
<form-upload-image id="stv-details-foto" v-model="data.foto"></form-upload-image>
|
||||
</div>
|
||||
<label for="stv-details-anmerkung" class="col-sm-1 col-form-label">Anmerkung</label>
|
||||
<div class="col-sm-3">
|
||||
<textarea id="stv-details-anmerkung" class="form-control" v-model="data.anmerkung"></textarea>
|
||||
</div>
|
||||
<label for="stv-details-homepage" class="col-sm-1 col-form-label">Homepage</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-homepage" type="text" class="form-control" v-model="data.homepage" maxlength="256">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Foto"
|
||||
type="UploadImage"
|
||||
v-model="data.foto"
|
||||
name="foto"
|
||||
>
|
||||
<img alt="No Image" :src="noImageSrc" class="w-100">
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Anmerkung"
|
||||
type="textarea"
|
||||
v-model="data.anmerkung"
|
||||
name="anmerkung"
|
||||
rows="8"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Homepage"
|
||||
type="text"
|
||||
v-model="data.homepage"
|
||||
name="homepage"
|
||||
maxlength="256"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</template>
|
||||
<div v-else>
|
||||
@@ -315,47 +367,79 @@ export default {
|
||||
<legend>StudentIn</legend>
|
||||
<template v-if="data">
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-student_uid" class="col-sm-1 col-form-label">UID</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-student_uid" type="text" class="form-control" v-model="data.student_uid" disabled>
|
||||
</div>
|
||||
<label for="stv-details-personenkennzeichen" class="col-sm-1 col-form-label">Personenkennzeichen</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-personenkennzeichen" type="text" class="form-control" v-model="data.matrikelnr" disabled>
|
||||
</div>
|
||||
<label for="stv-details-aktiv" class="col-sm-1 col-form-label">Aktiv</label>
|
||||
<div class="col-sm-3 align-self-center">
|
||||
<div class="form-check">
|
||||
<input id="stv-details-aktiv" type="checkbox" class="form-check-input" v-model="data.aktiv">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="UID"
|
||||
type="text"
|
||||
v-model="data.student_uid"
|
||||
name="student_uid"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Personenkennzeichen"
|
||||
type="text"
|
||||
v-model="data.matrikelnr"
|
||||
name="matrikelnr"
|
||||
readonly
|
||||
>
|
||||
</form-input>
|
||||
<div class="col-4 pt-4 d-flex align-items-center">
|
||||
<form-input
|
||||
container-class="form-check"
|
||||
label="Aktiv"
|
||||
type="checkbox"
|
||||
v-model="data.aktiv"
|
||||
name="aktiv"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-semester" class="col-sm-1 col-form-label">Semester</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-semester" type="text" class="form-control" v-model="data.semester" maxlength="2">
|
||||
</div>
|
||||
<label for="stv-details-verband" class="col-sm-1 col-form-label">Verband</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-verband" type="text" class="form-control" v-model="data.verband" maxlength="1">
|
||||
</div>
|
||||
<label for="stv-details-gruppe" class="col-sm-1 col-form-label">Gruppe</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-gruppe" type="text" class="form-control" v-model="data.gruppe" maxlength="1">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Semester"
|
||||
type="text"
|
||||
v-model="data.semester"
|
||||
name="semester"
|
||||
maxlength="2"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Verband"
|
||||
type="text"
|
||||
v-model="data.verband"
|
||||
name="verband"
|
||||
maxlength="1"
|
||||
>
|
||||
</form-input>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Gruppe"
|
||||
type="text"
|
||||
v-model="data.gruppe"
|
||||
name="gruppe"
|
||||
maxlength="1"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<label for="stv-details-alias" class="col-sm-1 col-form-label">Alias</label>
|
||||
<div class="col-sm-3">
|
||||
<input id="stv-details-alias" type="text" class="form-control" v-model="data.alias" :disabled="aliasNotAllowed">
|
||||
</div>
|
||||
<form-input
|
||||
container-class="col-4"
|
||||
label="Alias"
|
||||
type="text"
|
||||
v-model="data.alias"
|
||||
name="alias"
|
||||
:disabled="aliasNotAllowed"
|
||||
>
|
||||
</form-input>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
<div v-else>
|
||||
Loading...
|
||||
</div>
|
||||
</fieldset>
|
||||
<pv-toast ref="responseToast" style="z-index:9999"></pv-toast>
|
||||
</form>`
|
||||
</form-form>`
|
||||
};
|
||||
@@ -2,6 +2,8 @@ import {CoreFilterCmpt} from "../../../../filter/Filter.js";
|
||||
import {CoreRESTClient} from '../../../../../RESTClient.js';
|
||||
import ZeugnisActions from './Zeugnis/Actions.js';
|
||||
|
||||
const LOCAL_STORAGE_ID = 'stv_details_noten_zeugnis_2024-01-11_stdsem_all';
|
||||
|
||||
export default {
|
||||
components: {
|
||||
CoreFilterCmpt,
|
||||
@@ -13,12 +15,13 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
validStudent: true,
|
||||
tabulatorEvents: []
|
||||
tabulatorEvents: [],
|
||||
stdsem: ''
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
ajaxURL() {
|
||||
return CoreRESTClient._generateRouterURI('components/stv/Noten/getZeugnis/' + this.student.prestudent_id);
|
||||
return CoreRESTClient._generateRouterURI('components/stv/Noten/getZeugnis/' + this.student.prestudent_id + this.stdsem);
|
||||
},
|
||||
tabulatorOptions() {
|
||||
return {
|
||||
@@ -59,29 +62,50 @@ export default {
|
||||
};
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
ajaxURL(n) {
|
||||
if (this.$refs.table)
|
||||
this.$refs.table.tabulator.setData(n);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
setGrades(selected) {
|
||||
CoreRESTClient
|
||||
.post('components/stv/Noten/update', selected)
|
||||
.then(this.$refs.table.reloadTable)
|
||||
.catch(this.$fhcAlert.handleFormValidation);
|
||||
},
|
||||
saveStdsem(event) {
|
||||
window.localStorage.setItem(LOCAL_STORAGE_ID, event.target.value ? 'true' : '');
|
||||
}
|
||||
},
|
||||
created() {
|
||||
const savedPath = window.localStorage.getItem(LOCAL_STORAGE_ID);
|
||||
this.stdsem = savedPath ? '/all' : '';
|
||||
},
|
||||
// TODO(chris): phrasen
|
||||
template: `
|
||||
<div class="stv-details-noten-zeugnis h-100 d-flex flex-column">
|
||||
<div v-if="!validStudent">Kein Student</div>
|
||||
<core-filter-cmpt
|
||||
v-else
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
>
|
||||
<template #actions="{selected}">
|
||||
<zeugnis-actions :selected="selected" @set-grades="setGrades"></zeugnis-actions>
|
||||
</template>
|
||||
</core-filter-cmpt>
|
||||
<template v-else>
|
||||
<div class="mb-3">
|
||||
<select class="form-select" v-model="stdsem" @input="saveStdsem">
|
||||
<option value="">Aktuelles Semester</option>
|
||||
<option value="/all">Alle Semester</option>
|
||||
</select>
|
||||
</div>
|
||||
<core-filter-cmpt
|
||||
ref="table"
|
||||
:tabulator-options="tabulatorOptions"
|
||||
:tabulator-events="tabulatorEvents"
|
||||
table-only
|
||||
:side-menu="false"
|
||||
reload
|
||||
>
|
||||
<template #actions="{selected}">
|
||||
<zeugnis-actions :selected="selected" @set-grades="setGrades"></zeugnis-actions>
|
||||
</template>
|
||||
</core-filter-cmpt>
|
||||
</template>
|
||||
</div>`
|
||||
};
|
||||
@@ -11,9 +11,14 @@ export default {
|
||||
'changed'
|
||||
],
|
||||
props: {
|
||||
configUrl: String,
|
||||
// TODO(chris): rename to config?
|
||||
configUrl: {
|
||||
type: [String, Object],
|
||||
required: true
|
||||
},
|
||||
default: String,
|
||||
modelValue: [String, Number, Boolean, Array, Object, Date, Function, Symbol]
|
||||
modelValue: [String, Number, Boolean, Array, Object, Date, Function, Symbol],
|
||||
vertical: Boolean
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
@@ -37,60 +42,70 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
configUrl(n) {
|
||||
this.initConfig(n);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
change(key) {
|
||||
this.$emit("change", key)
|
||||
this.current = key;
|
||||
this.$nextTick(() => this.$emit("changed", key));
|
||||
}
|
||||
},
|
||||
created() {
|
||||
CoreRESTClient
|
||||
.get(this.configUrl)
|
||||
.then(result => CoreRESTClient.getData(result.data))
|
||||
.then(result => {
|
||||
if (!result)
|
||||
return;
|
||||
},
|
||||
initConfig(config) {
|
||||
if (!config)
|
||||
return;
|
||||
if (typeof config === 'string' || config instanceof String)
|
||||
return CoreRESTClient.get(config)
|
||||
.then(result => CoreRESTClient.getData(result.data))
|
||||
.then(this.initConfig)
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
|
||||
const tabs = {};
|
||||
console.log(config);
|
||||
const tabs = {};
|
||||
|
||||
if (Array.isArray(result)) {
|
||||
result.forEach((config, key) => {
|
||||
if (!config.component)
|
||||
return console.error('Component missing for ' + key);
|
||||
if (Array.isArray(config)) {
|
||||
config.forEach((item, key) => {
|
||||
if (!item.component)
|
||||
return console.error('Component missing for ' + key);
|
||||
|
||||
tabs[key] = {
|
||||
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(config.component))),
|
||||
title: config.title || key,
|
||||
config: config.config,
|
||||
key
|
||||
}
|
||||
});
|
||||
tabs[key] = {
|
||||
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(item.component))),
|
||||
title: item.title || key,
|
||||
config: item.config,
|
||||
key
|
||||
}
|
||||
});
|
||||
} else {
|
||||
Object.entries(config).forEach(([key, item]) => {
|
||||
if (!item.component)
|
||||
return console.error('Component missing for ' + key);
|
||||
|
||||
} else {
|
||||
Object.entries(result).forEach(([key, config]) => {
|
||||
if (!config.component)
|
||||
return console.error('Component missing for ' + key);
|
||||
tabs[key] = {
|
||||
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(item.component))),
|
||||
title: item.title || key,
|
||||
config: item.config,
|
||||
key
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
tabs[key] = {
|
||||
component: Vue.markRaw(Vue.defineAsyncComponent(() => import(config.component))),
|
||||
title: config.title || key,
|
||||
config: config.config,
|
||||
key
|
||||
}
|
||||
});
|
||||
}
|
||||
if (this.current === null || !tabs[this.current]) {
|
||||
if (tabs[this.default])
|
||||
this.current = this.default;
|
||||
else
|
||||
this.current = Object.keys(tabs)[0];
|
||||
this.tabs = tabs;
|
||||
})
|
||||
.catch(this.$fhcAlert.handleSystemError);
|
||||
}
|
||||
this.tabs = tabs;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.initConfig(this.configUrl);
|
||||
},
|
||||
template: `
|
||||
<div class="fhc-tabs d-flex flex-column" v-if="Object.keys(tabs).length">
|
||||
<div class="nav nav-tabs">
|
||||
<div class="fhc-tabs d-flex" :class="vertical ? 'align-items-stretch' : 'flex-column'" v-if="Object.keys(tabs).length">
|
||||
<div class="nav" :class="vertical ? 'nav-pills flex-column' : 'nav-tabs'">
|
||||
<div
|
||||
v-for="tab in tabs"
|
||||
:key="tab.key"
|
||||
@@ -98,12 +113,12 @@ export default {
|
||||
:class="{active: tab.key == current}"
|
||||
@click="change(tab.key)"
|
||||
:aria-current="tab.key == current ? 'page' : ''"
|
||||
v-accessibility:tab
|
||||
v-accessibility:tab.[vertical]
|
||||
>
|
||||
{{tab.title}}
|
||||
</div>
|
||||
</div>
|
||||
<div style="flex: 1 1 0%; height: 0%" class="border-bottom border-start border-end overflow-auto p-3">
|
||||
<div :style="vertical ? '' : 'flex: 1 1 0%; height: 0%'" class="overflow-auto p-3" :class="vertical ? '' : 'border-bottom border-start border-end'">
|
||||
<keep-alive>
|
||||
<component ref="current" :is="currentTab.component" v-model="value" :config="currentTab.config"></component>
|
||||
</keep-alive>
|
||||
|
||||
Reference in New Issue
Block a user