Merge branch 'MCUT' of https://gitea.mjm-staging.developers-homelab.net/mjm-group/ibms into MCUT
This commit is contained in:
commit
635a940d25
@ -5,8 +5,7 @@
|
||||
<iframe id="homeIframe"
|
||||
src="/ord?file:^px/AllMeter.px|view:?fullScreen=true"
|
||||
width="100%"
|
||||
height="100%"
|
||||
style="zoom: 0.1"></iframe>
|
||||
height="100%"></iframe>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
1
Frontend/ibms_solar/.env
Normal file
1
Frontend/ibms_solar/.env
Normal file
@ -0,0 +1 @@
|
||||
VITE_API_BASEURL = "http://192.168.0.206:8040"
|
24
Frontend/ibms_solar/.gitignore
vendored
Normal file
24
Frontend/ibms_solar/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
8
Frontend/ibms_solar/README.md
Normal file
8
Frontend/ibms_solar/README.md
Normal file
@ -0,0 +1,8 @@
|
||||
# React + Vite
|
||||
|
||||
This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
|
||||
|
||||
Currently, two official plugins are available:
|
||||
|
||||
- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
|
||||
- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
|
38
Frontend/ibms_solar/eslint.config.js
Normal file
38
Frontend/ibms_solar/eslint.config.js
Normal file
@ -0,0 +1,38 @@
|
||||
import js from '@eslint/js'
|
||||
import globals from 'globals'
|
||||
import react from 'eslint-plugin-react'
|
||||
import reactHooks from 'eslint-plugin-react-hooks'
|
||||
import reactRefresh from 'eslint-plugin-react-refresh'
|
||||
|
||||
export default [
|
||||
{ ignores: ['dist'] },
|
||||
{
|
||||
files: ['**/*.{js,jsx}'],
|
||||
languageOptions: {
|
||||
ecmaVersion: 2020,
|
||||
globals: globals.browser,
|
||||
parserOptions: {
|
||||
ecmaVersion: 'latest',
|
||||
ecmaFeatures: { jsx: true },
|
||||
sourceType: 'module',
|
||||
},
|
||||
},
|
||||
settings: { react: { version: '18.3' } },
|
||||
plugins: {
|
||||
react,
|
||||
'react-hooks': reactHooks,
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
rules: {
|
||||
...js.configs.recommended.rules,
|
||||
...react.configs.recommended.rules,
|
||||
...react.configs['jsx-runtime'].rules,
|
||||
...reactHooks.configs.recommended.rules,
|
||||
'react/jsx-no-target-blank': 'off',
|
||||
'react-refresh/only-export-components': [
|
||||
'warn',
|
||||
{ allowConstantExport: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
14
Frontend/ibms_solar/index.html
Normal file
14
Frontend/ibms_solar/index.html
Normal file
@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <link rel="icon" type="image/svg+xml" href="/vite.svg" /> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://rsms.me/inter/inter.css">
|
||||
<title>Marketing Dashboard - Application Intel - SmartAdmin v4.5.1</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
</body>
|
||||
</html>
|
5571
Frontend/ibms_solar/package-lock.json
generated
Normal file
5571
Frontend/ibms_solar/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
38
Frontend/ibms_solar/package.json
Normal file
38
Frontend/ibms_solar/package.json
Normal file
@ -0,0 +1,38 @@
|
||||
{
|
||||
"name": "ibms_solar",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"lint": "eslint .",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"apexcharts": "^3.53.0",
|
||||
"axios": "^1.7.7",
|
||||
"date-fns": "^3.6.0",
|
||||
"react": "^18.3.1",
|
||||
"react-apexcharts": "^1.4.1",
|
||||
"react-datepicker": "^7.3.0",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-icons": "^5.3.0",
|
||||
"react-router-dom": "^6.26.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.9.0",
|
||||
"@types/react": "^18.3.3",
|
||||
"@types/react-dom": "^18.3.0",
|
||||
"@vitejs/plugin-react": "^4.3.1",
|
||||
"autoprefixer": "^10.4.20",
|
||||
"eslint": "^9.9.0",
|
||||
"eslint-plugin-react": "^7.35.0",
|
||||
"eslint-plugin-react-hooks": "^5.1.0-rc.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.9",
|
||||
"globals": "^15.9.0",
|
||||
"postcss": "^8.4.44",
|
||||
"tailwindcss": "^3.4.10",
|
||||
"vite": "^5.4.1"
|
||||
}
|
||||
}
|
6
Frontend/ibms_solar/postcss.config.js
Normal file
6
Frontend/ibms_solar/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
1
Frontend/ibms_solar/public/vite.svg
Normal file
1
Frontend/ibms_solar/public/vite.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
23
Frontend/ibms_solar/src/App.jsx
Normal file
23
Frontend/ibms_solar/src/App.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import React from "react";
|
||||
import { HashRouter, Link, Route, Routes } from "react-router-dom";
|
||||
import { SiteProvider } from "@/context";
|
||||
import { Home, Energy } from "@/page";
|
||||
import { Navbar } from "@/components";
|
||||
|
||||
const App = () => (
|
||||
<HashRouter>
|
||||
<SiteProvider>
|
||||
<header>
|
||||
<Navbar />
|
||||
</header>
|
||||
<main className="py-8 w-full bg-zinc-700 min-h-[100vh]">
|
||||
<Routes>
|
||||
<Route path="/" element={<Home />} />
|
||||
<Route path="/energy" element={<Energy />} />
|
||||
</Routes>
|
||||
</main>
|
||||
</SiteProvider>
|
||||
</HashRouter>
|
||||
);
|
||||
|
||||
export default App;
|
5
Frontend/ibms_solar/src/assets/index.js
Normal file
5
Frontend/ibms_solar/src/assets/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import logo from './logo.png';
|
||||
|
||||
export {
|
||||
logo,
|
||||
};
|
BIN
Frontend/ibms_solar/src/assets/logo.png
Normal file
BIN
Frontend/ibms_solar/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
261
Frontend/ibms_solar/src/components/Navbar.jsx
Normal file
261
Frontend/ibms_solar/src/components/Navbar.jsx
Normal file
@ -0,0 +1,261 @@
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { logo } from "@ASSET";
|
||||
import { getCookie } from "@/utils";
|
||||
import { GET_AUTHPAGE_API, AUTHPAGES, GET_SUBAUTHPAGE_API } from "@/constant";
|
||||
import { AiOutlineClose, AiOutlineMenu } from "react-icons/ai";
|
||||
import {
|
||||
FaHome,
|
||||
FaSolarPanel,
|
||||
FaUserCircle,
|
||||
FaCommentDots,
|
||||
FaCommentSlash,
|
||||
} from "react-icons/fa";
|
||||
import axios from "axios";
|
||||
|
||||
const Navbar = () => {
|
||||
const [nav, setNav] = useState(false);
|
||||
const [showErr, setShowErr] = useState(false);
|
||||
const [authPage, setAuthPage] = useState([]);
|
||||
const [subAuthPage, setSubAuthPage] = useState([]);
|
||||
const [user, setUser] = useState(null);
|
||||
const [openDropdowns, setOpenDropdowns] = useState({
|
||||
home: false,
|
||||
pf1: false,
|
||||
user: false,
|
||||
});
|
||||
|
||||
const handleNav = () => {
|
||||
setNav(!nav);
|
||||
};
|
||||
|
||||
const toggleDropdown = (key) => {
|
||||
setOpenDropdowns((prev) => ({
|
||||
...prev,
|
||||
[key]: !prev[key],
|
||||
}));
|
||||
};
|
||||
|
||||
const toggleErrIcon = () => {
|
||||
setShowErr(!showErr);
|
||||
};
|
||||
|
||||
const iniFroList = async () => {
|
||||
const res = await axios.post(GET_AUTHPAGE_API);
|
||||
setAuthPage(
|
||||
res.data.data.map((d) => ({
|
||||
...d,
|
||||
icon: AUTHPAGES(d.authCode),
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
const getSubList = async () => {
|
||||
const res = await axios.post(GET_SUBAUTHPAGE_API);
|
||||
console.log("res", res);
|
||||
setSubAuthPage(
|
||||
res.data.data.map((d) => ({
|
||||
...d,
|
||||
}))
|
||||
);
|
||||
};
|
||||
|
||||
const navigate = (pageName) => {
|
||||
sessionStorage.setItem("lastPage", pageName);
|
||||
window.location.href = "/file/index.html";
|
||||
};
|
||||
|
||||
const navigateToSub = (sub) => {
|
||||
let pageAct = JSON.parse(sessionStorage.getItem("pageAct"));
|
||||
pageAct = {
|
||||
...pageAct,
|
||||
buildList: sub,
|
||||
buiTag: sub.building_tag,
|
||||
buiName: sub.full_name,
|
||||
};
|
||||
sessionStorage.setItem("lastPage", "dashboard");
|
||||
sessionStorage.setItem("pageAct", JSON.stringify(pageAct));
|
||||
window.location.href = "/file/index.html";
|
||||
};
|
||||
|
||||
// 獲取並初始化導航頁面
|
||||
useEffect(() => {
|
||||
iniFroList();
|
||||
getSubList();
|
||||
setUser(getCookie("niagara_userid"));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<nav className="bg-zinc-800 flex justify-between items-center h-16 max-w-full mx-auto px-8 text-white">
|
||||
<Link to="/" className="flex items-center space-x-3 rtl:space-x-reverse">
|
||||
<img src={logo} alt="logo" className="w-[200px] md:w-[272px] object-contain" />
|
||||
</Link>
|
||||
<ul
|
||||
className={
|
||||
nav
|
||||
? "fixed left-0 top-0 md:flex w-[60%] h-full border-r border-r-gray-900 bg-[#000300] ease-in-out duration-500"
|
||||
: "ease-in-out w-[60%] duration-500 fixed top-0 bottom-0 left-[-100%] md:flex md:left-0 md:relative md:w-auto"
|
||||
}
|
||||
>
|
||||
<li className=" hover:text-[#ccbfdf] cursor-pointer">
|
||||
<button
|
||||
onClick={() => toggleDropdown("home")}
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
<FaHome />
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">首頁</span>
|
||||
</button>
|
||||
<div
|
||||
className={`md:absolute ${
|
||||
openDropdowns.home ? "block" : "hidden"
|
||||
} bg-zinc-600 min-w-[200px] rounded shadow-md space-y-2`}
|
||||
>
|
||||
<ul className="">
|
||||
<li
|
||||
key="1"
|
||||
onClick={() => navigate("schoolView")}
|
||||
className="text-md ps-4 py-3 hover:bg-zinc-500"
|
||||
>
|
||||
校園總覽
|
||||
</li>
|
||||
<li
|
||||
key="2"
|
||||
onClick={() => navigate("elecSingleLine")}
|
||||
className="text-md ps-4 py-3 hover:bg-zinc-500"
|
||||
>
|
||||
電表單線路
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
{authPage.map((page) => (
|
||||
<li
|
||||
key={page.authCode}
|
||||
className="hover:text-[#ccbfdf] cursor-pointer"
|
||||
>
|
||||
{page.authCode === "PF1" ? (
|
||||
<>
|
||||
<button
|
||||
onClick={() => toggleDropdown("pf1")}
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
{page.icon}
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">
|
||||
{page.subName}
|
||||
</span>
|
||||
</button>
|
||||
<div
|
||||
className={`md:absolute ${
|
||||
openDropdowns.pf1 ? "block" : "hidden"
|
||||
} bg-zinc-600 min-w-[200px] rounded shadow-md space-y-2`}
|
||||
>
|
||||
<ul className="">
|
||||
{subAuthPage.map((sub) => (
|
||||
<li
|
||||
key={sub.building_tag}
|
||||
onClick={() => navigateToSub(sub)}
|
||||
className="text-md text-white ps-4 py-3 hover:bg-zinc-500"
|
||||
>
|
||||
{sub.full_name}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => navigate(page.showView)}
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
{page.icon}
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">
|
||||
{page.subName}
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</li>
|
||||
))}
|
||||
<li className=" hover:text-[#ccbfdf] cursor-pointer">
|
||||
<Link to="/"
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
<FaSolarPanel />
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">太陽能管理</span>
|
||||
</Link>
|
||||
</li>
|
||||
<li className="md:hidden hover:text-[#ccbfdf] cursor-pointer">
|
||||
<button
|
||||
onClick={toggleErrIcon}
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
{showErr ? <FaCommentSlash /> : <FaCommentDots />}
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">{showErr ? "隱藏警告" : "顯示警告"}</span>
|
||||
</button>
|
||||
</li>
|
||||
<li className="md:hidden hover:text-[#ccbfdf] cursor-pointer">
|
||||
<button
|
||||
onClick={() => toggleDropdown("user")}
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
<FaUserCircle />
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">{ user ? user : "webUser" }</span>
|
||||
</button>
|
||||
<div
|
||||
className={`md:absolute ${
|
||||
openDropdowns.user ? "block" : "hidden"
|
||||
} bg-zinc-600 min-w-[200px] rounded shadow-md space-y-2`}
|
||||
>
|
||||
<ul className="">
|
||||
<li
|
||||
className="text-md ps-4 py-3 hover:bg-zinc-500"
|
||||
>
|
||||
<a href="/logout">登出</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
{/* Mobile Navigation Icon */}
|
||||
<div onClick={handleNav} className="block md:hidden">
|
||||
{nav ? <AiOutlineClose size={20} /> : <AiOutlineMenu size={20} />}
|
||||
</div>
|
||||
{/* user */}
|
||||
<ul className="hidden md:flex">
|
||||
<li className=" hover:text-[#ccbfdf] cursor-pointer">
|
||||
<button
|
||||
onClick={toggleErrIcon}
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
{showErr ? <FaCommentSlash /> : <FaCommentDots />}
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">
|
||||
{showErr ? "隱藏警告" : "顯示警告"}
|
||||
</span>
|
||||
</button>
|
||||
</li>
|
||||
|
||||
<li className=" hover:text-[#ccbfdf] cursor-pointer">
|
||||
<button
|
||||
onClick={() => toggleDropdown("user")}
|
||||
className="flex md:flex-col justify-start md:justify-center items-center text-2xl md:text-3xl w-full px-4 border-b border-slate-500 md:border-none py-5 md:py-0"
|
||||
>
|
||||
<FaUserCircle />
|
||||
<span className="text-sm md:text-sm ps-2 md:ps-0">{ user ? user : "webUser" }</span>
|
||||
</button>
|
||||
<div
|
||||
className={`md:absolute right-0 ${
|
||||
openDropdowns.user ? "block" : "hidden"
|
||||
} bg-zinc-600 min-w-[200px] rounded shadow-md space-y-2`}
|
||||
>
|
||||
<ul className="">
|
||||
<li className="text-md ps-4 py-3 hover:bg-zinc-500">
|
||||
<a href="/logout">登出</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
);
|
||||
};
|
||||
|
||||
export default Navbar;
|
393
Frontend/ibms_solar/src/components/energy/HistoryData.jsx
Normal file
393
Frontend/ibms_solar/src/components/energy/HistoryData.jsx
Normal file
@ -0,0 +1,393 @@
|
||||
import React, { useState } from "react";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import { zhTW } from "date-fns/locale";
|
||||
import Chart from "react-apexcharts";
|
||||
import historyData from "@/mock/historyData";
|
||||
|
||||
const HistoryData = () => {
|
||||
const [selectedFilter, setSelectedFilter] = useState("day");
|
||||
const [subFilter, setSubFilter] = useState("today");
|
||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||
|
||||
const handleFilterChange = (filter, subFilter = null) => {
|
||||
setSelectedFilter(filter);
|
||||
|
||||
// 設置次篩選項並設置對應的日期
|
||||
if (subFilter) {
|
||||
setSubFilter(subFilter);
|
||||
}
|
||||
|
||||
switch (subFilter || filter) {
|
||||
case "today":
|
||||
setSelectedDate(new Date());
|
||||
break;
|
||||
case "yesterday":
|
||||
setSelectedDate(new Date(Date.now() - 86400000));
|
||||
break;
|
||||
case "currentMonth":
|
||||
const now = new Date();
|
||||
setSelectedDate(new Date(now.getFullYear(), now.getMonth(), 1));
|
||||
break;
|
||||
case "lastMonth":
|
||||
const lastMonth = new Date();
|
||||
setSelectedDate(
|
||||
new Date(lastMonth.getFullYear(), lastMonth.getMonth() - 1, 1)
|
||||
);
|
||||
break;
|
||||
case "currentYear":
|
||||
setSelectedDate(new Date(new Date().getFullYear(), 0, 1));
|
||||
break;
|
||||
case "lastYear":
|
||||
setSelectedDate(new Date(new Date().getFullYear() - 1, 0, 1));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
const renderButtons = () => {
|
||||
const buttonConfigs = {
|
||||
day: ["today", "yesterday"],
|
||||
month: ["currentMonth", "lastMonth"],
|
||||
year: ["currentYear", "lastYear"],
|
||||
};
|
||||
|
||||
// 根據 selectedFilter 來顯示相應的按鈕組
|
||||
return buttonConfigs[selectedFilter]?.map((filter) => (
|
||||
<button
|
||||
key={filter}
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 rounded me-2 ${
|
||||
subFilter === filter ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleFilterChange(selectedFilter, filter)}
|
||||
>
|
||||
{filter === "today" && "今日"}
|
||||
{filter === "yesterday" && "昨日"}
|
||||
{filter === "currentMonth" && "本月"}
|
||||
{filter === "lastMonth" && "上個月"}
|
||||
{filter === "currentYear" && "今年"}
|
||||
{filter === "lastYear" && "去年"}
|
||||
</button>
|
||||
));
|
||||
};
|
||||
|
||||
// 提取各項圖表數據
|
||||
const timestamps = historyData.map((item) => item.timestamp); //時間
|
||||
const tempData = historyData.map((item) => item.temp); //溫度
|
||||
const irrDayHourData = historyData.map((item) => item.irrDayHour); //累積日照
|
||||
const prData = historyData.map((item) => item.pr); //pr
|
||||
const kwhkwpData = historyData.map((item) => item.kwhkwp); //日均發電度數
|
||||
const kwhData = historyData.map((item) => item.kwh); //發電量
|
||||
|
||||
const options = {
|
||||
chart: {
|
||||
type: "line",
|
||||
height: 500,
|
||||
stacked: false,
|
||||
},
|
||||
stroke: {
|
||||
width: [0, 0, 0, 4, 4], // 對於 bar 是 0,對於 line 是 4
|
||||
},
|
||||
plotOptions: {
|
||||
bar: {
|
||||
columnWidth: "50%",
|
||||
},
|
||||
},
|
||||
colors: ["#008FFB", "#FEB019", "#FF4560", "#00E396", "#775DD0"],
|
||||
labels: timestamps,
|
||||
xaxis: {
|
||||
type: "category",
|
||||
},
|
||||
yaxis: [
|
||||
{
|
||||
seriesName: "發電量(kWh)",
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: "#008FFB",
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: "#008FFB",
|
||||
},
|
||||
formatter: function (value) {
|
||||
return value.toFixed(2); // 格式化Y軸數值,移除多餘的0
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "kWh",
|
||||
style: {
|
||||
color: "#008FFB",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
seriesName: "日均發電度數",
|
||||
opposite: true,
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: "#FEB019",
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: "#FEB019",
|
||||
},
|
||||
formatter: function (value) {
|
||||
return value.toFixed(2); // 格式化Y軸數值,移除多餘的0
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "kWh/kWp",
|
||||
style: {
|
||||
color: "#FEB019",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
seriesName: "PR(%)",
|
||||
opposite: true,
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: "#FF4560",
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: "#FF4560",
|
||||
},
|
||||
formatter: function (value) {
|
||||
return value.toFixed(2); // 格式化Y軸數值,移除多餘的0
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "PR(%)",
|
||||
style: {
|
||||
color: "#FF4560",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
seriesName: "累積日照量(Wh/m2)",
|
||||
opposite: true,
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: "#00E396",
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: "#00E396",
|
||||
},
|
||||
formatter: function (value) {
|
||||
return value.toFixed(2); // 格式化Y軸數值,移除多餘的0
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "Wh/m2",
|
||||
style: {
|
||||
color: "#00E396",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
seriesName: "溫度(℃)",
|
||||
opposite: true,
|
||||
axisTicks: {
|
||||
show: true,
|
||||
},
|
||||
axisBorder: {
|
||||
show: true,
|
||||
color: "#775DD0",
|
||||
},
|
||||
labels: {
|
||||
style: {
|
||||
colors: "#775DD0",
|
||||
},
|
||||
formatter: function (value) {
|
||||
return value.toFixed(2); // 格式化Y軸數值,移除多餘的0
|
||||
},
|
||||
},
|
||||
title: {
|
||||
text: "℃",
|
||||
style: {
|
||||
color: "#775DD0",
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
tooltip: {
|
||||
shared: true,
|
||||
intersect: false,
|
||||
},
|
||||
title: {
|
||||
text: "發電量及累積日照量",
|
||||
},
|
||||
theme: {
|
||||
mode: "dark",
|
||||
},
|
||||
};
|
||||
|
||||
const series = [
|
||||
{
|
||||
name: "發電量(kWh)",
|
||||
type: "column",
|
||||
data: kwhData,
|
||||
},
|
||||
{
|
||||
name: "日均發電度數",
|
||||
type: "column",
|
||||
data: kwhkwpData,
|
||||
},
|
||||
{
|
||||
name: "PR(%)",
|
||||
type: "column",
|
||||
data: prData,
|
||||
},
|
||||
{
|
||||
name: "累積日照量(Wh/m2)",
|
||||
type: "line",
|
||||
data: irrDayHourData,
|
||||
},
|
||||
{
|
||||
name: "溫度(℃)",
|
||||
type: "line",
|
||||
data: tempData,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<section className="p-8">
|
||||
<div className="flex items-center mb-8">
|
||||
<div className="inline-flex shadow-sm me-8" role="group">
|
||||
<button
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 rounded-l ${
|
||||
selectedFilter === "day" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleFilterChange("day", "today")}
|
||||
>
|
||||
日
|
||||
</button>
|
||||
<button
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 ${
|
||||
selectedFilter === "month" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleFilterChange("month", "currentMonth")}
|
||||
>
|
||||
月
|
||||
</button>
|
||||
<button
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 ${
|
||||
selectedFilter === "year" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleFilterChange("year", "currentYear")}
|
||||
>
|
||||
年
|
||||
</button>
|
||||
<button
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 rounded-r ${
|
||||
selectedFilter === "historical" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => setSelectedFilter("historical")}
|
||||
>
|
||||
歷年
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{selectedFilter !== "historical" && (
|
||||
<>
|
||||
<div className="inline-flex shadow-sm me-8" role="group">
|
||||
{renderButtons()}
|
||||
</div>
|
||||
|
||||
<DatePicker
|
||||
selected={selectedDate}
|
||||
onChange={(date) => setSelectedDate(date)}
|
||||
dateFormat={
|
||||
selectedFilter === "month"
|
||||
? "yyyy/MM"
|
||||
: selectedFilter === "year"
|
||||
? "yyyy"
|
||||
: "yyyy/MM/dd"
|
||||
}
|
||||
showMonthYearPicker={selectedFilter === "month"}
|
||||
showYearPicker={selectedFilter === "year"}
|
||||
locale={zhTW}
|
||||
className="border border-gray-300 rounded px-4 py-2 me-4"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<button className="text-white bg-sky-500 hover:bg-sky-600 rounded px-5 py-2">
|
||||
查詢
|
||||
</button>
|
||||
</div>
|
||||
<div className="bg-gray-600 shadow-sm shadow-gray-500 p-3 mb-8">
|
||||
<table className="min-w-full ">
|
||||
<caption className="text-start text-xl p-4 text-white">總結</caption>
|
||||
<thead>
|
||||
<tr className="border-t border-b border-gray-400 text-white ">
|
||||
<th className="px-4 py-3 text-start">時間</th>
|
||||
<th className="px-4 py-3 text-start">發電量(kWh)</th>
|
||||
<th className="px-4 py-3 text-start">日均發電度數</th>
|
||||
<th className="px-4 py-3 text-start">累積日照量(Wh/m2)</th>
|
||||
<th className="px-4 py-3 text-start">PR(%)</th>
|
||||
<th className="px-4 py-3 text-start">溫度(℃)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr className="text-white">
|
||||
<td className="px-4 py-3">2024-09-04</td>
|
||||
<td className="px-4 py-3">205.00</td>
|
||||
<td className="px-4 py-3">4.38</td>
|
||||
<td className="px-4 py-3">5,691.39</td>
|
||||
<td className="px-4 py-3">76.95</td>
|
||||
<td className="px-4 py-3">0.00</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<Chart options={options} series={series} type="line" height={500} />
|
||||
<div className="bg-gray-600 shadow-sm shadow-gray-500 p-3 mt-6">
|
||||
<table className="min-w-full ">
|
||||
<caption className="text-start text-xl p-4 text-white">
|
||||
詳細資訊
|
||||
</caption>
|
||||
<thead>
|
||||
<tr className="border-t border-b border-gray-400 text-white ">
|
||||
<th className="px-4 py-3 text-start">時間</th>
|
||||
<th className="px-4 py-3 text-start">發電量(kWh)</th>
|
||||
<th className="px-4 py-3 text-start">日均發電度數</th>
|
||||
<th className="px-4 py-3 text-start">累積日照量(Wh/m2)</th>
|
||||
<th className="px-4 py-3 text-start">PR(%)</th>
|
||||
<th className="px-4 py-3 text-start">溫度(℃)</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{historyData.map((item, index) => (
|
||||
<tr key={index} className="border-t border-gray-400 text-white">
|
||||
<td className="px-4 py-3">{item.timestamp}</td>
|
||||
<td className="px-4 py-3">{item.kwh}</td>
|
||||
<td className="px-4 py-3">{item.solarhour}</td>
|
||||
<td className="px-4 py-3">{item.irradiance}</td>
|
||||
<td className="px-4 py-3">{item.pr}</td>
|
||||
<td className="px-4 py-3">{item.temp}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default HistoryData;
|
@ -0,0 +1,17 @@
|
||||
import React, { forwardRef } from "react";
|
||||
|
||||
const IframeComponent = forwardRef(({ src, onLoad }, ref) => {
|
||||
return (
|
||||
<iframe
|
||||
ref={ref}
|
||||
src={src}
|
||||
width="100%"
|
||||
height="800"
|
||||
className="border-0"
|
||||
title="Solar Management"
|
||||
onLoad={onLoad}
|
||||
></iframe>
|
||||
);
|
||||
});
|
||||
|
||||
export default IframeComponent;
|
234
Frontend/ibms_solar/src/components/energy/InverterAnalysis.jsx
Normal file
234
Frontend/ibms_solar/src/components/energy/InverterAnalysis.jsx
Normal file
@ -0,0 +1,234 @@
|
||||
import React, { useState } from "react";
|
||||
import Chart from "react-apexcharts";
|
||||
import DatePicker from "react-datepicker";
|
||||
import "react-datepicker/dist/react-datepicker.css";
|
||||
import { zhTW } from "date-fns/locale";
|
||||
import inverterData from "@/mock/inverterData.json";
|
||||
import yearData from "@/mock/yearData.json";
|
||||
import quarterData from "@/mock/quarterData";
|
||||
import monthData from "@/mock/monthData";
|
||||
import todayData from "@/mock/todayData";
|
||||
|
||||
const InverterAnalysis = () => {
|
||||
const [date, setDate] = useState(new Date());
|
||||
const [activeButton, setActiveButton] = useState(null);
|
||||
const [quarter, setQuarter] = useState("1"); // 用於季度選擇
|
||||
const [barData, setBarData] = useState(todayData);
|
||||
|
||||
const handleDateChange = (date) => {
|
||||
setDate(date);
|
||||
setActiveButton(null);
|
||||
};
|
||||
|
||||
const handleButtonClick = (buttonType) => {
|
||||
if (buttonType === "today") {
|
||||
setDate(new Date());
|
||||
setBarData(todayData);
|
||||
} else if (buttonType === "yesterday") {
|
||||
setDate(new Date(Date.now() - 86400000));
|
||||
} else if (buttonType === "month") {
|
||||
setDate(new Date(Date.now() - 86400000));
|
||||
setBarData(monthData);
|
||||
} else if (buttonType === "quarter") {
|
||||
setDate(new Date(Date.now() - 86400000));
|
||||
setBarData(quarterData);
|
||||
} else if (buttonType === "year") {
|
||||
setDate(new Date(Date.now() - 86400000));
|
||||
setBarData(yearData);
|
||||
}
|
||||
setActiveButton(buttonType); // 设置激活的按钮
|
||||
};
|
||||
|
||||
// 生成 x 軸的分類標籤
|
||||
const xCategories = inverterData.xAxisOnTime;
|
||||
// 生成系列數據
|
||||
const series = Object.keys(inverterData.series).map((inverterId) => {
|
||||
return {
|
||||
name: inverterId,
|
||||
data: inverterData.series[inverterId].map((point) => point.value),
|
||||
};
|
||||
});
|
||||
const options = {
|
||||
chart: {
|
||||
type: "heatmap",
|
||||
height: 500,
|
||||
background: "#3f3f46",
|
||||
},
|
||||
plotOptions: {
|
||||
heatmap: {
|
||||
useFillColorAsStroke: true,
|
||||
colorScale: {
|
||||
ranges: [
|
||||
{ from: 0, to: 0.124999, color: "#0000ff", name: "Low" }, // Blue
|
||||
{ from: 0.125, to: 0.249999, color: "#5541b5", name: "Moderate" }, // Purple
|
||||
{ from: 0.25, to: 0.374999, color: "#ab836b", name: "High" }, // Brown
|
||||
{ from: 0.375, to: 0.499999, color: "#dba841", name: "Very High" }, // Yellow
|
||||
{ from: 0.5, to: 0.624999, color: "#eda42f", name: "Extreme" }, // Orange
|
||||
{ from: 0.625, to: 0.749999, color: "#f4621c", name: "Severe" }, // Dark Orange
|
||||
{ from: 0.75, to: 0.874999, color: "#f65017", name: "Critical" }, // Red-Orange
|
||||
{ from: 0.875, to: 1, color: "#fb2109", name: "Danger" }, // Red
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false,
|
||||
},
|
||||
xaxis: {
|
||||
type: "category",
|
||||
categories: xCategories,
|
||||
},
|
||||
title: {
|
||||
text: "各逆變器當日均發電度數",
|
||||
},
|
||||
theme: {
|
||||
mode: "dark",
|
||||
},
|
||||
};
|
||||
|
||||
const barseries = barData
|
||||
? Object.keys(barData.datasets).map((Id) => {
|
||||
return {
|
||||
name: Id,
|
||||
data: barData.datasets[Id],
|
||||
};
|
||||
})
|
||||
: [];
|
||||
const baroptions = {
|
||||
chart: {
|
||||
type: "bar",
|
||||
height: 500,
|
||||
background: "#3f3f46",
|
||||
},
|
||||
xaxis: {
|
||||
type: "category",
|
||||
categories: barData ? barData.labels : [],
|
||||
},
|
||||
title: {
|
||||
text: "發電量",
|
||||
},
|
||||
theme: {
|
||||
mode: "dark",
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="p-8">
|
||||
<div className="flex items-center mb-8">
|
||||
<div class="inline-flex shadow-sm me-8" role="group">
|
||||
<button
|
||||
type="button"
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 rounded-l ${
|
||||
activeButton === "today" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleButtonClick("today")}
|
||||
>
|
||||
今天
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 rounded-r ${
|
||||
activeButton === "yesterday" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleButtonClick("yesterday")}
|
||||
>
|
||||
昨天
|
||||
</button>
|
||||
</div>
|
||||
<DatePicker
|
||||
selected={date}
|
||||
onChange={handleDateChange}
|
||||
dateFormat="yyyy/MM/dd"
|
||||
locale={zhTW}
|
||||
className="border border-gray-300 rounded px-4 py-2 me-4"
|
||||
/>
|
||||
<button className="text-white bg-sky-500 hover:bg-sky-600 rounded px-5 py-2">
|
||||
查詢
|
||||
</button>
|
||||
</div>
|
||||
<div id="chart">
|
||||
<Chart options={options} series={series} type="heatmap" height={500} />
|
||||
</div>
|
||||
<div className="flex items-center mb-8 mt-16">
|
||||
<div class="inline-flex shadow-sm me-8" role="group">
|
||||
<button
|
||||
type="button"
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 rounded-l ${
|
||||
activeButton === "today" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleButtonClick("today")}
|
||||
>
|
||||
日
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 ${
|
||||
activeButton === "month" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleButtonClick("month")}
|
||||
>
|
||||
月
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 ${
|
||||
activeButton === "quarter" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleButtonClick("quarter")}
|
||||
>
|
||||
季
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className={`text-white px-5 py-2 hover:bg-gray-500 rounded-r ${
|
||||
activeButton === "year" ? "bg-sky-500" : "bg-gray-400"
|
||||
}`}
|
||||
onClick={() => handleButtonClick("year")}
|
||||
>
|
||||
年
|
||||
</button>
|
||||
</div>
|
||||
<DatePicker
|
||||
selected={date}
|
||||
onChange={handleDateChange}
|
||||
dateFormat={
|
||||
activeButton === "month"
|
||||
? "yyyy/MM"
|
||||
: activeButton === "year"
|
||||
? "yyyy"
|
||||
: activeButton === "quarter"
|
||||
? "yyyy"
|
||||
: "yyyy/MM/dd"
|
||||
}
|
||||
showMonthYearPicker={activeButton === "month"}
|
||||
showYearPicker={activeButton === "year" || activeButton === "quarter"}
|
||||
className="border border-gray-300 rounded px-4 py-2 me-4"
|
||||
locale={zhTW}
|
||||
/>
|
||||
{activeButton === "quarter" && (
|
||||
<select
|
||||
className="border border-gray-300 rounded px-4 py-2 me-4"
|
||||
value={quarter}
|
||||
onChange={(e) => {
|
||||
setQuarter(e.target.value);
|
||||
}}
|
||||
>
|
||||
<option value="1">1-3月</option>
|
||||
<option value="2">4-6月</option>
|
||||
<option value="3">7-9月</option>
|
||||
<option value="4">10-12月</option>
|
||||
</select>
|
||||
)}
|
||||
|
||||
<button className="text-white bg-sky-500 hover:bg-sky-600 rounded px-5 py-2">
|
||||
查詢
|
||||
</button>
|
||||
</div>
|
||||
<div id="chart">
|
||||
<Chart options={baroptions} series={barseries} type="bar" height={500} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default InverterAnalysis;
|
7
Frontend/ibms_solar/src/components/index.js
Normal file
7
Frontend/ibms_solar/src/components/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import Navbar from "./Navbar";
|
||||
|
||||
import IframeComponent from "./energy/IframeComponent";
|
||||
import HistoryData from "./energy/HistoryData";
|
||||
import InverterAnalysis from "./energy/InverterAnalysis";
|
||||
|
||||
export { Navbar, IframeComponent, HistoryData, InverterAnalysis };
|
4
Frontend/ibms_solar/src/constant/api_app.js
Normal file
4
Frontend/ibms_solar/src/constant/api_app.js
Normal file
@ -0,0 +1,4 @@
|
||||
const BASEURL = import.meta.env.VITE_API_BASEURL;
|
||||
export const POST_LOGIN = `${BASEURL}/api/Login/`;
|
||||
export const GET_AUTHPAGE_API = `${BASEURL}/api/GetUsrFroList`;
|
||||
export const GET_SUBAUTHPAGE_API = `${BASEURL}/api/Device/GetBuild`;
|
30
Frontend/ibms_solar/src/constant/authPage.jsx
Normal file
30
Frontend/ibms_solar/src/constant/authPage.jsx
Normal file
@ -0,0 +1,30 @@
|
||||
import { FaTv, FaChartPie, FaChartArea, FaChartLine, FaBell, FaServer, FaImage, FaUser } from "react-icons/fa";
|
||||
|
||||
const AUTHPAGES = (authCode) => {
|
||||
switch (authCode) {
|
||||
case "PF1":
|
||||
return <FaTv />;
|
||||
case "PF2":
|
||||
return <FaChartPie />;
|
||||
case "PF3":
|
||||
return <FaChartArea />;
|
||||
case "PF4":
|
||||
return <FaChartLine />;
|
||||
case "PF5":
|
||||
return <FaBell />;
|
||||
case "PF6":
|
||||
return <FaServer />;
|
||||
case "PF7":
|
||||
return <FaImage />;
|
||||
case "PF8":
|
||||
return <FaUser />;
|
||||
case "PF9":
|
||||
return <FaUser />;
|
||||
case "PF10":
|
||||
return <FaChartArea />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export default AUTHPAGES;
|
5
Frontend/ibms_solar/src/constant/index.js
Normal file
5
Frontend/ibms_solar/src/constant/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { GET_AUTHPAGE_API, GET_SUBAUTHPAGE_API } from "./api_app";
|
||||
import AUTHPAGES from "./authPage";
|
||||
import EnergyTABS from "./tabList";
|
||||
|
||||
export { GET_AUTHPAGE_API, GET_SUBAUTHPAGE_API, AUTHPAGES, EnergyTABS };
|
54
Frontend/ibms_solar/src/constant/tabList.jsx
Normal file
54
Frontend/ibms_solar/src/constant/tabList.jsx
Normal file
@ -0,0 +1,54 @@
|
||||
import {
|
||||
FaFileMedicalAlt,
|
||||
FaInfoCircle,
|
||||
FaRegListAlt,
|
||||
FaChartLine,
|
||||
FaHistory,
|
||||
FaExclamationTriangle,
|
||||
} from "react-icons/fa";
|
||||
import {
|
||||
IframeComponent,
|
||||
InverterAnalysis,
|
||||
HistoryData,
|
||||
} from "../components";
|
||||
|
||||
const EnergyTABS = (siteId) => [
|
||||
{
|
||||
id: "realtime",
|
||||
icon: <FaFileMedicalAlt />,
|
||||
title: "即時資訊",
|
||||
content: <IframeComponent src={`/ord?file:^Solar/${siteId}_Realtime.px|view:?fullScreen=true`} />,
|
||||
},
|
||||
{
|
||||
id: "basicInfo",
|
||||
icon: <FaInfoCircle />,
|
||||
title: "基本資料",
|
||||
content: <IframeComponent src={`/ord?file:^Solar/${siteId}_Info.px|view:?fullScreen=true`} />,
|
||||
},
|
||||
{
|
||||
id: "monitor",
|
||||
icon: <FaRegListAlt />,
|
||||
title: "逆變器監控",
|
||||
content: <IframeComponent src={`/ord?file:^Solar/${siteId}_Inverter.px|view:?fullScreen=true`} />,
|
||||
},
|
||||
{
|
||||
id: "analysis",
|
||||
icon: <FaChartLine />,
|
||||
title: "逆變器分析",
|
||||
content: <InverterAnalysis />,
|
||||
},
|
||||
{
|
||||
id: "history",
|
||||
icon: <FaHistory />,
|
||||
title: "歷史資料",
|
||||
content: <HistoryData />,
|
||||
},
|
||||
{
|
||||
id: "abnormal",
|
||||
icon: <FaExclamationTriangle />,
|
||||
title: "異常紀錄",
|
||||
content: <div className="text-white p-5 text-md">異常紀錄</div>,
|
||||
},
|
||||
];
|
||||
|
||||
export default EnergyTABS;
|
17
Frontend/ibms_solar/src/context/SiteContext.jsx
Normal file
17
Frontend/ibms_solar/src/context/SiteContext.jsx
Normal file
@ -0,0 +1,17 @@
|
||||
import React, { createContext, useState, useContext } from 'react';
|
||||
|
||||
const SiteContext = createContext();
|
||||
|
||||
export const SiteProvider = ({ children }) => {
|
||||
const [siteId, setSiteId] = useState(null);
|
||||
|
||||
return (
|
||||
<SiteContext.Provider value={{ siteId, setSiteId }}>
|
||||
{children}
|
||||
</SiteContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export const useSite = () => {
|
||||
return useContext(SiteContext);
|
||||
};
|
3
Frontend/ibms_solar/src/context/index.js
Normal file
3
Frontend/ibms_solar/src/context/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { SiteProvider, useSite } from './SiteContext.jsx';
|
||||
|
||||
export { SiteProvider, useSite } ;
|
8
Frontend/ibms_solar/src/index.css
Normal file
8
Frontend/ibms_solar/src/index.css
Normal file
@ -0,0 +1,8 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
|
||||
* {
|
||||
font-family: 'Inter', sans-serif;
|
||||
}
|
||||
|
45
Frontend/ibms_solar/src/main.jsx
Normal file
45
Frontend/ibms_solar/src/main.jsx
Normal file
@ -0,0 +1,45 @@
|
||||
import { StrictMode } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import App from "@/App.jsx";
|
||||
import axios from "axios";
|
||||
import "@/index.css";
|
||||
import { getCookie } from "@/utils";
|
||||
|
||||
// 添加請求攔截器
|
||||
axios.interceptors.request.use(
|
||||
function (config) {
|
||||
// 在發送請求之前做點什麼
|
||||
const token = getCookie("JWT-Authorization");
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
}
|
||||
return config;
|
||||
},
|
||||
function (error) {
|
||||
// 對請求錯誤做點什麼
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 添加響應攔截器
|
||||
axios.interceptors.response.use(
|
||||
function (response) {
|
||||
// 任何 2xx 范圍內的狀態碼會觸發該函數
|
||||
const { status, data } = response;
|
||||
return {
|
||||
status,
|
||||
data,
|
||||
};
|
||||
},
|
||||
function (error) {
|
||||
// 任何超出 2xx 范圍的狀態碼都會觸發此函數
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
// 創建應用並掛載到 DOM
|
||||
createRoot(document.getElementById("root")).render(
|
||||
<StrictMode>
|
||||
<App />
|
||||
</StrictMode>
|
||||
);
|
242
Frontend/ibms_solar/src/mock/historyData.json
Normal file
242
Frontend/ibms_solar/src/mock/historyData.json
Normal file
@ -0,0 +1,242 @@
|
||||
[
|
||||
{
|
||||
"timestamp": "02 AM",
|
||||
"kwh": 0,
|
||||
"solarhour": 0,
|
||||
"irradiance": 0,
|
||||
"pr": 0,
|
||||
"avgPR": 0,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 0,
|
||||
"irrDay": 0,
|
||||
"irrDayHour": 0,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "03 AM",
|
||||
"kwh": 0,
|
||||
"solarhour": 0,
|
||||
"irradiance": 0,
|
||||
"pr": 0,
|
||||
"avgPR": 0,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 0,
|
||||
"irrDay": 0,
|
||||
"irrDayHour": 0,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "04 AM",
|
||||
"kwh": 0,
|
||||
"solarhour": 0,
|
||||
"irradiance": 0,
|
||||
"pr": 0,
|
||||
"avgPR": 0,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 0,
|
||||
"irrDay": 0,
|
||||
"irrDayHour": 0,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "05 AM",
|
||||
"kwh": 0,
|
||||
"solarhour": 0,
|
||||
"irradiance": 27.86,
|
||||
"pr": 0,
|
||||
"avgPR": 0,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 0,
|
||||
"irrDay": 0,
|
||||
"irrDayHour": 0,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "06 AM",
|
||||
"kwh": 3,
|
||||
"solarhour": 1.9166666269302368,
|
||||
"irradiance": 91.07,
|
||||
"pr": 54.90191650390625,
|
||||
"avgPR": 54.90191650390625,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 0.06410256773233414,
|
||||
"irrDay": 116.26,
|
||||
"irrDayHour": 116.26,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "07 AM",
|
||||
"kwh": 10,
|
||||
"solarhour": 2.9166667461395264,
|
||||
"irradiance": 268.35,
|
||||
"pr": 72.0918197631836,
|
||||
"avgPR": 72.0918197631836,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 0.2777777910232544,
|
||||
"irrDay": 384.3,
|
||||
"irrDayHour": 268.04,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "08 AM",
|
||||
"kwh": 18,
|
||||
"solarhour": 3.9166667461395264,
|
||||
"irradiance": 469.84,
|
||||
"pr": 77.43690490722656,
|
||||
"avgPR": 77.43690490722656,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 0.6623931527137756,
|
||||
"irrDay": 853.82,
|
||||
"irrDayHour": 469.52,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "09 AM",
|
||||
"kwh": 26,
|
||||
"solarhour": 4.916666507720947,
|
||||
"irradiance": 645.26,
|
||||
"pr": 81.175537109375,
|
||||
"avgPR": 81.175537109375,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 1.2179486751556396,
|
||||
"irrDay": 1499.2,
|
||||
"irrDayHour": 645.38,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "10 AM",
|
||||
"kwh": 27,
|
||||
"solarhour": 5.916666507720947,
|
||||
"irradiance": 710.99,
|
||||
"pr": 81.14289093017578,
|
||||
"avgPR": 81.14289093017578,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 1.7948718070983887,
|
||||
"irrDay": 2210.18,
|
||||
"irrDayHour": 710.98,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "11 AM",
|
||||
"kwh": 21,
|
||||
"solarhour": 6.916666507720947,
|
||||
"irradiance": 593.44,
|
||||
"pr": 79.9531478881836,
|
||||
"avgPR": 79.9531478881836,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 2.2435896396636963,
|
||||
"irrDay": 2806.13,
|
||||
"irrDayHour": 595.95,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "12 PM",
|
||||
"kwh": 30,
|
||||
"solarhour": 7.916666507720947,
|
||||
"irradiance": 866.35,
|
||||
"pr": 78.54755401611328,
|
||||
"avgPR": 78.54755401611328,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 2.884615421295166,
|
||||
"irrDay": 3670.85,
|
||||
"irrDayHour": 864.72,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "13 PM",
|
||||
"kwh": 26,
|
||||
"solarhour": 8.916666984558105,
|
||||
"irradiance": 738.43,
|
||||
"pr": 77.9903793334961,
|
||||
"avgPR": 77.9903793334961,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 3.440171003341675,
|
||||
"irrDay": 4408.71,
|
||||
"irrDayHour": 737.86,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "14 PM",
|
||||
"kwh": 24,
|
||||
"solarhour": 9.916666984558105,
|
||||
"irradiance": 736.83,
|
||||
"pr": 76.79364776611328,
|
||||
"avgPR": 76.79364776611328,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 3.952991485595703,
|
||||
"irrDay": 5145.73,
|
||||
"irrDayHour": 737.02,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "15 PM",
|
||||
"kwh": 20,
|
||||
"solarhour": 10.916666984558105,
|
||||
"irradiance": 545.29,
|
||||
"pr": 76.94789123535156,
|
||||
"avgPR": 76.94789123535156,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 4.38034200668335,
|
||||
"irrDay": 5691.39,
|
||||
"irrDayHour": 545.66,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
},
|
||||
{
|
||||
"timestamp": "16 PM",
|
||||
"kwh": 10,
|
||||
"solarhour": 11.916666984558105,
|
||||
"irradiance": 0,
|
||||
"pr": 76.3684310913086,
|
||||
"avgPR": 76.3684310913086,
|
||||
"temp": 0,
|
||||
"diffSOLARHOUR": 0,
|
||||
"totaltime": "2024-09-04 ",
|
||||
"kwhkwp": 4.594017028808594,
|
||||
"irrDay": 0,
|
||||
"irrDayHour": 0,
|
||||
"generatingCapacity": 0,
|
||||
"days": 0
|
||||
}
|
||||
]
|
1165
Frontend/ibms_solar/src/mock/inverterData.json
Normal file
1165
Frontend/ibms_solar/src/mock/inverterData.json
Normal file
File diff suppressed because it is too large
Load Diff
7
Frontend/ibms_solar/src/mock/monthData.json
Normal file
7
Frontend/ibms_solar/src/mock/monthData.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"labels": ["2024-09-01", "2024-09-02", "2024-09-03"],
|
||||
"datasets": {
|
||||
"031010004010001": [179, 307, 154],
|
||||
"031010004010002": [95, 152, 78]
|
||||
}
|
||||
}
|
7
Frontend/ibms_solar/src/mock/quarterData.json
Normal file
7
Frontend/ibms_solar/src/mock/quarterData.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"labels": ["2024-01", "2024-02", "2024-03"],
|
||||
"datasets": {
|
||||
"031010004010001": [2461, 2404, 2955],
|
||||
"031010004010002": [1248, 1227, 1504]
|
||||
}
|
||||
}
|
11
Frontend/ibms_solar/src/mock/todayData.json
Normal file
11
Frontend/ibms_solar/src/mock/todayData.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"labels": [
|
||||
"02 AM", "03 AM", "04 AM", "05 AM", "06 AM",
|
||||
"07 AM", "08 AM", "09 AM", "10 AM", "11 AM"
|
||||
],
|
||||
"datasets": {
|
||||
"031010004010001": [0, 0, 0, 0, 2, 7, 12, 17, 18, 14],
|
||||
"031010004010002": [0, 0, 0, 0, 1, 3, 6, 9, 9, 7]
|
||||
}
|
||||
}
|
||||
|
11
Frontend/ibms_solar/src/mock/yearData.json
Normal file
11
Frontend/ibms_solar/src/mock/yearData.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"labels": [
|
||||
"2024-01", "2024-02", "2024-03", "2024-04",
|
||||
"2024-05", "2024-06", "2024-07", "2024-08", "2024-09"
|
||||
],
|
||||
"datasets": {
|
||||
"031010004010001": [2461, 2404, 2955, 2925, 3733, 3887, 5109, 5402, 640],
|
||||
"031010004010002": [1248, 1227, 1504, 1487, 1896, 1983, 2595, 2742, 325]
|
||||
}
|
||||
}
|
||||
|
68
Frontend/ibms_solar/src/page/Energy.jsx
Normal file
68
Frontend/ibms_solar/src/page/Energy.jsx
Normal file
@ -0,0 +1,68 @@
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSite } from "@/context";
|
||||
import { EnergyTABS } from "@/constant";
|
||||
import { FaCheckCircle, FaCloudSunRain } from "react-icons/fa";
|
||||
|
||||
const Energy = () => {
|
||||
const { siteId, setSiteId } = useSite();
|
||||
const navigate = useNavigate();
|
||||
const [activeTab, setActiveTab] = useState("realtime");
|
||||
const tabs = EnergyTABS(siteId);
|
||||
|
||||
const goback = () => {
|
||||
setSiteId(null);
|
||||
navigate("/");
|
||||
};
|
||||
|
||||
return (
|
||||
<section className="">
|
||||
<div className="flex items-center justify-between px-10 mb-4">
|
||||
<div className="flex items-center">
|
||||
<button
|
||||
className="text-white bg-emerald-500 shadow-md shadow-emerald-500/50 rounded px-5 py-2 me-2"
|
||||
onClick={goback}
|
||||
>
|
||||
上一頁
|
||||
</button>
|
||||
<FaCheckCircle className="text-teal-400 text-xl mx-2" />
|
||||
<span className="text-white text-xl ">{siteId}</span>
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<FaCloudSunRain className="text-white text-4xl me-2"/>
|
||||
<span className="text-white text-md">
|
||||
0°C
|
||||
<br />
|
||||
降雨機率: 30%
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-6 mt-6">
|
||||
<div className="border-b border-[#ffffff26] ">
|
||||
<ul className="flex flex-wrap text-sm font-medium text-center text-gray-500 dark:text-gray-400">
|
||||
{tabs.map((tab) => (
|
||||
<li className="me-2" key={tab.id}>
|
||||
<button
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`inline-flex items-center justify-center px-4 py-2 ${
|
||||
activeTab === tab.id
|
||||
? "text-gray-200 border-[#ffffff26] border border-b-0"
|
||||
: "border-transparent hover:text-gray-200 hover:border-[#ffffff26]"
|
||||
} rounded-t-sm`}
|
||||
>
|
||||
{tab.icon}
|
||||
<span className="ms-2">{tab.title}</span>
|
||||
</button>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
<div className="border border-[#ffffff26] min-h-[100vh]">
|
||||
{tabs.find((tab) => tab.id === activeTab)?.content}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Energy;
|
46
Frontend/ibms_solar/src/page/Home.jsx
Normal file
46
Frontend/ibms_solar/src/page/Home.jsx
Normal file
@ -0,0 +1,46 @@
|
||||
import React, { useRef } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSite } from "@/context";
|
||||
import { IframeComponent } from "@/components";
|
||||
|
||||
const Home = () => {
|
||||
const iframeRef = useRef(null);
|
||||
const { setSiteId } = useSite();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// 處理加載後的邏輯
|
||||
const handleIframeLoad = () => {
|
||||
const iframe = iframeRef.current;
|
||||
const currentUrl = iframe.contentWindow.location.href;
|
||||
|
||||
if (currentUrl.includes("solarEnergyItem")) {
|
||||
const urlParams = new URLSearchParams(currentUrl.split("?")[1]);
|
||||
const id = urlParams.get("siteId");
|
||||
if (id) {
|
||||
setSiteId(id);
|
||||
navigate("/energy");
|
||||
} else {
|
||||
console.log("找不到 siteId 参数");
|
||||
}
|
||||
} else {
|
||||
console.log("沒有solarEnergyItem");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<section>
|
||||
<div>
|
||||
<h1 className="font-light text-[#a5abb1] text-[26px] ms-12 mb-4">
|
||||
太陽能管理
|
||||
</h1>
|
||||
</div>
|
||||
<IframeComponent
|
||||
ref={iframeRef}
|
||||
src="/ord?file:^Solar/Site.px|view:?fullScreen=true"
|
||||
onLoad={handleIframeLoad}
|
||||
/>
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
7
Frontend/ibms_solar/src/page/index.js
Normal file
7
Frontend/ibms_solar/src/page/index.js
Normal file
@ -0,0 +1,7 @@
|
||||
import Home from './Home';
|
||||
import Energy from './Energy';
|
||||
|
||||
export {
|
||||
Home,
|
||||
Energy,
|
||||
};
|
8
Frontend/ibms_solar/src/utils/cookieHelpers.js
Normal file
8
Frontend/ibms_solar/src/utils/cookieHelpers.js
Normal file
@ -0,0 +1,8 @@
|
||||
export function getCookie(cookieName) {
|
||||
let cookie = {};
|
||||
document.cookie.split(";").forEach(function (el) {
|
||||
let [key, value] = el.split("=");
|
||||
cookie[key.trim()] = value;
|
||||
});
|
||||
return cookie[cookieName];
|
||||
}
|
3
Frontend/ibms_solar/src/utils/index.js
Normal file
3
Frontend/ibms_solar/src/utils/index.js
Normal file
@ -0,0 +1,3 @@
|
||||
import { getCookie } from "./cookieHelpers";
|
||||
|
||||
export { getCookie };
|
20
Frontend/ibms_solar/tailwind.config.js
Normal file
20
Frontend/ibms_solar/tailwind.config.js
Normal file
@ -0,0 +1,20 @@
|
||||
/** @type {import('tailwindcss').Config} */
|
||||
export default {
|
||||
content: ['./src/**/*.{js,jsx}'],
|
||||
theme: {
|
||||
extend: {
|
||||
screens: {
|
||||
xs: '480px',
|
||||
},
|
||||
fontFamily: {
|
||||
inter: ['Inter var', 'sans-serif'],
|
||||
},
|
||||
boxShadow: {
|
||||
card: '0 0 1px 0 rgba(189,192,207,0.06),0 10px 16px -1px rgba(189,192,207,0.2)',
|
||||
cardhover: '0 0 1px 0 rgba(189,192,207,0.06),0 10px 16px -1px rgba(189,192,207,0.4)',
|
||||
},
|
||||
},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
19
Frontend/ibms_solar/vite.config.js
Normal file
19
Frontend/ibms_solar/vite.config.js
Normal file
@ -0,0 +1,19 @@
|
||||
import { fileURLToPath, URL } from "node:url";
|
||||
import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
base: process.env.NODE_ENV === "production" ? "./" : "/",
|
||||
build: {
|
||||
outDir: "../solar_dist",
|
||||
emptyOutDir: true,
|
||||
},
|
||||
plugins: [react()],
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": fileURLToPath(new URL("./src", import.meta.url)),
|
||||
"@ASSET": fileURLToPath(new URL("./src/assets", import.meta.url)),
|
||||
},
|
||||
},
|
||||
})
|
@ -2058,7 +2058,7 @@ License: You must have a valid license purchased only from wrapbootstrap.com (li
|
||||
function (res) {
|
||||
if (!res || res.code != "0000" || !res.data) {
|
||||
} else {
|
||||
let strHtml = `<div class="btn-group mx-4">
|
||||
let strHtml = `<div class="btn-group ml-5">
|
||||
<a href="javascript:;" id="homeBtn" data-toggle="dropdown" data-page="dashboard" data-tabname="topFunBtn" class="text-center">
|
||||
<i class="fal fa-home fa-2x"></i><br>首頁
|
||||
</a>
|
||||
@ -2071,7 +2071,7 @@ License: You must have a valid license purchased only from wrapbootstrap.com (li
|
||||
|
||||
$.each(res.data, function (i, v) {
|
||||
if (v.authCode == "PF1") {
|
||||
strHtml += `<div class="btn-group mx-4" >
|
||||
strHtml += `<div class="btn-group ml-5" >
|
||||
<a href="javascript:;" id="sysMonTopBtn" class="text-center dropdown-toggle" data-toggle="dropdown" data-tabname="topFunBtn" class="text-center">
|
||||
<i class="fal fa-tv fa-2x"></i><br>${v.subName}
|
||||
</a>
|
||||
@ -2100,7 +2100,7 @@ License: You must have a valid license purchased only from wrapbootstrap.com (li
|
||||
: v.authCode == "PF8"
|
||||
? "fa-user"
|
||||
: "";
|
||||
strHtml += `<div class="btn-group mx-4">
|
||||
strHtml += `<div class="btn-group ml-5">
|
||||
<a href="javascript:;" name="topFunBtn" data-tabname="topFunBtn" class="dropdown-toggle no-arrow text-center"
|
||||
data-page="${v.showView}">
|
||||
<i class="fal ${icon} fa-2x"></i><br>${v.subName}
|
||||
@ -2108,7 +2108,11 @@ License: You must have a valid license purchased only from wrapbootstrap.com (li
|
||||
</div>`;
|
||||
}
|
||||
});
|
||||
|
||||
strHtml += `<div class="btn-group ml-5">
|
||||
<a href="./solar_dist/index.html" name="topFunBtn" data-tabname="topFunBtn" class="dropdown-toggle no-arrow text-center" >
|
||||
<i class="fal fa-solar-panel fa-2x"></i><br>太陽能管理
|
||||
</a>
|
||||
</div>`
|
||||
$("#froLisPage").html(strHtml);
|
||||
|
||||
// $("#homeBtn").YTNavbar("init");
|
||||
|
Loading…
Reference in New Issue
Block a user