first commit

This commit is contained in:
2025-01-10 15:20:33 +08:00
commit 4f5d2aa650
66 changed files with 15921 additions and 0 deletions

View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 KiB

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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>

View 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');

View 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

View 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);
}
}