- Implemented new endpoints in AdminController for managing Falukant regions, including fetching, updating, and deleting region distances. - Enhanced the FalukantService with methods for retrieving region distances and handling upsert operations. - Updated the router to expose new routes for region management and transport creation. - Introduced a transport management interface in the frontend, allowing users to create and manage transports between branches. - Added localization for new transport-related terms and improved the vehicle management interface to include transport options. - Enhanced the database initialization logic to support new region and transport models.
230 lines
5.5 KiB
Vue
230 lines
5.5 KiB
Vue
<template>
|
|
<DialogWidget
|
|
ref="dialog"
|
|
name="buy-vehicle"
|
|
:title="$t('falukant.branch.transport.title')"
|
|
icon="carriage.png"
|
|
showClose
|
|
:buttons="dialogButtons"
|
|
@close="onClose"
|
|
>
|
|
<div class="buy-vehicle-form" v-if="loaded">
|
|
<p class="hint">
|
|
{{ $t('falukant.branch.transport.balance') }}:
|
|
<strong>{{ formattedMoney }}</strong>
|
|
</p>
|
|
|
|
<label class="form-label">
|
|
{{ $t('falukant.branch.transport.vehicleType') }}
|
|
<select v-model.number="selectedTypeId" class="form-control">
|
|
<option v-for="t in vehicleTypes" :key="t.id" :value="t.id">
|
|
{{ $t(`falukant.branch.vehicles.${t.tr}`) }}
|
|
({{ formatCost(t.cost) }}, C={{ t.capacity }})
|
|
</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label class="form-label">
|
|
{{ $t('falukant.branch.transport.mode') }}
|
|
<select v-model="mode" class="form-control">
|
|
<option value="buy">
|
|
{{ $t('falukant.branch.transport.modeBuy') }}
|
|
</option>
|
|
<option value="build">
|
|
{{ $t('falukant.branch.transport.modeBuild') }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
|
|
<label class="form-label">
|
|
{{ $t('falukant.branch.transport.quantity') }}
|
|
<input
|
|
type="number"
|
|
class="form-control"
|
|
v-model.number="quantity"
|
|
min="1"
|
|
/>
|
|
</label>
|
|
|
|
<p class="total">
|
|
{{ $t('falukant.branch.transport.totalCost') }}:
|
|
<strong>{{ formatCost(totalCost) }}</strong>
|
|
</p>
|
|
|
|
<p v-if="selectedType" class="buildtime">
|
|
{{ $t('falukant.branch.transport.buildTime') }}:
|
|
<span>{{ formattedBuildTime }}</span>
|
|
</p>
|
|
|
|
<p v-if="totalCost > money" class="warning">
|
|
{{ $t('falukant.branch.transport.notEnoughMoney') }}
|
|
</p>
|
|
</div>
|
|
<div v-else class="loading">
|
|
{{ $t('loading') }}
|
|
</div>
|
|
</DialogWidget>
|
|
</template>
|
|
|
|
<script>
|
|
import DialogWidget from '@/components/DialogWidget.vue';
|
|
import apiClient from '@/utils/axios.js';
|
|
|
|
export default {
|
|
name: 'BuyVehicleDialog',
|
|
components: { DialogWidget },
|
|
props: {
|
|
regionId: { type: Number, required: true },
|
|
},
|
|
data() {
|
|
return {
|
|
vehicleTypes: [],
|
|
selectedTypeId: null,
|
|
quantity: 1,
|
|
mode: 'buy',
|
|
money: 0,
|
|
loaded: false,
|
|
};
|
|
},
|
|
computed: {
|
|
dialogButtons() {
|
|
return [
|
|
{ text: this.$t('Cancel'), action: this.close },
|
|
{
|
|
text:
|
|
this.mode === 'build'
|
|
? this.$t('falukant.branch.transport.buildAction')
|
|
: this.$t('falukant.branch.transport.buyAction'),
|
|
action: this.onConfirm,
|
|
disabled: !this.canBuy,
|
|
},
|
|
];
|
|
},
|
|
selectedType() {
|
|
return this.vehicleTypes.find((t) => t.id === this.selectedTypeId) || null;
|
|
},
|
|
totalCost() {
|
|
if (!this.selectedType) return 0;
|
|
const q = Math.max(1, this.quantity || 0);
|
|
const unit =
|
|
this.mode === 'build'
|
|
? Math.round(this.selectedType.cost * 0.75)
|
|
: this.selectedType.cost;
|
|
return unit * q;
|
|
},
|
|
canBuy() {
|
|
return (
|
|
this.loaded &&
|
|
this.selectedType &&
|
|
this.quantity >= 1 &&
|
|
this.totalCost > 0 &&
|
|
this.totalCost <= this.money
|
|
);
|
|
},
|
|
formattedMoney() {
|
|
return this.formatCost(this.money);
|
|
},
|
|
formattedBuildTime() {
|
|
if (!this.selectedType || !this.selectedType.buildTimeMinutes) return '-';
|
|
const total = this.selectedType.buildTimeMinutes;
|
|
const h = Math.floor(total / 60);
|
|
const m = total % 60;
|
|
if (h > 0 && m > 0) {
|
|
return `${h} h ${m} min`;
|
|
}
|
|
if (h > 0) return `${h} h`;
|
|
return `${m} min`;
|
|
},
|
|
},
|
|
methods: {
|
|
async open() {
|
|
this.loaded = false;
|
|
this.quantity = 1;
|
|
this.mode = 'buy';
|
|
await Promise.all([this.loadVehicleTypes(), this.loadMoney()]);
|
|
if (this.vehicleTypes.length && !this.selectedTypeId) {
|
|
this.selectedTypeId = this.vehicleTypes[0].id;
|
|
}
|
|
this.loaded = true;
|
|
this.$refs.dialog.open();
|
|
},
|
|
close() {
|
|
this.$refs.dialog.close();
|
|
},
|
|
onClose() {
|
|
this.close();
|
|
this.$emit('close');
|
|
},
|
|
async loadVehicleTypes() {
|
|
const { data } = await apiClient.get('/api/falukant/vehicles/types');
|
|
this.vehicleTypes = data;
|
|
},
|
|
async loadMoney() {
|
|
const { data } = await apiClient.get('/api/falukant/info');
|
|
this.money = Number(data.money) || 0;
|
|
},
|
|
formatCost(value) {
|
|
return new Intl.NumberFormat(navigator.language, {
|
|
minimumFractionDigits: 2,
|
|
maximumFractionDigits: 2,
|
|
}).format(value);
|
|
},
|
|
async onConfirm() {
|
|
if (!this.canBuy) return;
|
|
try {
|
|
await apiClient.post('/api/falukant/vehicles', {
|
|
vehicleTypeId: this.selectedTypeId,
|
|
quantity: this.quantity,
|
|
regionId: this.regionId,
|
|
mode: this.mode,
|
|
});
|
|
this.$emit('bought');
|
|
this.close();
|
|
} catch (err) {
|
|
console.error('Error buying vehicles', err);
|
|
this.$emit('error', err);
|
|
}
|
|
},
|
|
},
|
|
};
|
|
</script>
|
|
|
|
<style scoped>
|
|
.buy-vehicle-form {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
}
|
|
|
|
.form-label {
|
|
display: block;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.form-control {
|
|
width: 100%;
|
|
padding: 0.3rem;
|
|
margin-top: 0.25rem;
|
|
}
|
|
|
|
.hint {
|
|
margin: 0 0 0.5rem 0;
|
|
}
|
|
|
|
.total {
|
|
margin-top: 0.5rem;
|
|
}
|
|
|
|
.warning {
|
|
color: #c62828;
|
|
font-weight: 600;
|
|
}
|
|
|
|
.loading {
|
|
text-align: center;
|
|
padding: 1rem;
|
|
}
|
|
</style>
|
|
|
|
|