kkk-ops 1 month ago
commit 0df1124747

@ -31,6 +31,28 @@ export interface DeviceConnectParams {
isConnect: string | number
}
export interface LineDeviceVO {
id?: string | number
lineNode?: string
lineName?: string
deviceCode?: string
deviceName?: string
status?: string | number
collectionTime?: string | number
}
export interface LineDevicePageParams {
pageNo: number
pageSize: number
id?: string | number
lineNode?: string
lineName?: string
deviceCode?: string
deviceName?: string
status?: string | number
collectionTime?: string | number
}
// 物联设备 API
export const DeviceApi = {
// 查询物联设备分页
@ -75,6 +97,10 @@ export const DeviceApi = {
return await request.download({ url: `/iot/device/export-excel`, params })
},
getLineDevicePage: async (params: LineDevicePageParams) => {
return await request.get({ url: `/iot/device/lineDevicePage`, params })
},
// ==================== 子表(设备属性) ====================
// 获得设备属性分页

@ -1,30 +1,14 @@
<template>
<ContentWrap>
<!-- 搜索工作栏 -->
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="68px"
>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="设备编号" prop="deviceCode">
<el-input
v-model="queryParams.deviceCode"
placeholder="请输入设备编号"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
<el-input v-model="queryParams.deviceCode" placeholder="请输入设备编号" clearable @keyup.enter="handleQuery"
class="!w-240px" />
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input
v-model="queryParams.deviceName"
placeholder="请输入设备名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
<el-input v-model="queryParams.deviceName" placeholder="请输入设备名称" clearable @keyup.enter="handleQuery"
class="!w-240px" />
</el-form-item>
<!-- <el-form-item label="连接状态" prop="status">
@ -55,23 +39,17 @@
/>
</el-form-item> -->
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['iot:device:create']"
>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置
</el-button>
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['iot:device:export']"
>
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['iot:device:export']">
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
<el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device:delete']">
@ -83,23 +61,16 @@
<!-- 列表 -->
<ContentWrap>
<el-table
ref="tableRef"
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
@selection-change="handleSelectionChange"
>
<el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="设备编号" align="left" prop="deviceCode"/>
<el-table-column label="设备名称" align="left" prop="deviceName"/>
<el-table-column label="设备编号" align="left" prop="deviceCode" />
<el-table-column label="设备名称" align="left" prop="deviceName" />
<!-- <el-table-column label="设备类型" align="left" prop="deviceType" width="150px"> -->
<!-- <template #default="scope">
<!-- <template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_DEVICE_TYPE" :value="scope.row.deviceType" />
</template>
</el-table-column> -->
</el-table-column> -->
<el-table-column label="采集协议" align="left" prop="protocol" width="250px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_PROTOCOL" :value="scope.row.protocol" />
@ -110,12 +81,12 @@
<dict-tag :type="DICT_TYPE.IOT_GATEWAY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="采集周期(s)" align="left" prop="sampleCycle" width="200px"/>
<el-table-column label="采集周期(s)" align="left" prop="sampleCycle" width="200px" />
<!-- <el-table-column label="读主题" align="center" prop="readTopic" />
<el-table-column label="写主题" align="center" prop="writeTopic" />
<el-table-column label="网关id" align="center" prop="gatewayId" /> -->
<!-- <el-table-column label="设备品牌id" align="center" prop="deviceBrandId" />-->
<!-- <el-table-column label="设备品牌id" align="center" prop="deviceBrandId" />-->
<!-- <el-table-column label="离线间隔" align="center" prop="offLineDuration" /> -->
<!-- <el-table-column
label="最后上线时间"
@ -132,7 +103,7 @@
:formatter="dateFormatter"
width="170px"
/> -->
<el-table-column label="是否启用" align="center" prop="isEnable" fixed="right" width="200px">
<el-table-column label="是否启用" align="center" prop="isEnable" fixed="right" width="200px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.isEnable" />
</template>
@ -140,56 +111,28 @@
<el-table-column label="操作" align="center" fixed="right" width="380px">
<template #default="scope">
<el-button link type="primary" @click.stop="handleShowAttribute(scope.row)">点位</el-button>
<el-button
link
type="primary"
@click="openForm('setting', scope.row.id)"
v-hasPermi="['iot:device:update']"
>
<el-button link type="primary" @click="openForm('setting', scope.row.id)" v-hasPermi="['iot:device:update']">
设置
</el-button>
<el-button
link
type="primary"
@click="handleCopy(scope.row.id)"
v-hasPermi="['iot:device:create']"
>
<el-button link type="primary" @click="handleCopy(scope.row.id)" v-hasPermi="['iot:device:create']">
复制
</el-button>
<el-button
link
type="primary"
@click.stop="handleEdit(scope.row)"
v-hasPermi="['iot:device:update']"
>
<el-button link type="primary" @click.stop="handleEdit(scope.row)" v-hasPermi="['iot:device:update']">
编辑
</el-button>
<el-button
link
:type="isRowConnected(scope.row) ? 'warning' : 'success'"
:loading="!!connectLoadingMap[scope.row.id]"
@click.stop="handleToggleConnect(scope.row)"
>
<el-button link :type="isRowConnected(scope.row) ? 'warning' : 'success'"
:loading="!!connectLoadingMap[scope.row.id]" @click.stop="handleToggleConnect(scope.row)">
{{ isRowConnected(scope.row) ? '断开连接' : '连接' }}
</el-button>
<el-button
link
type="danger"
@click="handleDelete(scope.row.id)"
v-hasPermi="['iot:device:delete']"
>
<el-button link type="danger" @click="handleDelete(scope.row.id)" v-hasPermi="['iot:device:delete']">
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
@ -301,7 +244,7 @@ const handleDelete = async (ids: number | number[]) => {
}
//
await getList()
} catch {}
} catch { }
}
const handleBatchDelete = async () => {
@ -317,7 +260,7 @@ const handleCopy = async (id: number) => {
await DeviceApi.copyDevice(id)
message.success('复制成功')
await getList()
} catch {}
} catch { }
}
/** 导出按钮操作 */

