mirror of
https://git.beihong.wang/wangbeihong/iot-bedroom-environment-controller.git
synced 2026-04-23 11:43:04 +08:00
增加补充相关资料和小程序源码
This commit is contained in:
323
微信小程序源码/iot-home/pages/homeControl/README.md
Normal file
323
微信小程序源码/iot-home/pages/homeControl/README.md
Normal file
@@ -0,0 +1,323 @@
|
||||
# HomeControl 页面功能说明
|
||||
|
||||
## 概述
|
||||
|
||||
HomeControl 页面是用于控制 ESP32 设备的核心页面,支持 MQTT 消息通信、设备状态监控和控制指令发送等功能。
|
||||
|
||||
## 核心功能
|
||||
|
||||
### 1. MQTT 消息通信
|
||||
|
||||
- 自动连接 MQTT 服务器
|
||||
- 订阅设备主题,接收设备消息
|
||||
- 处理设备上报的传感器数据
|
||||
- 发送控制指令到设备
|
||||
|
||||
### 2. 设备数据管理
|
||||
|
||||
- 存储和解析 ESP32 设备数据
|
||||
- 实时更新设备状态
|
||||
- 提取特定设备信息(如温度、湿度、空气质量等)
|
||||
|
||||
### 3. 控制指令发送
|
||||
|
||||
支持发送控制指令到 ESP32 设备,并接收设备响应。
|
||||
|
||||
#### 基本用法
|
||||
|
||||
```javascript
|
||||
// 最简单的用法 - 只控制灯光
|
||||
this.sendControlCommand({
|
||||
light: "on",
|
||||
});
|
||||
|
||||
// 控制多个设备
|
||||
this.sendControlCommand({
|
||||
light: "on",
|
||||
fan: "off",
|
||||
curtain: "open",
|
||||
});
|
||||
```
|
||||
|
||||
#### 高级用法 - 带配置参数
|
||||
|
||||
```javascript
|
||||
// 控制灯光并设置亮度
|
||||
this.sendControlCommand({ light: "on" }, { brightness: 80 });
|
||||
```
|
||||
|
||||
#### 高级用法 - 带回调函数
|
||||
|
||||
```javascript
|
||||
// 发送控制并处理响应
|
||||
this.sendControlCommand({ light: "on" }, null, (response) => {
|
||||
console.log("控制结果:", response);
|
||||
if (response.success) {
|
||||
console.log("控制成功");
|
||||
} else {
|
||||
console.log("控制失败:", response.message);
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
#### 完整用法 - 所有参数
|
||||
|
||||
```javascript
|
||||
this.sendControlCommand(
|
||||
{ light: "on", fan: "off" }, // 控制参数
|
||||
{ brightness: 80 }, // 配置参数
|
||||
(response) => {
|
||||
// 回调函数
|
||||
console.log("响应:", response);
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
### 4. 请求-响应匹配机制
|
||||
|
||||
系统实现了完整的请求追踪和响应匹配功能:
|
||||
|
||||
#### 工作原理
|
||||
|
||||
1. 每个控制指令都会生成唯一的`request_id`
|
||||
2. 发送指令时自动保存请求信息到`pendingRequests`
|
||||
3. 接收到 ESP32 响应后,通过`request_id`匹配原始请求
|
||||
4. 根据响应状态自动显示提示信息
|
||||
5. 如果有回调函数,自动调用并传递响应数据
|
||||
6. 5 秒后自动清理已完成的请求
|
||||
|
||||
#### 请求格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "control_command",
|
||||
"device_id": "esp32_bedroom_001",
|
||||
"device_type": "bedroom_controller",
|
||||
"timestamp": 1768495346335,
|
||||
"message_type": "control_request",
|
||||
"request_id": "req_1768495346336_n3bhf90pu",
|
||||
"data": {
|
||||
"controls": {
|
||||
"light": "on"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应格式
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "control_response",
|
||||
"device_id": "esp32_bedroom_001",
|
||||
"device_type": "bedroom_controller",
|
||||
"timestamp": 1768495550428,
|
||||
"message_type": "control_result",
|
||||
"request_id": "req_1768495550428_d47w3fu8j",
|
||||
"data": {
|
||||
"result": {
|
||||
"status": "success",
|
||||
"message": "Control executed successfully"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 响应状态说明
|
||||
|
||||
- `success`: 控制指令执行成功
|
||||
- `error`: 控制指令执行出错
|
||||
- `failed`: 控制指令执行失败
|
||||
|
||||
## 数据结构
|
||||
|
||||
### 页面数据
|
||||
|
||||
```javascript
|
||||
data: {
|
||||
receivedMessages: [], // 存储接收到的消息
|
||||
esp32Device: null, // 存储ESP32设备数据
|
||||
deviceOnline: false, // 设备在线状态
|
||||
lastUpdateTime: null, // 最后更新时间
|
||||
pendingRequests: {} // 存储待处理的控制请求
|
||||
}
|
||||
```
|
||||
|
||||
### ESP32 设备数据结构
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: "device_message",
|
||||
deviceId: "esp32_bedroom_001",
|
||||
deviceType: "bedroom_controller",
|
||||
timestamp: 1768495346335,
|
||||
messageType: "telemetry_update",
|
||||
requestId: null,
|
||||
statusCode: null,
|
||||
statusMessage: null,
|
||||
state: {
|
||||
online: true,
|
||||
// 其他状态信息
|
||||
},
|
||||
telemetry: {
|
||||
temperature: 25.5,
|
||||
humidity: 60,
|
||||
air_quality: 85,
|
||||
// 其他遥测数据
|
||||
},
|
||||
controlResult: null,
|
||||
rawData: {...}
|
||||
}
|
||||
```
|
||||
|
||||
## API 说明
|
||||
|
||||
### sendControlCommand(controls, config, callback)
|
||||
|
||||
发送控制指令到 ESP32 设备
|
||||
|
||||
**参数:**
|
||||
|
||||
- `controls` (Object): 控制参数对象,例如 `{ light: "on" }`
|
||||
- `config` (Object, 可选): 配置参数对象,例如 `{ brightness: 80 }`
|
||||
- `callback` (Function, 可选): 响应回调函数
|
||||
|
||||
**返回值:**
|
||||
|
||||
- 成功: 返回 `request_id` (字符串)
|
||||
- 失败: 返回 `null`
|
||||
|
||||
**示例:**
|
||||
|
||||
```javascript
|
||||
const requestId = this.sendControlCommand({ light: "on" }, null, (response) => {
|
||||
if (response.success) {
|
||||
console.log("控制成功");
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
### handleControlResponse(response)
|
||||
|
||||
处理 ESP32 设备的控制响应
|
||||
|
||||
**参数:**
|
||||
|
||||
- `response` (Object): 响应消息对象
|
||||
|
||||
**功能:**
|
||||
|
||||
- 通过 request_id 匹配原始请求
|
||||
- 解析响应状态
|
||||
- 显示提示信息
|
||||
- 调用回调函数(如果有)
|
||||
- 自动清理已完成的请求
|
||||
|
||||
### parseESP32Data(message)
|
||||
|
||||
解析 ESP32 设备上传的数据
|
||||
|
||||
**参数:**
|
||||
|
||||
- `message` (Object|string): 接收到的消息内容
|
||||
|
||||
**返回值:**
|
||||
|
||||
- 成功: 返回解析后的数据对象
|
||||
- 失败: 返回 `null`
|
||||
|
||||
### extractESP32Info(path)
|
||||
|
||||
从解析后的 ESP32 数据中提取特定信息
|
||||
|
||||
**参数:**
|
||||
|
||||
- `path` (string): 数据路径,使用点号分隔,例如 "telemetry.air_quality"
|
||||
|
||||
**返回值:**
|
||||
|
||||
- 成功: 返回提取的值
|
||||
- 失败: 返回 `null`
|
||||
|
||||
**示例:**
|
||||
|
||||
```javascript
|
||||
// 提取空气质量
|
||||
const airQuality = this.extractESP32Info("telemetry.air_quality");
|
||||
|
||||
// 提取温度
|
||||
const temperature = this.extractESP32Info("telemetry.temperature");
|
||||
|
||||
// 获取所有数据
|
||||
const allData = this.extractESP32Info();
|
||||
```
|
||||
|
||||
## 注意事项
|
||||
|
||||
1. **回调函数是可选的**
|
||||
|
||||
- 不使用回调时,系统会自动显示 Toast 提示
|
||||
- 使用回调时,可以自定义处理响应逻辑
|
||||
|
||||
2. **请求自动清理**
|
||||
|
||||
- 已完成的请求会在 5 秒后自动清理
|
||||
- 发送失败的请求会立即移除
|
||||
|
||||
3. **错误处理**
|
||||
|
||||
- 所有函数都包含错误处理
|
||||
- 错误信息会输出到控制台
|
||||
- 关键错误会显示 Toast 提示
|
||||
|
||||
4. **MQTT 连接**
|
||||
- 使用前确保 MQTT 已连接
|
||||
- 未连接时会显示提示信息
|
||||
- 需要正确配置 MQTT 主题
|
||||
|
||||
## ESP32 端实现参考
|
||||
|
||||
### 接收控制指令
|
||||
|
||||
```cpp
|
||||
void handleControlCommand(String payload) {
|
||||
DynamicJsonDocument doc(1024);
|
||||
deserializeJson(doc, payload);
|
||||
|
||||
String requestId = doc["request_id"];
|
||||
String lightControl = doc["data"]["controls"]["light"];
|
||||
|
||||
// 执行控制操作
|
||||
if (lightControl == "on") {
|
||||
digitalWrite(LED_PIN, HIGH);
|
||||
} else if (lightControl == "off") {
|
||||
digitalWrite(LED_PIN, LOW);
|
||||
}
|
||||
|
||||
// 发送响应
|
||||
sendControlResponse(requestId, "success", "Control executed successfully");
|
||||
}
|
||||
```
|
||||
|
||||
### 发送控制响应
|
||||
|
||||
```cpp
|
||||
void sendControlResponse(String requestId, String status, String message) {
|
||||
DynamicJsonDocument doc(1024);
|
||||
|
||||
doc["type"] = "control_response";
|
||||
doc["device_id"] = deviceId;
|
||||
doc["device_type"] = "bedroom_controller";
|
||||
doc["timestamp"] = millis();
|
||||
doc["message_type"] = "control_result";
|
||||
doc["request_id"] = requestId;
|
||||
doc["data"]["result"]["status"] = status;
|
||||
doc["data"]["result"]["message"] = message;
|
||||
|
||||
String response;
|
||||
serializeJson(doc, response);
|
||||
client.publish(responseTopic, response.c_str());
|
||||
}
|
||||
```
|
||||
|
||||
## 更新日志
|
||||
1492
微信小程序源码/iot-home/pages/homeControl/homeContril.js
Normal file
1492
微信小程序源码/iot-home/pages/homeControl/homeContril.js
Normal file
File diff suppressed because it is too large
Load Diff
3
微信小程序源码/iot-home/pages/homeControl/homeContril.json
Normal file
3
微信小程序源码/iot-home/pages/homeControl/homeContril.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"usingComponents": {}
|
||||
}
|
||||
234
微信小程序源码/iot-home/pages/homeControl/homeContril.wxml
Normal file
234
微信小程序源码/iot-home/pages/homeControl/homeContril.wxml
Normal file
@@ -0,0 +1,234 @@
|
||||
<wxs module="utils">
|
||||
// utils.wxs
|
||||
var formatState = function(value) {
|
||||
if(value === true || value === 1 || value === "1" || value === "on" || value === "open") {
|
||||
return "开启";
|
||||
} else if(value === false || value === 0 || value === "0" || value === "off" || value === "close") {
|
||||
return "关闭";
|
||||
} else {
|
||||
return "--";
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
formatState: formatState
|
||||
}
|
||||
|
||||
</wxs>
|
||||
|
||||
<van-nav-bar title="控制页面" fixed placeholder safe-area-inset-top />
|
||||
<van-notice-bar left-icon="volume-o" text="必须确定已经添加了真实的设备。" />
|
||||
|
||||
<van-tabs active="{{ active }}" bind:change="onChange" swipeable animated>
|
||||
|
||||
<!-- 传感器数据 -->
|
||||
<van-tab title="传感器数据">
|
||||
<scroll-view scroll-y class="tab-scroll" style="height: 600px;">
|
||||
<van-cell-group inset title="环境数据">
|
||||
<van-cell title="温度" value="{{temperature != null ? temperature + '°C' : '--'}}" />
|
||||
<van-cell title="湿度" value="{{humidity != null ? humidity + '%' : '--'}}" />
|
||||
<van-cell title="光照强度" value="{{light_intensity != null ? light_intensity + 'lx' : '--'}}" />
|
||||
<van-cell title="空气质量" value="{{air_quality != null ? air_quality + 'Index':'--'}}" />
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group inset title="控制状态">
|
||||
<van-cell title="窗帘状态" value="{{utils.formatState(esp32Device && esp32Device.telemetry ? esp32Device.telemetry.curtain_state : null)}}" />
|
||||
<van-cell title="警报状态" value="{{utils.formatState(esp32Device && esp32Device.telemetry ? esp32Device.telemetry.buzzer_state : null)}}" />
|
||||
<van-cell title="风扇状态" value="{{utils.formatState(esp32Device && esp32Device.telemetry ? esp32Device.telemetry.fan_state : null)}}" />
|
||||
<van-cell title="灯光状态" value="{{utils.formatState(esp32Device && esp32Device.telemetry ? esp32Device.telemetry.led_state : null)}}" />
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group inset title="设备功率">
|
||||
<van-cell title="灯光功率" value="{{led_power != null ? led_power + '%' : '--'}}" />
|
||||
</van-cell-group>
|
||||
|
||||
<van-cell-group inset title="控制参数">
|
||||
<van-cell title="设备状态">
|
||||
<van-tag slot="right-icon" type="{{deviceOnline ? 'success' : 'danger'}}">{{deviceOnline ? '在线' : '离线'}}</van-tag>
|
||||
</van-cell>
|
||||
<van-cell title="最后更新" value="{{lastUpdateTime || '--'}}" />
|
||||
</van-cell-group>
|
||||
|
||||
<view style="height: env(safe-area-inset-bottom);" />
|
||||
</scroll-view>
|
||||
</van-tab>
|
||||
|
||||
<!-- 物理设备控制 -->
|
||||
<van-tab title="物理设备控制">
|
||||
<scroll-view scroll-y class="tab-scroll" style="height: 600px;">
|
||||
<van-grid direction="horizontal" column-num="2" >
|
||||
<van-grid-item use-slot >
|
||||
|
||||
<text>风扇开关</text>
|
||||
<van-switch checked="{{switch1}}" bind:change="onSwitch1Change" />
|
||||
|
||||
</van-grid-item>
|
||||
<van-grid-item use-slot>
|
||||
|
||||
<text>窗帘开关</text>
|
||||
<van-switch checked="{{switch2}}" bind:change="onSwitch2Change" />
|
||||
|
||||
</van-grid-item>
|
||||
<van-grid-item use-slot>
|
||||
|
||||
<text>警报开关</text>
|
||||
<van-switch checked="{{switch3}}" bind:change="onSwitch3Change" />
|
||||
|
||||
</van-grid-item>
|
||||
<van-grid-item use-slot>
|
||||
|
||||
<text>灯光开关</text>
|
||||
<van-switch checked="{{switch4}}" bind:change="onSwitch4Change" />
|
||||
|
||||
</van-grid-item>
|
||||
</van-grid>
|
||||
|
||||
<view class="slider-container">
|
||||
<van-tag plain type="primary">灯光功率控制</van-tag>
|
||||
<van-slider value="{{led_power_value}}" bind:change="led_powerChange">
|
||||
<view class="slider-btn">{{led_power_value}}%</view>
|
||||
</van-slider>
|
||||
</view>
|
||||
|
||||
<view style="height: env(safe-area-inset-bottom);" />
|
||||
</scroll-view>
|
||||
</van-tab>
|
||||
|
||||
<!-- 模式设置 -->
|
||||
<van-tab title="模式设置">
|
||||
<scroll-view scroll-y class="tab-scroll" style="height: 600px;">
|
||||
<!-- 闹钟设置 -->
|
||||
<van-cell-group inset title="闹钟设置">
|
||||
<!-- 闹钟1 -->
|
||||
<van-cell title="起床时间" icon="clock-o" use-slot>
|
||||
<view slot="right-icon" class="alarm-cell-right">
|
||||
<view class="alarm-time" bindtap="onAlarmTimeClick" data-alarm="1">
|
||||
<text class="time-text">{{alarm1.time}}</text>
|
||||
<van-icon name="arrow-down" size="14px" color="#969799" />
|
||||
</view>
|
||||
<van-switch checked="{{alarm1.enabled}}" bind:change="onAlarmEnableChange" data-alarm="1" size="24px" />
|
||||
</view>
|
||||
</van-cell>
|
||||
<!-- 闹钟2 -->
|
||||
<van-cell title="闹钟2" icon="clock-o" use-slot>
|
||||
<view slot="right-icon" class="alarm-cell-right">
|
||||
<view class="alarm-time" bindtap="onAlarmTimeClick" data-alarm="2">
|
||||
<text class="time-text">{{alarm2.time}}</text>
|
||||
<van-icon name="arrow-down" size="14px" color="#969799" />
|
||||
</view>
|
||||
<van-switch checked="{{alarm2.enabled}}" bind:change="onAlarmEnableChange" data-alarm="2" size="24px" />
|
||||
</view>
|
||||
</van-cell>
|
||||
<!-- 闹钟3 -->
|
||||
<van-cell title="闹钟3" icon="clock-o" use-slot>
|
||||
<view slot="right-icon" class="alarm-cell-right">
|
||||
<view class="alarm-time" bindtap="onAlarmTimeClick" data-alarm="3">
|
||||
<text class="time-text">{{alarm3.time}}</text>
|
||||
<van-icon name="arrow-down" size="14px" color="#969799" />
|
||||
</view>
|
||||
<van-switch checked="{{alarm3.enabled}}" bind:change="onAlarmEnableChange" data-alarm="3" size="24px" />
|
||||
</view>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<!-- 时间段设置 -->
|
||||
<van-cell-group inset title="时间段设置" custom-class="period-group">
|
||||
<!-- 白天时间 -->
|
||||
<van-cell title="白天时间" icon="sun-o" use-slot label="设置白天的起始和结束时间">
|
||||
<view slot="right-icon" class="period-cell-right">
|
||||
<view class="period-time-row">
|
||||
<view class="period-time-label">开始</view>
|
||||
<view class="period-time-value" bindtap="onPeriodTimeClick" data-period="day" data-type="start">
|
||||
<text>{{dayPeriod.start}}</text>
|
||||
<van-icon name="arrow-down" size="12px" color="#969799" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="period-time-row">
|
||||
<view class="period-time-label">结束</view>
|
||||
<view class="period-time-value" bindtap="onPeriodTimeClick" data-period="day" data-type="end">
|
||||
<text>{{dayPeriod.end}}</text>
|
||||
<van-icon name="arrow-down" size="12px" color="#969799" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</van-cell>
|
||||
<!-- 晚上时间 -->
|
||||
<van-cell title="晚上时间" icon="moon-o" use-slot label="设置夜晚的起始和结束时间">
|
||||
<view slot="right-icon" class="period-cell-right">
|
||||
<view class="period-time-row">
|
||||
<view class="period-time-label">开始</view>
|
||||
<view class="period-time-value" bindtap="onPeriodTimeClick" data-period="night" data-type="start">
|
||||
<text>{{nightPeriod.start}}</text>
|
||||
<van-icon name="arrow-down" size="12px" color="#969799" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="period-time-row">
|
||||
<view class="period-time-label">结束</view>
|
||||
<view class="period-time-value" bindtap="onPeriodTimeClick" data-period="night" data-type="end">
|
||||
<text>{{nightPeriod.end}}</text>
|
||||
<van-icon name="arrow-down" size="12px" color="#969799" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</van-cell>
|
||||
<!-- 下发按钮 -->
|
||||
<van-cell use-slot custom-class="period-action-cell">
|
||||
<van-button type="info" size="small" bind:click="sendPeriodControlCommand" icon="guide-o">
|
||||
下发时间段设置
|
||||
</van-button>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<!-- 温度阈值设置 -->
|
||||
<van-cell-group inset title="温度阈值设置" custom-class="threshold-group">
|
||||
<van-cell title="自动降温温度" icon="fire-o" use-slot label="当温度超过此值时自动开启降温模式">
|
||||
<view slot="right-icon" class="threshold-value">
|
||||
<text class="threshold-temp">{{temperatureThreshold}}°C</text>
|
||||
</view>
|
||||
</van-cell>
|
||||
<view class="slider-container">
|
||||
<van-slider
|
||||
value="{{temperatureThreshold}}"
|
||||
bind:change="onTemperatureThresholdChange"
|
||||
min="20"
|
||||
max="40"
|
||||
step="1"
|
||||
bar-height="4px"
|
||||
active-color="#ee0a24"
|
||||
/>
|
||||
<view class="slider-marks">
|
||||
<text>20°C</text>
|
||||
<text>30°C</text>
|
||||
<text>40°C</text>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 下发按钮 -->
|
||||
<van-cell use-slot custom-class="threshold-action-cell">
|
||||
<van-button type="danger" size="small" bind:click="sendTemperatureThresholdCommand" icon="warning-o">
|
||||
下发温度阈值设置
|
||||
</van-button>
|
||||
</van-cell>
|
||||
</van-cell-group>
|
||||
|
||||
<!-- 时间选择器 -->
|
||||
<van-popup show="{{showTimePicker}}" position="bottom" bind:close="onTimePickerClose" custom-style="background: #fff;">
|
||||
<view class="time-picker-header">
|
||||
<text class="time-picker-title">选择时间</text>
|
||||
<van-icon name="cross" size="20px" color="#969799" bind:click="onTimePickerClose" />
|
||||
</view>
|
||||
<picker-view class="time-picker-view" value="{{timePickerValue}}" bind:change="onTimePickerChange">
|
||||
<picker-view-column>
|
||||
<view wx:for="{{hours}}" wx:key="*this" class="picker-column-item">{{item}}时</view>
|
||||
</picker-view-column>
|
||||
<picker-view-column>
|
||||
<view wx:for="{{minutes}}" wx:key="*this" class="picker-column-item">{{item}}分</view>
|
||||
</picker-view-column>
|
||||
</picker-view>
|
||||
<view class="time-picker-btn" bind:tap="onTimePickerConfirm">确定</view>
|
||||
</van-popup>
|
||||
|
||||
<view style="height: env(safe-area-inset-bottom);" />
|
||||
</scroll-view>
|
||||
</van-tab>
|
||||
|
||||
</van-tabs>
|
||||
202
微信小程序源码/iot-home/pages/homeControl/homeContril.wxss
Normal file
202
微信小程序源码/iot-home/pages/homeControl/homeContril.wxss
Normal file
@@ -0,0 +1,202 @@
|
||||
/* 页面容器 */
|
||||
page {
|
||||
height: 100vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* tab容器 */
|
||||
.van-tabs {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* tab内容容器 */
|
||||
.van-tabs__content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* tab项 */
|
||||
.van-tab__pane {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* 滚动容器 */
|
||||
.tab-scroll {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
padding: 0 16px;
|
||||
box-sizing: border-box;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/*.grid-item {*/
|
||||
/* display: flex;*/
|
||||
/* flex-direction: column;*/
|
||||
/* align-items: center;*/
|
||||
/* justify-content: center;*/
|
||||
/* height: 120rpx; !* 固定高度让 square 生效 *!*/
|
||||
/*}*/
|
||||
|
||||
.slider-container {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.slider-btn {
|
||||
width: 40px;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
line-height: 18px;
|
||||
text-align: center;
|
||||
background-color: #1989fa;
|
||||
border-radius: 100px;
|
||||
}
|
||||
|
||||
/* 闹钟时间选择器样式 */
|
||||
.alarm-cell-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.alarm-time {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
.time-text {
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* 时间选择器样式 */
|
||||
.time-picker-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 24rpx 32rpx;
|
||||
border-bottom: 1rpx solid #ebedf0;
|
||||
}
|
||||
|
||||
.time-picker-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
.time-picker-view {
|
||||
height: 400rpx;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.picker-column-item {
|
||||
line-height: 80rpx;
|
||||
font-size: 32rpx;
|
||||
color: #323233;
|
||||
}
|
||||
|
||||
.time-picker-btn {
|
||||
margin: 24rpx 32rpx 32rpx;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
text-align: center;
|
||||
background-color: #1989fa;
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
border-radius: 8rpx;
|
||||
}
|
||||
|
||||
/* 时间段设置样式 */
|
||||
.period-group {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.period-cell-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.period-time-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.period-time-label {
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
.period-time-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8rpx;
|
||||
padding: 8rpx 16rpx;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #323233;
|
||||
min-width: 100rpx;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 下发按钮样式 */
|
||||
.period-action-cell {
|
||||
padding: 16rpx 32rpx !important;
|
||||
}
|
||||
|
||||
.period-action-cell .van-cell__value {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* 温度阈值设置样式 */
|
||||
.threshold-group {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.threshold-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.threshold-temp {
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
color: #ee0a24;
|
||||
margin-right: 8rpx;
|
||||
}
|
||||
|
||||
/* 滑块样式 */
|
||||
.slider-container {
|
||||
margin: 20px 32rpx;
|
||||
}
|
||||
|
||||
.slider-marks {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 16rpx;
|
||||
font-size: 24rpx;
|
||||
color: #969799;
|
||||
}
|
||||
|
||||
/* 温度阈值下发按钮样式 */
|
||||
.threshold-action-cell {
|
||||
padding: 16rpx 32rpx !important;
|
||||
margin-top: 32rpx;
|
||||
}
|
||||
|
||||
.threshold-action-cell .van-cell__value {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
Reference in New Issue
Block a user