Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
dwsproject
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
dws
dwsproject
Commits
b85dd09a
Commit
b85dd09a
authored
May 28, 2025
by
龚桂斌
Browse files
Options
Browse Files
Download
Plain Diff
合并分支 'qdry-project' 到 'master'
更新设备实时状态页面 查看合并请求
!16
parents
2edde48c
20663031
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
1314 additions
and
0 deletions
+1314
-0
qdry_project/static/src/view/equipment_status_qdry.html
+1314
-0
No files found.
qdry_project/static/src/view/equipment_status_qdry.html
0 → 100644
View file @
b85dd09a
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"UTF-8"
/>
<title>
设备实时看板
</title>
<meta
content=
"width=device-width,initial-scale=1.0, maximum-scale=1.0,user-scalable=0"
name=
"viewport"
/>
<!-- /roke_workstation_api/static/html/routing -->
<link
rel=
"stylesheet"
href=
"/roke_workstation_api/static/html/routing/element-ui/index.css"
/>
<script
src=
"/roke_workstation_api/static/html/routing/js/echarts.min.js"
></script>
<script
src=
"/roke_workstation_api/static/html/routing/js/moment.min.js"
></script>
<script
src=
"/roke_workstation_api/static/html/routing/js/vue.js"
></script>
<script
src=
"/roke_workstation_api/static/html/routing/js/axios.min.js"
></script>
<script
src=
"/roke_workstation_api/static/html/routing/element-ui/index.js"
></script>
</head>
<body
id=
"bodyId"
style=
"display: none"
>
<div
id=
"app"
v-loading
.
body
.
fullscreen
.
lock=
"loading"
ref=
"fullScreenElement"
>
<!-- 页面标题 -->
<div
class=
"dashboard-header"
>
<div
class=
"header-content"
>
<img
src=
"/roke_workstation_api/static/html/routing/image/header_bg.png"
class=
"header-bg"
alt=
"header background"
/>
<span
class=
"header-text"
>
设备实时看板
</span>
<span
class=
"hintText"
>
<i
style=
"font-size: 30px"
@
click=
"toggleFullscreen"
:class=
"isFullscreen ? 'el-icon-close' : 'el-icon-full-screen'"
></i>
<span>
[[currentTime ]]
</span>
</span>
</div>
</div>
<!-- 设备状态卡片区域 - 新设计 -->
<div
v-for=
"category in deviceList"
:key=
"category.category_name"
>
<div
class=
"device-category-name-container"
>
[[category.category_name]]
</div>
<div
class=
"device-cards-container"
>
<div
v-for=
"(device, index) in category.data"
:key=
"index"
class=
"device-card new-design"
:class=
"getCardClass(device.status)"
>
<!-- 设备名称 - 左上角 -->
<div
class=
"device-name"
>
[[truncateText(device.name, 12)]]
</div>
<!-- 设备状态 - 右上角 -->
<div
class=
"device-status-tag"
:class=
"getStatusClass(device.status)"
>
[[getStatusText(device.status)]]
</div>
<!-- 设备状态水波纹 - 中间 -->
<div
class=
"device-wave-container"
>
<!-- v-if="device.status === 'running' || device.status === 'waiting'" -->
<div
class=
"oee-text"
>
OEE
</div>
<!-- <div class="err-text" v-if="device.status === 'error'" :class="getErrClass(device.status)">
ERR
</div>
<div class="off-text" v-if="device.status === 'off'" :class="getOffClass(device.status)">
OFF
</div> -->
<!-- v-if="device.status === 'running' || device.status === 'waiting'" -->
<div
class=
"percentage-text"
>
[[ device.percentage > 100 ? 100 : device.percentage ]]%
</div>
<!-- 圆形容器 -->
<div
class=
"circle-container"
:class=
"getBorderClass(device.status)"
>
<!-- 水波纹效果 - 通过内联样式直接控制高度百分比 -->
<div
class=
"water-wave"
:class=
"getWaveClass(device.status)"
:style=
"{
'height': getWaveHeight(device.status, device.percentage) + '%',
'bottom': '0',
'left': '0',
'position': 'absolute',
'width': '100%',
'overflow': 'hidden'
}"
>
<div
class=
"water-wave-content"
>
<div
class=
"water-wave-ripple"
></div>
</div>
</div>
</div>
</div>
<!-- 设备状态信息 - 底部 -->
<div
class=
"device-status-info"
>
<span>
已持续 [[device.duration]]
</span>
</div>
<div
style=
"width: 100%; display: flex;margin-left: 13px;"
>
<div
class=
"flxe_sty"
>
<div
class=
"flxe_label_sty"
style=
" background-color: #00aa00;"
>
开工
</div>
[[ device.run_seconds ]]
</div>
<div
class=
"flxe_sty"
>
<div
class=
"flxe_label_sty"
style=
" background-color: #ffaa00;"
>
加工
</div>
[[ device.green_seconds ]]
</div>
</div>
<div
style=
"width: 100%;display: flex;margin-left: 13px;"
>
<div
class=
"flxe_sty"
>
<div
class=
"flxe_label_sty"
style=
" background-color: #797979;"
>
空闲
</div>
[[ device.yellow_seconds ]]
</div>
<div
class=
"flxe_sty"
>
<div
class=
"flxe_label_sty"
style=
" background-color: #f87171;"
>
故障
</div>
[[ device.red_seconds ]]
</div>
</div>
</div>
</div>
</div>
<!-- 底部状态图 -->
<div
class=
"status-chart glass-effect"
v-if=
"false"
>
<!-- 这部分保留但不显示,如果将来需要可以启用 -->
<div
class=
"section-title"
>
<span
class=
"title-text"
>
设备运行状态统计
</span>
<div
class=
"title-decoration"
></div>
</div>
<div
class=
"chart-header"
>
<el-select
v-model=
"selectedDevice"
placeholder=
"请选择设备"
size=
"medium"
@
change=
"selDeviceChange"
>
<el-option
v-for=
"item in deviceList"
:label=
"item.name"
:value=
"item.id"
:key=
"item.id"
>
</el-option>
</el-select>
<h4>
近10天设备运行状态统计
</h4>
</div>
<div
class=
"chart-container"
>
<!-- 图例区域 -->
<div
class=
"flex_legend"
>
<div
class=
"single_sty"
>
<div
style=
"background-color: red"
></div>
<span>
故障
</span>
</div>
<div
class=
"single_sty"
>
<div
style=
"background-color: yellow"
></div>
<span>
等待
</span>
</div>
<div
class=
"single_sty"
>
<div
style=
"background-color: green"
></div>
<span>
运行
</span>
</div>
<div
class=
"single_sty"
>
<div
style=
"background-color: gray"
></div>
<span>
关机
</span>
</div>
</div>
<!-- Y轴刻度 -->
<div
class=
"y-axis"
>
<span
v-for=
"(value, index) in yAxisValues"
:key=
"index"
class=
"y-axis-label"
>
[[value]]
<span
class=
"unit"
>
h
</span>
</span>
</div>
<!-- 图表主体区域 -->
<div
class=
"chart-content"
>
<!-- 网格区域 -->
<div
class=
"grid-area"
>
<!-- 背景网格 -->
<div
class=
""
>
<div
class=
"horizontal-lines"
>
<div
class=
"horizontal-line"
v-for=
"(value, index) in yAxisValues"
:key=
"index"
></div>
</div>
</div>
<!-- 柱状图组 -->
<div
class=
"columns-container"
>
<div
class=
"column-group"
v-for=
"(item, dayIndex) in pickingOrderList"
:key=
"dayIndex"
>
<div
class=
"column-stack"
>
<div
v-for=
"(segment, stackIndex) in item.data"
:key=
"stackIndex"
class=
"column-segment"
:style=
"{
'height': calculateHeight(segment.value) + '%',
'background-color': segment.color,
'margin-top': '0px'
}"
></div>
</div>
</div>
</div>
</div>
</div>
<!-- X轴 -->
<div
class=
"x-axis"
>
<span
v-for=
"(item, index) in pickingOrderList"
:key=
"index"
class=
"x-axis-label"
>
[[item.name_format]]
</span>
</div>
</div>
</div>
</div>
</body>
<script>
// 发送消息给父页面(关闭odoo的菜单弹窗)
document
.
addEventListener
(
"click"
,
()
=>
{
window
.
parent
.
postMessage
(
"hidePopover"
,
"*"
);
});
let
vue
=
new
Vue
({
el
:
"#app"
,
delimiters
:
[
"[["
,
"]]"
],
// 替换原本vue的[[ key ]]取值方式(与odoo使用的jinja2冲突问题)
data
()
{
return
{
isFullscreen
:
false
,
// 全屏状态
currentTime
:
null
,
// 当前时间
timer
:
null
,
// 定时器
windowHeight
:
window
.
innerHeight
,
// 窗口高度
// dwsURL: "https://workstation.rokeris.com", // 基地址
dwsURL
:
""
,
// 基地址
baseURL
:
"https://dws-platform.xbg.rokeris.com/dev-api"
,
// 基地址
loading
:
false
,
// 全局加载效果
deviceList
:
[],
// 设备列表
selectedDevice
:
null
,
// 选中的设备
yAxisValues
:
[
"24"
,
"20"
,
"16"
,
"12"
,
"8"
,
"4"
,
"0"
],
// Y轴刻度值
pickingOrderList
:
[],
// 拣选单列表
dateList
:
[],
// 日期列表
start_time
:
""
,
// 开始时间
end_time
:
""
,
// 结束时间
refreshInterval
:
null
,
// 定时刷新计时器
factoryCode
:
""
,
// 公司CODE
allEquipmentData
:
[],
// 所有已绑定设备数据
};
},
created
()
{
if
(
this
.
getUrlSearch
(
"factory_code"
))
{
this
.
factoryCode
=
this
.
getUrlSearch
(
"factory_code"
);
//截取url后面的参数
}
this
.
initCurrentTimeFn
()
},
computed
:
{
// 选中设备的信息
selDeviceInfo
()
{
return
this
.
deviceList
.
find
((
item
)
=>
item
.
id
==
this
.
selectedDevice
);
},
},
async
mounted
()
{
window
.
addEventListener
(
"resize"
,
this
.
handleResize
);
this
.
$nextTick
(()
=>
{
document
.
getElementById
(
"bodyId"
).
style
.
display
=
"block"
;
});
// // 先加载测试数据以便查看效果
// this.initMockData();
// 初始化数据 - 实际使用时取消这行的注释
await
this
.
initData
();
// 获取最后指定天数的日期
this
.
dateList
=
this
.
getLastAssignDays
();
// 设置定时刷新(每分钟刷新一次,静默模式)
this
.
refreshInterval
=
setInterval
(()
=>
{
this
.
initData
(
true
);
// 传入true表示静默刷新,不显示加载提示
},
60000
);
// 设置设备信息的title属性
this
.
$nextTick
(()
=>
{
document
.
querySelectorAll
(
".device-info"
).
forEach
((
el
)
=>
{
el
.
title
=
el
.
dataset
.
fullTitle
;
});
});
},
updated
()
{
// 在数据更新后也设置title属性
this
.
$nextTick
(()
=>
{
document
.
querySelectorAll
(
".device-info"
).
forEach
((
el
)
=>
{
el
.
title
=
el
.
dataset
.
fullTitle
;
});
});
},
beforeDestroy
()
{
// 清除定时器
if
(
this
.
refreshInterval
)
{
clearInterval
(
this
.
refreshInterval
);
}
if
(
this
.
timer
)
{
clearInterval
(
this
.
timer
)
}
// 移除事件监听
window
.
removeEventListener
(
"resize"
,
this
.
handleResize
);
},
methods
:
{
// 全屏icon点击事件
toggleFullscreen
:
function
()
{
if
(
!
this
.
isFullscreen
)
{
this
.
enterFullScreen
();
}
else
{
this
.
exitFullScreen
();
}
},
// 全屏方法
enterFullScreen
:
function
()
{
// 获取需要全屏的元素
var
elem
=
this
.
$refs
.
fullScreenElement
;
if
(
elem
&&
elem
.
requestFullscreen
)
{
elem
.
requestFullscreen
();
}
else
if
(
elem
&&
elem
.
mozRequestFullScreen
)
{
// Firefox
elem
.
mozRequestFullScreen
();
}
else
if
(
elem
&&
elem
.
webkitRequestFullscreen
)
{
// Chrome, Safari & Opera
elem
.
webkitRequestFullscreen
();
}
else
if
(
elem
&&
elem
.
msRequestFullscreen
)
{
// IE/Edge
elem
.
msRequestFullscreen
();
}
this
.
isFullscreen
=
true
;
},
exitFullScreen
:
function
()
{
if
(
document
.
exitFullscreen
)
{
document
.
exitFullscreen
();
}
else
if
(
document
.
mozCancelFullScreen
)
{
// Firefox
document
.
mozCancelFullScreen
();
}
else
if
(
document
.
webkitExitFullscreen
)
{
// Chrome, Safari and Opera
document
.
webkitExitFullscreen
();
}
else
if
(
document
.
msExitFullscreen
)
{
// IE/Edge
document
.
msExitFullscreen
();
}
this
.
isFullscreen
=
false
;
},
// 初始化当前时间
initCurrentTimeFn
()
{
this
.
timer
=
setInterval
(()
=>
{
const
now
=
new
Date
();
const
year
=
now
.
getFullYear
();
const
month
=
String
(
now
.
getMonth
()
+
1
).
padStart
(
2
,
'0'
);
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'
);
this
.
currentTime
=
`
${
year
}
-
${
month
}
-
${
day
}
${
hours
}
:
${
minutes
}
:
${
seconds
}
`
},
1000
)
},
// 通过网址跳转过来的页面,截取后面的参数
getUrlSearch
(
name
)
{
// 未传参,返回空
if
(
!
name
)
return
""
;
// 查询参数:先通过search取值,如果取不到就通过hash来取
var
after
=
window
.
location
.
search
;
after
=
after
.
substr
(
1
)
||
window
.
location
.
hash
.
split
(
"?"
)[
1
];
// 地址栏URL没有查询参数,返回空
if
(
!
after
)
return
null
;
// 如果查询参数中没有"name",返回空
if
(
after
.
indexOf
(
name
)
===
-
1
)
return
null
;
var
reg
=
new
RegExp
(
"(^|&)"
+
name
+
"=([^&]*)(&|$)"
);
// 当地址栏参数存在中文时,需要解码,不然会乱码
var
r
=
decodeURI
(
after
).
match
(
reg
);
// 如果url中"name"没有值,返回空
if
(
!
r
)
return
null
;
return
r
[
2
];
},
// 初始化数据
async
initData
(
silent
=
false
)
{
try
{
// 只有在非静默模式下才显示加载提示
if
(
!
silent
)
{
this
.
loading
=
true
;
}
// 并行请求设备计划运行时间和所有已绑定设备数据
const
[
planTimeResult
,
allEquipmentResult
]
=
await
Promise
.
all
([
this
.
getDevicePlanTime
(),
this
.
getAllEquipmentData
(),
]);
}
catch
(
error
)
{
// console.error("初始化数据出错:", error);
// 只有在非静默模式下才显示错误提示
if
(
!
silent
)
{
this
.
$message
.
error
(
"初始化数据出错: "
+
(
error
.
message
||
"未知错误"
));
}
// 如果接口请求失败,使用模拟数据
this
.
initMockData
();
}
finally
{
// 关闭加载提示
if
(
!
silent
)
{
this
.
loading
=
false
;
}
}
},
// 获取设备计划运行时间
async
getDevicePlanTime
()
{
try
{
// 发送请求获取计划运行时间
const
response
=
await
axios
({
method
:
"post"
,
url
:
this
.
dwsURL
+
"/roke/workstation/equipment/get_plan_time"
,
data
:
{},
headers
:
{
"Content-Type"
:
"application/json"
},
});
// 处理JSON-RPC格式的响应
if
(
response
.
data
&&
response
.
data
.
result
&&
response
.
data
.
result
.
code
===
0
)
{
// 获取计划运行时间数据
const
planTimeList
=
response
.
data
.
result
.
data
||
{}
if
(
this
.
factoryCode
)
{
// 调用设备状态接口
await
this
.
getDeviceStateList
(
planTimeList
);
}
else
{
await
this
.
getFactoryCode
(
planTimeList
);
}
return
planTimeList
;
}
else
{
const
errorMsg
=
response
.
data
.
result
?
response
.
data
.
result
.
message
:
"获取计划运行时间失败"
;
throw
new
Error
(
errorMsg
);
}
}
catch
(
error
)
{
// console.error("获取计划运行时间出错:", error);
throw
error
;
}
},
// 获取所有已绑定设备数据
async
getAllEquipmentData
()
{
try
{
// 发送请求获取所有已绑定设备数据
const
response
=
await
axios
({
method
:
"post"
,
url
:
this
.
dwsURL
+
"/roke/workstation/equipment/get_equipment_data"
,
data
:
{},
headers
:
{
"Content-Type"
:
"application/json"
,
},
});
// 处理JSON-RPC格式的响应
if
(
response
.
data
&&
response
.
data
.
result
&&
response
.
data
.
result
.
code
===
0
)
{
// 存储所有设备数据
this
.
allEquipmentData
=
response
.
data
.
result
.
data
||
[]
return
this
.
allEquipmentData
;
}
else
{
const
errorMsg
=
response
.
data
.
result
?
response
.
data
.
result
.
message
:
"获取所有已绑定设备数据失败"
;
throw
new
Error
(
errorMsg
);
}
}
catch
(
error
)
{
// console.error("获取所有已绑定设备数据出错:", error);
throw
error
;
}
},
// 获取设备状态列表
async
getDeviceStateList
(
planTimeList
)
{
try
{
// 使用CORS代理
// 发送请求获取设备状态
const
response
=
await
axios
({
method
:
"POST"
,
url
:
this
.
baseURL
+
"/public/device_efficiency/device_state_list"
,
data
:
{
factory_code
:
this
.
factoryCode
,
plan_time_list
:
planTimeList
,
},
headers
:
{
"Content-Type"
:
"application/json"
,
},
});
// 处理响应
if
(
response
.
data
&&
response
.
data
.
success
)
{
// 处理设备状态数据
this
.
processDeviceStateData
(
response
.
data
.
data
);
}
else
{
throw
new
Error
(
response
.
data
.
msg
||
"获取设备状态失败"
);
}
}
catch
(
error
)
{
// console.error("获取设备状态出错:", error);
throw
error
;
}
},
// 获取factoryCode
async
getFactoryCode
(
planTimeList
)
{
try
{
// 使用CORS代理
// 发送请求获取设备状态
const
response
=
await
axios
({
method
:
"post"
,
url
:
this
.
dwsURL
+
"/roke/workstation/db_uuid/get"
,
data
:
{},
headers
:
{
"Content-Type"
:
"application/json"
},
});
// 处理JSON-RPC格式的响应
if
(
response
.
data
&&
response
.
data
.
result
&&
response
.
data
.
result
.
code
===
0
)
{
// 获取计划运行时间数据
this
.
factoryCode
=
response
.
data
.
result
.
data
||
""
;
// 调用设备状态接口
await
this
.
getDeviceStateList
(
planTimeList
);
}
else
{
const
errorMsg
=
response
.
data
.
result
?
response
.
data
.
result
.
message
:
"获取账套db_uuid失败"
;
throw
new
Error
(
errorMsg
);
}
}
catch
(
error
)
{
// console.error("获取账套db_uuid失败:", error);
throw
error
;
}
},
// 处理设备状态数据
processDeviceStateData
(
deviceStateData
)
{
if
(
!
deviceStateData
||
!
Array
.
isArray
(
deviceStateData
))
{
return
;
}
// 将API返回的数据转换为页面所需的格式
let
list
=
deviceStateData
.
map
((
device
)
=>
{
// 根据API返回的状态确定前端显示的状态
let
status
=
"off"
;
if
(
device
.
state
===
"green"
)
{
status
=
"running"
;
}
else
if
(
device
.
state
===
"yellow"
)
{
status
=
"waiting"
;
}
else
if
(
device
.
state
===
"red"
)
{
status
=
"error"
;
}
else
if
(
device
.
state
===
"gray"
)
{
status
=
"off"
;
}
// 计算持续时间的显示格式
let
durationText
=
"0"
if
(
device
.
duration_hours
!==
undefined
)
{
durationText
=
this
.
formatTime
(
Number
(
device
.
duration_hours
*
3600
))
}
let
run_seconds
=
"0"
if
(
device
.
run_seconds
!==
undefined
)
run_seconds
=
this
.
formatTime
(
Number
(
device
.
run_seconds
))
let
green_seconds
=
"0"
if
(
device
.
green_seconds
!==
undefined
)
green_seconds
=
this
.
formatTime
(
Number
(
device
.
green_seconds
))
let
yellow_seconds
=
"0"
if
(
device
.
yellow_seconds
!==
undefined
)
yellow_seconds
=
this
.
formatTime
(
Number
(
device
.
yellow_seconds
))
let
red_seconds
=
"0"
if
(
device
.
red_seconds
!==
undefined
)
yellow_seconds
=
this
.
formatTime
(
Number
(
device
.
yellow_seconds
))
// 计算利用率百分比,确保有效值
const
percentage
=
device
.
utilization_rate
!==
undefined
?
Math
.
round
(
device
.
utilization_rate
)
:
0
// 从所有设备列表中获取准确的设备名称
let
deviceName
=
device
.
name
||
device
.
code
// 默认使用API返回的名称或编码
let
categoryName
=
null
// 在所有设备列表中查找匹配的设备
if
(
this
.
allEquipmentData
&&
this
.
allEquipmentData
.
length
>
0
)
{
const
matchedDevice
=
this
.
allEquipmentData
.
find
(
(
equip
)
=>
equip
.
code
===
device
.
code
)
// 如果找到匹配的设备,使用其名称
if
(
matchedDevice
&&
matchedDevice
.
name
)
{
deviceName
=
device
.
name
?
matchedDevice
.
name
:
device
.
code
categoryName
=
matchedDevice
.
category_name
}
}
return
{
id
:
device
.
code
,
code
:
device
.
code
,
name
:
deviceName
,
status
:
status
,
percentage
:
percentage
,
duration
:
durationText
,
run_seconds
:
run_seconds
,
green_seconds
:
green_seconds
,
yellow_seconds
:
yellow_seconds
,
red_seconds
:
red_seconds
,
category_name
:
categoryName
}
});
// 按category_name分组并组装成指定结构
const
grouped
=
{};
list
.
forEach
(
item
=>
{
const
cname
=
item
.
category_name
||
'未分类'
;
if
(
!
grouped
[
cname
])
grouped
[
cname
]
=
[];
grouped
[
cname
].
push
(
item
);
});
// 组装成 [{category_name:'', data:[]}, ...] 并排序
const
result
=
Object
.
keys
(
grouped
).
map
(
category_name
=>
{
// 每组内部按percentage降序排序
const
data
=
grouped
[
category_name
].
sort
((
a
,
b
)
=>
b
.
percentage
-
a
.
percentage
);
return
{
category_name
,
data
};
});
console
.
log
(
result
)
this
.
deviceList
=
result
},
formatTime
(
seconds
)
{
if
(
seconds
<
60
)
{
return
`0min`
;
// 不足 1 分钟显示 0min
}
else
if
(
seconds
<
3600
)
{
const
minutes
=
Math
.
floor
(
seconds
/
60
);
// 转换为分钟
return
`
${
minutes
}
min`
;
// 显示分钟
}
else
if
(
seconds
<
86400
)
{
// 小于 1 天
const
hours
=
Math
.
floor
(
seconds
/
3600
);
// 转换为小时
return
`
${
hours
}
h`
;
// 只返回小时
}
else
{
const
days
=
Math
.
floor
(
seconds
/
86400
);
// 转换为天数
return
`
${
days
}
d`
;
// 只返回天
}
},
// 初始化模拟数据 (保留原有的模拟数据方法,作为备用)
initMockData
()
{
// 模拟设备数据 - 添加不同状态和明显不同百分比的测试数据
this
.
deviceList
=
[
// {
// id: "device001",
// code: "device001",
// name: "设备名称1",
// status: "running", // 运行中
// percentage: 90, // 非常高的百分比
// duration: "2h15m",
// },
// {
// id: "device002",
// code: "device002",
// name: "设备名称2",
// status: "error", // 故障中
// percentage: 50, // 中等百分比
// duration: "4h30m",
// },
// {
// id: "device003",
// code: "device003",
// name: "设备名称3",
// status: "waiting", // 等待中
// percentage: 20, // 非常低的百分比
// duration: "1h45m",
// },
// {
// id: "device004",
// code: "device004",
// name: "设备名称4",
// status: "off", // 停机中
// percentage: 50, // 中等百分比
// duration: "8h20m",
// },
];
// 添加测试日志
console
.
log
(
"测试数据初始化完成,设备列表:"
,
this
.
deviceList
);
},
// 获取卡片的CSS类名
getCardClass
(
status
)
{
switch
(
status
)
{
case
"running"
:
return
"card-running"
;
case
"waiting"
:
return
"card-waiting"
;
case
"error"
:
return
"card-error"
;
case
"off"
:
default
:
return
"card-off"
;
}
},
// 获取边框的CSS类名
getBorderClass
(
status
)
{
switch
(
status
)
{
case
"running"
:
return
"border-running"
;
case
"waiting"
:
return
"border-waiting"
;
case
"error"
:
return
"border-error"
;
case
"off"
:
default
:
return
"border-off"
;
}
},
// 获取设备状态对应的CSS类名
getStatusClass
(
status
)
{
switch
(
status
)
{
case
"running"
:
return
"status-running"
;
case
"waiting"
:
return
"status-waiting"
;
case
"error"
:
return
"status-error"
;
case
"off"
:
default
:
return
"status-off"
;
}
},
// 获取波浪效果的类名
getWaveClass
(
status
)
{
switch
(
status
)
{
case
"running"
:
return
"wave-running"
;
case
"waiting"
:
return
"wave-waiting"
;
case
"error"
:
return
"wave-error"
;
case
"off"
:
default
:
return
"wave-off"
;
}
},
// 获取波浪高度
getWaveHeight
(
status
,
percentage
)
{
// 将百分比限制在10%-100%之间,确保即使是低百分比也有一定的水位可见
let
height
=
percentage
;
// 如果是故障或停机状态,固定显示50%
// if (status === "error" || status === "off") {
// height = 50;
// }
// 确保最小高度为10%,最大为100%
height
=
Math
.
min
(
Math
.
max
(
height
,
10
),
100
);
return
height
;
},
// 获取设备状态对应的文本
getStatusText
(
status
)
{
switch
(
status
)
{
case
"running"
:
return
"运行中"
;
case
"waiting"
:
return
"等待中"
;
case
"error"
:
return
"故障中"
;
case
"off"
:
default
:
return
"停机中"
;
}
},
// 处理小数位数方法
toFixedHandle
(
value
,
num
=
4
)
{
if
(
value
)
{
let
strValue
=
String
(
value
);
if
(
strValue
.
split
(
"."
).
length
>
1
||
strValue
.
split
(
"."
)[
1
]?.
length
>
num
)
{
strValue
=
Number
(
strValue
).
toFixed
(
num
);
}
return
Number
(
strValue
);
}
else
{
return
0
;
}
},
// 计算高度百分比
calculateHeight
(
hours
)
{
// 24小时对应整个高度(100%)
// 每4小时对应一个刻度区间,总共6个区间
// 计算每小时占的百分比:100% / 24 ≈ 4.167%
const
heightPerHour
=
100
/
24
;
const
percentage
=
hours
*
heightPerHour
;
// 确保高度在0-100%之间
return
Math
.
min
(
Math
.
max
(
percentage
,
0
),
100
);
},
// 获取最后指定天数的日期
getLastAssignDays
(
num
=
10
)
{
const
dates
=
[];
for
(
let
i
=
num
-
1
;
i
>=
0
;
i
--
)
{
const
date
=
new
Date
();
date
.
setDate
(
date
.
getDate
()
-
i
);
const
year
=
date
.
getFullYear
();
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
day
=
String
(
date
.
getDate
()).
padStart
(
2
,
"0"
);
dates
.
push
(
`
${
year
}
-
${
month
}
-
${
day
}
`
);
}
this
.
start_time
=
dates
[
0
];
// 第一天
this
.
end_time
=
dates
[
dates
.
length
-
1
];
// 最后一天
return
dates
;
},
// 处理窗口大小变化
handleResize
()
{
// 窗口大小变化时的处理逻辑
},
// 文本截断方法
truncateText
(
text
,
maxLength
)
{
if
(
!
text
)
return
""
;
if
(
text
.
length
<=
maxLength
)
return
text
;
return
text
.
substring
(
0
,
maxLength
)
+
"..."
;
},
// 显示完整标题
showFullTitle
(
event
,
device
)
{
const
fullTitle
=
device
.
name
+
" | "
+
device
.
code
;
event
.
target
.
setAttribute
(
"title"
,
fullTitle
);
},
// 获取ERR文字的CSS类名
getErrClass
(
status
)
{
if
(
status
===
"error"
)
{
return
"err-error"
;
}
return
""
;
},
// 获取OFF文字的CSS类名
getOffClass
(
status
)
{
if
(
status
===
"off"
)
{
return
"off-status"
;
}
return
""
;
},
},
});
</script>
<style>
/* 基础样式 */
*
{
margin
:
0
;
padding
:
0
;
box-sizing
:
border-box
;
}
body
{
color
:
#fff
;
overflow
:
hidden
;
}
#app
{
width
:
100vw
;
height
:
100vh
;
padding
:
20px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
10px
;
overflow-y
:
auto
;
background
:
url("/roke_workstation_api/static/html/routing/image/bg-ok.png")
no-repeat
center
center
fixed
;
background-size
:
cover
;
}
/* 玻璃态效果 */
.glass-effect
{
background
:
rgba
(
22
,
41
,
60
,
0.8
);
backdrop-filter
:
blur
(
10px
);
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.1
);
border-radius
:
15px
;
box-shadow
:
0
8px
32px
0
rgba
(
0
,
0
,
0
,
0.37
);
}
/* 标题样式 */
.dashboard-header
{
width
:
100vw
;
margin
:
-20px
-20px
10px
-20px
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
}
.header-content
{
width
:
100%
;
position
:
relative
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
}
.header-bg
{
width
:
100%
;
height
:
auto
;
}
.header-text
{
position
:
absolute
;
font-size
:
28px
;
font-weight
:
450
;
color
:
#fff
;
text-shadow
:
0
0
10px
rgba
(
0
,
195
,
255
,
0.5
);
z-index
:
1
;
}
.hintText
{
position
:
absolute
;
font-size
:
14px
;
bottom
:
-5px
;
right
:
30px
;
color
:
rgba
(
255
,
255
,
255
,
0.85
);
display
:
-webkit-box
;
display
:
-webkit-flex
;
display
:
-moz-box
;
display
:
-ms-flexbox
;
display
:
flex
;
-webkit-box-align
:
center
;
-webkit-align-items
:
center
;
-moz-align-items
:
center
;
-ms-flex-align
:
center
;
align-items
:
center
;
}
.hintText
>*
{
margin-left
:
10px
;
}
.hintText
>*
:first-child
{
margin-left
:
0
;
}
.device-category-name-container
{
width
:
100%
;
padding
:
0px
15px
;
font-weight
:
bold
;
font-size
:
20px
;
}
/* 设备卡片容器 */
.device-cards-container
{
display
:
grid
;
grid-template-columns
:
repeat
(
auto-fill
,
minmax
(
180px
,
1
fr
));
gap
:
20px
;
padding
:
15px
;
}
/* 新设计设备卡片样式 */
.device-card.new-design
{
position
:
relative
;
padding
:
5px
10px
10px
;
display
:
flex
;
flex-direction
:
column
;
align-items
:
center
;
min-height
:
180px
;
border-radius
:
12px
;
transition
:
all
0.3s
ease
;
background-color
:
rgba
(
20
,
22
,
28
,
0.95
);
/* 更深的背景色 */
}
/* 卡片颜色和阴影 - 调整为适度柔和的阴影效果 */
.card-running
{
border
:
none
;
box-shadow
:
0
0
0
1px
rgba
(
78
,
198
,
138
,
0.5
),
5px
5px
2px
0
rgba
(
78
,
198
,
138
,
0.7
),
5px
5px
15px
2px
rgba
(
78
,
198
,
138
,
0.5
);
}
.card-waiting
{
border
:
none
;
box-shadow
:
0
0
0
1px
rgba
(
235
,
186
,
22
,
0.5
),
5px
5px
2px
0
rgba
(
235
,
186
,
22
,
0.7
),
5px
5px
15px
2px
rgba
(
235
,
186
,
22
,
0.5
);
}
.card-error
{
border
:
none
;
box-shadow
:
0
0
0
1px
rgba
(
235
,
86
,
86
,
0.5
),
5px
5px
2px
0
rgba
(
235
,
86
,
86
,
0.7
),
5px
5px
15px
2px
rgba
(
235
,
86
,
86
,
0.5
);
}
.card-off
{
border
:
none
;
box-shadow
:
0
0
0
1px
rgba
(
144
,
147
,
153
,
0.5
),
5px
5px
2px
0
rgba
(
144
,
147
,
153
,
0.7
),
5px
5px
15px
2px
rgba
(
144
,
147
,
153
,
0.5
);
}
.device-card.new-design
:hover
{
transform
:
translateY
(
-3px
);
box-shadow
:
0
0
0
1px
rgba
(
78
,
198
,
138
,
0.5
),
7px
7px
4px
0
rgba
(
78
,
198
,
138
,
0.7
),
7px
7px
20px
2px
rgba
(
78
,
198
,
138
,
0.6
);
}
/* 设备名称 - 左上角 */
.device-name
{
align-self
:
flex-start
;
font-size
:
14px
;
font-weight
:
bold
;
color
:
#fff
;
max-width
:
100%
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
text-shadow
:
0
1px
2px
rgba
(
0
,
0
,
0
,
0.5
);
margin-bottom
:
5px
;
}
/* 设备状态标签 - 右上角 */
.device-status-tag
{
position
:
absolute
;
top
:
25px
;
right
:
5px
;
padding
:
2px
5px
5px
;
border-radius
:
4px
;
font-size
:
12px
;
font-weight
:
bold
;
color
:
#fff
;
min-width
:
50px
;
text-align
:
center
;
}
/* 移除状态标签的阴影 */
.status-running
{
background-color
:
rgba
(
78
,
198
,
138
,
0.9
);
box-shadow
:
none
;
}
.status-waiting
{
background-color
:
rgba
(
235
,
186
,
22
,
0.9
);
box-shadow
:
none
;
}
.status-error
{
background-color
:
rgba
(
235
,
86
,
86
,
0.9
);
box-shadow
:
none
;
}
.status-off
{
background-color
:
rgba
(
144
,
147
,
153
,
0.9
);
box-shadow
:
none
;
}
/* 设备波纹容器 */
.device-wave-container
{
width
:
100%
;
height
:
120px
;
display
:
flex
;
flex-direction
:
column
;
justify-content
:
center
;
align-items
:
center
;
position
:
relative
;
}
/* OEE文字 */
.oee-text
{
position
:
absolute
;
top
:
30px
;
font-size
:
16px
;
font-weight
:
bold
;
color
:
#fff
;
z-index
:
3
;
text-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.5
);
}
/* ERR文字 */
.err-text
{
position
:
absolute
;
top
:
30px
;
font-size
:
16px
;
font-weight
:
bold
;
z-index
:
3
;
text-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.5
);
left
:
50%
;
transform
:
translateX
(
-50%
);
}
/* ERR颜色 */
.err-error
{
color
:
rgba
(
235
,
86
,
86
,
0.9
);
}
/* OFF文字 */
.off-text
{
position
:
absolute
;
top
:
30px
;
font-size
:
16px
;
font-weight
:
bold
;
z-index
:
3
;
text-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.5
);
left
:
50%
;
transform
:
translateX
(
-50%
);
}
/* OFF颜色 */
.off-status
{
color
:
rgba
(
144
,
147
,
153
,
0.9
);
}
/* 百分比文字 */
.percentage-text
{
position
:
absolute
;
top
:
50px
;
font-size
:
25px
;
font-weight
:
bold
;
color
:
#fff
;
z-index
:
3
;
text-shadow
:
0
2px
4px
rgba
(
0
,
0
,
0
,
0.5
);
}
/* 圆形容器 */
.circle-container
{
width
:
100px
;
height
:
100px
;
border-radius
:
50%
;
overflow
:
hidden
;
position
:
relative
;
background-color
:
rgba
(
10
,
12
,
15
,
0.8
);
}
/* 移除圆形容器的阴影 */
.border-running
{
border
:
2px
solid
rgba
(
78
,
198
,
138
,
0.9
);
box-shadow
:
none
;
}
.border-waiting
{
border
:
2px
solid
rgba
(
235
,
186
,
22
,
0.9
);
box-shadow
:
none
;
}
.border-error
{
border
:
2px
solid
rgba
(
235
,
86
,
86
,
0.9
);
box-shadow
:
none
;
}
.border-off
{
border
:
2px
solid
rgba
(
144
,
147
,
153
,
0.9
);
box-shadow
:
none
;
}
/* 水波纹 */
.water-wave
{
position
:
absolute
;
bottom
:
0
;
left
:
0
;
width
:
100%
;
transition
:
height
0.7s
ease
;
overflow
:
hidden
;
border-radius
:
0
0
50px
50px
;
}
/* 水波纹颜色 */
.wave-running
{
background-color
:
rgba
(
78
,
198
,
138
,
0.7
);
}
.wave-waiting
{
background-color
:
rgba
(
235
,
186
,
22
,
0.7
);
}
.wave-error
{
background-color
:
rgba
(
235
,
86
,
86
,
0.7
);
}
.wave-off
{
background-color
:
rgba
(
144
,
147
,
153
,
0.7
);
}
/* 水波纹内容 - 设置相对定位以包含波浪元素 */
.water-wave-content
{
position
:
relative
;
width
:
100%
;
height
:
100%
;
overflow
:
hidden
;
}
/* 波浪容器 - 水平波浪效果 */
.water-wave-ripple
{
width
:
200%
;
height
:
100%
;
position
:
absolute
;
bottom
:
0
;
left
:
0
;
background
:
transparent
;
z-index
:
2
;
}
/* 波浪图形 - 使用SVG背景实现波浪形状 */
.water-wave-ripple
::before
{
content
:
""
;
position
:
absolute
;
top
:
-12px
;
left
:
0
;
width
:
100%
;
height
:
25px
;
background-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='white' opacity='0.5' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
;
background-size
:
100%
100%
;
-webkit-animation
:
wave-horizontal
6s
linear
infinite
;
animation
:
wave-horizontal
6s
linear
infinite
;
}
/* 第二层波浪 - 与第一层错开,增强效果 */
.water-wave-ripple
::after
{
content
:
""
;
position
:
absolute
;
top
:
-14px
;
left
:
0
;
width
:
100%
;
height
:
28px
;
background-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='white' opacity='0.3' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
;
background-size
:
100%
100%
;
-webkit-animation
:
wave-horizontal
8s
linear
infinite
;
animation
:
wave-horizontal
8s
linear
infinite
;
animation-delay
:
-2s
;
}
/* 为不同状态设置不同波浪颜色 */
.wave-running
.water-wave-ripple
::before
,
.wave-running
.water-wave-ripple
::after
{
background-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%234EC68A' opacity='0.5' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
,
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%234EC68A' opacity='0.3' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
;
}
.wave-waiting
.water-wave-ripple
::before
,
.wave-waiting
.water-wave-ripple
::after
{
background-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%23EBBA16' opacity='0.5' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
,
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%23EBBA16' opacity='0.3' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
;
}
.wave-error
.water-wave-ripple
::before
,
.wave-error
.water-wave-ripple
::after
{
background-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%23EB5656' opacity='0.5' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
,
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%23EB5656' opacity='0.3' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
;
}
.wave-off
.water-wave-ripple
::before
,
.wave-off
.water-wave-ripple
::after
{
background-image
:
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%23909399' opacity='0.5' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
,
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1200 120' preserveAspectRatio='none'%3E%3Cpath fill='%23909399' opacity='0.3' d='M0,0V46.29c47.79,22.2,103.59,32.17,158,28,70.36-5.37,136.33-33.31,206.8-37.5C438.64,32.43,512.34,53.67,583,72.05c69.27,18,138.3,24.88,209.4,13.08,36.15-6,69.85-17.84,104.45-29.34C989.49,25,1113,-14.29,1200,52.47V0Z'%3E%3C/path%3E%3C/svg%3E")
;
}
/* 顶部发光效果 */
.water-wave
::after
{
content
:
""
;
position
:
absolute
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
8px
;
background
:
linear-gradient
(
to
bottom
,
rgba
(
255
,
255
,
255
,
0.7
),
transparent
);
z-index
:
10
;
}
/* 设备状态信息 */
.device-status-info
{
font-size
:
12px
;
color
:
rgba
(
255
,
255
,
255
,
0.85
);
text-align
:
center
;
line-height
:
1.5
;
}
/* 响应式调整 */
@media
(
max-width
:
768px
)
{
.device-cards-container
{
grid-template-columns
:
repeat
(
auto-fill
,
minmax
(
150px
,
1
fr
));
}
.device-card.new-design
{
min-height
:
160px
;
padding
:
10px
;
}
.circle-container
{
width
:
80px
;
height
:
80px
;
}
.oee-text
,
.err-text
,
.off-text
{
top
:
20px
;
font-size
:
14px
;
}
.percentage-text
{
top
:
40px
;
font-size
:
20px
;
}
}
/* 为Safari和iOS设备的特别修复 */
@supports
(
-webkit-appearance
:
none
)
{
.water-wave-ripple
::before
,
.water-wave-ripple
::after
{
-webkit-animation-play-state
:
running
;
animation-play-state
:
running
;
}
}
/* 波浪水平移动动画 */
@-webkit-keyframes
wave-horizontal
{
0
%
{
transform
:
translateX
(
0
);
}
100
%
{
transform
:
translateX
(
-50%
);
}
}
@keyframes
wave-horizontal
{
0
%
{
transform
:
translateX
(
0
);
}
100
%
{
transform
:
translateX
(
-50%
);
}
}
.flxe_sty
{
width
:
50%
;
display
:
flex
;
align-items
:
center
;
font-size
:
10px
;
color
:
#fff
;
margin-top
:
5px
;
.flxe_label_sty
{
margin-right
:
2px
;
font-weight
:
bold
;
color
:
#000
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
font-size
:
10px
;
width
:
30px
;
height
:
auto
;
border-radius
:
2px
;
margin-left
:
10px
;
}
}
</style>
</html>
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment