1057 lines
27 KiB
Vue
1057 lines
27 KiB
Vue
<template>
|
||
<view class="relay-control">
|
||
<!-- 设备状态 -->
|
||
<!-- <button type="default" @click="consolebutton()">ceshi </button> -->
|
||
|
||
<view class="card">
|
||
<view class="status-titletop">{{title}}</view>
|
||
<view style="padding:20rpx;">
|
||
<u--form labelPosition="left" labelWidth="100"
|
||
:labelStyle="{ marginRight: '16px', lineHeight: '32px', width: '50px', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis',color: '#000000' }">
|
||
<view class="version-wrap">
|
||
<u-form-item :label="$tt('status.deviceVersion')">
|
||
<u-row>
|
||
<u-col span="8">
|
||
<u--text :text="'Version' + device.firmwareVersion"></u--text>
|
||
</u-col>
|
||
</u-row>
|
||
</u-form-item>
|
||
</view>
|
||
</u--form>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
|
||
|
||
<!-- 继电器状态显示 -->
|
||
<view class="relay-status">
|
||
<view class="status-title">继电器状态</view>
|
||
<view class="relay-grid">
|
||
<view class="relay-item" v-for="(item, index) in relayList" :key="index">
|
||
<!-- 可点击的继电器名称 -->
|
||
<view class="relay-name" @click="checkOnline(editRelayName, index)">
|
||
{{item.remark}}
|
||
<u-icon name="edit-pen" size="16" color="#999"></u-icon>
|
||
</view>
|
||
|
||
<view class="relay-state" :class="{'active': item.status === 1}">
|
||
{{item.status === 1 ? '开启' : '关闭'}}
|
||
</view>
|
||
|
||
<u-button type="primary" size="mini" @click="checkOnline(toggleRelay, index)"
|
||
:disabled="device.status !== 3">
|
||
{{item.status === 1 ? '关闭' : '开启'}}
|
||
</u-button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 时间方案配置 -->
|
||
<!-- <view class="time-schedule">
|
||
<view class="schedule-title">时间方案配置</view>
|
||
<view class="schedule-list">
|
||
<view class="schedule-item" v-for="(item, index) in scheduleList" :key="index">
|
||
<view class="schedule-info">
|
||
<text>时间:{{item.time}}</text>
|
||
<text>动作:{{item.action === 1 ? '开启' : '关闭'}}</text>
|
||
<text>继电器:{{item.relayIndexes.map(index => index + 1).join(', ')}}</text>
|
||
<text>重复日期:{{item.weekdays.map(index => weekdays[index]).join(', ')}}</text>
|
||
</view>
|
||
<u-button type="error" size="mini" @click="deleteSchedule(index)">删除</u-button>
|
||
</view>
|
||
</view>
|
||
<view class="add-schedule">
|
||
<u-button type="primary" @click="showAddSchedule">添加时间方案</u-button>
|
||
</view>
|
||
</view> -->
|
||
|
||
<!-- 时间方案配置 -->
|
||
<view class="time-schedule">
|
||
<view class="schedule-header">
|
||
<view class="schedule-title">时间方案配置</view>
|
||
<view style="display: flex; justify-content: flex-end;">
|
||
<u-button style="width: 180rpx; height: 40rpx;" type="primary" size="mini"
|
||
@click="checkOnline(showAddSchedule)" :disabled="device.status !== 3">
|
||
添加
|
||
</u-button>
|
||
</view>
|
||
</view>
|
||
<view class="empty-tip" v-if="scheduleList.length === 0">
|
||
<u-icon name="list" size="50" color="#c0c4cc"></u-icon>
|
||
<text>暂无时间方案,点击添加按钮创建</text>
|
||
</view>
|
||
<view class="schedule-list">
|
||
<view class="schedule-card" v-for="(item, index) in scheduleList" :key="index">
|
||
<view class="schedule-time">
|
||
<u-icon name="clock" size="18" color="#3c9cff"></u-icon>
|
||
<text>{{item.time}}</text>
|
||
</view>
|
||
|
||
<view class="schedule-content">
|
||
<!-- <view class="schedule-row">
|
||
<text class="label">动作:</text>
|
||
<u-tag :text="item.action === 1 ? '开启' : '关闭'"
|
||
:type="item.action === 1 ? 'success' : 'error'" size="mini"></u-tag>
|
||
</view> -->
|
||
|
||
<view class="schedule-row">
|
||
<text class="label">继电器:</text>
|
||
<view class="relay-tags">
|
||
<u-tag v-for="relayIndex in item.relayIndexes" :key="relayIndex"
|
||
:text="'继电器' + (relayIndex + 1)" type="primary" size="mini"></u-tag>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="schedule-row">
|
||
<text class="label">重复日期:</text>
|
||
<view class="weekday-tags">
|
||
<u-tag v-for="dayIndex in item.weekdays" :key="dayIndex" :text="weekdays[dayIndex]"
|
||
plain size="mini"></u-tag>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="schedule-actions">
|
||
<u-button type="error" size="mini" plain @click="checkOnline(deleteSchedule, index)"
|
||
:disabled="device.status !== 3">
|
||
删除
|
||
</u-button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
<!-- 添加时间方案弹窗 -->
|
||
<u-popup :show="showPopup" mode="center">
|
||
<view class="popup-content">
|
||
<view class="popup-title">添加时间方案</view>
|
||
<u--form :model="scheduleForm" ref="uForm">
|
||
<u-form-item label="时间">
|
||
<u-input v-model="scheduleForm.time" placeholder="请选择时间" @focus="openTimePicker" />
|
||
</u-form-item>
|
||
|
||
<!-- <u-form-item label="动作">
|
||
<u-radio-group v-model="scheduleForm.action">
|
||
<u-radio :name="1" label="开启"></u-radio>
|
||
<u-radio :name="0" label="关闭"></u-radio>
|
||
</u-radio-group>
|
||
</u-form-item> -->
|
||
|
||
<u-form-item label="选择继电器">
|
||
<u-checkbox-group v-model="scheduleForm.relayIndexes">
|
||
<u-checkbox v-for="(item, index) in relayList" :key="index" :name="index" shape="circle"
|
||
:label="item.name" class="custom-checkbox">
|
||
</u-checkbox>
|
||
</u-checkbox-group>
|
||
</u-form-item>
|
||
|
||
<u-form-item label="重复日期">
|
||
<u-checkbox-group v-model="scheduleForm.weekdays" class="repeat-weekdays-group">
|
||
<u-checkbox v-for="(day, index) in weekdays" :key="index" :name="index" shape="circle"
|
||
:label="day" class="custom-checkbox">
|
||
{{ day }}
|
||
</u-checkbox>
|
||
</u-checkbox-group>
|
||
</u-form-item>
|
||
</u--form>
|
||
|
||
<view class="popup-buttons">
|
||
<u-button @click="showPopup = false">取消</u-button>
|
||
<u-button type="primary" @click="addSchedule">确定</u-button>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
|
||
<!-- 时间选择器 -->
|
||
<u-datetime-picker :show="showTimePicker" v-model="tempTime" mode="time" @confirm="timeConfirm"
|
||
@cancel="showTimePicker = false" />
|
||
|
||
|
||
<!-- 继电器名称编辑弹窗 -->
|
||
<u-popup :show="showRelayEditPopup" mode="center" @close="showRelayEditPopup = false">
|
||
<view class="popup-content">
|
||
<view class="popup-title">修改继电器名称</view>
|
||
<u--form :model="relayEditForm" ref="uForm">
|
||
<!-- <u-form-item label="名称">
|
||
<u-input v-model="relayEditForm.name" placeholder="请输入继电器名称" />
|
||
</u-form-item> -->
|
||
|
||
<u-form-item label="备注">
|
||
<u-input v-model="relayEditForm.remark" placeholder="请输入备注信息" />
|
||
</u-form-item>
|
||
</u--form>
|
||
|
||
<view class="popup-buttons">
|
||
<u-button @click="showRelayEditPopup = false">取消</u-button>
|
||
<u-button type="primary" @click="confirmRelayEdit">确定</u-button>
|
||
</view>
|
||
</view>
|
||
</u-popup>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
getLatestFirmware,
|
||
relateChannelList,
|
||
getOrderControl
|
||
} from '@/apis/modules/device.js';
|
||
import {
|
||
serviceInvoke
|
||
} from '@/apis/modules/runtime.js';
|
||
import {
|
||
getSipStatusInfo,
|
||
getLocationWayInfo
|
||
} from '@/helpers/common.js'
|
||
|
||
export default {
|
||
name: 'relay-control',
|
||
props: {
|
||
device: {
|
||
type: Object,
|
||
default: null,
|
||
required: true
|
||
}
|
||
},
|
||
created() {
|
||
|
||
// 1秒后执行提取继电器列表
|
||
setTimeout(() => {
|
||
this.extractRelayList();
|
||
this.parseScheduleConfig(); // 解析时间方案 、
|
||
// this.consolebutton()
|
||
}, 1000);
|
||
this.updateDeviceStatus(this.device);
|
||
|
||
this.mqttCallback();
|
||
|
||
},
|
||
onLoad: function(option) {
|
||
console.log(JSON.stringify(this.device))
|
||
|
||
},
|
||
data() {
|
||
return {
|
||
title: '设备离线',
|
||
show: false,
|
||
firmware: {},
|
||
showTimePicker: false,
|
||
showPopup: false,
|
||
tempTime: '',
|
||
weekdays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],
|
||
bgStyle: '',
|
||
shadowUnEnable: false,
|
||
scheduleList: [], // 初始化为空数组,等待解析数据
|
||
showRelayEditPopup: false,
|
||
relayEditForm: {
|
||
index: null,
|
||
name: '',
|
||
remark: ''
|
||
},
|
||
relayList: [], // 初始化为空数组,等待提取数据
|
||
|
||
scheduleList: [],
|
||
scheduleForm: {
|
||
time: '12:00:00',
|
||
action: 1,
|
||
relayIndexes: [],
|
||
weekdays: []
|
||
}
|
||
}
|
||
},
|
||
|
||
methods: {
|
||
// 检查设备是否在线
|
||
checkOnline(callback, ...args) {
|
||
if (this.device.status !== 3) {
|
||
uni.showToast({
|
||
title: '设备离线,无法操作',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
if (typeof callback === 'function') {
|
||
return callback.apply(this, args);
|
||
}
|
||
return true;
|
||
},
|
||
// 编辑继电器名称
|
||
editRelayName(index) {
|
||
if (!this.checkOnline()) return;
|
||
|
||
this.relayEditForm = {
|
||
index,
|
||
name: this.relayList[index].name,
|
||
remark: this.relayList[index].remark || ''
|
||
};
|
||
this.showRelayEditPopup = true;
|
||
},
|
||
|
||
// 确认修改继电器信息
|
||
async confirmRelayEdit() {
|
||
try {
|
||
const {
|
||
index,
|
||
name,
|
||
remark
|
||
} = this.relayEditForm;
|
||
|
||
// 1. 更新本地数据
|
||
this.relayList[index].name = name;
|
||
this.relayList[index].remark = remark;
|
||
|
||
// 2. 找到对应的备注物模型
|
||
const channel = index + 1; // 继电器通道号(1-4)
|
||
const remarkModel = this.device.thingsModels.find(
|
||
item => item.id === `remark_ch${channel}`
|
||
);
|
||
|
||
if (remarkModel) {
|
||
// 3. 更新物模型值
|
||
remarkModel.shadow = remark;
|
||
|
||
// 4. 通过MQTT发布更新
|
||
await this.mqttPublish(this.device, remarkModel);
|
||
}
|
||
|
||
this.showRelayEditPopup = false;
|
||
uni.showToast({
|
||
title: '修改成功',
|
||
icon: 'success'
|
||
});
|
||
} catch (error) {
|
||
console.error(' 修改继电器信息失败:', error);
|
||
uni.showToast({
|
||
title: '修改失败: ' + error.message,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
|
||
// 解析时间方案配置
|
||
parseScheduleConfig() {
|
||
try {
|
||
// 从 thingsModelValue 中找到 cfg_param
|
||
const thingsModelValue = JSON.parse(this.device.thingsModelValue);
|
||
const cfgParam = thingsModelValue.find(item => item.id === 'cfg_param');
|
||
|
||
if (cfgParam && cfgParam.value) {
|
||
// 提取 JSON 部分(去掉前面的 "JSON=")
|
||
const jsonStr = cfgParam.value.startsWith('JSON=') ?
|
||
cfgParam.value.substring(5) :
|
||
cfgParam.value;
|
||
|
||
// 解析 JSON
|
||
const config = JSON.parse(jsonStr);
|
||
|
||
// 转换格式为 scheduleList 需要的格式
|
||
this.scheduleList = config.shedule.map(item => {
|
||
// 将 time_s(秒数)转换为 HH:mm:ss 格式
|
||
const hours = Math.floor(item.time_s / 3600);
|
||
const minutes = Math.floor((item.time_s % 3600) / 60);
|
||
const seconds = item.time_s % 60;
|
||
const time =
|
||
`${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
|
||
|
||
// 将 week 字符串转换为 weekdays 数组
|
||
const weekdays = item.week.split('').map((day, index) => day === '1' ? index : null)
|
||
.filter(index => index !== null);
|
||
|
||
// 获取选中的继电器索引
|
||
const relayIndexes = [];
|
||
if (item.sw1_s === 1) relayIndexes.push(0);
|
||
if (item.sw2_s === 1) relayIndexes.push(1);
|
||
if (item.sw3_s === 1) relayIndexes.push(2);
|
||
if (item.sw4_s === 1) relayIndexes.push(3);
|
||
|
||
return {
|
||
time,
|
||
action: relayIndexes.length > 0 ? 1 : 0, // 如果有选中的继电器则为开启
|
||
relayIndexes,
|
||
weekdays,
|
||
originalData: item // 保留原始数据
|
||
};
|
||
});
|
||
|
||
console.log(' 解析出的时间方案:', this.scheduleList);
|
||
}
|
||
} catch (error) {
|
||
console.error(' 解析时间方案配置出错:', error);
|
||
}
|
||
},
|
||
|
||
// 提取继电器列表
|
||
extractRelayList() {
|
||
// 从 device.thingsModels 中筛选出所有开关状态属性
|
||
const relayItems = this.device.thingsModels.filter(item =>
|
||
item.id.startsWith('sw_ch') && item.datatype.type === 'bool'
|
||
);
|
||
|
||
// 转换为继电器列表格式
|
||
this.relayList = relayItems.map((item, index) => ({
|
||
name: `继电器${index + 1}`,
|
||
status: parseInt(item.shadow) || 0, // 确保是数字类型
|
||
remark: this.getRelayRemark(index + 1), // 获取对应通道的备注
|
||
model: JSON.parse(JSON.stringify(item)) // 深拷贝物模型对象
|
||
}));
|
||
|
||
console.log(' 继电器列表:', JSON.stringify(this.relayList));
|
||
},
|
||
|
||
|
||
// 获取继电器备注(从 remark_chX 属性)
|
||
getRelayRemark(channel) {
|
||
const remarkItem = this.device.thingsModels.find(item =>
|
||
item.id === `remark_ch${channel}`
|
||
);
|
||
return remarkItem ? remarkItem.shadow : '';
|
||
},
|
||
// mqtt回调
|
||
/* Mqtt回调处理 */
|
||
mqttCallback() {
|
||
this.$mqttTool.client.on('message', (topic, message, buffer) => {
|
||
let topics = topic.split('/');
|
||
let productId = topics[1];
|
||
let deviceNum = topics[2];
|
||
message = JSON.parse(message.toString());
|
||
|
||
// 判断是否是当前设备的消息
|
||
if (this.device.serialNumber !== deviceNum) return;
|
||
|
||
// 处理状态更新
|
||
if (topics[3] == 'status') {
|
||
console.log(' 接收到【设备状态-运行】主题:', topic);
|
||
console.log(' 接收到【设备状态-运行】内容:', JSON.stringify(message));
|
||
this.device.status = message.status;
|
||
this.device.isShadow = message.isShadow;
|
||
this.device.rssi = message.rssi;
|
||
this.updateDeviceStatus(this.device);
|
||
}
|
||
// 处理物模型属性更新
|
||
else if (topics[3] == 'property' || topics[3] == 'function' || topic.endsWith('ws/service')) {
|
||
console.log(' 接收到【物模型】主题:', topic);
|
||
console.log(' 接收到【物模型】内容:', JSON.stringify(message));
|
||
|
||
// 更新继电器状态
|
||
if (Array.isArray(message.message)) {
|
||
message.message.forEach(item => {
|
||
// 检查是否是继电器状态更新
|
||
if (item.id && item.id.startsWith('sw_ch')) {
|
||
const channel = parseInt(item.id.replace('sw_ch', ''));
|
||
if (!isNaN(channel) && channel >= 1 && channel <= 4) {
|
||
const index = channel - 1;
|
||
this.relayList[index].status = parseInt(item.value) || 0;
|
||
}
|
||
}
|
||
// 检查是否是继电器备注更新
|
||
else if (item.id && item.id.startsWith('remark_ch')) {
|
||
const channel = parseInt(item.id.replace('remark_ch', ''));
|
||
if (!isNaN(channel) && channel >= 1 && channel <= 4) {
|
||
const index = channel - 1;
|
||
this.relayList[index].remark = item.value || '';
|
||
}
|
||
}
|
||
});
|
||
}
|
||
}
|
||
});
|
||
},
|
||
//发送指令
|
||
async mqttPublish(device, model) {
|
||
const command = {};
|
||
command[model.id] = model.shadow;
|
||
const params = {
|
||
deviceId: device.deviceId,
|
||
modelId: model.modelId
|
||
}
|
||
//判断是否有权限
|
||
const response = await getOrderControl(params);
|
||
if (response.code != 200) {
|
||
uni.$u.toast(response.msg);
|
||
return;
|
||
}
|
||
const data = {
|
||
serialNumber: device.serialNumber,
|
||
productId: device.productId,
|
||
remoteCommand: command,
|
||
identifier: model.id,
|
||
modelName: model.name,
|
||
isShadow: device.status != 3,
|
||
type: model.type,
|
||
}
|
||
serviceInvoke(data).then(response => {
|
||
if (response.code === 200) {
|
||
uni.showToast({
|
||
icon: 'none',
|
||
title: this.$tt('status.service')
|
||
});
|
||
}
|
||
})
|
||
},
|
||
/** 更新设备状态 */
|
||
updateDeviceStatus(device) {
|
||
if (device.status === 3) {
|
||
this.title = this.$tt('status.online');
|
||
this.bgStyle =
|
||
'background-color:#5ac725;border-top-left-radius:6px;border-top-right-radius:6px;';
|
||
this.shadowUnEnable = false;
|
||
} else {
|
||
if (device.isShadow === 1) {
|
||
this.bgStyle =
|
||
'background-color:#3c9cff;border-top-left-radius:6px;border-top-right-radius:6px;';
|
||
this.title = this.$tt('status.shadow');
|
||
this.shadowUnEnable = false;
|
||
} else {
|
||
this.bgStyle =
|
||
'background-color:#909399;border-top-left-radius:6px;border-top-right-radius:6px;';
|
||
this.title = this.$tt('status.deviceOffline');
|
||
this.shadowUnEnable = true;
|
||
}
|
||
}
|
||
},
|
||
consolebutton() {
|
||
console.log("按钮打印", JSON.stringify(this.device))
|
||
},
|
||
// 切换继电器状态
|
||
async toggleRelay(index) {
|
||
if (!this.checkOnline()) return;
|
||
|
||
try {
|
||
// 1. 更新本地状态
|
||
const newStatus = this.relayList[index].status === 1 ? 0 : 1;
|
||
this.relayList[index].status = newStatus;
|
||
|
||
// 2. 找到对应的物模型
|
||
const channel = index + 1; // 继电器通道号(1-4)
|
||
const switchModel = this.device.thingsModels.find(
|
||
item => item.id === `sw_ch${channel}`
|
||
);
|
||
|
||
if (switchModel) {
|
||
// 3. 更新物模型值
|
||
switchModel.shadow = newStatus.toString();
|
||
|
||
// 4. 通过MQTT发布更新
|
||
await this.mqttPublish(this.device, switchModel);
|
||
}
|
||
} catch (error) {
|
||
console.error(' 切换继电器状态失败:', error);
|
||
// 回滚状态
|
||
this.relayList[index].status = this.relayList[index].status === 1 ? 0 : 1;
|
||
uni.showToast({
|
||
title: '操作失败: ' + error.message,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
showAddSchedule() {
|
||
if (!this.checkOnline()) return;
|
||
|
||
const now = new Date();
|
||
const hours = now.getHours().toString().padStart(2, '0');
|
||
const minutes = now.getMinutes().toString().padStart(2, '0');
|
||
const seconds = now.getSeconds().toString().padStart(2, '0');
|
||
this.scheduleForm = {
|
||
time: `${hours}:${minutes}:${seconds}`,
|
||
action: 1,
|
||
relayIndexes: [],
|
||
weekdays: []
|
||
};
|
||
this.showPopup = true;
|
||
},
|
||
openTimePicker() {
|
||
this.showTimePicker = true;
|
||
this.tempTime = new Date(); // 打开时初始化临时时间
|
||
},
|
||
timeConfirm(e) {
|
||
const time = new Date(e.value);
|
||
const hours = time.getHours().toString().padStart(2, '0');
|
||
const minutes = time.getMinutes().toString().padStart(2, '0');
|
||
const seconds = time.getSeconds().toString().padStart(2, '0');
|
||
this.scheduleForm.time = `${hours}:${minutes}:${seconds}`;
|
||
this.showTimePicker = false;
|
||
},
|
||
async addSchedule() {
|
||
try {
|
||
// 1. 添加时间方案到本地列表
|
||
const newSchedule = {
|
||
...this.scheduleForm
|
||
};
|
||
this.scheduleList.push(newSchedule);
|
||
this.showPopup = false;
|
||
|
||
// 2. 转换为设备需要的格式
|
||
const deviceSchedule = this.convertToDeviceSchedule(newSchedule);
|
||
|
||
// 3. 准备要发送的数据
|
||
const scheduleConfig = {
|
||
shedule: [...this.scheduleList.map(s => s.originalData || this.convertToDeviceSchedule(s))]
|
||
};
|
||
|
||
// 4. 找到 cfg_param 模型
|
||
const cfgParamModel = this.device.thingsModels.find(item => item.id === 'cfg_param');
|
||
if (!cfgParamModel) {
|
||
throw new Error('找不到定时配置模型');
|
||
}
|
||
|
||
// 5. 更新模型值
|
||
cfgParamModel.shadow = `JSON=${JSON.stringify(scheduleConfig)}`;
|
||
|
||
// 6. 通过 MQTT 发布
|
||
await this.mqttPublish(this.device, cfgParamModel);
|
||
|
||
uni.showToast({
|
||
title: '时间方案添加成功',
|
||
icon: 'success'
|
||
});
|
||
} catch (error) {
|
||
console.error(' 添加时间方案失败:', error);
|
||
uni.showToast({
|
||
title: '添加失败: ' + error.message,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
|
||
// 将前端格式转换为设备格式
|
||
convertToDeviceSchedule(schedule) {
|
||
// 将 HH:mm:ss 转换为秒数
|
||
const [hours, minutes, seconds] = schedule.time.split(':').map(Number);
|
||
const time_s = hours * 3600 + minutes * 60 + seconds;
|
||
|
||
// 将星期数组转换为字符串
|
||
const weekArr = new Array(7).fill('0');
|
||
schedule.weekdays.forEach(day => weekArr[day] = '1');
|
||
const week = weekArr.join('');
|
||
|
||
// 设置继电器状态
|
||
const relayStatus = [0, 0, 0, 0]; // 默认全部关闭
|
||
schedule.relayIndexes.forEach(index => relayStatus[index] = 1);
|
||
|
||
return {
|
||
time_s,
|
||
week,
|
||
sw1_s: relayStatus[0],
|
||
sw2_s: relayStatus[1],
|
||
sw3_s: relayStatus[2],
|
||
sw4_s: relayStatus[3]
|
||
};
|
||
},
|
||
|
||
async deleteSchedule(index) {
|
||
if (!this.checkOnline()) return;
|
||
|
||
try {
|
||
// 1. 从列表中删除
|
||
this.scheduleList.splice(index, 1);
|
||
|
||
// 2. 准备要发送的数据
|
||
const scheduleConfig = {
|
||
shedule: this.scheduleList.map(s => s.originalData || this.convertToDeviceSchedule(s))
|
||
};
|
||
|
||
// 3. 找到 cfg_param 模型
|
||
const cfgParamModel = this.device.thingsModels.find(item => item.id === 'cfg_param');
|
||
if (!cfgParamModel) {
|
||
throw new Error('找不到定时配置模型');
|
||
}
|
||
|
||
// 4. 更新模型值
|
||
cfgParamModel.shadow = `JSON=${JSON.stringify(scheduleConfig)}`;
|
||
|
||
// 5. 通过 MQTT 发布
|
||
await this.mqttPublish(this.device, cfgParamModel);
|
||
|
||
uni.showToast({
|
||
title: '删除成功',
|
||
icon: 'success'
|
||
});
|
||
} catch (error) {
|
||
console.error(' 删除时间方案失败:', error);
|
||
uni.showToast({
|
||
title: '删除失败: ' + error.message,
|
||
icon: 'none'
|
||
});
|
||
}
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
/* 添加禁用状态的样式 */
|
||
.u-button[disabled] {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.relay-name[disabled] {
|
||
opacity: 0.6;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
/* 继电器名称样式 */
|
||
.relay-name {
|
||
color: #333;
|
||
font-weight: 500;
|
||
margin-bottom: 10rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
padding: 8rpx;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
.relay-name:active {
|
||
background-color: #f0f0f0;
|
||
}
|
||
|
||
/* 编辑图标样式 */
|
||
.u-icon-edit-pen {
|
||
opacity: 0.6;
|
||
transition: opacity 0.3s;
|
||
}
|
||
|
||
.relay-name:hover .u-icon-edit-pen {
|
||
opacity: 1;
|
||
}
|
||
|
||
.empty-tip {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 40rpx 0;
|
||
color: #c0c4cc;
|
||
font-size: 28rpx;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.card {
|
||
box-shadow: 0 1px 0px 0 rgba(0, 0, 0, 0.1);
|
||
border-radius: 20rpx;
|
||
background-color: #fff;
|
||
|
||
&:not(:last-child) {
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.status-title {
|
||
font-weight: bold;
|
||
font-size: 15px;
|
||
color: #333333;
|
||
line-height: 42rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
text-transform: none;
|
||
padding: 15rpx 28rpx;
|
||
}
|
||
|
||
.version-wrap {
|
||
background-color: #F7F7F7;
|
||
border-radius: 10rpx;
|
||
padding: 0 42rpx;
|
||
font-family: PingFang SC, PingFang SC;
|
||
font-weight: 400;
|
||
font-size: 24rpx;
|
||
color: #000000;
|
||
line-height: 42rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
}
|
||
}
|
||
|
||
// 重复日期的 u-checkbox-group 布局
|
||
::v-deep .repeat-weekdays-group {
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
gap: 15rpx;
|
||
padding: 10rpx 0;
|
||
|
||
.custom-checkbox {
|
||
justify-content: center;
|
||
}
|
||
}
|
||
|
||
.relay-control {
|
||
padding: 20rpx;
|
||
|
||
.relay-status,
|
||
.time-schedule {
|
||
background-color: #fff;
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.schedule-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.schedule-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.schedule-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.schedule-card {
|
||
background-color: #f9f9f9;
|
||
border-radius: 12rpx;
|
||
padding: 20rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 15rpx;
|
||
position: relative;
|
||
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
|
||
.schedule-time {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10rpx;
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.schedule-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 12rpx;
|
||
padding-left: 28rpx;
|
||
/* 对齐图标 */
|
||
}
|
||
|
||
.schedule-row {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 26rpx;
|
||
}
|
||
|
||
.label {
|
||
color: #666;
|
||
min-width: 120rpx;
|
||
}
|
||
|
||
.relay-tags,
|
||
.weekday-tags {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.schedule-actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
margin-top: 10rpx;
|
||
}
|
||
|
||
/* 响应式调整 */
|
||
@media (min-width: 768px) {
|
||
.schedule-card {
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.schedule-content {
|
||
flex: 1;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
gap: 15rpx 30rpx;
|
||
}
|
||
|
||
.schedule-row {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.schedule-actions {
|
||
margin-top: 0;
|
||
}
|
||
}
|
||
|
||
.status-title,
|
||
.schedule-title {
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 20rpx;
|
||
}
|
||
|
||
.status-titletop {
|
||
font-weight: bold;
|
||
font-size: 15px;
|
||
color: #333333;
|
||
line-height: 42rpx;
|
||
text-align: left;
|
||
font-style: normal;
|
||
text-transform: none;
|
||
padding: 15rpx 28rpx;
|
||
}
|
||
|
||
|
||
.relay-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 20rpx;
|
||
}
|
||
|
||
.relay-item {
|
||
background-color: #f5f5f5;
|
||
padding: 20rpx;
|
||
border-radius: 8rpx;
|
||
text-align: center;
|
||
|
||
.relay-name {
|
||
font-size: 28rpx;
|
||
margin-bottom: 10rpx;
|
||
}
|
||
|
||
.relay-state {
|
||
font-size: 24rpx;
|
||
color: #666;
|
||
margin-bottom: 10rpx;
|
||
|
||
&.active {
|
||
color: #2979ff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.schedule-list {
|
||
.schedule-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx;
|
||
border-bottom: 1px solid #eee;
|
||
|
||
.schedule-info {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10rpx;
|
||
}
|
||
}
|
||
}
|
||
|
||
.add-schedule {
|
||
margin-top: 20rpx;
|
||
text-align: center;
|
||
}
|
||
|
||
.popup-content {
|
||
background-color: #fff;
|
||
padding: 30rpx;
|
||
border-radius: 12rpx;
|
||
width: 600rpx;
|
||
|
||
.popup-title {
|
||
text-align: center;
|
||
font-size: 32rpx;
|
||
font-weight: bold;
|
||
margin-bottom: 30rpx;
|
||
}
|
||
|
||
.popup-buttons {
|
||
display: flex;
|
||
justify-content: space-around;
|
||
margin-top: 30rpx;
|
||
}
|
||
|
||
// 表单项样式
|
||
::v-deep .u-form-item {
|
||
margin-bottom: 20rpx;
|
||
|
||
.u-form-item__body {
|
||
padding: 10rpx 0;
|
||
}
|
||
}
|
||
|
||
// 继电器选择区域
|
||
::v-deep .u-checkbox-group {
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 15rpx;
|
||
padding: 10rpx 0;
|
||
}
|
||
|
||
// 星期选择区域
|
||
// 星期选择区域
|
||
::v-deep .u-checkbox-group+.u-checkbox-group {
|
||
width: 100%;
|
||
display: grid;
|
||
grid-template-columns: repeat(4, 1fr);
|
||
/* 每行放4个 */
|
||
grid-auto-rows: auto;
|
||
/* 高度自适应 */
|
||
gap: 15rpx;
|
||
padding: 10rpx 0;
|
||
justify-content: center;
|
||
}
|
||
|
||
// 自定义复选框样式
|
||
::v-deep .custom-checkbox {
|
||
background-color: #f8f8f8;
|
||
padding: 12rpx 8rpx;
|
||
border-radius: 6rpx;
|
||
text-align: center;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
min-height: 50rpx;
|
||
border: 1px solid #eee;
|
||
margin: 0 auto;
|
||
width: 90%;
|
||
|
||
&:hover {
|
||
background-color: #f0f0f0;
|
||
}
|
||
|
||
// 修改文本样式
|
||
::v-deep text {
|
||
white-space: normal !important;
|
||
word-break: break-all;
|
||
line-height: 1.2;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
margin-left: 6rpx;
|
||
}
|
||
|
||
// 调整复选框图标位置
|
||
::v-deep .u-checkbox__icon-wrap {
|
||
margin: 0;
|
||
transform: scale(0.85);
|
||
}
|
||
}
|
||
|
||
// 表单容器样式
|
||
::v-deep .u-form {
|
||
width: 100%;
|
||
}
|
||
|
||
// 表单项样式
|
||
::v-deep .u-form-item__body {
|
||
padding: 0;
|
||
width: 100%;
|
||
}
|
||
|
||
// 动作选择样式
|
||
::v-deep .u-radio-group {
|
||
display: flex;
|
||
gap: 30rpx;
|
||
padding: 10rpx 0;
|
||
}
|
||
}
|
||
}
|
||
</style> |