Web-UI: Schedules support

This commit is contained in:
Артём Кокос
2026-03-02 20:44:25 +07:00
parent 9a47f6f936
commit c661e2450d
2 changed files with 111 additions and 4 deletions

View File

@@ -58,10 +58,68 @@
<nav class="flex glass p-1.5 rounded-2xl">
<button @click="tab = 'control'" :class="tab === 'control' ? 'active-tab' : 'text-slate-400'" class="px-8 py-2.5 rounded-xl font-bold transition-all">ПУЛЬТ</button>
<button @click="tab = 'schedules'" :class="tab === 'schedules' ? 'active-tab' : 'text-slate-400'" class="px-8 py-2.5 rounded-xl font-bold transition-all">ГРАФИК</button>
<button @click="tab = 'admin'" :class="tab === 'admin' ? 'active-tab' : 'text-slate-400'" class="px-8 py-2.5 rounded-xl font-bold transition-all">АДМИНКА</button>
</nav>
</header>
<div v-if="tab === 'schedules'" class="space-y-10">
<div class="glass p-8 rounded-[2.5rem]">
<h2 class="text-2xl font-black mb-6 uppercase italic">Новая задача</h2>
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
<select v-model="newTask.target_id" class="bg-slate-900 border border-slate-700 p-4 rounded-xl outline-none focus:border-orange-500">
<option value="" disabled>Выберите группу...</option>
<option v-for="(g, id) in groups" :value="id">{{ g.name }}</option>
</select>
<div class="flex items-center gap-2 bg-slate-900 border border-slate-700 p-2 rounded-xl">
<select v-model="taskHour" class="bg-transparent outline-none flex-1 text-center font-bold">
<option v-for="h in 24" :value="(h-1).toString().padStart(2, '0')">{{ (h-1).toString().padStart(2, '0') }}</option>
</select>
<span class="font-bold">:</span>
<select v-model="taskMin" class="bg-transparent outline-none flex-1 text-center font-bold">
<option v-for="m in 60" :value="(m-1).toString().padStart(2, '0')">{{ (m-1).toString().padStart(2, '0') }}</option>
</select>
</div>
<select v-model="newTask.state" class="bg-slate-900 border border-slate-700 p-4 rounded-xl outline-none">
<option :value="true">ВКЛЮЧИТЬ</option>
<option :value="false">ВЫКЛЮЧИТЬ</option>
</select>
<button @click="addSchedule" class="bg-orange-600 hover:bg-orange-500 rounded-xl font-black transition-all">ДОБАВИТЬ</button>
</div>
</div>
<div class="glass p-8 rounded-[2.5rem]">
<h2 class="text-2xl font-black mb-6 uppercase italic">Активные задачи</h2>
<div class="space-y-4">
<div v-for="task in tasks" :key="task.id" class="bg-slate-900/50 border border-slate-800 p-6 rounded-2xl flex justify-between items-center group">
<div>
<div class="flex items-center gap-3 mb-1">
<span class="text-orange-500 text-xl font-black tracking-tighter">{{ task.hour }}:{{ task.minute }}</span>
<span class="text-[10px] bg-slate-800 px-2 py-0.5 rounded text-slate-400 uppercase font-bold">
{{ groups[task.target_id]?.name || task.target_id }}
</span>
</div>
<div class="flex items-center gap-2">
<span :class="task.state ? 'text-green-400' : 'text-red-400'" class="text-xs font-bold uppercase">
{{ task.state ? 'ВКЛЮЧИТЬ' : 'ВЫКЛЮЧИТЬ' }}
</span>
<span class="text-[10px] text-slate-600"></span>
<span class="text-[10px] text-slate-500 uppercase tracking-widest">
{{ task.next_run ? 'След. запуск: ' + new Date(task.next_run).toLocaleDateString() : 'Разовая задача' }}
</span>
</div>
</div>
<button @click="deleteTask(task.id)" class="text-slate-600 hover:text-red-500 p-2 transition-colors">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
</button>
</div>
<div v-if="tasks.length === 0" class="text-center py-10 text-slate-500 italic">Задач пока нет</div>
</div>
</div>
</div>
<div v-if="tab === 'control'" class="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div v-if="Object.keys(groups).length === 0" class="col-span-full text-center py-20 glass rounded-3xl opacity-50">
<p class="text-xl italic text-slate-400">Создайте группу в админке, чтобы управлять лампами</p>
@@ -159,6 +217,10 @@
sliders: {},
newGroup: { id: '', name: '', macs: [] },
isLoadingStatus: false,
taskHour: '12', // Начальное значение часа
taskMin: '00', // Начальное значение минут
newTask: { target_id: '', time: '', state: true },
tasks: [],
allScenes: [
"ocean", "romance", "party", "fireplace", "cozy", "forest",
"pastel_colors", "wake_up", "bedtime", "warm_white", "daylight",
@@ -219,6 +281,7 @@
await this.syncGroupStatuses();
}
if (dData) this.devices = Object.values(dData);
this.fetchTasks();
},
async control(id, params) {
await this.request(`/control/group/${id}`, 'POST', params);
@@ -280,6 +343,32 @@
}
}
this.isLoadingStatus = false;
},
async fetchTasks() {
const data = await this.request('/schedules/tasks');
if (data) this.tasks = data.tasks;
},
async addSchedule() {
// Проверяем, выбрана ли группа
if (!this.newTask.target_id) {
alert("Выбери группу!");
return;
}
// Отправляем данные. Теперь берем их прямо из taskHour и taskMin
await this.request('/schedules/cron', 'POST', {
target_id: this.newTask.target_id,
hour: this.taskHour,
minute: this.taskMin,
is_group: true,
state: this.newTask.state
});
this.fetchTasks(); // Обновляем список
},
async deleteTask(id) {
await this.request(`/schedules/${id}`, 'DELETE');
this.fetchTasks();
}
},
mounted() {