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
0706a370
Commit
0706a370
authored
Jun 20, 2025
by
guibin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
任原设备利用率设备添加排序
parent
d67f5b13
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
2972 additions
and
0 deletions
+2972
-0
qdry_project/controllers/controllers.py
+8
-0
qdry_project/static/src/view/oee_analysis.html
+2964
-0
No files found.
qdry_project/controllers/controllers.py
View file @
0706a370
...
@@ -22,6 +22,14 @@ class RokeMesThreeColourLight(http.Controller):
...
@@ -22,6 +22,14 @@ class RokeMesThreeColourLight(http.Controller):
data
=
{
"code"
:
1
,
"message"
:
"请求通过"
,
"data"
:
{
"factory_code"
:
factory_code
}}
data
=
{
"code"
:
1
,
"message"
:
"请求通过"
,
"data"
:
{
"factory_code"
:
factory_code
}}
template
=
env
.
get_template
(
'equipment_status_qdry.html'
)
template
=
env
.
get_template
(
'equipment_status_qdry.html'
)
return
template
.
render
(
data
)
return
template
.
render
(
data
)
@http.route
(
"/roke/three_color_light/device_analysis"
,
type
=
"http"
,
auth
=
'none'
,
cors
=
'*'
,
csrf
=
False
)
def
device_analysis
(
self
,
**
kwargs
):
_self
=
http
.
request
factory_code
=
http
.
request
.
env
(
user
=
SUPERUSER_ID
)[
'ir.config_parameter'
]
.
get_param
(
'database.uuid'
,
default
=
""
)
data
=
{
"code"
:
1
,
"message"
:
"请求通过"
,
"data"
:
{
"factory_code"
:
factory_code
}}
template
=
env
.
get_template
(
'oee_analysis.html'
)
return
template
.
render
(
data
)
@http.route
(
'/roke/workstation/plant/tree'
,
type
=
'json'
,
auth
=
'none'
,
csrf
=
False
,
cors
=
"*"
)
@http.route
(
'/roke/workstation/plant/tree'
,
type
=
'json'
,
auth
=
'none'
,
csrf
=
False
,
cors
=
"*"
)
def
get_roke_workstation_plant
(
self
):
def
get_roke_workstation_plant
(
self
):
...
...
qdry_project/static/src/view/oee_analysis.html
0 → 100644
View file @
0706a370
<!DOCTYPE html>
<html>
<head>
<meta
charset=
"UTF-8"
/>
<title>
OEE时间利用率分析
</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"
>
<!-- 页面标题 -->
<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"
>
OEE时间利用率分析
</span>
</div>
</div>
<!-- 替换原有的产线选择器 -->
<div
class=
"floating-selector"
id=
"floatingSelector"
>
<div
class=
"floating-button"
@
click=
"showSelectionPanel"
>
<i
class=
"el-icon-menu"
></i>
</div>
<!-- 选择面板 -->
<div
class=
"selection-panel"
v-show=
"selectionPanelVisible"
>
<div
class=
"panel-header"
>
<div
class=
"back-button"
v-if=
"selectionStep === 'line'"
@
click=
"selectionStep = 'workshop'"
>
<i
class=
"el-icon-arrow-left"
></i>
</div>
<div
class=
"panel-title"
>
[[ selectionStep === 'workshop' ? '车间名称' : '产线名称' ]]
</div>
<div
class=
"close-button"
@
click=
"selectionPanelVisible = false"
>
<i
class=
"el-icon-close"
></i>
</div>
</div>
<!-- 添加当前选择信息 -->
<div
class=
"current-selection"
v-if=
"selectedWorkshop || selectedLine"
>
<div
class=
"selection-info"
v-if=
"selectedWorkshop"
>
<span
class=
"info-label"
>
当前车间:
</span>
<span
class=
"info-value"
>
[[ selectedWorkshop.name ]]
</span>
</div>
<div
class=
"selection-info"
v-if=
"selectedLine"
>
<span
class=
"info-label"
>
当前产线:
</span>
<span
class=
"info-value"
>
[[ getCurrentLineName() ]]
</span>
</div>
</div>
<div
class=
"panel-content"
>
<!-- 车间选择 -->
<div
class=
"selection-list"
v-if=
"selectionStep === 'workshop'"
>
<div
class=
"selection-item"
v-for=
"workshop in workshops"
:key=
"workshop.id"
@
click=
"selectWorkshop(workshop)"
:class=
"{'selected': selectedWorkshop && selectedWorkshop.id === workshop.id}"
>
[[ workshop.name ]]
<i
class=
"el-icon-arrow-right"
></i>
</div>
</div>
<!-- 产线选择 -->
<div
class=
"selection-list"
v-else
>
<div
class=
"selection-item"
v-for=
"line in filteredProductionLines"
:key=
"line.id"
@
click=
"selectProductionLine(line)"
:class=
"{'selected': selectedLine === line.id}"
>
[[ line.name ]]
</div>
</div>
</div>
</div>
</div>
<!-- 图表网格布局 -->
<div
class=
"chart-grid"
>
<!-- 当前机台开机实时状态 -->
<div
class=
"chart-card"
>
<div
class=
"card-header"
>
<h3>
当前机台开机实时状态
</h3>
<span
class=
"current-date"
>
[[currentDate]]
</span>
</div>
<div
id=
"statusPieChart"
class=
"chart-container"
></div>
</div>
<!-- 设备OEE排名 -->
<div
class=
"chart-card"
>
<div
class=
"card-header"
>
<h3>
设备OEE时间利用率TOP5
</h3>
<el-select
v-model=
"timeRanges.oeeRank"
size=
"small"
@
change=
"updateChart('oeeRank')"
>
<el-option
v-for=
"item in timeOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
>
</el-option>
</el-select>
</div>
<div
id=
"oeeRankChart"
class=
"chart-container"
></div>
</div>
<!-- 设备效率等待损失量 -->
<div
class=
"chart-card"
>
<div
class=
"card-header"
>
<h3>
设备效率等待损失量(分钟)
</h3>
<el-select
v-model=
"timeRanges.waitingLoss"
size=
"small"
@
change=
"updateChart('waitingLoss')"
>
<el-option
v-for=
"item in timeOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
>
</el-option>
</el-select>
</div>
<div
id=
"waitingLossChart"
class=
"chart-container"
></div>
</div>
<!-- 设备OEE综合趋势 -->
<div
class=
"chart-card"
>
<div
class=
"card-header"
>
<h3>
设备OEE时间利用率综合趋势
</h3>
<div
class=
"header-controls"
>
<el-select
v-model=
"selectedDevice"
size=
"small"
placeholder=
"选择设备"
@
change=
"updateChart('oeeTrend')"
style=
"margin-right: 10px"
>
<el-option
v-for=
"device in devices"
:key=
"device.id"
:label=
"device.name"
:value=
"device.id"
>
</el-option>
</el-select>
<el-select
v-model=
"timeRanges.oeeTrend"
size=
"small"
@
change=
"updateChart('oeeTrend')"
>
<el-option
v-for=
"item in timeOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
>
</el-option>
</el-select>
<span
class=
"unit-label"
>
单位:%
</span>
</div>
</div>
<div
id=
"oeeTrendChart"
class=
"chart-container"
></div>
</div>
<!-- 设备效率故障损失量 -->
<div
class=
"chart-card"
>
<div
class=
"card-header"
>
<h3>
设备效率故障损失量(分钟)
</h3>
<el-select
v-model=
"timeRanges.faultLoss"
size=
"small"
@
change=
"updateChart('faultLoss')"
>
<el-option
v-for=
"item in timeOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
>
</el-option>
</el-select>
</div>
<div
id=
"faultLossChart"
class=
"chart-container"
></div>
</div>
</div>
</div>
</body>
<script>
// 发送消息给父页面(关闭odoo的菜单弹窗)
document
.
addEventListener
(
"click"
,
()
=>
{
window
.
parent
.
postMessage
(
"hidePopover"
,
"*"
);
});
let
vue
=
new
Vue
({
el
:
"#app"
,
delimiters
:
[
"[["
,
"]]"
],
data
()
{
return
{
loading
:
false
,
currentDate
:
moment
().
format
(
"YYYY/MM/DD"
),
// dwsURL: "https://workstation.rokeris.com", // 基地址
dwsURL
:
""
,
// 基地址
baseURL
:
"https://dws-platform.xbg.rokeris.com/dev-api"
,
// 基地址
factory_code
:
""
,
// 工厂编码
// 产线数据
selectedLine
:
null
,
productionLines
:
[],
// 将从接口获取
// 设备数据
selectedDevice
:
null
,
devices
:
[],
// 图表实例
charts
:
{
statusPie
:
null
,
oeeRank
:
null
,
waitingLoss
:
null
,
oeeTrend
:
null
,
faultLoss
:
null
,
},
// 时间范围选择
timeRanges
:
{
oeeRank
:
"today"
,
waitingLoss
:
"today"
,
faultLoss
:
"today"
,
oeeTrend
:
"today"
,
},
timeOptions
:
[
{
value
:
"today"
,
label
:
"今天"
},
{
value
:
"week"
,
label
:
"本周"
},
{
value
:
"month"
,
label
:
"本月"
},
{
value
:
"lastMonth"
,
label
:
"上月"
},
],
// 添加接口数据存储
apiData
:
{
deviceStates
:
[],
planTimeData
:
null
,
plantTree
:
[],
// 存储车间和产线树形数据
allEquipmentData
:
[],
// 存储所有已绑定设备数据
currentLineEquipment
:
[],
// 存储当前产线的设备数据
waitingLossData
:
[],
// 存储等待损失数据
faultLossData
:
[],
// 存储故障损失数据
oeeTrendData
:
[],
// 存储OEE趋势数据
oeeRankData
:
[],
// 存储OEE排名数据
},
// 浮动选择器相关
selectionPanelVisible
:
false
,
selectionStep
:
"workshop"
,
// 'workshop' 或 'line'
selectedWorkshop
:
null
,
workshops
:
[],
// 将从接口获取
};
},
created
()
{
if
(
this
.
getUrlSearch
(
"factory_code"
))
{
this
.
factory_code
=
this
.
getUrlSearch
(
"factory_code"
);
//截取url后面的参数
}
},
computed
:
{
filteredProductionLines
()
{
if
(
!
this
.
selectedWorkshop
)
return
[];
// 从apiData.plantTree中筛选当前选中车间的产线
const
workshop
=
this
.
apiData
.
plantTree
.
find
((
w
)
=>
w
.
id
===
this
.
selectedWorkshop
.
id
);
return
workshop
?
workshop
.
children
||
[]
:
[];
},
},
mounted
()
{
console
.
log
(
"组件挂载,准备初始化..."
);
this
.
init
();
// 直接调用init方法
this
.
$nextTick
(()
=>
{
// 这些初始化操作可能会与init方法中的操作重复,可以考虑移除
document
.
getElementById
(
"bodyId"
).
style
.
display
=
"block"
;
this
.
charts
.
statusPie
=
this
.
initStatusPieChart
();
this
.
charts
.
oeeRank
=
this
.
initOEERankChart
();
this
.
charts
.
waitingLoss
=
this
.
initWaitingLossChart
();
this
.
charts
.
oeeTrend
=
this
.
initOEETrendChart
();
this
.
charts
.
faultLoss
=
this
.
initFaultLossChart
();
// 直接更新图表,不需要先获取设备列表
this
.
updateAllCharts
();
// 设置默认选中的车间和产线
if
(
this
.
productionLines
.
length
>
0
)
{
this
.
selectedLine
=
this
.
productionLines
[
0
].
id
;
const
workshopId
=
this
.
productionLines
[
0
].
workshopId
;
this
.
selectedWorkshop
=
this
.
workshops
.
find
((
w
)
=>
w
.
id
===
workshopId
);
}
// 初始化拖拽功能
this
.
initDraggable
();
// 添加窗口大小变化监听
window
.
addEventListener
(
"resize"
,
this
.
handleResize
);
// 初始化完成后触发一次resize
this
.
handleResize
();
});
// 添加延迟更新,确保图表能正确显示
setTimeout
(()
=>
{
console
.
log
(
"延迟更新所有图表..."
);
if
(
this
.
apiData
.
faultLossData
&&
this
.
apiData
.
faultLossData
.
length
>
0
)
{
console
.
log
(
"故障损失数据存在,重新更新故障损失图表"
);
this
.
updateFaultLossChart
();
}
if
(
this
.
apiData
.
waitingLossData
&&
this
.
apiData
.
waitingLossData
.
length
>
0
)
{
console
.
log
(
"等待损失数据存在,重新更新等待损失图表"
);
this
.
updateWaitingLossChart
();
}
},
1000
);
},
methods
:
{
// 通过网址跳转过来的页面,截取后面的参数
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
init
()
{
this
.
loading
=
true
;
try
{
// console.log("开始初始化...");
// 初始化图表
this
.
charts
.
statusPie
=
this
.
initStatusPieChart
();
this
.
charts
.
oeeRank
=
this
.
initOEERankChart
();
this
.
charts
.
waitingLoss
=
this
.
initWaitingLossChart
();
this
.
charts
.
faultLoss
=
this
.
initFaultLossChart
();
this
.
charts
.
oeeTrend
=
this
.
initOEETrendChart
();
// 初始化时显示"暂无数据"提示
this
.
updateStatusPieChart
(
true
);
this
.
updateOEERankChart
(
true
);
this
.
updateWaitingLossChart
(
true
);
this
.
updateFaultLossChart
(
true
);
this
.
updateOEETrendChart
(
true
);
// 并行请求三个初始化数据
// console.log("开始并行请求车间产线、设备计划时间和设备列表数据...");
const
[
plantTreeResult
,
devicePlanTimeResult
,
allEquipmentDataResult
]
=
await
Promise
.
all
([
this
.
fetchPlantTree
(),
this
.
getDevicePlanTime
(),
this
.
getAllEquipmentData
(),
]);
// console.log("三个初始化数据请求已完成");
// 默认选择第一个车间和产线
if
(
this
.
apiData
.
plantTree
.
length
>
0
)
{
this
.
selectedWorkshop
=
this
.
apiData
.
plantTree
[
0
];
// console.log("默认选择车间:", this.selectedWorkshop.name);
if
(
this
.
filteredProductionLines
.
length
>
0
)
{
this
.
selectedLine
=
this
.
filteredProductionLines
[
0
].
id
;
// console.log("默认选择产线:", this.filteredProductionLines[0].name);
// 处理产线选择,获取设备状态数据
await
this
.
handleLineChange
();
}
else
{
// console.warn("没有可用的产线");
// 获取设备状态数据
await
this
.
getInitialDeviceStates
();
}
}
else
{
// console.warn("没有可用的车间");
// 获取设备状态数据
await
this
.
getInitialDeviceStates
();
}
// 初始化拖拽功能
this
.
initDraggable
();
// 添加窗口大小变化监听
window
.
addEventListener
(
"resize"
,
this
.
handleResize
);
// 初始化完成后触发一次resize
this
.
handleResize
();
}
catch
(
error
)
{
console
.
error
(
"初始化失败:"
,
error
);
this
.
$message
.
error
(
"初始化失败,请刷新页面重试"
);
}
finally
{
this
.
loading
=
false
;
}
},
// 获取车间和产线树形数据
async
fetchPlantTree
()
{
try
{
console
.
log
(
"发起请求: POST "
+
this
.
dwsURL
+
"/roke/workstation/plant/tree"
);
const
response
=
await
axios
({
method
:
"POST"
,
url
:
this
.
dwsURL
+
"/roke/workstation/plant/tree"
,
data
:
{
no_icon
:
true
},
headers
:
{
"Content-Type"
:
"application/json"
,
"x-session-id"
:
"1d8a662231e9bbab315c736b7551657c18b44366"
,
},
});
console
.
log
(
"接口返回数据:"
,
JSON
.
stringify
(
response
.
data
));
// 处理jsonrpc格式的返回数据
if
(
response
.
data
&&
response
.
data
.
jsonrpc
===
"2.0"
&&
response
.
data
.
result
)
{
const
result
=
response
.
data
.
result
;
if
(
result
.
code
===
0
)
{
// 处理返回的树形数据
this
.
apiData
.
plantTree
=
this
.
processPlantTreeData
(
result
.
data
||
[]);
console
.
log
(
"处理后的树形数据:"
,
JSON
.
stringify
(
this
.
apiData
.
plantTree
));
// 提取车间数据
this
.
workshops
=
this
.
apiData
.
plantTree
.
map
((
item
)
=>
({
id
:
item
.
id
,
name
:
item
.
name
,
}));
console
.
log
(
"提取的车间数据:"
,
JSON
.
stringify
(
this
.
workshops
));
}
else
{
console
.
error
(
"获取车间和产线数据失败:"
,
result
.
message
||
"未知错误"
);
// 使用默认数据
this
.
useDefaultPlantData
();
}
}
else
{
console
.
error
(
"获取车间和产线数据失败: 返回格式不正确"
);
// 使用默认数据
this
.
useDefaultPlantData
();
}
}
catch
(
error
)
{
console
.
error
(
"获取车间和产线数据异常:"
,
error
);
// 使用默认数据
this
.
useDefaultPlantData
();
}
},
// 处理树形数据,适配我们的数据结构
processPlantTreeData
(
data
)
{
// 处理从接口返回的真实数据
const
plantTree
=
[];
// 遍历车间数据
data
.
forEach
((
plant
)
=>
{
if
(
plant
.
type
===
"plant"
)
{
const
workshops
=
[];
// 遍历该车间下的产线
if
(
plant
.
workshops
&&
plant
.
workshops
.
length
>
0
)
{
plant
.
workshops
.
forEach
((
workshop
)
=>
{
if
(
workshop
.
type
===
"workshop"
)
{
workshops
.
push
({
id
:
workshop
.
id
,
name
:
workshop
.
name
,
workshopId
:
plant
.
id
,
centerCount
:
workshop
.
center_count
||
0
,
});
}
});
}
plantTree
.
push
({
id
:
plant
.
id
,
name
:
plant
.
name
,
children
:
workshops
,
});
}
});
return
plantTree
;
},
// 使用默认数据(当接口请求失败时)
useDefaultPlantData
()
{
console
.
log
(
"使用空数据代替默认数据"
);
// 使用空数据
this
.
workshops
=
[];
this
.
apiData
.
plantTree
=
[];
},
// 获取所有已绑定设备数据
async
getAllEquipmentData
()
{
try
{
// 发送请求获取所有已绑定设备数据
const
response
=
await
axios
({
method
:
"post"
,
url
:
this
.
dwsURL
+
"/roke/workstation/equipment/get_equipment_data"
,
data
:
{},
headers
:
{
"Content-Type"
:
"application/json"
,
"x-session-id"
:
"1d8a662231e9bbab315c736b7551657c18b44366"
,
},
});
// 处理JSON-RPC格式的响应
if
(
response
.
data
&&
response
.
data
.
result
&&
response
.
data
.
result
.
code
===
0
)
{
// 获取所有设备数据
const
equipmentData
=
response
.
data
.
result
.
data
||
[];
// 存储所有设备数据,供后续使用
this
.
apiData
.
allEquipmentData
=
equipmentData
;
console
.
log
(
"所有已绑定设备数据:"
,
JSON
.
stringify
(
equipmentData
));
return
equipmentData
;
}
else
{
const
errorMsg
=
response
.
data
.
result
?
response
.
data
.
result
.
message
:
"获取所有已绑定设备数据失败"
;
console
.
error
(
errorMsg
);
}
}
catch
(
error
)
{
console
.
error
(
"获取所有已绑定设备数据出错:"
,
error
);
}
},
// 根据产线ID筛选设备
filterEquipmentByLine
(
lineId
)
{
console
.
log
(
"筛选产线设备,产线ID:"
,
lineId
);
if
(
!
lineId
||
!
this
.
apiData
.
allEquipmentData
)
{
console
.
warn
(
"产线ID或设备数据为空,无法筛选"
);
return
[];
}
// 从所有已绑定设备数据中筛选出当前产线的设备
// 注意:根据数据结构,应该使用work_center_id来匹配产线ID
const
lineEquipment
=
this
.
apiData
.
allEquipmentData
.
filter
(
(
device
)
=>
device
.
work_center_id
==
lineId
);
console
.
log
(
`筛选到
${
lineEquipment
.
length
}
个设备,产线ID:
${
lineId
}
,设备数据:`
,
JSON
.
stringify
(
lineEquipment
)
);
// 如果没有筛选到设备,可能是因为产线ID实际上是workshop_id,尝试用workshop_id筛选
if
(
lineEquipment
.
length
===
0
)
{
console
.
log
(
"使用workshop_id尝试筛选设备"
);
const
workshopEquipment
=
this
.
apiData
.
allEquipmentData
.
filter
(
(
device
)
=>
device
.
workshop_id
==
lineId
);
console
.
log
(
`使用workshop_id筛选到
${
workshopEquipment
.
length
}
个设备`
);
return
workshopEquipment
;
}
return
lineEquipment
;
},
// 处理产线改变
async
handleLineChange
()
{
this
.
loading
=
true
;
try
{
console
.
log
(
"产线改变,新产线ID:"
,
this
.
selectedLine
);
// 直接从已有数据中筛选当前产线的设备数据
const
lineEquipment
=
this
.
filterEquipmentByLine
(
this
.
selectedLine
);
console
.
log
(
"当前产线设备数据:"
,
JSON
.
stringify
(
lineEquipment
));
// 存储当前产线的设备数据
this
.
apiData
.
currentLineEquipment
=
lineEquipment
;
// 更新设备选择器的设备列表
this
.
devices
=
lineEquipment
.
map
((
device
)
=>
({
id
:
device
.
code
,
name
:
device
.
name
||
device
.
code
,
}));
console
.
log
(
"更新后的设备列表:"
,
JSON
.
stringify
(
this
.
devices
));
// 如果有设备,默认选择第一个设备
if
(
this
.
devices
.
length
>
0
)
{
this
.
selectedDevice
=
this
.
devices
[
0
].
id
;
console
.
log
(
"默认选择设备:"
,
this
.
selectedDevice
);
}
else
{
this
.
selectedDevice
=
""
;
console
.
warn
(
"当前产线下没有设备"
);
}
// 准备设备编号列表和计划时间数据
// 使用当前产线的设备编码,而不是所有设备
const
deviceList
=
[];
// 从当前产线的设备数据中获取设备编码
if
(
this
.
apiData
.
currentLineEquipment
&&
this
.
apiData
.
currentLineEquipment
.
length
>
0
)
{
this
.
apiData
.
currentLineEquipment
.
forEach
((
device
)
=>
{
if
(
device
.
code
)
{
deviceList
.
push
(
device
.
code
);
}
});
}
console
.
log
(
"当前产线设备编码:"
,
deviceList
);
// 获取计划时间数据
const
planTimeList
=
this
.
apiData
.
planTimeData
||
{};
// 检查参数
if
(
deviceList
.
length
===
0
)
{
console
.
warn
(
"当前产线没有可用的设备编码,无法获取设备状态"
);
this
.
apiData
.
deviceStates
=
[];
this
.
updateStatusPieChart
(
true
);
return
;
}
console
.
log
(
"最终参数:"
,
{
planTimeList
,
deviceList
,
});
// 获取设备状态数据
await
this
.
getDeviceStateList
(
planTimeList
,
deviceList
);
// 获取OEE排名数据
const
{
startDate
,
endDate
}
=
this
.
getDateRange
(
this
.
timeRanges
.
oeeRank
);
await
this
.
getOEERankData
(
startDate
,
endDate
);
// 获取等待损失数据
const
waitingLossDates
=
this
.
getDateRange
(
this
.
timeRanges
.
waitingLoss
);
await
this
.
getLossData
(
waitingLossDates
.
startDate
,
waitingLossDates
.
endDate
,
"waiting"
);
// 获取故障损失数据
const
faultLossDates
=
this
.
getDateRange
(
this
.
timeRanges
.
faultLoss
);
await
this
.
getLossData
(
faultLossDates
.
startDate
,
faultLossDates
.
endDate
,
"fault"
);
// 获取OEE趋势数据
if
(
this
.
selectedDevice
)
{
const
oeeTrendDates
=
this
.
getDateRange
(
this
.
timeRanges
.
oeeTrend
);
await
this
.
getOEETrendData
(
this
.
selectedDevice
,
oeeTrendDates
.
startDate
,
oeeTrendDates
.
endDate
);
}
// 更新所有图表
console
.
log
(
"所有数据加载完成,开始更新图表"
);
this
.
updateAllCharts
();
}
catch
(
error
)
{
console
.
error
(
"切换产线失败:"
,
error
);
this
.
$message
.
error
(
"切换产线失败"
);
}
finally
{
this
.
loading
=
false
;
}
},
// 获取设备状态列表
async
getDeviceStateList
(
planTimeList
,
deviceList
)
{
try
{
console
.
log
(
"获取设备状态列表, 原始参数:"
,
{
planTimeList
,
deviceList
,
});
// 检查参数是否有效
if
(
!
deviceList
||
deviceList
.
length
===
0
)
{
console
.
error
(
"设备列表为空,无法获取设备状态"
);
this
.
apiData
.
deviceStates
=
[];
this
.
updateStatusPieChart
();
return
;
}
// 使用CORS代理
// 发送请求获取设备状态
const
response
=
await
axios
({
method
:
"POST"
,
url
:
this
.
baseURL
+
"/public/device_efficiency/device_state_list"
,
data
:
{
factory_code
:
this
.
factory_code
||
"f1991dbb-52d6-11ef-b13f-fa163e5bb392"
,
// 使用工厂代码
plan_time_list
:
planTimeList
,
// 直接使用API返回的计划时间数据
device_list
:
deviceList
,
// 真实的设备编码列表
},
headers
:
{
"Content-Type"
:
"application/json"
,
},
});
console
.
log
(
"设备状态接口返回:"
,
JSON
.
stringify
(
response
.
data
));
// 处理响应
if
(
response
.
data
&&
response
.
data
.
code
===
200
)
{
// 处理设备状态数据
const
deviceStates
=
response
.
data
.
data
||
[];
// 确保即使返回空数组也能正确处理
this
.
apiData
.
deviceStates
=
deviceStates
.
length
>
0
?
deviceStates
:
[];
console
.
log
(
"设备状态数据:"
,
JSON
.
stringify
(
this
.
apiData
.
deviceStates
));
console
.
log
(
"设备状态数据长度:"
,
this
.
apiData
.
deviceStates
.
length
);
// 更新状态饼图
this
.
updateStatusPieChart
(
this
.
apiData
.
deviceStates
.
length
===
0
);
return
this
.
apiData
.
deviceStates
;
}
else
{
const
errorMsg
=
response
.
data
.
msg
||
"获取设备状态失败"
;
console
.
error
(
errorMsg
);
this
.
$message
.
error
(
errorMsg
);
this
.
apiData
.
deviceStates
=
[];
this
.
updateStatusPieChart
(
true
);
// 明确传入true表示没有数据
}
}
catch
(
error
)
{
console
.
error
(
"获取设备状态出错:"
,
error
);
this
.
$message
.
error
(
"获取设备状态失败,请稍后重试"
);
// 不使用假数据,而是显示错误状态
this
.
apiData
.
deviceStates
=
[];
this
.
updateStatusPieChart
(
true
);
// 明确传入true表示没有数据
}
},
// 更新状态饼图
updateStatusPieChart
(
isEmpty
=
false
)
{
if
(
!
this
.
charts
.
statusPie
)
{
console
.
error
(
"状态饼图未初始化,无法更新"
);
return
;
}
console
.
log
(
"开始更新状态饼图..."
);
console
.
log
(
"设备状态数据:"
,
JSON
.
stringify
(
this
.
apiData
.
deviceStates
));
// 处理设备状态数据
const
statusCounts
=
{
running
:
0
,
offline
:
0
,
waiting
:
0
,
maintenance
:
0
,
};
if
(
!
isEmpty
&&
this
.
apiData
.
deviceStates
&&
this
.
apiData
.
deviceStates
.
length
>
0
)
{
this
.
apiData
.
deviceStates
.
forEach
((
device
)
=>
{
// 使用state字段映射到我们的状态类别
let
status
=
"offline"
;
// 默认为离线
if
(
device
.
state
)
{
if
(
device
.
state
===
"green"
)
{
status
=
"running"
;
}
else
if
(
device
.
state
===
"yellow"
)
{
status
=
"waiting"
;
}
else
if
(
device
.
state
===
"red"
)
{
status
=
"maintenance"
;
}
else
if
(
device
.
state
===
"gray"
)
{
status
=
"offline"
;
}
}
statusCounts
[
status
]
++
;
});
}
const
total
=
Object
.
values
(
statusCounts
).
reduce
((
sum
,
count
)
=>
sum
+
count
,
0
);
console
.
log
(
"状态计数:"
,
statusCounts
,
"总数:"
,
total
);
// 完全重新设置图表选项,而不是部分更新
const
option
=
{
tooltip
:
{
trigger
:
"item"
,
formatter
:
"{b}: {c} ({d}%)"
,
},
legend
:
{
orient
:
"horizontal"
,
bottom
:
0
,
itemWidth
:
10
,
itemHeight
:
10
,
textStyle
:
{
color
:
"#fff"
,
fontSize
:
12
},
data
:
[
"运行"
,
"离线"
,
"等待"
,
"维护"
],
},
series
:
[
{
name
:
"机台状态"
,
type
:
"pie"
,
radius
:
[
"40%"
,
"70%"
],
avoidLabelOverlap
:
true
,
emphasis
:
{
label
:
{
show
:
true
,
position
:
"outside"
,
formatter
:
"{d}%"
,
color
:
"#fff"
,
fontSize
:
12
,
},
},
labelLine
:
{
show
:
true
,
length
:
10
,
length2
:
40
,
lineStyle
:
{
color
:
"rgba(255, 255, 255, 0.3)"
,
},
},
data
:
[
{
value
:
statusCounts
.
running
,
name
:
"运行"
,
itemStyle
:
{
color
:
"#67C23A"
}
},
{
value
:
statusCounts
.
offline
,
name
:
"离线"
,
itemStyle
:
{
color
:
"#909399"
}
},
{
value
:
statusCounts
.
waiting
,
name
:
"等待"
,
itemStyle
:
{
color
:
"#E6A23C"
}
},
{
value
:
statusCounts
.
maintenance
,
name
:
"维护"
,
itemStyle
:
{
color
:
"#F56C6C"
},
},
],
},
{
name
:
"总数"
,
type
:
"pie"
,
radius
:
[
"0"
,
"30%"
],
label
:
{
position
:
"center"
,
formatter
:
[
"{a|总数量}"
,
"{b|"
+
total
+
"}"
].
join
(
"
\
n"
),
rich
:
{
a
:
{
fontSize
:
14
,
color
:
"#fff"
,
lineHeight
:
20
,
},
b
:
{
fontSize
:
24
,
color
:
"#fff"
,
fontWeight
:
"bold"
,
lineHeight
:
30
,
},
},
},
data
:
[
{
value
:
total
,
name
:
"总数"
,
itemStyle
:
{
color
:
"rgba(0, 0, 0, 0.2)"
},
},
],
},
],
};
// 如果没有数据,添加"暂无数据"提示
if
(
isEmpty
||
total
===
0
)
{
option
.
graphic
=
[
{
type
:
"text"
,
left
:
"center"
,
top
:
"middle"
,
style
:
{
text
:
"暂无数据"
,
fill
:
"#999"
,
font
:
"14px Microsoft YaHei"
,
},
},
];
}
// 应用完整的新配置
this
.
charts
.
statusPie
.
setOption
(
option
,
true
);
console
.
log
(
"状态饼图已更新,总数:"
,
total
);
},
// 选择车间
selectWorkshop
(
workshop
)
{
this
.
selectedWorkshop
=
workshop
;
this
.
selectionStep
=
"line"
;
},
// 选择产线
selectProductionLine
(
line
)
{
this
.
selectedLine
=
line
.
id
;
this
.
selectionPanelVisible
=
false
;
this
.
handleLineChange
();
},
// 获取当前产线名称
getCurrentLineName
()
{
if
(
!
this
.
selectedLine
)
return
""
;
// 从树形数据中查找当前产线
for
(
const
workshop
of
this
.
apiData
.
plantTree
)
{
if
(
workshop
.
children
)
{
const
line
=
workshop
.
children
.
find
((
line
)
=>
line
.
id
===
this
.
selectedLine
);
if
(
line
)
return
line
.
name
;
}
}
return
""
;
},
// 更新所有图表
updateAllCharts
()
{
console
.
log
(
"开始更新所有图表..."
);
// 更新状态饼图
this
.
updateStatusPieChart
();
console
.
log
(
"状态饼图更新完成"
);
// 更新OEE排名图表
this
.
updateOEERankChart
();
console
.
log
(
"OEE排名图表更新完成"
);
// 更新等待损失图表
this
.
updateWaitingLossChart
();
console
.
log
(
"等待损失图表更新完成"
);
// 更新故障损失图表
this
.
updateFaultLossChart
();
console
.
log
(
"故障损失图表更新完成"
);
// 更新OEE趋势图表
this
.
updateOEETrendChart
();
console
.
log
(
"OEE趋势图表更新完成"
);
console
.
log
(
"所有图表更新完成"
);
},
// 初始化所有图表
initCharts
()
{
console
.
log
(
"开始初始化所有图表..."
);
// 初始化状态饼图
this
.
charts
.
statusPie
=
this
.
initStatusPieChart
();
console
.
log
(
"状态饼图初始化完成"
);
// 初始化OEE排名图表
this
.
charts
.
oeeRank
=
this
.
initOEERankChart
();
// 初始化等待损失图表
this
.
charts
.
waitingLoss
=
this
.
initWaitingLossChart
();
// 初始化OEE趋势图表
this
.
charts
.
oeeTrend
=
this
.
initOEETrendChart
();
// 初始化故障损失图
this
.
charts
.
faultLoss
=
this
.
initFaultLossChart
();
console
.
log
(
"故障损失图表初始化完成"
);
// 监听窗口大小变化
window
.
addEventListener
(
"resize"
,
this
.
handleResize
);
console
.
log
(
"所有图表初始化完成"
);
},
// 初始化状态饼图
initStatusPieChart
()
{
// 获取DOM元素
const
statusPieChart
=
document
.
getElementById
(
"statusPieChart"
);
if
(
!
statusPieChart
)
{
console
.
error
(
"找不到状态饼图DOM元素"
);
return
null
;
}
console
.
log
(
"初始化状态饼图..."
);
// 初始化图表
const
chart
=
echarts
.
init
(
statusPieChart
);
// 使用原有的图表配置
const
option
=
{
tooltip
:
{
trigger
:
"item"
,
formatter
:
"{b}: {c} ({d}%)"
,
},
legend
:
{
orient
:
"horizontal"
,
bottom
:
0
,
itemWidth
:
10
,
itemHeight
:
10
,
textStyle
:
{
color
:
"#fff"
,
fontSize
:
12
},
data
:
[
"运行"
,
"离线"
,
"等待"
,
"维护"
],
},
series
:
[
{
name
:
"机台状态"
,
type
:
"pie"
,
avoidLabelOverlap
:
true
,
// 自动调整标签避免重叠
radius
:
[
"40%"
,
"70%"
],
emphasis
:
{
label
:
{
show
:
true
,
position
:
"outside"
,
formatter
:
"{d}%"
,
color
:
"#fff"
,
fontSize
:
12
,
},
},
labelLine
:
{
show
:
true
,
length
:
10
,
length2
:
10
,
lineStyle
:
{
color
:
"rgba(255, 255, 255, 0.3)"
,
},
},
data
:
[
{
value
:
0
,
name
:
"运行"
,
itemStyle
:
{
color
:
"#67C23A"
},
},
{
value
:
0
,
name
:
"离线"
,
itemStyle
:
{
color
:
"#909399"
},
},
{
value
:
0
,
name
:
"等待"
,
itemStyle
:
{
color
:
"#E6A23C"
},
},
{
value
:
0
,
name
:
"维护"
,
itemStyle
:
{
color
:
"#F56C6C"
},
},
],
},
{
name
:
"总数"
,
type
:
"pie"
,
radius
:
[
"0"
,
"30%"
],
label
:
{
position
:
"center"
,
formatter
:
[
"{a|总数量}"
,
"{b|0}"
].
join
(
"
\
n"
),
rich
:
{
a
:
{
fontSize
:
14
,
color
:
"#fff"
,
lineHeight
:
20
,
},
b
:
{
fontSize
:
24
,
color
:
"#fff"
,
fontWeight
:
"bold"
,
lineHeight
:
30
,
},
},
},
data
:
[
{
value
:
0
,
name
:
"总数"
,
itemStyle
:
{
color
:
"rgba(0, 0, 0, 0.2)"
},
},
],
},
],
};
// 应用配置
chart
.
setOption
(
option
);
console
.
log
(
"状态饼图初始化成功"
);
return
chart
;
},
// 处理窗口大小变化
handleResize
()
{
// 延迟执行以确保DOM已更新
setTimeout
(()
=>
{
if
(
this
.
charts
.
statusPie
)
this
.
charts
.
statusPie
.
resize
();
if
(
this
.
charts
.
oeeRank
)
this
.
charts
.
oeeRank
.
resize
();
if
(
this
.
charts
.
waitingLoss
)
this
.
charts
.
waitingLoss
.
resize
();
if
(
this
.
charts
.
oeeTrend
)
this
.
charts
.
oeeTrend
.
resize
();
if
(
this
.
charts
.
faultLoss
)
this
.
charts
.
faultLoss
.
resize
();
},
100
);
},
// 组件销毁前清理
beforeDestroy
()
{
window
.
removeEventListener
(
"resize"
,
this
.
handleResize
);
Object
.
values
(
this
.
charts
).
forEach
((
chart
)
=>
{
chart
&&
chart
.
dispose
();
});
},
// 初始化OEE排名图表
initOEERankChart
()
{
// 获取DOM元素
const
oeeRankChart
=
document
.
getElementById
(
"oeeRankChart"
);
if
(
!
oeeRankChart
)
{
console
.
error
(
"找不到OEE排名图表DOM元素"
);
return
null
;
}
console
.
log
(
"初始化OEE排名图表..."
);
// 初始化图表
const
chart
=
echarts
.
init
(
oeeRankChart
);
// 设置图表配置,保持原有样式
const
option
=
{
tooltip
:
{
trigger
:
"axis"
,
formatter
:
"{b}: {c}%"
,
axisPointer
:
{
type
:
"shadow"
,
},
},
grid
:
{
left
:
"3%"
,
right
:
"4%"
,
bottom
:
"3%"
,
containLabel
:
true
,
},
dataZoom
:
[{
type
:
'slider'
,
xAxisIndex
:
0
,
// 指定控制X轴:ml-citation{ref="4" data="citationList"}
start
:
0
,
// 初始显示起点:ml-citation{ref="1" data="citationList"}
end
:
40
,
// 初始显示终点(示例值,按需计算)
height
:
1
,
// 滚动条高度:ml-citation{ref="1" data="citationList"}
bottom
:
1
,
// 滚动条位置:ml-citation{ref="1" data="citationList"}
fillerColor
:
'rgba(84,112,198,0.6)'
,
// 选中区域颜色:ml-citation{ref="1" data="citationList"}
zoomLock
:
true
// 禁止缩放:ml-citation{ref="5" data="citationList"}
},
{
type
:
'inside'
,
// 内置滚动控制:ml-citation{ref="2" data="citationList"}
xAxisIndex
:
0
,
zoomOnMouseWheel
:
false
,
// 禁用滚轮缩放:ml-citation{ref="5" data="citationList"}
moveOnMouseWheel
:
true
// 启用滚轮平移:ml-citation{ref="5" data="citationList"}
}],
xAxis
:
{
type
:
"category"
,
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
,
interval
:
0
,
fontSize
:
10
,
align
:
'center'
},
data
:
[],
},
yAxis
:
{
type
:
"value"
,
max
:
100
,
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
,
formatter
:
"{value}%"
,
},
splitLine
:
{
lineStyle
:
{
color
:
"rgba(255,255,255,0.1)"
},
},
},
series
:
[
{
type
:
"bar"
,
barWidth
:
"40%"
,
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"#67C23A"
},
{
offset
:
1
,
color
:
"#409EFF"
},
]),
},
label
:
{
show
:
true
,
position
:
"top"
,
color
:
"#fff"
,
formatter
:
"{c}%"
,
},
data
:
[],
},
],
};
// 应用配置
chart
.
setOption
(
option
);
console
.
log
(
"OEE排名图表初始化成功"
);
return
chart
;
},
calculateRange
()
{
const
showNum
=
3
;
const
total
=
option
.
xAxis
.
data
.
length
;
return
total
<=
showNum
?
100
:
(
showNum
/
total
*
100
);
},
// 更新OEE排名图表
updateOEERankChart
(
isEmpty
=
false
)
{
if
(
!
this
.
charts
.
oeeRank
)
{
console
.
error
(
"OEE排名图表未初始化,无法更新"
);
return
;
}
// 处理OEE排名数据
let
deviceNames
=
[];
let
deviceValues
=
[];
let
hasRealData
=
false
;
// 添加标志,表示是否有真实数据
if
(
!
isEmpty
&&
this
.
apiData
.
oeeRankData
&&
this
.
apiData
.
oeeRankData
.
length
>
0
)
{
// 按照利用率排序(从高到低)
const
sortedData
=
this
.
apiData
.
oeeRankData
.
sort
((
a
,
b
)
=>
b
.
utilization_rate
-
a
.
utilization_rate
)
.
slice
(
0
,
5
);
// 只取前5个
// 提取设备名称和利用率值
sortedData
.
forEach
((
item
)
=>
{
// 修改这里:从所有设备列表中获取准确的设备名称
let
name
=
item
.
code
;
// 默认使用设备编码
// 在所有设备列表中查找匹配的设备
if
(
this
.
apiData
.
allEquipmentData
&&
this
.
apiData
.
allEquipmentData
.
length
>
0
)
{
const
matchedDevice
=
this
.
apiData
.
allEquipmentData
.
find
(
(
device
)
=>
device
.
code
===
item
.
code
);
// 如果找到匹配的设备,使用其名称
if
(
matchedDevice
&&
matchedDevice
.
name
)
{
name
=
matchedDevice
.
name
;
}
}
// 转换为百分比并保留一位小数
const
value
=
parseFloat
(
item
.
utilization_rate
.
toFixed
(
1
));
const
truncateBasic
=
(
str
)
=>
str
.
slice
(
0
,
12
);
deviceNames
.
push
(
truncateBasic
(
name
));
deviceValues
.
push
(
value
);
});
// 如果有真实数据,设置标志
if
(
deviceNames
.
length
>
0
)
{
hasRealData
=
true
;
}
console
.
log
(
"处理后的OEE排名数据:"
,
{
names
:
deviceNames
,
values
:
deviceValues
,
});
}
else
{
console
.
log
(
"没有OEE排名数据,显示暂无数据提示"
);
// 添加一个空数据,用于显示"暂无数据"提示
deviceNames
=
[
"暂无数据"
];
deviceValues
=
[
0
];
}
// 其余代码保持不变...
// 如果有真实数据,强制重新创建图表
if
(
hasRealData
)
{
// 销毁旧图表
this
.
charts
.
oeeRank
.
dispose
();
// 重新创建图表
this
.
charts
.
oeeRank
=
this
.
initOEERankChart
();
// 更新图表数据,不包含graphic属性
const
option
=
{
xAxis
:
{
data
:
deviceNames
,
},
series
:
[
{
data
:
deviceValues
,
},
],
};
// 应用更新
this
.
charts
.
oeeRank
.
setOption
(
option
);
}
else
{
// 没有真实数据,使用原来的逻辑
const
option
=
{
xAxis
:
{
data
:
deviceNames
,
},
series
:
[
{
data
:
deviceValues
,
},
],
graphic
:
[
{
type
:
"text"
,
left
:
"center"
,
top
:
"middle"
,
style
:
{
text
:
"暂无数据"
,
fill
:
"#999"
,
font
:
"14px Microsoft YaHei"
,
},
},
],
};
// 应用更新
this
.
charts
.
oeeRank
.
setOption
(
option
);
}
console
.
log
(
"OEE排名图表已更新,设备数:"
,
deviceNames
.
length
,
"是否有真实数据:"
,
hasRealData
);
},
// 初始化等待损失图表
initWaitingLossChart
()
{
// 获取DOM元素
const
waitingLossChart
=
document
.
getElementById
(
"waitingLossChart"
);
if
(
!
waitingLossChart
)
{
console
.
error
(
"找不到等待损失图表DOM元素"
);
return
null
;
}
console
.
log
(
"初始化等待损失图表..."
);
// 初始化图表
const
chart
=
echarts
.
init
(
waitingLossChart
);
// 设置图表配置
const
option
=
{
tooltip
:
{
trigger
:
"axis"
,
formatter
:
"{b}: {c}分钟"
,
axisPointer
:
{
type
:
"shadow"
,
},
},
grid
:
{
left
:
"5%"
,
// 增加左侧边距
right
:
"4%"
,
bottom
:
"3%"
,
containLabel
:
true
,
},
dataZoom
:
[{
type
:
'slider'
,
xAxisIndex
:
0
,
// 指定控制X轴:ml-citation{ref="4" data="citationList"}
start
:
0
,
// 初始显示起点:ml-citation{ref="1" data="citationList"}
end
:
40
,
// 初始显示终点(示例值,按需计算)
height
:
1
,
// 滚动条高度:ml-citation{ref="1" data="citationList"}
bottom
:
1
,
// 滚动条位置:ml-citation{ref="1" data="citationList"}
fillerColor
:
'rgba(84,112,198,0.6)'
,
// 选中区域颜色:ml-citation{ref="1" data="citationList"}
zoomLock
:
true
// 禁止缩放:ml-citation{ref="5" data="citationList"}
},
{
type
:
'inside'
,
// 内置滚动控制:ml-citation{ref="2" data="citationList"}
xAxisIndex
:
0
,
zoomOnMouseWheel
:
false
,
// 禁用滚轮缩放:ml-citation{ref="5" data="citationList"}
moveOnMouseWheel
:
true
// 启用滚轮平移:ml-citation{ref="5" data="citationList"}
}],
xAxis
:
{
type
:
"category"
,
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
,
interval
:
0
,
fontSize
:
10
,
align
:
'center'
},
data
:
[],
},
yAxis
:
{
type
:
"value"
,
name
:
"分钟"
,
nameGap
:
38
,
nameTextStyle
:
{
color
:
"#fff"
},
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
},
splitLine
:
{
lineStyle
:
{
color
:
"rgba(255,255,255,0.1)"
},
},
},
series
:
[
{
type
:
"bar"
,
barWidth
:
"40%"
,
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"#409EFF"
},
{
offset
:
1
,
color
:
"#36CE9E"
},
]),
},
label
:
{
show
:
true
,
position
:
"top"
,
color
:
"#fff"
,
formatter
:
"{c}分钟"
,
},
data
:
[],
},
],
};
// 应用配置
chart
.
setOption
(
option
);
console
.
log
(
"等待损失图表初始化成功"
);
return
chart
;
},
// 更新等待损失图表
updateWaitingLossChart
(
isEmpty
=
false
)
{
if
(
!
this
.
charts
.
waitingLoss
)
{
console
.
error
(
"等待损失图表未初始化,无法更新"
);
return
;
}
// 处理等待损失数据
let
deviceNames
=
[];
let
deviceValues
=
[];
let
hasRealData
=
false
;
// 添加标志,表示是否有真实数据
if
(
!
isEmpty
&&
this
.
apiData
.
waitingLossData
&&
this
.
apiData
.
waitingLossData
.
length
>
0
)
{
// 按照损失时间排序(从高到低)
const
sortedData
=
this
.
apiData
.
waitingLossData
.
sort
((
a
,
b
)
=>
b
.
analysis_loss
-
a
.
analysis_loss
)
.
slice
(
0
,
5
);
// 只取前5个
// 提取设备名称和损失时间值
sortedData
.
forEach
((
item
)
=>
{
// 修改这里:从所有设备列表中获取准确的设备名称
let
name
=
item
.
code
;
// 默认使用设备编码
// 在所有设备列表中查找匹配的设备
if
(
this
.
apiData
.
allEquipmentData
&&
this
.
apiData
.
allEquipmentData
.
length
>
0
)
{
const
matchedDevice
=
this
.
apiData
.
allEquipmentData
.
find
(
(
device
)
=>
device
.
code
===
item
.
code
);
// 如果找到匹配的设备,使用其名称
if
(
matchedDevice
&&
matchedDevice
.
name
)
{
name
=
matchedDevice
.
name
;
}
}
// 转换为分钟并保留整数
const
value
=
Math
.
round
(
item
.
analysis_loss
);
const
truncateBasic
=
(
str
)
=>
str
.
slice
(
0
,
12
);
deviceNames
.
push
(
truncateBasic
(
name
));
deviceValues
.
push
(
value
);
});
// 如果有真实数据,设置标志
if
(
deviceNames
.
length
>
0
)
{
hasRealData
=
true
;
}
console
.
log
(
"处理后的等待损失数据:"
,
{
names
:
deviceNames
,
values
:
deviceValues
,
});
}
else
{
console
.
log
(
"没有等待损失数据,显示暂无数据提示"
);
// 添加一个空数据,用于显示"暂无数据"提示
deviceNames
=
[
"暂无数据"
];
deviceValues
=
[
0
];
}
// 如果有真实数据,强制重新创建图表
if
(
hasRealData
)
{
// 销毁旧图表
this
.
charts
.
waitingLoss
.
dispose
();
// 重新创建图表
this
.
charts
.
waitingLoss
=
this
.
initWaitingLossChart
();
// 更新图表数据,不包含graphic属性
const
option
=
{
xAxis
:
{
data
:
deviceNames
,
},
series
:
[
{
data
:
deviceValues
,
},
],
};
// 应用更新
this
.
charts
.
waitingLoss
.
setOption
(
option
);
}
else
{
// 没有真实数据,使用原来的逻辑
const
option
=
{
xAxis
:
{
data
:
deviceNames
,
},
series
:
[
{
data
:
deviceValues
,
},
],
graphic
:
[
{
type
:
"text"
,
left
:
"center"
,
top
:
"middle"
,
style
:
{
text
:
"暂无数据"
,
fill
:
"#999"
,
font
:
"14px Microsoft YaHei"
,
},
},
],
};
// 应用更新
this
.
charts
.
waitingLoss
.
setOption
(
option
);
}
console
.
log
(
"等待损失图表已更新,设备数:"
,
deviceNames
.
length
,
"是否有真实数据:"
,
hasRealData
);
},
// 初始化故障损失图表
initFaultLossChart
()
{
// 获取DOM元素
const
faultLossChart
=
document
.
getElementById
(
"faultLossChart"
);
if
(
!
faultLossChart
)
{
console
.
error
(
"找不到故障损失图表DOM元素"
);
return
null
;
}
console
.
log
(
"初始化故障损失图表..."
);
// 初始化图表
const
chart
=
echarts
.
init
(
faultLossChart
);
// 设置图表配置
const
option
=
{
tooltip
:
{
trigger
:
"axis"
,
formatter
:
"{b}: {c}分钟"
,
axisPointer
:
{
type
:
"shadow"
,
},
},
grid
:
{
left
:
"5%"
,
// 增加左侧边距
right
:
"4%"
,
bottom
:
"3%"
,
containLabel
:
true
,
},
dataZoom
:
[{
type
:
'slider'
,
xAxisIndex
:
0
,
// 指定控制X轴:ml-citation{ref="4" data="citationList"}
start
:
0
,
// 初始显示起点:ml-citation{ref="1" data="citationList"}
end
:
40
,
// 初始显示终点(示例值,按需计算)
height
:
1
,
// 滚动条高度:ml-citation{ref="1" data="citationList"}
bottom
:
1
,
// 滚动条位置:ml-citation{ref="1" data="citationList"}
fillerColor
:
'rgba(84,112,198,0.6)'
,
// 选中区域颜色:ml-citation{ref="1" data="citationList"}
zoomLock
:
true
// 禁止缩放:ml-citation{ref="5" data="citationList"}
},
{
type
:
'inside'
,
// 内置滚动控制:ml-citation{ref="2" data="citationList"}
xAxisIndex
:
0
,
zoomOnMouseWheel
:
false
,
// 禁用滚轮缩放:ml-citation{ref="5" data="citationList"}
moveOnMouseWheel
:
true
// 启用滚轮平移:ml-citation{ref="5" data="citationList"}
}],
xAxis
:
{
type
:
"category"
,
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
,
interval
:
0
,
fontSize
:
10
,
align
:
'center'
},
data
:
[],
},
yAxis
:
{
type
:
"value"
,
name
:
"分钟"
,
nameGap
:
38
,
nameTextStyle
:
{
color
:
"#fff"
},
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
},
splitLine
:
{
lineStyle
:
{
color
:
"rgba(255,255,255,0.1)"
},
},
},
series
:
[
{
type
:
"bar"
,
barWidth
:
"40%"
,
itemStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"#F56C6C"
},
{
offset
:
1
,
color
:
"#E6A23C"
},
]),
},
label
:
{
show
:
true
,
position
:
"top"
,
color
:
"#fff"
,
formatter
:
"{c}分钟"
,
},
data
:
[],
},
],
};
// 应用配置
chart
.
setOption
(
option
);
console
.
log
(
"故障损失图表初始化成功"
);
return
chart
;
},
// 更新故障损失图表
updateFaultLossChart
(
isEmpty
=
false
)
{
if
(
!
this
.
charts
.
faultLoss
)
{
console
.
error
(
"故障损失图表未初始化,无法更新"
);
return
;
}
// 处理故障损失数据
let
deviceNames
=
[];
let
deviceValues
=
[];
let
hasRealData
=
false
;
// 添加标志,表示是否有真实数据
if
(
!
isEmpty
&&
this
.
apiData
.
faultLossData
&&
this
.
apiData
.
faultLossData
.
length
>
0
)
{
// 按照损失时间排序(从高到低)
const
sortedData
=
this
.
apiData
.
faultLossData
.
sort
((
a
,
b
)
=>
b
.
analysis_loss
-
a
.
analysis_loss
)
.
slice
(
0
,
5
);
// 只取前5个
console
.
log
(
"排序后的故障损失数据:"
,
JSON
.
stringify
(
sortedData
));
// 提取设备名称和损失时间值
sortedData
.
forEach
((
item
)
=>
{
// 修改这里:从所有设备列表中获取准确的设备名称
let
name
=
item
.
code
;
// 默认使用设备编码
// 在所有设备列表中查找匹配的设备
if
(
this
.
apiData
.
allEquipmentData
&&
this
.
apiData
.
allEquipmentData
.
length
>
0
)
{
const
matchedDevice
=
this
.
apiData
.
allEquipmentData
.
find
(
(
device
)
=>
device
.
code
===
item
.
code
);
// 如果找到匹配的设备,使用其名称
if
(
matchedDevice
&&
matchedDevice
.
name
)
{
name
=
matchedDevice
.
name
;
}
}
// 转换为分钟并保留整数
const
value
=
Math
.
round
(
item
.
analysis_loss
);
const
truncateBasic
=
(
str
)
=>
str
.
slice
(
0
,
12
);
deviceNames
.
push
(
truncateBasic
(
name
));
deviceValues
.
push
(
value
);
});
// 如果有真实数据,设置标志
if
(
deviceNames
.
length
>
0
)
{
hasRealData
=
true
;
}
console
.
log
(
"处理后的故障损失数据:"
,
{
names
:
deviceNames
,
values
:
deviceValues
,
});
}
else
{
console
.
log
(
"没有故障损失数据,显示暂无数据提示"
);
// 添加一个空数据,用于显示"暂无数据"提示
deviceNames
=
[
"暂无数据"
];
deviceValues
=
[
0
];
}
// 如果有真实数据,强制重新创建图表
if
(
hasRealData
)
{
// 销毁旧图表
this
.
charts
.
faultLoss
.
dispose
();
// 重新创建图表
this
.
charts
.
faultLoss
=
this
.
initFaultLossChart
();
// 更新图表数据,不包含graphic属性
const
option
=
{
xAxis
:
{
data
:
deviceNames
,
},
series
:
[
{
data
:
deviceValues
,
},
],
};
// 应用更新
this
.
charts
.
faultLoss
.
setOption
(
option
);
}
else
{
// 没有真实数据,使用原来的逻辑
const
option
=
{
xAxis
:
{
data
:
deviceNames
,
},
series
:
[
{
data
:
deviceValues
,
},
],
graphic
:
[
{
type
:
"text"
,
left
:
"center"
,
top
:
"middle"
,
style
:
{
text
:
"暂无数据"
,
fill
:
"#999"
,
font
:
"14px Microsoft YaHei"
,
},
},
],
};
// 应用更新
this
.
charts
.
faultLoss
.
setOption
(
option
);
}
console
.
log
(
"故障损失图表已更新,设备数:"
,
deviceNames
.
length
,
"是否有真实数据:"
,
hasRealData
);
},
// 使用默认设备数据
useDefaultDevices
()
{
// 根据不同的产线ID返回不同的设备列表
let
deviceList
=
[];
// 电源PCB产线
if
(
this
.
selectedLine
===
108
)
{
deviceList
=
[
{
id
:
1
,
name
:
"PCB贴片机"
,
code
:
"PCB001"
},
{
id
:
2
,
name
:
"PCB焊接机"
,
code
:
"PCB002"
},
{
id
:
3
,
name
:
"PCB检测仪"
,
code
:
"PCB003"
},
{
id
:
4
,
name
:
"PCB切割机"
,
code
:
"PCB004"
},
{
id
:
5
,
name
:
"PCB包装机"
,
code
:
"PCB005"
},
];
}
// S1产线
else
if
(
this
.
selectedLine
===
101
)
{
deviceList
=
[
{
id
:
6
,
name
:
"S1组装机"
,
code
:
"S1001"
},
{
id
:
7
,
name
:
"S1测试仪"
,
code
:
"S1002"
},
{
id
:
8
,
name
:
"S1包装机"
,
code
:
"S1003"
},
];
}
// 小米手表产线
else
if
(
this
.
selectedLine
===
123
)
{
deviceList
=
[
{
id
:
9
,
name
:
"手表屏幕组装机"
,
code
:
"W001"
},
{
id
:
10
,
name
:
"手表电池安装机"
,
code
:
"W002"
},
{
id
:
11
,
name
:
"手表测试仪"
,
code
:
"W003"
},
{
id
:
12
,
name
:
"手表包装机"
,
code
:
"W004"
},
];
}
// 其他产线使用默认设备
else
{
deviceList
=
[
{
id
:
1
,
name
:
"设备1"
,
code
:
"DEV001"
},
{
id
:
2
,
name
:
"设备2"
,
code
:
"DEV002"
},
{
id
:
3
,
name
:
"设备3"
,
code
:
"DEV003"
},
{
id
:
4
,
name
:
"设备4"
,
code
:
"DEV004"
},
{
id
:
5
,
name
:
"设备5"
,
code
:
"DEV005"
},
];
}
this
.
devices
=
deviceList
;
console
.
log
(
"使用默认设备列表:"
,
this
.
devices
);
// 默认选中第一个设备
if
(
this
.
devices
.
length
>
0
)
{
this
.
selectedDevice
=
this
.
devices
[
0
].
id
;
}
},
// 显示选择面板
showSelectionPanel
()
{
this
.
selectionPanelVisible
=
true
;
this
.
selectionStep
=
"workshop"
;
},
// 添加初始化拖拽功能的方法
initDraggable
()
{
const
floatingSelector
=
document
.
getElementById
(
"floatingSelector"
);
const
floatingButton
=
floatingSelector
.
querySelector
(
".floating-button"
);
let
isDragging
=
false
;
let
currentX
;
let
currentY
;
let
initialX
;
let
initialY
;
let
xOffset
=
20
;
// 初始化为左侧位置
let
yOffset
=
100
;
// 初始化为顶部位置
// 修改初始位置为左上角
floatingSelector
.
style
.
position
=
"fixed"
;
floatingSelector
.
style
.
top
=
yOffset
+
"px"
;
floatingSelector
.
style
.
left
=
xOffset
+
"px"
;
floatingSelector
.
style
.
right
=
"auto"
;
// 清除right属性
const
dragStart
=
(
e
)
=>
{
if
(
e
.
type
===
"touchstart"
)
{
initialX
=
e
.
touches
[
0
].
clientX
-
xOffset
;
initialY
=
e
.
touches
[
0
].
clientY
-
yOffset
;
}
else
{
initialX
=
e
.
clientX
-
xOffset
;
initialY
=
e
.
clientY
-
yOffset
;
}
if
(
e
.
target
===
floatingButton
||
e
.
target
.
parentNode
===
floatingButton
)
{
isDragging
=
true
;
floatingButton
.
style
.
cursor
=
"grabbing"
;
}
};
const
dragEnd
=
()
=>
{
isDragging
=
false
;
floatingButton
.
style
.
cursor
=
"grab"
;
};
const
drag
=
(
e
)
=>
{
if
(
isDragging
)
{
e
.
preventDefault
();
if
(
e
.
type
===
"touchmove"
)
{
currentX
=
e
.
touches
[
0
].
clientX
-
initialX
;
currentY
=
e
.
touches
[
0
].
clientY
-
initialY
;
}
else
{
currentX
=
e
.
clientX
-
initialX
;
currentY
=
e
.
clientY
-
initialY
;
}
xOffset
=
currentX
;
yOffset
=
currentY
;
// 限制拖动范围,防止按钮被拖出视口
const
maxX
=
window
.
innerWidth
-
floatingSelector
.
offsetWidth
;
const
maxY
=
window
.
innerHeight
-
floatingSelector
.
offsetHeight
;
xOffset
=
Math
.
min
(
Math
.
max
(
xOffset
,
0
),
maxX
);
yOffset
=
Math
.
min
(
Math
.
max
(
yOffset
,
0
),
maxY
);
// 更新位置
floatingSelector
.
style
.
top
=
yOffset
+
"px"
;
floatingSelector
.
style
.
left
=
xOffset
+
"px"
;
}
};
// 添加触摸事件监听器
floatingButton
.
addEventListener
(
"touchstart"
,
dragStart
,
false
);
document
.
addEventListener
(
"touchend"
,
dragEnd
,
false
);
document
.
addEventListener
(
"touchmove"
,
drag
,
false
);
// 添加鼠标事件监听器
floatingButton
.
addEventListener
(
"mousedown"
,
dragStart
,
false
);
document
.
addEventListener
(
"mouseup"
,
dragEnd
,
false
);
document
.
addEventListener
(
"mousemove"
,
drag
,
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"
,
"x-session-id"
:
"1d8a662231e9bbab315c736b7551657c18b44366"
,
},
});
// 处理JSON-RPC格式的响应
if
(
response
.
data
&&
response
.
data
.
result
&&
response
.
data
.
result
.
code
===
0
)
{
// 获取计划运行时间数据
const
planTimeList
=
response
.
data
.
result
.
data
||
{};
// 存储计划时间数据,供后续请求使用
this
.
apiData
.
planTimeData
=
planTimeList
;
console
.
log
(
"计划运行时间数据:"
,
JSON
.
stringify
(
planTimeList
));
return
planTimeList
;
}
else
{
const
errorMsg
=
response
.
data
.
result
?
response
.
data
.
result
.
message
:
"获取计划运行时间失败"
;
console
.
error
(
errorMsg
);
// 使用默认计划时间数据
this
.
useDefaultPlanTimeData
();
}
}
catch
(
error
)
{
console
.
error
(
"获取计划运行时间出错:"
,
error
);
// 使用默认计划时间数据
this
.
useDefaultPlanTimeData
();
}
},
// 使用默认计划时间数据
useDefaultPlanTimeData
()
{
// 创建默认的计划时间数据
this
.
apiData
.
planTimeData
=
{
// 根据实际需要设置默认值
// 例如:设备ID为键,计划时间为值
};
console
.
log
(
"使用默认计划运行时间数据"
);
},
// 获取初始设备状态数据
async
getInitialDeviceStates
()
{
console
.
log
(
"获取初始设备状态数据..."
);
try
{
// 筛选当前产线的设备数据
const
lineEquipment
=
this
.
filterEquipmentByLine
(
this
.
selectedLine
);
console
.
log
(
"当前产线设备数据:"
,
JSON
.
stringify
(
lineEquipment
));
// 存储当前产线的设备数据
this
.
apiData
.
currentLineEquipment
=
lineEquipment
;
// 更新设备选择器的设备列表
this
.
devices
=
lineEquipment
.
map
((
device
)
=>
({
id
:
device
.
code
,
name
:
device
.
name
||
device
.
code
,
}));
// 如果有设备,默认选择第一个设备
if
(
this
.
devices
.
length
>
0
)
{
this
.
selectedDevice
=
this
.
devices
[
0
].
id
;
}
else
{
this
.
selectedDevice
=
""
;
}
// 使用当前产线的设备编码,而不是所有设备
const
deviceList
=
[];
// 从当前产线的设备数据中获取设备编码
if
(
this
.
apiData
.
currentLineEquipment
&&
this
.
apiData
.
currentLineEquipment
.
length
>
0
)
{
this
.
apiData
.
currentLineEquipment
.
forEach
((
device
)
=>
{
if
(
device
.
code
)
{
deviceList
.
push
(
device
.
code
);
}
});
}
console
.
log
(
"初始设备编码列表:"
,
deviceList
);
// 获取计划时间数据
const
planTimeList
=
this
.
apiData
.
planTimeData
||
{};
// 检查参数
if
(
deviceList
.
length
===
0
)
{
console
.
warn
(
"没有可用的设备编码,无法获取初始设备状态"
);
return
;
}
console
.
log
(
"初始设备状态请求参数:"
,
{
planTimeList
,
deviceList
,
});
// 获取设备状态数据
await
this
.
getDeviceStateList
(
planTimeList
,
deviceList
);
// 获取初始OEE排名数据
const
{
startDate
,
endDate
}
=
this
.
getDateRange
(
this
.
selectedTime
);
await
this
.
getOEERankData
(
startDate
,
endDate
);
// 获取初始等待损失数据
const
waitingLossDates
=
this
.
getDateRange
(
this
.
timeRanges
.
waitingLoss
);
await
this
.
getLossData
(
waitingLossDates
.
startDate
,
waitingLossDates
.
endDate
,
"waiting"
);
// 获取初始故障损失数据
const
faultLossDates
=
this
.
getDateRange
(
this
.
timeRanges
.
faultLoss
);
await
this
.
getLossData
(
faultLossDates
.
startDate
,
faultLossDates
.
endDate
,
"fault"
);
// 获取初始OEE趋势数据
if
(
this
.
selectedDevice
)
{
const
oeeTrendDates
=
this
.
getDateRange
(
this
.
timeRanges
.
oeeTrend
);
await
this
.
getOEETrendData
(
this
.
selectedDevice
,
oeeTrendDates
.
startDate
,
oeeTrendDates
.
endDate
);
}
// 确保所有数据都已加载完成后,再更新图表
console
.
log
(
"所有数据加载完成,开始更新图表"
);
// 单独更新每个图表,确保它们都能正确显示
this
.
updateStatusPieChart
();
this
.
updateOEERankChart
();
this
.
updateWaitingLossChart
();
this
.
updateFaultLossChart
();
this
.
updateOEETrendChart
();
console
.
log
(
"所有图表已更新"
);
}
catch
(
error
)
{
console
.
error
(
"获取初始设备状态数据出错:"
,
error
);
this
.
$message
.
error
(
"获取初始设备状态数据失败,请稍后重试"
);
}
},
// 处理时间选择变化
async
handleTimeChange
()
{
console
.
log
(
"时间选择变化:"
,
this
.
selectedTime
);
// 获取选定时间范围的开始和结束日期
const
{
startDate
,
endDate
}
=
this
.
getDateRange
(
this
.
selectedTime
);
console
.
log
(
"时间范围:"
,
{
startDate
,
endDate
});
// 获取OEE排名数据
await
this
.
getOEERankData
(
startDate
,
endDate
);
// 调用updateChart方法更新OEE排名图表
this
.
updateChart
(
"oeeRank"
);
},
// 获取日期范围
getDateRange
(
timeOption
)
{
const
now
=
new
Date
();
let
startDate
,
endDate
;
// 格式化日期为 YYYY-MM-DD
const
formatDate
=
(
date
)
=>
{
const
year
=
date
.
getFullYear
();
const
month
=
String
(
date
.
getMonth
()
+
1
).
padStart
(
2
,
"0"
);
const
day
=
String
(
date
.
getDate
()).
padStart
(
2
,
"0"
);
return
`
${
year
}
-
${
month
}
-
${
day
}
`
;
};
switch
(
timeOption
)
{
case
"today"
:
// 今天
startDate
=
formatDate
(
now
);
endDate
=
formatDate
(
now
);
break
;
case
"week"
:
// 本周(周一到今天)
const
dayOfWeek
=
now
.
getDay
()
||
7
;
// 将周日的0转换为7
const
monday
=
new
Date
(
now
);
monday
.
setDate
(
now
.
getDate
()
-
(
dayOfWeek
-
1
));
startDate
=
formatDate
(
monday
);
endDate
=
formatDate
(
now
);
break
;
case
"month"
:
// 本月(1号到今天)
const
firstDayOfMonth
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
(),
1
);
startDate
=
formatDate
(
firstDayOfMonth
);
endDate
=
formatDate
(
now
);
break
;
case
"lastMonth"
:
// 上月(上月1号到上月最后一天)
const
firstDayOfLastMonth
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
()
-
1
,
1
);
const
lastDayOfLastMonth
=
new
Date
(
now
.
getFullYear
(),
now
.
getMonth
(),
0
);
startDate
=
formatDate
(
firstDayOfLastMonth
);
endDate
=
formatDate
(
lastDayOfLastMonth
);
break
;
default
:
// 默认为今天
startDate
=
formatDate
(
now
);
endDate
=
formatDate
(
now
);
}
return
{
startDate
,
endDate
};
},
// 获取OEE排名数据
async
getOEERankData
(
startDate
,
endDate
)
{
try
{
console
.
log
(
"获取OEE排名数据, 参数:"
,
{
startDate
,
endDate
,
});
// 使用当前产线的设备列表,而不是所有设备
const
deviceList
=
[];
// 从当前产线的设备数据中获取设备编码
if
(
this
.
apiData
.
currentLineEquipment
&&
this
.
apiData
.
currentLineEquipment
.
length
>
0
)
{
this
.
apiData
.
currentLineEquipment
.
forEach
((
device
)
=>
{
if
(
device
.
code
)
{
deviceList
.
push
(
device
.
code
);
}
});
}
console
.
log
(
`当前产线设备列表:`
,
deviceList
);
// 获取计划时间数据
const
planTimeList
=
this
.
apiData
.
planTimeData
||
{};
// 检查参数
if
(
deviceList
.
length
===
0
)
{
console
.
warn
(
"当前产线没有可用的设备编码,无法获取OEE排名数据"
);
// 设置空数据但不影响图表显示
this
.
apiData
.
oeeRankData
=
[];
// 更新OEE排名图表,显示"暂无数据"
this
.
updateOEERankChart
(
true
);
return
;
}
// 发送请求获取OEE排名数据
const
response
=
await
axios
({
method
:
"POST"
,
url
:
this
.
baseURL
+
"/public/device_efficiency/device_utilization_rate_sort"
,
data
:
{
factory_code
:
this
.
factory_code
||
"f1991dbb-52d6-11ef-b13f-fa163e5bb392"
,
plan_time_list
:
planTimeList
,
device_list
:
deviceList
,
start_date
:
startDate
,
end_date
:
endDate
,
},
headers
:
{
"Content-Type"
:
"application/json"
,
},
});
console
.
log
(
"OEE排名接口返回:"
,
JSON
.
stringify
(
response
.
data
));
// 处理响应
if
(
response
.
data
&&
response
.
data
.
code
===
200
)
{
// 处理OEE排名数据
const
oeeRankData
=
response
.
data
.
data
||
[];
// 存储OEE排名数据
this
.
apiData
.
oeeRankData
=
oeeRankData
;
if
(
this
.
apiData
.
allEquipmentData
&&
this
.
apiData
.
allEquipmentData
.
length
>
0
)
{
this
.
apiData
.
allEquipmentData
.
forEach
(
equipment
=>
{
equipment
.
utilization_rate
=
this
.
apiData
.
oeeRankData
.
find
(
(
device
)
=>
device
.
code
===
equipment
.
code
);
});
this
.
apiData
.
allEquipmentData
.
sort
((
a
,
b
)
=>
b
.
utilization_rate
-
a
.
utilization_rate
);
}
// 更新OEE排名图表
this
.
updateOEERankChart
();
return
oeeRankData
;
}
else
{
const
errorMsg
=
response
.
data
.
msg
||
"获取OEE排名数据失败"
;
console
.
error
(
errorMsg
);
this
.
$message
.
error
(
errorMsg
);
// 设置空数据但不影响图表显示
this
.
apiData
.
oeeRankData
=
[];
// 更新OEE排名图表,显示"暂无数据"
this
.
updateOEERankChart
(
true
);
}
}
catch
(
error
)
{
console
.
error
(
"获取OEE排名数据出错:"
,
error
);
this
.
$message
.
error
(
"获取OEE排名数据失败,请稍后重试"
);
// 设置空数据但不影响图表显示
this
.
apiData
.
oeeRankData
=
[];
// 更新OEE排名图表,显示"暂无数据"
this
.
updateOEERankChart
(
true
);
}
},
// 添加获取设备OEE趋势数据的方法
async
getOEETrendData
(
deviceCode
,
startDate
,
endDate
)
{
try
{
console
.
log
(
"获取设备OEE趋势数据, 参数:"
,
{
deviceCode
,
startDate
,
endDate
,
});
// 检查参数
if
(
!
deviceCode
)
{
console
.
warn
(
"没有设备编码,无法获取OEE趋势数据"
);
return
;
}
// 获取计划时间数据
const
planTimeList
=
this
.
apiData
.
planTimeData
||
{};
// 发送请求获取OEE趋势数据
const
response
=
await
axios
({
method
:
"POST"
,
url
:
this
.
baseURL
+
"/public/device_efficiency/device_oee_trend"
,
data
:
{
factory_code
:
this
.
factory_code
||
"f1991dbb-52d6-11ef-b13f-fa163e5bb392"
,
device_code
:
deviceCode
,
start_date
:
startDate
,
end_date
:
endDate
,
plan_time_list
:
planTimeList
,
},
headers
:
{
"Content-Type"
:
"application/json"
,
},
});
console
.
log
(
"OEE趋势接口返回:"
,
JSON
.
stringify
(
response
.
data
));
// 处理响应
if
(
response
.
data
&&
response
.
data
.
code
===
200
)
{
// 处理OEE趋势数据
const
oeeTrendData
=
response
.
data
.
data
||
[];
// 存储OEE趋势数据
this
.
apiData
.
oeeTrendData
=
oeeTrendData
;
return
oeeTrendData
;
}
else
{
const
errorMsg
=
response
.
data
.
msg
||
"获取OEE趋势数据失败"
;
console
.
error
(
errorMsg
);
this
.
$message
.
error
(
errorMsg
);
}
}
catch
(
error
)
{
console
.
error
(
"获取OEE趋势数据出错:"
,
error
);
this
.
$message
.
error
(
"获取OEE趋势数据失败,请稍后重试"
);
}
},
// 更新OEE趋势图表
updateOEETrendChart
()
{
if
(
!
this
.
charts
.
oeeTrend
)
{
console
.
error
(
"OEE趋势图表未初始化,无法更新"
);
return
;
}
console
.
log
(
"开始更新OEE趋势图表..."
);
console
.
log
(
"OEE趋势数据:"
,
JSON
.
stringify
(
this
.
apiData
.
oeeTrendData
));
// 处理OEE趋势数据
let
dates
=
[];
let
values
=
[];
if
(
this
.
apiData
.
oeeTrendData
&&
this
.
apiData
.
oeeTrendData
.
length
>
0
)
{
// 按照日期排序
const
sortedData
=
this
.
apiData
.
oeeTrendData
.
sort
((
a
,
b
)
=>
{
return
new
Date
(
a
.
date
)
-
new
Date
(
b
.
date
);
});
console
.
log
(
"排序后的OEE趋势数据:"
,
JSON
.
stringify
(
sortedData
));
// 提取日期和OEE值
sortedData
.
forEach
((
item
)
=>
{
// 格式化日期为MM-DD
const
date
=
new
Date
(
item
.
date
);
const
formattedDate
=
`
${
date
.
getMonth
()
+
1
}
-
${
date
.
getDate
()}
`
;
// 使用utilization_rate字段,而不是oee字段
// 转换为百分比并保留一位小数
const
value
=
parseFloat
((
item
.
utilization_rate
||
0
).
toFixed
(
1
));
dates
.
push
(
formattedDate
);
values
.
push
(
value
);
});
console
.
log
(
"处理后的OEE趋势数据:"
,
{
dates
,
values
,
});
}
else
{
console
.
log
(
"没有OEE趋势数据,使用空数据"
);
}
// 计算最大值,用于设置y轴的最大值
const
maxValue
=
Math
.
max
(...
values
,
100
);
// 至少为100%
const
yAxisMax
=
Math
.
ceil
(
maxValue
/
10
)
*
10
;
// 向上取整到最接近的10的倍数
// 更新图表数据
const
option
=
{
xAxis
:
{
data
:
dates
,
},
yAxis
:
{
max
:
yAxisMax
,
// 动态设置y轴最大值
},
series
:
[
{
data
:
values
,
},
],
};
// 应用更新
this
.
charts
.
oeeTrend
.
setOption
(
option
);
console
.
log
(
"OEE趋势图表已更新,日期数:"
,
dates
.
length
,
"最大值:"
,
maxValue
,
"Y轴最大值:"
,
yAxisMax
);
},
// 修改updateChart方法
async
updateChart
(
chartType
)
{
console
.
log
(
`更新图表:
${
chartType
}
`
);
// 根据图表类型执行不同的操作
switch
(
chartType
)
{
case
"oeeRank"
:
// 获取当前选择的时间范围
const
oeeTimeRange
=
this
.
timeRanges
.
oeeRank
;
console
.
log
(
"OEE排名时间范围:"
,
oeeTimeRange
);
// 获取对应的日期范围
const
oeeRankDates
=
this
.
getDateRange
(
oeeTimeRange
);
console
.
log
(
"OEE排名日期范围:"
,
oeeRankDates
);
// 获取新的OEE排名数据
await
this
.
getOEERankData
(
oeeRankDates
.
startDate
,
oeeRankDates
.
endDate
);
// 更新图表
this
.
updateOEERankChart
();
break
;
case
"waitingLoss"
:
// 获取当前选择的时间范围
const
waitingTimeRange
=
this
.
timeRanges
.
waitingLoss
;
console
.
log
(
"等待损失时间范围:"
,
waitingTimeRange
);
// 获取对应的日期范围
const
waitingLossDates
=
this
.
getDateRange
(
waitingTimeRange
);
console
.
log
(
"等待损失日期范围:"
,
waitingLossDates
);
// 获取新的等待损失数据
await
this
.
getLossData
(
waitingLossDates
.
startDate
,
waitingLossDates
.
endDate
,
"waiting"
);
// 更新图表
this
.
updateWaitingLossChart
();
break
;
case
"faultLoss"
:
// 获取当前选择的时间范围
const
faultTimeRange
=
this
.
timeRanges
.
faultLoss
;
console
.
log
(
"故障损失时间范围:"
,
faultTimeRange
);
// 获取对应的日期范围
const
faultLossDates
=
this
.
getDateRange
(
faultTimeRange
);
console
.
log
(
"故障损失日期范围:"
,
faultLossDates
);
// 获取新的故障损失数据
await
this
.
getLossData
(
faultLossDates
.
startDate
,
faultLossDates
.
endDate
,
"fault"
);
// 更新图表
this
.
updateFaultLossChart
();
break
;
case
"oeeTrend"
:
// 获取当前选择的时间范围
const
oeeTrendTimeRange
=
this
.
timeRanges
.
oeeTrend
;
console
.
log
(
"OEE趋势时间范围:"
,
oeeTrendTimeRange
);
// 获取对应的日期范围
const
oeeTrendDates
=
this
.
getDateRange
(
oeeTrendTimeRange
);
console
.
log
(
"OEE趋势日期范围:"
,
oeeTrendDates
);
// 获取当前选择的设备
const
deviceCode
=
this
.
selectedDevice
;
console
.
log
(
"当前选择的设备:"
,
deviceCode
);
// 获取新的OEE趋势数据
await
this
.
getOEETrendData
(
deviceCode
,
oeeTrendDates
.
startDate
,
oeeTrendDates
.
endDate
);
// 更新图表
this
.
updateOEETrendChart
();
break
;
case
"statusPie"
:
this
.
updateStatusPieChart
();
break
;
default
:
console
.
warn
(
`未知的图表类型:
${
chartType
}
`
);
}
},
// 初始化OEE趋势图表
initOEETrendChart
()
{
// 获取DOM元素
const
oeeTrendChart
=
document
.
getElementById
(
"oeeTrendChart"
);
if
(
!
oeeTrendChart
)
{
console
.
error
(
"找不到OEE趋势图表DOM元素"
);
return
null
;
}
console
.
log
(
"初始化OEE趋势图表..."
);
// 初始化图表
const
chart
=
echarts
.
init
(
oeeTrendChart
);
// 设置图表配置
const
option
=
{
tooltip
:
{
trigger
:
"axis"
,
formatter
:
"{b}: {c}%"
,
axisPointer
:
{
type
:
"line"
,
},
},
grid
:
{
left
:
"5%"
,
right
:
"4%"
,
bottom
:
"3%"
,
containLabel
:
true
,
},
xAxis
:
{
type
:
"category"
,
boundaryGap
:
false
,
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
},
data
:
[],
},
yAxis
:
{
type
:
"value"
,
name
:
"OEE(%)"
,
nameGap
:
38
,
nameTextStyle
:
{
color
:
"#fff"
},
min
:
0
,
// 移除max限制,让图表自动计算最大值
axisLine
:
{
lineStyle
:
{
color
:
"#fff"
}
},
axisLabel
:
{
color
:
"#fff"
},
splitLine
:
{
lineStyle
:
{
color
:
"rgba(255,255,255,0.1)"
},
},
},
series
:
[
{
name
:
"OEE"
,
type
:
"line"
,
smooth
:
true
,
symbol
:
"circle"
,
symbolSize
:
8
,
itemStyle
:
{
color
:
"#409EFF"
,
},
lineStyle
:
{
width
:
3
,
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
1
,
0
,
[
{
offset
:
0
,
color
:
"#409EFF"
},
{
offset
:
1
,
color
:
"#36CE9E"
},
]),
},
areaStyle
:
{
color
:
new
echarts
.
graphic
.
LinearGradient
(
0
,
0
,
0
,
1
,
[
{
offset
:
0
,
color
:
"rgba(64, 158, 255, 0.5)"
},
{
offset
:
1
,
color
:
"rgba(64, 158, 255, 0)"
},
]),
},
label
:
{
show
:
true
,
position
:
"top"
,
color
:
"#fff"
,
formatter
:
"{c}%"
,
},
data
:
[],
},
],
};
// 应用配置
chart
.
setOption
(
option
);
console
.
log
(
"OEE趋势图表初始化成功"
);
return
chart
;
},
// 获取损失数据(等待损失和故障损失共用)
async
getLossData
(
startDate
,
endDate
,
lossType
)
{
try
{
console
.
log
(
`获取
${
lossType
===
"waiting"
?
"等待"
:
"故障"
}
损失数据, 参数:`
,
{
startDate
,
endDate
,
lossType
,
});
// 使用当前产线的设备列表,而不是所有设备
const
deviceList
=
[];
// 从当前产线的设备数据中获取设备编码
if
(
this
.
apiData
.
currentLineEquipment
&&
this
.
apiData
.
currentLineEquipment
.
length
>
0
)
{
this
.
apiData
.
currentLineEquipment
.
forEach
((
device
)
=>
{
if
(
device
.
code
)
{
deviceList
.
push
(
device
.
code
);
}
});
}
console
.
log
(
`当前产线设备列表:`
,
deviceList
);
// 获取计划时间数据
const
planTimeList
=
this
.
apiData
.
planTimeData
||
{};
// 检查参数
if
(
deviceList
.
length
===
0
)
{
console
.
warn
(
`当前产线没有可用的设备编码,无法获取
${
lossType
===
"waiting"
?
"等待"
:
"故障"
}
损失数据`
);
// 设置空数据但不影响图表显示
if
(
lossType
===
"waiting"
)
{
this
.
apiData
.
waitingLossData
=
[];
// 更新等待损失图表,显示"暂无数据"
this
.
updateWaitingLossChart
(
true
);
}
else
{
this
.
apiData
.
faultLossData
=
[];
// 更新故障损失图表,显示"暂无数据"
this
.
updateFaultLossChart
(
true
);
}
return
;
}
// 发送请求获取损失数据
const
response
=
await
axios
({
method
:
"POST"
,
url
:
this
.
baseURL
+
"/public/device_efficiency/device_analysis_loss"
,
data
:
{
factory_code
:
this
.
factory_code
||
"f1991dbb-52d6-11ef-b13f-fa163e5bb392"
,
plan_time_list
:
planTimeList
,
device_list
:
deviceList
,
start_date
:
startDate
,
end_date
:
endDate
,
loss_type
:
lossType
,
},
headers
:
{
"Content-Type"
:
"application/json"
,
},
});
console
.
log
(
`
${
lossType
===
"waiting"
?
"等待"
:
"故障"
}
损失接口返回:`
,
JSON
.
stringify
(
response
.
data
)
);
// 处理响应
if
(
response
.
data
&&
response
.
data
.
code
===
200
)
{
// 处理损失数据
const
lossData
=
response
.
data
.
data
||
[];
// 存储损失数据
if
(
lossType
===
"waiting"
)
{
this
.
apiData
.
waitingLossData
=
lossData
;
}
else
{
this
.
apiData
.
faultLossData
=
lossData
;
}
return
lossData
;
}
else
{
const
errorMsg
=
response
.
data
.
msg
||
`获取
${
lossType
===
"waiting"
?
"等待"
:
"故障"
}
损失数据失败`
;
console
.
error
(
errorMsg
);
this
.
$message
.
error
(
errorMsg
);
// 设置空数据但不影响图表显示
if
(
lossType
===
"waiting"
)
{
this
.
apiData
.
waitingLossData
=
[];
// 更新等待损失图表,显示"暂无数据"
this
.
updateWaitingLossChart
(
true
);
}
else
{
this
.
apiData
.
faultLossData
=
[];
// 更新故障损失图表,显示"暂无数据"
this
.
updateFaultLossChart
(
true
);
}
}
}
catch
(
error
)
{
console
.
error
(
`获取
${
lossType
===
"waiting"
?
"等待"
:
"故障"
}
损失数据出错:`
,
error
);
this
.
$message
.
error
(
`获取
${
lossType
===
"waiting"
?
"等待"
:
"故障"
}
损失数据失败,请稍后重试`
);
// 设置空数据但不影响图表显示
if
(
lossType
===
"waiting"
)
{
this
.
apiData
.
waitingLossData
=
[];
// 更新等待损失图表,显示"暂无数据"
this
.
updateWaitingLossChart
(
true
);
}
else
{
this
.
apiData
.
faultLossData
=
[];
// 更新故障损失图表,显示"暂无数据"
this
.
updateFaultLossChart
(
true
);
}
}
},
// 获取设备状态数据
async
getDeviceStates
()
{
try
{
console
.
log
(
"获取设备状态数据..."
);
// 使用当前产线的设备列表,而不是所有设备
const
deviceList
=
[];
// 从当前产线的设备数据中获取设备编码
if
(
this
.
apiData
.
currentLineEquipment
&&
this
.
apiData
.
currentLineEquipment
.
length
>
0
)
{
this
.
apiData
.
currentLineEquipment
.
forEach
((
device
)
=>
{
if
(
device
.
code
)
{
deviceList
.
push
(
device
.
code
);
}
});
}
console
.
log
(
`当前产线设备列表:`
,
deviceList
);
// 检查参数
if
(
deviceList
.
length
===
0
)
{
console
.
warn
(
"当前产线没有可用的设备编码,无法获取设备状态数据"
);
// 设置空数据但不影响图表显示
this
.
apiData
.
deviceStates
=
[];
// 更新状态饼图,显示"暂无数据"
this
.
updateStatusPieChart
(
true
);
return
;
}
// 发送请求获取设备状态数据
const
response
=
await
axios
({
method
:
"POST"
,
url
:
this
.
baseURL
+
"/public/device_efficiency/device_state"
,
data
:
{
factory_code
:
this
.
factory_code
||
"f1991dbb-52d6-11ef-b13f-fa163e5bb392"
,
device_list
:
deviceList
,
},
headers
:
{
"Content-Type"
:
"application/json"
,
},
});
console
.
log
(
"设备状态接口返回:"
,
JSON
.
stringify
(
response
.
data
));
// 处理响应
if
(
response
.
data
&&
response
.
data
.
code
===
200
)
{
// 处理设备状态数据
const
deviceStates
=
response
.
data
.
data
||
[];
// 存储设备状态数据
this
.
apiData
.
deviceStates
=
deviceStates
;
// 更新状态饼图
this
.
updateStatusPieChart
();
return
deviceStates
;
}
else
{
const
errorMsg
=
response
.
data
.
msg
||
"获取设备状态数据失败"
;
console
.
error
(
errorMsg
);
this
.
$message
.
error
(
errorMsg
);
// 设置空数据但不影响图表显示
this
.
apiData
.
deviceStates
=
[];
// 更新状态饼图,显示"暂无数据"
this
.
updateStatusPieChart
(
true
);
}
}
catch
(
error
)
{
console
.
error
(
"获取设备状态数据出错:"
,
error
);
this
.
$message
.
error
(
"获取设备状态数据失败,请稍后重试"
);
// 设置空数据但不影响图表显示
this
.
apiData
.
deviceStates
=
[];
// 更新状态饼图,显示"暂无数据"
this
.
updateStatusPieChart
(
true
);
}
},
},
});
</script>
<style>
/* 基础样式 */
*
{
margin
:
0
;
padding
:
0
;
box-sizing
:
border-box
;
}
body
{
background
:
url("/roke_workstation_api/static/html/routing/image/bg-ok.png")
no-repeat
center
center
fixed
;
background-size
:
cover
;
color
:
#fff
;
min-height
:
100vh
;
}
#app
{
padding
:
20px
10px
;
}
/* 标题样式 */
.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
;
}
/* 浮动选择器样式 */
.floating-selector
{
position
:
fixed
;
top
:
100px
;
left
:
20px
;
/* 改为left而不是right */
z-index
:
1000
;
}
/* 修改浮动按钮样式,增加拖动时的视觉反馈 */
.floating-button
{
background-color
:
rgba
(
6
,
32
,
65
,
0.9
);
color
:
#fff
;
width
:
45px
;
height
:
45px
;
border-radius
:
50%
;
box-shadow
:
0
2px
12px
rgba
(
0
,
0
,
0
,
0.5
),
0
0
15px
rgba
(
64
,
158
,
255
,
0.5
);
cursor
:
grab
;
user-select
:
none
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
transition
:
all
0.3s
ease
;
border
:
2px
solid
rgba
(
64
,
158
,
255
,
0.5
);
animation
:
pulse
2s
infinite
;
}
.floating-button
:active
{
cursor
:
grabbing
;
transform
:
scale
(
0.95
);
}
.floating-button
i
{
font-size
:
22px
;
color
:
#409eff
;
}
/* 添加脉冲动画使按钮更醒目 */
@keyframes
pulse
{
0
%
{
box-shadow
:
0
0
0
0
rgba
(
64
,
158
,
255
,
0.7
);
}
70
%
{
box-shadow
:
0
0
0
10px
rgba
(
64
,
158
,
255
,
0
);
}
100
%
{
box-shadow
:
0
0
0
0
rgba
(
64
,
158
,
255
,
0
);
}
}
/* 选择面板样式 */
.selection-panel
{
position
:
absolute
;
top
:
50px
;
left
:
0
;
width
:
200px
;
background-color
:
rgba
(
6
,
32
,
65
,
0.95
);
border-radius
:
4px
;
box-shadow
:
0
2px
12px
rgba
(
0
,
0
,
0
,
0.3
);
overflow
:
hidden
;
border
:
1px
solid
rgba
(
64
,
158
,
255
,
0.2
);
}
.panel-header
{
display
:
flex
;
align-items
:
center
;
padding
:
10px
;
border-bottom
:
1px
solid
rgba
(
255
,
255
,
255
,
0.1
);
}
.back-button
,
.close-button
{
width
:
24px
;
height
:
24px
;
display
:
flex
;
align-items
:
center
;
justify-content
:
center
;
cursor
:
pointer
;
color
:
#fff
;
}
.panel-title
{
flex
:
1
;
text-align
:
center
;
color
:
#fff
;
font-size
:
14px
;
}
.panel-content
{
max-height
:
300px
;
overflow-y
:
auto
;
}
.selection-list
{
padding
:
5px
0
;
}
.selection-item
{
padding
:
10px
15px
;
color
:
#fff
;
cursor
:
pointer
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.selection-item
:hover
{
background-color
:
rgba
(
64
,
158
,
255
,
0.2
);
}
/* 图表网格布局 */
.chart-grid
{
display
:
grid
;
grid-template-columns
:
1
fr
1
fr
1
fr
;
grid-template-rows
:
auto
auto
;
gap
:
20px
;
padding
:
0
10px
;
margin-top
:
20px
;
width
:
100%
;
}
/* 第一行的三个图表 */
.chart-grid
>
div
:nth-child
(
1
)
{
grid-column
:
1
;
grid-row
:
1
;
}
.chart-grid
>
div
:nth-child
(
2
)
{
grid-column
:
2
;
grid-row
:
1
;
}
.chart-grid
>
div
:nth-child
(
3
)
{
grid-column
:
3
;
grid-row
:
1
;
}
/* 第二行的两个图表 */
.chart-grid
>
div
:nth-child
(
4
)
{
grid-column
:
1
/
span
2
;
grid-row
:
2
;
}
.chart-grid
>
div
:nth-child
(
5
)
{
grid-column
:
3
;
grid-row
:
2
;
}
/* 图表卡片样式 */
.chart-card
{
background
:
rgba
(
6
,
32
,
65
,
0.9
);
border-radius
:
4px
;
padding
:
12px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0.2
);
width
:
100%
;
box-sizing
:
border-box
;
overflow
:
hidden
;
}
.card-header
{
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
margin-bottom
:
15px
;
height
:
32px
;
}
.card-header
h3
{
font-size
:
14px
;
color
:
#fff
;
margin
:
0
;
position
:
relative
;
padding-left
:
15px
;
white-space
:
nowrap
;
overflow
:
hidden
;
text-overflow
:
ellipsis
;
max-width
:
70%
;
}
.card-header
h3
::before
{
content
:
""
;
position
:
absolute
;
left
:
0
;
top
:
50%
;
transform
:
translateY
(
-50%
);
width
:
4px
;
height
:
14px
;
background-color
:
#409eff
;
}
.current-date
{
color
:
#fff
;
font-size
:
14px
;
white-space
:
nowrap
;
}
.header-controls
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
}
.chart-container
{
height
:
280px
;
width
:
100%
;
overflow
:
hidden
;
}
/* Element UI 样式覆盖 */
.el-select
{
width
:
100px
;
display
:
flex
;
align-items
:
center
;
}
.el-select
.el-input__inner
{
height
:
28px
;
line-height
:
28px
;
background-color
:
rgba
(
0
,
0
,
0
,
0.2
);
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.1
);
color
:
#fff
!important
;
}
.el-select
.el-input__icon
{
color
:
#fff
!important
;
}
.el-select-dropdown
{
background-color
:
rgba
(
6
,
32
,
65
,
0.95
)
!important
;
border
:
1px
solid
rgba
(
255
,
255
,
255
,
0.1
)
!important
;
}
.el-select-dropdown__item
{
color
:
#fff
!important
;
}
.el-select-dropdown__item.hover
,
.el-select-dropdown__item
:hover
{
background-color
:
rgba
(
64
,
158
,
255
,
0.2
)
!important
;
}
.el-select-dropdown__item.selected
{
color
:
#409eff
!important
;
background-color
:
rgba
(
64
,
158
,
255
,
0.1
)
!important
;
}
.el-input.is-focus
.el-input__inner
,
.el-input.is-focus
.el-input__inner
:focus
{
color
:
#fff
!important
;
}
/* 修改单位标签样式 */
.unit-label
{
color
:
#fff
;
font-size
:
12px
;
margin-left
:
10px
;
display
:
flex
;
align-items
:
center
;
height
:
28px
;
}
/* 添加当前选择信息样式 */
.current-selection
{
padding
:
10px
15px
;
background-color
:
rgba
(
64
,
158
,
255
,
0.1
);
border-bottom
:
1px
solid
rgba
(
255
,
255
,
255
,
0.1
);
}
.selection-info
{
display
:
flex
;
justify-content
:
space-between
;
margin
:
3px
0
;
}
.info-label
{
color
:
rgba
(
255
,
255
,
255
,
0.7
);
font-size
:
12px
;
}
.info-value
{
color
:
#409eff
;
font-size
:
12px
;
font-weight
:
bold
;
}
/* 选中项样式 */
.selection-item.selected
{
background-color
:
rgba
(
64
,
158
,
255
,
0.2
);
color
:
#409eff
;
}
</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