gaoguangchao
4 months ago
14 changed files with 2514 additions and 12 deletions
@ -0,0 +1,240 @@ |
|||
package com.ruoyi.common.utils.poi; |
|||
|
|||
import cn.hutool.core.collection.CollUtil; |
|||
import cn.hutool.core.convert.Convert; |
|||
import cn.hutool.core.date.DateUtil; |
|||
import cn.hutool.core.util.ObjectUtil; |
|||
import cn.hutool.core.util.StrUtil; |
|||
import com.ruoyi.common.annotation.Excel; |
|||
import com.ruoyi.common.utils.DictUtils; |
|||
import org.apache.poi.ss.usermodel.*; |
|||
import org.apache.poi.xssf.usermodel.XSSFWorkbook; |
|||
import javax.servlet.http.HttpServletResponse; |
|||
import java.io.IOException; |
|||
import java.io.UnsupportedEncodingException; |
|||
import java.lang.reflect.Field; |
|||
import java.net.URLEncoder; |
|||
import java.util.Date; |
|||
import java.util.List; |
|||
import java.util.Map; |
|||
|
|||
public class ExcelMultipleSheetsUtil { |
|||
|
|||
/** |
|||
* 导出excel:可多个sheet页 |
|||
* |
|||
* @param data 数据:Map 集合【key == 每一个sheet页的名称,value == sheet页数据】 |
|||
* @param excelFileName excel文件名 |
|||
* @param suffixName 后缀名 |
|||
* @param response 响应 |
|||
* @throws IOException 异常 |
|||
*/ |
|||
public static void excelMultipleSheets(Map<String, Object> data, String excelFileName, String suffixName, HttpServletResponse response) throws IOException { |
|||
// 创建工作簿
|
|||
try (Workbook workbook = new XSSFWorkbook()) { |
|||
for (Map.Entry<String, Object> entry : data.entrySet()) { |
|||
String sheetName = entry.getKey(); |
|||
Object sheetData = entry.getValue(); |
|||
Sheet sheet = workbook.createSheet(sheetName); |
|||
if (ObjectUtil.isNotEmpty(sheetData)) { |
|||
createSheetWithData(sheet, sheetData); |
|||
} |
|||
} |
|||
|
|||
setResponseHeader(response, excelFileName, suffixName); |
|||
// 写出文件
|
|||
workbook.write(response.getOutputStream()); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建表单并填充数据 |
|||
* |
|||
* @param sheet 表单 |
|||
* @param data 数据 |
|||
*/ |
|||
private static void createSheetWithData(Sheet sheet, Object data) { |
|||
if (data instanceof List) { |
|||
createSheetWithListData(sheet, (List<?>) data); |
|||
} else { |
|||
createSheetWithObjectData(sheet, data); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建列表类型数据对应的Excel表单 |
|||
* |
|||
* @param sheet 表单 |
|||
* @param dataList 数据列表 |
|||
*/ |
|||
private static void createSheetWithListData(Sheet sheet, List<?> dataList) { |
|||
if (CollUtil.isNotEmpty(dataList)) { |
|||
Object firstItem = dataList.get(0); |
|||
createHeaderRow(sheet, firstItem.getClass()); |
|||
int rowIndex = 1; |
|||
for (Object item : dataList) { |
|||
createDataRow(sheet, item, rowIndex++); |
|||
} |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建对象类型数据对应的Excel表单 |
|||
* |
|||
* @param sheet 表单 |
|||
* @param data 数据 |
|||
*/ |
|||
private static void createSheetWithObjectData(Sheet sheet, Object data) { |
|||
createHeaderRow(sheet, data.getClass()); |
|||
createDataRow(sheet, data, 1); |
|||
} |
|||
|
|||
/** |
|||
* 创建表头行 |
|||
* |
|||
* @param sheet 表单 |
|||
* @param clazz 数据类 |
|||
*/ |
|||
private static void createHeaderRow(Sheet sheet, Class<?> clazz) { |
|||
// 创建单元格样式
|
|||
CellStyle headerCellStyle = createCellStyle(sheet.getWorkbook()); |
|||
|
|||
// 创建标题行
|
|||
Row headerRow = sheet.createRow(0); |
|||
Field[] fields = clazz.getDeclaredFields(); |
|||
for (int i = 0; i < fields.length; i++) { |
|||
createHeaderCell(sheet, headerCellStyle, fields, headerRow, i); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建数据行 |
|||
* |
|||
* @param sheet 表单 |
|||
* @param data 数据 |
|||
* @param rowIndex 行号 |
|||
*/ |
|||
private static void createDataRow(Sheet sheet, Object data, int rowIndex) { |
|||
// 创建单元格样式
|
|||
CellStyle dataCellStyle = createCellStyle(sheet.getWorkbook()); |
|||
|
|||
// 创建数据行
|
|||
Row dataRow = sheet.createRow(rowIndex); |
|||
Field[] fields = data.getClass().getDeclaredFields(); |
|||
for (int i = 0; i < fields.length; i++) { |
|||
createDataCell(dataCellStyle, fields, dataRow, i, data); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建单元格样式 |
|||
* |
|||
* @param workbook 工作簿 |
|||
* @return 单元格样式 |
|||
*/ |
|||
private static CellStyle createCellStyle(Workbook workbook) { |
|||
CellStyle cellStyle = workbook.createCellStyle(); |
|||
|
|||
// 设置 水平和垂直 居中对齐
|
|||
cellStyle.setAlignment(HorizontalAlignment.CENTER); |
|||
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER); |
|||
|
|||
// 设置 上 下 左 右 边框及颜色
|
|||
cellStyle.setBorderTop(BorderStyle.THIN); |
|||
cellStyle.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
|||
cellStyle.setBorderBottom(BorderStyle.THIN); |
|||
cellStyle.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
|||
cellStyle.setBorderLeft(BorderStyle.THIN); |
|||
cellStyle.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
|||
cellStyle.setBorderRight(BorderStyle.THIN); |
|||
cellStyle.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex()); |
|||
|
|||
// 设置字体
|
|||
Font dataFont = workbook.createFont(); |
|||
dataFont.setFontName("Arial"); |
|||
dataFont.setFontHeightInPoints((short) 10); |
|||
cellStyle.setFont(dataFont); |
|||
|
|||
return cellStyle; |
|||
} |
|||
|
|||
/** |
|||
* 创建Excel表头单元格 |
|||
* |
|||
* @param sheet 表单 |
|||
* @param headerCellStyle 单元格样式 |
|||
* @param fields 字段 |
|||
* @param headerRow 标题行 |
|||
* @param i 序号 |
|||
*/ |
|||
private static void createHeaderCell(Sheet sheet, CellStyle headerCellStyle, Field[] fields, Row headerRow, int i) { |
|||
// 默认宽度
|
|||
double width = 16; |
|||
Excel excelAnnotation = fields[i].getAnnotation(Excel.class); |
|||
if (excelAnnotation != null && !ObjectUtil.isEmpty(excelAnnotation.width())) { |
|||
width = excelAnnotation.width(); |
|||
} |
|||
|
|||
// 设置宽度
|
|||
sheet.setColumnWidth(i, (int) ((width + 0.72) * 256)); |
|||
|
|||
if (excelAnnotation != null) { |
|||
Cell cell = headerRow.createCell(i); |
|||
cell.setCellValue(excelAnnotation.name()); |
|||
cell.setCellStyle(headerCellStyle); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 创建Excel数据单元格 |
|||
* |
|||
* @param dataCellStyle 单元格样式 |
|||
* @param fields 字段 |
|||
* @param dataRow 数据行 |
|||
* @param i 序号 |
|||
* @param data 数据 |
|||
*/ |
|||
private static void createDataCell(CellStyle dataCellStyle, Field[] fields, Row dataRow, int i, Object data) { |
|||
Cell cell = dataRow.createCell(i); |
|||
cell.setCellStyle(dataCellStyle); |
|||
|
|||
try { |
|||
fields[i].setAccessible(true); |
|||
Object value = fields[i].get(data); |
|||
handleAnnotationAndSetValue(cell, fields[i], value); |
|||
} catch (IllegalAccessException e) { |
|||
e.printStackTrace(); |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 处理注解并设置单元格值 |
|||
* |
|||
* @param cell 单元格 |
|||
* @param field 字段 |
|||
* @param value 值 |
|||
*/ |
|||
private static void handleAnnotationAndSetValue(Cell cell, Field field, Object value) { |
|||
if (field.isAnnotationPresent(Excel.class) && field.getAnnotation(Excel.class).dictType().length() > 0) { |
|||
value = DictUtils.getDictLabel(field.getAnnotation(Excel.class).dictType(), String.valueOf(value)); |
|||
} |
|||
if (field.isAnnotationPresent(Excel.class) && StrUtil.isNotEmpty(field.getAnnotation(Excel.class).dateFormat())) { |
|||
value = DateUtil.format(Convert.convert(Date.class, value), field.getAnnotation(Excel.class).dateFormat()); |
|||
} |
|||
cell.setCellValue(ObjectUtil.isEmpty(value) ? null : value.toString()); |
|||
} |
|||
|
|||
/** |
|||
* 设置响应头 |
|||
* |
|||
* @param response 响应 |
|||
* @param excelFileName 文件名 |
|||
* @param suffixName 后缀名 |
|||
* @throws UnsupportedEncodingException 编码异常 |
|||
*/ |
|||
private static void setResponseHeader(HttpServletResponse response, String excelFileName, String suffixName) throws UnsupportedEncodingException { |
|||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); |
|||
response.setHeader("Content-Disposition", "attachment; filename=" + URLEncoder.encode(excelFileName + suffixName, "utf-8")); |
|||
} |
|||
|
|||
} |
@ -0,0 +1,53 @@ |
|||
import request from '@/utils/request' |
|||
|
|||
// 查询【请填写功能名称】列表
|
|||
export function listDevice(query) { |
|||
return request({ |
|||
url: '/business/device/list', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
|||
|
|||
// 查询【请填写功能名称】详细
|
|||
export function getDevice(id) { |
|||
return request({ |
|||
url: '/business/device/' + id, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// 新增【请填写功能名称】
|
|||
export function addDevice(data) { |
|||
return request({ |
|||
url: '/business/device', |
|||
method: 'post', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 修改【请填写功能名称】
|
|||
export function updateDevice(data) { |
|||
return request({ |
|||
url: '/business/device', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 删除【请填写功能名称】
|
|||
export function delDevice(id) { |
|||
return request({ |
|||
url: '/business/device/' + id, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
|
|||
// 导出【请填写功能名称】
|
|||
export function exportDevice(query) { |
|||
return request({ |
|||
url: '/business/device/export', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
@ -0,0 +1,35 @@ |
|||
import request from '@/utils/request' |
|||
|
|||
export function deviceOnlineChart(query) { |
|||
return request({ |
|||
url: '/system/status/list', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
|||
// 查询产品列表
|
|||
export function deviceOnlineTable(query) { |
|||
return request({ |
|||
url: '/system/status/tablist', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
|||
|
|||
export function networkLogTable(query) { |
|||
return request({ |
|||
url: '/system/status//networkLogTable', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
|||
|
|||
export function networkLogEcharts(query){ |
|||
return request({ |
|||
url: '/system/status/networkLogEcharts', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
|||
|
|||
|
@ -0,0 +1,53 @@ |
|||
import request from '@/utils/request' |
|||
|
|||
// 查询产品列表
|
|||
export function listProduct(query) { |
|||
return request({ |
|||
url: '/business/product/list', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
|||
|
|||
// 查询产品详细
|
|||
export function getProduct(id) { |
|||
return request({ |
|||
url: '/business/product/' + id, |
|||
method: 'get' |
|||
}) |
|||
} |
|||
|
|||
// 新增产品
|
|||
export function addProduct(data) { |
|||
return request({ |
|||
url: '/business/product', |
|||
method: 'post', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 修改产品
|
|||
export function updateProduct(data) { |
|||
return request({ |
|||
url: '/business/product', |
|||
method: 'put', |
|||
data: data |
|||
}) |
|||
} |
|||
|
|||
// 删除产品
|
|||
export function delProduct(id) { |
|||
return request({ |
|||
url: '/business/product/' + id, |
|||
method: 'delete' |
|||
}) |
|||
} |
|||
|
|||
// 导出产品
|
|||
export function exportProduct(query) { |
|||
return request({ |
|||
url: '/business/product/export', |
|||
method: 'get', |
|||
params: query |
|||
}) |
|||
} |
@ -0,0 +1,185 @@ |
|||
let type =[ |
|||
{ |
|||
name: '摄像机', |
|||
value: '1' |
|||
}, |
|||
{ |
|||
name: '可变信息标志', |
|||
value: '2' |
|||
}, |
|||
{ |
|||
name: '气象监测器', |
|||
value: '3' |
|||
}, |
|||
{ |
|||
name: '出口诱导灯', |
|||
value: '4' |
|||
}, |
|||
{ |
|||
name: '路段语音广播', |
|||
value: '5' |
|||
}, |
|||
{ |
|||
name: '护栏碰撞', |
|||
value: '6' |
|||
}, |
|||
{ |
|||
name: '毫米波雷达', |
|||
value: '7' |
|||
}, |
|||
{ |
|||
name: '合流区预警', |
|||
value: '8' |
|||
}, |
|||
{ |
|||
name: '智慧锥桶', |
|||
value: '9' |
|||
}, |
|||
{ |
|||
name: '激光疲劳唤醒', |
|||
value: '10' |
|||
}, |
|||
{ |
|||
name: '一类交通量调查站', |
|||
value: '11' |
|||
}, |
|||
{ |
|||
name: '行车诱导', |
|||
value: '12' |
|||
}, |
|||
{ |
|||
name: '智能设备箱', |
|||
value: '13' |
|||
}, |
|||
{ |
|||
name: '光线在线监测', |
|||
value: '14' |
|||
} |
|||
] |
|||
let direction =[ |
|||
{ |
|||
name: '菏泽方向', |
|||
value: '1' |
|||
}, |
|||
{ |
|||
name: '双向', |
|||
value: '2' |
|||
}, |
|||
{ |
|||
name: '济南方向', |
|||
value: '3' |
|||
} |
|||
] |
|||
let useState = [ |
|||
{ |
|||
name: '停用', |
|||
value: 0 |
|||
}, |
|||
{ |
|||
name: '在用', |
|||
value: 1 |
|||
} |
|||
] |
|||
let deviceState = [ |
|||
{ |
|||
name: '异常', |
|||
value: '0' |
|||
}, |
|||
{ |
|||
name: '正常', |
|||
value: '1' |
|||
} |
|||
] |
|||
let facilitiesTypeList = [ |
|||
{ |
|||
label: '主干道', |
|||
value: '0' |
|||
}, |
|||
{ |
|||
label: '服务区', |
|||
value: '1' |
|||
}, |
|||
{ |
|||
label: '收费站', |
|||
value: '3' |
|||
} |
|||
] |
|||
let typeTree = [ |
|||
{ |
|||
label: '高清网络枪型固定摄像机', |
|||
value: '1-1' |
|||
},{ |
|||
label: '高清网络球形摄像机', |
|||
value: '1-2' |
|||
}, { |
|||
label: '桥下高清网络球形摄像机', |
|||
value: '1-3' |
|||
}, { |
|||
label: '360°全景摄像机', |
|||
value: '1-4' |
|||
},{ |
|||
label: '180°全景摄像机', |
|||
value: '1-5' |
|||
},{ |
|||
label: '门架式可变信息标志', |
|||
value: '2-1' |
|||
}, { |
|||
label: '站前可变信息标志', |
|||
value: '2-2' |
|||
},{ |
|||
label: '雨棚可变信息标志', |
|||
value: '2-3' |
|||
}, { |
|||
label: '站前悬臂式可变信息标志', |
|||
value: '2-4' |
|||
}, { |
|||
label: '气象监测器', |
|||
value: '3' |
|||
},{ |
|||
label: '出口诱导灯', |
|||
value: '4' |
|||
}, { |
|||
label: '路段语音广播', |
|||
value: '5' |
|||
},{ |
|||
label: '护栏碰撞', |
|||
value: '6' |
|||
}, { |
|||
label: '毫米波雷达', |
|||
value: '7' |
|||
}, { |
|||
label: '合流区预警', |
|||
value: '8' |
|||
}, { |
|||
label: '智慧锥桶', |
|||
value: '9' |
|||
}, { |
|||
label: '激光疲劳唤醒', |
|||
value: '10' |
|||
},{ |
|||
label: '一类交通量调查站', |
|||
value: '11' |
|||
}, { |
|||
label: '行车诱导', |
|||
value: '12' |
|||
},{ |
|||
label: '智能设备箱', |
|||
value: '13' |
|||
}, { |
|||
label: '光线在线监测', |
|||
value: '14' |
|||
},{ |
|||
label: '太阳能板', |
|||
value: '15' |
|||
}, { |
|||
label: '远端机', |
|||
value: '16' |
|||
}] |
|||
export { |
|||
type, |
|||
direction, |
|||
useState, |
|||
deviceState, |
|||
facilitiesTypeList, |
|||
typeTree |
|||
} |
@ -0,0 +1,619 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px"> |
|||
<el-form-item label="设备桩号" prop="stakeMark"> |
|||
<el-input |
|||
size="mini" |
|||
v-model="queryParams.stakeMark" |
|||
placeholder="请输入设备桩号" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="使用状态" prop="useState"> |
|||
<el-select size="mini" v-model="queryParams.useState" placeholder="请选择使用状态" clearable> |
|||
<el-option |
|||
v-for="item in useStateOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="设备状态" prop="deviceState"> |
|||
<el-select size="mini" v-model="queryParams.deviceState" placeholder="请选择设备状态" clearable> |
|||
<el-option |
|||
v-for="item in deviceStateOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<span v-if="showSearch"> |
|||
<el-form-item label="设备方向" prop="direction"> |
|||
<el-select size="mini" v-model="queryParams.direction" placeholder="请选择设备方向" clearable> |
|||
<el-option |
|||
v-for="item in directionOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="IP地址" prop="deviceIp"> |
|||
<el-input |
|||
size="mini" |
|||
v-model="queryParams.deviceIp" |
|||
placeholder="请输入IP地址" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="设备类型" prop="deviceType"> |
|||
<el-select size="mini" v-model="queryParams.deviceType" placeholder="请选择设备类型" clearable> |
|||
<el-option |
|||
v-for="type in deviceTypeOptions" |
|||
:key="type.value" |
|||
:label="type.name" |
|||
:value="type.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
</span> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="primary" |
|||
plain |
|||
icon="el-icon-plus" |
|||
size="mini" |
|||
@click="handleAdd" |
|||
v-hasPermi="['deviceManage:device:add']" |
|||
>新增 |
|||
</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="success" |
|||
plain |
|||
icon="el-icon-edit" |
|||
size="mini" |
|||
:disabled="single" |
|||
@click="handleUpdate" |
|||
v-hasPermi="['deviceManage:device:edit']" |
|||
>修改 |
|||
</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="danger" |
|||
plain |
|||
icon="el-icon-delete" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleDelete" |
|||
v-hasPermi="['deviceManage:device:remove']" |
|||
>删除 |
|||
</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="warning" |
|||
plain |
|||
icon="el-icon-download" |
|||
size="mini" |
|||
@click="handleExport" |
|||
v-hasPermi="['deviceManage:device:export']" |
|||
>导出 |
|||
</el-button> |
|||
</el-col> |
|||
|
|||
</el-row> |
|||
|
|||
<el-table v-loading="loading" :data="deviceList" @selection-change="handleSelectionChange"> |
|||
<el-table-column type="selection" width="55" align="center"/> |
|||
<el-table-column label="设备名称" align="center" prop="deviceName" width="250"/> |
|||
<el-table-column label="物联编号" align="center" prop="iotDeviceId"/> |
|||
<el-table-column label="设备桩号" align="center" prop="stakeMark"/> |
|||
<el-table-column label="设备方向" align="center" prop="direction"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.direction === '1'">菏泽方向</span> |
|||
<span v-if="scope.row.direction === '2'">双向</span> |
|||
<span v-if="scope.row.direction === '3'">济南方向</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="IP地址" align="center" prop="deviceIp"/> |
|||
<el-table-column label="安装位置" align="center" prop="installationSite"/> |
|||
<el-table-column label="使用状态" align="center" prop="useState"> |
|||
<template slot-scope="scope"> |
|||
<el-switch |
|||
size="mini" |
|||
@change="handleUseState(scope.row)" |
|||
v-model="scope.row.useState" |
|||
active-color="#13ce66" |
|||
inactive-color="#ff4949" |
|||
:active-value="1" |
|||
:inactive-value="0"> |
|||
</el-switch> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="设备状态" align="center" prop="deviceState"> |
|||
<template slot-scope="scope"> |
|||
<el-tag size="mini" v-if="scope.row.deviceState === '1'" type="success" effect="dark">在线</el-tag> |
|||
<el-tag size="mini" type="danger" effect="dark" v-else>异常</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="设备图片" align="center" prop="deviceImg"> |
|||
<template slot-scope="scope"> |
|||
<el-image |
|||
v-if="scope.row.deviceImg" |
|||
style="width: 30px; height: 30px" |
|||
:src="baseUrl+scope.row.deviceImg" |
|||
:preview-src-list="[baseUrl+scope.row.deviceImg]" |
|||
> |
|||
</el-image> |
|||
<p v-else>暂无</p> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template slot-scope="scope"> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-edit" |
|||
@click="handleUpdate(scope.row)" |
|||
v-hasPermi="['deviceManage:device:edit']" |
|||
>修改 |
|||
</el-button> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-delete" |
|||
@click="handleDelete(scope.row)" |
|||
v-hasPermi="['deviceManage:device:remove']" |
|||
>删除 |
|||
</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="total>0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
|
|||
<!-- 添加或修改设备对话框 --> |
|||
<el-dialog :title="title" :visible.sync="open" width="1000px" append-to-body> |
|||
<el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
|||
<el-row > |
|||
<el-col :span="8"> |
|||
<el-form-item label="设备名称" prop="deviceName"> |
|||
<el-input v-model="form.deviceName" placeholder="请输入设备名称"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="物联编号" prop="iotDeviceId"> |
|||
<el-input v-model="form.iotDeviceId" placeholder="请输入物联编号"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="所属单位" prop="groupId"> |
|||
<el-select filterable v-model="form.groupId" clearable placeholder="请选择所属部门" class="select-width"> |
|||
<el-option |
|||
v-for="dict in groupList" |
|||
:key="dict.deptId" |
|||
:label="dict.deptName" |
|||
:value="dict.deptId" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="设备类型" prop="deviceType"> |
|||
<el-select v-model="form.deviceType" clearable placeholder="请选择设备类型" class="select-width"> |
|||
<el-option |
|||
v-for="dict in deviceTypeOptions" |
|||
:key="dict.value" |
|||
:label="dict.name" |
|||
:value="dict.value" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="产品型号" prop="productId"> |
|||
<el-select filterable v-model="form.productId" clearable placeholder="请选择产品型号" class="select-width"> |
|||
<el-option |
|||
v-for="dict in productList" |
|||
:key="dict.id" |
|||
:label="dict.productName" |
|||
:value="dict.id" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="设备桩号" prop="stakeMark"> |
|||
<el-input v-model="form.stakeMark" placeholder="请输入设备桩号"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="范围桩号" prop="stakeMarkRange"> |
|||
<el-input v-model="form.stakeMarkRange" placeholder="请输入范围桩号"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="设备方向" prop="direction"> |
|||
<el-select class="width100" v-model="form.direction" placeholder="请选择设备方向" clearable> |
|||
<el-option |
|||
v-for="item in directionOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="安装位置" prop="facilitiesType"> |
|||
<el-select v-model="form.facilitiesType" clearable placeholder="位置类型" class="select-width"> |
|||
<el-option |
|||
v-for="dict in facilitiesTypeOption" |
|||
:key="dict.value" |
|||
:label="dict.label" |
|||
:value="dict.value" |
|||
></el-option> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="位置描述" prop="installationSite"> |
|||
<el-input v-model="form.installationSite" placeholder="请输入安装位置"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="安装日期" prop="installationDate"> |
|||
<el-date-picker clearable class="width100" |
|||
v-model="form.installationDate" |
|||
type="date" |
|||
value-format="yyyy-MM-dd" |
|||
placeholder="请选择安装日期"> |
|||
</el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="生产日期" prop="productionDate"> |
|||
<el-date-picker clearable class="width100" |
|||
v-model="form.productionDate" |
|||
type="date" |
|||
value-format="yyyy-MM-dd" |
|||
placeholder="请选择生产日期"> |
|||
</el-date-picker> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="使用年限" prop="durableYears"> |
|||
<el-input v-model="form.durableYears" placeholder="请输入使用年限"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="使用状态" prop="useState"> |
|||
<el-select class="width100" v-model="form.useState" placeholder="请选择使用状态" clearable> |
|||
<el-option |
|||
v-for="item in useStateOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="设备状态" prop="deviceState"> |
|||
<el-select class="width100" v-model="form.deviceState" placeholder="请选择设备状态" clearable> |
|||
<el-option |
|||
v-for="item in deviceStateOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="8"> |
|||
<el-form-item label="IP地址" prop="deviceIp"> |
|||
<el-input v-model="form.deviceIp" placeholder="请输入IP地址"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="其他配置" prop="otherConfig"> |
|||
<el-input v-model="form.otherConfig" type="textarea" placeholder="请输入内容"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="12"> |
|||
<el-form-item label="备注" prop="remark"> |
|||
<el-input v-model="form.remark" type="textarea" placeholder="请输入备注"/> |
|||
</el-form-item> |
|||
</el-col> |
|||
<el-col :span="24"> |
|||
<el-form-item label="设备图片" prop="deviceImg"> |
|||
<image-upload @input="setImgUrl" :value="form.deviceImg" :limit="1"></image-upload> |
|||
</el-form-item> |
|||
</el-col> |
|||
</el-row> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="cancel">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import {listDevice, getDevice, delDevice, addDevice, updateDevice} from "@/api/system/device"; |
|||
import {type, direction, useState, deviceState, facilitiesTypeList} from "@/enum/deviceEnum"; |
|||
import imageUpload from "@/components/ImageUpload"; |
|||
import {listDept} from "@/api/system/dept"; |
|||
import {queryProduct} from "@/api/system/product"; |
|||
export default { |
|||
name: "Device", |
|||
components: {imageUpload}, |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 选中数组 |
|||
ids: [], |
|||
// 非单个禁用 |
|||
single: true, |
|||
// 非多个禁用 |
|||
multiple: true, |
|||
// 显示搜索条件 |
|||
showSearch: false, |
|||
// 总条数 |
|||
total: 0, |
|||
// 设备表格数据 |
|||
deviceList: [], |
|||
// 弹出层标题 |
|||
title: "", |
|||
// 是否显示弹出层 |
|||
open: false, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
iotDeviceId: null, |
|||
groupId: null, |
|||
productId: null, |
|||
stakeMark: null, |
|||
deviceCode: null, |
|||
direction: null, |
|||
deviceName: null, |
|||
deviceType: null, |
|||
installationDate: null, |
|||
productionDate: null, |
|||
durableYears: null, |
|||
installationSite: null, |
|||
useState: null, |
|||
otherConfig: null, |
|||
deviceState: null, |
|||
deviceImg: null, |
|||
deviceIp: null, |
|||
stakeMarkRange: null, |
|||
facilitiesType: null, |
|||
childType: null |
|||
}, |
|||
// 表单参数 |
|||
form: {}, |
|||
// 表单校验 |
|||
rules: { |
|||
stakeMark: [ |
|||
{required: true, message: "设备桩号不能为空", trigger: "blur"} |
|||
], |
|||
deviceName: [ |
|||
{required: true, message: "设备名称不能为空", trigger: "blur"} |
|||
], |
|||
deviceType: [ |
|||
{required: true, message: "设备主类不能为空", trigger: "change"} |
|||
], |
|||
}, |
|||
// 设备类型 |
|||
deviceTypeOptions: type, |
|||
// 设备方向 |
|||
directionOptions: direction, |
|||
// 使用状态 |
|||
useStateOptions: useState, |
|||
// 设备状态 |
|||
deviceStateOptions: deviceState, |
|||
// 安装位置 |
|||
facilitiesTypeOption: facilitiesTypeList, |
|||
// 环境基础地址(开发环境、生产环境) |
|||
baseUrl: process.env.VUE_APP_BASE_API, |
|||
// 产品列表 |
|||
productList:[], |
|||
// 部门列表 |
|||
groupList:[] |
|||
}; |
|||
}, |
|||
created() { |
|||
listDept().then(response => { |
|||
this.groupList = response.data |
|||
}) |
|||
queryProduct().then(response => { |
|||
this.productList = response.data |
|||
}) |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
/** 查询设备列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
listDevice(this.queryParams).then(response => { |
|||
this.deviceList = response.rows; |
|||
this.total = response.total; |
|||
this.loading = false; |
|||
}); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
handleUseState(row) { |
|||
updateDevice(row).then(response => { |
|||
this.$modal.msgSuccess("启用成功"); |
|||
this.getList(); |
|||
}); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = { |
|||
id: null, |
|||
iotDeviceId: null, |
|||
groupId: null, |
|||
productId: null, |
|||
stakeMark: null, |
|||
deviceCode: null, |
|||
direction: null, |
|||
deviceName: null, |
|||
deviceType: null, |
|||
installationDate: null, |
|||
productionDate: null, |
|||
durableYears: null, |
|||
installationSite: null, |
|||
useState: null, |
|||
otherConfig: null, |
|||
remark: null, |
|||
deviceState: null, |
|||
deviceImg: null, |
|||
createTime: null, |
|||
updateTime: null, |
|||
deviceIp: null, |
|||
stakeMarkRange: null, |
|||
facilitiesType: null, |
|||
childType: null |
|||
}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map(item => item.id) |
|||
this.single = selection.length !== 1 |
|||
this.multiple = !selection.length |
|||
}, |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
this.reset(); |
|||
this.open = true; |
|||
this.title = "添加设备"; |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleUpdate(row) { |
|||
this.reset(); |
|||
const id = row.id || this.ids |
|||
getDevice(id).then(response => { |
|||
console.log(response.data); |
|||
this.form = response.data; |
|||
console.log(this.form) |
|||
this.open = true; |
|||
this.title = "修改设备"; |
|||
}); |
|||
}, |
|||
/** 提交按钮 */ |
|||
submitForm() { |
|||
this.$refs["form"].validate(valid => { |
|||
if (valid) { |
|||
if (this.form.id != null) { |
|||
updateDevice(this.form).then(response => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} else { |
|||
addDevice(this.form).then(response => { |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const ids = row.id || this.ids; |
|||
this.$modal.confirm('是否确认删除设备编号为"' + ids + '"的数据项?').then(function () { |
|||
return delDevice(ids); |
|||
}).then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}).catch(() => { |
|||
}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download('deviceManage/device/export', { |
|||
...this.queryParams |
|||
}, `device_${new Date().getTime()}.xlsx`) |
|||
}, |
|||
// 设置图片地址 |
|||
setImgUrl(val) { |
|||
this.form.deviceImg = val |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
<style scoped lang="scss"> |
|||
$width: 150px; |
|||
$height: 150px; |
|||
::v-deep .el-upload { |
|||
border: 1px dashed #d9d9d9; |
|||
border-radius: 6px; |
|||
cursor: pointer; |
|||
position: relative; |
|||
overflow: hidden; |
|||
width: $width; |
|||
height: $height; |
|||
} |
|||
|
|||
// 鼠标移入边框颜色 |
|||
::v-deep .el-upload:hover { |
|||
border-color: #409EFF; |
|||
} |
|||
.avatar-uploader-icon { |
|||
font-size: 28px; |
|||
color: #8c939d; |
|||
width: $width; |
|||
height: $height; |
|||
line-height: $height; |
|||
text-align: center; |
|||
} |
|||
|
|||
.avatar { |
|||
width: $width; |
|||
height: $height; |
|||
display: block; |
|||
} |
|||
</style> |
@ -0,0 +1,325 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-row :gutter="10"> |
|||
<el-col :span="4"> |
|||
<type-tree @defaultCheck="defaultCheck" @nodeCheck="nodeCheck" :filter="false" :show_checkbox="true" :default_check_first="true"></type-tree> |
|||
</el-col> |
|||
<el-col :span="20"> |
|||
<el-button @click="handleExport">导出</el-button> |
|||
<line-chart height="30vh" :show-legend="true" :chart-data="chartData"></line-chart> |
|||
<el-table :data="tableData" height="45vh" style="width: 100%" @sort-change="handleSortChange"> |
|||
<el-table-column prop="deviceName" label="设备名称" width="200"></el-table-column> |
|||
<el-table-column prop="stakeMark" label="桩号"></el-table-column> |
|||
<el-table-column prop="direction" label="方向"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.direction=='1'">菏泽方向</span> |
|||
<span v-else-if="scope.row.direction=='3'">济南方向</span> |
|||
<span v-else>双向</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="deviceIp" label="IP"></el-table-column> |
|||
<el-table-column prop="deviceStatus" label="状态"> |
|||
<template slot-scope="scope"> |
|||
<el-tag type="success" size="small" v-if="scope.row.deviceStatus=='1'">在线</el-tag> |
|||
<el-tag type="danger" size="small" v-else>离线</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="lossRate" label="丢包率"> |
|||
<template slot-scope="scope"> |
|||
<span v-if="scope.row.lossRate > 0" style="color: #ff4949">{{scope.row.lossRate}}</span> |
|||
<span v-else>{{scope.row.lossRate}}</span> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="rttAvg" label="平均时延"></el-table-column> |
|||
<el-table-column prop="networkQuality" label="网络质量"> |
|||
<template slot-scope="scope"> |
|||
<el-tag type="success" size="small" v-if="scope.row.networkQuality=='优'">优</el-tag> |
|||
<el-tag type="primary" size="small" v-if="scope.row.networkQuality=='良'">良</el-tag> |
|||
<el-tag type="warning" size="small" v-if="scope.row.networkQuality=='一般'">一般</el-tag> |
|||
<el-tag type="danger" size="small" v-if="scope.row.networkQuality=='差'">差</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<!-- <el-table-column prop="statisticalDate" label="日期"></el-table-column>--> |
|||
<el-table-column prop="onlineRate" label="在线率" sortable="custom"> |
|||
<template slot-scope="scope"> |
|||
<el-progress :percentage="scope.row.onlineRate"></el-progress> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column align="center"> |
|||
<template slot="header" slot-scope="scope"> |
|||
<el-popover placement="bottom" width="300" trigger="click"> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" label-width="68px"> |
|||
<el-form-item label="设备桩号" prop="stakeMark"> |
|||
<el-input size="mini" v-model="queryParams.stakeMark" placeholder="请输入设备桩号" clearable @keyup.enter.native="deviceOnlineTable"/> |
|||
</el-form-item> |
|||
<el-form-item label="使用状态" prop="useState"> |
|||
<el-select size="mini" v-model="queryParams.useState" placeholder="请选择使用状态" clearable> |
|||
<el-option |
|||
v-for="item in useStateOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="设备状态" prop="deviceState"> |
|||
<el-select size="mini" v-model="queryParams.deviceState" placeholder="请选择设备状态" clearable> |
|||
<el-option |
|||
v-for="item in deviceStateOptions" |
|||
:key="item.value" |
|||
:label="item.name" |
|||
:value="item.value" |
|||
/> |
|||
</el-select> |
|||
</el-form-item> |
|||
<el-form-item label="IP地址" prop="deviceIp"> |
|||
<el-input |
|||
size="mini" |
|||
v-model="queryParams.deviceIp" |
|||
placeholder="请输入IP地址" |
|||
clearable |
|||
@keyup.enter.native="deviceOnlineTable" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" size="mini" @click="deviceOnlineTable">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
<el-button slot="reference" size="mini" icon="el-icon-search">搜索</el-button> |
|||
</el-popover> |
|||
</template> |
|||
<template slot-scope="scope" > |
|||
<el-button @click="seeLog(scope.row.deviceId)" type="text" size="mini" icon="el-icon-view">网络日志</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
<pagination |
|||
v-show="total>0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="deviceOnlineTable" |
|||
/> |
|||
</el-col> |
|||
</el-row> |
|||
<el-dialog title="设备网络日志" width="80vw" :visible.sync="logVisible"> |
|||
<el-row style="position: absolute;top: 45px;right: 40px"> |
|||
<el-button size="mini" @click="upDay">上一日</el-button> |
|||
<el-button style="margin-right: 10px" size="mini" @click="downDay">下一日</el-button> |
|||
<el-date-picker |
|||
style="width: 130px" |
|||
v-model="logQueryParams.queryDate" |
|||
type="date" |
|||
size="mini" |
|||
value-format="yyyy-MM-dd" |
|||
placeholder="选择日期" |
|||
@change="logQuery" |
|||
> |
|||
</el-date-picker> |
|||
</el-row> |
|||
<el-row style="width: 100%"> |
|||
<indexLine height="25vh" :chartData="logChartData"></indexLine> |
|||
</el-row> |
|||
<el-row> |
|||
<el-table :data="logTableData" height="50vh" style="width: 100%" @sort-change="handleSortChange"> |
|||
<el-table-column prop="deviceName" label="设备名称" width="300"></el-table-column> |
|||
<el-table-column prop="sendCount" label="发送"></el-table-column> |
|||
<el-table-column prop="receiveCount" label="返回"></el-table-column> |
|||
<el-table-column prop="lossCount" label="丢失"></el-table-column> |
|||
<el-table-column prop="deviceStatus" label="状态"> |
|||
<template slot-scope="scope"> |
|||
<el-tag type="success" size="small" v-if="scope.row.deviceStatus=='1'">可达</el-tag> |
|||
<el-tag type="danger" size="small" v-else>不可达</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column prop="lossRate" label="丢包率"></el-table-column> |
|||
<el-table-column prop="rttAvg" label="平均时延"></el-table-column> |
|||
<el-table-column prop="networkQuality" label="网络质量"> |
|||
<template slot-scope="scope"> |
|||
<el-tag type="success" size="small" v-if="scope.row.networkQuality=='优'">优</el-tag> |
|||
<el-tag type="primary" size="small" v-if="scope.row.networkQuality=='良'">良</el-tag> |
|||
<el-tag type="warning" size="small" v-if="scope.row.networkQuality=='一般'">一般</el-tag> |
|||
<el-tag type="danger" size="small" v-if="scope.row.networkQuality=='差'">差</el-tag> |
|||
</template> |
|||
</el-table-column> |
|||
<el-table-column width="200" prop="monitorTime" label="检测时间"></el-table-column> |
|||
|
|||
</el-table> |
|||
<!-- <pagination |
|||
v-show="logTotal>0" |
|||
:total="logTotal" |
|||
:page.sync="logQueryParams.pageNum" |
|||
:limit.sync="logQueryParams.pageSize" |
|||
@pagination="networkLogTable" |
|||
/>--> |
|||
</el-row> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import TypeTree from "@/views/deviceManage/typeTree"; |
|||
import LineChart from "@/views/deviceManage/lineChart"; |
|||
import indexLine from "@/views/deviceManage/deviceOnline/indexLine"; |
|||
import {deviceOnlineTable,deviceOnlineChart,networkLogTable,networkLogEcharts} from "@/api/deviceManage/deviceOnline"; |
|||
import {typeTree,direction, useState, deviceState} from "@/enum/deviceEnum"; |
|||
export default { |
|||
name: "index", |
|||
components: { TypeTree, LineChart, indexLine}, |
|||
data() { |
|||
return { |
|||
chartData:{ |
|||
xData:[], |
|||
yData:[] |
|||
}, |
|||
tableData:[], |
|||
// 总条数 |
|||
total: 0, |
|||
// 设备方向 |
|||
directionOptions: direction, |
|||
// 使用状态 |
|||
useStateOptions: useState, |
|||
// 设备状态 |
|||
deviceStateOptions: deviceState, |
|||
logVisible:false, |
|||
deviceType:typeTree, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
type:'', |
|||
orderByField:'online_rate', |
|||
orderDirection:'desc', |
|||
searchValue:'', |
|||
startTime:'2024-07-01 00:00:00', |
|||
time:'2024-07-24 00:00:00', |
|||
direction:'' |
|||
}, |
|||
logQueryParams: { |
|||
//pageNum: 1, |
|||
//pageSize: 10, |
|||
queryDate:new Date(), |
|||
deviceId:'', |
|||
}, |
|||
logChartData:{}, |
|||
logTableData:[], |
|||
logTotal:0 |
|||
} |
|||
}, |
|||
created() { |
|||
|
|||
}, |
|||
methods: { |
|||
defaultCheck(types){ |
|||
this.queryParams.type = types.join(','); |
|||
this.deviceOnlineTable(); |
|||
this.deviceOnlineChart(); |
|||
}, |
|||
nodeCheck(data, checked){ |
|||
this.queryParams.type = checked.checkedKeys.join(','); |
|||
this.deviceOnlineTable(); |
|||
this.deviceOnlineChart(); |
|||
}, |
|||
deviceOnlineTable(){ |
|||
deviceOnlineTable(this.queryParams).then(response => { |
|||
this.tableData = response.rows; |
|||
this.total = response.total; |
|||
}) |
|||
}, |
|||
deviceOnlineChart(){ |
|||
deviceOnlineChart(this.queryParams).then(response => { |
|||
let data = response.data; |
|||
let xData ,yData = []; |
|||
if(Object.keys(data).length !== 0){ |
|||
xData =Object.keys(data[Object.keys(data)[0]]); |
|||
for(const type in data){ |
|||
let yItem = {} |
|||
yItem.name = this.findType(type).label; |
|||
yItem.unit = '%'; |
|||
yItem.data = []; |
|||
let lineObj = data[type]; |
|||
for(const time in lineObj){ |
|||
yItem.data.push(lineObj[time].replace('%','')); |
|||
} |
|||
yData.push(yItem); |
|||
} |
|||
} |
|||
this.chartData.xData = xData; |
|||
this.chartData.yData = yData; |
|||
}) |
|||
}, |
|||
seeLog(deviceId){ |
|||
this.logVisible = true; |
|||
this.initQueryDate(); |
|||
this.logQueryParams.deviceId = deviceId; |
|||
this.logQuery(); |
|||
}, |
|||
networkLogEcharts(){ |
|||
networkLogEcharts(this.logQueryParams).then(response => { |
|||
this.logChartData = response.data; |
|||
}) |
|||
}, |
|||
networkLogTable(){ |
|||
networkLogTable(this.logQueryParams).then(response => { |
|||
console.log(response) |
|||
this.logTableData = response.rows; |
|||
this.logTotal = response.total; |
|||
}) |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.deviceOnlineTable(); |
|||
}, |
|||
handleSortChange({ prop, order }){ |
|||
this.queryParams.orderDirection = order === 'ascending'?'asc':'desc'; |
|||
if (prop === 'onlineRate') { |
|||
this.queryParams.orderByField = 'online_rate'; |
|||
} |
|||
this.deviceOnlineTable(); |
|||
}, |
|||
logQuery(){ |
|||
if(!this.logQueryParams.queryDate) return; |
|||
this.networkLogEcharts() |
|||
this.networkLogTable() |
|||
}, |
|||
initQueryDate(){ |
|||
const date = new Date(); |
|||
this.logQueryParams.queryDate = `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')}`; |
|||
}, |
|||
upDay(){ |
|||
this.logQueryParams.queryDate = this.addOrSubtractDays(this.logQueryParams.queryDate, -1); |
|||
this.logQuery(); |
|||
}, |
|||
downDay(){ |
|||
this.logQueryParams.queryDate = this.addOrSubtractDays(this.logQueryParams.queryDate, 1); |
|||
this.logQuery(); |
|||
}, |
|||
addOrSubtractDays(dateStr, numDays) { |
|||
const parts = dateStr.split('-'); |
|||
const year = parseInt(parts[0], 10); |
|||
const month = parseInt(parts[1], 10) - 1; // 月份需要减1,因为Date对象月份是从0开始的 |
|||
const day = parseInt(parts[2], 10); |
|||
const date = new Date(year, month, day); |
|||
date.setDate(date.getDate() + numDays); |
|||
const yearFormatted = date.getFullYear(); |
|||
const monthFormatted = (date.getMonth() + 1).toString().padStart(2, '0'); // 月份加1,并格式化为两位数 |
|||
const dayFormatted = date.getDate().toString().padStart(2, '0'); // 格式化为两位数 |
|||
return `${yearFormatted}-${monthFormatted}-${dayFormatted}`; |
|||
}, |
|||
findType(typeCode) { |
|||
return this.deviceType.find(item => item.value == typeCode) |
|||
}, |
|||
handleExport() { |
|||
this.download('system/status/export', { |
|||
...this.queryParams |
|||
}, `设备在线率${new Date().getTime()}.xlsx`) |
|||
}, |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
|
|||
</style> |
@ -0,0 +1,177 @@ |
|||
<template> |
|||
<div :class="className" :style="{height:height,width:width}" /> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as echarts from 'echarts'; |
|||
export default { |
|||
props: { |
|||
className: { |
|||
type: String, |
|||
default: 'chart' |
|||
}, |
|||
width: { |
|||
type: String, |
|||
default: '100%' |
|||
}, |
|||
height: { |
|||
type: String, |
|||
default: '110px' |
|||
}, |
|||
chartData: { |
|||
type: Object, |
|||
required: true |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
chart: null |
|||
} |
|||
}, |
|||
watch: { |
|||
chartData: { |
|||
deep: true, |
|||
handler(val) { |
|||
this.setOptions(val) |
|||
} |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.$nextTick(() => { |
|||
this.initChart() |
|||
}) |
|||
}, |
|||
beforeDestroy() { |
|||
if (!this.chart) { |
|||
return |
|||
} |
|||
this.chart.dispose() |
|||
this.chart = null |
|||
}, |
|||
methods: { |
|||
initChart() { |
|||
this.chart = echarts.init(this.$el) |
|||
this.setOptions(this.chartData) |
|||
}, |
|||
setOptions(data) { |
|||
this.chart.setOption({ |
|||
color:['rgba(24, 144, 255, 1)','rgba(255, 114, 114, 1)'], |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'cross' |
|||
}, |
|||
formatter: function (params) { |
|||
let res = params[0].name + '<br/>'; |
|||
// 遍历所有参数项 |
|||
params.forEach(item => { |
|||
let unit = item.seriesName === '丢包率' ? '%' : 'ms'; |
|||
res += `${item.seriesName} : ${item.value}${unit}<br/>`; |
|||
}); |
|||
return res; |
|||
}, |
|||
padding: [5, 10], |
|||
}, |
|||
legend: { |
|||
data: ['丢包率', '平均往返时延'], |
|||
right:150, |
|||
top:-5, |
|||
}, |
|||
grid: { |
|||
left: '1%', |
|||
right: '2%', |
|||
bottom: '3%', |
|||
top:'8%', |
|||
containLabel: true |
|||
}, |
|||
xAxis: [ |
|||
{ |
|||
type: 'category', |
|||
boundaryGap: false, |
|||
data: data.time |
|||
} |
|||
], |
|||
yAxis: [ |
|||
{ |
|||
name: '%', |
|||
type: 'value', |
|||
position: 'left', |
|||
splitLine: { |
|||
show: false, |
|||
lineStyle: { |
|||
color: '#F1F1F1' |
|||
} |
|||
}, |
|||
axisLabel: { |
|||
color: '#656161', |
|||
formatter: function (value) { |
|||
return value + ' %'; |
|||
} |
|||
} |
|||
}, |
|||
{ |
|||
name: 'ms', |
|||
type: 'value', |
|||
position: 'right', |
|||
splitLine: { |
|||
show: false |
|||
}, |
|||
axisLabel: { |
|||
color: '#656161', |
|||
formatter: function (value) { |
|||
return value + ' ms'; |
|||
} |
|||
} |
|||
} |
|||
], |
|||
series: [ |
|||
{ |
|||
name: '丢包率', |
|||
type: 'line', |
|||
yAxisIndex: 0, |
|||
//stack: 'Total', |
|||
smooth: true, |
|||
areaStyle: { |
|||
normal: { |
|||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
{ offset: 1, color: "rgba(59, 240, 240, 0)" }, |
|||
{ offset: 0, color: "rgba(136, 193, 245, 1)" } |
|||
]) |
|||
} |
|||
}, |
|||
showSymbol: false, |
|||
/* emphasis: { |
|||
focus: 'series' |
|||
},*/ |
|||
data: data.lostRate |
|||
}, |
|||
{ |
|||
name: '平均往返时延', |
|||
type: 'line', |
|||
yAxisIndex: 1, |
|||
stack: 'Total', |
|||
smooth: true, |
|||
showSymbol: false, |
|||
areaStyle: { |
|||
normal: { |
|||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ |
|||
{ offset: 1, color: "rgba(254, 192, 6, 0)" }, |
|||
{ offset: 0, color: "rgba(254, 192, 6, 0.38)" } |
|||
]) |
|||
} |
|||
}, |
|||
/* emphasis: { |
|||
focus: 'series' |
|||
},*/ |
|||
data: data.rttAvg |
|||
} |
|||
] |
|||
}) |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
|
|||
</style> |
@ -0,0 +1,172 @@ |
|||
<!--曲线图--> |
|||
<template> |
|||
<div ref="echarts" :style="{height:height,width:width}"/> |
|||
</template> |
|||
|
|||
<script> |
|||
import * as echarts from 'echarts' |
|||
|
|||
require('echarts/theme/macarons') // echarts theme |
|||
import resize from './mixins/resize' |
|||
|
|||
export default { |
|||
mixins: [resize], |
|||
props: { |
|||
width: { |
|||
type: String, |
|||
default: '100%' |
|||
}, |
|||
height: { |
|||
type: String, |
|||
default: '350px' |
|||
}, |
|||
chartData: { |
|||
type: Object, |
|||
required: true |
|||
}, |
|||
showLegend:{ |
|||
type:Boolean, |
|||
default:true |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
chart: null |
|||
} |
|||
}, |
|||
watch: { |
|||
chartData: { |
|||
deep: true, |
|||
handler(val) { |
|||
this.setOptions(val) |
|||
} |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.$nextTick(() => { |
|||
this.initChart() |
|||
}) |
|||
}, |
|||
beforeDestroy() { |
|||
if (!this.chart) { |
|||
return |
|||
} |
|||
this.chart.dispose() |
|||
this.chart = null |
|||
}, |
|||
methods: { |
|||
initChart() { |
|||
this.chart = echarts.init(this.$refs.echarts) |
|||
this.setOptions(this.chartData) |
|||
}, |
|||
setOptions({xData, yData} = {}) { |
|||
let legend = [] |
|||
let series = [] |
|||
let unit ='' |
|||
let seriesDefault = { |
|||
name: '', |
|||
symbol: 'circle', |
|||
symbolSize: 1, |
|||
showSymbol: false, |
|||
smooth: true, |
|||
type: 'line', |
|||
itemStyle: { |
|||
color: '#06b1f5', |
|||
lineStyle: { |
|||
color: '#06b1f5', |
|||
width: 2 |
|||
} |
|||
}, |
|||
data: [], |
|||
//animationDuration: 2800, |
|||
//animationEasing: 'cubicInOut' |
|||
markPoint: { |
|||
symbol: 'pin', //标记(气泡)的图形 |
|||
symbolSize: 0, //标记(气泡)的大小 |
|||
itemStyle: { |
|||
color: 'rgba(18, 187, 93,1)', //图形的颜色。 |
|||
borderColor: '#000', //图形的描边颜色。支持的颜色格式同 color,不支持回调函数。 |
|||
borderWidth: 0, //描边线宽。为 0 时无描边。 |
|||
borderType: 'solid' //柱条的描边类型,默认为实线,支持 ‘solid’, ‘dashed’, ‘dotted’。 |
|||
}, |
|||
data: [ |
|||
{type: 'max', name: '最大值'}, |
|||
{type: 'min', name: '最小值'} |
|||
], |
|||
label: { |
|||
show: true |
|||
} |
|||
}, |
|||
} |
|||
yData.forEach(item => { |
|||
unit = item.unit |
|||
legend.push(item.name) |
|||
let target = JSON.parse(JSON.stringify(seriesDefault)); |
|||
target.name = item.name |
|||
target.itemStyle.color = item.color |
|||
target.itemStyle.lineStyle.color = item.color |
|||
target.data = item.data |
|||
series.push(target) |
|||
}) |
|||
if (series.length <= 0) series.push(seriesDefault) |
|||
this.chart.setOption({ |
|||
grid: { |
|||
left: 20, |
|||
right: 10, |
|||
bottom: 0, |
|||
top: 40, |
|||
containLabel: true |
|||
}, |
|||
tooltip: { |
|||
trigger: 'axis', |
|||
axisPointer: { |
|||
type: 'cross' |
|||
}, |
|||
padding: [5, 10], |
|||
}, |
|||
legend: { |
|||
show:this.showLegend, |
|||
right: '1%', |
|||
top: '1%', |
|||
textStyle: { |
|||
color: '#556677' |
|||
}, |
|||
data: legend |
|||
}, |
|||
xAxis: { |
|||
data: xData, |
|||
boundaryGap: false, |
|||
axisLine: { |
|||
show: false, |
|||
}, |
|||
axisTick: { |
|||
show: false |
|||
}, |
|||
axisLabel: { |
|||
color: '#9da3a8', |
|||
width: 100, |
|||
// 默认x轴字体大小 |
|||
fontSize: 12, |
|||
// margin:文字到x轴的距离 |
|||
margin: 15 |
|||
}, |
|||
}, |
|||
yAxis: { |
|||
name: unit, |
|||
nameTextStyle: { |
|||
color: '#9da3a8', |
|||
fontSize: 15 |
|||
}, |
|||
axisTick: { |
|||
show: false |
|||
}, |
|||
axisLabel: { |
|||
color: '#9da3a8' |
|||
}, |
|||
}, |
|||
series: series |
|||
},true) |
|||
} |
|||
} |
|||
} |
|||
</script> |
@ -0,0 +1,56 @@ |
|||
import { debounce } from '@/utils' |
|||
|
|||
export default { |
|||
data() { |
|||
return { |
|||
$_sidebarElm: null, |
|||
$_resizeHandler: null |
|||
} |
|||
}, |
|||
mounted() { |
|||
this.initListener() |
|||
}, |
|||
activated() { |
|||
if (!this.$_resizeHandler) { |
|||
// avoid duplication init
|
|||
this.initListener() |
|||
} |
|||
|
|||
// when keep-alive chart activated, auto resize
|
|||
this.resize() |
|||
}, |
|||
beforeDestroy() { |
|||
this.destroyListener() |
|||
}, |
|||
deactivated() { |
|||
this.destroyListener() |
|||
}, |
|||
methods: { |
|||
// use $_ for mixins properties
|
|||
// https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
|
|||
$_sidebarResizeHandler(e) { |
|||
if (e.propertyName === 'width') { |
|||
this.$_resizeHandler() |
|||
} |
|||
}, |
|||
initListener() { |
|||
this.$_resizeHandler = debounce(() => { |
|||
this.resize() |
|||
}, 100) |
|||
window.addEventListener('resize', this.$_resizeHandler) |
|||
|
|||
this.$_sidebarElm = document.getElementsByClassName('sidebar-container')[0] |
|||
this.$_sidebarElm && this.$_sidebarElm.addEventListener('transitionend', this.$_sidebarResizeHandler) |
|||
}, |
|||
destroyListener() { |
|||
window.removeEventListener('resize', this.$_resizeHandler) |
|||
this.$_resizeHandler = null |
|||
|
|||
this.$_sidebarElm && this.$_sidebarElm.removeEventListener('transitionend', this.$_sidebarResizeHandler) |
|||
}, |
|||
resize() { |
|||
const { chart } = this |
|||
chart && chart.resize() |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,320 @@ |
|||
<template> |
|||
<div class="app-container"> |
|||
<el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px"> |
|||
<el-form-item label="产品名称" prop="productName"> |
|||
<el-input |
|||
v-model="queryParams.productName" |
|||
placeholder="请输入产品名称" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="生产厂商" prop="manufacturer"> |
|||
<el-input |
|||
v-model="queryParams.manufacturer" |
|||
placeholder="请输入生产厂商" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="设备品牌" prop="brand"> |
|||
<el-input |
|||
v-model="queryParams.brand" |
|||
placeholder="请输入设备品牌" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="设备型号" prop="model"> |
|||
<el-input |
|||
v-model="queryParams.model" |
|||
placeholder="请输入设备型号" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item label="设备产地" prop="productionPlace"> |
|||
<el-input |
|||
v-model="queryParams.productionPlace" |
|||
placeholder="请输入设备产地" |
|||
clearable |
|||
@keyup.enter.native="handleQuery" |
|||
/> |
|||
</el-form-item> |
|||
<el-form-item> |
|||
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button> |
|||
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button> |
|||
</el-form-item> |
|||
</el-form> |
|||
|
|||
<el-row :gutter="10" class="mb8"> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="primary" |
|||
plain |
|||
icon="el-icon-plus" |
|||
size="mini" |
|||
@click="handleAdd" |
|||
v-hasPermi="['deviceManage:product:add']" |
|||
>新增</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="success" |
|||
plain |
|||
icon="el-icon-edit" |
|||
size="mini" |
|||
:disabled="single" |
|||
@click="handleUpdate" |
|||
v-hasPermi="['deviceManage:product:edit']" |
|||
>修改</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="danger" |
|||
plain |
|||
icon="el-icon-delete" |
|||
size="mini" |
|||
:disabled="multiple" |
|||
@click="handleDelete" |
|||
v-hasPermi="['deviceManage:product:remove']" |
|||
>删除</el-button> |
|||
</el-col> |
|||
<el-col :span="1.5"> |
|||
<el-button |
|||
type="warning" |
|||
plain |
|||
icon="el-icon-download" |
|||
size="mini" |
|||
@click="handleExport" |
|||
v-hasPermi="['deviceManage:product:export']" |
|||
>导出</el-button> |
|||
</el-col> |
|||
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar> |
|||
</el-row> |
|||
|
|||
<el-table v-loading="loading" :data="productList" @selection-change="handleSelectionChange"> |
|||
<el-table-column type="selection" width="55" align="center" /> |
|||
<el-table-column label="主键" align="center" prop="id" /> |
|||
<el-table-column label="产品名称" align="center" prop="productName" /> |
|||
<el-table-column label="生产厂商" align="center" prop="manufacturer" /> |
|||
<el-table-column label="设备品牌" align="center" prop="brand" /> |
|||
<el-table-column label="设备型号" align="center" prop="model" /> |
|||
<el-table-column label="设备产地" align="center" prop="productionPlace" /> |
|||
<el-table-column label="备注" align="center" prop="remark" /> |
|||
<el-table-column label="其他配置" align="center" prop="otherConfig" /> |
|||
<el-table-column label="操作" align="center" class-name="small-padding fixed-width"> |
|||
<template slot-scope="scope"> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-edit" |
|||
@click="handleUpdate(scope.row)" |
|||
v-hasPermi="['deviceManage:product:edit']" |
|||
>修改</el-button> |
|||
<el-button |
|||
size="mini" |
|||
type="text" |
|||
icon="el-icon-delete" |
|||
@click="handleDelete(scope.row)" |
|||
v-hasPermi="['deviceManage:product:remove']" |
|||
>删除</el-button> |
|||
</template> |
|||
</el-table-column> |
|||
</el-table> |
|||
|
|||
<pagination |
|||
v-show="total>0" |
|||
:total="total" |
|||
:page.sync="queryParams.pageNum" |
|||
:limit.sync="queryParams.pageSize" |
|||
@pagination="getList" |
|||
/> |
|||
|
|||
<!-- 添加或修改产品对话框 --> |
|||
<el-dialog :title="title" :visible.sync="open" width="500px" append-to-body> |
|||
<el-form ref="form" :model="form" :rules="rules" label-width="80px"> |
|||
<el-form-item label="产品名称" prop="productName"> |
|||
<el-input v-model="form.productName" placeholder="请输入产品名称" /> |
|||
</el-form-item> |
|||
<el-form-item label="生产厂商" prop="manufacturer"> |
|||
<el-input v-model="form.manufacturer" placeholder="请输入生产厂商" /> |
|||
</el-form-item> |
|||
<el-form-item label="设备品牌" prop="brand"> |
|||
<el-input v-model="form.brand" placeholder="请输入设备品牌" /> |
|||
</el-form-item> |
|||
<el-form-item label="设备型号" prop="model"> |
|||
<el-input v-model="form.model" placeholder="请输入设备型号" /> |
|||
</el-form-item> |
|||
<el-form-item label="设备产地" prop="productionPlace"> |
|||
<el-input v-model="form.productionPlace" placeholder="请输入设备产地" /> |
|||
</el-form-item> |
|||
<el-form-item label="备注" prop="remark"> |
|||
<el-input v-model="form.remark" placeholder="请输入备注" /> |
|||
</el-form-item> |
|||
<el-form-item label="其他配置" prop="otherConfig"> |
|||
<el-input v-model="form.otherConfig" type="textarea" placeholder="请输入内容" /> |
|||
</el-form-item> |
|||
</el-form> |
|||
<div slot="footer" class="dialog-footer"> |
|||
<el-button type="primary" @click="submitForm">确 定</el-button> |
|||
<el-button @click="cancel">取 消</el-button> |
|||
</div> |
|||
</el-dialog> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import { listProduct, getProduct, delProduct, addProduct, updateProduct } from "@/api/deviceManage/product"; |
|||
|
|||
export default { |
|||
name: "Product", |
|||
data() { |
|||
return { |
|||
// 遮罩层 |
|||
loading: true, |
|||
// 选中数组 |
|||
ids: [], |
|||
// 非单个禁用 |
|||
single: true, |
|||
// 非多个禁用 |
|||
multiple: true, |
|||
// 显示搜索条件 |
|||
showSearch: true, |
|||
// 总条数 |
|||
total: 0, |
|||
// 产品表格数据 |
|||
productList: [], |
|||
// 弹出层标题 |
|||
title: "", |
|||
// 是否显示弹出层 |
|||
open: false, |
|||
// 查询参数 |
|||
queryParams: { |
|||
pageNum: 1, |
|||
pageSize: 10, |
|||
productName: null, |
|||
manufacturer: null, |
|||
brand: null, |
|||
model: null, |
|||
productionPlace: null, |
|||
otherConfig: null, |
|||
}, |
|||
// 表单参数 |
|||
form: {}, |
|||
// 表单校验 |
|||
rules: { |
|||
productName: [ |
|||
{ required: true, message: "产品名称不能为空", trigger: "blur" } |
|||
], |
|||
createTime: [ |
|||
{ required: true, message: "创建时间不能为空", trigger: "blur" } |
|||
], |
|||
} |
|||
}; |
|||
}, |
|||
created() { |
|||
this.getList(); |
|||
}, |
|||
methods: { |
|||
/** 查询产品列表 */ |
|||
getList() { |
|||
this.loading = true; |
|||
listProduct(this.queryParams).then(response => { |
|||
this.productList = response.rows; |
|||
this.total = response.total; |
|||
this.loading = false; |
|||
}); |
|||
}, |
|||
// 取消按钮 |
|||
cancel() { |
|||
this.open = false; |
|||
this.reset(); |
|||
}, |
|||
// 表单重置 |
|||
reset() { |
|||
this.form = { |
|||
id: null, |
|||
productName: null, |
|||
manufacturer: null, |
|||
brand: null, |
|||
model: null, |
|||
productionPlace: null, |
|||
remark: null, |
|||
otherConfig: null, |
|||
createTime: null, |
|||
updateTime: null |
|||
}; |
|||
this.resetForm("form"); |
|||
}, |
|||
/** 搜索按钮操作 */ |
|||
handleQuery() { |
|||
this.queryParams.pageNum = 1; |
|||
this.getList(); |
|||
}, |
|||
/** 重置按钮操作 */ |
|||
resetQuery() { |
|||
this.resetForm("queryForm"); |
|||
this.handleQuery(); |
|||
}, |
|||
// 多选框选中数据 |
|||
handleSelectionChange(selection) { |
|||
this.ids = selection.map(item => item.id) |
|||
this.single = selection.length!==1 |
|||
this.multiple = !selection.length |
|||
}, |
|||
/** 新增按钮操作 */ |
|||
handleAdd() { |
|||
this.reset(); |
|||
this.open = true; |
|||
this.title = "添加产品"; |
|||
}, |
|||
/** 修改按钮操作 */ |
|||
handleUpdate(row) { |
|||
this.reset(); |
|||
const id = row.id || this.ids |
|||
getProduct(id).then(response => { |
|||
this.form = response.data; |
|||
this.open = true; |
|||
this.title = "修改产品"; |
|||
}); |
|||
}, |
|||
/** 提交按钮 */ |
|||
submitForm() { |
|||
this.$refs["form"].validate(valid => { |
|||
if (valid) { |
|||
if (this.form.id != null) { |
|||
updateProduct(this.form).then(response => { |
|||
this.$modal.msgSuccess("修改成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} else { |
|||
addProduct(this.form).then(response => { |
|||
this.$modal.msgSuccess("新增成功"); |
|||
this.open = false; |
|||
this.getList(); |
|||
}); |
|||
} |
|||
} |
|||
}); |
|||
}, |
|||
/** 删除按钮操作 */ |
|||
handleDelete(row) { |
|||
const ids = row.id || this.ids; |
|||
this.$modal.confirm('是否确认删除产品编号为"' + ids + '"的数据项?').then(function() { |
|||
return delProduct(ids); |
|||
}).then(() => { |
|||
this.getList(); |
|||
this.$modal.msgSuccess("删除成功"); |
|||
}).catch(() => {}); |
|||
}, |
|||
/** 导出按钮操作 */ |
|||
handleExport() { |
|||
this.download('deviceManage/product/export', { |
|||
...this.queryParams |
|||
}, `product_${new Date().getTime()}.xlsx`) |
|||
} |
|||
} |
|||
}; |
|||
</script> |
@ -0,0 +1,229 @@ |
|||
<template> |
|||
<div> |
|||
<div class="box"> |
|||
<el-row v-if="filter"> |
|||
<el-input |
|||
v-model="nodeName" |
|||
placeholder="请输入关键字" |
|||
clearable |
|||
size="mini" |
|||
suffix-icon="el-icon-search" |
|||
style="width: 100%" |
|||
/> |
|||
</el-row> |
|||
<!-- 级联 全选 --> |
|||
<div class="check" v-if="show_checkbox"> |
|||
<el-checkbox v-model="check_strictly">级联选择</el-checkbox> |
|||
<el-checkbox v-model="check_all" @change="handleCheckedTreeNodeAll($event, 'menu')">全选</el-checkbox> |
|||
</div> |
|||
</div> |
|||
|
|||
<el-scrollbar> |
|||
<el-row :style="{ height: height }"> |
|||
<el-tree |
|||
class="tree" |
|||
accordion |
|||
:data="treeOptions" |
|||
:props="defaultProps" |
|||
:expand-on-click-node="true" |
|||
:check-on-click-node="true" |
|||
:show-checkbox="show_checkbox" |
|||
:check-strictly="!check_strictly" |
|||
:filter-node-method="filterNode" |
|||
ref="tree" |
|||
default-expand-all |
|||
@node-click="handleNodeClick" |
|||
@check="handleCheckChange" |
|||
node-key="value" |
|||
> |
|||
</el-tree> |
|||
</el-row> |
|||
</el-scrollbar> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import {typeTree} from "@/enum/deviceEnum" |
|||
export default { |
|||
name: "typeTree", |
|||
props: { |
|||
//开启过滤 |
|||
filter: { |
|||
type: Boolean, |
|||
default: true, |
|||
}, |
|||
//节点是否可被选择 |
|||
show_checkbox: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
//是否级联 |
|||
check_strictly: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
//开启默认全选 |
|||
default_check_all: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
//开启默认选中第一个子节点 |
|||
default_check_first: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
//默认第一个子节点高亮选中 |
|||
default_select_first: { |
|||
type: Boolean, |
|||
default: false, |
|||
}, |
|||
height: { |
|||
type: String, |
|||
default: "calc(100vh - 218px)", |
|||
}, |
|||
}, |
|||
data() { |
|||
return { |
|||
//名称 |
|||
nodeName: null, |
|||
//回路选项 |
|||
treeOptions: [], |
|||
defaultProps: { |
|||
value: "value", |
|||
label: "label", |
|||
children: "children", |
|||
}, |
|||
check_all: this.default_check_all, |
|||
}; |
|||
}, |
|||
watch: { |
|||
// 根据名称筛选树 |
|||
nodeName(val) { |
|||
this.$refs.tree.filter(val); |
|||
}, |
|||
}, |
|||
mounted() { |
|||
this.treeOptions = typeTree; |
|||
this.$nextTick(() => { |
|||
this.showCheckBox(); |
|||
this.selectFirstChild(); |
|||
}); |
|||
}, |
|||
methods: { |
|||
//全选 |
|||
handleCheckedTreeNodeAll(value, type) { |
|||
let arr = []; |
|||
if (value) { |
|||
this.getAllKeys(this.treeOptions, arr, "menuId"); |
|||
} |
|||
this.$refs.tree.setCheckedKeys(arr); |
|||
this.$emit("defaultCheck", arr); |
|||
}, |
|||
// 筛选节点时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏 |
|||
filterNode(value, data) { |
|||
if (!value) return true; |
|||
return data.label.indexOf(value) !== -1; |
|||
}, |
|||
//节点单击事件 |
|||
handleNodeClick(data) { |
|||
this.$emit("nodeClick", data); |
|||
}, |
|||
|
|||
//第一个子节点,高亮选中 |
|||
selectFirstChild() { |
|||
if (this.default_select_first) { |
|||
let first = this.getFirstChildren(this.treeOptions); |
|||
this.$refs.tree.setCurrentKey(first); |
|||
this.$emit("defaultSelect", first, this.$refs.tree.getCurrentNode()); |
|||
} |
|||
}, |
|||
//节点选中事件--复选框 |
|||
handleCheckChange(data, checked) { |
|||
// console.log(data, checked); |
|||
let n = this.getAllKeys(this.treeOptions); |
|||
// 反选 |
|||
if (checked.checkedKeys.length === n.length) { |
|||
this.check_all = true; |
|||
} else { |
|||
this.check_all = false; |
|||
} |
|||
this.$emit("nodeCheck", data, checked); |
|||
}, |
|||
//默认选中--复选框 |
|||
showCheckBox() { |
|||
if (this.show_checkbox) { |
|||
//默认全选 |
|||
if (this.default_check_all) { |
|||
let all = this.getAllKeys(this.treeOptions); |
|||
this.$refs.tree.setCheckedKeys(all); |
|||
this.$emit("defaultCheck", all); |
|||
} |
|||
//默认选中第一个子节点 |
|||
if (this.default_check_first) { |
|||
let arr = []; |
|||
let first = this.getFirstChildren(this.treeOptions); |
|||
if (first) arr.push(first); |
|||
this.$refs.tree.setCheckedKeys(arr); |
|||
this.$emit("defaultCheck", arr); |
|||
} |
|||
} |
|||
}, |
|||
|
|||
//获取所有节点 |
|||
getAllKeys(node, arr = []) { |
|||
for (let item of node) { |
|||
arr.push(item.value); |
|||
let parentArr = []; |
|||
if (item.children) parentArr.push(...item.children); |
|||
if (parentArr && parentArr.length) this.getAllKeys(parentArr, arr); |
|||
} |
|||
return arr; |
|||
}, |
|||
//获取第一个子节点 |
|||
getFirstChildren(node) { |
|||
if (node.length) { |
|||
return node[0].children && node[0].children.length > 0 |
|||
? this.getFirstChildren(node[0].children) |
|||
: node[0].value; |
|||
//return node[0].value; |
|||
} |
|||
}, |
|||
}, |
|||
}; |
|||
</script> |
|||
|
|||
<style lang="scss" scoped> |
|||
.box{ |
|||
padding-bottom: 10px; |
|||
} |
|||
::v-deep .el-tree-node__content { |
|||
margin: 10px 0 !important; |
|||
} |
|||
::v-deep .el-input--small .el-input__inner{ |
|||
text-align: center; |
|||
} |
|||
::v-deep .el-input--mini .el-input__inner{ |
|||
text-align: center; |
|||
} |
|||
::v-deep .el-scrollbar__thumb{ |
|||
display: none; |
|||
} |
|||
.check { |
|||
padding-bottom: 10px; |
|||
text-align: center; |
|||
::v-deep .el-checkbox { |
|||
width: 50%; |
|||
height: 20px; |
|||
margin: 0; |
|||
line-height: 20px; |
|||
} |
|||
::v-deep .el-checkbox__label { |
|||
font-size: 16px; |
|||
padding-left: 8px; |
|||
} |
|||
::v-deep .el-checkbox__inner { |
|||
margin-bottom: 2px; |
|||
} |
|||
} |
|||
|
|||
</style> |
Loading…
Reference in new issue