2026-02-12 21:19:23 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div class="ems-dashboard-editor-container" style="background-color:#ffffff">
|
|
|
|
|
|
<div class="header-row">
|
|
|
|
|
|
<div class="title-block">
|
|
|
|
|
|
<div class="page-title">单站监控项目点位配置</div>
|
2026-02-13 21:46:12 +08:00
|
|
|
|
<div class="page-desc">
|
|
|
|
|
|
<span class="site-label">站点:</span>
|
|
|
|
|
|
<span>{{ siteName || siteId || '-' }}</span>
|
|
|
|
|
|
</div>
|
2026-02-12 21:19:23 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<el-button @click="goBack">返回</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="saveData">保存</el-button>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-alert
|
|
|
|
|
|
title="当前字段清单与点位映射均从后端表读取,保存后可直接用于单站监控查询。"
|
|
|
|
|
|
type="info"
|
|
|
|
|
|
:closable="false"
|
|
|
|
|
|
style="margin-top: 16px;"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<el-tabs v-model="activeTopMenu" style="margin-top: 16px;">
|
|
|
|
|
|
<el-tab-pane
|
|
|
|
|
|
v-for="topMenu in topMenuList"
|
|
|
|
|
|
:key="topMenu.code"
|
|
|
|
|
|
:label="topMenu.name"
|
|
|
|
|
|
:name="topMenu.code"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
v-if="!topMenu.children || topMenu.children.length === 0"
|
|
|
|
|
|
class="common-table"
|
|
|
|
|
|
:data="topMenu.items"
|
|
|
|
|
|
stripe
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table-column prop="section" label="分组" min-width="180" />
|
|
|
|
|
|
<el-table-column prop="name" label="页面展示名称" min-width="280" />
|
|
|
|
|
|
<el-table-column prop="field" label="字段名" min-width="260" />
|
2026-02-13 21:46:12 +08:00
|
|
|
|
<el-table-column label="点位" min-width="300">
|
2026-02-12 21:19:23 +08:00
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model.trim="scope.row.point"
|
|
|
|
|
|
placeholder="请输入点位"
|
|
|
|
|
|
clearable
|
2026-02-13 21:46:12 +08:00
|
|
|
|
class="short-input"
|
2026-02-12 21:19:23 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-02-13 21:46:12 +08:00
|
|
|
|
<el-table-column label="固定展示值" min-width="300">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model.trim="scope.row.fixedValue"
|
|
|
|
|
|
placeholder="请输入固定展示值"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
class="short-input"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="固定展示" width="120" align="center">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-checkbox v-model="scope.row.useFixedDisplay" :true-label="1" :false-label="0" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="操作" width="120" fixed="right">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
@click="handleDeleteItem(scope.$index, topMenu.items)"
|
|
|
|
|
|
>
|
|
|
|
|
|
删除
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-02-12 21:19:23 +08:00
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
|
|
<el-tabs
|
|
|
|
|
|
v-else
|
|
|
|
|
|
v-model="activeChildMap[topMenu.code]"
|
|
|
|
|
|
class="child-menu-tabs"
|
|
|
|
|
|
type="border-card"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-tab-pane
|
|
|
|
|
|
v-for="child in topMenu.children"
|
|
|
|
|
|
:key="child.code"
|
|
|
|
|
|
:label="child.name"
|
|
|
|
|
|
:name="child.code"
|
|
|
|
|
|
>
|
|
|
|
|
|
<el-table class="common-table" :data="child.items" stripe>
|
|
|
|
|
|
<el-table-column prop="section" label="分组" min-width="180" />
|
|
|
|
|
|
<el-table-column prop="name" label="页面展示名称" min-width="280" />
|
|
|
|
|
|
<el-table-column prop="field" label="字段名" min-width="260" />
|
2026-02-13 21:46:12 +08:00
|
|
|
|
<el-table-column label="点位" min-width="300">
|
2026-02-12 21:19:23 +08:00
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model.trim="scope.row.point"
|
|
|
|
|
|
placeholder="请输入点位"
|
|
|
|
|
|
clearable
|
2026-02-13 21:46:12 +08:00
|
|
|
|
class="short-input"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="固定展示值" min-width="300">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-input
|
|
|
|
|
|
v-model.trim="scope.row.fixedValue"
|
|
|
|
|
|
placeholder="请输入固定展示值"
|
|
|
|
|
|
clearable
|
|
|
|
|
|
class="short-input"
|
2026-02-12 21:19:23 +08:00
|
|
|
|
/>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-02-13 21:46:12 +08:00
|
|
|
|
<el-table-column label="固定展示" width="120" align="center">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-checkbox v-model="scope.row.useFixedDisplay" :true-label="1" :false-label="0" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column label="操作" width="120" fixed="right">
|
|
|
|
|
|
<template slot-scope="scope">
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
type="text"
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
@click="handleDeleteItem(scope.$index, child.items)"
|
|
|
|
|
|
>
|
|
|
|
|
|
删除
|
|
|
|
|
|
</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
2026-02-12 21:19:23 +08:00
|
|
|
|
</el-table>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
|
import { getSingleMonitorProjectPointMapping, saveSingleMonitorProjectPointMapping } from '@/api/ems/site'
|
|
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
|
name: 'MonitorPointMapping',
|
|
|
|
|
|
data() {
|
|
|
|
|
|
return {
|
2026-02-13 21:46:12 +08:00
|
|
|
|
siteId: '',
|
|
|
|
|
|
siteName: '',
|
2026-02-12 21:19:23 +08:00
|
|
|
|
activeTopMenu: 'HOME',
|
|
|
|
|
|
activeChildMap: {},
|
2026-02-13 21:46:12 +08:00
|
|
|
|
topMenuList: [],
|
|
|
|
|
|
deletedFieldCodes: []
|
2026-02-12 21:19:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
2026-02-13 21:46:12 +08:00
|
|
|
|
watch: {
|
|
|
|
|
|
'$route.query.siteId': {
|
|
|
|
|
|
immediate: false,
|
|
|
|
|
|
async handler(newSiteId) {
|
|
|
|
|
|
if (newSiteId === this.siteId) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.siteId = newSiteId || ''
|
|
|
|
|
|
this.siteName = this.$route.query.siteName || this.siteId
|
|
|
|
|
|
await this.initMenuStructure()
|
|
|
|
|
|
}
|
2026-02-12 21:19:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
goBack() {
|
|
|
|
|
|
this.$router.back()
|
|
|
|
|
|
},
|
|
|
|
|
|
async initMenuStructure() {
|
|
|
|
|
|
if (!this.siteId) {
|
|
|
|
|
|
this.topMenuList = []
|
2026-02-13 21:46:12 +08:00
|
|
|
|
this.deletedFieldCodes = []
|
2026-02-12 21:19:23 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const response = await getSingleMonitorProjectPointMapping(this.siteId)
|
|
|
|
|
|
const list = response?.data || []
|
|
|
|
|
|
const topMap = new Map()
|
|
|
|
|
|
const childMap = new Map()
|
|
|
|
|
|
|
|
|
|
|
|
list.forEach((row, index) => {
|
|
|
|
|
|
const moduleCode = row.moduleCode || 'UNKNOWN'
|
|
|
|
|
|
const moduleName = row.moduleName || '未分组'
|
|
|
|
|
|
const menuCode = row.menuCode || moduleCode
|
|
|
|
|
|
const menuName = row.menuName || moduleName
|
|
|
|
|
|
const topKey = moduleCode
|
|
|
|
|
|
if (!topMap.has(topKey)) {
|
|
|
|
|
|
topMap.set(topKey, {
|
|
|
|
|
|
code: moduleCode,
|
|
|
|
|
|
name: moduleName,
|
|
|
|
|
|
children: [],
|
|
|
|
|
|
items: []
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
const item = {
|
|
|
|
|
|
code: `${moduleCode}_${menuCode}_${row.fieldCode || index}`,
|
|
|
|
|
|
section: row.sectionName || '-',
|
|
|
|
|
|
name: row.fieldName || '-',
|
|
|
|
|
|
field: row.fieldCode || '',
|
2026-02-13 21:46:12 +08:00
|
|
|
|
point: row.dataPoint || '',
|
|
|
|
|
|
fixedValue: row.fixedDataPoint || '',
|
|
|
|
|
|
useFixedDisplay: row.useFixedDisplay === 1 ? 1 : 0
|
2026-02-12 21:19:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
const topItem = topMap.get(topKey)
|
|
|
|
|
|
const isTopDirect = moduleCode === menuCode || moduleCode === 'HOME'
|
|
|
|
|
|
if (isTopDirect) {
|
|
|
|
|
|
topItem.items.push(item)
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const childKey = `${moduleCode}_${menuCode}`
|
|
|
|
|
|
if (!childMap.has(childKey)) {
|
|
|
|
|
|
const child = { code: menuCode, name: menuName, items: [] }
|
|
|
|
|
|
childMap.set(childKey, child)
|
|
|
|
|
|
topItem.children.push(child)
|
|
|
|
|
|
}
|
|
|
|
|
|
childMap.get(childKey).items.push(item)
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
this.topMenuList = Array.from(topMap.values())
|
|
|
|
|
|
const firstTop = this.topMenuList[0]
|
|
|
|
|
|
this.activeTopMenu = firstTop ? firstTop.code : 'HOME'
|
|
|
|
|
|
this.activeChildMap = {}
|
2026-02-13 21:46:12 +08:00
|
|
|
|
this.deletedFieldCodes = []
|
2026-02-12 21:19:23 +08:00
|
|
|
|
this.topMenuList.forEach(top => {
|
|
|
|
|
|
if (top.children && top.children.length > 0) {
|
|
|
|
|
|
this.$set(this.activeChildMap, top.code, top.children[0].code)
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
},
|
|
|
|
|
|
getAllMappingRows() {
|
|
|
|
|
|
const rows = []
|
|
|
|
|
|
this.topMenuList.forEach(top => {
|
|
|
|
|
|
;(top.items || []).forEach(item => rows.push(item))
|
|
|
|
|
|
;(top.children || []).forEach(child => {
|
|
|
|
|
|
;(child.items || []).forEach(item => rows.push(item))
|
|
|
|
|
|
})
|
|
|
|
|
|
})
|
|
|
|
|
|
return rows
|
|
|
|
|
|
},
|
2026-02-13 21:46:12 +08:00
|
|
|
|
handleDeleteItem(index, list) {
|
|
|
|
|
|
if (!Array.isArray(list) || index < 0 || index >= list.length) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
this.$confirm('是否确认删除该条明细记录?删除后需点击“保存”才会生效。', '提示', {
|
|
|
|
|
|
confirmButtonText: '确定',
|
|
|
|
|
|
cancelButtonText: '取消',
|
|
|
|
|
|
type: 'warning'
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
const fieldCode = list[index] && list[index].field
|
|
|
|
|
|
if (fieldCode && !this.deletedFieldCodes.includes(fieldCode)) {
|
|
|
|
|
|
this.deletedFieldCodes.push(fieldCode)
|
|
|
|
|
|
}
|
|
|
|
|
|
list.splice(index, 1)
|
|
|
|
|
|
this.$message.success('已删除该条明细记录')
|
|
|
|
|
|
}).catch(() => {})
|
|
|
|
|
|
},
|
2026-02-12 21:19:23 +08:00
|
|
|
|
async saveData() {
|
|
|
|
|
|
if (!this.siteId) {
|
|
|
|
|
|
this.$message.warning('缺少站点ID')
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
const mappings = this.getAllMappingRows()
|
|
|
|
|
|
.filter(item => item.field)
|
2026-02-13 21:46:12 +08:00
|
|
|
|
.map(item => ({
|
|
|
|
|
|
fieldCode: item.field,
|
|
|
|
|
|
dataPoint: item.point || '',
|
|
|
|
|
|
fixedDataPoint: item.fixedValue || '',
|
|
|
|
|
|
useFixedDisplay: item.useFixedDisplay === 1 ? 1 : 0
|
|
|
|
|
|
}))
|
|
|
|
|
|
const response = await saveSingleMonitorProjectPointMapping({
|
|
|
|
|
|
siteId: this.siteId,
|
|
|
|
|
|
mappings,
|
|
|
|
|
|
deletedFieldCodes: this.deletedFieldCodes
|
|
|
|
|
|
})
|
|
|
|
|
|
const affectedRows = Number(response?.data || 0)
|
|
|
|
|
|
if (affectedRows > 0) {
|
|
|
|
|
|
this.$message.success(`保存成功,已写入 ${affectedRows} 条`)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.$message.warning('保存完成,但写入 0 条,请检查字段编码和点位值是否有效')
|
|
|
|
|
|
}
|
2026-02-12 21:19:23 +08:00
|
|
|
|
await this.initMenuStructure()
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
async mounted() {
|
2026-02-13 21:46:12 +08:00
|
|
|
|
this.siteId = this.$route.query.siteId || ''
|
|
|
|
|
|
this.siteName = this.$route.query.siteName || this.siteId
|
2026-02-12 21:19:23 +08:00
|
|
|
|
await this.initMenuStructure()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
.header-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.title-block {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-title {
|
|
|
|
|
|
font-size: 18px;
|
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.page-desc {
|
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
color: #909399;
|
2026-02-13 21:46:12 +08:00
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.site-label {
|
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.short-input {
|
|
|
|
|
|
width: 220px;
|
2026-02-12 21:19:23 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.child-menu-tabs {
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|