feat:【antd】【iot】添加设备状态和颜色映射功能,优化设备卡片样式

This commit is contained in:
haohao
2025-11-10 17:05:35 +08:00
parent e12ac0c31d
commit 35dc2960b8
2 changed files with 171 additions and 55 deletions

View File

@@ -2,8 +2,9 @@
import { onMounted, ref } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { getDictLabel } from '@vben/hooks';
import { getDictLabel, getDictObj } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons';
import { isValidColor, TinyColor } from '@vben/utils';
import {
Button,
@@ -52,6 +53,91 @@ const queryParams = ref({
pageSize: 12,
});
const DEFAULT_STATUS_MAP: Record<
'default' | number,
{ bgColor: string; borderColor: string; color: string; text: string }
> = {
[DeviceStateEnum.ONLINE]: {
text: '在线',
color: '#52c41a',
bgColor: '#f6ffed',
borderColor: '#b7eb8f',
},
[DeviceStateEnum.OFFLINE]: {
text: '离线',
color: '#faad14',
bgColor: '#fffbe6',
borderColor: '#ffe58f',
},
[DeviceStateEnum.INACTIVE]: {
text: '未激活',
color: '#ff4d4f',
bgColor: '#fff1f0',
borderColor: '#ffccc7',
},
default: {
text: '未知状态',
color: '#595959',
bgColor: '#fafafa',
borderColor: '#d9d9d9',
},
};
const COLOR_TYPE_PRESETS: Record<
string,
{ bgColor: string; borderColor: string; color: string }
> = {
success: {
color: '#52c41a',
bgColor: '#f6ffed',
borderColor: '#b7eb8f',
},
processing: {
color: '#1890ff',
bgColor: '#e6f7ff',
borderColor: '#91d5ff',
},
warning: {
color: '#faad14',
bgColor: '#fffbe6',
borderColor: '#ffe58f',
},
error: {
color: '#ff4d4f',
bgColor: '#fff1f0',
borderColor: '#ffccc7',
},
default: {
color: '#595959',
bgColor: '#fafafa',
borderColor: '#d9d9d9',
},
};
function normalizeColorType(colorType?: string) {
switch (colorType) {
case 'danger': {
return 'error';
}
case 'default':
case 'error':
case 'processing':
case 'success':
case 'warning': {
return colorType;
}
case 'info': {
return 'default';
}
case 'primary': {
return 'processing';
}
default: {
return 'default';
}
}
}
// 获取产品名称
function getProductName(productId: number) {
const product = props.products.find((p: any) => p.id === productId);
@@ -90,21 +176,41 @@ function getDeviceTypeColor(deviceType: number) {
}
// 获取设备状态信息
function getStatusInfo(state: number) {
if (state === DeviceStateEnum.ONLINE) {
function getStatusInfo(state: number | string | null | undefined) {
const parsedState = Number(state);
const hasNumericState = Number.isFinite(parsedState);
const fallback = hasNumericState
? DEFAULT_STATUS_MAP[parsedState] || DEFAULT_STATUS_MAP.default
: DEFAULT_STATUS_MAP.default;
const dict = getDictObj(
DICT_TYPE.IOT_DEVICE_STATE,
hasNumericState ? parsedState : state,
);
if (dict) {
if (!dict.colorType && !dict.cssClass) {
return {
...fallback,
text: dict.label || fallback.text,
};
}
const presetKey = normalizeColorType(dict.colorType);
if (isValidColor(dict.cssClass)) {
const baseColor = new TinyColor(dict.cssClass);
return {
text: dict.label || fallback.text,
color: baseColor.toHexString(),
bgColor: baseColor.clone().setAlpha(0.15).toRgbString(),
borderColor: baseColor.clone().lighten(30).toHexString(),
};
}
const preset = COLOR_TYPE_PRESETS[presetKey] || COLOR_TYPE_PRESETS.default;
return {
text: '在线',
color: '#52c41a',
bgColor: '#f6ffed',
borderColor: '#b7eb8f',
text: dict.label || fallback.text,
...preset,
};
}
return {
text: '未激活',
color: '#ff4d4f',
bgColor: '#fff1f0',
borderColor: '#ffccc7',
};
return fallback;
}
onMounted(() => {
@@ -290,21 +396,21 @@ defineExpose({
.device-card {
height: 100%;
overflow: hidden;
background: #fff;
border: 1px solid #f0f0f0;
background: hsl(var(--card) / 0.95);
border: 1px solid hsl(var(--border) / 0.6);
border-radius: 8px;
box-shadow:
0 1px 2px 0 rgb(0 0 0 / 3%),
0 1px 6px -1px rgb(0 0 0 / 2%),
0 2px 4px 0 rgb(0 0 0 / 2%);
0 1px 2px 0 hsl(var(--foreground) / 0.04),
0 1px 6px -1px hsl(var(--foreground) / 0.05),
0 2px 4px 0 hsl(var(--foreground) / 0.05);
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
&:hover {
border-color: #e6e6e6;
border-color: hsl(var(--border));
box-shadow:
0 1px 2px -2px rgb(0 0 0 / 16%),
0 3px 6px 0 rgb(0 0 0 / 12%),
0 5px 12px 4px rgb(0 0 0 / 9%);
0 1px 2px -2px hsl(var(--foreground) / 0.12),
0 3px 6px 0 hsl(var(--foreground) / 0.1),
0 5px 12px 4px hsl(var(--foreground) / 0.08);
transform: translateY(-4px);
}
@@ -367,7 +473,7 @@ defineExpose({
font-size: 16px;
font-weight: 600;
line-height: 24px;
color: #262626;
color: hsl(var(--foreground) / 0.9);
white-space: nowrap;
}
@@ -390,7 +496,7 @@ defineExpose({
.label {
flex-shrink: 0;
font-size: 13px;
color: #8c8c8c;
color: hsl(var(--foreground) / 0.6);
}
.value {
@@ -399,17 +505,17 @@ defineExpose({
overflow: hidden;
text-overflow: ellipsis;
font-size: 13px;
color: #262626;
color: hsl(var(--foreground) / 0.85);
text-align: right;
white-space: nowrap;
&.link {
color: #1890ff;
color: hsl(var(--primary));
cursor: pointer;
transition: color 0.2s;
&:hover {
color: #40a9ff;
color: hsl(var(--primary) / 0.85);
}
}
@@ -418,7 +524,7 @@ defineExpose({
'SF Mono', Monaco, Inconsolata, 'Fira Code', Consolas, monospace;
font-size: 12px;
font-weight: 500;
color: #595959;
color: hsl(var(--foreground) / 0.6);
}
}
}
@@ -431,7 +537,7 @@ defineExpose({
display: flex;
gap: 8px;
padding-top: 12px;
border-top: 1px solid #f5f5f5;
border-top: 1px solid hsl(var(--border) / 0.4);
.action-btn {
display: flex;
@@ -445,7 +551,7 @@ defineExpose({
font-weight: 400;
pointer-events: auto;
cursor: pointer;
border: 1px solid;
border: 1px solid transparent;
border-radius: 6px;
transition: all 0.2s;
@@ -454,52 +560,60 @@ defineExpose({
}
&.btn-edit {
color: #1890ff;
background: #e6f7ff;
border-color: #91d5ff;
color: hsl(var(--primary));
background: hsl(var(--primary) / 0.12);
border-color: hsl(var(--primary) / 0.25);
&:hover {
color: #fff;
background: #1890ff;
border-color: #1890ff;
color: hsl(var(--primary-foreground));
background: hsl(var(--primary));
border-color: hsl(var(--primary));
}
}
&.btn-view {
color: #faad14;
background: #fffbe6;
border-color: #ffe58f;
color: hsl(var(--warning));
background: hsl(var(--warning) / 0.12);
border-color: hsl(var(--warning) / 0.25);
&:hover {
color: #fff;
background: #faad14;
border-color: #faad14;
background: hsl(var(--warning));
border-color: hsl(var(--warning));
}
}
&.btn-data {
color: #722ed1;
background: #f9f0ff;
border-color: #d3adf7;
color: hsl(var(--accent-foreground));
background: color-mix(
in srgb,
hsl(var(--accent)) 40%,
hsl(var(--card)) 60%
);
border-color: color-mix(
in srgb,
hsl(var(--accent)) 55%,
transparent
);
&:hover {
color: #fff;
background: #722ed1;
border-color: #722ed1;
color: hsl(var(--accent-foreground));
background: hsl(var(--accent));
border-color: hsl(var(--accent));
}
}
&.btn-delete {
flex: 0 0 32px;
padding: 4px;
color: #ff4d4f;
background: #fff1f0;
border-color: #ffa39e;
color: hsl(var(--destructive));
background: hsl(var(--destructive) / 0.12);
border-color: hsl(var(--destructive) / 0.3);
&:hover {
color: #fff;
background: #ff4d4f;
border-color: #ff4d4f;
color: hsl(var(--destructive-foreground));
background: hsl(var(--destructive));
border-color: hsl(var(--destructive));
}
}
}

View File

@@ -553,15 +553,17 @@ defineExpose({ open }); // 提供 open 方法,用于打开弹窗
.toolbar-wrapper {
padding: 16px;
background-color: #fafafa;
background-color: hsl(var(--card) / 0.9);
border-radius: 8px;
border: 1px solid hsl(var(--border) / 0.6);
}
.chart-container,
.table-container {
padding: 16px;
background-color: #fff;
background-color: hsl(var(--card));
border-radius: 8px;
border: 1px solid hsl(var(--border) / 0.6);
}
}
</style>