Files
IoTManagerWeb/src/pages/Config.svelte
2023-09-22 19:40:34 +02:00

303 lines
10 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script>
import Card from "../components/Card.svelte";
import CrossIcon from "../svg/Cross.svelte";
import OpenIcon from "../svg/Open.svelte";
import Alarm from "../components/Alarm.svelte";
export let configJson;
export let widgetsJson;
export let itemsJson;
export let scenarioTxt;
export let show;
let itemsJsonBind = 0;
let debug = false;
export let saveConfig = () => {};
export let rebootEsp = () => {};
//export let cleanLogs = () => {};
let exportJson = {};
function elementsDropdownChange() {
for (let i = 0; i < itemsJson.length; i++) {
let item = Object.assign({}, itemsJson[i]);
if (itemsJsonBind === item.num) {
delete item.num;
delete item.name;
item.id = item.id + randomInteger(0, 100);
configJson.push(item);
configJson = configJson;
itemsJsonBind = 0;
if (debug) console.log("[i]", "item added");
break;
}
}
}
function randomInteger(min, max) {
// получить случайное число от (min-0.5) до (max+0.5)
let rand = min - 0.5 + Math.random() * (max - min + 1);
return Math.round(rand);
}
function deleteLineFromConfig(num) {
for (let i = 0; i < configJson.length; i++) {
if (num === i) {
configJson.splice(i, 1);
configJson = configJson;
if (debug) console.log("[i]", "item " + num + " deleted from config");
break;
}
}
}
$: scenarioTxt, windowHeight();
let height;
function windowHeight() {
let scenStr = scenarioTxt;
height = scenStr.split("\n").length + 1;
}
// Function to download data to a file
function saveFile2(data, filename, type) {
var file = new Blob([data], { type: type });
if (window.navigator.msSaveOrOpenBlob)
// IE10+
window.navigator.msSaveOrOpenBlob(file, filename);
else {
// Others
var a = document.createElement("a"),
url = URL.createObjectURL(file);
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
setTimeout(function () {
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}, 0);
}
}
function saveFile(data, filename, type) {
var file = new Blob([data], { type: type });
if (window.navigator.msSaveOrOpenBlob) {
window.navigator.msSaveOrOpenBlob(file, filename);
} else {
const a = document.createElement("a");
document.body.appendChild(a);
const url = window.URL.createObjectURL(file);
a.href = url;
a.download = filename;
a.click();
setTimeout(() => {
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
}, 0);
}
}
const syntaxHighlight = (json) => {
try {
json = JSON.stringify(JSON.parse(json), null, 4);
} catch (e) {
return json;
}
json = json.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
json = json.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
return match;
});
return json;
};
function createExportFile() {
exportJson.mark = "iotm";
exportJson.config = configJson;
let exportAsText = syntaxHighlight(JSON.stringify(exportJson));
exportAsText = exportAsText + "\n\nscenario=>" + scenarioTxt;
saveFile(exportAsText, "export.json", "application/json");
}
let template = null;
let files = null;
const alertErr = "Файл не является файлом конфигурации";
const alertOk = "Применить конфигурацию?\nне забудьте нажать кнопку 'сохранить на устройстве'";
$: if (files) {
const fileText = files[0].text();
fileText.then((text) => {
template = text;
if (!template.includes("scenario=>")) {
window.alert(alertErr);
return;
}
let jsonPart = selectToMarker(template, "scenario=>");
let txtPart = deleteBeforeDelimiter(template, "scenario=>");
if (!IsJsonParse(jsonPart)) {
window.alert(alertErr);
return;
}
let json = JSON.parse(jsonPart);
if (json.mark !== "iotm") {
window.alert(alertErr);
return;
}
if (window.confirm(alertOk)) {
configJson = [];
scenarioTxt = "";
configJson = json.config;
scenarioTxt = txtPart;
console.log("config updated");
}
});
files = null;
}
function reset() {
files = null;
document.getElementById("formFile").value = "";
}
function IsJsonParse(str) {
try {
JSON.parse(str);
} catch (e) {
if (debug) console.log("[e]", "json parce error: ", str);
return false;
}
return true;
}
function selectToMarker(str, found) {
let p = str.indexOf(found);
return str.substring(0, p);
}
function deleteBeforeDelimiter(str, found) {
let p = str.indexOf(found) + found.length;
return str.substring(p);
}
export let moduleOrder = (id, key, value) => {};
</script>
{#if show}
<div class="my-4">
<div class="grd-2col1">
<Card title="Конфигуратор">
<div class="grd-2col2">
<select class="slct-lg" bind:value={itemsJsonBind} on:change={() => elementsDropdownChange()}>
{#each itemsJson as item}
{#if item.header}
<optgroup label={item.header} />
{/if}
{#if !item.header}
<option value={item.num}>
{item.name}
</option>
{/if}
{/each}
</select>
<select class="slct-lg"><option>{"Выберите пресет"}</option></select>
</div>
<table class="tbl">
<thead class="bg-gray-100">
<tr class="txt-sz txt-pad">
<th class="tbl-hd">Тип</th>
<th class="tbl-hd">Id</th>
<th class="tbl-hd">Виджет</th>
<th class="tbl-hd">Вкладка</th>
<th class="tbl-hd">Название</th>
<th class="tbl-hd w-7" />
<th class="tbl-hd w-7" />
</tr>
</thead>
<tbody class="bg-white">
{#each configJson as element, i}
<tr class="txt-sz txt-pad align-middle">
<td class="tbl-bdy-lg">{element.subtype}</td>
<td class="tbl-bdy-lg"><input bind:value={element.id} class="ipt-lg w-full" type="text" /></td>
<td class="tbl-bdy-lg"
><select bind:value={element.widget} class="ipt-lg w-full">
{#each widgetsJson as select}
<option value={select.name}>
{select.label}
</option>
{/each}
</select></td>
<td class="tbl-bdy-lg"><input bind:value={element.page} class="ipt-lg w-full" type="text" /></td>
<td class="tbl-bdy-lg"><input bind:value={element.descr} class="ipt-lg w-full" type="text" /></td>
<td class="tbl-bdy-lg"><OpenIcon click={() => (element.show = !element.show)} /></td>
<td class="tbl-bdy-lg"><CrossIcon click={() => deleteLineFromConfig(i)} /></td>
</tr>
{#if element.show}
{#each Object.entries(element) as [key, param]}
{#if key != "type" && key != "subtype" && key != "id" && key != "widget" && key != "page" && key != "descr" && key != "show"}
<tr class="txt-sz txt-pad">
<td />
<td />
<td />
{#if key.startsWith("btn")}
<td class="tbl-bdy-sm text-right">
<button on:click={() => moduleOrder(element.id, key.substring(4), element[key])} class="h-3 sm:h-6 md:h-6 lg:h-6 xl:h-6 2xl:h-6 w-auto bg-blue-100 inline-flex items-center border border-gray-300 hover:bg-blue-200">{key.substring(4)}</button>
</td>
{#if element[key] != "nil"}
<td class="tbl-bdy-sm text-center">
<input bind:value={element[key]} class="ipt-sm w-full text-sm" type="text" />
</td>
{/if}
{:else}
<td class="tbl-bdy-sm text-right">
<p class="txt-ita">{key}</p>
</td>
<td class="tbl-bdy-sm text-center">
<input bind:value={element[key]} class="ipt-sm w-full text-sm" type="text" />
</td>
{/if}
</tr>
{/if}
{/each}
<!--<br />-->
{/if}
{/each}
</tbody>
</table>
</Card>
<Card title="Сценарии">
<textarea bind:value={scenarioTxt} rows={height} class="px-2 bg-gray-50 border-2 border-gray-200 rounded text-gray-700 leading-tight focus:outline-none focus:bg-white focus:border-indigo-500 w-full" />
</Card>
</div>
</div>
<div class="grd-1col1">
<Card>
<div class="grd-2col1">
<button class="btn-lg" on:click={() => saveConfig()}>{"Сохранить на устройстве"}</button>
<button class="btn-lg" on:click={() => rebootEsp()}>{"Перезагрузить устройство"}</button>
<button class="btn-lg" on:click={() => createExportFile()}>{"Экспорт конфигурации"}</button>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<label on:click={() => reset()} class="btn-lg cursor-pointer select-none">
<input bind:files={files} accept="application/JSON" type="file" id="formFile" />
{"Импорт конфигурации"}
</label>
</div>
</Card>
</div>
{:else}
<Alarm title="Загрузка..." />
{/if}