Панель настроек, асинхронные запросы, калькулятор лазерной резки
This commit is contained in:
parent
fdd6e010ec
commit
503e25647c
|
@ -0,0 +1 @@
|
|||
./vendor
|
|
@ -0,0 +1,157 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\calculator\controllers;
|
||||
|
||||
use mirzaev\calculator\controllers\core;
|
||||
|
||||
use mirzaev\calculator\models\accounts_model as accounts;
|
||||
|
||||
/**
|
||||
* Контроллер пользователей
|
||||
*
|
||||
* @package mirzaev\calculator\controllers
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class accounts_controller extends core
|
||||
{
|
||||
/**
|
||||
* Страница профиля
|
||||
*
|
||||
* @param array $params
|
||||
* @return void
|
||||
*/
|
||||
public function index(array $vars = []): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Регистрация
|
||||
*
|
||||
* @param array $vars Параметры запроса
|
||||
*
|
||||
* @return string|null HTML-документ
|
||||
*/
|
||||
public function registration(array $vars = []): ?string
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['account' => []];
|
||||
|
||||
if ($vars['account'] = accounts::registration(email: $vars['email'] ?? null, password: $vars['password'] ?? null, errors: $vars['errors']['account'])) {
|
||||
// Удалось зарегистрироваться
|
||||
|
||||
if ($vars['account'] = accounts::authentication($vars['email'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), $vars)) {
|
||||
// Удалось аутентифицироваться
|
||||
} else {
|
||||
// Не удалось аутентифицироваться
|
||||
|
||||
// Запись кода ответа
|
||||
http_response_code(401);
|
||||
}
|
||||
} else {
|
||||
// Не удалось зарегистрироваться
|
||||
|
||||
// Запись кода ответа
|
||||
http_response_code(401);
|
||||
}
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Аутентификация
|
||||
*
|
||||
* @param array $vars Параметры запроса
|
||||
*
|
||||
* @return string|null HTML-документ
|
||||
*/
|
||||
public function authentication(array $vars = []): ?string
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['account' => []];
|
||||
|
||||
if ($vars['account'] = accounts::authentication($vars['email'] ?? null, $vars['password'] ?? null, (bool) ($vars['remember'] ?? false), errors: $vars['errors']['account'])) {
|
||||
// Удалось аутентифицироваться
|
||||
} else {
|
||||
// Не удалось аутентифицироваться
|
||||
|
||||
// Запись кода ответа
|
||||
http_response_code(401);
|
||||
}
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Деаутентификация
|
||||
*
|
||||
* @param array $vars Параметры запроса
|
||||
*
|
||||
* @return string|null HTML-документ
|
||||
*/
|
||||
public function deauthentication(array $vars = []): ?string
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['account' => []];
|
||||
|
||||
if (accounts::deauthentication(errors: $vars['errors']['account'])) {
|
||||
// Удалось деаутентифицироваться
|
||||
|
||||
// Деинициализация аккаунта
|
||||
$vars['account'] = null;
|
||||
} else {
|
||||
// Не удалось деаутентифицироваться
|
||||
|
||||
// Запись кода ответа
|
||||
http_response_code(500);
|
||||
}
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Данные аккаунта
|
||||
*
|
||||
* Если информацию запрашивает администратор, то вернётся вся, иначе только разрешённая публично
|
||||
*
|
||||
* @param array $vars Параметры запроса
|
||||
*
|
||||
* @return string JSON-документ
|
||||
*/
|
||||
public function data(array $vars = []): ?string
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['account' => []];
|
||||
|
||||
if ($account = accounts::read(['id' => $vars['id']], $vars['errors'])) {
|
||||
// Найдены данные запрашиваемого аккаунта
|
||||
|
||||
// Инициализация аккаунта
|
||||
$vars['account'] = accounts::account($vars['errors']);
|
||||
|
||||
if ($vars['account'] && $vars['account']['permissions']['accounts'] ?? 0 === 1) {
|
||||
// Удалось аутентифицироваться и пройдена проверка авторизации
|
||||
} else {
|
||||
// Не удалось аутентифицироваться
|
||||
|
||||
// Удаление запрещённых к публикации полей
|
||||
$account['password'] = $account['hash'] = $account['time'] = null;
|
||||
}
|
||||
|
||||
// Генерация ответа
|
||||
return json_encode($account ?? '');
|
||||
} else {
|
||||
// Не найдены данные запрашиваемого аккаунта
|
||||
|
||||
// Запись кода ответа
|
||||
http_response_code(404);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace mirzaev\calculator\controllers;
|
||||
|
||||
use mirzaev\calculator\controllers\core;
|
||||
use mirzaev\calculator\models\calculators_model as calculators;
|
||||
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use Twig\Environment as view;
|
||||
|
@ -119,4 +120,71 @@ final class calculator_controller extends core
|
|||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'modules' . DIRECTORY_SEPARATOR . 'calculators' . DIRECTORY_SEPARATOR . 'laser.html', $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Рассчёт
|
||||
*
|
||||
* Генерирует ответ в виде ['expenses' => 0, 'income' => 0, 'profit' => 0]
|
||||
*
|
||||
* @param array $vars Параметры
|
||||
*
|
||||
* @todo
|
||||
* 1. Отправлять данные в зависимости от разрешения (обычным пользователям только expenses)
|
||||
*/
|
||||
public function calculate(array $vars = []): ?string
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['calculators' => []];
|
||||
|
||||
// Инициализация калькуляторов из тела запроса (подразумевается, что там массивы с параметрами)
|
||||
$calculators = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
// Инициализация переменных для буфера вывода
|
||||
$machines = $managers = $engineers = $operators = 0;
|
||||
|
||||
foreach ($calculators as $i => $calculator) {
|
||||
// Перебор калькуляторов
|
||||
|
||||
foreach (['type'] as $parameter) {
|
||||
// Перебор мета-параметров
|
||||
|
||||
// Инициализация общего параметра
|
||||
extract([$parameter => $calculator[$parameter] ?? null]);
|
||||
|
||||
// Инициализация параметра для обработчика калькулятора
|
||||
unset($calculator[$parameter]);
|
||||
}
|
||||
|
||||
// Инициализация номера калькулятора в его категории
|
||||
$number = count($vars['errors']['calculators'][$type] ?? []);
|
||||
|
||||
// Инициализация журнала ошибок для калькулятора
|
||||
$calculator['errors'] = [];
|
||||
|
||||
// Инициализация журнала ошибок для буфера вывода
|
||||
$vars['errors']['calculators'][$type][$number] = &$calculator['errors'];
|
||||
|
||||
// Инициализация буфера параметров
|
||||
$parameters = [];
|
||||
|
||||
// Инициализация параметра типа покупателя (подразумевается, что если не "entity", то "individual")
|
||||
$parameters['company'] = $calculator['buyer'] === 'entity';
|
||||
unset($calculator['buyer']);
|
||||
|
||||
// Перенос остальных параметров в буфер параметров
|
||||
$parameters += $calculator;
|
||||
|
||||
// var_dump($parameters);
|
||||
|
||||
// Расчёт
|
||||
[$machines, $managers, $engineers, $operators] = calculators::$type(...$parameters);
|
||||
}
|
||||
|
||||
return json_encode([
|
||||
'machines' => $machines,
|
||||
'managers' => $managers,
|
||||
'engineers' => $engineers,
|
||||
'operators' => $operators
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace mirzaev\calculator\controllers;
|
||||
|
||||
use mirzaev\calculator\views\manager;
|
||||
use mirzaev\calculator\models\core as models;
|
||||
|
||||
use mirzaev\minimal\controller;
|
||||
|
||||
|
@ -25,6 +26,9 @@ class core extends controller
|
|||
{
|
||||
parent::__construct();
|
||||
|
||||
// Инициализация ядра моделей (соединение с базой данных...)
|
||||
new models();
|
||||
|
||||
$this->view = new manager;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,12 +19,18 @@ final class errors_controller extends core
|
|||
{
|
||||
public function error404()
|
||||
{
|
||||
// Запись кода ответа
|
||||
http_response_code(404);
|
||||
|
||||
// Генерация представления
|
||||
return 'Не найдено (404)';
|
||||
}
|
||||
|
||||
public function error500()
|
||||
{
|
||||
// Запись кода ответа
|
||||
http_response_code(500);
|
||||
|
||||
// Генерация представления
|
||||
return 'Внутренняя ошибка (500)';
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ declare(strict_types=1);
|
|||
namespace mirzaev\calculator\controllers;
|
||||
|
||||
use mirzaev\calculator\controllers\core;
|
||||
use mirzaev\calculator\models\accounts_model as accounts;
|
||||
|
||||
use Twig\Loader\FilesystemLoader;
|
||||
use Twig\Environment as view;
|
||||
|
@ -17,9 +18,15 @@ use Twig\Environment as view;
|
|||
*/
|
||||
final class main_controller extends core
|
||||
{
|
||||
public function index(array $params = [])
|
||||
public function index(array $vars = [])
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['account' => []];
|
||||
|
||||
// Проверка аутентифицированности
|
||||
$vars['account'] = accounts::account($vars['errors']);
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html');
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'main' . DIRECTORY_SEPARATOR . 'index.html', $vars);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,91 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\calculator\controllers;
|
||||
|
||||
use mirzaev\calculator\controllers\core;
|
||||
use mirzaev\calculator\models\accounts_model as accounts;
|
||||
use mirzaev\calculator\models\settings_model as settings;
|
||||
|
||||
/**
|
||||
* Контроллер страницы настроек
|
||||
*
|
||||
* @package mirzaev\calculator\controllers
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class settings_controller extends core
|
||||
{
|
||||
/**
|
||||
* Настройки (страница)
|
||||
*
|
||||
* HTML-документ со списком настроек
|
||||
*
|
||||
* @param array $vars Параметры
|
||||
*/
|
||||
public function index(array $vars = []): ?string
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['settings' => []];
|
||||
|
||||
// Инициализация аккаунта
|
||||
$vars['account'] = accounts::account($vars['errors']);
|
||||
|
||||
// Генерация представления
|
||||
return $this->view->render(DIRECTORY_SEPARATOR . 'settings' . DIRECTORY_SEPARATOR . 'index.html', $vars);
|
||||
}
|
||||
|
||||
/**
|
||||
* Записать
|
||||
*
|
||||
* @param array $vars Параметры
|
||||
*/
|
||||
public function write(array $vars = []): ?bool
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['settings' => []];
|
||||
|
||||
// Инициализация аккаунта
|
||||
$vars['account'] = accounts::account($vars['errors']);
|
||||
|
||||
|
||||
if ($vars['account'] && $vars['account']['permissions']['settings'] ?? 0 === 1) {
|
||||
// Удалось аутентифицироваться и пройдена проверка авторизации
|
||||
|
||||
foreach ($vars['settings'] ?? [] as $name => $value) {
|
||||
// Перебор настроек
|
||||
|
||||
settings::write($name, $value);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Прочитать
|
||||
*
|
||||
* @param array $vars Параметры
|
||||
*/
|
||||
public function read(array $vars = []): ?string
|
||||
{
|
||||
// Инициализация журнала ошибок
|
||||
$vars['errors'] = ['settings' => []];
|
||||
|
||||
// Инициализация аккаунта
|
||||
$vars['account'] = accounts::account($vars['errors']);
|
||||
|
||||
// Инициализация буфера вывода
|
||||
$settings = [];
|
||||
|
||||
foreach ($vars['settings'] ?? [] as $name) {
|
||||
// Перебор настроек
|
||||
|
||||
$settings[] = settings::read($name, $vars['account'] && $vars['account']['permissions']['settings'] ?? 0 === 1);
|
||||
}
|
||||
|
||||
return json_encode($settings);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,531 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\calculator\models;
|
||||
|
||||
use pdo;
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Модель регистрации, аутентификации и авторизации
|
||||
*
|
||||
* @package mirzaev\calculator\models
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class accounts_model extends core
|
||||
{
|
||||
/**
|
||||
* Регистрация
|
||||
*
|
||||
* @param string $name Входной псевдоним
|
||||
* @param string $email Почта
|
||||
* @param string $password Пароль (password)
|
||||
* @param bool $authentication Автоматическая аутентификация в случае успешной регистрации
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array|bool Аккаунт, если удалось аутентифицироваться
|
||||
*/
|
||||
public static function registration(string $name = null, string $email = null, string $password, array &$errors = []): array
|
||||
{
|
||||
try {
|
||||
if (static::account($errors)) {
|
||||
// Аутентифицирован пользователь
|
||||
|
||||
// Запись ошибки
|
||||
throw new exception('Уже аутентифицирован');
|
||||
}
|
||||
|
||||
if (empty($account = static::read(['name' => $name]) or $account = static::read(['email' => $email]))) {
|
||||
// Не удалось найти аккаунт
|
||||
|
||||
if (static::write($name, $email, $password, $errors)) {
|
||||
// Удалось зарегистрироваться
|
||||
|
||||
return $account;
|
||||
}
|
||||
} else {
|
||||
// Удалось найти аккаунт
|
||||
|
||||
return $account;
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Аутентификация
|
||||
*
|
||||
* @param string $login Входной псевдоним
|
||||
* @param string $password Пароль (password)
|
||||
* @param bool $remember Функция "Запомнить меня" - увеличенное время хранения cookies
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array Аккаунт (если не найден, то пустой массив)
|
||||
*/
|
||||
public static function authentication(string $login, string $password, bool $remember = false, array &$errors = []): array
|
||||
{
|
||||
try {
|
||||
if (static::account($errors)) {
|
||||
// Аутентифицирован пользователь
|
||||
|
||||
// Запись ошибки
|
||||
throw new exception('Уже аутентифицирован');
|
||||
}
|
||||
|
||||
|
||||
if (empty($account = static::read(['name' => $login]) or $account = static::read(['email' => $login]))) {
|
||||
// Не удалось найти аккаунт
|
||||
|
||||
throw new exception('Не удалось найти аккаунт');
|
||||
}
|
||||
|
||||
if (password_verify($password, $account['password'])) {
|
||||
// Совпадают хеши паролей
|
||||
|
||||
// Инициализация идентификатора сессии
|
||||
session_id($account['id']);
|
||||
|
||||
// Инициализация названия сессии
|
||||
session_name('id');
|
||||
|
||||
// Инициализация сессии
|
||||
session_start();
|
||||
|
||||
// Инициализация времени хранения хеша
|
||||
$time = time() + ($remember ? 604800 : 86400);
|
||||
|
||||
// Инициализация хеша
|
||||
$hash = static::hash((int) $account['id'], crypt($account['password'], time() . $account['id']), $time, $errors)['hash'];
|
||||
|
||||
// Инициализация cookies
|
||||
setcookie("hash", $hash, $time, path: '/', secure: true);
|
||||
|
||||
return $account;
|
||||
} else {
|
||||
// Не совпадают хеши паролей
|
||||
|
||||
throw new exception('Неправильный пароль');
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Аутентификация
|
||||
*
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return bool Удалось ли деаутентифицироваться
|
||||
*/
|
||||
public static function deauthentication(array &$errors = []): bool
|
||||
{
|
||||
try {
|
||||
if ($account = static::account($errors)) {
|
||||
// Аутентифицирован пользователь
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("UPDATE `accounts` SET `hash` = null, `time` = 0 WHERE `id` = :id");
|
||||
|
||||
// Параметры запроса
|
||||
$params = [
|
||||
":id" => $account['id'],
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
$request->fetch(pdo::FETCH_ASSOC);
|
||||
|
||||
// Деинициализация cookies
|
||||
setcookie("id", '', 0, path: '/', secure: true);
|
||||
setcookie("hash", '', 0, path: '/', secure: true);
|
||||
|
||||
return true;
|
||||
} else {
|
||||
// Не аутентифицирован пользователь
|
||||
|
||||
// Запись ошибки
|
||||
throw new exception('Не аутентифицирован');
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Прочитать данные аккаунта, если пользователь аутентифицирован
|
||||
*
|
||||
* Можно использовать как проверку на аутентифицированность
|
||||
*
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array Аккаунт (если не найден, то пустой массив)
|
||||
*
|
||||
* @todo 1. Сделать в static::read() возможность передачи нескольких параметров и перенести туда непосредственно чтение аккаунта с проверкой хеша
|
||||
*/
|
||||
public static function account(array &$errors = []): array
|
||||
{
|
||||
try {
|
||||
if (!empty($_COOKIE['id']) && !empty($_COOKIE['hash'])) {
|
||||
// Аутентифицирован аккаунт (найдены cookie и они хранят значения - подразумевается, что не null или пустое)
|
||||
|
||||
if ($_COOKIE['hash'] === static::hash((int) $_COOKIE['id'], errors: $errors)['hash']) {
|
||||
// Совпадает переданный хеш с тем, что хранится в базе данных
|
||||
} else {
|
||||
// Не совпадает переданный хеш с тем, что хранится в базе данных
|
||||
|
||||
// Генерация ошибки
|
||||
throw new exception('Вы аутентифицированы с другого устройства (не совпадают хеши аутентификации)');
|
||||
}
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("SELECT * FROM `accounts` WHERE `id` = :id && `hash` = :hash");
|
||||
|
||||
// Параметры запроса
|
||||
$params = [
|
||||
":id" => $_COOKIE['id'],
|
||||
":hash" => $_COOKIE['hash'],
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
if (empty($account = $request->fetch(pdo::FETCH_ASSOC))) {
|
||||
// Не найдена связка идентификатора с хешем
|
||||
|
||||
// Генерация ошибки
|
||||
throw new exception('Не найден пользотватель или время аутентификации истекло');
|
||||
}
|
||||
|
||||
// Чтение разрешений
|
||||
$account['permissions'] = static::permissions((int) $account['id'], $errors);
|
||||
|
||||
return $account;
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Прочитать разрешения аккаунта
|
||||
*
|
||||
* @param int $id Идентификатор аккаунта
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array Разрешения аккаунта, если найдены
|
||||
*/
|
||||
public static function permissions(int $id, array &$errors = []): array
|
||||
{
|
||||
try {
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("SELECT * FROM `permissions` WHERE `id` = :id");
|
||||
|
||||
// Параметры запроса
|
||||
$params = [
|
||||
":id" => $id
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
if (empty($response = $request->fetch(pdo::FETCH_ASSOC))) {
|
||||
// Не найдены разрешения
|
||||
|
||||
// Генерация ошибки
|
||||
throw new exception('Не найдены разрешения');
|
||||
}
|
||||
|
||||
// Удаление ненужных данных
|
||||
unset($response['id']);
|
||||
|
||||
return $response;
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись пользователя в базу данных
|
||||
*
|
||||
* @param string|null $name Имя
|
||||
* @param string|null $email Почта
|
||||
* @param string|null $password Пароль
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array Аккаунт (если не найден, то пустой массив)
|
||||
*/
|
||||
public static function write(string|null $name = null, string|null $email = null, string|null $password = null, array &$errors = []): array
|
||||
{
|
||||
try {
|
||||
// Инициализация параметров запроса
|
||||
$params = [];
|
||||
|
||||
if (isset($name)) {
|
||||
try {
|
||||
// Проверка параметра
|
||||
if (iconv_strlen($name) < 3) throw new exception('Длина имени должна быть не менее 3 символов');
|
||||
if (iconv_strlen($name) > 60) throw new exception('Длина имени должна быть не более 60 символов');
|
||||
|
||||
// Запись в буфер параметров запроса
|
||||
$params[':name'] = $name;
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($email)) {
|
||||
try {
|
||||
// Проверка параметра
|
||||
if (filter_var($email, FILTER_VALIDATE_EMAIL) === false) throw new exception('Не удалось распознать почту');
|
||||
if (iconv_strlen($email) < 3) throw new exception('Длина почты должна быть не менее 3 символов');
|
||||
if (iconv_strlen($email) > 60) throw new exception('Длина почты должна быть не более 80 символов');
|
||||
|
||||
// Запись в буфер параметров запроса
|
||||
$params[':email'] = $email;
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($password)) {
|
||||
try {
|
||||
// Проверка параметра
|
||||
if (iconv_strlen($password) < 3) throw new exception('Длина пароля должна быть не менее 3 символов');
|
||||
if (iconv_strlen($password) > 60) throw new exception('Длина пароля должна быть не более 120 символов');
|
||||
|
||||
// Запись в буфер параметров запроса
|
||||
$params[':password'] = password_hash($password, PASSWORD_BCRYPT);
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
|
||||
goto end;
|
||||
}
|
||||
}
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("INSERT INTO `accounts` (" . (isset($name) ? '`name`' : '') . (isset($name) && isset($email) ? ', ' : '') . (isset($email) ? '`email`' : '') . ((isset($name) || isset($email)) && isset($password) ? ', ' : '') . (isset($password) ? '`password`' : '') . ") VALUES (" . (isset($name) ? ':name' : '') . (isset($name) && isset($email) ? ', ' : '') . (isset($email) ? ':email' : '') . ((isset($name) || isset($email)) && isset($password) ? ', ' : '') . (isset($password) ? ':password' : '') . ")");
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
$request->fetch(pdo::FETCH_ASSOC);
|
||||
|
||||
try {
|
||||
if (isset($name)) {
|
||||
// Передано имя аккаунта
|
||||
|
||||
// Чтение аккаунта
|
||||
$account = static::read(['name' => $name]);
|
||||
} else if (isset($email)) {
|
||||
// Передана почта аккаунта
|
||||
|
||||
// Чтение аккаунта
|
||||
$account = static::read(['email' => $email]);
|
||||
} else {
|
||||
// Не передано ни имя, ни почта
|
||||
|
||||
throw new exception('Не переданны данные для полноценной регистрации аккаунта');
|
||||
}
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("INSERT INTO `permissions` (`id`) VALUES (:id)");
|
||||
|
||||
// Инициализация параметров
|
||||
$params = [
|
||||
':id' => $account['id']
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
$request->fetch(pdo::FETCH_ASSOC);
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
// Конец выполнения
|
||||
end:
|
||||
|
||||
return isset($account) && $account ? $account : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Чтение пользователя из базы данных
|
||||
*
|
||||
* @param array $search Поиск ('поле' => 'значение'), работает только с одним полем
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array Аккаунт, если найден
|
||||
*/
|
||||
public static function read(array $search, array &$errors = []): array
|
||||
{
|
||||
try {
|
||||
// Инициализация данных для поиска
|
||||
$field = array_keys($search)[0] ?? null;
|
||||
$value = $search[$field] ?? null;
|
||||
|
||||
if (empty($field)) {
|
||||
// Получено пустое значение поля
|
||||
|
||||
// Запись ошибки
|
||||
throw new exception('Пустое значение поля для поиска');
|
||||
}
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("SELECT * FROM `accounts` WHERE `$field` = :field LIMIT 1");
|
||||
|
||||
// Параметры запроса
|
||||
$params = [
|
||||
":field" => $value,
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
if ($account = $request->fetch(pdo::FETCH_ASSOC)) {
|
||||
// Найден аккаунт
|
||||
|
||||
try {
|
||||
if ($permissions = static::permissions((int) $account['id'], $errors)) {
|
||||
// Найдены разрешения
|
||||
|
||||
// Запись в буфер данных аккаунта
|
||||
$account['permissions'] = $permissions;
|
||||
} else {
|
||||
// Не найдены разрешения
|
||||
|
||||
throw new exception('Не удалось найти и прочитать разрешения');
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
} else {
|
||||
// Не найден аккаунт
|
||||
|
||||
throw new exception('Не удалось найти аккаунт');
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return isset($account) && $account ? $account : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Запись или чтение хеша из базы данных
|
||||
*
|
||||
* @param int $id Идентификатор аккаунта
|
||||
* @param int|null $hash Хеш аутентифиакции
|
||||
* @param string|null $time Время хранения хеша
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array ['hash' => $hash, 'time' => $time]
|
||||
*/
|
||||
public static function hash(int $id, string|null $hash = null, int|null $time = null, array &$errors = []): array
|
||||
{
|
||||
try {
|
||||
if (isset($hash, $time)) {
|
||||
// Переданы хеш и его время хранения
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("UPDATE `accounts` SET `hash` = :hash, `time` = :time WHERE `id` = :id");
|
||||
|
||||
// Параметры запроса
|
||||
$params = [
|
||||
":id" => $id,
|
||||
":hash" => $hash,
|
||||
":time" => $time,
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
$request->fetch(pdo::FETCH_ASSOC);
|
||||
} else {
|
||||
// Не переданы хеш и его время хранения
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("SELECT `hash`, `time` FROM `accounts` WHERE `id` = :id");
|
||||
|
||||
// Параметры запроса
|
||||
$params = [
|
||||
":id" => $id,
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
extract((array) $request->fetch(pdo::FETCH_ASSOC));
|
||||
|
||||
if (!empty($response['time']) && $response['time'] <= time()) {
|
||||
// Истекло время жизни хеша
|
||||
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("UPDATE `accounts` SET `hash` = :hash, `time` = :time WHERE `id` = :id");
|
||||
|
||||
// Параметры запроса
|
||||
$params = [
|
||||
":id" => $id,
|
||||
":hash" => null,
|
||||
":time" => null,
|
||||
];
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute($params);
|
||||
|
||||
// Генерация ответа
|
||||
$response = $request->fetch(pdo::FETCH_ASSOC);
|
||||
|
||||
// Генерация ошибки
|
||||
throw new exception('Время аутентификации истекло');
|
||||
}
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return ['hash' => $hash, 'time' => $time];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\calculator\models;
|
||||
|
||||
use mirzaev\calculator\models\settings_model as settings;
|
||||
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Модель калькуляторов
|
||||
*
|
||||
* @package mirzaev\calculator\models
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class calculators_model extends core
|
||||
{
|
||||
/**
|
||||
* Рассчет калькулятора лазерной резки
|
||||
*
|
||||
* @param bool|int|string|null $company Юридическое лицо? Или физическое лицо?
|
||||
* @param string|null $complexity Сложность ('easy', 'medium', 'hard')
|
||||
* @param int|string|null $width Высота (мм)
|
||||
* @param int|string|null $height Ширина (мм)
|
||||
* @param int|string|null $lenght Длина (мм)
|
||||
* @param int|string|null $amount Количество
|
||||
* @param string|null $metal Тип металла
|
||||
* @param int|string|null $holes Количество отверстий
|
||||
* @param int|string|null $diameter Диаметр отверстий (мм)
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array|bool Аккаунт, если удалось аутентифицироваться
|
||||
*
|
||||
* @todo
|
||||
* 1. Значения по умолчанию брать из настроек в базе данных
|
||||
*/
|
||||
public static function laser(
|
||||
bool|int|string|null $company = null,
|
||||
string|null $complexity = null,
|
||||
int|string|null $width = null,
|
||||
int|string|null $height = null,
|
||||
int|string|null $length = null,
|
||||
int|string|null $amount = null,
|
||||
string|null $metal = null,
|
||||
int|string|null $holes = null,
|
||||
int|string|null $diameter = null,
|
||||
array &$errors = []
|
||||
): array {
|
||||
try {
|
||||
// Инициализация переменных для буфера вывода
|
||||
$machine = $manager = $engineer = $operator = 0;
|
||||
|
||||
// Инициализация значений по умолчанию (см. @todo)
|
||||
$company = (bool) ($company ?? settings::read('default_buyer', $errors) === 'entity' ?? false);
|
||||
$complexity = (string) ($complexity ?? settings::read('default_complexity', $errors) ?? 'medium');
|
||||
$width = (int) ($width ?? settings::read('default_width', $errors) ?? 500);
|
||||
$height = (int) ($height ?? settings::read('default_height', $errors) ?? 500);
|
||||
$length = (int) ($length ?? settings::read('default_length', $errors) ?? 1);
|
||||
$amount = (int) ($amount ?? settings::read('default_amount', $errors) ?? 1);
|
||||
$metal = (string) ($metal ?? settings::read('default_metal', $errors) ?? 'stainless_steel');
|
||||
$holes = (int) ($holes ?? settings::read('default_holes', $errors) ?? 0);
|
||||
$diameter = (int) ($diameter ?? settings::read('default_diameter', $errors) ?? 0);
|
||||
|
||||
// Стоисмость киловатта электроэнергии
|
||||
$electricity = settings::read('electricity', $errors) ?? 6.5;
|
||||
|
||||
// Потребляемая электроэнергия станком (квт/ч)
|
||||
$power = settings::read('laser_power', $errors) ?? 2;
|
||||
|
||||
// 1 мм толщина = 220 мм/с рез у нержавеющей стали
|
||||
$speed = 220;
|
||||
|
||||
// Вычисление площади
|
||||
$area = $width * $height;
|
||||
|
||||
// Коэффициент сложности
|
||||
$coefficient = ($area <= (settings::read('coefficient_area_less', $errors) ?? 10000) || $area >= (settings::read('coefficient_area_more', $errors) ?? 100000) ? (settings::read('coefficient_area_degree', $errors) ?? 0.2) : 0) + match ($complexity) {
|
||||
'easy' => (settings::read('coefficient_complexity_easy', $errors) ?? 0.8),
|
||||
'medium' => (settings::read('coefficient_complexity_medium', $errors) ?? 1),
|
||||
'hard' => (settings::read('coefficient_complexity_hard', $errors) ?? 1.2),
|
||||
default => (settings::read('coefficient_complexity_medium', $errors) ?? 1)
|
||||
};
|
||||
|
||||
// Расчет длины реза (мм)
|
||||
eval(settings::read('laser_formula_cutting', $errors) ?? '$cutting = 3.14 * $diameter * $holes + ($width * 2 + $height * 2) * $coefficient;');
|
||||
|
||||
// Скорость реза в час (мм/с)
|
||||
eval(settings::read('laser_formula_speed', $errors) ?? '$speed = 3600 * $speed;');
|
||||
|
||||
// Стоимость реза в час
|
||||
eval(settings::read('laser_formula_hour', $errors) ?? '$hour = $electricity * $power;');
|
||||
|
||||
// Стоимость 1 миллиметра реза
|
||||
eval(settings::read('laser_formula_millimeter', $errors) ?? '$millimeter = $hour / $speed;');
|
||||
|
||||
// Cтанок (стоимость работы)
|
||||
eval(settings::read('laser_formula_machine', $errors) ?? '$machine = $cutting * $millimeter * $amount;');
|
||||
|
||||
var_dump($company, $complexity, $width, $height, $length, $amount, $metal, $holes, $diameter);
|
||||
|
||||
if ($company) {
|
||||
// Юридическое лицо
|
||||
|
||||
$min = settings::read('manager_entity_min', $errors) ?? 1;
|
||||
$max = settings::read('manager_entity_max', $errors) ?? 7;
|
||||
$average = ($min + $max) / 2;
|
||||
|
||||
// Менеджер (стоимость работы)
|
||||
$manager = (settings::read('manager_entity_hour', $errors) ?? 200) * match ($complexity) {
|
||||
'easy' => $min,
|
||||
'medium' => $average,
|
||||
'hard' => $max,
|
||||
default => $average
|
||||
};
|
||||
|
||||
$min = settings::read('engineer_entity_min', $errors) ?? 2;
|
||||
$max = settings::read('engineer_entity_max', $errors) ?? 72;
|
||||
$average = ($min + $max) / 2;
|
||||
|
||||
// Инженер (стоимость работы)
|
||||
$engineer = (settings::read('engineer_entity_hour', $errors) ?? 400) * match ($complexity) {
|
||||
'easy' => $min,
|
||||
'medium' => $average,
|
||||
'hard' => $max,
|
||||
default => $average
|
||||
};
|
||||
|
||||
$min = settings::read('operator_entity_min', $errors) ?? 0.33;
|
||||
$max = settings::read('operator_entity_max', $errors) ?? 16;
|
||||
$average = ($min + $max) / 2;
|
||||
|
||||
// Оператор
|
||||
$operator = (settings::read('operator_entity_hour', $errors) ?? 200) * match ($complexity) {
|
||||
'easy' => $min,
|
||||
'medium' => $average,
|
||||
'hard' => $max,
|
||||
default => $average
|
||||
};
|
||||
} else {
|
||||
// Физическое лицо
|
||||
|
||||
$min = settings::read('manager_individual_min', $errors) ?? 1;
|
||||
$max = settings::read('manager_individual_max', $errors) ?? 3;
|
||||
$average = ($min + $max) / 2;
|
||||
|
||||
// Менеджер (стоимость работы)
|
||||
$manager = (settings::read('manager_individual_hour', $errors) ?? 200) * match ($complexity) {
|
||||
'easy' => $min,
|
||||
'medium' => $average,
|
||||
'hard' => $max,
|
||||
default => $average
|
||||
};
|
||||
|
||||
$min = settings::read('manager_individual_min', $errors) ?? 2;
|
||||
$max = settings::read('manager_individual_max', $errors) ?? 10;
|
||||
$average = ($min + $max) / 2;
|
||||
|
||||
// Инженер (стоимость работы)
|
||||
$engineer = (settings::read('engineer_individual_hour', $errors) ?? 300) * match ($complexity) {
|
||||
'easy' => $min,
|
||||
'medium' => $average,
|
||||
'hard' => $max,
|
||||
default => $average
|
||||
};
|
||||
|
||||
$min = settings::read('manager_individual_min', $errors) ?? 0.33;
|
||||
$max = settings::read('manager_individual_max', $errors) ?? 7;
|
||||
$average = ($min + $max) / 2;
|
||||
|
||||
// Оператор (стоимость работы)
|
||||
$operator = (settings::read('operator_individual_hour', $errors) ?? 200) * match ($complexity) {
|
||||
'easy' => $min,
|
||||
'medium' => $average,
|
||||
'hard' => $max,
|
||||
default => $average
|
||||
};
|
||||
}
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return [[$machine], [$manager], [$engineer], [$operator]];
|
||||
}
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\calculator\models;
|
||||
|
||||
use mirzaev\minimal\model;
|
||||
|
||||
use pdo;
|
||||
use pdoexception;
|
||||
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Ядро моделей
|
||||
*
|
||||
* @package mirzaev\calculator\models
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
class core extends model
|
||||
{
|
||||
/**
|
||||
* Соединение с базой данных
|
||||
*/
|
||||
protected static PDO $db ;
|
||||
|
||||
public function __construct(pdo $db = null)
|
||||
{
|
||||
if (isset($db)) {
|
||||
// Получена инстанция соединения с базой данных
|
||||
|
||||
// Запись и инициализация соединения с базой данных
|
||||
$this->__set('db', $db);
|
||||
} else {
|
||||
// Не получена инстанция соединения с базой данных
|
||||
|
||||
// Инициализация соединения с базой данных по умолчанию
|
||||
$this->__get('db');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Записать свойство
|
||||
*
|
||||
* @param string $name Название
|
||||
* @param mixed $value Значение
|
||||
*/
|
||||
public function __set(string $name, mixed $value = null): void
|
||||
{
|
||||
match ($name) {
|
||||
'db' => (function () use ($value) {
|
||||
if ($this->__isset('db')) {
|
||||
// Свойство уже было инициализировано
|
||||
|
||||
// Выброс исключения (неудача)
|
||||
throw new exception('Запрещено реинициализировать соединение с базой данных ($this->db)', 500);
|
||||
} else {
|
||||
// Свойство ещё не было инициализировано
|
||||
|
||||
if ($value instanceof pdo) {
|
||||
// Передано подходящее значение
|
||||
|
||||
// Запись свойства (успех)
|
||||
self::$db = $value;
|
||||
} else {
|
||||
// Передано неподходящее значение
|
||||
|
||||
// Выброс исключения (неудача)
|
||||
throw new exception('Соединение с базой данных ($this->db) должен быть инстанцией PDO', 500);
|
||||
}
|
||||
}
|
||||
})(),
|
||||
default => parent::__set($name, $value)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Прочитать свойство
|
||||
*
|
||||
* @param string $name Название
|
||||
*
|
||||
* @return mixed Содержимое
|
||||
*/
|
||||
public function __get(string $name): mixed
|
||||
{
|
||||
return match ($name) {
|
||||
'db' => (function () {
|
||||
if (!$this->__isset('db')) {
|
||||
// Свойство не инициализировано
|
||||
|
||||
// Инициализация значения по умолчанию исходя из настроек
|
||||
$this->__set('db', new pdo(\TYPE . ':dbname=' . \BASE . ';host=' . \HOST, LOGIN, PASSWORD));
|
||||
}
|
||||
|
||||
return self::$db;
|
||||
})(),
|
||||
default => parent::__get($name)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Проверить свойство на инициализированность
|
||||
*
|
||||
* @param string $name Название
|
||||
*/
|
||||
public function __isset(string $name): bool
|
||||
{
|
||||
return match ($name) {
|
||||
default => parent::__isset($name)
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Удалить свойство
|
||||
*
|
||||
* @param string $name Название
|
||||
*/
|
||||
public function __unset(string $name): void
|
||||
{
|
||||
match ($name) {
|
||||
default => parent::__isset($name)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Статический вызов
|
||||
*
|
||||
* @param string $name Название
|
||||
* @param array $arguments Параметры
|
||||
*/
|
||||
public static function __callStatic(string $name, array $arguments): mixed
|
||||
{
|
||||
match ($name) {
|
||||
'db' => (new static)->__get('db'),
|
||||
default => throw new exception("Не найдено свойство или функция: $name", 500)
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace mirzaev\calculator\models;
|
||||
|
||||
use pdo;
|
||||
use exception;
|
||||
|
||||
/**
|
||||
* Модель настроек
|
||||
*
|
||||
* @package mirzaev\calculator\models
|
||||
* @author Arsen Mirzaev Tatyano-Muradovich <arsen@mirzaev.sexy>
|
||||
*/
|
||||
final class settings_model extends core
|
||||
{
|
||||
/**
|
||||
* Прочитать
|
||||
*
|
||||
* @param string $name Название параметра
|
||||
* @param array &$errors Журнал ошибок
|
||||
*
|
||||
* @return array Аккаунт, если найден
|
||||
*/
|
||||
public static function read(string $name, array &$errors = []): int|float|string|array|null
|
||||
{
|
||||
try {
|
||||
// Инициализация запроса
|
||||
$request = static::$db->prepare("SELECT `$name` FROM `settings` WHERE `id` = 1 ORDER BY `id` DESC LIMIT 1");
|
||||
|
||||
// Отправка запроса
|
||||
$request->execute();
|
||||
|
||||
// Генерация ответа
|
||||
return $request->fetchColumn();
|
||||
} catch (exception $e) {
|
||||
// Запись в журнал ошибок
|
||||
$errors[] = $e->getMessage();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
|
@ -1,8 +1,21 @@
|
|||
#authentication>form {
|
||||
#authentication>:is(form, div) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#authentication .exit {
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
#authentication p {
|
||||
margin: 0;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#authentication p>span {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#authentication>form>input:is([type="text"], [type="password"]) {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
@ -13,6 +26,7 @@
|
|||
|
||||
#authentication>form>.submit {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 10px;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
|
@ -28,3 +42,26 @@
|
|||
border: unset;
|
||||
border-radius: 0 3px 3px 0;
|
||||
}
|
||||
|
||||
|
||||
#authentication>form>input[type=submit].registration {
|
||||
padding: 7px 20px;
|
||||
background-color: #86781C;
|
||||
}
|
||||
|
||||
#authentication>form>input[type=submit].registration:hover {
|
||||
background-color: #9e8d20;
|
||||
}
|
||||
|
||||
#authentication>form>input[type=submit].registration:is(:active, :focus) {
|
||||
background-color: #776b19;
|
||||
}
|
||||
|
||||
#authentication>form>ul.errors {
|
||||
margin-top: 18px;
|
||||
margin-bottom: 0px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
list-style: none;
|
||||
background-color: #ae8f8f;
|
||||
}
|
||||
|
|
|
@ -32,26 +32,49 @@
|
|||
margin: 8px auto 15px;
|
||||
}
|
||||
|
||||
#calculator>#result>:last-child {
|
||||
#calculator>#result>div {
|
||||
width: 30%;
|
||||
}
|
||||
|
||||
#calculator>#result>nav {
|
||||
margin-right: 30px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#calculator>#result>nav>a {
|
||||
margin-top: auto;
|
||||
}
|
||||
#calculator>#result>nav>a#calculate {
|
||||
padding: 10px 5px;
|
||||
}
|
||||
|
||||
#calculator>#result,
|
||||
#calculator>#result :last-child>p {
|
||||
#calculator>#result>div>p {
|
||||
font-weight: bold;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#calculator>#result :last-child>p * {
|
||||
#calculator>#result>div>p * {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
#calculator>#result>:last-child,
|
||||
#calculator>#result :last-child>p>:is(#expenses, #income, #profit) {
|
||||
#calculator>#result>div,
|
||||
#calculator>#result>div>p>:is(#expenses, #income, #profit) {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#calculator>#result :last-child>p>:last-child {
|
||||
#calculator>#result>nav>a:first-child:not(:only-of-type),
|
||||
#calculator>#result>div>p:first-child:not(:only-of-type) {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
#calculator>#result>nav>a:last-child,
|
||||
#calculator>#result>div>p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#calculator>#result>div>p>:last-child {
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
|
@ -59,58 +82,58 @@
|
|||
margin-top: 30px;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting']) {
|
||||
#calculator>.calculator {
|
||||
padding: 5px 30px 10px 30px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div {
|
||||
#calculator>.calculator>div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>label {
|
||||
#calculator>.calculator>div>label {
|
||||
font-weight: bold;
|
||||
margin: auto 15px auto auto;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div:not(:last-child) {
|
||||
#calculator>.calculator>div:not(:last-child) {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div {
|
||||
#calculator>.calculator>div>div {
|
||||
width: 60%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div {
|
||||
#calculator>.calculator>div>div {
|
||||
height: 30px;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>:is(input, small, span):not(.measured, :last-child) {
|
||||
#calculator>.calculator>div>div>:is(input, small, span):not(.measured, :last-child) {
|
||||
margin-right: 5px !important;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>:is(input, .unit) {
|
||||
#calculator>.calculator>div>div>:is(input, .unit) {
|
||||
font-size: small;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>input {
|
||||
#calculator>.calculator>div>div>input {
|
||||
width: 25px;
|
||||
padding: 5px 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>input.measured {
|
||||
#calculator>.calculator>div>div>input.measured {
|
||||
padding-right: 3px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>small {
|
||||
#calculator>.calculator>div>div>small {
|
||||
margin: auto 0;
|
||||
}
|
||||
|
||||
#calculator>:is([id^='laser'], [id^='plasma'], [id^='bending'], [id^='painting'])>div>div>input+.unit {
|
||||
#calculator>.calculator>div>div>input+.unit {
|
||||
margin: auto 0;
|
||||
padding-top: unset;
|
||||
padding-bottom: unset;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
* {
|
||||
font-family: "open sans";
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
body {
|
||||
|
@ -69,7 +70,7 @@ header>nav {
|
|||
top: 0;
|
||||
width: 100%;
|
||||
height: 40px;
|
||||
padding: 8px calc(18.5% - 20px);
|
||||
padding: 8px 20%;
|
||||
position: sticky;
|
||||
display: flex;
|
||||
pointer-events: all;
|
||||
|
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 64 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 64 KiB |
|
@ -8,6 +8,11 @@ use mirzaev\minimal\core;
|
|||
use mirzaev\minimal\router;
|
||||
|
||||
define('VIEWS', realpath('..' . DIRECTORY_SEPARATOR . 'views'));
|
||||
define('TYPE', 'mysql');
|
||||
define('BASE', 'calculator');
|
||||
define('HOST', '127.0.0.1');
|
||||
define('LOGIN', 'root');
|
||||
define('PASSWORD', '');
|
||||
|
||||
// Автозагрузка
|
||||
require __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
|
||||
|
@ -17,13 +22,22 @@ $router = new router;
|
|||
|
||||
// Запись маршрутов
|
||||
$router->write('/', 'main', 'index');
|
||||
$router->write('/calculator', 'calculator', 'index');
|
||||
$router->write('/calculator/generate/buyer', 'calculator', 'buyer');
|
||||
$router->write('/calculator/generate/complexity', 'calculator', 'complexity');
|
||||
$router->write('/calculator/generate/menu', 'calculator', 'menu');
|
||||
$router->write('/calculator/generate/result', 'calculator', 'result');
|
||||
$router->write('/calculator/generate/divider', 'calculator', 'divider');
|
||||
$router->write('/calculator/generate/laser', 'calculator', 'laser');
|
||||
$router->write('/account/registration', 'accounts', 'registration', 'POST');
|
||||
$router->write('/account/authentication', 'accounts', 'authentication', 'POST');
|
||||
$router->write('/account/deauthentication', 'accounts', 'deauthentication', 'POST');
|
||||
$router->write('/account/deauthentication', 'accounts', 'deauthentication', 'GET');
|
||||
$router->write('/account/data', 'accounts', 'data', 'POST');
|
||||
$router->write('/calculator', 'calculator', 'index', 'POST');
|
||||
$router->write('/calculator/generate/buyer', 'calculator', 'buyer', 'POST');
|
||||
$router->write('/calculator/generate/complexity', 'calculator', 'complexity', 'POST');
|
||||
$router->write('/calculator/generate/menu', 'calculator', 'menu', 'POST');
|
||||
$router->write('/calculator/generate/result', 'calculator', 'result', 'POST');
|
||||
$router->write('/calculator/generate/divider', 'calculator', 'divider', 'POST');
|
||||
$router->write('/calculator/generate/laser', 'calculator', 'laser', 'POST');
|
||||
$router->write('/calculator/calculate', 'calculator', 'calculate', 'POST');
|
||||
$router->write('/settings', 'settings', 'index', 'GET');
|
||||
$router->write('/settings/write', 'settings', 'write', 'POST');
|
||||
$router->write('/settings/read', 'settings', 'read', 'POST');
|
||||
|
||||
// Инициализация ядра
|
||||
$core = new Core(namespace: __NAMESPACE__, router: $router);
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
'use strict';
|
||||
|
||||
function remember_switch(target) {
|
||||
if (target.classList.contains('fa-unlock')) {
|
||||
// Найден "открытый замок"
|
||||
|
||||
// Перезапись на "закрытый замок"
|
||||
target.classList.remove('fa-unlock');
|
||||
target.classList.add('fa-lock');
|
||||
|
||||
// Изменение отправляемого значения
|
||||
document.querySelector('input[name=' + target.getAttribute('for') + ']').checked = true;
|
||||
} else {
|
||||
// Не найден "открытый замок", подразумевается, что найден "закрытый замок"
|
||||
|
||||
// Перезапись на "открытый замок"
|
||||
target.classList.remove('fa-lock');
|
||||
target.classList.add('fa-unlock');
|
||||
|
||||
// Изменение отправляемого значения
|
||||
document.querySelector('input[name=' + target.getAttribute('for') + ']').checked = false;
|
||||
}
|
||||
}
|
||||
|
||||
function authentication(form) {
|
||||
// Инициализация адреса отправки формы
|
||||
form.action = '/account/authentication';
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function registration(form) {
|
||||
// Инициализация адреса отправки формы
|
||||
form.action = '/account/registration';
|
||||
|
||||
return true;
|
||||
}
|
|
@ -3,32 +3,132 @@
|
|||
let calculator = {
|
||||
index: document.getElementById("calculator"),
|
||||
calculators: [],
|
||||
account: [],
|
||||
settings: {
|
||||
defaults: {
|
||||
buyer: 'individual',
|
||||
complexity: 'hard',
|
||||
complexity: 'medium',
|
||||
}
|
||||
},
|
||||
init() {
|
||||
// Инициализация калькулятора
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИЗБАВИТЬСЯ ОТ ТАЙМАУТОВ
|
||||
// !!!!!!!!!!!!!!!!! РАЗОБРАТЬСЯ С ПОРЯДКОМ ВЫПОЛНЕНИЯ
|
||||
|
||||
this.generate.buyer(this.settings.defaults.buyer);
|
||||
setTimeout(() => {
|
||||
this.generate.complexity(this.settings.defaults.complexity);
|
||||
setTimeout(() => {
|
||||
this.generate.menu();
|
||||
setTimeout(() => {
|
||||
// this.calculate();
|
||||
this.generate.result();
|
||||
}, 100);
|
||||
}, 100);
|
||||
}, 100);
|
||||
this.generate.buyer(this.settings.defaults.buyer)
|
||||
.then(this.generate.complexity(this.settings.defaults.complexity)
|
||||
.then(this.generate.menu()
|
||||
.then(this.authenticate(cookie.read('id'))
|
||||
.then(success => {
|
||||
// Запись данных аккаунта
|
||||
this.account = success;
|
||||
|
||||
if (this.account !== undefined && typeof this.account === 'object' && this.account.permissions !== undefined) {
|
||||
// Найден аккаунт
|
||||
|
||||
if (this.account.permissions.calculate == 1) {
|
||||
// Разрешено использовать калькулятор
|
||||
|
||||
this.generate.result();
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Инициализирован');
|
||||
},
|
||||
calculate(repeated = false) {
|
||||
authenticate(id) {
|
||||
// Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)'
|
||||
|
||||
return fetch('/account/data?id=' + id, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json().then(
|
||||
success => {
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружены данные пользователя: ' + id);
|
||||
|
||||
return success;
|
||||
},
|
||||
error => {
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить данные пользователя: ' + id);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
calculate() {
|
||||
// Запрос и генерация HTML с данными о рассчете со всех калькуляторов
|
||||
|
||||
// Инициализация буферов вывода
|
||||
let expenses, income, profit;
|
||||
|
||||
// Инициализация буфера запроса
|
||||
let query = {};
|
||||
|
||||
for (const number in this.calculators) {
|
||||
// Перебор калькуляторов
|
||||
|
||||
// Инициализация буфера запроса для нового калькулятора
|
||||
query[number] = {};
|
||||
|
||||
// Инициализация типа калькулятора
|
||||
query[number]['type'] = this.calculators[number].getAttribute('data-calculator');
|
||||
|
||||
for (const buyer of this.index.querySelectorAll('input[name="buyer"]')) {
|
||||
// Перебор полей с параметрами типа заказчика
|
||||
|
||||
if (buyer.checked) {
|
||||
// Найдено выбранное поле
|
||||
|
||||
// Запись в буфер запроса
|
||||
query[number]['buyer'] = buyer.value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const complexity of this.index.querySelectorAll('input[name="complexity"]')) {
|
||||
// Перебор полей с параметрами сложности
|
||||
|
||||
if (complexity.checked) {
|
||||
// Найдено выбранное поле
|
||||
|
||||
// Запись в буфер запроса
|
||||
query[number]['complexity'] = complexity.value;
|
||||
}
|
||||
}
|
||||
|
||||
for (const field of this.calculators[number].querySelectorAll('[data-calculator-parameter]')) {
|
||||
// Перебор полей с параметрами
|
||||
|
||||
// Запись в буфер запроса
|
||||
query[number][field.getAttribute('data-calculator-parameter')] = field.value ?? field.options[field.selectedIndex].text;
|
||||
}
|
||||
|
||||
// Сортировка
|
||||
query[number] = this.sort[query[number]['type']](query[number]);
|
||||
}
|
||||
|
||||
fetch('/calculator/calculate', {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/json" },
|
||||
body: JSON.stringify(query)
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
return response.json().then(
|
||||
success => {
|
||||
console.log('[КАЛЬКУЛЯТОР] Сгенерирован результат');
|
||||
|
||||
return success;
|
||||
},
|
||||
error => {
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось сгенерировать результат');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let result = document.getElementById("result");
|
||||
|
||||
if (result === null) {
|
||||
|
@ -36,121 +136,118 @@ let calculator = {
|
|||
|
||||
// Инициализия
|
||||
this.generate.result();
|
||||
|
||||
if (repeated === false) {
|
||||
// Это первое выполнение функции в потенциальном цикле
|
||||
|
||||
// Повтор операции
|
||||
this.calculate(true);
|
||||
}
|
||||
}
|
||||
},
|
||||
generate: {
|
||||
buyer(value = 'individual') {
|
||||
// Запрос и генерация HTML с данными о типе покупателя (юр. лицо и физ. лицо)
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
return fetch('/calculator/generate/buyer?value=' + value, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.text().then(
|
||||
success => {
|
||||
calculator.index.insertAdjacentHTML('beforeend', success);
|
||||
|
||||
request.open('GET', '/calculator/generate/buyer?value=' + value);
|
||||
|
||||
request.setRequestHeader('Content-Type', 'application/x-www-form-url');
|
||||
|
||||
request.addEventListener("readystatechange", () => {
|
||||
if (request.readyState === 4 && request.status === 200) {
|
||||
calculator.index.insertAdjacentHTML('beforeend', request.responseText);
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора типа покупателя');
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора типа покупателя');
|
||||
},
|
||||
error => {
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора типа покупателя');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
request.send();
|
||||
},
|
||||
complexity(value = 'medium') {
|
||||
// Запрос и генерация HTML с данными о сложности работы
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
return fetch('/calculator/generate/complexity?value=' + value, {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.text().then(
|
||||
success => {
|
||||
calculator.index.insertAdjacentHTML('beforeend', success);
|
||||
|
||||
request.open('GET', '/calculator/generate/complexity?value=' + value);
|
||||
|
||||
request.setRequestHeader('Content-Type', 'application/x-www-form-url');
|
||||
|
||||
request.addEventListener("readystatechange", () => {
|
||||
if (request.readyState === 4 && request.status === 200) {
|
||||
calculator.index.insertAdjacentHTML('beforeend', request.responseText);
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора сложности');
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками выбора сложности');
|
||||
},
|
||||
error => {
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками выбора сложности');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
request.send();
|
||||
},
|
||||
menu() {
|
||||
// Запрос и генерация HTML с кнопками добавления калькулятора
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
return fetch('/calculator/generate/menu', {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.text().then(
|
||||
success => {
|
||||
calculator.index.insertAdjacentHTML('beforeend', success);
|
||||
|
||||
request.open('GET', '/calculator/generate/menu');
|
||||
|
||||
request.setRequestHeader('Content-Type', 'application/x-www-form-url');
|
||||
|
||||
request.addEventListener("readystatechange", () => {
|
||||
if (request.readyState === 4 && request.status === 200) {
|
||||
calculator.index.insertAdjacentHTML('beforeend', request.responseText);
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками добавления калькулятора');
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с кнопками добавления калькулятора');
|
||||
},
|
||||
error => {
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с кнопками добавления калькулятора');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
request.send();
|
||||
},
|
||||
result() {
|
||||
// Запрос и генерация HTML с данными о результате калькуляции
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
return fetch('/calculator/generate/result', {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.text().then(
|
||||
success => {
|
||||
calculator.index.insertAdjacentHTML('beforeend', success);
|
||||
|
||||
request.open('GET', '/calculator/generate/result');
|
||||
|
||||
request.setRequestHeader('Content-Type', 'application/x-www-form-url');
|
||||
|
||||
request.addEventListener("readystatechange", () => {
|
||||
if (request.readyState === 4 && request.status === 200) {
|
||||
calculator.index.insertAdjacentHTML('beforeend', request.responseText);
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с данными о результате калькуляции');
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен элемент с данными о результате калькуляции');
|
||||
},
|
||||
error => {
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить элемент с данными о результате калькуляции');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
request.send();
|
||||
},
|
||||
divider(element, position) {
|
||||
// Запрос и генерация HTML с данными о результате калькуляции
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
return fetch('/calculator/generate/divider', {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.text().then(
|
||||
success => {
|
||||
if (element === undefined || position === undefined) {
|
||||
// Не задан элемент и позиция добавляемого разделителя
|
||||
|
||||
request.open('GET', '/calculator/generate/divider');
|
||||
// Запись разделителя в конце калькулятора
|
||||
calculator.index.insertAdjacentHTML('beforeend', success);
|
||||
} else {
|
||||
// Задан элемент и позиция добавляемого разделителя
|
||||
|
||||
request.setRequestHeader('Content-Type', 'application/x-www-form-url');
|
||||
// Запись разделителя по заданным параметрам
|
||||
element.insertAdjacentHTML(position, success);
|
||||
}
|
||||
|
||||
request.addEventListener("readystatechange", () => {
|
||||
if (request.readyState === 4 && request.status === 200) {
|
||||
|
||||
if (element === undefined || position === undefined) {
|
||||
// Не задан элемент и позиция добавляемого разделителя
|
||||
|
||||
// Запись разделителя в конце калькулятора
|
||||
calculator.index.insertAdjacentHTML('beforeend', request.responseText);
|
||||
} else {
|
||||
// Задан элемент и позиция добавляемого разделителя
|
||||
|
||||
// Запись разделителя по заданным параметрам
|
||||
element.insertAdjacentHTML(position, request.responseText);
|
||||
}
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен разделитель');
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен разделитель');
|
||||
},
|
||||
error => {
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось загрузить разделитель');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
request.send();
|
||||
},
|
||||
calculators: {
|
||||
laser() {
|
||||
|
@ -158,100 +255,133 @@ let calculator = {
|
|||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИЗБАВИТЬСЯ ОТ ТАЙМАУТОВ
|
||||
|
||||
const request = new XMLHttpRequest();
|
||||
return fetch('/calculator/generate/laser', {
|
||||
method: "POST",
|
||||
headers: { "content-type": "application/x-www-form-urlencoded" }
|
||||
}).then((response) => {
|
||||
if (response.status === 200) {
|
||||
response.text().then(
|
||||
success => {
|
||||
// Поиск последнего калькулятора
|
||||
let last = calculator.calculators[calculator.calculators.length - 1];
|
||||
|
||||
request.open('GET', '/calculator/generate/laser');
|
||||
|
||||
request.setRequestHeader('Content-Type', 'application/x-www-form-url');
|
||||
|
||||
request.addEventListener("readystatechange", () => {
|
||||
if (request.readyState === 4 && request.status === 200) {
|
||||
// Поиск последнего калькулятора
|
||||
let last = calculator.calculators[calculator.calculators.length - 1];
|
||||
|
||||
if (last !== undefined && last !== null) {
|
||||
// Найден калькулятор
|
||||
|
||||
// Запись калькулятора после последнего калькулятора
|
||||
last.insertAdjacentHTML('afterend', request.responseText);
|
||||
|
||||
setTimeout(() => {
|
||||
// Инициализация разделителя перед меню
|
||||
calculator.generate.divider(last, 'afterend');
|
||||
}, 100);
|
||||
} else {
|
||||
// Не найден калькулятор
|
||||
|
||||
// Поиск меню
|
||||
let menu = document.getElementById("menu");
|
||||
|
||||
if (menu !== null) {
|
||||
// Найдено меню
|
||||
|
||||
// Инициализация разделителя перед меню
|
||||
calculator.generate.divider(menu, 'beforebegin');
|
||||
|
||||
setTimeout(() => {
|
||||
// Запись калькулятора перед меню
|
||||
menu.insertAdjacentHTML('beforebegin', request.responseText);
|
||||
}, 100);
|
||||
} else {
|
||||
// Не найдено меню
|
||||
|
||||
// Поиск результатов калькуляции
|
||||
let result = document.getElementById("result");
|
||||
|
||||
if (result !== null) {
|
||||
// Найден элемент с результатами калькуляции
|
||||
if (last !== undefined && last !== null) {
|
||||
// Найден калькулятор
|
||||
|
||||
// Инициализация разделителя перед меню
|
||||
calculator.generate.divider(result, 'beforebegin');
|
||||
calculator.generate.divider(last, 'afterend').then(
|
||||
divider => {
|
||||
// Запись калькулятора после последнего калькулятора
|
||||
last.insertAdjacentHTML('afterend', success);
|
||||
|
||||
setTimeout(() => {
|
||||
// Запись калькулятора перед элементом с результатами калькуляции
|
||||
result.insertAdjacentHTML('beforebegin', request.responseText);
|
||||
}, 100);
|
||||
// Поиск калькуляторов
|
||||
let calculators = calculator.index.querySelectorAll('section[data-calculator]');
|
||||
|
||||
// Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
|
||||
calculator.calculators.push(calculators[calculators.length - 1]);
|
||||
|
||||
// Запись в журнал
|
||||
console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Не найден элемент с результатами калькуляции
|
||||
// Не найден калькулятор
|
||||
|
||||
// Инициализация разделителя перед меню
|
||||
calculator.generate.divider();
|
||||
// Поиск меню
|
||||
let menu = document.getElementById("menu");
|
||||
|
||||
setTimeout(() => {
|
||||
// Запись калькулятора в конце калькулятора
|
||||
calculator.index.insertAdjacentHTML('beforeend', request.responseText);
|
||||
}, 100);
|
||||
if (menu !== null) {
|
||||
// Найдено меню
|
||||
|
||||
// Инициализация разделителя перед меню
|
||||
calculator.generate.divider(menu, 'beforebegin').then(
|
||||
divider => {
|
||||
// Запись калькулятора перед меню
|
||||
menu.insertAdjacentHTML('beforebegin', success);
|
||||
|
||||
// Поиск калькуляторов
|
||||
let calculators = calculator.index.querySelectorAll('section[data-calculator]');
|
||||
|
||||
// Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
|
||||
calculator.calculators.push(calculators[calculators.length - 1]);
|
||||
|
||||
// Запись в журнал
|
||||
console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Не найдено меню
|
||||
|
||||
// Поиск результатов калькуляции
|
||||
let result = document.getElementById("result");
|
||||
|
||||
if (result !== null) {
|
||||
// Найден элемент с результатами калькуляции
|
||||
|
||||
// Инициализация разделителя перед меню
|
||||
calculator.generate.divider(result, 'beforebegin').then(
|
||||
divider => {
|
||||
// Запись калькулятора перед элементом с результатами калькуляции
|
||||
result.insertAdjacentHTML('beforebegin', success);
|
||||
|
||||
// Поиск калькуляторов
|
||||
let calculators = calculator.index.querySelectorAll('section[data-calculator]');
|
||||
|
||||
// Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
|
||||
calculator.calculators.push(calculators[calculators.length - 1]);
|
||||
|
||||
// Запись в журнал
|
||||
console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// Не найден элемент с результатами калькуляции
|
||||
|
||||
// Инициализация разделителя перед меню
|
||||
calculator.generate.divider().then(
|
||||
divider => {
|
||||
// Запись калькулятора в конце калькулятора
|
||||
calculator.index.insertAdjacentHTML('beforeend', success);
|
||||
|
||||
// Поиск калькуляторов
|
||||
let calculators = calculator.index.querySelectorAll('section[data-calculator]');
|
||||
|
||||
// Запись в реестр последнего калькулятора (подразумевается, что он новый и только что был записан)
|
||||
calculator.calculators.push(calculators[calculators.length - 1]);
|
||||
|
||||
// Запись в журнал
|
||||
console.log('[КАЛЬКУЛЯТОР] Инициализирован калькулятор лазерной резки');
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ИЗБАВИТЬСЯ ОТ ТАЙМАУТОВ
|
||||
|
||||
setTimeout(() => {
|
||||
// Поиск только что созданного калькулятора
|
||||
let laser = document.getElementById('laser');
|
||||
|
||||
if (laser !== null) {
|
||||
// Найден только что инициализированный (подразумевается) калькулятор лазерной резки
|
||||
|
||||
// Реинициализация идентификатора
|
||||
laser.id = 'laser_' + calculator.calculators.length;
|
||||
|
||||
// Запись калькулятора в реестр
|
||||
calculator.calculators.push(laser);
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Загружен калькулятор лазерной резки');
|
||||
} else {
|
||||
// Не найден только что инициализированный (подразумевается) калькулятор лазерной резки
|
||||
|
||||
console.log('[КАЛЬКУЛЯТОР] Не удалось инициализировать калькулятор лазерной резки');
|
||||
}
|
||||
}, 100);
|
||||
},
|
||||
error => {
|
||||
// Запись в журнал
|
||||
console.log('[КАЛЬКУЛЯТОР] [ОШИБКА] Не удалось инициализировать калькулятор лазерной резки');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
request.send();
|
||||
}
|
||||
}
|
||||
},
|
||||
sort: {
|
||||
laser(parameters) {
|
||||
// Сортировка параметров для отправки на сервер (динамически вызывается функция-обработчик)
|
||||
|
||||
return {
|
||||
type: 'laser',
|
||||
buyer: parameters['buyer'] ?? null,
|
||||
complexity: parameters['complexity'] ?? null,
|
||||
width: parameters['width'] ?? null,
|
||||
height: parameters['width'] ?? null,
|
||||
length: parameters['length'] ?? null,
|
||||
amount: parameters['amount'] ?? null,
|
||||
metal: parameters['metal'] ?? null,
|
||||
holes: parameters['holes'] ?? null,
|
||||
diameter: parameters['diameter'] ?? null,
|
||||
};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
'use strict';
|
||||
|
||||
let cookie = {
|
||||
read(name) {
|
||||
// Поиск по регулярному выражению
|
||||
let matches = document.cookie.match(new RegExp(
|
||||
"(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)"
|
||||
));
|
||||
|
||||
return matches ? decodeURIComponent(matches[1]) : undefined;
|
||||
},
|
||||
write(name, value, options = {}) {
|
||||
// Инициализация параметров
|
||||
options = {
|
||||
path: '/',
|
||||
...options
|
||||
};
|
||||
|
||||
if (options.expires instanceof Date) {
|
||||
// Передана инстанция Date
|
||||
|
||||
// Запись параметра истечения срока
|
||||
options.expires = options.expires.toUTCString();
|
||||
}
|
||||
|
||||
// Инициализация cookie
|
||||
let updatedCookie = encodeURIComponent(name) + "=" + encodeURIComponent(value);
|
||||
|
||||
for (let optionKey in options) {
|
||||
// Перебор параметров
|
||||
|
||||
// Запись в cookie названия параметра
|
||||
updatedCookie += "; " + optionKey;
|
||||
|
||||
// Инициализация значения параметра
|
||||
let optionValue = options[optionKey];
|
||||
|
||||
if (optionValue !== true) {
|
||||
// Найдено значение параметра
|
||||
|
||||
// Запись в cookie значения параметра
|
||||
updatedCookie += "=" + optionValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Конкатенация нового cookie с остальными
|
||||
document.cookie = updatedCookie;
|
||||
},
|
||||
delete(name) {
|
||||
// Удаление
|
||||
setCookie(name, "", {
|
||||
'max-age': -1
|
||||
})
|
||||
}
|
||||
};
|
|
@ -1,26 +1,39 @@
|
|||
<link href="/css/auth.css" rel="stylesheet">
|
||||
|
||||
<section id="authentication">
|
||||
{% if account is not empty %}
|
||||
<h3>Аккаунт</h3>
|
||||
<div>
|
||||
<p><b>Почта:</b> <span>{{ account.email }}</span></p>
|
||||
<a class="exit" type="button" href='/account/deauthentication'>Выход</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<h3>Аутентификация</h3>
|
||||
<form method="post" accept-charset="UTF-8">
|
||||
<input type="hidden" name="action" value="users/login">
|
||||
|
||||
<input id="loginName" type="text" name="loginName" placeholder="Почта"
|
||||
value="{{ craft.app.user.rememberedUsername }}">
|
||||
|
||||
<input id="password" type="password" name="password" placeholder="Пароль">
|
||||
<form method="POST" accept-charset="UTF-8">
|
||||
<input type="text" name="email" placeholder="Почта">
|
||||
<input type="password" name="password" placeholder="Пароль">
|
||||
|
||||
<div class="submit">
|
||||
<label class="button unselectable" for="rememberMe">
|
||||
<i class="fas fa-unlock"></i>
|
||||
</label>
|
||||
<input type="checkbox" name="rememberMe" value="1">
|
||||
|
||||
<input type="submit" value="Войти">
|
||||
<label class="button unselectable fas fa-unlock" for="remember"
|
||||
onclick="return remember_switch(this);"></label>
|
||||
<input type="checkbox" name="remember" value="1">
|
||||
<input type="submit" value="Войти" onclick="return authentication(this.parentElement.parentElement);">
|
||||
</div>
|
||||
<input type="submit" class="registration" value="Зарегистрироваться"
|
||||
onclick="return registration(this.parentElement);">
|
||||
|
||||
{% if errorMessage is defined %}
|
||||
<p>{{ errorMessage }}</p>
|
||||
{% if errors is not empty %}
|
||||
{% if errors.account is not empty %}
|
||||
<ul class="errors">
|
||||
{% for error in errors.account %}
|
||||
<li>{{ error }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
</section>
|
||||
|
||||
<script type="text/javascript" src="/js/auth.js" defer></script>
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
<div class="divider"></div>
|
||||
</section>
|
||||
|
||||
<script type="text/javascript" src="/js/cookie.js" defer></script>
|
||||
<script type="text/javascript" src="/js/calculator.js" defer></script>
|
||||
<script>
|
||||
if (document.readyState === "complete") {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section id="buyer">
|
||||
<section id="buyer" class="unselectable">
|
||||
<input id="individual" type="radio" name="buyer" value="individual" {% if buyer is same as('individual') %}checked{% endif %}>
|
||||
<label type="button" for="individual">Физическое лицо</label>
|
||||
<input id="entity" type="radio" name="buyer" value="entity" {% if buyer is same as('entity') %}checked{% endif %}>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
<h3>Лазерная резка</h3>
|
||||
<section id="laser">
|
||||
<section class="calculator" data-calculator="laser">
|
||||
<div>
|
||||
<label>Металл</label>
|
||||
<div>
|
||||
<select name="metal">
|
||||
<select name="metal" data-calculator-parameter="metal">
|
||||
<option value="steel">Сталь</option>
|
||||
<option value="stainless_steel" selected>Нержавеющая сталь</option>
|
||||
<option value="brass">Латунь</option>
|
||||
|
@ -15,30 +15,30 @@
|
|||
<div>
|
||||
<label>Размер</label>
|
||||
<div>
|
||||
<input type="text" class="measured" title="Длина" value="100">
|
||||
<input data-calculator-parameter="width" type="text" class="measured" title="Длина" value="100">
|
||||
<span class="unit unselectable">мм</span>
|
||||
<small>x</small>
|
||||
<input type="text" class="measured" title="Ширина" value="100">
|
||||
<input data-calculator-parameter="height" type="text" class="measured" title="Ширина" value="100">
|
||||
<span class="unit unselectable">мм</span>
|
||||
<small>x</small>
|
||||
<input type="text" class="measured" title="Толщина" value="1">
|
||||
<input data-calculator-parameter="length" type="text" class="measured" title="Толщина" value="1">
|
||||
<span class="unit unselectable">мм</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Отверстия</label>
|
||||
<div>
|
||||
<input type="text" class="measured" title="Количество" value="1">
|
||||
<input data-calculator-parameter="holes" type="text" class="measured" title="Количество" value="1">
|
||||
<span class="unit unselectable">шт</span>
|
||||
<small>x</small>
|
||||
<input type="text" class="measured" title="Диаметр" value="1">
|
||||
<input data-calculator-parameter="diameter" type="text" class="measured" title="Диаметр" value="1">
|
||||
<span class="unit unselectable">мм</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label>Количество</label>
|
||||
<div>
|
||||
<input type="text" class="measured" title="Количество" value="1">
|
||||
<input data-calculator-parameter="amount" type="text" class="measured" title="Количество" value="1">
|
||||
<span class="unit unselectable">шт</span>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section id="menu">
|
||||
<section id="menu" class="unselectable">
|
||||
<a type="button" onclick="calculator.generate.calculators.laser(); return false;">
|
||||
<img src="/img/laser.png" title="Добавить лазерную резку">
|
||||
Лазерная резка
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<section id="complexity">
|
||||
<section id="complexity" class="unselectable">
|
||||
<input id="easy" type="radio" name="complexity" value="easy" {% if complexity is same as('easy') %}checked{% endif %}>
|
||||
<label type="button" for="easy">
|
||||
<img src="/img/easy.png" title="Лёгкий уровень сложности">
|
||||
|
|
|
@ -1 +1 @@
|
|||
<div class="divider"></div>
|
||||
<div class="divider unselectable"></div>
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
<section id="result">
|
||||
<nav class="unselectable">
|
||||
<a id="calculate" type="button" onclick="return calculator.calculate();">Рассчитать</a>
|
||||
</nav>
|
||||
<div>
|
||||
<p>Расходы: <span id="expenses">0</span><span>рублей</span></p>
|
||||
<p>Доход: <span id="income">0</span><span>рублей</span></p>
|
||||
<p>Прибыль: <span id="profit">0</span><span>рублей</span></p>
|
||||
<p class="unselectable">Расходы: <span id="expenses">0</span><span>рублей</span></p>
|
||||
<p class="unselectable">Доход: <span id="income">0</span><span>рублей</span></p>
|
||||
<p class="unselectable">Прибыль: <span id="profit">0</span><span>рублей</span></p>
|
||||
</div>
|
||||
</section>
|
||||
|
|
|
@ -5,5 +5,10 @@
|
|||
</a>
|
||||
<a class="link" href="/contacts" title="Связь с администрацией">Контакты</a>
|
||||
<a class="link" href="/journal" title="Записи рассчётов">Журнал</a>
|
||||
{% if account is not empty %}
|
||||
{% if account.permissions.settings is defined and account.permissions.settings == 1 %}
|
||||
<a class="link" href="/settings" title="Настройки калькуляторов">Настройки</a>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</nav>
|
||||
</header>
|
||||
|
|
Loading…
Reference in New Issue