integrate web ui project
This commit is contained in:
parent
f69835c8d4
commit
11bac96cbe
4
.gitignore
vendored
4
.gitignore
vendored
|
|
@ -1,2 +1,2 @@
|
|||
/build/
|
||||
|
||||
/build
|
||||
/WEBUI_DIST
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ project(${ProjectId})
|
|||
|
||||
# Include espfs ROM in foldef HTML
|
||||
include(components/webguiapp/extlibs/libespfs/cmake/include.cmake)
|
||||
target_add_espfs(WebguiappTemplate.elf espfs WEBUI)
|
||||
target_add_espfs(WebguiappTemplate.elf espfs WEBUI_DIST)
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{_ as t,V as o,W as s,a5 as a,af as e,Z as r,a4 as n}from"./index.e05c0c8e.js";const c=o({name:"ErrorNotFound"}),l={class:"fullscreen bg-blue text-white text-center q-pa-md flex flex-center"},d=e("div",{style:{"font-size":"30vh"}}," 404 ",-1),i=e("div",{class:"text-h2",style:{opacity:".4"}}," Oops. Nothing here... four-ow-four ",-1);function _(f,p,u,h,x,m){return s(),a("div",l,[e("div",null,[d,i,r(n,{class:"q-mt-xl",color:"white","text-color":"blue",unelevated:"",to:"/",label:"Go Home","no-caps":""})])])}var N=t(c,[["render",_]]);export{N as default};
|
||||
|
|
@ -1 +0,0 @@
|
|||
import{S as f,o as v,T as C,a as d,W as _,X as h,Y as s,ad as H,Z as o,ae as m,af as t,a2 as i,_ as g,V as S,a3 as q,a5 as x}from"./index.e05c0c8e.js";import{S as l}from"./network.98844594.js";import{s as $}from"./helpers.6b816d79.js";import"./axios.15d8ef65.js";const k=t("div",{class:"text-h6"},"Home",-1),y={class:"q-pa-md"},B={class:"q-gutter-md q-pa-none q-pb-none"},I=Object.assign({name:"HomeCard"},{__name:"HomeCard",setup(p){const e=f({time:0,uptime:0});l(e,2,0,"mykey",!1);let a;v(()=>{a=setInterval(()=>{l(e,2,0,"mykey",!1)},1e3)}),C(()=>clearInterval(a));const n=d({get(){return new Date(e.time*1e3).toISOString()}}),r=d({get(){return $(e.uptime)}});return(c,D)=>(_(),h(H,{flat:"",bordered:"",class:"card"},{default:s(()=>[o(m,null,{default:s(()=>[k]),_:1}),o(m,{class:"q-pt-none"},{default:s(()=>[t("div",y,[t("div",B,[t("div",null,i(n.value),1),t("div",null,"Uptime: "+i(r.value),1)])])]),_:1})]),_:1}))}}),b=S({name:"HomePage",components:{HomeCard:I}}),V={class:"cardholder"};function w(p,u,e,a,n,r){const c=q("HomeCard");return _(),x("div",V,[o(c)])}var U=g(b,[["render",w]]);export{U as default};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
.menu{font-family:monospace;font-size:larger}
|
||||
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
import{c as d,D as g,F as k,a as u,h as o,d as v,g as y,a9 as Q,aa as R,ab as E,r as q,ac as A,B as D,p as K}from"./index.e05c0c8e.js";var P=d({name:"QList",props:{...g,bordered:Boolean,dense:Boolean,separator:Boolean,padding:Boolean,tag:{type:String,default:"div"}},setup(e,{slots:t}){const n=y(),i=k(e,n.proxy.$q),l=u(()=>"q-list"+(e.bordered===!0?" q-list--bordered":"")+(e.dense===!0?" q-list--dense":"")+(e.separator===!0?" q-list--separator":"")+(i.value===!0?" q-list--dark":"")+(e.padding===!0?" q-list--padding":""));return()=>o(e.tag,{class:l.value},v(t.default))}});function j(){if(window.getSelection!==void 0){const e=window.getSelection();e.empty!==void 0?e.empty():e.removeAllRanges!==void 0&&(e.removeAllRanges(),Q.is.mobile!==!0&&e.addRange(document.createRange()))}else document.selection!==void 0&&document.selection.empty()}function F(e,t,n){return n<=t?t:Math.min(n,Math.max(t,e))}function M(e,t,n){if(n<=t)return t;const i=n-t+1;let l=t+(e-t)%i;return l<t&&(l=i+l),l===0?0:l}var N=d({name:"QItemSection",props:{avatar:Boolean,thumbnail:Boolean,side:Boolean,top:Boolean,noWrap:Boolean},setup(e,{slots:t}){const n=u(()=>`q-item__section column q-item__section--${e.avatar===!0||e.side===!0||e.thumbnail===!0?"side":"main"}`+(e.top===!0?" q-item__section--top justify-start":" justify-center")+(e.avatar===!0?" q-item__section--avatar":"")+(e.thumbnail===!0?" q-item__section--thumbnail":"")+(e.noWrap===!0?" q-item__section--nowrap":""));return()=>o("div",{class:n.value},v(t.default))}}),T=d({name:"QItemLabel",props:{overline:Boolean,caption:Boolean,header:Boolean,lines:[Number,String]},setup(e,{slots:t}){const n=u(()=>parseInt(e.lines,10)),i=u(()=>"q-item__label"+(e.overline===!0?" q-item__label--overline text-overline":"")+(e.caption===!0?" q-item__label--caption text-caption":"")+(e.header===!0?" q-item__label--header":"")+(n.value===1?" ellipsis":"")),l=u(()=>e.lines!==void 0&&n.value>1?{overflow:"hidden",display:"-webkit-box","-webkit-box-orient":"vertical","-webkit-line-clamp":n.value}:null);return()=>o("div",{style:l.value,class:i.value},v(t.default))}}),z=d({name:"QItem",props:{...g,...R,tag:{type:String,default:"div"},active:{type:Boolean,default:null},clickable:Boolean,dense:Boolean,insetLevel:Number,tabindex:[String,Number],focused:Boolean,manualFocus:Boolean},emits:["click","keyup"],setup(e,{slots:t,emit:n}){const{proxy:{$q:i}}=y(),l=k(e,i),{hasLink:m,linkAttrs:h,linkClass:_,linkTag:B,navigateOnClick:w}=E(),r=q(null),c=q(null),f=u(()=>e.clickable===!0||m.value===!0||e.tag==="label"),s=u(()=>e.disable!==!0&&f.value===!0),x=u(()=>"q-item q-item-type row no-wrap"+(e.dense===!0?" q-item--dense":"")+(l.value===!0?" q-item--dark":"")+(m.value===!0&&e.active===null?_.value:e.active===!0?` q-item--active${e.activeClass!==void 0?` ${e.activeClass}`:""}`:"")+(e.disable===!0?" disabled":"")+(s.value===!0?" q-item--clickable q-link cursor-pointer "+(e.manualFocus===!0?"q-manual-focusable":"q-focusable q-hoverable")+(e.focused===!0?" q-manual-focusable--focused":""):"")),L=u(()=>{if(e.insetLevel===void 0)return null;const a=i.lang.rtl===!0?"Right":"Left";return{["padding"+a]:16+e.insetLevel*56+"px"}});function S(a){s.value===!0&&(c.value!==null&&(a.qKeyEvent!==!0&&document.activeElement===r.value?c.value.focus():document.activeElement===c.value&&r.value.focus()),w(a))}function C(a){if(s.value===!0&&A(a,13)===!0){D(a),a.qKeyEvent=!0;const b=new MouseEvent("click",a);b.qKeyEvent=!0,r.value.dispatchEvent(b)}n("keyup",a)}function I(){const a=K(t.default,[]);return s.value===!0&&a.unshift(o("div",{class:"q-focus-helper",tabindex:-1,ref:c})),a}return()=>{const a={ref:r,class:x.value,style:L.value,role:"listitem",onClick:S,onKeyup:C};return s.value===!0?(a.tabindex=e.tabindex||"0",Object.assign(a,h.value)):f.value===!0&&(a["aria-disabled"]="true"),o(B.value,a,I())}}});export{N as Q,T as a,F as b,j as c,z as d,P as e,M as n};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +0,0 @@
|
|||
import"./index.e05c0c8e.js";function h(o){o=Number(o);var s=Math.floor(o/86400),r=Math.floor(o%86400/3600),a=Math.floor(o%3600/60),t=Math.floor(o%3600%60);return" "+s+"d "+(r<10?"0":"")+r+":"+(a<10?"0":"")+a+":"+(t<10?"0":"")+t}export{h as s};
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1,3 +0,0 @@
|
|||
<!DOCTYPE html><html><head><title>ESP32 web interface</title><meta charset=utf-8><meta name=description content="Web interface for ESP32 devices"><meta name=format-detection content="telephone=no"><meta name=msapplication-tap-highlight content=no><meta name=viewport content="user-scalable=no,initial-scale=1,maximum-scale=1,minimum-scale=1,width=device-width"><link rel=icon type=image/ico href="/favicon.ico"> <script type="module" crossorigin src="/assets/index.e05c0c8e.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.6b461d14.css">
|
||||
</head><body><div id=q-app></div></body></html>
|
||||
9
WEBUI_SRC/.editorconfig
Normal file
9
WEBUI_SRC/.editorconfig
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
7
WEBUI_SRC/.eslintignore
Normal file
7
WEBUI_SRC/.eslintignore
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
/dist
|
||||
/src-capacitor
|
||||
/src-cordova
|
||||
/.quasar
|
||||
/node_modules
|
||||
.eslintrc.js
|
||||
/quasar.config.*.temporary.compiled*
|
||||
66
WEBUI_SRC/.eslintrc.cjs
Normal file
66
WEBUI_SRC/.eslintrc.cjs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
module.exports = {
|
||||
// https://eslint.org/docs/user-guide/configuring#configuration-cascading-and-hierarchy
|
||||
// This option interrupts the configuration hierarchy at this file
|
||||
// Remove this if you have an higher level ESLint config file (it usually happens into a monorepos)
|
||||
root: true,
|
||||
|
||||
parserOptions: {
|
||||
ecmaVersion: 2021, // Allows for the parsing of modern ECMAScript features
|
||||
},
|
||||
|
||||
env: {
|
||||
node: true,
|
||||
browser: true,
|
||||
'vue/setup-compiler-macros': true
|
||||
},
|
||||
|
||||
// Rules order is important, please avoid shuffling them
|
||||
extends: [
|
||||
// Base ESLint recommended rules
|
||||
// 'eslint:recommended',
|
||||
|
||||
// Uncomment any of the lines below to choose desired strictness,
|
||||
// but leave only one uncommented!
|
||||
// See https://eslint.vuejs.org/rules/#available-rules
|
||||
'plugin:vue/vue3-essential', // Priority A: Essential (Error Prevention)
|
||||
// 'plugin:vue/vue3-strongly-recommended', // Priority B: Strongly Recommended (Improving Readability)
|
||||
// 'plugin:vue/vue3-recommended', // Priority C: Recommended (Minimizing Arbitrary Choices and Cognitive Overhead)
|
||||
|
||||
// https://github.com/prettier/eslint-config-prettier#installation
|
||||
// usage with Prettier, provided by 'eslint-config-prettier'.
|
||||
'prettier'
|
||||
],
|
||||
|
||||
plugins: [
|
||||
// https://eslint.vuejs.org/user-guide/#why-doesn-t-it-work-on-vue-files
|
||||
// required to lint *.vue files
|
||||
'vue',
|
||||
|
||||
// https://github.com/typescript-eslint/typescript-eslint/issues/389#issuecomment-509292674
|
||||
// Prettier has not been included as plugin to avoid performance impact
|
||||
// add it as an extension for your IDE
|
||||
|
||||
],
|
||||
|
||||
globals: {
|
||||
ga: 'readonly', // Google Analytics
|
||||
cordova: 'readonly',
|
||||
__statics: 'readonly',
|
||||
__QUASAR_SSR__: 'readonly',
|
||||
__QUASAR_SSR_SERVER__: 'readonly',
|
||||
__QUASAR_SSR_CLIENT__: 'readonly',
|
||||
__QUASAR_SSR_PWA__: 'readonly',
|
||||
process: 'readonly',
|
||||
Capacitor: 'readonly',
|
||||
chrome: 'readonly'
|
||||
},
|
||||
|
||||
// add your custom rules here
|
||||
rules: {
|
||||
|
||||
'prefer-promise-reject-errors': 'off',
|
||||
|
||||
// allow debugger during development only
|
||||
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
|
||||
}
|
||||
}
|
||||
33
WEBUI_SRC/.gitignore
vendored
Normal file
33
WEBUI_SRC/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
.DS_Store
|
||||
.thumbs.db
|
||||
node_modules
|
||||
|
||||
# Quasar core related directories
|
||||
.quasar
|
||||
/dist
|
||||
/quasar.config.*.temporary.compiled*
|
||||
|
||||
# Cordova related directories and files
|
||||
/src-cordova/node_modules
|
||||
/src-cordova/platforms
|
||||
/src-cordova/plugins
|
||||
/src-cordova/www
|
||||
|
||||
# Capacitor related directories and files
|
||||
/src-capacitor/www
|
||||
/src-capacitor/node_modules
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
|
||||
# local .env files
|
||||
.env.local*
|
||||
3
WEBUI_SRC/.npmrc
Normal file
3
WEBUI_SRC/.npmrc
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# pnpm-related options
|
||||
shamefully-hoist=true
|
||||
strict-peer-dependencies=false
|
||||
17
WEBUI_SRC/.project
Normal file
17
WEBUI_SRC/.project
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>webui</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.wst.validation.validationbuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.wst.jsdt.core.jsNature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
7
WEBUI_SRC/.settings/.jsdtscope
Normal file
7
WEBUI_SRC/.settings/.jsdtscope
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
|
||||
<classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
|
||||
<classpathentry kind="src" path=""/>
|
||||
<classpathentry kind="output" path=""/>
|
||||
</classpath>
|
||||
|
|
@ -0,0 +1 @@
|
|||
org.eclipse.wst.jsdt.launching.JRE_CONTAINER
|
||||
|
|
@ -0,0 +1 @@
|
|||
Global
|
||||
15
WEBUI_SRC/.vscode/extensions.json
vendored
Normal file
15
WEBUI_SRC/.vscode/extensions.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"editorconfig.editorconfig",
|
||||
"vue.volar",
|
||||
"wayou.vscode-todo-highlight"
|
||||
],
|
||||
"unwantedRecommendations": [
|
||||
"octref.vetur",
|
||||
"hookyqr.beautify",
|
||||
"dbaeumer.jshint",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin"
|
||||
]
|
||||
}
|
||||
15
WEBUI_SRC/.vscode/launch.json
vendored
Normal file
15
WEBUI_SRC/.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:8080",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
||||
21
WEBUI_SRC/.vscode/settings.json
vendored
Normal file
21
WEBUI_SRC/.vscode/settings.json
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"editor.bracketPairColorization.enabled": true,
|
||||
"editor.guides.bracketPairs": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.codeActionsOnSave": [
|
||||
"source.fixAll.eslint"
|
||||
],
|
||||
"eslint.validate": [
|
||||
"javascript",
|
||||
"javascriptreact",
|
||||
"typescript",
|
||||
"vue"
|
||||
],
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "Vue.volar"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "vscode.typescript-language-features"
|
||||
}
|
||||
}
|
||||
44
WEBUI_SRC/README.md
Normal file
44
WEBUI_SRC/README.md
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<<<<<<< HEAD
|
||||
# ESP32 web interface (webui)
|
||||
|
||||
Web interface for ESP32 devices
|
||||
|
||||
## Install the dependencies
|
||||
```bash
|
||||
yarn
|
||||
# or
|
||||
npm install
|
||||
```
|
||||
|
||||
### Start the app in development mode (hot-code reloading, error reporting, etc.)
|
||||
```bash
|
||||
quasar dev
|
||||
```
|
||||
|
||||
|
||||
### Lint the files
|
||||
```bash
|
||||
yarn lint
|
||||
# or
|
||||
npm run lint
|
||||
```
|
||||
|
||||
|
||||
### Format the files
|
||||
```bash
|
||||
yarn format
|
||||
# or
|
||||
npm run format
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Build the app for production
|
||||
```bash
|
||||
quasar build
|
||||
```
|
||||
|
||||
### Customize the configuration
|
||||
See [Configuring quasar.config.js](https://v2.quasar.dev/quasar-cli-vite/quasar-config-js).
|
||||
=======
|
||||
>>>>>>> refs/remotes/origin/main
|
||||
14
WEBUI_SRC/build_ui.bat
Normal file
14
WEBUI_SRC/build_ui.bat
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
@ECHO ON
|
||||
SETLOCAL
|
||||
SET "sourcedir=C:\BOGD-PROJECTS\WebguiappTemplate\WEBUI"
|
||||
SET "keepfile=espfs.paths"
|
||||
SET "keepdir=keep"
|
||||
|
||||
FOR /d %%a IN ("%sourcedir%\*") DO IF /i NOT "%%~nxa"=="%keepdir%" RD /S /Q "%%a"
|
||||
FOR %%a IN ("%sourcedir%\*") DO IF /i NOT "%%~nxa"=="%keepfile%" DEL "%%a
|
||||
xcopy /s "C:\BOGD-PROJECTS\webui\dist\spa" "C:\BOGD-PROJECTS\WebguiappTemplate\WEBUI"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
5
WEBUI_SRC/build_ui.sh
Executable file
5
WEBUI_SRC/build_ui.sh
Executable file
|
|
@ -0,0 +1,5 @@
|
|||
#!/bin/bash
|
||||
quasar b
|
||||
cd /home/bogdan/BOGD_PROJECTS/WebguiappTemplate/WEBUI_DIST
|
||||
ls | grep -xv "espfs.paths" | xargs rm -r
|
||||
cp -r /home/bogdan/BOGD_PROJECTS/WebguiappTemplate/WEBUI_SRC/dist/spa/. /home/bogdan/BOGD_PROJECTS/WebguiappTemplate/WEBUI_DIST/
|
||||
17
WEBUI_SRC/index.html
Normal file
17
WEBUI_SRC/index.html
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title><%= productName %></title>
|
||||
|
||||
<meta charset="utf-8">
|
||||
<meta name="description" content="<%= productDescription %>">
|
||||
<meta name="format-detection" content="telephone=no">
|
||||
<meta name="msapplication-tap-highlight" content="no">
|
||||
<meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width<% if (ctx.mode.cordova || ctx.mode.capacitor) { %>, viewport-fit=cover<% } %>">
|
||||
|
||||
<link rel="icon" type="image/ico" href="favicon.ico">
|
||||
</head>
|
||||
<body>
|
||||
<!-- quasar:entry-point -->
|
||||
</body>
|
||||
</html>
|
||||
39
WEBUI_SRC/jsconfig.json
Normal file
39
WEBUI_SRC/jsconfig.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"src/*": [
|
||||
"src/*"
|
||||
],
|
||||
"app/*": [
|
||||
"*"
|
||||
],
|
||||
"components/*": [
|
||||
"src/components/*"
|
||||
],
|
||||
"layouts/*": [
|
||||
"src/layouts/*"
|
||||
],
|
||||
"pages/*": [
|
||||
"src/pages/*"
|
||||
],
|
||||
"assets/*": [
|
||||
"src/assets/*"
|
||||
],
|
||||
"boot/*": [
|
||||
"src/boot/*"
|
||||
],
|
||||
"stores/*": [
|
||||
"src/stores/*"
|
||||
],
|
||||
"vue$": [
|
||||
"node_modules/vue/dist/vue.runtime.esm-bundler.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
"exclude": [
|
||||
"dist",
|
||||
".quasar",
|
||||
"node_modules"
|
||||
]
|
||||
}
|
||||
4924
WEBUI_SRC/package-lock.json
generated
Normal file
4924
WEBUI_SRC/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
39
WEBUI_SRC/package.json
Normal file
39
WEBUI_SRC/package.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"name": "webui",
|
||||
"version": "0.0.1",
|
||||
"description": "Web interface for ESP32 devices",
|
||||
"productName": "ESP32 web interface",
|
||||
"author": "bogdan <bogd@live.ru>",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"lint": "eslint --ext .js,.vue ./",
|
||||
"format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
|
||||
"test": "echo \"No test specified\" && exit 0",
|
||||
"dev": "quasar dev",
|
||||
"build": "quasar build"
|
||||
},
|
||||
"dependencies": {
|
||||
"@quasar/extras": "^1.16.4",
|
||||
"axios": "^1.2.1",
|
||||
"js-sha256": "^0.9.0",
|
||||
"quasar": "^2.6.0",
|
||||
"three": "^0.155.0",
|
||||
"vite-plugin-compression2": "^0.10.3",
|
||||
"vue": "^3.0.0",
|
||||
"vue-router": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@quasar/app-vite": "^1.3.0",
|
||||
"autoprefixer": "^10.4.2",
|
||||
"eslint": "^8.10.0",
|
||||
"eslint-config-prettier": "^8.1.0",
|
||||
"eslint-plugin-vue": "^9.0.0",
|
||||
"postcss": "^8.4.14",
|
||||
"prettier": "^2.5.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^18 || ^16 || ^14.19",
|
||||
"npm": ">= 6.13.4",
|
||||
"yarn": ">= 1.21.1"
|
||||
}
|
||||
}
|
||||
27
WEBUI_SRC/postcss.config.cjs
Normal file
27
WEBUI_SRC/postcss.config.cjs
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
/* eslint-disable */
|
||||
// https://github.com/michael-ciniawsky/postcss-load-config
|
||||
|
||||
module.exports = {
|
||||
plugins: [
|
||||
// https://github.com/postcss/autoprefixer
|
||||
require('autoprefixer')({
|
||||
overrideBrowserslist: [
|
||||
'last 4 Chrome versions',
|
||||
'last 4 Firefox versions',
|
||||
'last 4 Edge versions',
|
||||
'last 4 Safari versions',
|
||||
'last 4 Android versions',
|
||||
'last 4 ChromeAndroid versions',
|
||||
'last 4 FirefoxAndroid versions',
|
||||
'last 4 iOS versions'
|
||||
]
|
||||
})
|
||||
|
||||
// https://github.com/elchininet/postcss-rtlcss
|
||||
// If you want to support RTL css, then
|
||||
// 1. yarn/npm install postcss-rtlcss
|
||||
// 2. optionally set quasar.config.js > framework > lang to an RTL language
|
||||
// 3. uncomment the following line:
|
||||
// require('postcss-rtlcss')
|
||||
]
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.3 KiB |
212
WEBUI_SRC/quasar.config.js
Normal file
212
WEBUI_SRC/quasar.config.js
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
/* eslint-env node */
|
||||
|
||||
/*
|
||||
* This file runs in a Node context (it's NOT transpiled by Babel), so use only
|
||||
* the ES6 features that are supported by your Node version. https://node.green/
|
||||
*/
|
||||
|
||||
// Configuration for your app
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js
|
||||
|
||||
const { QInput } = require("quasar");
|
||||
const { configure } = require("quasar/wrappers");
|
||||
|
||||
module.exports = configure(function (/* ctx */) {
|
||||
return {
|
||||
eslint: {
|
||||
// fix: true,
|
||||
// include: [],
|
||||
// exclude: [],
|
||||
// rawOptions: {},
|
||||
warnings: true,
|
||||
errors: true,
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli/prefetch-feature
|
||||
// preFetch: true,
|
||||
|
||||
// app boot file (/src/boot)
|
||||
// --> boot files are part of "main.js"
|
||||
// https://v2.quasar.dev/quasar-cli/boot-files
|
||||
boot: ["axios"],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#css
|
||||
css: ["app.scss"],
|
||||
|
||||
// https://github.com/quasarframework/quasar/tree/dev/extras
|
||||
extras: [
|
||||
// 'ionicons-v4',
|
||||
// 'mdi-v5',
|
||||
// 'fontawesome-v6',
|
||||
// 'eva-icons',
|
||||
// 'themify',
|
||||
// 'line-awesome',
|
||||
// 'roboto-font-latin-ext', // this or either 'roboto-font', NEVER both!
|
||||
|
||||
"roboto-font", // optional, you are not bound to it
|
||||
"material-icons", // optional, you are not bound to it
|
||||
],
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#build
|
||||
build: {
|
||||
target: {
|
||||
browser: ["es2019", "edge88", "firefox78", "chrome87", "safari13.1"],
|
||||
node: "node16",
|
||||
},
|
||||
|
||||
vueRouterMode: "hash", // available values: 'hash', 'history'
|
||||
// vueRouterBase,
|
||||
// vueDevtools,
|
||||
// vueOptionsAPI: false,
|
||||
|
||||
// rebuildCache: true, // rebuilds Vite/linter/etc cache on startup
|
||||
|
||||
//publicPath: "/",
|
||||
// analyze: true,
|
||||
//env: { UITYPE: "sampleapp" },
|
||||
//rawDefine: { curapp: "sampleapp" },
|
||||
// ignorePublicFolder: true,
|
||||
// minify: false,
|
||||
// polyfillModulePreload: true,
|
||||
//distDir: "/home/bogdan/BOGD_PROJECTS/WebguiappTemplate/WEBUI",
|
||||
//distDir: "C:\\BOGD-PROJECTS\\WebguiappTemplate\\WEBUI",
|
||||
|
||||
// extendViteConf (viteConf) {},
|
||||
// viteVuePluginOptions: {},
|
||||
|
||||
vitePlugins: [
|
||||
[
|
||||
"vite-plugin-compression2",
|
||||
{
|
||||
deleteOriginalAssets: true,
|
||||
filename: "[path][base]",
|
||||
threshold: 4096,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#devServer
|
||||
devServer: {
|
||||
// https: true
|
||||
open: true, // opens browser window automatically
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#framework
|
||||
framework: {
|
||||
config: {
|
||||
|
||||
},
|
||||
|
||||
// iconSet: 'material-icons', // Quasar icon set
|
||||
// lang: 'en-US', // Quasar language pack
|
||||
|
||||
// For special cases outside of where the auto-import strategy can have an impact
|
||||
// (like functional components as one of the examples),
|
||||
// you can manually specify Quasar components/directives to be available everywhere:
|
||||
//
|
||||
components: [],
|
||||
// directives: [],
|
||||
|
||||
// Quasar plugins
|
||||
plugins: ["Dialog", "Notify"],
|
||||
},
|
||||
|
||||
// animations: 'all', // --- includes all animations
|
||||
// https://v2.quasar.dev/options/animations
|
||||
animations: [],
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli-vite/quasar-config-js#property-sourcefiles
|
||||
// sourceFiles: {
|
||||
// rootComponent: 'src/App.vue',
|
||||
// router: 'src/router/index',
|
||||
// store: 'src/store/index',
|
||||
// registerServiceWorker: 'src-pwa/register-service-worker',
|
||||
// serviceWorker: 'src-pwa/custom-service-worker',
|
||||
// pwaManifestFile: 'src-pwa/manifest.json',
|
||||
// electronMain: 'src-electron/electron-main',
|
||||
// electronPreload: 'src-electron/electron-preload'
|
||||
// },
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli/developing-ssr/configuring-ssr
|
||||
ssr: {
|
||||
// ssrPwaHtmlFilename: 'offline.html', // do NOT use index.html as name!
|
||||
// will mess up SSR
|
||||
|
||||
// extendSSRWebserverConf (esbuildConf) {},
|
||||
// extendPackageJson (json) {},
|
||||
|
||||
pwa: false,
|
||||
|
||||
// manualStoreHydration: true,
|
||||
// manualPostHydrationTrigger: true,
|
||||
|
||||
prodPort: 3000, // The default port that the production server should use
|
||||
// (gets superseded if process.env.PORT is specified at runtime)
|
||||
|
||||
middlewares: [
|
||||
"render", // keep this as last one
|
||||
],
|
||||
},
|
||||
|
||||
// https://v2.quasar.dev/quasar-cli/developing-pwa/configuring-pwa
|
||||
pwa: {
|
||||
workboxMode: "generateSW", // or 'injectManifest'
|
||||
injectPwaMetaTags: true,
|
||||
swFilename: "sw.js",
|
||||
manifestFilename: "manifest.json",
|
||||
useCredentialsForManifestTag: false,
|
||||
// useFilenameHashes: true,
|
||||
// extendGenerateSWOptions (cfg) {}
|
||||
// extendInjectManifestOptions (cfg) {},
|
||||
// extendManifestJson (json) {}
|
||||
// extendPWACustomSWConf (esbuildConf) {}
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-cordova-apps/configuring-cordova
|
||||
cordova: {
|
||||
// noIosLegacyBuildFlag: true, // uncomment only if you know what you are doing
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-capacitor-apps/configuring-capacitor
|
||||
capacitor: {
|
||||
hideSplashscreen: true,
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli/developing-electron-apps/configuring-electron
|
||||
electron: {
|
||||
// extendElectronMainConf (esbuildConf)
|
||||
// extendElectronPreloadConf (esbuildConf)
|
||||
|
||||
// specify the debugging port to use for the Electron app when running in development mode
|
||||
inspectPort: 5858,
|
||||
|
||||
bundler: "packager", // 'packager' or 'builder'
|
||||
|
||||
packager: {
|
||||
// https://github.com/electron-userland/electron-packager/blob/master/docs/api.md#options
|
||||
// OS X / Mac App Store
|
||||
// appBundleId: '',
|
||||
// appCategoryType: '',
|
||||
// osxSign: '',
|
||||
// protocol: 'myapp://path',
|
||||
// Windows only
|
||||
// win32metadata: { ... }
|
||||
},
|
||||
|
||||
builder: {
|
||||
// https://www.electron.build/configuration/configuration
|
||||
|
||||
appId: "webui",
|
||||
},
|
||||
},
|
||||
|
||||
// Full list of options: https://v2.quasar.dev/quasar-cli-vite/developing-browser-extensions/configuring-bex
|
||||
bex: {
|
||||
contentScripts: ["my-content-script"],
|
||||
|
||||
// extendBexScriptsConf (esbuildConf) {}
|
||||
// extendBexManifestJson (json) {}
|
||||
},
|
||||
};
|
||||
});
|
||||
11
WEBUI_SRC/src/App.vue
Normal file
11
WEBUI_SRC/src/App.vue
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'App'
|
||||
})
|
||||
</script>
|
||||
15
WEBUI_SRC/src/assets/quasar-logo-vertical.svg
Normal file
15
WEBUI_SRC/src/assets/quasar-logo-vertical.svg
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 356 360">
|
||||
<path
|
||||
d="M43.4 303.4c0 3.8-2.3 6.3-7.1 6.3h-15v-22h14.4c4.3 0 6.2 2.2 6.2 5.2 0 2.6-1.5 4.4-3.4 5 2.8.4 4.9 2.5 4.9 5.5zm-8-13H24.1v6.9H35c2.1 0 4-1.3 4-3.8 0-2.2-1.3-3.1-3.7-3.1zm5.1 12.6c0-2.3-1.8-3.7-4-3.7H24.2v7.7h11.7c3.4 0 4.6-1.8 4.6-4zm36.3 4v2.7H56v-22h20.6v2.7H58.9v6.8h14.6v2.3H58.9v7.5h17.9zm23-5.8v8.5H97v-8.5l-11-13.4h3.4l8.9 11 8.8-11h3.4l-10.8 13.4zm19.1-1.8V298c0-7.9 5.2-10.7 12.7-10.7 7.5 0 13 2.8 13 10.7v1.4c0 7.9-5.5 10.8-13 10.8s-12.7-3-12.7-10.8zm22.7 0V298c0-5.7-3.9-8-10-8-6 0-9.8 2.3-9.8 8v1.4c0 5.8 3.8 8.1 9.8 8.1 6 0 10-2.3 10-8.1zm37.2-11.6v21.9h-2.9l-15.8-17.9v17.9h-2.8v-22h3l15.6 18v-18h2.9zm37.9 10.2v1.3c0 7.8-5.2 10.4-12.4 10.4H193v-22h11.2c7.2 0 12.4 2.8 12.4 10.3zm-3 0c0-5.3-3.3-7.6-9.4-7.6h-8.4V307h8.4c6 0 9.5-2 9.5-7.7V298zm50.8-7.6h-9.7v19.3h-3v-19.3h-9.7v-2.6h22.4v2.6zm34.4-2.6v21.9h-3v-10.1h-16.8v10h-2.8v-21.8h2.8v9.2H296v-9.2h2.9zm34.9 19.2v2.7h-20.7v-22h20.6v2.7H316v6.8h14.5v2.3H316v7.5h17.8zM24 340.2v7.3h13.9v2.4h-14v9.6H21v-22h20v2.7H24zm41.5 11.4h-9.8v7.9H53v-22h13.3c5.1 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6H66c3.1 0 5.3-1.5 5.3-4.7 0-3.3-2.2-4.1-5.3-4.1H55.7v8.8zm47.9 6.2H89l-2 4.3h-3.2l10.7-22.2H98l10.7 22.2h-3.2l-2-4.3zm-1-2.3l-6.3-13-6 13h12.2zm46.3-15.3v21.9H146v-17.2L135.7 358h-2.1l-10.2-15.6v17h-2.8v-21.8h3l11 16.9 11.3-17h3zm35 19.3v2.6h-20.7v-22h20.6v2.7H166v6.8h14.5v2.3H166v7.6h17.8zm47-19.3l-8.3 22h-3l-7.1-18.6-7 18.6h-3l-8.2-22h3.3L204 356l6.8-18.5h3.4L221 356l6.6-18.5h3.3zm10 11.6v-1.4c0-7.8 5.2-10.7 12.7-10.7 7.6 0 13 2.9 13 10.7v1.4c0 7.9-5.4 10.8-13 10.8-7.5 0-12.7-3-12.7-10.8zm22.8 0v-1.4c0-5.7-4-8-10-8s-9.9 2.3-9.9 8v1.4c0 5.8 3.8 8.2 9.8 8.2 6.1 0 10-2.4 10-8.2zm28.3 2.4h-9.8v7.9h-2.8v-22h13.2c5.2 0 8 1.9 8 6.8 0 3.7-2 6.3-5.6 7l6 8.2h-3.3l-5.8-8zm-9.8-2.6h10.2c3 0 5.2-1.5 5.2-4.7 0-3.3-2.1-4.1-5.2-4.1h-10.2v8.8zm40.3-1.5l-6.8 5.6v6.4h-2.9v-22h2.9v12.3l15.2-12.2h3.7l-9.9 8.1 10.3 13.8h-3.6l-8.9-12z" />
|
||||
<path fill="#050A14"
|
||||
d="M188.4 71.7a10.4 10.4 0 01-20.8 0 10.4 10.4 0 1120.8 0zM224.2 45c-2.2-3.9-5-7.5-8.2-10.7l-12 7c-3.7-3.2-8-5.7-12.6-7.3a49.4 49.4 0 00-9.7 13.9 59 59 0 0140.1 14l7.6-4.4a57 57 0 00-5.2-12.5zM178 125.1c4.5 0 9-.6 13.4-1.7v-14a40 40 0 0012.5-7.2 47.7 47.7 0 00-7.1-15.3 59 59 0 01-32.2 27.7v8.7c4.4 1.2 8.9 1.8 13.4 1.8zM131.8 45c-2.3 4-4 8.1-5.2 12.5l12 7a40 40 0 000 14.4c5.7 1.5 11.3 2 16.9 1.5a59 59 0 01-8-41.7l-7.5-4.3c-3.2 3.2-6 6.7-8.2 10.6z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M224.2 98.4c2.3-3.9 4-8 5.2-12.4l-12-7a40 40 0 000-14.5c-5.7-1.5-11.3-2-16.9-1.5a59 59 0 018 41.7l7.5 4.4c3.2-3.2 6-6.8 8.2-10.7zm-92.4 0c2.2 4 5 7.5 8.2 10.7l12-7a40 40 0 0012.6 7.3c4-4.1 7.3-8.8 9.7-13.8a59 59 0 01-40-14l-7.7 4.4c1.2 4.3 3 8.5 5.2 12.4zm46.2-80c-4.5 0-9 .5-13.4 1.7V34a40 40 0 00-12.5 7.2c1.5 5.7 4 10.8 7.1 15.4a59 59 0 0132.2-27.7V20a53.3 53.3 0 00-13.4-1.8z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M178 9.2a62.6 62.6 0 11-.1 125.2A62.6 62.6 0 01178 9.2m0-9.2a71.7 71.7 0 100 143.5A71.7 71.7 0 00178 0z" />
|
||||
<path fill="#050A14"
|
||||
d="M96.6 212v4.3c-9.2-.8-15.4-5.8-15.4-17.8V180h4.6v18.4c0 8.6 4 12.6 10.8 13.5zm16-31.9v18.4c0 8.9-4.3 12.8-10.9 13.5v4.4c9.2-.7 15.5-5.6 15.5-18v-18.3h-4.7zM62.2 199v-2.2c0-12.7-8.8-17.4-21-17.4-12.1 0-20.7 4.7-20.7 17.4v2.2c0 12.8 8.6 17.6 20.7 17.6 1.5 0 3-.1 4.4-.3l11.8 6.2 2-3.3-8.2-4-6.4-3.1a32 32 0 01-3.6.2c-9.8 0-16-3.9-16-13.3v-2.2c0-9.3 6.2-13.1 16-13.1 9.9 0 16.3 3.8 16.3 13.1v2.2c0 5.3-2.1 8.7-5.6 10.8l4.8 2.4c3.4-2.8 5.5-7 5.5-13.2zM168 215.6h5.1L156 179.7h-4.8l17 36zM143 205l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.8-3.7H143zm133.7 10.7h5.2l-17.3-35.9h-4.8l17 36zm-25-10.7l7.4-15.7-2.4-5-15.1 31.4h5.1l3.3-7h18.3l-1.7-3.7h-14.8zm73.8-2.5c6-1.2 9-5.4 9-11.4 0-8-4.5-10.9-12.9-10.9h-21.4v35.5h4.6v-31.3h16.5c5 0 8.5 1.4 8.5 6.7 0 5.2-3.5 7.7-8.5 7.7h-11.4v4.1h10.7l9.3 12.8h5.5l-9.9-13.2zm-117.4 9.9c-9.7 0-14.7-2.5-18.6-6.3l-2.2 3.8c5.1 5 11 6.7 21 6.7 1.6 0 3.1-.1 4.6-.3l-1.9-4h-3zm18.4-7c0-6.4-4.7-8.6-13.8-9.4l-10.1-1c-6.7-.7-9.3-2.2-9.3-5.6 0-2.5 1.4-4 4.6-5l-1.8-3.8c-4.7 1.4-7.5 4.2-7.5 8.9 0 5.2 3.4 8.7 13 9.6l11.3 1.2c6.4.6 8.9 2 8.9 5.4 0 2.7-2.1 4.7-6 5.8l1.8 3.9c5.3-1.6 8.9-4.7 8.9-10zm-20.3-21.9c7.9 0 13.3 1.8 18.1 5.7l1.8-3.9a30 30 0 00-19.6-5.9c-2 0-4 .1-5.7.3l1.9 4 3.5-.2z" />
|
||||
<path fill="#00B4FF"
|
||||
d="M.5 251.9c29.6-.5 59.2-.8 88.8-1l88.7-.3 88.7.3 44.4.4 44.4.6-44.4.6-44.4.4-88.7.3-88.7-.3a7981 7981 0 01-88.8-1z" />
|
||||
<path fill="none" d="M-565.2 324H-252v15.8h-313.2z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
0
WEBUI_SRC/src/boot/.gitkeep
Normal file
0
WEBUI_SRC/src/boot/.gitkeep
Normal file
24
WEBUI_SRC/src/boot/axios.js
Normal file
24
WEBUI_SRC/src/boot/axios.js
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
import { boot } from "quasar/wrappers";
|
||||
import axios from "axios";
|
||||
|
||||
// Be careful when using SSR for cross-request state pollution
|
||||
// due to creating a Singleton instance here;
|
||||
// If any client changes this (global) instance, it might be a
|
||||
// good idea to move this instance creation inside of the
|
||||
// "export default () => {}" function below (which runs individually
|
||||
// for each client)
|
||||
const api = axios.create({ baseURL: "/" });
|
||||
|
||||
export default boot(({ app }) => {
|
||||
// for use inside Vue files (Options API) through this.$axios and this.$api
|
||||
|
||||
app.config.globalProperties.$axios = axios;
|
||||
// ^ ^ ^ this will allow you to use this.$axios (for Vue Options API form)
|
||||
// so you won't necessarily have to import axios in each vue file
|
||||
|
||||
app.config.globalProperties.$api = api;
|
||||
// ^ ^ ^ this will allow you to use this.$api (for Vue Options API form)
|
||||
// so you can easily perform requests against your app's API
|
||||
});
|
||||
|
||||
export { api };
|
||||
37
WEBUI_SRC/src/boot/helpers.js
Normal file
37
WEBUI_SRC/src/boot/helpers.js
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import { Dialog } from "quasar";
|
||||
|
||||
function secondsToHms(d) {
|
||||
d = Number(d);
|
||||
var dd = Math.floor(d / 86400);
|
||||
var h = Math.floor(d % 86400 / 3600);
|
||||
var m = Math.floor(d % 3600 / 60);
|
||||
var s = Math.floor(d % 3600 % 60);
|
||||
return (' ' + dd + 'd ' + (h < 10 ? "0" : "") + h + ":" + (m < 10 ? "0" : "") + m + ":" + (s < 10 ? "0" : "") + s);
|
||||
}
|
||||
|
||||
function ShowDelayDialog(mess, time, onexpire) {
|
||||
const dialog = Dialog.create({
|
||||
message: '',
|
||||
progress: true,
|
||||
persistent: true, // we want the user to not be able to close it
|
||||
ok: false // we want the user to not be able to close it
|
||||
})
|
||||
|
||||
let percentage = 0
|
||||
const interval = setInterval(() => {
|
||||
percentage = Math.min(100, percentage + 10000 / time);
|
||||
dialog.update({ message: `${mess} ${percentage}%` })
|
||||
|
||||
|
||||
// if we are done, we're gonna close it
|
||||
if (percentage === 100) {
|
||||
clearInterval(interval)
|
||||
setTimeout(() => {
|
||||
dialog.hide();
|
||||
onexpire();
|
||||
}, 350)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
export { secondsToHms, ShowDelayDialog }
|
||||
64
WEBUI_SRC/src/boot/network.js
Normal file
64
WEBUI_SRC/src/boot/network.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { api } from "boot/axios";
|
||||
import { sha256 } from "js-sha256";
|
||||
import { Notify, Dialog } from "quasar";
|
||||
|
||||
const API_URL = "/api";
|
||||
const SHA256_HMAC_KEY = "mykey";
|
||||
|
||||
function ShowSaveDialog(apltype) {
|
||||
const opername = ['Data applying...', 'Data saving...', 'Data saving and reboot...'];
|
||||
let step = (apltype == 2) ? 1 : 10;
|
||||
let percentage = 0;
|
||||
const dialog = Dialog.create({ message: opername[apltype], progress: true, persistent: true, ok: false })
|
||||
const interval = setInterval(() => {
|
||||
percentage = Math.min(100, percentage + step);
|
||||
dialog.update({
|
||||
message: `${opername[apltype]} ${percentage}%`
|
||||
})
|
||||
if (percentage === 100) {
|
||||
clearInterval(interval);
|
||||
setTimeout(() => { dialog.hide() }, 350)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function PostData(varlist, messtype, applytype, onfinished) {
|
||||
var pld = {};
|
||||
var data = {};
|
||||
data.msgid = Math.floor(Date.now() / 1000);
|
||||
data.time = new Date().toISOString();
|
||||
data.msgtype = messtype;
|
||||
data.payloadtype = 1;
|
||||
data.payload = {};
|
||||
data.payload.applytype = applytype;
|
||||
data.payload.variables = varlist;
|
||||
pld.data = data;
|
||||
pld.signature = sha256.hmac(SHA256_HMAC_KEY, JSON.stringify(data));
|
||||
|
||||
api
|
||||
.post(API_URL, JSON.stringify(pld), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
})
|
||||
.then((response) => {
|
||||
var resp = response.data.data.payload.variables;
|
||||
for (var k in resp) varlist[k] = resp[k];
|
||||
if (onfinished) onfinished();
|
||||
})
|
||||
.catch((err) => {
|
||||
Notify.create({ color: "negative", position: "top", message: err.message, icon: "report_problem", });
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function SendAndRequest(varlist, mstp, apltp, shakey, okreport) {
|
||||
var onfinish = (okreport) ? () => { ShowSaveDialog(apltp) } : null;
|
||||
PostData(varlist, mstp, apltp, onfinish);
|
||||
}
|
||||
|
||||
export { SendAndRequest, PostData };
|
||||
|
||||
|
||||
|
||||
|
||||
11
WEBUI_SRC/src/components/webguicomp/CardActions.vue
Normal file
11
WEBUI_SRC/src/components/webguicomp/CardActions.vue
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<template>
|
||||
<q-card-actions>
|
||||
<q-btn flat v-on:click="SendAndRequest(senddata, 1, 0, 'mykey', true)">Apply</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(senddata, 1, 1, 'mykey', true)">Save</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(senddata, 1, 2, 'mykey', true)">Save&Reboot</q-btn>
|
||||
</q-card-actions>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { SendAndRequest } from "boot/network";
|
||||
</script>
|
||||
48
WEBUI_SRC/src/components/webguicomp/EssentialLink.vue
Normal file
48
WEBUI_SRC/src/components/webguicomp/EssentialLink.vue
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
<template>
|
||||
<q-item clickable tag="a" target="_self" :href="link">
|
||||
<q-item-section v-if="icon" avatar>
|
||||
<q-icon :name="icon" />
|
||||
</q-item-section>
|
||||
|
||||
<q-item-section class="menu">
|
||||
<q-item-label>{{ title }}</q-item-label>
|
||||
<q-item-label caption>{{ caption }}</q-item-label>
|
||||
</q-item-section>
|
||||
</q-item>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
.menu {
|
||||
font-family: monospace;
|
||||
font-size: larger;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "EssentialLink",
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
|
||||
caption: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
|
||||
link: {
|
||||
type: String,
|
||||
default: "#",
|
||||
},
|
||||
|
||||
icon: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
67
WEBUI_SRC/src/components/webguicomp/SelectWiFiDialog.vue
Normal file
67
WEBUI_SRC/src/components/webguicomp/SelectWiFiDialog.vue
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
<template>
|
||||
<q-dialog ref="dialogRef" @hide="onDialogHide">
|
||||
<q-card class="q-dialog-plugin">
|
||||
<div class="q-pa-md q-gutter-md">
|
||||
<q-card-section>
|
||||
<q-inner-loading :showing="visible" label="Scaning WiFi..." label-class="text-teal"
|
||||
label-style="font-size: 1.1em" />
|
||||
</q-card-section>
|
||||
<q-card-section>
|
||||
<q-table title="WiFi networks" :rows="scandata.wifi_scan_res" :columns="columns" row-key="ssid"
|
||||
hide-no-data="true" wrap-cells="true" @row-click="ClickOnRaw" />
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn label="OK" @click="onOKClick" />
|
||||
<q-btn label="Cancel" @click="onDialogCancel" />
|
||||
</q-card-actions>
|
||||
</div>
|
||||
</q-card>
|
||||
</q-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useDialogPluginComponent } from 'quasar'
|
||||
import { PostData } from "boot/network";
|
||||
import { reactive, ref } from 'vue';
|
||||
|
||||
defineEmits([
|
||||
// REQUIRED; need to specify some events that your
|
||||
// component will emit through useDialogPluginComponent()
|
||||
...useDialogPluginComponent.emits
|
||||
])
|
||||
|
||||
const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } = useDialogPluginComponent()
|
||||
// dialogRef - Vue ref to be applied to QDialog
|
||||
// onDialogHide - Function to be used as handler for @hide on QDialog
|
||||
// onDialogOK - Function to call to settle dialog with "ok" outcome
|
||||
// example: onDialogOK() - no payload
|
||||
// example: onDialogOK({ /*...*/ }) - with payload
|
||||
// onDialogCancel - Function to call to settle dialog with "cancel" outcome
|
||||
|
||||
// this is part of our example (so not required)
|
||||
function onOKClick() {
|
||||
// on OK, it is REQUIRED to
|
||||
// call onDialogOK (with optional payload)
|
||||
onDialogOK()
|
||||
// or with payload: onDialogOK({ ... })
|
||||
// ...and it will also hide the dialog automatically
|
||||
}
|
||||
|
||||
function ClickOnRaw(evt, row, index) {
|
||||
alert(`clicked SSID ${row.ssid}`)
|
||||
}
|
||||
|
||||
function onDataReady() {
|
||||
visible.value = false;
|
||||
}
|
||||
let visible = ref(true);
|
||||
const scandata = reactive({ wifi_scan_res: 10 });
|
||||
PostData({ wifi_scan: 1 }, 2, 0, () => { });
|
||||
setTimeout(() => { PostData(scandata, 2, 0, () => onDataReady()) }, 7000);
|
||||
|
||||
const columns = [
|
||||
{ name: 'ssid', label: 'SSID', align: 'left', field: 'ssid', sortable: true },
|
||||
{ name: 'rssi', label: 'RSSI', field: 'rssi', sortable: true },
|
||||
{ name: 'ch', label: 'CHANNEL', field: 'ch', sortable: true },
|
||||
]
|
||||
</script>
|
||||
45
WEBUI_SRC/src/components/webguicomp/cards/EthSetCard.vue
Normal file
45
WEBUI_SRC/src/components/webguicomp/cards/EthSetCard.vue
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card" v-show="data.eth_visible">
|
||||
<q-card-section>
|
||||
<div class="text-h6">ETHERNET</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<q-toggle :dense="true" v-model="data.eth_enab" label="Ethernet enable" />
|
||||
<q-input :dense="true" v-model="data.eth_ip" label="IP address:" />
|
||||
<q-input :dense="true" v-model="data.eth_mask" label="Subnet mask:" />
|
||||
<q-input :dense="true" v-model="data.eth_gw" label="Gateway address:" />
|
||||
<q-toggle :dense="true" v-model="data.eth_isdhcp" label="DHCP enabled"></q-toggle>
|
||||
<q-input :dense=true v-model="data.eth_dns1" label="DNS1:" />
|
||||
<q-input :dense=true v-model="data.eth_dns2" label="DNS2:" />
|
||||
<q-input :dense=true v-model="data.eth_dns3" label="DNS3:" />
|
||||
<q-input :dense=true v-model="data.eth_mac" label="MAC:" />
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 0, 'mykey', true)">Apply</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 1, 'mykey', true)">Save</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 2, 'mykey', true)">Save&Reboot</q-btn>
|
||||
</q-card-actions>
|
||||
|
||||
</q-card>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
|
||||
defineOptions({
|
||||
name: 'EthSetCard'
|
||||
})
|
||||
|
||||
const init = {
|
||||
eth_visible: false,
|
||||
eth_enab: true, eth_isdhcp: true, eth_ip: "", eth_mask: "",
|
||||
eth_gw: "", eth_dns1: "", eth_dns2: "", eth_dns3: "", eth_mac: ""
|
||||
}
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
</script>
|
||||
41
WEBUI_SRC/src/components/webguicomp/cards/FirmwareCard.vue
Normal file
41
WEBUI_SRC/src/components/webguicomp/cards/FirmwareCard.vue
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">FIRMWARE</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<q-toggle :dense="true" v-model="data.ota_enab" label="Eanble OTA autoudate" />
|
||||
<q-toggle :dense="true" v-model="data.res_ota_enab" label="Enable reset after update" />
|
||||
<q-input :dense="true" v-model="data.ota_url" label="OTA firmware file URL" />
|
||||
<q-input :dense="true" v-model="data.ota_auto_int" label="New firmware check interval, sec" />
|
||||
<div>Current firmware version:: {{ data.fw_rev }}</div>
|
||||
<div>Available firmware version:: {{ data.fw_rev }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 0, 'mykey', true)">Apply</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 1, 'mykey', true)">Save</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 2, 'mykey', true)">Save&Reboot</q-btn>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
|
||||
defineOptions({
|
||||
name: 'FirmwareCard'
|
||||
})
|
||||
|
||||
const init = {
|
||||
ota_enab: false, res_ota_enab: false, ota_url: "", ota_auto_int: 0, fw_rev: ""
|
||||
}
|
||||
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
|
||||
</script>
|
||||
44
WEBUI_SRC/src/components/webguicomp/cards/HomeCard.vue
Normal file
44
WEBUI_SRC/src/components/webguicomp/cards/HomeCard.vue
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">Home</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<div>{{ timestr }}</div>
|
||||
<div>Uptime: {{ uptimestr }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onUnmounted, reactive, onMounted } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
import { secondsToHms } from "boot/helpers"
|
||||
|
||||
defineOptions({
|
||||
name: 'HomeCard'
|
||||
})
|
||||
|
||||
const init = {
|
||||
time: 0,
|
||||
uptime: 0
|
||||
}
|
||||
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
|
||||
let intervalId
|
||||
onMounted(() => {
|
||||
intervalId = setInterval(() => {
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
}, 1000)
|
||||
})
|
||||
onUnmounted(() => clearInterval(intervalId))
|
||||
|
||||
const timestr = computed({ get() { return (new Date(data.time * 1000).toISOString()) } })
|
||||
const uptimestr = computed({ get() { return (secondsToHms(data.uptime)) } })
|
||||
</script>
|
||||
46
WEBUI_SRC/src/components/webguicomp/cards/MQTT1Card.vue
Normal file
46
WEBUI_SRC/src/components/webguicomp/cards/MQTT1Card.vue
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">MQTT 1</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<q-toggle :dense="true" v-model="data.mqtt_1_enab" label="Eanble MQTT 1" />
|
||||
<q-input :dense="true" v-model="data.mqtt_1_serv" label="MQTT broker URL " />
|
||||
<q-input :dense="true" v-model="data.mqtt_1_port" label="MQTT broker port " />
|
||||
<q-input :dense="true" v-model="data.mqtt_1_syst" label="Global system name " />
|
||||
<q-input :dense="true" v-model="data.mqtt_1_group" label="Group name " />
|
||||
<q-input :dense="true" v-model="data.mqtt_1_clid" label="Device ID prefix" />
|
||||
<q-input :dense="true" v-model="data.mqtt_1_uname" label="Login" />
|
||||
<q-input :dense="true" v-model="data.mqtt_1_pass" label="Password" />
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 0, 'mykey', true)">Apply</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 1, 'mykey', true)">Save</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 2, 'mykey', true)">Save&Reboot</q-btn>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
|
||||
|
||||
const init = {
|
||||
mqtt_1_enab: false,
|
||||
mqtt_1_serv: "",
|
||||
mqtt_1_port: 1000,
|
||||
mqtt_1_syst: "",
|
||||
mqtt_1_group: "",
|
||||
mqtt_1_clid: "",
|
||||
mqtt_1_uname: "",
|
||||
mqtt_1_pass: ""
|
||||
}
|
||||
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
</script>
|
||||
51
WEBUI_SRC/src/components/webguicomp/cards/MQTT2Card.vue
Normal file
51
WEBUI_SRC/src/components/webguicomp/cards/MQTT2Card.vue
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">MQTT 2</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<q-toggle :dense="true" v-model="data.mqtt_2_enab" label="Eanble MQTT 2" />
|
||||
<q-input :dense="true" v-model="data.mqtt_2_serv" label="MQTT broker URL " />
|
||||
<q-input :dense="true" v-model="data.mqtt_2_port" label="MQTT broker port " />
|
||||
<q-input :dense="true" v-model="data.mqtt_2_syst" label="Global system name " />
|
||||
<q-input :dense="true" v-model="data.mqtt_2_group" label="Group name " />
|
||||
<q-input :dense="true" v-model="data.mqtt_2_clid" label="Device ID prefix" />
|
||||
<q-input :dense="true" v-model="data.mqtt_2_uname" label="Login" />
|
||||
<q-input :dense="true" v-model="data.mqtt_2_pass" label="Password" />
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-actions>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 0, 'mykey', true)">Apply</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 1, 'mykey', true)">Save</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 2, 'mykey', true)">Save&Reboot</q-btn>
|
||||
</q-card-actions>
|
||||
|
||||
<CardActions />
|
||||
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
|
||||
const init = {
|
||||
mqtt_2_enab: false,
|
||||
mqtt_2_serv: "",
|
||||
mqtt_2_port: 1000,
|
||||
mqtt_2_syst: "",
|
||||
mqtt_2_group: "",
|
||||
mqtt_2_clid: "",
|
||||
mqtt_2_uname: "",
|
||||
mqtt_2_pass: ""
|
||||
}
|
||||
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
</script>
|
||||
39
WEBUI_SRC/src/components/webguicomp/cards/SNTPCard.vue
Normal file
39
WEBUI_SRC/src/components/webguicomp/cards/SNTPCard.vue
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">SNTP</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<q-toggle :dense="true" v-model="data.sntp_enab" label="Eanble SNTP" />
|
||||
<q-input :dense="true" v-model="data.sntp_serv1" label="SNTP server 1:" />
|
||||
<q-input :dense="true" v-model="data.sntp_serv2" label="SNTP server 2:" />
|
||||
<q-input :dense="true" v-model="data.sntp_serv3" label="SNTP server 3:" />
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 0, 'mykey', true)">Apply</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 1, 'mykey', true)">Save</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 2, 'mykey', true)">Save&Reboot</q-btn>
|
||||
</q-card-actions>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
|
||||
|
||||
|
||||
const init = {
|
||||
sntp_serv1: "",
|
||||
sntp_serv2: "",
|
||||
sntp_serv3: "",
|
||||
sntp_enab: false
|
||||
}
|
||||
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
</script>
|
||||
65
WEBUI_SRC/src/components/webguicomp/cards/StatCard.vue
Normal file
65
WEBUI_SRC/src/components/webguicomp/cards/StatCard.vue
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">INFO</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<div>{{ timestr }}</div>
|
||||
<div>Uptime: {{ uptimestr }}</div>
|
||||
<div>WiFi signal: {{ data.wifi_level }}</div>
|
||||
<q-separator inset />
|
||||
<div>Ethernet state: {{ data.eth_stat }}</div>
|
||||
<div>WiFi state: {{ data.wifi_stat }}</div>
|
||||
<div>GPRS state: {{ data.gsm_stat }}</div>
|
||||
<q-separator inset />
|
||||
<div>MQTT1 state: {{ data.mqtt_1_stat }}</div>
|
||||
<div>MQTT2 state: {{ data.mqtt_2_stat }}</div>
|
||||
<q-separator inset />
|
||||
<div>Free RAM: {{ data.free_ram }}</div>
|
||||
<div>Minimal free RAM: {{ data.free_ram_min }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
|
||||
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onUnmounted, reactive, onMounted } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
import { secondsToHms } from "boot/helpers"
|
||||
|
||||
defineOptions({
|
||||
name: 'StatCard'
|
||||
})
|
||||
|
||||
const init = {
|
||||
time: 0,
|
||||
uptime: 0,
|
||||
wifi_level: "",
|
||||
eth_stat: "",
|
||||
wifi_stat: "",
|
||||
gsm_stat: "",
|
||||
mqtt_1_stat: "",
|
||||
mqtt_2_stat: "",
|
||||
free_ram: 0,
|
||||
free_ram_min: 0
|
||||
}
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
|
||||
let intervalId
|
||||
onMounted(() => {
|
||||
intervalId = setInterval(() => {
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
}, 1000)
|
||||
})
|
||||
onUnmounted(() => clearInterval(intervalId))
|
||||
const timestr = computed({ get() { return (new Date(data.time * 1000).toISOString()) } })
|
||||
const uptimestr = computed({ get() { return (secondsToHms(data.uptime)) } })
|
||||
|
||||
</script>
|
||||
43
WEBUI_SRC/src/components/webguicomp/cards/SystemCard.vue
Normal file
43
WEBUI_SRC/src/components/webguicomp/cards/SystemCard.vue
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">SYSTEM</div>
|
||||
</q-card-section>
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<q-input :dense="true" v-model="data.net_bios_name" label="Device bios name" />
|
||||
<q-input :dense="true" v-model="data.sys_name" label="User name" />
|
||||
<q-input :dense="true" v-model="data.sys_pass" label="User password" />
|
||||
<div>Device model: {{ data.model_name }}</div>
|
||||
<div>Hardware revision: {{ data.hw_rev }}</div>
|
||||
<div>Firmware version: {{ data.fw_rev }}</div>
|
||||
<div>IDF version: {{ data.idf_rev }}</div>
|
||||
<div>Build date: {{ data.build_date }}</div>
|
||||
<div>Serial number: {{ data.ser_num }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<q-card-actions>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 0, 'mykey', true)">Apply</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 1, 'mykey', true)">Save</q-btn>
|
||||
<q-btn flat v-on:click="SendAndRequest(data, 1, 2, 'mykey', true)">Save&Reboot</q-btn>
|
||||
</q-card-actions>
|
||||
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from "vue";
|
||||
import { SendAndRequest } from "boot/network";
|
||||
|
||||
defineOptions({
|
||||
name: 'SystemCard'
|
||||
})
|
||||
const init = {
|
||||
net_bios_name: "", sys_name: "", sys_pass: "",
|
||||
model_name: "", hw_rev: 0, fw_rev: "", idf_rev: "", build_date: "", ser_num: ""
|
||||
}
|
||||
const data = reactive(init);
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
</script>
|
||||
80
WEBUI_SRC/src/components/webguicomp/cards/WifiSetCard.vue
Normal file
80
WEBUI_SRC/src/components/webguicomp/cards/WifiSetCard.vue
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
<template>
|
||||
<q-card flat bordered class="card">
|
||||
<q-card-section>
|
||||
<div class="text-h6">WiFi</div>
|
||||
</q-card-section>
|
||||
|
||||
<q-card-section class="q-pt-none">
|
||||
<div class="q-pa-md">
|
||||
<div class="q-gutter-md q-pa-none q-pb-none">
|
||||
<q-toggle :dense="true" v-model="data.wifi_enab" label="WiFi enable" />
|
||||
<q-btn :dense="true" label="Scan WiFi" @click="OpenScanDialog()"></q-btn>
|
||||
<q-select :dense="true" v-model="wifimodestr" :options="wifimodes" :map-options="true" :emit-value="true"
|
||||
label="WiFi mode"></q-select>
|
||||
<q-input :dense="true" v-model="wifipwr" label="Max power, dBm" />
|
||||
<q-input :dense="true" v-model="data.wifi_ap_ssid" label="WiFi network name(AP):" />
|
||||
<q-input :dense="true" v-model="data.wifi_ap_key" label="WiFi network key(AP):" />
|
||||
<q-input :dense="true" v-model="data.wifi_ap_ip" label="IP address(AP):" />
|
||||
<q-input :dense="true" v-model="data.wifi_sta_ssid" label="WiFi network name(CLN):" />
|
||||
<q-input :dense="true" v-model="data.wifi_sta_key" label="WiFi network key(CLN):" />
|
||||
<q-toggle :dense="true" v-model="data.wifi_isdhcp" label="DHCP enabled"></q-toggle>
|
||||
<q-input :dense="true" v-model="data.wifi_sta_ip" label="IP address(CLN):" />
|
||||
<q-input :dense="true" v-model="data.wifi_sta_mask" label="Subnet mask(CLN):" />
|
||||
<q-input :dense="true" v-model="data.wifi_sta_gw" label="Gateway address(CLN):" />
|
||||
<q-input :dense=true v-model="data.wifi_dns1" label="DNS1:" />
|
||||
<q-input :dense=true v-model="data.wifi_dns2" label="DNS2:" />
|
||||
<q-input :dense=true v-model="data.wifi_dns3" label="DNS3:" />
|
||||
<q-input :dense=true v-model="data.wifi_sta_mac" label="MAC(CLN):" />
|
||||
<q-input :dense=true v-model="data.wifi_ap_mac" label="MAC(AP):" />
|
||||
</div>
|
||||
</div>
|
||||
</q-card-section>
|
||||
<CardActions :senddata="data"></CardActions>
|
||||
</q-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, reactive } from "vue";
|
||||
import { SendAndRequest } from "components/webguicomp/network";
|
||||
import { useQuasar } from 'quasar'
|
||||
import SelectWiFiDialog from 'components/webguicomp/SelectWiFiDialog.vue'
|
||||
import CardActions from "components/webguicomp/CardActions.vue"
|
||||
|
||||
const $q = useQuasar();
|
||||
defineOptions({
|
||||
name: 'WifiSetCard'
|
||||
})
|
||||
|
||||
const init = {
|
||||
wifi_enab: true, wifi_mode: 1, wifi_power: 0, wifi_ap_ssid: "", wifi_ap_key: "",
|
||||
wifi_ap_ip: "", wifi_sta_ssid: "", wifi_sta_key: "", wifi_isdhcp: true, wifi_sta_ip: "",
|
||||
wifi_sta_mask: "", wifi_sta_gw: "", wifi_dns1: "", wifi_dns2: "", wifi_dns3: "",
|
||||
wifi_sta_mac: "", wifi_ap_mac: ""
|
||||
}
|
||||
const wifimodes = [
|
||||
{ label: 'Station (STA)', value: '1' },
|
||||
{ label: 'Access point (AP)', value: '2' },
|
||||
{ label: 'Mixed mode (AP+STA)', value: '3' }];
|
||||
|
||||
const data = reactive(init);
|
||||
|
||||
|
||||
const wifimodestr = computed({
|
||||
get() { return (data.wifi_mode).toString() },
|
||||
set(val) { data.wifi_mode = Number(val); }
|
||||
})
|
||||
const wifipwr = computed({
|
||||
get() { return (data.wifi_power) / 4 },
|
||||
set(val) { data.wifi_power = val * 4; }
|
||||
})
|
||||
|
||||
function OpenScanDialog() {
|
||||
$q.dialog({
|
||||
component: SelectWiFiDialog
|
||||
});
|
||||
};
|
||||
|
||||
SendAndRequest(data, 2, 0, 'mykey', false);
|
||||
|
||||
|
||||
</script>
|
||||
64
WEBUI_SRC/src/components/webguicomp/network.js
Normal file
64
WEBUI_SRC/src/components/webguicomp/network.js
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
import { api } from "boot/axios";
|
||||
import { sha256 } from "js-sha256";
|
||||
import { Notify, Dialog } from "quasar";
|
||||
|
||||
const API_URL = "/api";
|
||||
const SHA256_HMAC_KEY = "mykey";
|
||||
|
||||
function ShowSaveDialog(apltype) {
|
||||
const opername = ['Data applying...', 'Data saving...', 'Data saving and reboot...'];
|
||||
let step = (apltype == 2) ? 1 : 10;
|
||||
let percentage = 0;
|
||||
const dialog = Dialog.create({ message: opername[apltype], progress: true, persistent: true, ok: false })
|
||||
const interval = setInterval(() => {
|
||||
percentage = Math.min(100, percentage + step);
|
||||
dialog.update({
|
||||
message: `${opername[apltype]} ${percentage}%`
|
||||
})
|
||||
if (percentage === 100) {
|
||||
clearInterval(interval);
|
||||
setTimeout(() => { dialog.hide() }, 350)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
|
||||
|
||||
|
||||
function PostData(varlist, messtype, applytype, onfinished) {
|
||||
var pld = {};
|
||||
var data = {};
|
||||
data.msgid = Math.floor(Date.now() / 1000);
|
||||
data.time = new Date().toISOString();
|
||||
data.msgtype = messtype;
|
||||
data.payloadtype = 1;
|
||||
data.payload = {};
|
||||
data.payload.applytype = applytype;
|
||||
data.payload.variables = varlist;
|
||||
pld.data = data;
|
||||
pld.signature = sha256.hmac(SHA256_HMAC_KEY, JSON.stringify(data));
|
||||
|
||||
api
|
||||
.post(API_URL, JSON.stringify(pld), {
|
||||
headers: { "Content-Type": "application/json" },
|
||||
})
|
||||
.then((response) => {
|
||||
var resp = response.data.data.payload.variables;
|
||||
for (var k in resp) varlist[k] = resp[k];
|
||||
if (onfinished) onfinished();
|
||||
})
|
||||
.catch((err) => {
|
||||
Notify.create({ color: "negative", position: "top", message: err.message, icon: "report_problem", });
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function SendAndRequest(varlist, mstp, apltp, shakey, okreport) {
|
||||
var onfinish = (okreport) ? () => { ShowSaveDialog(apltp) } : null;
|
||||
PostData(varlist, mstp, apltp, onfinish);
|
||||
}
|
||||
|
||||
export { SendAndRequest, PostData };
|
||||
|
||||
|
||||
|
||||
|
||||
28
WEBUI_SRC/src/css/app.scss
Normal file
28
WEBUI_SRC/src/css/app.scss
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
// app global css in SCSS form
|
||||
.body{
|
||||
background: #e0e0e0;
|
||||
}
|
||||
.card {
|
||||
border-radius: 10px;
|
||||
font-family: monospace;
|
||||
font-size: medium;
|
||||
break-inside: avoid;
|
||||
margin: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
.cardholder {
|
||||
column-count: 1;
|
||||
column-gap: 0;
|
||||
|
||||
padding: 10px 10px 10px 0;
|
||||
}
|
||||
@media (min-width: 1024px) {
|
||||
.cardholder {
|
||||
column-count: 2;
|
||||
}
|
||||
}
|
||||
@media (min-width: 1400px) {
|
||||
.cardholder {
|
||||
column-count: 3;
|
||||
}
|
||||
}
|
||||
25
WEBUI_SRC/src/css/quasar.variables.scss
Normal file
25
WEBUI_SRC/src/css/quasar.variables.scss
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
// Quasar SCSS (& Sass) Variables
|
||||
// --------------------------------------------------
|
||||
// To customize the look and feel of this app, you can override
|
||||
// the Sass/SCSS variables found in Quasar's source Sass/SCSS files.
|
||||
|
||||
// Check documentation for full list of Quasar variables
|
||||
|
||||
// Your own variables (that are declared here) and Quasar's own
|
||||
// ones will be available out of the box in your .vue/.scss/.sass files
|
||||
|
||||
// It's highly recommended to change the default colors
|
||||
// to match your app's branding.
|
||||
// Tip: Use the "Theme Builder" on Quasar's documentation website.
|
||||
|
||||
$primary : #1976D2;
|
||||
$secondary : #26A69A;
|
||||
$accent : #9C27B0;
|
||||
|
||||
$dark : #1D1D1D;
|
||||
$dark-page : #121212;
|
||||
|
||||
$positive : #21BA45;
|
||||
$negative : #C10015;
|
||||
$info : #31CCEC;
|
||||
$warning : #F2C037;
|
||||
37
WEBUI_SRC/src/layouts/MainLayout.vue
Normal file
37
WEBUI_SRC/src/layouts/MainLayout.vue
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<q-layout view="hLr Lpr lFf" class="body">
|
||||
<q-header>
|
||||
<q-toolbar>
|
||||
<q-btn flat dense round icon="menu" aria-label="Menu" @click="toggleLeftDrawer" />
|
||||
<q-toolbar-title> ESP32 WEBGUIAPP </q-toolbar-title>
|
||||
</q-toolbar>
|
||||
</q-header>
|
||||
<q-drawer v-model="leftDrawerOpen" show-if-above bordered>
|
||||
<q-list>
|
||||
<EssentialLink v-for="link in linksList" :key="link.title" v-bind="link" />
|
||||
</q-list>
|
||||
</q-drawer>
|
||||
<q-page-container>
|
||||
<router-view></router-view>
|
||||
</q-page-container>
|
||||
</q-layout>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
<script setup>
|
||||
import { ref, defineAsyncComponent } from "vue";
|
||||
import EssentialLink from "components/webguicomp/EssentialLink.vue";
|
||||
|
||||
const linksList = [
|
||||
{ title: "HOME", caption: "Main page", icon: "home", link: "#/home" },
|
||||
{ title: "APPLICATION", caption: "Application", icon: "apps", link: "#/page2" },
|
||||
{ title: "NETWORK", caption: "Network settings", icon: "public", link: "#/ifsettings" },
|
||||
{ title: "SERVICES", caption: "System services", icon: "miscellaneous_services", link: "#/services" },
|
||||
{ title: "SYSTEM", caption: "System tools", icon: "build", link: "#/system" }
|
||||
];
|
||||
|
||||
function toggleLeftDrawer() { leftDrawerOpen.value = !leftDrawerOpen.value; }
|
||||
const leftDrawerOpen = ref(false);
|
||||
|
||||
</script>
|
||||
31
WEBUI_SRC/src/pages/ErrorNotFound.vue
Normal file
31
WEBUI_SRC/src/pages/ErrorNotFound.vue
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
<template>
|
||||
<div class="fullscreen bg-blue text-white text-center q-pa-md flex flex-center">
|
||||
<div>
|
||||
<div style="font-size: 30vh">
|
||||
404
|
||||
</div>
|
||||
|
||||
<div class="text-h2" style="opacity:.4">
|
||||
Oops. Nothing here... four-ow-four
|
||||
</div>
|
||||
|
||||
<q-btn
|
||||
class="q-mt-xl"
|
||||
color="white"
|
||||
text-color="blue"
|
||||
unelevated
|
||||
to="/"
|
||||
label="Go Home"
|
||||
no-caps
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from 'vue'
|
||||
|
||||
export default defineComponent({
|
||||
name: 'ErrorNotFound'
|
||||
})
|
||||
</script>
|
||||
15
WEBUI_SRC/src/pages/Home.vue
Normal file
15
WEBUI_SRC/src/pages/Home.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<div class="cardholder">
|
||||
<HomeCard></HomeCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import HomeCard from "components/webguicomp/cards/HomeCard.vue";
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "HomePage",
|
||||
components: { HomeCard }
|
||||
});
|
||||
</script>
|
||||
36
WEBUI_SRC/src/pages/IndexPage2.vue
Normal file
36
WEBUI_SRC/src/pages/IndexPage2.vue
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
<template>
|
||||
<q-page class="flex flex-center">
|
||||
<div class="q-gutter-md row justify-center" style="font-size: 15em">
|
||||
<q-spinner-audio color="secondary" />
|
||||
<q-spinner-ball color="red" />
|
||||
<q-spinner-bars color="purple" />
|
||||
<q-spinner-box color="deep-orange" />
|
||||
<q-spinner-clock color="brown" />
|
||||
<q-spinner-comment color="deep-purple" />
|
||||
<q-spinner-cube color="indigo" />
|
||||
<q-spinner-dots color="blue" />
|
||||
<q-spinner-facebook color="light-blue" />
|
||||
<q-spinner-gears color="cyan" />
|
||||
<q-spinner-grid color="teal" />
|
||||
<q-spinner-hearts color="green" />
|
||||
<q-spinner-hourglass color="light-green" />
|
||||
<q-spinner-infinity color="lime" />
|
||||
<q-spinner-ios color="yellow" />
|
||||
<q-spinner-orbit color="blue" />
|
||||
<q-spinner-oval color="amber" />
|
||||
<q-spinner-pie color="orange" />
|
||||
<q-spinner-puff color="deep-orange" />
|
||||
<q-spinner-radio color="brown" />
|
||||
<q-spinner-rings color="grey" />
|
||||
<q-spinner-tail color="blue-grey" />
|
||||
</div>
|
||||
</q-page>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { defineComponent } from "vue";
|
||||
|
||||
export default defineComponent({
|
||||
name: "IndexPage2",
|
||||
});
|
||||
</script>
|
||||
15
WEBUI_SRC/src/pages/InterfacesSettings.vue
Normal file
15
WEBUI_SRC/src/pages/InterfacesSettings.vue
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<div class="cardholder">
|
||||
<WifiSetCard></WifiSetCard>
|
||||
<EthSetCard></EthSetCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import WifiSetCard from "components/webguicomp/cards/WifiSetCard.vue";
|
||||
import EthSetCard from "components/webguicomp/cards/EthSetCard.vue";
|
||||
|
||||
defineOptions({
|
||||
name: 'InterfacesSettings'
|
||||
})
|
||||
</script>
|
||||
17
WEBUI_SRC/src/pages/ServicesPage.vue
Normal file
17
WEBUI_SRC/src/pages/ServicesPage.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<template>
|
||||
<div class="cardholder">
|
||||
<SNTPCard></SNTPCard>
|
||||
<MQTT1Card></MQTT1Card>
|
||||
<MQTT2Card></MQTT2Card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SNTPCard from "components/webguicomp/cards/SNTPCard.vue";
|
||||
import MQTT1Card from "components/webguicomp/cards/MQTT1Card.vue";
|
||||
import MQTT2Card from "components/webguicomp/cards/MQTT2Card.vue";
|
||||
|
||||
defineOptions({
|
||||
name: 'ServicesPage'
|
||||
})
|
||||
</script>
|
||||
17
WEBUI_SRC/src/pages/SystemPage.vue
Normal file
17
WEBUI_SRC/src/pages/SystemPage.vue
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<template>
|
||||
<div class="cardholder">
|
||||
<SystemCard></SystemCard>
|
||||
<FirmwareCard></FirmwareCard>
|
||||
<StatCard></StatCard>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import SystemCard from "components/webguicomp/cards/SystemCard.vue";
|
||||
import FirmwareCard from "components/webguicomp/cards/FirmwareCard.vue";
|
||||
import StatCard from "components/webguicomp/cards/StatCard.vue";
|
||||
|
||||
defineOptions({
|
||||
name: 'SystemPage'
|
||||
})
|
||||
</script>
|
||||
30
WEBUI_SRC/src/router/index.js
Normal file
30
WEBUI_SRC/src/router/index.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
import { route } from 'quasar/wrappers'
|
||||
import { createRouter, createMemoryHistory, createWebHistory, createWebHashHistory } from 'vue-router'
|
||||
import routes from './routes'
|
||||
|
||||
/*
|
||||
* If not building with SSR mode, you can
|
||||
* directly export the Router instantiation;
|
||||
*
|
||||
* The function below can be async too; either use
|
||||
* async/await or return a Promise which resolves
|
||||
* with the Router instance.
|
||||
*/
|
||||
|
||||
export default route(function (/* { store, ssrContext } */) {
|
||||
const createHistory = process.env.SERVER
|
||||
? createMemoryHistory
|
||||
: (process.env.VUE_ROUTER_MODE === 'history' ? createWebHistory : createWebHashHistory)
|
||||
|
||||
const Router = createRouter({
|
||||
scrollBehavior: () => ({ left: 0, top: 0 }),
|
||||
routes,
|
||||
|
||||
// Leave this as is and make changes in quasar.conf.js instead!
|
||||
// quasar.conf.js -> build -> vueRouterMode
|
||||
// quasar.conf.js -> build -> publicPath
|
||||
history: createHistory(process.env.VUE_ROUTER_BASE)
|
||||
})
|
||||
|
||||
return Router
|
||||
})
|
||||
23
WEBUI_SRC/src/router/routes.js
Normal file
23
WEBUI_SRC/src/router/routes.js
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
const routes = [
|
||||
{
|
||||
path: "/",
|
||||
component: () => import("layouts/MainLayout.vue"),
|
||||
children: [
|
||||
{ path: "", component: () => import("pages/Home.vue") },
|
||||
{ path: "home", component: () => import("pages/Home.vue") },
|
||||
{ path: "page2", component: () => import("pages/IndexPage2.vue") },
|
||||
{ path: "ifsettings", component: () => import("pages/InterfacesSettings.vue") },
|
||||
{ path: "services", component: () => import("pages/ServicesPage.vue") },
|
||||
{ path: "system", component: () => import("pages/SystemPage.vue") },
|
||||
],
|
||||
},
|
||||
|
||||
// Always leave this as last one,
|
||||
// but you can also remove it
|
||||
{
|
||||
path: "/:catchAll(.*)*",
|
||||
component: () => import("pages/ErrorNotFound.vue"),
|
||||
},
|
||||
];
|
||||
|
||||
export default routes;
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit ac3be6bfdc745ec23e97961657fc9bc19e55333f
|
||||
Subproject commit fc2b1879e4ae43213c255567077b46fa41092159
|
||||
|
|
@ -1,26 +1,26 @@
|
|||
assets
|
||||
assets/ErrorNotFound.20af62e3.js
|
||||
assets/Home.937c89f4.js
|
||||
assets/IndexPage2.cfd8712b.js
|
||||
assets/InterfacesSettings.b0f34fc0.js
|
||||
assets/KFOkCnqEu92Fr1MmgVxIIzQ.34e9582c.woff
|
||||
assets/KFOlCnqEu92Fr1MmEU9fBBc-.9ce7f3ac.woff
|
||||
assets/KFOlCnqEu92Fr1MmSU5fBBc-.bf14c7d7.woff
|
||||
assets/KFOlCnqEu92Fr1MmWUlfBBc-.e0fd57c0.woff
|
||||
assets/KFOlCnqEu92Fr1MmYUtfBBc-.f6537e32.woff
|
||||
assets/KFOmCnqEu92Fr1Mu4mxM.f2abf7fb.woff
|
||||
assets/MainLayout.c9e97164.css
|
||||
assets/MainLayout.d8111854.js
|
||||
assets/QItem.9b856cf7.js
|
||||
assets/ServicesPage.e7ae390c.js
|
||||
assets/SystemPage.60d49a6a.js
|
||||
assets/axios.15d8ef65.js
|
||||
assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.fd84f88b.woff
|
||||
assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.4a4dbc62.woff2
|
||||
assets/helpers.6b816d79.js
|
||||
assets/index.6b461d14.css
|
||||
assets/index.e05c0c8e.js
|
||||
assets/network.98844594.js
|
||||
favicon.ico
|
||||
index.html
|
||||
assets
|
||||
assets/ErrorNotFound.9e15f11d.js
|
||||
assets/Home.4d198b4c.js
|
||||
assets/IndexPage2.9b840c18.js
|
||||
assets/InterfacesSettings.bbb3ac8c.js
|
||||
assets/KFOkCnqEu92Fr1MmgVxIIzQ.34e9582c.woff
|
||||
assets/KFOlCnqEu92Fr1MmEU9fBBc-.9ce7f3ac.woff
|
||||
assets/KFOlCnqEu92Fr1MmSU5fBBc-.bf14c7d7.woff
|
||||
assets/KFOlCnqEu92Fr1MmWUlfBBc-.e0fd57c0.woff
|
||||
assets/KFOlCnqEu92Fr1MmYUtfBBc-.f6537e32.woff
|
||||
assets/KFOmCnqEu92Fr1Mu4mxM.f2abf7fb.woff
|
||||
assets/MainLayout.3e18d42f.js
|
||||
assets/MainLayout.c9e97164.css
|
||||
assets/QItem.d253ebaa.js
|
||||
assets/ServicesPage.cf8d157c.js
|
||||
assets/SystemPage.719ebe80.js
|
||||
assets/axios.06fc16f1.js
|
||||
assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNa.fd84f88b.woff
|
||||
assets/flUhRq6tzZclQEJ-Vdg-IuiaDsNcIhQ8tQ.4a4dbc62.woff2
|
||||
assets/helpers.8d380ce8.js
|
||||
assets/index.4735cda6.js
|
||||
assets/index.6b461d14.css
|
||||
assets/network.fb530964.js
|
||||
favicon.ico
|
||||
index.html
|
||||
test.json
|
||||
Loading…
Reference in New Issue
Block a user