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

8
Web/imasBackend/.idea/.gitignore generated vendored Normal file
View File

@ -0,0 +1,8 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

12
Web/imasBackend/.idea/dataSources.xml generated Normal file
View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="@localhost" uuid="76688b31-a8ea-4f6d-a3ba-125f73939125">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

9
Web/imasBackend/.idea/imas-backend.iml generated Normal file
View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="jquery" level="application" />
</component>
</module>

View File

@ -0,0 +1,6 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{jquery}" />
</component>
</project>

8
Web/imasBackend/.idea/modules.xml generated Normal file
View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/imas-backend.iml" filepath="$PROJECT_DIR$/.idea/imas-backend.iml" />
</modules>
</component>
</project>

20
Web/imasBackend/.idea/php.xml generated Normal file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="MessDetectorOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCSFixerOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PHPCodeSnifferOptionsConfiguration">
<option name="highlightLevel" value="WARNING" />
<option name="transferred" value="true" />
</component>
<component name="PhpProjectSharedConfiguration" php_language_level="7.2" />
<component name="PhpStanOptionsConfiguration">
<option name="transferred" value="true" />
</component>
<component name="PsalmOptionsConfiguration">
<option name="transferred" value="true" />
</component>
</project>

4
Web/imasBackend/.idea/vcs.xml generated Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings" defaultProject="true" />
</project>

View File

@ -0,0 +1,21 @@
<?php
include_once("../db_config.php");
date_default_timezone_set('Asia/Shanghai');
header("Content-Type: application/json"); // 返回 JSON 格式的响应
header("Access-Control-Allow-Origin: *"); // 允许跨域请求
header("Access-Control-Allow-Methods: POST, OPTIONS"); // 允许的方法
header("Access-Control-Allow-Headers: Content-Type"); // 允许的请求头
$queryString = "SELECT * FROM status LIMIT 1";
$result = mysqli_query($conn, $queryString);
$row = mysqli_fetch_assoc($result);
$standby = $row['standby'];
$reboot = $row['reboot'];
$data = array(
"standby" => $standby,
"reboot" => $reboot
);
echo json_encode($data);

View File

@ -0,0 +1,8 @@
<?php
$servername = "localhost";
$username = "root";
$password = "";
$dbname = "IMAS";
// Create connection
$conn = mysqli_connect($servername, $username, $password, $dbname);

62
Web/imasBackend/debug.php Normal file
View File

