|
|
@@ -12,13 +12,8 @@
|
|
|
</div>
|
|
|
</template>
|
|
|
<div class="device-grid">
|
|
|
- <el-card
|
|
|
- v-for="item in deviceConfigGroup.SENSORS || []"
|
|
|
- :key="item.code"
|
|
|
- class="device-item"
|
|
|
- shadow="hover"
|
|
|
- :body-style="{ padding: '12px' }"
|
|
|
- >
|
|
|
+ <el-card v-for="item in deviceConfigGroup.SENSORS || []" :key="item.code" class="device-item"
|
|
|
+ shadow="hover" :body-style="{ padding: '12px' }">
|
|
|
<div class="device-info">
|
|
|
<div class="device-name-code">
|
|
|
<div class="device-name">{{ item.equipmentName || item.code }}</div>
|
|
|
@@ -28,28 +23,18 @@
|
|
|
<!-- 实际值显示 -->
|
|
|
<div class="actual-value">
|
|
|
<span class="value-label">实际值:</span>
|
|
|
- <el-tag
|
|
|
- :type="getValueType(sensorActualValues[item.code])"
|
|
|
- size="large"
|
|
|
- class="actual-tag"
|
|
|
- >
|
|
|
+ <el-tag :type="getValueType(sensorActualValues[item.code])" size="large"
|
|
|
+ class="actual-tag">
|
|
|
{{ formatValue(sensorActualValues[item.code]) }}
|
|
|
</el-tag>
|
|
|
</div>
|
|
|
<!-- 设置值输入框 -->
|
|
|
<div class="set-value">
|
|
|
<span class="value-label">设置值:</span>
|
|
|
- <el-input-number
|
|
|
- v-model="sensorSetValues[item.code]"
|
|
|
- :min="0"
|
|
|
- :max="100"
|
|
|
- :step="0.1"
|
|
|
- :precision="2"
|
|
|
- size="large"
|
|
|
- controls-position="right"
|
|
|
+ <el-input-number v-model="sensorSetValues[item.code]" :min="0" :max="100"
|
|
|
+ :step="0.1" :precision="2" size="large" controls-position="right"
|
|
|
@change="(val) => handleSensorValueChange(item.code, val)"
|
|
|
- class="sensor-input"
|
|
|
- />
|
|
|
+ class="sensor-input" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -65,14 +50,9 @@
|
|
|
<el-tag type="info" size="small">{{ deviceConfigGroup.VALVES?.length || 0 }}个</el-tag>
|
|
|
</div>
|
|
|
</template>
|
|
|
- <div class="device-grid">
|
|
|
- <el-card
|
|
|
- v-for="item in deviceConfigGroup.VALVES || []"
|
|
|
- :key="item.code"
|
|
|
- class="device-item"
|
|
|
- shadow="hover"
|
|
|
- :body-style="{ padding: '12px' }"
|
|
|
- >
|
|
|
+ <div class="device-grid">
|
|
|
+ <el-card v-for="item in deviceConfigGroup.VALVES || []" :key="item.code" class="device-item"
|
|
|
+ shadow="hover" :body-style="{ padding: '12px' }">
|
|
|
<div class="device-info">
|
|
|
<div class="device-name-code">
|
|
|
<div class="device-name">{{ item.equipmentName || item.code }}</div>
|
|
|
@@ -82,23 +62,18 @@
|
|
|
<!-- 实际值显示 -->
|
|
|
<div class="actual-value">
|
|
|
<span class="value-label">实际值:</span>
|
|
|
- <el-tag
|
|
|
- :type="valveActualValues[item.code] ? 'success' : 'danger'"
|
|
|
- size="large"
|
|
|
- class="actual-tag"
|
|
|
- >
|
|
|
+ <el-tag :type="valveActualValues[item.code] ? 'success' : 'danger'" size="large"
|
|
|
+ class="actual-tag">
|
|
|
{{ valveActualValues[item.code] ? '开启' : '关闭' }}
|
|
|
</el-tag>
|
|
|
</div>
|
|
|
<!-- 设置值按钮 -->
|
|
|
<div class="set-value">
|
|
|
<span class="value-label">设置值:</span>
|
|
|
- <el-button
|
|
|
- :type="valveSetValues[item.code] ? 'success' : 'danger'"
|
|
|
+ <el-button :type="valveSetValues[item.code] ? 'success' : 'danger'"
|
|
|
:loading="valveLoadingMap[item.code]"
|
|
|
@click="handleValveToggle(item.code, !valveSetValues[item.code])"
|
|
|
- class="valve-button"
|
|
|
- >
|
|
|
+ class="valve-button">
|
|
|
{{ valveSetValues[item.code] ? '开启' : '关闭' }}
|
|
|
</el-button>
|
|
|
</div>
|
|
|
@@ -107,6 +82,39 @@
|
|
|
</el-card>
|
|
|
</div>
|
|
|
</el-card>
|
|
|
+
|
|
|
+ <el-card class="light-control-card" shadow="hover">
|
|
|
+ <template #header>
|
|
|
+ <div class="card-header">
|
|
|
+ <span class="card-title">灯光控制</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div class="light-button-grid">
|
|
|
+ <el-button type="primary" class="light-button" @click="lightControl(0)">
|
|
|
+ 初始化
|
|
|
+ </el-button>
|
|
|
+ <el-button type="danger" class="light-button" @click="lightSceneControl(1)">
|
|
|
+ 分离模块罐体<br>
|
|
|
+ 液体泄漏
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" class="light-button" @click="lightSceneControl(2)">
|
|
|
+ 料仓加料模块<br>
|
|
|
+ 物料短缺
|
|
|
+ </el-button>
|
|
|
+ <el-button type="info" class="light-button" @click="lightSceneControl(3)">
|
|
|
+ 分离模块罐体<br>
|
|
|
+ 试验测试
|
|
|
+ </el-button>
|
|
|
+ <el-button type="warning" class="light-button" @click="lightSceneControl(4)">
|
|
|
+ 韫珠车间<br>
|
|
|
+ 开关异常
|
|
|
+ </el-button>
|
|
|
+ <el-button type="primary" class="light-button" @click="lightSceneControl(5)">
|
|
|
+ 热交换模块<br>
|
|
|
+ 液体开关检修维护
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
@@ -183,7 +191,7 @@ function updatePageConfig() {
|
|
|
deviceDataGroup.VALVES = Object.values(data.VALVES)
|
|
|
// deviceDataGroup.PUMPS = Object.values(data.PUMPS)
|
|
|
deviceDataGroup.SENSORS = Object.values(data.SENSORS)
|
|
|
-
|
|
|
+
|
|
|
// 初始化阀门和传感器设置值和实际值(仅在首次加载时)
|
|
|
if (data.VALVES && Object.keys(valveSetValues.value).length === 0) {
|
|
|
Object.values(data.VALVES).forEach(valve => {
|
|
|
@@ -205,7 +213,7 @@ function updatePageConfig() {
|
|
|
}
|
|
|
})
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
if (Object.keys(valveSetValues.value).length > 0 || Object.keys(sensorSetValues.value).length > 0) {
|
|
|
// 后续更新只更新实际值
|
|
|
updateActualValues(data)
|
|
|
@@ -218,31 +226,31 @@ function updatePageConfig() {
|
|
|
}
|
|
|
|
|
|
const { getDataArrByCode, getValveStatusArr } = useValveHelper(computed(() => [
|
|
|
- ...deviceDataGroup.VALVES,
|
|
|
- ...deviceDataGroup.SENSORS
|
|
|
+ ...deviceDataGroup.VALVES,
|
|
|
+ ...deviceDataGroup.SENSORS
|
|
|
]))
|
|
|
|
|
|
// 格式化传感器数值
|
|
|
const formatValue = (value) => {
|
|
|
- if (typeof value === 'number') {
|
|
|
- return value.toFixed(2)
|
|
|
- }
|
|
|
- return value || '0.00'
|
|
|
+ if (typeof value === 'number') {
|
|
|
+ return value.toFixed(2)
|
|
|
+ }
|
|
|
+ return value || '0.00'
|
|
|
}
|
|
|
|
|
|
// 获取传感器数值类型(用于标签颜色)
|
|
|
const getValueType = (value) => {
|
|
|
- if (typeof value === 'number') {
|
|
|
- if (value > 0.8) return 'danger'
|
|
|
- if (value > 0.5) return 'warning'
|
|
|
- return 'success'
|
|
|
- }
|
|
|
- return 'info'
|
|
|
+ if (typeof value === 'number') {
|
|
|
+ if (value > 0.8) return 'danger'
|
|
|
+ if (value > 0.5) return 'warning'
|
|
|
+ return 'success'
|
|
|
+ }
|
|
|
+ return 'info'
|
|
|
}
|
|
|
|
|
|
// 获取阀门状态类型
|
|
|
const getValveStatusType = (status) => {
|
|
|
- return status ? 'success' : 'danger'
|
|
|
+ return status ? 'success' : 'danger'
|
|
|
}
|
|
|
|
|
|
// 阀门切换处理函数
|
|
|
@@ -252,14 +260,14 @@ const handleValveToggle = async (code, value) => {
|
|
|
console.log(`阀门 ${code} 正在操作中,请稍候...`)
|
|
|
return
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
// 设置加载状态,防止重复点击
|
|
|
valveLoadingMap.value[code] = true
|
|
|
-
|
|
|
+
|
|
|
try {
|
|
|
const res = await getMainSetParam(code)
|
|
|
const registerData = res.data
|
|
|
-
|
|
|
+
|
|
|
const data = {
|
|
|
protocol: registerData.protocolId,
|
|
|
unitId: registerData.unitId,
|
|
|
@@ -271,12 +279,12 @@ const handleValveToggle = async (code, value) => {
|
|
|
ipAddress: registerData.ipAddress,
|
|
|
port: registerData.port
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
await setValues(data)
|
|
|
-
|
|
|
+
|
|
|
// 更新设置值
|
|
|
valveSetValues.value[code] = value
|
|
|
-
|
|
|
+
|
|
|
console.log(`阀门 ${code} 状态切换成功: ${value ? '开启' : '关闭'}`)
|
|
|
} catch (err) {
|
|
|
console.error('阀门状态切换失败:', err)
|
|
|
@@ -293,7 +301,7 @@ const handleSensorValueChange = async (code, value) => {
|
|
|
try {
|
|
|
const res = await getMainSetParam(code)
|
|
|
const registerData = res.data
|
|
|
-
|
|
|
+
|
|
|
const data = {
|
|
|
protocol: registerData.protocolId,
|
|
|
unitId: registerData.unitId,
|
|
|
@@ -305,12 +313,12 @@ const handleSensorValueChange = async (code, value) => {
|
|
|
ipAddress: registerData.ipAddress,
|
|
|
port: registerData.port
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
await setValues(data)
|
|
|
-
|
|
|
+
|
|
|
// 更新设置值
|
|
|
sensorSetValues.value[code] = value
|
|
|
-
|
|
|
+
|
|
|
console.log(`传感器 ${code} 数值设置成功: ${value}`)
|
|
|
} catch (err) {
|
|
|
console.error('传感器数值设置失败:', err)
|
|
|
@@ -319,20 +327,149 @@ const handleSensorValueChange = async (code, value) => {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+
|
|
|
+// const lightList = [100, 0]// 灯控配置
|
|
|
+const lightList = [
|
|
|
+ '00', // 0 白色
|
|
|
+ '01', // 韫珠办公楼
|
|
|
+ '02', // 雪豹办公室
|
|
|
+ '03', // 分离模块
|
|
|
+ '04', // 吸附模块
|
|
|
+ '05', // 加料模块
|
|
|
+ '06', // 公共模块
|
|
|
+ '07',
|
|
|
+ '08',
|
|
|
+ '09',
|
|
|
+ '10',
|
|
|
+ '11',
|
|
|
+ '12'
|
|
|
+]// 灯控配置
|
|
|
+// 0 白色 1 绿色 2 红色 3橙色 4 蓝色 5紫色 6 彩虹色 7 青色 8黄色
|
|
|
+const lightColor = [
|
|
|
+ 'ffffff', // 0 白色
|
|
|
+ '00ff00', // 1 绿色 正常运行
|
|
|
+ 'ff0000', // 2 红色 严重异常/生产线停止
|
|
|
+ 'ffa500', // 3 橙色 告警/预警
|
|
|
+ '0000ff', // 4 蓝色 物料请求/投料
|
|
|
+ '800080', // 5 紫色 质检/审核
|
|
|
+ 'ff69b4', // 6 彩虹色 粉色识别为彩虹色 测试/实验
|
|
|
+ '00ffff', // 7 青色 待启动
|
|
|
+ 'ffff00' // 8 黄色 保养中
|
|
|
+]
|
|
|
+const lightState = ref(0)// 当前灯光状态
|
|
|
+
|
|
|
+// 6种情景场景定义,存储在lightScene数组中
|
|
|
+const lightScene = [
|
|
|
+ [
|
|
|
+ {id: 1, color: 6}, {id: 7, color: 6},
|
|
|
+ {id: 2, color: 6}, {id: 8, color: 6},
|
|
|
+ {id: 3, color: 6}, {id: 9, color: 6},
|
|
|
+ {id: 4, color: 6}, {id: 10, color: 6},
|
|
|
+ {id: 5, color: 6}, {id: 11, color: 6},
|
|
|
+ {id: 6, color: 6}, {id: 12, color: 6}
|
|
|
+ ],
|
|
|
+ // 情景1:分离模块罐体液体泄露
|
|
|
+ [
|
|
|
+ {id: 10, color: 2}, {id: 4, color: 2},
|
|
|
+ {id: 6, color: 2},
|
|
|
+ ],
|
|
|
+ // 情景2:料仓加料模块的物料短缺
|
|
|
+ [
|
|
|
+ {id: 12, color: 4}, {id: 5, color: 4},
|
|
|
+ ],
|
|
|
+ // 情景3:分离模块罐体1进行试验测试
|
|
|
+ [
|
|
|
+ {id: 10, color: 6}, {id: 3, color: 6},
|
|
|
+ ],
|
|
|
+ // 情景4:韫珠车间开关异常
|
|
|
+ [
|
|
|
+ {id: 8, color: 3}, {id: 1, color: 3},
|
|
|
+ ],
|
|
|
+ // 情景5:热交换模块液体开关检修维护
|
|
|
+ [
|
|
|
+ {id: 8, color: 7}, {id: 3, color: 7},
|
|
|
+ ],
|
|
|
+]
|
|
|
+function lightControl(e, colorItem) {
|
|
|
+ if (e == 0) {
|
|
|
+ setValues({
|
|
|
+ "dataType": "string",
|
|
|
+ "lengthOrQos": 1,
|
|
|
+ "unitId": 0,
|
|
|
+ "offsetOrIndex": 0,
|
|
|
+ "protocol": 3,
|
|
|
+ "data": [
|
|
|
+ '00 00 000000', "ygtx", "T!8dFz$kW3#pRgM2"
|
|
|
+ ],
|
|
|
+ "ipAddress": "222.243.138.146",
|
|
|
+ "port": 1883,
|
|
|
+ "resource": "yzkj/zkshi/esp32/12led",
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if (e == 99) {
|
|
|
+ console.log('99')
|
|
|
+ asyncLightControl(
|
|
|
+ ...lightScene[0]
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+ lightChange(e)
|
|
|
+}
|
|
|
+
|
|
|
+function lightSceneControl(e) {
|
|
|
+ asyncLightControl(
|
|
|
+ ...lightScene[e]
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+function asyncLightControl(...e) {
|
|
|
+ if (e.length === 0) return;
|
|
|
+ lightChange(e[0].id, e[0].color);
|
|
|
+ if (e.length > 1) {
|
|
|
+ setTimeout(() => {
|
|
|
+ asyncLightControl(...e.slice(1));
|
|
|
+ }, 100);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+function lightChange(i, colorItem) {
|
|
|
+ lightState.value = i
|
|
|
+ const light = lightList[i]
|
|
|
+ const color = colorItem == undefined ? lightColor[i<=8?i:i-8] : lightColor[colorItem]
|
|
|
+ const state = color == 'ff69b4' ? '02' : '01'
|
|
|
+
|
|
|
+ console.log('灯控', light, state, color)
|
|
|
+
|
|
|
+ const data = {
|
|
|
+ "dataType": "string",
|
|
|
+ "lengthOrQos": 1,
|
|
|
+ "unitId": 0,
|
|
|
+ "offsetOrIndex": 0,
|
|
|
+ "protocol": 3,
|
|
|
+ "data": [
|
|
|
+ light + " " + state + " " + color, "ygtx", "T!8dFz$kW3#pRgM2"
|
|
|
+ ],
|
|
|
+ "ipAddress": "222.243.138.146",
|
|
|
+ "port": 1883,
|
|
|
+ "resource": "yzkj/zkshi/esp32/12led",
|
|
|
+ }
|
|
|
+ setValues(data)
|
|
|
+}
|
|
|
+
|
|
|
</script>
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
.page {
|
|
|
- width: 1920px;
|
|
|
- height: 1080px;
|
|
|
+ width: 100%;
|
|
|
+ min-height: 100vh;
|
|
|
background-color: #0b172c;
|
|
|
|
|
|
.content_page {
|
|
|
position: relative;
|
|
|
width: 100%;
|
|
|
- height: 1000px;
|
|
|
+ min-height: 100vh;
|
|
|
padding: 20px;
|
|
|
- overflow-y: auto;
|
|
|
|
|
|
|
|
|
|
|
|
@@ -414,17 +551,17 @@ const handleSensorValueChange = async (code, value) => {
|
|
|
|
|
|
.sensor-input {
|
|
|
width: 120px;
|
|
|
-
|
|
|
+
|
|
|
:deep(.el-input-number__decrease),
|
|
|
:deep(.el-input-number__increase) {
|
|
|
background-color: #f5f7fa;
|
|
|
border-color: #dcdfe6;
|
|
|
-
|
|
|
+
|
|
|
&:hover {
|
|
|
background-color: #e4e7ed;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
:deep(.el-input__inner) {
|
|
|
text-align: center;
|
|
|
font-weight: 600;
|
|
|
@@ -457,7 +594,7 @@ const handleSensorValueChange = async (code, value) => {
|
|
|
font-weight: 600;
|
|
|
border-radius: 6px;
|
|
|
font-size: 14px;
|
|
|
-
|
|
|
+
|
|
|
&:hover {
|
|
|
transform: translateY(-1px);
|
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
|
@@ -483,24 +620,41 @@ const handleSensorValueChange = async (code, value) => {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- }
|
|
|
-}
|
|
|
|
|
|
-// 响应式设计
|
|
|
-@media (max-width: 1400px) {
|
|
|
- .device-grid {
|
|
|
- grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)) !important;
|
|
|
- }
|
|
|
-}
|
|
|
+ .light-control-card {
|
|
|
+ background: rgba(255, 255, 255, 0.95);
|
|
|
+ border-radius: 8px;
|
|
|
|
|
|
-@media (max-width: 768px) {
|
|
|
- .device-grid {
|
|
|
- grid-template-columns: 1fr !important;
|
|
|
- }
|
|
|
-
|
|
|
- .content_page {
|
|
|
- padding: 10px !important;
|
|
|
+ .card-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+
|
|
|
+ .card-title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .light-button-grid {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: repeat(3, 1fr);
|
|
|
+ gap: 12px;
|
|
|
+
|
|
|
+ .el-button+.el-button {
|
|
|
+ margin-left: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .light-button {
|
|
|
+ width: 100%;
|
|
|
+ font-size: 12px;
|
|
|
+ padding: 8px 4px;
|
|
|
+ line-height: 1.2;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
</style>
|