Spiel erweitert
This commit is contained in:
258
frontend/src/views/falukant/DirectorView.vue
Normal file
258
frontend/src/views/falukant/DirectorView.vue
Normal file
@@ -0,0 +1,258 @@
|
||||
<template>
|
||||
<div class="director-view">
|
||||
<StatusBar />
|
||||
<div class="content-container">
|
||||
<!-- Left: Director list -->
|
||||
<div class="list-panel">
|
||||
<h2>{{ $t('falukant.director.title') }}</h2>
|
||||
<table class="director-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.director.name') }}</th>
|
||||
<th>{{ $t('falukant.director.branch') }}</th>
|
||||
<th>{{ $t('falukant.director.age') }}</th>
|
||||
<th>{{ $t('falukant.director.satisfaction') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="dir in directors" :key="dir.id" @click="selectDirector(dir)"
|
||||
:class="{ selected: dir.id === selected?.id }" class="director-row">
|
||||
<td>
|
||||
{{ $t(`falukant.titles.${dir.character.gender}.${dir.character.nobleTitle.labelTr}`) }}
|
||||
{{ dir.character.definedFirstName.name }} {{ dir.character.definedLastName.name }}
|
||||
</td>
|
||||
<td>{{ dir.region || '-' }}</td>
|
||||
<td>{{ dir.age }}</td>
|
||||
<td>{{ dir.satisfaction }} %</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<!-- Right: Selected director detail -->
|
||||
<div class="detail-panel" v-if="selected">
|
||||
<h2>
|
||||
{{ $t(`falukant.titles.${selected.character.gender}.${selected.character.nobleTitle.labelTr}`) }}
|
||||
{{ selected.character.definedFirstName.name }} {{ selected.character.definedLastName.name }}
|
||||
</h2>
|
||||
<p>{{ $t('falukant.director.age') }}: {{ selected.age }}</p>
|
||||
<h3>{{ $t('falukant.director.knowledge.title') }}</h3>
|
||||
<div class="table-container">
|
||||
<table class="knowledge-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ $t('falukant.director.product') }}</th>
|
||||
<th>{{ $t('falukant.director.knowledge.knowledge') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in selected.character.knowledges" :key="item.productId">
|
||||
<td>{{ $t(`falukant.product.${item.productType.labelTr}`) }}</td>
|
||||
<td>{{ item.knowledge }} %</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div>
|
||||
<label>
|
||||
{{ $t('falukant.director.satisfaction') }}:
|
||||
<span> {{ selected.satisfaction }} %</span>
|
||||
</label>
|
||||
</div>
|
||||
<div>
|
||||
<label>
|
||||
{{ $t('falukant.director.income') }}:
|
||||
<input type="text" v-model="selected.income" />
|
||||
</label>
|
||||
<span v-if="selected.satisfaction < 100" @click="setWishedIncome" class="link">({{ $t('falukant.director.wishedIncome') }}: {{ selected.wishedIncome }})</span>
|
||||
</div>
|
||||
<div>
|
||||
<button @click="updateDirector">{{ $t('falukant.director.updateButton') }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import StatusBar from '@/components/falukant/StatusBar.vue';
|
||||
import apiClient from '@/utils/axios.js';
|
||||
import { mapState } from 'vuex';
|
||||
|
||||
export default {
|
||||
name: 'DirectorView',
|
||||
components: { StatusBar },
|
||||
data() {
|
||||
return {
|
||||
directors: [],
|
||||
selected: null,
|
||||
editIncome: '',
|
||||
editSatisfaction: 0
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapState(['daemonSocket'])
|
||||
},
|
||||
async mounted() {
|
||||
await this.loadDirectors();
|
||||
if (this.daemonSocket) {
|
||||
this.daemonSocket.addEventListener('message', this.handleDaemonMessage);
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
if (this.daemonSocket) {
|
||||
this.daemonSocket.removeEventListener('message', this.handleDaemonMessage);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async loadDirectors() {
|
||||
try {
|
||||
const { data } = await apiClient.get('/api/falukant/directors');
|
||||
this.directors = data.map(d => ({
|
||||
...d,
|
||||
branchName: d.branch?.regionName || null
|
||||
}));
|
||||
} catch (err) {
|
||||
console.error('Error loading directors', err);
|
||||
}
|
||||
},
|
||||
selectDirector(dir) {
|
||||
this.selected = { ...dir };
|
||||
this.editIncome = dir.income;
|
||||
this.editSatisfaction = dir.satisfaction;
|
||||
},
|
||||
async updateDirector() {
|
||||
try {
|
||||
await apiClient.post('/api/falukant/directors', {
|
||||
directorId: this.selected.id,
|
||||
income: this.selected.income,
|
||||
});
|
||||
await this.loadDirectors();
|
||||
this.selected = this.directors.find(d => d.id === this.selected.id);
|
||||
} catch (err) {
|
||||
console.error('Error updating director', err);
|
||||
}
|
||||
},
|
||||
handleDaemonMessage(evt) {
|
||||
try {
|
||||
if (evt.data === 'ping') {
|
||||
return;
|
||||
}
|
||||
const msg = JSON.parse(evt.data);
|
||||
if (msg.event === 'directorchanged') {
|
||||
this.loadDirectors();
|
||||
if (this.selected) {
|
||||
const updated = this.directors.find(d => d.id === this.selected.id);
|
||||
if (updated) {
|
||||
this.selected = { ...updated };
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Error parsing daemon message', err, evt.data);
|
||||
}
|
||||
},
|
||||
|
||||
setWishedIncome() {
|
||||
this.selected.income = this.selected.wishedIncome;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.director-view .content-container {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.list-panel {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.detail-panel {
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.director-table,
|
||||
.knowledge-table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.director-table th,
|
||||
.director-table td,
|
||||
.knowledge-table th,
|
||||
.knowledge-table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.selected {
|
||||
background-color: #f0f8ff;
|
||||
}
|
||||
|
||||
.actions {
|
||||
margin-top: 10px;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.actions label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 6px 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
h2 {
|
||||
padding-top: 20px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
max-height: 50vh;
|
||||
/* maximal 50% der Viewport-Höhe */
|
||||
min-height: 5em;
|
||||
/* mindestens 5em hoch */
|
||||
overflow-y: auto;
|
||||
/* nur vertikales Scrollen */
|
||||
}
|
||||
|
||||
.knowledge-table {
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
/* Spalten fest verteilen */
|
||||
}
|
||||
|
||||
/* Header-Zellen kleben oben im scrollenden Container */
|
||||
.knowledge-table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
background: white;
|
||||
/* Hintergrund, damit darunterliegender Inhalt nicht durchscheint */
|
||||
z-index: 1;
|
||||
/* sicherstellen, dass der Header immer oben liegt */
|
||||
padding: 0.5em;
|
||||
border: 1px solid #ccc;
|
||||
}
|
||||
|
||||
/* Zellen-Styles für Körper und Kopf */
|
||||
.knowledge-table th,
|
||||
.knowledge-table td {
|
||||
padding: 0.5em;
|
||||
border: 1px solid #ccc;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.director-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user