Bläddra i källkod

Реализованы CR и D из CRUD

Вадим Королёв 1 år sedan
förälder
incheckning
ad98152ecd

+ 3 - 0
doc/z-index.txt

@@ -0,0 +1,3 @@
+0: body
+1: .card
+2: .modal

+ 28 - 0
src/controllers/ApiController.php

@@ -0,0 +1,28 @@
+<?php
+// Контроллер API
+
+class ApiController extends Controller {
+
+	// Удаление предмета
+	public function deleteSubject() {
+		SubjectModel::deleteById($_GET['id']);
+	}
+	
+	// Добавление предмета
+	public function createSubject() {
+		$id = SubjectModel::create($_POST['name'], $_POST['code'], $_POST['teacher_id']);
+		$subject = SubjectModel::getById($id);
+		echo json_encode($subject);
+	}
+
+	// Получение всех преподавателей
+	public function getTeachers() {
+		$teachers = TeacherModel::all();
+		$output = [];
+		while ($teacher = $teachers->fetchArray(SQLITE3_ASSOC)) {
+			$teacher['repr'] = $teacher['surname'];
+			$output[] = $teacher;
+		}
+		echo json_encode($output);
+	}
+}

+ 12 - 0
src/controllers/RegenController.php

@@ -3,6 +3,18 @@
 
 class RegenController extends Controller {
 
+	// Архив отчётов
+	public function archive() {
+		$subjects = SubjectModel::all();
+
+		$view = new RegenArchiveView([
+			"page_title" => "Regen: архив",
+			"crumbs" => ["Главная" => "/", "Regen: архив" => "/"],
+			"subjects" => $subjects
+		]);
+		$view->view();
+	}
+
 	// Редактирование отчёта
 	public function edit() {
 		$parts = explode("/", $this->request_uri);

+ 48 - 0
src/css/pockit.css

@@ -33,11 +33,21 @@ body {
 	margin: 0;
 }
 
+h1 {
+	margin: 0;
+}
+
 nav {
 	background-color: var(--gray-0);
 	border-bottom: 1px solid var(--gray-2);
 }
 
+/* Colors */
+.danger {
+	background-color: var(--error);
+}
+/* End Colors */
+
 /* Forms */
 .form-control-container {
 	margin: 1em auto;
@@ -216,10 +226,48 @@ a:hover {
 	border-color: red;
 	color: #f9fbf8;
 }
+
+.card.modal {
+	position: fixed;
+	top: 10%;
+	left: 0;
+	right: 0;
+	z-index: 2;
+}
 /* End Cards */
 
 .filename {
 	user-select: all;
 	font-weight: bold;
 	text-decoration: underline;
+}
+
+/* CRUD */
+.crud-item {
+	margin: 1rem auto;
+	padding: 1em;
+	background-color: var(--gray-2);
+	border-radius: 5px;
+	display: grid;
+	grid-template-columns: 75% 25%;
+}
+
+.crud-buttons {
+	display: flex;
+	flex-direction: column;
+}
+
+.crud-buttons button {
+	margin: 0.1em;
+}
+/* End CRUD */
+
+.dark-overlay {
+	background-color: rgba(0,0,0,0.5);
+	position: fixed;
+	top: 0;
+	bottom: 0;
+	left: 0;
+	right: 0;
+	z-index: 1;
 }

BIN
src/img/favicons/book.png


BIN
src/img/favicons/cross.png


BIN
src/img/favicons/file.png


+ 0 - 0
src/img/favicons/home.png → src/img/favicons/pockit.png


BIN
src/img/favicons/shield.png


+ 11 - 0
src/index.php

@@ -9,13 +9,24 @@ spl_autoload_register("Pockit::autoload");
 
 // Определение маршрутов
 $router = new Router();
+
+// Главная
 $router->register('', ['HomeController', 'index']);
+
+// Оценки
 $router->register('/grades', ['GradesController', 'index']);
 $router->register('/grades/get', ['GradesController', 'collect']);
 
+// Regen
 $router->register('/regen/new', ['RegenController', 'newReport']);
 $router->register('/regen/edit/\d+', ['RegenController', 'edit']);
 $router->register("/regen/gethtml", ['RegenController', 'getHtml']);
+$router->register("/regen/archive", ['RegenController', 'archive']);
+
+// API
+$router->register('/subjects/create', ['ApiController', 'createSubject']);
+$router->register('/subjects/delete', ['ApiController', 'deleteSubject']);
+$router->register('/teachers/read', ['ApiController', 'getTeachers']);
 
 $router->register404(['NotFoundController', 'index']);
 

+ 96 - 0
src/js/pockit.js

@@ -0,0 +1,96 @@
+// Возвращает список значений из БД
+async function crudRead(route, limit = 999) {
+	const url = "/"+route+"/read?limit="+limit;
+	return $.ajax({
+		type: 'POST',
+		url: url
+	});
+}
+
+// Отправляет запрос к API на удаление
+// После удаления на странице удаляется элемент с ID контейнера
+function crudDelete(route, id, containerID=null) {
+	if (!confirm("Точно удалить?")) {
+		return;
+	}
+	const url = '/'+route+'/delete?id='+id;
+	$.ajax({
+		url: url,
+		success: function() {
+			document.getElementById(containerID).remove();
+		}
+	});
+}
+
+// Показывает форму создания элемента
+// Элемент создаётся через API, с помощью route
+async function crudCreateShowWindow(route, options, name, afterCreatedCallback) {
+	// Создание карточки-контейнера
+	const card = $("<div class='card modal'></div>");
+
+	// Создание формы и привязка к ней обратного вызова
+	const form = $("<form class='crudcreateform' method='post' action='/"+route+"/create'></form>");
+	form.submit(function(e) {
+		e.preventDefault();
+		$.ajax({
+			url: "/"+route+"/create",
+			type: 'post',
+			data: $(this).serialize(),
+			success: function(response) {
+				// Окно и слой затемнения удаляется
+				removeModalWindows();
+				// Вызывается функция обратного вызова с параметром - объектом с информацией о созданном элементе
+				afterCreatedCallback(JSON.parse(response));
+			}
+		});
+	});
+
+	// Добавление к форме полей ввода
+	for (const [key, value] of Object.entries(options)) {
+		
+		// Добавляем контейнер поля
+		let control_container = $("<div class='form-control-container'></div>");
+
+		// Надпись
+		control_container.append($('<label for="'+key+'">'+ key +'</label>'));
+
+		// Непосредственно поле ввода
+		if (value.type == 'plain')  {
+			// Текстовое
+			control_container.append($('<input class="form-control" id="'+key+'" type="text" name="'+value.name+'"/>'));
+			
+		} else if (value.type == 'crudRead') {
+			// Выбор из нескольких вариантов
+			
+			const values = JSON.parse(await crudRead(value.route));
+			const selectInput = $('<select class="form-control" id="'+key+'" name="'+value.name+'">');
+			for (let i = 0; i < values.length; ++i) {
+				selectInput.append($('<option value="'+values[i].id+'">'+values[i].repr+'</option>'))
+			}
+			control_container.append(selectInput);
+			
+		} else {
+			console.log("Неизвестный тип: " + value.type);
+		}
+
+		form.append(control_container);
+	};
+
+	// Кнопки добавления и отмены
+	form.append($(`
+	<div style='display: grid;grid-template-columns: auto 25%;grid-gap: 1em;width: 100%;'>
+		<button type="submit" class="crudcreate createbutton form-control">Создать</button>
+		<button type="submit" onclick='removeModalWindows()' class="crudcancel form-control">Отмена</button>
+	</div>`));
+
+	// Добавление всех элементов и показ
+    card.append($('<h1>'+name+'</h1>'));
+    card.append(form);
+    $(document.body).append(card);
+	$(document.body).append($('<div class="dark-overlay"></div>'));
+}
+
+// Удаляет все компоненты с модальными окнами
+function removeModalWindows() {
+	$('.modal, .dark-overlay').remove();
+}

+ 0 - 1
src/js/regenpreview.js

@@ -21,7 +21,6 @@ $("#switchMarkup").click(function() {
 });
 
 $("#printReport").click(function() {
-
 	updatePreview(true);
 })
 

+ 1 - 2
src/models/Model.php

@@ -29,11 +29,10 @@ class Model {
 	}
 
 	// Удаляет запись по ID
-	public static function delete($id) {
+	public static function deleteById($id) {
 		$db = Database::getConnection();
 		$stm = $db->prepare("DELETE FROM ".static::$table_name." WHERE id=:id");
 		$stm->bindValue(":id", $id);
-		echo $stm->getSQL(true);
 		$stm->execute();
 	}
 }

+ 11 - 0
src/models/SubjectModel.php

@@ -1,4 +1,15 @@
 <?php
 class SubjectModel extends Model {
 	protected static $table_name = "regen_subjects";
+
+	// Создаёт запись в таблице
+	public static function create($name, $code, $teacher_id) : int {
+		$db = Database::getConnection();
+		$stm = $db->prepare("INSERT INTO ".static::$table_name." (name, code, teacher_id) VALUES (:name, :code, :teacher_id)");
+		$stm->bindValue(":name", $name);
+		$stm->bindValue(":code", $code);
+		$stm->bindValue(":teacher_id", $teacher_id);
+		$stm->execute();
+		return $db->lastInsertRowID();
+	}
 }

+ 1 - 1
src/views/HomeView.php

@@ -23,7 +23,7 @@ class HomeView extends LayoutView {
 		</div>
 		<div class='action'>
 			<img src="/img/actions/archive.png">
-			<a href="/regen/new">Архив отчётов<span class='stretched-link'></span></a>
+			<a href="/regen/archive">Архив отчётов<span class='stretched-link'></span></a>
 		</div>
 		<div class='action'>
 			<img src="/img/actions/exam.png">

+ 2 - 1
src/views/LayoutView.php

@@ -32,11 +32,12 @@ class LayoutView extends View {
 		<meta name="viewport" content="width=device-width, initial-scale=1.0">
 		<meta http-equiv="Cache-control" content="public">
 		<title><?= $this->page_title ?></title>
+		<link rel='icon' type='image/png' href='/img/favicons/pockit.png'>
 		<link rel="stylesheet" href="/jqueryui/themes/base/jquery-ui.min.css">
 		<link rel="stylesheet" href="/css/pockit.css">
 		<script src="/jquery/jquery.min.js"></script>
 		<script src="/jqueryui/jquery-ui.min.js"></script>
-
+		<script src="/js/pockit.js"></script>
 		<?php $this->customHead() ?>
 	</head>
 	<body>

+ 46 - 0
src/views/RegenArchiveView.php

@@ -0,0 +1,46 @@
+<?php
+// Архив Regen
+
+class RegenArchiveView extends LayoutView {
+	protected $subjects;
+	
+	public function content():void { ?>
+
+<div class='text-center'>
+	<h1>Архив отчётов</h1>
+	<h3>Выбери дисциплину</h3>
+</div>
+
+<div id='subjectsList' class='card'>
+	<?php while ($subject = $this->subjects->fetchArray()) { ?>
+		<div id='subject<?= $subject['id'] ?>' class='crud-item'>
+			<p><?= $subject['name'] ?></p>
+			<div class='crud-buttons'>
+				<button>Отчёты</button>
+				<button>Изменить</button>
+				<button onclick='crudDelete("subjects", <?= $subject['id'] ?>, "subject<?= $subject['id'] ?>")' class='danger'>Удалить</button>
+			</div>
+		</div>
+	<?php } ?>
+
+	<button onclick='crudCreateShowWindow("subjects", {"Название": {type: "plain", name: "name"}, "Шифр": {type: "plain", name: "code"}, "Преподаватель": {type: "crudRead", name: "teacher_id", route: "teachers"}}, "Добавление дисциплины", createSubject)' class='createbutton form-control'>Добавить</button>
+</div>
+
+<script>
+	function createSubject(subject) {
+
+        $("#subjectsList").prepend($(`
+            <div id='subject`+subject.id+`' class='crud-item'>
+                <p>`+subject.name+`</p>
+                <div class='crud-buttons'>
+                    <button>Отчёты</button>
+                    <button>Изменить</button>
+                    <button onclick='crudDelete("subjects", `+subject.id+`, "subject`+subject.id+`")' class='danger'>Удалить</button>
+                </div>
+            </div>
+            `));
+	}
+</script>
+
+<?php }
+}