|
|
@@ -156,10 +156,11 @@
|
|
|
:prop="`deviceBinds.${index}.nodeProp`"
|
|
|
:rules="rules.nodeProp">
|
|
|
<el-select
|
|
|
- v-model="bindItem.nodeProp"
|
|
|
+ :model-value="getBindTargetValue(bindItem)"
|
|
|
filterable
|
|
|
clearable
|
|
|
- placeholder="选择要驱动的组件属性">
|
|
|
+ placeholder="选择要驱动的组件属性"
|
|
|
+ @update:model-value="onNodePropChange(bindItem, $event)">
|
|
|
<el-option
|
|
|
v-for="nodeProp in nodePropOptions"
|
|
|
:key="nodeProp.value"
|
|
|
@@ -226,7 +227,7 @@
|
|
|
<span class="device-bind-card__mapping-arrow">→</span>
|
|
|
<div>
|
|
|
<span>组件属性</span>
|
|
|
- <strong>{{ getNodePropLabel(bindItem.nodeProp) }}</strong>
|
|
|
+ <strong>{{ getNodePropLabel(bindItem) }}</strong>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
@@ -248,15 +249,24 @@
|
|
|
<script setup lang="ts">
|
|
|
import type { WebtopoProjectLinkedDeviceVO } from '@/api/pms/maotu'
|
|
|
import type { IDoneJson, IDoneJsonDeviceBind } from '@/components/mt-edit/store/types'
|
|
|
+import { flattenDoneJson } from '@/components/mt-edit/utils'
|
|
|
import { Check, Connection, Delete, EditPen, Monitor, Plus } from '@element-plus/icons-vue'
|
|
|
import { ElMessage, type FormInstance, type FormItemRule } from 'element-plus'
|
|
|
import { computed, nextTick, reactive, ref } from 'vue'
|
|
|
|
|
|
type DeviceBindRow = IDoneJsonDeviceBind & {
|
|
|
+ targetNodeId?: string
|
|
|
editing?: boolean
|
|
|
isNew?: boolean
|
|
|
}
|
|
|
|
|
|
+type NodePropOption = {
|
|
|
+ label: string
|
|
|
+ value: string
|
|
|
+ nodeId: string
|
|
|
+ nodeProp: string
|
|
|
+}
|
|
|
+
|
|
|
type Props = {
|
|
|
item: IDoneJson
|
|
|
devices?: WebtopoProjectLinkedDeviceVO[]
|
|
|
@@ -279,15 +289,38 @@ const rules: Record<string, FormItemRule | FormItemRule[]> = {
|
|
|
nodeProp: [{ required: true, message: '请选择图形属性', trigger: 'change' }]
|
|
|
}
|
|
|
|
|
|
-const nodePropOptions = computed(() => {
|
|
|
- return Object.entries(props.item.props || {})
|
|
|
+function collectNodePropOptions(
|
|
|
+ item: IDoneJson,
|
|
|
+ path: string[] = [],
|
|
|
+ options: NodePropOption[] = []
|
|
|
+) {
|
|
|
+ Object.entries(item.props || {})
|
|
|
.filter(([, prop]) => !prop.disabled && prop.bindable !== false)
|
|
|
- .map(([key, prop]) => ({
|
|
|
- label: prop.title,
|
|
|
- value: `props.${key}.val`
|
|
|
- }))
|
|
|
-})
|
|
|
-const bindCount = computed(() => props.item.deviceBinds?.length || 0)
|
|
|
+ .forEach(([key, prop]) => {
|
|
|
+ const nodeProp = `props.${key}.val`
|
|
|
+ options.push({
|
|
|
+ label: path.length ? `${path.join(' / ')} · ${prop.title}` : prop.title,
|
|
|
+ value: `${item.id}::${nodeProp}`,
|
|
|
+ nodeId: item.id,
|
|
|
+ nodeProp
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ item.children?.forEach((child, index) => {
|
|
|
+ collectNodePropOptions(
|
|
|
+ child,
|
|
|
+ [...path, `${index + 1}. ${child.title || '未命名组件'}`],
|
|
|
+ options
|
|
|
+ )
|
|
|
+ })
|
|
|
+ return options
|
|
|
+}
|
|
|
+
|
|
|
+const allNodes = computed(() => flattenDoneJson([props.item]))
|
|
|
+const nodePropOptions = computed(() => collectNodePropOptions(props.item))
|
|
|
+const bindCount = computed(() =>
|
|
|
+ allNodes.value.reduce((count, item) => count + (item.deviceBinds?.length || 0), 0)
|
|
|
+)
|
|
|
const savedBindCount = computed(() => bindCount.value)
|
|
|
const hasEditingBind = computed(() => formModel.deviceBinds.some((item) => item.editing))
|
|
|
|
|
|
@@ -324,29 +357,47 @@ function getDevicePropLabel(bindItem: DeviceBindRow) {
|
|
|
)
|
|
|
}
|
|
|
|
|
|
-function getNodePropLabel(nodeProp?: string) {
|
|
|
+function getBindTargetValue(bindItem: DeviceBindRow) {
|
|
|
return (
|
|
|
- nodePropOptions.value.find((item) => item.value === nodeProp)?.label || nodeProp || '未知属性'
|
|
|
+ nodePropOptions.value.find(
|
|
|
+ (item) => item.nodeId === bindItem.targetNodeId && item.nodeProp === bindItem.nodeProp
|
|
|
+ )?.value || ''
|
|
|
)
|
|
|
}
|
|
|
|
|
|
-function updateItemDeviceBinds(item: IDoneJson, deviceBinds: IDoneJsonDeviceBind[]) {
|
|
|
- item.deviceBinds = deviceBinds
|
|
|
+function onNodePropChange(bindItem: DeviceBindRow, value?: string) {
|
|
|
+ const option = nodePropOptions.value.find((item) => item.value === value)
|
|
|
+ bindItem.targetNodeId = option?.nodeId
|
|
|
+ bindItem.nodeProp = option?.nodeProp || ''
|
|
|
}
|
|
|
|
|
|
-function syncDeviceBinds() {
|
|
|
- updateItemDeviceBinds(
|
|
|
- props.item,
|
|
|
- formModel.deviceBinds.map(({ editing, isNew, ...item }) => item)
|
|
|
+function getNodePropLabel(bindItem: DeviceBindRow) {
|
|
|
+ return (
|
|
|
+ nodePropOptions.value.find(
|
|
|
+ (item) => item.nodeId === bindItem.targetNodeId && item.nodeProp === bindItem.nodeProp
|
|
|
+ )?.label ||
|
|
|
+ bindItem.nodeProp ||
|
|
|
+ '未知属性'
|
|
|
)
|
|
|
}
|
|
|
|
|
|
+function syncDeviceBinds() {
|
|
|
+ allNodes.value.forEach((node) => {
|
|
|
+ node.deviceBinds = formModel.deviceBinds
|
|
|
+ .filter((item) => item.targetNodeId === node.id)
|
|
|
+ .map(({ targetNodeId, editing, isNew, ...item }) => item)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
async function openDrawer() {
|
|
|
- formModel.deviceBinds = (props.item.deviceBinds || []).map((item) => ({
|
|
|
- ...item,
|
|
|
- editing: false,
|
|
|
- isNew: false
|
|
|
- }))
|
|
|
+ formModel.deviceBinds = allNodes.value.flatMap((node) =>
|
|
|
+ (node.deviceBinds || []).map((item) => ({
|
|
|
+ ...item,
|
|
|
+ targetNodeId: node.id,
|
|
|
+ editing: false,
|
|
|
+ isNew: false
|
|
|
+ }))
|
|
|
+ )
|
|
|
editBackup.value = undefined
|
|
|
drawerVisible.value = true
|
|
|
}
|
|
|
@@ -362,6 +413,7 @@ function addBind() {
|
|
|
deviceId: undefined,
|
|
|
deviceProp: '',
|
|
|
nodeProp: '',
|
|
|
+ targetNodeId: undefined,
|
|
|
editing: true,
|
|
|
isNew: true
|
|
|
})
|
|
|
@@ -415,7 +467,10 @@ async function saveBind(index: number) {
|
|
|
|
|
|
const currentItem = formModel.deviceBinds[index]
|
|
|
const duplicateNodeProp = formModel.deviceBinds.some(
|
|
|
- (item, itemIndex) => itemIndex !== index && item.nodeProp === currentItem.nodeProp
|
|
|
+ (item, itemIndex) =>
|
|
|
+ itemIndex !== index &&
|
|
|
+ item.targetNodeId === currentItem.targetNodeId &&
|
|
|
+ item.nodeProp === currentItem.nodeProp
|
|
|
)
|
|
|
if (duplicateNodeProp) {
|
|
|
ElMessage.warning('该组件属性已经存在设备绑定,请选择其他属性')
|