138 lines
6.0 KiB
Vue
138 lines
6.0 KiB
Vue
<template>
|
||
<AppLayout>
|
||
<template #header>
|
||
<div>
|
||
<Link :href="route('migrations.index')" class="inline-flex items-center text-primary hover:underline text-sm">
|
||
<i class="pi pi-arrow-left mr-1"></i> Назад к расписаниям
|
||
</Link>
|
||
<h2 class="text-xl font-bold mt-1">{{ schedule.name }}</h2>
|
||
</div>
|
||
</template>
|
||
|
||
<div class="grid gap-6">
|
||
<Card header="Информация о расписании">
|
||
<template #content>
|
||
<div class="grid grid-cols-2 gap-4">
|
||
<div>
|
||
<p class="text-sm text-muted-color">Исходная БД</p>
|
||
<p class="font-bold">{{ schedule.source_database?.name }}</p>
|
||
<p class="text-sm text-muted-color">{{ schedule.source_database?.host }}/{{ schedule.source_database?.database }}</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-muted-color">Целевая БД</p>
|
||
<p class="font-bold">{{ schedule.target_database?.name }}</p>
|
||
<p class="text-sm text-muted-color">{{ schedule.target_database?.host }}/{{ schedule.target_database?.database }}</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-muted-color">Расписание</p>
|
||
<p class="font-bold font-mono">{{ schedule.cron_expression }}</p>
|
||
<p class="text-sm text-muted-color">Часовой пояс: {{ schedule.timezone }}</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-muted-color">Статус</p>
|
||
<Tag :value="schedule.is_active ? 'Активно' : 'Неактивно'"
|
||
:severity="schedule.is_active ? 'success' : 'secondary'" />
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-muted-color">Таблицы</p>
|
||
<p class="font-bold">{{ schedule.scheduled_tables?.length || 0 }} выбрано</p>
|
||
</div>
|
||
<div>
|
||
<p class="text-sm text-muted-color">Размер пакета</p>
|
||
<p class="font-bold">{{ schedule.batch_size }}</p>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="flex gap-2 mt-6 pt-4 border-t border-surface">
|
||
<Button label="Запустить сейчас" icon="pi pi-play" severity="success"
|
||
@click="runNow" :loading="running" />
|
||
<Link :href="route('migrations.edit', schedule.id)">
|
||
<Button label="Редактировать" icon="pi pi-pencil" severity="secondary" />
|
||
</Link>
|
||
</div>
|
||
</template>
|
||
</Card>
|
||
|
||
<Card header="Запуски миграций">
|
||
<template #content>
|
||
<DataTable :value="runs" stripedRows>
|
||
<Column field="id" header="ID" sortable></Column>
|
||
<Column field="status" header="Статус">
|
||
<template #body="slotProps">
|
||
<Tag :value="getStatusLabel(slotProps.data.status)"
|
||
:severity="getStatusSeverity(slotProps.data.status)" />
|
||
</template>
|
||
</Column>
|
||
<Column field="total_tables" header="Таблиц"></Column>
|
||
<Column field="processed_tables" header="Обработано"></Column>
|
||
<Column field="migrated_rows" header="Строк мигрировано"></Column>
|
||
<Column field="started_at" header="Начало">
|
||
<template #body="slotProps">
|
||
{{ slotProps.data.started_at ? formatDate(slotProps.data.started_at) : '-' }}
|
||
</template>
|
||
</Column>
|
||
<Column field="completed_at" header="Завершение">
|
||
<template #body="slotProps">
|
||
{{ slotProps.data.completed_at ? formatDate(slotProps.data.completed_at) : '-' }}
|
||
</template>
|
||
</Column>
|
||
</DataTable>
|
||
</template>
|
||
</Card>
|
||
</div>
|
||
</AppLayout>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref } from 'vue';
|
||
import { Link, router } from '@inertiajs/vue3';
|
||
import AppLayout from '@/Layouts/AppLayout.vue';
|
||
import Card from 'primevue/card';
|
||
import DataTable from 'primevue/datatable';
|
||
import Column from 'primevue/column';
|
||
import Button from 'primevue/button';
|
||
import Tag from 'primevue/tag';
|
||
|
||
const props = defineProps({
|
||
schedule: Object,
|
||
runs: Array,
|
||
});
|
||
|
||
const running = ref(false);
|
||
|
||
const getStatusLabel = (status) => {
|
||
const labels = {
|
||
pending: 'Ожидает',
|
||
running: 'Выполняется',
|
||
completed: 'Завершено',
|
||
failed: 'Ошибка',
|
||
};
|
||
return labels[status] || status;
|
||
};
|
||
|
||
const getStatusSeverity = (status) => {
|
||
const severityMap = {
|
||
pending: 'secondary',
|
||
running: 'info',
|
||
completed: 'success',
|
||
failed: 'danger',
|
||
};
|
||
return severityMap[status] || 'secondary';
|
||
};
|
||
|
||
const formatDate = (dateString) => {
|
||
return new Date(dateString).toLocaleString('ru-RU');
|
||
};
|
||
|
||
const runNow = () => {
|
||
if (confirm('Запустить эту миграцию сейчас?')) {
|
||
running.value = true;
|
||
router.post(route('migrations.run', props.schedule.id), {}, {
|
||
onFinish: () => {
|
||
running.value = false;
|
||
}
|
||
});
|
||
}
|
||
};
|
||
</script>
|