121 lines
2.8 KiB
Vue
121 lines
2.8 KiB
Vue
<script setup>
|
|
import { ref, shallowRef, onMounted } from "vue"
|
|
|
|
const props = defineProps({
|
|
tabs: {
|
|
type: Array,
|
|
required: true,
|
|
// [{ title: "Titel", component: Component, endpoint: "/wp-json/..." }]
|
|
},
|
|
subTabIndex: {
|
|
type: Number,
|
|
required: false,
|
|
default: 0
|
|
}
|
|
})
|
|
|
|
const activeTab = ref(null) // aktuell ausgewählter Tab
|
|
const tabData = ref({}) // Daten für jeden Tab
|
|
const tabComponent = shallowRef(null) // Komponente für aktuellen Tab
|
|
const loading = ref(false)
|
|
const error = ref(null)
|
|
|
|
async function selectTab(index) {
|
|
const tab = props.tabs[index]
|
|
activeTab.value = index
|
|
tabComponent.value = null
|
|
error.value = null
|
|
|
|
loading.value = true
|
|
|
|
try {
|
|
const csrfToken = document
|
|
.querySelector('meta[name="csrf-token"]')
|
|
?.getAttribute('content')
|
|
|
|
const response = await fetch(tab.endpoint, {
|
|
method: 'GET', // oder POST, PUT, DELETE …
|
|
headers: {
|
|
'Accept': 'application/json',
|
|
'X-CSRF-TOKEN': csrfToken,
|
|
'X-Requested-With': 'XMLHttpRequest',
|
|
},
|
|
credentials: 'same-origin', // wichtig für Session/Auth
|
|
})
|
|
|
|
if (!response.ok) throw new Error("Fehler: " + response.status)
|
|
|
|
const json = await response.json()
|
|
|
|
tabData.value[index] = json
|
|
tabComponent.value = tab.component
|
|
} catch (err) {
|
|
error.value = err.message
|
|
} finally {
|
|
loading.value = false
|
|
}
|
|
}
|
|
|
|
// ersten Tab automatisch laden
|
|
onMounted(() => {
|
|
if (props.tabs.length > 0) {
|
|
selectTab(props.subTabIndex)
|
|
}
|
|
})
|
|
</script>
|
|
|
|
<template>
|
|
<div class="tabs">
|
|
<!-- Tab Header -->
|
|
<div class="tab-header">
|
|
<button
|
|
v-for="(tab, index) in tabs"
|
|
:key="index"
|
|
:class="['tab-button', { active: activeTab === index }]"
|
|
@click="selectTab(index)"
|
|
>
|
|
{{ tab.title }}
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Tab Content -->
|
|
<div class="tab-content" id="tab-content">
|
|
<div v-if="loading">⏳ Lädt...</div>
|
|
<div v-else-if="error">❌ {{ error }}</div>
|
|
<component
|
|
v-else-if="tabComponent"
|
|
:is="tabComponent"
|
|
:data="tabData[activeTab]"
|
|
:deep_jump_id="tabs[activeTab].deep_jump_id"
|
|
:deep_jump_id_sub="tabs[activeTab].deep_jump_id_sub"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.tabs {
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
}
|
|
.tab-header {
|
|
display: flex;
|
|
border-bottom: 1px solid #ddd;
|
|
}
|
|
.tab-button {
|
|
padding: 0.5rem 1rem;
|
|
cursor: pointer;
|
|
border: none;
|
|
background: #f8f8f8;
|
|
}
|
|
.tab-button.active {
|
|
font-weight: bold;
|
|
background: white;
|
|
border-bottom: 2px solid #0073aa;
|
|
}
|
|
.tab-content {
|
|
padding: 1rem;
|
|
width: 100%;
|
|
}
|
|
</style>
|