253 lines
9.1 KiB
Vue
253 lines
9.1 KiB
Vue
<template>
|
|
<div class="statusbar">
|
|
<div class="status-item messages" @click="openMessages" :title="$t('falukant.messages.tooltip')">
|
|
<span class="badge" v-if="unreadCount > 0">{{ unreadCount }}</span>
|
|
<img src="/images/icons/falukant/messages24.png" class="menu-icon" />
|
|
</div>
|
|
<template v-for="item in statusItems" :key="item.key">
|
|
<div class="status-item" v-if="item.value !== null && item.image == null">
|
|
<span class="status-icon-wrapper">
|
|
<template v-if="item.iconImage">
|
|
<img :src="'/images/icons/' + item.iconImage" class="inline-icon" width="16" height="16" :title="String(item.value)" />
|
|
</template>
|
|
<template v-else>
|
|
<span class="status-icon-symbol" :title="String(item.value)">{{ item.icon }}</span>
|
|
</template>
|
|
</span>
|
|
<span class="status-label" :title="$t(`falukant.statusbar.${item.key}`)">{{ item.value }}</span>
|
|
</div>
|
|
<div class="status-item" v-else-if="item.image !== null">
|
|
<span class="status-icon-wrapper">
|
|
<span class="status-icon-symbol" :title="$t(`falukant.statusbar.${item.key}`)">{{ item.icon }}</span>
|
|
</span>
|
|
<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">
|
|
<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}`)" />
|
|
</template>
|
|
</span>
|
|
<MessagesDialog ref="msgs" />
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
import { mapState, mapGetters } from "vuex";
|
|
import apiClient from "@/utils/axios.js";
|
|
import MessagesDialog from './MessagesDialog.vue';
|
|
|
|
export default {
|
|
name: "StatusBar",
|
|
components: { MessagesDialog },
|
|
data() {
|
|
return {
|
|
statusItems: [
|
|
{ key: "age", icon: null, iconImage: 'falukant/age.png', value: 0 },
|
|
{ key: "relationship", icon: "💑", image: null },
|
|
{ key: "wealth", icon: "💰", value: 0 },
|
|
{ key: "health", icon: "❤️", value: "Good" },
|
|
{ key: "events", icon: "📰", value: null, image: null },
|
|
{ key: "children", icon: "👶", value: null },
|
|
],
|
|
unreadCount: 0,
|
|
};
|
|
},
|
|
computed: {
|
|
...mapState(["socket", "daemonSocket"]),
|
|
...mapGetters(['menu']),
|
|
},
|
|
watch: {
|
|
// Wenn sich das Menü ändert, lade die Bilder neu
|
|
'menu.falukant': {
|
|
handler() {
|
|
this.preloadQuickAccessImages();
|
|
},
|
|
deep: true
|
|
}
|
|
},
|
|
async mounted() {
|
|
await this.fetchStatus();
|
|
|
|
// Bilder für Schnellzugriff vorladen und cachen
|
|
this.preloadQuickAccessImages();
|
|
|
|
// Live-Socket-Events
|
|
["falukantUpdateStatus", "stock_change", "familychanged"].forEach(eventName => {
|
|
if (this.daemonSocket) {
|
|
this.daemonSocket.addEventListener('message', (event) => {
|
|
try {
|
|
const data = JSON.parse(event.data);
|
|
if (data.event === eventName) {
|
|
this.handleEvent(data);
|
|
}
|
|
} catch (error) {
|
|
// Ignore non-JSON messages like ping/pong
|
|
}
|
|
});
|
|
}
|
|
});
|
|
},
|
|
beforeUnmount() {
|
|
// Daemon WebSocket wird automatisch beim Logout geschlossen
|
|
},
|
|
methods: {
|
|
preloadQuickAccessImages() {
|
|
// Lade alle Schnellzugriffs-Bilder vor, damit sie gecacht werden
|
|
if (this.menu.falukant && this.menu.falukant.children) {
|
|
Object.keys(this.menu.falukant.children).forEach(key => {
|
|
const img = new Image();
|
|
img.src = `/images/icons/falukant/shortmap/${key}.png`;
|
|
});
|
|
}
|
|
|
|
// Lade auch andere häufig verwendete Bilder vor
|
|
const commonImages = [
|
|
'/images/icons/falukant/messages24.png',
|
|
'/images/icons/falukant/age.png',
|
|
// Relationship-Bilder vorladen
|
|
'/images/icons/falukant/relationship-engaged.png',
|
|
'/images/icons/falukant/relationship-wooing.png',
|
|
'/images/icons/falukant/relationship-married.png',
|
|
'/images/icons/falukant/relationship-widow.png',
|
|
'/images/icons/falukant/relationship-.png'
|
|
];
|
|
|
|
commonImages.forEach(src => {
|
|
const img = new Image();
|
|
img.src = src;
|
|
});
|
|
},
|
|
async fetchStatus() {
|
|
try {
|
|
const response = await apiClient.get("/api/falukant/info");
|
|
const { money, character, events } = response.data;
|
|
const { age, health } = character;
|
|
const relationship = response.data.character.relationshipsAsCharacter1[0]?.relationshipType?.tr
|
|
|| response.data.character.relationshipsAsCharacter2[0]?.relationshipType?.tr
|
|
|| null;
|
|
// Children counts and unread notifications are now part of /info
|
|
const childCount = Number(response.data.childrenCount) || 0;
|
|
const unbaptisedCount = Number(response.data.unbaptisedChildrenCount) || 0;
|
|
this.unreadCount = Number(response.data.unreadNotifications) || 0;
|
|
const childrenDisplay = `${childCount}${unbaptisedCount > 0 ? `(${unbaptisedCount})` : ''}`;
|
|
let healthStatus = '';
|
|
if (health > 90) {
|
|
healthStatus = this.$t("falukant.health.amazing");
|
|
} else if (health > 75) {
|
|
healthStatus = this.$t("falukant.health.good");
|
|
} else if (health > 50) {
|
|
healthStatus = this.$t("falukant.health.normal");
|
|
} else if (health > 25) {
|
|
healthStatus = this.$t("falukant.health.bad");
|
|
} else {
|
|
healthStatus = this.$t("falukant.health.very_bad");
|
|
}
|
|
this.statusItems = [
|
|
{ key: "age", icon: null, iconImage: 'falukant/age.png', value: age },
|
|
{ key: "relationship", icon: "💑", image: relationship },
|
|
{ key: "wealth", icon: "💰", value: Intl.NumberFormat(navigator.language, { minimumFractionDigits: 2, maximumFractionDigits: 2 }).format(money) },
|
|
{ key: "health", icon: "❤️", value: healthStatus },
|
|
{ key: "events", icon: "📰", value: events || null, image: null },
|
|
{ key: "children", icon: "👶", value: childrenDisplay },
|
|
];
|
|
} catch (error) {
|
|
// Error fetching status
|
|
}
|
|
},
|
|
handleEvent(eventData) {
|
|
switch (eventData.event) {
|
|
case 'falukantUpdateStatus':
|
|
case 'stock_change':
|
|
case 'familychanged':
|
|
this.fetchStatus();
|
|
break;
|
|
}
|
|
},
|
|
openPage(url, hasSubmenu = false) {
|
|
if (hasSubmenu) {
|
|
return;
|
|
}
|
|
if (url) {
|
|
this.$router.push(url);
|
|
}
|
|
},
|
|
openMessages() {
|
|
this.$refs.msgs.open();
|
|
// After opening, refresh unread count after a short delay (server marks them shown)
|
|
setTimeout(() => this.fetchStatus(), 500);
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.statusbar {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
background-color: #f4f4f4;
|
|
border: 1px solid #ccc;
|
|
border-radius: 4px;
|
|
width: calc(100% + 40px);
|
|
gap: 1.2em;
|
|
margin: -21px -20px 1.5em -20px;
|
|
position: fixed;
|
|
}
|
|
|
|
.status-item {
|
|
text-align: center;
|
|
cursor: pointer;
|
|
display: inline-flex;
|
|
align-items: center;
|
|
}
|
|
|
|
.status-icon-wrapper {
|
|
display: inline-flex;
|
|
align-items: center;
|
|
margin-right: 4px;
|
|
}
|
|
|
|
.status-icon-symbol {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.status-label {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.menu-icon {
|
|
width: 30px;
|
|
height: 30px;
|
|
cursor: pointer;
|
|
padding: 4px 2px 0 0;
|
|
}
|
|
|
|
.relationship-icon {
|
|
max-width: 24px;
|
|
max-height: 24px;
|
|
}
|
|
|
|
.messages { position: relative; }
|
|
.badge {
|
|
position: absolute;
|
|
top: -6px;
|
|
right: -2px;
|
|
background: #e53935;
|
|
color: #fff;
|
|
border-radius: 10px;
|
|
padding: 0 6px;
|
|
font-size: 12px;
|
|
line-height: 18px;
|
|
min-width: 18px;
|
|
text-align: center;
|
|
}
|
|
|
|
.inline-icon {
|
|
width: 16px;
|
|
height: 16px;
|
|
vertical-align: middle;
|
|
margin-right: 4px;
|
|
}
|
|
</style>
|