We can add weapons and equipment
This commit is contained in:
@@ -4,6 +4,7 @@ import (
|
|||||||
"bamort/character"
|
"bamort/character"
|
||||||
"bamort/config"
|
"bamort/config"
|
||||||
"bamort/database"
|
"bamort/database"
|
||||||
|
"bamort/equipment"
|
||||||
"bamort/gsmaster"
|
"bamort/gsmaster"
|
||||||
"bamort/importer"
|
"bamort/importer"
|
||||||
"bamort/logger"
|
"bamort/logger"
|
||||||
@@ -73,6 +74,7 @@ func main() {
|
|||||||
// Register your module routes
|
// Register your module routes
|
||||||
gsmaster.RegisterRoutes(protected)
|
gsmaster.RegisterRoutes(protected)
|
||||||
character.RegisterRoutes(protected)
|
character.RegisterRoutes(protected)
|
||||||
|
equipment.RegisterRoutes(protected)
|
||||||
maintenance.RegisterRoutes(protected)
|
maintenance.RegisterRoutes(protected)
|
||||||
importer.RegisterRoutes(protected)
|
importer.RegisterRoutes(protected)
|
||||||
pdfrender.RegisterRoutes(protected)
|
pdfrender.RegisterRoutes(protected)
|
||||||
|
|||||||
@@ -77,3 +77,66 @@ func DeleteAusruestung(c *gin.Context) {
|
|||||||
|
|
||||||
c.JSON(http.StatusOK, gin.H{"message": "Ausruestung deleted successfully"})
|
c.JSON(http.StatusOK, gin.H{"message": "Ausruestung deleted successfully"})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Endpoints for Managing Weapons (Waffen)
|
||||||
|
*/
|
||||||
|
|
||||||
|
func CreateWaffe(c *gin.Context) {
|
||||||
|
var waffe models.EqWaffe
|
||||||
|
if err := c.ShouldBindJSON(&waffe); err != nil {
|
||||||
|
respondWithError(c, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.DB.Create(&waffe).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to create Waffe")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusCreated, waffe)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListWaffen(c *gin.Context) {
|
||||||
|
characterID := c.Param("character_id")
|
||||||
|
|
||||||
|
var waffen []models.EqWaffe
|
||||||
|
if err := database.DB.Where("character_id = ?", characterID).Find(&waffen).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to retrieve Waffen")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, waffen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateWaffe(c *gin.Context) {
|
||||||
|
waffeID := c.Param("waffe_id")
|
||||||
|
var waffe models.EqWaffe
|
||||||
|
|
||||||
|
if err := database.DB.First(&waffe, waffeID).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusNotFound, "Waffe not found")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := c.ShouldBindJSON(&waffe); err != nil {
|
||||||
|
respondWithError(c, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.DB.Save(&waffe).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to update Waffe")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, waffe)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteWaffe(c *gin.Context) {
|
||||||
|
waffeID := c.Param("waffe_id")
|
||||||
|
if err := database.DB.Delete(&models.EqWaffe{}, waffeID).Error; err != nil {
|
||||||
|
respondWithError(c, http.StatusInternalServerError, "Failed to delete Waffe")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{"message": "Waffe deleted successfully"})
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package equipment
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func RegisterRoutes(r *gin.RouterGroup) {
|
||||||
|
// Equipment (Ausrüstung) routes
|
||||||
|
equipGrp := r.Group("/equipment")
|
||||||
|
equipGrp.POST("", CreateAusruestung)
|
||||||
|
equipGrp.GET("/character/:character_id", ListAusruestung)
|
||||||
|
equipGrp.PUT("/:ausruestung_id", UpdateAusruestung)
|
||||||
|
equipGrp.DELETE("/:ausruestung_id", DeleteAusruestung)
|
||||||
|
|
||||||
|
// Weapon (Waffen) routes
|
||||||
|
weaponGrp := r.Group("/weapons")
|
||||||
|
weaponGrp.POST("", CreateWaffe)
|
||||||
|
weaponGrp.GET("/character/:character_id", ListWaffen)
|
||||||
|
weaponGrp.PUT("/:waffe_id", UpdateWaffe)
|
||||||
|
weaponGrp.DELETE("/:waffe_id", DeleteWaffe)
|
||||||
|
}
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="fullwidth-container">
|
<div class="fullwidth-container">
|
||||||
|
<div class="header-section">
|
||||||
|
<h2>{{ $t('EquipmentView') }}</h2>
|
||||||
|
<button @click="openAddEquipmentDialog" class="btn-add-equipment">
|
||||||
|
{{ $t('equipment.add') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="cd-list">
|
<div class="cd-list">
|
||||||
<table class="cd-table">
|
<table class="cd-table">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -11,11 +18,12 @@
|
|||||||
<th>{{ $t('equipment.amount') }}</th>
|
<th>{{ $t('equipment.amount') }}</th>
|
||||||
<th>{{ $t('equipment.contained_in') }}</th>
|
<th>{{ $t('equipment.contained_in') }}</th>
|
||||||
<th>{{ $t('equipment.bonus') }}</th>
|
<th>{{ $t('equipment.bonus') }}</th>
|
||||||
|
<th>{{ $t('equipment.actions') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template v-for="equipment in character.ausruestung">
|
<template v-if="character.ausruestung && character.ausruestung.length > 0">
|
||||||
<tr>
|
<tr v-for="equipment in character.ausruestung" :key="equipment.id">
|
||||||
<td>{{ equipment.name || '-' }}</td>
|
<td>{{ equipment.name || '-' }}</td>
|
||||||
<td>{{ equipment.beschreibung || '-' }}</td>
|
<td>{{ equipment.beschreibung || '-' }}</td>
|
||||||
<td>{{ equipment.gewicht || '-' }}</td>
|
<td>{{ equipment.gewicht || '-' }}</td>
|
||||||
@@ -23,20 +31,366 @@
|
|||||||
<td>{{ equipment.anzahl || '-' }}</td>
|
<td>{{ equipment.anzahl || '-' }}</td>
|
||||||
<td>{{ equipment.beinhaltet_in || '-' }}</td>
|
<td>{{ equipment.beinhaltet_in || '-' }}</td>
|
||||||
<td>{{ equipment.bonus || '-' }}</td>
|
<td>{{ equipment.bonus || '-' }}</td>
|
||||||
|
<td class="action-cell">
|
||||||
|
<button @click="deleteEquipment(equipment)" class="btn-delete" title="Löschen">
|
||||||
|
🗑️
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<tr>
|
||||||
|
<td colspan="8" class="empty-state">{{ $t('equipment.noEquipment') }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div> <!--- end cd-list-->
|
</div>
|
||||||
</div> <!--- end character -datasheet-->
|
|
||||||
|
<!-- Dialog für Ausrüstung hinzufügen -->
|
||||||
|
<div v-if="showAddDialog" class="modal-overlay" @click.self="closeDialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>{{ $t('equipment.addEquipment') }}</h3>
|
||||||
|
<button @click="closeDialog" class="close-button">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('equipment.search') }}:</label>
|
||||||
|
<input
|
||||||
|
v-model="searchQuery"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('equipment.searchPlaceholder')"
|
||||||
|
@input="filterEquipment"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isLoading" class="loading">{{ $t('equipment.loading') }}</div>
|
||||||
|
|
||||||
|
<div v-else-if="filteredMasterEquipment.length > 0" class="equipment-list">
|
||||||
|
<div
|
||||||
|
v-for="equipment in filteredMasterEquipment"
|
||||||
|
:key="equipment.id"
|
||||||
|
class="equipment-item"
|
||||||
|
:class="{ selected: selectedEquipment && selectedEquipment.id === equipment.id }"
|
||||||
|
@click="selectEquipment(equipment)"
|
||||||
|
>
|
||||||
|
<div class="equipment-name">{{ equipment.name }}</div>
|
||||||
|
<div class="equipment-details">
|
||||||
|
{{ equipment.beschreibung || '-' }} |
|
||||||
|
{{ equipment.weight }}kg |
|
||||||
|
{{ equipment.value || '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="no-results">
|
||||||
|
{{ searchQuery ? $t('equipment.noResults') : $t('equipment.noMasterData') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="selectedEquipment" class="selected-equipment-details">
|
||||||
|
<h4>{{ $t('equipment.selectedEquipment') }}</h4>
|
||||||
|
<p><strong>{{ $t('equipment.name') }}:</strong> {{ selectedEquipment.name }}</p>
|
||||||
|
<p><strong>{{ $t('equipment.description') }}:</strong> {{ selectedEquipment.beschreibung || '-' }}</p>
|
||||||
|
<p><strong>{{ $t('equipment.weight') }}:</strong> {{ selectedEquipment.weight }}kg</p>
|
||||||
|
<p><strong>{{ $t('equipment.value') }}:</strong> {{ selectedEquipment.value || '-' }}</p>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('equipment.amount') }}:</label>
|
||||||
|
<input v-model.number="equipmentAmount" type="number" min="1" value="1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button @click="closeDialog" class="btn-cancel">{{ $t('equipment.cancel') }}</button>
|
||||||
|
<button
|
||||||
|
@click="addEquipment"
|
||||||
|
class="btn-confirm"
|
||||||
|
:disabled="!selectedEquipment || isSubmitting"
|
||||||
|
>
|
||||||
|
{{ isSubmitting ? $t('equipment.adding') : $t('equipment.add') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
|
.fullwidth-container {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-equipment {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #1da766;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-equipment:hover {
|
||||||
|
background: #16a085;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-table th,
|
||||||
|
.cd-table td {
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-table th {
|
||||||
|
background-color: #1da766;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-cell {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 4px 8px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
max-height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
animation: modalSlideIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modalSlideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9) translateY(-20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 2px solid #1da766;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-item {
|
||||||
|
padding: 12px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-item:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-item.selected {
|
||||||
|
background: #e8f5e9;
|
||||||
|
border-left: 3px solid #1da766;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-name {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.equipment-details {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-results {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-equipment-details {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-equipment-details h4 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #1da766;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-equipment-details p {
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
padding: 8px 20px;
|
||||||
|
background: #1da766;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm:hover:not(:disabled) {
|
||||||
|
background: #16a085;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm:disabled {
|
||||||
|
background: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel {
|
||||||
|
padding: 8px 20px;
|
||||||
|
background: #6c757d;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel:hover {
|
||||||
|
background: #5a6268;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import API from '@/utils/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "EquipmentView",
|
name: "EquipmentView",
|
||||||
props: {
|
props: {
|
||||||
@@ -44,6 +398,114 @@ name: "EquipmentView",
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showAddDialog: false,
|
||||||
|
isLoading: false,
|
||||||
|
isSubmitting: false,
|
||||||
|
masterEquipment: [],
|
||||||
|
filteredMasterEquipment: [],
|
||||||
|
selectedEquipment: null,
|
||||||
|
searchQuery: '',
|
||||||
|
equipmentAmount: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$api = API
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async openAddEquipmentDialog() {
|
||||||
|
this.showAddDialog = true
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$api.get('/api/maintenance/equipment')
|
||||||
|
this.masterEquipment = response.data || []
|
||||||
|
this.filteredMasterEquipment = this.masterEquipment
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Ausrüstungs-Stammdaten:', error)
|
||||||
|
alert(this.$t('equipment.loadError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
filterEquipment() {
|
||||||
|
const query = this.searchQuery.toLowerCase().trim()
|
||||||
|
if (!query) {
|
||||||
|
this.filteredMasterEquipment = this.masterEquipment
|
||||||
|
} else {
|
||||||
|
this.filteredMasterEquipment = this.masterEquipment.filter(equipment =>
|
||||||
|
equipment.name.toLowerCase().includes(query) ||
|
||||||
|
(equipment.beschreibung && equipment.beschreibung.toLowerCase().includes(query))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectEquipment(equipment) {
|
||||||
|
this.selectedEquipment = equipment
|
||||||
|
},
|
||||||
|
|
||||||
|
async addEquipment() {
|
||||||
|
if (!this.selectedEquipment) {
|
||||||
|
alert(this.$t('equipment.pleaseSelect'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSubmitting = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const equipmentData = {
|
||||||
|
character_id: this.character.id,
|
||||||
|
name: this.selectedEquipment.name,
|
||||||
|
beschreibung: this.selectedEquipment.beschreibung || '',
|
||||||
|
gewicht: this.selectedEquipment.weight || 0,
|
||||||
|
wert: this.selectedEquipment.value || 0,
|
||||||
|
anzahl: this.equipmentAmount,
|
||||||
|
bonus: this.selectedEquipment.bonus || 0,
|
||||||
|
beinhaltet_in: '',
|
||||||
|
contained_in: 0,
|
||||||
|
container_type: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.$api.post('/api/equipment', equipmentData)
|
||||||
|
|
||||||
|
alert(this.$t('equipment.addSuccess'))
|
||||||
|
this.closeDialog()
|
||||||
|
this.$emit('character-updated')
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Hinzufügen der Ausrüstung:', error)
|
||||||
|
alert(this.$t('equipment.addError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
} finally {
|
||||||
|
this.isSubmitting = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteEquipment(equipment) {
|
||||||
|
if (!confirm(this.$t('equipment.confirmDelete').replace('{name}', equipment.name))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$api.delete(`/api/equipment/${equipment.id}`)
|
||||||
|
alert(this.$t('equipment.deleteSuccess'))
|
||||||
|
this.$emit('character-updated')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Löschen der Ausrüstung:', error)
|
||||||
|
alert(this.$t('equipment.deleteError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
closeDialog() {
|
||||||
|
this.showAddDialog = false
|
||||||
|
this.selectedEquipment = null
|
||||||
|
this.searchQuery = ''
|
||||||
|
this.equipmentAmount = 1
|
||||||
|
this.filteredMasterEquipment = []
|
||||||
|
this.masterEquipment = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="cd-view">
|
<div class="cd-view">
|
||||||
|
<div class="header-section">
|
||||||
|
<h2>{{ $t('WeaponView') }}</h2>
|
||||||
|
<button @click="openAddWeaponDialog" class="btn-add-weapon">
|
||||||
|
{{ $t('weapon.add') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="cd-list">
|
<div class="cd-list">
|
||||||
<table class="cd-table">
|
<table class="cd-table">
|
||||||
<thead>
|
<thead>
|
||||||
@@ -11,33 +18,493 @@
|
|||||||
<th>{{ $t('weapon.amount') }}</th>
|
<th>{{ $t('weapon.amount') }}</th>
|
||||||
<th>{{ $t('weapon.contained_in') }}</th>
|
<th>{{ $t('weapon.contained_in') }}</th>
|
||||||
<th>{{ $t('weapon.bonus') }}</th>
|
<th>{{ $t('weapon.bonus') }}</th>
|
||||||
|
<th>{{ $t('weapon.actions') }}</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<template v-for="weapon in character.waffen">
|
<template v-if="character.waffen && character.waffen.length > 0">
|
||||||
|
<tr v-for="weapon in character.waffen" :key="weapon.id">
|
||||||
|
<td>{{ weapon.name || '-' }}<span v-if="weapon.ist_magisch" class="magic-indicator">*</span></td>
|
||||||
|
<td>{{ weapon.beschreibung || '-' }}</td>
|
||||||
|
<td>{{ weapon.gewicht || '-' }}</td>
|
||||||
|
<td>{{ weapon.wert || '-' }}</td>
|
||||||
|
<td>{{ weapon.anzahl || '-' }}</td>
|
||||||
|
<td>{{ weapon.beinhaltet_in || '-' }}</td>
|
||||||
|
<td>{{ weapon.anb || '-' }}/{{ weapon.abwb || '-' }}</td>
|
||||||
|
<td class="action-cell">
|
||||||
|
<button @click="editWeapon(weapon)" class="btn-edit" title="Bearbeiten">
|
||||||
|
✏️
|
||||||
|
</button>
|
||||||
|
<button @click="deleteWeapon(weapon)" class="btn-delete" title="Löschen">
|
||||||
|
🗑️
|
||||||
|
</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ weapon.name || '-' }}</td>
|
<td colspan="8" class="empty-state">{{ $t('weapon.noWeapons') }}</td>
|
||||||
<td>{{ weapon.description || '-' }}</td>
|
|
||||||
<td>{{ weapon.weight || '-' }}</td>
|
|
||||||
<td>{{ weapon.value || '-' }}</td>
|
|
||||||
<td>{{ weapon.amount || '-' }}</td>
|
|
||||||
<td>{{ weapon.contained_in || '-' }}</td>
|
|
||||||
<td>{{ weapon.Anb || '-' }}/{{ weapon.Abwb || '-' }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</template>
|
</template>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div> <!--- end cd-list-->
|
</div>
|
||||||
</div> <!--- end character -datasheet-->
|
|
||||||
|
<!-- Dialog für Waffe hinzufügen -->
|
||||||
|
<div v-if="showAddDialog" class="modal-overlay" @click.self="closeDialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>{{ $t('weapon.addWeapon') }}</h3>
|
||||||
|
<button @click="closeDialog" class="close-button">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.search') }}:</label>
|
||||||
|
<input
|
||||||
|
v-model="searchQuery"
|
||||||
|
type="text"
|
||||||
|
:placeholder="$t('weapon.searchPlaceholder')"
|
||||||
|
@input="filterWeapons"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="isLoading" class="loading">{{ $t('weapon.loading') }}</div>
|
||||||
|
|
||||||
|
<div v-else-if="filteredMasterWeapons.length > 0" class="weapon-list">
|
||||||
|
<div
|
||||||
|
v-for="weapon in filteredMasterWeapons"
|
||||||
|
:key="weapon.id"
|
||||||
|
class="weapon-item"
|
||||||
|
:class="{ selected: selectedWeapon && selectedWeapon.id === weapon.id }"
|
||||||
|
@click="selectWeapon(weapon)"
|
||||||
|
>
|
||||||
|
<div class="weapon-name">{{ weapon.name }}</div>
|
||||||
|
<div class="weapon-details">
|
||||||
|
{{ weapon.damage || '-' }} |
|
||||||
|
{{ weapon.weight }}kg |
|
||||||
|
{{ weapon.value || '-' }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="no-results">
|
||||||
|
{{ searchQuery ? $t('weapon.noResults') : $t('weapon.noMasterData') }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="selectedWeapon" class="selected-weapon-details">
|
||||||
|
<h4>{{ $t('weapon.selectedWeapon') }}</h4>
|
||||||
|
<p><strong>{{ $t('weapon.name') }}:</strong> {{ selectedWeapon.name }}</p>
|
||||||
|
<p><strong>{{ $t('weapon.damage') }}:</strong> {{ selectedWeapon.damage || '-' }}</p>
|
||||||
|
<p><strong>{{ $t('weapon.weight') }}:</strong> {{ selectedWeapon.weight }}kg</p>
|
||||||
|
<p><strong>{{ $t('weapon.value') }}:</strong> {{ selectedWeapon.value || '-' }}</p>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.amount') }}:</label>
|
||||||
|
<input v-model.number="weaponAmount" type="number" min="1" value="1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button @click="closeDialog" class="btn-cancel">{{ $t('weapon.cancel') }}</button>
|
||||||
|
<button
|
||||||
|
@click="addWeapon"
|
||||||
|
class="btn-confirm"
|
||||||
|
:disabled="!selectedWeapon || isSubmitting"
|
||||||
|
>
|
||||||
|
{{ isSubmitting ? $t('weapon.adding') : $t('weapon.add') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Dialog für Waffe bearbeiten -->
|
||||||
|
<div v-if="showEditDialog" class="modal-overlay" @click.self="closeEditDialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h3>{{ $t('weapon.editWeapon') }}</h3>
|
||||||
|
<button @click="closeEditDialog" class="close-button">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-body">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.name') }}:</label>
|
||||||
|
<input v-model="editingWeapon.name" type="text" disabled />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.description') }}:</label>
|
||||||
|
<textarea v-model="editingWeapon.beschreibung" rows="3"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>
|
||||||
|
<input type="checkbox" v-model="editingWeapon.ist_magisch" />
|
||||||
|
{{ $t('weapon.magical') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.amount') }}:</label>
|
||||||
|
<input v-model.number="editingWeapon.anzahl" type="number" min="1" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.value') }}:</label>
|
||||||
|
<input v-model.number="editingWeapon.wert" type="number" min="0" step="0.01" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-row">
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.attackBonus') }} (Anb):</label>
|
||||||
|
<input v-model.number="editingWeapon.anb" type="number" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.defenseBonus') }} (Abwb):</label>
|
||||||
|
<input v-model.number="editingWeapon.abwb" type="number" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label>{{ $t('weapon.damageBonus') }} (Schb):</label>
|
||||||
|
<input v-model.number="editingWeapon.schb" type="number" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button @click="closeEditDialog" class="btn-cancel">{{ $t('weapon.cancel') }}</button>
|
||||||
|
<button
|
||||||
|
@click="saveWeapon"
|
||||||
|
class="btn-confirm"
|
||||||
|
:disabled="isSubmitting"
|
||||||
|
>
|
||||||
|
{{ isSubmitting ? $t('weapon.saving') : $t('weapon.save') }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style>
|
<style scoped>
|
||||||
|
.cd-view {
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-weapon {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background: #1da766;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-weapon:hover {
|
||||||
|
background: #16a085;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-table th,
|
||||||
|
.cd-table td {
|
||||||
|
padding: 8px;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cd-table th {
|
||||||
|
background-color: #1da766;
|
||||||
|
color: white;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
padding: 2rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.magic-indicator {
|
||||||
|
color: #9c27b0;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-cell {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-edit {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 4px 8px;
|
||||||
|
margin-right: 8px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-edit:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
padding: 4px 8px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-delete:hover {
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 15px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row .form-group {
|
||||||
|
flex: 1;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group textarea {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
resize: vertical;
|
||||||
|
font-family: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600px;
|
||||||
|
max-height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
animation: modalSlideIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes modalSlideIn {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: scale(0.9) translateY(-20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: scale(1) translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header {
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 2px solid #1da766;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-header h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
font-size: 28px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1;
|
||||||
|
padding: 0;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button:hover {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-body {
|
||||||
|
padding: 20px;
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-footer {
|
||||||
|
padding: 15px 20px;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-item {
|
||||||
|
padding: 12px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-item:hover {
|
||||||
|
background: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-item.selected {
|
||||||
|
background: #e8f5e9;
|
||||||
|
border-left: 3px solid #1da766;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-name {
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.weapon-details {
|
||||||
|
font-size: 0.9em;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-results {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: #999;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-weapon-details {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-weapon-details h4 {
|
||||||
|
margin-top: 0;
|
||||||
|
color: #1da766;
|
||||||
|
}
|
||||||
|
|
||||||
|
.selected-weapon-details p {
|
||||||
|
margin: 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm {
|
||||||
|
padding: 8px 20px;
|
||||||
|
background: #1da766;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm:hover:not(:disabled) {
|
||||||
|
background: #16a085;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-confirm:disabled {
|
||||||
|
background: #ccc;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel {
|
||||||
|
padding: 8px 20px;
|
||||||
|
background: #6c757d;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-cancel:hover {
|
||||||
|
background: #5a6268;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import API from '@/utils/api'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: "WeaponView",
|
name: "WeaponView",
|
||||||
props: {
|
props: {
|
||||||
@@ -45,6 +512,171 @@ name: "WeaponView",
|
|||||||
type: Object,
|
type: Object,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showAddDialog: false,
|
||||||
|
showEditDialog: false,
|
||||||
|
isLoading: false,
|
||||||
|
isSubmitting: false,
|
||||||
|
masterWeapons: [],
|
||||||
|
filteredMasterWeapons: [],
|
||||||
|
selectedWeapon: null,
|
||||||
|
editingWeapon: null,
|
||||||
|
searchQuery: '',
|
||||||
|
weaponAmount: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.$api = API
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async openAddWeaponDialog() {
|
||||||
|
this.showAddDialog = true
|
||||||
|
this.isLoading = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await this.$api.get('/api/maintenance/weapons')
|
||||||
|
this.masterWeapons = response.data || []
|
||||||
|
this.filteredMasterWeapons = this.masterWeapons
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Laden der Waffen-Stammdaten:', error)
|
||||||
|
alert(this.$t('weapon.loadError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
filterWeapons() {
|
||||||
|
const query = this.searchQuery.toLowerCase().trim()
|
||||||
|
if (!query) {
|
||||||
|
this.filteredMasterWeapons = this.masterWeapons
|
||||||
|
} else {
|
||||||
|
this.filteredMasterWeapons = this.masterWeapons.filter(weapon =>
|
||||||
|
weapon.name.toLowerCase().includes(query) ||
|
||||||
|
(weapon.beschreibung && weapon.beschreibung.toLowerCase().includes(query))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectWeapon(weapon) {
|
||||||
|
this.selectedWeapon = weapon
|
||||||
|
},
|
||||||
|
|
||||||
|
async addWeapon() {
|
||||||
|
if (!this.selectedWeapon) {
|
||||||
|
alert(this.$t('weapon.pleaseSelect'))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSubmitting = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const weaponData = {
|
||||||
|
character_id: this.character.id,
|
||||||
|
name: this.selectedWeapon.name,
|
||||||
|
beschreibung: this.selectedWeapon.beschreibung || '',
|
||||||
|
gewicht: this.selectedWeapon.weight || 0,
|
||||||
|
wert: this.selectedWeapon.value || 0,
|
||||||
|
anzahl: this.weaponAmount,
|
||||||
|
ist_magisch: false,
|
||||||
|
anb: this.selectedWeapon.attack_bonus || 0,
|
||||||
|
abwb: this.selectedWeapon.defense_bonus || 0,
|
||||||
|
schb: this.selectedWeapon.damage_bonus || 0,
|
||||||
|
beinhaltet_in: '',
|
||||||
|
contained_in: 0,
|
||||||
|
container_type: ''
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.$api.post('/api/weapons', weaponData)
|
||||||
|
|
||||||
|
alert(this.$t('weapon.addSuccess'))
|
||||||
|
this.closeDialog()
|
||||||
|
this.$emit('character-updated')
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Hinzufügen der Waffe:', error)
|
||||||
|
alert(this.$t('weapon.addError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
} finally {
|
||||||
|
this.isSubmitting = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
editWeapon(weapon) {
|
||||||
|
this.editingWeapon = {
|
||||||
|
id: weapon.id,
|
||||||
|
name: weapon.name,
|
||||||
|
beschreibung: weapon.beschreibung || '',
|
||||||
|
ist_magisch: weapon.ist_magisch || false,
|
||||||
|
anzahl: weapon.anzahl || 1,
|
||||||
|
wert: weapon.wert || 0,
|
||||||
|
anb: weapon.anb || 0,
|
||||||
|
abwb: weapon.abwb || 0,
|
||||||
|
schb: weapon.schb || 0
|
||||||
|
}
|
||||||
|
this.showEditDialog = true
|
||||||
|
},
|
||||||
|
|
||||||
|
async saveWeapon() {
|
||||||
|
if (!this.editingWeapon) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isSubmitting = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const weaponData = {
|
||||||
|
beschreibung: this.editingWeapon.beschreibung,
|
||||||
|
ist_magisch: this.editingWeapon.ist_magisch,
|
||||||
|
anzahl: this.editingWeapon.anzahl,
|
||||||
|
wert: this.editingWeapon.wert,
|
||||||
|
anb: this.editingWeapon.anb,
|
||||||
|
abwb: this.editingWeapon.abwb,
|
||||||
|
schb: this.editingWeapon.schb
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.$api.put(`/api/weapons/${this.editingWeapon.id}`, weaponData)
|
||||||
|
|
||||||
|
alert(this.$t('weapon.editSuccess'))
|
||||||
|
this.closeEditDialog()
|
||||||
|
this.$emit('character-updated')
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Speichern der Waffe:', error)
|
||||||
|
alert(this.$t('weapon.editError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
} finally {
|
||||||
|
this.isSubmitting = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async deleteWeapon(weapon) {
|
||||||
|
if (!confirm(this.$t('weapon.confirmDelete').replace('{name}', weapon.name))) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.$api.delete(`/api/weapons/${weapon.id}`)
|
||||||
|
alert(this.$t('weapon.deleteSuccess'))
|
||||||
|
this.$emit('character-updated')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Fehler beim Löschen der Waffe:', error)
|
||||||
|
alert(this.$t('weapon.deleteError') + ': ' + (error.response?.data?.error || error.message))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
closeEditDialog() {
|
||||||
|
this.showEditDialog = false
|
||||||
|
this.editingWeapon = null
|
||||||
|
},
|
||||||
|
|
||||||
|
closeDialog() {
|
||||||
|
this.showAddDialog = false
|
||||||
|
this.selectedWeapon = null
|
||||||
|
this.searchQuery = ''
|
||||||
|
this.weaponAmount = 1
|
||||||
|
this.filteredMasterWeapons = []
|
||||||
|
this.masterWeapons = []
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -54,6 +54,25 @@ export default {
|
|||||||
quelle:'Quelle',
|
quelle:'Quelle',
|
||||||
system:'System',
|
system:'System',
|
||||||
personal_item:'Pers',
|
personal_item:'Pers',
|
||||||
|
add: 'Ausrüstung hinzufügen',
|
||||||
|
addEquipment: 'Ausrüstung hinzufügen',
|
||||||
|
search: 'Suche',
|
||||||
|
searchPlaceholder: 'Ausrüstung suchen...',
|
||||||
|
loading: 'Lade Ausrüstung...',
|
||||||
|
noEquipment: 'Keine Ausrüstung vorhanden. Klicken Sie auf "Ausrüstung hinzufügen", um eine hinzuzufügen.',
|
||||||
|
noResults: 'Keine Ausrüstung gefunden.',
|
||||||
|
noMasterData: 'Keine Ausrüstungs-Stammdaten verfügbar.',
|
||||||
|
selectedEquipment: 'Ausgewählte Ausrüstung',
|
||||||
|
cancel: 'Abbrechen',
|
||||||
|
adding: 'Wird hinzugefügt...',
|
||||||
|
pleaseSelect: 'Bitte wählen Sie eine Ausrüstung aus.',
|
||||||
|
addSuccess: 'Ausrüstung erfolgreich hinzugefügt!',
|
||||||
|
addError: 'Fehler beim Hinzufügen der Ausrüstung',
|
||||||
|
loadError: 'Fehler beim Laden der Ausrüstungs-Stammdaten',
|
||||||
|
confirmDelete: 'Möchten Sie die Ausrüstung "{name}" wirklich löschen?',
|
||||||
|
deleteSuccess: 'Ausrüstung erfolgreich gelöscht!',
|
||||||
|
deleteError: 'Fehler beim Löschen der Ausrüstung',
|
||||||
|
actions: 'Aktionen'
|
||||||
},
|
},
|
||||||
skill:{
|
skill:{
|
||||||
id:'ID',
|
id:'ID',
|
||||||
@@ -132,6 +151,34 @@ export default {
|
|||||||
personal_item:'Pers',
|
personal_item:'Pers',
|
||||||
quelle:'Quelle',
|
quelle:'Quelle',
|
||||||
system:'System',
|
system:'System',
|
||||||
|
add: 'Waffe hinzufügen',
|
||||||
|
addWeapon: 'Waffe hinzufügen',
|
||||||
|
search: 'Suche',
|
||||||
|
searchPlaceholder: 'Waffe suchen...',
|
||||||
|
loading: 'Lade Waffen...',
|
||||||
|
noWeapons: 'Keine Waffen vorhanden. Klicken Sie auf "Waffe hinzufügen", um eine hinzuzufügen.',
|
||||||
|
noResults: 'Keine Waffen gefunden.',
|
||||||
|
noMasterData: 'Keine Waffen-Stammdaten verfügbar.',
|
||||||
|
selectedWeapon: 'Ausgewählte Waffe',
|
||||||
|
cancel: 'Abbrechen',
|
||||||
|
adding: 'Wird hinzugefügt...',
|
||||||
|
pleaseSelect: 'Bitte wählen Sie eine Waffe aus.',
|
||||||
|
addSuccess: 'Waffe erfolgreich hinzugefügt!',
|
||||||
|
addError: 'Fehler beim Hinzufügen der Waffe',
|
||||||
|
loadError: 'Fehler beim Laden der Waffen-Stammdaten',
|
||||||
|
confirmDelete: 'Möchten Sie die Waffe "{name}" wirklich löschen?',
|
||||||
|
deleteSuccess: 'Waffe erfolgreich gelöscht!',
|
||||||
|
deleteError: 'Fehler beim Löschen der Waffe',
|
||||||
|
actions: 'Aktionen',
|
||||||
|
editWeapon: 'Waffe bearbeiten',
|
||||||
|
magical: 'Magisch',
|
||||||
|
attackBonus: 'Angriffsbonus',
|
||||||
|
defenseBonus: 'Abwehrbonus',
|
||||||
|
damageBonus: 'Schadensbonus',
|
||||||
|
save: 'Speichern',
|
||||||
|
saving: 'Wird gespeichert...',
|
||||||
|
editSuccess: 'Waffe erfolgreich aktualisiert!',
|
||||||
|
editError: 'Fehler beim Speichern der Waffe'
|
||||||
},
|
},
|
||||||
Weapons:'Waffen',
|
Weapons:'Waffen',
|
||||||
weaponskill:{
|
weaponskill:{
|
||||||
|
|||||||
+47
-2
@@ -53,8 +53,25 @@ export default {
|
|||||||
wert:'Value',
|
wert:'Value',
|
||||||
quelle:'Source',
|
quelle:'Source',
|
||||||
system:'System',
|
system:'System',
|
||||||
personal_item:'Pers',
|
personal_item:'Pers', add: 'Add Equipment',
|
||||||
},
|
addEquipment: 'Add Equipment',
|
||||||
|
search: 'Search',
|
||||||
|
searchPlaceholder: 'Search equipment...',
|
||||||
|
loading: 'Loading equipment...',
|
||||||
|
noEquipment: 'No equipment available. Click "Add Equipment" to add one.',
|
||||||
|
noResults: 'No equipment found.',
|
||||||
|
noMasterData: 'No equipment master data available.',
|
||||||
|
selectedEquipment: 'Selected Equipment',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
adding: 'Adding...',
|
||||||
|
pleaseSelect: 'Please select equipment.',
|
||||||
|
addSuccess: 'Equipment successfully added!',
|
||||||
|
addError: 'Error adding equipment',
|
||||||
|
loadError: 'Error loading equipment master data',
|
||||||
|
confirmDelete: 'Do you really want to delete the equipment "{name}"?',
|
||||||
|
deleteSuccess: 'Equipment successfully deleted!',
|
||||||
|
deleteError: 'Error deleting equipment',
|
||||||
|
actions: 'Actions' },
|
||||||
skill:{
|
skill:{
|
||||||
id:'ID',
|
id:'ID',
|
||||||
name:'Name',
|
name:'Name',
|
||||||
@@ -132,6 +149,34 @@ export default {
|
|||||||
personal_item:'Pers',
|
personal_item:'Pers',
|
||||||
quelle:'Source',
|
quelle:'Source',
|
||||||
system:'System',
|
system:'System',
|
||||||
|
add: 'Add Weapon',
|
||||||
|
addWeapon: 'Add Weapon',
|
||||||
|
search: 'Search',
|
||||||
|
searchPlaceholder: 'Search weapon...',
|
||||||
|
loading: 'Loading weapons...',
|
||||||
|
noWeapons: 'No weapons available. Click "Add Weapon" to add one.',
|
||||||
|
noResults: 'No weapons found.',
|
||||||
|
noMasterData: 'No weapon master data available.',
|
||||||
|
selectedWeapon: 'Selected Weapon',
|
||||||
|
cancel: 'Cancel',
|
||||||
|
adding: 'Adding...',
|
||||||
|
pleaseSelect: 'Please select a weapon.',
|
||||||
|
addSuccess: 'Weapon successfully added!',
|
||||||
|
addError: 'Error adding weapon',
|
||||||
|
loadError: 'Error loading weapon master data',
|
||||||
|
confirmDelete: 'Do you really want to delete the weapon "{name}"?',
|
||||||
|
deleteSuccess: 'Weapon successfully deleted!',
|
||||||
|
deleteError: 'Error deleting weapon',
|
||||||
|
actions: 'Actions',
|
||||||
|
editWeapon: 'Edit Weapon',
|
||||||
|
magical: 'Magical',
|
||||||
|
attackBonus: 'Attack Bonus',
|
||||||
|
defenseBonus: 'Defense Bonus',
|
||||||
|
damageBonus: 'Damage Bonus',
|
||||||
|
save: 'Save',
|
||||||
|
saving: 'Saving...',
|
||||||
|
editSuccess: 'Weapon successfully updated!',
|
||||||
|
editError: 'Error saving weapon'
|
||||||
},
|
},
|
||||||
Weapons:'Waffen',
|
Weapons:'Waffen',
|
||||||
weaponskill:{
|
weaponskill:{
|
||||||
|
|||||||
Reference in New Issue
Block a user