first commit
This commit is contained in:
105
Web/imasFrontend/src/App.vue
Normal file
105
Web/imasFrontend/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
Web/imasFrontend/src/assets/logo.png
Normal file
BIN
Web/imasFrontend/src/assets/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.7 KiB |
BIN
Web/imasFrontend/src/assets/triangles-1430105_1280.png
Normal file
BIN
Web/imasFrontend/src/assets/triangles-1430105_1280.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 595 KiB |
59
Web/imasFrontend/src/components/APIView.vue
Normal file
59
Web/imasFrontend/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
Web/imasFrontend/src/components/ButtonArea.vue
Normal file
37
Web/imasFrontend/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
Web/imasFrontend/src/components/DebugView.vue
Normal file
198
Web/imasFrontend/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
Web/imasFrontend/src/components/HomeView.vue
Normal file
20
Web/imasFrontend/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
Web/imasFrontend/src/components/RoomCard.vue
Normal file
68
Web/imasFrontend/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
Web/imasFrontend/src/components/SettingsView.vue
Normal file
155
Web/imasFrontend/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
Web/imasFrontend/src/components/ThresholdPicker.vue
Normal file
85
Web/imasFrontend/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
Web/imasFrontend/src/components/TimePicker.vue
Normal file
72
Web/imasFrontend/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
Web/imasFrontend/src/components/TitleBlock.vue
Normal file
24
Web/imasFrontend/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
Web/imasFrontend/src/components/WeatherCard.vue
Normal file
152
Web/imasFrontend/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
Web/imasFrontend/src/main.js
Normal file
28
Web/imasFrontend/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
Web/imasFrontend/src/router/index.js
Normal file
52
Web/imasFrontend/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
Web/imasFrontend/src/services/weatherService.js
Normal file
35
Web/imasFrontend/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);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user