Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
D
DC-TOM
Project
Project
Details
Activity
Releases
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
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
刘照晖
DC-TOM
Commits
6dd78db8
Commit
6dd78db8
authored
May 23, 2025
by
liuzhaoh
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'dev' of
http://app.bmetech.com/liuzhaohui/dctomproject
into dev
parents
a5e2a398
bbb317a3
Changes
11
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
2360 additions
and
308 deletions
+2360
-308
package.json
package.json
+1
-0
index.js
src/router/index.js
+12
-1
chart.js
src/utils/chart.js
+194
-48
collectorList.vue
src/views/collectorList/collectorList.vue
+498
-0
chart-line.vue
src/views/collectorList/components/chart-line.vue
+70
-0
map-svg.vue
src/views/dashboard/components/map-svg.vue
+2
-1
bag-monitoring.vue
src/views/dustMonitoring/bag-monitoring.vue
+610
-0
addDustCollectorDialog.vue
src/views/dustOverview/components/addDustCollectorDialog.vue
+281
-0
roomSettingDialog.vue
src/views/dustOverview/components/roomSettingDialog.vue
+209
-0
valveSettingDialog.vue
src/views/dustOverview/components/valveSettingDialog.vue
+371
-0
index.vue
src/views/dustOverview/index.vue
+112
-258
No files found.
package.json
View file @
6dd78db8
...
...
@@ -9,6 +9,7 @@
"preview"
:
"vite preview"
},
"dependencies"
:
{
"
@element-plus/icons-vue
"
:
"
^2.3.1
"
,
"
axios
"
:
"
^1.9.0
"
,
"
crypto-js
"
:
"
^4.2.0
"
,
"
echarts
"
:
"
^5.6.0
"
,
...
...
src/router/index.js
View file @
6dd78db8
...
...
@@ -2,13 +2,14 @@ import { createWebHistory, createRouter } from 'vue-router'
import
Dashboard
from
'../views/dashboard/index.vue'
import
DustOverview
from
'../views/dustOverview/index.vue'
import
HomeView
from
'../views/HomeView.vue'
import
AboutView
from
'../views/AboutView.vue'
import
CollectorList
from
'../views/collectorList/collectorList.vue'
import
User
from
'../views/user.vue'
import
Layout
from
'../layout/index.vue'
import
Login
from
'../views/login/index.vue'
import
equipmentManagement
from
'../views/equipmentManagement/index.vue'
import
dustMonitoring
from
'../views/dustMonitoring/index.vue'
import
bagMonitoring
from
'../views/dustMonitoring/bag-monitoring.vue'
import
myAgency
from
'@/views/closeManage/myAgency.vue'
;
import
myDone
from
'@/views/closeManage/myDone.vue'
;
...
...
@@ -36,6 +37,16 @@ const routes = [
path
:
'/monitor'
,
component
:
dustMonitoring
,
meta
:
{
title
:
'除尘器监控'
},
},
{
path
:
'/collectorList'
,
component
:
CollectorList
,
meta
:
{
title
:
'布袋周期'
},
},
{
path
:
'/bag-monitor'
,
component
:
bagMonitoring
,
meta
:
{
title
:
'BME布袋监测'
},
},
{
path
:
'/alerts'
,
...
...
src/utils/chart.js
View file @
6dd78db8
import
{
color
}
from
"echarts"
;
export
const
getLineOption
=
(
xData
=
[],
seriesData
=
[])
=>
({
tooltip
:
{
trigger
:
'axis'
,
},
grid
:
{
left
:
'3%'
,
right
:
'4%'
,
bottom
:
'15%'
,
top
:
'5%'
,
containLabel
:
true
,
},
legend
:
{
data
:
[
'健康度指数(%)'
],
bottom
:
'0%'
,
icon
:
"circle"
,
itemWidth
:
10
,
itemHeight
:
10
,
itemGap
:
10
,
},
xAxis
:
{
type
:
'category'
,
axisTick
:
{
show
:
false
,
},
axisLine
:
{
lineStyle
:
{
color
:
'#E9ECF2'
,
},
tooltip
:
{
trigger
:
"axis"
,
},
grid
:
{
left
:
"3%"
,
right
:
"4%"
,
bottom
:
"15%"
,
top
:
"5%"
,
containLabel
:
true
,
},
legend
:
{
data
:
[
"健康度指数(%)"
],
bottom
:
"0%"
,
icon
:
"circle"
,
itemWidth
:
10
,
itemHeight
:
10
,
itemGap
:
10
,
},
xAxis
:
{
type
:
"category"
,
axisTick
:
{
show
:
false
,
},
axisLine
:
{
lineStyle
:
{
color
:
"#E9ECF2"
,
},
axisLabel
:
{
color
:
'rgba(0,0,0,0.6)'
,
},
axisLabel
:
{
color
:
"rgba(0,0,0,0.6)"
,
},
data
:
xData
,
},
yAxis
:
{
type
:
"value"
,
axisLabel
:
{
color
:
"rgba(0,0,0,0.6)"
,
},
splitLine
:
{
lineStyle
:
{
color
:
"#E9ECF2"
,
type
:
"dashed"
,
},
data
:
xData
,
},
},
series
:
[
{
name
:
"健康度指数(%)"
,
type
:
"line"
,
color
:
"#399DFA"
,
data
:
seriesData
,
smooth
:
true
,
},
],
});
export
const
getDustLineOption
=
(
xData
=
[],
seriesData
=
[])
=>
({
tooltip
:
{
trigger
:
"axis"
,
},
grid
:
{
left
:
"3%"
,
right
:
"4%"
,
bottom
:
"15%"
,
top
:
"5%"
,
containLabel
:
true
,
},
legend
:
{
data
:
[
"健康度指数(%)"
],
bottom
:
"0%"
,
icon
:
"circle"
,
itemWidth
:
10
,
itemHeight
:
10
,
itemGap
:
10
,
},
xAxis
:
{
type
:
"category"
,
axisTick
:
{
show
:
false
,
},
yAxis
:
{
type
:
'value'
,
axisLabel
:
{
color
:
'rgba(0,0,0,0.6)'
,
axisLine
:
{
lineStyle
:
{
color
:
"#E9ECF2"
,
},
splitLine
:
{
lineStyle
:
{
color
:
'#E9ECF2'
,
type
:
'dashed'
,
},
},
axisLabel
:
{
color
:
"rgba(0,0,0,0.6)"
,
},
data
:
xData
,
},
yAxis
:
{
type
:
"value"
,
axisLabel
:
{
color
:
"rgba(0,0,0,0.6)"
,
},
splitLine
:
{
lineStyle
:
{
color
:
"#E9ECF2"
,
type
:
"dashed"
,
},
},
},
series
:
[
{
name
:
""
,
type
:
"line"
,
color
:
"#399DFA"
,
data
:
seriesData
,
smooth
:
true
,
},
],
});
// 布袋监测图表配置
export
const
getBagMonitoringChartOption
=
(
xData
=
[],
seriesData
=
[])
=>
({
tooltip
:
{
trigger
:
"axis"
,
axisPointer
:
{
type
:
"line"
,
lineStyle
:
{
color
:
"#6E7079"
,
width
:
1
,
},
},
},
grid
:
{
left
:
"20"
,
right
:
"20"
,
bottom
:
"30"
,
top
:
"3%"
,
containLabel
:
true
,
},
xAxis
:
{
type
:
"category"
,
boundaryGap
:
false
,
axisLine
:
{
lineStyle
:
{
color
:
"#E9ECF2"
,
},
},
axisLabel
:
{
color
:
"rgba(0,0,0,0.6)"
,
fontSize
:
12
,
interval
:
"auto"
,
rotate
:
0
,
},
axisTick
:
{
show
:
false
,
},
data
:
xData
,
},
yAxis
:
{
type
:
"value"
,
min
:
0
,
max
:
600
,
interval
:
200
,
axisLabel
:
{
color
:
"rgba(0,0,0,0.6)"
,
formatter
:
"{value}"
,
},
series
:
[
{
name
:
'健康度指数(%)'
,
type
:
'line'
,
color
:
'#399DFA'
,
data
:
seriesData
,
smooth
:
true
,
splitLine
:
{
lineStyle
:
{
color
:
"#E9ECF2"
,
type
:
"dashed"
,
},
],
});
\ No newline at end of file
},
},
series
:
[
{
name
:
"监测值"
,
type
:
"line"
,
smooth
:
false
,
symbol
:
"none"
,
sampling
:
"average"
,
itemStyle
:
{
color
:
"#7B68EE"
,
},
lineStyle
:
{
width
:
1
,
},
areaStyle
:
{
color
:
{
type
:
"linear"
,
x
:
0
,
y
:
0
,
x2
:
0
,
y2
:
1
,
colorStops
:
[
{
offset
:
0
,
color
:
"rgba(123, 104, 238, 0.4)"
,
},
{
offset
:
1
,
color
:
"rgba(123, 104, 238, 0.1)"
,
},
],
},
},
data
:
seriesData
,
},
],
});
src/views/collectorList/collectorList.vue
0 → 100644
View file @
6dd78db8
This diff is collapsed.
Click to expand it.
src/views/collectorList/components/chart-line.vue
0 → 100644
View file @
6dd78db8
<
template
>
<div
ref=
"chartDustRef"
class=
"chart-line"
></div>
</
template
>
<
script
setup
>
import
{
onMounted
,
onBeforeUnmount
,
ref
,
watch
,
nextTick
}
from
"vue"
;
import
*
as
echarts
from
"echarts"
;
import
{
getDustLineOption
}
from
"@/utils/chart"
;
const
props
=
defineProps
({
// 表格数据
dustLineInfo
:
{
type
:
Array
,
required
:
true
,
default
:
()
=>
[],
},
});
const
chartDustRef
=
ref
(
null
);
let
chartInstance
=
null
;
const
initChart
=
()
=>
{
if
(
chartDustRef
.
value
)
{
let
xData
=
[];
let
seriesData
=
[];
if
(
props
.
dustLineInfo
)
{
props
.
dustLineInfo
.
forEach
((
i
)
=>
{
xData
.
push
(
i
.
time
);
seriesData
.
push
(
i
.
value
);
});
}
chartInstance
=
echarts
.
init
(
chartDustRef
.
value
);
const
option
=
getDustLineOption
(
xData
,
seriesData
);
nextTick
(()
=>
{
// DOM 更新完成后执行
chartInstance
.
setOption
(
option
);
});
}
};
watch
(
()
=>
props
.
dustLineInfo
,
()
=>
{
initChart
();
console
.
log
(
"props.dustLineInfo"
,
props
.
dustLineInfo
);
},
{
deep
:
true
}
);
onMounted
(()
=>
{
initChart
();
window
.
addEventListener
(
"resize"
,
()
=>
{
chartInstance
?.
resize
();
});
});
onBeforeUnmount
(()
=>
{
chartInstance
?.
dispose
();
});
</
script
>
<
style
lang=
"scss"
scoped
>
.chart-line
{
canvas
{
width
:
100%
;
height
:
100%
;
}
}
</
style
>
src/views/dashboard/components/map-svg.vue
View file @
6dd78db8
...
...
@@ -234,6 +234,7 @@ onBeforeUnmount(() => {
&
.active
{
box-shadow
:
0
0
10px
rgba
(
255
,
255
,
255
,
0
.5
);
z-index
:
100
;
}
.pulse
{
...
...
@@ -284,7 +285,7 @@ onBeforeUnmount(() => {
padding
:
12px
;
color
:
#fff
;
pointer-events
:
none
;
z-index
:
3
;
z-index
:
999
;
.text-status-normal
{
color
:
#00ff9d
!
important
;
...
...
src/views/dustMonitoring/bag-monitoring.vue
0 → 100644
View file @
6dd78db8
This diff is collapsed.
Click to expand it.
src/views/dustOverview/components/addDustCollectorDialog.vue
0 → 100644
View file @
6dd78db8
<
template
>
<el-dialog
:model-value=
"modelValue"
title=
"新增除尘器"
width=
"460px"
:close-on-click-modal=
"false"
:close-on-press-escape=
"false"
@
update:model-value=
"$emit('update:modelValue', $event)"
>
<div
class=
"add-dust-form"
>
<div
class=
"form-item"
>
<div
class=
"selector-wrap"
>
<el-select
v-model=
"formData.dustType"
placeholder=
"除尘器选择"
style=
"width: 238px"
>
<el-option
v-for=
"item in dustTypeOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
<el-button
type=
"primary"
size=
"default"
@
click=
"selectDustType"
>
选择
</el-button>
</div>
</div>
<div
class=
"form-content"
>
<el-form
ref=
"formRef"
:model=
"formData"
label-width=
"100px"
:rules=
"rules"
>
<el-form-item
label=
"除尘器名称"
prop=
"name"
>
<el-input
v-model=
"formData.name"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"除尘器编号"
prop=
"code"
>
<el-input
v-model=
"formData.code"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"所属工序"
prop=
"process"
>
<el-select
v-model=
"formData.process"
placeholder=
"工序选择"
>
<el-option
v-for=
"item in processOptions"
:key=
"item.value"
:label=
"item.label"
:value=
"item.value"
/>
</el-select>
</el-form-item>
<el-form-item
label=
"仓室数量"
prop=
"chamberCount"
>
<el-input
v-model
.
number=
"formData.chamberCount"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"电磁阀数量"
prop=
"valveCount"
>
<el-input
v-model
.
number=
"formData.valveCount"
placeholder=
"请输入"
/>
</el-form-item>
<el-form-item
label=
"合理压差范围"
prop=
"pressureRange"
>
<div
class=
"range-input"
>
<el-input
v-model
.
number=
"formData.pressureMin"
placeholder=
"请输入"
/>
<span
class=
"separator"
>
~
</span>
<el-input
v-model
.
number=
"formData.pressureMax"
placeholder=
"请输入"
/>
</div>
</el-form-item>
<el-form-item
label=
"服务器IP"
prop=
"serverIp"
>
<el-input
v-model=
"formData.serverIp"
placeholder=
"请输入"
/>
</el-form-item>
</el-form>
</div>
<div
class=
"form-footer"
>
<el-button
@
click=
"cancel"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm"
>
确认
</el-button>
</div>
</div>
</el-dialog>
</
template
>
<
script
setup
>
import
{
ref
,
reactive
}
from
'vue'
;
const
props
=
defineProps
({
modelValue
:
{
type
:
Boolean
,
default
:
false
}
});
const
emit
=
defineEmits
([
'update:modelValue'
,
'save'
]);
const
formRef
=
ref
(
null
);
const
dustTypeOptions
=
[
{
value
:
1
,
label
:
'除尘器类型1'
},
{
value
:
2
,
label
:
'除尘器类型2'
},
{
value
:
3
,
label
:
'除尘器类型3'
}
];
const
processOptions
=
[
{
value
:
1
,
label
:
'转炉'
},
{
value
:
2
,
label
:
'炼铁'
}
];
const
formData
=
reactive
({
dustType
:
''
,
name
:
''
,
code
:
''
,
process
:
''
,
chamberCount
:
null
,
valveCount
:
null
,
pressureMin
:
null
,
pressureMax
:
null
,
serverIp
:
''
});
const
rules
=
{
name
:
[{
required
:
true
,
message
:
'请输入除尘器名称'
,
trigger
:
'blur'
}],
code
:
[{
required
:
true
,
message
:
'请输入除尘器编号'
,
trigger
:
'blur'
}],
process
:
[{
required
:
true
,
message
:
'请选择所属工序'
,
trigger
:
'change'
}],
chamberCount
:
[
{
required
:
true
,
message
:
'请输入仓室数量'
,
trigger
:
'blur'
},
{
type
:
'number'
,
min
:
1
,
message
:
'仓室数量必须大于0'
,
trigger
:
'blur'
}
],
valveCount
:
[
{
required
:
true
,
message
:
'请输入电磁阀数量'
,
trigger
:
'blur'
},
{
type
:
'number'
,
min
:
1
,
message
:
'电磁阀数量必须大于0'
,
trigger
:
'blur'
}
],
pressureMin
:
[
{
required
:
true
,
message
:
'请输入最小压差'
,
trigger
:
'blur'
},
{
type
:
'number'
,
message
:
'必须为数字'
,
trigger
:
'blur'
}
],
pressureMax
:
[
{
required
:
true
,
message
:
'请输入最大压差'
,
trigger
:
'blur'
},
{
type
:
'number'
,
message
:
'必须为数字'
,
trigger
:
'blur'
}
],
serverIp
:
[
{
required
:
true
,
message
:
'请输入服务器IP'
,
trigger
:
'blur'
},
{
pattern
:
/^
(\d{1,3}\.){3}\d{1,3}
$/
,
message
:
'IP地址格式不正确'
,
trigger
:
'blur'
}
]
};
const
selectDustType
=
()
=>
{
// 这里可以根据选择的除尘器类型预填充表单数据
// 如果有预设模板数据,可以在这里设置
console
.
log
(
'Selected dust type:'
,
formData
.
dustType
);
};
const
submitForm
=
()
=>
{
if
(
!
formRef
.
value
)
return
;
formRef
.
value
.
validate
((
valid
)
=>
{
if
(
valid
)
{
// 组装压差范围
const
pressureRange
=
`
${
formData
.
pressureMin
}
~
${
formData
.
pressureMax
}
`
;
// 准备提交的数据
const
submitData
=
{
dustType
:
formData
.
dustType
,
deviceName
:
formData
.
name
,
deviceNo
:
formData
.
code
,
process
:
formData
.
process
,
healthScore
:
formData
.
chamberCount
,
// 仓室数量保存在healthScore
health2Score
:
formData
.
valveCount
,
// 电磁阀数量保存在health2Score
lastAlarmTime
:
pressureRange
,
// 合理压差范围保存在lastAlarmTime字段
serverIp
:
formData
.
serverIp
,
// 生成默认的状态矩阵
status
:
generateDefaultStatus
(
formData
.
chamberCount
)
};
// 发送数据到父组件处理保存逻辑
emit
(
'save'
,
submitData
);
emit
(
'update:modelValue'
,
false
);
}
else
{
console
.
error
(
'Form validation failed'
);
return
false
;
}
});
};
const
cancel
=
()
=>
{
// 重置表单状态和数据
formRef
.
value
.
resetFields
();
formData
.
dustType
=
''
;
formData
.
name
=
''
;
formData
.
code
=
''
;
formData
.
process
=
''
;
formData
.
chamberCount
=
null
;
formData
.
valveCount
=
null
;
emit
(
'update:modelValue'
,
false
);
};
// 生成默认的状态矩阵
const
generateDefaultStatus
=
(
totalChambers
)
=>
{
// 默认状态:正常(status=1),每排最多5个仓室
const
rows
=
Math
.
ceil
(
totalChambers
/
5
);
const
result
=
[];
let
chamberIndex
=
1
;
for
(
let
rowIndex
=
0
;
rowIndex
<
rows
;
rowIndex
++
)
{
const
row
=
[];
const
chambersInRow
=
Math
.
min
(
5
,
totalChambers
-
rowIndex
*
5
);
for
(
let
i
=
0
;
i
<
chambersInRow
;
i
++
)
{
row
.
push
({
index
:
chamberIndex
++
,
status
:
1
,
// 正常状态
count
:
1
// 默认阀门数量
});
}
result
.
push
(
row
);
}
return
result
;
};
</
script
>
<
style
lang=
"scss"
scoped
>
.add-dust-form
{
.form-item
{
display
:
flex
;
align-items
:
center
;
margin-bottom
:
20px
;
padding
:
0
0
0
50px
;
box-sizing
:
border-box
;
.number-badge
{
width
:
24px
;
height
:
24px
;
line-height
:
24px
;
text-align
:
center
;
background-color
:
#f8cd4e
;
color
:
#000
;
border-radius
:
4px
;
font-weight
:
bold
;
margin-right
:
10px
;
}
.selector-wrap
{
display
:
flex
;
align-items
:
center
;
gap
:
10px
;
flex
:
1
;
}
}
.form-content
{
padding
:
0
30px
;
.range-input
{
display
:
flex
;
align-items
:
center
;
.separator
{
margin
:
0
5px
;
color
:
#606266
;
}
}
}
.form-footer
{
margin-top
:
20px
;
display
:
flex
;
justify-content
:
center
;
.el-button
{
width
:
120px
;
}
}
}
:deep
(
.el-form-item__label
)
{
font-size
:
14px
;
color
:
#606266
;
}
:deep
(
.el-input
)
{
width
:
100%
;
}
:deep
(
.el-select
)
{
width
:
100%
;
}
</
style
>
\ No newline at end of file
src/views/dustOverview/components/roomSettingDialog.vue
0 → 100644
View file @
6dd78db8
<
template
>
<el-dialog
:model-value=
"modelValue"
title=
"仓室数量设置"
width=
"500px"
:close-on-click-modal=
"false"
:close-on-press-escape=
"false"
@
update:model-value=
"$emit('update:modelValue', $event)"
@
open=
"initializeState"
>
<el-form
:model=
"roomForm"
label-width=
"120px"
>
<el-form-item
label=
"仓室数量"
>
<el-input
v-model=
"roomForm.totalRooms"
controls-position=
"right"
style=
"width: 150px"
disabled
/>
</el-form-item>
<el-form-item
label=
"仓室分几排"
>
<el-input-number
v-model=
"roomForm.rows"
:min=
"1"
:max=
"20"
controls-position=
"right"
@
change=
"handleRowsChange"
/>
</el-form-item>
</el-form>
<!-- 分布表格 -->
<div
class=
"distribution-table"
>
<div
class=
"table-title"
>
仓室数量分布
<div
v-if=
"distributionDiff !== 0"
:class=
"[
'distribution-warning',
distributionDiff > 0 ? 'warning-less' : 'warning-less',
]"
>
当前仓室总数
{{
distributionDiff
>
0
?
"大于"
:
"小于"
}}
默认仓室数量
{{
Math
.
abs
(
distributionDiff
)
}}
个,请修改。
</div>
</div>
<el-table
:data=
"distributionData"
style=
"width: 100%"
size=
"small"
border
>
<el-table-column
prop=
"row"
label=
"排"
width=
"180"
/>
<el-table-column
prop=
"count"
label=
"仓数量"
>
<template
#
default=
"
{ row }">
<el-input-number
v-model=
"row.count"
:min=
"1"
:max=
"50"
controls-position=
"right"
size=
"small"
@
change=
"handleDistributionChange"
/>
</
template
>
</el-table-column>
</el-table>
</div>
<
template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"$emit('update:modelValue', false)"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"handleConfirm"
:disabled=
"distributionDiff !== 0"
>
确认
</el-button>
</span>
</
template
>
</el-dialog>
</template>
<
script
setup
>
import
{
ref
,
computed
,
watch
}
from
"vue"
;
const
props
=
defineProps
({
modelValue
:
Boolean
,
totalRooms
:
{
type
:
Number
,
required
:
true
},
initialDistribution
:
{
type
:
Array
,
default
:
()
=>
[]
}
});
const
emit
=
defineEmits
([
"update:modelValue"
,
"confirm"
]);
const
roomForm
=
ref
({
totalRooms
:
0
,
rows
:
0
});
const
distributionData
=
ref
([]);
const
initializeState
=
()
=>
{
if
(
props
.
modelValue
)
{
roomForm
.
value
.
totalRooms
=
props
.
totalRooms
;
if
(
props
.
initialDistribution
&&
props
.
initialDistribution
.
length
>
0
)
{
roomForm
.
value
.
rows
=
props
.
initialDistribution
.
length
;
distributionData
.
value
=
props
.
initialDistribution
.
map
((
row
,
index
)
=>
({
row
:
`第
${
index
+
1
}
排`
,
count
:
row
.
length
}));
}
else
{
roomForm
.
value
.
rows
=
0
;
distributionData
.
value
=
[];
}
}
};
watch
(()
=>
props
.
modelValue
,
(
newVal
)
=>
{
if
(
newVal
)
{
initializeState
();
}
},
{
immediate
:
true
});
const
distributionDiff
=
computed
(()
=>
{
const
total
=
distributionData
.
value
.
reduce
(
(
sum
,
item
)
=>
sum
+
item
.
count
,
0
);
return
total
-
roomForm
.
value
.
totalRooms
;
});
const
handleRowsChange
=
(
value
)
=>
{
const
total
=
roomForm
.
value
.
totalRooms
;
if
(
!
total
||
!
value
)
return
;
// 计算每行的基础数量(向下取整)和余数
const
baseValue
=
Math
.
floor
(
total
/
value
);
const
remainder
=
total
%
value
;
// 生成分布数据
distributionData
.
value
=
Array
.
from
({
length
:
value
},
(
_
,
index
)
=>
{
return
{
row
:
`第
${
index
+
1
}
排`
,
count
:
index
<
remainder
?
baseValue
+
1
:
baseValue
};
});
};
const
handleDistributionChange
=
()
=>
{
// 分布数量变化时不需要特殊处理,distributionDiff会自动计算
};
const
handleConfirm
=
()
=>
{
if
(
distributionDiff
.
value
===
0
)
{
emit
(
"confirm"
,
distributionData
.
value
);
emit
(
"update:modelValue"
,
false
);
}
};
</
script
>
<
style
lang=
"scss"
scoped
>
.dialog-footer
{
display
:
flex
;
justify-content
:
center
;
gap
:
12px
;
}
.distribution-table
{
margin-top
:
20px
;
border-top
:
1px
solid
#ebeef5
;
padding-top
:
20px
;
}
.table-title
{
font-size
:
14px
;
color
:
#2182a0
;
margin-bottom
:
16px
;
font-weight
:
500
;
}
.distribution-warning
{
display
:
inline-block
;
font-size
:
13px
;
margin-left
:
12px
;
padding
:
1px
8px
;
box-sizing
:
border-box
;
border-radius
:
4px
;
&
.warning-more
{
color
:
#e6a23c
;
background-color
:
#fdf6ec
;
}
&
.warning-less
{
color
:
#f56c6c
;
background-color
:
#fef0f0
;
}
}
:deep
(
.el-input-number.el-input-number--small
)
{
width
:
130px
;
}
:deep
(
.distribution-table
.el-table--border
.el-table__cell
)
{
border-right
:
1px
solid
#ebeef5
!
important
;
}
</
style
>
\ No newline at end of file
src/views/dustOverview/components/valveSettingDialog.vue
0 → 100644
View file @
6dd78db8
<
template
>
<el-dialog
:model-value=
"modelValue"
title=
"电磁阀数量设置"
width=
"800px"
:close-on-click-modal=
"false"
:close-on-press-escape=
"false"
@
update:model-value=
"$emit('update:modelValue', $event)"
@
open=
"initializeState"
>
<div
class=
"valve-setting"
>
<div
class=
"setting-row"
>
<div
class=
"input-group"
>
<span>
电磁阀总数量:
</span>
<el-input
v-model=
"valveForm.total"
style=
"width: 120px"
disabled
/>
</div>
<el-button
type=
"primary"
@
click=
"handleAverageDistribute"
>
平均分布生成
</el-button
>
</div>
<div
class=
"table-title"
>
仓室脉冲阀数量分布
</div>
<div
class=
"setting-row"
>
<div
class=
"input-group"
>
<span>
仓室编号:
</span>
<el-select
v-model=
"valveForm.roomNo"
style=
"width: 120px"
@
change=
"handleRoomChange"
>
<el-option
v-for=
"cell in statusData"
:key=
"cell.index"
:label=
"`$
{cell.index}仓`"
:value="cell.index"
/>
</el-select>
</div>
<div
class=
"input-group"
>
<span>
电磁阀分布数量:
</span>
<el-input-number
v-model=
"valveForm.valveCount"
:min=
"1"
controls-position=
"right"
style=
"width: 120px"
:class=
"
{ 'is-error': errorMessage }"
/>
</div>
<div
class=
"input-group"
>
<span>
布阀数量:
</span>
<el-input-number
v-model=
"valveForm.distribution"
:min=
"1"
controls-position=
"right"
style=
"width: 120px"
/>
</div>
</div>
<!-- 错误提示 -->
<div
v-if=
"errorMessage"
class=
"error-message"
>
<i
class=
"el-icon-warning"
></i>
{{
errorMessage
}}
</div>
<div>
<el-button
type=
"primary"
class=
"save-btn"
@
click=
"handleSave"
:disabled=
"!!errorMessage || valveForm.valveCount === null || valveForm.distribution === null"
>
保存
</el-button>
</div>
</div>
<!-- 分布表格 -->
<div
class=
"distribution-table"
>
<div
class=
"table-title"
>
仓室脉冲阀数量分布
<span
class=
"valve-count-info"
>
(当前分配:
{{
currentTotalValves
}}
/
{{
valveForm
.
total
}}
)
</span>
</div>
<div
class=
"valve-distribution-grid"
>
<div
class=
"valve-row"
>
<div
v-for=
"(cell, colIndex) in statusData"
:key=
"colIndex"
class=
"valve-cell"
:class=
"
{ 'active-cell': cell.index === valveForm.roomNo }"
@click="valveForm.roomNo = cell.index; handleRoomChange(cell.index)"
>
<div
class=
"cell-index"
>
{{
cell
.
index
}}
仓
</div>
<div
class=
"cell-value"
>
{{
cell
.
count
}}
</div>
</div>
</div>
</div>
</div>
<template
#
footer
>
<span
class=
"dialog-footer"
>
<el-button
@
click=
"$emit('update:modelValue', false)"
>
取消
</el-button>
<el-button
type=
"primary"
@
click=
"handleConfirm"
:disabled=
"!!errorMessage"
>
保存
</el-button>
</span>
</
template
>
</el-dialog>
</template>
<
script
setup
>
import
{
ref
,
watch
,
computed
}
from
"vue"
;
const
props
=
defineProps
({
modelValue
:
Boolean
,
totalValves
:
{
type
:
Number
,
required
:
true
},
statusData
:
{
type
:
Array
,
default
:
()
=>
[]
}
});
const
emit
=
defineEmits
([
"update:modelValue"
,
"confirm"
]);
const
valveForm
=
ref
({
total
:
0
,
rows
:
0
,
roomNo
:
0
,
valveCount
:
0
,
distribution
:
0
});
const
statusData
=
ref
([]);
const
errorMessage
=
ref
(
""
);
// 计算当前已分配的电磁阀总数量
const
currentTotalValves
=
computed
(()
=>
{
return
statusData
.
value
.
reduce
((
sum
,
cell
)
=>
sum
+
cell
.
count
,
0
);
});
// 计算当前修改后的总数量
const
projectedTotalValves
=
computed
(()
=>
{
// 找到当前修改的仓室
const
selectedRoom
=
statusData
.
value
.
find
(
cell
=>
cell
.
index
===
valveForm
.
value
.
roomNo
);
if
(
!
selectedRoom
)
return
currentTotalValves
.
value
;
// 计算替换后的总数
const
diff
=
valveForm
.
value
.
valveCount
-
selectedRoom
.
count
;
return
currentTotalValves
.
value
+
diff
;
});
// 验证是否超过总数限制
const
validateValveCount
=
computed
(()
=>
{
if
(
projectedTotalValves
.
value
>
valveForm
.
value
.
total
)
{
return
{
valid
:
false
,
message
:
`超出电磁阀总数限制,当前配置后总数为
${
projectedTotalValves
.
value
}
,超出限制
${
projectedTotalValves
.
value
-
valveForm
.
value
.
total
}
个`
};
}
return
{
valid
:
true
,
message
:
""
};
});
const
initializeState
=
()
=>
{
if
(
props
.
modelValue
)
{
valveForm
.
value
.
total
=
props
.
totalValves
;
statusData
.
value
=
[...
props
.
statusData
];
// 深拷贝状态数据
errorMessage
.
value
=
""
;
// 如果有数据,默认选中第一个仓室
if
(
statusData
.
value
.
length
>
0
)
{
valveForm
.
value
.
roomNo
=
statusData
.
value
[
0
].
index
;
valveForm
.
value
.
valveCount
=
statusData
.
value
[
0
].
count
;
valveForm
.
value
.
distribution
=
statusData
.
value
[
0
].
count
;
// 默认布阀数量等于电磁阀数量
}
}
};
// 当选中的仓室改变时,更新电磁阀分布数量
const
handleRoomChange
=
(
roomIndex
)
=>
{
const
selectedRoom
=
statusData
.
value
.
find
(
cell
=>
cell
.
index
===
roomIndex
);
if
(
selectedRoom
)
{
valveForm
.
value
.
valveCount
=
selectedRoom
.
count
;
valveForm
.
value
.
distribution
=
selectedRoom
.
count
;
errorMessage
.
value
=
""
;
}
};
watch
(()
=>
props
.
modelValue
,
(
newVal
)
=>
{
if
(
newVal
)
{
initializeState
();
}
},
{
immediate
:
true
});
// 监听阀门数量变化,实时验证
watch
(()
=>
valveForm
.
value
.
valveCount
,
()
=>
{
const
validation
=
validateValveCount
.
value
;
errorMessage
.
value
=
validation
.
valid
?
""
:
validation
.
message
;
});
const
handleAverageDistribute
=
()
=>
{
if
(
!
statusData
.
value
||
statusData
.
value
.
length
===
0
)
return
;
const
total
=
Number
(
valveForm
.
value
.
total
)
||
0
;
const
totalRooms
=
statusData
.
value
.
length
;
if
(
totalRooms
===
0
||
total
===
0
)
return
;
// 计算基础平均值(向下取整)和余数
const
baseValue
=
Math
.
floor
(
total
/
totalRooms
);
const
remainder
=
total
%
totalRooms
;
// 平均分配阀数量 多余的阀数量分配到前面的仓室
statusData
.
value
.
forEach
((
cell
,
index
)
=>
{
cell
.
count
=
baseValue
+
(
index
<
remainder
?
1
:
0
);
});
// 更新当前选中仓室的值
handleRoomChange
(
valveForm
.
value
.
roomNo
);
errorMessage
.
value
=
""
;
// 重置错误信息
};
const
handleSave
=
()
=>
{
// 验证是否超出限制
const
validation
=
validateValveCount
.
value
;
console
.
log
(
valveForm
.
value
.
valveCount
);
if
(
!
validation
.
valid
)
{
errorMessage
.
value
=
validation
.
message
;
return
;
}
// 保存当前选中仓室的电磁阀分布数量
const
selectedRoom
=
statusData
.
value
.
find
(
cell
=>
cell
.
index
===
valveForm
.
value
.
roomNo
);
if
(
selectedRoom
)
{
selectedRoom
.
count
=
valveForm
.
value
.
valveCount
;
// 更新错误信息
errorMessage
.
value
=
""
;
}
};
const
handleConfirm
=
()
=>
{
// 最终确认时再次验证总数是否符合要求
const
totalValves
=
statusData
.
value
.
reduce
((
sum
,
cell
)
=>
sum
+
cell
.
count
,
0
);
if
(
totalValves
>
valveForm
.
value
.
total
)
{
errorMessage
.
value
=
`电磁阀总数超出限制
${
totalValves
-
valveForm
.
value
.
total
}
个,请调整后再提交`
;
return
;
}
emit
(
"confirm"
,
statusData
.
value
);
emit
(
"update:modelValue"
,
false
);
};
</
script
>
<
style
lang=
"scss"
scoped
>
.dialog-footer
{
display
:
flex
;
justify-content
:
center
;
gap
:
12px
;
}
.valve-setting
{
.setting-row
{
display
:
flex
;
align-items
:
center
;
gap
:
20px
;
margin-bottom
:
20px
;
.input-group
{
display
:
flex
;
align-items
:
center
;
gap
:
8px
;
span
{
white-space
:
nowrap
;
color
:
#606266
;
}
}
}
.save-btn
{
display
:
block
;
width
:
120px
;
margin
:
0
auto
;
}
}
.error-message
{
color
:
#F56C6C
;
background-color
:
#FEF0F0
;
padding
:
8px
16px
;
border-radius
:
4px
;
margin
:
10px
0
;
font-size
:
14px
;
display
:
flex
;
align-items
:
center
;
i
{
margin-right
:
8px
;
font-size
:
16px
;
}
}
.distribution-table
{
margin-top
:
20px
;
border-top
:
1px
solid
#ebeef5
;
padding-top
:
20px
;
}
.table-title
{
font-size
:
14px
;
color
:
#2182a0
;
margin-bottom
:
16px
;
font-weight
:
500
;
.valve-count-info
{
font-size
:
12px
;
color
:
#909399
;
margin-left
:
8px
;
}
}
.valve-distribution-grid
{
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
padding
:
12px
;
background
:
#f5f7fa
;
border-radius
:
4px
;
.valve-row
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
8px
;
}
.valve-cell
{
width
:
41px
;
background
:
white
;
padding
:
4px
;
border-radius
:
4px
;
text-align
:
center
;
border
:
1px
solid
#dcdfe6
;
transition
:
all
0
.3s
;
cursor
:
pointer
;
&
.active-cell
{
box-shadow
:
0
0
10px
0
rgba
(
64
,
158
,
255
,
0
.8
);
transform
:
scale
(
1
.05
);
}
.cell-index
{
font-size
:
12px
;
color
:
#909399
;
margin-bottom
:
8px
;
}
.cell-value
{
font-size
:
14px
;
color
:
#606266
;
}
}
}
:deep
(
.is-error
)
{
.el-input__inner
{
border-color
:
#F56C6C
;
}
.el-input-number__decrease
,
.el-input-number__increase
{
border-color
:
#F56C6C
;
}
}
</
style
>
\ No newline at end of file
src/views/dustOverview/index.vue
View file @
6dd78db8
This diff is collapsed.
Click to expand it.
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