iot-ui-vue/src/views/device/components/InklingDevice/index.vue

361 lines
9.0 KiB
Vue

<template>
<div class='inkling-device'>
<j-spin :spinning='spinning'>
<div class='search-box'>
<div class='search-warp'>
<j-advanced-search
v-if='!spinning'
:columns='columns'
type='simple'
@search='handleSearch'
class='device-inkling'
target='device-inkling'
/>
</div>
<div class='multiple' v-if='multiple'>
<j-checkbox @change='checkChange'>全选</j-checkbox>
</div>
</div>
<div class='device-list-warp'>
<j-scrollbar v-if='deviceList.length'>
<j-spin :spinning='deviceSpinning'>
<div class='device-list-items'>
<template v-for='item in deviceList'>
<template v-if='disabledKeys.includes(item.id)'>
<j-tooltip
title='该设备已绑定平台设备'
>
<div
:class='{
"device-list-item": true,
"active": checkKeys.includes(item.id),
"disabled": disabledKeys.includes(item.id)
}'
@click='() => deviceClick(item.id, item)'
>
<div class='item-title'>
<span class="title-name no-tooltip">
{{item.name}}
</span>
<span class="title-id">
({{ item.id }})
</span>
</div>
</div>
</j-tooltip>
</template>
<div
v-else
:class='{
"device-list-item": true,
"active": checkKeys.includes(item.id),
"disabled": disabledKeys.includes(item.id)
}'
@click='() => deviceClick(item.id, item)'
>
<div class='item-title'>
<span class="title-name">
<j-ellipsis>
{{item.name}}
</j-ellipsis>
</span>
<span class="title-id">
<j-ellipsis>
({{ item.id }})
</j-ellipsis>
</span>
</div>
<a-icon
v-if='checkKeys.includes(item.id)'
type='CheckOutlined'
/>
</div>
</template>
</div>
</j-spin>
</j-scrollbar>
<j-empty
v-else
description='暂无数据'
style='padding-top: 24px'
/>
<div class='device-list-pagination'>
<j-pagination
v-if='showPage'
:total='pageData.total'
:current='pageData.pageIndex + 1'
:pageSize='pageData.pageSize'
:show-total='() => {
const minSize = pageData.pageIndex * pageData.pageSize + 1;
const MaxSize = (pageData.pageIndex + 1) * pageData.pageSize;
return `${minSize} - ${MaxSize > pageData.total ? pageData.total : MaxSize } 条/总共 ${pageData.total}`;
}'
@change='pageChange'
/>
</div>
</div>
</j-spin>
</div>
</template>
<script setup lang='ts' name='InklingDevice'>
import { getCommandsByAccess, getCommandsDevicesByAccessId } from '@/api/link/accessConfig'
import { getInkingDevices } from '@/api/device/instance'
import { isArray } from 'lodash-es'
type Emit = {
(e: 'update:value', data: string | string[]): void
(e: 'change', data: any | any[]): void
}
const props = defineProps({
value: {
type: [String, Array],
default: undefined
},
accessId: {
type: String,
default: undefined
},
multiple: {
type: Boolean,
default: false
}
})
const emit = defineEmits<Emit>()
const spinning = ref(true)
const deviceSpinning = ref(false)
const deviceList = ref([])
const disabledKeys = ref<string[]>([])
const checkKeys = ref<string[]>([])
const checkCache = ref<Map<string, any>>(new Map())
const showPage = ref(false)
const pageData = reactive({
pageSize: 10,
pageIndex: 0,
total: 0
})
const params = ref({
terms: []
})
const columns = ref([])
const queryInkingDevices = (data: string[]) => {
return new Promise(async (resolve) => {
if (!data.length) {
resolve(true)
return
}
const res = await getInkingDevices(data)
if (res) {
disabledKeys.value = res.result?.map(item => item.externalId)
}
resolve(true)
})
}
const getDeviceList = async () => {
const resp = await getCommandsDevicesByAccessId(props.accessId!, {
pageIndex: pageData.pageIndex,
pageSize: pageData.pageSize,
terms: params.value.terms
}).catch(() => ({ success: false }))
if (resp.success) {
await queryInkingDevices(resp.result?.data.map(item => item.id) || [])
deviceList.value = resp.result?.data || []
pageData.total = resp.result?.total || 0
}
}
const checkChange = (e: any) => { // 全选
if (e.target.checked) {
const keys = deviceList.value.filter(item => {
// 过滤已选中和已绑定
const type = !checkKeys.value.includes(item.id) && !disabledKeys.value.includes(item.id)
if (type && !checkCache.value.has(item.id)) {
checkCache.value.set(item.id, item)
}
return type
}).map(item => item.id)
checkKeys.value = [...checkKeys.value, ...keys]
emit('update:value', checkKeys.value)
emit('change', [...checkCache.value.values()])
} else {
checkCache.value.clear()
checkKeys.value = []
emit('update:value', [])
emit('change', [])
}
}
const handleSearch = (p: any) => { // 查询
pageData.pageIndex = 0
params.value = p
getDeviceList()
}
const pageChange = (page: number, pageSize: number) => { // 分页变化
pageData.pageSize = pageSize
pageData.pageIndex = page - 1
getDeviceList()
}
const init = async () => {
if (props.accessId) {
const resp = await getCommandsByAccess(props.accessId)
if (resp.success) {
const item = resp.result?.[0]
if (item) {
showPage.value = item.id === 'QueryDevicePage' // 分页
columns.value = item.expands?.terms?.map(t => ({
title: t.name,
dataIndex: t.id,
search: {
type: t.valueType.type
}
}))
}
}
spinning.value = false
await getDeviceList()
}
}
const deviceClick = (id: string, option: any) => {
if (option.disabled || disabledKeys.value.includes(id)) return
const _check = new Set(checkKeys.value)
if (props.multiple) { // 多选
if (_check.has(id)) {
_check.delete(id)
checkCache.value.delete(id)
} else {
checkCache.value.set(id, option)
_check.add(id)
}
checkKeys.value = [..._check.values()]
emit('update:value', checkKeys.value)
emit('change', [...checkCache.value.values()])
} else {
checkKeys.value = [id]
emit('update:value', id)
emit('change', option)
}
}
watch(() => props.value, (newValue) => {
if (!newValue) {
checkKeys.value = []
return
}
if (isArray(newValue)) {
checkKeys.value = newValue
} else {
checkKeys.value = [newValue as string]
}
}, { immediate: true, deep: true })
onMounted(() => {
init()
})
</script>
<style scoped lang='less'>
.inkling-device {
min-height: 200px;
}
.search-box {
padding-bottom: 24px;
border-bottom: 1px solid #f0f0f0;
display: flex;
margin-bottom: 12px;
gap: 24px;
align-items: center;
:deep(.device-inkling) {
padding: 0;
margin: 0;
padding-bottom: 0;
}
.search-warp {
flex: 1 1 auto;
}
.multiple {
width: 60px;
}
}
.device-list-warp {
.device-list-items {
.device-list-item {
padding: 10px 16px;
color: #4F4F4F;
border-bottom: 1px solid #f0f0f0;
display: flex;
justify-content: space-between;
> .item-title {
display: flex;
min-width: 0;
flex-grow: 1;
gap: 8px;
.title-name {
max-width: 70%;
}
.no-tooltip {
overflow: hidden;
vertical-align: bottom;
cursor: pointer;
display: -webkit-box;
-webkit-box-orient: vertical;
word-break: break-all;
max-height: 380px;
-webkit-line-clamp: 1;
text-overflow: ellipsis;
}
.title-id {
flex: 1 1 auto;
color: #a3a3a3;
}
}
&:hover {
background-color: rgba(47, 84, 235, 0.06);
}
&.active {
background-color: rgba(153, 153, 153, 0.06);
color: @primary-color;
.title-id {
color: @primary-color;
opacity: .8;
}
}
&.disabled {
cursor: not-allowed;
background-color: rgba(153, 153, 153, 0.06);
}
}
}
.device-list-pagination {
margin-top: 24px;
text-align: right;
}
}
</style>