采集设备九宫格优化

pull/1/head
liutao 1 month ago
parent a4af8de233
commit 39aa6ef858

@ -3865,7 +3865,7 @@ export default {
password: '密码',
topic: '订阅主题',
settingDialogTitle: '设备设置',
settingDialogTitle: '设置',
connect: '连接',
disconnect: '断开连接',
@ -4242,7 +4242,7 @@ export default {
username: '用户名',
password: '密码',
settingDialogTitle: '设备设置',
settingDialogTitle: '设置',
connect: '连接',
disconnect: '断开连接',

@ -204,66 +204,116 @@
class="grid-card"
@click="openDetailForm(item?.id,item?.deviceName)"
>
<!-- attributeDeviceId.value = row?.id
attributeDeviceName.value = row?.deviceName ?? ''-->
<!-- 设备状态指示 status-${item.deviceStatus} -->
<div class="status-indicator" :class="getStatusText(item.operatingStatus)"></div>
<!-- 设备图标 -->
<!-- <div class="card-icon">
<el-icon :size="32" :color="getEquipmentColor(item.type)">
<component :is="getEquipmentIcon(item.type)"/>
</el-icon>
</div>-->
<!-- 设备基本信息 -->
<div class="card-content">
<!-- 设备状态 -->
<div class="card-status">
<el-tag
:type="getStatusTag(item.operatingStatus)"
size="small"
class="status-tag"
>
<div class="device-card">
<div class="header">
<div class="device-icon">
<i>📊</i>
</div>
<div class="device-info">
<div class="device-name">{{ item.deviceName }}</div>
<div class="device-id">{{ item.deviceCode }}</div>
</div>
<div class="status-badge" :class="getStatusText(item.operatingStatus)">
<span class="status-dot" :class="getStatusText(item.operatingStatus)"></span>
{{ item.operatingStatus }}
</el-tag>
</div>
<div class="card-title"> {{ t('DataCollection.Device.deviceName') }}:{{ item.deviceName }}</div>
<div class="card-code"> {{ t('DataCollection.Device.deviceCode') }}:{{ item.deviceCode }}</div>
<div class="card-model"> {{ t('DataCollection.Device.collectionTime') }}:{{ item.collectionTime }}</div>
</div>
<div class="content">
<div class="info-row">
<div class="info-label">
<i>📡</i>
采集协议
</div>
<div class="info-value">
<dict-tag :type="DICT_TYPE.IOT_PROTOCOL" :value="item.protocol" />
</div>
</div>
<div class="info-row">
<div class="info-label">
<i></i>
采集时间
</div>
<div class="info-value">
{{ item.collectionTime }}
</div>
</div>
<!-- &lt;!&ndash; 运行信息 &ndash;&gt;
<div v-if="item.runningHours" class="card-running">
<span>运行: {{ formatRunningHours(item.runningHours) }}</span>
<div class="toggle-container" @click.stop>
<div class="toggle-label">
<i>🔘</i>
是否启用
</div>
<el-switch
:model-value="isDeviceEnabled(item)"
@change="(val) => handleDeviceEnableChange(item, val)"
/>
<!-- <label class="toggle-switch">
<input type="checkbox" v-model="item.operatingStatus" @change.stop="enabledChange(item,item?.id)" />
<span class="slider"></span>
</label>-->
</div>
</div>
&lt;!&ndash; 位置信息 &ndash;&gt;
<div v-if="item.location" class="card-location">
<el-icon :size="12">
<Location/>
</el-icon>
<span>{{ item.location }}</span>
</div>-->
<div class="footer" @click.stop>
<div class="action-icons">
<el-button class="action-btn btn-primary" @click.stop="openDetailForm(item?.id,item?.deviceName)" >
<i>📍</i>
{{ t('DataCollection.Device.attributeModuleName') }}
</el-button>
<el-button class="action-btn btn-success" @click.stop="openForm('setting', item?.id)" v-hasPermi="['iot:device:update']">
<i></i>
{{ t('DataCollection.Device.settingDialogTitle') }}
</el-button>
<el-tooltip
:content="t('action.copy')"
placement="top"
effect="dark"
>
<el-button
class="icon-btn"
link
type="primary"
@click="handleCopy(item?.id)"
v-hasPermi="['iot:device:create']"
>
📋
</el-button>
</el-tooltip>
<el-tooltip
:content="t('action.edit')"
placement="top"
effect="dark"
>
<el-button
link
type="primary"
@click.stop="handleEdit(item)"
v-hasPermi="['iot:device:update']"
>
</el-button>
</el-tooltip>
<el-tooltip
:content="t('DataCollection.Device.gridView')"
placement="top"
effect="dark"
>
<el-button class="icon-btn" @click.stop="changeTable">🏠</el-button>
</el-tooltip>
</div>
</div>
</div>
</div>
</div>
<Pagination
:total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList"/>
<!-- 分页 -->
<!-- <div class="simple-pagination">
<el-pagination
v-model:current-page="queryParams.pageNo"
v-model:page-size="queryParams.pageSize"
:page-sizes="[12, 24, 48, 96]"
layout="total, sizes, prev, pager, next"
:total="total"
@size-change="handleSizeChange"
@current-change="handlePageChange"
/>
</div>-->
</div>
@ -849,6 +899,13 @@ const toggleView = () => {
//
localStorage.setItem('equipment-view', currentView.value)
}
//
const changeTable = () => {
currentView.value = 'table'
ifShow.value=true;
//
localStorage.setItem('equipment-view', currentView.value)
}
/** 物联设备 列表 */
defineOptions({ name: 'Device' })
@ -859,6 +916,7 @@ const { t } = useI18n() // 国际化
const tableRef = ref()
const loading = ref(true) //
const showDetailForm =ref(true)
const list = ref<DeviceVO[]>([]) //
const total = ref(0) //
const queryParams = reactive({
@ -972,6 +1030,24 @@ const openDetailForm = (id?: number,deviceName?:string) => {
//formRef.value.open(id,deviceName)
}
const enabledChange = (row: DeviceVO, value: boolean) => {
showDetailForm.value=false
if (!row.id) return
const oldValue = (row as any).isEnable
;(row as any).isEnable = value
try {
DeviceApi.updateDeviceEnabled(row.id, value ? 'true' : 'false')
const name = (row as any).deviceName ?? (row as any).deviceCode ?? ''
const suffix = value ? '已启用' : '已停用'
if (name) {
message.success(`${name}${suffix}`)
} else {
message.success(suffix)
}
} catch {
;(row as any).is
}
}
/** 删除按钮操作 */
const buildIdsParam = (ids: number | number[]) => {
@ -1884,7 +1960,7 @@ const handleShowDeviceAlarmHistory = async () => {
position: relative;
border: 1px solid #ebeef5;
border-radius: 8px;
padding: 18px;
background-color: #fafafa;
cursor: pointer;
transition: all 0.3s ease;
@ -1940,62 +2016,6 @@ const handleShowDeviceAlarmHistory = async () => {
border-radius: 8px;
}
.card-content {
flex: 1;
min-width: 0;
.card-title {
font-size: 16px;
font-weight: 600;
color: #303133;
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-left: 50px;
}
.card-code {
font-size: 12px;
color: #909399;
margin-bottom: 2px;
margin-left: 50px;
}
.card-model {
font-size: 13px;
color: #606266;
margin-bottom: 8px;
margin-left: 50px;
}
.card-status {
margin-bottom: 8px;
.status-tag {
width: 50px;
justify-content: center;
}
}
.card-running {
font-size: 12px;
color: #606266;
margin-bottom: 4px;
}
.card-location {
display: flex;
align-items: center;
gap: 4px;
font-size: 12px;
color: #909399;
.el-icon {
color: #909399;
}
}
}
}
}
@ -2069,5 +2089,339 @@ const handleShowDeviceAlarmHistory = async () => {
}
}
}
.device-card {
width: 100%;
background-color: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
overflow: hidden;
}
.header {
display: flex;
align-items: center;
padding: 12px 16px;
border-bottom: 1px solid #eee;
}
.device-icon {
width: 40px;
height: 40px;
background-color: #f0f7ff;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
}
.device-info {
flex: 1;
}
.device-name {
font-size: 16px;
font-weight: 600;
color: #333;
margin-bottom: 4px;
}
.device-id {
font-size: 12px;
color: #999;
}
.content {
padding: 10px;
}
.info-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
border-bottom: 1px solid #f0f0f0;
}
.info-label {
display: flex;
align-items: center;
color: #666;
font-size: 14px;
}
.info-label i {
margin-right: 6px;
color: #1890ff;
}
.info-value {
color: #333;
font-size: 14px;
}
.toggle-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 0;
}
.toggle-label {
color: #666;
font-size: 14px;
}
.toggle-switch {
position: relative;
display: inline-block;
width: 44px;
height: 24px;
}
.toggle-switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: .4s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: .4s;
border-radius: 50%;
}
input:checked + .slider {
background-color: #1890ff;
}
input:checked + .slider:before {
transform: translateX(20px);
}
.footer {
display: flex;
justify-content: space-between;
padding: 16px 20px;
border-top: 1px solid #eee;
}
.action-btn {
display: flex;
align-items: center;
padding: 2px 4px;
border-radius: 6px;
font-size: 13px;
//cursor: pointer;
border: none;
background-color: transparent;
}
.btn-primary {
color: #1890ff;
border: 1px solid #1890ff;
}
.btn-success {
color: #28a745;
border: 1px solid #28a745;
}
.action-btn i {
margin-right: 6px;
font-size: 16px;
}
.action-icons {
display: flex;
}
.icon-btn {
width: 32px;
height: 32px;
border-radius: 6px;
display: flex;
align-items: center;
justify-content: center;
background-color: #f5f7fa;
color: #666;
cursor: pointer;
border: none;
}
/* 基础样式 */
.status-badge {
display: inline-flex;
align-items: center;
padding: 4px 10px;
border-radius: 20px;
font-size: 12px;
font-weight: 500;
line-height: 1;
white-space: nowrap;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
margin-right: 6px;
display: inline-block;
}
/* 在线状态 */
.status-online {
background-color: #e6f7e6;
color: #67c23a;
border: 1px solid rgba(40, 167, 69, 0.2);
}
.dot-online {
background-color: #28a745;
box-shadow: 0 0 4px rgba(40, 167, 69, 0.5);
}
/* 离线状态 */
.status-offline {
background-color: #f5f5f5;
color: #909399;
border: 1px solid rgba(108, 117, 125, 0.2);
}
.dot-offline {
background-color: #6c757d;
box-shadow: 0 0 4px rgba(108, 117, 125, 0.3);
}
/* 运行中 */
.status-running {
background-color: #67c23a;
color: white;
border: 1px solid rgba(24, 144, 255, 0.2);
}
.dot-running {
background-color: #67c23a;
animation: pulse 2s infinite;
box-shadow: 0 0 4px rgba(24, 144, 255, 0.5);
}
@keyframes pulse {
0% { opacity: 1; }
50% { opacity: 0.5; }
100% { opacity: 1; }
}
/* 已停止 */
.status-stopped {
background-color: #f5f5f5;
color: #999;
border: 1px solid rgba(153, 153, 153, 0.2);
}
.dot-stopped {
background-color: #999;
}
/* 待机中 */
.status-standby {
background-color: #f5f5f5;
color: #999;
border: 1px solid rgba(153, 153, 153, 0.2);
}
.dot-stopped {
background-color: #999;
}
/* 故障 */
.status-fault {
background-color: #fde8e8;
color: #f5222d;
border: 1px solid rgba(245, 34, 45, 0.2);
}
.dot-fault {
background-color: #f5222d;
box-shadow: 0 0 4px rgba(245, 34, 45, 0.5);
}
/* 报警 */
.status-alarm {
background-color: #fff7e6;
color: #fa8c16;
border: 1px solid rgba(250, 140, 22, 0.2);
}
.dot-alarm {
background-color: #fa8c16;
animation: blink 1s infinite;
box-shadow: 0 0 4px rgba(250, 140, 22, 0.5);
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.3; }
}
/* 维护中 */
.status-maintenance {
background-color: #f0f7ff;
color: #1890ff;
border: 1px solid rgba(24, 144, 255, 0.2);
}
.dot-maintenance {
background-color: #1890ff;
animation: maintenance 3s infinite;
}
@keyframes maintenance {
0%, 100% { transform: scale(1); }
50% { transform: scale(1.2); }
}
/* 未启用 */
.status-disabled {
background-color: #fafafa;
color: #bfbfbf;
border: 1px solid rgba(191, 191, 191, 0.2);
text-decoration: line-through;
}
.dot-disabled {
background-color: #bfbfbf;
}
/* 默认状态 */
.status-default {
background-color: #f5f5f5;
color: #666;
border: 1px solid rgba(102, 102, 102, 0.2);
}
.dot-default {
background-color: #666;
}
</style>

Loading…
Cancel
Save