Enhance backend configuration and error handling: Update CORS settings to allow dynamic origins, improve RabbitMQ connection handling in chat services, and adjust API server host configuration. Refactor environment variables for better flexibility and add fallback mechanisms for WebSocket and chat services. Update frontend environment files for consistent API and WebSocket URLs.
This commit is contained in:
@@ -1,10 +1,12 @@
|
||||
<template>
|
||||
<main class="contenthidden">
|
||||
<div class="contentscroll">
|
||||
<main class="app-content contenthidden">
|
||||
<div class="app-content__scroll contentscroll">
|
||||
<div class="app-content__inner">
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
</div>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
@@ -12,15 +14,31 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
main {
|
||||
padding: 0;
|
||||
background-color: #ffffff;
|
||||
flex: 1;
|
||||
<style scoped>
|
||||
.app-content {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.app-content__scroll {
|
||||
background: transparent;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.app-content__inner {
|
||||
max-width: var(--shell-max-width);
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
margin: 0 auto;
|
||||
padding: 14px 18px;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.app-content__inner {
|
||||
padding: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
.contentscroll {
|
||||
padding: 20px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
<template>
|
||||
<footer>
|
||||
<div class="logo" @click="showFalukantDaemonStatus"><img src="/images/icons/logo_color.png"></div>
|
||||
<div class="window-bar">
|
||||
<footer class="app-footer">
|
||||
<div class="app-footer__inner">
|
||||
<button class="footer-brand" type="button" @click="showFalukantDaemonStatus">
|
||||
<img src="/images/icons/logo_color.png" alt="YourPart" />
|
||||
<span>System</span>
|
||||
</button>
|
||||
<div class="window-bar">
|
||||
<button v-for="dialog in openDialogs" :key="dialog.dialog.name" class="dialog-button"
|
||||
@click="toggleDialogMinimize(dialog.dialog.name)" :title="dialog.dialog.localTitle">
|
||||
<img v-if="dialog.dialog.icon" :src="'/images/icons/' + dialog.dialog.icon" />
|
||||
<span class="button-text">{{ dialog.dialog.isTitleTranslated ? $t(dialog.dialog.localTitle) :
|
||||
dialog.dialog.localTitle }}</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="static-block">
|
||||
<a href="#" @click.prevent="openImprintDialog">{{ $t('imprint.button') }}</a>
|
||||
<a href="#" @click.prevent="openDataPrivacyDialog">{{ $t('dataPrivacy.button') }}</a>
|
||||
<a href="#" @click.prevent="openContactDialog">{{ $t('contact.button') }}</a>
|
||||
</div>
|
||||
<div class="static-block">
|
||||
<a href="#" @click.prevent="openImprintDialog">{{ $t('imprint.button') }}</a>
|
||||
<a href="#" @click.prevent="openDataPrivacyDialog">{{ $t('dataPrivacy.button') }}</a>
|
||||
<a href="#" @click.prevent="openContactDialog">{{ $t('contact.button') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
@@ -63,18 +68,47 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
footer {
|
||||
display: flex;
|
||||
background-color: var(--color-primary-green);
|
||||
height: 38px;
|
||||
width: 100%;
|
||||
color: #1F2937; /* Dunkles Grau für besseren Kontrast auf hellem Grün */
|
||||
.app-footer {
|
||||
flex: 0 0 auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.logo,
|
||||
.window-bar,
|
||||
.static-block {
|
||||
text-align: center;
|
||||
.app-footer__inner {
|
||||
max-width: var(--shell-max-width);
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
min-height: 44px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 0;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(242, 248, 243, 0.96) 0%, rgba(224, 238, 227, 0.98) 100%);
|
||||
border-top: 1px solid rgba(120, 195, 138, 0.28);
|
||||
box-shadow: 0 -6px 18px rgba(93, 64, 55, 0.06);
|
||||
}
|
||||
|
||||
.footer-brand {
|
||||
min-height: 32px;
|
||||
padding: 0 10px 0 8px;
|
||||
background: rgba(120, 195, 138, 0.12);
|
||||
border: 1px solid rgba(120, 195, 138, 0.22);
|
||||
color: #24523a;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.footer-brand:hover {
|
||||
background: rgba(120, 195, 138, 0.18);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.footer-brand img {
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.footer-brand span {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.window-bar {
|
||||
@@ -83,24 +117,25 @@ footer {
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
gap: 10px;
|
||||
padding-left: 10px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.dialog-button {
|
||||
max-width: 12em;
|
||||
max-width: 15em;
|
||||
min-height: 30px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
padding: 5px 10px;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
background: none;
|
||||
height: 1.8em;
|
||||
border: 1px solid #0a4337;
|
||||
box-shadow: 1px 1px 2px #484949;
|
||||
padding: 0 12px;
|
||||
border-radius: 999px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
color: var(--color-text-primary);
|
||||
border: 1px solid rgba(120, 195, 138, 0.18);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.dialog-button:hover {
|
||||
background: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
|
||||
.dialog-button>img {
|
||||
@@ -111,16 +146,35 @@ footer {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.logo>img {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.static-block {
|
||||
line-height: 38px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.static-block>a {
|
||||
padding-right: 1.5em;
|
||||
color: #42634e;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
|
||||
.static-block > a:hover {
|
||||
color: #24523a;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.app-footer__inner {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.window-bar,
|
||||
.static-block {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.static-block {
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,15 +1,25 @@
|
||||
<template>
|
||||
<header>
|
||||
<div class="logo"><img src="/images/logos/logo.png" /></div>
|
||||
<div class="advertisement">Advertisement</div>
|
||||
<div class="connection-status" v-if="isLoggedIn">
|
||||
<div class="status-indicator" :class="backendStatusClass">
|
||||
<span class="status-dot"></span>
|
||||
<span class="status-text">B</span>
|
||||
<header class="app-header">
|
||||
<div class="app-header__inner">
|
||||
<div class="brand">
|
||||
<div class="logo"><img src="/images/logos/logo.png" alt="YourPart" /></div>
|
||||
<div class="brand-copy">
|
||||
<strong>YourPart</strong>
|
||||
<span>Community, Spiele und Lernen auf einer Plattform</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="status-indicator" :class="daemonStatusClass">
|
||||
<span class="status-dot"></span>
|
||||
<span class="status-text">D</span>
|
||||
<div class="header-meta">
|
||||
<div class="header-pill">Beta</div>
|
||||
<div class="connection-status" v-if="isLoggedIn">
|
||||
<div class="status-indicator" :class="backendStatusClass">
|
||||
<span class="status-dot"></span>
|
||||
<span class="status-text">Backend</span>
|
||||
</div>
|
||||
<div class="status-indicator" :class="daemonStatusClass">
|
||||
<span class="status-dot"></span>
|
||||
<span class="status-text">Daemon</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
@@ -43,43 +53,107 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
header {
|
||||
.app-header {
|
||||
position: relative;
|
||||
flex: 0 0 auto;
|
||||
padding: 8px 14px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(255, 248, 236, 0.94) 0%, rgba(247, 235, 216, 0.98) 100%);
|
||||
color: #2b1f14;
|
||||
border-bottom: 1px solid rgba(93, 64, 55, 0.12);
|
||||
box-shadow: 0 6px 18px rgba(93, 64, 55, 0.08);
|
||||
}
|
||||
|
||||
.app-header__inner {
|
||||
max-width: var(--shell-max-width);
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 10px;
|
||||
background-color: #f8a22b;
|
||||
gap: 16px;
|
||||
}
|
||||
.logo, .title, .advertisement {
|
||||
text-align: center;
|
||||
|
||||
.brand {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
.advertisement {
|
||||
flex: 1;
|
||||
|
||||
.logo {
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
padding: 6px;
|
||||
border-radius: 14px;
|
||||
background:
|
||||
linear-gradient(180deg, rgba(248, 162, 43, 0.2) 0%, rgba(255, 255, 255, 0.7) 100%);
|
||||
border: 1px solid rgba(248, 162, 43, 0.22);
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.55);
|
||||
}
|
||||
|
||||
.logo > img {
|
||||
max-height: 50px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.brand-copy {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
}
|
||||
|
||||
.brand-copy strong {
|
||||
font-size: 1.05rem;
|
||||
line-height: 1.1;
|
||||
color: #3a2a1b;
|
||||
}
|
||||
|
||||
.brand-copy span {
|
||||
font-size: 0.8rem;
|
||||
color: rgba(95, 75, 57, 0.88);
|
||||
}
|
||||
|
||||
.header-meta {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.header-pill {
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
background: rgba(248, 162, 43, 0.12);
|
||||
border: 1px solid rgba(248, 162, 43, 0.24);
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: #8a5411;
|
||||
}
|
||||
|
||||
.connection-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-left: 10px;
|
||||
gap: 5px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 2px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 6pt;
|
||||
font-weight: 500;
|
||||
gap: 8px;
|
||||
min-height: 28px;
|
||||
padding: 0 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 700;
|
||||
border: 1px solid rgba(93, 64, 55, 0.1);
|
||||
background: rgba(255, 255, 255, 0.62);
|
||||
}
|
||||
|
||||
.status-dot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 4px;
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@@ -100,23 +174,23 @@ header {
|
||||
}
|
||||
|
||||
.status-connected {
|
||||
background-color: rgba(76, 175, 80, 0.1);
|
||||
color: #2e7d32;
|
||||
background-color: rgba(76, 175, 80, 0.12);
|
||||
color: #245b2c;
|
||||
}
|
||||
|
||||
.status-connecting {
|
||||
background-color: rgba(255, 152, 0, 0.1);
|
||||
color: #f57c00;
|
||||
background-color: rgba(255, 152, 0, 0.12);
|
||||
color: #8b5e0d;
|
||||
}
|
||||
|
||||
.status-disconnected {
|
||||
background-color: rgba(244, 67, 54, 0.1);
|
||||
color: #d32f2f;
|
||||
background-color: rgba(244, 67, 54, 0.12);
|
||||
color: #8f2c27;
|
||||
}
|
||||
|
||||
.status-error {
|
||||
background-color: rgba(244, 67, 54, 0.1);
|
||||
color: #d32f2f;
|
||||
background-color: rgba(244, 67, 54, 0.12);
|
||||
color: #8f2c27;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
@@ -124,4 +198,24 @@ header {
|
||||
50% { opacity: 0.5; }
|
||||
100% { opacity: 1; }
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
.app-header {
|
||||
padding: 6px 10px;
|
||||
}
|
||||
|
||||
.app-header__inner {
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.header-meta {
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.brand-copy span {
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
> </span>
|
||||
<span>{{ $t(`navigation.${key}`) }}</span>
|
||||
|
||||
<!-- Untermenü Ebene 1 -->
|
||||
<ul v-if="item.children" class="submenu1">
|
||||
<!-- Untermenü Ebene 1 -->
|
||||
<ul v-if="hasTopLevelSubmenu(item)" class="submenu1">
|
||||
<li
|
||||
v-for="(subitem, subkey) in item.children"
|
||||
:key="subkey"
|
||||
@@ -29,7 +29,7 @@
|
||||
> </span>
|
||||
<span>{{ subitem?.label || $t(`navigation.m-${key}.${subkey}`) }}</span>
|
||||
<span
|
||||
v-if="subkey === 'forum' || subkey === 'vocabtrainer' || subitem.children"
|
||||
v-if="hasSecondLevelSubmenu(subitem, subkey)"
|
||||
class="subsubmenu"
|
||||
>▶</span>
|
||||
|
||||
@@ -183,6 +183,34 @@ export default {
|
||||
methods: {
|
||||
...mapActions(['loadMenu', 'logout']),
|
||||
|
||||
hasChildren(item) {
|
||||
if (!item?.children) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Array.isArray(item.children)) {
|
||||
return item.children.length > 0;
|
||||
}
|
||||
|
||||
return Object.keys(item.children).length > 0;
|
||||
},
|
||||
|
||||
hasTopLevelSubmenu(item) {
|
||||
return this.hasChildren(item) || (item?.showLoggedinFriends === 1 && this.friendsList.length > 0);
|
||||
},
|
||||
|
||||
hasSecondLevelSubmenu(subitem, subkey) {
|
||||
if (subkey === 'forum') {
|
||||
return this.forumList.length > 0;
|
||||
}
|
||||
|
||||
if (subkey === 'vocabtrainer') {
|
||||
return this.vocabLanguagesList.length > 0;
|
||||
}
|
||||
|
||||
return this.hasChildren(subitem);
|
||||
},
|
||||
|
||||
openMultiChat() {
|
||||
// Räume können später dynamisch geladen werden, hier als Platzhalter ein Beispiel:
|
||||
const exampleRooms = [
|
||||
@@ -254,7 +282,7 @@ export default {
|
||||
event.stopPropagation();
|
||||
|
||||
// 1) nur aufklappen, wenn es echte Untermenüs gibt (nicht bei leerem children wie bei Startseite)
|
||||
if (item.children && Object.keys(item.children).length > 0) return;
|
||||
if (this.hasChildren(item)) return;
|
||||
|
||||
// 2) view → Dialog/Window
|
||||
if (item.view) {
|
||||
@@ -295,8 +323,6 @@ nav,
|
||||
nav > ul {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background-color: #f8a22b;
|
||||
color: #000;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
cursor: pointer;
|
||||
@@ -304,6 +330,28 @@ nav > ul {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
nav {
|
||||
max-width: var(--shell-max-width);
|
||||
margin: 0 auto;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
padding: 6px 12px;
|
||||
border-radius: 0;
|
||||
background: var(--color-primary-orange-light);
|
||||
border-top: 1px solid rgba(93, 64, 55, 0.08);
|
||||
border-bottom: 1px solid rgba(93, 64, 55, 0.12);
|
||||
box-shadow: none;
|
||||
color: var(--color-text-primary);
|
||||
}
|
||||
|
||||
nav > ul {
|
||||
flex: 1;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: transparent;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
@@ -311,18 +359,23 @@ ul {
|
||||
}
|
||||
|
||||
nav > ul > li {
|
||||
padding: 0 1em;
|
||||
line-height: 2.5em;
|
||||
transition: background-color 0.25s;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
min-height: 36px;
|
||||
padding: 0 12px;
|
||||
line-height: 1;
|
||||
border-radius: 999px;
|
||||
transition: background-color 0.25s, color 0.25s, transform 0.2s;
|
||||
}
|
||||
|
||||
nav > ul > li:hover {
|
||||
background-color: #f8a22b;
|
||||
background-color: rgba(248, 162, 43, 0.16);
|
||||
white-space: nowrap;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
nav > ul > li:hover > span {
|
||||
color: #000;
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
nav > ul > li:hover > ul {
|
||||
@@ -335,17 +388,22 @@ a {
|
||||
|
||||
.right-block {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.logoutblock {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.menuitem {
|
||||
cursor: pointer;
|
||||
color: #5D4037;
|
||||
color: var(--color-primary);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.mailbox {
|
||||
@@ -353,20 +411,29 @@ a {
|
||||
background-size: contain;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
padding-left: 24px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
text-align: left;
|
||||
border-radius: 999px;
|
||||
background-color: rgba(120, 195, 138, 0.12);
|
||||
border: 1px solid rgba(93, 64, 55, 0.1);
|
||||
}
|
||||
|
||||
.mainmenuitem {
|
||||
position: relative;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.submenu1 {
|
||||
position: absolute;
|
||||
border: 1px solid #5D4037;
|
||||
background-color: #f8a22b;
|
||||
border: 1px solid rgba(93, 64, 55, 0.12);
|
||||
background: rgba(255, 252, 247, 0.98);
|
||||
left: 0;
|
||||
top: 2.5em;
|
||||
top: calc(100% + 10px);
|
||||
min-width: 220px;
|
||||
padding: 8px;
|
||||
border-radius: 18px;
|
||||
box-shadow: 0 10px 18px rgba(93, 64, 55, 0.12);
|
||||
max-height: 0;
|
||||
overflow: visible;
|
||||
opacity: 0;
|
||||
@@ -386,15 +453,16 @@ a {
|
||||
}
|
||||
|
||||
.submenu1 > li {
|
||||
padding: 0.5em;
|
||||
padding: 0.75em 0.9em;
|
||||
line-height: 1em;
|
||||
color: #5D4037;
|
||||
color: var(--color-text-secondary);
|
||||
position: relative;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.submenu1 > li:hover {
|
||||
color: #000;
|
||||
background-color: #f8a22b;
|
||||
color: var(--color-text-primary);
|
||||
background-color: rgba(248, 162, 43, 0.12);
|
||||
}
|
||||
|
||||
.menu-icon,
|
||||
@@ -407,7 +475,7 @@ a {
|
||||
.menu-icon {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
margin-right: 3px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.submenu-icon {
|
||||
@@ -419,10 +487,14 @@ a {
|
||||
|
||||
.submenu2 {
|
||||
position: absolute;
|
||||
background-color: #f8a22b;
|
||||
left: 100%;
|
||||
background: rgba(255, 252, 247, 0.98);
|
||||
left: calc(100% + 8px);
|
||||
top: 0;
|
||||
border: 1px solid #5D4037;
|
||||
min-width: 220px;
|
||||
padding: 8px;
|
||||
border-radius: 18px;
|
||||
border: 1px solid rgba(71, 52, 35, 0.12);
|
||||
box-shadow: 0 10px 18px rgba(93, 64, 55, 0.12);
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
opacity: 0;
|
||||
@@ -442,14 +514,15 @@ a {
|
||||
}
|
||||
|
||||
.submenu2 > li {
|
||||
padding: 0.5em;
|
||||
padding: 0.75em 0.9em;
|
||||
line-height: 1em;
|
||||
color: #5D4037;
|
||||
color: var(--color-text-secondary);
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.submenu2 > li:hover {
|
||||
color: #000;
|
||||
background-color: #f8a22b;
|
||||
color: var(--color-text-primary);
|
||||
background-color: rgba(120, 195, 138, 0.14);
|
||||
}
|
||||
|
||||
.subsubmenu {
|
||||
@@ -457,4 +530,37 @@ a {
|
||||
font-size: 8pt;
|
||||
margin-right: -4px;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
@media (max-width: 960px) {
|
||||
nav {
|
||||
margin: 0;
|
||||
flex-direction: column;
|
||||
padding: 8px 10px;
|
||||
}
|
||||
|
||||
nav > ul,
|
||||
.right-block {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.right-block {
|
||||
justify-content: space-between;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.logoutblock {
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.submenu1,
|
||||
.submenu2 {
|
||||
position: static;
|
||||
min-width: 100%;
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,13 @@
|
||||
<template>
|
||||
<div ref="container" class="character-3d-container"></div>
|
||||
<div class="character-3d-shell">
|
||||
<div v-show="!showFallback" ref="container" class="character-3d-container"></div>
|
||||
<img
|
||||
v-if="showFallback"
|
||||
class="character-fallback"
|
||||
:src="fallbackImageSrc"
|
||||
:alt="`Character ${actualGender}`"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@@ -41,7 +49,8 @@ export default {
|
||||
animationId: null,
|
||||
mixer: null,
|
||||
clock: markRaw(new THREE.Clock()),
|
||||
baseYPosition: 0 // Basis-Y-Position für Animation
|
||||
baseYPosition: 0,
|
||||
showFallback: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@@ -93,6 +102,11 @@ export default {
|
||||
const base = getApiBaseURL();
|
||||
const prefix = base ? `${base}${MODELS_API_PATH}` : MODELS_API_PATH;
|
||||
return `${prefix}/${this.actualGender}_${age}y.glb`;
|
||||
},
|
||||
fallbackImageSrc() {
|
||||
return this.actualGender === 'female'
|
||||
? '/images/mascot/mascot_female.png'
|
||||
: '/images/mascot/mascot_male.png';
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
@@ -115,6 +129,7 @@ export default {
|
||||
init3D() {
|
||||
const container = this.$refs.container;
|
||||
if (!container) return;
|
||||
this.showFallback = false;
|
||||
|
||||
// Scene erstellen - markRaw verwenden, um Vue's Reactivity zu vermeiden
|
||||
this.scene = markRaw(new THREE.Scene());
|
||||
@@ -301,6 +316,7 @@ export default {
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading 3D model:', error);
|
||||
this.showFallback = true;
|
||||
}
|
||||
},
|
||||
|
||||
@@ -375,10 +391,25 @@ export default {
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.character-3d-shell {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.character-3d-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.character-fallback {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: contain;
|
||||
object-position: center bottom;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -23,11 +23,19 @@
|
||||
<img :src="'/images/icons/falukant/relationship-' + item.image + '.png'" class="relationship-icon" :title="$t(`falukant.statusbar.${item.key}`)" />
|
||||
</div>
|
||||
</template>
|
||||
<span v-if="statusItems.length > 0 && menu.falukant && menu.falukant.children">
|
||||
<div
|
||||
v-if="statusItems.length > 0 && menu.falukant && menu.falukant.children"
|
||||
class="quick-access"
|
||||
>
|
||||
<template v-for="(menuItem, key) in menu.falukant.children" :key="menuItem.id" >
|
||||
<img :src="'/images/icons/falukant/shortmap/' + key + '.png'" class="menu-icon" @click="openPage(menuItem)" :title="$t(`navigation.m-falukant.${key}`)" />
|
||||
<img
|
||||
:src="'/images/icons/falukant/shortmap/' + key + '.png'"
|
||||
class="menu-icon"
|
||||
@click="openPage(menuItem)"
|
||||
:title="$t(`navigation.m-falukant.${key}`)"
|
||||
/>
|
||||
</template>
|
||||
</span>
|
||||
</div>
|
||||
<MessagesDialog ref="msgs" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -220,13 +228,18 @@ export default {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
background-color: #f4f4f4;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
width: calc(100% + 40px);
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
gap: 1.2em;
|
||||
margin: -21px -20px 1.5em -20px;
|
||||
position: fixed;
|
||||
padding: 0.4rem 0.75rem;
|
||||
margin: 0 0 1.5em 0;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
@@ -237,6 +250,14 @@ export default {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.quick-access {
|
||||
display: inline-flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 0.2rem;
|
||||
}
|
||||
|
||||
.status-icon-wrapper {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
@@ -254,6 +275,8 @@ export default {
|
||||
.menu-icon {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: block;
|
||||
flex: 0 0 auto;
|
||||
cursor: pointer;
|
||||
padding: 4px 2px 0 0;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user