@ -3,13 +3,11 @@
<!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="100px">
<el-form-item label="点位编码" prop="attributeCode">
<el-input
v-model="queryParams.attributeCode" placeholder="请输入点位编码" clearable @keyup.enter="handleQuery"
<el-input v-model="queryParams.attributeCode" placeholder="请输入点位编码" clearable @keyup.enter="handleQuery"
class="!w-240px" />
</el-form-item>
<el-form-item label="点位名称" prop="attributeName">
<el-input
v-model="queryParams.attributeName" placeholder="请输入点位名称" clearable @keyup.enter="handleQuery"
<el-input v-model="queryParams.attributeName" placeholder="请输入点位名称" clearable @keyup.enter="handleQuery"
class="!w-240px" />
</el-form-item>
<el-form-item label="点位类型" prop="attributeType">
@ -47,8 +45,7 @@ v-model="queryParams.attributeName" placeholder="请输入点位名称" clearabl
<el-button type="primary" plain @click="openForm('create')" v-hasPermi="['iot:device-model:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button
type="success" plain @click="handleExport" :loading="exportLoading"
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['iot:device-model:export']">
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
@ -62,8 +59,7 @@ type="success" plain @click="handleExport" :loading="exportLoading"
<!-- 列表 -->
<ContentWrap>
<el-table
ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"
<el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true" row-key="id"
@selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" reserve-selection />
<!-- <el-table-column label="ID" align="center" prop="id" /> -->
@ -79,8 +75,7 @@ ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-to
<el-table-column label="创建时间" align="center" prop="createTime" :formatter="dateFormatter" width="180px" />
<el-table-column label="操作" align="center" width="150px" fixed="right">
<template #default="scope">
<el-button
link type="primary" @click="openForm('update', scope.row.id)"
<el-button link type="primary" @click="openForm('update', scope.row.id)"
v-hasPermi="['iot:device-model:update']">
编辑
</el-button>
@ -91,8 +86,7 @@ link type="primary" @click="openForm('update', scope.row.id)"
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>

