mirror of
https://github.com/IoTManagerProject/IoTManagerWeb.git
synced 2026-03-26 23:12:34 +03:00
1677 lines
58 KiB
Svelte
1677 lines
58 KiB
Svelte
<script>
|
||
import { onMount } from "svelte";
|
||
import Cookies, { get } from "js-cookie";
|
||
//import { Tabs, Tab, TabList, TabPanel } from "svelte-tabs";
|
||
import Toggle from "svelte-toggle";
|
||
import Chart from "svelte-frappe-charts";
|
||
import Logo from "./Logo.svelte";
|
||
import mqtt from "mqtt/dist/mqtt.min";
|
||
// -------------------Swipe-------------------------------------
|
||
|
||
import { Swipe, SwipeItem } from "svelte-swipe";
|
||
let swipe_holder_height = 0;
|
||
let active_item;
|
||
let SwipeComp;
|
||
function heightChanged({ detail }) {
|
||
if (detail.height < 700) {
|
||
swipe_holder_height = 700;
|
||
} else {
|
||
swipe_holder_height = detail.height;
|
||
}
|
||
}
|
||
let newPage;
|
||
function changeSlide(i) {
|
||
newPage = 0;
|
||
newPage = 1;
|
||
SwipeComp.goTo(i);
|
||
}
|
||
const swipeConfig = {
|
||
autoplay: false,
|
||
delay: 2000,
|
||
showIndicators: false,
|
||
transitionDuration: 1000,
|
||
defaultIndex: 0,
|
||
};
|
||
|
||
let items = [];
|
||
let MQTTconnections = [];
|
||
// темная тема
|
||
onMount(async () => {
|
||
if (Cookies.get("darktheme") == "true") {
|
||
window.document.body.classList.value = "dark-mode";
|
||
//window.document.body.classList.toggle("dark-mode");
|
||
}
|
||
});
|
||
|
||
function toggleTheme() {
|
||
// window.document.body.classList.toggle("dark-mode");
|
||
if (window.document.body.classList.value == "dark-mode") {
|
||
window.document.body.classList.value = "light-mode";
|
||
Cookies.set("darktheme", "False", { expires: 365 });
|
||
} else {
|
||
window.document.body.classList.toggle("dark-mode");
|
||
Cookies.set("darktheme", "true", { expires: 365 });
|
||
}
|
||
}
|
||
//секция переменных============================================
|
||
let myip = document.location.hostname;
|
||
let configSetupJson = "{}";
|
||
let espName = "IotManager";
|
||
let chipID = "";
|
||
let colors;
|
||
let lineOptions;
|
||
let axisOptions;
|
||
let graf_1 = [];
|
||
let graf_2 = [];
|
||
let NewWiget = "";
|
||
let NewPage = "";
|
||
let NewPrefics = "";
|
||
let date = "";
|
||
let hours = "";
|
||
let minutes = "";
|
||
let temp;
|
||
let selectedprefics;
|
||
// декларируем массив для списка всех ESP в сети
|
||
let devices = [];
|
||
// декларируем массив для списка всех websocket
|
||
let socket = [];
|
||
// декларируем массив для списка всех виджетов
|
||
let wigets = [];
|
||
// декларируем массив для списка всех страниц
|
||
let pages = [];
|
||
// декларируем массив для списка всех префиксов
|
||
let prefics = [{ prefics: "" }];
|
||
// выбраный префикс
|
||
let selected;
|
||
// массивы для построения графиков
|
||
let graf_time = [];
|
||
let graf_val = [];
|
||
let dataLine = [];
|
||
|
||
// ==на время разработки==
|
||
//myip = "192.168.36.127";
|
||
|
||
let connectionType = "MQTT";
|
||
let client;
|
||
let topic;
|
||
let connected = false;
|
||
|
||
let clientId = "IotManager_";
|
||
|
||
// MQTT====================================================================
|
||
function MQTTChange() {
|
||
// console.log("The selected option is " + JSON.stringify(selected));
|
||
Cookies.set("selectedMQTT", selected, { expires: 365 });
|
||
|
||
wigets = [];
|
||
prefics = [];
|
||
pages = [];
|
||
newPage = 0;
|
||
clientId += "_" + Math.floor(Math.random() * 10000);
|
||
connected = false;
|
||
const mqtt_options = {
|
||
clientId: clientId,
|
||
servers: [
|
||
{
|
||
host: selected.mqtt_host,
|
||
port: Number(selected.mqtt_port),
|
||
protocol: selected.connection_protocol,
|
||
},
|
||
],
|
||
path: "" + selected.mqtt_path,
|
||
protocolId: "MQTT",
|
||
protocolVersion: 4,
|
||
keepalive: 30,
|
||
clean: true,
|
||
reconnectPeriod: 100000,
|
||
connectTimeout: 30 * 1000,
|
||
rejectUnauthorized: false,
|
||
};
|
||
mqtt_options.username = selected.mqtt_username;
|
||
mqtt_options.password = selected.mqtt_password;
|
||
topic = selected.mqtt_prefix;
|
||
const url = `${mqtt_options.servers[0].protocol}://${mqtt_options.servers[0].host}:${mqtt_options.servers[0].port}${mqtt_options.path}`;
|
||
|
||
const onConnect = () => {
|
||
console.log(`MQTT connected ${url}`);
|
||
connected = client.connected;
|
||
const topic = selected.mqtt_prefix;
|
||
client.subscribe(topic + "/#", function (err) {
|
||
if (err) {
|
||
console.error(err);
|
||
} else {
|
||
console.log(`MQTT subscribed on topic '${topic}'`);
|
||
client.publish(topic, "HELLO");
|
||
}
|
||
});
|
||
};
|
||
|
||
const onMessage = (topic, message) => {
|
||
const msg = message.toString();
|
||
const time = new Date().getTime();
|
||
addMessage(msg, topic);
|
||
};
|
||
|
||
//try{
|
||
client = mqtt.connect(mqtt_options);
|
||
client.on("connect", onConnect);
|
||
client.on("message", onMessage);
|
||
client.on("error", (err) => {
|
||
console.log("MQTT", err);
|
||
client.end();
|
||
connected = client.connected;
|
||
});
|
||
client.on("close", () => {
|
||
console.log("MQTT " + clientId + " disconnected");
|
||
connected = client.connected;
|
||
});
|
||
}
|
||
if (connectionType == "MQTT") {
|
||
// ==на время разработки==
|
||
// MQTTconnections = JSON.parse(MQTTconnections);
|
||
|
||
MQTTconnections = [
|
||
{
|
||
user_id: "1",
|
||
connection_name: "demo",
|
||
connection_protocol: "wss",
|
||
mqtt_host: "meef.ru",
|
||
mqtt_port: "18883",
|
||
mqtt_prefix: "/demo",
|
||
mqtt_username: "IotManager:guest",
|
||
mqtt_password: "guest",
|
||
mqtt_path: "/ws",
|
||
mqtt_id: "mqtt_id",
|
||
},
|
||
{
|
||
user_id: "1",
|
||
connection_name: "IotManager",
|
||
connection_protocol: "wss",
|
||
mqtt_host: "meef.ru",
|
||
mqtt_port: "18883",
|
||
mqtt_prefix: "/IotManager",
|
||
mqtt_username: "IotManager:guest",
|
||
mqtt_password: "guest",
|
||
mqtt_path: "/ws",
|
||
mqtt_id: "mqtt_id",
|
||
},
|
||
];
|
||
|
||
if (Cookies.get("selectedMQTT")) {
|
||
selected = Cookies.get("selectedMQTT");
|
||
} else {
|
||
selected = MQTTconnections[0];
|
||
}
|
||
if (selected) {
|
||
selected = JSON.parse(selected);
|
||
}
|
||
MQTTChange();
|
||
|
||
if (Cookies.get("darktheme") == "") {
|
||
window.document.body.classList.value = "dark-mode";
|
||
}
|
||
if (!Cookies.get("darktheme")) {
|
||
window.document.body.classList.value = "dark-mode";
|
||
Cookies.set("darktheme", "true", { expires: 365 });
|
||
}
|
||
}
|
||
|
||
// MQTT =============================================================================================
|
||
|
||
// подключаемся к локальной машине, получаем карту сети
|
||
function getNetworkMap() {
|
||
devices = JSON.parse(
|
||
'[{"deviceID":"","deviceIP":"' + myip + '","deviceName":""}]'
|
||
);
|
||
devices[0].id = 0;
|
||
devices[0].status = false;
|
||
console.log("Подключаемся к WS на localhost ", devices);
|
||
if (connectionType != "MQTT") {
|
||
socket[0] = new WebSocket("ws://" + myip + "/ws");
|
||
socket[0].addEventListener("open", function (event) {
|
||
devices[0].id = 0;
|
||
devices[0].status = true;
|
||
connected = true;
|
||
devices = devices;
|
||
console.log("WS CONNECTED! " + myip);
|
||
// получаем "карту сети" по websocket
|
||
socket[0].send("getNetworkMap");
|
||
// получаем конфигурацию ESP по websocket
|
||
socket[0].send("HELLO");
|
||
});
|
||
socket[0].addEventListener("message", function (event) {
|
||
if (Cookies.get("consolelog") == "true") {
|
||
console.log("NEW data packet " + myip, event.data);
|
||
}
|
||
// запускаем обработку пришедшего сообщения
|
||
|
||
addMessage(event.data, 0);
|
||
});
|
||
// Обработка ошибок websocket
|
||
socket[0].addEventListener("close", (event) => {
|
||
if (item) {
|
||
console.log("ws close " + item.deviceIP);
|
||
} else {
|
||
console.log("ws close " + myip);
|
||
}
|
||
devices[0].status = false;
|
||
connected = false;
|
||
devices = devices;
|
||
});
|
||
socket[0].addEventListener("error", function (event) {
|
||
if (item) {
|
||
console.log(item.deviceIP + " WebSocket error: ", event);
|
||
} else {
|
||
console.log(myip + " WebSocket error: ", event);
|
||
}
|
||
devices[0].status = false;
|
||
connected = false;
|
||
devices = devices;
|
||
});
|
||
}
|
||
}
|
||
if (connectionType != "MQTT") {
|
||
getNetworkMap();
|
||
}
|
||
|
||
function addConnection(devices) {
|
||
if (devices) {
|
||
devices.forEach(function (item, i, arr) {
|
||
console.log("trying connect", item);
|
||
|
||
// Create WebSocket connection.
|
||
socket[i] = new WebSocket("ws://" + item.deviceIP + "/ws");
|
||
// Connection opened
|
||
socket[i].addEventListener("open", function (event) {
|
||
devices[i].id = i;
|
||
devices[i].status = true;
|
||
connected = true;
|
||
devices = devices;
|
||
console.log("WS CONNECTED! " + item.deviceIP);
|
||
socket[i].send("HELLO");
|
||
});
|
||
// Listen for messages
|
||
socket[i].addEventListener("message", function (event) {
|
||
// вывод отладочных сообщений в консоль
|
||
|
||
if (Cookies.get("consolelog") == "true") {
|
||
console.log("NEW data packet " + item.deviceIP, event.data);
|
||
}
|
||
|
||
// запускаем обработку пришедшего сообщения
|
||
let time = new Date().getTime();
|
||
addMessage(event.data, i);
|
||
});
|
||
// Обработка ошибок websocket
|
||
socket[i].addEventListener("close", (event) => {
|
||
console.log("ws close " + item.deviceIP);
|
||
devices[i].status = false;
|
||
connected = false;
|
||
devices = devices;
|
||
Shuttervisibil();
|
||
});
|
||
socket[i].addEventListener("error", function (event) {
|
||
console.log(item.deviceIP + " WebSocket error: ", event);
|
||
devices[i].status = false;
|
||
connected = false;
|
||
devices = devices;
|
||
Shuttervisibil();
|
||
});
|
||
});
|
||
}
|
||
}
|
||
//console.log(devices);
|
||
|
||
//обрабатываем пришедшее сообщение =====================================================================
|
||
|
||
function addMessage(data, socket) {
|
||
if (connectionType == "MQTT") {
|
||
// console.log("NEW data packet " + socket, data);
|
||
topic = socket;
|
||
}
|
||
|
||
let json;
|
||
let tmp;
|
||
|
||
try {
|
||
let net = JSON.parse(data);
|
||
|
||
// Если пришла карта сети
|
||
if (net[0].deviceID) {
|
||
console.log("пришла карта сети :", net);
|
||
devices = net;
|
||
//Conf=[];
|
||
|
||
addConnection(devices);
|
||
NetworkMap = data;
|
||
}
|
||
} catch (e) {
|
||
// console.log('ERROR parse JSON', tmp);
|
||
}
|
||
|
||
tmp = "[" + data + "]";
|
||
|
||
//tmp = [data];
|
||
try {
|
||
tmp = JSON.parse(tmp);
|
||
tmp.forEach(function (json, i, array) {
|
||
// собираем виджеты
|
||
// если пришедшее сообщение виджет
|
||
if (json.widget) {
|
||
// ищем его в массиве виджетов
|
||
NewWiget = "";
|
||
wigets.forEach(function (item, i, array) {
|
||
if (json.topic == item.topic) {
|
||
NewWiget = json.topic;
|
||
// console.log("Нашли совпадение виджета");
|
||
}
|
||
});
|
||
|
||
// ищем новые страницы
|
||
NewPage = "";
|
||
pages.forEach(function (item, i, array) {
|
||
if (json.page == item.page) {
|
||
NewPage = json.page;
|
||
// console.log("Нашли совпадение страницы");
|
||
}
|
||
});
|
||
|
||
// ищем новые префиксы
|
||
NewPrefics = "";
|
||
prefics.forEach(function (item, i, array) {
|
||
let topic = getprefics(json.topic);
|
||
if (topic == item.prefics) {
|
||
NewPrefics = item.prefics;
|
||
// console.log("Нашли совпадение префикса");
|
||
}
|
||
});
|
||
|
||
// Новый виджет дописываем в массив виджетов
|
||
if (!NewWiget) {
|
||
// дописываем виджету из какого сокета он получен
|
||
json.socket = socket;
|
||
// дописываем виджету префикс
|
||
json.prefics = getprefics(json.topic);
|
||
json.closed = true;
|
||
|
||
wigets = [...wigets, json];
|
||
|
||
// сортируем
|
||
wigets.sort((a, b) => a.order - b.order);
|
||
}
|
||
|
||
// Дописываем новую страницу в массив страниц
|
||
if (!NewPage) {
|
||
pages = [
|
||
...pages,
|
||
JSON.parse(
|
||
JSON.stringify({ page: json.page, topic: json.topic })
|
||
),
|
||
];
|
||
|
||
pages.sort(function (a, b) {
|
||
if (a.page < b.page) {
|
||
return -1;
|
||
}
|
||
if (a.page > b.page) {
|
||
return 1;
|
||
}
|
||
return 0;
|
||
});
|
||
}
|
||
|
||
// Дописываем новый префикс в массив префиксов
|
||
if (!NewPrefics) {
|
||
prefics = [
|
||
...prefics,
|
||
JSON.parse(JSON.stringify({ prefics: getprefics(json.topic) })),
|
||
];
|
||
}
|
||
}
|
||
// закончили сбор виджетов
|
||
|
||
// если новое сообщение статус
|
||
if (json.status) {
|
||
// ищем виджет к которому относится этот статус
|
||
wigets.forEach(function (element) {
|
||
//отличие MQTT и WS========================================================!!!!!!!!!!!!!!
|
||
let messegetopic;
|
||
if (connectionType === "MQTT") {
|
||
messegetopic = topic.replace("/status", "");
|
||
} else {
|
||
messegetopic = json.topic.replace("/status", "");
|
||
}
|
||
// ========================================================================
|
||
|
||
if (element.topic == messegetopic) {
|
||
// если получен статус график
|
||
|
||
if (element.widget == "chart") {
|
||
graf_time = [];
|
||
graf_val = [];
|
||
try {
|
||
temp = json.status;
|
||
temp.forEach(function (item, i, arr) {
|
||
// получаем время
|
||
date = new Date(item.x * 1000);
|
||
hours = date.getHours();
|
||
// minutes =date.getMinutes();
|
||
minutes = checkTime(date.getMinutes());
|
||
function checkTime(i) {
|
||
return i < 10 ? "0" + i : i;
|
||
}
|
||
let days = ["Вс", "Пн", "Вт", "Ср", "Чт", "Пт", "Сб"];
|
||
let dayW = days[date.getDay()];
|
||
//let dw = days[dayW];
|
||
let day = date.getDate();
|
||
let month = checkTime(date.getMonth());
|
||
|
||
//graf_time = [... graf_time, hours+':'+minutes+' '+day+'.'+month, ];
|
||
if ((element.dateFormat = "HH:mm")) {
|
||
graf_time = [
|
||
...graf_time,
|
||
hours + ":" + minutes + " " + dayW,
|
||
];
|
||
} else if ((element.dateFormat = "DD.MM.YYYY")) {
|
||
graf_time = [
|
||
...graf_time,
|
||
day + "." + month + " " + dayW,
|
||
];
|
||
} else {
|
||
graf_time = [...graf_time, hours + ":" + minutes];
|
||
}
|
||
|
||
graf_val = [...graf_val, item.y1];
|
||
});
|
||
|
||
axisOptions = { xAxisMode: "tick", xIsSeries: true };
|
||
if (element.pointRadius == "0") {
|
||
lineOptions = { hideDots: 1, regionFill: 1, spline: 1 };
|
||
} else {
|
||
lineOptions = { regionFill: 1, dotSize: 3, spline: 1 };
|
||
}
|
||
|
||
dataLine = {
|
||
labels: graf_time,
|
||
datasets: [
|
||
{
|
||
name: element.descr,
|
||
values: graf_val,
|
||
},
|
||
],
|
||
};
|
||
|
||
if (!element.status) {
|
||
// архив графика отсутсутствует, то создаем статус и заполняем
|
||
element.status = dataLine;
|
||
} else {
|
||
//console.log(" дописываем новые значения к имеющимся");
|
||
element.status.labels = [
|
||
...element.status.labels,
|
||
dataLine.labels[0],
|
||
];
|
||
element.status.datasets[0].values = [
|
||
...element.status.datasets[0].values,
|
||
dataLine.datasets[0].values[0],
|
||
];
|
||
}
|
||
} catch (e) {
|
||
console.log(
|
||
"полученные данные для графика содержат ошибки",
|
||
element.status
|
||
);
|
||
}
|
||
|
||
//два графика в одном / пока работает криво только для WS , надо делать
|
||
if (connectionType === "MQTT") {
|
||
if (topic.includes("_1/status")) {
|
||
graf_1 = element.status.datasets[0];
|
||
}
|
||
if (topic.includes("_2/status")) {
|
||
graf_2 = element.status.datasets[0];
|
||
element.status.datasets[1] = graf_1;
|
||
}
|
||
} else {
|
||
}
|
||
}
|
||
// данные для прочих витжетов (заменяем статус на новый)
|
||
else {
|
||
element.status = json.status;
|
||
element.send = false;
|
||
}
|
||
}
|
||
wigets = wigets;
|
||
devices = devices;
|
||
pages = pages;
|
||
});
|
||
}
|
||
// для mysens
|
||
if (json.info) {
|
||
// ищем виджет к которому относится INFO
|
||
wigets.forEach(function (element) {
|
||
//отличие MQTT и WS========================================================!!!!!!!!!!!!!!
|
||
let messegetopic;
|
||
if (connectionType === "MQTT") {
|
||
messegetopic = topic.replace("/status", "");
|
||
} else {
|
||
messegetopic = json.topic.replace("/status", "");
|
||
}
|
||
// ========================================================================
|
||
|
||
if (element.topic == messegetopic) {
|
||
element.nodeInfo = json.info;
|
||
}
|
||
});
|
||
}
|
||
if (json.color) {
|
||
// ищем виджет к которому относится цвет INFO
|
||
wigets.forEach(function (element) {
|
||
//отличие MQTT и WS========================================================!!!!!!!!!!!!!!
|
||
let messegetopic;
|
||
if (connectionType === "MQTT") {
|
||
messegetopic = topic.replace("/status", "");
|
||
} else {
|
||
messegetopic = json.topic.replace("/status", "");
|
||
}
|
||
// ========================================================================
|
||
|
||
if (element.topic == messegetopic) {
|
||
element.nodeInfoColor = json.color;
|
||
}
|
||
});
|
||
}
|
||
});
|
||
} catch (e) {
|
||
console.log("ERROR parse JSON", tmp);
|
||
}
|
||
|
||
tmp = null;
|
||
temp = null;
|
||
json = null;
|
||
data = null;
|
||
NewWiget = null;
|
||
NewPage = null;
|
||
NewPrefics = null;
|
||
date = null;
|
||
hours = null;
|
||
minutes = null;
|
||
}
|
||
|
||
// функция получаем префикс
|
||
|
||
function getprefics(topic) {
|
||
if (topic[0] == "/") {
|
||
topic = topic.slice(0, topic.indexOf("/", 2));
|
||
return topic;
|
||
} else {
|
||
topic = topic.slice(0, topic.indexOf("/", 0));
|
||
return topic;
|
||
}
|
||
}
|
||
// функция если произошла смена префикса в интерфейсе
|
||
function handleSubmit() {
|
||
console.log("The selected option is " + JSON.stringify(selected.prefics));
|
||
selectedprefics = selected.prefics;
|
||
}
|
||
|
||
// шлем данные в websocket
|
||
function WSpush(ws, uri, val) {
|
||
// console.log(ws, uri, val);
|
||
if (connectionType == "MQTT") {
|
||
client.publish(uri + "/control", val.toString());
|
||
} else {
|
||
socket[ws].send(
|
||
'{"path":"' + uri + '/control", "status":"' + val.toString() + '"}'
|
||
);
|
||
|
||
if (socket[ws].readyState != 1) {
|
||
addConnection(devices);
|
||
|
||
// добавить GET запрос
|
||
}
|
||
}
|
||
}
|
||
|
||
if (connectionType != "MQTT") {
|
||
// addConnection(devices);
|
||
}
|
||
|
||
// обратный отсчет для Shutter
|
||
let frame;
|
||
let elapsed = 0;
|
||
let duration = 5000;
|
||
let last_time = window.performance.now();
|
||
(function update() {
|
||
frame = requestAnimationFrame(update);
|
||
const time = window.performance.now();
|
||
elapsed += Math.min(time - last_time, duration - elapsed);
|
||
last_time = time;
|
||
if (elapsed == duration) {
|
||
Shutterhiddn();
|
||
}
|
||
})();
|
||
|
||
let Shuttervisibl = "visibility: hidden;";
|
||
|
||
function Shuttervisibil() {
|
||
Shuttervisibl = "visibility: visible;";
|
||
// duration = sec;
|
||
elapsed = 0;
|
||
if (elapsed === duration) {
|
||
Shuttervisibl = "visibility: hidden;";
|
||
}
|
||
}
|
||
function Shutterhiddn() {
|
||
Shuttervisibl = "visibility: hidden;";
|
||
}
|
||
Shuttervisibil();
|
||
</script>
|
||
|
||
<!---->
|
||
|
||
{#if connectionType == "MQTT"}
|
||
{#if MQTTconnections[1]}
|
||
<form on:submit|preventDefault={MQTTChange}>
|
||
<select bind:value={selected} on:change={MQTTChange}>
|
||
{#each MQTTconnections as MQTTconnection}
|
||
{#if MQTTconnection.connection_name == selected.connection_name}
|
||
<option selected value={MQTTconnection}>
|
||
{MQTTconnection.connection_name}
|
||
</option>
|
||
{:else}
|
||
<option value={MQTTconnection}>
|
||
{MQTTconnection.connection_name}
|
||
</option>
|
||
{/if}
|
||
{/each}
|
||
</select>
|
||
</form>
|
||
{/if}
|
||
{:else}
|
||
<div
|
||
class="Shutter"
|
||
style="{Shuttervisibl} position: absolute; z-index: 1; right: 1%; top: 0px"
|
||
id="layer1"
|
||
>
|
||
<span>
|
||
<progress value={elapsed / duration} />
|
||
<br /><br />
|
||
<!--Перечисляем девайсы в сети-->
|
||
{#each devices as NetworkDevice, i}
|
||
{#if !NetworkDevice.status && NetworkDevice.status != false}
|
||
<p align="left" style=" margin-top: -5px; margin-bottom: -5px">
|
||
{NetworkDevice.deviceIP}
|
||
{NetworkDevice.deviceName} waiting
|
||
</p>
|
||
{/if}
|
||
{#if NetworkDevice.status == true}
|
||
<p
|
||
align="left"
|
||
style="color: green; margin-top: -5px; margin-bottom: -5px"
|
||
>
|
||
{NetworkDevice.deviceIP}
|
||
{NetworkDevice.deviceName} connected
|
||
</p>
|
||
{/if}
|
||
{#if NetworkDevice.status == false}
|
||
<p
|
||
align="left"
|
||
style="color: red; margin-top: -5px; margin-bottom: -5px"
|
||
on:click={addConnection(devices)}
|
||
>
|
||
{NetworkDevice.deviceIP}
|
||
{NetworkDevice.deviceName} disconnected
|
||
</p>
|
||
{/if}
|
||
{/each}
|
||
</span>
|
||
</div>
|
||
{/if}
|
||
|
||
<div
|
||
style=" position: absolute; z-index: 2; right: 7%; top: 0%; color:red"
|
||
on:click={toggleTheme}
|
||
id="toggleTheme"
|
||
>
|
||
🔆
|
||
</div>
|
||
|
||
{#if connectionType == "MQTT"}
|
||
{#if connected == false}
|
||
<div
|
||
style=" position: absolute; z-index: 2; right: 2%; top: 0%; color:red"
|
||
on:click={toggleTheme}
|
||
id="layerMQTT"
|
||
>
|
||
MQTT
|
||
</div>
|
||
{#if MQTTconnections[0].mqtt_port == ""}
|
||
<div align="center">
|
||
Для подключения MQTT обязателььно должен быть указан порт "MQTT wss port
|
||
(TLS)"
|
||
</div>
|
||
{/if}
|
||
{/if}
|
||
{#if connected == true}
|
||
<div
|
||
style=" position: absolute; z-index: 2; right: 2%; top: 0%; color:green"
|
||
on:click={toggleTheme}
|
||
id="layerMQTT"
|
||
>
|
||
MQTT
|
||
</div>
|
||
{/if}
|
||
{:else if connected == true}
|
||
<div
|
||
on:mouseenter={Shuttervisibil}
|
||
on:click={Shutterhiddn}
|
||
style="color:green; position: absolute; z-index: 2; right: 1.5%; top: 0px"
|
||
id="layerWS; "
|
||
>
|
||
localNET
|
||
</div>
|
||
{:else}
|
||
<div
|
||
on:mouseenter={Shuttervisibil}
|
||
on:click={Shutterhiddn}
|
||
style="color:red; position: absolute; z-index: 2; right: 1.5%; top: 0px"
|
||
id="layerWS; "
|
||
>
|
||
localNET
|
||
</div>
|
||
{/if}
|
||
<!-- селектор префиксов -->
|
||
{#if prefics[2]}
|
||
<form on:submit|preventDefault={handleSubmit}>
|
||
<select style="float: left" bind:value={selected} on:change={handleSubmit}>
|
||
{#each prefics as curentprefics}
|
||
<option value={curentprefics}>
|
||
{curentprefics.prefics}
|
||
</option>
|
||
{/each}
|
||
</select>
|
||
</form>
|
||
{/if}
|
||
|
||
<!--закончили селектор префиксов -->
|
||
|
||
{#if !pages[0]}
|
||
<p align="center"><Logo /></p>
|
||
{/if}
|
||
|
||
<!-------------------------------------------------------->
|
||
<p align="center">
|
||
{#each pages as page, i}
|
||
<button type="button" on:click={() => changeSlide(i)}>{page.page}</button>
|
||
{/each}
|
||
</p>
|
||
{#if newPage == 1}
|
||
<div class="swipe-holder" style="height:{swipe_holder_height}px">
|
||
<Swipe bind:active_item bind:this={SwipeComp}>
|
||
{#each pages as pagesName, i}
|
||
<SwipeItem
|
||
active={active_item == i}
|
||
allow_dynamic_height={true}
|
||
on:swipe_item_height_change={heightChanged}
|
||
>
|
||
<table border="0" style="margin-left: 0%" width="99%">
|
||
{#each wigets as widget, i}
|
||
<!--Отображаем виджеты только для выбранного префикса-->
|
||
|
||
{#if !selectedprefics || selectedprefics === widget.prefics}
|
||
{#if widget.page === pagesName.page}
|
||
<tr>
|
||
<!-- Toggle -->
|
||
{#if widget.widget === "toggle"}
|
||
<td style="width: 100%;">
|
||
<span style="float: left"> {widget.descr}</span>
|
||
</td>
|
||
|
||
<td />
|
||
|
||
<td>
|
||
{#if widget.status == "1"}
|
||
<span style="float: right">
|
||
<Toggle
|
||
style="float: right"
|
||
on:toggle={WSpush(widget.socket, widget.topic, 0)}
|
||
label=""
|
||
toggledColor="#6495ED"
|
||
untoggledColor="#FF6347"
|
||
switchColor="#eee"
|
||
/>
|
||
</span>
|
||
{:else}
|
||
<span style="float: right">
|
||
<Toggle
|
||
on:toggle={WSpush(widget.socket, widget.topic, 1)}
|
||
label=""
|
||
toggledColor="#FF6347"
|
||
untoggledColor="gray"
|
||
switchColor="#eee"
|
||
toggled=""
|
||
/>
|
||
</span>
|
||
{/if}
|
||
</td>{/if}
|
||
<!-- anydata -->
|
||
{#if widget.widget === "anydata"}
|
||
<td>
|
||
{#if widget.descrColor}
|
||
<span style="float: left">
|
||
<lable
|
||
align="left"
|
||
style="color: {widget.descrColor}; font-family: '{widget.descrfont}'"
|
||
>{widget.descr}</lable
|
||
>
|
||
</span>
|
||
{:else}
|
||
<span
|
||
style="float: left; font-family: '{widget.descrfont}'"
|
||
>
|
||
{widget.descr}
|
||
<!--Инфо от ноды mysens отображается под названием виджета -->
|
||
<div
|
||
class="letter"
|
||
align="left"
|
||
style="color: {!widget.nodeInfoColor
|
||
? 'gray'
|
||
: widget.nodeInfoColor}"
|
||
>
|
||
{!widget.nodeInfo ? "" : widget.nodeInfo}
|
||
</div>
|
||
</span>
|
||
{/if}
|
||
</td>
|
||
<td>
|
||
<!--Инфо от ноды mysens отображается по середине
|
||
<lable align='left' style='color: {!widget.nodeInfoColor ? 'gray' : widget.nodeInfoColor}'>
|
||
|
||
</lable>-->
|
||
</td>
|
||
<td align="right" style="white-space: nowrap;">
|
||
<!--Делаем anidata разноцветными если есть кастомизация цвета-->
|
||
{#if Array.isArray(widget.color) && widget.status}
|
||
{#each widget.color as anydataColor, i}
|
||
<!--// убираем знаки после запятой -->
|
||
{#if anydataColor.level && widget.status < anydataColor.level && widget.status > widget.color[i - 1].level && i > 0}
|
||
<lable
|
||
align="left"
|
||
style="color: {anydataColor.value}; font-family: '{widget.font}' "
|
||
>{Math.round(widget.status * 10) / 10
|
||
? Math.round(widget.status * 10) / 10
|
||
: widget.status}{!widget.after
|
||
? ""
|
||
: widget.after}</lable
|
||
>
|
||
{/if}
|
||
{/each}
|
||
<!--если цвет задан значением а не массивом-->
|
||
{:else if widget.color && widget.status}
|
||
<lable
|
||
align="left"
|
||
style="color: {widget.color}; font-family: '{widget.font}'"
|
||
>{Math.round(widget.status * 10) / 10
|
||
? Math.round(widget.status * 10) / 10
|
||
: widget.status}{widget.after}</lable
|
||
>
|
||
<!--если цвет не задан и статус пустой-->
|
||
{:else if !widget.status}
|
||
<lable align="left">...</lable>
|
||
<!--если цвет не задан-->
|
||
{:else if widget.status}
|
||
<lable
|
||
align="left"
|
||
style="font-family: '{widget.font}'"
|
||
>{Math.round(widget.status * 10) / 10
|
||
? Math.round(widget.status * 10) / 10
|
||
: widget.status}{!widget.after
|
||
? ""
|
||
: widget.after}</lable
|
||
>
|
||
{:else}
|
||
<lable
|
||
align="left"
|
||
style="font-family: '{widget.font}'"
|
||
>
|
||
{!widget.status ? "" : widget.status}{!widget.after
|
||
? ""
|
||
: widget.after}
|
||
</lable>
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
<!-- input -->
|
||
{#if widget.widget === "input"}
|
||
<td><span style="float: left"> {widget.descr}</span></td>
|
||
<td />
|
||
{#if widget.type === "number"}
|
||
<td align="right">
|
||
<div
|
||
style="float: right; display:inline; width: 120px "
|
||
>
|
||
<input
|
||
type="button"
|
||
value="- "
|
||
style=" border: 1px solid lightblue; width: 25px"
|
||
on:click={WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status - 1
|
||
)}
|
||
/>
|
||
<input
|
||
class:red={widget["send"] == true}
|
||
style="width: 45px "
|
||
type="tel"
|
||
neme={widget.topic}
|
||
bind:value={widget.status}
|
||
size="20"
|
||
on:change={((widget["send"] = true),
|
||
WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status
|
||
))}
|
||
min="-1000"
|
||
max="1000000"
|
||
/>
|
||
<input
|
||
type="button"
|
||
value="+ "
|
||
style="border: 1px solid lightblue; width: 25px"
|
||
on:click={WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status - 1 + 2
|
||
)}
|
||
/>
|
||
</div>
|
||
</td>
|
||
{:else if widget.type === "time"}
|
||
<td align="right">
|
||
<div style="float: right; display:inline-block">
|
||
<input
|
||
class:red={widget["send"] == true}
|
||
type="time"
|
||
bind:value={widget.status}
|
||
size="20"
|
||
on:change={((widget["send"] = true),
|
||
WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status
|
||
))}
|
||
min="00:00"
|
||
max="23:59"
|
||
required
|
||
/>
|
||
</div>
|
||
</td>
|
||
{:else}
|
||
<td align="right"
|
||
><div style="display:inline-block">
|
||
<input
|
||
class:red={widget["send"] == true}
|
||
bind:value={widget.status}
|
||
size="20"
|
||
on:change={((widget["send"] = true),
|
||
WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status
|
||
))}
|
||
/>
|
||
</div></td
|
||
>
|
||
{/if}
|
||
{/if}
|
||
<!-- btn -->
|
||
{#if widget.widget == "btn"}
|
||
<td><span style="float: left">{widget.descr}</span></td>
|
||
<td />
|
||
<td align="right">
|
||
{#if widget.status != 0 && widget.status != 1}
|
||
<button
|
||
class="btn"
|
||
on:click={((widget["send"] = true),
|
||
WSpush(widget.socket, widget.topic, 1))}
|
||
><span
|
||
> {!widget.status
|
||
? ""
|
||
: widget.status} </span
|
||
></button
|
||
><br />
|
||
{:else}
|
||
<button
|
||
class="btn"
|
||
on:click={((widget["send"] = true),
|
||
WSpush(widget.socket, widget.topic, 1))}
|
||
><span
|
||
> {!widget.text
|
||
? ""
|
||
: widget.text} </span
|
||
></button
|
||
><br />
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
<!-- select -->
|
||
{#if widget.widget === "select"}
|
||
<td><span style="float: left">{widget.descr}</span></td>
|
||
<td />
|
||
<td align="right">
|
||
{#if widget.status == 0}
|
||
<button
|
||
class="btnoff"
|
||
on:click={((widget["send"] = true),
|
||
(this.style.border = "1px solid red"),
|
||
WSpush(widget.socket, widget.topic, 1))}
|
||
><span
|
||
>{!widget.options[0]
|
||
? "OFF"
|
||
: widget.options[0]}</span
|
||
></button
|
||
><br />
|
||
{:else}
|
||
<button
|
||
class="btn"
|
||
on:click={((widget["send"] = true),
|
||
(this.style.border = "1px solid red"),
|
||
WSpush(widget.socket, widget.topic, 0))}
|
||
><span
|
||
>{!widget.options[1]
|
||
? "ON"
|
||
: widget.options[1]}</span
|
||
></button
|
||
><br />
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
<!-- chart -->
|
||
{#if widget.widget === "chart"}
|
||
<td colspan="3">
|
||
{#if widget.status}
|
||
{#if widget.topic.includes("_2")}
|
||
<Chart
|
||
data={widget.status}
|
||
{lineOptions}
|
||
{axisOptions}
|
||
colors={[!widget.color ? "red" : widget.color]}
|
||
type={!widget.type ? "line" : widget.type}
|
||
height="300"
|
||
/>
|
||
{:else if !widget.topic.includes("_1")}
|
||
{widget.descr}
|
||
<Chart
|
||
data={widget.status}
|
||
{lineOptions}
|
||
{axisOptions}
|
||
colors={[
|
||
!widget.color ? "light-blue" : widget.color,
|
||
]}
|
||
type={!widget.type ? "line" : widget.type}
|
||
height="300"
|
||
/>
|
||
{/if}
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
<!-- range -->
|
||
{#if widget.widget === "range"}
|
||
<td colspan="3">
|
||
<div>
|
||
{widget.descr}
|
||
{widget.status / 10}
|
||
{widget.after}
|
||
</div>
|
||
|
||
<label>
|
||
<input
|
||
type="range"
|
||
bind:value={widget.status}
|
||
min={widget.min}
|
||
max={widget.max * 10}
|
||
on:change={WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status
|
||
)}
|
||
/>
|
||
</label>
|
||
</td>
|
||
{/if}
|
||
</tr>
|
||
{/if}
|
||
{/if}
|
||
{/each}
|
||
</table>
|
||
</SwipeItem>
|
||
{/each}
|
||
</Swipe>
|
||
</div>
|
||
{/if}
|
||
<!------------------------------------------------------->
|
||
<!--
|
||
<Tabs>
|
||
<table border="0" style="margin-left: 0%" width="99%">
|
||
<tr
|
||
><td>
|
||
<TabList>
|
||
{#each pages as pagesName, i}
|
||
<Tab>{pagesName.page}</Tab>
|
||
{/each}
|
||
</TabList>
|
||
</td></tr
|
||
>
|
||
</table>
|
||
|
||
{#each pages as pagesName, i}
|
||
<TabPanel>
|
||
<table border="0" style="margin-left: 0%" width="99%">
|
||
{#each wigets as widget, i}
|
||
!--Отображаем виджеты только для выбранного префикса--
|
||
|
||
{#if !selectedprefics || selectedprefics === widget.prefics}
|
||
{#if widget.page === pagesName.page}
|
||
<tr>
|
||
-- Toggle --
|
||
{#if widget.widget === "toggle"}
|
||
<td style="width: 100%;">
|
||
<span style="float: left"> {widget.descr}</span>
|
||
</td>
|
||
|
||
<td />
|
||
|
||
<td>
|
||
{#if widget.status == "1"}
|
||
<span style="float: right">
|
||
<Toggle
|
||
style="float: right"
|
||
on:toggle={WSpush(widget.socket, widget.topic, 0)}
|
||
label=""
|
||
toggledColor="#6495ED"
|
||
untoggledColor="#FF6347"
|
||
switchColor="#eee"
|
||
/>
|
||
</span>
|
||
{:else}
|
||
<span style="float: right">
|
||
<Toggle
|
||
on:toggle={WSpush(widget.socket, widget.topic, 1)}
|
||
label=""
|
||
toggledColor="#FF6347"
|
||
untoggledColor="gray"
|
||
switchColor="#eee"
|
||
toggled=""
|
||
/>
|
||
</span>
|
||
{/if}
|
||
</td>{/if}
|
||
-- anydata --
|
||
{#if widget.widget === "anydata"}
|
||
<td>
|
||
{#if widget.descrColor}
|
||
<span style="float: left">
|
||
<lable
|
||
align="left"
|
||
style="color: {widget.descrColor}; font-family: '{widget.descrfont}'"
|
||
>{widget.descr}</lable
|
||
>
|
||
</span>
|
||
{:else}
|
||
<span
|
||
style="float: left; font-family: '{widget.descrfont}'"
|
||
>
|
||
{widget.descr}
|
||
--Инфо от ноды mysens отображается под названием виджета --
|
||
<div
|
||
class="letter"
|
||
align="left"
|
||
style="color: {!widget.nodeInfoColor
|
||
? 'gray'
|
||
: widget.nodeInfoColor}"
|
||
>
|
||
{!widget.nodeInfo ? "" : widget.nodeInfo}
|
||
</div>
|
||
</span>
|
||
{/if}
|
||
</td>
|
||
<td>
|
||
--Инфо от ноды mysens отображается по середине
|
||
<lable align='left' style='color: {!widget.nodeInfoColor ? 'gray' : widget.nodeInfoColor}'></lable>
|
||
--
|
||
</td>
|
||
<td align="right" style="white-space: nowrap;">
|
||
--Делаем anidata разноцветными если есть кастомизация цвета--
|
||
{#if Array.isArray(widget.color) && widget.status}
|
||
{#each widget.color as anydataColor, i}
|
||
--// убираем знаки после запятой --
|
||
{#if anydataColor.level && widget.status < anydataColor.level && widget.status > widget.color[i - 1].level && i > 0}
|
||
<lable
|
||
align="left"
|
||
style="color: {anydataColor.value}; font-family: '{widget.font}' "
|
||
>{Math.round(widget.status * 10) / 10
|
||
? Math.round(widget.status * 10) / 10
|
||
: widget.status}{!widget.after
|
||
? ""
|
||
: widget.after}</lable
|
||
>
|
||
{/if}
|
||
{/each}
|
||
--если цвет задан значением а не массивом--
|
||
{:else if widget.color && widget.status}
|
||
<lable
|
||
align="left"
|
||
style="color: {widget.color}; font-family: '{widget.font}'"
|
||
>{Math.round(widget.status * 10) / 10
|
||
? Math.round(widget.status * 10) / 10
|
||
: widget.status}{widget.after}</lable
|
||
>
|
||
--если цвет не задан и статус пустой--
|
||
{:else if !widget.status}
|
||
<lable align="left">...</lable>
|
||
--если цвет не задан--
|
||
{:else if widget.status}
|
||
<lable align="left" style="font-family: '{widget.font}'"
|
||
>{Math.round(widget.status * 10) / 10
|
||
? Math.round(widget.status * 10) / 10
|
||
: widget.status}{!widget.after
|
||
? ""
|
||
: widget.after}</lable
|
||
>
|
||
{:else}
|
||
<lable align="left" style="font-family: '{widget.font}'">
|
||
{!widget.status ? "" : widget.status}{!widget.after
|
||
? ""
|
||
: widget.after}
|
||
</lable>
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
-- input --
|
||
{#if widget.widget === "input"}
|
||
<td><span style="float: left"> {widget.descr}</span></td>
|
||
<td />
|
||
{#if widget.type === "number"}
|
||
<td align="right">
|
||
<div
|
||
style="float: right; display:inline; width: 120px "
|
||
>
|
||
<input
|
||
type="button"
|
||
value="- "
|
||
style=" border: 1px solid lightblue; width: 25px"
|
||
on:click={WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status - 1
|
||
)}
|
||
/>
|
||
<input
|
||
class:red={widget["send"] == true}
|
||
style="width: 45px "
|
||
type="tel"
|
||
neme={widget.topic}
|
||
bind:value={widget.status}
|
||
size="20"
|
||
on:change={((widget["send"] = true),
|
||
WSpush(widget.socket, widget.topic, widget.status))}
|
||
min="-1000"
|
||
max="1000000"
|
||
/>
|
||
<input
|
||
type="button"
|
||
value="+ "
|
||
style="border: 1px solid lightblue; width: 25px"
|
||
on:click={WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status - 1 + 2
|
||
)}
|
||
/>
|
||
</div>
|
||
</td>
|
||
{:else if widget.type === "time"}
|
||
<td align="right">
|
||
<div style="float: right; display:inline-block">
|
||
<input
|
||
class:red={widget["send"] == true}
|
||
type="time"
|
||
bind:value={widget.status}
|
||
size="20"
|
||
on:change={((widget["send"] = true),
|
||
WSpush(widget.socket, widget.topic, widget.status))}
|
||
min="00:00"
|
||
max="23:59"
|
||
required
|
||
/>
|
||
</div>
|
||
</td>
|
||
{:else}
|
||
<td align="right"
|
||
><div style="display:inline-block">
|
||
<input
|
||
class:red={widget["send"] == true}
|
||
bind:value={widget.status}
|
||
size="20"
|
||
on:change={((widget["send"] = true),
|
||
WSpush(widget.socket, widget.topic, widget.status))}
|
||
/>
|
||
</div></td
|
||
>
|
||
{/if}
|
||
{/if}
|
||
-- btn --
|
||
{#if widget.widget == "btn"}
|
||
<td><span style="float: left">{widget.descr}</span></td>
|
||
<td />
|
||
<td align="right">
|
||
{#if widget.status != 0 && widget.status != 1}
|
||
<button
|
||
class="btn"
|
||
on:click={((widget["send"] = true),
|
||
WSpush(widget.socket, widget.topic, 1))}
|
||
><span
|
||
> {!widget.status
|
||
? ""
|
||
: widget.status} </span
|
||
></button
|
||
><br />
|
||
{:else}
|
||
<button
|
||
class="btn"
|
||
on:click={((widget["send"] = true),
|
||
WSpush(widget.socket, widget.topic, 1))}
|
||
><span
|
||
> {!widget.text
|
||
? ""
|
||
: widget.text} </span
|
||
></button
|
||
><br />
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
-- select --
|
||
{#if widget.widget === "select"}
|
||
<td><span style="float: left">{widget.descr}</span></td>
|
||
<td />
|
||
<td align="right">
|
||
{#if widget.status == 0}
|
||
<button
|
||
class="btnoff"
|
||
on:click={((widget["send"] = true),
|
||
(this.style.border = "1px solid red"),
|
||
WSpush(widget.socket, widget.topic, 1))}
|
||
><span
|
||
>{!widget.options[0]
|
||
? "OFF"
|
||
: widget.options[0]}</span
|
||
></button
|
||
><br />
|
||
{:else}
|
||
<button
|
||
class="btn"
|
||
on:click={((widget["send"] = true),
|
||
(this.style.border = "1px solid red"),
|
||
WSpush(widget.socket, widget.topic, 0))}
|
||
><span
|
||
>{!widget.options[1] ? "ON" : widget.options[1]}</span
|
||
></button
|
||
><br />
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
-- chart --
|
||
{#if widget.widget === "chart"}
|
||
<td colspan="3">
|
||
{#if widget.status}
|
||
{#if widget.topic.includes("_2")}
|
||
<Chart
|
||
data={widget.status}
|
||
{lineOptions}
|
||
{axisOptions}
|
||
colors={[!widget.color ? "red" : widget.color]}
|
||
type={!widget.type ? "line" : widget.type}
|
||
height="300"
|
||
/>
|
||
{:else if !widget.topic.includes("_1")}
|
||
{widget.descr}
|
||
<Chart
|
||
data={widget.status}
|
||
{lineOptions}
|
||
{axisOptions}
|
||
colors={[!widget.color ? "light-blue" : widget.color]}
|
||
type={!widget.type ? "line" : widget.type}
|
||
height="300"
|
||
/>
|
||
{/if}
|
||
{/if}
|
||
</td>
|
||
{/if}
|
||
-- range --
|
||
{#if widget.widget === "range"}
|
||
<td colspan="3">
|
||
<div>
|
||
{widget.descr}
|
||
{widget.status / 10}
|
||
{widget.after}
|
||
</div>
|
||
|
||
<label>
|
||
<input
|
||
type="range"
|
||
bind:value={widget.status}
|
||
min={widget.min}
|
||
max={widget.max * 10}
|
||
on:change={WSpush(
|
||
widget.socket,
|
||
widget.topic,
|
||
widget.status
|
||
)}
|
||
/>
|
||
</label>
|
||
</td>
|
||
{/if}
|
||
</tr>
|
||
{/if}
|
||
{/if}
|
||
{/each}
|
||
</table>
|
||
</TabPanel>
|
||
{/each}
|
||
</Tabs>
|
||
-->
|
||
<br /><br />
|
||
<div class="letter" align="center">developed by: avaks@meef.ru</div>
|
||
|
||
<style>
|
||
:global(.svelte-tabs__tab-list) {
|
||
display: flex;
|
||
justify-content: space-evenly;
|
||
flex-wrap: wrap;
|
||
}
|
||
:global(.svelte-tabs li.svelte-tabs__tab) {
|
||
color: gray;
|
||
}
|
||
:global(.svelte-tabs li.svelte-tabs__selected) {
|
||
color: green;
|
||
}
|
||
.red {
|
||
color: crimson;
|
||
}
|
||
.green {
|
||
color: rgb(20, 220, 37);
|
||
}
|
||
|
||
.letter {
|
||
color: grey;
|
||
font-size: 60%;
|
||
padding-left: 15px;
|
||
opacity: 0.8;
|
||
}
|
||
progress {
|
||
height: 4px;
|
||
}
|
||
|
||
input {
|
||
text-align: right;
|
||
border: 1px solid #6699ff;
|
||
width: 100%;
|
||
}
|
||
|
||
label {
|
||
display: flex;
|
||
justify-content: space-evenly;
|
||
flex-wrap: wrap;
|
||
}
|
||
:global(body.light-mode) {
|
||
background-color: white;
|
||
}
|
||
|
||
:global(body.dark-mode) {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
}
|
||
|
||
:global(body.dark-mode) span {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
}
|
||
|
||
:global(body.dark-mode) div {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
}
|
||
|
||
:global(body.dark-mode) input {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
}
|
||
|
||
:global(body.dark-mode) select {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
padding: 10px;
|
||
border-radius: 10px;
|
||
padding-left: 20px;
|
||
}
|
||
:global(body.dark-mode) lable {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
}
|
||
select {
|
||
padding: 10px;
|
||
border-radius: 10px;
|
||
padding-left: 20px;
|
||
}
|
||
.Shutter {
|
||
background-color: hsl(200, 16%, 96%);
|
||
color: blak;
|
||
padding: 10px;
|
||
border-radius: 5px;
|
||
}
|
||
:global(body.dark-mode) .Shutter {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
padding: 10px;
|
||
border-radius: 5px;
|
||
}
|
||
|
||
.btn {
|
||
display: inline-block;
|
||
box-sizing: border-box;
|
||
padding: 1px;
|
||
margin: 0 0px 5px 0;
|
||
outline: none;
|
||
border: 1px solid #63b8ff;
|
||
border-radius: 10px;
|
||
height: 36px;
|
||
line-height: 0;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
color: #fff;
|
||
background-color: #fff;
|
||
position: relative;
|
||
overflow: hidden;
|
||
vertical-align: top;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
appearance: none;
|
||
touch-action: manipulation;
|
||
}
|
||
.btn span {
|
||
box-shadow: 7px 7px 5px rgba(0, 0, 0, 0.5);
|
||
display: block;
|
||
box-sizing: border-box;
|
||
padding: 0 8px;
|
||
height: 32px;
|
||
line-height: 33px;
|
||
border: 1px solid #63b8ff;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
color: black;
|
||
text-align: center;
|
||
font-weight: 600;
|
||
}
|
||
.btnoff {
|
||
display: inline-block;
|
||
box-sizing: border-box;
|
||
padding: 1px;
|
||
margin: 0 0px 5px 0;
|
||
outline: none;
|
||
border: 1px solid rgb(85, 84, 84);
|
||
border-radius: 10px;
|
||
height: 36px;
|
||
line-height: 0;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
text-decoration: none;
|
||
color: #fff;
|
||
background-color: #fff;
|
||
position: relative;
|
||
overflow: hidden;
|
||
vertical-align: top;
|
||
cursor: pointer;
|
||
user-select: none;
|
||
appearance: none;
|
||
touch-action: manipulation;
|
||
}
|
||
.btnoff span {
|
||
box-shadow: 7px 7px 5px rgba(0, 0, 0, 0.5);
|
||
display: block;
|
||
box-sizing: border-box;
|
||
padding: 0 8px;
|
||
height: 32px;
|
||
line-height: 33px;
|
||
border: 1px solid rgb(85, 84, 84);
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
color: black;
|
||
text-align: center;
|
||
font-weight: 600;
|
||
}
|
||
.swipe-holder {
|
||
height: 30vh;
|
||
width: 100%;
|
||
}
|
||
button {
|
||
color: gray;
|
||
background-color: #fff;
|
||
border-bottom-style: solid;
|
||
border-bottom-width: 1px;
|
||
border-top-width: 0px;
|
||
border-left-width: 0px;
|
||
border-right-width: 0px;
|
||
padding-left: 4px;
|
||
padding-right: 4px;
|
||
padding-top: 1px;
|
||
padding-bottom: 1px;
|
||
}
|
||
:global(body.dark-mode) button {
|
||
background-color: #1d3040;
|
||
color: #bfc2c7;
|
||
}
|
||
</style>
|