init
This commit is contained in:
23
.gitignore
vendored
Normal file
23
.gitignore
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/dist
|
||||||
|
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env.local
|
||||||
|
.env.*.local
|
||||||
|
|
||||||
|
# Log files
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
9
LICENSE
Normal file
9
LICENSE
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 monjack
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
24
README.md
Normal file
24
README.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# imas
|
||||||
|
|
||||||
|
## Project setup
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and hot-reloads for development
|
||||||
|
```
|
||||||
|
npm run serve
|
||||||
|
```
|
||||||
|
|
||||||
|
### Compiles and minifies for production
|
||||||
|
```
|
||||||
|
npm run build
|
||||||
|
```
|
||||||
|
|
||||||
|
### Lints and fixes files
|
||||||
|
```
|
||||||
|
npm run lint
|
||||||
|
```
|
||||||
|
|
||||||
|
### Customize configuration
|
||||||
|
See [Configuration Reference](https://cli.vuejs.org/config/).
|
5
babel.config.js
Normal file
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
'@vue/cli-plugin-babel/preset'
|
||||||
|
]
|
||||||
|
}
|
19
jsconfig.json
Normal file
19
jsconfig.json
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es5",
|
||||||
|
"module": "esnext",
|
||||||
|
"baseUrl": "./",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"src/*"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"lib": [
|
||||||
|
"esnext",
|
||||||
|
"dom",
|
||||||
|
"dom.iterable",
|
||||||
|
"scripthost"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
12412
package-lock.json
generated
Normal file
12412
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
58
package.json
Normal file
58
package.json
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{
|
||||||
|
"name": "imas",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"serve": "vue-cli-service serve",
|
||||||
|
"build": "vue-cli-service build",
|
||||||
|
"lint": "vue-cli-service lint"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.7.9",
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
|
"bootstrap-icons": "^1.11.3",
|
||||||
|
"bootstrap-timepicker": "^0.5.2",
|
||||||
|
"bootstrap-vue": "^2.23.1",
|
||||||
|
"core-js": "^3.8.3",
|
||||||
|
"flatpickr": "^4.6.13",
|
||||||
|
"lodash": "^4.17.21",
|
||||||
|
"pinia": "^2.3.0",
|
||||||
|
"toastify-js": "^1.12.0",
|
||||||
|
"vue": "^3.2.13",
|
||||||
|
"vue-json-viewer": "^3.0.4",
|
||||||
|
"vue-router": "^4.5.0",
|
||||||
|
"vue3-timepicker": "^1.0.0-beta.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.12.16",
|
||||||
|
"@babel/eslint-parser": "^7.12.16",
|
||||||
|
"@popperjs/core": "^2.11.8",
|
||||||
|
"@vue/cli-plugin-babel": "~5.0.0",
|
||||||
|
"@vue/cli-plugin-eslint": "~5.0.0",
|
||||||
|
"@vue/cli-service": "~5.0.0",
|
||||||
|
"bootstrap": "^5.3.3",
|
||||||
|
"eslint": "^7.32.0",
|
||||||
|
"eslint-plugin-vue": "^8.0.3",
|
||||||
|
"jquery": "^3.7.1"
|
||||||
|
},
|
||||||
|
"eslintConfig": {
|
||||||
|
"root": true,
|
||||||
|
"env": {
|
||||||
|
"node": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"eslint:recommended"
|
||||||
|
],
|
||||||
|
"parserOptions": {
|
||||||
|
"parser": "@babel/eslint-parser"
|
||||||
|
},
|
||||||
|
"rules": {}
|
||||||
|
},
|
||||||
|
"browserslist": [
|
||||||
|
"> 1%",
|
||||||
|
"last 2 versions",
|
||||||
|
"not dead",
|
||||||
|
"not ie 11"
|
||||||
|
]
|
||||||
|
}
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
17
public/index.html
Normal file
17
public/index.html
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||||
|
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||||
|
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
|
||||||
|
<title><%= htmlWebpackPlugin.options.title %></title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<noscript>
|
||||||
|
<strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.</strong>
|
||||||
|
</noscript>
|
||||||
|
<div id="app"></div>
|
||||||
|
<!-- built files will be auto injected -->
|
||||||
|
</body>
|
||||||
|
</html>
|
105
src/App.vue
Normal file
105
src/App.vue
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<template>
|
||||||
|
<div class="Main-title h1 mt-0 ms-2 mb-3 ps-3 border-start border-5 border-dark-subtle" @click="clickTitle" >控制中心</div>
|
||||||
|
<router-view></router-view>
|
||||||
|
<footer class="mt-5 text-center border-top border-1">Copyright © 2024-2025 <a href="https://blog.monjack.cn" class="btn-link" style="text-decoration: none">王仁杰</a></footer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'App',
|
||||||
|
components: {
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
clickTitle() {
|
||||||
|
this.$router.push('/HomeView');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@100..900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=ZCOOL+KuaiLe&display=swap');
|
||||||
|
#app {
|
||||||
|
font-family: Avenir, Helvetica, Arial, sans-serif;
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.roboto-thin {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-light {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-regular {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-medium {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-bold {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-black {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-thin-italic {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 100;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-light-italic {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 300;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-regular-italic {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 400;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-medium-italic {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 500;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-bold-italic {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.roboto-black-italic {
|
||||||
|
font-family: "Roboto", serif;
|
||||||
|
font-weight: 900;
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
.Main-title:hover {
|
||||||
|
color: #007bff;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
BIN
src/assets/logo.png
Normal file
BIN
src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
BIN
src/assets/triangles-1430105_1280.png
Normal file
BIN
src/assets/triangles-1430105_1280.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 595 KiB |
59
src/components/APIView.vue
Normal file
59
src/components/APIView.vue
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
<script>
|
||||||
|
import {getWeather} from '@/services/weatherService'
|
||||||
|
import TitleBlock from "@/components/TitleBlock.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "APIView",
|
||||||
|
components: {
|
||||||
|
TitleBlock
|
||||||
|
},
|
||||||
|
data(){
|
||||||
|
return(
|
||||||
|
{
|
||||||
|
res: '',
|
||||||
|
weather: '',
|
||||||
|
temperature: '',
|
||||||
|
winddirection: '',
|
||||||
|
windpower: '',
|
||||||
|
reporttime: ''
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
const weatherData = await getWeather();
|
||||||
|
this.res = weatherData;
|
||||||
|
this.weather = weatherData.weather;
|
||||||
|
this.winddirection = weatherData.winddirection;
|
||||||
|
this.windpower = weatherData.windpower;
|
||||||
|
this.temperature = weatherData.temperature;
|
||||||
|
this.reporttime = weatherData.reporttime;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取天气数据失败:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TitleBlock :title="'查看API数据'" />
|
||||||
|
<div class="ms-4 p-2 border rounded-3">
|
||||||
|
<div>天气 : {{ weather }}</div>
|
||||||
|
<div>温度 : {{temperature}}</div>
|
||||||
|
<div>风向 : {{winddirection}}</div>
|
||||||
|
<div>风力 : {{windpower}}</div>
|
||||||
|
<div>更新时间 : {{reporttime}}</div>
|
||||||
|
</div>
|
||||||
|
<div class="ms-4 mt-3 p-2 border rounded-3">
|
||||||
|
<json-viewer
|
||||||
|
:value="res"
|
||||||
|
:expand-depth=5
|
||||||
|
copyable
|
||||||
|
boxed
|
||||||
|
sort></json-viewer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
37
src/components/ButtonArea.vue
Normal file
37
src/components/ButtonArea.vue
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
<script>
|
||||||
|
import Toastify from 'toastify-js';
|
||||||
|
export default {
|
||||||
|
|
||||||
|
name: "ButtonArea",
|
||||||
|
methods: {
|
||||||
|
showToast() {
|
||||||
|
Toastify({
|
||||||
|
text: "请以管理员身份登录",
|
||||||
|
duration: 3000,
|
||||||
|
close: true
|
||||||
|
}).showToast();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mt-4 text-center">
|
||||||
|
<router-link to="/APIView">
|
||||||
|
<button class="btn btn-secondary w-25 shadow-sm m-2 fs-4"><a>查看API</a></button>
|
||||||
|
</router-link>
|
||||||
|
<router-link to="/SettingsView">
|
||||||
|
<button class="btn btn-secondary w-25 shadow-sm m-2 fs-4"><a>设置</a></button>
|
||||||
|
</router-link>
|
||||||
|
<router-link to="/DebugView">
|
||||||
|
<button class="btn btn-secondary w-25 shadow-sm m-2 fs-4">调试</button>
|
||||||
|
</router-link>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
template{
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
198
src/components/DebugView.vue
Normal file
198
src/components/DebugView.vue
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
<script>
|
||||||
|
import TitleBlock from "@/components/TitleBlock.vue";
|
||||||
|
import $ from "jquery";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "DebugView",
|
||||||
|
components: {TitleBlock},
|
||||||
|
created() {
|
||||||
|
this.getStatusData();
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
Label_button1: "待机",
|
||||||
|
Label_button2: "重启",
|
||||||
|
Label_button3: "启动加湿",
|
||||||
|
Label_button4: "关闭屏幕",
|
||||||
|
button1: false,
|
||||||
|
button2: false,
|
||||||
|
button3: false,
|
||||||
|
button4: false,
|
||||||
|
data: {
|
||||||
|
standby: 0,
|
||||||
|
reboot: 0,
|
||||||
|
humidifier: 0,
|
||||||
|
screen: 1,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
button1_click() {
|
||||||
|
console.log("待机");
|
||||||
|
this.button1 = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.button1 = false;
|
||||||
|
}, 5000);
|
||||||
|
if (this.Label_button1 === "待机") {
|
||||||
|
this.Label_button1 = "工作";
|
||||||
|
this.button2 = true;
|
||||||
|
this.button3 = true;
|
||||||
|
this.button4 = true;
|
||||||
|
this.data.standby = 1;
|
||||||
|
} else {
|
||||||
|
this.Label_button1 = "待机";
|
||||||
|
this.button2 = false;
|
||||||
|
this.button3 = false;
|
||||||
|
this.button4 = false;
|
||||||
|
this.data.standby = 0;
|
||||||
|
}
|
||||||
|
this.updateData();
|
||||||
|
},
|
||||||
|
button2_click() {
|
||||||
|
console.log("重启");
|
||||||
|
this.button1 = true;
|
||||||
|
this.button2 = true;
|
||||||
|
this.button3 = true;
|
||||||
|
this.button4 = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.button1 = false;
|
||||||
|
this.button2 = false;
|
||||||
|
this.button3 = false;
|
||||||
|
this.button4 = false;
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
|
|
||||||
|
this.data.reboot = 1;
|
||||||
|
this.updateData();
|
||||||
|
this.data.reboot = 0;
|
||||||
|
},
|
||||||
|
button3_click() {
|
||||||
|
console.log("启动加湿");
|
||||||
|
this.button3 = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.button3 = false;
|
||||||
|
}, 1000);
|
||||||
|
if (this.Label_button3 === "启动加湿") {
|
||||||
|
this.Label_button3 = "停止加湿";
|
||||||
|
this.data.humidifier = 1;
|
||||||
|
} else {
|
||||||
|
this.Label_button3 = "启动加湿";
|
||||||
|
this.data.humidifier = 0;
|
||||||
|
}
|
||||||
|
this.updateData();
|
||||||
|
},
|
||||||
|
button4_click() {
|
||||||
|
console.log("关闭屏幕");
|
||||||
|
this.button4 = true;
|
||||||
|
setTimeout(() => {
|
||||||
|
this.button4 = false;
|
||||||
|
}, 1000);
|
||||||
|
if (this.Label_button4 === "关闭屏幕") {
|
||||||
|
this.Label_button4 = "打开屏幕";
|
||||||
|
this.data.screen = 0;
|
||||||
|
} else {
|
||||||
|
this.Label_button4 = "关闭屏幕";
|
||||||
|
this.data.screen = 1;
|
||||||
|
}
|
||||||
|
this.updateData();
|
||||||
|
},
|
||||||
|
updateData() {
|
||||||
|
$.ajax({
|
||||||
|
url: "http://localhost/debug.php",
|
||||||
|
type: "POST",
|
||||||
|
contentType: "application/json",
|
||||||
|
data: JSON.stringify(this.data),
|
||||||
|
success: (response) => {
|
||||||
|
console.log(response);
|
||||||
|
},
|
||||||
|
error: (xhr, status, error) => {
|
||||||
|
console.log(error);
|
||||||
|
console.log(xhr.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
getStatusData(){
|
||||||
|
$.ajax({
|
||||||
|
url: "http://localhost/debug.php",
|
||||||
|
type: "GET",
|
||||||
|
contentType: "application/json",
|
||||||
|
success: (response) => {
|
||||||
|
console.log(response);
|
||||||
|
this.data.standby = parseInt(JSON.parse(response).standby);
|
||||||
|
this.data.reboot = parseInt(JSON.parse(response).reboot);
|
||||||
|
this.data.humidifier = parseInt(JSON.parse(response).humidifier);
|
||||||
|
this.data.screen = parseInt(JSON.parse(response).screen);
|
||||||
|
if (this.data.standby) {
|
||||||
|
this.Label_button1 = "工作";
|
||||||
|
} else {
|
||||||
|
this.Label_button1 = "待机";
|
||||||
|
}
|
||||||
|
if (this.data.reboot) {
|
||||||
|
this.Label_button2 = "重启中";
|
||||||
|
} else {
|
||||||
|
this.Label_button2 = "重启";
|
||||||
|
}
|
||||||
|
if (this.data.humidifier) {
|
||||||
|
this.Label_button3 = "停止加湿";
|
||||||
|
} else {
|
||||||
|
this.Label_button3 = "启动加湿";
|
||||||
|
}
|
||||||
|
if (this.data.screen) {
|
||||||
|
this.Label_button4 = "关闭屏幕";
|
||||||
|
} else {
|
||||||
|
this.Label_button4 = "打开屏幕";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (xhr, status, error) => {
|
||||||
|
console.log(error);
|
||||||
|
console.log(xhr.responseText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TitleBlock class="mb-4" title="调试" /><br>
|
||||||
|
|
||||||
|
<div class="text-center me-1 pe-5 ps-5">
|
||||||
|
<table class="table table-sm w-25">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>功能</th>
|
||||||
|
<th>状态</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody class="table-group-divider">
|
||||||
|
<tr>
|
||||||
|
<td>在线状态</td>
|
||||||
|
<td>{{ data.standby? "离线" : data.reboot? "重启中": "在线" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>启动加湿</td>
|
||||||
|
<td>{{ data.humidifier? "启动中" : "已停止" }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td>关闭屏幕</td>
|
||||||
|
<td>{{ data.screen? "打开" : "关闭" }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ms-5 mt-4 btn-group">
|
||||||
|
<button class="btn btn-outline-secondary" v-bind:disabled="button1" @click="button1_click" hidden>{{ Label_button1 }}</button>
|
||||||
|
<button class="btn btn-outline-secondary" v-bind:disabled="button2" @click="button2_click">{{ Label_button2 }}</button>
|
||||||
|
<button class="btn btn-outline-secondary" v-bind:disabled="button3" @click="button3_click">{{ Label_button3 }}</button>
|
||||||
|
<button class="btn btn-outline-secondary" v-bind:disabled="button4" @click="button4_click">{{ Label_button4 }}</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
</style>
|
20
src/components/HomeView.vue
Normal file
20
src/components/HomeView.vue
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<script>
|
||||||
|
import ButtonArea from "@/components/ButtonArea.vue";
|
||||||
|
import WeatherCard from "@/components/WeatherCard.vue";
|
||||||
|
import RoomCard from "@/components/RoomCard.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "HomeView",
|
||||||
|
components: {RoomCard, WeatherCard, ButtonArea}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<WeatherCard style=""></WeatherCard>
|
||||||
|
<RoomCard></RoomCard>
|
||||||
|
<ButtonArea></ButtonArea>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
68
src/components/RoomCard.vue
Normal file
68
src/components/RoomCard.vue
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
<script>
|
||||||
|
import {getLocalData} from "@/services/weatherService";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "RoomCard",
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
RoomTemp: 20,
|
||||||
|
RoomHumidity: 40,
|
||||||
|
LastUpdateTime: "未获取",
|
||||||
|
RoomNumber: "101",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
TempMap(temp) {
|
||||||
|
if (temp < 18) {
|
||||||
|
return "冷";
|
||||||
|
} else if (temp < 25) {
|
||||||
|
return "适中";
|
||||||
|
} else if (temp >34) {
|
||||||
|
return "热";
|
||||||
|
} else{
|
||||||
|
return "暖";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
HumidityMap(humidity) {
|
||||||
|
if (humidity < 30) {
|
||||||
|
return "较干燥";
|
||||||
|
} else if (humidity < 60) {
|
||||||
|
return "正常";
|
||||||
|
} else {
|
||||||
|
return "较湿";
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
async created() {
|
||||||
|
const localData = await getLocalData();
|
||||||
|
console.log(localData);
|
||||||
|
this.RoomTemp = localData[0].temperature;
|
||||||
|
console.log(this.RoomTemp);
|
||||||
|
this.RoomHumidity = localData[0].humidity;
|
||||||
|
console.log(this.RoomHumidity);
|
||||||
|
this.LastUpdateTime = localData[0].time;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="border border-0 border-dark-subtle m-3 p-4 rounded-4 shadow">
|
||||||
|
<div class="h1 ms-2 mb-2 fw-semibold roboto-bold">房间</div>
|
||||||
|
<div class="fs-3 ms-3 text-black-75">
|
||||||
|
<div class="fs-2 mt-3 mb-2 border-bottom border-2">
|
||||||
|
<span class=""></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<i class="bi bi-thermometer-half"></i>
|
||||||
|
{{ TempMap(RoomTemp) }} {{ RoomTemp }}℃
|
||||||
|
<div class="mt-4 ms-1 fs-4">
|
||||||
|
<i class="bi bi-moisture"></i> <span class="ms-1 me-4">{{HumidityMap(RoomHumidity)}}</span><span>{{ RoomHumidity }}%</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-end fs-6 fw-light mt-3"><span class="me-4">更新时间</span><span>{{ LastUpdateTime }}</span></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
155
src/components/SettingsView.vue
Normal file
155
src/components/SettingsView.vue
Normal file
@ -0,0 +1,155 @@
|
|||||||
|
<script>
|
||||||
|
import TitleBlock from "@/components/TitleBlock.vue";
|
||||||
|
import TimePicker from "@/components/TimePicker.vue";
|
||||||
|
import ThresholdPicker from "@/components/ThresholdPicker.vue";
|
||||||
|
|
||||||
|
import axios from "axios";
|
||||||
|
export default {
|
||||||
|
name: "SettingsView",
|
||||||
|
components: {TimePicker, TitleBlock, ThresholdPicker},
|
||||||
|
created() {
|
||||||
|
this.getSettings();
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
timeSettings:{
|
||||||
|
times_Power: {
|
||||||
|
times:{
|
||||||
|
PowerOn: "12:00",
|
||||||
|
PowerOff: "12:30"
|
||||||
|
},
|
||||||
|
enable_boot: false,
|
||||||
|
},
|
||||||
|
times_Humidity: {
|
||||||
|
times:{
|
||||||
|
HumidityOn: "12:00",
|
||||||
|
HumidityOff: "12:30",
|
||||||
|
},
|
||||||
|
enable_boot: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
thresholdSettings:{
|
||||||
|
Threshold_Humidity:{
|
||||||
|
thresholds: {
|
||||||
|
min: 20,
|
||||||
|
max: 80,
|
||||||
|
},
|
||||||
|
enable_boot: false,
|
||||||
|
},
|
||||||
|
Threshold_Temperature:{
|
||||||
|
thresholds: {
|
||||||
|
min: 20,
|
||||||
|
max: 26,
|
||||||
|
},
|
||||||
|
enable_boot: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
loaded: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
save() {
|
||||||
|
this.postSettings();
|
||||||
|
},
|
||||||
|
cancel() {
|
||||||
|
window.location.reload();
|
||||||
|
},
|
||||||
|
getSettings() {
|
||||||
|
axios.get("http://locahost/getSetting.php").then(response => {
|
||||||
|
console.log(response.data);
|
||||||
|
this.timeSettings.times_Power = {
|
||||||
|
times: {
|
||||||
|
PowerOn: response.data[0].boot,
|
||||||
|
PowerOff: response.data[0].shutdown,
|
||||||
|
},
|
||||||
|
enable_boot: response.data[0].enable_boot == 1,
|
||||||
|
}
|
||||||
|
console.log(this.timeSettings.times_Power);
|
||||||
|
this.timeSettings.times_Humidity = {
|
||||||
|
times: {
|
||||||
|
HumidityOn: response.data[0].start,
|
||||||
|
HumidityOff: response.data[0].end,
|
||||||
|
},
|
||||||
|
enable_boot: response.data[0].enable_humidify == 1,
|
||||||
|
}
|
||||||
|
this.thresholdSettings.Threshold_Humidity = {
|
||||||
|
thresholds: {
|
||||||
|
min: response.data[0].min_humidity,
|
||||||
|
max: response.data[0].max_humidity,
|
||||||
|
},
|
||||||
|
enable_boot: response.data[0].enable_humi_threshold == 1,
|
||||||
|
}
|
||||||
|
this.thresholdSettings.Threshold_Temperature = {
|
||||||
|
thresholds: {
|
||||||
|
min: response.data[0].min_temperature,
|
||||||
|
max: response.data[0].max_temperature,
|
||||||
|
},
|
||||||
|
enable_boot: response.data[0].enable_temp_threshold == 1,
|
||||||
|
}
|
||||||
|
this.loaded = true;
|
||||||
|
}).catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
postSettings() {
|
||||||
|
axios.post("http://locahost/postSetting.php", {
|
||||||
|
boot: this.timeSettings.times_Power.times.PowerOn,
|
||||||
|
shutdown: this.timeSettings.times_Power.times.PowerOff,
|
||||||
|
start: this.timeSettings.times_Humidity.times.HumidityOn,
|
||||||
|
end: this.timeSettings.times_Humidity.times.HumidityOff,
|
||||||
|
min_humidity: this.thresholdSettings.Threshold_Humidity.thresholds.min,
|
||||||
|
max_humidity: this.thresholdSettings.Threshold_Humidity.thresholds.max,
|
||||||
|
min_temperature: this.thresholdSettings.Threshold_Temperature.thresholds.min,
|
||||||
|
max_temperature: this.thresholdSettings.Threshold_Temperature.thresholds.max,
|
||||||
|
enable_boot: this.timeSettings.times_Power.enable_boot? 1 : 0,
|
||||||
|
enable_humidify: this.timeSettings.times_Humidity.enable_boot? 1 : 0,
|
||||||
|
enable_humi_threshold: this.thresholdSettings.Threshold_Humidity.enable_boot? 1 : 0,
|
||||||
|
enable_temp_threshold: this.thresholdSettings.Threshold_Temperature.enable_boot? 1 : 0,
|
||||||
|
}).then(response => {
|
||||||
|
console.log(response);
|
||||||
|
this.loaded = false;
|
||||||
|
}).catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
this.loaded = true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div v-if="loaded">
|
||||||
|
<TitleBlock title="设置" />
|
||||||
|
<span hidden>{{timeSettings.times_Power.times.PowerOn}}</span>
|
||||||
|
<span hidden>{{timeSettings.times_Power.times.PowerOff}}</span>
|
||||||
|
<span hidden>{{timeSettings.times_Humidity.times.HumidityOn}}</span>
|
||||||
|
<span hidden>{{timeSettings.times_Humidity.times.HumidityOff}}</span>
|
||||||
|
<span hidden>{{thresholdSettings.Threshold_Humidity.thresholds.min}}</span>
|
||||||
|
<span hidden>{{thresholdSettings.Threshold_Humidity.thresholds.max}}</span>
|
||||||
|
<span hidden>{{thresholdSettings.Threshold_Temperature.thresholds.min}}</span>
|
||||||
|
<span hidden>{{thresholdSettings.Threshold_Temperature.thresholds.max}}</span>
|
||||||
|
|
||||||
|
<div class="ms-5 mt-2 fs-5 ">
|
||||||
|
<TimePicker :pickers="timeSettings.times_Power.times" v-model="timeSettings.times_Power" :title="'定时开关机'" :enable="timeSettings.times_Power.enable_boot" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ms-5 mt-2 fs-5 ">
|
||||||
|
<TimePicker v-model="timeSettings.times_Humidity" :title="'定时加湿'" :enable="timeSettings.times_Humidity.enable_boot" :pickers=timeSettings.times_Humidity.times />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ms-5 mt-2 fs-5 ">
|
||||||
|
<ThresholdPicker v-model="thresholdSettings.Threshold_Temperature" :title="'温度阈值'" :enable="thresholdSettings.Threshold_Temperature.enable_boot" :Thresholds=thresholdSettings.Threshold_Temperature.thresholds />
|
||||||
|
</div>
|
||||||
|
<div class="ms-5 mt-2 fs-5 ">
|
||||||
|
<ThresholdPicker v-model="thresholdSettings.Threshold_Humidity" :title="'湿度阈值'" :enable="thresholdSettings.Threshold_Humidity.enable_boot" :Thresholds=thresholdSettings.Threshold_Humidity.thresholds />
|
||||||
|
</div>
|
||||||
|
<div class="mt-5">
|
||||||
|
<button class="btn btn-secondary ms-5 mt-2 fs-5" @click="save">保存</button>
|
||||||
|
<button class="btn btn-secondary ms-5 mt-2 fs-5" @click="cancel">重置</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
85
src/components/ThresholdPicker.vue
Normal file
85
src/components/ThresholdPicker.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "ThresholdPicker",
|
||||||
|
components: {},
|
||||||
|
mounted() {
|
||||||
|
this.LocalThresholds = this.Thresholds
|
||||||
|
this.showThresholdPicker = this.enable
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateThresholds() {
|
||||||
|
this.$emit("update:Thresholds", this.LocalThresholds)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showThresholdPicker: false,
|
||||||
|
LocalThresholds: {
|
||||||
|
min: "",
|
||||||
|
max: "",
|
||||||
|
},
|
||||||
|
unit: "%"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ""
|
||||||
|
},
|
||||||
|
Thresholds: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
min: "",
|
||||||
|
max: "",
|
||||||
|
})
|
||||||
|
},
|
||||||
|
enable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="d-flex align-items-center flex-wrap">
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="form-check-input"
|
||||||
|
role="switch"
|
||||||
|
id="showThresholdPicker"
|
||||||
|
v-model="showThresholdPicker"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label me-4" for="showThresholdPicker">{{ title }}</label>
|
||||||
|
</div>
|
||||||
|
<div v-if="showThresholdPicker">
|
||||||
|
<input
|
||||||
|
v-for="(threshold, index) in LocalThresholds"
|
||||||
|
:key="index"
|
||||||
|
:placeholder="index ==='min'? '输入最小阈值' : '输入最大阈值'"
|
||||||
|
@input="updateThresholds"
|
||||||
|
v-model="LocalThresholds[index]"
|
||||||
|
type="text"
|
||||||
|
class="picker-input fs-5" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.picker-input {
|
||||||
|
width: 200px;
|
||||||
|
margin-left: 9px;
|
||||||
|
padding: 10px 10px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.picker-input:focus {
|
||||||
|
border: 2px solid #101010;
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
72
src/components/TimePicker.vue
Normal file
72
src/components/TimePicker.vue
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
<template>
|
||||||
|
<div class="d-flex align-items-center flex-wrap ">
|
||||||
|
<!-- Checkbox to toggle timepicker visibility -->
|
||||||
|
<div class="form-check form-switch">
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
class="form-check-input"
|
||||||
|
role="switch"
|
||||||
|
id="showTimepicker"
|
||||||
|
v-model="showTimepicker"
|
||||||
|
/>
|
||||||
|
<label class="form-check-label me-4" for="showTimepicker">{{ title }}</label>
|
||||||
|
</div>
|
||||||
|
<div v-if="showTimepicker">
|
||||||
|
<VueTimepicker
|
||||||
|
v-for="(picker, index) in pickers"
|
||||||
|
:key="index"
|
||||||
|
:picker="picker"
|
||||||
|
class="mt-2 ms-2"
|
||||||
|
:placeholder="index"
|
||||||
|
v-model="time[index]"
|
||||||
|
@change="updateTime(index)"
|
||||||
|
>
|
||||||
|
<template v-slot:icon>
|
||||||
|
<i class="bi bi-clock"></i>
|
||||||
|
</template>
|
||||||
|
</VueTimepicker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import VueTimepicker from "vue3-timepicker";
|
||||||
|
export default {
|
||||||
|
name: "TimePicker",
|
||||||
|
props: {
|
||||||
|
title: String,
|
||||||
|
pickers: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
default: () => ({
|
||||||
|
time: "00:00",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
enable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
showTimepicker: false,
|
||||||
|
time: {}, // 本地时间数据
|
||||||
|
};
|
||||||
|
},
|
||||||
|
components: {
|
||||||
|
VueTimepicker,
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.showTimepicker = this.enable;
|
||||||
|
this.time = { ...this.pickers }; // 初始化 time 数据
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
updateTime() {
|
||||||
|
this.$emit("update:pickers", Object.assign({}, this.time, this.showTimepicker)); // 确保事件名称和父组件一致
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
24
src/components/TitleBlock.vue
Normal file
24
src/components/TitleBlock.vue
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "TitleBlock",
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="title ms-4 ps-1 pt-2 pe-1 mb-3 mt-1 border-bottom border-3 ">
|
||||||
|
<h2>{{ title }}</h2>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.title {
|
||||||
|
display: inline-block; /* 使宽度适应内容 */
|
||||||
|
user-select: none; /* 防止选中文本 */
|
||||||
|
}
|
||||||
|
</style>
|
152
src/components/WeatherCard.vue
Normal file
152
src/components/WeatherCard.vue
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
<script>
|
||||||
|
|
||||||
|
import {getWeather, getIPInfo} from "@/services/weatherService";
|
||||||
|
import TitleBlock from "@/components/TitleBlock.vue";
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: "WeatherCard",
|
||||||
|
components: {
|
||||||
|
TitleBlock
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
weather: '',
|
||||||
|
temperature: '',
|
||||||
|
winddirection: '',
|
||||||
|
windpower: '',
|
||||||
|
reporttime: '',
|
||||||
|
clothingIndex: '',
|
||||||
|
windspeedMap: {
|
||||||
|
'0': 0.5,
|
||||||
|
'1': 0.9,
|
||||||
|
'2': 2.5,
|
||||||
|
'3': 4.4,
|
||||||
|
'4': 6.7,
|
||||||
|
'5': 9.3,
|
||||||
|
'6': 12.5,
|
||||||
|
'7': 15.5,
|
||||||
|
'8': 19.0,
|
||||||
|
'9': 22.6,
|
||||||
|
'10': 26.45,
|
||||||
|
'11': 30.55,
|
||||||
|
'12': 32
|
||||||
|
},
|
||||||
|
CloudMap: {
|
||||||
|
'晴': 0,
|
||||||
|
'阴': 9,
|
||||||
|
'多云': 6,
|
||||||
|
'雨': 10,
|
||||||
|
},
|
||||||
|
ip: '',
|
||||||
|
address: '',
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
GetClothingIndex(temp, windSpeed) {
|
||||||
|
const tFelt = calculateWindChill(temp, windSpeed);
|
||||||
|
function calculateWindChill(temp, windSpeed) {
|
||||||
|
return (
|
||||||
|
13.12 +
|
||||||
|
0.6215 * temp -
|
||||||
|
11.37 * Math.pow(windSpeed, 0.16) +
|
||||||
|
0.3965 * temp * Math.pow(windSpeed, 0.16)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (tFelt > 26) {
|
||||||
|
return "短袖或薄长袖";
|
||||||
|
} else if (tFelt >= 20 && tFelt <= 26) {
|
||||||
|
return "薄外套、长袖";
|
||||||
|
} else if (tFelt >= 10 && tFelt < 20) {
|
||||||
|
return "厚外套";
|
||||||
|
} else if (tFelt >= 0 && tFelt < 10) {
|
||||||
|
return "棉衣或羽绒服";
|
||||||
|
} else {
|
||||||
|
return "加厚羽绒服,注意保暖";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// GetClothingIndex() {
|
||||||
|
// let Ic, Ts, Tv, Tr, Ta, H, Rat, Ia, V, Nt, Nl;
|
||||||
|
// Nl = 0;
|
||||||
|
// Ts = 33;
|
||||||
|
// V = this.windspeedMap[this.windpower.match(/\d+/g)];
|
||||||
|
// Nt = this.weather in this.CloudMap? this.CloudMap[this.weather] : 5;
|
||||||
|
// Ta = 20;
|
||||||
|
// Ia = 0.40 / Math.pow(V, 0.406);
|
||||||
|
// Tv = 0.0246 * Math.pow(Math.log10(7.23 * V), 3) - 0.4525 * Math.pow(Math.log10(7.23 * V), 2) + 3.2398 * Math.log10(7.23 * V);
|
||||||
|
// Tr = 0.42 * (1- 0.45 * (Nt + Nl)) * 0.5 * Ia;
|
||||||
|
// H = 0.93244 * 4.1841 * (0.104 * Ta*Ta - 5.1403 * Ta + 117.13);
|
||||||
|
// Ta >= 25?
|
||||||
|
// Rat = 0.0775 + 0.001 * Ta * (0.1 * Ta * (0.01 * Ta * (1.235 * Ta - 54.8752) + 10.1044) - 3.2813)
|
||||||
|
// : Rat = 0.24;
|
||||||
|
// Ic = (Ts - Ta - Tv + Tr)/(H * (1 - Rat) * 0.043) - Ia;
|
||||||
|
// console.log(Ic);
|
||||||
|
// return Ic;
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
async created() {
|
||||||
|
try {
|
||||||
|
const weatherData = await getWeather();
|
||||||
|
const ipInfo = await getIPInfo();
|
||||||
|
this.address = ipInfo.province + ipInfo.city;
|
||||||
|
this.weather = weatherData.weather;
|
||||||
|
this.winddirection = weatherData.winddirection;
|
||||||
|
this.windpower = weatherData.windpower;
|
||||||
|
this.temperature = weatherData.temperature;
|
||||||
|
this.reporttime = weatherData.reporttime;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取天气数据失败:', error);
|
||||||
|
}
|
||||||
|
this.clothingIndex = this.GetClothingIndex(this.temperature, this.windspeedMap[this.windpower.match(/\d+/g)]);
|
||||||
|
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
iconClass() {
|
||||||
|
switch (this.weather) {
|
||||||
|
case '晴':
|
||||||
|
return 'bi-sun';
|
||||||
|
case '阴':
|
||||||
|
return 'bi-cloud-drizzle';
|
||||||
|
case '多云':
|
||||||
|
return 'bi-cloud-fog';
|
||||||
|
case '雨':
|
||||||
|
return 'bi-cloud-rain';
|
||||||
|
case '雪':
|
||||||
|
return 'bi-cloud-snow';
|
||||||
|
default:
|
||||||
|
return 'bi-cloud';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<TitleBlock title="概览"></TitleBlock>
|
||||||
|
<div class="border border-0 border-dark-subtle m-3 p-4 rounded-4 shadow">
|
||||||
|
<div class="h1 ms-2 mb-2 fw-semibold roboto-bold">天气</div>
|
||||||
|
<div class="fs-3 ms-3 text-black-75">
|
||||||
|
<div class="fs-2 mt-3 mb-2 border-bottom border-2">
|
||||||
|
<span class=""></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="fs-4 mb-3 mt-3">{{ address }}</div>
|
||||||
|
<i :class="iconClass"></i>
|
||||||
|
{{ weather }} {{ temperature }} ℃
|
||||||
|
<div class="mt-2 fs-4">
|
||||||
|
<i class="bi bi-wind"></i> <span class="me-4">{{ winddirection }}</span><span>{{ windpower }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="mt-2 fs-4">
|
||||||
|
<span class="me-4">适宜衣物</span><span>{{ clothingIndex }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-end fs-6 fw-light mt-3">
|
||||||
|
点击查看未来天气
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="text-end fs-6 fw-light mt-1"><span class="me-4">更新时间</span><span>{{reporttime}}</span></div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
28
src/main.js
Normal file
28
src/main.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { createApp } from 'vue'
|
||||||
|
import { createPinia } from 'pinia'
|
||||||
|
|
||||||
|
import 'jquery/dist/jquery.min.js'
|
||||||
|
import 'bootstrap/dist/css/bootstrap.min.css'
|
||||||
|
import 'bootstrap/dist/js/bootstrap.min.js'
|
||||||
|
import 'bootstrap-icons/font/bootstrap-icons.css'
|
||||||
|
import "toastify-js/src/toastify.css"
|
||||||
|
import Toastify from 'toastify-js'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import 'vue3-timepicker/dist/VueTimepicker.css'
|
||||||
|
import VueTimepicker from 'vue3-timepicker'
|
||||||
|
|
||||||
|
import JsonViewer from 'vue-json-viewer'
|
||||||
|
import router from './router'
|
||||||
|
import App from './App.vue'
|
||||||
|
|
||||||
|
const pinia = createPinia()
|
||||||
|
const app = createApp(App);
|
||||||
|
app.use(router);
|
||||||
|
app.use(JsonViewer);
|
||||||
|
app.use(VueTimepicker);
|
||||||
|
app.use(Toastify);
|
||||||
|
app.use(pinia);
|
||||||
|
|
||||||
|
app.mount('#app');
|
52
src/router/index.js
Normal file
52
src/router/index.js
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {createRouter, createWebHistory} from "vue-router";
|
||||||
|
import WeatherCard from "@/components/WeatherCard.vue";
|
||||||
|
import RoomCard from "@/components/RoomCard.vue";
|
||||||
|
import HomeView from "@/components/HomeView.vue";
|
||||||
|
import DebugView from "@/components/DebugView.vue";
|
||||||
|
import APIView from "@/components/APIView.vue";
|
||||||
|
import SettingsView from "@/components/SettingsView.vue";
|
||||||
|
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
{
|
||||||
|
path: '/WeatherCard',
|
||||||
|
name: 'WeatherCard',
|
||||||
|
component: WeatherCard
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/RoomCard',
|
||||||
|
name: 'RoomCard',
|
||||||
|
component: RoomCard
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/DebugView',
|
||||||
|
name: 'DebugView',
|
||||||
|
component: DebugView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/HomeView',
|
||||||
|
name: 'HomeView',
|
||||||
|
component: HomeView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/HomeView',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/APIView',
|
||||||
|
name: 'APIView',
|
||||||
|
component: APIView
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/SettingsView',
|
||||||
|
name: 'SettingsView',
|
||||||
|
component: SettingsView
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHistory(),
|
||||||
|
routes
|
||||||
|
});
|
||||||
|
|
||||||
|
export default router
|
35
src/services/weatherService.js
Normal file
35
src/services/weatherService.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// weatherService.js
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
export async function getWeather() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get("http://monjack.cn/weatherInfo.php");
|
||||||
|
const weatherData = response.data.lives[0];
|
||||||
|
return weatherData;
|
||||||
|
// 将 API 数据更新到 Pinia Store
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch weather data:", error);
|
||||||
|
throw error; // 确保将错误抛出,以便调用者可以捕获
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getIPInfo() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get("https://restapi.amap.com/v3/ip?key=12085a54026b8e80ed3f69ec9c328e3e");
|
||||||
|
return response.data;
|
||||||
|
// 将 API 数据更新到 Pinia Store
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch IP info:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getLocalData() {
|
||||||
|
try {
|
||||||
|
const response = await axios.get("http://localhost/getLocalData.php");
|
||||||
|
return response.data;
|
||||||
|
// 将 API 数据更新到 Pinia Store
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to fetch local data:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
22
vue.config.js
Normal file
22
vue.config.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
const { defineConfig } = require('@vue/cli-service')
|
||||||
|
// module.exports = defineConfig({
|
||||||
|
// transpileDependencies: true
|
||||||
|
// })
|
||||||
|
// defineConfig 这里是vue3 的默认代码
|
||||||
|
const webpack = require("webpack")
|
||||||
|
|
||||||
|
module.exports = defineConfig({
|
||||||
|
// 配置插件参数
|
||||||
|
configureWebpack: {
|
||||||
|
plugins: [
|
||||||
|
// 配置 jQuery 插件的参数
|
||||||
|
new webpack.ProvidePlugin({
|
||||||
|
$: 'jquery',
|
||||||
|
jQuery: 'jquery',
|
||||||
|
'window.jQuery': 'jquery',
|
||||||
|
Popper: ['popper.js', 'default']
|
||||||
|
})
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
Reference in New Issue
Block a user