|
|
|
|
@ -7,78 +7,97 @@
|
|
|
|
|
label-width="100px"
|
|
|
|
|
v-loading="formLoading"
|
|
|
|
|
>
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="编码" prop="code">
|
|
|
|
|
<el-input v-model="formData.code" placeholder="请输入编码" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="名称" prop="name">
|
|
|
|
|
<el-input v-model="formData.name" placeholder="请输入名称" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="能耗类型" prop="deviceTypeId">
|
|
|
|
|
<el-form-item label="表编码" prop="code">
|
|
|
|
|
<el-input v-model="formData.code" placeholder="请输入表编码" :disabled="formType === 'update'" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="表名称" prop="name">
|
|
|
|
|
<el-input v-model="formData.name" placeholder="请输入表名称" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="能源类型" prop="deviceTypeId">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="formData.deviceTypeId"
|
|
|
|
|
@change="handleQuery"
|
|
|
|
|
placeholder="请选择能耗类型"
|
|
|
|
|
@change="handleDeviceTypeChange"
|
|
|
|
|
placeholder="请选择能源类型"
|
|
|
|
|
clearable
|
|
|
|
|
filterable
|
|
|
|
|
class="!w-full"
|
|
|
|
|
>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="item in typeList"
|
|
|
|
|
:key="item.id"
|
|
|
|
|
:label="item.name"
|
|
|
|
|
:value="item.id"
|
|
|
|
|
/>
|
|
|
|
|
<el-option v-for="item in typeList" :key="item.id" :label="item.name" :value="item.id" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="单位" prop="unitName">
|
|
|
|
|
<el-input disabled v-model="formData.unitName" placeholder="" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="抄表周期cron" prop="checkCron">
|
|
|
|
|
<el-input v-model="formData.checkCron" placeholder="" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="是否启用" prop="isEnable">
|
|
|
|
|
<el-radio-group v-model="formData.isEnable">
|
|
|
|
|
<el-radio
|
|
|
|
|
v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
|
|
|
|
|
:key="dict.value"
|
|
|
|
|
:label="dict.value"
|
|
|
|
|
|
|
|
|
|
<el-form-item label="所属区域" prop="orgId">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="formData.orgId"
|
|
|
|
|
placeholder="根据区域结构筛选列表"
|
|
|
|
|
clearable
|
|
|
|
|
filterable
|
|
|
|
|
class="!w-full"
|
|
|
|
|
:loading="analysisLoading"
|
|
|
|
|
@change="handleOrgChange"
|
|
|
|
|
>
|
|
|
|
|
<el-option v-for="org in analysisList" :key="org.id" :label="org.name" :value="org.id" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="计算规则" prop="operationRulesVOList">
|
|
|
|
|
<div class="w-full flex flex-col gap-8px">
|
|
|
|
|
<div
|
|
|
|
|
v-for="(rule, index) in formData.operationRulesVOList"
|
|
|
|
|
:key="index"
|
|
|
|
|
class="w-full flex items-center gap-8px"
|
|
|
|
|
>
|
|
|
|
|
<el-tree-select
|
|
|
|
|
v-model="rule.pointValue"
|
|
|
|
|
:data="equipmentTree"
|
|
|
|
|
:props="treeSelectProps"
|
|
|
|
|
check-strictly
|
|
|
|
|
filterable
|
|
|
|
|
clearable
|
|
|
|
|
class="!w-full"
|
|
|
|
|
placeholder="根据设备选择点位"
|
|
|
|
|
:disabled="!formData.orgId"
|
|
|
|
|
@change="(val) => handlePointSelected(index, val)"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<template v-if="index < formData.operationRulesVOList.length - 1">
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="formData.operationRulesVOList[index].operator"
|
|
|
|
|
placeholder="运算符"
|
|
|
|
|
class="!w-110px"
|
|
|
|
|
clearable
|
|
|
|
|
>
|
|
|
|
|
{{ dict.label }}
|
|
|
|
|
</el-radio>
|
|
|
|
|
</el-radio-group>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-row>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="所属区域" prop="orgName">
|
|
|
|
|
<el-input v-model="formData.orgName" placeholder="" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
<el-col :span="12">
|
|
|
|
|
<el-form-item label="计算规则" prop="rules">
|
|
|
|
|
<el-input v-model="formData.rules" placeholder="" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-col>
|
|
|
|
|
</el-row>
|
|
|
|
|
<el-option v-for="op in operatorOptions" :key="op" :label="op" :value="op" />
|
|
|
|
|
</el-select>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<div class="flex items-center gap-4px">
|
|
|
|
|
<el-button link type="primary" @click="addRule">
|
|
|
|
|
<Icon icon="ep:plus" />
|
|
|
|
|
</el-button>
|
|
|
|
|
<el-button
|
|
|
|
|
link
|
|
|
|
|
type="danger"
|
|
|
|
|
:disabled="formData.operationRulesVOList.length <= 1"
|
|
|
|
|
@click="removeRule"
|
|
|
|
|
>
|
|
|
|
|
<Icon icon="ep:minus" />
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
|
|
<el-form-item label="是否启用" prop="isEnable">
|
|
|
|
|
<el-switch v-model="formData.isEnable" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<el-form-item label="信息资料" prop="info">
|
|
|
|
|
<!-- <el-form-item label="信息资料" prop="info">
|
|
|
|
|
<Editor v-model="formData.info" height="250px" />
|
|
|
|
|
</el-form-item>
|
|
|
|
|
</el-form-item> -->
|
|
|
|
|
<template #footer>
|
|
|
|
|
<el-button @click="submitForm" type="primary" :disabled="formLoading">确 定</el-button>
|
|
|
|
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
|
|
|
|
@ -86,9 +105,9 @@
|
|
|
|
|
</Dialog>
|
|
|
|
|
</template>
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import {getStrDictOptions, DICT_TYPE, getBoolDictOptions} from '@/utils/dict'
|
|
|
|
|
import { EnergyDeviceApi, EnergyDeviceVO } from '@/api/mes/energydevice'
|
|
|
|
|
import {EnergyTypeApi, EnergyTypeVO} from "@/api/mes/energytype";
|
|
|
|
|
import { OrganizationApi, DeviceParameterAnalysisNodeVO } from '@/api/mes/organization'
|
|
|
|
|
|
|
|
|
|
/** 能源设备 表单 */
|
|
|
|
|
defineOptions({ name: 'EnergyDeviceForm' })
|
|
|
|
|
@ -113,27 +132,68 @@ const formData = ref({
|
|
|
|
|
lastCheckValue: undefined,
|
|
|
|
|
unitName: undefined,
|
|
|
|
|
isEnable: undefined,
|
|
|
|
|
orgId: undefined,
|
|
|
|
|
orgName: undefined,
|
|
|
|
|
rules: undefined,
|
|
|
|
|
operationRulesVOList: [{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }],
|
|
|
|
|
})
|
|
|
|
|
const formRules = reactive({
|
|
|
|
|
name: [{ required: true, message: '设备名称不能为空', trigger: 'blur' }],
|
|
|
|
|
code: [{ required: true, message: '设备编码不能为空', trigger: 'blur' }],
|
|
|
|
|
name: [{ required: true, message: '表名称不能为空', trigger: 'blur' }],
|
|
|
|
|
code: [{ required: true, message: '表编码不能为空', trigger: 'blur' }],
|
|
|
|
|
orgId: [{ required: true, message: '所属区域不能为空', trigger: 'change' }],
|
|
|
|
|
operationRulesVOList: [{ required: true, validator: (_rule, _value, callback) => validateRules(callback), trigger: 'change' }],
|
|
|
|
|
isEnable: [{ required: true, message: '是否启用不能为空', trigger: 'blur' }]
|
|
|
|
|
})
|
|
|
|
|
const formRef = ref() // 表单 Ref
|
|
|
|
|
|
|
|
|
|
const analysisLoading = ref(false)
|
|
|
|
|
const analysisList = ref<DeviceParameterAnalysisNodeVO[]>([])
|
|
|
|
|
|
|
|
|
|
const treeSelectProps = {
|
|
|
|
|
label: 'name',
|
|
|
|
|
children: 'children',
|
|
|
|
|
disabled: 'disabled',
|
|
|
|
|
value: 'id'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const operatorOptions = ['+', '-', '*', '/']
|
|
|
|
|
|
|
|
|
|
const currentOrgNode = computed(() => {
|
|
|
|
|
return analysisList.value.find((o) => o.id === formData.value.orgId)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const equipmentTree = computed(() => {
|
|
|
|
|
const equipments = currentOrgNode.value?.equipments ?? []
|
|
|
|
|
return equipments.map((eq) => ({
|
|
|
|
|
id: `device:${eq.id}`,
|
|
|
|
|
name: eq.name,
|
|
|
|
|
disabled: true,
|
|
|
|
|
children: (eq.parameters ?? []).map((p) => ({
|
|
|
|
|
id: `${eq.id}:${p.id}`,
|
|
|
|
|
name: `${eq.name}: ${p.name}`
|
|
|
|
|
}))
|
|
|
|
|
}))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/** 打开弹窗 */
|
|
|
|
|
const open = async (type: string, id?: number) => {
|
|
|
|
|
dialogVisible.value = true
|
|
|
|
|
dialogTitle.value = t('action.' + type)
|
|
|
|
|
formType.value = type
|
|
|
|
|
resetForm()
|
|
|
|
|
|
|
|
|
|
await loadAnalysis()
|
|
|
|
|
// 修改时,设置数据
|
|
|
|
|
if (id) {
|
|
|
|
|
formLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
formData.value = await EnergyDeviceApi.getEnergyDevice(id)
|
|
|
|
|
if (!Array.isArray((formData.value as any).operationRulesVOList)) {
|
|
|
|
|
;(formData.value as any).operationRulesVOList = [
|
|
|
|
|
{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
hydratePointValues()
|
|
|
|
|
} finally {
|
|
|
|
|
formLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
@ -151,6 +211,14 @@ const submitForm = async () => {
|
|
|
|
|
try {
|
|
|
|
|
const data = formData.value as unknown as EnergyDeviceVO
|
|
|
|
|
data.deviceTypeName = typeList.value.find(item => item.id === data.deviceTypeId)?.name
|
|
|
|
|
|
|
|
|
|
const org = analysisList.value.find((o) => o.id === data.orgId)
|
|
|
|
|
if (org) {
|
|
|
|
|
;(data as any).orgName = org.name
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
;(data as any).operationRulesVOList = buildOperationRulesPayload()
|
|
|
|
|
|
|
|
|
|
if (formType.value === 'create') {
|
|
|
|
|
await EnergyDeviceApi.createEnergyDevice(data)
|
|
|
|
|
message.success(t('common.createSuccess'))
|
|
|
|
|
@ -179,7 +247,11 @@ const resetForm = () => {
|
|
|
|
|
lastCheckTime: undefined,
|
|
|
|
|
lastCheckValue: undefined,
|
|
|
|
|
unitName: undefined,
|
|
|
|
|
isEnable: true
|
|
|
|
|
isEnable: true,
|
|
|
|
|
orgId: undefined,
|
|
|
|
|
orgName: undefined,
|
|
|
|
|
rules: undefined,
|
|
|
|
|
operationRulesVOList: [{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }]
|
|
|
|
|
}
|
|
|
|
|
formRef.value?.resetFields()
|
|
|
|
|
}
|
|
|
|
|
@ -189,8 +261,102 @@ onMounted(async () => {
|
|
|
|
|
typeList.value = await EnergyTypeApi.getEnergyTypeList()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
/** 下拉框 */
|
|
|
|
|
const handleQuery = () => {
|
|
|
|
|
const loadAnalysis = async () => {
|
|
|
|
|
if (analysisList.value.length > 0) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
analysisLoading.value = true
|
|
|
|
|
try {
|
|
|
|
|
analysisList.value = await OrganizationApi.deviceParameterAnalysis({})
|
|
|
|
|
} finally {
|
|
|
|
|
analysisLoading.value = false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDeviceTypeChange = () => {
|
|
|
|
|
formData.value.unitName = typeList.value.find(item => item.id === formData.value.deviceTypeId)?.unit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleOrgChange = () => {
|
|
|
|
|
const org = analysisList.value.find((o) => o.id === formData.value.orgId)
|
|
|
|
|
formData.value.orgName = org?.name
|
|
|
|
|
formData.value.operationRulesVOList = [
|
|
|
|
|
{ deviceId: undefined, pointId: undefined, operator: undefined, pointValue: undefined }
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handlePointSelected = (index: number, val: any) => {
|
|
|
|
|
if (!val || typeof val !== 'string' || !val.includes(':')) {
|
|
|
|
|
formData.value.operationRulesVOList[index].deviceId = undefined
|
|
|
|
|
formData.value.operationRulesVOList[index].pointId = undefined
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const [deviceIdStr, pointIdStr] = val.split(':')
|
|
|
|
|
const deviceId = Number(deviceIdStr)
|
|
|
|
|
const pointId = Number(pointIdStr)
|
|
|
|
|
formData.value.operationRulesVOList[index].deviceId = Number.isNaN(deviceId) ? undefined : deviceId
|
|
|
|
|
formData.value.operationRulesVOList[index].pointId = Number.isNaN(pointId) ? undefined : pointId
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const addRule = () => {
|
|
|
|
|
formData.value.operationRulesVOList.push({
|
|
|
|
|
deviceId: undefined,
|
|
|
|
|
pointId: undefined,
|
|
|
|
|
operator: undefined,
|
|
|
|
|
pointValue: undefined
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const removeRule = () => {
|
|
|
|
|
if (formData.value.operationRulesVOList.length <= 1) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
formData.value.operationRulesVOList.pop()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const buildOperationRulesPayload = () => {
|
|
|
|
|
const list = (formData.value.operationRulesVOList ?? []).map((r, idx) => {
|
|
|
|
|
const item: any = {
|
|
|
|
|
deviceId: r.deviceId,
|
|
|
|
|
pointId: r.pointId
|
|
|
|
|
}
|
|
|
|
|
if (r.operator) {
|
|
|
|
|
item.operator = r.operator
|
|
|
|
|
}
|
|
|
|
|
return item
|
|
|
|
|
})
|
|
|
|
|
return list
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const hydratePointValues = () => {
|
|
|
|
|
const list = (formData.value as any).operationRulesVOList as any[]
|
|
|
|
|
if (!Array.isArray(list)) {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
list.forEach((r) => {
|
|
|
|
|
if (r?.deviceId && r?.pointId) {
|
|
|
|
|
r.pointValue = `${r.deviceId}:${r.pointId}`
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const validateRules = (callback: any) => {
|
|
|
|
|
const list = formData.value.operationRulesVOList ?? []
|
|
|
|
|
if (!list.length) {
|
|
|
|
|
callback(new Error('计算规则不能为空'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
const r = list[i] as any
|
|
|
|
|
if (!r?.deviceId || !r?.pointId) {
|
|
|
|
|
callback(new Error('请选择设备点位'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (i > 0 && !list[i - 1]?.operator) {
|
|
|
|
|
callback(new Error('请选择运算符'))
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
callback()
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|