701 lines
21 KiB
Vue
701 lines
21 KiB
Vue
<template>
|
||
<view class="container">
|
||
<!-- 控制设置标题 -->
|
||
<view class="control-title">控制设置</view>
|
||
<uni-section title="请选择大棚:" titleFontSize="18px" type="line">
|
||
<view class="uni-px-5 uni-pb-5">
|
||
<uni-data-select v-model="value" :localdata="range" @change="change"></uni-data-select>
|
||
</view>
|
||
</uni-section>
|
||
|
||
|
||
<uni-section title="实时温湿度" titleFontSize="16px" type="line" v-if="value!== 1">
|
||
<template v-slot:right >
|
||
{{ temp }}
|
||
</template>
|
||
|
||
<view>
|
||
<view class="uni-flex_control uni-row" >
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.temp1 }}</text>
|
||
<text class="data">温度1</text>
|
||
</view>
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.temp2 }}</text>
|
||
<text class="data">温度2</text>
|
||
</view>
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.temp3 }}</text>
|
||
<text class="data">温度3</text>
|
||
</view>
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.temp4 }}</text>
|
||
<text class="data">温度4</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="uni-flex_control uni-row" >
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.humi1 }}</text>
|
||
<text class="data">湿度1</text>
|
||
</view>
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.humi2 }}</text>
|
||
<text class="data">湿度2</text>
|
||
</view>
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.humi3 }}</text>
|
||
<text class="data">湿度3</text>
|
||
</view>
|
||
<view class="text uni-flex_control uni-view">
|
||
<text class="data" :style="fontStyle">{{ liveData.humi4 }}</text>
|
||
<text class="data">湿度4</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
</uni-section>
|
||
|
||
<uni-section title="设备控制" titleFontSize="16px" type="line" v-if="value!== 1">
|
||
|
||
<!-- 卷膜/卷被卡片容器(2列栅格布局) -->
|
||
<view class="card-grid">
|
||
<!-- 卷被开卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jbk, 'jbk')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷被开</text>
|
||
<text class="card-sub">{{ show.jbk }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jbk === 1 }">
|
||
<uni-icons :type=" (status.jbk === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卷被关卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jbg,'jbg')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷被关</text>
|
||
<text class="card-sub">{{ show.jbg }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jbg === 1 }">
|
||
<uni-icons :type=" (status.jbg === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卷膜1开卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jm1k, 'jm1k')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷膜1开</text>
|
||
<text class="card-sub">{{ show.jm1k }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jm1k === 1 }">
|
||
<uni-icons :type="(status.jm1k === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卷膜1关卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jm1g, 'jm1g')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷膜1关</text>
|
||
<text class="card-sub">{{ show.jm1g }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jm1g === 1 }">
|
||
<uni-icons :type="(status.jm1g === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卷膜2卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jm2k, 'jm2k')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷膜2开</text>
|
||
<text class="card-sub">{{ show.jm2k }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jm2k === 1 }">
|
||
<uni-icons :type="(status.jm2k === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卷膜2关卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jm2g, 'jm2g')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷膜2关</text>
|
||
<text class="card-sub">{{ show.jm2g }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jm2g === 1 }">
|
||
<uni-icons :type="(status.jm2g === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
<!-- 卷膜3开卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jm3k, 'jm3k')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷膜3开</text>
|
||
<text class="card-sub">{{ show.jm3k }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jm3k === 1 }">
|
||
<uni-icons :type="(status.jm3k === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 卷膜3关卡片 -->
|
||
<view class="control-card" @click="handleCardClick(1-status.jm3g, 'jm3g')">
|
||
<view class="card-text">
|
||
<text class="card-main">卷膜3关</text>
|
||
<text class="card-sub">{{ show.jm3g }}</text>
|
||
</view>
|
||
<view class="card-icon" :class="{ active: status.jm3g === 1 }">
|
||
<uni-icons :type="(status.jm3g === 1)?'circle':'circle-filled'" size="24" color="#fff"/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</uni-section>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import mqtt from 'mqtt'
|
||
import UniDatetimePicker
|
||
from "../../uni_modules/uni-datetime-picker/components/uni-datetime-picker/uni-datetime-picker.vue";
|
||
import { findDtuDataByInfo } from "@/api/system/data";
|
||
|
||
export default {
|
||
dicts: ['sys_data_map'],
|
||
components: {
|
||
UniDatetimePicker
|
||
},
|
||
data() {
|
||
return {
|
||
temp: "",
|
||
mqttConfig: {
|
||
host: '1.94.254.176',
|
||
port: 9001,
|
||
clientId: 'uniapp_mqtt_' + Math.random().toString(16).substr(2, 8),
|
||
username: 'admin',
|
||
password: 'Admin#12345678',
|
||
subscribeTopic:'/up',
|
||
},
|
||
value: 1,
|
||
range: [{
|
||
"value": '864865085016294',
|
||
"text": "十方北棚"
|
||
}, {
|
||
"value": '864536071808560',
|
||
"text": "七方北棚",
|
||
}, {
|
||
"value": '864865085008135',
|
||
"text": "八方北棚"
|
||
}],
|
||
imei:'',
|
||
publishTopic: '/down',
|
||
title:'',
|
||
message: {},
|
||
connected:false,
|
||
liveData: {
|
||
temp1: '数据加载中...',
|
||
temp2: '数据加载中...',
|
||
temp3: '数据加载中...',
|
||
temp4: '数据加载中...',
|
||
humi1: '数据加载中...',
|
||
humi2: '数据加载中...',
|
||
humi3: '数据加载中...',
|
||
humi4: '数据加载中...'
|
||
},
|
||
// 卡片状态(模拟后端返回数据)
|
||
show: {
|
||
jbk: "暂停",
|
||
jbg: "暂停",
|
||
jm1k: "暂停",
|
||
jm1g: "暂停",
|
||
jm2k: "暂停",
|
||
jm2g: "暂停",
|
||
jm3k: "暂停",
|
||
jm3g: "暂停"
|
||
},
|
||
deviceType: '',
|
||
status: {
|
||
jbk: 0,
|
||
jbg: 0,
|
||
jm1k: 0,
|
||
jm1g: 0,
|
||
jm2k: 0,
|
||
jm2g: 0,
|
||
jm3k: 0,
|
||
jm3g: 0
|
||
},
|
||
fontStyle: ''
|
||
};
|
||
},
|
||
onLoad() {
|
||
this.title="";
|
||
if (this.status.jbk===1) {
|
||
this.status.jbg = 0;
|
||
}
|
||
if (this.status.jbg===1) {
|
||
this.status.jbk = 0;
|
||
}
|
||
if (this.status.jm1k===1) {
|
||
this.status.jm1g = 0;
|
||
}
|
||
if (this.status.jm2k===1) {
|
||
this.status.jm2g = 0;
|
||
}
|
||
if (this.status.jm3k===1) {
|
||
this.status.jm3g = 0;
|
||
}
|
||
if (this.status.jm1g===1) {
|
||
this.status.jm1k = 0;
|
||
}
|
||
if (this.status.jm2g===1) {
|
||
this.status.jm2k = 0;
|
||
}
|
||
if (this.status.jm2g===1) {
|
||
this.status.jm3k = 0;
|
||
}
|
||
|
||
},
|
||
onShow() {
|
||
this.connectMqtt();
|
||
},
|
||
onUnload() {
|
||
this.disconnectMqtt()
|
||
},
|
||
methods: {
|
||
change(e) {
|
||
this.imei = e;
|
||
this.publishTopic = "dtu/"+this.imei+"/down";
|
||
this.mqttConfig.subscribeTopic = "dtu/"+this.imei+"/up";
|
||
const selectedItem = this.range.find(item => item.value === e);
|
||
if (selectedItem) {
|
||
this.selectedText = selectedItem.text; // 获取展示文本
|
||
this.title= this.selectedText;
|
||
var queryParams = {
|
||
imei: this.imei
|
||
}
|
||
findDtuDataByInfo(queryParams).then(response => {
|
||
this.liveData = {
|
||
temp1: response.data.temp1 || '已离线...',
|
||
temp2: response.data.temp2 || '已离线..',
|
||
temp3: response.data.temp3 || '已离线..',
|
||
temp4: response.data.temp4 || '已离线..',
|
||
humi1: response.data.humi1 || '已离线..',
|
||
humi2: response.data.humi2 || '已离线..',
|
||
humi3: response.data.humi3 || '已离线..',
|
||
humi4: response.data.humi4 || '已离线..'
|
||
}
|
||
this.temp = "最后更新时间:"+response.data.time;
|
||
this.fontStyle = 'font-size:16px'
|
||
})
|
||
} else {
|
||
this.selectedText = ''; // 无匹配项时清空
|
||
this.title='';
|
||
this.value=1;
|
||
}
|
||
this.reset();
|
||
this.style="";
|
||
// this.disconnectMqtt();
|
||
},
|
||
|
||
reset() {
|
||
this.show = {
|
||
jbk: "暂停",
|
||
jbg: "暂停",
|
||
jm1k: "暂停",
|
||
jm1g: "暂停",
|
||
jm2k: "暂停",
|
||
jm2g: "暂停",
|
||
jm3k: "暂停",
|
||
jm3g: "暂停"
|
||
}
|
||
this.deviceType = '';
|
||
this.status = {
|
||
jbk: 0,
|
||
jbg: 0,
|
||
jm1k: 0,
|
||
jm1g: 0,
|
||
jm2k: 0,
|
||
jm2g: 0,
|
||
jm3k: 0,
|
||
jm3g: 0
|
||
};
|
||
this.message = {};
|
||
this.temp = '';
|
||
this.liveData = {
|
||
temp1: '数据加载中...',
|
||
temp2: '数据加载中...',
|
||
temp3: '数据加载中...',
|
||
temp4: '数据加载中...',
|
||
humi1: '数据加载中...',
|
||
humi2: '数据加载中...',
|
||
humi3: '数据加载中...',
|
||
humi4: '数据加载中...'
|
||
}
|
||
},
|
||
// 卡片点击事件(实际项目中调用接口修改状态) 功能标识
|
||
handleCardClick(status, type) {
|
||
// 校验
|
||
// 定义类型与提示文案的映射关系,减少重复代码
|
||
const tipMap = {
|
||
'jbk': {opposite: 'jbg', name: '卷被关', op: '卷被开'},
|
||
'jbg': {opposite: 'jbk', name: '卷被开', op: '卷被关'},
|
||
'jm1k': {opposite: 'jm1g', name: '卷膜1关', op: '卷膜1开'},
|
||
'jm1g': {opposite: 'jm1k', name: '卷膜1开', op: '卷膜1关'},
|
||
'jm2k': {opposite: 'jm2g', name: '卷膜2关', op: '卷膜2开'},
|
||
'jm2g': {opposite: 'jm2k', name: '卷膜2开', op: '卷膜2关'},
|
||
'jm3k': {opposite: 'jm3g', name: '卷膜3关', op: '卷膜3开'},
|
||
'jm3g': {opposite: 'jm3k', name: '卷膜3开', op: '卷膜3关'}
|
||
};
|
||
|
||
// 先判断类型是否在映射表中,避免无效case
|
||
if (!tipMap[type]) return;
|
||
|
||
const {opposite, name, op} = tipMap[type];
|
||
// 核心校验逻辑(只写一次,无需重复)
|
||
if (status === 1 && this.status[opposite] === 1) {
|
||
this.$modal.msgError(`【${this.selectedText}】${name}在运行状态,不能运行${op}操作!`);
|
||
// 此处return可保留,也可去掉(因为函数执行到这里已完成核心逻辑)
|
||
return;
|
||
}
|
||
|
||
if (!this.connected) {
|
||
// 链接mqtt
|
||
this.connectMqtt()
|
||
}
|
||
if (this.value === 1) {
|
||
this.$modal.msgError("设备控制失败!");
|
||
console.info("大棚选取失败!")
|
||
return;
|
||
}
|
||
uni.showModal({
|
||
title: '操作提示:',
|
||
content: '确定' + (status === 1 ? "运行" : "暂停") + '【' + this.selectedText + '】设备?',
|
||
cancelText: '取消',
|
||
confirmText: '确定',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
// console.info("操作功能:【"+type+"】,变更状态为:"+ status)
|
||
|
||
// // 组装消息
|
||
this.message = JSON.stringify({[type]: status})
|
||
// console.info("指令:"+this.message+";大棚:"+this.publishTopic)
|
||
// 控制设备
|
||
this.publishMessage();
|
||
// 设备回执
|
||
this.deviceType = type;
|
||
//todo
|
||
|
||
// this.status[type] = this.status[type] === 0 ? 1 : 0;
|
||
// this.show[type] = this.status[type] === 0 ? "运行" : "暂停";
|
||
|
||
}
|
||
}
|
||
})
|
||
// console.info(this.status)
|
||
},
|
||
connectMqtt() {
|
||
const options = {
|
||
clientId: this.mqttConfig.clientId,
|
||
username: this.mqttConfig.username,
|
||
password: this.mqttConfig.password,
|
||
clean: true,
|
||
connectTimeout: 4000,
|
||
reconnectPeriod: 1000
|
||
}
|
||
|
||
const url = `ws://${this.mqttConfig.host}:${this.mqttConfig.port}/mqtt`
|
||
|
||
this.client = mqtt.connect(url, options)
|
||
|
||
this.client.on('connect', () => {
|
||
this.connected = true
|
||
this.client.subscribe('dtu/+/up', {qos: 0})
|
||
this.addMessage('已连接到MQTT服务器')
|
||
})
|
||
|
||
this.client.on("message", this.ackMessage);
|
||
|
||
this.client.on('error', (err) => {
|
||
this.addMessage(`控制失败: ${err.message}`)
|
||
this.connected = false
|
||
})
|
||
|
||
this.client.on('reconnect', () => {
|
||
this.addMessage('正在重新连接...')
|
||
})
|
||
|
||
this.client.on('close', () => {
|
||
this.addMessage('连接已关闭')
|
||
this.connected = false
|
||
})
|
||
},
|
||
|
||
disconnectMqtt() {
|
||
if (this.client && this.connected) {
|
||
this.client.end()
|
||
this.connected = false
|
||
this.addMessage('已断开MQTT连接')
|
||
}
|
||
},
|
||
|
||
publishMessage() {
|
||
// console.info("开始发布消息",this.connected,this.publishTopic,this.message)
|
||
if (!this.connected || !this.publishTopic || !this.message) {
|
||
uni.showToast({
|
||
title: '控制设备失败',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
this.client.publish(this.publishTopic, this.message, (err) => {
|
||
if (!err) {
|
||
this.addMessage(`【指令已发送】imei: ${this.publishTopic},指令: ${this.message}`);
|
||
|
||
} else {
|
||
this.addMessage(`发布失败: ${err.message},设备:[${this.publishTopic}]`)
|
||
}
|
||
})
|
||
|
||
this.message = {};
|
||
},
|
||
|
||
ackMessage(topic, payload) {
|
||
|
||
|
||
// console.info("消息解析:"+topic,payload)
|
||
// 1. 先判断是否是目标订阅主题(如dtu/xxx/up)
|
||
if (topic !== this.mqttConfig.subscribeTopic) return;
|
||
|
||
// 2. 解析消息体(注意异常捕获)
|
||
let msgData = {};
|
||
try {
|
||
msgData = JSON.parse(payload);
|
||
} catch (e) {
|
||
console.error("消息解析失败:", e);
|
||
return;
|
||
}
|
||
// console.info("回执消息:"+msgData)
|
||
// 3. 区分“回执”和“其他内容”
|
||
if (msgData.prop && "suc" in msgData) {
|
||
// console.info("处理回执消息")
|
||
// 👉 这是“指令回执”
|
||
this.handleCommandAck(msgData, this.deviceType);
|
||
} else {
|
||
this.handleOtherContent(msgData,payload)
|
||
}
|
||
},
|
||
addMessage(content) {
|
||
|
||
console.info("提示消息:" + content)
|
||
},
|
||
|
||
// 处理指令回执的函数
|
||
handleCommandAck(ackData, type) {
|
||
// console.info("回执消息11111:"+ackData)
|
||
// 拿到指令字段(如jm2k)和执行状态(suc)
|
||
const commandField = Object.keys(ackData.prop)[0]; // 这里是"jm2k"
|
||
const commandValue = ackData.prop[commandField]; // 这里是0
|
||
const isSuccess = ackData.suc; // 这里是true
|
||
|
||
if (isSuccess) {
|
||
this.status[type] = this.status[type] === 0 ? 1 : 0;
|
||
this.show[type] = this.status[type] === 0 ? "运行" : "暂停";
|
||
}
|
||
this.deviceType = '';
|
||
this.$modal.msgSuccess("设备操作成功!")
|
||
|
||
// 业务逻辑:提示“指令执行成功/失败”
|
||
console.log(`指令[${commandField}=${commandValue}]执行${isSuccess ? "成功" : "失败"}`);
|
||
// (可匹配之前发布的指令msgId,更新UI状态)
|
||
},
|
||
|
||
// 处理其他内容的函数
|
||
handleOtherContent(msgData,payload) {
|
||
// 业务逻辑:处理传感器数据、设备状态等
|
||
// console.log("收到其他内容:", msgData);
|
||
// 例如:更新温湿度显示、设备在线状态等
|
||
|
||
// 设备状态展示
|
||
if (this.value !== 1) {
|
||
var arr = ['jbk', "jbg", "jm1k", "jm1g", "jm2k", "jm2g", "jm3k", "jm3g"]
|
||
const allKeysNumeric = Object.keys(msgData).some(key => arr.includes(key));
|
||
if (allKeysNumeric) {
|
||
// console.info(msgData)
|
||
this.status = {...msgData}
|
||
// console.info("imei: "+this.publishTopic+"copy: ",this.status)
|
||
}
|
||
const allKeysNumeric2 = Object.keys(msgData).every(key => /^\d+$/.test(key));
|
||
if (Object.keys(msgData).length > 0 && allKeysNumeric2) {
|
||
|
||
// 你的键:温度101~104、湿度201~204(按你之前约定温度/10、湿度/10)
|
||
const div10 = (v) => (v == null ? null : Math.round((Number(v)/10)*10)/10)
|
||
|
||
this.liveData = {
|
||
temp1: msgData["201"]==null ? "已离线...":div10(msgData["201"])+"℃",
|
||
humi1: msgData["101"]==null ? "已离线...":div10(msgData["101"])+"%RH",
|
||
temp2: msgData["202"]==null ? "已离线...":div10(msgData["202"])+"℃",
|
||
humi2: msgData["102"]==null ? "已离线...":div10(msgData["102"])+"%RH",
|
||
temp3: msgData["203"]==null ? "已离线...":div10(msgData["203"])+"℃",
|
||
humi3: msgData["103"]==null ? "已离线...":div10(msgData["103"])+"%RH",
|
||
temp4: msgData["204"]==null ? "已离线...":div10(msgData["204"])+"℃",
|
||
humi4: msgData["104"]==null ? "已离线...":div10(msgData["104"])+"%RH"
|
||
}
|
||
// 调用函数获取并输出格式化后的当前时间
|
||
this.temp = "最新更新时间:" + this.getCurrentTime();
|
||
this.fontStyle = 'font-size:16px'
|
||
}
|
||
|
||
}
|
||
},
|
||
|
||
/**
|
||
* 获取格式化后的当前时间
|
||
* @returns {string} 格式为 YYYY-MM-DD HH:mm:ss 的当前时间
|
||
*/
|
||
getCurrentTime() {
|
||
const now = new Date();
|
||
|
||
// 获取年、月、日(补零确保两位数)
|
||
const year = now.getFullYear();
|
||
const month = String(now.getMonth() + 1).padStart(2, '0'); // 月份从 0 开始,需 +1
|
||
const day = String(now.getDate()).padStart(2, '0');
|
||
|
||
// 获取时、分、秒(补零确保两位数)
|
||
const hours = String(now.getHours()).padStart(2, '0');
|
||
const minutes = String(now.getMinutes()).padStart(2, '0');
|
||
const seconds = String(now.getSeconds()).padStart(2, '0');
|
||
|
||
// 拼接成标准格式
|
||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||
},
|
||
},
|
||
onHide() {
|
||
this.disconnectMqtt();
|
||
},
|
||
beforeDestroy() {
|
||
if (this.client) {
|
||
this.client.end()
|
||
}
|
||
},
|
||
|
||
|
||
};
|
||
</script>
|
||
|
||
<style scoped>
|
||
.container {
|
||
padding: 20rpx;
|
||
background-color: #f5f5f5;
|
||
}
|
||
|
||
/* 控制设置标题 */
|
||
.control-title {
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
text-align: center;
|
||
margin-bottom: 30rpx;
|
||
color: #333;
|
||
}
|
||
|
||
/* 2列栅格布局 */
|
||
.card-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 20rpx;
|
||
padding: 20rpx;
|
||
}
|
||
|
||
/* 卡片样式 */
|
||
.control-card {
|
||
background-color: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 30rpx 20rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
box-shadow: 0 2rpx 8rpx #bfbec1;
|
||
}
|
||
|
||
/* 卡片文字区域 */
|
||
.card-text {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8rpx;
|
||
}
|
||
|
||
.card-main {
|
||
font-size: 30rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.card-sub {
|
||
font-size: 24rpx;
|
||
color: #999;
|
||
}
|
||
|
||
/* 卡片图标容器 */
|
||
.card-icon {
|
||
width: 48rpx;
|
||
height: 48rpx;
|
||
border-radius: 50%;
|
||
background-color: #e5e5e5;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
/* 激活状态(运行) */
|
||
.card-icon.active {
|
||
background-color: #007aff;
|
||
}
|
||
.uni-px-5 {
|
||
padding-left: 20rpx;
|
||
padding-right: 20rpx;
|
||
}
|
||
|
||
.uni-pb-5 {
|
||
padding-bottom: 40rpx;
|
||
}
|
||
|
||
.text {
|
||
width: 50rpx;
|
||
margin: 10rpx 10rpx 8rpx 0;
|
||
padding: 0;
|
||
height: 70rpx;
|
||
line-height: 70rpx;
|
||
text-align: center;
|
||
background:#fff;
|
||
font-size: 26rpx;
|
||
box-shadow: 0 2rpx 8rpx #bfbec1
|
||
}
|
||
|
||
.text:first-child {
|
||
margin-left: 10rpx;
|
||
}
|
||
|
||
.uni-view {
|
||
-webkit-flex: 1;
|
||
flex: 1;
|
||
height: 150rpx;
|
||
-webkit-justify-content: center;
|
||
justify-content: center;
|
||
-webkit-align-items: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.data {
|
||
font-size: 13px;
|
||
display: inline-block;
|
||
line-height: 23px;
|
||
color: #3a3a3a;
|
||
}
|
||
.data:nth-child(even) {
|
||
font-size: 12px;
|
||
margin-top: 3px;
|
||
color: #9c9c9c;
|
||
}
|
||
/deep/ .uni-section-header__slot-right {
|
||
color: green;
|
||
}
|
||
</style>
|