@ -3,20 +3,17 @@
<!-- 搜索工作栏 -->
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="模型编码" prop="code">
<el-input
v-model="queryParams.code" placeholder="请输入模型编码" clearable @keyup.enter="handleQuery"
<el-input v-model="queryParams.code" placeholder="请输入模型编码" clearable @keyup.enter="handleQuery"
class="!w-240px" />
</el-form-item>
<el-form-item label="模型名称" prop="name">
<el-input
v-model="queryParams.name" placeholder="请输入模型名称" clearable @keyup.enter="handleQuery"
<el-input v-model="queryParams.name" placeholder="请输入模型名称" clearable @keyup.enter="handleQuery"
class="!w-240px" />
</el-form-item>
<el-form-item label="通讯协议" prop="protocol">
<el-select v-model="queryParams.protocol" placeholder="请选择通讯协议" clearable class="!w-240px">
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)" :key="dict.value" :label="dict.label"
<el-option v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)" :key="dict.value" :label="dict.label"
:value="dict.value" />
</el-select>
</el-form-item>
@ -54,8 +51,7 @@ v-for="dict in getStrDictOptions(DICT_TYPE.IOT_PROTOCOL)" :key="dict.value" :lab
<el-button type="danger" plain @click="handleBatchDelete" v-hasPermi="['iot:device-model:delete']">
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
<el-button
type="success" plain @click="handleExport" :loading="exportLoading"
<el-button type="success" plain @click="handleExport" :loading="exportLoading"
v-hasPermi="['iot:device-model:export']">
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
@ -65,8 +61,7 @@ type="success" plain @click="handleExport" :loading="exportLoading"
<!-- 列表 -->
<ContentWrap>
<el-table
ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
<el-table ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true"
highlight-current-row row-key="id" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55" reserve-selection />
<el-table-column label="模型编码" align="center" prop="code" />
@ -84,8 +79,7 @@ ref="tableRef" v-loading="loading" :data="list" :stripe="true" :show-overflow-to
<el-button link type="primary" @click="handleCopy(scope.row.id)" v-hasPermi="['iot:device-model:create']">
复制
</el-button>
<el-button
link type="primary" @click="openForm('update', scope.row.id)"
<el-button link type="primary" @click="openForm('update', scope.row.id)"
v-hasPermi="['iot:device-model:update']">
编辑
</el-button>
@ -96,8 +90,7 @@ link type="primary" @click="openForm('update', scope.row.id)"
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
<Pagination :total="total" v-model:page="queryParams.pageNo" v-model:limit="queryParams.pageSize"
@pagination="getList" />
</ContentWrap>
@ -208,12 +201,16 @@ const handleBatchDelete = async () => {
/** 导出按钮操作 */
const handleExport = async () => {
if (!selectedIds.value.length) {
message.error('请选择需要导出的数据')
return
}
try {
//
await message.exportConfirm()
//
exportLoading.value = true
const data = await DeviceModelApi.exportDeviceModel(queryParams)
const data = await DeviceModelApi.exportDeviceModel({ ids: selectedIds.value.join(',') })
download.excel(data, '采集设备模型.xls')
} catch {
} finally {

@ -0,0 +1,290 @@
<template>
<ContentWrap>
<el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true" label-width="68px">
<el-form-item label="产线编码" prop="lineNode">
<el-input
v-model="queryParams.lineNode"
placeholder="请输入产线编码"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="产线名称" prop="lineName">
<el-input
v-model="queryParams.lineName"
placeholder="请输入产线名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="设备编码" prop="deviceCode">
<el-input
v-model="queryParams.deviceCode"
placeholder="请输入设备编码"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="设备名称" prop="deviceName">
<el-input
v-model="queryParams.deviceName"
placeholder="请输入设备名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-table
v-loading="loading"
:data="list"
:stripe="true"
:show-overflow-tooltip="true"
row-key="id"
>
<el-table-column label="产线编码" align="left" prop="lineNode" min-width="140px" />
<el-table-column label="产线名称" align="left" prop="lineName" min-width="160px" />
<el-table-column label="设备编码" align="left" prop="deviceCode" min-width="140px" />
<el-table-column label="设备名称" align="left" prop="deviceName" min-width="160px" />
<el-table-column label="连接状态" align="center" prop="status" width="120px">
<template #default="scope">
<dict-tag :type="DICT_TYPE.IOT_GATEWAY_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column
label="最新采集时间"
align="center"
prop="collectionTime"
:formatter="dateFormatter"
width="180px"
/>
<el-table-column label="操作" align="center" fixed="right" width="150px">
<template #default="scope">
<el-button link type="primary" @click="handleSingleMonitor(scope.row)"></el-button>
</template>
</el-table-column>
</el-table>
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<Dialog v-model="monitorDialogVisible" :title="monitorDialogTitle" width="900">
<ContentWrap>
<el-table
v-loading="monitorLoading"
:data="monitorList"
:stripe="true"
:show-overflow-tooltip="true"
row-key="pointCode"
>
<el-table-column label="点位编码" align="left" prop="pointCode" min-width="140px" />
<el-table-column label="点位名称" align="left" prop="pointName" min-width="160px" />
<el-table-column label="数据类型" align="center" prop="dataType" width="120px" />
<el-table-column label="最新值" align="center" prop="latestValue" width="120px">
<template #default="scope">
{{ scope.row.latestValue === null ? 'null' : scope.row.latestValue }}
</template>
</el-table-column>
<el-table-column label="单位" align="center" prop="unit" width="100px" />
<el-table-column
label="最新采集时间"
align="center"
prop="latestCollectTime"
:formatter="dateFormatter"
width="180px"
/>
</el-table>
</ContentWrap>
</Dialog>
</template>
<script setup lang="ts">
import { DICT_TYPE } from '@/utils/dict'
import { dateFormatter } from '@/utils/formatTime'
import { DeviceApi, LineDeviceVO } from '@/api/iot/device'
defineOptions({ name: 'RealTimeMonitoring' })
const loading = ref(true)
const list = ref<LineDeviceVO[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
id: undefined,
lineNode: undefined,
lineName: undefined,
deviceCode: undefined,
deviceName: undefined,
status: undefined,
collectionTime: undefined
})
const queryFormRef = ref() //
type MonitorRow = {
pointCode: string
pointName: string
dataType: string
latestValue: any
unit: string
latestCollectTime: string
}
const monitorDialogVisible = ref(false)
const monitorDialogTitle = ref('单设备监控')
const monitorLoading = ref(false)
const monitorList = ref<MonitorRow[]>([])
const buildMockCollectTime = (hour: number, minute: number, second: number) => {
const hh = String(hour).padStart(2, '0')
const mm = String(minute).padStart(2, '0')
const ss = String(second).padStart(2, '0')
return `2025-11-25 ${hh}:${mm}:${ss}`
}
const buildMockMonitorList = () => {
return [
{
pointCode: 'state',
pointName: '运行状态',
dataType: 'bool',
latestValue: 100,
unit: '',
latestCollectTime: buildMockCollectTime(9, 16, 12)
},
{
pointCode: 'faultCode',
pointName: '故障代码',
dataType: 'int',
latestValue: null,
unit: '',
latestCollectTime: buildMockCollectTime(9, 16, 18)
},
{
pointCode: 'machineOutp',
pointName: '产量',
dataType: 'long',
latestValue: 100,
unit: '',
latestCollectTime: buildMockCollectTime(9, 16, 25)
},
{
pointCode: 'cycleTime',
pointName: '节拍(秒)',
dataType: 'int',
latestValue: 45,
unit: '',
latestCollectTime: buildMockCollectTime(9, 16, 31)
},
{
pointCode: 'goodCount',
pointName: '良品数',
dataType: 'long',
latestValue: 980,
unit: '',
latestCollectTime: buildMockCollectTime(9, 16, 36)
},
{
pointCode: 'rejectCount',
pointName: '不良数',
dataType: 'long',
latestValue: 12,
unit: '',
latestCollectTime: buildMockCollectTime(9, 16, 42)
},
{
pointCode: 'alarmFlag',
pointName: '报警标识',
dataType: 'bool',
latestValue: 0,
unit: '',
latestCollectTime: buildMockCollectTime(9, 16, 49)
}
] as MonitorRow[]
}
const buildQueryParams = () => {
const params: Record<string, any> = {
pageNo: queryParams.pageNo,
pageSize: queryParams.pageSize
}
const keys = ['id', 'lineNode', 'lineName', 'deviceCode', 'deviceName', 'status', 'collectionTime']
for (const key of keys) {
const value = (queryParams as any)[key]
if (value === undefined || value === null || value === '') {
continue
}
params[key] = value
}
return params
}
const getList = async () => {
loading.value = true
try {
const data = await DeviceApi.getLineDevicePage(buildQueryParams())
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value.resetFields()
handleQuery()
}
const handleSingleMonitor = (row: LineDeviceVO) => {
monitorDialogTitle.value = row?.deviceName ? `单设备监控:${row.deviceName}` : '单设备监控'
monitorLoading.value = true
monitorDialogVisible.value = true
try {
monitorList.value = buildMockMonitorList()
} finally {
monitorLoading.value = false
}
}
let timer: any = null
onMounted(() => {
getList()
timer = setInterval(async () => {
try {
const data = await DeviceApi.getLineDevicePage(buildQueryParams())
list.value = data.list
total.value = data.total
} catch {}
}, 5000)
})
onUnmounted(() => {
if (timer) {
clearInterval(timer)
}
})
</script>
Loading…
Cancel
Save