@ -0,0 +1,62 @@
<?php
include_once 'db_config.php';
//header('Content-Type: application/json');
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: GET, POST, OPTIONS, PUT, DELETE");
header("Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
if($_SERVER['REQUEST_METHOD'] == 'POST'){
// 获取 POST 数据
$postData = file_get_contents('php://input');
echo $postData;
// 解析 JSON 数据
$data = json_decode($postData, true);
// 检查是否成功解析
if ($data === null && json_last_error() !== JSON_ERROR_NONE) {
echo json_encode(['error' => 'JSON 解析错误: ' . json_last_error_msg()]);
exit;
}
// 处理数据
$standby = isset($data['standby']) ? $data['standby'] : 0;
$reboot = isset($data['reboot']) ? $data['reboot'] : 0;
$humidifier = isset($data['humidifier']) ? $data['humidifier'] : 0;
$screen = isset($data['screen']) ? $data['screen'] : 1;
// 处理逻辑示例
$response = [
'message' => '数据接收成功',
'standby' => $standby,
'reboot' => $reboot,
'humidifier' => $humidifier,
'screen' => $screen,
];
// 返回 JSON 响应
$queryString = "UPDATE `status` SET `standby`='$standby', `reboot`='$reboot', `humidifier`='$humidifier', `screen`='$screen'";
mysqli_query($conn, $queryString);
$queryString = "select * FROM status";
$result = mysqli_query($conn, $queryString);
if(mysqli_num_rows($result) > 0){
$data = []; // 初始化为空数组
while ($row = mysqli_fetch_object($result)) { // 获取查询结果作为对象
$data[] = $row; // 将对象追加到数组中
}
echo substr(json_encode($data),1,58) ; // 输出 JSON 格式的响应
}
}else{
$queryString = "select * FROM status";
$result = mysqli_query($conn, $queryString);
if(mysqli_num_rows($result) > 0){
$data = []; // 初始化为空数组
while ($row = mysqli_fetch_object($result)) { // 获取查询结果作为对象
$data[] = $row; // 将对象追加到数组中
}
echo substr(json_encode($data),1,58) ; // 输出 JSON 格式的响应
}
}

View File

@ -0,0 +1,33 @@
<?php
include_once "db_config.php"; // 数据库配置文件
date_default_timezone_set('Asia/Shanghai');
header("Content-Type: application/json"); // 返回 JSON 格式的响应
header("Access-Control-Allow-Origin: *"); // 允许跨域请求
header("Access-Control-Allow-Methods: POST, OPTIONS"); // 允许的方法
header("Access-Control-Allow-Headers: Content-Type"); // 允许的请求头
// 这里假设连接已经建立,并且您有 $link 变量代表数据库连接
// 定义SQL查询获取最近的10条记录
$query = "SELECT * FROM localdata ORDER BY time DESC LIMIT 1";
// 执行查询
$result = mysqli_query($conn, $query);
// 检查查询结果
if (!$result) {
die("查询失败: " . mysqli_error($conn)); // 处理错误
}
// 处理查询结果
$data = [];
while ($row = mysqli_fetch_assoc($result)) {
$data[] = $row; // 将每一行数据存入数组
}
// 返回 JSON 格式的数据
header("Content-Type: application/json");
echo json_encode($data);
// 关闭数据库连接
mysqli_close($conn);

View File

@ -0,0 +1,22 @@
<?php
include_once("db_config.php");
date_default_timezone_set('Asia/Shanghai');
header("Content-Type: application/json"); // 返回 JSON 格式的响应
header("Access-Control-Allow-Origin: *"); // 允许跨域请求
header("Access-Control-Allow-Methods: POST, OPTIONS"); // 允许的方法
header("Access-Control-Allow-Headers: Content-Type"); // 允许的请求头
$queryString = "SELECT * FROM control_list";
$result = mysqli_query($conn, $queryString);
if(mysqli_error($conn)){
echo json_encode(["code" => 1, "msg" => mysqli_error($conn)]);
exit();
}
else{
$data = array();
while($row = mysqli_fetch_assoc($result)){
$data[] = $row;
}
echo json_encode($data);
}

View File

@ -0,0 +1,52 @@
<?php
include_once "db_config.php";
date_default_timezone_set('Asia/Shanghai');
header("Content-Type: application/json"); // 返回 JSON 格式的响应
header("Access-Control-Allow-Origin: *"); // 允许跨域请求
header("Access-Control-Allow-Methods: POST, OPTIONS"); // 允许的方法
header("Access-Control-Allow-Headers: Content-Type"); // 允许的请求头
// 处理 OPTIONS 请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit();
}
// 获取原始 POST 数据
$input = json_decode(file_get_contents("php://input"), true); // 将 JSON 转换为 PHP 数组
// 检查是否成功解析 JSON
if (json_last_error() !== JSON_ERROR_NONE) {
echo json_encode(["error" => "无效的 JSON 数据"]);
exit();
}
$local_temperature = $input["Temperature"] ?? "";
$local_humidity = $input["Humidity"] ?? "";
//$standby = $input["status"]["standby"] ?? 0;
//$reboot = $input["status"]["reboot"] ?? 0;
//$humidifier = $input["status"]["humidifier"] ?? 0;
//$screen = $input["status"]["screen"] ?? 1;
$time = time();
$date = date("Y-m-d H:i:s", $time);
$queryString = "INSERT INTO localdata (time, temperature, humidity) VALUES ('$date', '$local_temperature', '$local_humidity')";
mysqli_query($conn, $queryString);
if(mysqli_error($conn)){
echo json_encode(["error" => "数据库错误"]);
}
$queryString = "select * FROM status";
$result = mysqli_query($conn, $queryString);
if(mysqli_num_rows($result) > 0){
$data = []; // 初始化为空数组
while ($row = mysqli_fetch_object($result)) { // 获取查询结果作为对象
$data[] = $row; // 将对象追加到数组中
echo substr(json_encode($data),1, 58); // 返回 JSON 格式的响应
}
}

View File

