|
|
|
|
@ -129,6 +129,7 @@
|
|
|
|
|
:title="dialogMode === 'create' ? '新增数据大屏' : '编辑数据大屏'"
|
|
|
|
|
width="600px"
|
|
|
|
|
draggable
|
|
|
|
|
@closed="handleCreateDialogClosed"
|
|
|
|
|
>
|
|
|
|
|
<el-form :model="createForm" ref="createFormRef" label-width="80px" :rules="createFormRules">
|
|
|
|
|
<el-form-item label="名称" prop="name">
|
|
|
|
|
@ -157,24 +158,66 @@
|
|
|
|
|
/>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="设备" v-if="createForm.type === '1'">
|
|
|
|
|
<div class="dashboard-device-group-list">
|
|
|
|
|
<div
|
|
|
|
|
v-for="(group, index) in deviceAttrSelections"
|
|
|
|
|
:key="index"
|
|
|
|
|
class="dashboard-device-group"
|
|
|
|
|
>
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="selectedDeviceIds"
|
|
|
|
|
multiple
|
|
|
|
|
:multiple-limit="8"
|
|
|
|
|
collapse-tags
|
|
|
|
|
collapse-tags-tooltip
|
|
|
|
|
placeholder="请选择设备(必须选择8个)"
|
|
|
|
|
v-model="group.deviceId"
|
|
|
|
|
placeholder="请选择设备"
|
|
|
|
|
clearable
|
|
|
|
|
filterable
|
|
|
|
|
class="!w-240px"
|
|
|
|
|
class="!w-160px mr-8px"
|
|
|
|
|
@change="(val) => handleDeviceChange(val, index)"
|
|
|
|
|
>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="item in deviceList"
|
|
|
|
|
:key="item.id"
|
|
|
|
|
:label="item.deviceName"
|
|
|
|
|
:value="String(item.id)"
|
|
|
|
|
:value="item.id"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
<el-select
|
|
|
|
|
v-model="group.attributeIds"
|
|
|
|
|
multiple
|
|
|
|
|
collapse-tags
|
|
|
|
|
collapse-tags-tooltip
|
|
|
|
|
:disabled="!group.deviceId"
|
|
|
|
|
placeholder="请选择点位"
|
|
|
|
|
clearable
|
|
|
|
|
filterable
|
|
|
|
|
class="!w-260px"
|
|
|
|
|
>
|
|
|
|
|
<el-option
|
|
|
|
|
v-for="attr in (deviceAttributeOptionsMap[String(group.deviceId)] || [])"
|
|
|
|
|
:key="attr.id"
|
|
|
|
|
:label="attr.attributeName"
|
|
|
|
|
:value="attr.id"
|
|
|
|
|
/>
|
|
|
|
|
</el-select>
|
|
|
|
|
<el-button
|
|
|
|
|
v-if="deviceAttrSelections.length > 1"
|
|
|
|
|
type="danger"
|
|
|
|
|
text
|
|
|
|
|
class="dashboard-device-remove-btn"
|
|
|
|
|
@click="removeDeviceAttrGroup(index)"
|
|
|
|
|
circle
|
|
|
|
|
>
|
|
|
|
|
<Icon icon="ep:minus" />
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<el-button
|
|
|
|
|
type="primary"
|
|
|
|
|
text
|
|
|
|
|
@click="addDeviceAttrGroup"
|
|
|
|
|
:disabled="deviceAttrSelections.length >= 8"
|
|
|
|
|
class="mt-8px"
|
|
|
|
|
>
|
|
|
|
|
<Icon icon="ep:plus" class="mr-5px" /> 添加设备
|
|
|
|
|
</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="内容">
|
|
|
|
|
<el-input
|
|
|
|
|
@ -230,7 +273,8 @@ interface DashboardItem {
|
|
|
|
|
type?: string
|
|
|
|
|
orgId?: number | string
|
|
|
|
|
orgName?: string
|
|
|
|
|
deviceIds?: string
|
|
|
|
|
deviceIds?: string | { deviceId: number; attributesIds: number[] }[]
|
|
|
|
|
deviceIdsList?: { deviceId: number; attributesIds: number[] }[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const loading = ref(false)
|
|
|
|
|
@ -269,7 +313,7 @@ const createForm = reactive({
|
|
|
|
|
type: '',
|
|
|
|
|
orgId: undefined as number | string | undefined,
|
|
|
|
|
orgName: '',
|
|
|
|
|
deviceIds: '',
|
|
|
|
|
deviceIdsList: [] as { deviceId: number; attributesIds: number[] }[],
|
|
|
|
|
indexImage: '',
|
|
|
|
|
route: '',
|
|
|
|
|
content: ''
|
|
|
|
|
@ -289,7 +333,10 @@ const lineTreeProps = {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const deviceList = ref<any[]>([])
|
|
|
|
|
const selectedDeviceIds = ref<string[]>([])
|
|
|
|
|
const deviceAttrSelections = ref<{ deviceId?: number | string; attributeIds: number[] }[]>([
|
|
|
|
|
{ deviceId: undefined, attributeIds: [] }
|
|
|
|
|
])
|
|
|
|
|
const deviceAttributeOptionsMap = ref<Record<string, any[]>>({})
|
|
|
|
|
|
|
|
|
|
const getList = async () => {
|
|
|
|
|
loading.value = true
|
|
|
|
|
@ -324,8 +371,27 @@ const handlePreview = (item: DashboardItem) => {
|
|
|
|
|
}
|
|
|
|
|
const path = route.startsWith('/') ? route : `/${route}`
|
|
|
|
|
const queryParams = new URLSearchParams()
|
|
|
|
|
if (item.id) queryParams.append('goviewId', String(item.id))
|
|
|
|
|
if (item.orgId) queryParams.append('orgId', String(item.orgId))
|
|
|
|
|
if (item.type === '1' && item.deviceIds) queryParams.append('deviceIds', item.deviceIds)
|
|
|
|
|
if (item.type === '1') {
|
|
|
|
|
let deviceIdsStr = ''
|
|
|
|
|
if (Array.isArray(item.deviceIdsList) && item.deviceIdsList.length) {
|
|
|
|
|
deviceIdsStr = item.deviceIdsList
|
|
|
|
|
.map((g) => g.deviceId)
|
|
|
|
|
.filter((id) => id !== undefined && id !== null)
|
|
|
|
|
.join(',')
|
|
|
|
|
} else if (Array.isArray(item.deviceIds)) {
|
|
|
|
|
deviceIdsStr = item.deviceIds
|
|
|
|
|
.map((g: any) => g?.deviceId)
|
|
|
|
|
.filter((id: any) => id !== undefined && id !== null)
|
|
|
|
|
.join(',')
|
|
|
|
|
} else if (typeof item.deviceIds === 'string') {
|
|
|
|
|
deviceIdsStr = item.deviceIds
|
|
|
|
|
}
|
|
|
|
|
if (deviceIdsStr && deviceIdsStr.trim()) {
|
|
|
|
|
queryParams.append('deviceIds', deviceIdsStr.trim())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
const queryString = queryParams.toString()
|
|
|
|
|
const url = router.resolve(path + (queryString ? `?${queryString}` : '')).href
|
|
|
|
|
window.open(url, '_blank')
|
|
|
|
|
@ -338,11 +404,16 @@ const resetCreateForm = () => {
|
|
|
|
|
createForm.type = ''
|
|
|
|
|
createForm.orgId = undefined
|
|
|
|
|
createForm.orgName = ''
|
|
|
|
|
createForm.deviceIds = ''
|
|
|
|
|
createForm.deviceIdsList = []
|
|
|
|
|
createForm.indexImage = ''
|
|
|
|
|
createForm.route = ''
|
|
|
|
|
createForm.content = ''
|
|
|
|
|
selectedDeviceIds.value = []
|
|
|
|
|
deviceAttrSelections.value = [{ deviceId: undefined, attributeIds: [] }]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleCreateDialogClosed = () => {
|
|
|
|
|
resetCreateForm()
|
|
|
|
|
createFormRef.value?.resetFields?.()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const openCreateDialog = () => {
|
|
|
|
|
@ -352,6 +423,38 @@ const openCreateDialog = () => {
|
|
|
|
|
createDialogVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const normalizeDeviceIdsList = (val: any): { deviceId: number; attributesIds: number[] }[] => {
|
|
|
|
|
if (!val) return []
|
|
|
|
|
if (Array.isArray(val)) {
|
|
|
|
|
return val
|
|
|
|
|
.map((v: any) => ({
|
|
|
|
|
deviceId: Number(v?.deviceId),
|
|
|
|
|
attributesIds: Array.isArray(v?.attributesIds)
|
|
|
|
|
? v.attributesIds.map((id: any) => Number(id)).filter((id: number) => !Number.isNaN(id))
|
|
|
|
|
: []
|
|
|
|
|
}))
|
|
|
|
|
.filter((v) => v.deviceId && !Number.isNaN(v.deviceId))
|
|
|
|
|
}
|
|
|
|
|
if (typeof val === 'string') {
|
|
|
|
|
const trimmed = val.trim()
|
|
|
|
|
if (!trimmed) return []
|
|
|
|
|
if (trimmed.startsWith('[')) {
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(trimmed)
|
|
|
|
|
if (Array.isArray(parsed)) {
|
|
|
|
|
return normalizeDeviceIdsList(parsed)
|
|
|
|
|
}
|
|
|
|
|
} catch {}
|
|
|
|
|
}
|
|
|
|
|
const ids = trimmed
|
|
|
|
|
.split(',')
|
|
|
|
|
.map((s) => Number(s.trim()))
|
|
|
|
|
.filter((id) => !Number.isNaN(id))
|
|
|
|
|
return ids.map((id) => ({ deviceId: id, attributesIds: [] }))
|
|
|
|
|
}
|
|
|
|
|
return []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const openEditDialog = (item: DashboardItem) => {
|
|
|
|
|
dialogMode.value = 'edit'
|
|
|
|
|
editingId.value = item.id
|
|
|
|
|
@ -361,13 +464,26 @@ const openEditDialog = (item: DashboardItem) => {
|
|
|
|
|
createForm.type = item.type || ''
|
|
|
|
|
createForm.orgId = item.orgId || undefined
|
|
|
|
|
createForm.orgName = item.orgName || ''
|
|
|
|
|
createForm.deviceIds = item.deviceIds || ''
|
|
|
|
|
const fromNew = Array.isArray(item.deviceIdsList) && item.deviceIdsList.length
|
|
|
|
|
? normalizeDeviceIdsList(item.deviceIdsList)
|
|
|
|
|
: []
|
|
|
|
|
const fromLegacy = !fromNew.length ? normalizeDeviceIdsList(item.deviceIds) : []
|
|
|
|
|
createForm.deviceIdsList = fromNew.length ? fromNew : fromLegacy
|
|
|
|
|
createForm.indexImage = item.indexImage || ''
|
|
|
|
|
createForm.route = item.route || ''
|
|
|
|
|
createForm.content = item.content || ''
|
|
|
|
|
selectedDeviceIds.value = createForm.deviceIds
|
|
|
|
|
? createForm.deviceIds.split(',').filter((v) => v)
|
|
|
|
|
: []
|
|
|
|
|
deviceAttrSelections.value =
|
|
|
|
|
createForm.deviceIdsList && createForm.deviceIdsList.length
|
|
|
|
|
? createForm.deviceIdsList.slice(0, 8).map((g) => ({
|
|
|
|
|
deviceId: g.deviceId,
|
|
|
|
|
attributeIds: Array.isArray(g.attributesIds) ? g.attributesIds.slice() : []
|
|
|
|
|
}))
|
|
|
|
|
: [{ deviceId: undefined, attributeIds: [] }]
|
|
|
|
|
deviceAttrSelections.value.forEach((g) => {
|
|
|
|
|
if (g.deviceId) {
|
|
|
|
|
loadDeviceAttributes(g.deviceId)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
createDialogVisible.value = true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -384,17 +500,22 @@ const submitDialog = async () => {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (createForm.type === '1') {
|
|
|
|
|
if (!selectedDeviceIds.value.length) {
|
|
|
|
|
message.error('设备不能为空')
|
|
|
|
|
const groups = deviceAttrSelections.value
|
|
|
|
|
.map((g) => ({
|
|
|
|
|
deviceId: g.deviceId,
|
|
|
|
|
attributesIds: (g.attributeIds || []).filter((v) => v !== undefined && v !== null)
|
|
|
|
|
}))
|
|
|
|
|
.filter((g) => g.deviceId && g.attributesIds.length)
|
|
|
|
|
if (!groups.length) {
|
|
|
|
|
message.error('请至少配置一组设备和点位')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if (selectedDeviceIds.value.length !== 8) {
|
|
|
|
|
message.error('设备必须选择8个')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
createForm.deviceIds = selectedDeviceIds.value.join(',')
|
|
|
|
|
createForm.deviceIdsList = groups.map((g) => ({
|
|
|
|
|
deviceId: Number(g.deviceId),
|
|
|
|
|
attributesIds: g.attributesIds.map((id: any) => Number(id))
|
|
|
|
|
}))
|
|
|
|
|
} else {
|
|
|
|
|
createForm.deviceIds = ''
|
|
|
|
|
createForm.deviceIdsList = []
|
|
|
|
|
}
|
|
|
|
|
if (createForm.orgId) {
|
|
|
|
|
const org = findOrgNode(organizationTree.value || [], createForm.orgId)
|
|
|
|
|
@ -475,6 +596,39 @@ const loadDeviceList = async () => {
|
|
|
|
|
deviceList.value = await DeviceApi.getDeviceList()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const loadDeviceAttributes = async (deviceId: number | string) => {
|
|
|
|
|
const key = String(deviceId)
|
|
|
|
|
if (!key) return
|
|
|
|
|
if (deviceAttributeOptionsMap.value[key]) return
|
|
|
|
|
const data = await request.get({
|
|
|
|
|
url: '/iot/device/device-attribute/page',
|
|
|
|
|
params: {
|
|
|
|
|
pageNo: 1,
|
|
|
|
|
pageSize: 100,
|
|
|
|
|
deviceId
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
deviceAttributeOptionsMap.value[key] = data?.list || []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const handleDeviceChange = async (value: number | string, index: number) => {
|
|
|
|
|
const group = deviceAttrSelections.value[index]
|
|
|
|
|
if (!group) return
|
|
|
|
|
group.attributeIds = []
|
|
|
|
|
if (!value) return
|
|
|
|
|
await loadDeviceAttributes(value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const addDeviceAttrGroup = () => {
|
|
|
|
|
if (deviceAttrSelections.value.length >= 8) return
|
|
|
|
|
deviceAttrSelections.value.push({ deviceId: undefined, attributeIds: [] })
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const removeDeviceAttrGroup = (index: number) => {
|
|
|
|
|
if (deviceAttrSelections.value.length <= 1) return
|
|
|
|
|
deviceAttrSelections.value.splice(index, 1)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
getList()
|
|
|
|
|
loadOrganizationTree()
|
|
|
|
|
@ -554,4 +708,21 @@ onMounted(() => {
|
|
|
|
|
.dashboard-card-menu {
|
|
|
|
|
min-width: 120px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-device-group-list {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-device-group {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
gap: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.dashboard-device-remove-btn {
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
}
|
|
|
|
|
</style>
|
|
|
|
|
|