291 lines
14 KiB
HTML
291 lines
14 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>MercyV Garage</title>
|
|
<link rel="stylesheet" href="style.css">
|
|
<script src="vue.js"></script>
|
|
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
|
</head>
|
|
<body>
|
|
|
|
<div id="app" v-show="show">
|
|
|
|
<!-- ══════════════════════════ GARAGE PANEL ══════════════════════════ -->
|
|
<div class="mv-backdrop-transparent" v-show="!showAdminPanel">
|
|
<div class="mv-modal">
|
|
|
|
<!-- HEADER -->
|
|
<div class="mv-header">
|
|
<div class="mv-header-left">
|
|
<div class="mv-header-icon">
|
|
<img src="images/logo/logo.png" class="mv-logo-img" alt="MercyV" onerror="this.style.display='none'">
|
|
</div>
|
|
<div class="mv-header-title">
|
|
<span class="mv-title-main">MercyV Garage</span>
|
|
<span class="mv-title-sub">{{ garageId || 'Garage' }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="mv-header-right">
|
|
<div class="mv-player-name">{{ playerName }}</div>
|
|
<button class="mv-close-btn" @click="close()"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- BODY -->
|
|
<div class="mv-body">
|
|
|
|
<!-- SIDEBAR -->
|
|
<div class="mv-sidebar">
|
|
<div class="mv-sidebar-item" :class="activeTab==='all'?'active':''" @click="setTab('all')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'||garageType==='impoundboat'||garageType==='impoundplane'">
|
|
<i class="fas fa-border-all"></i><span>Alle</span>
|
|
</div>
|
|
<div class="mv-sidebar-item" :class="activeTab==='cars'?'active':''" @click="setTab('cars')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'">
|
|
<i class="fas fa-car"></i><span>Autos</span>
|
|
</div>
|
|
<div class="mv-sidebar-item" :class="activeTab==='motor'?'active':''" @click="setTab('motor')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'">
|
|
<i class="fas fa-motorcycle"></i><span>Moto</span>
|
|
</div>
|
|
<div class="mv-sidebar-item" :class="activeTab==='bikes'?'active':''" @click="setTab('bikes')" v-show="garageType==='normal'||garageType==='jobgarage'||garageType==='impound'">
|
|
<i class="fas fa-bicycle"></i><span>Bikes</span>
|
|
</div>
|
|
<div class="mv-sidebar-item" :class="activeTab==='boat'?'active':''" @click="setTab('boat')" v-show="garageType==='boat'||garageType==='impoundboat'">
|
|
<i class="fas fa-ship"></i><span>Boote</span>
|
|
</div>
|
|
<div class="mv-sidebar-item" :class="activeTab==='aircraft'?'active':''" @click="setTab('aircraft')" v-show="garageType==='aircraft'||garageType==='impoundplane'">
|
|
<i class="fas fa-plane"></i><span>Flug</span>
|
|
</div>
|
|
<div class="mv-sidebar-divider"></div>
|
|
<div class="mv-sidebar-item" :class="showFavorites?'active-fav':''" @click="toggleFavorites()" v-show="garageType!=='jobgarage'">
|
|
<i :class="showFavorites?'fas fa-star':'far fa-star'"></i><span>Favoriten</span>
|
|
</div>
|
|
<div class="mv-sidebar-spacer"></div>
|
|
<div class="mv-sidebar-item mv-admin-item" v-if="isAdmin" @click="openAdminFromGarage()">
|
|
<i class="fas fa-cog"></i><span>Admin</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- MAIN -->
|
|
<div class="mv-main">
|
|
|
|
<!-- TOPBAR -->
|
|
<div class="mv-topbar">
|
|
<div class="mv-search-wrap">
|
|
<i class="fas fa-search mv-search-icon"></i>
|
|
<input v-model="searchQuery" class="mv-search" placeholder="Fahrzeug suchen...">
|
|
</div>
|
|
<div class="mv-impound-badge" v-if="garageType==='impound'||garageType==='impoundboat'||garageType==='impoundplane'">
|
|
<i class="fas fa-euro-sign"></i> Abholgebühr: <strong>{{ impoundPrice }}$</strong>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CONTENT ROW: Grid + Detail -->
|
|
<div class="mv-content-row">
|
|
|
|
<!-- GRID -->
|
|
<div class="mv-grid">
|
|
<div class="mv-card" v-for="(v, i) in filteredVehicles" :key="v.plate"
|
|
@click="selectVehicle(v, i)"
|
|
:class="selectedIndex===i ? 'mv-card-active' : ''">
|
|
<div class="mv-card-status" :class="v.nearby ? 'status-nearby' : v.stored===0 ? 'status-out' : 'status-in'">
|
|
{{ v.nearby ? 'In der Nähe' : v.stored===0 ? 'Draußen' : 'Eingelagert' }}
|
|
</div>
|
|
<img :src="'images/cars/'+v.carimage+'.png'" class="mv-card-img" onerror="this.src='images/defaultimage.png'">
|
|
<div class="mv-card-name">{{ v.carname }}</div>
|
|
<div class="mv-card-plate">{{ v.plate }}</div>
|
|
<div class="mv-card-fav" @click.stop="toggleFav(v.plate, v.favorite)">
|
|
<i :class="v.favorite===1?'fas fa-star':'far fa-star'"></i>
|
|
</div>
|
|
</div>
|
|
<div class="mv-empty" v-if="filteredVehicles.length===0">
|
|
<i class="fas fa-car-side"></i>
|
|
<p>Keine Fahrzeuge</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- DETAIL -->
|
|
<div class="mv-detail" v-if="selectedVehicle">
|
|
<div class="mv-detail-img-wrap">
|
|
<img :src="'images/cars/'+selectedVehicle.carimage+'.png'" class="mv-detail-img" onerror="this.src='images/defaultimage.png'">
|
|
</div>
|
|
<div class="mv-detail-name">{{ selectedVehicle.carname }}</div>
|
|
<div class="mv-detail-plate"><i class="fas fa-id-card"></i> {{ selectedVehicle.plate }}</div>
|
|
<div class="mv-detail-garage" v-if="selectedVehicle.parking"><i class="fas fa-warehouse"></i> {{ selectedVehicle.parking }}</div>
|
|
<div class="mv-detail-status" :class="selectedVehicle.stored===0?'ds-out':'ds-in'">
|
|
<i :class="selectedVehicle.stored===0?'fas fa-map-marker-alt':'fas fa-lock'"></i>
|
|
{{ selectedVehicle.stored===0 ? 'Aktuell draußen' : 'Eingelagert' }}
|
|
</div>
|
|
<button class="mv-takeout-btn" @click="takeOut()" v-if="selectedVehicle.stored===1">
|
|
<i class="fas fa-car"></i> Fahrzeug holen
|
|
</button>
|
|
<button class="mv-parkin-btn" @click="parkFromPanel()" v-if="selectedVehicle.nearby && selectedVehicle.stored===0">
|
|
<i class="fas fa-parking"></i> Fahrzeug einparken
|
|
</button>
|
|
<div class="mv-already-out" v-if="!selectedVehicle.nearby && selectedVehicle.stored===0">
|
|
<i class="fas fa-map-marker-alt"></i> Fahrzeug ist draußen
|
|
</div>
|
|
<div class="mv-detail-fav" @click="toggleFav(selectedVehicle.plate, selectedVehicle.favorite)">
|
|
<i :class="selectedVehicle.favorite===1?'fas fa-star':'far fa-star'"></i>
|
|
{{ selectedVehicle.favorite===1 ? 'Favorit entfernen' : 'Zu Favoriten' }}
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /.mv-content-row -->
|
|
</div><!-- /.mv-main -->
|
|
</div><!-- /.mv-body -->
|
|
</div><!-- /.mv-modal -->
|
|
</div><!-- garage panel -->
|
|
|
|
<!-- ══════════════════════════ ADMIN PANEL ══════════════════════════ -->
|
|
<div class="mv-backdrop-transparent" v-if="showAdminPanel">
|
|
<div class="mv-modal mv-modal-wide">
|
|
|
|
<div class="mv-header">
|
|
<div class="mv-header-left">
|
|
<div class="mv-header-icon">
|
|
<i class="fas fa-cog" style="color:#E8830A;font-size:14px;"></i>
|
|
</div>
|
|
<div class="mv-header-title">
|
|
<span class="mv-title-main">Garage Editor</span>
|
|
<span class="mv-title-sub">Admin Tool</span>
|
|
</div>
|
|
</div>
|
|
<div class="mv-header-right">
|
|
<button class="mv-btn-orange" @click="newGarage()"><i class="fas fa-plus"></i> Neue Garage</button>
|
|
<button class="mv-close-btn" @click="closeAdmin()"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mv-body">
|
|
|
|
<div class="mv-sidebar mv-admin-sidebar">
|
|
<div style="padding:10px 12px 6px;font-size:11px;color:rgba(255,255,255,0.3);letter-spacing:0.1em;text-transform:uppercase;">
|
|
Garagen ({{ adminGarages.length }})
|
|
</div>
|
|
<div class="mv-admin-list">
|
|
<div class="mv-admin-list-item"
|
|
v-for="g in adminGarages" :key="g.id"
|
|
:class="editingGarage && editingGarage.id===g.id ? 'admin-item-active':''"
|
|
@click="editGarage(g)">
|
|
<div class="mv-admin-item-icon" :class="'type-icon-'+g.type">
|
|
<i :class="getTypeIcon(g.type)"></i>
|
|
</div>
|
|
<div class="mv-admin-item-info">
|
|
<div class="mv-admin-item-label">{{ g.label }}</div>
|
|
<div class="mv-admin-item-type">{{ g.type }}</div>
|
|
</div>
|
|
<div class="mv-admin-item-btns">
|
|
<button class="mv-icon-btn mv-icon-btn-blue" @click.stop="teleportTo(g)"><i class="fas fa-location-arrow"></i></button>
|
|
<button class="mv-icon-btn mv-icon-btn-red" @click.stop="confirmDelete(g.id)"><i class="fas fa-trash"></i></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mv-main mv-admin-main" v-if="editingGarage">
|
|
<div class="mv-topbar" style="border-bottom:1px solid rgba(255,255,255,0.06);">
|
|
<div style="font-size:14px;font-weight:600;color:#fff;">
|
|
{{ editingGarage._isNew ? 'Neue Garage erstellen' : 'Bearbeiten: ' + editingGarage.label }}
|
|
</div>
|
|
<button class="mv-btn-orange" @click="saveGarage()"><i class="fas fa-save"></i> Speichern</button>
|
|
</div>
|
|
<div class="mv-form-scroll">
|
|
<div class="mv-form-section">
|
|
<div class="mv-form-section-title"><i class="fas fa-info-circle"></i> Basis</div>
|
|
<div class="mv-form-grid">
|
|
<div class="mv-form-field">
|
|
<label>ID</label>
|
|
<input v-model="editingGarage.id" :disabled="!editingGarage._isNew" placeholder="garage_ls_01">
|
|
</div>
|
|
<div class="mv-form-field">
|
|
<label>Anzeigename</label>
|
|
<input v-model="editingGarage.label" placeholder="Garage LS">
|
|
</div>
|
|
<div class="mv-form-field">
|
|
<label>Typ</label>
|
|
<select v-model="editingGarage.type">
|
|
<option value="normal">Normal</option>
|
|
<option value="aircraft">Flugzeuge</option>
|
|
<option value="boat">Boote</option>
|
|
<option value="jobgarage">Job-Garage</option>
|
|
<option value="impound">Abschlepphof (Auto)</option>
|
|
<option value="impoundboat">Abschlepphof (Boot)</option>
|
|
<option value="impoundplane">Abschlepphof (Flugzeug)</option>
|
|
</select>
|
|
</div>
|
|
<div class="mv-form-field">
|
|
<label>Job-Zugang</label>
|
|
<input v-model="editingGarage.access" placeholder="none">
|
|
</div>
|
|
<div class="mv-form-field">
|
|
<label>NPC Model</label>
|
|
<input v-model="editingGarage.npc_model" placeholder="a_m_m_prolhost_01">
|
|
</div>
|
|
<div class="mv-form-field mv-form-field-check">
|
|
<label>Blip anzeigen</label>
|
|
<input type="checkbox" v-model="editingGarage.blip_show_bool">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="mv-form-section">
|
|
<div class="mv-form-section-title">
|
|
<i class="fas fa-map-marker-alt"></i> Positionen
|
|
<span class="mv-hint-pill"><i class="fas fa-info-circle"></i> Position aufsuchen → "Hier erfassen" → E drücken</span>
|
|
</div>
|
|
<div class="mv-pos-block" v-for="pos in posFields" :key="pos.field">
|
|
<div class="mv-pos-label">{{ pos.label }}</div>
|
|
<div class="mv-pos-row">
|
|
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_x']" placeholder="X">
|
|
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_y']" placeholder="Y">
|
|
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_z']" placeholder="Z">
|
|
<input type="number" step="0.0001" v-model.number="editingGarage[pos.field+'_heading']" placeholder="Heading" v-if="pos.hasHeading">
|
|
</div>
|
|
<button class="mv-capture-btn" @click="capturePos(pos.field)">
|
|
<i class="fas fa-crosshairs"></i> Hier erfassen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mv-admin-empty" v-else>
|
|
<i class="fas fa-warehouse"></i>
|
|
<p>Garage auswählen oder neue erstellen</p>
|
|
</div>
|
|
|
|
</div><!-- /.mv-body -->
|
|
</div><!-- /.mv-modal-wide -->
|
|
</div><!-- admin panel -->
|
|
|
|
<!-- DELETE CONFIRM -->
|
|
<div class="mv-confirm-overlay" v-if="deleteConfirmId">
|
|
<div class="mv-confirm-box">
|
|
<i class="fas fa-exclamation-triangle mv-confirm-icon"></i>
|
|
<p class="mv-confirm-title">Garage löschen?</p>
|
|
<p class="mv-confirm-sub"><strong>{{ deleteConfirmId }}</strong> wird permanent entfernt.</p>
|
|
<div class="mv-confirm-btns">
|
|
<button class="mv-btn-danger" @click="deleteGarage()"><i class="fas fa-trash"></i> Löschen</button>
|
|
<button class="mv-btn-ghost" @click="deleteConfirmId=null">Abbrechen</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- CAPTURE OVERLAY -->
|
|
<div class="mv-capture-overlay" v-if="capturingField">
|
|
<div class="mv-capture-box">
|
|
<i class="fas fa-crosshairs mv-capture-icon"></i>
|
|
<p class="mv-capture-title">Position erfassen</p>
|
|
<p class="mv-capture-field">{{ getFieldLabel(capturingField) }}</p>
|
|
<p class="mv-capture-hint">Geh zur Position → <kbd>E</kbd> drücken</p>
|
|
<p class="mv-capture-cancel"><kbd>ESC</kbd> = Abbrechen</p>
|
|
</div>
|
|
</div>
|
|
|
|
</div><!-- /#app -->
|
|
|
|
<script src="script.js"></script>
|
|
</body>
|
|
</html>
|