@ -0,0 +1,54 @@
<?php
include_once "db_config.php";
date_default_timezone_set('Asia/Shanghai');
header("Access-Control-Allow-Origin: *");
header("Access-Control-Allow-Methods: POST, GET, OPTIONS");
header("Access-Control-Allow-Headers: Content-Type");
$input = json_decode(file_get_contents('php://input'), true);
if (!isset($input["boot"]) || !isset($input["shutdown"]) || !isset($input["start"]) ||
!isset($input["end"]) || !isset($input["min_humidity"]) || !isset($input["max_humidity"]) ||
!isset($input["min_temperature"]) || !isset($input["max_temperature"]) ||
!isset($input["enable_boot"]) || !isset($input["enable_humidify"]) ||
!isset($input["enable_temp_threshold"]) || !isset($input["enable_humi_threshold"])) {
echo json_encode(["error" => "无效的请求数据"]);
exit();
}
// 获取输入数据
$boot = $input["boot"];
$shutdown = $input["shutdown"];
$start = $input["start"];
$end = $input["end"];
$min_humidity = $input["min_humidity"];
$max_humidity = $input["max_humidity"];
$min_temperature = $input["min_temperature"];
$max_temperature = $input["max_temperature"];
$enable_boot = $input["enable_boot"];
$enable_humidify = $input["enable_humidify"];
$enable_temp_threshold = $input["enable_temp_threshold"];
$enable_humi_threshold = $input["enable_humi_threshold"];
// 更新数据库
$queryString = "UPDATE control_list SET
boot='$boot',
shutdown='$shutdown',
start='$start',
end='$end',
min_humidity='$min_humidity',
max_humidity='$max_humidity',
min_temperature='$min_temperature',
max_temperature='$max_temperature',
enable_boot='$enable_boot',
enable_humidify='$enable_humidify',
enable_temp_threshold='$enable_temp_threshold',
enable_humi_threshold='$enable_humi_threshold'";
if (mysqli_query($conn, $queryString)) {
echo json_encode(["success" => "设置更新成功"]);
} else {
echo json_encode(["error" => "设置更新失败: " . mysqli_error($conn)]);
}

View File

@ -0,0 +1,41 @@
<?php
include_once "db_config.php";
date_default_timezone_set('Asia/Shanghai');
header("Content-Type: application/json"); // 返回 JSON 格式的响应
header("Access-Control-Allow-Origin: *"); // 允许跨域请求
header("Access-Control-Allow-Methods: POST, OPTIONS"); // 允许的方法
header("Access-Control-Allow-Headers: Content-Type"); // 允许的请求头
// 处理 OPTIONS 请求
if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
http_response_code(204);
exit();
}
// 获取原始 POST 数据
$input = json_decode(file_get_contents("php://input"), true); // 将 JSON 转换为 PHP 数组
// 检查是否成功解析 JSON
if (json_last_error() !== JSON_ERROR_NONE) {
echo json_encode(["error" => "无效的 JSON 数据"]);
exit();
}
$standby = $input["standby"];
$reboot = $input["reboot"];
$humidifier = $input["humidifier"];
$screen = $input["screen"];
$queryString = "UPDATE status SET standby='$standby', reboot='$reboot', humidifier='$humidifier', screen='$screen'";
mysqli_query($conn, $queryString);
if(mysqli_error($conn)){
echo json_encode(["error" => "状态更新失败"]);
}
else{
echo json_encode(["success" => "状态更新成功"]);
}

18
Web/imasBackend/test.php Normal file
View File

@ -0,0 +1,18 @@
<?php
header('Content-Type: text/html; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST');
header('Access-Control-Allow-Headers: Content-Type');
header('Access-Control-Allow-Headers: Authorization');
header('Access-Control-Allow-Headers: X-Requested-With');
header("Content-Type: application/json"); // 返回 JSON 格式的响应
include_once "db_config.php";
$queryString = "select * FROM status";
$result = mysqli_query($conn, $queryString);
if(mysqli_num_rows($result) > 0){
$data = []; // 初始化为空数组
while ($row = mysqli_fetch_object($result)) { // 获取查询结果作为对象
$data[] = $row; // 将对象追加到数组中
}
echo substr(json_encode($data),1,58) ; // 输出 JSON 格式的响应
}

View File

@ -0,0 +1,7 @@
<?php
header("Access-Control-Allow-Origin: *"); // 允许所有域名访问
header("Access-Control-Allow-Methods: GET, POST, OPTIONS"); // 允许的请求方法
header("Access-Control-Allow-Headers: Content-Type"); // 允许的请求头
header("Content-type:application/json");
$weather = json_decode(file_get_contents('https://restapi.amap.com/v3/weather/weatherInfo?key=12085a54026b8e80ed3f69ec9c328e3e&city=510115&extensions=base&output=JSON'));
echo json_encode($weather);

View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/cli-plugin-babel/preset'
]
}

View File

@ -0,0 +1,19 @@
{
"compilerOptions": {
"target": "es5",
"module": "esnext",
"baseUrl": "./",
"moduleResolution": "node",
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
}
}

12412
Web/imasFrontend/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View 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"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

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

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

View 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']
})
]
}
})