iot-ui-vue/src/views/device/Instance/index.vue

397 lines
14 KiB
Vue

<template>
<JTable
ref="instanceRef"
:columns="columns"
:request="query"
:defaultParams="{sorts: [{name: 'createTime', order: 'desc'}]}"
:rowSelection="{
selectedRowKeys: _selectedRowKeys,
onChange: onSelectChange
}"
@cancelSelect="cancelSelect"
:params="params"
>
<template #headerTitle>
<a-space>
<a-button type="primary" @click="handleAdd">新增</a-button>
<a-dropdown>
<a-button>批量操作 <AIcon type="DownOutlined" /></a-button>
<template #overlay>
<a-menu>
<a-menu-item>
<a-button @click="exportVisible = true"><AIcon type="ExportOutlined" />批量导出设备</a-button>
</a-menu-item>
<a-menu-item>
<a-button @click="importVisible = true"><AIcon type="ImportOutlined" />批量导入设备</a-button>
</a-menu-item>
<a-menu-item>
<a-popconfirm @confirm="activeAllDevice" title="确认激活全部设备?">
<a-button type="primary" ghost><AIcon type="CheckCircleOutlined" />激活全部设备</a-button>
</a-popconfirm>
</a-menu-item>
<a-menu-item>
<a-button @click="syncDeviceStatus" type="primary"><AIcon type="SyncOutlined" />同步设备状态</a-button>
</a-menu-item>
<a-menu-item v-if="_selectedRowKeys.length">
<a-popconfirm @confirm="delSelectedDevice" title="已启用的设备无法删除,确认删除选中的禁用状态设备?">
<a-button type="primary" danger><AIcon type="DeleteOutlined" />删除选中设备</a-button>
</a-popconfirm>
</a-menu-item>
<a-menu-item v-if="_selectedRowKeys.length" title="确认激活选中设备?">
<a-popconfirm @confirm="activeSelectedDevice" >
<a-button type="primary"><AIcon type="CheckOutlined" />激活选中设备</a-button>
</a-popconfirm>
</a-menu-item>
<a-menu-item v-if="_selectedRowKeys.length">
<a-popconfirm @confirm="disabledSelectedDevice" title="确认禁用选中设备?">
<a-button type="primary" danger><AIcon type="StopOutlined" />禁用选中设备</a-button>
</a-popconfirm>
</a-menu-item>
</a-menu>
</template>
</a-dropdown>
</a-space>
</template>
<template #card="slotProps">
<CardBox
:value="slotProps"
@click="handleClick"
:actions="getActions(slotProps, 'card')"
v-bind="slotProps"
:active="_selectedRowKeys.includes(slotProps.id)"
:status="slotProps.state.value"
:statusText="slotProps.state.text"
:statusNames="{
online: 'success',
offline: 'error',
notActive: 'warning',
}"
>
<template #img>
<slot name="img">
<img :src="getImage('/device/instance/device-card.png')" />
</slot>
</template>
<template #content>
<h3 @click="handleView(slotProps.id)">{{ slotProps.name }}</h3>
<a-row>
<a-col :span="12">
<div class="card-item-content-text">设备类型</div>
<div>{{slotProps.deviceType.text}}</div>
</a-col>
<a-col :span="12">
<div class="card-item-content-text">产品名称</div>
<div>{{slotProps.productName}}</div>
</a-col>
</a-row>
</template>
<template #actions="item">
<a-tooltip v-bind="item.tooltip" :title="item.disabled && item.tooltip.title">
<a-popconfirm
v-if="item.popConfirm"
v-bind="item.popConfirm"
:disabled="item.disabled"
>
<a-button :disabled="item.disabled">
<AIcon type="DeleteOutlined" v-if="item.key === 'delete'" />
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item.text }}</span>
</template>
</a-button>
</a-popconfirm>
<template v-else>
<a-button :disabled="item.disabled" @click="item.onClick">
<AIcon type="DeleteOutlined" v-if="item.key === 'delete'" />
<template v-else>
<AIcon :type="item.icon" />
<span>{{ item.text }}</span>
</template>
</a-button>
</template>
</a-tooltip>
</template>
</CardBox>
</template>
<template #state="slotProps">
<a-badge :text="slotProps.state.text" :status="statusMap.get(slotProps.state.value)" />
</template>
<template #action="slotProps">
<a-space :size="16">
<a-tooltip
v-for="i in getActions(slotProps, 'table')"
:key="i.key"
v-bind="i.tooltip"
>
<a-popconfirm v-if="i.popConfirm" v-bind="i.popConfirm" :disabled="i.disabled">
<a-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-popconfirm>
<a-button
style="padding: 0"
type="link"
v-else
@click="i.onClick && i.onClick(slotProps)"
>
<a-button
:disabled="i.disabled"
style="padding: 0"
type="link"
><AIcon :type="i.icon"
/></a-button>
</a-button>
</a-tooltip>
</a-space>
</template>
</JTable>
<Import v-if="importVisible" @close="importVisible = false" />
<Export v-if="exportVisible" @close="exportVisible = false" :data="params" />
<Process v-if="operationVisible" @close="operationVisible = false" :api="api" :type="type" />
</template>
<script setup lang="ts">
import { query, _delete, _deploy, _undeploy, batchUndeployDevice, batchDeployDevice, batchDeleteDevice } from '@/api/device/instance'
import type { ActionsType } from '@/components/Table/index.vue'
import { getImage, LocalStore } from '@/utils/comm';
import { message } from "ant-design-vue";
import Import from './Import/index.vue'
import Export from './Export/index.vue'
import Process from './Process/index.vue'
import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable';
const instanceRef = ref<Record<string, any>>({});
const params = ref<Record<string, any>>({pageIndex: 0, pageSize: 12})
const _selectedRowKeys = ref<string[]>([])
const importVisible = ref<boolean>(false)
const exportVisible = ref<boolean>(false)
const current = ref<Record<string, any>>({})
const operationVisible = ref<boolean>(false)
const api = ref<string>('')
const type = ref<string>('')
const statusMap = new Map();
statusMap.set('online', 'processing');
statusMap.set('offline', 'error');
statusMap.set('notActive', 'warning');
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id'
},
{
title: '设备名称',
dataIndex: 'name',
key: 'name',
},
{
title: '产品名称',
dataIndex: 'productName',
key: 'productName',
},
{
title: '创建时间',
dataIndex: 'createTime',
key: 'createTime',
scopedSlots: true
},
{
title: '状态',
dataIndex: 'state',
key: 'state',
scopedSlots: true
},
{
title: '说明',
dataIndex: 'describe',
key: 'describe'
},
{
title: '操作',
key: 'action',
fixed: 'right',
width: 250,
scopedSlots: true
}
]
const paramsFormat = (config: any, _terms: any, name?: string) => {
if (config?.terms && Array.isArray(config.terms) && config?.terms.length > 0) {
(config?.terms || []).map((item: any, index: number) => {
if (item?.type) {
_terms[`${name ? `${name}.` : ''}terms[${index}].type`] = item.type;
}
paramsFormat(item, _terms, `${name ? `${name}.` : ''}terms[${index}]`);
});
} else if (!config?.terms && Object.keys(config).length > 0) {
Object.keys(config).forEach((key) => {
if (config[key]) {
_terms[`${name ? `${name}.` : ''}${key}`] = config[key];
}
});
}
}
const handleParams = (config: any) => {
const _terms: any = {};
paramsFormat(config, _terms);
const url = new URLSearchParams();
Object.keys(_terms).forEach((key) => {
url.append(key, _terms[key]);
});
return url.toString();
}
/**
* 新增
*/
const handleAdd = () => {
message.warn('新增')
}
/**
* 查看
*/
const handleView = (dt: any) => {
// message.warn('查看')
}
const getActions = (data: Partial<Record<string, any>>, type: 'card' | 'table'): ActionsType[] => {
if(!data) return []
const actions = [
{
key: 'view',
text: "查看",
tooltip: {
title: '查看'
},
icon: 'EyeOutlined',
onClick: () => {
handleView(data)
}
},
{
key: 'edit',
text: "编辑",
tooltip: {
title: '编辑'
},
icon: 'EditOutlined',
onClick: () => {
message.warn('edit')
}
},
{
key: 'action',
text: data.state.value !== 'notActive' ? "禁用" : "启用",
tooltip: {
title: data.state.value !== 'notActive' ? "禁用" : "启用",
},
icon: data.state.value !== 'notActive' ? 'StopOutlined' : 'CheckCircleOutlined',
popConfirm: {
title: `确认${data.state.value !== 'notActive' ? "禁用" : "启用"}?`,
onConfirm: async () => {
let response = undefined
if(data.state.value !== 'notActive') {
response = await _undeploy(data.id)
} else {
response = await _deploy(data.id)
}
if(response && response.status === 200) {
message.success('操作成功!')
instanceRef.value?.reload()
} else {
message.error('操作失败!')
}
}
}
},
{
key: 'delete',
text: "删除",
disabled: data.state.value !== 'notActive',
tooltip: {
title: data.state.value !== 'notActive' ? '已启用的设备不能删除' : '删除'
},
popConfirm: {
title: '确认删除?',
onConfirm: async () => {
const resp = await _delete(data.id)
if(resp.status === 200) {
message.success('操作成功!')
instanceRef.value?.reload()
} else {
message.error('操作失败!')
}
}
},
icon: 'DeleteOutlined'
}
]
if(type === 'card') return actions.filter((i: ActionsType) => i.key !== 'view')
return actions
}
const onSelectChange = (keys: string[]) => {
_selectedRowKeys.value = [...keys]
}
const cancelSelect = () => {
_selectedRowKeys.value = []
}
const handleClick = (dt: any) => {
if(_selectedRowKeys.value.includes(dt.id)) {
const _index = _selectedRowKeys.value.findIndex(i => i === dt.id)
_selectedRowKeys.value.splice(_index, 1)
} else {
_selectedRowKeys.value = [..._selectedRowKeys.value, dt.id]
}
}
const activeAllDevice = () => {
type.value = 'active'
const activeAPI = `/${BASE_API_PATH}/device-instance/deploy?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}&${handleParams(params)}`;
api.value = activeAPI
operationVisible.value = true
}
const syncDeviceStatus = () => {
type.value = 'sync'
const syncAPI = `/${BASE_API_PATH}/device-instance/state/_sync?:X_Access_Token=${LocalStore.get(TOKEN_KEY)}&${handleParams(params)}`;
api.value = syncAPI
operationVisible.value = true
}
const delSelectedDevice = async () => {
const resp = await batchDeleteDevice(_selectedRowKeys.value)
if(resp.status === 200){
message.success('操作成功!')
_selectedRowKeys.value = []
instanceRef.value?.reload()
}
}
const activeSelectedDevice = async () => {
const resp = await batchDeployDevice(_selectedRowKeys.value)
if(resp.status === 200){
message.success('操作成功!')
_selectedRowKeys.value = []
instanceRef.value?.reload()
}
}
const disabledSelectedDevice = async () => {
const resp = await batchUndeployDevice(_selectedRowKeys.value)
if(resp.status === 200){
message.success('操作成功!')
_selectedRowKeys.value = []
instanceRef.value?.reload()
}
}
</script>