This commit is contained in:
2026-02-15 16:24:29 +08:00
parent 50c72d6989
commit 41a3ab45b3
36 changed files with 4896 additions and 2089 deletions

View File

@ -2,9 +2,7 @@
<el-dialog :visible.sync="dialogTableVisible" :close-on-press-escape="false" :close-on-click-modal="false" :show-close="false" destroy-on-close lock-scroll append-to-body width="400px" class="ems-dialog" :title="mode === 'add'?'新增配置':`编辑配置` " >
<el-form v-loading="loading>0" ref="addTempForm" :model="formData" :rules="rules" size="medium" label-width="140px">
<el-form-item label="站点" prop="siteId">
<el-select v-model="formData.siteId" :disabled="mode === 'edit'" placeholder="请选择站点" :loading="searchLoading" loading-text="正在加载数据">
<el-option v-for="(item,index) in siteList" :key="index+'zdxeSelect'" :label="item.siteName" :value="item.siteId" ></el-option>
</el-select>
<el-input v-model="formData.siteId" placeholder="请先在顶部选择站点" disabled />
</el-form-item>
<el-form-item label="消息等级" prop="qos">
<el-select v-model="formData.qos" placeholder="请选择消息等级">
@ -30,13 +28,10 @@
</template>
<script>
import {editMqtt,addMqtt,getMqttDetail} from "@/api/ems/site";
import {getAllSites} from '@/api/ems/zddt'
export default {
data() {
return {
loading:0,
siteList:[],
searchLoading:false,
dialogTableVisible:false,
mode:'',
formData: {
@ -48,7 +43,7 @@ export default {
},
rules: {
siteId:[
{ required: true, message: '请选择站点', trigger: 'blur'},
{ required: true, message: '请先在顶部选择站点', trigger: 'blur'},
],
qos:[
{ required: true, message: '请选择消息等级', trigger: 'blur'},
@ -63,23 +58,20 @@ export default {
}
},
methods: {
showDialog(id){
getRouteSiteId() {
const siteId = this.$route?.query?.siteId
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
},
showDialog(id, siteId = ''){
this.dialogTableVisible = true
this.getZdList()
if(id){
this.mode = 'edit'
this.formData.id = id
this.getDetail(id)
}else{
this.mode = 'add'
this.formData.siteId = siteId || this.getRouteSiteId()
}
},
//获取站点列表
getZdList(){
this.searchLoading=true
getAllSites().then(response => {
this.siteList = response?.data || []
}).finally(() => {this.searchLoading=false})
},
getDetail(id){
getMqttDetail(id).then(response => {
@ -132,6 +124,8 @@ export default {
// 清空所有数据
this.formData= {
id:'',//设备唯一标识
siteId:'',
qos:'',
mqttTopic:'',
topicName:''
}

View File

@ -160,7 +160,7 @@ export default {
}).finally(() => {this.loading=false})
},
addPowerConfig(id=''){
this.$refs.addMqtt.showDialog(id);
this.$refs.addMqtt.showDialog(id, this.form.siteId);
},
deleteMqtt(row){
this.$confirm(`确认要删除该配置吗?`, {

View File

@ -29,6 +29,9 @@
/>
</el-select>
</el-form-item>
<el-form-item label="点位ID">
<el-input v-model.trim="queryParams.pointId" placeholder="请输入点位ID" clearable style="width: 180px" />
</el-form-item>
<el-form-item label="数据键">
<el-input v-model="queryParams.dataKey" placeholder="请输入数据键" clearable style="width: 180px" />
</el-form-item>
@ -115,6 +118,18 @@
<el-form ref="pointForm" :model="form" :rules="rules" label-width="120px">
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="点位ID" prop="pointId">
<el-input v-model.trim="form.pointId" placeholder="请输入点位ID系统唯一" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="点位名称" prop="pointName">
<el-input v-model.trim="form.pointName" placeholder="请输入点位名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col v-if="form.id" :span="12">
<el-form-item label="站点" prop="siteId">
<el-input v-model="form.siteId" disabled />
</el-form-item>
@ -129,19 +144,7 @@
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12">
<el-form-item label="点位ID" prop="pointId">
<el-input v-model.trim="form.pointId" placeholder="请输入点位ID系统唯一" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="点位名称" prop="pointName">
<el-input v-model.trim="form.pointName" placeholder="请输入点位名称" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="16">
<el-col :span="12" v-if="form.pointType === 'data' || form.pointType === 'calc'">
<el-col :span="12" v-if="form.pointType === 'data'">
<el-form-item label="设备类型" prop="deviceCategory">
<el-select v-model="form.deviceCategory" placeholder="请选择设备类型" @change="handleFormCategoryChange">
<el-option
@ -199,6 +202,9 @@
:rows="3"
placeholder="例如voltageA * currentA + powerLoss"
/>
<div class="calc-expression-tips">
示例A + B * 2(A + B) / CvoltageA * currentA + powerLoss
</div>
</el-form-item>
</el-col>
<el-col :span="24" v-if="form.pointType === 'calc'">
@ -356,6 +362,7 @@ export default {
siteId: '',
deviceCategory: '',
deviceId: '',
pointId: '',
dataKey: '',
pointDesc: '',
pointType: 'data'
@ -370,6 +377,8 @@ export default {
siteId: '',
deviceId: '',
pointId: '',
pointName: '',
dataKey: '',
pointType: 'data',
rangeType: 'custom',
startTime: '',
@ -439,6 +448,15 @@ export default {
}
},
methods: {
formatPointCurveName(point = {}) {
const pointId = String(point.pointId || '').trim()
const pointName = String(point.pointName || '').trim()
const dataKey = String(point.dataKey || '').trim()
const idPart = pointId || '-'
const namePart = pointName || '-'
const keyPart = dataKey || '-'
return `${idPart}-${namePart}(${keyPart})`
},
formatDeviceInfo(row) {
if (!row) {
return '-'
@ -505,14 +523,14 @@ export default {
return
}
const points = this.tableData
.filter(item => item.pointType !== 'calc' && item.siteId && item.deviceId && item.dataKey)
.filter(item => item.pointType === 'data' && item.siteId && item.deviceId && item.dataKey)
.map(item => ({
siteId: item.siteId,
deviceId: item.deviceId,
dataKey: item.dataKey
}))
if (!points.length) {
const calcRows = this.tableData.filter(item => item.pointType === 'calc')
const calcRows = this.tableData.filter(item => item.pointType !== 'data')
if (!calcRows.length) {
this.tableData = this.applyCalcLatestValues(this.tableData)
return
@ -544,13 +562,13 @@ export default {
})
const mergedRows = this.applyCalcLatestValues([...depRowsWithLatest, ...this.tableData])
const calcLatestMap = mergedRows
.filter(item => item.pointType === 'calc')
.filter(item => item.pointType !== 'data')
.reduce((acc, item) => {
acc[this.getCalcRowKey(item)] = item.latestValue
return acc
}, {})
this.tableData = this.tableData.map(row => {
if (row.pointType !== 'calc') return row
if (row.pointType === 'data') return row
const nextLatest = calcLatestMap[this.getCalcRowKey(row)]
return {
...row,
@ -584,12 +602,83 @@ export default {
getCalcRowKey(row) {
return `${row.id || ''}__${row.siteId || ''}__${row.pointId || ''}`
},
extractExpressionTokens(expression) {
tokenizeCalcExpression(expression) {
const expr = String(expression || '').trim()
if (!expr) return []
if (!/^[0-9A-Za-z_+\-*/().\s]+$/.test(expr)) return []
const matched = expr.match(/\b[A-Za-z_][A-Za-z0-9_]*\b/g) || []
return Array.from(new Set(matched.map(item => String(item || '').trim()).filter(Boolean)))
if (!expr) {
return []
}
if (!/^[0-9A-Za-z_+\-*/().\s]+$/.test(expr)) {
throw new Error('计算表达式仅支持四则运算和括号')
}
const tokens = []
let index = 0
while (index < expr.length) {
const ch = expr[index]
if (/\s/.test(ch)) {
index += 1
continue
}
if (/[0-9.]/.test(ch)) {
const start = index
let hasDot = ch === '.'
index += 1
while (index < expr.length) {
const next = expr[index]
if (/[0-9]/.test(next)) {
index += 1
continue
}
if (next === '.' && !hasDot) {
hasDot = true
index += 1
continue
}
break
}
const numText = expr.slice(start, index)
if (!Number.isFinite(Number(numText))) {
throw new Error(`数值格式错误: ${numText}`)
}
tokens.push({ type: 'number', value: Number(numText) })
continue
}
if (/[A-Za-z_]/.test(ch)) {
const start = index
index += 1
while (index < expr.length && /[A-Za-z0-9_]/.test(expr[index])) {
index += 1
}
const word = expr.slice(start, index)
tokens.push({ type: 'identifier', value: word })
continue
}
if (ch === '(' || ch === ')') {
tokens.push({ type: ch, value: ch })
index += 1
continue
}
if (['+', '-', '*', '/'].includes(ch)) {
tokens.push({ type: 'operator', value: ch })
index += 1
continue
}
throw new Error(`表达式包含非法字符: ${ch}`)
}
tokens.push({ type: 'eof', value: '<eof>' })
return tokens
},
extractExpressionTokens(expression) {
const reserved = new Set(['IF'])
try {
const tokens = this.tokenizeCalcExpression(expression)
const identifiers = tokens
.filter(token => token.type === 'identifier')
.map(token => String(token.value || '').trim())
.filter(token => token && !reserved.has(token.toUpperCase()))
return Array.from(new Set(identifiers))
} catch (e) {
return []
}
},
loadCalcDependencyRows(calcRows = []) {
if (!calcRows.length) {
@ -656,7 +745,7 @@ export default {
applyCalcLatestValues(rows = []) {
const dataPointMap = {}
rows.forEach(row => {
if (row.pointType === 'calc') return
if (row.pointType !== 'data') return
if (row.latestValue === '-' || row.latestValue === '' || row.latestValue === null || row.latestValue === undefined) {
return
}
@ -687,7 +776,7 @@ export default {
})
})
return rows.map(row => {
if (row.pointType !== 'calc') return row
if (row.pointType === 'data') return row
return {
...row,
latestValue: this.evaluateCalcExpression(row.calcExpression, row.siteId, row.deviceId, dataPointMap)
@ -697,37 +786,260 @@ export default {
evaluateCalcExpression(expression, siteId, deviceId, dataPointMap) {
const expr = String(expression || '').trim()
if (!expr) return '-'
if (!/^[0-9A-Za-z_+\-*/().\s]+$/.test(expr)) return '-'
const tokenPattern = /\b[A-Za-z_][A-Za-z0-9_]*\b/g
const missingKeys = []
const resolvedExpr = expr.replace(tokenPattern, token => {
const candidates = [token, token.toUpperCase(), token.toLowerCase()]
let value
for (let i = 0; i < candidates.length; i++) {
const candidate = candidates[i]
const deviceKey = `${siteId || ''}__${deviceId || ''}__${candidate}`
const siteKey = `${siteId || ''}__${candidate}`
value = dataPointMap[deviceKey] !== undefined ? dataPointMap[deviceKey] : dataPointMap[siteKey]
if (value !== undefined && value !== null && value !== '' && value !== '-') {
break
try {
const tokens = this.tokenizeCalcExpression(expr)
let current = 0
const peek = () => tokens[current] || { type: 'eof', value: '<eof>' }
const matchType = type => {
if (peek().type !== type) return false
current += 1
return true
}
const matchOperator = op => {
const token = peek()
if (token.type !== 'operator' || token.value !== op) return false
current += 1
return true
}
const expectType = (type, message) => {
if (!matchType(type)) {
throw new Error(message)
}
}
if (value === undefined || value === null || value === '' || value === '-') {
missingKeys.push(token)
return 'NaN'
const resolveIdentifierValue = name => {
const candidates = [name, name.toUpperCase(), name.toLowerCase()]
for (let i = 0; i < candidates.length; i += 1) {
const candidate = candidates[i]
const deviceKey = `${siteId || ''}__${deviceId || ''}__${candidate}`
const siteKey = `${siteId || ''}__${candidate}`
const value = dataPointMap[deviceKey] !== undefined ? dataPointMap[deviceKey] : dataPointMap[siteKey]
if (value === undefined || value === null || value === '' || value === '-') {
continue
}
const numberValue = Number(value)
if (Number.isFinite(numberValue)) {
return numberValue
}
}
throw new Error(`缺少变量: ${name}`)
}
const numberValue = Number(value)
if (Number.isNaN(numberValue)) {
missingKeys.push(token)
return 'NaN'
const toBoolean = value => Number(value) !== 0
const evaluateNode = node => {
if (!node || !node.type) {
throw new Error('表达式节点无效')
}
if (node.type === 'number') return node.value
if (node.type === 'variable') return resolveIdentifierValue(node.name)
if (node.type === 'unary') {
const value = evaluateNode(node.node)
if (node.operator === '+') return value
if (node.operator === '-') return -value
if (node.operator === '!') return toBoolean(value) ? 0 : 1
throw new Error(`不支持的一元操作符: ${node.operator}`)
}
if (node.type === 'binary') {
if (node.operator === '&&') {
const left = evaluateNode(node.left)
if (!toBoolean(left)) return 0
return toBoolean(evaluateNode(node.right)) ? 1 : 0
}
if (node.operator === '||') {
const left = evaluateNode(node.left)
if (toBoolean(left)) return 1
return toBoolean(evaluateNode(node.right)) ? 1 : 0
}
const left = evaluateNode(node.left)
const right = evaluateNode(node.right)
if (node.operator === '+') return left + right
if (node.operator === '-') return left - right
if (node.operator === '*') return left * right
if (node.operator === '/') {
if (right === 0) {
throw new Error('除数不能为0')
}
return left / right
}
if (node.operator === '>=') return left >= right ? 1 : 0
if (node.operator === '<=') return left <= right ? 1 : 0
if (node.operator === '>') return left > right ? 1 : 0
if (node.operator === '<') return left < right ? 1 : 0
if (node.operator === '==') return left === right ? 1 : 0
if (node.operator === '!=') return left !== right ? 1 : 0
throw new Error(`不支持的操作符: ${node.operator}`)
}
if (node.type === 'ternary') {
return toBoolean(evaluateNode(node.condition))
? evaluateNode(node.trueNode)
: evaluateNode(node.falseNode)
}
throw new Error(`不支持的节点类型: ${node.type}`)
}
return String(numberValue)
})
if (missingKeys.length > 0) {
return '-'
}
try {
const result = Function(`"use strict"; return (${resolvedExpr});`)()
const parseExpression = () => parseTernary()
const parseTernary = () => {
const conditionNode = parseOr()
if (matchType('?')) {
const trueNode = parseTernary()
expectType(':', '三元表达式缺少 :')
const falseNode = parseTernary()
return {
type: 'ternary',
condition: conditionNode,
trueNode,
falseNode
}
}
return conditionNode
}
const parseOr = () => {
let left = parseAnd()
while (matchOperator('||')) {
left = {
type: 'binary',
operator: '||',
left,
right: parseAnd()
}
}
return left
}
const parseAnd = () => {
let left = parseEquality()
while (matchOperator('&&')) {
left = {
type: 'binary',
operator: '&&',
left,
right: parseEquality()
}
}
return left
}
const parseEquality = () => {
let left = parseComparison()
while (true) {
if (matchOperator('==')) {
left = {
type: 'binary',
operator: '==',
left,
right: parseComparison()
}
continue
}
if (matchOperator('!=')) {
left = {
type: 'binary',
operator: '!=',
left,
right: parseComparison()
}
continue
}
break
}
return left
}
const parseComparison = () => {
let left = parseAddSub()
while (true) {
if (matchOperator('>=')) {
left = { type: 'binary', operator: '>=', left, right: parseAddSub() }
continue
}
if (matchOperator('<=')) {
left = { type: 'binary', operator: '<=', left, right: parseAddSub() }
continue
}
if (matchOperator('>')) {
left = { type: 'binary', operator: '>', left, right: parseAddSub() }
continue
}
if (matchOperator('<')) {
left = { type: 'binary', operator: '<', left, right: parseAddSub() }
continue
}
break
}
return left
}
const parseAddSub = () => {
let left = parseMulDiv()
while (true) {
if (matchOperator('+')) {
left = { type: 'binary', operator: '+', left, right: parseMulDiv() }
continue
}
if (matchOperator('-')) {
left = { type: 'binary', operator: '-', left, right: parseMulDiv() }
continue
}
break
}
return left
}
const parseMulDiv = () => {
let left = parseUnary()
while (true) {
if (matchOperator('*')) {
left = { type: 'binary', operator: '*', left, right: parseUnary() }
continue
}
if (matchOperator('/')) {
left = { type: 'binary', operator: '/', left, right: parseUnary() }
continue
}
break
}
return left
}
const parseUnary = () => {
if (matchOperator('+')) return { type: 'unary', operator: '+', node: parseUnary() }
if (matchOperator('-')) return { type: 'unary', operator: '-', node: parseUnary() }
if (matchOperator('!')) return { type: 'unary', operator: '!', node: parseUnary() }
return parsePrimary()
}
const parsePrimary = () => {
const token = peek()
if (matchType('number')) {
return { type: 'number', value: token.value }
}
if (matchType('identifier')) {
const identifier = String(token.value || '')
if (matchType('(')) {
if (identifier.toUpperCase() !== 'IF') {
throw new Error(`不支持的函数: ${identifier}`)
}
const condition = parseExpression()
expectType(',', 'IF函数缺少第1个逗号')
const trueValue = parseExpression()
expectType(',', 'IF函数缺少第2个逗号')
const falseValue = parseExpression()
expectType(')', 'IF函数缺少右括号')
return {
type: 'ternary',
condition,
trueNode: trueValue,
falseNode: falseValue
}
}
return { type: 'variable', name: identifier }
}
if (matchType('(')) {
const node = parseExpression()
expectType(')', '括号不匹配')
return node
}
throw new Error(`表达式语法错误: ${token.value}`)
}
const rootNode = parseExpression()
if (peek().type !== 'eof') {
throw new Error('表达式尾部有多余内容')
}
const result = evaluateNode(rootNode)
if (!Number.isFinite(result)) {
return '-'
}
@ -748,6 +1060,7 @@ export default {
siteId: currentSiteId,
deviceCategory: '',
deviceId: '',
pointId: '',
dataKey: '',
pointDesc: '',
pointType: this.activePointTab
@ -820,7 +1133,7 @@ export default {
id: null,
siteId: querySiteId,
pointType: this.activePointTab,
deviceCategory: this.queryParams.deviceCategory || '',
deviceCategory: this.activePointTab === 'data' ? (this.queryParams.deviceCategory || '') : '',
deviceId: '',
registerAddress: '',
pointId: '',
@ -848,8 +1161,9 @@ export default {
}
this.resetForm()
this.dialogVisible = true
this.getFormDeviceList()
if (this.form.pointType === 'calc') {
if (this.form.pointType === 'data') {
this.getFormDeviceList()
} else {
this.loadCalcDataPointOptions()
}
},
@ -889,6 +1203,10 @@ export default {
handleFormPointTypeChange() {
if (this.form.pointType !== 'calc') {
this.form.calcExpression = ''
} else {
this.form.deviceCategory = ''
this.form.deviceId = ''
this.formDeviceList = []
}
this.loadCalcDataPointOptions()
},
@ -929,11 +1247,14 @@ export default {
this.selectedCalcDataKey = ''
},
openCurveDialog(row) {
this.curveDialogTitle = `曲线 - ${row.pointDesc || row.dataKey || ''}`
const curveName = this.formatPointCurveName(row)
this.curveDialogTitle = `曲线 - ${curveName}`
this.curveQuery = {
siteId: row.siteId || '',
deviceId: row.deviceId || '',
pointId: row.pointId || '',
pointName: row.pointName || '',
dataKey: row.dataKey || '',
pointType: row.pointType || 'data',
rangeType: 'custom',
startTime: '',
@ -1080,7 +1401,7 @@ export default {
],
series: [
{
name: this.curveQuery.pointId || '点位值',
name: this.formatPointCurveName(this.curveQuery),
type: 'line',
data: yData,
connectNulls: true,
@ -1149,8 +1470,10 @@ export default {
this.$message.warning('请输入计算表达式')
return
}
if (!/^[0-9A-Za-z_+\-*/().\s]+$/.test(calcExpression)) {
this.$message.warning('计算表达式格式不正确')
try {
this.tokenizeCalcExpression(calcExpression)
} catch (e) {
this.$message.warning(e?.message || '计算表达式格式不正确')
return
}
this.form.registerAddress = this.form.registerAddress || ''
@ -1162,12 +1485,14 @@ export default {
this.form.pointType = pointType
if (pointType === 'calc') {
this.form.deviceCategory = ''
this.form.deviceId = ''
const request = this.form.id ? updatePointMatch : addPointMatch
const calcData = {
id: this.form.id,
siteId: this.form.siteId,
pointType: 'calc',
deviceCategory: this.form.deviceCategory,
deviceCategory: '',
deviceId: '',
registerAddress: '',
pointId: this.form.pointId,
@ -1301,6 +1626,13 @@ export default {
color: #606266;
}
.calc-expression-tips {
margin-top: 6px;
color: #909399;
font-size: 12px;
line-height: 1.6;
}
@media (max-width: 992px) {
.point-config-layout {
flex-direction: column;

View File

@ -5,9 +5,7 @@
<div class="items-container">
<div class="item-title">站点:</div>
<div class="item-content">
<el-select v-model="siteId" :disabled="mode === 'edit'" placeholder="请选择站点" :loading="searchLoading" loading-text="正在加载数据">
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList" :key="index+'zdxeSelect'"></el-option>
</el-select>
<el-input v-model="siteId" placeholder="请先在顶部选择站点" disabled />
</div>
</div>
<div class="items-container">
@ -103,15 +101,12 @@
</template>
<script>
import {addPriceConfig,editPriceConfig,detailPriceConfig} from '@/api/ems/powerTariff'
import {getAllSites} from '@/api/ems/zddt'
export default {
data() {
return {
mode:'',
id:'',
searchLoading:false,
siteId:'',
siteList:[],
powerDate:'',//时间
//尖-peak,峰-high,平-flat,谷=valley
priceTypeOptions:[{
@ -137,6 +132,10 @@ export default {
}
},
methods: {
getRouteSiteId() {
const siteId = this.$route?.query?.siteId
return siteId === undefined || siteId === null ? '' : String(siteId).trim()
},
addRow(){
this.hoursOptions.push({
startTime:'',
@ -147,15 +146,7 @@ export default {
deleteRow(index){
this.hoursOptions.splice(index,1)
},
//获取站点列表
getZdList(){
this.searchLoading=true
getAllSites().then(response => {
this.siteList = response?.data || []
}).finally(() => {this.searchLoading=false})
},
showDialog(id){
this.getZdList()
showDialog(id, siteId = ''){
this.id = id
if(id) {
this.mode='edit'
@ -172,11 +163,12 @@ export default {
}).finally(()=>this.loading = false)
}else {
this.mode='add'
this.siteId = siteId || this.getRouteSiteId()
}
this.dialogTableVisible=true
},
saveDialog() {
if(this.siteId === '') return this.$message.error('请选择站点')
if(this.siteId === '') return this.$message.error('请先在顶部选择站点')
if(this.powerDate === '') return this.$message.error('请选择时间')
let priceArr=[]
this.priceTypeOptions.forEach(item=>{
@ -244,8 +236,6 @@ export default {
this.mode=''
this.id=''
this.siteId=''
this.siteList=[]
this.searchLoading=false
this.powerDate=''
this.hoursOptions=[]
this.priceTypeOptions.forEach(item=>{
@ -303,4 +293,4 @@ export default {
}
}
</style>
</style>

View File

@ -129,7 +129,7 @@ export default {
}).finally(() => {this.loading=false})
},
addPowerConfig(id=''){
this.$refs.addPowerTariff.showDialog(id);
this.$refs.addPowerTariff.showDialog(id, this.siteId);
},
deletePowerConfig(row){
this.$confirm(`确认要删除${row.month}月的电价配置吗?`, {

File diff suppressed because it is too large Load Diff

View File

@ -1,109 +1,92 @@
<template>
<div
class="ems-dashboard-editor-container"
style="background-color: #ffffff"
v-loading="loading"
>
<el-form :inline="true" class="select-container">
<el-form-item label="故障名称">
<el-input
<div class="protect-plan-page" v-loading="loading">
<el-card class="query-card" shadow="never">
<div class="query-head">
<div class="query-title">设备保护方案</div>
<el-button type="primary" @click="addPlan" native-type="button">新增方案</el-button>
</div>
<el-form :inline="true" class="query-form" @submit.native.prevent>
<el-form-item label="故障名称">
<el-input
v-model="form.faultName"
clearable
placeholder="请输入故障名称"
style="width: 150px"
></el-input>
</el-form-item>
<el-form-item>
style="width: 220px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSearch" native-type="button">搜索</el-button>
<el-button @click="onReset" native-type="button">重置</el-button>
</el-form-item>
</el-form>
<el-button type="primary" @click="addPlan" native-type="button"
>新增方案</el-button
>
<el-table
class="common-table"
:data="tableData"
stripe
max-height="600px"
style="width: 100%; margin-top: 25px"
>
<el-table-column prop="siteId" label="站点" width="100"> </el-table-column>
<el-table-column prop="faultName" label="设备保护名称" width="100"> </el-table-column>
<el-table-column prop="faultLevel" label="故障等级" width="100">
<template slot-scope="scope">等级{{scope.row.faultLevel}}</template>
</el-table-column>
<el-table-column prop="isAlert" label="是否告警" width="100">
<template slot-scope="scope">{{scope.row.isAlert === 1 ? '是' : '否'}}</template>
</el-table-column>
<el-table-column prop="description" label="处理方案描述" width="200" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="protectionSettings" label="保护前提" show-overflow-tooltip width="400">
<template slot-scope="scope">
<div v-html="handleProtectionSettings(scope.row.protectionSettings)"></div>
</template>
</el-table-column>
<el-table-column prop="faultDelaySeconds" label="保护前提延时(s)" width="120">
</el-table-column>
<el-table-column prop="protectionPlan" label="保护方案" show-overflow-tooltip width="200">
<template slot-scope="scope">
<div v-html="handleProtectionPlan(scope.row.protectionPlan)"></div>
</template>
</el-table-column>
<el-table-column prop="releaseDelaySeconds" label="保护方案延时(s)" width="120">
</el-table-column>
<el-table-column fixed="right" label="操作" width="150">
<template slot-scope="scope">
<el-button @click="editDevice(scope.row)" type="warning" size="mini">
编辑
</el-button>
<el-button type="danger" @click="deleteDevice(scope.row)" size="mini">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-show="tableData.length > 0"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalSize"
style="margin-top: 15px; text-align: center"
>
</el-pagination>
<add-plan
ref="addPlan"
@update="getData"
/>
</el-form-item>
</el-form>
</el-card>
<el-card class="table-card" shadow="never">
<el-table class="common-table" :data="tableData" stripe max-height="620px">
<el-table-column prop="faultName" label="设备保护名称" min-width="140" />
<el-table-column prop="faultLevel" label="故障等级" width="100">
<template slot-scope="scope">等级{{ scope.row.faultLevel }}</template>
</el-table-column>
<el-table-column prop="isAlert" label="是否告警" width="100">
<template slot-scope="scope">{{ scope.row.isAlert === 1 ? '是' : '否' }}</template>
</el-table-column>
<el-table-column prop="description" label="处理方案描述" min-width="180" show-overflow-tooltip />
<el-table-column prop="protectionSettings" label="保护前提" min-width="360" show-overflow-tooltip>
<template slot-scope="scope">
<div class="rich-lines" v-html="handleProtectionSettings(scope.row.protectionSettings)"></div>
</template>
</el-table-column>
<el-table-column prop="faultDelaySeconds" label="前提延时(s)" width="110" />
<el-table-column prop="protectionPlan" label="保护方案" min-width="260" show-overflow-tooltip>
<template slot-scope="scope">
<div class="rich-lines" v-html="handleProtectionPlan(scope.row.protectionPlan)"></div>
</template>
</el-table-column>
<el-table-column prop="releaseDelaySeconds" label="方案延时(s)" width="110" />
<el-table-column fixed="right" label="操作" width="150">
<template slot-scope="scope">
<el-button @click="editDevice(scope.row)" type="warning" size="mini">编辑</el-button>
<el-button type="danger" @click="deleteDevice(scope.row)" size="mini">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-show="tableData.length > 0"
background
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="pageNum"
:page-size="pageSize"
:page-sizes="[10, 20, 30, 40]"
layout="total, sizes, prev, pager, next, jumper"
:total="totalSize"
class="pager"
/>
</el-card>
<add-plan ref="addPlan" @update="getData" />
</div>
</template>
<script>
import {
protectPlanList,
deleteProtectPlan,
} from "@/api/ems/site";
import getQuerySiteId from '@/mixins/ems/getQuerySiteId'
import { protectPlanList, deleteProtectPlan } from "@/api/ems/site";
import getQuerySiteId from "@/mixins/ems/getQuerySiteId";
import AddPlan from "./AddPlan.vue";
export default {
name: "SBBH",
components: { AddPlan },
mixins: [getQuerySiteId],
data() {
return {
form:{
faultName:''
form: {
faultName: "",
},
loading: false,
tableData: [],
pageSize: 10, //分页栏当前每个数据总数
pageNum: 1, //分页栏当前页数
totalSize: 0, //table表格数据总数
pageSize: 10,
pageNum: 1,
totalSize: 0,
dialogTableVisible: false,
};
},
@ -112,39 +95,52 @@ export default {
this.pageNum = 1;
this.getData();
},
handleProtectionSettings(data){
if(!data || !JSON.parse(data)) return
const arr = JSON.parse(data),
str= arr.map((item,index)=>{
const {categoryName='',deviceId='',point='',faultOperator='',faultValue='',releaseOperator='',releaseValue='',relationNext=''} = item
return `<div>${index+1}、 <span>${categoryName ? categoryName + '-' : ''}${deviceId ? deviceId + '-' : ''}${ point || ''}</span> <span>故障:${faultOperator || ''}${ faultValue || ''}</span> <span>释放:${releaseOperator || ''}${releaseValue || ''}</span> ${arr[index+1] ? '<span>关系:'+(relationNext || '')+'</span>' : ''}</div>`
})
return str.join('')
handleProtectionSettings(data) {
if (!data || !JSON.parse(data)) return;
const arr = JSON.parse(data);
const str = arr.map((item, index) => {
const {
categoryName = "",
deviceId = "",
point = "",
faultOperator = "",
faultValue = "",
releaseOperator = "",
releaseValue = "",
relationNext = "",
} = item;
return `<div>${index + 1}、 <span>${categoryName ? categoryName + "-" : ""}${
deviceId ? deviceId + "-" : ""
}${point || ""}</span> <span>故障:${faultOperator || ""}${faultValue || ""}</span> <span>释放:${
releaseOperator || ""
}${releaseValue || ""}</span> ${
arr[index + 1] ? "<span>关系:" + (relationNext || "") + "</span>" : ""
}</div>`;
});
return str.join("");
},
handleProtectionPlan(data){
if(!data || !JSON.parse(data)) return
const arr = JSON.parse(data),
str= arr.map((item,index)=>{
const {categoryName='',deviceId='',point='',value=''} = item
return `<div>${index+1}、 <span>${categoryName ? categoryName + '-' : ''}${deviceId ? deviceId + '-' : ''}${ point || ''}</span> <span>故障:=${ value || ''}</span> </div>`
})
return str.join('')
handleProtectionPlan(data) {
if (!data || !JSON.parse(data)) return;
const arr = JSON.parse(data);
const str = arr.map((item, index) => {
const { categoryName = "", deviceId = "", point = "", value = "" } = item;
return `<div>${index + 1}、 <span>${categoryName ? categoryName + "-" : ""}${
deviceId ? deviceId + "-" : ""
}${point || ""}</span> <span>故障:=${value || ""}</span> </div>`;
});
return str.join("");
},
// 新增方案 展示弹窗
addPlan() {
if (!this.siteId) {
this.$message.warning('请先在顶部选择站点')
return
this.$message.warning("请先在顶部选择站点");
return;
}
this.$refs.addPlan.open('', this.siteId)
this.$refs.addPlan.open("", this.siteId);
},
// 编辑设备
editDevice(row) {
this.$refs.addPlan.open(row.id, this.siteId || row.siteId)
this.$refs.addPlan.open(row.id, this.siteId);
},
//删除设备
deleteDevice(row) {
console.log('删除')
this.$confirm(`确认要设备保护${row.faultName}吗?`, {
confirmButtonText: "确定",
cancelButtonText: "取消",
@ -167,19 +163,14 @@ export default {
},
})
.then(() => {
//只有在废弃成功的情况下会走到这里
this.$message({
type: "success",
message: "删除成功!",
});
this.getData();
//调用接口 更新表格数据
})
.catch(() => {
//取消关机
});
.catch(() => {});
},
// 分页
handleSizeChange(val) {
this.pageSize = val;
this.$nextTick(() => {
@ -192,29 +183,27 @@ export default {
this.getData();
});
},
// 搜索
onSearch() {
this.pageNum = 1; //每次搜索从1开始搜索
this.pageNum = 1;
this.getData();
},
// 重置
onReset() {
this.form={
this.form = {
faultName: "",
}
this.pageNum = 1; //每次搜索从1开始搜索
};
this.pageNum = 1;
this.getData();
},
// 获取数据
getData() {
if (!this.siteId) {
this.tableData = []
this.totalSize = 0
return
this.tableData = [];
this.totalSize = 0;
return;
}
this.loading = true;
const { pageNum, pageSize } = this,{faultName=''}=this.form;
protectPlanList({ siteId: this.siteId, faultName,pageNum, pageSize })
const { pageNum, pageSize } = this;
const { faultName = "" } = this.form;
protectPlanList({ siteId: this.siteId, faultName, pageNum, pageSize })
.then((response) => {
this.tableData = response?.rows || [];
this.totalSize = response?.total || 0;
@ -227,4 +216,46 @@ export default {
};
</script>
<style scoped lang="scss"></style>
<style scoped lang="scss">
.protect-plan-page {
padding: 16px;
background: linear-gradient(180deg, #f5f8ff 0%, #f7f9fc 100%);
.query-card,
.table-card {
border-radius: 12px;
border: 1px solid #e7edf7;
margin-bottom: 12px;
}
.query-head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 14px;
}
.query-title {
font-size: 16px;
font-weight: 600;
color: #1d2a3a;
letter-spacing: 0.5px;
}
.query-form {
display: flex;
flex-wrap: wrap;
margin-bottom: -18px;
}
.rich-lines {
color: #3e4b5a;
line-height: 1.45;
}
.pager {
margin-top: 16px;
text-align: center;
}
}
</style>

View File

@ -7,10 +7,12 @@
<el-form v-loading="loading>0" ref="addTempForm" inline :model="formData" :rules="rules" size="medium"
label-width="120px" class="device-form base-form">
<el-form-item label="站点" prop="siteId">
<el-select v-model="formData.siteId" placeholder="请选择" :style="{width: '100%'}" @change="changeType">
<el-option :label="item.siteName" :value="item.siteId" v-for="(item,index) in siteList"
:key="index+'siteOptions'"></el-option>
</el-select>
<el-input
v-model="formData.siteId"
placeholder="请先在顶部选择站点"
disabled
:style="{width: '100%'}"
/>
</el-form-item>
<el-form-item label="设备id" prop="deviceId">
<el-input v-model="formData.deviceId" placeholder="请输入" maxlength="60" clearable :style="{width: '100%'}">
@ -25,12 +27,6 @@
:style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="工作状态" prop="communicationStatus">
<el-select v-model="formData.communicationStatus" placeholder="请选择" :style="{width: '100%'}">
<el-option :label="value" :value="key" v-for="(value,key) in communicationStatusOptions"
:key="key+'communicationStatusOptions'"></el-option>
</el-select>
</el-form-item>
<el-form-item label="设备类型" prop="deviceType">
<el-select v-model="formData.deviceType" placeholder="请选择" :style="{width: '100%'}">
<el-option :label="value" :value="key" v-for="(value,key) in deviceTypeOptions"
@ -46,7 +42,7 @@
</el-form-item>
<el-form-item label="上级设备" prop="parentId" v-if="dccDeviceCategoryList.includes(formData.deviceCategory)">
<el-select v-model="formData.parentId"
:placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先选择站点' : '请选择'"
:placeholder="parentDeviceList.length === 0 && !formData.siteId ? '请先在顶部选择站点' : '请选择'"
:style="{width: '100%'}">
<el-option :label="item.deviceName" :value="item.id" v-for="(item,index) in parentDeviceList"
:key="index+'parentDeviceList'"></el-option>
@ -86,50 +82,43 @@
</el-form-item>
</el-form>
<!-- pcs配置-->
<el-form v-if="isPcs" ref="pcsSettingForm" inline :model="pcsSetting" size="medium"
label-width="120px" class="device-form pcs-form" :rules="pcsSettingRules">
<div style="font-size: 14px;padding: 10px 0 20px;font-weight: 600;">PCS配置</div>
<el-form-item label="开关机地址" prop="pointAddress">
<el-input v-model="pcsSetting.pointAddress" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="功率地址" prop="powerAddress">
<el-input v-model="pcsSetting.powerAddress" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="开机指令" prop="startCommand">
<el-input v-model="pcsSetting.startCommand" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="开机目标功率" prop="startPower">
<el-input v-model="pcsSetting.startPower" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="关机指令" prop="stopCommand">
<el-input v-model="pcsSetting.stopCommand" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="关机目标功率" prop="stopPower">
<el-input v-model="pcsSetting.stopPower" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="倍率" prop="powerMultiplier">
<el-input v-model="pcsSetting.powerMultiplier" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<el-form-item label="电池簇数" prop="clusterNum">
<el-input v-model="pcsSetting.clusterNum" placeholder="请输入" clearable :style="{width: '100%'}">
</el-input>
</el-form-item>
<br>
<template v-for="index in parseInt(pcsSetting.clusterNum) || 0">
<el-form-item :label="'电池簇'+(index)+'地址'"
prop="clusterPointAddress">
<el-input v-model="pcsSetting.clusterPointAddress[index-1]" placeholder="请输入" clearable
:style="{width: '100%'}">
</el-input>
<el-form v-if="isPcs" ref="pcsSettingForm" :model="pcsSetting" size="medium"
label-position="top" class="pcs-form" :rules="pcsSettingRules">
<div class="pcs-form__title">PCS配置</div>
<div class="pcs-form__grid">
<el-form-item label="开关机地址" prop="pointAddress" class="pcs-form__item">
<el-input v-model="pcsSetting.pointAddress" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
</template>
<el-form-item label="功率地址" prop="powerAddress" class="pcs-form__item">
<el-input v-model="pcsSetting.powerAddress" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
<el-form-item label="开机指令" prop="startCommand" class="pcs-form__item">
<el-input v-model="pcsSetting.startCommand" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
<el-form-item label="关机指令" prop="stopCommand" class="pcs-form__item">
<el-input v-model="pcsSetting.stopCommand" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
<el-form-item label="开机目标功率" prop="startPower" class="pcs-form__item">
<el-input v-model="pcsSetting.startPower" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
<el-form-item label="关机目标功率" prop="stopPower" class="pcs-form__item">
<el-input v-model="pcsSetting.stopPower" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
<el-form-item label="倍率" prop="powerMultiplier" class="pcs-form__item">
<el-input v-model="pcsSetting.powerMultiplier" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
<el-form-item label="电池簇数" prop="clusterNum" class="pcs-form__item">
<el-input v-model="pcsSetting.clusterNum" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
</div>
<div v-if="(parseInt(pcsSetting.clusterNum) || 0) > 0" class="pcs-form__cluster">
<div class="pcs-form__cluster-title">电池簇地址</div>
<template v-for="index in parseInt(pcsSetting.clusterNum) || 0">
<el-form-item :key="'clusterAddress' + index" :label="'电池簇' + index + '地址'" prop="clusterPointAddress">
<el-input v-model="pcsSetting.clusterPointAddress[index - 1]" placeholder="请输入" clearable :style="{width: '100%'}" />
</el-form-item>
</template>
</div>
</el-form>
</div>
</div>
@ -141,7 +130,6 @@
</template>
<script>
import {mapState} from "vuex";
import {getAllSites} from '@/api/ems/zddt'
import {validText} from '@/utils/validate'
import {addDevice, getDeviceDetailInfo, getParentDeviceId, updateDevice} from "@/api/ems/site";
import {getAllDeviceCategory} from '@/api/ems/search'
@ -191,7 +179,6 @@ export default {
dccDeviceCategoryList: ['CLUSTER', 'BATTERY'],//需要展示上级设备的设备类型
dialogTableVisible: false,
parentDeviceList: [],//上级设备列表 从接口获取数据
siteList: [],//站点列表 从接口获取数据
deviceCategoryList: [],//设备类别列表 从接口获取数据
formData: {
id: '',//设备唯一标识
@ -199,7 +186,6 @@ export default {
deviceId: '',//设备id
deviceName: '',//设备名称
description: '',//设备描述
communicationStatus: '',//工作状态
deviceType: '',//设备类型
deviceCategory: '',//设备类别
parentId: '',//上级设备id
@ -226,7 +212,7 @@ export default {
},
rules: {
siteId: [
{required: true, message: '请选择站点', trigger: ['blur', 'change']}
{required: true, message: '请先在顶部选择站点', trigger: ['blur', 'change']}
],
deviceId: [
{required: true, message: '请输入设备id', trigger: 'blur'},
@ -240,9 +226,6 @@ export default {
{required: true, message: '请输入设备描述', trigger: 'blur'},
{validator: validateText, trigger: 'blur'}
],
communicationStatus: [
{required: true, message: '请选择工作状态', trigger: ['blur', 'change']}
],
deviceType: [
{required: true, message: '请选择设备类型', trigger: ['blur', 'change']}
],
@ -312,7 +295,6 @@ export default {
},
computed: {
...mapState({
communicationStatusOptions: state => state?.ems?.communicationStatusOptions || {},
deviceTypeOptions: state => state?.ems?.deviceTypeOptions || {}
}),
isPcs() {
@ -324,12 +306,22 @@ export default {
handler(newVal) {
//打开弹窗
if (newVal) {
this.getZdList()
if (this.mode === 'add') {
this.syncSiteFromRoute(true)
}
this.getDeviceCategoryList()
}
},
immediate: true,
},
'$route.query.siteId': {
handler() {
if (!this.dialogTableVisible || this.mode !== 'add') {
return
}
this.syncSiteFromRoute(true)
}
},
id: {
handler(newVal) {
if ((newVal || newVal === 0) && this.mode !== 'add') {
@ -355,20 +347,24 @@ export default {
}
},
methods: {
syncSiteFromRoute(force = false) {
const routeSiteId = this.$route?.query?.siteId
const normalizedSiteId = routeSiteId === undefined || routeSiteId === null ? '' : String(routeSiteId).trim()
if (!normalizedSiteId) {
if (force) {
this.formData.siteId = ''
}
return
}
if (force || !this.formData.siteId) {
this.formData.siteId = normalizedSiteId
}
},
changeType() {
if (this.dccDeviceCategoryList.includes(this.formData.deviceCategory)) {
this.getParentDeviceList()
}
},
//获取站点列表
getZdList() {
this.loading += 1
getAllSites().then(response => {
this.siteList = response?.data || []
}).finally(() => {
this.loading -= 1
})
},
// 获取设备类别
getDeviceCategoryList() {
this.loading += 1
@ -381,7 +377,8 @@ export default {
//获取上级id列表
getParentDeviceList(init = false) {
if (!this.formData.siteId) {
return console.log('请先选择站点')
this.$message.warning('请先在顶部选择站点')
return
}
!init && (this.formData.parentId = '')
this.loading = this.loading + 1
@ -399,7 +396,6 @@ export default {
deviceId = '',//设备id
deviceName = '',//设备名称
description = '',//设备描述
communicationStatus = '',//工作状态
deviceType = '',//设备类型
deviceCategory = '',//设备类别
parentId = '',//上级设备id
@ -429,7 +425,6 @@ export default {
deviceId,
deviceName,
description,
communicationStatus,
deviceType,
deviceCategory,
parentId,
@ -503,7 +498,6 @@ export default {
deviceId: '',//设备id
deviceName: '',//设备名称
description: '',//设备描述
communicationStatus: '',//工作状态
deviceType: '',//设备类型
deviceCategory: '',//设备类别
parentId: '',//上级设备id
@ -516,6 +510,7 @@ export default {
parity: '',//校验位
slaveId: '',//从站地址
}
this.parentDeviceList = []
this.pcsSetting = {
deviceSettingId: '',
powerAddress: '',//功率地址
@ -538,24 +533,17 @@ export default {
</script>
<style scoped lang="scss">
.form-layout {
display: block;
position: relative;
}
.form-layout.has-pcs {
display: flex;
align-items: flex-start;
gap: 16px;
overflow: visible;
}
.base-form {
width: 100%;
}
.form-layout.has-pcs .base-form,
.form-layout.has-pcs .pcs-form {
width: calc(50% - 8px);
}
.device-form {
::v-deep .el-form-item--medium .el-form-item__content {
width: 260px;
@ -567,7 +555,92 @@ export default {
}
}
.form-layout.has-pcs .device-form .el-form-item {
width: 100%;
.pcs-form {
position: absolute;
top: 0;
right: -360px;
width: 340px;
max-height: 520px;
overflow-y: auto;
padding: 14px;
border: 1px solid #dcdfe6;
border-radius: 4px;
background: #fff;
box-shadow: none;
z-index: 2;
}
.pcs-form__title {
margin-bottom: 12px;
padding-bottom: 10px;
font-size: 14px;
font-weight: 600;
color: #303133;
border-bottom: 1px solid #ebeef5;
}
.pcs-form__grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0 10px;
}
.pcs-form__item {
margin-bottom: 0;
}
.pcs-form__cluster {
margin-top: 10px;
padding-top: 10px;
border-top: 1px solid #ebeef5;
}
.pcs-form__cluster-title {
margin-bottom: 8px;
font-size: 13px;
font-weight: 600;
color: #606266;
}
.pcs-form {
::v-deep .el-form-item {
width: 100%;
margin-right: 0;
margin-bottom: 18px;
}
::v-deep .el-form-item__label {
line-height: 20px;
padding-bottom: 4px;
font-size: 14px;
color: #606266;
}
::v-deep .el-form-item__content {
width: 100%;
line-height: normal;
}
::v-deep .el-input__inner {
height: 34px;
line-height: 34px;
}
::v-deep .el-form-item__error {
position: static;
line-height: 1.2;
padding-top: 4px;
}
::v-deep .el-form-item.is-required:not(.is-no-asterisk) > .el-form-item__label:before {
margin-right: 3px;
}
}
.ems-dialog {
::v-deep .el-dialog,
::v-deep .el-dialog__body {
overflow: visible;
}
}
</style>

View File

@ -110,6 +110,11 @@
sortable="custom"
>
</el-table-column>
<el-table-column v-if="selectable" label="操作" width="90" align="center">
<template slot-scope="scope">
<el-button type="text" size="mini" @click="selectPoint(scope.row)">选择</el-button>
</template>
</el-table-column>
</el-table>
<el-pagination
v-show="tableData.length > 0"
@ -147,6 +152,7 @@ export default {
this.pageNum = 1;
this.totalSize = 0;
this.dataType = '';
this.selectable = false;
this.form = {
sortMethod: "desc", //升序不传或者asc、降序desc
sortData: this.defaultSort.prop,
@ -191,9 +197,14 @@ export default {
pageSize: 10, //分页栏当前每个数据总数
pageNum: 1, //分页栏当前页数
totalSize: 0, //table表格数据总数
selectable: false
};
},
methods: {
selectPoint(row) {
this.$emit('select-point', row || {})
this.show = false
},
showChart({pointName}) {
if (pointName) {
const {deviceCategory, deviceId} = this;
@ -235,12 +246,13 @@ export default {
this.getData()
});
},
showTable({deviceCategory, siteId, deviceId, parentId = ""}, dataType) {
showTable({deviceCategory, siteId, deviceId, parentId = ""}, dataType, options = {}) {
this.dataType = dataType;
this.deviceCategory = deviceCategory;
this.siteId = siteId;
this.deviceId = deviceId;
this.parentId = deviceCategory === "BATTERY" ? parentId : ""; //只有单体电池需要这个值
this.selectable = !!options.selectable
this.show = true;
this.getData()
},

View File

@ -27,10 +27,6 @@
prop="siteId"
label="站点ID">
</el-table-column>
<el-table-column
prop="siteName"
label="站点名称">
</el-table-column>
<el-table-column
prop="deviceId"
label="设备ID"
@ -111,6 +107,17 @@ export default {
}
this.siteId = normalizedSiteId
this.onSearch()
},
'$route.query.siteName'(newSiteName) {
const normalizedSiteName = this.getSelectedSiteName(newSiteName)
if (normalizedSiteName === this.selectedSiteName) {
return
}
this.selectedSiteName = normalizedSiteName
this.tableData = (this.tableData || []).map(item => ({
...item,
siteName: normalizedSiteName
}))
}
},
data() {
@ -119,6 +126,7 @@ export default {
mode: '',//新增、编辑设备
editDeviceId: '',//编辑设备id
siteId: '',
selectedSiteName: '',
deviceCategory: '',//搜索栏设备类型
deviceCategoryList: [],//设备类别
tableData: [],
@ -153,6 +161,17 @@ export default {
hasValidSiteId(siteId) {
return !!(siteId !== undefined && siteId !== null && String(siteId).trim())
},
getSelectedSiteName(routeSiteName) {
const name = routeSiteName === undefined || routeSiteName === null ? '' : String(routeSiteName).trim()
if (name) {
return name
}
const matchedSite = (this.$store.getters.zdList || []).find(item => item.siteId === this.siteId)
if (matchedSite && matchedSite.siteName) {
return matchedSite.siteName
}
return this.siteId || ''
},
// 获取设备类别
getDeviceCategoryList() {
getAllDeviceCategory().then(response => {
@ -259,7 +278,12 @@ export default {
this.loading = true
const {siteId, deviceCategory, pageNum, pageSize} = this
getDeviceInfoList({siteId, deviceCategory, pageNum, pageSize}).then(response => {
this.tableData = response?.rows || [];
const selectedSiteName = this.getSelectedSiteName(this.$route.query.siteName)
this.selectedSiteName = selectedSiteName
this.tableData = (response?.rows || []).map(item => ({
...item,
siteName: selectedSiteName
}));
this.totalSize = response?.total || 0
}).finally(() => {
this.loading = false
@ -268,6 +292,7 @@ export default {
},
mounted() {
this.siteId = this.hasValidSiteId(this.$route.query.siteId) ? String(this.$route.query.siteId).trim() : ''
this.selectedSiteName = this.getSelectedSiteName(this.$route.query.siteName)
this.pageNum = 1//每次搜索从1开始搜索
this.getDeviceCategoryList()
this.getData()

File diff suppressed because it is too large Load Diff